2. CAE solver landscape

Before writing a single line of batch automation, you need a precise mental model of what a CAE solver actually is from the operating system's point of view. A solver is a command-line executable that reads one or more input files, allocates scratch space, consumes CPU and memory for minutes to days, and writes a predictable set of output artifacts. Everything a Python batch harness does — launching, monitoring, retrying, harvesting — is built on that contract. This chapter maps the five solvers you will encounter most often, their input deck formats, their command-line interfaces, and the files they leave behind.

The Five Solvers and Their Execution Models

Abaqus (Dassault Systèmes) is driven by the abaqus driver script. A typical implicit job is launched as abaqus job=beam input=beam.inp cpus=4 interactive. The input deck is a keyword-based .inp file: *NODE, *ELEMENT, *MATERIAL, *STEP, *BOUNDARY. Abaqus writes beam.odb (results database), beam.dat, beam.msg (solver messages and convergence), beam.sta (incremental status), and beam.log. The .sta and .msg files are the primary monitoring targets for a batch runner.

ANSYS Mechanical APDL runs in batch mode via ansys -b -i model.dat -o solve.out (or mapdl -b in newer releases). The input is a sequence of APDL commands in a .dat or .inp file. Results land in file.rst (result file), solve.out (stdout capture), and file.err. ANSYS also supports a .inp import path from Workbench, but for automation the APDL batch route is the most scriptable.

Nastran (MSC Nastran, NX Nastran) uses a fixed-format or free-field bulk data deck, conventionally .bdf or .dat. Execution is nastran job.bdf scr=yes. Outputs include job.f06 (the printed results and fatal messages), job.log, and optionally job.op2 or job.xdb for post-processing. The .f06 file is the canonical place to detect FATAL or WARNING messages.

OpenFOAM is not a single binary but a set of utilities operating on a case directory. The workflow is blockMesh → decomposePar → mpirun -np 8 simpleFoam -parallel → reconstructPar. Inputs are dictionaries (controlDict, fvSchemes, fvSolution) plus a system/ and constant/ tree. Outputs are time directories (0/, 100/, 200/) containing field files, plus log.simpleFoam and residual data in the solver log. Monitoring means tailing the log for residuals and continuity errors.

LS-DYNA is invoked as lsdyna i=crash.k ncpu=8 memory=200m. The input is a keyword deck (*KEYWORD, *CONTROL_TERMINATION, *MAT_PLASTIC_KINEMATIC). Outputs include d3plot (binary state database), d3hsp (ASCII status), messag (messages), and glstat/matsum (energy summaries). The d3hsp and messag files are the monitoring surface.

Solver Execution Contract: Input → CLI → Artifacts Input Deck CLI Invocation Primary Outputs Monitor Target Abaqus beam.inp keyword deck abaqus job=beam input=beam.inp cpus=4 interactive beam.odb beam.dat beam.msg beam.sta beam.log .sta increment status .msg convergence .log exit code ANSYS MAPDL model.dat APDL commands ansys -b -i model.dat -o solve.out (or mapdl -b) file.rst file.err solve.out file.db solve.out tail file.err fatal check rst existence Nastran job.bdf bulk data deck nastran job.bdf scr=yes (batch mode) job.f06 job.log job.op2 / job.xdb job.master .f06 FATAL scan .log completion op2 size check OpenFOAM system/ constant/ dictionaries blockMesh decomposePar mpirun simpleFoam -parallel time dirs 0/ 100/ 200/ log.simpleFoam processor*/ residual tail continuity errors time dir count LS-DYNA crash.k keyword deck lsdyna i=crash.k ncpu=8 memory=200m (MPP or SMP) d3plot d3hsp messag glstat matsum d3hsp termination messag error scan glstat energy

Input Decks: What the Batch Runner Must Know

Every solver's input deck has a restart and parameterization surface that batch automation exploits. Abaqus supports *RESTART, WRITE, FREQUENCY=n and *PARAMETER substitution; you can generate a family of decks by templating a base .inp and substituting material constants or load magnitudes. Nastran uses SOL 101, SOL 103, SOL 106 to select analysis type, and INCLUDE cards to pull in shared bulk data. OpenFOAM's controlDict holds endTime, deltaT, writeInterval, and functions for runtime post-processing. LS-DYNA's *CONTROL_TERMINATION sets ENDTIM, and *DATABASE_BINARY_D3PLOT controls output frequency.

For a Python harness, the practical implication is that you rarely hand-write decks. You keep a template directory, render decks with string.Template or Jinja2, and write them into a per-run working directory. This isolates runs, makes cleanup trivial, and lets you record the exact rendered deck alongside the results for reproducibility.

Command-Line Interfaces and Process Behavior

All five solvers share a common CLI pattern: an executable name, a set of key=value or flag arguments, and a working directory. The differences that matter for automation are:

  • Blocking vs. non-blocking. Abaqus interactive blocks; without it, the driver returns immediately and the solver runs in the background. Python's subprocess.run() blocks by default; subprocess.Popen() does not. Choose deliberately.
  • Exit codes. Abaqus returns 0 on success, non-zero on fatal error. Nastran's exit code is less reliable — always scan .f06 for FATAL. OpenFOAM utilities return non-zero on missing dictionaries. LS-DYNA returns 0 even for some error terminations, so d3hsp must be inspected.
  • Parallel launch. Abaqus and LS-DYNA take cpus=/ncpu= directly. OpenFOAM requires decomposePar then mpirun. ANSYS uses -np with a distributed solver.
  • License checkout. All five check out a license token at launch. A batch runner must handle the "license unavailable" case as a retryable failure, not a fatal one.
A robust Python launcher captures stdout and stderr to per-job log files, records the start timestamp, and stores the exact command line. When a job fails at 3 a.m., that command line is the only reliable way to reproduce it.

Output Artifacts and What They Tell You

Output artifacts fall into four categories, and each category drives a different monitoring strategy:

CategoryExamplesMonitoring Use
Status / progress.sta, d3hsp, log.simpleFoam, solve.outTail for increment number, residual, or termination reason
Diagnostic / messages.msg, .f06, messag, file.errScan for ERROR, FATAL, WARNING, divergence
Results database.odb, .rst, .op2, d3plot, time dirsCheck existence and file size; harvest for post-processing
Restart / checkpoint.res, .abq, restart*Enable resume after wall-clock timeout or crash

A concrete example: an Abaqus implicit job that diverges will show in beam.sta as a series of attempted increments with decreasing time increment, followed by ***ERROR: TOO MANY ATTEMPTS MADE FOR THIS INCREMENT in beam.msg. A Python monitor that tails .sta can detect the stall (no new increment line for N seconds) and either kill the job or trigger a restart with a smaller initial increment. For OpenFOAM, the equivalent signal is a residual that stops decreasing across iterations in log.simpleFoam; for LS-DYNA, it is a negative volume or a sudden energy spike in glstat.

Do not assume a zero exit code means success. LS-DYNA and some Nastran configurations return 0 after a fatal termination. Always pair the exit code with a content scan of the primary diagnostic file before marking a job as complete.

Putting It Together: A Solver-Agnostic Job Descriptor

The practical takeaway is that you can abstract all five solvers behind a small descriptor. The following is a teaching example, not a measured result — it illustrates the fields a batch runner needs:

job = {
    "solver": "abaqus",
    "workdir": "/scratch/runs/beam_001",
    "input": "beam.inp",
    "command": ["abaqus", "job=beam", "input=beam.inp",
                "cpus=4", "interactive"],
    "monitor_files": ["beam.sta", "beam.msg"],
    "result_files": ["beam.odb"],
    "error_patterns": ["***ERROR", "TOO MANY ATTEMPTS"],
    "timeout_sec": 7200,
    "restartable": True,
}

With this descriptor, the same Python loop can launch Abaqus, ANSYS, Nastran, OpenFOAM, or LS-DYNA. The solver-specific knowledge lives in the descriptor, not in the orchestration code. That separation is what makes a batch harness maintainable across a mixed-solver CAE environment.

The next step is to turn this descriptor into a working launcher: process creation, log capture, timeout enforcement, and exit-code interpretation. That is where the Python subprocess module and its sharp edges become the center of attention.