4. Parametric model generation in LS-DYNA

A dataset for AI model training is only as good as the diversity of the simulations behind it. If every LS-DYNA run uses the same geometry, the same material properties, and the same loading, the resulting dataset teaches a surrogate model exactly one point in the design space — and it will fail the moment it is asked to interpolate. The practical answer is parametric model generation: build one master keyword deck in which the quantities that should vary are expressed as parameters, then drive those parameters from a script so that each run produces a distinct, traceable design point. This chapter covers the three mechanisms LS-DYNA provides for this — *PARAMETER, the *DEFINE family, and external scripting — and shows how to combine them into a repeatable variation generator.

Why parameterize at the deck level

There are two broad ways to create design variations: regenerate the mesh for every design point (via CAD or a meshing tool), or keep a fixed topology and vary dimensions, materials, and loads through keyword parameters. For AI training datasets the second approach is usually preferable, because it preserves node and element numbering across the dataset. That consistency matters enormously: a surrogate model that predicts nodal displacement or element stress needs a correspondence between outputs of different runs. If element 10432 is the same physical location in every sample, the dataset is directly learnable; if the mesh is remeshed per sample, you inherit a registration problem on top of the regression problem.

Deck-level parameterization also keeps the generation loop fast. A parameter sweep that only rewrites a handful of numeric fields and re-invokes the solver can run unattended for thousands of cases, which is exactly the scale a training set requires.

*PARAMETER: the foundation

*PARAMETER defines named scalars and simple expressions that can be referenced anywhere in the deck using the &name syntax. A minimal block looks like this:

*PARAMETER
R  plate_thk      2.0
R  plate_len      300.0
R  impact_vel    -8.0
R  rho_steel      7.85e-9
R  youngs_mod    210.0
R  dens_scaled    &rho_steel
I  nseg_len       20

The type flag matters: R declares a real, I an integer. Parameters can reference earlier parameters, so &dens_scaled resolves to the value of &rho_steel. This chaining is what makes a deck maintainable — change one base quantity and every derived quantity follows. Note the unit system: the values above are in a consistent mm–ms–kg–MPa set, where density is expressed in kg/mm³ (7.85e-9) and stress in MPa. Mixing unit systems inside a parameter block is one of the most common sources of silently wrong results, so declare the system explicitly in a comment at the top of the deck.

Once defined, parameters are substituted into ordinary keyword fields. For a shell section thickness:

*SECTION_SHELL
$#  secid    elform      shrf       nip     propt   qr/irid       icomp
         1         2       1.0         5       1.0         0         0
$#    t1        t2        t3        t4
&plate_thk &plate_thk &plate_thk &plate_thk

And for an initial velocity on a rigid wall or a prescribed node set:

*INITIAL_VELOCITY_GENERATION
$#     nsid       styp        omega        vx        vy        vz
         1          0        0.0       0.0       0.0  &impact_vel

Because &impact_vel is negative, the velocity points in the −Z direction; this sign convention should be stated once and kept consistent across the whole dataset, otherwise samples will contain contradictory loading directions that poison training.

*DEFINE: reusable curves and functions

Where *PARAMETER handles scalars, the *DEFINE family handles the objects that depend on parameters. The most useful for dataset generation are *DEFINE_CURVE (for load and material curves), *DEFINE_FUNCTION (for computed expressions), and *DEFINE_TABLE (for strain-rate-dependent or temperature-dependent behavior).

A typical case is a strain-rate-sensitive material where the yield stress curve must shift with the applied loading rate. You can define the curve once with parameterized abscissa and ordinate values:

*DEFINE_CURVE
$#    lcid      sidr       sfa       sfo      offa      offo    dattyp
         10         0       1.0       1.0       0.0       0.0         0
$#                a1                  o1
                 0.0                 0.0
              &eps_y            &sig_yield
             &eps_u              &sig_ult

Here &eps_y, &sig_yield, and the ultimate values are all parameters, so a single curve definition serves every material variant in the sweep. *DEFINE_FUNCTION goes further and lets you express a quantity as a formula over parameters, which is convenient for derived loads such as a pressure pulse whose peak scales with a design variable. The key discipline is to keep the structure of these definitions fixed and vary only the parameter values — that is what preserves mesh and keyword consistency across the dataset.

Scripting the variation loop

The parameters themselves are inert until something rewrites them per run. The standard pattern is a template deck containing placeholder tokens, plus a driver script that substitutes values and launches the solver. A compact Python driver illustrates the full loop:

import itertools, subprocess, pathlib

template = pathlib.Path("plate_master.k").read_text()

thickness = [1.5, 2.0, 2.5, 3.0]
velocity  = [-5.0, -8.0, -12.0]
yield_str = [280.0, 355.0, 460.0]

runs = []
for i, (t, v, sy) in enumerate(itertools.product(thickness, velocity, yield_str)):
    deck = (template
            .replace("__THK__",  f"{t:.3f}")
            .replace("__VEL__",  f"{v:.3f}")
            .replace("__SY__",   f"{sy:.1f}"))
    case = pathlib.Path(f"case_{i:04d}")
    case.mkdir(exist_ok=True)
    (case / "main.k").write_text(deck)
    subprocess.run(["ls-dyna", "i=main.k", "ncpu=4"],
                   cwd=case, check=True)
    runs.append((i, t, v, sy))

This produces 4 × 3 × 3 = 36 design points, each in its own directory with a fully resolved deck. The runs list is the provenance record: it maps case index to the exact parameter triple, which is what lets you later join simulation outputs back to inputs when assembling the training set. Without that record the dataset is unusable, because you cannot label a sample you cannot describe.

Use a fixed-width, zero-padded case index (case_0000, case_0001) and write the parameter triple to a sidecar CSV at generation time. Reconstructing provenance after the fact from directory timestamps is error-prone and unnecessary.

Choosing what to vary

Not every quantity should become a parameter. Vary the quantities that define the design space you want the surrogate to cover — geometric dimensions, material strengths, impact velocities, boundary-condition magnitudes — and hold fixed everything that is merely bookkeeping: contact definitions, hourglass controls, output intervals. A useful rule is that the number of varied parameters should stay small enough that a full-factorial or Latin-hypercube sweep remains tractable, while the ranges should be wide enough to bracket the intended operating envelope. If a parameter's effect on the response is negligible, varying it adds noise and cost without adding information.

The figure below shows how the three mechanisms compose into a single generation pipeline, from the master template through parameter resolution to the labeled dataset.

Parametric Variation Pipeline for LS-DYNA Dataset Generation Master Deck plate_master.k fixed topology placeholder tokens __THK__ __VEL__ __SY__ *PARAMETER plate_thk, impact_vel sig_yield, eps_y scalar + chained refs &name substitution *DEFINE_CURVE *DEFINE_FUNCTION *DEFINE_TABLE parameterized curves rate-dependent yield Driver Script itertools.product token replace case_0000 … case_0035 ls-dyna i=main.k LS-DYNA Solver 36 resolved decks → 36 runs d3plot, nodout, glstat Labeled Dataset inputs (t, v, σy) ↔ outputs (peak force, energy) Provenance Sidecar case_id, thickness, velocity, yield_strength, run_status join on case_id Legend template scalar params curves/functions driver solver

Verification and common failure modes

Before launching a large sweep, verify that parameter substitution actually reached the solver. The cheapest check is to run one case with a short termination time and inspect the resolved deck: open main.k and confirm that no & tokens or placeholder strings remain, and that the numeric fields carry the intended values. A second check is to read the glstat file and confirm the total mass matches the expected value for the chosen thickness and density — a mismatch usually means a parameter resolved to zero or to the wrong unit.

Three failure modes recur in practice. First, unresolved tokens: if a parameter is referenced but never defined, LS-DYNA may abort with a keyword error, or worse, substitute a default that silently changes the model. Second, unit inconsistency: a density entered in g/cm³ while stresses are in MPa produces a model that runs but gives nonsense results, and the error is invisible unless you check mass. Third, provenance drift: if the driver script is edited between runs without regenerating the sidecar, the case-to-parameter mapping becomes wrong, and the dataset is corrupted in a way no amount of training can fix. Treat the sidecar as part of the deliverable, not an afterthought.

Always run a single smoke-test case with a reduced termination time before committing to a full sweep. A parameter error discovered after 36 runs wastes hours; discovered after 3,600 runs it wastes days.

With the pipeline in place, the dataset becomes a controlled product: each sample is a resolved deck, a solver output set, and a provenance row that ties them together. That structure is what allows the next stages — output extraction, feature engineering, and train/validation splitting — to proceed without ambiguity about what each sample represents.