RunMat
GitHub

Using FEA

FEA workflows in RunMat start with a geometry file and end with a run result you can inspect or store.

Use one of these entry points:

Entry pointBest for
runmat check <file>Checking a .m script or .fea study before running it.
runmat run <file>Running either a .m script or a .fea study/sweep.
Named project entrypointGiving a stable name to a .m file or .fea file in runmat.toml.
RunMat codeLoading geometry, building typed FEA studies, or loading .fea files from .m.
Rust/runtime operationsHost integrations that need low-level control over models, operations, artifacts, or result queries.

Run A .fea File

A .fea file is a YAML file that defines a study or parametric sweep.

runmat check studies/bracket_static.fea
runmat run studies/bracket_static.fea

runmat check loads geometry, resolves the study, validates it, and plans it. It does not solve. runmat run executes the study or sweep and prints a run summary by default.

.fea files use the same runmat run command as .m files. Positional script arguments and --emit-bytecode apply only to .m files.

By default, FEA run output is human-readable and includes the run_id, status, quality gates, evidence path, and a fea.results("<run_id>") hint for post-processing. Use runmat run --json studies/bracket_static.fea when a script or CI job needs structured JSON. Use runmat check --json studies/bracket_static.fea for structured validation and plan output.

Minimal Study File

This is the smallest useful .fea study. The geometry path is resolved relative to the .fea file.

version: 1
kind: study
id: bracket_static

geometry:
  path: ../geometry/bracket.stl
  units: meter

model:
  profile: linear_static_structural

run:
  backend: cpu

The default model mode is profile_scaffold. RunMat loads the geometry and creates a scaffold model from the selected profile during validation, planning, and running.

Full Study Shape

Use explicit fields when the study file should carry model data directly:

version: 1
kind: study
id: bracket_static

geometry:
  path: ../geometry/bracket.step
  units: millimeter
  import:
    max_triangles: 16000000

model:
  id: bracket_static_model
  profile: linear_static_structural
  defaults: none
  frame: global

regions:
  fixed_base:
    selector: "name:Base_Mount"
  load_face:
    selector: "name:Tip_Load"

materials:
  aluminum_6061:
    name: Aluminum 6061
    mechanical:
      youngs_modulus_pa: 69000000000.0
      poisson_ratio: 0.33

material_assignments:
  - region: fixed_base
    material: aluminum_6061
  - region: load_face
    material: aluminum_6061

boundary_conditions:
  - id: fixed_base
    region: fixed_base
    kind: fixed

loads:
  - id: tip_force
    region: load_face
    type: force
    vector: [0.0, -1000.0, 0.0]

steps:
  - id: static_step
    kind: static

run:
  backend: cpu
  options:
    deterministic_mode: true
    precision_mode: fp64
    preconditioner_mode: auto
    quality_policy: balanced

Supported region selectors are direct region ids, id:<region-id>, region:<region-id>, name:<region-name>, and tag:<tag>. CAD imports commonly expose tags such as occt_face, cad_body, cad_layer, and cad_material; inspect the loaded geometry before relying on a selector.

Sweep File

Use a sweep when several studies should be checked or run as one deterministic sequence. Nested studies use the same study fields but do not include kind.

version: 1
kind: sweep
id: bracket_material_sweep
fail_fast: true

studies:
  - version: 1
    id: bracket_aluminum
    geometry:
      path: ../geometry/bracket.stl
      units: meter
    model:
      profile: linear_static_structural
    run:
      backend: cpu

  - version: 1
    id: bracket_steel
    geometry:
      path: ../geometry/bracket.stl
      units: meter
    model:
      profile: linear_static_structural
    run:
      backend: cpu

Sweeps execute sequentially in v1. fail_fast: false lets planning or running continue after a study-level failure and records failure entries in the sweep result.

Run From .m Code

The RunMat-code API can express the same study content as .fea. Use typed FEA objects when the study is generated, parameterized, or mixed with normal numeric code.

geom = geometry.load("geometry/bracket.step");

steel = fea.material("steel", ...
    "YoungsModulusPa", 200e9, ...
    "PoissonRatio", 0.30);

fixed = fea.boundaryCondition("fixed_base", "name:Base_Mount", "fixed");
tipLoad = fea.loadCase("tip_force", "name:Tip_Load", "force", ...
    "Vector", [0 -1000 0]);

model = fea.model("bracket_static_model", geom, ...
    "Profile", "linear_static_structural", ...
    "Materials", {steel}, ...
    "BoundaryConditions", {fixed}, ...
    "Loads", {tipLoad});

opts = fea.runOptions("linear_static", ...
    "DeterministicMode", true, ...
    "PrecisionMode", "fp64", ...
    "PreconditionerMode", "auto", ...
    "QualityPolicy", "balanced");

study = fea.study("bracket_static", geom, ...
    "Profile", "linear_static_structural", ...
    "Backend", "cpu", ...
    "Model", model, ...
    "RunOptions", opts);

validation = fea.validate(study);
plan = fea.plan(study);
result = fea.run(study);
results = fea.results(result);
stress = fea.field(results, "von_mises");
fea.plot(result, "von_mises");
print("bracket_von_mises.png", "-dpng");

Use .fea when the study definition should be checked in as a portable declarative artifact:

study = fea.load("studies/bracket_static.fea");
validation = fea.validate(study);
result = fea.run(study);
results = result.results();
stress = result.field("structural.von_mises");
peakStressPa = max(stress.values);

fea.load(...) returns either a fea.Study or fea.Sweep object. geometry.load(...) returns a geometry.Asset object. The workflow builtins accept .fea paths, fea.Study objects, or fea.Sweep objects. fea.RunResult objects expose results(), field(fieldId), and plot(fieldId) methods. fea.Field objects expose script-friendly values, shape, unit, location, kind, family, and quantity properties.

For manual visualization, geometry.meshes(asset) returns patch-ready surface topology:

asset = geometry.load("geometry/bracket.step");
meshes = geometry.meshes(asset);
m = meshes{1};
patch("Faces", m.faces, "Vertices", m.vertices);

The typed constructors are:

BuiltinUse
fea.material(...)Mechanical, thermal, electrical, and plastic material data.
fea.materialAssignment(...)Region-to-material assignment.
fea.boundaryCondition(...)Constraints such as fixed or EM boundary kinds.
fea.loadCase(...)Force, moment/torque, pressure, body force, current density, or coil current.
fea.step(...)Analysis step.
fea.domain(...)Thermo-mechanical, electro-thermal, electromagnetic, or CFD domain data.
fea.interface(...)Contact, conjugate heat-transfer, or fluid-structure interfaces.
fea.runOptions(...)Family-specific solver and quality options.
fea.model(...)Explicit model assembled from typed components.
fea.study(...)Study assembled from geometry, physics profile/model data, backend, and run options.
fea.sweep(...)Study sweep assembled from fea.Study objects.
fea.plot(...)Create a RunMat figure for a result field on the study geometry mesh.

Region strings in these constructors use the same selector grammar as .fea: direct region id, id:<region-id>, region:<region-id>, name:<region-name>, or tag:<tag>.

Moment loads are structural load resultants in N*m. Use "moment" as the canonical load kind; "torque" is accepted as an alias and resolves to the same model load kind. Direct moment loads require structural elements with rotational DOFs, such as beams or shells. Solid-only displacement regions reject direct moment loads because continuum solids do not own nodal rotations.

tipMoment = fea.loadCase("tip_moment", "node:2", "moment", ...
    "Vector", [0 0 125]);

Project Entrypoints

Named entrypoints can target .m or .fea files:

[sources]
roots = ["src"]

[entrypoints.bracket_static]
path = "studies/bracket_static.fea"

Run it with the same command:

runmat run bracket_static

Runtime Config

Use [runtime.fea] for FEA artifacts:

[runtime.fea]
artifact_store = "filesystem"
artifact_root = "artifacts"
geometry_prep_require_latest_revision = true

When artifact_root is omitted, RunMat uses ./artifacts. Run artifacts are written under runs, study evidence under studies, geometry prep under geometry-prep, and thermo-field artifacts under thermo-fields. Override the specific roots only when a host needs a different layout.

Rust Host Usage

Rust hosts call runtime operations when they need the operation envelope, trace ids, direct model construction, or result queries:

use runmat_analysis_fea::ComputeBackend;
use runmat_runtime::analysis::{
    analysis_create_model_op, analysis_run_linear_static_op, analysis_validate,
    AnalysisCreateModelIntentSpec, AnalysisCreateModelProfile,
};
use runmat_runtime::geometry::geometry_load_op;
use runmat_runtime::operations::OperationContext;

let context = OperationContext::new(Some("trace-001".to_string()), None);
let geometry = geometry_load_op("bracket.step", &bytes, context.clone())?.data;

let model = analysis_create_model_op(
    &geometry,
    AnalysisCreateModelIntentSpec {
        model_id: "bracket_static_model".to_string(),
        profile: AnalysisCreateModelProfile::LinearStaticStructural,
        prep_context: None,
    },
    context.clone(),
)?.data;

analysis_validate(
    &model,
    geometry.units,
    &runmat_analysis_core::ReferenceFrame::Global,
    context.clone(),
)?;

let run = analysis_run_linear_static_op(&model, ComputeBackend::Cpu, context)?.data;

The public operation identifiers in envelopes are fea.*, such as fea.create_model/v1 and fea.run_linear_static/v1.