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.
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.
Common Installation Failures and Fixes
| Symptom | Likely Cause | Fix |
|---|---|---|
ModuleNotFoundError: lasso | Wrong package installed | pip uninstall lasso then pip install lasso-python |
ImportError: numpy.core.multiarray | NumPy ABI mismatch | Reinstall both: pip install --force-reinstall numpy lasso-python |
| Binout read returns empty | File path wrong or file truncated | Check os.path.getsize(); confirm LS-DYNA run completed |
| Plot window does not open | Headless / no display | matplotlib.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.
config.py and reference it in every axis label.
Checklist Before Moving On
- Virtual environment created and activated;
pip listshowslasso-python,numpy,pandas,matplotlib. from lasso.dyna import Binoutsucceeds.- 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.