2. Data model for iteration sets

Multi-iteration CAE comparison only becomes reliable when every result file can be traced back to a well-defined design point and a reproducible solver run. The data model for an iteration set is the contract that makes this possible. It defines what an "iteration" is, which attributes travel with each result, how design variables are typed and stored, and how run identifiers remain unique across solvers, machines, and re-submissions. Without this contract, comparison degenerates into matching filenames by hand and guessing which result_03.odb belongs to which thickness value.

In practice, an iteration set is a collection of run records, each of which binds four things together: a design point (the vector of design variable values), a run identifier (a stable, unique key), a set of result artifacts (solver output files plus extracted scalar/field metrics), and metadata (solver version, mesh, boundary conditions, timestamps, status). The design point answers "what did we analyze"; the run identifier answers "which execution produced it"; the artifacts answer "what came out"; the metadata answers "can we trust and reproduce it".

Core entities and their relationships

A workable schema separates the study (the overall DOE or optimization campaign), the iteration (one design point within the study), and the run (one execution of the solver for that iteration). This three-level split matters because a single design point is frequently re-run — after a mesh refinement, a solver upgrade, or a failed convergence — and each attempt must be preserved rather than overwritten. The iteration is the logical unit of comparison; the run is the physical unit of execution.

The following SVG shows the entity structure and how identifiers, design variables, artifacts, and metadata attach to an iteration, including the one-to-many relationship between an iteration and its runs.

Study study_id solver, DOE method Iteration (design point) iteration_id design_vector status, created_at Run run_id (unique) attempt, solver_version started_at, finished_at Design variables name, unit, type nominal, lower, upper value (per iteration) scaling / normalization Result artifacts result file path extracted metrics field data / images checksum, size Metadata mesh id, BC set material revision host, solver build convergence flag 1 : N 1 : N defines produces annotates writes Key rules • run_id is immutable and globally unique; re-runs create new run records, never overwrite. • design_vector is stored with units and scaling so comparisons are dimensionally consistent. • artifacts are referenced by path + checksum; metadata is versioned with the run, not the iteration.

Run identifiers: uniqueness and traceability

The run identifier is the backbone of traceability. A robust convention encodes enough context to be human-readable while remaining machine-unique. A common pattern is a composite key such as {study_id}-{iteration_id}-{attempt}-{timestamp}, for example BRKT-OPT-0042-02-20250314T0912. The attempt field is essential: when iteration 42 is re-run after a mesh fix, the new run gets attempt=03 and the previous attempts remain queryable. Comparison logic should always resolve to a specific run, not merely to an iteration, so that a ranking table can state exactly which execution produced each number.

Two failure modes are worth guarding against explicitly. First, timestamp-only identifiers collide when jobs are submitted in parallel from a batch script; include a short hash of the input deck or a monotonic counter. Second, identifiers that embed mutable attributes (such as the current best objective value) become misleading after re-runs; keep identifiers opaque and push descriptive attributes into metadata.

Design variables: typing, units, and scaling

Design variables must be stored as typed records, not as bare numbers. Each variable needs a name, a unit, a type (continuous, integer, categorical), bounds, and a nominal value. Units are non-negotiable: a thickness stored as 2.5 is ambiguous between millimetres and inches, and a comparison that mixes them silently produces wrong rankings. Store the unit string alongside the value and normalize to a canonical unit at ingestion.

Scaling deserves its own field. Optimization and DOE workflows frequently normalize variables to [0, 1] or to a mean/std basis; if the scaling is not recorded, a later reader cannot reconstruct the physical design point. A minimal variable record looks like this:

{
  "name": "rib_thickness",
  "unit": "mm",
  "type": "continuous",
  "bounds": { "lower": 1.2, "upper": 4.0 },
  "nominal": 2.5,
  "scaling": { "method": "minmax", "offset": 1.2, "factor": 2.8 }
}

For categorical variables (material grade, laminate layup), store the label and a stable code; never rely on list position, which changes when options are reordered.

Result artifacts and extracted metrics

Raw solver output is large and solver-specific. The data model should distinguish between artifact references (paths to the original files, with checksums) and extracted metrics (the scalars and fields actually used for comparison). Extracted metrics are what ranking consumes: maximum von Mises stress, first natural frequency, mass, peak displacement, safety factor. Each metric record should carry its value, unit, location or probe identifier, and the extraction method or script version. This separation lets you re-extract metrics from archived artifacts without re-running the solver, and it makes the comparison layer independent of the solver's file format.

Store a checksum (for example SHA-256) for every artifact. When a comparison later produces an unexpected value, the checksum quickly confirms whether the file was replaced, truncated, or copied from the wrong run.

Metadata: the reproducibility envelope

Metadata captures everything needed to judge whether two runs are comparable. At minimum it should include solver name and version, mesh identifier and element count, boundary-condition set identifier, material revision, host or cluster, convergence status, and wall-clock timestamps. The convergence flag is particularly important: a run that terminated at the iteration limit without converging should be marked and excluded from ranking by default, rather than silently competing with converged designs.

A practical rule is to version metadata with the run, not the iteration. If the mesh is refined between attempts, the new run carries the new mesh identifier while the iteration's design vector stays unchanged. This keeps the logical design point stable while preserving the physical history of how it was evaluated.

Putting it together: a minimal iteration-set manifest

The following manifest illustrates how the entities combine for two iterations of a bracket study. It is a teaching example with illustrative values, not measured data.

{
  "study_id": "BRKT-OPT",
  "solver": "Abaqus 2024",
  "iterations": [
    {
      "iteration_id": "0041",
      "design_vector": { "rib_thickness": 2.5, "fillet_radius": 6.0 },
      "runs": [
        {
          "run_id": "BRKT-OPT-0041-01-20250313T1042",
          "status": "converged",
          "metadata": { "mesh_id": "M2", "bc_set": "LC1", "material_rev": "AL6061-r3" },
          "metrics": { "mass_kg": 1.84, "max_stress_MPa": 212.0, "f1_Hz": 148.3 }
        }
      ]
    },
    {
      "iteration_id": "0042",
      "design_vector": { "rib_thickness": 3.0, "fillet_radius": 8.0 },
      "runs": [
        {
          "run_id": "BRKT-OPT-0042-01-20250313T1105",
          "status": "not_converged",
          "metadata": { "mesh_id": "M2", "bc_set": "LC1", "material_rev": "AL6061-r3" },
          "metrics": {}
        },
        {
          "run_id": "BRKT-OPT-0042-02-20250314T0912",
          "status": "converged",
          "metadata": { "mesh_id": "M3", "bc_set": "LC1", "material_rev": "AL6061-r3" },
          "metrics": { "mass_kg": 2.01, "max_stress_MPa": 176.5, "f1_Hz": 161.9 }
        }
      ]
    }
  ]
}

Reading this manifest, a comparison tool can immediately see that iteration 0042 has two runs, that the first did not converge and should be excluded, and that the second used a refined mesh (M3 versus M2). That last point is a comparability warning: mass and stress from M3 are not strictly equivalent to M2 results, so any ranking that mixes them should flag the mesh difference rather than treat the numbers as interchangeable.

Do not let the comparison layer infer design variables from filenames or directory structure. Filenames drift, directories get reorganized, and parallel jobs overwrite each other. The manifest is the single source of truth; filenames are only hints.

Validation checks before comparison

Before an iteration set is handed to the ranking stage, run a small set of integrity checks: every iteration has at least one run; every run has a unique identifier; every converged run has a complete metric set; every design variable value lies within its declared bounds; and every artifact checksum matches. These checks are cheap and catch the majority of data-model errors that would otherwise surface as inexplicable ranking anomalies. When a check fails, the correct response is to fix or quarantine the offending run record, not to patch the comparison output.

The data model described here is deliberately solver-agnostic. Whether results come from Abaqus, ANSYS, Nastran, or an in-house solver, the same four bindings — design point, run identifier, artifacts, metadata — provide the structure that multi-iteration comparison and design ranking depend on.