Skip to content

Latest commit

 

History

History
365 lines (294 loc) · 18.7 KB

4_2_co-simulation_api.adoc

File metadata and controls

365 lines (294 loc) · 18.7 KB

FMI Application Programming Interface

This section contains the interface description to access the in/output data and status information of a co-simulation slave from a C program.

Transfer of Input / Output Values and Parameters

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".

Computation

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])

Retrieving Status Information from the Slave

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.

State Machine of Calling Sequence from Master to Slave

The following state machine defines the supported calling sequences.

Calling sequence of Co-Simulation C functions in form of an UML 2.0 state machine.

figure11

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 with causality = "output". Variables with initial = "exact", as well as variables with variability = "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 calling fmi3EnterConfigurationMode() and left back to stepComplete by calling fmi3ExitConfigurationMode(). 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
fmi3DoStep
fmi3GetXXX on outputs
fmi3SetXXX on inputs
fmi3DoStep
fmi3GetXXX on outputs

fmi3SetXXX on inputs
fmi3DoStep
fmi3GetXXX on outputs
fmi3SetXXX on inputs
fmi3GetXXX on outputs // not allowed
fmi3DoStep
fmi3GetXXX on outputs

]

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

Pseudo-code Example

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:

co simulation connection of slaves
Figure 1. Connection graph of the slaves.

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]:

  1. Constant communication step size.

  2. No repeating of communication steps.

  3. 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);
}