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

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, English language documentation or a semantic model 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 give an overview and definition of 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. Overview of CDL and Terminology¶

CDL is a declarative, modular language for expressing block diagrams that was introduced in [WGH18]. CDL allows hierarchical modeling to encapsulate and reuse, through object instantiation, preconfigured control sequences. CDL also defines syntax for connecting inputs and outputs of blocks and for propagating the values of parameters. CDL allows users to declare new blocks, store them in a library, and instantiate them for use in a control sequence. CDL also has annotations that declare how to graphically render the block diagrams, and to document control sequences.

CDL uses a small subset of the Modelica language that is needed for declaration of block diagrams. We selected Modelica as it is an open standard, as it provides various open-source and commercial modeling and simulation environments, as it allows to generate highly efficient code for simulation, and because it is increasingly used to simulate building energy and control systems.

As the model of computation, CDL uses the synchronous data flow principle and single assignment rule, which is consistent with the Modelica 3.4 Language Specification [Mod21]. Therefore, all variables keep their value until the value is explicitly changed, values are always present (and hence can be accessed at any time instant), computation and communication at an event instant do not take time, and every input connector must be connected to exactly one output connector.

CDL consists of three types of blocks:

• Elementary blocks: These are built-in blocks that cannot be changed by users. All implementations of CDL need to provide the functionality of these blocks. An example is a block that outputs the sum of two inputs.

• Composite blocks: These are blocks that are composed hierarchically using elementary blocks or other composite blocks. Composite blocks can be used to declare control sequences, they can be stored in a library for reuse, and they can be instantiated and configured for a particular energy system.

• Extension blocks: Extension blocks allow users to implement new blocks that may be difficult or impossible to implement using the rules of composite blocks. For example, an extension block could be used to call a web service, or to implement a finite state machine that rotates chillers in a chiller plant.

The functionality of elementary blocks, but not their implementation, is part of the CDL specification. Thus, in the most general form, elementary blocks can be considered as functions that for given parameters $$p$$, time $$t$$ and internal state $$x(t)$$, map inputs $$u(t)$$ to new values for the outputs $$y(t)$$ and states $$x'(t)$$, e.g., $$(p, t, u(t), x(t)) \mapsto (y(t), x'(t))$$. By the composition rules of composite blocks, composite blocks are also such functions. This abstraction is important as it allows to execute CDL sequences that are composed of composite blocks using a variety of programming languages, it guarantees that the elementary and composite blocks have a well-defined scope and it guarantees that the calculations of a block have no side effects on other blocks. This however is not necessarily true for extension blocks (for example, two extension blocks could exchange data through a web service, thereby causing one block to have side effects on the behavior of the other block). Thus, use of composite blocks is preferred. To execute extension blocks, extension blocks need to be compiled and implemented using the Functional Mockup Interface Standard to provide run-time interoperability.

The CDL language consists of the following elements:

• A list of elementary 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.

• how to add new blocks that go beyond the capabilities of composite blocks.

• A model of computation that describes when blocks are executed and when outputs are assigned to inputs.

Table 7.1 gives an overview of the terminology used to describe CDL.

Table 7.1 Main terminology used in CDL. For a more detailed definition, follow the corresponding links.

Term

Description

definition and instantiation

We call the implementation of an object (such as a block or parameter) an object definition. To use an object, one declares an instance of it.

For example, the statement

block myBlock
CDL.Continuous.Sources.Constant c(k=1);
end myBlock;


is a block definition for myBlock, and the second line declares an instance of the block CDL.Continuous.Sources.Constant.

block

A block is an object that has any number of constants, parameters, input connectors, output connectors and instances of other blocks. Blocks typically encapsulate calculations. We distinguish between elementary blocks, composite blocks and extension blocks.

elementary block

An elementary block (Section 7.6) is a block that is part of the CDL library. Elementary blocks are the basic language blocks and are not to be changed by users.

composite block

A composite block (Section 7.12) is a block (and thus can have any number of constants, parameters, input connectors and output connectors) that instantiates any number of other elementary blocks or composite blocks, and declares connections between inputs and outputs. Composite blocks are used to implement control sequences.

extension block

An extension block (Section 7.13) is a block that conforms in CDL to the Modelica definition of a block (and thus can have textual equations, call C functions or functions in a dynamically linked library). In CDL-json, the json specification declares its constants, parameters, input connectors and output connectors, and it declares the file name of a Functional Mockup Unit for Model Exchange that can be used to compute its outputs.

parameter

A parameter (Section 7.4.2) is an instance of a native data type (such as a Real or Integer) whose value is time invariant, and hence its value cannot be changed based on an input signal. To change its value when simulating a control logic, one would need to stop the simulation and change the value. In an actual controller, one may change the value through a graphical user interface.

constant

A constant (Section 7.4.3) is an instance of a native data type (such as a Real or Integer) whose value cannot be changed after compilation.

input (output) connector

An input (output) connector (Section 7.8) is an object to which a connection can be made to transfer a signal value into (out of) a block.

connection

A connection (Section 7.10) is used to connect an input connector to an output connector, thereby indicating that the value at the input connector is equal to the value at the output connector.

function

A function (Section 7.7.2) is an object that can have any fixed number of arguments and returns a scalar- or array-valued object, such as a Real number or an Integer array. Functions can be used to assign values to constants and parameters, and to assign values to attributes of constants, parameters, inputs and outputs.

annotation

An annotation (Section 7.11) is a declaration that is used to store information about blocks, input connectors, output connectors and parameters that does not affect the computations. Annotations are used for example to store documentation, to provide a means to group related parameters of a block so they can be shown next to each other in a graphical user interface, or to store semantic information.

## 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.5 specification [Mod21]. 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 (except inside the extension blocks, Section 7.13):

1. extends

2. redeclare

3. constrainedby

4. inner and outer

Also, the following Modelica language features are not supported in CDL, except inside extension blocks:

1. Clocks [which are used in Modelica for hybrid system modeling].

2. algorithm sections [because the elementary blocks are black-box models as far as CDL is concerned and thus CDL compliant tools do not parse the algorithm section.]

3. initial equation and initial 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.15.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. Parameters¶

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";


]

### 7.4.3. Constants¶

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.4. Arrays¶

Each of these data types, including the elementary blocks, composite blocks, extension blocks and connectors, 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.

Values of arrays may be declared using

• the notation {x1, x2, ...}, for example parameter Integer k[3,2] = {{1, 2}, {3, 4}, {5, 6}},

• one or several iterators, for example parameter Real k[2,3] = {i*0.5+j for i in 1:3, j in 1:2},

• a fill or cat function, see Section 7.7.1.

[For example, to following declarations all assign the array {1, 2, 3} to parameters:

parameter Real k1[3] = {1, 2, 3};
parameter Real k2[3] = {i for i in 1:3};
parameter Real k3[3] = k1;
parameter Real k4[3] = fill(1, 3) + {0, 1, 2};
parameter Real k5[3] = cat(1, {1}, {2}, {3});


The following declaration instantiates two blocks, and sets the value of the parameter k to 2 and 3:

MultiplyByParameter mul[2](k={2, 3});


]

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.5 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 blocks (Section 7.6) or composite blocks (Section 7.12).

## 7.6. Elementary Blocks¶

The CDL library contains elementary blocks that are used to compose control sequences. The functionality of elementary blocks, but not their implementation, is part of the CDL specification. Thus, in the most general form, elementary 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.,

$(p, t, u(t), x(t)) \mapsto y(t).$

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 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 blocks. Rather, users can use the existing elementary blocks to implement composite blocks (Section 7.12).

Note

The elementary blocks can be browsed in any of these ways:

An actual implementation of an elementary 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>"));



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.2. For Real and Integer, expressions are allowed that involve

• the basic arithmetic functions +, -, *, -,

• the relations >, >=, <, <=, ==, <>,

• calls to the functions listed in Table 7.2.

[For example, to instantiate a block that multiplies its input by a parameter, one would write

CDL.Continuous.MultiplyByParameter 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.]

### 7.7.2. Functions¶

CDL provide built-in functions that can be used when assigning values of parameters and attributes of constants, parameters, inputs, and outputs. Table 7.2 lists the supported functions.

Table 7.2 Functions that are allowed in parameter assignments. The functions are consistent with Modelica 3.5.

Function

Description

abs(v)

Absolute value of v.

sign(v)

Returns if v>0 then 1 else if v<0 then –1 else 0.

sqrt(v)

Returns the square root of v if v >=0, or an error otherwise.

div(x, y)

Returns the algebraic quotient x/y with any fractional part discarded (also known as truncation toward zero). [Note: this is defined for / in C99; in C89 the result for negative numbers is implementation-defined, so the standard function div() must be used.]. Result and arguments shall have type Real or Integer. If either of the arguments is Real the result is Real otherwise it is Integer.

mod(x, y)

Returns the integer modulus of x/y , i.e. mod(x,y)=x-floor(x/y)*y. Result and arguments shall have type Real or Integer. If either of the arguments is Real the result is Real otherwise it is Integer.

[Examples are mod(3,1.4)=0.2, mod(-3,1.4)=1.2 and mod(3,-1.4)=-1.2.]

rem(x,y)

Returns the integer remainder of x/y, such that div(x,y)*y + rem(x, y) = x. Result and arguments shall have type Real or Integer. If either of the arguments is Real the result is Real otherwise it is Integer.

[Examples are rem(3,1.4)=0.2 and rem(-3,1.4)=-0.2.]

ceil(x)

Returns the smallest integer not less than x. Result and argument shall have type Real.

floor(x)

Returns the largest integer not greater than x. Result and argument shall have type Real.

integer(x)

Returns the largest integer not greater than x. The argument shall have type Real. The result has type Integer.

min(A)

Returns the least element of array expression A.

min(x, y)

Returns the least element of the scalars x and y.

max(A)

Returns the greatest element of array expression A.

max(x, y)

Returns the greatest element of the scalars x and y.

sum(...)

The expression sum( e(i, ..., j) for i in u, ..., j in v) returns the sum of the expression e(i, ..., j) evaluated for all combinations of i in u, ..., j in v: e(u[1], ... ,v[1]) + e(u[2], ... ,v[1])+... +e(u[end],... ,v[1])+...+e(u[end],... ,v[end])

The type of sum(e(i, ..., j) for i in u, ..., j in v) is the same as the type of e(i,...j).

fill(s, n1, n2, ...)

Returns the $$n_1 \times n_2 \times n_3 \times \dots$$ array with all elements equal to scalar or array expression s ($$n_i \ge 0$$). The returned array has the same type as s.

Recursive definition: fill(s, n1, n2, n3, ...) = fill( fill(s, n2, n3, ...), n1);, fill(s,n)={s, s, ..., s}

The function needs two or more arguments; that is fill(s) is not legal.

size(...)

Returns dimensions of an array. For $$1 \le i \le n$$, where $$n$$ is the number of dimensions in A, the expression size(A,i) returns the size of dimension $$i$$ of array expression A. The expression size(A) returns a vector of length $$n$$ containing the dimension sizes of A. [Examples are size([1, 2, 3; 3, -4, 5], 1)=2 and size([1, 2, 3; 3, -4, 5])={2,3}.]

[For example, if a controller has a parameter for the set point for the outdoor air flow rate of ten (equally sized) zones that needs to be set to $$0.1 \, \mathrm{m^3/s}$$, a declaration may look like:

parameter Real VSet_flow[10](
final unit=fill("m3/s", 10)) = fill(0.1, 10);


]

### 7.7.3. Evaluation of Assignment of Values to Parameters¶

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.MultiplyByParameter 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.Divide, and then connect the output of the constant source with the inputs of the division blocks.

We will now describe how assignments of values to parameters can optionally be evaluated by the CDL translator. While such an evaluation is not preferred, it is allowed in CDL to accommodate 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
1. the parameter Real pRel(unit="Pa") = 50 has been removed as it is no longer used anywhere.

2. the parameter con.k has now the unit attribute set as this information would otherwise be lost.

3. 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.4. 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.MultiplyByParameter 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: If quantity="TemperatureDifference", the default is $$0$$ K, otherwise it is $$293.15$$ K.

• If unit=Pa: If quantity="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 is false.

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.5. 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 and IntegerInput are analog inputs.

• the connectors RealOutput and IntegerOutput are analog outputs.

• the connectors BooleanInput and BooleanOutput are digital inputs and outputs.

#### 7.7.5.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.3 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 instantiation 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.4), 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 fine grained propagation to individual blocks of a composite block. ]

#### 7.7.5.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.5.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.3.

Table 7.3 Sample point list table generated by the modelica-json tool.

System/Equipment

Name

Type

Hardwired?

Trend [s]

Description

Terminal unit

uWin

DI

Yes

60

Windows status

Terminal unit

yVal

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 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 or max 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 or max 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, because displayUnit 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;
equation


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


]

## 7.11. Annotations¶

Annotations follow the same rules as described in the following Modelica 3.5 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.5 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.4.

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¶

A composite block is a block that is composed of any number of instances of

• constants,

• parameters,

• input connectors,

• output connectors,

• elementary blocks, and

• other composite blocks.

Composite blocks also contain an equation section in which connections are instantiated to connect inputs connectors and output connectors of the composite block and its elementary and composite blocks. These rules allow the definition of composite blocks in a library, and the instantiation and possible configuration of these instances to implement a particular control sequence.

A simple example of a composite block that multiplies one of its inputs, adds it to the other input and produces at its output connector the sum is shown in Fig. 7.3.

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.MultiplyByParameter 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. Extension Blocks¶

To support functionalities that cannot, or may be hard to, implement with a composite block, extension blocks are introduced.

Note

Extension blocks are introduced to allow implementation of blocks that contain statistical functions such as for regression, fault detection and diagnostics methods, or state machines for operation mode switches, as well as proprietary code.

Extension blocks are also suited to propose new elementary blocks for later inclusion in ASHRAE Standard 231P. In fact, elementary blocks are implemented using extension blocks, except that the annotation __cdl(extensionBlock=true) (see below) is not present because tools can recognize them because they are stored in the CDL package.

In CDL, extension blocks must have the annotations

annotation(__cdl(extensionBlock=true))


This annotation allows translators to recognize them as extension blocks. Extension blocks are equivalent to the class block in Modelica. Thus, extension blocks can contain any declarations that are allowed in a Modelica block.

Note

The fact that extension blocks allow any declaration that is allow in a Modelica block implies that extension blocks can have any number of parameters, inputs and outputs, identical to composite blocks. It also implies that extension blocks can be used to

• call code, for example in C or from a compiled library,

• import a Functional Mockup Unit that may contain a process model or a fault detection and diagnostics method, and

• implement state machines.

For example, the demand response client Buildings.Controls.DemandResponse.Client would be an extension block if it were to contain the annotation __cdl(extensionBlock=true), as would the Kalman filter that is used in the Example Buildings.Utilities.IO.Python_3_8.Examples.KalmanFilter.

Translation of an extension block to json must reproduce the following:

• All public parameters, inputs and outputs.

• A Functional Mockup Unit for Model Exchange or for Co-simulation, version 2.0, with the file name being the full class name and the extension being .fmu.

Note

With OpenModelica 1.20.0, a Functional Mockup Unit for Model Exchange 2.0 of an extension block can be generated with the commands:

echo "loadFile(\"Buildings/package.mo\");" > translate.mos
echo "translateModelFMU(Buildings.Controls.OBC.CDL.Continuous.PID);" >> translate.mos
omc translate.mos


This will generate the fmu Buildings.Controls.OBC.CDL.Continuous.PID.fmu.

## 7.14. 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.5 Specification, Section 8.4.]

1. All variables keep their actual values until these values are explicitly changed. Variable values can be accessed at any time instant.

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

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

## 7.15. Tags¶

CDL has sufficient information for tools that process CDL to generate for example point lists that list all analog temperature sensors, or to verify that a pressure control signal is not connected to a temperature input of a controller. Some, but not all, of this information can be inferred from the CDL language described above. We will use tags, implemented through Modelica vendor annotations, to provide this additional information. In Section 7.15.1, we will explain the properties that can be inferred, and in Section 7.15.2, we will explain how to use tagging schemes in CDL.

Note

None of this information affects the computation of a control signal. Rather, it can be used for example to facilitate the implementation of cost estimation tools, or to detect incorrect connections between outputs and inputs.

### 7.15.1. Inferred Properties¶

To avoid that signals with physically incompatible quantities are connected, tools that parse CDL can infer the physical quantities from the unit and quantity attributes.

[For example, a differential pressure input signal with name u can be declared as

Interfaces.RealInput u(
quantity="PressureDifference",
unit="Pa") "Differential pressure signal" annotation (...);


Hence, tools can verify that the PressureDifference is not connected to AbsolutePressure, and they can infer that the input has units of Pascal.

Therefore, tools that process CDL can infer the following information:

• Numerical value: Binary value (which in CDL is represented by a Boolean data type), analog value, (which in CDL is represented by a Real data type) mode (which in CDL is presented by an Integer data type or an enumeration, which allow for example encoding of the ASHRAE Guideline 36 Freeze Protection which has 4 stages).

• Source: Hardware point or software point.

• Quantity: such as Temperature, Pressure, Humidity or Speed.

• Unit: Unit and preferred display unit. (The display unit can be overwritten by a tool. This allows for example a control vendor to use the same sequences in North America displaying IP units, and in the rest of the world displaying SI units.)

]

### 7.15.2. Tagged Properties¶

The buildings industry uses different tagging schemes such as Brick (https://brickschema.org/) and Haystack (https://project-haystack.org/). CDL allows, but does not require, use of the Brick or Haystack tagging scheme.

CDL allows to add tags to declarations that instantiate

[We currently do not see a use case that would require adding a tag to a parameter declaration.]

To implement such tags, CDL blocks can contain vendor annotations with the following syntax:

annotation :
annotation "(" [annotations ","]
__cdl "(" [ __cdl_annotation ] ")" ["," annotations] ")"


where __cdl__annotation is the annotation for CDL.

For Brick, the __cdl_annotation is

brick_annotation:
brick "(" RDF ")"


where RDF is the RDF 1.1 Turtle (https://www.w3.org/TR/turtle/) specification of the Brick object.

[Note that, for example for a controller with two output signals $$y_1$$ and $$y_2$$, Brick seems to have no means to specify that $$y_1$$ controls a fan speed and $$y_2$$ controls a heating valve, where controls is the Brick relationship. Therefore, we allow the brick_annotation to only be at the block level, but not at the level of instances of input or output connectors.

For example, the Brick specification

soda_hall:flow_sensor_SODA1F1_VAV_AV a brick:Supply_Air_Flow_Sensor ;
bf:hasTag brick:Average ;
bf:isLocatedIn soda_hall:floor_1 .


can be declared in CDL as

annotation(__cdl(brick="soda_hall:flow_sensor_SODA1F1_VAV_AV a brick:Supply_Air_Flow_Sensor  ;
bf:hasTag brick:Average ;
bf:isLocatedIn soda_hall:floor_1 ."));


]

For Haystack, the __cdl_annotation is

haystack_annotation:
haystack "(" JSON ")"


where JSON is the JSON encoding of the Haystack object.

[For example, the AHU discharge air temperature setpoint is in Haystack declared as

id: @whitehouse.ahu3.dat
dis: "White House AHU-3 DischargeAirTemp"
point
siteRef: @whitehouse
equipRef: @whitehouse.ahu3
discharge
air
temp
sensor
kind: "Number"
unit: "degF"


can be declared in CDL as

annotation(__cdl( haystack=
"{\"id\"   : \"@whitehouse.ahu3.dat\",
\"dis\" : \"White House AHU-3 DischargeAirTemp\",
\"point\" : \"m:\",
\"siteRef\" : \"@whitehouse\",
\"equipRef\" : \"@whitehouse.ahu3\",
\"discharge\" : \"m:\",
\"air\"       : \"m:\",
\"temp\"      : \"m:\",
\"sensor\"    : \"m:\",
\"kind\"       : \"Number\"
\"unit\"       : \"degF\"}"));


Tools that process CDL can interpret the brick or haystack annotation, but for control purposes CDL will ignore it. [This avoids potential conflict for entities that are declared differently in Brick (or Haystack) and CDL, and may be conflicting. For example, the above sensor input declares in Haystack that it belongs to an ahu3. CDL, however, has a different syntax to declare such dependencies: In CDL, through the connect(whitehouse.ahu3.TSup, ...) statement, a tool can infer what upstream component sends the input signal.]