2. MADYMO model structure and input files
Before any parametric study can be automated, you must understand what a MADYMO model actually is on disk. MADYMO does not store a model as a single opaque binary. Instead, a model is a structured collection of input files that the solver reads, validates, and assembles at runtime. Automation lives or dies on how cleanly you can locate, read, and rewrite the parameters inside those files. This section maps the three input formats you will encounter — XML, FE (finite element), and keyword — and shows where each one exposes the values your Python scripts need to drive.
The three input formats and their roles
MADYMO input is organized around a small number of file types, each with a distinct purpose:
- XML input files (
*.xml) — the primary model definition. They describe systems, bodies, joints, ellipsoids, facets, contacts, belts, and control definitions in a hierarchical, tag-based structure. This is where most parametric values (masses, positions, stiffness coefficients, initial velocities) live. - FE input files (
*.inc,*.key, or referenced mesh files) — finite element meshes, node coordinates, element connectivity, and material definitions. These are typically referenced from the XML via include or FE-body declarations. - Keyword files (
*.key,*.dat) — a flat, card-based format inherited from LS-DYNA-style conventions. Keyword decks define materials, sections, and control cards, and are often the interface between MADYMO and external FE preprocessors.
The key insight for automation is that XML is the orchestration layer. It references the FE and keyword data, and it is the file you will most often rewrite programmatically. FE and keyword files are usually large and mesh-dominated; you touch them only when a parameter is genuinely a mesh property (node position, element thickness, material constant).
How XML exposes parameters
MADYMO XML is a tree. A typical fragment defining a rigid body with an ellipsoid looks like this:
<SYSTEM NAME="dummy">
<BODY NAME="head" MASS="4.5">
<INITIAL_POSITION X="0.0" Y="0.0" Z="0.72"/>
<INITIAL_VELOCITY X="-12.0" Y="0.0" Z="0.0"/>
<ELLIPSOID NAME="head_ell" DIM1="0.09" DIM2="0.11" DIM3="0.10"/>
</BODY>
</SYSTEM>
Every attribute you might want to sweep — MASS, X, Y, Z, DIM1, DIM2, DIM3 — is addressable by a path plus an attribute name. This is exactly the structure that Python's xml.etree.ElementTree or lxml is built to manipulate. You do not need to parse text with regular expressions; you navigate the tree and set attributes.
Two XML features matter enormously for automation:
- Includes. A model can pull in other XML files with an include directive. This means a single logical model may span several physical files. Your automation must resolve includes before it can guarantee it has found every instance of a parameter.
- Named objects. Because bodies, systems, and ellipsoids carry
NAMEattributes, you can target a specific object unambiguously. A parameter sweep on "head mass" should locateBODY[@NAME='head'], not the thirdMASSattribute in the file.
NAME and attribute, never by line number or ordinal position. Ordinal-based edits break the moment someone reorders the model.How FE and keyword files expose parameters
FE and keyword files are card-based. Each line is a record, and fields are fixed-width or comma/space separated. A material card might read:
*MAT_ELASTIC
$# mid ro e pr
101 7.85e-9 210000.0 0.3
Here the parameter of interest — Young's modulus e — sits in a fixed column position on the data line following the keyword. Automation on keyword files is therefore positional: you find the keyword card, advance to the correct data line, and rewrite the field while preserving column alignment. This is more fragile than XML editing, so the practical rule is: only automate keyword files when the parameter is genuinely a material or section property that cannot be expressed in XML.
FE mesh files (node and element blocks) are the least automation-friendly. Node coordinates are dense numeric tables; changing one node means rewriting a line while keeping the node ID and the rest of the coordinates intact. For parametric studies, prefer to keep geometry variation in XML-level ellipsoids or in a small number of FE parameters rather than in raw mesh coordinates.
Choosing the right edit target
The following table summarizes where a given parameter type typically lives and how safely it can be automated.
| Parameter type | Typical file | Edit method | Automation safety |
|---|---|---|---|
| Body mass, initial position/velocity | XML | Attribute set by NAME | High |
| Ellipsoid dimensions | XML | Attribute set by NAME | High |
| Joint stiffness / characteristics | XML | Attribute or referenced curve | High |
| Material constants (E, density, yield) | Keyword | Positional field rewrite | Medium |
| Element thickness / section | Keyword | Positional field rewrite | Medium |
| Node coordinates | FE mesh | Line rewrite by node ID | Low |
Resolving includes and building a parameter map
Real models are rarely one file. A robust automation workflow first builds a parameter map: a resolved view of every file in the model, with each candidate parameter tagged by file, path, and attribute. The process is:
- Start from the top-level XML file named in the run command.
- Recursively follow include directives, recording the absolute path of each included file.
- For each XML file, walk the tree and index elements by
NAMEand tag. - For each keyword file, index cards by keyword and data-line offset.
- Store the result as a dictionary keyed by a stable identifier such as
dummy/head@MASS.
Once this map exists, a parametric study becomes a loop: for each design point, apply the parameter values to the map, write the modified files to a run-specific directory, invoke the solver, and collect results. The map is what makes the loop safe — you never guess where a value lives.
Worked example: locating and sweeping head mass
Suppose the study varies head mass from 4.0 to 5.0 kg in 0.25 kg steps. The automation must (1) find the body named head, (2) confirm it has a MASS attribute, and (3) write each value into a fresh copy of the model.
import xml.etree.ElementTree as ET
import shutil, os
def load_model(src, run_dir):
shutil.copytree(src, run_dir, dirs_exist_ok=True)
tree = ET.parse(os.path.join(run_dir, "model.xml"))
return tree
def set_body_mass(tree, body_name, mass):
for body in tree.iter("BODY"):
if body.get("NAME") == body_name:
body.set("MASS", f"{mass:.3f}")
return True
return False
for m in [4.0, 4.25, 4.5, 4.75, 5.0]:
run = f"run_m{m:.2f}"
tree = load_model("baseline", run)
if not set_body_mass(tree, "head", m):
raise RuntimeError("body 'head' not found")
tree.write(os.path.join(run, "model.xml"))
After each run, verify the edit by re-parsing the written file and asserting the attribute equals the intended value. A common failure symptom is a silent no-op: the body name does not match (for example, Head versus head), so set_body_mass returns False and the run silently uses the baseline mass. Always check the return value and assert on the written file.
Practical guidance for building the parameter map
Keep the map explicit and inspectable. A JSON or YAML file listing each parameter with its file, path, and current value lets you review a study design before running anything. When a run fails, the map tells you exactly which file and attribute were touched, which shortens debugging dramatically. Treat the map as the contract between your study definition and the model files — if a parameter is not in the map, your automation should refuse to run rather than silently skip it.
Finally, remember that XML, FE, and keyword files are not interchangeable. XML gives you named, hierarchical, safe-to-edit parameters. Keyword and FE files give you positional, fragile, but sometimes unavoidable parameters. A well-designed parametric study pushes as much variation as possible into XML and reserves keyword or mesh edits for the cases where the physics genuinely demands them.