2. Compilation

This section brings up how to compile a model to an FMU-ME / FMU-CS. Compiling a model to an FMU-ME / FMU-CS will be demonstrated in Section 2.1, “Simple FMU-ME compilation example” and Section 2.2, “Simple FMU-CS compilation example” respectively.

For more advanced usage of the compiler functions, there are compiler options and arguments which can be modified. These will be explained in Section 2.4, “Compiler settings”.

Section 2.6, “Compilation in more detail”, will go through some parts of the compilation process and how to perform these steps one by one.

2.1. Simple FMU-ME compilation example

The following steps compile a model to an FMU-ME version 1.0:

  1. Import the JModelica.org compiler function compile_fmu from the package pymodelica.

  2. Specify the model and model file.

  3. Perform the compilation.

This is demonstrated in the following code example:

# Import the compiler function
from pymodelica import compile_fmu

# Specify Modelica model and model file (.mo or .mop)
model_name = 'myPackage.myModel'
mo_file = 'myModelFile.mo'

# Compile the model and save the return argument, which is the file name of the FMU
my_fmu = compile_fmu(model_name, mo_file)

There is a compiler argument target that controls whether the model will be exported as an FMU-ME or FMU-CS. The default is to compile an FMU-ME, so target does not need to be set in this example. The compiler argument version specifies if the model should be exported as an FMU 1.0 or 2.0. As the default is to compile an FMU 1.0, version does not need to be set either in this example. To compile an FMU 2.0, version should be set to '2.0'.

Once compilation has completed successfully, an FMU-ME 1.0 will have been created on the file system. The FMU is essentially a compressed file archive containing the files created during compilation that are needed when instantiating a model object. Return argument for compile_fmu is the full file path of the FMU that has just been created, this will be useful later when we want to create model objects. More about the FMU and loading models can be found in Section 3, “Loading models”.

In the above example, the model is compiled using default arguments and compiler options - the only arguments set are the model class and file name. However, compile_fmu has several other named arguments which can be modified. The different arguments, their default values and interpretation will be explained in Section 2.4, “Compiler settings”.

2.2. Simple FMU-CS compilation example

The following steps compiles a model to an FMU-CS version 1.0:

  1. Import the JModelica.org compiler function compile_fmu from the package pymodelica.

  2. Specify the model and model file.

  3. Set the argument target = 'cs'

  4. Perform the compilation.

This is demonstrated in the following code example:

# Import the compiler function
from pymodelica import compile_fmu

# Specify Modelica model and model file (.mo or .mop)
model_name = 'myPackage.myModel'
mo_file = 'myModelFile.mo'

# Compile the model and save the return argument, which is the file name of the FMU
my_fmu = compile_fmu(model_name, mo_file, target='cs')

In a Co-Simulation FMU, the integrator for solving the system is contained in the model. With an FMU-CS exported with JModelica.org, two different solvers are supported: CVode and Explicit Euler.

2.3. Compiling from libraries

The model to be compiled might not be in a standalone .mo file, but rather part of a library consisting of a directory structure containing several Modelica files. In this case, the file within the library that contains the model should not be given on the command line. Instead, the entire library should to added to the list of libraries that the compiler searches for classes in. This can be done in several ways (here library directory refers to the top directory of the library, which should have the same name as the top package in the library):

  • Adding the directory containing the library directory to the environment variable MODELICAPATH. The compiler will search for classes in all libraries found in any on the directories in MODELICAPATH. In this case the file_name argument of the compilation function can be omitted, assuming no additional Modelica files are needed.

  • Setting the 'extra_lib_dirs' compiler option to the path to the directory containing the library directory. This is equivalent to adding it to the MODELICAPATH, but only for that compilation.

  • Giving the path to the library directory in the file_name argument of the compilation function. This allows adding a specific library to the search list (as opposed to adding all libraries in a specific directory).

By default, the script starting a JModelica.org Python shell sets the MODELICAPATH to the directory containing the version of the Modelica Standard Library (MSL) that is included in the installation. Thus, all classes in the MSL are available without any need to specify its location.

The Python code example below demonstrates these methods:

# Import the compiler function
from pymodelica import compile_fmu

# Compile an example model from the MSL
fmu1 = compile_fmu('Modelica.Mechanics.Rotational.Examples.First')

# Compile a model from the library MyLibrary, located in C:\MyLibs
fmu2 = compile_fmu('MyLibrary.MyModel', compiler_options = {'extra_lib_dirs':'C:/MyLibs'})

# The same as the last command, if no other libraries in C:\MyLibs are needed
fmu3 = compile_fmu('MyLibrary.MyModel', 'C:/MyLibs/MyLibrary')

2.4. Compiler settings

The compiler function arguments can be listed with the interactive help in Python. The arguments are explained in the corresponding Python docstring which is visualized with the interactive help. This is demonstrated for compile_fmu below. The docstring for any other Python function for can be displayed in the same way.

2.4.1. compile_fmu arguments

The compile_fmu arguments can be listed with the interactive help.

# Display the docstring for compile_fmu with the Python command 'help'
from pymodelica import compile_fmu
help(compile_fmu)
Help on function compile_fmu in module pymodelica.compiler:

compile_fmu(class_name, file_name=[], compiler='auto', target='me', version='1.0', 
            compiler_options={}, compile_to='.', compiler_log_level='warning', 
            separate_process=True, jvm_args='')
Compile a Modelica model to an FMU.

A model class name must be passed, all other arguments have default values.
The different scenarios are:

* Only class_name is passed:
    - Class is assumed to be in MODELICAPATH.

* class_name and file_name is passed:
    - file_name can be a single path as a string or a list of paths
      (strings). The paths can be file or library paths.
    - Default compiler setting is 'auto' which means that the appropriate
      compiler will be selected based on model file ending, i.e.
      ModelicaCompiler if a .mo file and OptimicaCompiler if a .mop file is
      found in file_name list.

Library directories can be added to MODELICAPATH by listing them in a
special compiler option 'extra_lib_dirs', for example:

    compiler_options =
        {'extra_lib_dirs':['c:\MyLibs1','c:\MyLibs2']}

Other options for the compiler should also be listed in the compiler_options
dict.

The compiler target is 'me' by default which means that the shared
file contains the FMI for Model Exchange API. Setting this parameter to
'cs' will generate an FMU containing the FMI for Co-Simulation API.

Parameters::

    class_name --
        The name of the model class.

    file_name --
        A path (string) or paths (list of strings) to model files and/or
        libraries.
        Default: Empty list.

    compiler --
        The compiler used to compile the model. The different options are:
          - 'auto': the compiler is selected automatically depending on
             file ending
          - 'modelica': the ModelicaCompiler is used
          - 'optimica': the OptimicaCompiler is used
        Default: 'auto'

    target --
        Compiler target. Possible values are 'me', 'cs' or 'me+cs'.
        Default: 'me'

    version --
        The FMI version. Valid options are '1.0' and '2.0'.
        Default: '1.0'

    compiler_options --
        Options for the compiler.
        Default: Empty dict.

    compile_to --
        Specify target file or directory. If file, any intermediate directories 
        will be created if they don't exist. If directory, the path given must 
        exist.
        Default: Current directory.

    compiler_log_level --
        Set the logging for the compiler. Takes a comma separated list with
        log outputs. Log outputs start with a flag :'warning'/'w',
        'error'/'e', 'info'/'i' or 'debug'/'d'. The log can be written to file
        by appended flag with a colon and file name.
        Default: 'warning'

    separate_process --
        Run the compilation of the model in a separate process.
        Checks the environment variables (in this order):
            1. SEPARATE_PROCESS_JVM
            2. JAVA_HOME
        to locate the Java installation to use.
        For example (on Windows) this could be:
            SEPARATE_PROCESS_JVM = C:\Program Files\Java\jdk1.6.0_37
        Default: True

    jvm_args --
        String of arguments to be passed to the JVM when compiling in a
        separate process.
        Default: Empty string


Returns::

    A compilation result, represents the name of the FMU which has been
    created and a list of warnings that was raised.

2.4.2. Compiler options

Compiler options can be modified using the compile_fmu argument compiler_options. This is shown in the example below.

# Compile with the compiler option 'enable_variable_scaling' set to True

# Import the compiler function
from pymodelica import compile_fmu
 
# Specify model and model file
model_name = 'myPackage.myModel'
mo_file = 'myModelFile.mo'

# Compile
my_fmu = compile_fmu(model_name, mo_file, compiler_options={"enable_variable_scaling":True})

There are four types of options: string, real, integer and boolean. The complete list of options can be found in Appendix A, Compiler options.

2.5. Compiling in a separate process

In JModelica.org, the compilers (ModelicaCompiler and OptimicaCompiler) are written in Java. When compiling a model from the Python interface, with e.g. compile_fmu, the default behavior is to compile the model in a separate process. This means that a specific JRE (Java Runtime Environment) is used for the compilation. For those on a 64 bit Windows this is can be very useful as the default JRE used with JPype is 32 bit. Also, in most cases, the JVM (Java Virtual Machine) can be given a larger heap space (especially when using a 64 bit JRE instead of a 32 bit) which enables compilation of larger models.

The environment variable SEPARATE_PROCESS_JVM can be set to point at a specific Java installation (JRE or JDK) for the compilation. For Windows users, the environment variable can be found (and set) in the file setenv.bat which is located in the JModelica.org installation folder. It can also be set locally in the Python shell. If SEPARATE_PROCESS_JVM is not set, JAVA_HOME will be used instead. It is also possible to pass arguments to the JVM with the compile_fmu argument 'jvm_args'.

The following example demonstrates how to set the maximum heap space for the JVM to one gigabyte by setting the argument jvm_args:

# Import the compiler function
from pymodelica import compile_fmu

# Compile in separate process
compile_fmu('myPackage.myModel', 'myModelFile.mo', jvm_args='-Xmx1g')

Another option is to access the compilers through the Python package JPype (this used to be the default behavior). This option is still available and can be ebabled by setting the argument separate_process to False when calling e.g. compile_fmu.

2.6. Compilation in more detail

Compiling with compile_fmu bundles quite a few steps required for the compilation from model file to FMU. Some of these steps will be briefly described in this section with code examples. For a more detailed review of the compile procedure, see Section 4, “Architecture” in Chapter 1, Introduction.

2.6.1. Creating a compiler

A compiler (which can be either a Modelica or Optimica compiler) is created by importing the Python classes from the compiler module. This example code will create a Modelica compiler and a target object.

# Import the class ModelicaCompiler from the compiler module
from pymodelica.compiler_wrappers import ModelicaCompiler

# Create a compiler and compiler target object for FMU-ME version 1.0
mc = ModelicaCompiler()
target = mc.create_target_object("me", "1.0")

2.6.2. Source tree generation and flattening

In the first step of the compilation, the model is parsed and instantiated. Then the model is transformed into a flat representation which can be used to generate C and XML code. If there are errors in the model, for example syntax or type errors, Python exceptions will be thrown during these steps.

Note that the default setting for the compiler is to compile an FMU.

# Parse the model and get a reference to the root of the source AST
source_root = mc.parse_model('myPackage.mo')

# Generate an instance tree representation and get a reference to the model instance
model_instance = mc.instantiate_model(source_root, 'myPackage.myModel', target)

# Perform flattening and get a flat representation
flat_rep = mc.flatten_model(model_instance, target)

2.6.3. Code generation

The next step is code generation, which produces C code containing the model equations, and XML files containing model meta data such as variable names and types.

# Generate code
mc.generate_code(flat_rep, target)