2. Python environment setup for MADYMO automation

MADYMO does not ship with a Python interpreter, and it does not manage Python packages for you. That means the first real engineering decision in any automation project is not "which script do I write" but "which interpreter runs the script, and how does that interpreter talk to the MADYMO solver and its libraries." Get this wrong and you will spend days chasing ImportError, DLL load failed, or silent version mismatches instead of building models. This section covers interpreter selection, package management, and the concrete integration points between Python and MADYMO binaries.

Why Interpreter Choice Is an Engineering Decision

MADYMO's Python-facing tooling has historically been tied to specific CPython versions. The madymo Python module, the madymo_py bindings, and the madpost post-processing library are compiled extensions, not pure Python. A compiled extension is built against one CPython ABI (for example, CPython 3.9 on Windows x64). If you run it under a different minor version, the import fails at the binary level, not at the syntax level.

Three practical constraints follow:

  • Match the ABI. If your MADYMO installation provides bindings for CPython 3.9, use CPython 3.9. Do not assume 3.10 or 3.11 will work just because the syntax is compatible.
  • Match the architecture. A 64-bit MADYMO installation requires a 64-bit interpreter. Mixing 32-bit Python with 64-bit solver DLLs produces OSError: [WinError 193] %1 is not a valid Win32 application.
  • Isolate per project. Different MADYMO versions may require different Python versions. A single global interpreter cannot serve both.

For most current MADYMO releases on Windows, CPython 3.9 x64 is the safe baseline. On Linux, the same rule applies: use the CPython minor version that the vendor bindings were compiled against, typically confirmed in the MADYMO installation's python/ or lib/ directory.

Creating an Isolated Environment

Use venv rather than a system-wide install. The following creates a project-local environment pinned to the correct interpreter:

# Windows (PowerShell), CPython 3.9 x64 installed at a known path
py -3.9 -m venv .venv-madymo
.\.venv-madymo\Scripts\Activate.ps1

# Linux
python3.9 -m venv .venv-madymo
source .venv-madymo/bin/activate

# Confirm the interpreter identity before installing anything
python -c "import sys, platform; print(sys.version); print(platform.architecture())"

The verification step matters. If the printed version is not the one your MADYMO bindings expect, stop and fix the interpreter before proceeding. Installing packages into the wrong environment is a common and time-consuming mistake.

Package Management: What You Actually Need

MADYMO automation rarely needs a large dependency tree. The core set is small and stable:

PackagePurpose in MADYMO workflowsNotes
numpyArray handling for time-history and injury-metric dataPin a version compatible with your bindings
pandasTabulating multi-run results, parameter sweepsOptional but common
matplotlibPlotting signals from madpost outputUse a non-interactive backend in batch runs
lxml / xml.etreeEditing MADYMO XML input decksStandard library is often sufficient
madymo / madpostVendor bindings to solver and post-processorInstalled from the MADYMO distribution, not PyPI

Pin versions in a requirements.txt so that a working environment can be reproduced on another machine or in CI:

numpy==1.23.5
pandas==1.5.3
matplotlib==3.7.1
lxml==4.9.2

Install with pip install -r requirements.txt. Avoid pip install --upgrade on the whole environment after it works; an accidental NumPy major-version bump can break binary compatibility with the MADYMO bindings.

Do not install the MADYMO Python bindings from PyPI. They are distributed with the MADYMO installation. Installing a same-named package from the public index will shadow the vendor module and produce confusing import errors.

Integrating with MADYMO Binaries

Integration happens at three levels: the solver executable, the shared libraries, and the Python bindings. Each has its own configuration point.

1. Solver executable on PATH. The automation script typically launches the solver as a subprocess. The executable must be discoverable, or referenced by absolute path. On Windows, the MADYMO bin directory must be on PATH; on Linux, set LD_LIBRARY_PATH to include the MADYMO lib directory.

# Windows
set MADYMO_HOME=C:\MADYMO\2023.1
set PATH=%MADYMO_HOME%\bin;%PATH%

# Linux
export MADYMO_HOME=/opt/madymo/2023.1
export LD_LIBRARY_PATH=$MADYMO_HOME/lib:$LD_LIBRARY_PATH

2. Shared libraries. The Python bindings load MADYMO shared libraries at import time. If LD_LIBRARY_PATH (Linux) or PATH (Windows) does not include the MADYMO lib directory, the import raises ImportError: libmadymo.so: cannot open shared object file or the Windows equivalent. Setting the environment variable before starting Python is the reliable fix; setting it from inside Python after the interpreter has started is not, because the loader has already resolved its search paths.

3. Python bindings. Add the MADYMO Python directory to PYTHONPATH, or install the provided wheel into the active virtual environment. A quick smoke test confirms all three levels are wired correctly:

python -c "import madymo; print(madymo.__file__)"
python -c "from madymo import Madymo; print('bindings OK')"

If the first command prints a path inside your MADYMO installation, the binding is found. If the second command succeeds, the shared libraries resolved. If either fails, the error message tells you which level is broken: a path error points to PYTHONPATH, a library-load error points to LD_LIBRARY_PATH or PATH.

Verifying the Full Stack

A minimal end-to-end check runs the solver on a trivial deck and confirms the output file appears. This validates interpreter, bindings, libraries, and executable in one pass:

import subprocess, os, sys
solver = os.path.join(os.environ["MADYMO_HOME"], "bin", "madymo")
result = subprocess.run(
    [solver, "-i", "smoke.xml", "-o", "smoke_out"],
    capture_output=True, text=True
)
print("returncode:", result.returncode)
print("stdout tail:", result.stdout[-200:])
assert os.path.exists("smoke_out.xml"), "solver produced no output"

Expected observable result: returncode: 0 and the presence of smoke_out.xml. Typical failure symptoms and their causes are summarized below.

SymptomLikely causeFix
ImportError: DLL load failedArchitecture or ABI mismatchUse matching CPython version and bitness
libmadymo.so: cannot openLD_LIBRARY_PATH not setExport before launching Python
FileNotFoundError: madymoSolver not on PATHAdd MADYMO_HOME\bin to PATH
Non-zero return code, no outputInvalid input deck or license issueCheck solver stdout and license server
Record the exact interpreter version, package versions, and MADYMO version in a project README. When a script works on one workstation and fails on another, this record is the fastest way to find the difference.

The diagram below shows how the interpreter, the virtual environment, the environment variables, and the MADYMO binaries connect at runtime.

Python–MADYMO Runtime Integration CPython 3.9 x64 .venv-madymo numpy, pandas matplotlib, lxml isolated per project Environment Variables MADYMO_HOME PATH / LD_LIBRARY_PATH PYTHONPATH set before interpreter start MADYMO Installation bin/madymo (solver) lib/ shared libraries python/ bindings version-specific Automation Script (Python) import madymo → load bindings → resolve shared libs subprocess.run([solver, "-i", deck, "-o", out]) Outputs smoke_out.xml · time-history · injury metrics Verification returncode == 0 output file exists

With the environment verified, the next step is to structure the automation script itself: how it locates input decks, parameterizes them, and dispatches solver runs. The environment you built here is the foundation that makes those scripts portable across workstations and reproducible in batch execution.