modelicares.exps

Set up and help run Modelica simulation experiments.

This module supports two approaches to managing simulations. The first is to create a Modelica script (using write_script()) and run it within a Modelica environment (see the scripts in examples/ChuaCircuit/), which translates and simulates the models with the prescribed settings. The second approach is to execute pre-translated models. The run_models() method handles this by writing to initialization file(s) (e.g, dsin.txt) and launching the appropriate model executables. The advantage of the first approach is that formal parameters (those that are hard-coded during translation) can be adjusted. However, the second approach is faster because it does not require a model to be recompiled when only tunable parameters (those that are not hard-coded during translation) are changed.

The first step in either case is to create a dictionary to specify model parameters and other settings for simulation experiment. A single model parameter may have multiple possible values. The dictionary is passed to the gen_experiments() function (see that function for a description of the dictionary format), which combines the values of all the variables (by piecewise alignment or permutation) and returns a generator to step through the experiments. Finally, the generator is passed to the write_script() or run_models() function (see the first paragraph).

Classes:

Functions:

Submodules:

  • doe - Functions for design of experiments (DOE)
class modelicares.exps.Experiment

Bases: modelicares.exps.Experiment

namedtuple to represent a simulation experiment

Instances of this class may be used in the experiments argument of write_script() and run_models(), although there are some differences in the entries (see those functions for details).

Example:

>>> experiment = Experiment('ChuaCircuit', params={'L.L': 18}, args={})
>>> experiment.model
'ChuaCircuit'
class modelicares.exps.ParamDict

Bases: dict

Dictionary that prints its items (string mapping) as nested tuple-based modifiers, formatted for Modelica

Otherwise, this class is the same as dict. The underlying structure is not nested or reformatted—only the informal representation (__str__()).

In printing this dictionary (string representation), each key is interpreted as a parameter name (including the full model path in Modelica dot notation) and each entry is a parameter value. The value may be a number (integer or float), Boolean constant (in Python format—True or False, not ‘true’ or ‘false’), string, or NumPy arrays of these. Modelica strings must be given with double quotes included (e.g., ‘“hello”’). Enumerations may be used as values (e.g., ‘Axis.x’). Values may include functions, but the entire value must be expressed as a Python string (e.g., ‘fill(true, 2, 2)’). Items with a value of None are not shown.

Redeclarations and other prefixes must be included in the key along with the name of the instance (e.g., ‘redeclare Region regions[n_x, n_y, n_z]’). The single quotes must be explicitly included for instance names that contain symbols (e.g., “‘H+’”).

Note that Python dictionaries do not preserve order.

Examples:

>>> import numpy as np

>>> d = ParamDict({'a': 1, 'b.c': np.array([2, 3]), 'b.d': False,
...                'b.e': '"hello"', 'b.f': None})
>>> print(d) 
(a=1, b(c={2, 3}, e="hello", d=false))

The formal representation (and the internal structure) is not affected:

>>> d 
{'a': 1, 'b.c': array([2, 3]), 'b.f': None, 'b.e': '"hello"', 'b.d': False}

An empty dictionary prints as an empty string (not “()”):

>>> print(ParamDict({}))
modelicares.exps.gen_experiments(models=None, params={}, args={}, design=<function fullfact at 0x7f0d9b04db18>)

Return a generator for a set of simulation experiments using permutation or simple element-wise grouping.

The generator yields instances of Experiment—named tuples of (model, params, args), where model is the name of a single model (type str), params is a specialized dictionary (ParamDict) of model parameter names and values, and arg_dict is a dictionary (dict) of command arguments (keyword and value) for the Modelica tool or environment.

Arguments:

  • models: List of model names (including the full model path in Modelica dot notation)

  • params: Dictionary of model parameters

    Each key is a variable name and each entry is a list of values. The keys must indicate the hierarchy within the model—either in Modelica dot notation or via nested dictionaries.

  • args: Dictionary of command arguments for the Modelica tool or environment (e.g., to the simulateModel() command in Dymola)

    Each key is an argument name and each entry is a list of settings.

  • design: Method of generating the simulation experiments (i.e., design of experiments)

    This is a function that returns a iterable object that contains or generates the simulation settings. Several options are available in doe.

Example 1 (element-wise list of experiments):

>>> experiments = gen_experiments(
...                  ['Modelica.Electrical.Analog.Examples.ChuaCircuit']*3,
...                  {'L.L': [16, 18, 20], 'C2.C': [80, 100, 120]},
...                  design=doe.aslisted)
>>> for experiment in experiments: 
...     print(experiment.model + str(experiment.params))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=16))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=100), L(L=18))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=120), L(L=20))
>>> # Note that the model name must be repeated in the models argument.

Example 2 (one-factor-at-a-time; first entries are baseline):

>>> experiments = gen_experiments(
...                  ['Modelica.Electrical.Analog.Examples.ChuaCircuit'],
...                  {'L.L': [16, 18, 20], 'C2.C': [80, 100, 120]},
...                  design=doe.ofat)
>>> for experiment in experiments: 
...     print(experiment.model + str(experiment.params))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=16))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=18))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=20))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=100), L(L=16))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=120), L(L=16))

Example 3 (permutation—full-factorial design of experiments):

>>> experiments = gen_experiments(
...                  ['Modelica.Electrical.Analog.Examples.ChuaCircuit'],
...                  {'L.L': [16, 18, 20], 'C2.C': [80, 100, 120]},
...                  design=doe.fullfact)
>>> for experiment in experiments: 
...     print(experiment.model + str(experiment.params))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=16))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=100), L(L=16))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=120), L(L=16))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=18))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=100), L(L=18))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=120), L(L=18))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=80), L(L=20))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=100), L(L=20))
Modelica.Electrical.Analog.Examples.ChuaCircuit(C2(C=120), L(L=20))

Example 4 (parameters given in nested form):

>>> models = ['Modelica.Mechanics.MultiBody.Examples.Systems.RobotR3.oneAxis']
>>> params = dict(axis=dict(motor=dict(i_max=[5, 15],
...                                    Ra=dict(R=[200, 300]))))
>>> for experiment in gen_experiments(models, params):
...     print(experiment.model + str(experiment.params)) 
Modelica.Mechanics.MultiBody.Examples.Systems.RobotR3.oneAxis(axis(motor(i_max=5, Ra(R=200))))
Modelica.Mechanics.MultiBody.Examples.Systems.RobotR3.oneAxis(axis(motor(i_max=15, Ra(R=200))))
Modelica.Mechanics.MultiBody.Examples.Systems.RobotR3.oneAxis(axis(motor(i_max=5, Ra(R=300))))
Modelica.Mechanics.MultiBody.Examples.Systems.RobotR3.oneAxis(axis(motor(i_max=15, Ra(R=300))))

Note that the underlying representation of the parameters is actually flat:

>>> for experiment in gen_experiments(models, params): 
...     experiment.params
{'axis.motor.Ra.R': 200, 'axis.motor.i_max': 5}
{'axis.motor.Ra.R': 200, 'axis.motor.i_max': 15}
{'axis.motor.Ra.R': 300, 'axis.motor.i_max': 5}
{'axis.motor.Ra.R': 300, 'axis.motor.i_max': 15}

Also note that Python dictionaries do not preserve order (and it is not necessary here).

modelicares.exps.modelica_str(value)

Express a Python value as a Modelica string

A Boolean variable (bool) becomes ‘true’ or ‘false’ (lowercase).

For NumPy arrays, square brackets are curled.

Examples:

Booleans:

>>> # Booleans:
>>> modelica_str(True)
'true'

Arrays:

>>> import numpy as np

>>> modelica_str(np.array([[1, 2], [3, 4]]))
'{{1, 2}, {3, 4}}'

>>> modelica_str(np.array([[True, True], [False, False]]))
'{{true, true}, {false, false}}'
modelicares.exps.read_params(names, fname='dsin.txt')

Read parameter values from an initialization or final values file (e.g., dsin.txt or dsfinal.txt).

Arguments:

  • names: Parameter name or list of names (with full model path in Modelica dot notation)

    A parameter name includes array indices (if any) in Modelica representation (1-based indexing); the values are scalar.

  • fname: Name of the file (may include the file path)

Example:

>>> read_params(['Td', 'Ti'], 'examples/dsin.txt')
[0.1, 0.5]
modelicares.exps.run_models(experiments=[(None, {}, {})], filemap={'dslog.txt': '%s_%i.log', 'dsres.mat': '%s_%i.mat'})

Run Modelica models via pairs of executables and initialization files.

Warning

This function has not yet been implemented.

Arguments:

  • experiments: Tuple or (list or generator of) tuples specifying the simulation experiment(s)

    The first entry of each tuple is the name of the model executable. The second is a dictionary of model parameter names and values. The third is a dictionary of simulation settings (keyword and value).

    Each tuple may be (optionally) an instance of the tuple subclass Experiment, which names the entries as model, params, and args. These designations are used below for clarity.

    model may include the file path. It is not necessary to include the extension (e.g., ”.exe”). There must be a corresponding model initialization file on the same path with the same base name and the extension ”.in”. For Dymola®, the executable is the “dymosim” file (possibly renamed) and the initialization file is a renamed ‘dsin.txt’ file.

    The keys or variable names in the params dictionary must indicate the hierarchy within the model—either in Modelica dot notation or via nested dictionaries. The items in the dictionary must correspond to parameters in the initialization file. In Dymola, these are integers or floating point numbers. Therefore, arrays must be broken into scalars by indicating the indices (Modelica 1-based indexing) in the key along with the variable name. Enumerations and Booleans must be given as their unsigned integer equivalents (e.g., 0 for False). Strings and prefixes are not supported.

    Items with values of None in params and args are skipped.

  • filemap: Dictionary of result file mappings

    Each key is the path/name of a file that is generated during simulation (source) and each value is the path/name it will be copied as (destination). The sources and destinations are relative to the directory indicated by the model subargument. ‘%s’ may be included in the destination to indicate the model name (model) without the full path or extension. ‘%i’ may be included to indicate the simulation number in the sequence of experiments.

There are no return values.

modelicares.exps.write_params(params, fname='dsin.txt')

Write parameter values to a simulation initialization file (e.g., dsin.txt).

Arguments:

  • params: Dictionary of parameters

    Each key is a parameter name (including the full model path in Modelica dot notation) and each entry is a parameter value. The parameter name includes array indices (if any) in Modelica representation (1-based indexing). The values must be representable as scalar numbers (integer or floating point). True and False (not ‘true’ and ‘false’) are automatically mapped to 1 and 0. Enumerations must be given explicitly as the unsigned integer equivalent. Strings, functions, redeclarations, etc. are not supported.

  • fname: Name of the file (may include the file path)

Example:

>>> write_params({'Td': 1, 'Ti': 5}, 'examples/dsin.txt')

This updates the appropriate lines in examples/dsin.txt:

-1      10                  0       0                  1  280   # L.L
...
-1      15                  0  1.000000000000000E+100  1  280   # C1.C
modelicares.exps.write_script(experiments=[(None, {}, {})], packages=[], working_dir='~/Documents/Modelica', fname='run_sims.mos', command='simulateModel', results=['dsin.txt', 'dslog.txt', 'dsres.mat', 'dymosim%x', 'dymolalg.txt'])

Write a Modelica script to run simulations.

Arguments:

  • experiments: Tuple or (list or generator of) tuples specifying the simulation experiment(s)

    The first entry of each tuple is the name of the model to be simulated, including the full path in Modelica dot notation. The second is a dictionary of parameter names and values. The third is a dictionary of command arguments (keyword and value) for the Modelica tool or environment (see below for Dymola®).

    Each tuple may be (optionally) an instance of the tuple subclass Experiment, which names the entries as model, params, and args. These designations are used below for clarity.

    The keys or variable names in the params dictionary must indicate the hierarchy within the model—either in Modelica dot notation or via nested dictionaries. If model is None, then params is not used. Python values are automatically mapped to their Modelica equivalent (see ParamDict.__str__()). Redeclarations and other prefixes must be included in the keys along with the variable names.

    gen_experiments() can be used to create a generator for this argument.

    Items with values of None in params and args are skipped.

  • working_dir: Working directory (for the executable, log files, etc.)

    ‘~’ may be included to represent the user directory.

  • packages: List of Modelica packages that should be preloaded or scripts that should be run

    Each may be a “*.mo” file, a folder that contains a “package.mo” file, or a “*.mos” file. The path may be absolute or relative to working_dir. It may be necessary to include in packages the file or folder that contains the model specified by the model subargument, but the Modelica Standard Library generally does not need to be included. If an entry is a script (“*.mos”), it is run from its folder.

  • fname: Name of the script file to be written (usually in the form

    “*.mos”)

    This may include the path (‘~’ for user directory). The results will be stored relative to the same folder. If the folder does not exist, it will be created.

  • command: Simulation or other command to the Modelica tool or environment

    Instead of the default (‘simulateModel’), this could be ‘linearizeModel’ to create a state space representation or ‘translateModel’ to create model executables without running them.

  • results: List of files to copy to the results folder

    Each entry is the path/name of a file that is generated during simulation. The path is relative to the working directory. ‘%x’ may be included in the filename to represent ‘.exe’ if the operating system is Windows and ‘’ otherwise. The result folders are named by the number of the simulation run and placed within the folder that contains the simulation script (fname).

If command is ‘simulateModel’ and the Modelica environment is Dymola®, then the following keywords may be used in args (see experiments above). The defaults (shown in parentheses) are applied by Dymola®—not by this function.

  • startTime (0): Start of simulation
  • stopTime (1): End of simulation
  • numberOfIntervals (0): Number of output points
  • outputInterval (0): Distance between output points
  • method (“Dassl”): Integration method
  • tolerance (0.0001): Tolerance of integration
  • fixedstepsize (0): Fixed step size for Euler
  • resultFile (“dsres.mat”): Where to store result

Note that problem is not listed. It is generated from model and params. If model is None, the currently/previously translated model will be simulated.

Returns:

  1. List of model names without full model paths
  2. Directory where the script has been saved

Example 1 (single simulation):

>>> from modelicares import Experiment, write_script

>>> experiment = Experiment(model='Modelica.Electrical.Analog.Examples.ChuaCircuit',
...                         params={},
...                         args=dict(stopTime=2500))
>>> write_script(experiment,
...              fname="examples/ChuaCircuit/run_sims1.mos") 
(['ChuaCircuit'], '...examples/ChuaCircuit')

In examples/ChuaCircuit/run_sims1.mos:

// Modelica experiment script written by modelicares ...
import Modelica.Utilities.Files.copy;
import Modelica.Utilities.Files.createDirectory;
Advanced.TranslationInCommandLog = true "Also include translation log in command log";
cd(".../Documents/Modelica");
destination = ".../examples/ChuaCircuit/";

// Experiment 1
ok = simulateModel(problem="Modelica.Electrical.Analog.Examples.ChuaCircuit", stopTime=2500);
if ok then
    savelog();
    createDirectory(destination + "1");
    copy("dsin.txt", destination + "1/dsin.txt", true);
    copy("dslog.txt", destination + "1/dslog.txt", true);
    copy("dsres.mat", destination + "1/dsres.mat", true);
    copy("dymosim", destination + "1/dymosim", true);
    copy("dymolalg.txt", destination + "1/dymolalg.txt", true);
end if;
clearlog();

exit();

where ”...” depends on the local system.

Example 2 (full-factorial design of experiments):

>>> from modelicares import gen_experiments, write_script

>>> experiments = gen_experiments(
...     models=["Modelica.Electrical.Analog.Examples.ChuaCircuit"],
...     params={'L.L': [18, 20],
...             'C1.C': [8, 10],
...             'C2.C': [80, 100, 120]})
>>> write_script(experiments, fname="examples/ChuaCircuit/run_sims2.mos") 
(['ChuaCircuit', ..., 'ChuaCircuit'], '...examples/ChuaCircuit')

In examples/ChuaCircuit/run_sims2.mos, there are commands to run and save results from twelve simulations.