-
Notifications
You must be signed in to change notification settings - Fork 17
/
example_building.py
323 lines (292 loc) · 14.2 KB
/
example_building.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# import the rdflib
from rdflib import Graph, Namespace, URIRef, Literal
import rdflib
# declare the namespaces. This will pull in the definition of the schema
# if it exists. Else, we need to parse some file to fill in the missing symbols
# is not pulling anything in from the web, I don't think?
RDF = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
RDFS = Namespace('http://www.w3.org/2000/01/rdf-schema#')
BRICK = Namespace('https://brickschema.org/schema/1.0.1/Brick#')
BRICKFRAME = Namespace('https://brickschema.org/schema/1.0.1/BrickFrame#')
BRICKTAG = Namespace('https://brickschema.org/schema/1.0.1/BrickTag#')
# This is the graph object that contains all of the schemas. We use this
# graph to pull out classes and relationships, but any *instances* of these
# are going to be stored in another graph.
g = rdflib.Graph()
# "bind()" simply means that we can use 'rdf' instead of the full URI for rdf when
# referring to entities in its namespace. Just shorthand
g.bind( 'rdf', RDF)
g.bind( 'rdfs', RDFS)
g.bind( 'brick', BRICK)
g.bind( 'bf', BRICKFRAME)
g.bind( 'btag', BRICKTAG)
#g.parse('../BuildingSchema/BrickV2.ttl', format='turtle')
#g.parse('../BuildingSchema/BrickFrame.ttl', format='turtle')
#g.parse('../BuildingSchema/BrickTag.ttl', format='turtle')
# We want to add nodes to the graph for our building. The building exists in its
# own namespace, which we are calling EX
EX = Namespace('https://brickschema.org/schema/1.0.1/building_example#')
g.bind('ex', EX)
# Once we have our namespace, we can start placing things in it. Lets create
# some entity that will represent our building. We have a "Building" Location in
# the brick schema, so we need to declare that some unique label within the EX
# namespace "is a" building. This is achieved using the builtin RDF.type relationship.
# Here, the label "building_1" is implicitly created when we mention it in the add()
# function. Add()'s 3 arguments are: subject, predicate, object.
g.add((EX.building_1, RDF.type, BRICK.Building))
# we can give it a name too
g.add((EX.building_1, RDFS.label, Literal("Example Building Hall")))
# we can check now if our entity is known to be a location. This should be true because
# we inherited that from BRICK.building, which is a subclass of Location
print "SHOULD be True: ", (BRICK.Building, RDFS.subClassOf, BRICK.Location) in g
# TODO: need a sparql query for this?
res = g.query("""SELECT DISTINCT ?bldg
WHERE {
?bldg rdf:type brick:Building
}
""")
print "All buildings:"
for row in res:
print "%s" % row
# now that we have a building, lets add 2 floors to it.
# We implicitly create the 2 labels floor_1 and floor_2 that represent
# the floor entities. We use the RDF.type relationship to say that they are
# instances of the Floor class as defined by the BRICK schema.
g.add((EX.floor_1, RDF.type, BRICK.Floor))
g.add((EX.floor_2, RDF.type, BRICK.Floor))
# We then link these floors to our example building by using the BRICKFRAME relationsihp
# "isPartOf".
g.add((EX.floor_1, BRICKFRAME.isPartOf, EX.building_1))
g.add((EX.floor_2, BRICKFRAME.isPartOf, EX.building_1))
# the query for "all floors in this building"
res = g.query("""SELECT DISTINCT ?bldg ?floor
WHERE {
?bldg rdf:type brick:Building .
?floor rdf:type brick:Floor .
?floor bf:isPartOf+ ?bldg .
}
""")
for row in res:
print "Building %s has floor %s" % row
# now lets do the same for a few rooms. Here, to autogenerate some labels, we're tkaing
# advantage of the fact that EX.floor_1 and EX["floor_1"] are equivalent.
# Remember, each label needs to be *unique* within the namespace.
for floor_num in [1,2]:
floor = EX["floor_{0}".format(floor_num)]
for room_num in [1,2,3,4]:
room = EX["room_{0}_{1}".format(floor_num, room_num)]
g.add((room, RDF.type, BRICK.Room))
g.add((room, BRICKFRAME.isPartOf, floor))
# TODO: query to demonstrate that we now know that those rooms are in the building
res = g.query("""SELECT DISTINCT ?bldg ?room
WHERE {
?bldg rdf:type brick:Building .
?room rdf:type brick:Room .
?room bf:isPartOf+ ?bldg .
}
""")
for row in res:
print "Building %s has room %s" % row
# We have some structure to our building, so lets start adding in some equipment.
# Sensors are straightforward to add. Lets put some CO2 and PIR and temperature sensors
# in some rooms.
# We *could* do the following:
# for num in range(1,5):
# temp_sensor = EX["temp_sensor_{0}".format(num)]
# co2_sensor = EX["co2_sensor_{0}".format(num)]
# pir_sensor = EX["pir_sensor_{0}".format(num)]
# g.add((temp_sensor, RDF.type, BRICK.Sensor))
# g.add((co2_sensor, RDF.type, BRICK.Sensor))
# g.add((pir_sensor, RDF.type, BRICK.Sensor))
#
# This successfully identifies all of these labels as being sensors, but it doesn't yet
# tell us what kind of sensor they are. BRICK schema defines a tag Temperature which
# is a subclass of PhysicalProperties, which is a subclass of MeasurementProperty, which
# is a subclass of Tag.
#
# If we wanted to add this tag, we could do so to each of the instances, using a line like:
# g.add((EX.temp_sensor_1, BRICKFRAME.hasTag, BRICK.Temperature))
#
# However, we'd have to do this for *each* instance of a temperature sensor, co2 sensor, etc.
# What if we forgot to label one? What if we later on want to associate more information
# with what consists a temperature sensor? What we really want here is a subclass for each
# of these sensor types.
#
# We would declare a temperature sensor class like the following:
# BRICK.Temperature_Sensor RDFS.subClassOf BRICK.Sensor
# BRICK.Temperature_Sensor BRICKFRAME.hasTag BRICK.Temperature
#
# Then we end up with something like
#
# for num in range(1,5):
# temp_sensor = EX["temp_sensor_{0}".format(num)]
# co2_sensor = EX["co2_sensor_{0}".format(num)]
# pir_sensor = EX["pir_sensor_{0}".format(num)]
# g.add((temp_sensor, RDF.type, BRICK.Temperature_Sensor))
# g.add((co2_sensor, RDF.type, BRICK.CO2_Sensor))
# g.add((pir_sensor, RDF.type, BRICK.PIR_Sensor))
#
# It could be that in our building, we know slightly more about the temperature sensors: we want
# to indicate that the temperature sensors in our building measure in Fahrenheit. We don't
# want to add that to the *main* BRICK schema, because it isn't true for all temperature
# sensors. However, we can create our own subclass and attach the property there
#
# EX.Fahrenheit_Temperature_Sensor RDFS.subClassOf BRICK.Temperature_Sensor
# EX.Fahrenheit_Temperature_Sensor BRICKFRAME.hasUnit BRICK.Fahrenheit
#
# and then declare our instances using
# g.add((temp_sensor, RDF.type, EX.Fahrenheit_Temperature_Sensor))
# add temperature sensor class
g.add((EX.Fahrenheit_Temperature_Sensor, RDFS.subClassOf, BRICK.Temperature_Sensor))
g.add((EX.Fahrenheit_Temperature_Sensor, BRICKFRAME.hasUnit, BRICK.Fahrenheit))
# create the instances of our sensors
for num in range(1,9):
temp_sensor = EX["temp_sensor_{0}".format(num)]
co2_sensor = EX["co2_sensor_{0}".format(num)]
pir_sensor = EX["pir_sensor_{0}".format(num)]
# declare sensors
g.add((temp_sensor, RDF.type, EX.Fahrenheit_Temperature_Sensor))
g.add((co2_sensor, RDF.type, BRICK.CO2_Sensor))
g.add((pir_sensor, RDF.type, BRICK.Occupancy_Sensor))
# add sensor to room
g.add((EX.temp_sensor_1, BRICKFRAME.isLocatedIn, EX.room_1_1))
g.add((EX.temp_sensor_2, BRICKFRAME.isLocatedIn, EX.room_1_2))
g.add((EX.temp_sensor_3, BRICKFRAME.isLocatedIn, EX.room_1_3))
g.add((EX.temp_sensor_4, BRICKFRAME.isLocatedIn, EX.room_1_4))
g.add((EX.temp_sensor_5, BRICKFRAME.isLocatedIn, EX.room_2_1))
g.add((EX.temp_sensor_6, BRICKFRAME.isLocatedIn, EX.room_2_2))
g.add((EX.temp_sensor_7, BRICKFRAME.isLocatedIn, EX.room_2_3))
g.add((EX.temp_sensor_8, BRICKFRAME.isLocatedIn, EX.room_2_4))
g.add((EX.temp_sensor_1, BRICKFRAME.isPointOf, EX.room_1_1))
g.add((EX.temp_sensor_2, BRICKFRAME.isPointOf, EX.room_1_2))
g.add((EX.temp_sensor_3, BRICKFRAME.isPointOf, EX.room_1_3))
g.add((EX.temp_sensor_4, BRICKFRAME.isPointOf, EX.room_1_4))
g.add((EX.temp_sensor_5, BRICKFRAME.isPointOf, EX.room_2_1))
g.add((EX.temp_sensor_6, BRICKFRAME.isPointOf, EX.room_2_2))
g.add((EX.temp_sensor_7, BRICKFRAME.isPointOf, EX.room_2_3))
g.add((EX.temp_sensor_8, BRICKFRAME.isPointOf, EX.room_2_4))
g.add((EX.co2_sensor_1, BRICKFRAME.isLocatedIn, EX.room_1_1))
g.add((EX.co2_sensor_2, BRICKFRAME.isLocatedIn, EX.room_1_2))
g.add((EX.co2_sensor_3, BRICKFRAME.isLocatedIn, EX.room_1_3))
g.add((EX.co2_sensor_4, BRICKFRAME.isLocatedIn, EX.room_1_4))
g.add((EX.co2_sensor_5, BRICKFRAME.isLocatedIn, EX.room_2_1))
g.add((EX.co2_sensor_6, BRICKFRAME.isLocatedIn, EX.room_2_2))
g.add((EX.co2_sensor_7, BRICKFRAME.isLocatedIn, EX.room_2_3))
g.add((EX.co2_sensor_8, BRICKFRAME.isLocatedIn, EX.room_2_4))
g.add((EX.co2_sensor_1, BRICKFRAME.isPointOf, EX.room_1_1))
g.add((EX.co2_sensor_2, BRICKFRAME.isPointOf, EX.room_1_2))
g.add((EX.co2_sensor_3, BRICKFRAME.isPointOf, EX.room_1_3))
g.add((EX.co2_sensor_4, BRICKFRAME.isPointOf, EX.room_1_4))
g.add((EX.co2_sensor_5, BRICKFRAME.isPointOf, EX.room_2_1))
g.add((EX.co2_sensor_6, BRICKFRAME.isPointOf, EX.room_2_2))
g.add((EX.co2_sensor_7, BRICKFRAME.isPointOf, EX.room_2_3))
g.add((EX.co2_sensor_8, BRICKFRAME.isPointOf, EX.room_2_4))
g.add((EX.pir_sensor_1, BRICKFRAME.isLocatedIn, EX.room_1_1))
g.add((EX.pir_sensor_2, BRICKFRAME.isLocatedIn, EX.room_1_2))
g.add((EX.pir_sensor_3, BRICKFRAME.isLocatedIn, EX.room_1_3))
g.add((EX.pir_sensor_4, BRICKFRAME.isLocatedIn, EX.room_1_4))
g.add((EX.pir_sensor_5, BRICKFRAME.isLocatedIn, EX.room_2_1))
g.add((EX.pir_sensor_6, BRICKFRAME.isLocatedIn, EX.room_2_2))
g.add((EX.pir_sensor_7, BRICKFRAME.isLocatedIn, EX.room_2_3))
g.add((EX.pir_sensor_8, BRICKFRAME.isLocatedIn, EX.room_2_4))
g.add((EX.pir_sensor_1, BRICKFRAME.isPointOf, EX.room_1_1))
g.add((EX.pir_sensor_2, BRICKFRAME.isPointOf, EX.room_1_2))
g.add((EX.pir_sensor_3, BRICKFRAME.isPointOf, EX.room_1_3))
g.add((EX.pir_sensor_4, BRICKFRAME.isPointOf, EX.room_1_4))
g.add((EX.pir_sensor_5, BRICKFRAME.isPointOf, EX.room_2_1))
g.add((EX.pir_sensor_6, BRICKFRAME.isPointOf, EX.room_2_2))
g.add((EX.pir_sensor_7, BRICKFRAME.isPointOf, EX.room_2_3))
g.add((EX.pir_sensor_8, BRICKFRAME.isPointOf, EX.room_2_4))
#TODO: do we *ALSO* use "hasPoint" here?, or conversely isPointOf?
# these two lines are equivalent
g.add((EX.pir_sensor_8, BRICKFRAME.isPointOf, EX.room_2_4))
g.add((EX.room_2_4, BRICKFRAME.hasPoint, EX.pir_sensor_8))
# now we need a notion of a power meter. The BRICK schema has a Power Meter concept,
# under the hierarchy: Tag -> Point -> Meter -> Power -> Power Meter.
# Let's attach a power meter to each of the floors
g.add((EX.floor_power_meter_1, RDF.type, BRICK.Power_Meter))
g.add((EX.floor_power_meter_2, RDF.type, BRICK.Power_Meter))
g.add((EX.floor_1, BRICKFRAME.hasPoint, EX.floor_power_meter_1))
g.add((EX.floor_2, BRICKFRAME.hasPoint, EX.floor_power_meter_2))
# hasPoint is how we associate some sensor with what it measures?
# TODO: Does the act of associating EX.floor_1 with EX.floor_power_meter_1 using the BRICKFRAME.hasPoint have any recursive qualities?
# In our example building, we don't know a whole lot about the HVAC system. We do
# know that we have an AHU, and that it has 3 VAVs per floor. 1 VAV serves 2 of the rooms.
# The other 2 rooms are each served by 1 VAV
# instantiate an AHU
g.add((EX.ahu_1, RDF.type, BRICK.AHU))
# associate the AHU with the building
# TODO: is there another, better way of doing this?
g.add((EX.building_1, BRICKFRAME.hasPart, EX.ahu_1))
# instantiate our 6 VAVs
g.add((EX.vav_1_1, RDF.type, BRICK.VAV))
g.add((EX.vav_1_2, RDF.type, BRICK.VAV))
g.add((EX.vav_1_3, RDF.type, BRICK.VAV))
g.add((EX.vav_2_1, RDF.type, BRICK.VAV))
g.add((EX.vav_2_2, RDF.type, BRICK.VAV))
g.add((EX.vav_2_3, RDF.type, BRICK.VAV))
# establish the VAVs as downstream of the AHU
g.add((EX.ahu_1, BRICKFRAME.feeds, EX.vav_1_1))
g.add((EX.ahu_1, BRICKFRAME.feeds, EX.vav_1_2))
g.add((EX.ahu_1, BRICKFRAME.feeds, EX.vav_1_3))
g.add((EX.ahu_1, BRICKFRAME.feeds, EX.vav_2_1))
g.add((EX.ahu_1, BRICKFRAME.feeds, EX.vav_2_2))
g.add((EX.ahu_1, BRICKFRAME.feeds, EX.vav_2_3))
# instantiate 6 HVAC zones; one for each VAV
g.add((EX.hvac_zone_1_1, RDF.type, BRICK.HVAC_Zone))
g.add((EX.hvac_zone_1_2, RDF.type, BRICK.HVAC_Zone))
g.add((EX.hvac_zone_1_3, RDF.type, BRICK.HVAC_Zone))
g.add((EX.hvac_zone_2_1, RDF.type, BRICK.HVAC_Zone))
g.add((EX.hvac_zone_2_2, RDF.type, BRICK.HVAC_Zone))
g.add((EX.hvac_zone_2_3, RDF.type, BRICK.HVAC_Zone))
# declare which rooms are in which HVAC Zone
g.add((EX.hvac_zone_1_1, BRICKFRAME.hasPart, EX.room_1_1))
g.add((EX.hvac_zone_1_1, BRICKFRAME.hasPart, EX.room_1_2))
g.add((EX.hvac_zone_1_2, BRICKFRAME.hasPart, EX.room_1_3))
g.add((EX.hvac_zone_1_3, BRICKFRAME.hasPart, EX.room_1_4))
g.add((EX.hvac_zone_2_1, BRICKFRAME.hasPart, EX.room_1_1))
g.add((EX.hvac_zone_2_1, BRICKFRAME.hasPart, EX.room_1_2))
g.add((EX.hvac_zone_2_2, BRICKFRAME.hasPart, EX.room_1_3))
g.add((EX.hvac_zone_2_3, BRICKFRAME.hasPart, EX.room_1_4))
# declare that the VAVs feed the HVAC Zones
g.add((EX.vav_1_1, BRICKFRAME.feeds, EX.hvac_zone_1_1))
g.add((EX.vav_1_2, BRICKFRAME.feeds, EX.hvac_zone_1_2))
g.add((EX.vav_1_3, BRICKFRAME.feeds, EX.hvac_zone_1_3))
g.add((EX.vav_2_1, BRICKFRAME.feeds, EX.hvac_zone_2_1))
g.add((EX.vav_2_2, BRICKFRAME.feeds, EX.hvac_zone_2_2))
g.add((EX.vav_2_3, BRICKFRAME.feeds, EX.hvac_zone_2_3))
# all temperature sensors in rooms downstream of an AHU
res = g.query("""SELECT ?sensor ?room
WHERE {
?sensor_type rdfs:subClassOf brick:Sensor .
?sensor_type bf:hasTag brick:Temperature .
?sensor rdf:type ?sensor_type .
?ahu rdf:type brick:AHU .
?ahu bf:feeds+ ?zone .
?zone bf:hasPart ?room .
?sensor bf:isPointOf ?room .
}
""")
print "AHU has %d downstream temperature sensors" % len(res)
for row in res:
print row
res = g.query("""
SELECT ?sensor ?sensor_type ?room
WHERE {
?sensor_type rdfs:subClassOf brick:Sensor .
?sensor rdf:type ?sensor_type .
?room rdf:type brick:Room .
?sensor bf:isLocatedIn ?room .
?sensor bf:isPointOf ?room .
{ ?sensor_type bf:hasTag brick:Temperature }
UNION
{ ?sensor_type bf:hasTag brick:CO2 }
UNION
{ ?sensor_type bf:hasTag brick:Occupancy }
}
""")
print len(res)
# TODO: add Lighting
# how to save our results to a Turtle file, which Protege can read
g.serialize(destination='example_building.ttl', format='turtle')
print len(g)