Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hasSubstance to meters #490

Merged
merged 12 commits into from
Mar 26, 2023
70 changes: 2 additions & 68 deletions bricksrc/equipment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rdflib import Literal
from .namespaces import TAG, OWL, BRICK
from .meters import meter_subclasses

"""
Set up subclasses of the equipment superclass
Expand Down Expand Up @@ -60,74 +61,7 @@
"Gas_Distribution": {"tags": [TAG.Gas, TAG.Distribution, TAG.Equipment]},
"Meter": {
"tags": [TAG.Meter, TAG.Equipment],
"subclasses": {
"Electrical_Meter": {
"tags": [TAG.Electrical, TAG.Meter, TAG.Equipment],
"subclasses": {
"Building_Electrical_Meter": {
"tags": [
TAG.Building,
TAG.Electrical,
TAG.Meter,
TAG.Equipment,
],
"parents": [BRICK.Building_Meter],
}
},
},
"Gas_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Gas],
"subclasses": {
"Building_Gas_Meter": {
"tags": [TAG.Building, TAG.Gas, TAG.Meter, TAG.Equipment],
"parents": [BRICK.Building_Meter],
}
},
},
"Thermal_Power_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Thermal, TAG.Power],
},
"Water_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Water],
"subclasses": {
"Building_Water_Meter": {
"tags": [TAG.Building, TAG.Water, TAG.Meter, TAG.Equipment],
"parents": [BRICK.Building_Meter],
},
"Chilled_Water_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Water, TAG.Chilled],
"subclasses": {
"Building_Chilled_Water_Meter": {
"tags": [
TAG.Building,
TAG.Chilled,
TAG.Water,
TAG.Meter,
TAG.Equipment,
],
"parents": [BRICK.Building_Meter],
},
},
},
"Hot_Water_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Water, TAG.Hot],
"subclasses": {
"Building_Hot_Water_Meter": {
"tags": [
TAG.Building,
TAG.Hot,
TAG.Water,
TAG.Meter,
TAG.Equipment,
],
"parents": [BRICK.Building_Meter],
},
},
},
},
},
"Building_Meter": {"tags": [TAG.Meter, TAG.Equipment, TAG.Building]},
},
"subclasses": meter_subclasses,
},
"Motor": {
"tags": [TAG.Equipment, TAG.Motor],
Expand Down
81 changes: 81 additions & 0 deletions bricksrc/meters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from .namespaces import TAG, BRICK

meter_subclasses = {
"Electrical_Meter": {
"tags": [TAG.Electrical, TAG.Meter, TAG.Equipment],
"subclasses": {
"Building_Electrical_Meter": {
"tags": [
TAG.Building,
TAG.Electrical,
TAG.Meter,
TAG.Equipment,
],
"parents": [BRICK.Building_Meter],
}
},
},
"Gas_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Gas],
"subclasses": {
"Building_Gas_Meter": {
"tags": [TAG.Building, TAG.Gas, TAG.Meter, TAG.Equipment],
"parents": [BRICK.Building_Meter],
BRICK.hasSubstance: BRICK.Natural_Gas,
}
},
BRICK.hasSubstance: BRICK.Natural_Gas,
},
"Thermal_Power_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Thermal, TAG.Power],
},
"Water_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Water],
BRICK.hasSubstance: BRICK.Water,
"subclasses": {
"Building_Water_Meter": {
"tags": [TAG.Building, TAG.Water, TAG.Meter, TAG.Equipment],
"parents": [BRICK.Building_Meter],
BRICK.hasSubstance: BRICK.Water,
},
"Chilled_Water_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Water, TAG.Chilled],
BRICK.hasSubstance: BRICK.Chilled_Water,
"subclasses": {
"Building_Chilled_Water_Meter": {
BRICK.hasSubstance: BRICK.Chilled_Water,
"tags": [
TAG.Building,
TAG.Chilled,
TAG.Water,
TAG.Meter,
TAG.Equipment,
],
"parents": [BRICK.Building_Meter],
},
},
},
"Hot_Water_Meter": {
BRICK.hasSubstance: BRICK.Hot_Water,
"tags": [TAG.Meter, TAG.Equipment, TAG.Water, TAG.Hot],
"subclasses": {
"Building_Hot_Water_Meter": {
BRICK.hasSubstance: BRICK.Hot_Water,
"tags": [
TAG.Building,
TAG.Hot,
TAG.Water,
TAG.Meter,
TAG.Equipment,
],
"parents": [BRICK.Building_Meter],
},
},
},
},
},
"Building_Meter": {
"tags": [TAG.Meter, TAG.Equipment, TAG.Building],
"constraints": {BRICK.meters: [BRICK.Building]},
},
}
62 changes: 62 additions & 0 deletions bricksrc/rules.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,68 @@ bsh:VirtualMeterRule a sh:NodeShape ;
""" ;
] .

# infer the hasSubstance relationship for meter instances
bsh:MeterInferSubstance a sh:NodeShape ;
sh:targetClass brick:Meter ;
sh:rule [
a sh:SPARQLRule ;
sh:prefixes <https://brickschema.org/schema/1.3/Brick> ;
sh:construct """
CONSTRUCT {
$this brick:hasSubstance ?substance .
} WHERE {
$this rdf:type ?meter .
?meter rdfs:subClassOf* brick:Meter .
?meter brick:hasSubstance ?substance .
}
""" ;
] ;
.

# infer the meter subclass from the hasSubstance relationship
# don't do this for building meters -- handled below
bsh:MeterInferSubclassNonBuildingMeter a sh:NodeShape ;
sh:targetClass brick:Meter ;
sh:rule [
a sh:SPARQLRule ;
sh:prefixes <https://brickschema.org/schema/1.3/Brick> ;
sh:construct """
CONSTRUCT {
$this rdf:type ?metertype .
} WHERE {
$this rdf:type/rdfs:subClassOf* brick:Meter .
$this brick:hasSubstance ?substance .
?metertype brick:hasSubstance ?substance .
FILTER NOT EXISTS {
$this rdf:type/rdf:subClassOf* brick:Building_Meter
}
MINUS {
?metertype rdfs:subClassOf* brick:Building_Meter .
}
}
""" ;
] ;
.

# infer the *building* meter subclass from the hasSubstance relationship
bsh:MeterInferSubclassBuildingMeter a sh:NodeShape ;
sh:targetClass brick:Building_Meter ;
sh:rule [
a sh:SPARQLRule ;
sh:prefixes <https://brickschema.org/schema/1.3/Brick> ;
sh:construct """
CONSTRUCT {
$this rdf:type ?metertype .
} WHERE {
$this rdf:type/rdfs:subClassOf* brick:Building_Meter .
$this brick:hasSubstance ?substance .
?metertype brick:hasSubstance ?substance .
?metertype rdfs:subClassOf+ brick:Building_Meter .
}
""" ;
] ;
.

bsh:OneLastKnownValuePerEntity a sh:NodeShape ;
sh:targetSubjectsOf brick:lastKnownValue ;
sh:property [
Expand Down
2 changes: 1 addition & 1 deletion bricksrc/substances.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .namespaces import TAG, BRICK, OWL, SKOS
from .namespaces import BRICK, OWL, SKOS

# Defining substances
substances = {
Expand Down
36 changes: 34 additions & 2 deletions tests/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import pytest
import brickschema
from .util import make_readable
import os, sys
import os
import sys

sys.path.append("..")
from bricksrc.namespaces import BRICK, TAG, A, SKOS # noqa: E402
Expand Down Expand Up @@ -60,7 +61,6 @@

@pytest.mark.slow
def test_tag_inference():

# Apply reasoner
g.load_file("extensions/brick_extension_shacl_tag_inference.ttl")
g.expand(profile="shacl+shacl+shacl")
Expand Down Expand Up @@ -234,3 +234,35 @@ def test_virtual_meter():
g.add((BLDG.abcdef, BRICK.isVirtualMeter, [(BRICK.value, Literal(False))]))
valid, _, report = g.validate()
assert valid, report


def test_meter_substance_inference():
g = brickschema.Graph()
g.load_file("Brick.ttl")
g.add((BLDG.water_meter, A, BRICK.Water_Meter))
g.expand("shacl") # run shacl inference
assert (BLDG.water_meter, BRICK.hasSubstance, BRICK.Water) in g
assert (BLDG.water_meter, BRICK.hasSubstance, BRICK.Chilled_Water) not in g

g = brickschema.Graph()
g.load_file("Brick.ttl")
g.add((BLDG.water_meter, A, BRICK.Building_Water_Meter))
g.expand("shacl") # run shacl inference
assert (BLDG.water_meter, BRICK.hasSubstance, BRICK.Water) in g
assert (BLDG.water_meter, BRICK.hasSubstance, BRICK.Chilled_Water) not in g

g = brickschema.Graph()
g.load_file("Brick.ttl")
g.add((BLDG.water_meter, A, BRICK.Meter))
g.add((BLDG.water_meter, BRICK.hasSubstance, BRICK.Water))
g.expand("shacl") # run shacl inference
assert (BLDG.water_meter, A, BRICK.Water_Meter) in g
assert (BLDG.water_meter, A, BRICK.Building_Water_Meter) not in g

g = brickschema.Graph()
g.load_file("Brick.ttl")
g.add((BLDG.water_meter, A, BRICK.Building_Meter))
g.add((BLDG.water_meter, BRICK.hasSubstance, BRICK.Water))
g.expand("shacl") # run shacl inference
assert (BLDG.water_meter, A, BRICK.Water_Meter) not in g
assert (BLDG.water_meter, A, BRICK.Building_Water_Meter) in g