Skip to content

FMI 3 - Overview

Feral Logo FMI Logo

API Documentation

feral.fmi3

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:

  1. Instantiate the simulation scenario and parent director that will command the FMU's execution.
  2. Instantiate and set the FERALCosimulationConfiguration component.
  3. Instantiate the FMI3CoSimWorker component.
  4. Instantiate the FERAL components participating in the simulation in direct communication with the FMU instance.
  5. Link the ports of the FMU with the other communicating components.
  6. 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.

FMI3ExampleScenario.java
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:

FERALWorker.java
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));
        // ...

    }

}