FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
OBSERVERS section

The <OBSERVERS> section declares what should happen with simulation output: writing to file, logging to console, streaming over the network, rendering 3D graphics, or loading a custom observer plugin.

Overview

<OBSERVERS>
<FileOutput outputFile="results.csv" Select="objects:all"/>
<ConsoleLog/>
<Network endpoint="tcp://*:5555"/>
<Visualization frameRate="30"/>
<Plugin library="my_plugin.so" config="key=value"/>
</OBSERVERS>

Behaviour when <OBSERVERS> is absent: a FileOutput observer is created automatically if an output file path is configured (via CLI -o or XML). A Visualization observer is created for FhVis when the provider is an integrator.

Behaviour when <OBSERVERS> is present: only the listed observers are created. An empty <OBSERVERS/> tag runs with no observers at all (useful for benchmarking).


<FileOutput> — write CSV results

Writes simulation results to a semicolon-separated CSV file.

Attribute Required Default Description
outputFile No modeloutput.csv Output file path. Relative paths resolve from the working directory. CLI --output-file overrides this.
TOutput No (every step) Output schedule specification — controls when rows are written (see below).
Select No "objects:all" Filter expression selecting which signals to write (see below).

Output scheduling (TOutput)

By default, FileOutput writes one row per integrator step. The TOutput attribute lets you control which time points are written, using interpolation to produce evenly-spaced output independent of the integrator's internal steps.

Supported formats:

Format Description
"start:delta:end" Output from start with interval delta until end.
"start::end" Output every integrator step from start until end.
":delta:end" Output every delta from simulation start until end.
":delta:" Output every delta from simulation start until simulation end.
"start:delta" Output from start with interval delta until simulation end.
"start:end" Output only at start and end times.
"t1,t2,t3,..." Output at specific explicit times (interpolated).
"start:delta:Inf" Inf in the end position means "until simulation end".

Examples:

<!-- Output every 0.1 seconds from t=0 to t=60 -->
<FileOutput outputFile="results.csv" TOutput="0:0.1:60"/>
<!-- Output every 0.5 seconds for the entire simulation -->
<FileOutput outputFile="results.csv" TOutput=":0.5:"/>
<!-- Output at specific times -->
<FileOutput outputFile="results.csv" TOutput="0,1,5,10,30,60"/>
<!-- Output every integrator step between t=10 and t=20 -->
<FileOutput outputFile="results.csv" TOutput="10::20"/>

Inf (any capitalisation) is accepted only in the end position of a colon form — "0:0.1:Inf" and "10:Inf" are valid, "Inf:0.1:60" is not. It is read as the simulation end time, so it is equivalent to leaving the end field empty.

Note
Requested times outside [TStart, TEnd] are dropped. A schedule whose times all fall outside the simulated interval produces no rows.

When TOutput is omitted or empty, every integrator step is written.


Select filter expressions

The Select attribute controls which SimObject signals appear in the output file:

Expression Description
"objects:all" All ports and states of all SimObjects.
"objects:ports" Output ports of all SimObjects.
"objects:states" States of all SimObjects.
"object.spring1:all" All ports and states of the SimObject named spring1.
"object.spring1:ports" Output ports of the SimObject named spring1.
"object.spring1:states" States of the SimObject named spring1.
"object.spring1:port.ForceA" Only the ForceA output port of spring1.
"object.spring1:state.x" Only the x state of spring1.
"union(A, B)" Union of two expressions.
"intersect(A, B)" Intersection of two expressions.
"minus(A, B)" Expression A minus expression B (set difference).

The object selector uses the grammar object.<name>:<atom> or object.<name>:<atom>.<signal>, where <name> is the SimObject instance name (not its type) and <atom> is one of all, ports/port, or states/state. The plural form objects:<atom> selects across every SimObject and takes no name.

Warning
The grammar also has an objects.<Type>:<atom> form, but it cannot be used from <FileOutput Select>. The observer evaluates the expression against the output metadata, which carries no SimObject type, so objects.Mass:all parses without complaint and matches nothing — leaving a CSV with only a Time column. Select by instance name.

An unknown object name or signal name behaves the same way: it selects nothing rather than reporting an error. A malformed expression — a missing bracket, an unknown atom — is an error.

Signal names carry no scalar-index suffix. In a port.<signal> or state.<signal> selector, use the bare signal tag (e.g. Pos, ForceA) — not the _0/_1 scalar-index suffix that appears in the CSV header. A vector signal is selected once by its base name and expands to all its scalar columns automatically.

Examples:

<!-- Write all ports of spring1 AND all states of the SimObject named mass1 -->
<FileOutput outputFile="results.csv"
Select="union(object.spring1:ports, object.mass1:states)"/>
<!-- Write everything EXCEPT ports from the SimObject named mass1 -->
<FileOutput outputFile="results.csv"
Select="minus(objects:all, object.mass1:ports)"/>

CSV output format

The output file uses semicolon-separated values with this layout:

Time;Object1;Object1;Object2;...
;signal_0;signal_1;signal_0;...
1.000000e+00;2.500000e-01;0.000000e+00;-1.200000e+00;...
  • Row 1: Object names, one per scalar column. Time in the first column.
  • Row 2: Signal tag names with scalar index suffix (signalName_0, signalName_1, …). First column is empty.
  • Data rows: One row per simulation step, values in scientific notation.

Column order: ODE state columns first, then port output columns.

This format is also read back by the <Replay> state provider.


<ConsoleLog> — log to console

Logs step index, simulation time, and timing statistics each step.

<ConsoleLog/>

No attributes. Silently skipped when running without a console (e.g. from a DLL context).


<Network> — publish over ZeroMQ

Serialises each simulation snapshot and broadcasts it over a ZeroMQ PUB socket.

Attribute Required Default Description
endpoint Yes ZeroMQ bind endpoint, e.g. tcp://*:5555.

When used as an input provider (a <Network> element inside <SIMULATION>, as in the subscriber example below), it also accepts a timeout attribute (connection timeout in milliseconds, default 5000). The observer form (inside <OBSERVERS>) reads only endpoint.

A metadata handshake socket (REP) is automatically created on TCP port+1, allowing late-joining subscribers to request metadata before subscribing.

Example: endpoint="tcp://*:5555" → data on :5555, metadata on :5556.

Publisher / subscriber example

Publisher (simulation process):

<OBSERVERS>
<FileOutput outputFile="results.csv"/>
<Network endpoint="tcp://*:5555"/>
</OBSERVERS>

Subscriber (separate process):

<SIMULATION>
<Network endpoint="tcp://192.168.1.10:5555" timeout="10000"/>
</SIMULATION>
<OBSERVERS>
<FileOutput outputFile="received.csv"/>
</OBSERVERS>

<Visualization> — 3D rendering (FhVis only)

Renders the simulation in a 3D window. Only available in FhVis builds (compiled with FH_VISUALIZATION).

Attribute Required Default Description
frameRate No 30 Target render frame rate in Hz. CLI --frame-rate overrides this.
sceneManager No "DefaultSceneManager" Ogre3D scene manager type name.
<Visualization frameRate="60"/>

See 3D scene navigation for camera controls and keyboard shortcuts.


<Plugin> — custom observer plugin

Loads a shared library (.so / .dll) at runtime and delegates all observer calls to it.

Attribute Required Default Description
library Yes Shared library filename. Relative paths resolve against observerPlugins/ in the working directory.
config No "" Opaque string forwarded to CreateObserver(config) in the plugin.
<Plugin library="my_plugin.so" config='{"key":"value"}'/>

Writing a plugin

Implement the fhsim::IStateObserver interface and export two C functions:

#include "IStateObserver.h"
#include "IStateObserverPlugin.h"
class MyObserver : public fhsim::IStateObserver
{
public:
void OnSimulationStart(const fhsim::SimMetadata&) override { /* ... */ }
void OnUpdate(const fhsim::SimSnapshot& s) override { /* ... */ }
void OnSimulationEnd(fhsim::ESimRes) override { /* ... */ }
int GetRequiredBufferDepth() const override { return 1; }
};
extern "C" fhsim::IStateObserver* CreateObserver(const char* config)
{
return new MyObserver();
}
extern "C" void DestroyObserver(fhsim::IStateObserver* obs)
{
delete obs;
}

Build as a CMake MODULE library and place the resulting .so/.dll in the observerPlugins/ directory.

To turn SimMetadata::outputColumns into flat column indices, header labels (signal_0, signal_1, …) or (object, signal) lookups, construct a fhsim::OutputView (#include "fhsim/OutputView.h") in OnSimulationStart instead of re-implementing the arithmetic. It also evaluates a Select expression when given one. The example plugin in examples/plugin_observer_file/ shows the pattern.


Results HTML viewer

results.html plots the CSV written by <FileOutput> in a browser: pick the X signal and any number of Y signals, zoom, pan and hover. It is installed next to the executable — playpen/bin/results.html in the playpen layout, or share/fhsim/results.html in a plain install — so in the directory a run writes its results.csv into.

By hand. Open results.html and drop one or more results files onto it (or use the file picker). Several files can be loaded at once and are drawn in the same axes: the Y list is the union of the signal names across them, and every ticked file contributes a trace per selected signal, so the same selection compares two runs directly. Runs sampled on different time grids need no alignment. Selections can be stored as named presets, which are kept in the browser and hold signal names only, so a preset made on one run applies to the next.

From the command line. fhsim_plot writes a copy of the viewer with the data baked in and opens it in the default browser:

fhsim_plot results.csv # -> results_plot.html, opened
fhsim_plot runA/results.csv runB/results.csv # both runs in one plot
fhsim_plot results.csv -o /tmp/run.html --no-open

A diagnostics JSON file is dispatched to the diagnostics viewer instead, so the same command covers both:

fhsim_plot diagnostics.json

The data is baked into the page because a browser refuses to let a page opened from file:// read a file named in its own URL. Opening results.html itself and dropping the file on it is therefore equivalent, and the baked page needs no network and no server.

Note
Both viewers load Plotly from https://cdn.plot.ly. On a machine without internet access, put plotly-2.35.2.min.js next to the viewer and it is used instead.

Path resolution

Every relative path written inside an input file is resolved against the working directory, and FhSim chooses that directory itself at startup — before any XML path is resolved. This is the single rule behind outputFile, <Plugin library>, <Replay csvFile>, JsonOutput, InitialStatesFile / FinalStatesFile, <FromFile FileName> and LibName. Absolute paths are always used as-is.

How the working directory is chosen

At startup FhSim looks for a marker file named FHSIM_DIR_IDENTIFIER and moves into the first place that has one:

  1. The directory the process was launched from. If the marker is there, nothing changes.
  2. The directory containing the FhSim executable.
  3. The directory named by the FHSIM_DIR environment variable.

If none of them has the marker, the launch directory is kept and a message says so. The move is silent in normal output: when a marker sits next to the executable, running FhSim from an arbitrary directory writes its results next to the binary instead of where you started. Run with -c 4 (the highest console verbosity) to see which directories were searched and which one was chosen.

Note
Two paths are resolved before the move and are therefore relative to the directory you launched from: the input XML file named on the command line, and an output file given with -o / --output-file. Everything named inside the XML is relative to the directory chosen above. When in doubt, use absolute paths, or run FhSim from the directory that holds the marker file.
<working-dir>/ ← the directory holding FHSIM_DIR_IDENTIFIER
FHSIM_DIR_IDENTIFIER
model.xml
results.csv ← output files
SimObjectLibraries/
libfhsim_base.so ← SimObject libraries (LibName)
observerPlugins/
my_plugin.so ← observer plugin libraries

See also