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

The <VARIABLES> section defines named variables that can be referenced throughout the input file using $VarName substitution. This is useful for avoiding repetition and making input files easier to maintain.

Warning
Substitution reaches only four places in the file. Writing $Var in an attribute outside that scope is not an error — the literal text $Var is handed to the numeric parser, which reads it as 0. See Where substitution applies before using a variable.

Syntax

<VARIABLES>
<Var Name="WarpLength" Value="35"/>
<Var Name="AnchorPos" Value="10,0,-50"/>
<Var Name="Stiffness" Value="500"/>
</VARIABLES>

Each <Var> element defines a single variable:

Attribute Required Description
Name Yes Variable name. May contain letters, digits, and underscores. Case-sensitive.
Value Yes Replacement value. Can be any string — numbers, vectors, or text.
Note
The element must be spelled <Var> or <var>. Any other child of <VARIABLES> is ignored without warning. If the same Name is defined twice, the first definition wins.

Using variables

Reference a variable by prefixing its name with $ in any attribute value inside the scope described below. The substitution happens at parse time, before SimObjects are created.

<VARIABLES>
<Var Name="Mass" Value="2000"/>
<Var Name="StartPos" Value="10,0,-5"/>
</VARIABLES>
<OBJECTS>
<Lib LibName="fhsim_base" SimObject="Body/Mass" Name="mass"
Mass="$Mass" Scale="0.7"/>
</OBJECTS>
<INITIALIZATION>
<InitialCondition mass.Pos="$StartPos"/>
</INITIALIZATION>

Where substitution applies

Substitution is applied attribute-by-attribute in exactly four places, and nowhere else:

Where What is substituted
<OBJECTS> Every attribute of every child element — including LibName, SimObject and Name, not just the model parameters.
<INTERCONNECTIONS> Every attribute of every <Connection> element (both port references and constants).
<INITIALIZATION> Every attribute of every <InitialCondition> element.
<SIMULATION> The attributes on the <Integrator> element itself — Method, NumCores, InitialStatesFile, FinalStatesFile.

Everywhere else the $ text survives into the value that the option parser reads. In particular there is no substitution in:

  • <Timing> — TStart and TEnd.
  • The <Integrator> child elements: <StepControl>, <LinearSolver>, <Jacobian>, <Diagnostics> and its <Triggers>/<Settings> subtree, and <SundialsExtra>.
  • <Replay>, <Network>, and the simulationSpeed attribute of <SIMULATION>.
  • The whole <OBSERVERS> section.
  • <FromFile> and its <State> children inside <INITIALIZATION>.
Warning
A numeric attribute outside the scope is read with a C atof-style conversion, which yields 0 for text it cannot parse. So <Timing TEnd="$Duration"/> does not fail — it runs a simulation that ends at t = 0. Put the duration in the XML directly, or have the batch script that sets $Duration rewrite TEnd as well.

Rules and behaviour

  • Case-sensitive: $Mass and $mass are different variables.
  • Variable names: May contain letters (a-z, A-Z), digits (0-9), and underscores (_). The name ends at the first character that is not in this set.
  • Multiple variables in one value: You can use multiple $ references in a single attribute value:

    <Var Name="X" Value="10"/>
    <Var Name="Y" Value="20"/>
    <InitialCondition mass.Pos="$X,$Y,0"/>
    <!-- Expands to: mass.Pos="10,20,0" -->
  • Inline mixing: Variables can appear anywhere in a value:

    <Var Name="Suffix" Value="Vis"/>
    <Lib LibName="fhsim_base$Suffix" SimObject="Body/Mass" Name="mass"/>
    <!-- Expands to: LibName="fhsim_baseVis" -->
  • Undefined variables cause an error: If a $Name reference does not match any defined variable, FhSim reports an error and stops.
  • Substituted text is rescanned: the scan resumes at the start of the text it just inserted, so a variable whose Value itself contains $Other is expanded further. This makes variables composable, but a variable that references itself never terminates — do not write <Var Name="A" Value="$A"/>.

Arithmetic in attribute values

After substitution, values in <OBJECTS>, <INTERCONNECTIONS> and <INITIALIZATION> are passed through an arithmetic evaluator. It supports +, -, *, /, ^ and parentheses, so a value can be written as a calculation instead of a pre-computed number:

<VARIABLES>
<Var Name="X" Value="10"/>
<Var Name="Depth" Value="50"/>
</VARIABLES>
<OBJECTS>
<Lib LibName="fhsim_base" SimObject="Body/Mass" Name="mass"
Mass="2*1000"/>
</OBJECTS>
<INITIALIZATION>
<InitialCondition mass.Pos="$X*2,0,-$Depth/2"/>
<!-- Expands to: mass.Pos="20,0,-25" -->
</INITIALIZATION>

This applies to the three sections above only. <Integrator> attributes get variable substitution but no arithmetic, and nothing outside the four places listed above gets either.

Where the arithmetic happens

<INTERCONNECTIONS> and <INITIALIZATION> carry numbers, so their values are evaluated while the input file is read. An <OBJECTS> attribute is different: it is stored exactly as written, and the arithmetic is evaluated when the SimObject asks for the parameter as a number (GetDoubleParam, GetIntParam and their array forms). A parameter the SimObject reads as text (GetStringParam) is handed over verbatim.

This is what lets an attribute carry structured text. A JSON document, a regular expression or a bracketed coordinate list contains punctuation the evaluator would rewrite — [0.0,3,6] would come out as [0.0,3,6 — so a value is only offered to the evaluator once it is known to be a number. Earlier versions evaluated every object attribute as it was read, and structured text had to be moved to a separate file.

When a value is treated as an expression

The evaluator is deliberately conservative, because a numeric attribute may still carry a matrix, a unit string or an endpoint. The whole value is left untouched when it

  • is empty, or is a single character; or
  • contains a semicolon (;) — the row separator of a matrix parameter, and the marker for a path or unit string.

Otherwise the value is split on commas and each field is evaluated on its own. A field is left untouched when it

  • looks like an IP address — more than one . and none of * / ^ + -; or
  • contains any letter other than e/E (an underscore also counts as a letter here), so Simple/Black and tcp://*:5555 survive; or
  • contains no digit, or contains an e/E before its first digit.

Every field that passes those tests is evaluated — including one that is already a plain number.

The result is written back as the shortest decimal text that reads back as the same double, so an evaluated field keeps every digit it is worth: Mass="1.23456789" stays 1.23456789 and Ratio="1/3" becomes 0.3333333333333333. Scientific notation survives (1e-7 stays 1e-07). Earlier versions wrote six significant digits, which silently rounded both of those.

Note
Unary minus is only recognised at the start of a field, after (, and after +/-. 2*-3 is not an expression the evaluator accepts; write 2*(0-3). A field the evaluator cannot read — 2*-3, 1+, an unbalanced parenthesis — stops the run with an error naming the parameter or the initial condition and quoting the value. It used to crash the process instead.

Typical use cases

Parameterising a scenario for sweep studies

Define key parameters as variables so they can be easily changed (or overridden by batch scripts that rewrite the <VARIABLES> section):

<VARIABLES>
<Var Name="WarpLength" Value="35"/>
<Var Name="TowSpeed" Value="2.5"/>
</VARIABLES>
<INTERCONNECTIONS>
<Connection
winch.WireLength="$WarpLength"
vessel.Velocity="$TowSpeed,0,0"
/>
</INTERCONNECTIONS>

The simulated duration is not a candidate: <Timing TEnd> is outside the substitution scope.

Sharing positions across objects

<VARIABLES>
<Var Name="MountPoint" Value="5,0,3"/>
</VARIABLES>
<INTERCONNECTIONS>
<Connection
cable.PosA="$MountPoint"
sensor.ReferencePoint="$MountPoint"
/>
</INTERCONNECTIONS>

See also