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:

  1. writing some simple templates

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

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