An S223 air path#
The API is the same for 223P building systems. This tutorial builds an air handling unit around a damper and a fan, inspects the chosen connection, walks the air path, and validates the RDF.
1. Create and connect#
The damper and fan are created inside the AHU, so the AHU contains them. A zone damper outside the AHU takes its supply air:
from plumb import Model, connect
from plumb.s223 import AirHandlingUnit, Damper, Fan, enums
with Model("urn:example:air-path#") as model:
with AirHandlingUnit("ahu") as ahu:
damper = Damper("outside-air-damper")
fan = Fan("supply-fan")
zone_damper = Damper("zone-damper")
air_link = connect(damper, fan)
connect(ahu, zone_damper, name="supply-duct")
print(damper.name, fan.name, air_link.name)
outside-air-damper supply-fan outside-air-damper--supply-fan
damper >> fan also records the connection, but returns the fan for
chaining.
The AHU’s own inlet and outlet stand for points inside it. maps_to says
which: air enters through the damper and leaves through the fan.
model["outside-air-damper-in"].maps_to = model["ahu-in"]
model["supply-fan-out"].maps_to = model["ahu-out"]
2. Resolve#
check() reports whether one interpretation exists. resolve() returns a
frozen snapshot with the chosen points and connection:
print(model.check().status)
resolved = model.resolve()
connection = resolved.connection(air_link)
explanation = resolved.explain(air_link)
print(connection.type, connection.medium)
print(explanation.source_point, "->", explanation.target_point)
complete
Duct <enum Fluid-Air>
outside-air-damper-out -> supply-fan-in
The connection is a Duct because the medium is air. The snapshot holds the
derived points; the authored model does not:
print("authored:", len(model))
print("resolved:", len(resolved))
authored: 12
resolved: 14
resolved_fan = resolved["supply-fan"]
list(resolved_fan.ports)
[<OutletConnectionPoint 'supply-fan-out'>,
<InletConnectionPoint 'supply-fan-in'>]
3. Walk the air path#
find_all_previous() walks upstream along ducts and points. By default
equipment is a single node, so the zone damper sees only the AHU:
zone = resolved["zone-damper"]
zone.find_all_previous()
[<AirHandlingUnit 'ahu'>]
descend=True also steps into containers through their maps_to points.
walk() shows how far away each object is:
for hop in zone.walk("upstream", descend=True):
print(hop.depth, hop.entity.name)
1 ahu
2 supply-fan
3 outside-air-damper
The fan is one step in, behind the AHU’s outlet. The damper is one step past it, through the duct the solver chose. Containment answers the other question, what holds the fan:
resolved["supply-fan"].find_parent(AirHandlingUnit)
<AirHandlingUnit 'ahu'>
A walk can also start inside a container. It leaves through maps_to
without needing descend:
resolved["supply-fan"].find_all_next()
[<Damper 'zone-damper'>]
4. Validate#
validation = model.validate()
print("conforms:", validation.ok, "issues:", len(validation.issues))
print("triples:", len(model.graph()))
conforms: True issues: 2
triples: 69
examples/s223/ahu/s223_quickstart.py is a larger air and water example.