3. CAE solver landscape

Automating a CAE workflow in Python begins with a clear-eyed inventory of the solvers you intend to drive. A solver is not a single executable; it is a bundle of a physics kernel, a file format contract, a licensing model, and one or more programmatic entry points. Choosing the wrong integration surface — for example, screen-scraping a GUI when a documented Python API exists — creates fragile automation that breaks on every minor release. This chapter maps the solver landscape along four axes: license and distribution model, native input format, execution mode, and integration surface. Understanding these axes lets you predict how much glue code a given solver will require before you write a single line.

Commercial versus open-source solvers

Commercial solvers such as Ansys Mechanical, Abaqus, Nastran (MSC or NX), COMSOL Multiphysics, and STAR-CCM+ ship with vendor-supported APIs, versioned documentation, and regression-tested behavior across releases. Their licensing typically uses a license server (FlexNet or similar), which means an automated run can fail not because of a modeling error but because a token was unavailable. Open-source solvers such as CalculiX, Code_Aster, OpenFOAM, SU2, FEniCS/FEniCSx, and deal.II offer unrestricted execution and source-level transparency, but their input formats and Python bindings vary widely in maturity. A practical rule: commercial solvers reward you with stable APIs but constrain concurrency through licensing; open-source solvers reward you with unlimited parallel runs but require you to validate the build and the input deck yourself.

Input formats and what they imply for automation

The input format determines whether you can generate a model programmatically or must template an existing file. Three families dominate:

  • Text decks (Abaqus .inp, CalculiX .inp, Nastran .bdf, OpenFOAM dictionaries): human-readable, diff-friendly, and easy to generate from Python string templates or dedicated writers. Parameter sweeps are straightforward because you can rewrite a single keyword block.
  • Binary or hierarchical containers (COMSOL .mph, STAR-CCM+ .sim): not safely editable as text. Automation must go through the vendor API, which manipulates the model in memory and saves a new container.
  • Structured mesh and field formats (CGNS, VTK/VTU, Exodus II, MED): these carry geometry, mesh, and boundary tags but usually not solver control cards. They pair with a separate solver input file.

For example, a CalculiX deck for a static cantilever can be generated by substituting a thickness parameter into a template:

*NODE
1, 0.0, 0.0, 0.0
2, 100.0, 0.0, 0.0
*ELEMENT, TYPE=C3D8, ELSET=EALL
1, 1, 2, ...
*MATERIAL, NAME=STEEL
*ELASTIC
210000.0, 0.3
*STEP
*STATIC
*BOUNDARY
1, 1, 3, 0.0
*CLOAD
2, 2, -500.0
*END STEP

Here the load -500.0 and the elastic modulus 210000.0 are the natural sweep variables. A Python driver reads a base template, writes a new deck per sample, and launches the solver. The same pattern applies to Nastran and OpenFOAM, though OpenFOAM splits control across controlDict, fvSchemes, and fvSolution, so a sweep may touch several files.

Execution modes

Solvers run in three modes that matter for orchestration:

  1. Batch/command-line: ccx job.inp, abaqus job=job.inp interactive, simpleFoam. Python invokes these with subprocess.run, captures stdout/stderr, and checks the exit code plus a solver-specific success marker (for example, *ERROR absence in the .dat file, or End in the OpenFOAM log).
  2. In-process library: FEniCSx, deal.II, and PyMAPDL load the solver into the Python process. No file round-trip is needed, but a crash takes down your driver, so isolate these runs in separate processes when sweeping.
  3. Server/session: COMSOL mphserver and STAR-CCM+ run as persistent sessions that accept multiple jobs. This amortizes startup cost but requires connection management and explicit teardown.
Always capture the solver's own convergence log, not just the exit code. A solver can exit 0 while reporting a diverged or unconverged solution. Parse the residual or convergence block and treat it as a first-class result field.

Integration surfaces

The integration surface is the contract your Python code depends on. Ranked from most to least robust:

SurfaceExampleStabilityTypical use
Native Python APIPyMAPDL, PyFluent, FEniCSx, compas_feaHighModel build, solve, post-process in one process
Vendor scripting (wrapped)COMSOL mph, Abaqus abaqus pythonMedium-highContainer models, batch parametric runs
File templating + CLICalculiX, OpenFOAM, SU2MediumLarge sweeps, HPC queues
GUI automationpywinauto, pyautoguiLowLast resort for legacy tools

Prefer the highest row you can reach. File templating is often the pragmatic choice for open-source solvers because it is transparent and parallelizes trivially, but it demands that you validate the generated deck. A useful safeguard is to run one reference case through the solver, store its key outputs, and re-run it after any template change as a regression check.

Choosing a solver for an automated pipeline

Before committing, answer four questions. Does the solver expose a documented, versioned API or a stable CLI? Can its input be generated without a GUI? Does its license permit the concurrency your sweep needs? And can you detect failure programmatically? A solver that answers "no" to the last question will silently poison an optimization loop with garbage objective values.

Licensing is a hidden coupling. A 200-sample DOE that launches 200 concurrent commercial solver processes will exhaust tokens and produce spurious failures that look like modeling errors. Throttle concurrency to the token count, or serialize through a queue.

The diagram below summarizes how the four axes combine for representative solvers and where the Python integration point sits in each case.

Solver Landscape: Format, Execution Mode, and Integration Surface Python Driver subprocess / API template render log parse result collect orchestration layer CalculiX (open) Format: .inp text deck Mode: batch CLI (ccx) Surface: template + CLI check .dat for *ERROR OpenFOAM (open) Format: dictionary files Mode: batch CLI (simpleFoam) Surface: template + CLI check log for "End" FEniCSx (open) Format: in-memory mesh Mode: in-process library Surface: native Python API isolate per run Abaqus (commercial) Format: .inp / .cae Mode: batch + abaqus python Surface: CLI + scripting license token bound COMSOL (commercial) Format: .mph container Mode: mphserver session Surface: mph Python API persistent connection Ansys MAPDL (comm.) Format: APDL / .cdb Mode: in-process / batch Surface: PyMAPDL API native Python binding Integration rank Native API Vendor scripting Template + CLI GUI automation prefer higher rows

Practical checklist before writing the driver

For each candidate solver, record the following in a short integration note. This note becomes the specification your Python driver must satisfy.

FieldWhat to recordWhy it matters
Solver + versionExact build stringAPI and format behavior change across releases
Input formatText deck, container, or in-memoryDetermines generation strategy
InvocationCLI command or API entry pointDefines the subprocess or import call
Success markerExit code plus log tokenPrevents false positives in sweeps
License constraintToken count / concurrency limitSets the sweep's parallelism ceiling
Output artifactsResult files and their parsersFeeds the objective function

With this inventory complete, the next step is to wrap each solver behind a uniform Python interface so that the optimization layer never needs to know which solver it is calling. That abstraction — a common solve(parameters) -> results contract — is what makes a multi-solver CAE pipeline maintainable, and it is the subject of the following chapter.