|
FhSim
3.1.0
Marine systems simulation
|
The FhSim FMU (Functional Mock-up Unit) tool makes it possible to export an FhSim model as a stand-alone FMU, see https://fmi-standard.org/. In particular, you can define a set of SimObjects in an FhSim configuration file and export the model, which then bundles necessary libraries and resources in an FMU. The exported FMU implements the Co-Simulation interface of either FMI 2.0 or FMI 3.0; --fmi-version {2,3} picks which, and the default is 2. What each version can and cannot do is set out in What the exported FMU can and cannot do.
FhFmuExport is the program that exports an FhSim model file as a stand-alone FMU.
FhFmuExport is a tool to pack an fhsim model file into an FMU with the required binaries to run the model in an FMU Co-Simulation environment. An FMU is a collection of binaries and resources needed to execute a simulation and communicate over a predefined interface. The folder hierarchy of an FMU is as follows:
The two FMI versions name that platform directory differently, and the exporter follows whichever version you asked for:
| FMI version | Directory naming | Examples |
|---|---|---|
| 2.0 | operating system plus word size | linux64, win64, linux32, win32, darwin64 |
| 3.0 | processor architecture and operating system, joined by a hyphen | x86_64-linux, x86_64-windows, aarch64-linux, x86_64-darwin |
There is no requirement for a complete set of binaries as long as binaries for the platform in use is present. Missing binaries will only influence the ability to exchange FMUs with users of different platforms. This tool only export binaries for the platform it was compiled for. Note that we only actively maintain the 64 bit version of FhSim.
The tool parses the FhSim input file and collects a list of all dynamic libraries used (in the LibName attribute of the Lib elements in the input file). The tool creates the folder structure for the FMU and creates an installation directory for FhSim, which includes the base executables and libraries for simulation and visualization. Everything executable lands under binaries/<platform>/:
| What | Where it lands | When |
|---|---|---|
The FMI slave module (fhFMUDll for FMI 2.0, fhFMU3Dll for FMI 3.0) | binaries/<platform>/ | always |
The engine libraries fhSimDll and, with visualization, fhVisDll | binaries/<platform>/ | always |
| The Ogre rendering libraries and their plugins | binaries/<platform>/, flat | unless --no-bundled-vis |
The Assimp mesh-importer plugin Codec_Assimp | beside the other Ogre plugins | only with --with-codec-assimp |
| The SimObject libraries the model actually uses | binaries/<platform>/SimObjectLibraries/ | always |
A license file named license.lic | binaries/<platform>/license.lic | with --include-license-file |
Beside them, every FMU carries its own documentation:
| What | Where it lands | When |
|---|---|---|
interface.html, the model's inputs, outputs and parameters as a table | documentation/interface.html | always — written by the exporter, unless --documentation supplied one |
index.html, the entry point an FMI importer offers the reader | documentation/index.html | always — a placeholder pointing at the table, unless --documentation supplied one |
Whatever else --documentation names: prose, images, further pages | documentation/, keeping its own tree | with --documentation |
FMI 2.0 §2.2.1 and FMI 3.0 §2.5.1.1 both reserve documentation/ for the human-readable description of a model and name documentation/index.html as its entry point. The interface table is written here because this is what knows the interface; everything a reader wants beyond it — what the model is for, who publishes it, under what licence — is not in the model file, so it comes in through --documentation. A file whose name begins with a dot is not copied, because an FMU that carries one fails tests/fmu/CheckFmuArchive.cmake, and neither is a symbolic link. See The documentation an FMU carries.
Only the SimObject libraries the model refers to are copied, not the whole playpen. The copy is not best-effort: if any library named by a LibName attribute cannot be found in the playpen's SimObjectLibraries directory, the export fails rather than producing an FMU that would break on the host's first Instantiate — see Exit codes.
resources/plugins.cfg names are decided together. Ogre throws when it cannot load a plugin the file lists, so leaving Codec_Assimp out of the archive also comments its Plugin=Codec_Assimp line out of the bundled plugins.cfg, and --with-codec-assimp restores both. The plugins.cfg inside the FMU is a rewritten copy in either case — its PluginFolder is repointed at the bundled binaries/<platform> directory, which Ogre reads relative to the resources/ directory the file sits in — so editing the playpen's own copy after an export changes nothing.binaries/<platform>/, beside the Ogre libraries, and not in an OGRE/ subdirectory as the playpen keeps them. A SimObject module bundled at binaries/<platform>/SimObjectLibraries/ was linked in the playpen, with the playpen's RUNPATH, and the one entry of that RUNPATH which resolves inside an FMU is $ORIGIN/.. — the platform directory. The ogre3d package puts the Ogre plugins on the link line, so a module can carry a DT_NEEDED on one; the dynamic loader resolves that entry before anything in the module runs, and it never reads plugins.cfg. A plugin in a subdirectory is therefore invisible to it, however the config file spells the path and whether or not the file is in the archive (FHSIM-0009).The tool then collects all SimObjects of type ExternalLinkStandard used for data exchange during simulation.
ExternalLinkStandard object's input ports are inputs to the object and therefore outputs of FhSim, so they become FMU outputs; its output ports are outputs of the object, inputs to FhSim, and become FMU inputs. Reading the attribute names as if they were FMI causalities is the single most common source of confusion when first inspecting a generated modelDescription.xml.Those ports become FMU variables of the version's real type — Real in FMI 2.0, Float64 in FMI 3.0. They are not the only variables the FMU carries: the built-in and model-supplied parameters described in Parameters the exported FMU carries add Boolean, String and real-typed variables too. How the variables are named and numbered is set out in Variables, names and value references.
The mapping between the FMU value reference and the FhSim ‘'object’:'port'pair is stored in the fileiomapping.xmlin the FMU resources folder, and is what the FhSim Co-Simulation implementation —fhFMUDllfor FMI 2.0,fhFMU3Dllfor FMI 3.0 — reads at run time to decide which FhSim port a value reference refers to. That file also carries an *instantiation token*, a generated identifier the FMI 3.0 model description repeats: it lets the runtime refuse aresourcesdirectory that does not belong to the modelDescription.xml‘ the host read, which is what a stale unpacked FMU or two exports’ files mixed by hand look like.
The FhSim model file is stored as model.xml in the FMU resources folder, and the appropriate FMU modelDescription.xml file is written to the FMU folder hierarchy.
Finally, the tool compresses the folder into an FMU (a zipped archive with the .fmu extension). The archive is written in-process with the bundled miniz library, so no external zip program is needed. Pass --no-compression to store the entries uncompressed, which is faster and useful in CI.
FhFmuExport finds the FhSim installation to bundle from the directory its own executable sits in — the SimObjectLibraries beside it, and the lib/ (or bin/) and resources/ directories one level up. The working directory plays no part in that search; it only decides where the output directory and the .fmu file are written. Running the exporter from inside a playpen's bin therefore works because that is where the executable is, not because of the cd.The table below provides a short description of available command line options for FhFmuExport.
| Program option | Short | Default | Description |
|---|---|---|---|
--input-file | -i | required | The FhSim input file defining the model to export as an FMU |
--model-name | -n | fhsimexport | The name of the FMU. Also the name of the output directory and of <name>.fmu |
--fmi-version | — | 2 | FMI version of the exported FMU: 2 or 3. Picks the modelDescription.xml format, the binaries/<platform> directory name and which slave module is bundled. Any other value is an error |
--author-name | -a | SINTEF Ocean | The author of the FMU |
--description | -d | Model exported from FhSim with FhFMUExport | Description of the FMU |
--include-license-file | -l | none | Path to a license file to include in the FMU. The file is copied and renamed to binaries/<platform>/license.lic, whatever it was called |
--documentation | — | none | A directory copied into the FMU's documentation/ folder, keeping its own tree. Supply an index.html here and it becomes the entry point an importer offers the reader, in place of the placeholder the exporter would write. Files whose name begins with a dot are left behind, and so are symbolic links, which are not followed. A path that is not a directory is an error, because an export asked for documentation that quietly produced none is the mistake this flag exists to prevent |
--add-dir-to-resources, --add-res-dir | -p | none | Copy a directory — or a single file — into the FMU resource folder to make it available for the FMU. A directory keeps its own leaf name, so data/textures lands as resources/textures. May be repeated |
--autocopy | -c | off | Auto copy files and folders referenced in the FhSim model attributes into the FMU. Every relative path in an attribute is resolved against the FhSim installation the exporter runs from — the bin its own executable sits in, the directory FhSim runs a model from — and not against the working directory, so the same model exports the same FMU from anywhere. The copies go into resources/auto_copied/, each under its own last path component, and references in the FMU's own fhsim model file are rewritten to point at the bundled copies. A file already inside the playpen's resources/ is bundled too: only part of that tree travels into the FMU, so a reference left in place would not have reached a bundled copy |
--debug | -D | off | Build the FMU from debug binaries, useful for debugging in single threaded mode in Common Simulation Platform. |
--no-compression | — | off | Store the FMU archive uncompressed. Faster packaging; useful for CI and testing. |
--no-bundled-vis | — | off | Skip bundling the visualization/Ogre binaries in the FMU, and export the visualization parameter with a start value of false, so the FMU never declares a renderer it does not carry. Useful for CI and testing where visualization is disabled |
--with-codec-assimp | — | off | Bundle the Assimp mesh-importer Ogre plugin. It is left out by default because it is the largest single file the renderer carries — about 26 MB of an export, counting the versioned file and the symlink beside it, both of which are stored in full. Pass it when a model's visualization loads meshes in a format only Assimp reads |
--guess-input-interface | — | off | Use constant valued source ports (example: "0,0,1") as input to the FMU. A new ExternalLinkStandard object named fmiInput is created for input to the FMU and connected to the previously constant ports. Initial values are preserved, and the generated variables are named after that object |
--guess-output-interface | — | none | Point to a previously generated fhsim output file to make outputs for the FMU. A new ExternalLinkStandard object named fmiOutput is created for outputs and the ports in the output file are connected to it, so the generated variables are named after that object. This requires that only port outputs are used in the example output file; no state outputs are allowed |
--verbosity | -v | off | Print progress detail while exporting. This is not a version flag: FhFmuExport has no --version. |
--help | -h | — | Display the help menu |
--input-file is the only required option, and it may be given at most once, as may every other option except --add-dir-to-resources.off is a switch that is simply absent unless you pass it, while none is an option that takes a value and contributes nothing when no value is given.fmiInput and fmiOutput, become the object part of every variable name they generate — so --guess-input-interface produces FMU inputs called fmiInput.<port>[<i>]. There is no option to choose a different name.To print a help message type: FhFmuExport --help and you will get:
fhFMUDll must come from a configuration with visualization. A functional procedure is thus to unzip a playpen with visualization enabled into a non-visualization playpen.FhFmuExport reports every failure through its exit status, so a build script or CI job does not have to parse its output.
| Code | Meaning | What to do |
|---|---|---|
| 0 | The FMU was written. | — |
| 1 | Bad command line, an FhSim input file that could not be loaded or parsed, a malformed port size or initial value, a refusal to overwrite the output directory, or a failure while writing the archive. The message on standard error says which. | Read the message; it names the file and the reason. |
| 10 | The FhSim installation beside the exporter is incomplete: the slave module, or both the visualization and the non-visualization engine library, could not be found. | Export from a complete playpen, and remember that the search starts from the exporter's own directory. |
| 11 | The SimObject library list is incomplete — at least one library named by a LibName attribute is not in the playpen's SimObjectLibraries directory. | The preceding message names the two file names that were tried. Build or copy that library into the playpen. |
FHSIM_FMI3_FMU, which is on by default. In a build configured with -DFHSIM_FMI3_FMU=OFF the module does not exist, so --fmi-version 3 would package an FMU with no slave in it. Nothing in the exporter detects that, so check the option before trusting an FMI 3.0 export from an unfamiliar build.Exporting an FMU and uploading it is slow, and most builds change nothing about it. --print-build-key prints a digest of everything that decides the FMU's content and exports nothing:
Every export also writes that digest beside the archive, as <model>.fmu.buildkey. Upload the two together, and the next build can ask whether there is anything new to publish:
| Covered | Not covered |
|---|---|
| The model input file, byte for byte | The author (-a), the description (-d), the verbosity |
The name and content of every binary the FMU would carry: the slave module, the engine libraries, the Ogre libraries and plugins, and each SimObject library the model names, and of every file --documentation would bundle | The compression flag (--no-compression), which changes the archive's encoding and not its content |
The options that change what is bundled: --fmi-version, -D/--debug, --no-bundled-vis, --with-codec-assimp, -c/--autocopy, -p/--add-res-dir, -n/--model-name, -l/--include-license-file, --documentation | The content of the playpen resources/ tree, of an --add-res-dir directory, or of a file --autocopy pulls in — only the fact that they were asked for |
A change to something in the right-hand column does not change the key, which is the point: a build that alters only a description string should not cost an upload.
A bundled library's identity is the digest of the file. Nothing records a SimObject library's version — a module exports only getSimObject, and neither the playpen nor the build keeps a manifest — so the file is the only identity there is. That is exact, and it is stable as long as the libraries come from a package cache. A pipeline that recompiles them from unchanged sources gets a new file, and therefore a new key, for no real change.
Where a stable identity is known, fold it in with --build-key-extra, which is repeatable:
The key is then whatever those strings and the model file say, and survives a rebuild.
--fmi-version is part of the key, and the two versions bundle different slave modules, so an FMI 2.0 and an FMI 3.0 export of the same model always have different keys and are skipped or re-exported independently. They do collide on disk: both write <model>.fmu and <model>.fmu.buildkey from -n, so export each version under its own name or into its own directory, exactly as the test exports do.
modelDescription.xml. It answers only the question it exists for: would exporting again produce a different FMU?A host sees the FMU only through its modelDescription.xml: a flat list of variables, each with a name it can show a user and a numeric value reference it uses in every get and set call. Both are generated, and knowing the rules is what lets you predict a variable's name before you have exported anything — which you need, for instance, to write the headless start values in Running an FhSim FMU in an automated host.
Names. Every variable name ends in a bracketed element index, because even a single-element signal is exported as a one-element array.
<Object>.<Port>[<i>], with the FhSim SimObject name, the port name on that object, and the 0-based element index within the signal.<Object>[<i>] — there is no port segment, because a parameter maps to no port.Value references. There is a single 0-based counter, shared by every data type. It is not one counter per type, as some FMI tooling leads one to expect: value reference 0 is one particular variable in the whole FMU, not "the first Real" and also "the first
Boolean". The counter is allocated in port-mapping order — the three built-in parameters first, then the model's VARIABLES entries, then each ExternalLinkStandard object's FMU outputs and then its FMU inputs — and each mapping consumes as many consecutive numbers as its signal has elements. In FMI 3.0 the required time variable takes the next free number after every mapped scalar.
The two files must agree. The runtime resolves a value reference to an FhSim port through resources/iomapping.xml alone; modelDescription.xml is what the host reads. Editing either file by hand therefore breaks the FMU, and does so silently: the host would read and write ports other than the ones it named, with no diagnostic. Re-export instead.
**variableNamingConvention** is "structured" only when every generated name conforms to the structured-name grammar the FMI specifications define — which means every object and port name it concatenates must start with a non-digit and contain only letters, digits and underscores. Nothing validates the names your input file supplies, so one SimObject called Net 1 or main-body makes the whole model description fall back to "flat". Hosts that offer to group variables into a tree rely on this attribute, so name objects and ports conservatively if that matters to you.
The FirstOrder test model in this repository is small enough to enumerate completely. It holds one first-order SimObject and one ExternalLinkStandard for data exchange:
Exported, it becomes exactly six variables — five in FMI 2.0, and those five plus time in FMI 3.0:
| Value reference | Variable name | FMI 2.0 type | FMI 3.0 type | Causality | Where it comes from |
|---|---|---|---|---|---|
| 0 | visualization[0] | Boolean | Boolean | parameter, fixed — or local, constant, when the renderer is bundled | built in |
| 1 | state_file[0] | String | String | parameter, fixed | built in |
| 2 | integrator_timestep[0] | Real | Float64 | parameter, fixed | <StepControl Step="0.01">, so its start value is 0.01 |
| 3 | ExtLink.y[0] | Real | Float64 | output | ExtLink's inputPortNames="y" — an input to the object is an output of the FMU |
| 4 | ExtLink.u[0] | Real | Float64 | input | ExtLink's outputPortNames="u", with Initial_u="2.0" as its start value |
| 5 | time | — | Float64 | independent | FMI 3.0 only; the first number no mapping covers |
Note that the object name ExtLink and the port names are reproduced verbatim, that y and u swap sides relative to the FhSim attribute they came from, and that the parameters carry no port segment in their names.
An FMU is a file somebody downloads. Everything else in it is written for a program — modelDescription.xml for the importer, resources/iomapping.xml for the runtime, binaries/ for the loader — so documentation/ is the only part of an archive a person reads. FMI reserves it, and names documentation/index.html as the entry point an importer offers (FMI 2.0 §2.2.1, FMI 3.0 §2.5.1.1).
documentation/interface.html lists every variable the FMU exposes, grouped into inputs, outputs and parameters, with the type, the value reference and the start value the model states. The names and types are the ones modelDescription.xml uses, because both are written from the same port mappings — a table that renamed a port would send a reader looking for something no host can address.
documentation/index.html is the entry point. Without --documentation it is a placeholder that says the archive carries no description and links to the interface table, so that the standard's entry point is always present and always leads somewhere.
Everything under doc/NetCage is copied into the archive's documentation/, keeping its own tree. An index.html in it becomes the entry point, in place of the placeholder, and an interface.html in it is kept in place of the generated table. Write it for a reader who has unpacked the archive on a machine with no network: an absolute URL still works where there is one, but a stylesheet, a font or an image fetched from elsewhere leaves the page broken exactly where it is meant to be read. Reference images by relative path and let them travel in the same directory.
Two rules are worth knowing before you fill the directory:
.git directory left behind would otherwise fail tests/fmu/CheckFmuArchive.cmake, which refuses an archive carrying a build-harness artefact.tests/fmu/CheckFmuDocumentation.cmake holds an exported archive to both halves of this: the entry point is there, and every relative src/href in a bundled page names a file the archive really carries. That second check is the one that catches the realistic mistake — whether an image was copied into the archive is decided by the export, not by the author, so a page that renders perfectly in the source tree can arrive with every image broken.
Every FMU FhFmuExport writes carries three built-in parameters, whatever the model. They are declared fixed, which in FMI means they may be written while the instance is in Instantiated or Initialization Mode and not afterwards; the FMU reads all three once, at ExitInitializationMode, on its way to building the engine.
The exception is visualization in an FMU that bundles the renderer. There it is written as a local constant and cannot be set at all, because such an archive carries only the Vis-suffixed SimObject modules: the engine a false would select cannot resolve them, and initialisation fails with Simobject library file not found. Rather than offer a setting that cannot work, the export states the fact. Running such an FMU without a window is visible=false at instantiation, which works and is described below.
| Name | Type | Start value | Effect |
|---|---|---|---|
visualization[0] | Boolean | true when the renderer was bundled, false for --no-bundled-vis or a playpen without fhVisDll | Whether the FMU loads the visualization engine and opens a render window. Settable only when it is false, i.e. when there is no renderer to turn off; see Running an FhSim FMU in an automated host |
state_file[0] | String | the model's InitialStatesFile, or empty | The initial-states file to load, overriding whatever the model named |
integrator_timestep[0] | Real / Float64 | the model's <StepControl Step="..."> | The integrator step size the run uses. It is not the communication step size, which the master chooses independently |
In addition, every <Var> entry in the model's <VARIABLES> block becomes a fixed FMU parameter. Its name is the variable's Name attribute, and its type is decided by its Value: Real/Float64 when every comma-separated token parses as a number, String otherwise. A value with several comma-separated tokens becomes a parameter with that many elements, named <Name>[0], <Name>[1] and so on.
This makes the VARIABLES block the model author's control over the FMU's parameter surface: anything you want a co-simulation host to be able to set, express as a $Variable in the model and give it a <Var> entry. Anything you leave as a literal attribute value is baked into the exported model.xml and cannot be changed from outside.
fixed rather than tunable for a reason a host cannot see: FhSim applies a VARIABLES value by patching the model file it is about to load, which happens once, at ExitInitializationMode. A tunable declaration would invite a host to write one in Step Mode and the write would never reach the engine. The FMU refuses such a write outright instead — see FMU export and co-simulation.The FMU implements Co-Simulation only. There is no Model Exchange export, and FMI 3.0's Event Mode is not offered either: the FMU declares hasEventMode="false".
FhSim also has no FMU import. Nothing in FhSim can load a third-party FMU as a SimObject; the traffic is one-way, out of FhSim into someone else's master.
The FMU declares canBeInstantiatedOnlyOncePerProcess="true", and it means it. Loading the FMU twice in one process is not supported, and neither is loading two FhSim FMUs there: the engine reads several of its inputs relative to the process working directory, so the slave changes the whole process's working directory while it builds the model. A second instance initializing at the same time would see the first one's directory. Run each FhSim FMU in its own process — with Open Simulation Platform that means proxyfmu, as in the cosim example further down this page. (The FMU says so itself: it logs not running in proxyfmu, could cause segfaults/crashes due to library conflicts! whenever the process that loaded it is not proxyfmu. Outside OSP, with one FhSim FMU in the process, that warning is expected and harmless.)
GetFMUState, SetFMUState and the serialize/deserialize pair are all supported and declared. Three caveats apply:
FhIntegrator single-step family — including the adaptive RK45_i — whose next step depends on nothing beyond the time, the state vector and the step size, all three of which the snapshot carries. CVode and ARKODE keep adaptive internal step and multistep order/history that the snapshot cannot reach, and restoring a time discards whatever history existed. If you need state rollback, configure a single-step method.Both versions provide directional derivatives; FMI 3.0 additionally provides adjoint derivatives, which FMI 2.0 has no entry point for.
Both derivative calls are Step Mode only. A query in Initialization Mode is refused, because the engine does not build the model until ExitInitializationMode returns and there is nothing to differentiate before that.
An FMI 3.0 host gets Float64, Int32, Boolean and String — the four type families FhSim exchanges. Every other family, Float32 and the 8-, 16- and 64-bit integers, the unsigned integers, Binary and Clock, is refused with fmi3Error. A host that probes for them will see an error rather than a crash, and the instance stays usable.
The model description carries a <DefaultExperiment> read from the model's own <Timing TStart TEnd> and <Integrator>/<StepControl Step RelTol>. A model written in the legacy pre-v3 form has neither element, so its FMU carries an empty <DefaultExperiment> and the host must be told the horizon itself.
fmi2Discard/fmi3Discard and a last-successful time at the stop time. A master that walks past stopTime therefore gets a refusal it can act on, not a success at a point that was never simulated.integrator_timestep[0] and the question does not arise.All parameters are fixed, so a parameter write is refused once Step Mode has been entered, with an error that names the value reference. Inputs stay writable throughout, as co-simulation requires.
An unknown value reference is a clean error: the call fails, the instance is not poisoned, and the run continues. So is visible=false at instantiation, which suppresses the render window even when the visualization parameter is true — FMI defines visible as "open no windows", and an instantiation-time argument is not something a bundled default should override.
Suppose you have created an FhSim configure file awesomeness.xml and want to export an FMU for this model. You have a working FhSim installation (also known as FhSim playpen) located at /opt/fhsim_awesome/playpen/bin. This installation is FhSim with visualization, where your license.lic file is located in the bin folder. The following commands will create an FMU:
On Windows the same invocation is a cmd session, where the line continuation is ^ and not \:
This will produce a folder awesomeness and awesomeness.fmu next to each other in the directory you ran from. The cd chooses where the output lands and nothing else: the playpen the FMU is built from is the one beside the FhFmuExport executable.
Add --fmi-version 3 to get an FMI 3.0 FMU instead. A model needed in both versions is exported twice, under two different --model-name values, because one output directory cannot hold both layouts.
awesomeness.xml needs further third-party libraries beyond those FhSim itself brings, those transitive dependencies are not discovered and not bundled; you must add them to the awesomeness folder and re-compress it into an FMU yourself. (Tip: .fmu is really a .zip.) A missing SimObject library is a different matter — that is detected and fails the export with exit code 11, rather than producing a broken FMU.An exported FMU defaults to visualization on whenever the renderer was bundled with it. In a CI job, a container, a batch sweep or any host with no display that is not what you want: the FMU tries to open a render window, and ExitInitializationMode fails if it cannot.
Instantiate with visible=false. FMI defines this as "open no windows", and the FMU honours it whatever visualization says: the rendering engine is loaded, no window is opened, and the run proceeds. This works for every FhSim FMU, it needs no special export, and it is what fmpy does by default — see Running an FhSim FMU with fmpy. It is the measure to reach for when you are handed an FMU you did not export yourself.
Setting visualization[0] false is not an alternative for an FMU that bundles the renderer. The exporter writes the variable as a constant there, so an importer refuses the write; the reason is that such an archive carries only the Vis-suffixed SimObject modules, and the engine a false would select cannot resolve them. Where the renderer was not bundled — --no-bundled-vis, or an install without fhVisDll — the variable is a settable fixed parameter whose start value is already false, so a host that sets nothing runs headless anyway.
fmpy is a Python FMI host that reads the model description, resolves the whole function set and runs the co-simulation lifecycle itself, so it is a good first check that an exported FMU is well formed and gives the right numbers. This walkthrough uses the FirstOrder model enumerated in Variables, names and value references, whose solution is known in closed form: with A = B = 1 and the input held at its start value u = 2, the output is
\[ y(t) = u \left( 1 - e^{-t} \right). \]
Install fmpy (pip install fmpy) and export the model, once per FMI version:
First, validate. validate_fmu returns a list of problems and an empty list means fmpy found nothing to complain about:
Then simulate. fmpy instantiates with visible=False, which is what keeps the run headless; no start_values entry is needed for it. Where the FMU was exported without the renderer, visualization[0] is a settable parameter and is already false.
The run prints the FMU's own reminder that it is not inside proxyfmu — expected with a single FhSim FMU in the process — and then the trajectory, which agrees with the closed form to about 1e-7:
Pointing the same two commands at FirstOrderTest3.fmu gives the same numbers through the FMI 3.0 interface: the model description, the platform directory and the slave module differ, the trajectory does not.
Note the two names you could not have guessed from the model file alone and that the rules in Variables, names and value references give you: visualization[0], the built-in headless switch, and ExtLink.y[0], whose y was an input port name in the FhSim input file.
fmusim, the Modelica Association's reference command-line simulator, and the FMI Compliance Checker (fmu-compliance-checker/2.0.4@sintef/stable as a Conan package), which covers FMI 1 and 2.You can use an FMU simulation engine to run your exported FhSim FMU awesomeness. We recommend Open Simulation Platform Software. For instance, you can use cosim to run awesomeness.fmu.
canBeInstantiatedOnlyOncePerProcess="true". Running two FhSim FMUs in one co-simulation, or one FhSim FMU with visualization alongside cosim itself, therefore needs each of them in its own process: use proxyfmu in the source attribute for each afflicted Simulator, as below. The cause is not leaking library symbols — the FMU module exports only the fmi2*/fmi3* C entry points and nothing else — but the process-wide state the engine depends on, chiefly the working directory the slave changes while it builds the model. That is a property of the engine, not a packaging accident, so proxyfmu is the answer rather than a workaround.Go ahead an create a OspSystemStructure.xml next to your awesomeness.fmu with the following content:
Now, run the simulation scenario for 10 seconds:
Symptoms specific to exporting and running an FhSim FMU — a render window in CI, two FMUs that will not coexist, a refused parameter write, an export that stops with exit code 11 — are catalogued with their causes and fixes in FMU export and co-simulation.