2. LS-DYNA input deck structure
An LS-DYNA crash model is not a single monolithic file. It is a tree of plain-text keyword files that the solver assembles at runtime. Understanding that tree is the prerequisite for any automation: a Python script that cannot reliably locate, parse, and rewrite a given card cannot safely modify a model. This section dissects the deck from the top-level main file down to individual part, material, and section definitions, and shows how to navigate the structure programmatically.
The main deck and the *INCLUDE mechanism
Every LS-DYNA run starts from one main input file, conventionally named main.k or <jobname>.k, passed to the solver as ls-dyna i=main.k. The main file rarely contains geometry. Instead it holds a sequence of *INCLUDE cards, each pointing to a subordinate file:
*INCLUDE
$# filename
../parts/body_in_white.k
*INCLUDE
../parts/bumper_system.k
*INCLUDE
../materials/materials.k
*INCLUDE
../control/control_cards.k
*INCLUDE
../boundary/constraints.k
Paths are resolved relative to the directory of the file containing the *INCLUDE card, not the working directory of the solver. This is the single most common source of "file not found" failures when a deck is moved or when a script rewrites paths. Nested includes are allowed to arbitrary depth, and LS-DYNA flattens the entire tree into one logical keyword stream before processing. Two consequences matter for automation:
- Order is global. A
*PARTmay reference a*MATand a*SECTIONdefined in a different include file, because resolution happens after flattening. But duplicate keyword IDs across files are a hard error. - Include depth is a real cost. Deeply nested trees slow down parsing and make diffing painful. A practical convention is to keep the tree two levels deep: one main file plus one level of category folders.
A useful automation primitive is a recursive include resolver. Given a main file, walk every *INCLUDE card, resolve the path against the parent file's directory, and return an ordered list of absolute paths. This list is the ground truth for any downstream operation — searching, patching, or reporting.
Keyword card syntax
LS-DYNA keyword files are fixed-format, column-sensitive text. Each keyword begins with * in column 1. Immediately after the keyword line come one or more data lines, optionally preceded by a comment line beginning with $# that documents the field names. Fields are ten characters wide by default, though the *KEYWORD card can switch to comma-delimited free format. A representative part definition looks like this:
*PART
$# title
front_rail_left
$# pid secid mid eosid hgid grav adpopt tmid
101 5 3 0 0 0 0 0
The pid is the part ID, secid points to a *SECTION, and mid points to a *MAT. These three IDs form the core reference triangle of any crash model. A part with a dangling secid or mid will terminate the run during initialization with an error naming the offending ID.
Control cards: the run's global switches
Control cards govern termination time, time-step scaling, output frequency, and energy behavior. They are typically grouped in a single include file so that a script can swap an entire run configuration by replacing one path. The cards that most affect crash workflows are:
| Card | Key fields | Effect on a crash run |
|---|---|---|
*CONTROL_TERMINATION | ENDTIM | Physical end time in milliseconds; 100–150 ms is typical for a full frontal barrier event. |
*CONTROL_TIMESTEP | DTINIT, TSSFAC | Initial time step and mass-scaling factor; TSSFAC below 0.9 is rarely justified. |
*CONTROL_ENERGY | HGEN, RWEN, SLNTEN | Hourglass and rigid-wall energy accounting; essential for validating a result. |
*DATABASE_BINARY_D3PLOT | DT, LCDT | State-dump interval; drives post-processing file size. |
*DATABASE_ASCII_GLSTAT | DT | Global energy history; the first file to inspect after a run. |
Because these cards are scalar and self-contained, they are the safest target for automated parameter sweeps. A script can load the control include, replace the ENDTIM field, and write it back without touching geometry.
Parts, materials, and sections
The *PART card is the assembly point. Materials define constitutive behavior; sections define geometry and integration. For crash models the dominant combinations are:
- Shell sections (
*SECTION_SHELL) withELFORM=2(Belytschko-Tsay) for body panels, andELFORM=16for fully integrated shells where hourglass control is a concern. - Solid sections (
*SECTION_SOLID) withELFORM=1for castings andELFORM=-1or-2for tetrahedral meshes. - Spotweld and beam sections (
*SECTION_BEAM) for joining stamped assemblies.
On the material side, *MAT_PIECEWISE_LINEAR_PLASTICITY (MAT_024) is the workhorse for mild and high-strength steels, driven by a density, Young's modulus, Poisson ratio, yield stress, and a hardening curve. *MAT_RIGID (MAT_020) is used for barriers and fixtures. *MAT_ADD_EROSION is frequently attached to MAT_024 to delete elements past a strain threshold.
The reference triangle means a single material change propagates to every part sharing that mid. This is powerful for design studies but dangerous for one-off edits. Before modifying a material, a script should enumerate all parts referencing it:
def parts_using_material(parts, mid):
return [p for p in parts if p.mid == mid]
If the list is longer than expected, the edit is probably not what the engineer intended.
How the pieces connect
The diagram below shows the include tree on the left and the reference graph on the right. The two are deliberately separated: file organization and ID references are independent concerns, and confusing them is a frequent source of automation bugs.
Practical validation before editing
Before a script modifies anything, it should run a structural validation pass. The minimum viable check is: resolve all includes, collect every defined ID per keyword type, then verify that each *PART resolves its secid and mid, and that each *MAT_ADD_EROSION resolves its mid. A dangling reference produces a solver error like *** Error 10244 (KEY+044) naming the missing ID, but catching it in Python is far cheaper than a failed solver run.
With the structure understood, the next step is to build a parser that turns this tree into an in-memory model — a dictionary of parts, materials, and sections keyed by ID — so that subsequent chapters can perform targeted edits, parameter sweeps, and automated report generation without re-reading the raw text each time.