2. Compilation

This section brings up how to compile a model to an FMU-ME / FMU-CS or JMU. Compiling a model to an FMU-ME / FMU-CS will be demonstrated in Section 2.1 and Section 2.2 respectively, and compiling to a JMU in Section 2.3.

For more advanced usage of the compiler functions, there are compiler options and parameters which can be modified. These will be explained in Section 2.5.

Section 2.7, 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 compiles 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 parameter: target which controls whether the model will be exported as an FMU-ME or FMU-CS. The default is to compile an FMU-ME, so target must not be set in this example. The compiler parameter version specifies if the model should be exported as a FMU 1.0 or 2.0 RC1. As the default is to compile a FMU 1.0, version must not be set either in this example. To compile an FMU 2.0 RC1, 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.

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 parameters which can be modified. The different parameters, their default values and interpretation will be explained in Section 2.5.

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 parameter 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. Simple JMU compilation example

The steps required to compile a model to a JMU is very similar to compiling a model to an FMU:

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

  2. Specify the model and model file

  3. Perform the compilation

The only difference is really the requirement on the model, the FMU must be a pure Modelica model whilst a JMU can also be an Optimica model. The following code example demonstrates how to compile an JMU.

# Import the compiler function
from pymodelica import compile_jmu

# Specify Modelica or Optimica 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_jmu = compile_jmu(model_name, mo_file)

As in the FMU case, when the compilation has completed successfully a JMU file will have been created on the file system. The return argument is also the full file path of the JMU that has just been created, which will be useful later when we want to create model objects. More about the JMU file and loading models can be found in Section 3.

2.4. Compiling from libraries

The model to be compiled might not be in a loose '.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 the compiler searches for classes in. This can be done in several ways (here library directory refers to the top directory of the library, that 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.5. Compiler settings

The compiler function parameters can be listed with the interactive help in Python. The parameters are explained in the corresponding Python docstring which is visualized with the interactive help. This is demonstrated for compile_fmu below. The parameters for compile_jmu and compile_fmux (and for any other Python function for that matter) can be displayed in the same way.

2.5.1. compile_fmu parameters

The compile_fmu parameters 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.5.2. Compiler options

Compiler options are read from an XML file, options.xml, which can be found in the JModelica.org installation folder under the folder Options. The options are loaded from the file when a compiler is created, that is when compile_fmu, compile_jmu or compile_fmux is run. Options for a compiler instance can also be modified interactively when compiling using the parameter dict 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 most relevant options for users are listed with default values and description in Table 4.1. The complete list of options can be found in the options.xml file.

Table 4.1. Compiler options

Option Option type / Default value Description
algorithms_as_functions boolean / false When this option is true, algorithm sections are converted to function calls.
automatic_add_initial_equations boolean / true When this option is set to true, then additional initial equations are added to the model based on a the result of a matching algorithm. Initial equations are added for states that are not matched to an equation.
automatic_tearing boolean / true If this option is set to true, automatic tearing of equation systems is performed.
block_jacobian_check boolean / false Compares the analytic block jacobians with the finite difference block jacobians during block evaluation. An error is given if the relative error is to big.
block_jacobian_check_tol real / 1.0E-6 Specifies the relative tolerance for block jacobian check.
check_inactive_contitionals boolean / false Check for errors in inactive conditional components when compiling. When checking a class, this is always done.
convert_free_dependent_ parameters_to_algebraics boolean / true If this option is set to true, free dependent parameters are converted to algebraic variables.
cs_solver integer / 0 Specifies the internal solver used in co-simulation. 0 == CVode, 1 == Euler.
cs_rel_tol real / 1.0E-6 Default tolerance for the adaptive solvers in the CS case.
cs_step_size real / 0.001 Default step-size for the non-adaptive solvers in the CS case.
divide_by_vars_in_tearing boolean / false If this option is set to true, a less restrictive strategy is used for solving equations in the tearing algorithm. Specifically, division by parameters and variables is permitted, by default no such divisions are made during tearing.
eliminate_alias_variables boolean / true If this option is true, then alias variables are eliminated from the model.
enable_structural_diagnosis boolean / true Enable this option to invoke the structural error diagnosis based on the matching algorithm.
enable_variable_scaling boolean / false If this option is true, then the "nominal" attribute will be used to scale variables in the model.
enforce_bounds boolean / false Enforce min-max bounds on variables in the equation blocks.
equation_sorting boolean / true If this option is true, equations are sorted using the BLT algorithm. Note: When compiling an FMU, this option is set to true by default and can not be changed.
events_default_tol real / 1.0E-10 Default tolerance for the event iterations.
events_tol_factor real / 1.0E-4 Tolerance safety factor for the event iterations. Used when external solver specifies relative tolerance.
export_functions boolean / false Export used Modelica functions to generated C code in a manner that is compatible with the external C interface in the Modelica Language Specification.
export_functions_vba boolean / false If this option is true, then VBA-compatible wrappers for exported functions are created. Requires export_functions = true.
extra_lib_dirs string / "" The value of this option is appended to the value of the MODELICAPATH environment variable for determining in what directories to search for libraries.
generate_block_jacobian boolean / false If this option is set to true, code for computing block Jacobians is generated. If blocks are needed to compute ODE jacobians they will be generated anyway.
generate_dae boolean / false If this option is set to true, code for solving DAEs is generated. Note: When compiling an FMU, this option is set to false by default and can not be changed.
generate_dae_jacobian boolean / false If this option is true, code for computing DAE Jacobians is generated.
generate_html_diagnostics boolean / false When this option is set to true model diagnostics is generated in HTML format. This includes the flattened model, connection sets, alias sets and BLT form.
generate_ode boolean / true If this option is set to true, code for solving ODEs is generated. Note: When compiling an FMU, this option is set to true by default and can not be changed.
generate_ode_jacobian boolean / false If this option is set to true, code for computing ODE Jacobians are generated.
generate_only_initial_system boolean / false If this option is set to true, only the initial equation system will be generated.
halt_on_warning boolean / false If this option is set to false one or more compiler warnings will not stop compilation of the model.
hand_guided_tearing boolean / false If this option is set to true, hand guided tearing annotations are parsed and hand guided tearing is performed.
ignore_within boolean / false Ignore within clauses, both when reading input files and when error-checking.
index_reduction boolean / true If this option is true, index reduction is performed.
inline_functions string / "trivial" Perform function inlining on model after flattening (allowed values are "none", "trivial" or "all").
iteration_variable_scaling integer / 1 Iteration variables scaling mode in equation block solvers: 0 - no scaling, 1 - scaling based on nominals only (default), 2 - utilize heuristics to guess nominal based on min,max,start, etc.
local_iteration_in_tearing boolean / false If this option is set to true, equations that can be solved with local fixedpoint iteration will be considered solved.
log_level integer / 3 Log level for the runtime: 0 - none, 1 - fatal error, 2 - error, 3 - warning, 4 - info, 5 -verbose, 6 - debug.
merge_blt_blocks boolean / false If this option is set to true, BLT blocks will be merged so that all hand guided tearing equations and variables reside inside the same BLT block.
nle_solver_check_jac_cond boolean / false If this option is set to true, the NLE solver should check Jacobian condition number and log it.
nle_solver_default_tol real / 1.0E-10 Default tolerance for the equation block solver.
nle_solver_max_iter integer / 100 Maximum number of iterations for the equation block solver before failure.
nle_solver_min_tol real / 1.0E-12 Minimum tolerance for the equation block solver. Note that, for instance, default Kinsol tolerance is machine precision pwr 1/3, i.e., 1e-6. Tighter tolerance is default in JModelica.org.
nle_solver_regularization_tolerance real / -1.0 Tolerance for deciding when regularization should kick in (i.e. when condition number > reg tol).
nle_solver_step_limit_factor real / 10.0 Factor limiting the step-size taken by the nonlinear solver.
nle_solver_tol_factor real / 1.0E-4 Tolerance safety factor for the non-linear equation block solver. Used when external solver specifies relative tolerance.
normalize_minimum_time_problems boolean / true When this option is set to true then minimum time optimal control problems encoded in Optimica are converted to fixed interval problems by scaling of the derivative variables.
rescale_after_singular_jac boolean / true When this option is set to true, scaling should be updated after singular jac was detected (only active if use_automatic_scaling is set).
rescale_each_step boolean / false When this option is set to true, scaling should be updated at every step (only active if use_automatic_scaling is set).
residual_equation_scaling integer / 1 Equations scaling mode in equation block solvers:0-no scaling,1-automatic scaling,2-manual scaling.
runtime_log_to_file boolean / false Enable to write log messages from the runtime directly to a file, besides passing it to the FMU loader (e.g. FMIL). The log file name is generated based on the FMU name.
state_initial_equations boolean / false Neglect initial equations in the model and add initial equations, and parameters, for the states.
state_start_values_fixed boolean / false This option enables the user to specify if initial equations should be generated automatically for differentiated variables even though the fixed attribute is equal to fixed. Setting this option to true is, however, often practical in optimization problems.
use_Brent_in_1d boolean / false Use Brent search to improve accuracy in solution of 1D non-linear equations.
use_jacobian_equilibration boolean / false Set to true jacobian equilibration should be utilized in equation block solvers to improve linear solver accuracy.
write_iteration_variables_to_file boolean / false If the option is set to true, two text files containing one iteration variablename per row is written to disk. The files contain the iteration variables for the DAE and the DAE initialization system respectively. The files are output to the resource directory.
write_tearing_pairs_to_file boolean / false If the option is set to true, two text files containing tearing pairs is written to disk. The files contains the tearing pairs for the DAE and theDAE initialization system respectively. The files are outputed to the working directory.


2.6. Compile in 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 parameter 'jvm_args'.

The following example demonstrates how to set the maximum heap space for the JVM to one gigabyte by setting the parameter 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 possible and can be turned on by setting the argument separate_process to False when calling e.g. compile_fmu.

2.7. Compilation in more detail

Compiling with compile_fmu or compile_jmu bundles quite a few steps required for the compilation from model file to FMU or JMU. 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 in Chapter 1.

2.7.1. Create 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.7.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. So, to compile a JMU in this way, a few compiler options need to be set manually.

# 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.7.3. Code generation

The next step is the code generation which produces C code containing the model equations and a couple of XML files containing model meta data such as variable names and types. Note that in the compile_fmux case, only XML code is generated in this step.

# Generate code
mc.generate_code(flat_rep, target)