Wolfram language modelica
Skill WolframResearch/system-modeler-ai-toolkit/wolfram-language-modelica
AI assistant skills for building, simulating and debugging Wolfram System Modeler models
npx -y skills add WolframResearch/system-modeler-ai-toolkit --skill wolfram-language-modelicaAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Simulate and analyze Modelica models from within Wolfram Language / a notebook (WSM SystemModel* functions, WSMRealTimeSimulate) — extract numerical results and make custom plots. Use this skill whenever the user wants to work with a Modelica model inside WL / a notebook — running parameter sweeps, pulling time series into Wolfram arrays for analysis (e.g. AnomalyDetection, Predict, Classify, SystemModelCalibrate), validating against requirements (SystemModelValidate, Monte Carlo, uncertainty bands), training surrogates, making custom plots, or driving a live simulation interactively (pause/resume, change inputs or parameters mid-run, agent/LLM-in-the-loop). Triggers on phrases like 'simulate this model in WL', 'get the results into Wolfram Language', 'extract a time series', 'SystemModelSimulate', 'SystemModelPlot', 'sweep a parameter', 'validate against requirements', 'stays within limits', 'show me any violations', 'SystemModelValidate', 'failure plot', 'uncertainty plot', 'train a surrogate', 'real-time simulation', 'interactive simulation', 'change inputs while it runs', 'WSMRealTimeSimulate'.
SKILL.md
35.3 KB, as published. Nobody here has run it
System Modeling with Wolfram Language
This skill covers how to drive Modelica simulation from Wolfram Language using the built-in SystemModel* family. Use this when the downstream work lives in WL, for example, analysis of simulated data, calibration, optimization, surrogates, custom plots.
For pure command-line simulation / validation with no WL work afterward, prefer the simulate-modelica or validate-modelica skills — they are faster because they avoid kernel startup and WL context.
When to use which
| Goal | Use |
|---|---|
Does this .mo compile? | validate-modelica |
| Run a sim, get pass/fail + log | simulate-modelica |
| Debug torn systems, stiff init, blocks | diagnose-modelica |
Pull time series into WL for Predict / AnomalyDetection / Fit | this skill |
SystemModelCalibrate, SystemModelParametricSimulate | this skill |
| Requirement validation, uncertainty bands, surrogates | this skill |
| Live simulation you can pause / poke inputs & parameters mid-run | this skill (WSMRealTimeSimulate) |
Official reference docs (LLM-friendly variant)
Every page of the official Wolfram documentation has an LLM-friendly Markdown
variant — append .en.md to the page URL. Fetch these on demand when you need
the full signature, all options, or more examples than this skill carries:
- Hub:
https://reference.wolfram.com/language/guide/SystemModelingOverview.en.md - Any
SystemModel*function:https://reference.wolfram.com/language/ref/<Name>.en.md(e.g.SystemModelSimulate.en.md,SystemModelCalibrate.en.md,SystemModelValidate.en.md) WSMLink`(real-time) functions:https://reference.wolfram.com/system-modeler/WSMLink/ref/<Name>.en.md
The reference pages carry signatures and options but not the operational gotchas in this skill — those are verified against live kernels, and several (silent failures, headless-vs-notebook differences) are documented nowhere else. Where this skill and a doc example conflict in a headless/agent context, trust this skill.
Prerequisites — Wolfram Language + the Wolfram MCP server
Everything in this skill is Wolfram Language code (SystemModelSimulate,
SystemModelPlot, SystemModelCalibrate, the requirement language, …). To run
it the assistant needs a way to evaluate WL, which means two things:
- Wolfram Language 14.3 or later — Mathematica or Wolfram|One (the
SystemModel*simulation functions are not included in the free Wolfram Engine). - A C++ compiler.
SystemModelSimulatecompiles each model to a native executable before running it, so a working compiler toolchain must be available. Check (and fix) from within WL:SystemModel; (* Trigger loading of the system modeling functionality *) SystemModelConfiguration`VerifyCompiler[] (* -> <|"Success" -> True|> when a working compiler is found *) SystemModelConfiguration`InstallCompiler[] (* installs / configures a compiler if the check fails *) - The Wolfram MCP server connected to your agent, so the assistant can evaluate that WL on your machine.
Check before installing. First see whether Wolfram MCP tools (e.g. a
WolframLanguageEvaluator) are already available in this session. If they are,
skip setup and go straight to the workflow below.
Install — same on Windows, macOS, and Linux. In any Wolfram front end (a
Mathematica notebook or wolframscript), evaluate:
PacletInstall["Wolfram/AgentTools"];
Needs["Wolfram`AgentTools`"];
InstallMCPServer["ClaudeCode"] (* or "ClaudeDesktop", "Cursor", … for other clients *)
This writes the server entry into the MCP client's config and runs the kernel locally — no API key, nothing leaves the machine. Then fully restart the client (quit completely; closing the window often just minimizes it to the tray/menu bar).
If anything goes wrong — the paclet won't install, the server doesn't show up in the client after restart, or evaluations fail — point the user to the official setup and troubleshooting page: https://www.wolfram.com/artificial-intelligence/mcp/local/
If the user doesn't have Wolfram Language 14.3 or later (Mathematica or
Wolfram|One), this skill can't run — point them to the page above and ask whether
they want to install it. Meanwhile, the command-line skills (simulate-modelica,
validate-modelica, diagnose-modelica) need only System Modeler, not WL, and
can cover compile / run / debug in the interim.
Core workflow
1. Load the package
From a file / package directory:
sm = Import["/abs/path/to/package.mo", "MO"] (* or "C:/..." on Windows *)
(* Returns SystemModel["PackageName", True] *)
From a Modelica source string (useful for inline models, templates, or LLM-generated models):
sm = ImportString[
"model Tiny
Real x(start=0);
equation
der(x) = 1 - x;
end Tiny;",
"MO"
]
(* Returns SystemModel["Tiny", True] *)
Notes:
- For multi-file packages, pass the top-level
package.mo. - The return value is a
SystemModel[...]you can pass directly toSystemModelSimulate,SystemModelPlot, etc. - You can also reference any loaded model by its full Modelica path as a string:
"MyPackage.SubPackage.MyModel". - Modelica source always goes through
Import/ImportString—CreateSystemModelis for building models from WL equations and does not accept raw Modelica source (fails withSystemModel::nvr).
2. Simulate
sim = SystemModelSimulate["MyPackage.MyModel", {tmin, tmax}] (* simulates over the simulation interval {tmin, tmax} *)
sim = SystemModelSimulate["MyPackage.MyModel", {"var1", "var2", "var3"}, {tmin, tmax}] (* store results for specific variables, more efficient when only some results are of interest *)
Note: the list controls exactly what is stored — parameters you don't name in
it are dropped too, so sim["ParameterNames"] returns {} unless the list
includes them (e.g. {"var1", "k"}). Simulate without a variable list to keep
everything.
sim = SystemModelSimulate["MyPackage.MyModel", {"var1", "var2", "var3"}, {tmin, tmax}, spec] (* uses Association spec for initial values, parameters and inputs *)
Allowed spec keys:
| Key | Purpose | Example |
|---|---|---|
"ParameterValues" | Override tunable parameters | "ParameterValues" -> {"k" -> 2.5, "m" -> 10} |
"InitialValues" | Override start values | "InitialValues" -> {"x" -> 0.1} |
"Inputs" | Drive top-level inputs | "Inputs" -> {"u" -> (Sin[2 * #] &)} |
sim = SystemModelSimulate["MyPackage.MyModel", {"var1", "var2", "var3"}, {tmin, tmax}, <|"ParameterValues" -> {"k" -> 2.5, "m" -> 10}|>] (* uses the indicated parameter values *)
Pass ProgressReporting -> False — it drops the progress UI, which makes
calls faster and keeps output clean. (Other options like Method for solver
choice are rarely needed; see the SystemModelSimulate reference page if a
model demands a specific solver.)
Parameter sweeps — pass a list for any parameter:
sweep = SystemModelSimulate["MyModel", {0, 10}, <|"ParameterValues" -> {"k" -> {1.0, 2.0, 5.0}}|>]
(* sweep is a list of SystemModelSimulationData objects *)
3. Extract numerical data
SystemModelSimulate returns a SystemModelSimulationData object. Several access patterns:
sim["Properties"] (* list of properties this object supports *)
sim["VariableNames"] (* list all time-dependent variables *)
sim["ParameterNames"] (* list all parameters *)
(* An InterpolatingFunction or a Function over the simulation interval: *)
f = sim["var1"];
f[500.0] (* value of variable at t = 500.0 *)
(* Values at a single time: *)
sim[{"var1", "var2", "var3"}, t] (* list of variable values at t *)
sim[{"var1", "var2", "var3"}, 500.0] (* list of variable values at 500.0 *)
(* Values at a list of times: *)
sim[{"var1", "var2", "var3"}, {500.0, 700.0}] (* list of lists: values at 500.0 and 700.0 for each variable *)
(* Raw time/value pairs (faster than interpolation). Repeated times indicate events: *)
sim["RawData", {"var1", "var2"}]
(* All variables as a rules list, this is time consuming for large number of stored variables *)
sim["VariableValues"]
(* Association of parameter values, initial values and inputs used in the simulation call*)
sim["Configuration"]
4. Plot
SystemModelPlot[sim] (* default plots stored in the model — errors with SystemModelPlot::nov if the model has no stored plots; pass a variable list in that case *)
SystemModelPlot[sim, {"x", "y"}] (* plot specific variables *)
SystemModelPlot[{simA, simB, simC}, {"x"}] (* compare results for several simulations — auto legend *)
SystemModelPlot[model, ...] (* simulate + plot in one shot *)
Useful options: PlotLegends, PlotStyle, Filling, TargetUnits, ScalingFunctions.
For a custom plot with Plot or ParametricPlot:
(* with Plot *)
Plot[Evaluate[sim[{"x", "y"}, t]], {t, tmin, tmax}, PlotLegends -> {"x", "y"}]
(* with ParametricPlot *)
ParametricPlot[Evaluate[sim[{"x", "y"}, t]], {t, tmin, tmax}]
(Note: using Evaluate is important so sim[...] is resolved once, not at every plot point.)
5. Resample onto a uniform grid (common for ML)
vars = {"var1", "var2", "var3"};
ts = Range[tmin, tmax, dt];
mat = Transpose[sim[vars, ts]];
(* mat has Dimensions {Length[ts], Length[vars]} — ready for AnomalyDetection etc. *)
Canonical end-to-end example
(* 1. Load *)
Import["/path/to/pkg/package.mo", "MO"]; (* use the OS-appropriate absolute path *)
(* 2. Simulate three scenarios *)
scenarios = {"pkg.Baseline", "pkg.Scenario1", "pkg.Scenario2"};
sims = SystemModelSimulate[#, {0, 1000}, ProgressReporting -> False] & /@ scenarios;
(* 3. Inspect what's in there *)
Take[First[sims]["VariableNames"], UpTo[10]]
(* 4. Extract a 3-variable time series from each scenario *)
variables = {"mIn.m_flow", "pTee.p", "mA.m_flow"};
ts = Range[0, 1000, 2.];
series = Through[sims[variables, ts]];
(* series is an array, has Dimensions {Length[scenarios], Length[variables], Length[ts]}, can be indexed with Part *)
(* 5. Compare with SystemModelPlot *)
SystemModelPlot[sims, variables]
(* 6. Downstream: anomaly detection trained on baseline *)
detector = AnomalyDetection[Transpose[series[[1]]]];
anomalyScores = detector[Transpose[series[[2]]], "RarerProbability"];
Gotchas
-
Printing
SystemModelSimulationDatainside a List dumps every variable name. A rawsimobject displays compactly, but wrapping it in aList(e.g. evaluating{sim1, sim2}as the output of a cell) strips the compact box form and you get the entire variable-name list printed inline — hundreds of kilobytes of output per simulation. Fix: end the assignment cell with;, then either pass the list directly into consumers (SystemModelPlot[sims, ...], extractions) without displaying it, or produce a compact summary:KeyTake[sim1["Summary"], {"ModelName", "SimulationInterval", "VariableValues"}] (* single simulation sim1 *) "NumberOfSimulations" -> Length[sims] (* list of simulations sims *) -
AnomalyDetection[...]has no"AnomalyProbability"property. The continuous score is called"RarerProbability", and its polarity is the opposite of what the name "anomaly probability" suggests: it is high (→ 1) for typical samples and low (→ 0) for anomalies. For an intuitive "higher = more anomalous" score, compute1 - detector[x, "RarerProbability"]. For a hard yes/no,detector[x](or"Decision") is already polarity-correct. -
SystemModelValidationData[...]["FirstFailureTime"]and other properties return a Dataset, not a number. On passing scenarios it is an empty Dataset; on failing scenarios it wraps the scalar in a row that also includes an emptyConfigurationcolumn. Extract cleanly before displaying:prop = "FirstFailureTime"; d = SystemModelValidationData[...][prop]; With[{n = Normal[d, Dataset]}, If[n === {}, "\[LongDash]", First[n][prop]] (* "FirstFailureTime" for the first failure configuration, if there is one *) ]; -
Variable name mismatches. Modelica uses dots:
pipe1.port_a.p. Protected / mangled names can appear. Always runsim["VariableNames"]when in doubt instead of guessing. -
.matfiles from a kernel simulation aren't readable viasim[...]directly. Those come from the WSM kernel simulation. To read them in WL, load viaSystemModelSimulationData[path]— and even then, the file path must still exist. Prefer going throughSystemModelSimulateend-to-end when you need data in WL. -
Load
WSMLink`in its own evaluation, before any code that uses it. WL binds symbols at parse time, so a single evaluation containing bothNeeds["WSMLink`"]andWSMRealTimeSimulate[...]createsGlobal`WSMRealTimeSimulatebefore the package loads, and the call returns unevaluated (Symbol::undefined). Evaluate theNeedson its own first, then the code. If loading itself emits messages (e.g.Set::write: Tag ... is Protected), the load went wrong — don't dismiss it; restart the kernel and load cleanly, and report it if it persists. -
Unit annotations.
SystemModelPlotrespects Modelicaunitannotations; variables without units show raw numbers.
Requirements and validation
For anomaly detection, fault diagnosis, or safety-case work, Wolfram ships a
requirement language plus SystemModelValidate that expresses assertions
directly over the simulated trajectory — no scaffolding needed.
The requirement language
Temporal operators that wrap a predicate over a free time variable t:
| Operator | Meaning |
|---|---|
SystemModelAlways[t, texpr] | texpr holds for every t in the validation interval |
SystemModelAlways[t, cond, texpr] | texpr holds whenever cond[t] is true (scoped "always") |
SystemModelEventually[t, texpr] | texpr holds at some t |
SystemModelUntil[...] | Hold until another condition fires |
SystemModelSustain[...] | Hold continuously for at least a given duration |
SystemModelDelay[...] | Shift a condition in time |
Predicates compose with <, <=, >, >=, ==, &&, ||, !, etc.,
and reference any variable from the model by bracket syntax var[t], or parameter as par.
Calling SystemModelValidate
(* Against a live model *)
val = SystemModelValidate[model, req]
val = SystemModelValidate[model, req, spec]
(* Against an already-computed SystemModelSimulationData *)
val = SystemModelValidate[sim, req]
(* Direct property extraction *)
SystemModelValidate[sys, req, "FailureIntervals"]
Unlike SystemModelSimulate, SystemModelValidate does not accept a bare
{tmin, tmax} as a positional argument — a simulation interval must be passed
inside spec as "SimulationInterval" -> {tmin, tmax}. Passing {tmin, tmax}
positionally leaves the call unevaluated.
Names in "ParameterValues" must match the model's parameters exactly, or the
call fails with SystemModelValidate::pvf ("not among the expected ones").
When unsure, list them first with model["ParameterNames"].
Method -> {"InterpolationPoints" -> n} evaluates the requirement on an
n-point time grid — reported failure times snap to grid points, and fewer
points mean less post-processing on large sweeps.
spec is an Association with any of:
<|
"InitialValues" -> {v1 -> val1, ...},
"ParameterValues" -> {p1 -> val1, ...}, (* accepts lists / intervals / distributions *)
"Inputs" -> {in1 -> fun1, ...},
"SimulationInterval" -> {tmin, tmax},
"SimulationCount" -> 200, (* for stochastic sweeps *)
"MaxFailureIntervals"-> Automatic
|>
SystemModelValidationData properties
| Property | Returns |
|---|---|
"SuccessProbability" | Overall pass/fail or probability across a sweep |
"FailureIntervals" | Time intervals where the requirement was violated |
"FirstFailureTime" | First t where a failure occurred |
"Configuration" | Parameter/initial-value/inputs that produced a failure |
"FailurePlot" | Pass/fail step chart over time (index selects the failure configuration, e.g. val["FailurePlot", 1]) |
"FailureIntervalsPlot" | Time-window diagram of failure intervals |
For a plot of the variable itself against its requirement bound with the failure intervals shaded, validate an existing simulation and combine trajectory, bound and intervals:
requirementFailurePlot[sim_, var_, bound_, val_] :=
Module[{ints, ymin, ymax, pad, tspan},
ints = Join @@ (#FailureIntervals & /@ Normal[val["FailureIntervals"]]);
{ymin, ymax} = MinMax[Join[sim[var]["ValuesOnGrid"], {bound}]];
pad = 0.08 (ymax - ymin);
tspan = sim["SimulationInterval"];
SystemModelPlot[sim, var,
Prolog -> {Opacity[0.12], Red,
Rectangle[{Min[#], ymin - pad}, {Max[#], ymax + pad}] & /@ ints},
Epilog -> {Dashed, Red, Line[{{tspan[[1]], bound}, {tspan[[2]], bound}}]},
PlotRange -> {ymin - pad, ymax + pad}]]
sim = SystemModelSimulate[model, 1000];
val = SystemModelValidate[sim, SystemModelAlways[t, "tank1.level"[t] < 0.45]];
requirementFailurePlot[sim, "tank1.level", 0.45, val]
This is the single-run form (validate the sim you plot); for a sweep, pick
one configuration's intervals instead of joining all rows.
Uncertainty bands
SystemModelUncertaintyPlot[sys, spec] plots median and quantile bands over a
stochastic sweep. It takes the same uncertainty spec as SystemModelValidate
("ParameterValues", "SimulationCount", ...), with the plotted variables
under "Outputs":
SystemModelUncertaintyPlot[model, <|
"Outputs" -> {"tank1.level"},
"ParameterValues" -> {"inflow.qFlow" -> NormalDistribution[0.001, 0.0001]},
"SimulationCount" -> 200|>,
Method -> <|"SimulationMethod" -> {"InterpolationPoints" -> 25}|>]
It is a two-argument form: passing variables positionally leaves the call
unevaluated. Its interpolation-points setting nests under the
"SimulationMethod" key (unlike SystemModelValidate's flat Method list).
Surrogate models for fast repeated evaluation
When a workflow needs many evaluations across a parameter range (optimization, Pareto sweeps, interactive exploration), train a surrogate once and evaluate it in milliseconds instead of resimulating:
sur = SystemModelSurrogateTrain[model, <|
"Outputs" -> {"tank1.level", "tank2.level"},
"ParameterValues" -> {"inflow.qFlow" -> CenteredInterval[0.001, 0.0002]},
"SimulationCount" -> 200, "SimulationInterval" -> {0, 1000},
"ReservoirNeuronsCount" -> 100, "InterpolationPoints" -> 100|>];
{level1Fun, level2Fun} = sur["ParametricFunction"]; (* one function per output *)
(level1Fun @@ {0.0011})[500] (* trajectory value at t = 500 *)
- Same spec family as
SystemModelValidate/SystemModelUncertaintyPlot, plus training hyperparameters as spec keys:"ReservoirNeuronsCount","InterpolationPoints","CentroidsCount","CholeskyFactorDensity". Bigger reservoirs are not automatically better — sweep reservoir size × sample count and pick the knee ofsur["MeanLoss"](a per-output table). - For multi-parameter design spaces, pass an explicit joint-sample matrix:
"ParameterValues" -> {{"p1", "p2"} -> samples}(n×d rows, e.g. Latin Hypercube). Training then runs exactly those n samples — omit"SimulationCount". - With one output,
"ParametricFunction"is a single function; with several, a list in"Outputs"order. Call asfun @@ point; the result is a trajectoryInterpolatingFunctionof time. - Surrogates serialize:
Export["sur.wxf", sur, "WXF"],Importback — train once, reuse across sessions. - If the downstream use is scalar objectives (cycle time, integrated losses,
peaks), compute them as variables inside the Modelica model and include
them in
"Outputs"— applying quadrature or root-finding to surrogate-predicted trajectories compounds error. - Other properties:
"SystemModel"(use like any model),"Net","SimulationCount", error plots"RMSEPlot"/"MAEPlot"/"RRMSEPlot". Check an error plot (or spot-check against a real simulation) before trusting a surrogate.
Why this matters for anomaly detection
A physics-based requirement is already a detector. Two patterns worth knowing:
1. Mass / energy balance as a hard requirement. In a pipe network:
leakFree = SystemModelAlways[t, Abs["mIn.m_flow"[t] - "mA.m_flow"[t] - "mB.m_flow"[t]] < 0.5];
val = SystemModelValidate[sim, leakFree];
val["ValidationSucceeded"]
(* True / False - flags a leak instantly *)
val["FailureIntervals"]
(* Dataset with failure intervals *)
A detector you can explain to a CIO in one sentence, derived from first principles, with zero training data.
2. Physics-based baseline envelope. For signatures that don't come from a conservation law, run the healthy model once, build a tolerance band around each channel, and validate future runs against it.
Do not splice a raw InterpolatingFunction (i.e. simBaseline["pTee.p", t]
or simBaseline["pTee.p"]) directly into the requirement — the validator
walks the requirement expression looking for model variables, descends into the
InterpolatingFunction's packed-array internals, and fails with SystemModel::nvr
("Developer\PackedArrayForm" is not a valid identifier) / SystemModelValidate::bms. Instead, capture the baseline trajectory in a NumericQ`-guarded wrapper function so the validator treats it as opaque and
only ever evaluates it at numeric sample times:
simBaseline = SystemModelSimulate["pkg.Baseline", {0, T}];
simScenario1 = SystemModelSimulate["pkg.Scenario1", {0, T}];
ref = simBaseline["pTee.p"]; (* the InterpolatingFunction *)
gref[tt_?NumericQ] := ref[tt]; (* opaque numeric-guarded reference *)
req = SystemModelAlways[t, Abs["pTee.p"[t] - gref[t]] < 0.05 * Abs[gref[t]]];
val = SystemModelValidate[simScenario1, req];
val["ValidationSucceeded"]
When to use what
| Situation | Use |
|---|---|
| A conservation law or a clean inequality defines "healthy" | Requirement + SystemModelValidate |
| Signature is multivariate / subtle / data-driven | AnomalyDetection on extracted channels |
| Need both a yes/no certificate AND continuous anomaly scoring | Layer them — requirement for the hard gate, AnomalyDetection for early warning |
| Hardware-in-the-loop, formal safety case | Requirement language — it's the proof artifact you hand to auditors |
The combination is the Wolfram story: physics-aware requirements set the floor (no false negatives for known faults), statistical anomaly detection catches the unknown ones.
Parameter calibration (fitting a model to measured data)
SystemModelCalibrate takes measured data, a model, and a list of parameters
to tune, and returns a model with calibrated parameter values, or, if specified
a CalibratedSystemModel object with information on the fitted values,
residuals, and prediction bands.
Signature
SystemModelCalibrate[data, model, params]
SystemModelCalibrate[data, model, params, spec]
SystemModelCalibrate[..., "prop"] (* direct property extraction *)
The bare SystemModelCalibrate[data, model, params] form returns a calibrated
SystemModel (ready to simulate), not an information object — so
cal["CalibratedParameters"] / cal["RMSE"] on it will not resolve. To get
the fitted values, residuals, or bands, either use the direct property form
SystemModelCalibrate[..., "CalibratedParameters"] or grab the info object once
with SystemModelCalibrate[..., "CalibratedSystemModel"] and query that.
The measured data's time span must be consistent with the model's simulation
interval, otherwise calibration fails with SystemModelCalibrate::bvs. Pass
spec with "SimulationInterval" -> {tmin, tmax} (or supply data only within
the model's interval).
datais anAssociationmapping the measured variable name to a list of{t_i, y_i}pairs:<|"sensor.p" -> {{0., 1.2e5}, {0.1, 1.4e5}, ...}|>. Multiple variables can be calibrated against simultaneously by adding more entries to the association.modelis aSystemModel[...]or model-name string.paramsis a list of parameter names or{name, initial_guess}pairs:{{"compressor.a0", 100000.}, {"compressor.a1", 1000.}}. Initial guesses far from the truth slow convergence but don't prevent it.
Key properties of the result
| Property | What it gives you |
|---|---|
"CalibratedParameters" | The fitted values as a rules list, ready to pass into SystemModelSimulate via "ParameterValues". |
"CalibratedSystemModel" | Information object with properties. |
"ParameterConfidence" | Uncertainty on each fitted parameter. |
"RMSE" | Root-mean-square residual between measured and predicted output. |
"SinglePredictionBandsPlot" | Visual diagnostic: measured vs. predicted with 95% bands. |
Practical tips
- Parameters live on the component, not the testbench. Reference them with
their full Modelica path:
"compressor.a0", not"a0". - Starting guesses matter for robustness. Order-of-magnitude right is fine; being off by a sign will usually still converge but slowly. Bad starting guesses with narrow parameter bounds is where calibrations get stuck.
Interactive exploration with sliders (SystemModelManipulate)
For a notebook Manipulate over model parameters and initial values —
sliders that re-simulate and re-plot on release — use the Function Repository
resource:
ResourceFunction["SystemModelManipulate"][model, {"inflow.qFlow", 0.0005, 0.0015}]
With no control spec it exposes all top-level parameters and initial values;
controls follow Manipulate syntax ({u, umin, umax}, {{u, uinit}, ...},
discrete value lists). Its defaults are pre-tuned so slow models stay stable
(ContinuousAction -> False, SynchronousUpdating -> False — simulate on
slider release, not on every drag tick). Prefer this over hand-rolling
Manipulate[SystemModelPlot[SystemModelSimulate[...]], ...], which with
default Manipulate settings re-simulates on every drag tick. First use
fetches the resource from the cloud.
Real-time interactive simulation (WSMRealTimeSimulate)
For a live simulation you interact with while it runs — pause/resume, change
inputs or parameters mid-run, read variable values at the current sim time —
use WSMRealTimeSimulate from the WSMLink` package instead of the batch
SystemModelSimulate. This is the right tool for turn-based / agent-in-the-loop
setups (an LLM or optimizer making sequential decisions against a running
model), hardware-style demos, and injecting disturbances mid-run. It avoids the
extract-final-state / re-initialize plumbing that chunked batch simulation
would need: hidden states carry over automatically because the simulation never
stops.
Works in standalone Wolfram Language — no separate System Modeler installation
is needed. The model must already be loaded (e.g. via Import[..., "MO"]).
Recipe
Needs["WSMLink`"];
conn = WSMRealTimeSimulate["MyModel", stopTime,
Method -> {"WriteSimulationData" -> True, (* log the trajectory for later retrieval *)
"SimulationRate" -> 10}] (* 10 sim-seconds per wall-second *)
(* conn is a WSMSimulationConnection; the simulator runs as a separate local process *)
conn["Start"]; (* run *)
WSMLink`Simulate`SetInputs[conn, "u" -> 2]; (* change a top-level input, effective immediately *)
Pause[3]; (* let it run 3 wall-seconds = 30 sim-seconds *)
conn["Pause"]; (* freeze sim time *)
conn[{"y", "x"}] (* read live values at current sim time *)
WSMLink`Simulate`SetParameters[conn, "k" -> 5]; (* parameters can change mid-run too *)
conn["Start"]; (* resume — states carried over *)
...
conn["Stop"];
sd = Quiet@Check[conn["SimulationData"], $Failed]; (* best-effort — unreliable headless, see gotchas *)
If[sd =!= $Failed, WSMPlot[sd, {"y"}]];
conn["Close"]; (* kill the simulator process when done *)
Other useful pieces: WSMLink`Simulate`SetInputs[conn, "u" -> Function[...], RefreshRate -> 0.5]
(input driven by a WL function evaluated periodically),
WSMLink`Simulate`AddDataMonitor[conn, f, vars] (callback when variables
update — event triggers like "cash < 0 → stop"), and WSMRealTimePlot for a
continuously updating plot in a notebook front end.
Turn-based agent loop (the pattern that works)
(* one turn: run a fixed chunk of sim time, then hand control to the agent *)
conn["Start"]; Pause[turnWallSeconds]; conn["Pause"]; Pause[0.3]; (* let pause settle *)
now = First[conn[{"clk"}]]; (* ACTUAL sim time — see gotchas: never assume it *)
obs = conn[{"revenue", "cash"}]; (* observables for the agent *)
(* ... agent decides ... *)
WSMLink`Simulate`SetInputs[conn, {"price" -> p, "adBudget" -> a}];
(* next turn *)
The connection object persists in the kernel session, so an agent driving the
loop through separate evaluator calls (e.g. over MCP) works — create conn
once, keep using it across calls.
Driving the connection headless — do it this way
The notebook front end papers over several connection behaviors that surface when WSMLink is driven headless (e.g. over MCP). These rules keep the loop solid:
- Read everything through live variable reads.
conn[{"y", ...}]is reliable headless. Don't branch onconn["Time"]orconn["State"]— they return stale values ("Time"stuck near 0;"State"reporting"Running"after a successfulPause/Stop, or"NotStarted"while the simulation is demonstrably running). - For sim time, put a clock state in the model and live-read it. Plain
time, or an alias liketclock = time, is alias-eliminated and reads back as0.over the connection. A genuine state works:Real clk(start = 0, fixed = true); equation der(clk) = 1; // clk == sim time, live-readable via conn[{"clk"}] - Timestamp each turn from
clk, never by arithmetic. Each Start→Pause window overshoots by roughly 10–50 ms of wall-clock command latency, which becomeslatency × SimulationRateof extra sim time (measured: +0.03–0.05 sim-s per window at rate 1; up to +4 sim-s at rate 100). The error is always positive, per-window jitter, and does not accumulate beyond that — but it meansnTurns × Pause × ratedrifts from the truth. Readclkback after each pause instead. - Collect the trajectory yourself while the simulation runs — log the
observables you need at each turn with live reads, or accumulate them with
WSMLink`Simulate`AddDataMonitor.conn["SimulationData"]is unreliable headless: it always fails while paused mid-run, and often fails withWSMSimulationConnection::noseven afterconn["Stop"]or natural completion, regardless of"WriteSimulationData" -> Trueor a stored-vars list at creation. Treat a successful retrieval as a bonus, not a plan. - Expose every decision knob as a top-level
input.SetInputstargets only declared top-levelinputvariables; knobs buried inside components (time tables, ramp/pulse sources) must first be promoted to top-levelinputs in a model variant.
Common follow-on tools
Once data is in WL, the useful downstream functions are:
| Tool | Use |
|---|---|
SystemModelValidate + SystemModelAlways / SystemModelEventually | Requirement-based validation (mass balance, envelopes, safety bounds) |
SystemModelUncertaintyPlot | Median/quantile uncertainty bands from stochastic parameter sweeps |
SystemModelSurrogateTrain | Fast trained surrogate for repeated evaluation across a parameter range |
ResourceFunction["SystemModelManipulate"] | Slider-driven interactive exploration of parameters/initial values |
AnomalyDetection | Statistical detector trained on baseline residuals |
Predict / Classify | Supervised models trained on simulated scenarios |
FindFit / NonlinearModelFit | Parameter identification from data |
ModelFit | Fit/select empirical models on extracted data, with cross-validation |
SystemModelCalibrate | Direct Modelica-aware calibration against measurements |
SystemModelParametricSimulate | Pre-compiled parametric simulator (fast sweeps) |
SystemModelLinearize | Extract a linear model around an operating point |
PredictorMeasurements | Evaluate model performance |
ModelFit usage notes
ModelFit distills sweep or trajectory data into an empirical formula — e.g.
fitting steady-state level vs. flow recovers the valve's quadratic law:
ModelFit[data, {PolynomialModel[1], PolynomialModel[2], PowerModel[]},
{"Name", "Expression", "RSquared"}]
Two traps:
ModelFit[data, model]returns the fitted model as a callable function. Request properties in the third argument —fit["Expression"]does not query a property, it silently evaluates the model at the string.- For model selection, pass model objects (
PolynomialModel[2],PowerModel[], ...). A list of string names ({"Linear", "Quadratic"}) fails withModelFit::nofiteven on clean data; string names work only in the single-model form.
Writing results to disk
(* Native WL — fast round-trip, keeps InterpolatingFunctions *)
DumpSave["sim.mx", sim];
Get["sim.mx"]; (* restores the symbol `sim` in place — do NOT write `sim = Get[...]` *)
(* Portable / CSV — for handoff to Python etc. *)
Export["sim.csv", sim[variables, times]];
Do not write sim = Get["sim.mx"]. Get on a DumpSave file restores the
saved symbol (sim) in place and itself returns Null, so the assignment
would immediately clobber the just-restored value with Null. Just evaluate
Get["sim.mx"] (or << "sim.mx") on its own — sim is valid afterward.