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 point | Best 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 entrypoint | Giving a stable name to a .m file or .fea file in runmat.toml. |
| RunMat code | Loading geometry, building typed FEA studies, or loading .fea files from .m. |
| Rust/runtime operations | Host 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.fearunmat 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: cpuThe 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: balancedSupported 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: cpuSweeps 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");fea.boundaryCondition accepts finite real numeric scalars for the fields of prescribedRotation, acousticImpedance, thermalPrescribedTemperature, thermalHeatFlux, thermalConvection, cfdInletVelocity, and cfdOutletPressure. All eight built-in integer classes are accepted for those fields; the constructor preserves the exact scalar until one explicit IEEE-754 binary64 model-storage conversion, so sufficiently wide integers can round. The fea.boundaryCondition builtin reference lists the required field names for each kind.
The same host-only physical-value boundary applies to the documented numeric fields of fea.material, fea.loadCase, fea.domain, and fea.interface: field names, scalar/vector shape, finiteness, and kind-specific requirements are validated before one binary64 model-storage conversion, and sufficiently wide integers can round. Nested domain revision values, geometry/model revisions, plan counts, field shape/count metadata, and comparison count deltas are structural integers instead; they remain exact and do not enter the physical-value conversion. The conventional plotting handle remains a double at the scripting boundary. Resident numeric constructor fields are rejected because typed FEA object construction performs no provider execution.
fea.runOptions distinguishes structural controls from physical solver settings. Mode, step, iteration, retry, backtrack, and tangent-refresh counts accept all eight integer classes and ordinary integral doubles exactly; tolerances, time steps, scales, ratios, frequencies, and residual thresholds cross one checked binary64 boundary. Logical settings require logical scalars. Public PrepContext injection is rejected because it is internal runtime state, while PrepArtifactId and PrepCalibrationProfile remain optional text settings. The complete per-family option/default table is in the operation reference.
fea.results uses positive one-based exact vectors for ModeIndices and TransientSnapshotIndices, matching normal RunMat indexing. Its inclusion flags accept logical scalars or exact numeric zero and one. Selector shape, positivity, integrality, and range are validated before artifact lookup; resident selectors reject without provider access. Structural result metadata such as shapes, counts, indexes, byte sizes, iteration counts, and fingerprints remains exact, while field values and physical/timing/residual quantities remain double. fea.trends("WindowSize", n) applies the same exact structural rule to a positive scalar and defaults to 16.
fea.materialAssignment, fea.model, fea.plan, fea.field, fea.plot, and fea.compare have no integer-array input role. Their inputs are exact text identifiers, typed FEA or geometry objects, or document paths, while any integer-valued metadata they return follows the structural-output rule. fea.compare accepts exactly the baseline and candidate run identifiers and has no Name, Value options.
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.
The typed constructors and post-processing helpers do not intrinsically require native OCCT. They can consume an existing geometry.Asset, STL-backed or synthetic geometry, and persisted mesh/result artifacts without OCCT; only an earlier import of topology-bearing CAD such as STEP, IGES, or BREP may require the configured OCCT geometry backend.
Argument decoding for fea.run, and artifact filtering for fea.results and fea.trends, is host-side. The solver invoked by fea.run is different: it may execute through the CPU or configured GPU backend selected by the study. Result queries return stored device references as metadata and do not gather or execute them.
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:
| Builtin | Use |
|---|---|
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_staticRuntime Config
Use [runtime.fea] for FEA artifacts:
[runtime.fea]
artifact_store = "filesystem"
artifact_root = "artifacts"
geometry_prep_require_latest_revision = trueWhen 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.