5. Software Architecture

5.1. Overall software architecture

Fig. 5.1 shows the overall software architecture of SOEP.

The Application on top of the figure may be an equipment sales tool that uses an open, or a proprietary, Modelica model library of their product line, which allows a sales person to test and size equipment for a particular customer.

The HVAC Systems Editor allows drag and drop of components, which are read dynamically from the Modelica Library AST, similar to what can be done in today’s OS schematic editor. In contrast, the Schematic Editor allows to freely place and graphically connect Modelica components. Once models have been manipulated in the Schematic Editor, OS can only simulate them, but not apply OS Measures to it, as models may have become incompatible with the OS Measures. (This could in the future be changed by parsing the AST of the model.)

In the Schematic Editor, user-provided Modelica libraries can be loaded (for example, if a company has their own control sequence library), manipulated and stored again in the user-provided Modelica library. This functionality is also needed for the OpenBuildingControl project.

Currently, the OpenStudio Model Library is compiled C++ code. Our integration will generate a representation of the Modelica library that allows OpenStudio to dynamic load models for the SOEP mode. Note that the JModelica distribution includes a C++ compiler.

title Overall software architecture

scale max 1024 width

skinparam componentStyle uml2

package OpenStudio {
interface API
API - [Core]

package Legacy-Mode {
database "Legacy\nModel Library"
[Core] -> [Legacy\nModel Library]: integrates
[Core] -> [HVAC Systems Editor\n(Legacy Mode)]: integrates
[Core] -> [EnergyPlus\nSimulator Interface]: integrates

package SOEP-Mode {

[Core] --> [Model Library]: integrates
[Core] --> [HVAC Systems Editor\n(SOEP Mode)]: integrates
[Core] --> [SOEP\nSimulator Interface]: integrates

package SOEP {
database "Modelica\nLibrary AST" as mod_AST
database "Modelica\nBuildings Library"

[Model Library] --> mod_AST : parses json\nAST

[HVAC Systems Editor\n(SOEP Mode)] ..> mod_AST : parses json\nAST

[Conversion Script] .> [JModelica]: parses\nAST
[SOEP\nSimulator Interface] .> [JModelica] : writes inputs,\nruns simulation,\nreads outputs

[Conversion Script] -> mod_AST: generates
[JModelica] -> [Modelica\nBuildings Library]: imports

actor Developer as epdev
[Legacy\nModel Library] <.. epdev : updates

actor "Developer or User" as modev
[Conversion Script] <.. modev : invokes

actor Developer as budev
[Modelica\nBuildings Library] <.. budev : adds annotations

actor User as mouse
[User-Provided\nModelica Library] <.. mouse : adds annotations

[Application] ..> () API : uses
[Measures] ..> () API : uses

database "User-Provided\nModelica Library"
[JModelica] --> [User-Provided\nModelica Library]: imports

EnergyPlus <.. [EnergyPlus\nSimulator Interface]: writes inputs,\nruns simulation,\nreads outputs

package EnergyPlus {

note left of mod_AST
  Used as an intermediate format and
  to verify incompatible changes.
end note

note bottom of [User-Provided\nModelica Library]
  Allows companies to use
  their own Modelica libraries
  with custom HVAC systems and
  control sequences, or
  to integrate an electronic
  equipment catalog or a
  library used for an equipment
  sales tool.
end note

note right of Application
  Application that uses
  the OpenStudio SDK.
end note

Fig. 5.1 : Overall software architecture.

5.2. Coupling of EnergyPlus envelope and room with Modelica-based HVAC and control

This section describes the refactoring of the EnergyPlus room model, which will remain in the C/C++ implementation of EnergyPlus, to a form that exposes the time derivative of its room model. EnergyPlus will be exported as an FMU for model exchange.

The time integration of the room air temperature, moisture and trace substance concentrations will be done by the master algorithm.

EnergyPlus will synchronize the room model and the envelope model.

We will use the following terminology: By envelope model, we mean the model for the heat and moisture transfer through opaque constructions and through windows. By room model, we mean the room air heat, mass and trace substance balance. By HVAC model, we mean the HVAC and control model.

The physical quantities that need to be exchanged are as follows. For a convective HVAC system, the convective and latent heat gain added by the HVAC system, the mass flow rates of trace substances such as CO2 and VoC, and the state of the return air, e.g., temperature, relative humidity and pressure. For radiant systems, the temperature of the radiant surface, and the heat flow rates due to conduction, short-wave and long-wave radiation.

5.2.1. Assumptions and limitations

For the current implementation, we will make the following assumption:

  1. Only the lumped room air model will be refactored, not the room model with stratified room air. The reason is to keep focus and make progress before increasing complexity.

  2. The HVAC and the pressure driven air exchange (airflow network) are either in legacy EnergyPlus or in FMUs of the SOEP. The two methods cannot be combined. The reason is that the legacy EnergyPlus computes in its “predictor/corrector” the room temperature as follows:

    1. It computes the HVAC power required to meet the temperature set point.
    2. It simulates the HVAC system to see whether it can meet this load.
    3. It updates the room temperature using the HVAC power from step (b).

    This is fundamentally different from the ODE solver used by SOEP who sets the new room temperature and time, requests its time derivative, and then recomputes the new time step.

  3. In each room, mass, as opposed to volume, is conserved. The reason is that this does not induce an air flow in the HVAC system if the room temperature changes. Hence, it decouples the thermal and the mass balance.

5.2.2. Partitioning of the models

To link the envelope model, the room model and the HVAC model, we partition the simulation as shown in Figure 2.


Fig. 5.2 : Partitioning of the envelope, room and HVAC model.

The EnergyPlus FMU is for model exchange and contains the envelope and the room model. All of the HVAC system and other pressure driven mass flow rates, such as infiltration due to wind pressure or static pressure differences, are computed in the HVAC FMUs. There can be one or several HVAC FMUs, which is irrelevant as EnergyPlus will only see one set of variables that it exchanges with the master algorithm.

5.2.3. Data exchange

The communication occurs through the EnergyPlus external interface. The following variables are sent between the master algorithm and the EnergyPlus FMU for each thermal zone.

Variable Dimension Quantity Unit
From master algorithm to EnergyPlus FMU
T \(1\) Temperature of the zone air degC
X \(1\) Water vapor mass fraction per total air mass of the zone kg/kg
mInlet_flow \(n\) Mass flow rate into the zone for the \(n\), \(n \ge 0\), air inlets (including infiltration) kg/s
TInlet \(n\) Temperature of the medium carried by the mass flow rate degC
QGaiRad_flow \(1\) Radiative sensible heat gain added to the zone W
t \(1\) Model time at which the above inputs are valid, with \(t=0\) defined as January 1, 0 am local time, and with no correction for daylight savings time s
From EnergyPlus FMU to master algorithm
TRad \(1\) Average radiative temperature in the room degC
QConSen_flow \(1\) Convective sensible heat added to the zone, e.g., as entered in the EnergyPlus’ People or Equipment schedule W
QLat_flow \(1\) Latent heat gain added to the zone, e.g., from mass transfer with moisture buffering material and from EnergyPlus’ People schedule W
QPeo_flow \(1\) Heat gain due to people (only to be used to optionally compute CO2 emitted by people) W
nextEventTime \(1\) Model time \(t\) when EnergyPlus needs to be called next (typically the next zone time step) s

Note that the EnergyPlus object ZoneAirContaminantBalance either allows CO2 concentration modeling, or a generic contaminant modeling (such as from material outgasing), or no contaminant modeling, or both. To allow ambiguities regarding what contaminant is being modeled, we do not receive the contaminant emission from EnergyPlus. Instead, we obtain the heat gain due to people, which is then used to optionally computed the CO2 emitted by people.

How to connect variables from the External Interface to the EnergyPlus zone is defined by using an object in the idf file of the form

South Office, !- EnergyPlus name of the zone
3;            !- 0 <= n, number of air flow inlets from the External Interface

The External Interface then maps the data from the above variable table to data structure in EnergyPlus.

5.2.4. Time synchronization

As shown in Figure 2, the EnergyPlus FMU is invoked at a variable time step. Internally, it samples its heat conduction model at the envelope time step \(\Delta t_z\). EnergyPlus needs to report this to the FMI interface. To report such time events, the FMI interface uses a C structure called fmi2EventInfo which is implemented as follows:

typedef struct{
  fmi2Boolean newDiscreteStatesNeeded;
  fmi2Boolean terminateSimulation;
  fmi2Boolean nominalsOfContinuousStatesChanged;
  fmi2Boolean valuesOfContinuousStatesChanged;
  fmi2Boolean nextEventTimeDefined;
  nextEventTime; // next event if nextEventTimeDefined=fmi2True
  } fmi2EventInfo;

The variable nextEventTime needs to be set to the time when the next event happens in EnergyPlus. This is, for example, whenever the envelope model advances time, or when a schedule changes its value and this change affects the variables that are sent from the EnergyPlus FMU to the master algorithm. Such a schedule could for example be a time schedule for internal heat gains, which may change at times that do not coincide with the zone time step \(\Delta t_z\).

5.2.5. Requirements for Exporting EnergyPlus as an FMU for model exchange

To export EnergyPlus as an FMU for model exchange, EnergyPlus must be compiled as a shared library. The shared library must export the functions which are described in the next section. These functions are then used by the FMI for model exchange wrapper that LBL is developing.


In the current implementation, we assume that EnergyPlus does not support roll back in time. This will otherwise require EnergyPlus to be able to save and restore its complete internal state. This internal state consists especially of the values of the continuous-time states, iteration variables, parameter values, input values, delay buffers, file identifiers and internal status information. This limitation is indicated in the model description file with the capability flag canGetAndSetFMUstate being set to false. If this capability were supported, then EnergyPlus could be used with ODE solvers which can reject and repeat steps. Rejecting steps is needed by ODE solvers such as DASSL or even Euler with step size control (but not for QSS) as they may reject a step size if the error is too large. Also, rejecting steps is needed to identify state events (but not for QSS solvers).

In the remainder of this section, we note that time is

  • the variable described as t in the table of section Section 5.2.3,
  • a monotonically increasing variable.


Monotonically increasing means that if a function has as argument time and is called at time t1, then its next call must happen at time t2 with t2 >= t1. For efficiency reasons, if a function which updates internal variables is called at the same time instant multiple times then only the first call will update the variables, subsequent calls will cause the functions to return the same variable values.

unsigned int instantiate(const char const *input,
                         const char const *weather,
                         const char const *idd,
                         const char const *instanceName,
                         const unsigned int valueReferences[],
                         double* variablePointers[],
                         size_t nVars,
                         const char *log);
  • input: Absolute or relative path to an EnergyPlus input file with file name.
  • weather: Absolute or relative path to an EnergyPlus weather file with file name.
  • idd: Absolute or relative path to an EnergyPlus idd file with file name.
  • instanceName: String to uniquely identify an EnergyPlus instance. This string must be non-empty and will be used for logging message.
  • valueReferences: A vector of value references. Value references uniquely identify values of variables
    defined in the model description file of an EnergyPlus FMU.
  • variablePointers: A vector of pointers to variables whose value references are defined in valueReferences.
  • nVars: Number of elements of valueReferences and variablePointers.
  • log: Logging message returned on error.

This function will read the idf file, sets up the data structure in EnergyPlus, gets a vector of value references (as in modelDescription.xml) and returns a vector of pointers to the aforementioned value references. The ordering of the value references must match the ordering of the vector of pointers.

It returns zero if there was no error, or else a positive non-zero integer.

unsigned int setupExperiment(double tStart,
                             bool stopTimeDefined,
                             double tEnd,
                             const char *log);
  • tStart: Start of simulation in seconds.
  • stopTimeDefined: If false, then the value of tEnd must be ignored. If stopTimeDefined = true and the environment tries to compute past tEnd, then setTime() has to return an error message. (Setting stopTimeDefined = false allows use of the simulator as part of a controller.)
  • tEnd: End of simulation in seconds.
  • log: Logging message returned on error.

This functions sets the start and end time of EnergyPlus to the values tStart and tEnd. There is no warm-up simulation.

It returns zero if there was no error, or else a positive non-zero integer.


The EnergyPlus start and stop time is as retrieved from the arguments of setupExperiment(...). The RunPeriod in the idf file is only used to determine the day of the week.


  1. Users may set tStart and tEnd to times other than midnight. We think EnergyPlus cannot yet handle an arbitrary start and end time. In this case, it should return an error.
  2. Users may set tStart=tEnd, which is valid in some simulators. Can EneryPlus handle this or should it return an error?
unsigned int setTime(double time,
                     const char *log);
  • time: Model time.
  • log: Logging message returned on error.

This function sets a new time in EnergyPlus. This time becomes the current model time.

It returns zero if there was no error, or else a positive non-zero integer.

unsigned int setVariables(const unsigned int valueReferences[],
                          const double* const variablePointers[],
                          size_t nVars1,
                          const char *log);
  • valueReferences: Vector of value references.
  • variablePointers: Vector of pointers to variables.
  • nVars1: Number of elements of valueReferences, and variablePointers.
  • log: Logging message returned on error.

This function sets the value of variables in EnergyPlus. The vector variablePointers could be a subset of the pointer variablePointers that was setup in instantiate(...), i.e., nVars1 <= nVars (to allow updating only specific variables as needed by QSS).

It returns zero if there was no error, or else a positive non-zero integer.

unsigned int getVariables(const unsigned int valueReferences[],
                          const double* variablePointers[],
                          size_t nVars2,
                          const char *log);
  • valueReferences: Vector of value references.
  • variablePointers: Vector of pointers to variables.
  • nVars2: Number of elements of valueReferences, and variablePointers.
  • log: Logging message returned on error.

This function gets the value of variables in EnergyPlus. EnergyPlus must write the values to the elements that are setup in variablePointers during the instantiate(...) call. nVars2 <= nVars if only certain output variables are required.

It returns zero if there was no error, or else a positive non-zero integer.

unsigned int getNextEventTime(fmi2EventInfo *eventInfo,
                              const char *log);
  • eventInfo: A structure with event info as defined in section Time synchronization
  • log: Logging message returned on error.

This function writes a structure which contains among its variables a non-zero flag to indicate that the next event time is defined, and the next event time in EnergyPlus.

It returns zero if there was no error, or else a positive non-zero integer.

unsigned int terminate(const char *log);
  • log: Logging message returned on error.

This function must free all allocated variables in EnergyPlus.

Any further call to the EnergyPlus shared library is prohibited after call to this function.

It returns zero if there was no error, or else a positive non-zero integer.

unsigned int writeOutputFiles(const char *log);
  • log: Logging message returned on error.

This function writes the output to the EnergyPlus output files.

It returns zero if there was no error, or else a positive non-zero integer.

5.2.6. Pseudo Code Example

In the next section, the usage of the FMI functions along with the equivalent EnergyPlus functions are used in a typical calling sequence. This should clarify the need of the EnergyPlus equivalent functions and show how these functions will be used in a simulation environment. In the pseudo code, -> points to the EnergyPlus equivalent FMI functions. NA indicates that the FMI functions do not require EnergyPlus equivalent.

 // instantiate
    m = M_fmi2Instantiate(...) -> instantiate(...)

    tStart = 0
    tEnd   = 10
    dt     = 0.01

    // set the start time
    time  = tStart

    // set the input values and the initial values for the states at time = tStart
    M_fmi2SetReal(m, ...) -> setVariables (...)

    // initialize
    M_fmi2SetupExperiment(m,fmi2False,0.0, Tstart, fmi2True,Tend) -> setupExperiment(...)
    M_fmi2EnterInitializationMode(m) -> NA
    M_fmi2ExitInitializationMode(m) -> NA

    initialEventMode = fmi2True
    enterEventMode = fmi2False
    timeEvent = fmi2False
    stateEvent = fmi2False
    previous_z = zeros(nz)

    // The time in EnergyPlus is now at Tstart

      // handle events
      if initialEventMode or enterEventMode or timeEvent or stateEvent then
        if not initialEventMode then
          M_fmi2EnterEventMode(...) -> NA
        end if

        // event iteration
        eventInfo.newDiscreteStatesNeeded = fmi2True;
        M_valuesOfContinuousStatesChanged = fmi2False;
         while eventInfo.newDiscreteStatesNeeded loop
           // update discrete states
           M_fmi2NewDiscreteStates(eventInfo, ...) -> getNextEventTime(eventInfo, ...)
           // See specification on page 80
           M_fmi2SetReal(...) -> setVariables (...)
           M_fmi2GetReal(...) -> getVariables (...)
           if eventInfo.terminateSimulation then goto TERMINATE_MODEL
        end while

        // enter Continuous-Time Mode
        M_fmi2EnterContinuousTimeMode(m) -> NA

        // retrieve solution at simulation (re)start
        M_fmi2GetReal(...) -> getVariables (...)
        if initialEventMode or eventInfo.valuesOfContinuousStatesChanged then
          //the model signals a value change of states, retrieve them
          M_fmi2GetContinuousStates(...) -> NA
        end if

        if initialEventMode or eventInfo.nominalsOfContinuousStatesChanged then
          //the meaning of states has changed; retrieve new nominal values
          M_fmi2GetNominalsOfContinuousStates(...) -> NA
        end if

        if eventInfo.nextEventTimeDefined then
           tNext = min(eventInfo.nextEventTime, tEnd)
           tNext = tEnd
        end if

        initialEventMode = fmi2False
      end if

      if time >= tEnd then
        goto TERMINATE_MODEL
      end if

      // compute derivatives
      M_fmi2GetDerivatives(...) -> NA
      // Note we might have to compute time derivatives at different time instants
      // to approximate higher order derivatives for QSS integration methods.

      // Note that a forward Euler method with event detection, or more sophisticated
      // methods such as dassl, will not work with EnergyPlus.
      // The reason is that EnergyPlus does not allow rollback (and this limitation
      // is indicated in the model description file with the capability flag
      // canGetAndSetFMUstate=false.

      // When using QSS numerical methods,
      // compute minimal next quantization time tq
      time_old = time
      time = min (tq, tNext) // tq is the next predicted quantization time

      M_fmi2SetTime(...) -> setTime(...)

      // set inputs at t = time
      M_fmi2SetReal(m, ...) -> setVariables (...)

      // do a time integration up to t = time
      x = integral(x, der_x, time_old, time)
      M_fmi2SetContinuousStates(...) -> NA

      // get event indicators for state events detection at t = time
      M_fmi2GetEventIndicators(...) -> NA

      // EnergyPlus has no zero crossing functions, hence
      // there is no state event detection for this FMU.

      // inform the model about an accepted step
      // To ensure that EnergyPlus call getNextEventTime(...),
      // enterEventMode will be set to true in M_fmi2CompletedIntegratorStep().
      // Furthermore, the capability flag completedIntegratorStepNotNeeded
      // will be set to false to ensure that this function is called
      // after an accepted step.
      M_fmi2CompletedIntegratorStep(enterEventMode...) -> writeOutputFiles(...)

      // get the outputs
      M_fmi2GetReal(m, ...) -> getVariables (...)

    until terminateSimulation

    // terminate simulation and retrieve final values
    M_fmi2GetReal(m, ...) -> getVariables()
    M_fmi2Terminate(m) -> terminate(...)

    // cleanup
    M_fmi2FreeInstance(m) -> NA

5.2.7. Tool for Exporting EnergyPlus as an FMU

To export EnergyPlus as an FMU, a utility is needed which will get as inputs the paths to the EnergyPlus idf, idd, and weather files. The utility will parse the idf file and write an XML model description file which contains the inputs, outputs, and states of EnergyPlus to be exposed through the FMI interface. The utility will compile the EnergyPlus FMI functions into a shared library, and package the library with the idf, idd, and weather file in the resources folder of the FMU. An approach to develop such a utility is to extend EnergyPlusToFMU (http://simulationresearch.lbl.gov/fmu/EnergyPlus/export/index.html) to support FMI 2.0 for model exchange. Another approach is to extend SimulatorToFMU (https://github.com/LBNL-ETA/SimulatorToFMU) to support the export of EnergyPlus.

5.3. JModelica Integration

This section describes the integration of the QSS solver in JModelica.

We will first introduce terminology. Consider the code-snippet

when (x > a) then
end when;

We will say that \(z = a - x\) is the event indicator.

For the discussion, we consider a system of initial value ODEs of the form

(1)\[\begin{split}[\dot x_c(t), x_d(t)] & = f(x_c(t), x_d(t^-), u_c(t), u_d(t), p, t),\end{split}\]\[\begin{split}[y_c(t), y_d(t)] & = g(x_c(t), x_d(t), u_c(t), u_d(t), p, t),\\\end{split}\]\[\begin{split}0 & = z(x_c(t), x_d(t), u_c(t), u_d(t), p, t),\\\end{split}\]\[\begin{split}[x_c(t_0), x_d(t_0)] & = [x_{c,0}, x_{d,0}],\end{split}\]

where \(x(\cdot)\) is the continuous-time state vector, with superscript \(c\) denoting continuous-time states and \(d\) denoting discrete variables or states, \(u(\cdot)\) is the external input, \(p\) are parameters, \(f(\cdot, \cdot, \cdot, \cdot, \cdot, \cdot)\) is the state transitions function, \(g(\cdot, \cdot, \cdot, \cdot, \cdot, \cdot)\) is the output function, \(z(\cdot, \cdot, \cdot, \cdot, \cdot, \cdot)\) is the event indicator (sometimes called zero crossing function).

Because we anticipate that the FMU can have direct feed-through from the input \(u(t)\) to the output \(y(t)\), we use FMI for Model-Exchange (FMI-ME) version 2.0, because the Co-Simulation standard does not allow a zero time step size as needed for direct feed-through.

Fig. 5.3 shows the software architecture with the extended FMI API. For simplicity the figure only shows single FMUs, but we anticipated having multiple interconnected FMUs.

title Software architecture for QSS integration with JModelica with extended FMI API

skinparam componentStyle uml2

package FMU-QSS {
  [QSS solver] as qss_sol

package PyFMI {
[Master algorithm] -> qss_sol : "inputs, time"
[Master algorithm] <- qss_sol : "next event time, discrete states"
[Master algorithm] - [Sundials]

[FMU-ME] as ode

[Sundials] -> ode : "(x, t)"
[Sundials] <- ode : "dx/dt"

[FMU-ME (envelope)] as ep_env
[Master algorithm] -> ep_env : "h"
[Master algorithm] <- ep_env : "(next event time, discrete states)"

package Optimica {
[JModelica compiler] as jmc

jmc -> FMU_QSS

FMU_QSS -down-> qss_sol : "derivatives"
qss_sol -down-> FMU_QSS : "inputs, time, states"

Fig. 5.3 : Software architecture for QSS integration with JModelica with extended FMI API.

In Fig. 5.3, FMU-ME (envelope) is the envelop model that uses a refactored version of the EnergyPlus code. In earlier design, this was an FMU-CS. However, the PyFMI master algorithm requires either all FMU-ME, or all FMU-CS, but if the latter were used, then direct feedthrough would not be allowed. Hence, we are using FMU-ME for EnergyPlus, but similiar as the FMU-QSS, the FMU-ME (envelope) has only discrete states, and the time instant when these states are updated is constant and equal to the EnergyPlus CTF time step.


We still need to design how to handle algebraic loops inside the FMU (see also Cellier’s and Kofman’s book) and algebraic loops that cross multiple FMUs.

The QSS solvers require the derivatives shown in Table 5.1.

Table 5.1 Derivatives required by QSS algorithms. One asteriks indicates that they are provided by FMI-ME 2.0, and two asteriks indicate that they can optionally be computed exactly if directional derivatives are provided by the FMU. The others cannot be provided through the FMI API.
Type of QSS Continuous-time state derivative event indicator derivative
QSS1 \(dx_c/dt\) * \(dz/dt\)
QSS2 \(dx_c/dt\) * , \(d^2x_c/dt^2\) ** \(dz/dt\) , \(d^2z/dt^2\)
QSS3 \(dx_c/dt\) * , \(d^2x_c/dt^2\) ** , \(d^3x_c/dt^3\) \(dz/dt\) , \(d^2z/dt^2\), \(d^3z/dt^3\)

Because the FMI API does not provide access to the required derivatives, and FMI has limited support for QSS, we discuss extensions that are needed for an efficient implementation of QSS.

5.3.1. FMI Changes for QSS

QSS generally requires to only update a subset of the continuous-time state vector. We therefore propose to use the function

fmi2Status fmi2SetReal(fmi2Component c,
                       const fmi2Real x[],
                       const fmi2ValueReference vr[],
                       size_t nx);

to set a subset of the continuous-time state vector. This function exists in FMI-ME 2.0, but the standard only allows to call it for continuous-time state variables during the initialization.

We therefore propose that the standard is being changed as follows:

fmi2SetReal can be called during the continuous time mode and during event mode not only for inputs, as is allowed in FMI-ME 2.0, but also for continuous-time states.

To retrieve individual state derivatives, we introduce the extensions shown in Listing 5.1 to the <Derivatives> element of the modelDescription.xml file.

Listing 5.1 Extensions for obtaining higher order state derivatives. XML additions are marked yellow.
 <ModelVariables> <!-- Remains unchanged -->
   <ScalarVariable name="x",      ...> ... </ScalarVariable> <!-- index="5" -->
   <ScalarVariable name="der(x)", ...> ... </ScalarVariable> <!-- index="8" -->

   <!-- The ScalarVariable with index 8 is der(x) -->
   <Unknown     index="8" dependencies="6" />
   <!-- index 5 is the index of the state variable x -->
   <HigherOrder index="5" order="2" valueReference="124" /> <!-- This is d^2 x/dt^2 -->
   <HigherOrder index="5" order="3" valueReference="125" /> <!-- This is d^3 x/dt^3 -->
 </Derivatives> Event Handling State Events

For efficiency, QSS requires to know the dependencies of event indicators. Also, it will need to have access to, or else approximate numerically, the time derivatives of the event indicator. FMI 2.0 outputs an array of real-valued event indicators, but no variable dependencies.

Therefore, we introduce the following xml section, which assumes we have three event indicators.

    <!-- This is z[0] which depends on ScalarVariable with index 2 and 3 -->
    <Element index="1" order="0" dependencies="2 3" valueReference="200" />
    <!-- This is z[1] which declares no dependencies, hence it may depend on everything -->
    <Element index="2" order="0" valueReference="201" />
     <!-- This is z[2] which declares that it depends only on time -->
    <Element index="3" order="0" dependencies="" valueReference="202" />

    <!-- With order > 0, higher order derivatives can be specified. -->
    <!-- This is dz[0]/dt -->
    <Element index="1" order="1" valueReference="210" />

The attribute dependencies is optional. However, if it is not specified, a tool shall assume that the event indicator depends on all variables. Write dependencies="" to declare that this event indicator depends on no variable (other than possibly time). Note that for performance reasons, for QSS dependencies should be declared for order="0". For higher order, QSS does not use the dependencies.


The index uses in the <EventIndicators> element is different from the index used in the <ModelVariables> element. The first event indicator has an index 1, the second has an index 2, and so on. A new index is introduced because the event indicators do not show up in the list of <ModelVariables>.

The dependencies variables of event indicators are

  • inputs (variables with causality = “input”)
  • continuous-time states
  • independent variable (usually time; causality = “independent”)

Consider the following model

within QSS.Docs;
model StateEvent1 "This model tests state event detection"
  extends Modelica.Icons.Example;
  Real x1(start=0.0, fixed=true) "State variable";
  Real x2(start=0.5, fixed=true) "State variable";
  discrete Modelica.Blocks.Interfaces.RealOutput y(start=1.0, fixed=true)
    "Ouput variable";
  der(x1) = y + 1;
  der(x2) = x2;
  when (x1 > 0.5 and x2 > 1.0) then
    y = -1.0;
  end when;
  annotation (experiment(StopTime=1), Documentation(info="<html>
This model has 2 state events, one at t=0.25
and one at t=0.6924 when simulated from 0 to 1 s.
end StateEvent1;

For efficiency reason, QSS requires the FMU which exports this model to indicate in its model description file the dependency of der(x1) on y. This allows der(x1) to update when y changes. However, y can only change when an event indicator changes its domain. Hence, rather than declaring the dependency on y, it suffices to declare the dependency on the event indicator. Therefore, we propose to include event indicators to the list of state derivative dependencies.

However, FMI states on page 61 that dependencies are optional attributes defining the dependencies of the unknown (directly or indirectly via auxiliary variables) with respect to known. For state derivatives and outputs, known variables are

  • inputs (variables with causality = “input”)
  • continuous-time states
  • independent variable (usually time; causality = “independent”)

Therefore we require to extend the <Derivatives> element with a new attribute eventIndicatorsDependencies which lists all event indicator variables which trigger changes to state derivative trajectories.

An excerpt of such a <Derivatives> element with the new addition is shown in Listing 5.2.

Listing 5.2 Extensions with inclusion of a new attribute for event indicator dependencies. XML additions are marked yellow.
    <!-- The ScalarVariable with index 8 is der(x) -->
    <!-- eventIndicatorsDependencies="1" declares that der(x)
         depends on the event indicator with index 1  -->
    <Unknown     index="8" dependencies="6" eventIndicatorsDependencies="1"/>
    <!-- index 5 is the index of the state variable x -->
    <HigherOrder index="5" order="2" valueReference="124" /> <!-- This is d^2 x/dt^2 -->
    <HigherOrder index="5" order="3" valueReference="125" /> <!-- This is d^3 x/dt^3 -->

For the elements Unknown and HigherOrder, the attributes dependencies and eventIndicatorsDependencies are optional. However, if dependencies is not declared, a tool shall assume that they depend on all variables. Similarly, if eventIndicatorsDependencies is not declared, a tool shall assume that they depend on all event indicators. Write dependencies="" to declare that this derivative depends on no variable. Similarly, write eventIndicatorsDependencies="" to declare that this derivative depends on no event indicator. Note that for performance reasons, for QSS dependencies and eventIndicatorsDependencies should be declared for Unknown. For HigherOrder, QSS does not use the dependencies and eventIndicatorsDependencies.

In Listing 5.2, the higher order derivatives depend on the event indicator.


The indexes of eventIndicatorsDependencies are the indexes of the event indicators specified in the <EventIndicators> element. Event indicators that depend on the input

Consider following model

within QSS.Docs;
model StateEvent2 "This model tests state event detection"
  extends Modelica.Icons.Example;
  Real x(start=-0.5, fixed=true) "State variable";
  discrete Real y(start=1.0, fixed=true "Discrete variable");
  Modelica.Blocks.Interfaces.RealInput u "Input variable"
    annotation (Placement(transformation(extent={{-140,-20},{-100,20}})));
  der(x) = y;
  when (u > x) then
    y = -1.0;
  end when;
  annotation (experiment(StopTime=1), Documentation(info="<html>
This model has a state event
when u becomes bigger than x.
end StateEvent2;

This model has one event indicator \(z = u-x\). The derivative of the event indicator is \({dz}/{dt} = {du}/{dt} - {dx}/{dt}\).

Hence, a tool requires the derivative of the input u to compute the derivative of the event indicator. Since the derivative of the input u is unkown in the FMU, we propose for cases where the event indicator has a direct feedthrough on the input to exclude event indicator derivatives from the <EventIndicators> element. In this situation, the QSS solver will detect the missing information from the XML file and numerically approximate the event indicator derivatives. Handling of variables reinitialized with reinit()

Consider following model

within QSS.Docs;
model StateEvent3 "This model tests state event detection"
  extends Modelica.Icons.Example;
  Real x1(start=0.0, fixed=true) "State variable";
  Real x2(start=0.0, fixed=true) "State variable";
  der(x1) = 1;
  der(x2) = x1;
when time > 0.5 then
  reinit(x1, 0);
end when;
  annotation (experiment(StopTime=1), Documentation(info="<html>
This model has 1 state event at t=0.5s
when simulated from 0 to 1s.
end StateEvent3;

This model has a variable x1 which is reinitialized with the reinit() function. Such variables have in the model description file an attribute reinit which can be set to true or false depending on whether they can be reinitialized at an event or not. Since a reinit() statement is only valid in a when-equation block, we propose for variables with reinit set to true, that at every state event, the QSS solver gets the value of the variable, updates variables which depend on it, and proceeds with its calculation. Workaround for implementing event indicators

While waiting for the implementation of the FMI extensions in JModelica, LBNL will refactor some Modelica models to expose event indicators and their first derivatives as FMU output variables.

The names of event indicators variables will start with __zc_. The names of derivatives of event indicators will start with __zc_der_. As an example, __zc_z1 and __zc_der_z1 are the names of the event indicator z1 with its derivative der_z1.

If the number of event indicators is equal to the numberOfEventIndicators attribute, then only __zc_ and __zc_der_ need to be used by QSS. If the number of event indicators does not match, the FMU shall be rejected with an error message.


Per design, Dymola (2018) generates twice as many event indicators as actually existing in the model. Hence the master algorithm shall detect if the tool which exported the FMU is Dymola, and if it is, the number of event indicators shall be equal to half the value of the numberOfEventIndicators attribute. Time Events

This section discusses additional requirements for handling time events with QSS.

Consider following model

within QSS.Docs;
model TimeEvent "This model tests time event detection"
  extends Modelica.Icons.Example;
  Real x1(start=0.0, fixed=true) "State variable";
  Real x2(start=0.0, fixed=true) "State variable";
  discrete Modelica.Blocks.Interfaces.RealOutput y(start=1.0, fixed=true)
    "Output variable";
  der(x1) = y + 1;
  der(x2) = -x2;
  when (time >= 0.5) then
    y = x2;
  end when;
  annotation (experiment(StopTime=1), Documentation(info="<html>
This model has 1 time event at t=0.5s
when simulated from 0 to 1s.
end TimeEvent;

This model has a time event at \(t \ge 0.5\). For efficiency, QSS requires the FMU which exports this model to indicate in its model description file the dependency of der(x1) on y. However, since y updates when a time event happens but a time event is not described with event indicator, it is not possible to use the same approach as done for StateEvent1 without further modificaton.

We therefore propose that JModelica turns all time events into state events, add new event indicators generated by those state events to the <EventIndicators> element, and include those event indicators to the eventIndicatorsDependencies attribute of state derivatives which depend on them.


All proposed XML changes will be initially implemented in the VendorAnnotation element of the model description file until they got approved and included in the FMI standard. SmoothToken for QSS

This section discusses a proposal for a new data type which should be used for input and output variables of FMU-QSS. FMU-QSS is an FMU for Model Exchange (FMU-ME) which uses QSS to integrate an imported FMU-ME. We propose that FMU-QSS communicates with other FMUs using a SmoothToken data type.

A smooth token is a time-stamped event that has a real value (approximated as a double) that represents the current sample of a real-valued smooth signal. But in addition to the sample value, the smooth token contains zero or more time-derivatives of the signal at the stored time stamp. For example, in the figure below, FMU-ME has a real input \(u\) and a real output \(y\). A smooth token of the input variable will be a variable \(u^* \triangleq [u, n, du/dt, ..., d^n u/dt^n, t_u]\), where \(n \in \{0, 1, 2, \ldots \}\) is a parameter that defines the number of time derivatives that are present in the smooth token and \(t_u\) is the timestamp of the smooth token. If \(u^*\) has a discontinuity at \(t_u\), then the derivatives are the derivatives from above, e.g., \(du/dt \triangleq \lim_{s \downarrow 0} (u(t_u+s)-u(t_u))/s\).

At simulation time \(t\), FMU-QSS will receive \(u^*\) and convert it to a real signal using the Taylor expansion

\[y_s(t) = \frac{u^{(n)}(t_u)}{n!} \, (t-t_u)^n,\]

where \(u^{(n)}\) denotes the \(n\)-th derivative. As shown in Fig. 5.4, the FMU-ME will receive the value \(y_s(t)\).


Fig. 5.4 : Conversion of input signal between FMU-QSS and FMU-ME.

To avoid frequent changes in the input signal, each input signal will have a quantum defined. The quantum \(\delta q\) will be computed at runtime as

\[\delta q = \max(\epsilon_{rel} \, |u^-|, \epsilon_{abs}),\]

where \(\epsilon_{rel}\) is the relative tolerance, \(u^-\) is the last value seen at the input of FMU-ME, and \(\epsilon_{abs} \triangleq \epsilon_{rel} \, |u_{nom}|\), where \(u_{nom}\) is the nominal value of the variable \(u\). During initialization, we set \(u^- = u_0\). The input signal will be updated only if it has changed by more than a quantum,

\[|y_s(t) - u^-| \ge \delta q.\]

To parametrize the smooth token, we propose to extend the FMI for model exchange specification to include fmi2SetRealInputDerivatives and fmi2GetRealOutputDerivatives. These two functions exist in the FMI for co-simulation API.


  • If a tool can not provide the derivative of an output variable with respect to time, fmi2GetRealOutputDerivatives then a master algorithm could approximate the output derivative as follows:

    • If there was no event, then the time derivatives from below and above are equal, and hence past and current output values can be used, e.g.,

      \[dy/dt \approx \frac{y(t_k)-y(t_{k-1})}{t_k - t_{k-1}}.\]
    • If there was an event, then the derivative from above need to be approximated. This could be done by assuming first \(dy/dt =0\) and then building up derivative information in the next step, or by evaluating the FMU for \(t=t_k+\epsilon\), where \(\epsilon\) is a small number, and then computing

      \[dy/dt \approx \frac{y(t_k+\epsilon)-y(t_{k})}{\epsilon}.\]
  • For FMU-ME, if there is a direct feedthrough, e.g., \(y=f(u)\), then \(dy/dt\) cannot be computed, because by the chain rule,

    \[\frac{df(u)}{dt} = \frac{df(u)}{du} \, \frac{du}{dt}\]

    but \(du/dt\) is not available in the FMU.

5.3.2. Summary of Proposed Changes

Here is a list with a summary of proposed changes

  • fmi2SetReal can be called during the continuous, and event time mode for continuous-time states.
  • The <Derivatives> element of the model description file will be extended to include higher order derivatives information.
  • A new <EventIndicators> element wil be added to the model description file. This element will expose event indicators with their dependencies and time derivatives.
  • If a model has an event indicator, and the event indicator has a direct feedthrough on an input variable, then JModelica will exclude the derivatives of that event indicator from the model description file.
  • A new dependency attribute eventIndicatorsDependencies will be added to state derivatives listed in the <Derivatives> element to include event indicators on which the state derivatives depend on.
  • JModelica will convert time event into state events, generate event indicators for those state events, and add those event indicators to the eventIndicatorsDependencies of state derivatives which depend on them.
  • A new function fmi2SetRealInputDerivatives will be included to parametrize smooth token.
  • A new function fmi2GetRealOutputDerivatives will be included to parametrize smooth token.
  • All proposed XML changes will be initially implemented in the VendorAnnotation element of the model description file until they got approved and included in the FMI standard.


  • We need to determine when to efficiently call fmi2CompletedIntegratorStep() to signalize that an integrator step is complete.
  • We need to determine how an FMU deals with state selection, detect it, and reject it on the QSS side.

5.3.3. Open Topics

This section includes a list of measures which could further improve the efficiency of QSS. Some of the measures should be implemented and benchmarked to ensure their necessity for QSS. Atomic API

A fundamental property of QSS is that variables are advanced at different time rates. To make this practically efficient with FMUs, an API for individual values and derivatives is essential. XML/API

All variables with non-constant values probably need to be exposed via the xml with all their interdependencies. The practicality and benefit of trying to hide some variables such as algebraic variables by short-circuiting their dependencies in the xml (or doing this short-circuiting on the QSS side) should be considered for efficiency reasons. Higher Derivatives

Numerical differentiation significantly complicates and slows the QSS code: automatic differentiation provided by the FMU will be a major improvement and allows practical development of 3rd order QSS solvers. Input Variables

  • Input functions with discontinuities up to the QSS order need to be exposed to QSS and provide next event time access to the master algorithm.
  • Input functions need to be true (non-path-dependent) functions for QSS use or at least provide a way to evaluate without “memory” to allow numeric differentiation and event trigger stepping. Annotations

Some per-variable annotations that will allow for more efficient solutions by overriding global settings (which are also needed as annotations) include:

  • Various time steps: dt_min, dt_max, dt_inf, ...
  • Various flags: QSS method/order (or traditional ODE method for mixed solutions), inflection point requantization, ...
  • Extra variability flags: constant, linear, quadratic, cubic, variable, ... Conditional Expressions and Event Indicators

  • How to reliably get the FMU to detect an event at the time QSS predicts one?

    QSS predicts zero-crossing event times that, even with Newton refinement, may be slightly off. To get the FMU to “detect” these events with its “after the fact” event indicators the QSS currently bumps the time forward by some epsilon past the predicted event time in the hopes that the FMU will detect it. Even if made smarter about how big a step to take this will never be robust. Missing events can invalidate simulations. If there is no good and efficient FMU API solution we may need to add the capability for the QSS to handle “after the fact” detected events but with the potential for large QSS time steps and without rollback capability this would give degraded results at best.

  • How much conditional structure should we expose to QSS?

    Without full conditional structure information the QSS must fire events that aren’t relevant in the model/FMU. This will be inefficient for models with many/complex conditionals. These non-event events also alter the QSS trajectories so, for example, adding a conditional clause that never actually fires will change the solution somewhat, which is non-ideal.

  • QSS needs a mechanism similar to zero crossing functions to deal with Modelica’s event generating functions (such as div(x,y) See http://book.xogeny.com/behavior/discrete/events/#event-generating-functions) to avoid missing solution discontinuities.

  • Discrete and non-smooth internal variables need to be converted to input variables or exposed (with dependencies) to QSS.

  • QSS need dependency information for algebraic and boolean/discrete variables either explicitly or short-circuited through the exposed variables for those the QSS won’t track.

  • The xml needs to expose the structure of each conditional block: if or when, sequence order of if/when/else conditionals, and all the (continuous and discrete/boolean) variables appearing in each conditional.

  • Non-input boolean/discrete/integer variables should ideally be altered only by event indicator handlers or time events that are exposed by the FMU (during loop or by direct query?). Are there other ways that such variables can change that are only detectable after the fact? If so, this leaves the QSS with the bad choices of late detection (due to large time steps) or forcing regular time step value checks on them.

  • QSS needs the dependencies of conditional expressions on variables appearing in them.

  • QSS needs the dependencies of variables altered when each conditional fires on the conditional expression variables.

  • It is not robust for the QSS to try and guess a time point where the FMU will reliably detect a zero crossing so we need an API to tell the FMU that a zero crossing occurred at a given time (and maybe with crossing direction information).

  • If the xml can expose the zero crossing directions of interest that will allow for more efficiency.

5.4. OpenStudio integration


This section needs to be revised in view of the move towards a json-driven architecture.

5.4.1. Automated Documentation/AST Support Tool

This section describes an agreed-upon workflow, toolchain and process to generate automated SOEP “documentation” and abstract syntax trees from Modelica libraries; in particular, the Modelica Buildings Library (MBL).

The goal of this effort is to build a computer program that makes the abstract syntax tree (AST) of the Modelica Buildings Library (MBL) [1] available to other tools via an XML file. Specifically, this work should be of use to the OpenStudio team for discovering available models and their parameters and other metadata for use from the OpenStudio interface for SOEP. Background

Modelica source code contains quite a lot of meta-data in addition to the mathematical model itself. An annotation system exists within Modelica and custom annotations can be written to pass data to tools. Annotations were designed to be an extensible mechanism for capturing meta-data related to a model including html-documentation, vendor-specific data, and graphical annotations. Parameters, variables, equations, and models can contain documentation strings and annotations. Furthermore, packages can be documented and the display order of sub-packages, models, classes, functions, connectors and other objects can be specified. Modelica libraries have a hierarchical structure consisting mainly of packages which contain models and other objects as well as sub-packages. Models themselves can be composed of multiple (possibly replaceable) components. Much of this information will be required by the OpenStudio tool in order to understand which component models are available, where they reside in the library’s package structure (i.e., their fully qualified name), what parameters they have, how they can be configured, how to display them, and what other meta-data are available (such as documentation strings and full-on HTML documentation).

This section is concerned about making the AST of Modelica source files, including the metadata mentioned above, available to OpenStudio. We will specifically focus on the Modelica Buildings Library (MBL), though as we discuss later, the tool should also be able to work with other Modelica packages and blocks.

The intent of the AST representation is primarily to be consumed by the OpenStudio application for identification of models, model documentation, model connecting points, inputs/outputs, and parameters and related meta-data.

Presumably, an OpenStudio application will consume some or all of the above information, use it to provide an interface to the user, and ultimately write out a Modelica file which connects the various library components, configures various components and packages (for example, setting the “media” or working fluid of fluid component models in various HVAC loops), and assigning values to parameters.

To create models, information from other libraries, most notably, the Modelica Standard Library (MSL) itself, may need to be made available. Typical applications and examples from the MBL use component models from both the MSL and MBL. There is also interest in being able to support additional third party libraries over and above the MSL and MBL. Therefore, it will be important that this tool is not limited to just parsing/processing the MBL.

Also, related to meta-data, it has been proposed that the MBL add any OpenStudio-specific metadata to the MBL files themselves. It was originally envisioned that Modelica’s annotations should support just such a use-case. It has been noted that if components from other libraries need to be made available to OpenStudio, they could be pulled into the MBL and annotated (for example, components from Modelica.Blocks). Proposed Workflow, Process, and Toolchain

We propose to build a stand-alone batch-process program that will transform any Modelica input file or library (specifically, the Modelica Buildings Library in particular) into an XML document. The XML document will contain:

  • identification of which models, classes, connectors, functions, etc. exist
  • identification of the relative hierarchy of the above components within the package structure of the library
  • for each package, to know
    • what models are available
    • what connection “ports” are available
    • for fluid ports, which ports carry the same fluid (so that media assignments can be propagated through a circuit)
    • what control inputs/outputs are available
    • what parameters are available
    • what configuration management options exist (i.e., replaceable components and packages and which parameters belong to which component)
    • including meta-data and attributes for all the above such as graphics annotations, vendor annotations, html documentation, etc.

Although we intend to build a stand-alone tool, there is interest from the OpenStudio team in using this tool directly and/or incorporating the algorithms into the OpenStudio interface to import new Modelica libraries and files. As such, there are some upstream constraints such as a desire to minimize dependencies. We will discuss this more below when we talk about programming language.

We believe the JModelica tool suite will be able to provide the proper parsing tools for the AST we need. Specifically, we must ensure that the AST we derive from JModelica meets all of our needs – most notably, we must ensure we have access to all annotations. Specifically, we are looking to JModelica for the following:

  1. Provide programmatic access to the full AST of Modelica libraries
  2. Provide that access through the Java and/or Python API.

We have confirmed with preliminary work that we can walk a source AST of a single Modelica file using the Python API of JModelica 1.17. We have also determined that the AST information does include annotation data. The OpenStudio team has expressed a preference for using the Java API directly in hopes of reducing the dependencies required for packaging. Therefore, we will use the Java API and develop a Java application for accessing the AST parser.

The signature of the batch program we will write will be:

ast_doc_gen <options> --out <outputfile-path> <path_to_modelica_library>

Or looking at this as a data-flow diagram:

modelica-library-on-filesystem ==> xml-file

The ast_doc_gen tool will generate an XML file at outputfile-path based on the library available at path_to_modelica_library. We propose that there should only be one library documented per XML file. If additional library data is generated, (for example, the MSL), each library should get its own file.

Options could include flags to allow turning off/on the reporting of various constructs in the source library and for selecting only certain packages to import. For example, the OpenStudio team may need to make the Modelica.Block package of the MSL available for use with the MBL but would not necessarily need to include other packages such as Modelica.Magnetic, Modelica.Electrical, or Modelica.Mechanics.

An additional feature of the tool will be to perform a “diff” (i.e., logical differences) between two generated XML files. The signature for this application would be:

ast_doc_diff <options> --out <path_to_diff_report> <path1> <path2>

The data-flow diagram would be:

(path1, path2) ==> xml-file

The purpose of the ast_doc_diff tool would be to detect non-trivial differences between two generated XML files that hold the AST of a Modelica library and report those changes out to another XML file. The content of the “difference” XML file would explicitly show the differences between the two input manifests. The options here would allow for tweaking the meaning of what it means to be “different”.

The following two lists attempt to list out examples of changes that cannot be ignored as well as changes that can be ignored.

Significant Changes (Cannot be Ignored)

  • changes to names of any public model/block, public parameters, variables, connectors, or subcomponents
  • addition/subtraction of any public parameters, variables, connectors, or subcomponents
  • addition/subtraction of packages/models/blocks/functions meant to be consumed by OpenStudio
  • moving a model/block/function between packages (i.e., changing the path)

Changes that may be Ignored

  • changes in style without changes in meaning (addition or removal of whitespace, windows newlines to linux newlines or vice versa, reordering within a section)
  • changes to documentation strings
  • changes to text in embedded HTML documentation
  • changes to revision notes
  • changes to graphical annotations
  • changes in ordering of models/blocks/functions within a package
  • changes to protected sections of code
  • changes to equation or algorithm sections of code
  • changes to some models/blocks/functions may be completely ignored if, by convention, we deem certain paths as “not directly consumable” by OpenStudio. For example, we may wish to not consume paths under a BaseClasses, Examples, or Functions designation.

To summarize, the creation of or changes to the “public API” (public parameters, variables, subcomponents, connectors) of models, blocks, or functions must be compared for change detection. Documentation (HTML)/ documentation strings/graphics annotations will not affect the model interface or semantics. Changes to protected properties or equations will not affect the interface (but may affect the actual numeric output quantities).

Annotations could be used to signal status changes such as “deprecation”.

The ast_doc_diff tool would be of use in particular when new versions of the MBL are released and the OpenStudio team would like to check if there are non-trivial changes they need to integrate. Literature review

There have been several attempts to represent or use XML in relation to Modelica in the past ([Lan14], [Fri03], [PF03], [PSAssmannF05], and [RKH+06]).

In particular, N. Landin did work with Modelon using JModelica to export XML for the purpose of model exchange [Lan14] – this is very similar to our use case. Unfortunately, this work deals only with “flattened” models – Modelica models that have been instantiated with all of the hierarchy removed. For our use case, the hierarchy must be preserved so that the OpenStudio team can build a new model through instantiation of models from the MBL.

The paper by Reisenbichler 2006 motivates the usage of XML in association with Modelica without getting into specifics [RKH+06]. The remaining work by Pop and Fritzson is thus the only comprehensive work on an XML representation of Modelica source AST that appears in the literature ([PF03], [PSAssmannF05], and [Fri03]). The purpose of the XML work by Pop and Fritzson was to create a complete XML representation of the entire Modelica source. It is generally a good reference but we note that it is, perhaps unnecessarily, verbose for our current needs. As such, although we will refer to this work, we do not plan to duplicate it. Discussion and Details


We are currently evaluating whether to use the commercial JModelica API directly to do the following or whether to generate XML. We will leave the discussion in as-is for now as it captures some elements of the design work but it needs to be updated once a decision is made.

A key area of work will be on designing the data model of the XML output. Specifically, we need to think through how to represent the models in the MBL in such a way that they can be consumed by the OpenStudio toolchain. At the planning meeting on February 1, 2017, it was discussed that we generally want all of the information from the source AST except equation and algorithm sections. All annotations should be made available.

One consideration will be: which version of the AST should be used to represent packages, classes, models, etc. The JModelica User’s Guide 1.17 in Chapter 9 talks about three kinds of AST: source level, instance level, and flattened. The flattened AST is not relevant for us (it corresponds to a fully flattened model instance ready to be compiled; our interest is in browsing all objects for potential configuration).

The source level AST corresponds 1:1 to the original files in both structure and content. Although the source AST is what we need, it does not expand out components and extended classes and thus may require additional processing by consumers.

An instance level AST, in contrast, represents the fully expanded instance of a given model or class, including configurations. Although this is tempting to use, we must remember that we are dealing with a library, not a model instance. It will be OpenStudio‘s job to build and specify a model class to instantiate. Especially due to Modelica’s configuration mechanism, it would be dangerous to treat object classes as instances.

Therefore, we delivered something closer to the source AST but with a mind to construct the data model such that it is easy to trace dependencies such as class extensions (i.e., inheritance) and replaceable components.

For an example, consider the following model

within Buildings.Fluid.HeatExchangers;
model HeaterCooler_T
  "Ideal heater or cooler with a prescribed outlet temperature"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface;
  extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    final computeFlowResistance=(abs(dp_nominal) > Modelica.Constants.eps));
  extends Buildings.Fluid.Interfaces.PrescribedOutletStateParameters(

  parameter Boolean homotopyInitialization = true "= true, use homotopy method"
    annotation(Evaluate=true, Dialog(tab="Advanced"));

  Modelica.Blocks.Interfaces.RealInput TSet(unit="K", displayUnit="degC")
    "Set point temperature of the fluid that leaves port_b"
    annotation (Placement(transformation(extent={{-140,40},{-100,80}})));

  Modelica.Blocks.Interfaces.RealOutput Q_flow(unit="W")
    "Heat added to the fluid (if flow is from port_a to port_b)"
    annotation (Placement(transformation(extent={{100,50},{120,70}})));

  Buildings.Fluid.FixedResistances.PressureDrop preDro(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final deltaM=deltaM,
    final allowFlowReversal=allowFlowReversal,
    final show_T=false,
    final from_dp=from_dp,
    final linearized=linearizeFlowResistance,
    final homotopyInitialization=homotopyInitialization,
    final dp_nominal=dp_nominal) "Flow resistance"
    annotation (Placement(transformation(extent={{-50,-10},{-30,10}})));

  Buildings.Fluid.Interfaces.PrescribedOutletState heaCoo(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_small=m_flow_small,
    final show_T=false,
    final show_V_flow=false,
    final Q_flow_maxHeat=Q_flow_maxHeat,
    final Q_flow_maxCool=Q_flow_maxCool,
    final m_flow_nominal=m_flow_nominal,
    final tau=tau,
    final T_start=T_start,
    final energyDynamics=energyDynamics) "Heater or cooler"
    annotation (Placement(transformation(extent={{20,-10},{40,10}})));
  connect(port_a, preDro.port_a) annotation (Line(
  connect(preDro.port_b, heaCoo.port_a) annotation (Line(
  connect(heaCoo.port_b, port_b) annotation (Line(
  connect(heaCoo.TSet, TSet) annotation (Line(
  connect(heaCoo.Q_flow, Q_flow) annotation (Line(
  annotation (Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,
            -100},{100,100}}), graphics={
Model for an ideal heater or cooler with a prescribed outlet temperature.
This model forces the outlet temperature at <code>port_b</code> to be equal to the temperature
of the input signal <code>TSet</code>, subject to optional limits on the
heating or cooling capacity <code>Q_flow_max</code> and <code>Q_flow_min</code>.
For unlimited capacity, set <code>Q_flow_maxHeat = Modelica.Constant.inf</code>
and <code>Q_flow_maxCool=-Modelica.Constant.inf</code>.
The output signal <code>Q_flow</code> is the heat added (for heating) or subtracted (for cooling)
to the medium if the flow rate is from <code>port_a</code> to <code>port_b</code>.
If the flow is reversed, then <code>Q_flow=0</code>.
The outlet temperature at <code>port_a</code> is not affected by this model.
If the parameter <code>energyDynamics</code> is not equal to
the component models the dynamic response using a first order differential equation.
The time constant of the component is equal to the parameter <code>tau</code>.
This time constant is adjusted based on the mass flow rate using
<p align=\"center\" style=\"font-style:italic;\">
&tau;<sub>eff</sub> = &tau; |m&#775;| &frasl; m&#775;<sub>nom</sub>
<i>&tau;<sub>eff</sub></i> is the effective time constant for the given mass flow rate
<i>m&#775;</i> and
<i>&tau;</i> is the time constant at the nominal mass flow rate
This type of dynamics is equal to the dynamics that a completely mixed
control volume would have.
Optionally, this model can have a flow resistance.
If no flow resistance is requested, set <code>dp_nominal=0</code>.
For a model that uses a control signal <i>u &isin; [0, 1]</i> and multiplies
this with the nominal heating or cooling power, use
<a href=\"modelica://Buildings.Fluid.HeatExchangers.HeaterCooler_u\">
This model only adds or removes heat for the flow from
<code>port_a</code> to <code>port_b</code>.
The enthalpy of the reverse flow is not affected by this model.
This model does not affect the humidity of the air. Therefore,
if used to cool air below the dew point temperature, the water mass fraction
will not change.
The model has been validated against the analytical solution in
the examples
<a href=\"modelica://Buildings.Fluid.HeatExchangers.Validation.HeaterCooler_T\">
<a href=\"modelica://Buildings.Fluid.HeatExchangers.Validation.HeaterCooler_T_dynamic\">
December 1, 2016, by Michael Wetter:<br/>
Updated model as <code>use_dh</code> is no longer a parameter in the pressure drop model.<br/>
This is for
<a href=\"https://github.com/ibpsa/modelica/issues/480\">#480</a>.
November 11, 2014, by Michael Wetter:<br/>
Revised implementation.
March 19, 2014, by Christoph Nytsch-Geusen:<br/>
First implementation.
end HeaterCooler_T;

When parse to a json representation, the output of its public declarations is as follows:

"Buildings.Fluid.HeatExchangers.HeaterCooler_T": {
    "name": "Buildings.Fluid.HeatExchangers.HeaterCooler_T",
    "comment": "Ideal heater or cooler with a prescribed outlet temperature",
    "qualifiers": [
    "superClasses": [
            "nameOfExtendedClass": "Buildings.Fluid.Interfaces.PartialTwoPortInterface"
            "nameOfExtendedClass": "Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters",
            "modifications": [
                    "name": "computeFlowResistance",
                    "qualifiers": [
                    "value": "abs(dp_nominal)>Modelica.Constants.eps"
            "nameOfExtendedClass": "Buildings.Fluid.Interfaces.PrescribedOutletStateParameters",
            "modifications": [
                    "name": "T_start",
                    "value": "Medium.T_default"
    "components": [
            "className": "Boolean",
            "qualifiers": [
            "name": "homotopyInitialization",
            "comment": "= true, use homotopy method",
            "value": "true",
            "annotations": {
                "dialog": "Dialog(tab = \"Advanced\")"
            "className": "Modelica.Blocks.Interfaces.RealInput",
            "name": "TSet",
            "comment": "Set point temperature of the fluid that leaves port_b",
            "modifications": [
                    "name": "unit",
                    "value": "\"K\""
                    "name": "displayUnit",
                    "value": "\"degC\""
            "annotations": {
                "placement": "Placement(transformation(extent = {{-140,40},{-100,80}}))"
            "className": "Modelica.Blocks.Interfaces.RealOutput",
            "name": "Q_flow",
            "comment": "Heat added to the fluid (if flow is from port_a to port_b)",
            "modifications": [
                    "name": "unit",
                    "value": "\"W\""
            "annotations": {
                "placement": "Placement(transformation(extent = {{100,50},{120,70}}))"
    "annotations": {
        "documentationInfo": "info = \"<html>\n<p>\nModel for an ideal heater or cooler with
        a prescribed outlet temperature.\n</p>\n<p>\nThis model forces the outlet temperature at <code>port_b</code>
        [further text has been omitted]
        "icon": "Icon(coordinateSystem(preserveAspectRatio = false,
                       extent = {{-100,-100},{100,100}}),
                       graphics = {Rectangle(),Rectangle(),Rectangle(),Text(),Rectangle(),Text()})"

Hence, OpenStudio can reads its parameters from the json file. Note that it also will need to parse the json representation from the superClasses, for example to obtain the connectors and parameters, including their modifications, from Buildings.Fluid.Interfaces.PartialTwoPortInterface. Summary of Questions and Next Steps


  • What pre-processing on the extracted data would be useful?

Next Steps:

  • Make a decision as to the JModelica API to use
  • Determine what constitutes a significant difference between different versions of the Modelica Buildings Library and how to communicate those
  • Write the proposed programs using JModelica to extract AST data from Modelica Models in a library and write that data out as XML.
  • Create diff tool for comparing versions of the MBL in a meaningful way


[1]Our main focus is to support the Modelica Buildings Library but the tool should also work for other Modelica libraries.