4. Extracting model metadata and solution controls
Before a single contour plot can be rendered or a dashboard tile populated, the automation pipeline needs to know what is inside the model. A CAE input deck is a complete, self-describing record of the simulation: it declares the mesh, the materials, the boundary conditions, the load cases, and the solver controls. For a reporting workflow, this file is the single source of truth. Extracting structured metadata from it programmatically is what turns a folder of solver decks into a queryable dataset.
This section focuses on the extraction layer. The goal is a Python routine that reads a deck, parses the relevant keyword blocks, and returns a normalized dictionary or DataFrame row that downstream report generators can consume without re-reading the raw file.
Why Parse the Deck Instead of the Solver Output
Solver output files (for example, .odb, .rst, or .f06) contain results, but they are large, binary or semi-structured, and often require a licensed API to read. The input deck, by contrast, is plain text, version-controllable, and available before the solve even runs. Extracting metadata from the deck means the dashboard can be built and validated independently of solver availability, and it can flag modeling issues (missing material, unconstrained nodes) before compute time is spent.
The trade-off is that input decks vary in dialect. A Nastran bulk data file, an Abaqus .inp, and a CalculiX .inp share concepts but differ in keyword syntax. A robust extractor therefore separates lexing (splitting the file into keyword blocks) from interpretation (mapping each block to a normalized field).
Deck Structure and the Keyword-Block Model
Most decks follow a line-oriented grammar: a keyword line begins a block, and subsequent data lines belong to it until the next keyword. Continuation lines and comment characters differ by solver, so the lexer must be configurable. The diagram below shows how a raw deck is decomposed into blocks and then mapped to normalized metadata fields.
Building the Extractor
A practical extractor is a small class with a solver-specific keyword map. The example below targets a Nastran-style bulk deck but the pattern generalizes. It reads the file once, groups lines by keyword, and exposes typed accessors.
import re
from collections import defaultdict
class DeckExtractor:
def __init__(self, path, comment_char="$"):
self.path = path
self.comment_char = comment_char
self.blocks = defaultdict(list)
self._lex()
def _lex(self):
current = None
with open(self.path) as fh:
for raw in fh:
line = raw.split(self.comment_char, 1)[0].rstrip()
if not line.strip():
continue
# continuation: leading '+' or blank first field
if line[0] in "+ " and current:
self.blocks[current].append(line)
continue
token = line.split(",")[0].strip().upper()
current = token
self.blocks[current].append(line)
def mesh_stats(self):
nodes = len(self.blocks.get("GRID", []))
elems = sum(len(self.blocks.get(k, []))
for k in ("CTETRA", "CHEXA", "CQUAD4", "CTRIA3"))
return {"nodes": nodes, "elements": elems}
def materials(self):
mats = {}
for line in self.blocks.get("MAT1", []):
f = [x.strip() for x in line.split(",")]
mats[f[1]] = {"E": float(f[2]) if f[2] else None,
"nu": float(f[4]) if len(f) > 4 and f[4] else None}
return mats
def subcases(self):
cases = {}
for line in self.blocks.get("SUBCASE", []):
cid = line.split()[-1]
cases[cid] = {}
# associate LOAD / SPC lines that follow each SUBCASE
# (simplified: assumes ordered blocks)
return cases
Two details matter for correctness. First, continuation handling: Nastran uses a leading + or a blank field to mark continuation, and ignoring this silently truncates long element or property records. Second, ID resolution: a PSOLID references a material ID, and a SUBCASE references load and SPC set IDs. Counting blocks is not enough; the extractor must resolve these references so the dashboard can answer "which material is on this property?" and "which load sets belong to subcase 1?"
Normalizing Across Solvers
Once extraction works for one dialect, the same normalized schema should be filled from others. The table below shows how equivalent concepts map. Keeping a single output schema means the dashboard code never branches on solver type.
| Normalized field | Nastran | Abaqus | CalculiX |
|---|---|---|---|
| nodes | GRID | *NODE | *NODE |
| solid elements | CTETRA / CHEXA | *ELEMENT, TYPE=C3D* | *ELEMENT, TYPE=C3D* |
| material | MAT1 | *MATERIAL + *ELASTIC | *MATERIAL + *ELASTIC |
| property | PSOLID / PSHELL | *SOLID SECTION | *SOLID SECTION |
| constraints | SPC1 | *BOUNDARY | *BOUNDARY |
| loads | FORCE / PLOAD4 | *CLOAD / *DLOAD | *CLOAD / *DLOAD |
| load case | SUBCASE | *STEP | *STEP |
| solver control | SOL, EIGRL | *STEP, NLGEOM | *STATIC / *FREQUENCY |
Extracting Solution Controls
Solution controls determine what the solver actually computes, and they are frequently the source of mismatched expectations in reports. The extractor should capture at minimum: the analysis type (static, modal, buckling, transient), the number of requested modes or time steps, and any nonlinear or solver-selection flags. For a modal run, the EIGRL card's mode count and frequency range are the key parameters; for a static run, the subcase-to-load mapping is the key parameter.
SUBCASE resolves to at least one load set and one constraint set. A subcase with no load is a modeling error that will produce a zero-result report, and catching it at extraction time is far cheaper than debugging an empty contour plot.
Validation and Failure Symptoms
Extraction bugs are quiet. A missed continuation line reduces the element count without raising an error; a case-sensitivity mistake drops a keyword block entirely. Build in cheap assertions: compare the extracted node and element counts against the solver's own echo in the output file, and log any keyword that appears in the deck but is absent from the keyword map. When counts disagree, the usual causes are comment characters not matching the solver's convention, continuation lines being treated as new keywords, or fixed-format fields being split on the wrong delimiter.
The output of this stage is a tidy record — one row per model, with nested fields for materials, boundary conditions, load cases, and solver settings. That record is the input to every subsequent reporting step, so its schema should be fixed early and versioned alongside the extractor code.