2. Split a Monolithic Deck into Logical INC

Why split a working flat deck

A monolithic LS-DYNA deck that already runs is not automatically a good deck to maintain. When mesh, materials, parts, contacts, and boundary conditions all live in one file, every change forces a full-file diff, merge conflicts become routine, and reviewers cannot tell whether a modification touched geometry or physics. The refactoring task in this case is therefore not "make it run" — it already runs — but "make it maintainable without changing its behavior." The acceptance criterion is strict: after splitting, the assembled model must produce an identical keyword stream to the original, with the same definition order and the same cross-references resolving to the same IDs.

Two properties of LS-DYNA make this refactoring safe and predictable. First, *INCLUDE is a textual splice: the referenced file's contents are inserted at the point of the include, so ordering is preserved exactly as written. Second, most cross-references in LS-DYNA are by numeric ID (PID, MID, SID, SSID), not by position, so moving a block into another file does not break a reference as long as the ID is unchanged and the definition still appears before it is consumed. The refactoring is therefore a partitioning problem: choose file boundaries that respect definition-before-use, then verify the assembled stream.

Partitioning rule: definition before use

The single constraint that governs the whole split is that a definition must be read before any keyword that references it. In practice this yields a stable layer order for a typical structural deck:

  1. *CONTROL_* and *DATABASE_* cards — global settings, no dependencies.
  2. *MAT_* — materials, referenced by sections and parts.
  3. *SECTION_* — section properties, referenced by parts.
  4. *PART / *PART_INERTIA — parts, referencing SID and MID.
  5. *NODE / *ELEMENT_* / *SET_* — mesh and sets, referenced by parts, contacts, and boundary conditions.
  6. *CONTACT_* — contacts, referencing part and set IDs.
  7. *BOUNDARY_* and *LOAD_* — boundary conditions and loads, referencing node and set IDs.

Note that mesh can legitimately appear after parts in the file, because *PART references elements only through PID on the element cards, and LS-DYNA resolves that association after the full deck is read. What must not happen is a *CONTACT that names a *SET_PART defined later in the stream, or a *PART whose MID is defined after it. The safest discipline is to keep the original block order and only cut between blocks, never reorder them.

Worked refactoring of a small deck

Consider a flat deck with the following block sequence (abbreviated):

*KEYWORD
*CONTROL_TERMINATION
*CONTROL_TIMESTEP
*DATABASE_BINARY_D3PLOT
*MAT_PIECEWISE_LINEAR_PLASTICITY
         1    7.85e-9   210000.0       0.3
*MAT_RIGID
         2    7.85e-9   210000.0       0.3
*SECTION_SHELL
         1         2
*PART
plate                1         1         1
*PART
punch                2         2         2
*NODE
*ELEMENT_SHELL
*SET_PART_LIST
         1
         1         2
*CONTACT_AUTOMATIC_SURFACE_TO_SURFACE
         1         1         1         1
*BOUNDARY_SPC_SET
         1         0         1         1         1
*SET_NODE_LIST
         1
*END

The split below keeps every block intact and cuts only at safe boundaries. The top-level deck becomes a manifest of includes:

*KEYWORD
*INCLUDE
control.k
*INCLUDE
materials.k
*INCLUDE
sections.k
*INCLUDE
parts.k
*INCLUDE
mesh.k
*INCLUDE
contacts.k
*INCLUDE
boundary.k
*END

Each child file contains only its own blocks, with no *KEYWORD or *END — those belong to the top-level deck only. For example materials.k holds the two *MAT_* blocks verbatim, and parts.k holds the two *PART blocks verbatim. The *SET_PART_LIST and *SET_NODE_LIST blocks are the only judgement calls: they are referenced by *CONTACT and *BOUNDARY_SPC_SET respectively, so they must be read before those consumers. Placing the part set at the end of parts.k and the node set at the end of mesh.k satisfies that, because both files are included before contacts.k and boundary.k.

A quick sanity rule when choosing a cut point: if the block immediately after the cut references an ID defined in the block immediately before it, the cut is still legal as long as the earlier file is included earlier. The cut is illegal only when the reference points forward across the cut.

Verifying the assembled stream

Do not trust the split by inspection. Assemble the includes back into a single stream and diff it against the original. LS-DYNA itself can do this: run the deck with *CONTROL_TERMINATION set to a single cycle and inspect the echoed input in d3hsp, or use the preprocessor's include-expansion output. A textual equivalent is to concatenate the child files in include order and compare against the original with whitespace normalization:

# assemble in the same order as the top-level *INCLUDE list
cat control.k materials.k sections.k parts.k mesh.k contacts.k boundary.k \
  | sed 's/[[:space:]]\+/ /g' > assembled.norm
sed 's/[[:space:]]\+/ /g' original_flat.k | \
  grep -v '^\*INCLUDE' > original.norm
diff assembled.norm original.norm

An empty diff (ignoring the *INCLUDE lines themselves) confirms that the keyword stream is preserved. If the diff shows a block in the wrong position, the include order in the top-level deck is wrong, not the child file contents. If the diff shows a missing block, a child file was omitted from the manifest.

Cross-reference integrity check

Ordering is necessary but not sufficient; IDs must also still resolve. A lightweight check is to extract every defined ID per keyword type and every referenced ID, then confirm each reference has a definition. For the small deck above, the expected reference map is:

Consumer keywordFieldReferenced definitionDefined in
*PART plateSID*SECTION_SHELL 1sections.k
*PART plateMID*MAT_PIECEWISE_LINEAR_PLASTICITY 1materials.k
*PART punchMID*MAT_RIGID 2materials.k
*CONTACT_AUTOMATIC_SURFACE_TO_SURFACESSID/MSID*SET_PART_LIST 1parts.k
*BOUNDARY_SPC_SETNSID*SET_NODE_LIST 1mesh.k

If a reference is unresolved, LS-DYNA reports it during initialization with a message naming the keyword and the missing ID, and typically terminates before the first cycle. That failure mode is easy to distinguish from an ordering problem: an ordering problem usually produces a "part not found" or "set not found" error at the same initialization stage, but the fix is to move the include, not to add a definition.

Top-level manifest and definition-before-use order main.k *KEYWORD … *END *INCLUDE control.k *INCLUDE materials.k *INCLUDE sections.k *INCLUDE parts.k *INCLUDE mesh.k *INCLUDE contacts.k *INCLUDE boundary.k control.k — *CONTROL_*, *DATABASE_* materials.k — *MAT_* (MID 1, 2) sections.k — *SECTION_SHELL (SID 1) parts.k — *PART, *SET_PART_LIST mesh.k — *NODE, *ELEMENT_*, *SET_NODE_LIST contacts.k — *CONTACT_* boundary.k — *BOUNDARY_*, *LOAD_* MID → *PART SID → *PART SSID/MSID → *CONTACT NSID → *BOUNDARY_SPC_SET include splice (textual, order preserved) ID cross-reference (definition must precede consumer)

Failure symptoms and their fixes

  • "Part ID not found" at initialization. A *PART was moved into a file included after the block that references it, or the *PART block itself was split across two files. Fix: keep each keyword block whole and reorder the manifest.
  • "Set ID not found" for a contact. The *SET_PART_LIST sits in contacts.k after the *CONTACT block. Fix: move the set into parts.k or earlier.
  • Deck runs but results differ. Almost always a duplicated block: the same *MAT or *PART appears in two child files, and the later definition silently overrides the earlier one. Fix: grep each keyword type across all child files and confirm a single definition per ID.
  • Include not found. A relative path in *INCLUDE resolves against the working directory, not the manifest's directory. Fix: run from the manifest directory or use paths relative to it consistently.
Do not place *KEYWORD or *END inside child files. LS-DYNA treats the first *KEYWORD as the start of the deck and *END as termination; a stray *END in an included file truncates the model at that point, and the run may complete with a silently incomplete mesh.

Refactoring checklist

CheckPass condition
Block integrityEvery keyword block appears whole in exactly one child file
Manifest ordercontrol → materials → sections → parts → mesh → contacts → boundary
No stray terminatorsNo *KEYWORD or *END in child files
Stream equivalenceNormalized concatenation diffs empty against the original
Reference resolutionEvery MID, SID, SSID, MSID, NSID has a definition in an earlier-included file
Single definitionNo duplicate ID per keyword type across child files

Once these six checks pass, the split deck is behaviorally equivalent to the flat original and can be committed as the new baseline. Subsequent cases build on this structure: duplicate definitions across includes and path resolution become tractable precisely because the file boundaries now carry meaning.