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 *PART may reference a *MAT and a *SECTION defined 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.

When writing a parser, do not assume fixed-width columns. Real decks in the wild mix fixed-width and comma-delimited cards. A robust approach is to split on commas when a comma is present in the data line, and fall back to ten-character slicing otherwise.

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:

CardKey fieldsEffect on a crash run
*CONTROL_TERMINATIONENDTIMPhysical end time in milliseconds; 100–150 ms is typical for a full frontal barrier event.
*CONTROL_TIMESTEPDTINIT, TSSFACInitial time step and mass-scaling factor; TSSFAC below 0.9 is rarely justified.
*CONTROL_ENERGYHGEN, RWEN, SLNTENHourglass and rigid-wall energy accounting; essential for validating a result.
*DATABASE_BINARY_D3PLOTDT, LCDTState-dump interval; drives post-processing file size.
*DATABASE_ASCII_GLSTATDTGlobal 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) with ELFORM=2 (Belytschko-Tsay) for body panels, and ELFORM=16 for fully integrated shells where hourglass control is a concern.
  • Solid sections (*SECTION_SOLID) with ELFORM=1 for castings and ELFORM=-1 or -2 for 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.

Include tree (files) Reference graph (IDs) main.k parts materials sections control body.k mat.k sec.k ctrl.k *PART pid=101 *SECTION_SHELL secid=5 *MAT_024 mid=3 *MAT_ADD_EROSION mid=3 secid mid mid Automation checklist 1. Resolve all *INCLUDE paths relative to parent file. 2. Flatten to an ordered list of absolute paths. 3. Index every *PART, *MAT, *SECTION by ID. 4. Validate reference triangle: pid → secid, mid. 5. Report dangling IDs before any write operation.

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.

Never write a modified deck in place. Write to a new file or a versioned directory. A partially written deck that fails mid-edit is indistinguishable from a corrupted model, and the original is gone.

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.