Skip to main content

Rule-based framework for building Brick models from simple functions

Project description

Rule-Based Model Builder

This repository implements an experimental rule-based approach to creating Brick models (or really any RDF graph) from variably-structured metadata sources. The APIs and interfaces in this repository may change drastically with little notice, and documentation is sparse! That said, this is fast emerging as my favorite way for creating Brick models from arbitrary sources.

The key idea is to write small functions that generate part of a Brick graph when certain conditions are met on the input data. The input data is structured as a sequence of key-value documents, called rows. It is usually straightforward to turn some source data into a sequence of key-value documents, so this is a natural "intermediate representation" (IR) for arbitrary metadata sources.

The framework defined in this repository provides a number of simple "rule conditions" for triggering these functions which produce parts of the Brick graph. These rule conditions are designed to make it easier to deal with heterogeneously-structured input data.

Rule Conditions

Each condition is implemented as a Python decorator. Decorated functions should take a single argument which is the "row" (a Python dict) that matches the conditions defined by the decorator

tags: triggers the rule when all of the keys are present

@tags("AHU")
def add_ahu(row):
    ahu_name = row.get("AHU")
    G.add((BLDG[ahu_name], A, BRICK.AHU))
    G.add((BLDG[ahu_name], RDFS.label, Literal(row.get("AHU"))))

oneof: triggers the rule when at least one of the provided keys is present

point_types = {
    "alarm variable": BRICK.Alarm,
    "air volume": BRICK.Supply_Air_Flow_Sensor,
    "differential pressure status sensor": BRICK.Differential_Pressure_Sensor,
    "discharge air pressure sensor": BRICK.Discharge_Air_Static_Pressure_Sensor,
    "fan speed reset": BRICK.Speed_Reset_Command,
    "zone temp": BRICK.Zone_Air_Temperature_Sensor,
    "zone temp setpoint": BRICK.Zone_Air_Temperature_Setpoint,
}
@oneof(*point_types.keys())
def define_point(row):
    for tag, brickclass in point_types.items():
        if tag in row:
            sensor = row.get(tag)
            G.add((BLDG[sensor], A, brickclass))

values: triggers the rule when the row has the given key-value pairs

@values({"Category": "Level", "ModelObject": "IfcBuildingStorey"})
def add_floors(row):
    floor_id = EX[row["ModelID"]]
    G.add((floor_id, RDFS.label, Literal(row["Name"])))
    G.add((floor_id, A, BRICK.Floor))
    G.add((floor_id, BRICK.elevation, [
        (BRICK.value, Literal(float(row["Elevation"]))),
        (BRICK.unit, units["linear"]),
    ]))

_and_: triggers the rule when all of the given conditions are met. Sorry for the funky syntax; ideally the decorators compose easily but this will take some work to figure out how to do

@_and_([tags, "AHU", "uuid"], [oneof, *point_types.keys()])
def ahu_points(row):
    ahu_name = row.get('AHU')
    for cls, brickclass in point_types.items():
        if cls in row:
            point = row.get(cls)
            G.add((BLDG[point], A, brickclass))
            G.add((BLDG[point], BRICK.timeseries, [
                (BRICK.hasTimeseriesId, Literal(row.get("uuid")))
            ]))
            G.add((BLDG[ahu_name], BRICK.hasPoint, BLDG[point]))

fixedpoint: executes the rule if any new triples were added to the graph since it was last executed. This is helpful to avoid any dependency constraints between rules (e.g. making sure the floors exist so the biulding can be linked to them). The argument to the fixedpoint decorator is the string name of the graph object you are appending to (should be G in most cases)

The triples generated by this rule should be consistent so that RDFlib can effectively deduplicate. Generating a blank node with a random identifier can lead this rule to execute forever. Use with caution! This should always be the decorator immediately above the function definition.

@tags("AHU")
@fixedpoint("G")
def ahu_feeds(row):
    ahu = row.get("AHU")
    idnum = ahu[-1]
    vavs = G.subjects(predicate=A, object=BRICK.VAV)
    for vav in vavs:
        label = list(G.objects(subject=vav, predicate=RDFS.label))[0]
        if label.startswith(idnum):
            G.add((BLDG[ahu], BRICK.feeds, vav))

Driving

The stream of documents are done through a Stream object. A Stream object implements an optional setup method and a next method. A CSVFileStream and JSONFileStream implementation are provided. Some files (e.g. examples/smc/smc.py will define their own extensions to these streams to handle tasks like column renaming, data cleaning and transformations.

Call the drive function on 1 or more Stream objects to create the graph (by default, stored in a global G variable), then optionally validate (G.validate()) and serialize.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

brick-bodge-0.2.0.tar.gz (5.9 kB view details)

Uploaded Source

Built Distribution

brick_bodge-0.2.0-py3-none-any.whl (5.6 kB view details)

Uploaded Python 3

File details

Details for the file brick-bodge-0.2.0.tar.gz.

File metadata

  • Download URL: brick-bodge-0.2.0.tar.gz
  • Upload date:
  • Size: 5.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.9.7 Linux/5.15.23-76051523-generic

File hashes

Hashes for brick-bodge-0.2.0.tar.gz
Algorithm Hash digest
SHA256 1b8e00405b48bdd79bcb333ab632c6a0c5434054cda8f24b5fab4c9e27b70a08
MD5 0f21d73027558ada1c3f472e36493092
BLAKE2b-256 01c3b509fa865e48a421a1d7f72b1c747749d168c01d3d6ef70c8c6ba1f06c59

See more details on using hashes here.

File details

Details for the file brick_bodge-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: brick_bodge-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 5.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.13 CPython/3.9.7 Linux/5.15.23-76051523-generic

File hashes

Hashes for brick_bodge-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 798e65a618b873090d135d130616664b6e299aaea42ccb4d169d44d26a5337c1
MD5 05a0ee16d3322b4647d56928620e51b1
BLAKE2b-256 52e34e11bea0a9d878414bacc26c454fb9b10fbc6b0094e707c4f1a95946c511

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page