3. Simulation of Models

Simulation of models in JModelica.org is performed via the simulate method of a model object. The FMU model objects in JModelica.org are located in PyFMI:

FMUModelME* / FMUModelCS* also supports compiled models from other simulation/modelling tools that follow the FMI standard (extension .fmu) (either Model exchange FMUs or Co-Simulation FMUs). The support is both for FMI version 1.0 and FMI version 2.0 RC2. For more information about compiling a model in JModelica.org see Chapter 4, Working with Models.

The simulation method is the preferred method for simulation of models and which by default is connected to the Assimulo simulation package but can also be connected to other simulation platforms. The simulation method for FMUModelME* / FMUModelCS* is defined as:

class FMUModel(ME/CS)(...)
    ...
    def simulate(self,
             start_time=0.0,
             final_time=1.0,
             input=(),
             algorithm='AssimuloFMIAlg', 
             options={}):

And used in the following way:

res = FMUModel(ME/CS)*.simulate() # Using default values

For FMUModelCS*, the FMU contains the solver and is thus used (although using the same interface).

3.1. Convenience method, load_fmu

As that the FMI standard contains different specifications for Model exchange and Co-Simulation and will also differ between its own version, a convenience method, load_fmu has been created. This method is the preferred access point for loading an FMU and will return an instance of the appropriate underlying FMUModel(CS/ME)* class.

model = load_fmu("myFMU.fmu")

3.2. Arguments

The start and final time attributes are simply the time where the solver should start the integration and stop the integration. The input however is a bit more complex and is described in more detail in the following section. The algorithm attribute is where the different simulation package can be specified, however currently only a connection to Assimulo is supported and connected through the algorithm AssimuloFMIAlg for FMUModelME*.

3.2.1. Input

The input defines the input trajectories to the model and should be a 2-tuple consisting of the name(s) of the input variables and the second argument should be either a data matrix or a function. If the argument is a data matrix it should contain a time vector as the first column and the second column should correspond to the first name in the first argument and so forth. If instead the second argument is a function it should be defined to take the time as input and return the number of inputs in the order defined by the first argument.

For example, consider that we have a model with an input variable u1 and that the model should be driven by a sinus wave as input. Also we are interested in the interval 0 to 10.

import numpy as N
t = N.linspace(0.,10.,100)            # Create one hundred evenly spaced points
u = N.sin(t)                          # Create the input vector
u_traj = N.transpose(N.vstack((t,u))) # Create the data matrix and transpose 
                                      # it to the correct form

The above code have created the data matrix that we are interested in giving to the model as input, we just need to connect the data to a specific input variable, u1:

input_object = ('u1', u_traj)

Now we are ready to simulate using the input and simulate 10 seconds.

res = model.simulate(final_time=10, input=input_object)

If we on the other hand would have two input variables, u1 and u2 the script would instead look like:

import numpy as N
t = N.linspace(0.,10.,100)                     # Create one hundred evenly spaced points
u1 = N.sin(t)                                  # Create the first input vector
u2 = N.cos(t)                                  # Create the second input vector
u_traj = N.transpose(N.vstack((t,u1,u2)))      # Create the data matrix and 
                                               # transpose it to the correct form
input_object = (['u1','u2'], u_traj)
res = model.simulate(final_time=10, input=input_object)

Note that the variables are now a List of variables.

If we were to do the same example using input functions instead, the code would look like for the single input case:

input_object = ('u1', N.sin)

and for the double input case:

def input_function(t):
    return N.array([N.sin(t),N.cos(t)])

input_object = (['u1','u2'],input_function)

3.2.2. Options for FMUModelME1 and FMUModelME2

The options attribute are where options to the specified algorithm are stored and are preferably used together with:

opts = FMUModelME*.simulate_options()

which returns the default options for the default algorithm. Information about the available options can be viewed by typing help on the opts variable:

>>> help(opts)
   Options for the solving the FMU using the Assimulo simulation package.
   Currently, the only solver in the Assimulo package that fully supports
   simulation of FMUs is the solver CVode.
   
   ...

In Table 5.1, “General options for AssimuloFMIAlg.” the general options for the AssimuloFMIAlg algorithm are described while in Table 5.2, “Selection of solver arguments for CVode” a selection of the different solver arguments for the ODE solver CVode is shown. More information regarding the solver options can be found here, http://www.jmodelica.org/assimulo.

Table 5.1. General options for AssimuloFMIAlg.

OptionDefaultDescription
solver'CVode'Specifies the simulation method that is to be used. Currently supported solvers are, CVode, Radau5ODE, RungeKutta34, Dopri5, RodasODE, LSODAR, ExplicitEuler. Although the recommended solver is "CVode".
ncp0Number of communication points. If ncp is zero, the solver will return the internal steps taken.
initializeTrueIf set to True, the initializing algorithm defined in the FMU model is invoked, otherwise it is assumed the user have manually invoked model.initialize()
write_scaled_resultFalseSet this parameter to True to write the result to file without taking scaling into account. If the value of scaled is False, then the variable scaling factors of the model are used to reproduced the unscaled variable values.
result_file_nameEmpty string (default generated file name will be used)Specifies the name of the file where the simulation result is written. Setting this option to an empty string results in a default file name that is based on the name of the model class.
filterNoneA filter for choosing which variables to actually store result for. The syntax can be found in http://en.wikipedia.org/wiki/Glob_%28programming%29 . An example is filter = "*der" , store all variables ending with 'der' and filter = ["*der*", "summary*"], store all variables with "der" in the name and all variables starting with "summary".
result_handling"file"Specifies how the result should be handled. Either stored to file or stored in memory. One can also use a custom handler. Available options: "file", "memory", "custom"

Lets look at an example, consider that you want to simulate a FMU model using the solver CVode together with changing the discretization method (discr) from BDF to Adams:

...
opts = model.simulate_options()          # Retrieve the default options
#opts['solver'] = 'CVode'                # Not necessary, default solver is CVode
opts['CVode_options']['discr'] = 'Adams' # Change from using BDF to Adams
opts['initialize'] = False               # Don't initialize the model
model.simulate(options=opts)             # Pass in the options to simulate and simulate

It should also be noted from the above example the options regarding a specific solver, say the tolerances for CVode, should be stored in a double dictionary where the first is named after the solver concatenated with _options:

opts['CVode_options']['atol'] = 1.0e-6   # Options specific for CVode

For the general options, as changing the solver, they are accessed as a single dictionary:

opts['solver'] = 'CVode'  # Changing the solver
opts['ncp'] = 1000        # Changing the number of communication points.

Table 5.2. Selection of solver arguments for CVode

OptionDefaultDescription
discr'BDF'The discretization method. Can be either 'BDF' or 'Adams'
iter'Newton'The iteration method. Can be either 'Newton' or 'FixedPoint'.
maxord5The maximum order used. Maximum for 'BDF' is 5 while for the 'Adams' method the maximum is 12
maxhInfMaximum step-size. Positive float.
atolrtol*0.01*(nominal values of the continuous states)Absolute Tolerance. Can be an array of floats where each value corresponds to the absolute tolerance for the corresponding variable. Can also be a single positive float.
rtol1.0e-4The relative tolerance. The relative tolerance are retrieved from the 'default experiment' section in the XML-file and if not found are set to 1.0e-4

3.2.3. Options for FMUModelCS1 and FMUModelCS2

The options attribute are where options to the specified algorithm are stored and are preferably used together with:

opts = FMUModelCS*.simulate_options()

which returns the default options for the default algorithm. Information about the available options can be viewed by typing help on the opts variable:

>>> help(opts)
   Options for the solving the CS FMU.
   
   ...

In Table 5.3, “General options for FMICSAlg.” the general options for the FMICSAlg algorithm are described.

Table 5.3. General options for FMICSAlg.

OptionDefaultDescription
ncp500Number of communication points.
initializeTrueIf set to True, the initializing algorithm defined in the FMU model is invoked, otherwise it is assumed the user have manually invoked model.initialize()
write_scaled_resultFalseSet this parameter to True to write the result to file without taking scaling into account. If the value of scaled is False, then the variable scaling factors of the model are used to reproduced the unscaled variable values.
result_file_nameEmpty string (default generated file name will be used)Specifies the name of the file where the simulation result is written. Setting this option to an empty string results in a default file name that is based on the name of the model class.
filterNoneA filter for choosing which variables to actually store result for. The syntax can be found in http://en.wikipedia.org/wiki/Glob_%28programming%29 . An example is filter = "*der" , store all variables ending with 'der' and filter = ["*der*", "summary*"], store all variables with "der" in the name and all variables starting with "summary".
result_handling"file"Specifies how the result should be handled. Either stored to file or stored in memory. One can also use a custom handler. Available options: "file", "memory", "custom"

3.3. Return argument

The return argument from the simulate method is an object derived from a common result object ResultBase in algorithm_drivers.py with a few extra convenience methods for retrieving the result of a variable. The result object can be accessed in the same way as a dictionary type in Python with the name of the variable as key.

res = model.simulate()
y = res['y']           # Return the result for the variable/parameter/constant y
dery = res['der(y)']   # Return the result for the variable/parameter/constant der(y)

This can be done for all the variables, parameters and constants defined in the model and is the preferred way of retrieving the result. There are however some more options available in the result object, see Table 5.4, “Result Object”.

Table 5.4. Result Object

OptionTypeDescription
optionsPropertyGets the options object that was used during the simulation.
solverPropertyGets the solver that was used during the integration.
result_filePropertyGets the name of the generated result file.
is_variable(name)MethodReturns True if the given name is a time-varying variable.
data_matrixPropertyGets the raw data matrix.
is_negated(name)MethodReturns True if the given name is negated in the result matrix.
get_column(name)MethodReturns the column number in the data matrix which corresponds to the given variable.