Template Writing#
Recall that BuildingMOTIF Templates
are functions that generate parts of an RDF model. Templates are written as YAML documents (.yaml
or .yml
files)[1] that can contain one or more templates.
Note
This tutorial has the following learning objectives:
writing some simple templates
saving and organizing templates
Parts of a Template#
An indivdual template is made up of a key (the template’s name
) and it’s associated values (the content of the template). To start, let’s look at a simple example template that represents a variable air volume (VAV) terminal unit with reheat from ASHRAE Guideline 36 (G36).
vav-terminal-reheat:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Variable_Air_Volume_Box_With_Reheat ;
brick:hasPart p:damper, p:htg-coil ;
brick:hasPoint p:sa-flow, p:sa-temp, p:za-temp, p:occ, p:co2 ;
brick:feeds p:zone .
optional: ['occ', 'co2']
dependencies:
- template: damper
args: {"name": "damper"}
- template: htg-coil
args: {"name": "htg-coil"}
- template: https://brickschema.org/schema/Brick#Supply_Air_Flow_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "sa-flow"}
- template: https://brickschema.org/schema/Brick#Supply_Air_Temperature_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "sa-temp"}
- template: https://brickschema.org/schema/Brick#Zone_Air_Temperature_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "za-temp"}
- template: https://brickschema.org/schema/Brick#HVAC_Zone
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "zone"}
- template: https://brickschema.org/schema/Brick#Occupancy_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "occ"}
- template: https://brickschema.org/schema/Brick#CO2_Level_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "co2"}
Body#
vav-terminal-reheat:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Variable_Air_Volume_Box_With_Reheat ;
brick:hasPart p:damper, p:htg-coil ;
brick:hasPoint p:sa-flow, p:sa-temp, p:za-temp, p:occ, p:co2 ;
brick:feeds p:zone .
The template definition provides the structure of the graph and allows the content of the graph to be determined in part through the use of parameters. Parameters have a name (typically short and descriptive), can be required or optional, and are identified by their prefix (p) urn:___param___#
. For example, this template has parameters for the VAV terminal’s name (p:name
), its parts (those following the brick:hasPart
relationship), its points (those following the brick:hasPoint
relationship), and what it feeds (brick:feeds
relationship).
Optional#
vav-terminal-reheat:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Variable_Air_Volume_Box_With_Reheat ;
brick:hasPart p:damper, p:htg-coil ;
brick:hasPoint p:sa-flow, p:sa-temp, p:za-temp, p:occ, p:co2 ;
brick:feeds p:zone .
optional: ['occ', 'co2']
This template has two optional parameters, occ
and co2
that represent points for an occupancy sensor and zone CO2 sensor, which only apply to some applications (denoted by an A in Section 4 of G36).
Dependencies#
The template body describes the VAV terminal as having some parts and points and a topologic relationship to a zone, but no further properties. We also don’t know what kind of thing p:zoom
should be bound to when the template is used. Let’s address the second problem first by introducing dependencies
.
vav-terminal-reheat:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Variable_Air_Volume_Box_With_Reheat ;
brick:hasPart p:damper, p:htg-coil ;
brick:hasPoint p:sa-flow, p:sa-temp, p:za-temp, p:occ, p:co2 ;
brick:feeds p:zone .
optional: ['occ', 'co2']
dependencies:
- template: damper
args: {"name": "damper"}
- template: htg-coil
args: {"name": "htg-coil"}
- template: https://brickschema.org/schema/Brick#Supply_Air_Flow_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "sa-flow"}
- template: https://brickschema.org/schema/Brick#Supply_Air_Temperature_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "sa-temp"}
- template: https://brickschema.org/schema/Brick#Zone_Air_Temperature_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "za-temp"}
- template: https://brickschema.org/schema/Brick#HVAC_Zone
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "zone"}
- template: https://brickschema.org/schema/Brick#Occupancy_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "occ"}
- template: https://brickschema.org/schema/Brick#CO2_Level_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "co2"}
The addition of dependencies
states that the template is dependent upon several other templates. For example, the https://brickschema.org/schema/Brick#Supply_Air_Flow_Sensor
template (automatically produced by importing the Brick ontology as a library) and the name
parameter of the zone
template is bound to the value of the zone
parameter in this template.
Adding a Dependency#
Now let’s add another template to the library, which vav-terminal-reheat
will depend on, that defines the damper part of the terminal and its dependecy (a point for damper position).
damper:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Damper ;
brick:hasPoint p:dmppos .
dependencies:
- template: https://brickschema.org/schema/Brick#Damper_Position_Command
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "dmppos"}
(There is syntax sugar for the above pattern)
Now the original vav-terminal-reheat
template depends on this template. By binding this template to the name
paramter of the original template, we are essentially composing the two templates together. The full template library is now as follows:
vav-terminal-reheat:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Variable_Air_Volume_Box_With_Reheat ;
brick:hasPart p:damper, p:htg-coil ;
brick:hasPoint p:sa-flow, p:sa-temp, p:za-temp, p:occ, p:co2 ;
brick:feeds p:zone .
optional: ['occ', 'co2']
dependencies:
- template: damper
args: {"name": "damper"}
- template: htg-coil
args: {"name": "htg-coil"}
- template: https://brickschema.org/schema/Brick#Supply_Air_Flow_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "sa-flow"}
- template: https://brickschema.org/schema/Brick#Supply_Air_Temperature_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "sa-temp"}
- template: https://brickschema.org/schema/Brick#Zone_Air_Temperature_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "za-temp"}
- template: https://brickschema.org/schema/Brick#HVAC_Zone
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "zone"}
- template: https://brickschema.org/schema/Brick#Occupancy_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "occ"}
- template: https://brickschema.org/schema/Brick#CO2_Level_Sensor
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "co2"}
damper:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Damper ;
brick:hasPoint p:dmppos .
dependencies:
- template: https://brickschema.org/schema/Brick#Damper_Position_Command
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "dmppos"}
Attention
Next try adding a dependency for the htg-coil
part of the VAV terminal, which has a Brick Position Command.
Hint
htg-coil:
body: >
@prefix p: <urn:___param___#> .
@prefix brick: <https://brickschema.org/schema/Brick#> .
p:name a brick:Heating_Coil ;
brick:hasPoint p:cmd .
dependencies:
- template: https://brickschema.org/schema/Brick#Position_Command
library: https://brickschema.org/schema/1.3/Brick
args: {"name": "cmd"}
Saving and Organizing Templates#
Templates are saved in YAML
format (.yaml
or .yml
files). Templates can be grouped into a Library
by placing all of the templates in the same directory. The name of that directory is treated as the name of the Library. You can have as many YAML files inside the directory as you want, and the organization of templates among those YAML files can follow any principle you like (equipment manufacturer, application profiles, etc). For example, if we saved the YAML document above into a vav-terminal-reheat.yaml
file and placed that file into a my-templates
directory, the file structure would look like this:
$ tree my-templates
my-templates/
└── vav-terminal-reheat.yaml
The my-templates
library (and the vav-terminal-reheat
template) could be loaded into BuildingMOTIF as follows:
from rdflib import Namespace, Graph
from buildingmotif import BuildingMOTIF
from buildingmotif.dataclasses import Model, Library
lib = Library.load(directory="my-templates")
templ = lib.get_template_by_name("vav-terminal-reheat")
A few of the existing libraries follow different organizational principles:
The Guideline 36 library divides templates into different files depending on the system configuration they correspond to
The 223P Components library divides templates into different files depending on the kind of subsystem they are for