This section contains the interface description to access the in/output data and status information of a co-simulation slave from a C program.
Input and output variables and variables are transferred via the fmi3GetXXX
and fmi3SetXXX
functions, defined in [get-and-set-variable-values].
In order to enable the slave to interpolate the continuous real inputs between communication steps, the derivatives of the inputs with respect to time can be provided.
Also, higher derivatives can be set to allow higher order interpolation.
Whether a slave is able to interpolate and therefore needs this information is provided by the capability attribute canInterpolateInputs
.
link:../headers/fmi3FunctionTypes.h[role=include]
Sets the n-th time derivative of real input variables.
Argument vr
is a vector of value references that define the variables whose derivatives shall be set.
The array order
contains the orders of the respective derivative (1 means the first derivative, 0 is not allowed).
Argument "value" is a vector with the values of the derivatives.
nValueReferences
is the dimension of the vectors.
Restrictions on using the function are the same as for the fmi3SetReal
function.
Inputs and their derivatives are set with respect to the beginning of a communication time step.
To allow interpolation/approximation of the real output variables between communication steps (if they are used as inputs for other slaves), the derivatives of the outputs with respect to time can be read.
Whether the slave is able to provide the derivatives of outputs is given by the unsigned integer capability flag MaxOutputDerivativeOrder
.
It delivers the maximum order of the output derivative.
If the actual order is lower (because the order of integration algorithm is low), the retrieved value is 0.
[Example: If the internal polynomial is of order 1 and the master inquires the second derivative of an output, the slave will return zero.]
The derivatives can be retrieved by:
link:../headers/fmi3FunctionTypes.h[role=include]
Retrieves the n-th derivative of output values.
Argument vr
is a vector of nValueReferences
value references that define the variables whose derivatives shall be retrieved.
The array order
contains the order of the respective derivative (1 means the first derivative, 0 is not allowed).
Argument value
is a vector with the actual values of the derivatives.
Restrictions on using the function are the same as for the fmi3GetReal
function.
The returned outputs correspond to the current slave time.
E.g. after a successful fmi3DoStep(…)
the returned values are related to the end of the communication time step.
This standard supports polynomial interpolation and extrapolation as well as more sophisticated signal extrapolation schemes like rational extrapolation, see the companion document "FunctionalMockupInterface-ImplementationHints.pdf".
The computation of time steps is controlled by the following function.
link:../headers/fmi3FunctionTypes.h[role=include]
The computation of a time step is started.
Argument currentCommunicationPoint
is the current communication point of the master (\(tc_i\)) and argument communicationStepSize
is the communication step size (\(hc_i\)).
The latter must be \(> 0.0\).
The slave must integrate until time instant \(tc_{i+1} = tc_i + hc_i\).
[The calling environment defines the communication points and fmi3DoStep
must synchronize to these points by always integrating exactly to \(tc_i + hc_i\).
It is up to fmi3DoStep
how to achieve this.]
At the first call to fmi3DoStep
after fmi3ExitInitializationMode
was called currentCommunicationPoint
must be equal to startTime
as set with fmi3SetupExperiment
.
[Formally, argument currentCommunicationPoint
is not needed.
It is present in order to handle a mismatch between the master and the FMU state of the slave: The currentCommunicationPoint
and the FMU state of the slaves defined by former fmi3DoStep
or fmi3SetFMUState
calls have to be consistent with respect to each other.
For example, if the slave does not use the update formula for the independent variable as required above, \(tc_{i+1} = tc_i + hc_i\) (using argument \(tc_i\) = currentCommunicationPoint
of fmi3DoStep
) but uses internally an own update formula, such as \(tc_{s,i+1} = tc_{s,i} + hc_{s,i}\) then the slave could use as time increment \(\text{hc}_{s,i} := (tc_i - tc_{s,i}) + hc_i\) (instead of \(\text{hc}_{s,i} := hc_i\) ) to avoid a mismatch between the master time \(tc_{i+1}\) and the slave internal time \(tc_{s,i+1}\) for large i.]
Argument noSetFMUStatePriorToCurrentPoint
is fmi3True
if fmi3SetFMUState
will no longer be called for time instants prior to currentCommunicationPoint
in this simulation run [the slave can use this flag to flush a result buffer].
The function returns:
fmi3OK
- if the communication step was computed successfully until its end.
fmi3Discard
- if the slave computed successfully only a subinterval of the communication step.
The master can call the appropriate fmi3GetXXXStatus
functions to get further information.
If possible, the master should retry the simulation with a shorter communication step size.
[Redoing a step is only possible if the FMU state has been recorded at the beginning of the current (failed) step with fmi3GetFMUState
.
Redoing a step is performed by calling fmi3SetFMUState
and afterwards calling fmi3DoStep
with the new communicationStepSize.
Note that it is not possible to change currentCommunicationPoint
in such a call.]
fmi3Error
- the communication step could not be carried out at all.
The master can try to repeat the step with other input values and/or a different communication step size in the same way as described in the fmi3Discard
case above.
fmi3Fatal
- if an error occurred which corrupted the FMU irreparably.
[The master should stop the simulation run immediatlely.] See [status-returned-by-functions] for details.
fmi3Pending
- this status is returned if the slave executes the function asynchronously.
That means the slave starts the computation but returns immediately.
The master has to call fmi3GetDoStepPendingStatus
to find out if the slave is done.
An alternative is to wait until the callback function fmi3StepFinished
is called by the slave.
fmi3CancelStep
can be called to cancel the current computation.
It is not allowed to call any other function during a pending fmi3DoStep
.
link:../headers/fmi3FunctionTypes.h[role=include]
Can be called if fmi3DoStep
returned fmi3Pending
in order to stop the current asynchronous execution.
The master calls this function if, for example, the co-simulation run is stopped by the user or one of the slaves.
Afterwards only calls to fmi3Reset
, fmi3FreeInstance
, or fmi3SetFMUState
are valid to exit the step Canceled state.
Refer to State Machine of Calling Sequence from Master to Slave for all other valid functions in this state.
It depends on the capabilities of the slave which parameter constellations and calling sequences are allowed (see [CoSimulation])
Status information is retrieved from the slave by the following functions:
link:../headers/fmi3FunctionTypes.h[role=include]
Can be called when the fmi3DoStep
function returned fmi3Pending
.
status
is the result of the asynchronously executed fmi3DoStep
call or fmi3Pending
if the computation is not finished.
message
is a pointer to string which informs about the status of the currently running asynchronous fmi3DoStep
computation.
link:../headers/fmi3FunctionTypes.h[role=include]
Can be called after fmi3DoStep
returned fmi3Discard
.
terminate
is true if the slave wants to terminate the simulation.
lastSuccessfulTime
is the time instant at which the slave stopped the fmi3DoStep
call.
The following state machine defines the supported calling sequences.
Each state of the state machine corresponds to a certain phase of a simulation as follows:
- instantiated
-
In this state, start and guess values (= variables that have
initial
="exact"
or"approx"
) can be set.
Configuration Mode:
In this state structural parameters with variability
= fixed
or variability
= tunable
can be changed.
This state is entered from state instantiated by calling fmi3EnterConfigurationMode()
and left back to instantiated by calling fmi3ExitConfigurationMode()
.
fmi3EnterConfigurationMode()
can only be called if the FMU contains at least one structural parameter.
- Initialization Mode
-
In this state, equations are active to determine all outputs (and optionally other variables exposed by the exporting tool). The variables that can be retrieved by
fmi3GetXXX
calls are (1) defined in the XML file under<ModelStructure><InitialUnknowns>
, and (2) variables withcausality
="output"
. Variables withinitial
="exact"
, as well as variables withvariability
="input"
can be set. - slaveInitialized
-
In this state, the slave is initialized and the co-simulation computation is performed. The calculation until the next communication point is performed with function
fmi3DoStep
. Depending on the return value, the slave is in a different state (step Complete, step Failed, step Canceled). - Reconfiguration Mode
-
In this state structural parameters with
variability
=tunable
can be changed. This state is entered from state stepComplete by callingfmi3EnterConfigurationMode()
and left back to stepComplete by callingfmi3ExitConfigurationMode()
.fmi3EnterConfigurationMode()
can only be called if the FMU contains at least one structural parameter. - terminated
-
In this state, the solution at the final time of the simulation can be retrieved.
Note that in Initialization Mode input variables can be set with fmi3SetXXX
and output variables can be retrieved with fmi3GetXXX
interchangeably according to the model structure defined under element <ModelStructure><InitialUnknowns>
in the XML file.
[For example, if one output y1
depends on two inputs u1
, u2
, then these two inputs must be set, before y1
can be retrieved.
If additionally an output y2
depends on an input u3
, then u3
can be set and y2
can be retrieved afterwards.
As a result, artificial or "real" algebraic loops over connected FMUs in Initialization Mode can be handled by using appropriate numerical algorithms.]
There is the additional restriction in slaveInitialized state that it is not allowed to call fmi3GetXXX
functions after fmi3SetXXX
functions without an fmi3DoStep
call in between.
_[The reason is to avoid different interpretations of the caching, since contrary to FMI for Model Exchange, fmi3DoStep
will perform the actual calculation instead of fmi3GetXXX
, and therefore, dummy algebraic loops at communication points cannot be handeled by an appropriate sequence of fmi3GetXXX
and, fmi3SetXXX
calls as for ModelExchange.
Examples:_
Correct calling sequence | Wrong calling sequence |
---|---|
fmi3SetXXX on inputs |
fmi3SetXXX on inputs |
]
The allowed function calls in the respective states are summarized in the following table (functions marked in "yellow" are only available for "Co-Simulation", the other functions are available both for "Model Exchange" and "Co-Simulation"):
Function |
FMI 2.0 for Co-Simulation |
|||||||||
start, end |
instantiated |
Initialization Mode |
stepComplete |
stepInProgress |
stepFailed |
stepCanceled |
terminated |
error |
fatal |
|
fmi3GetVersion |
x |
x |
x |
x |
x |
x |
x |
x |
x |
|
fmi3SetDebugLogging |
x |
x |
x |
x |
x |
x |
x |
x |
||
fmi3Instantiate |
x |
|||||||||
fmi3FreeInstance |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3SetupExperiment |
x |
|||||||||
fmi3EnterInitializationMode |
x |
|||||||||
fmi3ExitInitializationMode |
x |
|||||||||
fmi3Terminate |
x |
x |
||||||||
fmi3Reset |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3GetReal |
2 |
x |
8 |
7 |
x |
7 |
||||
fmi3GetInteger |
2 |
x |
8 |
7 |
x |
7 |
||||
fmi3GetBoolean |
2 |
x |
8 |
7 |
x |
7 |
||||
fmi3GetString |
2 |
x |
8 |
7 |
x |
7 |
||||
fmi3SetReal |
1 |
3 |
6 |
|||||||
fmi3SetInteger |
1 |
3 |
6 |
|||||||
fmi3SetBoolean |
1 |
3 |
6 |
|||||||
fmi3SetString |
1 |
3 |
6 |
|||||||
fmi3GetFMUState |
x |
x |
x |
8 |
7 |
x |
7 |
|||
fmi3SetFMUState |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3FreeFMUState |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3SerializedFMUStateSize |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3SerializeFMUState |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3DeSerializeFMUState |
x |
x |
x |
x |
x |
x |
x |
|||
fmi3GetDirectionalDerivative |
x |
x |
8 |
7 |
x |
7 |
||||
fmi3SetRealInputDerivatives |
x |
x |
x |
|||||||
fmi3GetRealOutputDerivatives |
x |
8 |
x |
x |
7 |
|||||
fmi3DoStep |
x |
|||||||||
fmi3CancelStep |
x |
|||||||||
fmi3GetDoStepPendingStatus |
x |
|||||||||
fmi3GetDoStepDiscardedStatus |
x |
x means: call is allowed in the corresponding state
number means: call is allowed if the indicated condition holds:
1 for a variable with variability = "constant"
that has initial = "exact"
or "approx"
2 for a variable with causality = "output"
or continuous-time states or state derivatives
(if element <Derivatives>
is present)
3 for a variable with variability = "constant"
that has initial = "exact"
,
or causality
= "input"
6 for a variable with causality
= "input"
or (causality
= "parameter"
and variability
= "tunable"
)
7 always, but retrieved values are usable for debugging only
8 always, but if status is other than fmi3Terminated
,
retrieved values are useable for debugging only
In the following example, the usage of the FMI functions is sketched in order to clarify the typical calling sequence of the functions in a simulation environment. The example is given in a mix of pseudo-code and C, in order to keep it small and understandable. We consider two slaves, where both have one continuous real input and one continuous real output which are connected in the following way:
We assume no algebraic dependency between input and output of each slave. The code demonstrates the simplest master algorithm as shown in [math-co-simulation]:
-
Constant communication step size.
-
No repeating of communication steps.
-
The slaves do not support asynchronous execution of
fmi3DoStep
.
The error handling is implemented in a very rudimentary way.
//////////////////////////
//Initialization sub-phase
//Set callback functions,
fmi3CallbackFunctions cbf;
cbf.logMessage = logMessageFunction; //logMessage function
cbf.allocateMemory = calloc;
cbf.freeMemory = free;
cbf.stepFinished = NULL; //synchronous execution
cbf.instanceEnvironment = NULL;
//Instantiate both slaves
fmi3Instance s1 = s1_fmi3Instantiate("Tool1" , fmi3CoSimulation, instantiationToken1, "",
fmi3False, fmi3False, &cbf, fmi3True);
fmi3Instance s2 = s2_fmi3Instantiate("Tool2" , fmi3CoSimulation, instantiationToken2, "",
fmi3False, fmi3False, &cbf, fmi3True);
if ((s1 == NULL) || (s2 == NULL))
return FAILURE;
// Start and stop time
startTime = 0;
stopTime = 10;
//communication step size
h = 0.01;
// set all variable start values (of "ScalarVariable / <type> / start")
s1_fmi3SetReal/Integer/Boolean/String(s1, ...);
s2_fmi3SetReal/Integer/Boolean/String(s2, ...);
//Initialize slaves
s1_fmi3SetupExperiment(s1, fmi3False, 0.0, startTime, fmi3True, stopTime);
s2_fmi3SetupExperiment(s1, fmi3False, 0.0, startTime, fmi3True, stopTime);
s1_fmi3EnterInitializationMode(s1);
s2_fmi3EnterInitializationMode(s2);
// set the input values at time = startTime
s1_fmi3SetReal/Integer/Boolean/String(s1, ...);
s2_fmi3SetReal/Integer/Boolean/String(s2, ...);
s1_fmi3ExitInitializationMode(s1);
s2_fmi3ExitInitializationMode(s2);
//////////////////////////
//Simulation sub-phase
tc = startTime; //Current master time
while ((tc < stopTime) && (status == fmi3OK))
{
//retrieve outputs
s1_fmi3GetReal(s1, ..., 1, &y1);
s2_fmi3GetReal(s2, ..., 1, &y2);
//set inputs
s1_fmi3SetReal(s1, ..., 1, &y2);
s2_fmi3SetReal(s2, ..., 1, &y1);
//call slave s1 and check status
status = s1_fmi3DoStep(s1, tc, h, fmi3True);
switch (status) {
case fmi3Discard:
s1_fmi3GetDoStepDiscardedStatus(s1, &boolVal, &lastSuccessfulTime);
if (boolVal == fmi3True)
printf("Slave s1 wants to terminate simulation.");
case fmi3Error:
case fmi3Fatal:
terminateSimulation = true;
break;
}
if (terminateSimulation)
break;
//call slave s2 and check status as above
status = s2_fmi3DoStep(s2, tc, h, fmi3True);
...
//increment master time
tc += h;
}
//////////////////////////
//Shutdown sub-phase
if ((status != fmi3Error) && (status != fmi3Fatal))
{
s1_fmi3Terminate(s1);
s2_fmi3Terminate(s2);
}
if (status != fmi3Fatal)
{
s1_fmi3FreeInstance(s1);
s2_fmi3FreeInstance(s2);
}