Skip to content

FMI 2 - Overview

Feral Logo FMI Logo

API Documentation

feral.fmi2

FERAL offers importing functionality for executing co-simulation FMUs that comply with the FMI Standard 1 version 2.0 2.

For the list of supported and not supported features, please visit the Features page.

FMU2WorkerCS

The FMU2WorkerCS component enables FERAL to automatically import FMI 2 compliant FMUs and execute co-simulation scenarios. It implements a time-triggered FERAL worker behaviour.

Instantiation

To instantiate a FMU2WorkerCS object, one must provide the parent DiscreteTimeMOCC FERAL Director object instance and the FMU file path.

  • public FMU2WorkerCS(DiscreteTimeMOCC parent, String fmuFilePath)

Configuration

To configure the FMU2WorkerCS component, one must:

  1. Indicate which ports will be used in the simulation scenario. The ports are declared according to their String names by means of the following functions:

    • void publishFMUInputPorts(String[] fmuInputPortNames)
    • void publishFMUVariableParameterPorts(String[] fmuInputPortNames)
    • void publishFMUIntegerOutputPorts(String[] fmuInputPortNames)
    • void publishFMURealOutputPorts(String[] fmuInputPortNames)
    • void publishFMUBooleanOutputPorts(String[] fmuInputPortNames)
  2. Set up the desired initial values. The ports are indicated using their String names.

    • void setStartValue(String parameterName, double... value)
    • void setStartValue(String parameterName, int... value)
    • void setStartValue(String parameterName, boolean... value)
    • void setStartValue(String parameterName, String... value)

FMI 2 Component Configuration Example

The example below presents how the instantiation and initialization of a FMU2WorkerCS object is done.

// ...

FMU2WorkerCS worker = new FMU2WorkerCS(director, fmuPath);
worker.setName("ExampleFMUName");

// The published FMU ports will be converted into FERAL ports
worker.publishFMUInputPorts(new String[] { "inputPort1", "inputPortN"});
worker.publishFMURealOutputPorts(new String[] { "realOutput1", "realOutputN"});
worker.publishFMUIntegerOutputPorts(new String[] {"intOutput1", "intOutputN"});

// ...

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 configure the FMU2WorkerCS component.
  3. Instantiate the FERAL components participating in the simulation in direct communication with the FMU instance.
  4. Link the ports of the FMU with the other communicating components.
  5. 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 2 Component Example

The example below illustrates the important steps to set up a scenario using the FMU2WorkerCS object.

FMI2ExampleScenario.java
public class FMI2ExampleScenario {
    // ... Declare and instantiate class and instance variables as necessary

    public main(String[] args) {
        // 1. Instantiate SimulationScenario and Director objects
        Scenario simulationScenario = new SimulationScenario();
        Director director = new DiscreteTimeMOCC("Root Director", simulationScenario, SIMULATION_STEP_DURATION_NS);

        // 2. Instantiate the co-simulation FMU
        String fmuPath = "my/fmu/path.fmu";
        FMU2WorkerCS worker = new FMU2WorkerCS(director, fmuPath);
        worker.setName("ExampleFMUName");

        // Set the input and output ports required by the simulation
        // The names must correspond to FMU variable names
        worker.publishFMUInputPorts(new String[] { "inputPort1", "inputPortN"});
        worker.publishFMURealOutputPorts(new String[] { "realOutput1", "realOutputN"});
        worker.publishFMUIntegerOutputPorts(new String[] {"intOutput1", "intOutputN"});

        // 3. Initialize other FERAL workers that will be co-simulated with the FMU
        FERALWorker feralWorker = new FERALWorker(director);
        // ...

        // 4. Link the FMU and FERAL worker ports
        new Link(feralWorker.getOutputPort("transmitterPortNameN"), worker.getInputPort("inputPortN"));
        // ...
        new Link(worker.getOutputPort("realOutput1"), feralWorker.getInputPort("receiverDoublePortName1"));

        // 5. Start simulation execution
        simulationScenario.startSimulation(SIMULATION_DURATION_NS);
    }
}

At the communicating FERAL worker, we have the following:

FERALWorker.java
public class FERALWorker extends TimeTriggeredWorker {
    DiscreteEventRxPort rxPortReceiverDoublePortName1 = (DiscreteEventRxPort) createInputPort("receiverDoublePortName1");
    // ...
    DiscreteEventRxPort rxPortReceiverIntPortName1 = (DiscreteEventRxPort) createInputPort("receiverIntPortName1");
    // ...
    DiscreteEventTxPort txPortNameN = (DiscreteEventTxPort) createOutputPort("transmitterPortNameN");

    public FERALWorker(Director parent) {
        super(parent);
    }

    @Override 
    public void timeStep() {
        // The worker receives the values at each port and converts them to the expected types
        Double receiverDoubleValueName1 = (Double) rxPortReceiverDoublePortName1.getValue();
        Integer receiverIntValueName1 = (Integer) rxPortReceiverIntPortName1.getValue();
        // ...

        // Perform calculations, update states, etc. using the received values

        txPortName1.send(new ValueEvent<>(variable1));
        // ...

    }

}