Introduction

The OpenModelica system described in this document has both short-term and long-term goals:

  • The short-term goal is to develop an efficient interactive computational environment for the Modelica language, as well as a rather complete implementation of the language. It turns out that with support of appropriate tools and libraries, Modelica is very well suited as a computational language for development and execution of both low level and high level numerical algorithms, e.g. for control system design, solving nonlinear equation systems, or to develop optimization algorithms that are applied to complex applications.

  • The long-term goal is to have a complete reference implementation of the Modelica language, including simulation of equation based models and additional facilities in the programming environment, as well as convenient facilities for research and experimentation in language design or other research activities. However, our goal is not to reach the level of performance and quality provided by current commercial Modelica environments that can handle large models requiring advanced analysis and optimization by the Modelica compiler.

The long-term research related goals and issues of the OpenModelica open source implementation of a Modelica environment include but are not limited to the following:

  • Development of a complete formal specification of Modelica, including both static and dynamic semantics. Such a specification can be used to assist current and future Modelica implementers by providing a semantic reference, as a kind of reference implementation.

  • Language design, e.g. to further extend the scope of the language, e.g. for use in diagnosis, structural analysis, system identification, etc., as well as modeling problems that require extensions such as partial differential equations, enlarged scope for discrete modeling and simulation, etc.

  • Language design to improve abstract properties such as expressiveness, orthogonality, declarativity, reuse, configurability, architectural properties, etc.

  • Improved implementation techniques, e.g. to enhance the performance of compiled Modelica code by generating code for parallel hardware.

  • Improved debugging support for equation based languages such as Modelica, to make them even easier to use.

  • Easy-to-use specialized high-level (graphical) user interfaces for certain application domains.

  • Visualization and animation techniques for interpretation and presentation of results.

  • Application usage and model library development by researchers in various application areas.

The OpenModelica environment provides a test bench for language design ideas that, if successful, can be submitted to the Modelica Association for consideration regarding possible inclusion in the official Modelica standard.

The current version of the OpenModelica environment allows most of the expression, algorithm, and function parts of Modelica to be executed interactively, as well as equation models and Modelica functions to be compiled into efficient C code. The generated C code is combined with a library of utility functions, a run-time library, and a numerical DAE solver.

System Overview

The OpenModelica environment consists of several interconnected subsystems, as depicted in Figure 1.

media/systemoverview.png

Figure 1 The architecture of the OpenModelica environment. Arrows denote data and control flow. The interactive session handler receives commands and shows results from evaluating commands and expressions that are translated and executed. Several subsystems provide different forms of browsing and textual editing of Modelica code. The debugger currently provides debugging of an extended algorithmic subset of Modelica.

The following subsystems are currently integrated in the OpenModelica environment:

  • An interactive session handler, that parses and interprets commands and Modelica expressions for evaluation, simulation, plotting, etc. The session handler also contains simple history facilities, and completion of file names and certain identifiers in commands.

  • A Modelica compiler subsystem, translating Modelica to C code, with a symbol table containing definitions of classes, functions, and variables. Such definitions can be predefined, user-defined, or obtained from libraries. The compiler also includes a Modelica interpreter for interactive usage and constant expression evaluation. The subsystem also includes facilities for building simulation executables linked with selected numerical ODE or DAE solvers.

  • An execution and run-time module. This module currently executes compiled binary code from translated expressions and functions, as well as simulation code from equation based models, linked with numerical solvers. In the near future event handling facilities will be included for the discrete and hybrid parts of the Modelica language.

  • Eclipse plugin editor/browser. The Eclipse plugin called MDT (Modelica Development Tooling) provides file and class hierarchy browsing and text editing capabilities, rather analogous to previously described Emacs editor/browser. Some syntax highlighting facilities are also included. The Eclipse framework has the advantage of making it easier to add future extensions such as refactoring and cross referencing support.

  • OMNotebook DrModelica model editor. This subsystem provides a lightweight notebook editor, compared to the more advanced Mathematica notebooks available in MathModelica. This basic functionality still allows essentially the whole DrModelica tutorial to be handled. Hierarchical text documents with chapters and sections can be represented and edited, including basic formatting. Cells can contain ordinary text or Modelica models and expressions, which can be evaluated and simulated. However, no mathematical typesetting facilities are yet available in the cells of this notebook editor.

  • Graphical model editor/browser OMEdit. This is a graphical connection editor, for component based model design by connecting instances of Modelica classes, and browsing Modelica model libraries for reading and picking component models. The graphical model editor also includes a textual editor for editing model class definitions, and a window for interactive Modelica command evaluation.

  • Optimization subsystem OMOptim. This is an optimization subsystem for OpenModelica, currently for design optimization choosing an optimal set of design parameters for a model. The current version has a graphical user interface, provides genetic optimization algorithms and Pareto front optimization, works integrated with the simulators and automatically accesses variables and design parameters from the Modelica model.

  • Dynamic Optimization subsystem. This is dynamic optimization using collocation methods, for Modelica models extended with optimization specifications with goal functions and additional constraints. This subsystem is integrated with in the OpenModelica compiler.

  • Modelica equation model debugger. The equation model debugger shows the location of an error in the model equation source code. It keeps track of the symbolic transformations done by the compiler on the way from equations to low-level generated C code, and also explains which transformations have been done.

  • Modelica algorithmic code debugger. The algorithmic code Modelica debugger provides debugging for an extended algorithmic subset of Modelica, excluding equation-based models and some other features, but including some meta-programming and model transformation extensions to Modelica. This is a conventional full-feature debugger, using Eclipse for displaying the source code during stepping, setting breakpoints, etc. Various back-trace and inspection commands are available. The debugger also includes a data-view browser for browsing hierarchical data such as tree- or list structures in extended Modelica.

Interactive Session with Examples

The following is an interactive session using the interactive session handler in the OpenModelica environment, called OMShell - the OpenModelica Shell. Most of these examples are also available in the OMNotebook with DrModelica and DrControl UsersGuideExamples.onb as well as the testmodels in:

>>> getInstallationDirectoryPath() + "/share/doc/omc/testmodels/"
"«OPENMODELICAHOME»/share/doc/omc/testmodels/"

The following commands were run using OpenModelica version:

>>> getVersion()
"OMCompiler v1.25.0-dev.184+g229c087a3c"

Starting the Interactive Session

Under Windows, go to the Start Menu and run OpenModelica->OpenModelica Shell which responds with an interaction window.

Under Linux, run OMShell-terminal to start the interactive session at the prompt.

We enter an assignment of a vector expression, created by the range construction expression 1:12, to be stored in the variable x. The value of the expression is returned.

>>> x := 1:12
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}

Using the Interactive Mode

When running OMC in interactive mode (for instance using OMShell) one can make load classes and execute commands. Here we give a few example sessions.

Example Session 1

>>> model A Integer t = 1.5; end A; //The type is Integer but 1.5 is of Real Type
{A}
>>> instantiateModel(A)
""
"[<interactive>:1:9-1:23:writable] Error: Type mismatch in binding t = 1.5, expected subtype of Integer, got type Real.
"

Example Session 2

If you do not see the error-message when running the example, use the command getErrorString().

model C
  Integer a;
  Real b;
equation
  der(a) = b; // der(a) is illegal since a is not a Real number
  der(b) = 12.0;
end C;
>>> instantiateModel(C)
""

Error

[<interactive>:5:3-5:13:writable] Error: Argument 'a' of der is not differentiable.

Trying the Bubblesort Function

Load the function bubblesort, either by using the pull-down menu File->Load Model, or by explicitly giving the command:

>>> loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/bubblesort.mo")
true

The function bubblesort is called below to sort the vector x in descending order. The sorted result is returned together with its type. Note that the result vector is of type Real[:], instantiated as Real[12], since this is the declared type of the function result. The input Integer vector was automatically converted to a Real vector according to the Modelica type coercion rules. The function is automatically compiled when called if this has not been done before.

>>> bubblesort(x)
{12.0, 11.0, 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}

Another call:

>>> bubblesort({4,6,2,5,8})
{8.0, 6.0, 5.0, 4.0, 2.0}

Trying the system and cd Commands

It is also possible to give operating system commands via the system utility function. A command is provided as a string argument. The example below shows the system utility applied to the UNIX command cat, which here outputs the contents of the file bubblesort.mo to the output stream when running omc from the command-line.

>>> system("cat '"+getInstallationDirectoryPath()+"/share/doc/omc/testmodels/bubblesort.mo' > bubblesort.mo")
0
function bubblesort
  input Real[:] x;
  output Real[size(x,1)] y;
protected
  Real t;
algorithm
  y := x;
  for i in 1:size(x,1) loop
    for j in 1:size(x,1) loop
      if y[i] > y[j] then
        t := y[i];
        y[i] := y[j];
        y[j] := t;
      end if;
    end for;
  end for;
end bubblesort;

Note: The output emitted into stdout by system commands is put into log-files when running the CORBA-based clients, not into the visible GUI windows. Thus the text emitted by the above cat command would not be returned, which is why it is redirected to another file.

A better way to read the content of files would be the readFile command:

>>> readFile("bubblesort.mo")
function bubblesort
  input Real[:] x;
  output Real[size(x,1)] y;
protected
  Real t;
algorithm
  y := x;
  for i in 1:size(x,1) loop
    for j in 1:size(x,1) loop
      if y[i] > y[j] then
        t := y[i];
        y[i] := y[j];
        y[j] := t;
      end if;
    end for;
  end for;
end bubblesort;

The system command only returns a success code (0 = success).

>>> system("dir")
0
>>> system("Non-existing command")
127

Another built-in command is cd, the change current directory command. The resulting current directory is returned as a string.

>>> dir:=cd()
"«DOCHOME»"
>>> cd("source")
"«DOCHOME»/source"
>>> cd(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/")
"/var/lib/jenkins1/ws/OpenModelica_master_2/build/share/doc/omc/testmodels"
>>> cd(dir)
"«DOCHOME»"

Modelica Library and DCMotor Model

We load a model, here the whole Modelica standard library, which also can be done through the File->Load Modelica Library menu item:

>>> loadModel(Modelica, {"3.2.3"})
true

We also load a file containing the dcmotor model:

>>> loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/dcmotor.mo")
true

Note

Notification: dcmotor requested package Modelica of version 3.2.2. Modelica 3.2.3 is used instead which states that it is fully compatible without conversion script needed.

It is simulated:

>>> simulate(dcmotor, startTime=0.0, stopTime=10.0)
record SimulationResult
    resultFile = "«DOCHOME»/dcmotor_res.mat",
    simulationOptions = "startTime = 0.0, stopTime = 10.0, numberOfIntervals = 500, tolerance = 1e-6, method = 'dassl', fileNamePrefix = 'dcmotor', options = '', outputFormat = 'mat', variableFilter = '.*', cflags = '', simflags = ''",
    messages = "LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.
LOG_SUCCESS       | info    | The simulation finished successfully.
",
    timeFrontend = 0.26369832699999995,
    timeBackend = 0.029449207,
    timeSimCode = 0.007882847,
    timeTemplates = 0.21680527400000002,
    timeCompile = 1.2035620630000001,
    timeSimulation = 0.067542882,
    timeTotal = 1.789131235
end SimulationResult;

Note

Notification: dcmotor requested package Modelica of version 3.2.2. Modelica 3.2.3 is used instead which states that it is fully compatible without conversion script needed.

We list the source code of the model:

>>> list(dcmotor)
model dcmotor
  import Modelica.Electrical.Analog.Basic;
  Basic.Resistor resistor1(R = 10);
  Basic.Inductor inductor1(L = 0.2, i.fixed = true);
  Basic.Ground ground1;
  Modelica.Mechanics.Rotational.Components.Inertia load(J = 1, phi.fixed = true, w.fixed = true);
  Basic.EMF emf1(k = 1.0);
  Modelica.Blocks.Sources.Step step1;
  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage1;
equation
  connect(step1.y, signalVoltage1.v);
  connect(signalVoltage1.p, resistor1.p);
  connect(resistor1.n, inductor1.p);
  connect(inductor1.n, emf1.p);
  connect(emf1.flange, load.flange_a);
  connect(signalVoltage1.n, ground1.p);
  connect(ground1.p, emf1.n);
  annotation(
    uses(Modelica(version = "3.2.2")));
end dcmotor;

We test code instantiation of the model to flat code:

>>> instantiateModel(dcmotor)
class dcmotor
  parameter Real resistor1.R(quantity = "Resistance", unit = "Ohm", start = 1.0) = 10.0 "Resistance at temperature T_ref";
  parameter Real resistor1.T_ref(quantity = "ThermodynamicTemperature", unit = "K", displayUnit = "degC", min = 0.0, start = 288.15, nominal = 300.0) = 300.15 "Reference temperature";
  parameter Real resistor1.alpha(quantity = "LinearTemperatureCoefficient", unit = "1/K") = 0.0 "Temperature coefficient of resistance (R_actual = R*(1 + alpha*(T_heatPort - T_ref))";
  Real resistor1.v(quantity = "ElectricPotential", unit = "V") "Voltage drop of the two pins (= p.v - n.v)";
  Real resistor1.i(quantity = "ElectricCurrent", unit = "A") "Current flowing from pin p to pin n";
  Real resistor1.p.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real resistor1.p.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real resistor1.n.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real resistor1.n.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  final parameter Boolean resistor1.useHeatPort = false "=true, if heatPort is enabled";
  parameter Real resistor1.T(quantity = "ThermodynamicTemperature", unit = "K", displayUnit = "degC", min = 0.0, start = 288.15, nominal = 300.0) = resistor1.T_ref "Fixed device temperature if useHeatPort = false";
  Real resistor1.LossPower(quantity = "Power", unit = "W") "Loss power leaving component via heatPort";
  Real resistor1.T_heatPort(quantity = "ThermodynamicTemperature", unit = "K", displayUnit = "degC", min = 0.0, start = 288.15, nominal = 300.0) "Temperature of heatPort";
  Real resistor1.R_actual(quantity = "Resistance", unit = "Ohm") "Actual resistance = R*(1 + alpha*(T_heatPort - T_ref))";
  Real inductor1.v(quantity = "ElectricPotential", unit = "V") "Voltage drop of the two pins (= p.v - n.v)";
  Real inductor1.i(quantity = "ElectricCurrent", unit = "A", start = 0.0, fixed = true) "Current flowing from pin p to pin n";
  Real inductor1.p.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real inductor1.p.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real inductor1.n.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real inductor1.n.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  parameter Real inductor1.L(quantity = "Inductance", unit = "H", start = 1.0) = 0.2 "Inductance";
  Real ground1.p.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real ground1.p.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real load.flange_a.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "Absolute rotation angle of flange";
  Real load.flange_a.tau(quantity = "Torque", unit = "N.m") "Cut torque in the flange";
  Real load.flange_b.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "Absolute rotation angle of flange";
  Real load.flange_b.tau(quantity = "Torque", unit = "N.m") "Cut torque in the flange";
  parameter Real load.J(quantity = "MomentOfInertia", unit = "kg.m2", min = 0.0, start = 1.0) = 1.0 "Moment of inertia";
  final parameter enumeration(never, avoid, default, prefer, always) load.stateSelect = StateSelect.default "Priority to use phi and w as states";
  Real load.phi(quantity = "Angle", unit = "rad", displayUnit = "deg", fixed = true, stateSelect = StateSelect.default) "Absolute rotation angle of component";
  Real load.w(quantity = "AngularVelocity", unit = "rad/s", fixed = true, stateSelect = StateSelect.default) "Absolute angular velocity of component (= der(phi))";
  Real load.a(quantity = "AngularAcceleration", unit = "rad/s2") "Absolute angular acceleration of component (= der(w))";
  final parameter Boolean emf1.useSupport = false "= true, if support flange enabled, otherwise implicitly grounded";
  parameter Real emf1.k(quantity = "ElectricalTorqueConstant", unit = "N.m/A", start = 1.0) = 1.0 "Transformation coefficient";
  Real emf1.v(quantity = "ElectricPotential", unit = "V") "Voltage drop between the two pins";
  Real emf1.i(quantity = "ElectricCurrent", unit = "A") "Current flowing from positive to negative pin";
  Real emf1.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "Angle of shaft flange with respect to support (= flange.phi - support.phi)";
  Real emf1.w(quantity = "AngularVelocity", unit = "rad/s") "Angular velocity of flange relative to support";
  Real emf1.tau(quantity = "Torque", unit = "N.m") "Torque of flange";
  Real emf1.tauElectrical(quantity = "Torque", unit = "N.m") "Electrical torque";
  Real emf1.p.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real emf1.p.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real emf1.n.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real emf1.n.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real emf1.flange.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "Absolute rotation angle of flange";
  Real emf1.flange.tau(quantity = "Torque", unit = "N.m") "Cut torque in the flange";
  protected parameter Real emf1.fixed.phi0(quantity = "Angle", unit = "rad", displayUnit = "deg") = 0.0 "Fixed offset angle of housing";
  protected Real emf1.fixed.flange.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "Absolute rotation angle of flange";
  protected Real emf1.fixed.flange.tau(quantity = "Torque", unit = "N.m") "Cut torque in the flange";
  protected Real emf1.internalSupport.tau(quantity = "Torque", unit = "N.m") = -emf1.tau "External support torque (must be computed via torque balance in model where InternalSupport is used; = flange.tau)";
  protected Real emf1.internalSupport.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "External support angle (= flange.phi)";
  protected Real emf1.internalSupport.flange.phi(quantity = "Angle", unit = "rad", displayUnit = "deg") "Absolute rotation angle of flange";
  protected Real emf1.internalSupport.flange.tau(quantity = "Torque", unit = "N.m") "Cut torque in the flange";
  parameter Real step1.height = 1.0 "Height of step";
  Real step1.y "Connector of Real output signal";
  parameter Real step1.offset = 0.0 "Offset of output signal y";
  parameter Real step1.startTime(quantity = "Time", unit = "s") = 0.0 "Output y = offset for time < startTime";
  Real signalVoltage1.p.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real signalVoltage1.p.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real signalVoltage1.n.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real signalVoltage1.n.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
  Real signalVoltage1.v(unit = "V") "Voltage between pin p and n (= p.v - n.v) as input signal";
  Real signalVoltage1.i(quantity = "ElectricCurrent", unit = "A") "Current flowing from pin p to pin n";
equation
  emf1.internalSupport.flange.phi = emf1.fixed.flange.phi;
  step1.y = signalVoltage1.v;
  signalVoltage1.p.v = resistor1.p.v;
  resistor1.n.v = inductor1.p.v;
  inductor1.n.v = emf1.p.v;
  emf1.flange.phi = load.flange_a.phi;
  ground1.p.v = emf1.n.v;
  ground1.p.v = signalVoltage1.n.v;
  inductor1.p.i + resistor1.n.i = 0.0;
  emf1.p.i + inductor1.n.i = 0.0;
  load.flange_b.tau = 0.0;
  emf1.flange.tau + load.flange_a.tau = 0.0;
  emf1.internalSupport.flange.tau + emf1.fixed.flange.tau = 0.0;
  signalVoltage1.p.i + resistor1.p.i = 0.0;
  signalVoltage1.n.i + emf1.n.i + ground1.p.i = 0.0;
  assert(1.0 + resistor1.alpha * (resistor1.T_heatPort - resistor1.T_ref) >= 1e-15, "Temperature outside scope of model!");
  resistor1.R_actual = resistor1.R * (1.0 + resistor1.alpha * (resistor1.T_heatPort - resistor1.T_ref));
  resistor1.v = resistor1.R_actual * resistor1.i;
  resistor1.LossPower = resistor1.v * resistor1.i;
  resistor1.T_heatPort = resistor1.T;
  resistor1.v = resistor1.p.v - resistor1.n.v;
  0.0 = resistor1.p.i + resistor1.n.i;
  resistor1.i = resistor1.p.i;
  inductor1.L * der(inductor1.i) = inductor1.v;
  inductor1.v = inductor1.p.v - inductor1.n.v;
  0.0 = inductor1.p.i + inductor1.n.i;
  inductor1.i = inductor1.p.i;
  ground1.p.v = 0.0;
  load.phi = load.flange_a.phi;
  load.phi = load.flange_b.phi;
  load.w = der(load.phi);
  load.a = der(load.w);
  load.J * load.a = load.flange_a.tau + load.flange_b.tau;
  emf1.fixed.flange.phi = emf1.fixed.phi0;
  emf1.internalSupport.flange.tau = emf1.internalSupport.tau;
  emf1.internalSupport.flange.phi = emf1.internalSupport.phi;
  emf1.v = emf1.p.v - emf1.n.v;
  0.0 = emf1.p.i + emf1.n.i;
  emf1.i = emf1.p.i;
  emf1.phi = emf1.flange.phi - emf1.internalSupport.phi;
  emf1.w = der(emf1.phi);
  emf1.k * emf1.w = emf1.v;
  emf1.tau = -emf1.k * emf1.i;
  emf1.tauElectrical = -emf1.tau;
  emf1.tau = emf1.flange.tau;
  step1.y = step1.offset + (if time < step1.startTime then 0.0 else step1.height);
  signalVoltage1.v = signalVoltage1.p.v - signalVoltage1.n.v;
  0.0 = signalVoltage1.p.i + signalVoltage1.n.i;
  signalVoltage1.i = signalVoltage1.p.i;
end dcmotor;

Note

Notification: dcmotor requested package Modelica of version 3.2.2. Modelica 3.2.3 is used instead which states that it is fully compatible without conversion script needed.

We plot part of the simulated result:

_images/dcmotor.svg

Figure 2 Rotation and rotational velocity of the DC motor

The val() function

The val(variableName,time) scription function can be used to retrieve the interpolated value of a simulation result variable at a certain point in the simulation time, see usage in the BouncingBall simulation below.

BouncingBall and Switch Models

We load and simulate the BouncingBall example containing when-equations and if-expressions (the Modelica keywords have been bold-faced by hand for better readability):

>>> loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/BouncingBall.mo")
true
>>> list(BouncingBall)
model BouncingBall
  parameter Real e = 0.7 "coefficient of restitution";
  parameter Real g = 9.81 "gravity acceleration";
  Real h(fixed = true, start = 1) "height of ball";
  Real v(fixed = true) "velocity of ball";
  Boolean flying(fixed = true, start = true) "true, if ball is flying";
  Boolean impact;
  Real v_new(fixed = true);
  Integer foo;
equation
  impact = h <= 0.0;
  foo = if impact then 1 else 2;
  der(v) = if flying then -g else 0;
  der(h) = v;
  when {h <= 0.0 and v <= 0.0, impact} then
    v_new = if edge(impact) then -e*pre(v) else 0;
    flying = v_new > 0;
    reinit(v, v_new);
  end when;
end BouncingBall;

Instead of just giving a simulate and plot command, we perform a runScript command on a .mos (Modelica script) file sim_BouncingBall.mos that contains these commands:

>>> writeFile("sim_BouncingBall.mos", "
  loadFile(getInstallationDirectoryPath() + \"/share/doc/omc/testmodels/BouncingBall.mo\");
  simulate(BouncingBall, stopTime=3.0);
  /* plot({h,flying}); */
")
true
>>> runScript("sim_BouncingBall.mos")
"true
record SimulationResult
    resultFile = \"«DOCHOME»/BouncingBall_res.mat\",
    simulationOptions = \"startTime = 0.0, stopTime = 3.0, numberOfIntervals = 500, tolerance = 1e-6, method = 'dassl', fileNamePrefix = 'BouncingBall', options = '', outputFormat = 'mat', variableFilter = '.*', cflags = '', simflags = ''\",
    messages = \"LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.
LOG_SUCCESS       | info    | The simulation finished successfully.
\",
    timeFrontend = 0.004714810000000001,
    timeBackend = 0.01034439,
    timeSimCode = 0.0028300590000000002,
    timeTemplates = 0.17343988300000002,
    timeCompile = 1.32133518,
    timeSimulation = 0.06983317800000001,
    timeTotal = 1.5826272239999999
end SimulationResult;
"
model Switch
  Real v;
  Real i;
  Real i1;
  Real itot;
  Boolean open;
equation
  itot = i + i1;
  if open then
    v = 0;
  else
    i = 0;
  end if;
  1 - i1 = 0;
  1 - v - i = 0;
  open = time >= 0.5;
end Switch;
>>> simulate(Switch, startTime=0, stopTime=1)
record SimulationResult
    resultFile = "«DOCHOME»/Switch_res.mat",
    simulationOptions = "startTime = 0.0, stopTime = 1.0, numberOfIntervals = 500, tolerance = 1e-6, method = 'dassl', fileNamePrefix = 'Switch', options = '', outputFormat = 'mat', variableFilter = '.*', cflags = '', simflags = ''",
    messages = "LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.
LOG_SUCCESS       | info    | The simulation finished successfully.
",
    timeFrontend = 0.007922684000000001,
    timeBackend = 0.011656963000000001,
    timeSimCode = 0.002142433,
    timeTemplates = 0.190487012,
    timeCompile = 1.302051766,
    timeSimulation = 0.08517491299999991,
    timeTotal = 1.599590917
end SimulationResult;

Retrieve the value of itot at time=0 using the val(variableName, time) function:

>>> val(itot,0)
1.0

Plot itot and open:

_images/switch.svg

Figure 3 Plot when the switch opens

We note that the variable open switches from false (0) to true (1), causing itot to increase from 1.0 to 2.0.

Clear All Models

Now, first clear all loaded libraries and models:

>>> clear()
true

List the loaded models - nothing left:

>>> list()
""

VanDerPol Model and Parametric Plot

We load another model, the VanDerPol model (or via the menu File->Load Model):

>>> loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/VanDerPol.mo")
true

It is simulated:

>>> simulate(VanDerPol, stopTime=80)
record SimulationResult
    resultFile = "«DOCHOME»/VanDerPol_res.mat",
    simulationOptions = "startTime = 0.0, stopTime = 80.0, numberOfIntervals = 500, tolerance = 1e-6, method = 'dassl', fileNamePrefix = 'VanDerPol', options = '', outputFormat = 'mat', variableFilter = '.*', cflags = '', simflags = ''",
    messages = "LOG_SUCCESS       | info    | The initialization finished successfully without homotopy method.
LOG_SUCCESS       | info    | The simulation finished successfully.
",
    timeFrontend = 0.003641478,
    timeBackend = 0.004235648,
    timeSimCode = 0.0012951190000000002,
    timeTemplates = 0.178713369,
    timeCompile = 1.2403688050000001,
    timeSimulation = 0.058775825000000004,
    timeTotal = 1.48717408
end SimulationResult;

It is plotted:

>>> plotParametric("x","y")
_images/VanDerPol.svg

Figure 4 VanDerPol plotParametric(x,y)

Perform code instantiation to flat form of the VanDerPol model:

>>> instantiateModel(VanDerPol)
class VanDerPol "Van der Pol oscillator model"
  Real x(start = 1.0, fixed = true);
  Real y(start = 1.0, fixed = true);
  parameter Real lambda = 0.3;
equation
  der(x) = y;
  der(y) = lambda * (1.0 - x * x) * y - x;
end VanDerPol;

Using Japanese or Chinese Characters

Japenese, Chinese, and other kinds of UniCode characters can be used within quoted (single quote) identifiers, see for example the variable name to the right in the plot below:

_images/bb-japanese.png

Scripting with For-Loops, While-Loops, and If-Statements

A simple summing integer loop (using multi-line input without evaluation at each line into OMShell requires copy-paste as one operation from another document):

>>> k := 0;
>>> for i in 1:1000 loop
  k := k + i;
end for;
>>> k
500500

A nested loop summing reals and integers:

>>> g := 0.0;
>>> h := 5;
>>> for i in {23.0,77.12,88.23} loop
  for j in i:0.5:(i+1) loop
    g := g + j;
    g := g + h / 2;
  end for;
  h := h + g;
end for;

By putting two (or more) variables or assignment statements separated by semicolon(s), ending with a variable, one can observe more than one variable value:

>>> h; g
1997.4500000000003
1479.0900000000001

A for-loop with vector traversal and concatenation of string elements:

>>> i:="";
>>> lst := {"Here ", "are ","some ","strings."};
>>> s := "";
>>> for i in lst loop
  s := s + i;
end for;
>>> s
"Here are some strings."

Normal while-loop with concatenation of 10 "abc " strings:

>>> s:="";
>>> i:=1;
>>> while i<=10 loop
  s:="abc "+s;
  i:=i+1;
end while;
>>> s
"abc abc abc abc abc abc abc abc abc abc "

A simple if-statement. By putting the variable last, after the semicolon, its value is returned after evaluation:

>>> if 5>2 then a := 77; end if; a
77

An if-then-else statement with elseif:

>>> if false then
  a := 5;
elseif a > 50 then
  b:= "test"; a:= 100;
else
  a:=34;
end if;

Take a look at the variables a and b:

>>> a;b
100
"test"

Variables, Functions, and Types of Variables

Assign a vector to a variable:

>>> a:=1:5
{1, 2, 3, 4, 5}

Type in a function:

function mySqr
  input Real x;
  output Real y;
algorithm
  y:=x*x;
end mySqr;

Call the function:

>>> b:=mySqr(2)
4.0

Look at the value of variable a:

>>> a
{1, 2, 3, 4, 5}

Look at the type of a:

>>> typeOf(a)
"Integer[5]"

Retrieve the type of b:

>>> typeOf(b)
"Real"

What is the type of mySqr? Cannot currently be handled.

>>> typeOf(mySqr)

List the available variables:

>>> listVariables()
{b, a, s, lst, i, h, g, k, currentSimulationResult}

Clear again:

>>> clear()
true

Getting Information about Error Cause

Call the function getErrorString() in order to get more information about the error cause after a simulation failure:

>>> getErrorString()
""

Alternative Simulation Output Formats

There are several output format possibilities, with mat being the default. plt and mat are the only formats that allow you to use the val() or plot() functions after a simulation. Compared to the speed of plt, mat is roughly 5 times for small files, and scales better for larger files due to being a binary format. The csv format is roughly twice as fast as plt on data-heavy simulations. The plt format allocates all output data in RAM during simulation, which means that simulations may fail due applications only being able to address 4GB of memory on 32-bit platforms. Empty does no output at all and should be by far the fastest. The csv and plt formats are suitable when using an external scripts or tools like gnuplot to generate plots or process data. The mat format can be post-processed in MATLAB or Octave.

>>> simulate(... , outputFormat="mat")
>>> simulate(... , outputFormat="csv")
>>> simulate(... , outputFormat="plt")
>>> simulate(... , outputFormat="empty")

It is also possible to specify which variables should be present in the result-file. This is done by using POSIX Extended Regular Expressions. The given expression must match the full variable name (^ and $ symbols are automatically added to the given regular expression).

// Default, match everything

>>> simulate(... , variableFilter=".*")

// match indices of variable myVar that only contain the numbers using combinations

// of the letters 1 through 3

>>> simulate(... , variableFilter="myVar\\\[[1-3]*\\\]")

// match x or y or z

>>> simulate(... , variableFilter="x|y|z")

Using External Functions

See Chapter Interoperability - C and Python for more information about calling functions in other programming languages.

Using Parallel Simulation via OpenMP Multi-Core Support

Faster simulations on multi-core computers can be obtained by using a new OpenModelica feature that automatically partitions the system of equations and schedules the parts for execution on different cores using shared-memory OpenMP based execution. The speedup obtained is dependent on the model structure, whether the system of equations can be partitioned well. This version in the current OpenModelica release is an experimental version without load balancing. The following command, not yet available from the OpenModelica GUI, will run a parallel simulation on a model:

>>> omc -d=openmp model.mo

Loading Specific Library Version

There exist many different versiosn of Modelica libraries which are not compatible. It is possible to keep multiple versions of the same library stored in the directory given by calling getModelicaPath(). By calling loadModel(Modelica,{"3.2"}), OpenModelica will search for a directory called "Modelica 3.2" or a file called "Modelica 3.2.mo". It is possible to give several library versions to search for, giving preference for a pre-release version of a library if it is installed. If the searched version is "default", the priority is: no version name (Modelica), main release version (Modelica 3.1), pre-release version (Modelica 3.1Beta 1) and unordered versions (Modelica Special Release).

The loadModel command will also look at the uses annotation of the top-level class after it has been loaded. Given the following package, Complex 1.0 and ModelicaServices 1.1 will also be loaded into the AST automatically.

package Modelica
  annotation(uses(Complex(version="1.0"),
  ModelicaServices(version="1.1")));
end Modelica;

Warning

Notification: Automatically loaded package Complex 4.0.0 due to uses annotation from Modelica.

Notification: Automatically loaded package ModelicaServices 4.0.0 due to uses annotation from Modelica.

Warning: Requested package Modelica of version 4.0.0, but this package was already loaded with version . OpenModelica cannot reason about compatibility between the two packages since they are not semantic versions.

>>> clear()
true

Packages will also be loaded if a model has a uses-annotation:

model M
  annotation(uses(Modelica(version="3.2.1")));
end M;

Note

Notification: Automatically loaded package Modelica 3.2.1 due to uses annotation from M.

Notification: Automatically loaded package Complex 3.2.1 due to uses annotation from Modelica.

Notification: Automatically loaded package ModelicaServices 3.2.1 due to uses annotation from Modelica.

>>> instantiateModel(M)
class M
end M;

Packages will also be loaded by looking at the first identifier in the path:

>>> instantiateModel(Modelica.Electrical.Analog.Basic.Ground)
class Modelica.Electrical.Analog.Basic.Ground "Ground node"
  Real p.v(quantity = "ElectricPotential", unit = "V") "Potential at the pin";
  Real p.i(quantity = "ElectricCurrent", unit = "A") "Current flowing into the pin";
equation
  p.i = 0.0;
  p.v = 0.0;
end Modelica.Electrical.Analog.Basic.Ground;

Note

Notification: Automatically loaded package Complex 4.0.0 due to uses annotation from Modelica.

Notification: Automatically loaded package ModelicaServices 4.0.0 due to uses annotation from Modelica.

Notification: Automatically loaded package Modelica default due to usage.

Calling the Model Query and Manipulation API

In the OpenModelica System Documentation, an external API (application programming interface) is described which returns information about models and/or allows manipulation of models. Calls to these functions can be done interactively as below, but more typically by program clients to the OpenModelica Compiler (OMC) server. Current examples of such clients are the OpenModelica MDT Eclipse plugin, OMNotebook, the OMEdit graphic model editor, etc. This API is untyped for performance reasons, i.e., no type checking and minimal error checking is done on the calls. The results of a call is returned as a text string in Modelica syntax form, which the client has to parse. An example parser in C++ is available in the OMNotebook source code, whereas another example parser in Java is available in the MDT Eclipse plugin.

Below we show a few calls on the previously simulated BouncingBall model. The full documentation on this API is available in the system documentation. First we load and list the model again to show its structure:

>>> loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/BouncingBall.mo");
>>> list(BouncingBall)
model BouncingBall
  parameter Real e = 0.7 "coefficient of restitution";
  parameter Real g = 9.81 "gravity acceleration";
  Real h(fixed = true, start = 1) "height of ball";
  Real v(fixed = true) "velocity of ball";
  Boolean flying(fixed = true, start = true) "true, if ball is flying";
  Boolean impact;
  Real v_new(fixed = true);
  Integer foo;
equation
  impact = h <= 0.0;
  foo = if impact then 1 else 2;
  der(v) = if flying then -g else 0;
  der(h) = v;
  when {h <= 0.0 and v <= 0.0, impact} then
    v_new = if edge(impact) then -e*pre(v) else 0;
    flying = v_new > 0;
    reinit(v, v_new);
  end when;
end BouncingBall;

Different kinds of calls with returned results:

>>> getClassRestriction(BouncingBall)
"model"
>>> getClassInformation(BouncingBall)
("model", "", false, false, false, "/var/lib/jenkins1/ws/OpenModelica_master_2/build/share/doc/omc/testmodels/BouncingBall.mo", false, 1, 1, 23, 17, {}, false, false, "", "", false, "", "", "", "", "")
>>> isFunction(BouncingBall)
false
>>> existClass(BouncingBall)
true
>>> getComponents(BouncingBall)
{{Real, e, "coefficient of restitution", "public", false, false, false, false, "parameter", "none", "unspecified", {}}, {Real, g, "gravity acceleration", "public", false, false, false, false, "parameter", "none", "unspecified", {}}, {Real, h, "height of ball", "public", false, false, false, false, "unspecified", "none", "unspecified", {}}, {Real, v, "velocity of ball", "public", false, false, false, false, "unspecified", "none", "unspecified", {}}, {Boolean, flying, "true, if ball is flying", "public", false, false, false, false, "unspecified", "none", "unspecified", {}}, {Boolean, impact, "", "public", false, false, false, false, "unspecified", "none", "unspecified", {}}, {Real, v_new, "", "public", false, false, false, false, "unspecified", "none", "unspecified", {}}, {Integer, foo, "", "public", false, false, false, false, "unspecified", "none", "unspecified", {}}}
>>> getConnectionCount(BouncingBall)
0
>>> getInheritanceCount(BouncingBall)
0
>>> getComponentModifierValue(BouncingBall,e)
"0.7"
>>> getComponentModifierNames(BouncingBall,"e")
{}
>>> getClassRestriction(BouncingBall)
"model"
>>> getVersion() // Version of the currently running OMC
"OMCompiler v1.25.0-dev.184+g229c087a3c"

Quit OpenModelica

Leave and quit OpenModelica:

>>> quit()

Dump XML Representation

The command dumpXMLDAE dumps an XML representation of a model, according to several optional parameters.

dumpXMLDAE(modelname[,asInSimulationCode=<Boolean>] [,filePrefix=<String>] [,storeInTemp=<Boolean>] [,addMathMLCode =<Boolean>])

This command dumps the mathematical representation of a model using an XML representation, with optional parameters. In particular, asInSimulationCode defines where to stop in the translation process (before dumping the model), the other options are relative to the file storage: filePrefix for specifying a different name and storeInTemp to use the temporary directory. The optional parameter addMathMLCode gives the possibility to don't print the MathML code within the xml file, to make it more readable. Usage is trivial, just: addMathMLCode=true/false (default value is false).

Dump Matlab Representation

The command export dumps an XML representation of a model, according to several optional parameters.

exportDAEtoMatlab(modelname);

This command dumps the mathematical representation of a model using a Matlab representation. Example:

>>> loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/BouncingBall.mo")
true
>>> exportDAEtoMatlab(BouncingBall)
"The equation system was dumped to Matlab file:BouncingBall_imatrix.m"
% Adjacency Matrix
% ====================================
% number of rows: 6
IM={{3,6},{1,{'if', 'true','==' {3},{},}},{{'if', 'true','==' {4},{},}},{5},{2,{'if', 'edge(impact)' {3},{5},}},{4,2}};
VL = {'foo','v_new','impact','flying','v','h'};


EqStr = {'impact = h <= 0.0;','foo = if impact then 1 else 2;','der(v) = if flying then -g else 0.0;','der(h) = v;','when {h <= 0.0 and v <= 0.0, impact} then v_new = if edge(impact) then (-e) * pre(v) else 0.0; end when;','when {h <= 0.0 and v <= 0.0, impact} then flying = v_new > 0.0; end when;'};


OldEqStr={'class BouncingBall','  parameter Real e = 0.7 "coefficient of restitution";','  parameter Real g = 9.81 "gravity acceleration";','  Real h(start = 1.0, fixed = true) "height of ball";','  Real v(fixed = true) "velocity of ball";','  Boolean flying(start = true, fixed = true) "true, if ball is flying";','  Boolean impact;','  Real v_new(fixed = true);','  Integer foo;','equation','  impact = h <= 0.0;','  foo = if impact then 1 else 2;','  der(v) = if flying then -g else 0.0;','  der(h) = v;','  when {h <= 0.0 and v <= 0.0, impact} then','    v_new = if edge(impact) then -e * pre(v) else 0.0;','    flying = v_new > 0.0;','    reinit(v, v_new);','  end when;','end BouncingBall;',''};

Summary of Commands for the Interactive Session Handler

The following is the complete list of commands currently available in the interactive session hander.

simulate(modelname) Translate a model named modelname and simulate it.

simulate(modelname[,startTime=<Real>][,stopTime=<Real>][,numberOfIntervals
=<Integer>][,outputInterval=<Real>][,method=<String>]
[,tolerance=<Real>][,fixedStepSize=<Real>]
[,outputFormat=<String>]) Translate and simulate a model, with optional start time, stop time, and optional number of simulation intervals or steps for which the simulation results will be computed. More intervals will give higher time resolution, but occupy more space and take longer to compute. The default number of intervals is 500. It is possible to choose solving method, default is “dassl”, “euler” and “rungekutta” are also available. Output format “mat” is default. “plt” and “mat” (MATLAB) are the only ones that work with the val() command, “csv” (comma separated values) and “empty” (no output) are also available (see section Alternative Simulation Output Formats).

plot(vars) Plot the variables given as a vector or a scalar, e.g. plot({x1,x2}) or plot(x1).

plotParametric(var1, var2) Plot var2 relative to var1 from the most recently simulated model, e.g. plotParametric(x,y).

cd() Return the current directory.

cd(dir) Change directory to the directory given as string.

clear() Clear all loaded definitions.

clearVariables() Clear all defined variables.

dumpXMLDAE(modelname, ...) Dumps an XML representation of a model, according to several optional parameters.

exportDAEtoMatlab(name) Dumps a Matlab representation of a model.

instantiateModel(modelname)Performs code instantiation of a model/class and return a string containing the flat class definition.

list() Return a string containing all loaded class definitions.

list(modelname) Return a string containing the class definition of the named class.

listVariables() Return a vector of the names of the currently defined variables.

loadModel(classname) Load model or package of name classname from the path indicated by the environment variable OPENMODELICALIBRARY.

loadFile(str) Load Modelica file (.mo) with name given as string argument str.

readFile(str) Load file given as string str and return a string containing the file content.

runScript(str) Execute script file with file name given as string argument str.

system(str) Execute str as a system(shell) command in the operating system; return integer success value. Output into stdout from a shell command is put into the console window.

timing(expr) Evaluate expression expr and return the number of seconds (elapsed time) the evaluation took.

typeOf(variable) Return the type of the variable as a string.

saveModel(str,modelname) Save the model/class with name modelname in the file given by the string argument str.

val(variable,timePoint) Return the (interpolated) value of the variable at time timePoint.

help() Print this helptext (returned as a string).

quit() Leave and quit the OpenModelica environment

Running the compiler from command line

The OpenModelica compiler can also be used from command line, in Windows cmd.exe or a Unix shell. The following examples assume omc is on the PATH; if it is not, you can run C:\OpenModelica 1.16.0\build\bin\omc.exe or similar (depending on where you installed OpenModelica).

Example Session 1 - obtaining information about command line parameters

$ omc --help
OpenModelica Compiler OMCompiler v1.25.0-dev.184+g229c087a3c
Copyright © 2019 Open Source Modelica Consortium (OSMC)
Distributed under OMSC-PL and GPL, see www.openmodelica.org

Usage: omc [Options] (Model.mo | Script.mos) [Libraries | .mo-files] 
* Libraries: Fully qualified names of libraries to load before processing Model or Script.
...
Documentation is available in the built-in package OpenModelica.Scripting or
online <https://build.openmodelica.org/Documentation/OpenModelica.Scripting.html>.

Example Session 2 - create an TestModel.mo file and run omc on it

model TestModel
  parameter Real x = 1;
end TestModel;
$ omc TestModel.mo
class TestModel
  parameter Real x = 1.0;
end TestModel;

Example Session 3 - create a mos-script and run omc on it

loadModel(Modelica);
getErrorString();
simulate(Modelica.Mechanics.MultiBody.Examples.Elementary.Pendulum);
getErrorString();
$ omc TestScript.mos
false
"Error: Failed to open file for writing: //.openmodelica/libraries/index.json.tmp1
Error: Failed to download package index https://libraries.openmodelica.org/index/v1/index.json to file //.openmodelica/libraries/index.json.
Error: Failed to open file for writing: //.openmodelica/libraries/index.json.tmp1
Error: Failed to download package index https://libraries.openmodelica.org/index/v1/index.json to file //.openmodelica/libraries/index.json.
Error: Failed to load package Modelica (default) using MODELICAPATH //.openmodelica/libraries/.
"
record SimulationResult
    resultFile = "",
    simulationOptions = "startTime = 0.0, stopTime = 1.0, numberOfIntervals = 500, tolerance = 1e-6, method = 'dassl', fileNamePrefix = 'Modelica.Mechanics.MultiBody.Examples.Elementary.Pendulum', options = '', outputFormat = 'mat', variableFilter = '.*', cflags = '', simflags = ''",
    messages = "Simulation Failed. Model: Modelica.Mechanics.MultiBody.Examples.Elementary.Pendulum does not exist! Please load it first before simulation.",
    timeFrontend = 0.0,
    timeBackend = 0.0,
    timeSimCode = 0.0,
    timeTemplates = 0.0,
    timeCompile = 0.0,
    timeSimulation = 0.0,
    timeTotal = 0.0
end SimulationResult;
""

In order to obtain more information from the compiler one can use the command line options --showErrorMessages -d=failtrace when running the compiler:

$ omc --showErrorMessages -d=failtrace TestScript.mos
InstFunction.getRecordConstructorFunction failed for OpenModelica.Scripting.loadModel
- Static.elabCrefSubs failed on: [top:<Prefix.NOPRE()>].<Prefix.NOPRE()>.Modelica env: <global scope>
- Static.elabCref failed: Modelica in env: <global scope>
- Static.elabCrefSubs failed on: [top:<Prefix.NOPRE()>].<Prefix.NOPRE()>.Modelica env: <global scope>
...
    timeSimulation = 0.0,
    timeTotal = 0.0
end SimulationResult;
""