|
FhSim
3.1.0
Marine systems simulation
|
This page explains how to integrate an FhSim simulation into your own C++ application.
You can initialize from:
To exchange data with your host application, include at least one System/ExternalLinkStandard SimObject in your model.
Example model (v3 schema):
The engine is loaded as a shared library (fhSimDll, or fhVisDll when you want the renderer). It exposes a flat C ABI, listed once in fhsim/dll/FhsimDllApi.h. On top of that sits a thin C++ convenience class, FhSimDll (fhsim/dll/DllWrapper.h), which loads the library, resolves every export, owns the opaque context and takes std::string arguments.
The wrapper covers the calls a typical host needs, but not all of them. Where a section below is marked C ABI only, the call has no wrapper method: resolve the exported symbol yourself, or use the function-pointer typedefs the table generates (see fhsim/dll/DllEntry.h). Every export takes the opaque void* context as its first argument.
| Call | Layer | Purpose |
|---|---|---|
InitFromFile | wrapper + C ABI | Build a simulation from an XML model file. |
InitFromString | wrapper + C ABI | Build a simulation from an XML string. |
StopSim | wrapper + C ABI | Stop the run and release the engine's resources. |
FreeFhSimContext | C ABI only | Free the context itself. |
FhSimDll runs StopSim and then FreeFhSimContext from its destructor, so a host that uses the wrapper never calls FreeFhSimContext by hand. A host on the raw C ABI must call both, in that order: StopSim releases the engine, FreeFhSimContext releases the context that still holds the error message.
StopSim() returns true when an error was recorded at any point in the run. This is the inverse of Simulate() and PerformSingleStep(), which return true on success. A true from StopSim() does not mean the stop failed; read the message with GetErrorString().Use the DLL wrapper API (fhsim/dll/DllWrapper.h):
SetInputCopy copies values into internal buffers. Use it when input data lifetime is short or stack-allocated.For full API details, see headers in:
include/fhsim/dll/DllWrapper.hinclude/fhsim/visual/dll/FhVisDllWrapper.h| Call | Layer | Purpose |
|---|---|---|
Simulate(deltaSimTime, maxDeltaRealTime) | wrapper + C ABI | Advance by deltaSimTime of model time, spending at most maxDeltaRealTime wall-clock seconds. |
PerformSingleStep() | C ABI only | Take exactly one integration step, whatever its size. |
GetDesiredSimTime() / SetDesiredSimTime(t) | C ABI only | Read or set the end time Simulate() is working towards. |
GetTCurr() | C ABI only | The current simulation time, without publishing the state vector. |
Simulate() does not run a fixed number of steps. It adds deltaSimTime to an accumulated desired simulation time and then steps until the model reaches that time or the wall-clock budget runs out, whichever comes first. So a Simulate() call that hits the budget leaves the model short of the requested time, and the shortfall carries over: the next call adds to the same accumulator instead of resetting it. GetDesiredSimTime() reports that accumulator and SetDesiredSimTime() overwrites it, which is how a host resynchronises after a step that ran out of budget.
Use PerformSingleStep() when the host wants step-level control — a co-simulation master, or a controller that must see every integrator step — and Simulate() when the host wants to advance a fixed slice of model time per control period.
Every port call addresses a System/ExternalLinkStandard object by name, or by a pair of indices on the fast path.
| Call | Layer | Purpose |
|---|---|---|
GetOutput(object, port) | wrapper + C ABI | Values of a named output port, nullptr if unknown. |
GetOutputSize(object, port) | wrapper + C ABI | Number of doubles in a named output port. |
SetInput(object, port, values) | wrapper + C ABI | Point an input port at caller-owned values. |
SetInputCopy(object, port, values) | wrapper + C ABI | Copy values into the port's own storage. |
GetInputSize(object, port) | wrapper + C ABI | Number of doubles in a named input port. |
GetSimObjectIndex(object) | wrapper + C ABI | Index of an ExternalLink object, -1 if unknown. |
GetSimObjectPortIndex(object, port) | wrapper + C ABI | Index of a port on that object, -1 if unknown. |
GetOutputWithIndex(objectIndex, portIndex) | wrapper + C ABI | Output values by index (fast path). |
GetOutputSizeWithIndex(objectIndex, portIndex) | wrapper + C ABI | Output size by index. |
EvaluateOutputs() | C ABI only | Refresh the output ports without advancing time. |
Query the sizes when port dimensions come from the XML and are not known at compile time:
Resolve the indices once, outside the loop, when the same ports are read every control period:
EvaluateOutputs() re-runs the ExternalLink outputs and drops the port caches at the current (T, X) without taking a step. Use it after SetStates(), or after SetInput() when the host needs to see the model's response to the new inputs before it commits to a step.
| Call | Layer | Purpose |
|---|---|---|
GetStates(&stateVector) | wrapper + C ABI | Publish the state vector; returns the current time. |
SetStates(stateVector, tNew) | C ABI only | Overwrite the state vector and the current time. |
GetNumStates() | wrapper + C ABI | Number of state variables. |
GetStateObjects(&objectNames) | wrapper + C ABI | Owning object name of every state variable. |
GetStateTags(&tagNames) | wrapper + C ABI | Tag name of every state variable. |
For diagnostics or custom logging, you can access the full simulation state. The state array is read-only, so the receiving pointer must be const double*:
To understand which state belongs to which object:
Simulate() or StopSim().SetStates() writes the whole state vector back and sets the current time in one call. It is the entry point for a host that resets, rewinds or re-initialises the model — a co-simulation master rolling back to a saved point, or a filter injecting a corrected estimate. Pass GetNumStates() doubles in the layout GetStateObjects() and GetStateTags() describe; the call does no range checking and does not evaluate outputs, so follow it with EvaluateOutputs() when the host needs the ports to match the new state.
SetStates() has no wrapper method. Call the exported C symbol directly.You can pass pointers between your host application and the simulation via named pointer registration:
To retrieve a pointer registered by a SimObject:
For advanced use cases (custom observers, direct object access), you can retrieve the simulation manager:
The engine can answer directional-derivative questions about the model at its current (T, X): if these input ports move in this direction, how do these output ports move? Both calls are C ABI only — the C++ wrapper has no method for either.
| Call | Layer | Derivative source |
|---|---|---|
GetDirectionalDerivative(...) | C ABI only | Finite differences over the model. |
GetAnalyticalDirectionalDerivative(...) | C ABI only | The Jacobian machinery. |
Both take the outputs as parallel outputObj[] / outputPort[] arrays with a count, the inputs the same way, a seed vector dvKnown[] and a result buffer dvUnknown[]:
The last argument is what separates them:
mode on the finite-difference call selects the direction. Mode 0 is forward: it reads one seed per input and writes one result per output. Mode 1 is adjoint and reverses both. The time differential is the integrator's current step size; the host does not choose it.dt on the analytical call is the time differential the host wants the derivative over, so the host chooses it.GetDirectionalDerivative() locates ports through the states of their owning SimObject. A model whose ports depend on no states — a pure feed-through — therefore answers zero. Use GetAnalyticalDirectionalDerivative() for those.Neither call returns a status. A failure inside the model is recorded on the context, so check GetErrorString() afterwards.
All methods that return bool return false on failure. Retrieve the error message with:
After StopSim() the context is stopped: every query (GetOutput, GetStates, GetNumStates, GetSimMgr, …) returns an empty result — nullptr, 0 or false — and Simulate() returns false. GetErrorString() still reports the error that ended the run, so read it before freeing the context. The index lookups GetSimObjectIndex() and GetSimObjectPortIndex() return -1 for an unknown name and for a stopped or failed context; a valid index is never negative.
The context outlives the engine precisely so the message survives StopSim(). Read it with GetErrorString() before calling FreeFhSimContext() — after that the message is gone. Hosts using the FhSimDll wrapper get the free from the destructor, so the message must be read before the wrapper goes out of scope.
Common failure scenarios:
InitFromFile / InitFromString: XML parsing errors, missing libraries, invalid parameters.Simulate: integrator failure, NaN in state vector.SetInput / SetInputCopy: object or port name not found.See Runtime errors and diagnostics for a complete list of error messages.