FMI 3 - Overview¶

API Documentation
FERAL offers importing functionality for executing co-simulation FMUs that comply with the FMI Standard 1 version 3.0 2.
For the list of supported and not supported features, please visit the Features page.
FMI3CoSimWorker¶
The FMI3CoSimWorker component enables FERAL to automatically import FMI 3 compliant FMUs and execute co-simulation scenarios. It implements an event-triggered FERAL worker behaviour.
Configuration - FERALCosimulationConfiguration¶
The FERALCosimulationConfiguration component centralizes the configuration of a FMI 3 compliant FMU. For the FMU to be instantiated one must to define a minimum set of variables. This required set of values is reflected in the constructor method:
public FERALCosimulationConfiguration(Console console, String fmuPath, Double startTime, Double stepSize, String instanceName, String instantiationToken)
The FMU ports that should be made available to the simulation scenario are to be declared by enumerating their value references by means of the following method:
public void setUsedModelVariables(Set<Integer> valueReferences)
Variable start values can be set by means of sequences of calls of the following method:
public void setStartValue(Integer valueReference, Object value)
FERALCosimulationConfiguration Usage Example
The code below exemplifies how the FERALCosimulationConfiguration might be used.
// Instantiate the configuration object
FERALCosimulationConfiguration config = new FERALCosimulationConfiguration(
console,
fmuPath,
new SimulationTime(fmuStartTimeNS).getTime(TimeUnit.SECONDS),
new SimulationTime(stepSizeNS).getTime(TimeUnit.SECONDS),
"instanceName",
instantiationToken
);
// Declare the FMU ports that must be available as FERAL ports
config.setUsedModelVariables(new HashSet<Integer>(
Arrays.asList(
0,1,2 // ...
)
)
);
// Set up other configuration parameters
config.setStopTime(new SimulationTime(fmuEndTimeNS).getTime(TimeUnit.SECONDS));
config.setStopTimeDefined(true);
config.setEarlyReturnAllowed(false);
config.setEventModeUsed(false);
config.setLogMessage(new PrintLogCallback());
config.setLogCategories(Arrays.asList(FMI3LogCategory.LogEvents, FMI3LogCategory.LogStatusError));
// ...
// Set start values for individual variables
// E.g., if variable reference number 5 and 6 points to variables of type double
// with a single element
config.setStartValue(5, Arrays.asList(1.0));
config.setStartValue(6, Arrays.asList(2.0));
Instantiation¶
To instantiate a FMI3CoSimWorker object, one must provide the parent DiscreteEventMOCC FERAL Director object instance and the pre-filled FERALCosimulationConfiguration instance.
public FMI3CoSimWorker(DiscreteEventMOCC parent, FERALCosimulationConfiguration config)
Integration with a Simulation Scenario¶
To integrate your FMU using the FMU2WorkerCS component, one needs to:
- Instantiate the simulation scenario and parent director that will command the FMU's execution.
- Instantiate and set the FERALCosimulationConfiguration component.
- Instantiate the FMI3CoSimWorker component.
- Instantiate the FERAL components participating in the simulation in direct communication with the FMU instance.
- Link the ports of the FMU with the other communicating components.
- Command the scenario to execute.
Note
The workers that communicate with the FMU must know the types that will be received at each port for their correct run-time conversion.
FMI 3 Component Example
The example below illustrates the important steps to set up a scenario using the FMU2WorkerCS object.
public class FMI3ExampleScenario {
// ... Declare and instantiate class and instance variables as necessary
public main(String[] args) {
// 1. Initialize SimulationScenario and Director objects
Scenario simulationScenario = new SimulationScenario();
Director director = new DiscreteEventMOCC(simulationScenario);
// 2. Initialize the co-simulation FMU configuration object
String fmuPath = "my/fmu/path.fmu";
FERALCosimulationConfiguration config = new FERALCosimulationConfiguration(
director.getConsole(),
fmuPath,
new SimulationTime(fmuStartTimeNS).getTime(TimeUnit.SECONDS),
new SimulationTime(stepSizeNS).getTime(TimeUnit.SECONDS),
"instanceName",
fmuInstantiationToken
);
// Declare the Value References (VR) that will be used in the simulation scenario
// These include input, output, parameter, constants, etc.
config.setUsedModelVariables(new HashSet<Integer>(
Arrays.asList(
fmuWorker.getVRFromName("NameOfVariableVR1"),
2,
6 // ...
)
)
);
config.setStopTime(new SimulationTime(fmuEndTimeNS).getTime(TimeUnit.SECONDS));
config.setStopTimeDefined(true);
config.setEarlyReturnAllowed(true); // If Early Return is supported by the FMU
config.setLogMessage(new PrintLogCallback()); // Set any Java JNA Callback object with the appropriate function signature
// Configure the desired log categories
config.setLogCategories(Arrays.asList(FMI3LogCategory.LogEvents, FMI3LogCategory.LogStatusError));
// 3. Instantiate the FMU with the previously set configuration object
FMI3CoSimWorker fmuWorker = new FMI3CoSimWorker(rootDirector, config);
// 4. Initialize other FERAL workers that will be co-simulated with the FMU
FERALWorker feralWorker = new FERALWorker(director);
// ...
// 5. Link the FMU and FERAL worker ports
// FMU worker ports are indexed by their respective Value References in String format
new Link(feralWorker.getOutputPort("transmitterPortVR1"), fmuWorker.getInputPort("1"));
// ...
new Link(fmuWorker.getOutputPort("6"), feralWorker.getInputPort("receiverPortVR6"));
// ...
// 6. Start simulation execution
simulationScenario.startSimulation(SIMULATION_DURATION_NS);
}
}
At the communicating FERAL worker, we have the following:
public class FERALWorker extends EventTriggeredWorker {
DiscreteEventRxPort rxPortReceiverVR2 = (DiscreteEventRxPort) createInputPort("receiverPortVR2");
// ...
DiscreteEventTxPort txPortVRN = (DiscreteEventTxPort) createOutputPort("transmitterPortVRN");
public FERALWorker(Director parent) {
super(parent);
// ...
}
@Override
public void messageReceived(Event<? extends Object> messageEvent, RxPort fromPort) {
// Receive a given message corresponding to an FMU port
String portName = fromPort.getName();
// All values transmitted by FMI 3 FMUs are sent as matrices in row-major order
if (portName == "receiverPortVR2") {
// Assuming that the type of the FMU's variable referenced by VR 2 is double
List<Double> receiverValueVR2 = (List<Double>) rxPortReceiverPortVR2.getValue();
} // else if (portName == ...) // ...
}
public void timerExpired(TimedEvent<?> timerEvent) {
// Perform calculations, update states, etc. using the received values
// Send values to the output ports
// Note that values that are to be sent to FMI 3 FMUs must be of type List<T>
// Assuming that the type of the FMU's variable referenced by VR 1 is Unsigned Long with 2 elements
// The values below will be interpreted as the maximum unsigned long and 1 by the FMU
txPortVR1.send(new ValueEvent<>(Arrays.asList(-1l, 1l));
// ...
}
}