7. Control Description Language¶
7.1. Introduction¶
This section specifies the Control Description Language (CDL), a declarative language that can be used to express control sequences using block-diagrams. It is designed in such a way that it can be used to conveniently specify building control sequences in a vendor-independent format, use them within whole building energy simulation, and translate them for use in building control systems.
A key technical challenge encountered when developing CDL was that existing control product lines are heterogeneous. They differ in their functionality for expressing control sequences, in their semantics of how control output gets updated, and in their syntax which ranges from graphical languages to textual languages. Code generation for a variety of products is common in the Electronic Design Automation industry. However, in the Electronic Design Automation industry, engineers write models and controllers are built to conform to the models. If this were to be applied to the buildings industry, then control providers would need to update their product line in order to be able to faithfully comply with the model. We think such costly product line reconfigurations are not reasonable to expect in the next decade. Therefore, for the immediate future, we will need to build digital models of control sequences that can conform to their implementation on target control product lines; while ensuring that as new product lines are being developed, the manufacturers can invert the paradigm and build controllers that conform to the models. We therefore selected the path of designing CDL in such a way that it provide a minimum set of capabilities that can be expected to be supported by current control product lines, while allowing future control product lines to directly use CDL for the implementation of the control sequences. As we have demonstrated with one commercial product, the barrier to translate CDL to the programming language of a current control product line is low.
Fig. 7.1 Translation of CDL to the CDL-JSON intermediate format and to a product line or English language documentation.¶
To put CDL in context, and to introduce terminology, Fig. 7.1 shows the translation of CDL to a control product line
or to English language documentation.
Input into the translation is CDL. An open-source tool called modelica-json
translator
(see also Section 10.3 and https://github.com/lbl-srg/modelica-json)
translates CDL to an intermediate format that we call CDL-JSON.
From CDL-JSON, further translations can be done to a control product line, or to
generate point lists or English language documentation of the control sequences.
We anticipate that future control product lines use directly CDL as shown in the right of
Fig. 7.1. Such a translation can then be done using
various existing Modelica tools to generate code for real-time simulation.
The next sections define the CDL language.
A collection of control sequences that are composed using the CDL language is described
in Section 9. These sequences can be simulated with Modelica simulation environments.
The translation of such sequences to control product lines using modelica-json
,
or other means of translation, is described in
Section 10.
7.2. Basic Elements of CDL¶
The CDL consists of the following elements:
A list of elementary control blocks, such as a block that adds two signals and outputs the sum, or a block that represents a PID controller.
Connectors through which these blocks receive values and output values.
Permissible data types.
Syntax to specify
how to instantiate these blocks and assign values of parameters, such as a proportional gain.
how to connect inputs of blocks to outputs of other blocks.
how to document blocks.
how to add annotations such as for graphical rendering of blocks and their connections.
how to specify composite blocks.
A model of computation that describes when blocks are executed and when outputs are assigned to inputs.
7.3. Syntax¶
In order to use CDL with building energy simulation programs, and to not invent yet another language with new syntax, the CDL syntax conforms to a subset of the Modelica 3.3 specification [Mod12]. The selected subset is needed to instantiate classes, assign parameters, connect objects and document classes. This subset is fully compatible with Modelica, e.g., no construct that violates the Modelica Standard has been added, thereby allowing users to view, modify and simulate CDL-conformant control sequences with any Modelica-compliant simulation environment.
To simplify the support of CDL for tools and control systems, the following Modelica keywords are not supported in CDL:
extends
redeclare
constrainedby
inner
andouter
Also, the following Modelica language features are not supported in CDL:
Clocks [which are used in Modelica for hybrid system modeling].
algorithm
sections. [As the elementary building blocks are black-box models as far as CDL is concerned and thus CDL compliant tools need not parse thealgorithm
section.]initial equation
andinitial algorithm
sections.
7.4. Permissible Data Types¶
7.4.1. Data Types¶
This section defines the basic data types. The definition is a subset of Modelica in which we left out attributes that are not needed for CDL.
The attributes that are present in Modelica but not in CDL are marked with //--
.
[Note the following: The start
attribute is not needed in CDL because the start value of states is
declared through a parameter.
The equation
section has been removed because how to deal with variables that
are out of limit should be left to the implementation of the control system.
]
7.4.1.1. Real Type¶
The following is the predefined Real
type:
type Real // Note: Defined with Modelica syntax although predefined
RealType value; // Accessed without dot-notation
parameter StringType quantity = "";
parameter StringType unit = "" "Unit used in equations";
parameter StringType displayUnit = "" "Default display unit";
parameter RealType min=-Inf, max=+Inf; // Inf denotes a large value
//-- parameter RealType start = 0; // Initial value
//-- parameter BooleanType fixed = true, // default for parameter/constant;
//-- = false; // default for other variables
parameter RealType nominal; // Nominal value
//-- parameter BooleanType unbounded=false; // For error control
//-- parameter StateSelect stateSelect = StateSelect.default;
//-- equation
//-- assert(value >= min and value <= max, "Variable value out of limit");
end Real;
Real Type/double
matches the IEC 60559:1989 (ANSI/IEEE 754-1985) double format.
The quantity
attribute is optional, can take on the following values:
""
, which is the default, is considered as no quantity being specified.Angle
for area (such as used for sun position).Area
for area.Energy
for energy.Frequency
for frequency.Illuminance
for illuminance.Irradiance
for solar irradiance.MassFlowRate
for mass flow rate.MassFraction
for mass fraction.Power
for power.PowerFactor
for power factor.Pressure
for absolute pressure.PressureDifference
for pressure difference.SpecificEnergy
for specific energy.TemperatureDifference
for temperature difference.Time
for time.ThermodynamicTemperature
for absolute temperature.Velocity
for velocity.VolumeFlowRate
for volume flow rate.
[These quantities are compatible with the quantities used in the Modelica Standard Library, to allow connecting CDL models to Modelica models, see also Section 7.10.]
[The quantity attribute could be used for example to declare in a sequence that a real signal is a AbsolutePressure
.
This could be used to aid connecting signals or filtering data.
Quantities serve a different purpose than tagged properties (Section 7.14.2).]
The value of displayUnit
is used as a recommendation for how to display units to the user.
[For example, tools that implement CDL may convert the value
from unit
to displayUnit
before showing it in a GUI or a log file.
Moreover, tools may have a global list where users can specify, for example,
to display degC
and K
in degF
.]
The nominal attribute is meant to be used for scaling purposes and to define tolerances, such as for integrators, in relative terms.
7.4.1.2. Integer Type¶
The following is the predefined Integer
type:
type Integer // Note: Defined with Modelica syntax although predefined
IntegerType value; // Accessed without dot-notation
//-- parameter StringType quantity = "";
parameter IntegerType min=-Inf, max=+Inf;
//-- parameter IntegerType start = 0; // Initial value
//-- parameter BooleanType fixed = true, // default for parameter/constant;
//-- = false; // default for other variables
//-- equation
//-- assert(value >= min and value <= max, "Variable value out of limit");
end Integer;
The minimal recommended number range for IntegerType is from \(-2147483648\) to \(+2147483647\), corresponding to a two’s-complement 32-bit integer implementation.
[The quantity
attribute could be used for example to declare in a sequence that a integer signal is a NumberOfHeatingRequest
.
This could be used to aid connecting signals or filtering data.]
7.4.1.3. Boolean Type¶
The following is the predefined Boolean
type:
type Boolean // Note: Defined with Modelica syntax although predefined
BooleanType value; // Accessed without dot-notation
//-- parameter StringType quantity = "";
//-- parameter BooleanType start = false; // Initial value
//-- parameter BooleanType fixed = true, // default for parameter/constant;
//-- = false, // default for other variables
end Boolean;
[The quantity
attribute could be used for example to declare in a sequence that a boolean signal is a ChillerOn
command.]
7.4.1.4. String Type¶
The following is the predefined String
type:
type String // Note: Defined with Modelica syntax although predefined
StringType value; // Accessed without dot-notation
//-- parameter StringType quantity = "";
//-- parameter StringType start = ""; // Initial value
//-- parameter BooleanType fixed = true, // default for parameter/constant;
//-- = false, // default for other variables
end String;
7.4.1.5. Enumeration Types¶
A declaration of the form
type E = enumeration([enumList]);
defines an enumeration type E
and the associated enumeration literals of the enumList
.
The enumeration literals shall be distinct within the enumeration type.
The names of the enumeration literals are defined inside the scope of E
.
Each enumeration literal in the enumList
has type E
.
[Example:
type SimpleController = enumeration(P, PI, PD, PID);
parameter SimpleController = SimpleController.P;
]
An optional comment string can be specified with each enumeration literal.
[Example:
type SimpleController = enumeration(
P "P controller",
PI "PI controller",
PD "PD controller",
PID "PID controller")
"Enumeration defining P, PI, PD, or PID simple controller type";
]
[Enumerations can for example be used to declare a list of mode of operations, such as on
, off
, startUp
, coolDown
.]
7.4.2. Parameter and constant declarations¶
A parameter
is a value that does not change as time progresses, except through
stopping the executation of the control sequence,
setting a new value through a user interaction or an API, and restarting the execution.
In other words, the value of a parameter
cannot be changed through an input connector (Section 7.8).
Parameters are declared with the parameter
prefix.
[For example, to declare a proportional gain, use
parameter Real k(min=0) = 1 "Proportional gain of controller";
]
A constant
is a value that is fixed at compilation time.
Constants are declared with the constant
prefix.
[For example,
constant Real pi = 3.14159;
]
7.4.3. Arrays¶
Each of these data types, including the elementary building blocks,
can be a single instance, one-dimensional array or two-dimensional array (matrix).
Array indices shall be of type Integer
only.
The first element of an array has index 1
.
An array of size 0
is an empty array.
Arrays may be constructed using the notation {x1,x2,...}
,
for example parameter Integer k[3,2] = {{1,2},{3,4},{5,6}}
, or using
one or several iterators, for example
parameter Real k[2,3] = {i*0.5+j for i in 1:3, j in 1:2};
.
They can also be constructed using
a fill
or cat
function, see Section 7.7.1.
The size of arrays will be fixed at translation. It cannot be changed during run-time.
[enumeration
or Boolean
data types are not permitted as array indices.]
See the Modelica 3.3 specification Chapter 10 for array notation and these functions.
7.5. Encapsulation of Functionality¶
All computations are encapsulated in a block
.
Blocks expose parameters (used to configure
the block, such as a control gain), and they
expose inputs and outputs using connectors.
Blocks are either elementary building blocks (Section 7.6) or composite blocks (Section 7.12).
7.6. Elementary Building Blocks¶

Fig. 7.2 Screenshot of CDL library.¶
The CDL library contains elementary building blocks that are used to compose control sequences. The functionality of elementary building blocks, but not their implementation, is part of the CDL specification. Thus, in the most general form, elementary building blocks can be considered as functions that for given parameters \(p\), time \(t\) and internal states \(x(t)\), map inputs \(u(t)\) to new outputs \(y(t)\), e.g.,
Control providers who support CDL need to be able to implement the same functionality as is provided by the elementary CDL blocks.
[CDL implementations are allowed to use a different implementation of the elementary building blocks, because the implementation is language specific. However, implementations shall have the same inputs, outputs and parameters, and they shall compute the same response for the same value of inputs and state variables.]
Users are not allowed to add new elementary building blocks. Rather, users can use the existing elementary blocks to implement composite blocks (Section 7.12).
Note
The elementary building blocks can be browsed in any of these ways:
Open a web browser at https://simulationresearch.lbl.gov/modelica/releases/latest/help/Buildings_Controls_OBC_CDL.html.
Download https://github.com/lbl-srg/modelica-buildings/archive/master.zip, unzip the file, and open
Buildings/package.mo
in the graphical model editor of OpenModelica, Impact, or Dymola. All models in the Examples and Validation packages can be simulated with these tools, as well as with OPTIMICA, which is the simulation engine in Impact.
An actual implementation of an elementary building block looks as follows, where we omitted the annotations that are used for graphical rendering:
block AddParameter "Output the sum of an input plus a parameter"
parameter Real p "Value to be added";
Interfaces.RealInput u "Connector of Real input signal";
Interfaces.RealOutput y "Connector of Real output signal";
equation
y = u + p;
annotation(Documentation(info("
<html>
<p>
Block that outputs <code>y = u + p</code>,
where <code>p</code> is parameter and <code>u</code> is an input.
</p>
</html>"));
end AddParameter;
For the complete implementation, see the github repository.
7.7. Instantiation¶
7.7.1. Parameter Declaration and Assigning of Values to Parameters¶
Parameters are values that do not depend on time. The values of parameters can be changed during run-time through a user interaction with the control program (such as to change a control gain), unless a parameter is a structural parameter.
The declaration of parameters and their values is identical to Modelica,
but we limit the type of expressions that are allowed in such assignments. In particular,
for Boolean
parameters, we allow expressions involving
and
, or
and not
and the function fill(..)
in Table 7.1.
For Real
and Integer
, expressions are allowed that involve
the basic arithmetic functions
+
,-
,*
,-
,the relations
>
,>=
,<
,<=
,==
,<>
,calls to the functions listed in Table 7.1.
Function |
Descrition |
---|---|
|
Absolute value of |
|
Returns |
|
Returns the square root of |
|
Returns the algebraic quotient |
|
Returns the integer modulus of [Examples are |
|
Returns the integer remainder of [Examples are |
|
Returns the smallest integer not less than |
|
Returns the largest integer not greater than |
|
Returns the largest integer not greater than |
|
Returns the least element of array expression |
|
Returns the least element of the scalars |
|
Returns the greatest element of array expression |
|
Returns the greatest element of the scalars |
|
The expression The type of |
|
Returns the \(n_1 \times n_2 \times n_3 \times \dots\) array with all
elements equal to scalar or array
expression Recursive definition:
The function needs two or more arguments; that is |
[For example, to instantiate a gain, one would write
Continuous.Gain gai(k=-1) "Constant gain of -1" annotation(...);
where the documentation string is optional. The annotation is typically used for the graphical positioning of the instance in a block diagram.]
Using expressions in parameter assignments, and propagating values of parameters
in a hierarchical formulation of a control sequence, are convenient language constructs
to express relations between
parameters. However, most of today’s building control product lines do not support
propagation of parameter values and evaluation of expressions in parameter assignments.
For CDL to be compatible with this limitation, the modelica-json
translator
has optional flags, described below, that trigger the evaluation of propagated parameters,
and that evaluate expressions that involve parameters.
CDL also has a keyword called final
that prevents a declaration from being changed by the user.
This can be used in a hierarchical controller to ensure that parameter values are propagated to lower level controller
in such a way that users can only change their value at the top-level location.
It can also be used in CDL to enforce that different instances of blocks have the same parameter value.
For example, if a controller samples two signals, then final
could be used to ensure that they
sample at the same rate.
However, most of today’s building control product lines do not support such a language construct.
Therefore, while the CDL translator preserves the final
keyword in the CDL-JSON
format,
a translator from CDL-JSON
to a control product line is allowed to ignore this declaration.
Note
People who implement control sequences that require that values of parameters are identical
among multiple instances of blocks
must use blocks that take these values as an input, rather than rely on the final
keyword.
This could be done as explained in
these two examples:
Example 1: If a controller has two samplers called sam1
and sam2
and their parameter
samplePeriod
must satisfy sam1.samplePeriod = sam2.samplePeriod
for the logic to work correctly,
then the controller can be implemented using
CDL.Logical.Sources.SampleTrigger
and connect its output to two instances of
CDL.Discrete.TriggeredSampler
that sample the corresponding signals.
Example 2: If a controller normalized two input signals by dividing it by a gain k1
, then
rather than using two instances of
CDL.Continuous.Gain
with parameter k = 1/k1
, one could use
a constant source
CDL.Continuous.Sources.Constant
with parameter k=k1
and
two instances of
CDL.Continuous.Division,
and then connect
the output of the constant source with the inputs of the division blocks.
7.7.2. Evaluation of Assignment of Values to Parameters¶
We will now describe how assignments of values to parameters can optionally be evaluated by the CDL translator. While such an evaluation is not prefered, it is allowed in CDL to accomodate the situation that most building control product lines, in contrast to modeling tools such as Modelica, Simulink or LabVIEW, do not support the propagation of parameters, nor do they support the use of expressions in parameter assignments.
Consider the statement
parameter Real pRel(unit="Pa") = 50 "Pressure difference across damper";
CDL.Continuous.Sources.Constant con(
k = pRel) "Block producing constant output";
CDL.Logical.Hysteresis hys(
uLow = pRel-25,
uHigh = pRel+25) "Hysteresis for fan control";
Some building control product lines will need to evaluate this at translation because they cannot propagate parameters and/or cannot evaluate expressions.
To lower the barrier for the development of a CDL translator to a control product line,
the modelica-json
translator has two flags.
One flag, called evaluatePropagatedParameters
will cause the translator to evaluate the propagated parameter,
leading to a CDL-JSON declaration that is equivalent to the declaration
CDL.Continuous.Sources.Constant con(
k(unit="Pa") = 50) "Block producing constant output";
CDL.Logical.Hysteresis hys(
uLow = 50-25,
uHigh = 50+25) "Hysteresis for fan control";
- Note
the
parameter Real pRel(unit="Pa") = 50
has been removed as it is no longer used anywhere.the parameter
con.k
has now theunit
attribute set as this information would otherwise be lost.the parameter
hys.uLow
has the unit not set because the assignment involves an expression. As expressions can be used to convert a value to a different unit, the unit will not be propagated if the assignment involves an expression.
Another flag called evaluateExpressions
will cause all mathematical expressions to be evaluated,
leading to a CDL-JSON declaration that is equivalent to the CDL declaration
parameter Real pRel(unit="Pa") = 50 "Pressure difference across damper";
CDL.Continuous.Sources.Constant con(
k = pRel) "Block producing constant output";
CDL.Logical.Hysteresis hys(
uLow = 25,
uHigh = 75) "Hysteresis for fan control";
If both evaluatePropagatedParameters
and evaluateExpressions
are set, the result would be
equivalent of the declaration
CDL.Continuous.Sources.Constant con(
k(unit="Pa") = 50) "Block producing constant output";
CDL.Logical.Hysteresis hys(
uLow = 25,
uHigh = 75) "Hysteresis for fan control";
Clearly, use of these flags is not preferred, but they have been introduced to accomodate the capabilities that are present in most of today’s building control product lines.
Note
A commonly used construct in control sequences is to declare a parameter
and
then use the parameter once to assign the value of a block in this sequences.
In CDL, this construct looks like
parameter Real pRel(unit="Pa") = 50 "Pressure difference across damper";
CDL.Continuous.Sources.Constant con(k = pRel) "Block producing constant output";
Note that the English language sequence description would typically refer to the parameter pRel
.
If this is evaluated during translation due to the evaluatePropagatedParameters
flag,
then pRel
would be removed as it is no longer used.
Hence, such a translation should then rename the block con
to pRel
, e.g., it should
produce a sequence that is equivalent to the CDL declaration
CDL.Continuous.Sources.Constant pRel(k = 50) "Block producing constant output";
In this way, references in the English language sequence to pRel
are still valid.
7.7.3. Conditionally Removing Instances¶
Instances can be conditionally removed by using an if
clause.
This allows, for example, to have an implementation of a controller that optionally takes as an input the number of occupants in a zone.
An example code snippet is
parameter Boolean have_occSen=false
"Set to true if zones have occupancy sensor";
CDL.Interfaces.IntegerInput nOcc if have_occSen
"Number of occupants"
annotation (__cdl(default = 0));
CDL.Continuous.Gain gai(
k = VOutPerPer_flow) if have_occSen
"Outdoor air per person";
equation
connect(nOcc, gai.u);
By the Modelica language definition, all connections (Section 7.10)
to nOcc
will be removed if have_occSen = false
.
Some building automation systems do not allow to conditionally removing instances
of blocks, inputs and outputs, and their connections. Rather, these instances
are always present, and a value for the input must be present.
To accomodate this case, every input connector that can be conditionally removed
can declare a default value of the form __cdl(default = value)
,
where value
is the default value that will be used
if the building automation system does not support conditionally removing instances.
The type of value
must be the same as the type of the connector.
For Boolean
connectors, the allowed values are true
and false
.
If the __cdl(default = value)
annotation is absent, then the following values are
assumed as default:
For RealInput, the default values are:
If
unit=K
: Ifquantity="TemperatureDifference"
, the default is \(0\) K, otherwise it is \(293.15\) K.If
unit=Pa
: Ifquantity="PressureDifference"
, the default is \(0\) K, otherwise it is \(101325\) Pa.For all other units, the default value is \(0\).
For
IntegerInput
, the default value is \(0\).For
BooleanInput
, the default value isfalse
.
Note that output connectors must not have a specification of a default value, because if a building automation system cannot conditionally remove instances, then the block (or input connector) upstream of the output will always be present (or will have a default value).
7.7.4. Point list¶
From CDL-conforming sequences, point lists can be generated.
[This could be accomplished using the modelica-json
tool,
see Fig. 7.1.]
For point lists,
the connectors
RealInput
andIntegerInput
are analog inputs.the connectors
RealOutput
andIntegerOutput
are analog outputs.the connectors
BooleanInput
andBooleanOutput
are digital inputs and outputs.
7.7.4.1. Annotations that Cause Point Lists to be Generated¶
The vendor annotation __cdl(generatePointlist=Boolean, controlledDevice=String)
at the class level specifies
that a point list of the sequence is generated.
If not specified, it is assumed that __cdl(generatePointlist=false)
.
The key controlledDevice
is optional. It can be used to list the device that is being controlled.
Its value will be written to the point list, but not used otherwise, see Table 7.2 for an example.
When instantiating a block, the __cdl(generatePointlist=Boolean)
annotation
can also be added to the instantiation clause,
and it will override the class level declaration.
[For example,
block A
MyController con1;
MyController con2 annotation(__cdl(generatePointlist=false));
annotation(__cdl(generatePointlist=true));
end A;
generates a point list for A.con1 only, while
block A
MyController con1;
MyController con2 annotation(__cdl(generatePointlist=true));
annotation(__cdl(generatePointlist=false));
end A;
generates a point list for A.con2 only.]
The generatePointlist annotation can be propagated down in a composite block (see Section 7.12) by specifying in the instantiantion clause the annotation
__cdl(propagate(instance="subCon1", generatePointlist=true))
Controllers deeper in the hierarchy are referred to using the dot notation, such as in
instance="subCon1.subSubCon1"
where subSubCon1
is an instance of an elementary or composite block in subCon1
.
The value of instance=
must be an elementary block (see Section 7.6)
or a composite block (see Section 7.12). It
must declared, but it can be conditionally removed (see Section 7.7.3),
in which case the declaration can safely be ignored.
Higher-level declarations override lower-level declarations.
[For example, assume con1 has a block called subCon1. Then, the declaration
MyController con1 annotation(__cdl(propagate(instance="subCon1", generatePointlist=true)));
sets generatePointlist=true
in the instance con1.subCon1
.]
There can be any number of propagate(...)
annotations for a controller.
[Specifying multiple propagate(...)
annotations is useful for composite controllers.
For example,
MyController con1 annotation(
__cdl(
propagate(instance="subCon1", generatePointlist=true),
propagate(instance="subCon1.subSubCon1", generatePointlist=true),
propagate(instance="subCon1.subSubCon2", generatePointlist=false)
)
);
allows a finegrained propagation to individual blocks of a composite block. ]
7.7.4.2. Annotations for Connectors¶
Connectors (see Section 7.8) can have a vendor annotation of the form
__cdl(connection(hardwired=Boolean))
The field hardwired
specifies whether the connection should be hardwired or not,
the default value is false
.
Connectors can also have a vendor annotation of the form
__cdl(trend(interval=Real, enable=Boolean))
The field interval
must be specified and its value is the trending interval in seconds.
The field enable
is optional, with default value of true
, and
it can be used to overwrite the value used in the sequence declaration.
Similar to generatePointlist
, the connection
and trend
annotations can be
propagated. If a composite block contains a block con1
, which in turn contains a block subCon1
that
has an input u
, the declaration
MyController con1 annotation(
__cdl(propagate(instance="subCon1.u", connection(hardwired=Boolean)));
can be used to set the type of connection of input (or output) con1.subCon1.u
.
The value of instance==
must be a connector.
Similarly, the declaration
MyController con1 annotation(
__cdl(propagate(instance="subCon1.u", trend(interval=Real, enable=Boolean)));
can be used to set how to trend that input (or output).
These statements can also be combined into
MyController con1 annotation(
__cdl(propagate(instance="subCon1.u", connection(hardwired=Boolean),
trend(interval=Real, enable=Boolean)));
As in Section 7.7.4.1,
the instance in
instance=
must exist, (but it can be conditionally removed in which case the annotation can be ignored),higher-level declarations override lower-level declarations, and
any number of
propagate(...)
annotations can be present.
[For example, consider the pseudo-code
block Controller
Interfaces.RealInput u1
annotation(__cdl(connection(hardwired=true), trend(interval=60, enable=true)));
Interfaces.RealInput u2
annotation(__cdl(connection(hardwired=false),
trend(interval=120, enable=true),
propagate(instance="con1.u1",
connection(hardwired=false),
trend(interval=120, enable=true))));
MyController con1 annotation(__cdl(generatePointlist=true));
MyController con2 annotation(__cdl(generatePointlist=false,
propagate(instance="subCon1", generatePointlist=true),
propagate(instance="subCon2", generatePointlist=true)));
equation
connect(u1, con1.u1);
connect(u2, con1.u2);
connect(u1, con2.u1);
connect(u2, con2.u2);
annotation(__cdl(generatePointlist=true));
end Controller;
...
block MyController
Interfaces.RealInput u1
annotation(__cdl(connection(hardwired=false), trend(interval=120, enable=true)));
Interfaces.RealInput u2
annotation(__cdl(connection(hardwired=true), trend(interval=60, enable=true)));
...
SubController1 subCon1;
SubController2 subCon2;
...
annotation(__cdl(generatePointlist=true));
end MyController;
The translator will generate an annotation propagation list as shown below. There will be point
list for Controller
, Controller.con1
, Controller.con2.subCon1
and
Controller.con2.subCon1
. Also, the annotation connection(hardwired=true), trend(interval=60, enable=true)
of con1.u2
will be overridden as connection(hardwired=false), trend(interval=120, enable=true)
.
[
{
"className": "Controller",
"points": [
{
"name": "u1",
"hardwired": true,
"trend": {
"enable": true,
"interval": 60
}
},
{
"name": "u2",
"hardwired": false,
"trend": {
"enable": true,
"interval": 120
}
}
]
},
{
"className": "Controller.con1",
"points": [
{
"name": "u1",
"hardwired": false,
"trend": {
"enable": true,
"interval": 120
}
},
{
"name": "u2",
"hardwired": false,
"trend": {
"enable": true,
"interval": 120
}
}
]
},
{
"className": "Controller.con2.subCon1",
"points": [
...
]
},
{
"className": "Controller.con2.subCon2",
"points": [
...
]
}
]
]
[For an example of a point list generation, consider the pseudo-code shown below.
within Buildings.Controls.OBC.ASHRAE.G36_PR1.TerminalUnits
block Controller "Controller for room VAV box"
...;
CDL.Interfaces.BooleanInput uWin "Windows status"
annotation (__cdl(connection(hardwired=true),
trend(interval=60, enable=true)));
CDL.Interfaces.RealOutput yVal "Signal for heating coil valve"
annotation (__cdl(connection(hardwired=false),
trend(interval=60, enable=true)));
...
annotation (__cdl(generatePointlist=true, controlledDevice="Terminal unit"));
It specifies that a point list should be generated for the sequence that controls the
system or equipment specified by controlledDevice
, that uWin
is a
digital input point that is hardwired, and that yVal
is a analog output point that
is not hardwired. Both of them can be trended with a time interval of 1 minute.
The point list table will look as shown in Table 7.2.
System/Equipment |
Name |
Type |
Hardwired? |
Trend [s] |
Description |
---|---|---|---|---|---|
Terminal unit |
|
DI |
Yes |
60 |
Windows status |
Terminal unit |
|
AO |
No |
60 |
Signal for heating coil valve |
… |
… |
… |
… |
… |
… |
]
7.8. Connectors¶
Blocks expose their inputs and outputs through input and output connectors.
The permissible connectors are implemented in the package
CDL.Interfaces
, and are
BooleanInput
, BooleanOutput
,
IntegerInput
, IntegerOutput
,
RealInput
and RealOutput
.
Connectors must be in a public
section.
Connectors can carry scalar variables, vectors or arrays of values (each having the same data type). For arrays, the connectors need to be explicitly declared as an array.
[ For example, to declare an array of nin
input signals, use
parameter Integer nin(min=1) "Number of inputs";
Interfaces.RealInput u[nin] "Connector for 2 Real input signals";
]
Note
In general, today’s building control product lines only support scalar variables on graphical connections. This leads to the situation that different control sequences need to be implemented for any combination of equipment. For example, if only scalars are allowed in connections, then a chiller plant with two chillers needs a different sequence than a chiller plant with three chillers. With vectors, however, one sequence can be implemented for chiller plants with any number of chillers. This is currently done when implementing sequences from ASHRAE RP-1711 in CDL.
If control product lines do not support vectors on connections, then during translation from CDL to the control product line, the vectors (or arrays) can be flattened. For example, blocks of the form
parameter Integer n = 2 "Number of blocks";
CDL.Continuous.Sources.Constant con[n](k={1, 2});
CDL.Continuous.MultiSum mulSum(nin=n); // multiSum that contains an input connector u[nin]
equation
connect(con.y, mulSum.u);
could be translated to the equivalent of
CDL.Continuous.Sources.Constant con_1(k=1);
CDL.Continuous.Sources.Constant con_2(k=1);
CDL.Continuous.MultiSum mulSum(nin=2);
equation
connect(con_1.y, mulSum.u_1);
connect(con_2.y, mulSum.u_2);
E.g., two instances of CDL.Continuous.Sources.Constant
are used, the vectorized input mulSum.u[2]
is flattened
to two inputs, and two separate connections are instantiated.
This will preserve the control logic, but the components will need to be graphically rearranged after translation.
7.9. Equations¶
After the instantiations (Section 7.7),
a keyword equation
must be present
to introduce the equation section.
The equation section can only contain
connections (Section 7.10) and
annotations (Section 7.11).
Unlike in Modelica, an equation
section shall not contain
equations such as y=2*u;
or commands such as for
, if
,
while
and when
.
Furthermore, unlike in Modelica, there shall not be an initial equation
,
initial algorithm
or algorithm
section. (They can however be part of a elementary building block.)
7.10. Connections¶
Connections connect input to output connector (Section 7.8). For scalar connectors, each input connector of a block needs to be connected to exactly one output connector of a block. For vectorized connectors, or vectorized instances with scalar connectors, each (element of an) input connector needs to be connected to exactly one (element of an) output connector.
Connections are listed after the instantiation of the blocks in an equation
section. The syntax is
connect(port_a, port_b) annotation(...);
where annotation(...)
is used to declare
the graphical rendering of the connection (see Section 7.11).
The order of the connections and the order of the arguments in the
connect
statement does not matter.
[For example, to connect an input u
of an instance gain
to the output
y
of an instance maxValue
, one would declare
Continuous.Max maxValue "Output maximum value";
Continous.Gain gain(k=60) "Gain";
equation
connect(gain.u, maxValue.y);
]
Only connectors that carry the same data type (Section 7.4.1) can be connected.
Attributes of the variables that are connected are handled as follows:
If the
quantity
,unit
,min
ormax
attributes are set to a non-default value for both connector variables, then they must be equal. Otherwise an error should be issued.If only one of the two connector variables declares the
quantity
,unit
,min
ormax
attribute, then this value is applied to both connector variables.If two connectors have different values for the
displayUnit
attribute, then either can be used. [It is a quality of the implementation that a warning is issued if declarations are inconsistent. However, becausedisplayUnit
does not affect the computations in the sequence, the connection is still valid.]
[For example,
Continuous.Max maxValue(y(unit="m/s")) "Output maximum value";
Continous.Gain gain( k=60) "Gain";
Continous.Gain gainOK( u(unit="m/s" ), k=60) "Gain";
Continous.Gain gainWrong(u(unit="kg/s"), k=60) "Gain";
equation
connect(gain.u, maxValue.y); // This sets gain.u(unit="m/s")
// as gain.u does not declare its unit
connect(gainOK.u, maxValue.y); // Correct, because unit attributes are consistent
connect(gainWrong.u, maxValue.y); // Not allowed, because of inconsistent unit attributes
]
Signals shall be connected using a connect
statement;
assigning the value of a signal in the instantiation of the
output connnector is not allowed.
[This ensures that all control sequences are expressed as block diagrams. For example, the following model is valid
block MyAdderValid
Interfaces.RealInput u1;
RealInput u2;
Interfaces.RealOutput y;
Continuous.Add add;
equation
connect(add.u1, u1);
connect(add.u2, u2);
connect(add.y, y);
end MyAdderValid;
whereas the following implementation is not valid in CDL, although it is valid in Modelica
block MyAdderInvalid
Interfaces.RealInput u1;
Interfaces.RealInput u2;
Interfaces.RealOutput y = u1 + u2; // not allowed
end MyAdderInvalid;
]
7.11. Annotations¶
Annotations follow the same rules as described in the following Modelica 3.3 Specifications
18.2 Annotations for Documentation
18.6 Annotations for Graphical Objects, with the exception of
18.6.7 User input
18.8 Annotations for Version Handling
[For CDL, annotations are primarily used to graphically visualize block layouts, graphically visualize input and output signal connections, and to declare vendor annotations, (Sec. 18.1 in Modelica 3.3 Specification), such as to specify default value of connector as below.]
CDL also uses annotations to declare default values for conditionally removable input connectors, see Section 7.7.3.
For CDL implementations of sources such as ASHRAE Guideline 36, any instance,
such as a parameter, input or output, that is not provided in
the original documentation shall be annotated. For instances,
the annotation is __cdl(InstanceInReference=False)
while for parameter values,
the annotation is __cdl(ValueInReference=False)
. For both, if not specified
the default value is True
.
[ A specification may look like
parameter Real anyOutOfScoMult(
final unit = "1",
final min = 0,
final max = 1)=0.8
"Outside of G36 recommended staging order chiller type SPLR multiplier"
annotation(Evaluate=true, __cdl(ValueInReference=False));
]
Note
This annotation is not provided for parameters that are in general not specified in the ASHRAE Guideline 36, such as hysteresis deadband, default gains for a controller, or any reformulations of ASHRAE parameters that are needed for sequence generalization, for instance a matrix variable used to indicate which chillers are used in each stage.
7.12. Composite Blocks¶
CDL allows building composite blocks such as shown in Fig. 7.3.
Fig. 7.3 Example of a composite control block that outputs \(y = \min( k \, e, \, y_{max})\) where \(k\) is a parameter.¶
Composite blocks can contain other composite blocks.
Each composite block shall be stored on the file system under the name of the composite block
with the file extension .mo
, and with each package name being a directory.
The name shall be an allowed Modelica class name.
[For example, if a user specifies a new composite block MyController.MyAdder
, then it
shall be stored in the file MyController/MyAdder.mo
on Linux or OS X, or MyController\MyAdder.mo
on Windows.]
[The following statement, when saved as CustomPWithLimiter.mo
, is the
declaration of the composite block shown in Fig. 7.3
block CustomPWithLimiter
"Custom implementation of a P controller with variable output limiter"
parameter Real k "Constant gain";
CDL.Interfaces.RealInput yMax "Maximum value of output signal"
annotation (Placement(transformation(extent={{-140,20},{-100,60}})));
CDL.Interfaces.RealInput e "Control error"
annotation (Placement(transformation(extent={{-140,-60},{-100,-20}})));
CDL.Interfaces.RealOutput y "Control signal"
annotation (Placement(transformation(extent={{100,-10},{120,10}})));
CDL.Continuous.Gain gain(final k=k) "Constant gain"
annotation (Placement(transformation(extent={{-60,-50},{-40,-30}})));
CDL.Continuous.Min minValue "Outputs the minimum of its inputs"
annotation (Placement(transformation(extent={{20,-10},{40,10}})));
equation
connect(yMax, minValue.u1) annotation (
Line(points={{-120,40},{-120,40},{-20,40},{-20, 6},{18,6}},
color={0,0,127}));
connect(e, gain.u) annotation (
Line(points={{-120,-40},{-92,-40},{-62,-40}},
color={0,0,127}));
connect(gain.y, minValue.u2) annotation (
Line(points={{-39,-40},{-20,-40},{-20,-6}, {18,-6}},
color={0,0,127}));
connect(minValue.y, y) annotation (
Line(points={{41,0},{110,0}},
color={0,0,127}));
annotation (Documentation(info="<html>
<p>
Block that outputs <code>y = min(yMax, k*e)</code>,
where
<code>yMax</code> and <code>e</code> are real-valued input signals and
<code>k</code> is a parameter.
</p>
</html>"));
end CustomPWithLimiter;
Composite blocks are needed to preserve grouping of control blocks and their connections, and are needed for hierarchical composition of control sequences.]
7.13. Model of Computation¶
CDL uses the synchronous data flow principle and the single assignment rule, which are defined below. [The definition is adopted from and consistent with the Modelica 3.3 Specification, Section 8.4.]
All variables keep their actual values until these values are explicitly changed. Variable values can be accessed at any time instant.
Computation and communication at an event instant does not take time. [If computation or communication time has to be simulated, this property has to be explicitly modeled.]
Every input connector shall be connected to exactly one output connector.
In addition, the dependency graph from inputs to outputs that directly depend on inputs shall be directed and acyclic. I.e., connections that form an algebraic loop are not allowed. [To break an algebraic loop, one could place a delay block or an integrator in the loop, because the outputs of a delay or integrator does not depend directly on the input.]