Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions code_snippets/mechbench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# MECHBench

Evaluates a function from [MECHBench](https://github.com/BayesOptApp/MECHBench) — a benchmark suite of structural mechanics optimisation problems powered by [OpenRadioss](https://openradioss.org) finite element simulations. It provides three real-world crash and bending test cases with continuous design variables, supporting single-objective, multi-objective, constrained, and unconstrained problem formulations.

> [!WARNING]
> MECHBench only runs on **Linux** and **Windows**. OpenRadioss provides no macOS binaries, and MECHBench raises `ValueError: Unsupported platform` on startup.

## Quick Start

1. Install [uv](https://docs.astral.sh/uv/) if you don't have it yet:

```bash
pip install uv
```

2. Clone the MECHBench repository into the same folder as `call_mechbench.py`:

```bash
git clone https://github.com/BayesOptApp/MECHBench.git
```

Your directory should look like:

```
├── call_mechbench.py
└── MECHBench/
├── main.py
├── src/
│ └── sob/
│ └── ...
└── requirements.txt
```

## Usage

```bash
uv run call_mechbench.py
```

OpenRadioss binaries are downloaded automatically on first run if not already present.

## What the Snippet Does

The script instantiates a MECHBench problem, evaluates it at the zero vector, and prints the objective value. You can adjust the behaviour by editing these variables in the script:

- **`problem_id`** — selects the mechanical test case:
- `1` — Star Box (crash box with a star-shaped cross-section)
- `2` — Three-Point Bending (structural beam under transverse load)
- `3` — Crash Tube (thin-walled tube with trigger geometry optimisation)
- **`dim`** — number of design variables (problem dimensionality)
- **`sim_id`** — unique integer ID for the simulation run; results are stored in a folder named after this ID
- **`eval_point`** — the design vector at which the objective is evaluated (default: all zeros)
- **`runnerOptions`** — dictionary controlling the FEM solver:
- `np` — number of MPI processes (default: `1`)
- `nt` — number of OpenMP threads per process (default: `1`)
- `h_level` — mesh refinement level; higher values increase resolution and cost (default: `1`)
- `gmsh_verbosity` — GMSH output verbosity; `0` = silent (default: `0`)
- `write_vtk` — export results in VTK format for visualisation (default: `False`)
- `open_radioss_main_path` *(optional)* — path to a custom OpenRadioss installation

### How MECHBench Works

Each problem wraps an explicit FEM simulation. The design vector is mapped to geometric or material parameters of a structural model. MECHBench then: generates a mesh via GMSH, assembles the FEM model, runs an OpenRadioss simulation, and post-processes the output to return one or more scalar metrics (e.g., absorbed energy, intrusion, peak force). The objective function is a callable `f` obtained via `sob.get_problem(problem_id, dim)` and evaluated as `f(vector, sim_id)`.

### Available Metrics

| Metric | Requires FEM Simulation | Notes |
|---|---|---|
| `mass` | No | |
| `absorbed_energy` | No | |
| `specific_energy_absorbed` | No | Derived from `mass` and `absorbed_energy` |
| `intrusion` | Yes | |
| `mean_impact_force` | Yes | |
| `peak_impact_force` | Yes | |
| `usage_ratio` | Yes | Derived from impact forces |
| `load_uniformity` | Yes | Derived from impact forces |
| `penalized_sea` | Yes | Only for Problem 1 |
| `penalized_mass` | Yes | Only for Problem 2 |

By default `sob.get_problem` returns a single-objective function. To access multiple metrics or define constrained problems, pass a `target_metric` argument (string or list of strings) to `sob.get_problem`.

## Resources

- [MECHBench GitHub repository](https://github.com/BayesOptApp/MECHBench)
- [OpenRadioss](https://openradioss.org)
36 changes: 36 additions & 0 deletions code_snippets/mechbench/call_mechbench.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# /// script
# dependencies = [
# "numpy",
# "MECHBench @ git+https://github.com/BayesOptApp/MECHBench.git",
# ]
# ///

import sys
import os
import numpy as np

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "MECHBench__"))
from src import sob

### input
sim_id = 1 # unique ID for this simulation run (used to name results folder)
problem_id = 1 # 1: Star Box, 2: Three-Point Bending, 3: Crash Tube
dim = 5 # number of design variables

runnerOptions = {
"np": 1, # number of MPI processes
"nt": 1, # number of OpenMP threads
"h_level": 1, # mesh refinement level (higher = finer, slower)
"gmsh_verbosity": 0,
"write_vtk": False,
}

### create problem instance
f = sob.get_problem(problem_id, dim, runner_options=runnerOptions)

### evaluation point
eval_point = np.zeros(dim).tolist()

### print function value for eval_point
print(f"Problem ID: {problem_id}, dim: {dim}")
print(f"Objective value: {f(eval_point, sim_id)}")
68 changes: 68 additions & 0 deletions code_snippets/mpm2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# MPM2 (Multiple Peaks Model 2)

Evaluates a function generated by the [Multiple Peaks Model 2](https://github.com/jakobbossek/smoof/blob/master/inst/mpm2.py) — a test problem generator that creates multimodal landscapes by combining randomly distributed peaks on [0, 1]^n. The objective function is a minimization problem with values in [0, 1], where 0 is the global optimum.

MPM2 is part of the [smoof](https://github.com/jakobbossek/smoof) R package and is also available as a standalone Python file.

## Quick Start

1. Install [uv](https://docs.astral.sh/uv/) if you don't have it yet:

```bash
pip install uv
```

2. Download `mpm2.py` and place it in the same directory as `call_mpm2.py`:

```bash
curl -O https://raw.githubusercontent.com/jakobbossek/smoof/master/inst/mpm2.py
```

Your directory should look like:

```
├── call_mpm2.py
└── mpm2.py
```

## Usage

```bash
uv run call_mpm2.py
```

## What the Snippet Does

The script creates a problem instance with 4 peaks in 2 dimensions using random topology, evaluates it at the origin, and prints the result. You can adjust the behavior by editing these variables in the script:

- **`numberOptima`** — number of local optima / peaks in the landscape (default: `4`)
- **`numberVariables`** — problem dimensionality (default: `2`)
- **`topology`** — peak arrangement: `"random"` (uniform) or `"funnel"` (clustered around global optimum) (default: `"random"`)
- **`shapeHeightCorrelation`** — correlation between peak shape and height: `-1` (anti-correlated), `0` (none), or `1` (correlated) (default: `0`)
- **`rotatedPeaks`** — whether peaks use random rotation matrices (default: `True`)
- **`peakShape`** — `"ellipse"` (different radii per axis) or `"sphere"` (uniform radii) (default: `"ellipse"`)
- **`seed`** — random seed for reproducibility (default: `42`)
- **`eval_point`** — the point at which the function is evaluated (default: all zeros)

### How MPM2 Works

Each problem instance is a landscape of overlapping peaks on [0, 1]^n. The objective function returns `1 - max(g(x, peak))` over all peaks, where `g` is a unimodal function parameterized by each peak's height, shape, radius, and covariance structure (based on Mahalanobis distance). The global optimum has value 0, corresponding to the peak with height 1.

### Useful Methods

| Method | Description |
|---|---|
| `objectiveFunction(x)` | Evaluate the problem at point `x` |
| `getLocalOptima()` | Return positions of all local optima |
| `getOptimalSolutions()` | Return positions of global optima |
| `getAllPeaks()` | Return all peak positions as a matrix |
| `getAllHeights()` | Return height of each peak |
| `getAllShapes()` | Return shape parameter of each peak |
| `getAllRadii()` | Return radius of each peak |
| `getActivePeak(x)` | Return the dominant peak at point `x` |

## Resources

- [mpm2.py source code](https://github.com/jakobbossek/smoof/blob/master/inst/mpm2.py)
- [smoof R package](https://github.com/jakobbossek/smoof)
- [MPM2 benchmark documentation](http://infinity77.net/go_2021/mpm2.html)
31 changes: 31 additions & 0 deletions code_snippets/mpm2/call_mpm2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# /// script
# dependencies = [
# "numpy",
# ]
# ///

import random
import numpy as np
from mpm2 import MultiplePeaksModel2

### input
seed = 42
random.seed(seed)
np.random.seed(seed)

problem = MultiplePeaksModel2.createInstanceWithExactNumberOfOptima(
numberOptima=4,
numberVariables=2,
topology="random", # "random" or "funnel"
shapeHeightCorrelation=0, # -1, 0, or 1
rotatedPeaks=True,
peakShape="ellipse", # "ellipse" or "sphere"
)

### evaluation point
dim = problem.numberVariables
print(f"Problem dimension: {dim}")
eval_point = [0.0]*dim

### print function value for eval_point
print(problem.objectiveFunction(eval_point))
Loading
Loading