2. Crash simulation data landscape
Feature stores for crash simulation do not ingest a single homogeneous dataset. They ingest a federation of solver-specific artifacts produced by LS-DYNA, PAM-CRASH, Radioss, and Abaqus, each with its own deck syntax, result container format, and naming conventions. Before any feature engineering can begin, an engineering team must map this landscape: which solver produced the run, which model variant was exercised, which load case was applied, and which result types are available for extraction. This chapter builds that map and shows how to normalize it into a queryable schema.
Solver Output Families and Their Containers
The four solvers share a common conceptual pipeline — input deck, explicit or implicit time integration, d3plot-style state output, and a binary or ASCII results database — but the concrete file formats diverge sharply.
- LS-DYNA writes
d3plot(binary state data),binout(nodal/element time histories),nodout,elout,glstat,matsum, andrcforc. The keyword deck (*.k) is the canonical model definition. - PAM-CRASH (ESI) uses
DSYfor time-history data,THSfor nodal/element curves, andPAMCRASH.INas the input deck. Result containers are typically.dsyand.ths. - Radioss (Altair) produces
Annn(animation/state) andTnn(time-history) files, with the input deck in_0000.rador_0001.radformat. - Abaqus writes
.odb(output database),.dat,.fil, and.sta. The input deck is.inp.
For a feature store, the critical insight is that state data (full-field snapshots at discrete time steps) and history data (scalar or vector time series at specific nodes/elements) require different storage strategies. State data is large and typically stored as chunked arrays keyed by (run_id, time_step, part_id). History data is small and stored as time-indexed tables keyed by (run_id, channel_id).
Model Variants and Their Feature Implications
A single vehicle program may generate dozens of model variants: baseline, trimmed mass, reinforced B-pillar, alternative material grades, mesh-refined crash box, and so on. Each variant changes the feature vector in ways that must be tracked explicitly.
| Variant Dimension | Typical Change | Feature Impact |
|---|---|---|
| Mass trim | Added/removed ballast, interior components | Kinetic energy, deceleration pulse, intrusion |
| Material grade | DP590 → DP780, or aluminum substitution | Plastic strain, thinning, failure flags |
| Gauge thickness | 1.2 mm → 1.5 mm on rails | Section force, energy absorption, mass |
| Mesh refinement | 5 mm → 3 mm in crash box | Hourglass energy, contact stability, runtime |
| Joint definition | Spot weld → structural adhesive | Force-displacement, separation flags |
In the feature store, variant metadata should be stored as a structured record — not as free-text comments in the deck. A practical schema uses a variant table with columns variant_id, parent_model_id, change_type, changed_entity, old_value, new_value, and solver_deck_hash. The deck hash ensures reproducibility: if the input deck changes without a variant record, the feature store flags the run as orphaned.
Load Cases and Result Types
Crash load cases are defined by regulatory and internal protocols. Common ones include full frontal rigid barrier (FRB) at 56 km/h, offset deformable barrier (ODB) at 64 km/h, side pole at 32 km/h, and rear moving deformable barrier (RMDB) at 80 km/h. Each load case maps to a specific set of result types that feature engineering consumes.
Result types fall into four families:
- Kinematic: nodal displacement, velocity, acceleration. Extracted from
nodout(LS-DYNA),THS(PAM-CRASH),Tnn(Radioss), or.odbhistory output (Abaqus). - Structural: element stress, strain, plastic strain, section forces. Extracted from
elout,binout, or equivalent. - Energy: internal energy, kinetic energy, hourglass energy, sliding interface energy. Extracted from
glstatandmatsum. - Contact/Constraint: interface force, penetration, spot weld rupture, adhesive failure. Extracted from
rcforcand solver-specific contact output.
Normalization rule: Every result channel must be stored with its solver-native name, a canonical feature name, and a unit string. For example, LS-DYNA nodout acceleration in mm/ms² and Abaqus .odb acceleration in m/s² both map to canonical feature acceleration_resultant with unit m/s² after conversion.
From Raw Output to Feature-Ready Records
The transformation from solver output to feature-ready records follows a consistent pattern regardless of solver. The following SVG illustrates the data flow from four solver families through a normalization layer into a unified feature store schema.
The normalization layer performs five operations. First, unit conversion: LS-DYNA decks commonly use mm, ms, kg, and kN, while Abaqus often uses m, s, kg, and N. Second, channel mapping: solver-native names such as NODOUT column headers or THS channel labels are mapped to canonical feature names. Third, time-step alignment: solvers write state data at different intervals; the store resamples to a common time base (e.g., 0.1 ms) using linear interpolation for history channels and nearest-neighbor for state snapshots. Fourth, variant tagging: each run is linked to a variant record. Fifth, deck hash validation: the SHA-256 hash of the input deck is compared against the variant record; a mismatch flags the run for review.
Practical Extraction Example
Consider extracting the acceleration time history at the driver seat rail from an LS-DYNA frontal impact run. The nodout file contains a block per node with columns for time, x-acceleration, y-acceleration, and z-acceleration. A Python extraction script using lasso-python or qd-ldyna reads the node set, converts mm/ms² to m/s² by multiplying by 1000, and writes a record with run_id, channel_id = "driver_seat_rail_accel", time, and acceleration_resultant.
from lasso.dyna import D3plot, ArrayType
import numpy as np
d3plot = D3plot("run_001/d3plot")
node_ids = d3plot.arrays[ArrayType.node_ids]
# Find node closest to driver seat rail coordinate
target = np.array([1200.0, 0.0, 450.0]) # mm
coords = d3plot.arrays[ArrayType.node_coordinates]
dist = np.linalg.norm(coords - target, axis=1)
node_idx = np.argmin(dist)
# Extract acceleration history (mm/ms^2 -> m/s^2)
accel = d3plot.arrays[ArrayType.node_acceleration][:, node_idx, :]
accel_si = accel * 1000.0 # mm/ms^2 to m/s^2
time = d3plot.arrays[ArrayType.global_timesteps]
# Write to feature store
for t, ax, ay, az in zip(time, accel_si[:, 0], accel_si[:, 1], accel_si[:, 2]):
resultant = np.sqrt(ax**2 + ay**2 + az**2)
store.write_history(run_id="run_001", channel_id="driver_seat_rail_accel",
time=t, value=resultant, unit="m/s^2")
Typical failure symptoms during extraction include: missing node IDs because the node set was defined in the deck but not activated in DATABASE_NODOUT; unit mismatch producing acceleration values off by 10³; and time-step misalignment when comparing LS-DYNA and Abaqus runs in the same feature space. Each symptom maps to a specific validation check in the normalization layer.
Validation checklist for new solver integrations: (1) Confirm the result container is readable by the chosen parser. (2) Verify unit strings against the deck's CONTROL_UNITS or equivalent. (3) Check that all required channels are present in the output request block. (4) Compare a known scalar (e.g., total mass) against the deck definition. (5) Run a single load case end-to-end and inspect the feature store record for completeness.
The data landscape is wide, but the feature store's job is narrow: capture run identity, variant identity, load case identity, and normalized result channels. Once that schema is stable, downstream feature engineering — peak deceleration, intrusion velocity, energy absorption ratio, plastic strain hotspots — becomes a query rather than a file-parsing exercise.