|
FhSim
3.1.0
Marine systems simulation
|
This guide covers all breaking changes when upgrading from FhSim 2.x (main branch) to FhSim 3.0 (new_structure branch). Most downstream consumers are SimObject plugin libraries — the guide is structured with this audience first.
An automated migration script is available: see Using the migration script below.
v3 API stability. FhSim 3.0 is the current major version. The v3 XML input schema and the SimObject C++ API documented here are the stable target you should build against — the churn described in this guide is the one-time 2.x→3.0 break, not an ongoing state. Any future breaking change will be recorded in this guide with a Before/After mapping (per the project's deprecation policy) and announced on the issue tracker; retired names and attributes are also enforced by the documentation checks. There is no separate deprecation schedule beyond that.
FhSim 3.0 introduces:
FhSim::fhsim CMake target is split into purpose-built components. SimObject plugin authors now link the lighter FhSim::simobject target instead of the full engine.C prefix is removed from core utility classes (e.g., CPrintDuringExec → PrintDuringExec).CPrintDuringExec.h → PrintDuringExec.h).<INTEGRATION> is renamed to <SIMULATION> (backward compatible with deprecation warning). The <Engine> and <Sundials> child elements are replaced by a new unified <Integrator Method="..."> element with structured sub-elements.OdeFcn is now const and no longer takes isMajorTimeStep. Once-per-accepted-step side effects should move to AcceptedStep(double T, const double* X).This section covers the typical consumer: a downstream library that defines SimObject subclasses and is compiled as a shared plugin (MODULE).
Replace FhSim::fhsim with FhSim::simobject in your CMakeLists.txt:
Before (v2):
After (v3):
The FhSim::simobject target provides everything a SimObject plugin needs: the SimObject base class, port definitions, ISimObjectCreator, utilities, and transitive dependencies (FhLib, libxml2, Eigen, and optionally Ogre/OIS).
In your conanfile.py or conanfile.txt, bump the FhSim version:
Before:
After:
Note: the first release on the 3.x line is 3.1.0, not 3.0.0 —
fhsim_base/3.0.0is an already-released reference on the 2.x line. Use the caret range[^3.1.0]rather than an exact pin.
Ensure your Conan profile has compiler.cppstd=20.
The following headers have been renamed (the C prefix is removed):
Old #include | New #include |
|---|---|
#include "CPrintDuringExec.h" | #include <fhsim/PrintDuringExec.h> |
#include "CFhSimMgr.h" | #include <fhsim/FhSimMgr.h> |
#include "CExceptions.h" | #include <fhsim/Exceptions.h> |
#include "CAttributePath.h" | #include <fhsim/AttributePath.h> |
#include "CInputReader.h" | #include <fhsim/InputReader.h> |
Headers that are unchanged (no action needed): SimObjectInclude.h, SimObject.h, ISimObjectCreator.h, NFhSim.h, ISimObjectLogger.h, ICommonComputation.h, ISignalPort.h, IStateUpdater.h, KalmanObject.h, ExternalLink.h, PortDefs.h, PortStructDefs.h.
Note:
dllLib.hwas renamed toDllLib.h. If your SimObject DLL entry point file includes it, update to#include <fhsim/DllLib.h>.
Apply these find-and-replace operations across your C++ source files:
| Old class name | New class name |
|---|---|
CPrintDuringExec | PrintDuringExec |
CFhSimMgr | FhSimMgr |
CExceptions | Exceptions |
CAttributePath | AttributePath |
CInputReader | InputReader |
Classes that are unchanged (no action needed): SimObject, ExternalLink, KalmanObject, ISimObjectCreator, ICommonComputation, ISignalPort, IStateUpdater, SerialCommonComputation.
The protected member variable that stores the object's registered name was renamed in the SimObject base class:
| Old | New |
|---|---|
m_SimObjectName | m_simObjectName |
This only affects code that accesses this member directly by name. Most code uses the public GetName() accessor, which is unchanged.
v3 removed the using std::string; declaration that v2 had at file scope in ISimObjectCreator.h. Any of your own headers that relied on this transitive using declaration will now fail to compile with ‘'string’ was not declared`.
Fix your headers by qualifying the type explicitly:
Fix your **.cpp implementation files** by adding a using declaration after your includes, or by qualifying at usage sites:
Test stubs and mock ISimObjectCreator classes hit this most often — local Creator subclasses defined inside _Test.cpp files often override GetStringParam, GetDoubleParamArray, AddInport, AddOutportIndexed with bare string parameters. Find all occurrences with:
Then qualify each one as std::string.
If you use FhSim's test utilities:
Before:
After:
Note the change from hyphen (-) to underscore (_).
If your library compiles source files that include visualization headers (FhCamera.h, FhVisualization.h, etc.), you must explicitly link the FhSim::fhsim_vis component, which provides these headers in v3.
In the CMakeLists.txt that builds your SimObject library:
FHSIM_FH_VISUALIZATION is a CMake cache variable set by fhsim-variables.cmake (included automatically when find_package(FhSim) is called) when the fhsim package was built with with_visualization=True.
Include form for visualization headers:
The FhSim::fhsim_vis component registers include/ as its include root. Headers must therefore be included with the full fhsim/ prefix:
Visualization class and shared-resource renames:
| Old | New |
|---|---|
#include <CFhCamera.h> | #include <fhsim/visual/renderer/FhCamera.h> |
CFhCamera* cam | FhCamera* cam |
(CFhCamera*)creator->GetSharedResource("CFhCamera") | (FhCamera*)creator->GetSharedResource("FhCamera") |
Before (v2 SimObject source):
After (v3 SimObject source):
if (isMajorTimeStep) inside OdeFcn(...) should move to AcceptedStep(double T, const double* X).PreOdeFcn(T, X, updater) remains the pre-step hook for discrete updates.ICommonComputation::ComputeFunction(...) is now const. If your implementation updates cached internals, use mutable members.CommonComputation callback typedef now expects a const SimObject member function.IntegratorOptionsCommon and the kSimMgrKeyIntegratorOptions ("IntegratorOptions") registry key were removed with the unified IntegratorConfig. A SimObject that asked the simulation manager for the integrator options now reads fhsim::IntegratorSetup under fhsim::kSimMgrKeyIntegratorSetup:
Removed (IntegratorOptionsCommon) | Replacement (fhsim::IntegratorSetup) |
|---|---|
GetStartTime() | tStart |
GetEndTime() | tEnd |
GetStepSize() | step (the requested fixed step; 0 when the method steps adaptively) |
GetMethod() | method |
method is the canonical v3 method name, so a comparison against a v2 name has to be updated too — "Euler1" becomes "Euler_i" and "Heun" becomes "Heun_i". See the method-name table under `<Engine>` / `<Sundials>` → `<Integrator Method="...">`.
Do not reach for kSimMgrKeyIntegrator in a setup hook. The integrator is built from the finished model, so it is registered only after every SimObject's Init(), FinalSetup() and InitialConditionSetup() have run; the pointer is null in all three and dereferencing it crashes. IFhIntegrator (now public, <fhsim/engine/IFhIntegrator.h>) is for reading the running integrator — current time, step size, diagnostics — from OdeFcn/AcceptedStep onwards.
Migration checklist:
OdeFcn overrides updated to new signature (const, no isMajorTimeStep)AcceptedStep(...)ICommonComputation callbacks updated to const"IntegratorOptions" lookups replaced by fhsim::kSimMgrKeyIntegratorSetupIf your project embeds the full simulation engine (constructing FhSim objects directly), you still link FhSim::fhsim:
The FhSim::fhsim target now depends on FhSim::simobject, so you get everything. Additional steps for engine embedders:
| Old class name | New class name |
|---|---|
CFhSim | FhSim |
CFhRtSim | *(no replacement class — see CFhRtSim is gone: real time is now a pacing mode)* |
CFhSimLicenceManager | FhSimLicenceManager |
CFhIntegrator | CFhIntegrator *(unchanged — engine-internal)* |
Old #include | New #include |
|---|---|
#include "CFhSim.h" | #include <fhsim/FhSim.h> |
#include "CFhRtSim.h" | *(remove — see CFhRtSim is gone: real time is now a pacing mode)* |
There is no real-time class in v3, and no header to include. A v2 project that constructed a CFhRtSim to get a wall-clock-paced run does not get a renamed class — real time became a property of the run instead.
Two things replaced it:
simulationSpeed attribute on the <SIMULATION> element (1.0 = real time, 2.0 = twice real time, 0 = as fast as possible). This is the only route for a headless FhSim run. On the command line, FhVis accepts -s / --simulation-speed and FhSimUI accepts --rtf / --rt; FhSim has no pacing flag. See Real-time pacing.fhsim::RealTimeStateProvider (src/engine/io/RealTimeStateProvider.h) wraps any IStateProvider and sleeps the remaining wall time after each step. If the model cannot keep up it warns and does not sleep — the same behaviour CFhRtSim::Simulate() had. Build it the way SimulationManagerFactory does and hand it to a fhsim::SimulationManager.Note that this is a behavioural move as well as a rename: pacing is applied around the step loop, so it composes with any provider (live integration, <Replay>, <Network>) instead of being a separate simulator subclass.
fhsim::SimulationManager (src/engine/io/SimulationManager.h) is now the one owner of the simulation lifecycle, and the loop methods that duplicated it on the engine facade are gone. FhSim::Step() remains: it advances exactly one integrator step.
| Removed | Replacement |
|---|---|
FhSim::Integrate() | Build a SimulationManager over an IntegratorStateProvider and call Run() (see src/apps/fhExe/main.cpp), or loop FhSim::Step() while it returns simRes_Success. |
FhSim::StepToTFinal() | Same as above. |
FhSim::GetOutput(XOut, TOut) | Register an observer (FileOutputObserver, or fhsim::test::CapturingObserver in tests) — the integrator no longer accumulates a state history. |
FhSim::GetNumOutput() | Same as above. |
FhSim::PrintStateValues() | The <Diagnostics><Settings><LogStates/></Settings></Diagnostics> diagnostic. |
FhVisSDL::IntegrateVis() | SimulationManager::Run() with a visualization observer registered (see src/apps/fhSimUI/FhSimUIMain.cpp). |
IFhIntegrator lost the matching virtuals — StepToTFinal(), GetOutputCopy(), GetNumOutput() and PrintStateValues(). Custom integrators must drop those overrides; Step(), the time-span accessors, the state accessors, LogConfiguration() and GetDiagnostics() are unchanged.
If your application uses the FhSim visualization layer (SDL/Ogre rendering), link the new FhSim::fhsim_vis target:
The <INTEGRATION> XML element has been renamed to <SIMULATION>. The old name is still accepted with a deprecation warning but will be removed in a future release.
The <Engine> and <Sundials> child elements inside <SIMULATION> are removed and replaced by a single unified <Integrator Method="..."> element. <Replay> and <Network> are unchanged.
Old (<Engine IntegratorMethod="N">) | New (<Integrator Method="...">) |
|---|---|
IntegratorMethod="0" (Euler1) | Method="Euler_i" |
IntegratorMethod="1" (StableSolver) | Method="StableSolver_i" |
IntegratorMethod="2" (RKCK45) | Method="RK45_i" |
IntegratorMethod="3" (RKDOPRI54) | Method="DOPRI54_i" |
IntegratorMethod="4" (Heun) | Method="Heun_i" |
IntegratorMethod="5" (RKF45) | Method="RKF45_i" |
IntegratorMethod="6" (BS23) | Method="BS23_i" |
IntegratorMethod="12" (Euler1imp) | Method="BackwardEuler_i" |
Sundials BDF (cvode_method="BDF") | Method="BDF" |
Sundials Adams (cvode_method="Adams") | Method="Adams" |
| ARKode DIRK | Method="DIRK" |
| ARKode ERK | Method="ERK" |
Legacy Engine method names (RKCK45, Euler1imp, Euler1) are accepted as aliases with a deprecation warning. Use the canonical _i-suffixed names for Engine methods and plain names for Sundials methods.
Simulation start and end times move to a <Timing> sibling element:
Step size and tolerance settings move to a <StepControl> child of <Integrator>:
Old attribute (on <Engine> or <Sundials>) | New (<StepControl> attribute) |
|---|---|
dt / stepsize / Dt | Step |
abstol / AbsTol | AbsTol |
reltol / RelTol | RelTol |
HMax | StepMax |
HMin | StepMin |
| (Sundials) initial step | Step0 |
| (Sundials) max steps | MaxSteps |
| Old attribute | New element |
|---|---|
lin_type="SPGMR" | <LinearSolver Type="SPGMR"/> |
jac_type="dense" / jtype="dense" | <Jacobian Type="dense"/> |
<SundialsExtra linear_solver="component"/> (early v3) | <LinearSolver Type="SPGMR" Preconditioner="component"/> |
<SundialsExtra jacobian_policy="hybrid"/> (early v3) | <Jacobian Policy="hybrid"/> |
<LinearSolver Type="component" GsSweeps="N" GsSymmetric="true"/> (early v3) | <LinearSolver Type="component"/> — the two attributes tuned a fallback no model could reach and are now rejected |
The two <SundialsExtra> pseudo-keys selected model-side settings rather than SUNDIALS API calls; they are now rejected with an error that names the replacement attribute.
For advanced Sundials settings not covered by the schema, add a <SundialsExtra> child element with arbitrary attribute-value pairs that are forwarded to the Sundials API:
Before (v2 Engine):
After (v3):
Before (v2 Sundials BDF):
After (v3):
FhSim 3.0 introduces an observer/provider pipeline. Observers can be configured via a new optional <OBSERVERS> XML section:
See OBSERVERS section for full documentation.
| Condition | Behaviour |
|---|---|
Only <SIMULATION> present | ✅ Preferred — no warning |
Only <INTEGRATION> present | ⚠️ Accepted with deprecation warning |
Both <INTEGRATION> and <SIMULATION> present | ❌ Fatal error |
| Neither present | ❌ Fatal error |
<Integrator Method="..."> missing for Engine/Sundials simulations | ❌ Fatal error |
Unknown Method value | ❌ Fatal error |
Legacy method alias (RKCK45, Euler1imp, Euler1) | ⚠️ Accepted with deprecation warning |
Additional strict validation:
<Engine> and <Sundials> inside <SIMULATION> are rejected with a message pointing to <Integrator Method="...">; they are no longer parsed into anything.<Integrator MaxOrder> is rejected: the attribute lives on <StepControl MaxOrder>.<Integrator TOutput> is rejected: the output schedule lives on <OBSERVERS><FileOutput TOutput="..."/></OBSERVERS>. Without an <OBSERVERS> block the -o output file receives every step.<SIMULATION> fail fast.<OBSERVERS> fail fast.FMU note:
<SIMULATION> + <Integrator> when possible. If you hit migration issues in FMU export/import, contact the FhSim team.Most SimObject plugins use the InputReader / XmlDoc abstraction layer for XML access. The interface of these classes is unchanged — you do not need to modify your XML-reading code even though the underlying implementation switched from TinyXML to libxml2.
If your SimObject used TinyXML API directly (e.g., TiXmlElement*, TiXmlDocument), you must switch to the libxml2 API or (preferably) use the InputReader / XmlDoc wrappers instead.
The libxml2 API uses C-style allocation — always free strings returned by xmlGetProp / xmlNodeListGetString with xmlFree().
| TinyXML (v2) | libxml2 (v3) |
|---|---|
#include <tinyxml.h> | #include <libxml/parser.h> + #include <libxml/tree.h> |
TiXmlDocument doc(path); doc.LoadFile() | xmlDocPtr doc = xmlReadFile(path, nullptr, XML_PARSE_NONET); |
doc.ErrorDesc() | No direct equivalent; libxml2 writes to stderr by default |
doc.RootElement() | xmlDocGetRootElement(doc) |
elem->Value() | reinterpret_cast<const char*>(node->name) |
elem->Attribute("x") | xmlChar* v = xmlGetProp(node, BAD_CAST "x"); xmlFree(v); |
elem->FirstChildElement("tag") | Iterate node->children, skip node->type != XML_ELEMENT_NODE |
child->NextSiblingElement() | Iterate node->next, skip non-element nodes |
attr->Name() | reinterpret_cast<const char*>(attr->name) |
attr->Value() | xmlChar* v = xmlNodeListGetString(doc, attr->children, 1); xmlFree(v); |
| (no explicit free needed) | xmlFreeDoc(doc) after all nodes are done |
Also remove tinyxml::tinyxml from any target_link_libraries() call in your CMakeLists.txt — the migration script (--only cmake) does this automatically.
| Old (v2) | New (v3) | Impact on consumers |
|---|---|---|
tinyxml/2.6.2 | libxml2/[~2.13] | Transitive; no action unless you used TinyXML API directly |
sundials/5.4.0 | sundials/7.5.0 | Transitive; only affects engine embedders using Sundials API directly |
| — | eigen/[~5.0.0] | New transitive dependency on simobject target |
| — | sdl/2.32.10 | Only with with_visualization=True |
| — | imgui/[<1.92] | Only with with_visualization=True |
| — | zeromq/4.3.5 | Internal to fhsim; not visible to consumers |
| — | miniz/[~3.0.2] | Internal to fhsim; not visible to consumers |
Ensure your Conan profile specifies:
An automated migration script is provided at scripts/migrate_to_v3.py. It performs header renames, class renames, CMake target updates, and XML element renames across your project.
.cpp, .h, .hpp):#include directives (e.g., "CPrintDuringExec.h" → "PrintDuringExec.h")CPrintDuringExec → PrintDuringExec)CMakeLists.txt, *.cmake):FhSim::fhsim with FhSim::simobject (SimObject target migration)FhSim::fhsim-testtools and FhSim::fhsim_testtools with FhSim::fhsim_test.xml):<INTEGRATION> / </INTEGRATION> with <SIMULATION> / </SIMULATION>conanfile.py, conanfile.txt):The following changes cannot be automated safely and must be done by hand after running the script:
string → std::string in headers** — the regex for word-boundary replacement would produce too many false positives. See Step 5b: Update headers that use unqualified string above.<Engine> / <Sundials> → <Integrator Method="..."> migration** — the script cannot safely automate this transformation. Timing, step-size, tolerance, linear-solver, and Jacobian attributes are restructured into sub-elements, and Engine IntegratorMethod integer codes must be translated to canonical method names. See the XML input file changes section above for the mapping tables and before/after examples.GetSharedResource("CFhCamera") string literal** — string literal content is not renamed by the class-rename pass. Search manually: FhSim::fhsim_vis CMake link guard** — the script does not add new target_link_libraries calls. See Step 7: Link FhSim::fhsim_vis and update visualization includes (if applicable).FhSim::fhsim → FhSim::simobject) handles both the mixed-case form (FhSim::fhsim) and the all-lowercase Conan CMakeDeps alias form (fhsim::fhsim). If you embed the engine rather than authoring a plugin, skip the CMake pass or restore FhSim::fhsim linkage manually where needed.--apply..git/, build/, or node_modules/ directories.