3. Python environment setup

Before any binout file can be parsed or any time-history curve can be plotted, you need a Python environment that can actually read LS-DYNA binary output. This chapter walks through installing and configuring the core stack — lasso-python, numpy, pandas, and matplotlib — and verifying that they interoperate correctly on real LS-DYNA data. The goal is a reproducible environment you can rebuild on any workstation or CI runner.

Why lasso-python Is the Anchor Library

LS-DYNA writes binout files in a proprietary binary format. The lasso-python package (developed by the DYNAmore team) provides the lasso.dyna.Binout class, which reads these files directly and exposes the contained data as NumPy arrays. It also handles d3plot and d3plot-family files, but for time-history workflows the binout reader is the primary entry point. Because lasso-python returns NumPy arrays, the rest of the stack — pandas for tabular manipulation, matplotlib for plotting — integrates naturally.

A critical detail: lasso-python is distributed on PyPI as lasso-python, but the import name is lasso. Mixing these up is a common source of ModuleNotFoundError.

Creating an Isolated Environment

Never install this stack into a system Python. LS-DYNA workflows often coexist with other engineering tools that pin conflicting NumPy versions. Use a dedicated virtual environment:

python -m venv .venv-lsdyna
# Windows
.venv-lsdyna\Scripts\activate
# Linux / macOS
source .venv-lsdyna/bin/activate

python -m pip install --upgrade pip
pip install lasso-python numpy pandas matplotlib

For reproducibility, pin versions in a requirements.txt after confirming the stack works:

lasso-python==2024.1.1
numpy==1.26.4
pandas==2.2.2
matplotlib==3.8.4

Version pinning matters because lasso-python releases track LS-DYNA versions. A reader built against LS-DYNA R13 binout files may fail on R11 files if the reader version is mismatched. Check the release notes for the LS-DYNA version you target.

Verifying the Installation

Run a minimal import and version check before touching real data:

import lasso, numpy, pandas, matplotlib
print("lasso", lasso.__version__)
print("numpy", numpy.__version__)
print("pandas", pandas.__version__)
print("matplotlib", matplotlib.__version__)

from lasso.dyna import Binout
print("Binout class loaded:", Binout)

If lasso imports but Binout fails, you likely installed the wrong package (there is an unrelated lasso on PyPI). Uninstall and reinstall lasso-python.

Reading a Binout File: First Contact

With the environment ready, load a binout and inspect its structure. The Binout object behaves like a nested dictionary keyed by branch (e.g., glstat, matsum, nodout, rcforc), then by variable:

from lasso.dyna import Binout
import numpy as np

b = Binout("run01/binout")
print(b.read())          # list all branches
print(b.read("glstat"))  # list variables in glstat

time = b.read("glstat", "time")
ke   = b.read("glstat", "kinetic_energy")
ie   = b.read("glstat", "internal_energy")
print(time.shape, ke.shape)   # e.g. (1201,) (1201,)

Each variable is a 1-D NumPy array aligned to the same time vector. This alignment is what makes pandas conversion trivial and safe.

From Binout to DataFrame

For multi-channel analysis — say, comparing kinetic energy across several matsum parts — assemble a DataFrame with time as the index:

import pandas as pd

b = Binout("run01/binout")
t = b.read("matsum", "time")
df = pd.DataFrame({"time": t})
for pid in b.read("matsum", "part_id"):
    df[f"ie_part_{pid}"] = b.read("matsum", "internal_energy", pid)

df = df.set_index("time")
print(df.head())
print(df.describe())

Note the third positional argument to read: it selects a specific entity ID when the branch stores per-part data. Omitting it returns the full 2-D array.

Plotting with matplotlib

Matplotlib works directly on the DataFrame index and columns:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(8, 4.5))
for col in df.columns:
    ax.plot(df.index, df[col], label=col)
ax.set_xlabel("Time [ms]")
ax.set_ylabel("Internal Energy [mJ]")
ax.set_title("Part Internal Energy — run01")
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
fig.tight_layout()
fig.savefig("ie_by_part.png", dpi=150)

Always set explicit axis labels with units. LS-DYNA's default unit system is user-defined; a plot without units is ambiguous and dangerous in a report.

Headless environments: On CI or remote servers, set matplotlib.use("Agg") before importing pyplot to avoid display errors.

Environment Topology

The diagram below shows how the libraries relate: lasso-python reads the binary binout and emits NumPy arrays; pandas wraps those arrays into labeled tables; matplotlib renders them. NumPy is the shared substrate.

LS-DYNA Python Data Stack — Dependency and Data Flow binout binary file lasso-python Binout reader → NumPy arrays numpy ndarray substrate pandas DataFrame / index matplotlib figures / PNG report plots / CSV NumPy arrays flow upward into pandas and matplotlib; lasso-python is the only component that touches the binary format.

Common Installation Failures and Fixes

SymptomLikely CauseFix
ModuleNotFoundError: lassoWrong package installedpip uninstall lasso then pip install lasso-python
ImportError: numpy.core.multiarrayNumPy ABI mismatchReinstall both: pip install --force-reinstall numpy lasso-python
Binout read returns emptyFile path wrong or file truncatedCheck os.path.getsize(); confirm LS-DYNA run completed
Plot window does not openHeadless / no displaymatplotlib.use("Agg") before pyplot import

Configuration for Reproducible Workflows

Two configuration habits pay off immediately. First, centralize paths and unit conventions in a small config module so every script reads the same assumptions:

# config.py
BINOUT_DIR = "run01"
TIME_UNIT = "ms"
ENERGY_UNIT = "mJ"
FORCE_UNIT = "kN"

Second, wrap the read-and-convert pattern in a helper so downstream scripts stay short and consistent:

def load_branch(path, branch, variables, entity=None):
    b = Binout(path)
    data = {"time": b.read(branch, "time")}
    for v in variables:
        data[v] = b.read(branch, v, entity) if entity else b.read(branch, v)
    return pd.DataFrame(data).set_index("time")

With this helper, a full analysis becomes a few lines, and the unit conventions from config.py can be applied uniformly when labeling plots.

Do not mix LS-DYNA unit systems across scripts. If your model uses mm-ton-s-N, energy is in mJ and force in N. A plot labeled "kN" from a model in that system will be off by 1000×. Record the unit system in config.py and reference it in every axis label.

Checklist Before Moving On

  • Virtual environment created and activated; pip list shows lasso-python, numpy, pandas, matplotlib.
  • from lasso.dyna import Binout succeeds.
  • A real binout file opens and b.read() lists expected branches.
  • Time vector and at least one variable load with matching shapes.
  • A DataFrame is built with time as index and saved to PNG without errors.
  • Unit conventions are recorded in a config module.

Once these checks pass, the environment is ready for the parsing and analysis patterns in the following chapters. The key discipline is isolation: one environment per LS-DYNA version, pinned dependencies, and a single source of truth for units and paths.