4. Compute von Mises stress from d3plot str
Why the von Mises value must be recomputed, not read
LS-DYNA writes the six stress components (σxx, σyy, σzz, σxy, σyz, σzx) per element per state into d3plot. The equivalent (von Mises) stress is a derived scalar, so any extraction pipeline that needs it must compute it from the tensor. The practical risk is not the formula — it is the component ordering, the sign convention for shear, and the element type (solid vs. shell), all of which can silently produce a plausible but wrong peak. This case builds a small, self-contained extractor that reads the tensor for a solid part, computes von Mises at every state, and then reconciles the computed peak against the value LS-PrePost reports for the same part and state.
Reference convention used throughout
All formulas below use the standard continuum-mechanics convention: tensile normal stresses positive, and shear components ordered as LS-DYNA stores them in the d3plot stress block for solids — sigxx, sigyy, sigzz, sigxy, sigyz, sigzx. The von Mises equivalent stress is
sigma_vm = sqrt( 0.5 * [ (sxx-syy)^2 + (syy-szz)^2 + (szz-sxx)^2 ]
+ 3 * (sxy^2 + syz^2 + szx^2) )
This is invariant to the sign of each shear component individually (they enter squared), but it is not invariant to swapping which component is which — so the ordering check is the real correctness gate. If your reader returns sigxy, sigyz, sigzx in a different order than the writer stored them, the peak will be wrong even though every individual number looks reasonable.
Reading the tensor and computing the field
The extractor below uses lasso-python (the maintained reader for LS-DYNA binary results). It selects a solid part by part ID, pulls the full tensor at every state, computes von Mises per element, and records the global peak together with the element ID and state where it occurs. The part ID and the state range are the only inputs you need to change for a different model.
import numpy as np
from lasso.dyna import D3plot, ArrayType
D3PLOT = "run01/d3plot"
PART_ID = 42 # solid part of interest
STATE_LO, STATE_HI = 0, None # None = all states
d3 = D3plot(D3PLOT)
# element ids belonging to the part
part_ids = d3.arrays[ArrayType.part_ids] # per part
elem_part = d3.arrays[ArrayType.element_solid_part_id] # per solid element
elem_ids = d3.arrays[ArrayType.element_solid_ids]
mask = (elem_part == PART_ID)
sel_ids = elem_ids[mask]
if sel_ids.size == 0:
raise SystemExit(f"part {PART_ID} has no solid elements")
# stress: shape (n_states, n_solid_elements, 6)
stress = d3.arrays[ArrayType.element_solid_stress][:, mask, :]
states = np.arange(stress.shape[0])
if STATE_HI is not None:
states = states[STATE_LO:STATE_HI]
stress = stress[STATE_LO:STATE_HI]
sxx, syy, szz, sxy, syz, szx = (stress[..., i] for i in range(6))
vm = np.sqrt(
0.5 * ((sxx - syy)**2 + (syy - szz)**2 + (szz - sxx)**2)
+ 3.0 * (sxy**2 + syz**2 + szx**2)
)
# global peak over selected states and elements
flat = vm.reshape(-1)
k = int(np.argmax(flat))
st_idx, el_idx = np.unravel_index(k, vm.shape)
peak_vm = float(vm[st_idx, el_idx])
peak_elem = int(sel_ids[el_idx])
peak_state = int(states[st_idx])
print(f"peak von Mises = {peak_vm:.3f}")
print(f"element {peak_elem}, state {peak_state}")
Two details matter for reproducibility. First, element_solid_stress is indexed by the solid element array, not by the global element array — the mask must be built from element_solid_part_id, not from a mixed element list. Second, the six components are returned in the order LS-DYNA wrote them; do not re-sort them by name. If you are unsure of the ordering for your LS-DYNA version, verify it once against a single element whose tensor you can read in LS-PrePost, then keep that ordering fixed.
Reconciling the peak against LS-PrePost
LS-PrePost reports the maximum von Mises value for a part as a fringe maximum, together with the element and state where it occurs. The reconciliation is a three-way check, not a single number comparison:
- Open the same
d3plotin LS-PrePost, select part 42, and switch the fringe to von Mises stress. - Step to the state returned by the script (
peak_state) and read the fringe maximum and the element ID at that maximum. - Compare
peak_vmto the fringe maximum, andpeak_elemto the reported element ID.
A correct pipeline matches on all three: value, element, and state. A value match with an element mismatch usually means two elements are within rounding of each other and the tie is broken differently — acceptable. A value mismatch beyond a few tenths of a percent is a real defect and should be traced to one of the failure modes below.
| Symptom | Likely cause | Fix |
|---|---|---|
| Peak is ~10–30% low, element ID plausible | Shear components read in wrong order (e.g. sxz where syz expected) | Confirm ordering against a single element in LS-PrePost; do not re-sort by name |
| Peak is wildly high or NaN | Shell elements included in a solid mask, or 5-component shell tensor read as 6 | Build mask from element_solid_part_id; handle shells separately |
| Peak matches but at wrong state | State indexing off-by-one (0-based vs 1-based) | Print states[peak_state] and compare to LS-PrePost state number |
| Peak matches at one state, drifts at others | Part mask includes elements from a neighboring part with same ID range | Verify sel_ids.size equals the solid element count for the part |
peak_vm is over the selected state range. If STATE_HI is set, restrict the LS-PrePost comparison to the same range or the numbers will not be comparable.Deliverable: peak reconciliation record
For each part you validate, record the following so the check is auditable and repeatable across models:
| Field | Example value | Source |
|---|---|---|
| d3plot path | run01/d3plot | input |
| Part ID | 42 | input |
| Solid element count in part | 1,284 | sel_ids.size |
| State range | 0–199 | input |
| Computed peak von Mises | 412.7 MPa | script output |
| Peak element / state | elem 10573 / state 87 | script output |
| LS-PrePost fringe max | 412.7 MPa | manual read |
| Relative difference | 0.0% | computed |
| Verdict | PASS | judgment |
The example values above are illustrative; substitute the numbers your own run produces. A PASS requires the relative difference to be within your model's rounding tolerance (typically ≤0.5% for single-precision d3plot output) and the peak element and state to agree. Once this record passes for one part, the same script and the same ordering assumption can be applied to every solid part in the model without re-validating the convention.