43. FMI Export

AGX Dynamics supports FMI export for co-simulation using the FMI-standard (see http://fmi-standard.org/). The standard (and its interface) is called Functional Mockup Interface (FMI), and one unit created or connected via this standard is called a Functional Mockup Unit (FMU).

The following tutorial demonstrates how to build an AGX Dynamics FMI module compatible with the FMI 2.0 standard. The resulting FMI module can, according to the FMI standard, also be referred to as an FMU (see above).

The FMI export interface is exposed in AGX using the agxFMI2::Export namespace. The interface bindings are defined using Python. AGX Dynamics only supports creating FMUs using the FMI 2.0 standard.

The following diagram displays the structure of a Python based AGX FMI module:

../_images/fmi_export_1.png

Building an AGX FMU consists of the following steps:

  1. Setting up a directory structure matching the structure described in the FMI standard. The FMI module structure is described in section 2.3 (FMU Distribution) in the FMI2 specification.

  2. Implementing the Python script, which is responsible for

  1. Defining the AGX scene to be simulated (in Python or C++)

  2. Registering the FMI variables to be exported

  1. Running the AGX FmiExporter tool that will

  1. Automatically generate the FMI XML interface definition (modelDescription.xml)

  2. Package the FMU as a zip file according to the standard

43.1. AGX Python FMU generation

The FMU root directory, from here on referred to as $FMU_ROOT, must contain a Python file named main.agxPy. This is the main entry point for building the scene and setting up the module parameters.

The main.agxPy file must contain a function called fmiInit, which takes no arguments. The module of type agxFMI2::Export::Module* can be accessed through the global agxPython context:

def fmiInit():
    context = agxPython.getContext()
    export_module = context.environment.getFMI2Module()

This module contains a simulation instance, to which the simulated scene is added, an OSG render node for debug rendering, and methods for registering FMI parameters.

You can find an FMU example in the data/FmiModules/Tutorial/Python/SpinningBox directory.

The AGX-FMUs are exported using the FmiExporter tool, which has the following syntax

FmiExporter --name <FMU module name> --output <FMU output path> <FMU data root>

Name and output have implicit values so you can simply run

FmiExporter myFmu

where myFmu is the directory containing the FMU resources, including a mandantory main.agxPy file. In this case the module name will be myFmu and output path will be myFmu.fmu.

The FmiExporter will automatically generate a FMI modelDescription.xml and package the FMU zip file.

In order to run the FMU, AGX has to be installed on the computer with a valid license and valid environment variables.

You can test your generated FMU using the command:

> fmucheck -n 10 myFmu.fmu

43.2. AGX C++ FMU generation

The directory data/FmiModules/Tutorial/CppModule/ExternalBuild contains an example of how to structure and setup an AGX C++ FMU. It assumes an installed AGX package, and use a cmake build file to locate AGX, compile source code, and run the FmiExporter command line tool to produce an FMU zip package. To build the sample FMU:s as part of building the tutorials/examples, the CMake flag AGX_BUILD_FMI_TUTORIALS must be set to ON.

Also, if the CMake flag AGX_ENABLE_TESTS is set to ON, it is possible to test the built C++ FMU by executing the command:

> ctest -C Release

Note

It is important to build and run the FMU with the correct configuration. Because the naming convention of FMU:s it is not easy to make a distinction between release or debug builds. In Mac and Linux releases, it is only possible to build and run the FMU:s in release mode. We recommend building FMU:s in Release mode when possible due to conflicts between different runtime libraries.

The general structure for a C++ based FMU is similar to the python FMU:s. At least one source file named main.cpp must be present in the resource directory and all files in the named directory will be part of the FMU, including the source files. The main.cpp source file must at least contain the following functions:

#include <agxFMI2/export/Module.h>
#include <agxFMI2/export/VariableUtil.h>
#include <agxCollide/Box.h>
#include <agx/Hinge.h>

using namespace agx;

namespace
{
  // Called when the fmi is shutdown
  fmi2Status fmiShutdown(agxFMI2::Export::Module *)
  {
    return fmi2OK;
  }

  // Called when the fmi is initialized
  fmi2Status fmiInit(agxFMI2::Export::Module *module)
  {
    agxSDK::Simulation *simulation = module->getSimulation();

    // Here is where we build our model
    RigidBody *body = new RigidBody(new agxCollide::Geometry(new agxCollide::Box(1, 1, 1)));
    simulation->add(body);


    agx::Hinge *hinge = new agx::Hinge(body, new agx::Frame());
    hinge->setName("TheHinge");

    hinge->getMotor1D()->setSpeed( 1.456 );
    hinge->getMotor1D()->setEnable( true );
    simulation->add(hinge);

    // Create motor speed input parameter
    auto vSetSpeed = new agxFMI2::Export::InputVariable_Real("targetMotorSpeed");

    // Callback when parameter is changed from FMI master
    vSetSpeed->setCallback([=](Real val) {
      hinge->getMotor1D()->setSpeed(val);
    });

    vSetSpeed->setDescription("The motor speed controller");
    vSetSpeed->setStartValue(hinge->getMotor1D()->getSpeed());
    module->registerVariable(vSetSpeed);

    // Create box angular velocity output parameter
    auto vSpeedOutput = new agxFMI2::Export::OutputVariable_Real("boxAngularVelocity");

    // Callback when parameter is read from FMI master
    vSpeedOutput->setCallback([=](Real& val) {
      val = body->getAngularVelocity().z();
    });

    vSpeedOutput->setDescription("The current angular velocity");
    module->registerVariable(vSpeedOutput);

    return fmi2OK;
  }

  // Called when the application is initialized
  fmi2Status fmiInitApplication(agxFMI2::Export::Module *)
  {
    return fmi2OK;
  }
}