1. Inspect d3plot file structure and state inventory

Why a State Inventory Comes First

A d3plot database is LS-DYNA's binary state database. Every output interval writes one state containing the full model geometry (nodal coordinates, element connectivity, part/element material IDs) plus the requested result variables (displacement, velocity, acceleration, stress, strain, and so on). Before any extraction task — a nodal time history, an element stress tensor, a section force — you need to know exactly what the file contains: how many states exist, what simulation time each state corresponds to, and which result variables were actually written. Guessing at these facts is the most common cause of misaligned time axes, empty arrays, and silently wrong component ordering downstream.

This case establishes that baseline inventory. You will open a d3plot database, enumerate every state with its time value, and list the available result variables per state. The output is a small, reusable inventory that later extraction tasks can consume directly.

What a d3plot File Actually Contains

A d3plot file is a sequence of records. The control record at the start of the file declares the number of states, the number of nodes, the number of solid/beam/shell elements, and the word size (single or double precision). Each state then begins with a state header carrying the simulation time, followed by geometry blocks and result blocks. LS-DYNA may also split large databases into family files (d3plot01, d3plot02, …) when the model exceeds the per-file size limit; the state count in the control record refers to the whole family, not just the first file.

Two practical consequences follow. First, the number of states is a property of the database, not of any single file. Second, the result variables present in a state depend on what was requested in the *DATABASE_BINARY_D3PLOT and *DATABASE_EXTENT_BINARY keywords — a database written with only displacement will not contain stress, no matter how you query it.

d3plot database — structural inventory Control record nstates = 41 nnodes = 128,450 nsolid = 96,200 word size = double State sequence (one block per output interval) S0 t=0.0 S1 t=0.5 S2 t=1.0 … S40 t=20.0 Time values are stored per state; spacing need not be uniform. Family files d3plot01… continue the same state index. Contents of each state Geometry coords, connectivity Nodal results disp, vel, accel Element results stress, strain, energy Presence of a block is decided at write time by *DATABASE_EXTENT_BINARY — absent blocks cannot be recovered later. Inventory output state time vars 0 0.0 disp,vel 1 0.5 disp,vel … 40 20.0 disp,vel Baseline for later extraction Verification checks state count matches control record · time values strictly increasing · variable list identical across states · family files fully enumerated

Opening the Database and Enumerating States

Two paths are practical. In LS-PrePost, load the database via File → Open → LS-DYNA → d3plot; the state slider at the bottom of the viewport shows the total state count, and the State panel lists each state with its time. The Fringe component selector then reveals which result variables are available. This is the fastest sanity check and is worth doing before writing any script, because it confirms the file is readable and the state count is what you expect.

For a scriptable inventory, use the lasso-python reader (the maintained successor to the older lasso package). The following script opens a database, walks every state, and writes a CSV inventory. It is self-contained and runs against any valid d3plot.

import csv
from pathlib import Path
from lasso.dyna import D3plot, ArrayType

D3PLOT = Path("run/d3plot")          # first file of the family
OUT_CSV = Path("inventory.csv")

ds = D3plot(str(D3PLOT))

# --- database-level facts from the control record ---
n_states = ds.get_global("state_count") if hasattr(ds, "get_global") else None
if n_states is None:
    n_states = len(ds.arrays[ArrayType.global_simulation_time])

times = ds.arrays[ArrayType.global_simulation_time]

# --- which result arrays are actually populated ---
candidate_vars = {
    "disp":  ArrayType.node_displacement,
    "vel":   ArrayType.node_velocity,
    "accel": ArrayType.node_acceleration,
    "stress": ArrayType.element_solid_stress,
    "strain": ArrayType.element_solid_strain,
    "energy": ArrayType.element_solid_energy,
}

available = {}
for name, key in candidate_vars.items():
    try:
        arr = ds.arrays[key]
        available[name] = arr.shape
    except KeyError:
        pass

print(f"states: {n_states}")
print(f"time range: {times[0]:.6g} .. {times[-1]:.6g}")
for name, shape in available.items():
    print(f"  {name:8s} shape={shape}")

# --- per-state inventory ---
with OUT_CSV.open("w", newline="") as fh:
    w = csv.writer(fh)
    w.writerow(["state", "time", "variables"])
    for i, t in enumerate(times):
        w.writerow([i, f"{t:.9g}", ";".join(sorted(available))])

print(f"wrote {OUT_CSV}")

Two details in the script matter. First, ArrayType.global_simulation_time is the authoritative per-state time vector; do not derive time from a nominal output interval, because LS-DYNA may write extra states at termination or on adaptive remeshing. Second, the try/except KeyError pattern is the correct way to test for a result block: the reader raises KeyError when a block was never written, which is exactly the "absent block" condition described above. Do not assume a block exists because the keyword was present in the input deck — check the file.

If the database is split into family files, pass the first file (d3plot) to the reader. The reader follows the family automatically; the state count in the control record already covers all files. Passing d3plot01 directly will give you a partial inventory.

Reading the Inventory and Judging It

A correct inventory for a typical explicit run looks like this (values are illustrative for a 41-state, 20 ms simulation):

CheckExpected resultFailure symptom
State countMatches control record nstatesReader returns fewer states than the slider shows in LS-PrePost — family file missing or truncated
Time vectorStrictly increasing, starts at 0.0Duplicate or non-monotonic times — restart appended states; de-duplicate before extraction
Variable listIdentical across all statesA variable present in early states but absent later — output request changed mid-run; extraction must handle ragged arrays
Array shapesdisp is (nstates, nnodes, 3); stress is (nstates, nsolid, 6)Shape mismatch with control record — wrong word size or corrupted file

The shape check is the most valuable single test. For a database with 41 states, 128,450 nodes, and 96,200 solids, node_displacement.shape must be (41, 128450, 3) and element_solid_stress.shape must be (41, 96200, 6). The trailing dimension of 6 for solid stress reflects the six tensor components in LS-DYNA's stored order (σxx, σyy, σzz, σxy, σyz, σzx); confirming this now prevents component-ordering errors in later tensor extraction. If the shapes disagree with the control record, stop and re-check the file before proceeding — every downstream extraction will inherit the error.

Deliverable: Baseline Inventory Record

Keep the generated inventory.csv alongside the database and record the following header facts in your run notes. This is the artifact later extraction tasks reference.

FieldExample valueSource
Database pathrun/d3plotInput
State count41Control record / time vector length
Time range0.0 – 20.0 msglobal_simulation_time[0], [-1]
Nodes / solids128,450 / 96,200Control record
Available variablesdisp, velPopulated ArrayType keys
Word sizedoubleControl record
Family filesnoneDirectory listing
Do not infer available variables from the input deck alone. A deck that requests stress but writes with *DATABASE_EXTENT_BINARY set to suppress element results will produce a database with no stress block. The inventory must come from the file, and the KeyError test in the script is the reliable way to detect absence.

With the inventory in hand — state count, time vector, variable list, and array shapes all confirmed against the control record — you have a trustworthy baseline. Any later extraction that disagrees with this baseline (a time axis that does not match, a variable that suddenly appears) is a signal to re-inspect the database rather than to patch the extraction code.