Skip to content

Commit 8b8a97f

Browse files
mnabianclaude
andauthored
Openradioss dataset generation recipe (#1582)
* openradioss dataset generation recipe * add docstrings + SPDX headers to satisfy CI checks Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * add SPDX headers to remaining recipe files Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 91b55d3 commit 8b8a97f

13 files changed

Lines changed: 1588 additions & 0 deletions

File tree

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
<!-- markdownlint-disable -->
2+
# OpenRadioss Dataset Generation
3+
4+
End-to-end recipe for generating parameterized OpenRadioss simulation datasets
5+
and converting the output to the LS-DYNA-style `d3plot` layout expected by
6+
[PhysicsNeMo-Curator](https://github.com/NVIDIA/physicsnemo-curator) — the
7+
upstream step for training structural dynamics surrogates in
8+
`examples/structural_mechanics/crash/` (for bumper beam) and
9+
`examples/structural_mechanics/drop_test` (for drop test).
10+
11+
Two flows are supported out of the box:
12+
13+
| Flow | Base case | Varied parameters | Default DoE size |
14+
|----------------|------------------------------|------------------------------------------------|------------------|
15+
| **bumper_beam**| Front-impact bumper beam | geometry scale, shell thickness, impact velocity, rigid-wall diameter/origin | 135 runs |
16+
| **drop_test** | Cell-phone drop onto a plane | per-material Young's modulus (E), rigid-wall plane orientation (rx, ry, rz) | 192 runs |
17+
18+
Both flows share the same runner, d3plot-renaming step, and summary helpers;
19+
only the parameter-mutation logic differs.
20+
21+
22+
## Datasets
23+
24+
Each flow is a Design-of-Experiments sweep: the generator takes the Cartesian
25+
product of the configured axes, writes one case folder per combination, and
26+
applies the mutations directly to the `_0000.rad` starter deck. The defaults
27+
below live in `DEFAULT_EXPERIMENT_SETUP` near the bottom of each flow's
28+
`generate_dataset.py` — edit or replace that dict to resize or reshape the sweep.
29+
30+
### Bumper beam
31+
32+
Front-impact bumper-beam setup driven by an impactor on a rigid wall. The
33+
sweep targets the crash-response envelope by perturbing *geometry and loading*:
34+
bumper shape, wall-thickness mass distribution, impact speed, and the
35+
impactor's relative location on the beam.
36+
37+
Default: `5 × 3 × 3 × 1 × 3 = 135` runs.
38+
39+
| # | Parameter | Deck target | Default values | Count |
40+
|---|-----------|-------------|----------------|------:|
41+
| 1 | **Geometry scale `(sx, sy, sz)`** — per-axis multiplier applied to every node | `/NODE` block | `(1,1,1)`, `(1,0.5,1)`, `(1,1,0.5)`, `(1,2,1)`, `(1,1,2)` | 5 |
42+
| 2 | **Initial velocity `(vx, vy, vz)`** of the impactor (mm/ms) | `/INIVEL` line 2 | `(-5,0,0)`, `(-3,0,0)`, `(-7,0,0)` | 3 |
43+
| 3 | **Shell thickness multiplier** | `/PROP/SHELL` line 4, col 3 | `1.0`, `0.7`, `1.3` | 3 |
44+
| 4 | **Rigid-wall diameter** (mm) | `/RWALL` line 3, col 3 | `254.0` | 1 |
45+
| 5 | **Rigid-wall origin `(x, y, z)`**`M` is replaced; `M1` shifts by the same delta so the normal is preserved | `/RWALL` lines 4 + 5 | `(-170,0,0)`, `(-170,120,0)`, `(-170,240,0)` | 3 |
46+
47+
Global-feature columns emitted for training:
48+
`geo_scale_{x,y,z}`, `velocity_{x,y,z}`, `thickness_scale`, `rwall_diameter`,
49+
`rwall_origin_{x,y,z}`.
50+
51+
### Drop test
52+
53+
Cell-phone drop onto a plane. The sweep targets *material stiffness × impact
54+
pose* — perturbing Young's modulus for every deformable component while
55+
tilting the contact plane — to cover the stiffness/orientation interactions
56+
that drive peak stress and deflection.
57+
58+
Default: `2⁵ × 6 = 192` runs.
59+
60+
| # | Parameter | Deck target | Default values | Combinations |
61+
|---|-----------|-------------|----------------|-------------:|
62+
| 1 | **Young's modulus scale per material** — scales the `E` value on the line after the `# E Nu` comment in each `/MAT/ELAST` or `/MAT/PLAS_TAB` block | mat 1 (polymer), mat 4 (battery), mat 5 (glass), mat 8 (PCB), mat 9 (composites) | each mat ∈ `{0.8, 1.2}` (±20%) | 2⁵ = 32 |
63+
| 2 | **Rigid-wall plane orientation `(rx, ry, rz)`** — rotates the `M→M1` normal about `M` by the given XYZ Euler angles (degrees); `M1` is rewritten | `/RWALL` line 5 | `(0,0,0)`, `(±10,0,0)`, `(0,±10,0)`, `(0,0,10)` | 6 |
64+
65+
Global-feature columns emitted for training:
66+
`e_scale_mat{1,4,5,8,9}`, `rwall_orientation_{rx,ry,rz}`.
67+
68+
69+
## Directory layout
70+
71+
```
72+
openradioss_dataset_gen/
73+
├── README.md
74+
├── common/
75+
│ ├── radioss_runner.py # Starter / Engine / VTK / D3PLOT batch runner
76+
│ ├── rename_d3plot.py # Rename <BASE>.d3plot* -> d3plot*
77+
│ └── summary_utils.py # summary.json + summary.csv writer
78+
├── bumper_beam/
79+
│ ├── generate_dataset.py # DoE generator (see DEFAULT_EXPERIMENT_SETUP)
80+
│ ├── run_simulations.py # Thin wrapper over common.radioss_runner
81+
│ ├── restructure_global_features.py # summary.json -> global_features.json
82+
│ └── templates/ # place Bumper_Beam_AP_meshed_0000.rad / _0001.rad here
83+
└── drop_test/
84+
├── generate_dataset.py
85+
├── run_simulations.py
86+
├── restructure_global_features.py
87+
└── templates/ # place Cell_Phone_Drop_0000.rad / _0001.rad here
88+
```
89+
90+
91+
## Prerequisites
92+
93+
- **OpenRadioss** built with the GFortran-Linux executables (`starter_linux64_gf`,
94+
`engine_linux64_gf`, `anim_to_vtk_linux64_gf`). Point the runner at your build
95+
via the `OPENRADIOSS_ROOT` env var.
96+
- **`vortex_radioss`** for the `anim -> d3plot` conversion step:
97+
```bash
98+
pip install vortex-radioss
99+
```
100+
101+
### Base case inputs
102+
103+
The `.rad` starter and engine files are **not** shipped in the recipe because
104+
the bumper deck is ~1.8 MB and the drop-test deck is ~42 MB. Fetch them from
105+
the upstream OpenRadioss sources and place them under the corresponding
106+
`templates/` directory:
107+
108+
| Flow | Files to place under `templates/` | Source |
109+
|-------------|------------------------------------------------------------|--------|
110+
| bumper_beam | `Bumper_Beam_AP_meshed_0000.rad`, `Bumper_Beam_AP_meshed_0001.rad` | https://openradioss.atlassian.net/wiki/spaces/OPENRADIOSS/pages/11075585/Bumper+Beam |
111+
| drop_test | `Cell_Phone_Drop_0000.rad`, `Cell_Phone_Drop_0001.rad` | OpenRadioss sample models (Cell Phone Drop example) |
112+
113+
114+
## Workflow (same for both flows)
115+
116+
All commands below assume you are in the flow directory (`bumper_beam/` or
117+
`drop_test/`).
118+
119+
### 1. Generate the parameterised dataset
120+
121+
```bash
122+
python generate_dataset.py
123+
```
124+
125+
Creates:
126+
- `dataset/run1/`, `dataset/run2/`, … each containing a mutated `_0000.rad`
127+
starter, a copy of the `_0001.rad` engine deck, and a per-run `<run>.json`
128+
metadata file.
129+
- `dataset/summary.json` — hierarchical log of every run.
130+
- `dataset/summary.csv` — flat, ML-loader-friendly view.
131+
132+
To customise the DoE, edit `DEFAULT_EXPERIMENT_SETUP` near the bottom of
133+
`generate_dataset.py`, or import `generate_dataset(...)` from another driver
134+
script.
135+
136+
### 2. Run the simulations
137+
138+
```bash
139+
export OPENRADIOSS_ROOT=/path/to/OpenRadioss # required
140+
export MAX_PARALLEL_JOBS=4 # optional
141+
export OMP_NUM_THREADS=8 # optional
142+
python run_simulations.py
143+
```
144+
145+
For each `run*/` folder, the runner executes:
146+
1. `starter_linux64_gf -i <BASE>_0000.rad -nt $OMP_NUM_THREADS` (log: `starter.log`)
147+
2. `engine_linux64_gf -i <BASE>_0001.rad` (log: `engine.log`)
148+
3. `anim_to_vtk_linux64_gf <BASE>A### > <BASE>A###.vtk` per animation frame
149+
4. `vortex_radioss.animtod3plot.Anim_to_D3plot.readAndConvert(<BASE>)` — emits
150+
`<BASE>.d3plot`, `<BASE>.d3plot01`, … (log: `d3plot_conv.log`)
151+
152+
Total wall time scales with `MAX_PARALLEL_JOBS * OMP_NUM_THREADS`; do not
153+
oversubscribe CPU cores.
154+
155+
Set `DEBUG_MODE=1` to run only the first case.
156+
157+
### 3. Rename d3plot files to the LS-DYNA convention
158+
159+
```bash
160+
python ../common/rename_d3plot.py \
161+
--dataset-dir ./dataset \
162+
--base-name Bumper_Beam_AP_meshed # or Cell_Phone_Drop
163+
```
164+
165+
PhysicsNeMo-Curator expects the plain `d3plot`, `d3plot01`, … layout; the
166+
runner produces `<BASE>.d3plot*`. This step renames them in place.
167+
168+
### 4. Restructure global features for the datapipe
169+
170+
The recipes' datapipe expects a `global_features.json` keyed by run ID
171+
(see `examples/structural_mechanics/crash/README.md`, *Global features*
172+
section). Convert `dataset/summary.json` into that format:
173+
174+
```bash
175+
python restructure_global_features.py # writes ./global_features.json
176+
```
177+
178+
Bumper-beam keys: `geo_scale_{x,y,z}`, `velocity_{x,y,z}`, `thickness_scale`,
179+
`rwall_diameter`, `rwall_origin_{x,y,z}`.
180+
181+
Drop-test keys: `e_scale_mat{1,4,5,8,9}`,
182+
`rwall_orientation_{rx,ry,rz}`.
183+
184+
185+
## One-command end-to-end
186+
187+
After placing the `.rad` templates in `templates/`:
188+
189+
```bash
190+
cd bumper_beam # or: cd drop_test
191+
python generate_dataset.py && \
192+
python run_simulations.py && \
193+
python ../common/rename_d3plot.py --dataset-dir ./dataset \
194+
--base-name Bumper_Beam_AP_meshed && \
195+
python restructure_global_features.py
196+
```
197+
198+
199+
## Handing off to PhysicsNeMo-Curator
200+
201+
After step 3, the dataset layout matches what PhysicsNeMo-Curator expects:
202+
203+
```
204+
dataset/
205+
├── run1/
206+
│ ├── d3plot
207+
│ ├── d3plot01
208+
│ └── ...
209+
├── run2/
210+
│ └── ...
211+
└── ...
212+
```
213+
214+
Point the curator's `etl.source.input_dir` at this `dataset/` folder and
215+
follow the VTP/Zarr export instructions in
216+
[`examples/structural_mechanics/crash/README.md`](../crash/README.md#data-preprocessing).
217+
Once curated, the `global_features.json` produced in step 4 slots directly
218+
into the training experiment configs via `training.global_features_filepath`.
219+
220+
221+
## Troubleshooting
222+
223+
- **`starter.log` reports "BAD CARD" or zero E on a `/MAT` block.**
224+
OpenRadioss parses materials with fixed-width 20-char columns. If you add
225+
new materials with very large or very small E scales, inspect the rewritten
226+
`_0000.rad` to confirm the scaled value still fits the field.
227+
- **`anim_to_vtk` or `vortex_radioss` cannot find shared libraries.**
228+
The runner sets `LD_LIBRARY_PATH` from `OPENRADIOSS_ROOT/extlib/{hm_reader,h3d}/lib/linux64`.
229+
If your OpenRadioss layout differs, edit `common/radioss_runner.build_radioss_env`.
230+
- **`run_simulations.py` reports "No animation files (A001) found".**
231+
The Engine step did not finish a first animation checkpoint — check
232+
`engine.log`. Common cause: timestep collapse after an over-aggressive E
233+
reduction on the drop-test flow.
234+
- **`restructure_global_features.py` fails with `Duplicate run_id`.**
235+
Indicates `summary.json` was appended to twice. Regenerate from a clean
236+
`dataset/` folder.
237+
238+
239+
## Assumptions & OpenRadioss version notes
240+
241+
- Assumed Radioss deck format: block-style `.rad` files with whitespace- or
242+
comma-separated fixed-width fields, matching the Altair docs shipped with
243+
OpenRadioss 2024.x. No `/INCLUDE` or `/SUBMODEL` support.
244+
- Material E detection in the drop-test flow relies on a preceding comment
245+
line that mentions `E` and `nu`/`Nu`. Remove this comment in the starter
246+
and the scaling silently becomes a no-op — intended, since the deck would
247+
then have an unknown field layout.
248+
- The bumper flow scales `/PROP/SHELL` thickness at line 4, column 3. Decks
249+
that place thickness elsewhere (e.g. `/PROP/TYPE1`) need the mutation logic
250+
adapted in `bumper_beam/generate_dataset.py`.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2023 - 2026 NVIDIA CORPORATION & AFFILIATES.
2+
# SPDX-FileCopyrightText: All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.

0 commit comments

Comments
 (0)