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.

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.

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

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.

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:

- It computes the HVAC power required to meet the temperature set point.
- It simulates the HVAC system to see whether it can meet this load.
- 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.

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.

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

The EnergyPlus FMU is for model exchange and contains the envelope and the room surfaces. 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.

The following parameters are sent from EnergyPlus to Modelica. These are sent only once during the initialization for each thermal zone.

Variable | Dimension | Quantity | Unit |
---|---|---|---|

From EnergyPlus to Modelica |
|||

V | \(1\) | Volume of the zone air | m3 |

AFlo | \(1\) | Floor area of the zone | m2 |

mSenFac | \(1\) | Factor for scaling the sensible thermal mass of the zone air volume | 1 |

The following time-dependent variables are exchanged between EnergyPlus and Modelica during the time integration for each thermal zone.

Variable | Dimension | Quantity | Unit |
---|---|---|---|

From Modelica to EnergyPlus |
|||

T | \(1\) | Temperature of the zone air | degC |

X | \(1\) | Water vapor mass fraction per total air mass of the zone | kg/kg |

mInlets_flow | \(1\) | Sum of positive mass flow rates into the zone for all air inlets (including infiltration) | kg/s |

TInlet | \(1\) | Average of inlets medium temperatures carried by the mass flow rates | 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 to Modelica |
|||

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.

For this coupling, all zones of EnergyPlus will be accessed from Modelica. For example, if a building has two zones, then both zones need to be modeled in Modelica.

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;
fmi2Real
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\).

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.

Note

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.

Note

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 char ** parameterNames,
const unsigned int parameterValueReferences[],
size_t nPar,
const char ** inputNames,
const unsigned int inputValueReferences[],
size_t nInp,
const char ** outputNames,
const unsigned int outputValueReferences[],
size_t nOut,
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.`parameterNames`

: A vector of`nPar`

strings that identifies the names of the parameters that are to be retrieved from EnergyPlus.`parameterValueReferences`

: A vector of value references for the quantities in`parameterNames`

. Value references uniquely identify the variables, and are used in the`setVariables`

and`getVariables`

calls below.`nPar`

: Number of elements of the parameter vector, e.g., length of`parameterNames`

and`parameterValueReferences`

.`inputNames`

: A vector of`nInp`

strings that identifies the names of the inputs sent to EnergyPlus.`inputValueReferences`

: A vector of value references for the quantities in`inputNames`

.`nInp`

: Number of elements of the input vector, e.g., length of`inputNames`

and`inputValueReferences`

.`outputNames`

: A vector of`nOut`

strings that identifies the names of the outputs to be retrieved from EnergyPlus.`outputValueReferences`

: A vector of value references for the quantities in`outputNames`

.`nOut`

: Number of elements of the output vector, e.g., length of`outputNames`

and`outputValueReferences`

.`log`

: Logging message returned on error.

For example, if a building has two zones called `basement`

and `office`

, then the parameter names are

To configure the variables to be exchanged, the following data structures will be used.

```
const char ** parameterNames = {"basement,V", "basement,AFlo", "basement,mSenFac", "office,V", "office,AFlo", "office,mSenFac"};
const unsigned int parameterValueReferences[] = {0, 1, 2, 3, 4, 5, 6};
```

The inputs into EnergyPlus will be

```
const char ** inputNames = {"basement,T", "basement,X", "basement,mInlets_flow", "basement,TInlet", "basement,QGaiRad_flow",
"office,T", "office,X", "office,mInlets_flow", "office,TInlet", "office,QGaiRad_flow"};
const unsigned int inputValueReferences[] = {7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
```

The outputs of EnergyPlus will be

```
const char ** outputNames = {"basement,TRad", "basement,QConSen_flow", "basement,QLat_flow", "basement,QPeo_flow",
"office,TRad", "office,QConSen_flow", "office,QLat_flow", "office,QPeo_flow"};
const unsigned int outputValueReferences[] = {17, 18, 19, 20, 21, 22, 23, 24};
```

This function will read the `idf`

file and sets up the data structure in EnergyPlus.

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

```
unsigned int setupExperiment(double tStart,
const char *log);
```

`tStart`

: Start of simulation in seconds.`log`

: Logging message returned on error.

This functions sets the start time of EnergyPlus to the value `tStart`

.
There is no warm-up simulation. EnergyPlus will continue the simulation until
`terminate(const char *)`

(see below) is called.

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

Note

The EnergyPlus start time is as retrieved from the argument of
`setupExperiment(...)`

. The `RunPeriod`

in the `idf`

file is only
used to determine the day of the week.

*Possible complication*:

Users may set `tStart`

to a time other than midnight. We think EnergyPlus cannot yet handle an arbitrary start time.
In this case, it should 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 `valueReferences`

could be a subset of the pointer `inputValueReferences`

that was setup in `instantiate(...)`

, i.e., `nVars1 <= nInp`

(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 `outputValueReferences`

during the `instantiate(...)`

call.
`nVars2 <= nOut`

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.

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.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | ```
// 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. Note that tEnd is not set in M_fmi2SetupExperiment
M_fmi2SetupExperiment(m, fmi2False, 0.0, Tstart, fmi2False, 0.0) -> 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
do
// 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)
else
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
TERMINATE_MODEL:
M_fmi2GetReal(m, ...) -> getVariables()
M_fmi2Terminate(m) -> terminate(...)
// cleanup
M_fmi2FreeInstance(m) -> NA
``` |

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.

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

(5.1)¶\[ \begin{align}\begin{aligned}\left[\dot x_c(t), x_d(t)\right] = f(x_c(t), x_d(t^-), u_c(t), u_d(t), p, t),\\\begin{split}\left[y_c(t), y_d(t)\right] = 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}\\\left[x_c(t_0), x_d(t_0)\right] = [x_{c,0}, x_{d,0}],\end{aligned}\end{align} \]

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.

Note

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.

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.

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.

```
<ModelVariables> <!-- Remains unchanged -->
...
<ScalarVariable name="x", ...> ... </ScalarVariable> <!-- index="5" -->
...
<ScalarVariable name="der(x)", ...> ... </ScalarVariable> <!-- index="8" -->
</ModelVariables>
<Derivatives>
<!-- 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>
```

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.

```
<ModelStructure>
<EventIndicators>
<!-- 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" />
</EventIndicators>
</ModelStructure>
```

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.

Note

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";
equation
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>
<p>
This model has 2 state events, one at t=0.25
and one at t=0.6924 when simulated from 0 to 1 s.
</p>
</html>"));
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.

```
<Derivatives>
<!-- 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 -->
</Derivatives>
```

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.

Note

The indexes of `eventIndicatorsDependencies`

are the indexes of the
event indicators specified in the `<EventIndicators>`

element.

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}})));
equation
der(x) = y;
when (u > x) then
y = -1.0;
end when;
annotation (experiment(StopTime=1), Documentation(info="<html>
<p>
This model has a state event
when u becomes bigger than x.
</p>
</html>"));
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.

`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";
equation
der(x1) = 1;
der(x2) = x1;
when time > 0.5 then
reinit(x1, 0);
end when;
annotation (experiment(StopTime=1), Documentation(info="<html>
<p>
This model has 1 state event at t=0.5s
when simulated from 0 to 1s.
</p>
</html>"));
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.

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.

Note

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.

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";
equation
der(x1) = y + 1;
der(x2) = -x2;
when (time >= 0.5) then
y = x2;
end when;
annotation (experiment(StopTime=1), Documentation(info="<html>
<p>
This model has 1 time event at t=0.5s
when simulated from 0 to 1s.
</p>
</html>"));
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.

Note

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.

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)\).

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.

Note

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.

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.

Note

- 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.

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.

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.

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.

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 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.

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, …

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.

Note

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

This section describes how to generate automated SOEP documentation and abstract syntax trees from the Modelica Buildings Library (MBL), or from other Modelica libraries that users may use with SOEP. The goal is to expose the abstract syntax tree (AST) of the MBL, and make it available to other tools via one or several json files. These json files can then be used by OpenStudio to discover available models and their parameters and other metadata for use from the OpenStudio interface for SOEP.

Modelica source code contains 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 which determines 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 type of connectors that can be connected with each other.

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), but other Modelica libraries can be used as well.

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.

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 assigns values to parameters.

To create models, information from other libraries, most notably, the Modelica Standard Library (MSL), 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.

This section will describe the requirements of a batch-process program that will transform any Modelica input file or library into a json file. The json file shall 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,
- 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) [1]
- 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)
- meta-data and attributes for all the above such as graphics annotations, vendor annotations, html documentation, etc.

For integration with OpenStudio, the tool should reduce dependencies that complicate the distribution to users.

The tool shall also have options to exclude certain packages. For example, OpenStudio may give access to the package Modelica.Blocks but not to Modelica.Magnetic.

The tool shall also allow to perform a “diff” (i.e., logical differences) between two generated json files. The purpose of the diff tool would be to detect non-trivial differences between two generated json files that hold the AST of a Modelica library and report those changes. The content of the “difference” file would show the differences between the two input manifests.

The 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.

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

*Significant Changes (backward-incompatible)*

- changes to names of any public package, model, block, function, record, as well as public constants, parameters, variables, connectors, or subcomponents
- addition of any public parameters that has no default value, variables that have no equation (in a partial model), connectors (except for output connectors), or subcomponents that require changes to models that extend or instantiate these components
- addition/subtraction of packages/models/blocks/functions meant to be consumed by OpenStudio
- removing of any public constant, parameter, or class (model, block, function, type etc.)
- moving a class between packages (i.e., changing the path)

*Changes that may be ignored (backward-compatible)*

- changes in style without changes in meaning (addition or removal of whitespace, 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 classes in the packages BaseClasses, Examples and Validations.

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).

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, Landin [Lan14] did work with Modelon using JModelica to export XML for the purpose of model exchange, which is 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. For this purpose, JModelica also provides access to the the source AST and instance AST (see the JModelica user guide).

Reisenbichler [RKH+06] motivates the usage of XML in association with
Modelica without getting into specifics.
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.

ANTLR (ANother Tool for Language Recognition) is a parser generator for reading, processing, executing, or translating structured text or binary files. It is widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build parse trees and also generates a listener interface (or visitor) that makes it easy to respond to the recognition of phrases of interest. For ANTLR, a Modelica grammar is available at https://github.com/antlr/grammars-v4/blob/master/modelica/modelica.g4.

Work started on the implementation of a modelica-json translator. The development page is https://github.com/lbl-srg/modelica-json

To illustrate the translation, consider the following simple model:

```
within FromModelica;
block BlockWithBlock1 "A block that instantiates another public and protected block"
Block1 bloPub "A public block";
protected
Block1 bloPro "A protected block";
end BlockWithBlock1;
```

When parsed to json, the output is:

```
[
{
"modelicaFile": "./BlockWithBlock1.mo",
"within": "FromModelica",
"topClassName": "FromModelica.BlockWithBlock1",
"comment": "A block that instantiates another public and protected block",
"public": {
"models": [
{
"className": "Block1",
"name": "bloPub",
"comment": "A public block"
}
]
},
"protected": {
"models": [
{
"className": "Block1",
"name": "bloPro",
"comment": "A protected block"
}
]
}
}
]
```

Footnotes

[1] | We anticipate that the MBL will be redesigned so that users no longer need to assign media. |