8.3.101-stable Switch to dev

fmi

Load and run FMI Functional Mock-up Units (.fmu) from GCL, built on top of FMI4C.

The library loads an FMU, exposes its model description, and drives a Co-Simulation (instantiate → initialize → step → read/write variables → terminate). FMI 1.0, 2.0 and 3.0 are all supported through one version-agnostic API: the FMU’s FMI version is detected at load time and every call routes to the matching implementation internally.

@library("fmi", "0.0.0");

Quick start

var fmu = Fmu::load("model.fmu", "inst");
var sim = fmu.instantiate(false);

sim.setup(0.0, 10.0, 0.0);      // startTime, stopTime, tolerance (0 => undefined)
sim.enterInitialization();
// set parameters / initial inputs here, e.g. sim.setReal(6, 0.7);
sim.exitInitialization();

var t = 0.0;
var dt = 0.01;
while (t < 10.0) {
    sim.doStep(t, dt);
    t = t + dt;
    var height = sim.getReal(1);  // read an output by its value reference
    info("t=${t} h=${height}");
}

sim.terminate();

Loading and inspecting an FMU

Fmu::load(path, instanceName) unzips and parses the FMU. The unzip happens in-process, so no external unzip tool is required. It throws a runtime error if the file cannot be loaded.

var fmu = Fmu::load("model.fmu", "inst");
fmu.version();          // FmiVersion::fmi1 | fmi2 | fmi3
fmu.modelName();        // String
fmu.modelIdentifier();  // String (co-simulation binary base name)
fmu.guid();             // String (FMI3: the instantiation token)
fmu.variableCount();    // int
fmu.variables();        // Array<FmuVariable>

Each FmuVariable is a plain snapshot object of the model-description entry:

type FmuVariable {
    name: String;
    valueReference: int;          // the handle used by get*/set* below
    causality: FmiCausality;      // input | output | parameter | ...
    variability: FmiVariability;  // constant | fixed | tunable | discrete | continuous
    dataType: FmiDataType;        // real | integer | boolean | string | ...
}

Variables are addressed by their integer value reference, which you read from variables(). Use it with the get*/set* methods below.

Running a co-simulation

Fmu::instantiate(loggingOn) creates a Co-Simulation instance. Passing loggingOn = true forwards the FMU’s own debug logging to the GreyCat logger (visible with --log=trace). The instance keeps its parent Fmu alive, so the Fmu may go out of scope while a FmuInstance is still in use.

Initialization is a three-phase sequence mirroring the FMI standard. Set parameters and initial inputs between enterInitialization and exitInitialization:

var sim = fmu.instantiate(false);
sim.setup(0.0, 10.0, 1e-6);   // startTime, stopTime, tolerance
sim.enterInitialization();
sim.setReal(6, 0.7);          // e.g. set a parameter
sim.exitInitialization();
  • setup(startTime, stopTime, tolerance)tolerance <= 0 means “tolerance undefined”; stopTime <= startTime means “stop time undefined”.
  • doStep(currentTime, stepSize) — advance the simulation by one communication step. Returns an FmiStatus.
  • terminate() — end the simulation.

Every stepping/IO method returns an FmiStatus (ok, warning, discard, error, fatal, pending). A production loop should check it:

if (sim.doStep(t, dt) != FmiStatus::ok) {
    // handle discard / error
}

Reading and writing variables

Scalar accessors take a value reference; the setters return an FmiStatus:

var h = sim.getReal(1);          // float
sim.setReal(6, 0.7);             // FmiStatus

sim.getInt(vr);   sim.setInt(vr, 3);
sim.getBool(vr);  sim.setBool(vr, true);
sim.getString(vr); sim.setString(vr, "text");

Batch accessors read or write several variables in one call. For reads the result has one entry per value reference, in order; for writes the two arrays must be the same length:

var vals = sim.getRealArray(Array<int> { 1, 3 });   // Array<float>
sim.setRealArray(Array<int> { 1, 3 }, Array<float> { 1.0, 0.0 });

sim.getIntArray(vrs);    sim.setIntArray(vrs, values);
sim.getBoolArray(vrs);   sim.setBoolArray(vrs, values);
sim.getStringArray(vrs); sim.setStringArray(vrs, values);

For FMI 3 FMUs, getReal/getInt (and their array forms) automatically dispatch on each variable’s declared width — a getReal works whether the variable is Float32 or Float64, and a getInt works for any of the Int8Int64 / UInt8UInt64 types, widening the result to a GCL int. Note that a UInt64 value above int range is truncated.

Version differences handled for you

concept FMI 1 FMI 2 FMI 3
initialize initializeSlave on exit setupExperiment + enter/exit enter/exit initialization
step fmi1_doStep fmi2_doStep fmi3_doStep
real type Real Real Float64/Float32
integer type Integer Integer Int8UInt64

The same GCL calls work across all three; the differences above are applied internally based on the loaded FMU’s version.

Scope

  • Co-Simulation only. Model Exchange (bring-your-own solver) is not exposed.
  • FMU state save/restore, derivatives, and event handling are not exposed.