Model Exploration#
Note
The purpose of this tutorial is to explore a model by learning the following:
Parsing (loading) an existing model
Querying an existing model.
For this tutorial, we’ll use an existing equipment model of a variable air volume (VAV) terminal unit with cooling only from section 4.1 of ASHRAE Guideline 36-2021. This and other example models are available from Open223 Models.
Model Parsing#
First, we’ll create a new empty graph then parse (load) an existing graph into it using the Python RDFLib library.
from rdflib import Graph
# Create a Graph
g = Graph()
# Parse in an RDF file hosted on the Internet
g.parse("https://models.open223.info/guideline36-2021-4.1.ttl", format="ttl")
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
Cell In[1], line 7
4 g = Graph()
6 # Parse in an RDF file hosted on the Internet
----> 7 g.parse("https://models.open223.info/guideline36-2021-4.1.ttl", format="ttl")
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/site-packages/rdflib/graph.py:1467, in Graph.parse(self, source, publicID, format, location, file, data, **args)
1372 def parse(
1373 self,
1374 source: Optional[
(...)
1382 **args: Any,
1383 ) -> Graph:
1384 """
1385 Parse an RDF source adding the resulting triples to the Graph.
1386
(...)
1464
1465 """
-> 1467 source = create_input_source(
1468 source=source,
1469 publicID=publicID,
1470 location=location,
1471 file=file,
1472 data=data,
1473 format=format,
1474 )
1475 if format is None:
1476 format = source.content_type
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/site-packages/rdflib/parser.py:735, in create_input_source(source, publicID, location, file, data, format)
728 assert data is None
729 assert source is None
730 (
731 absolute_location,
732 auto_close,
733 file,
734 input_source,
--> 735 ) = _create_input_source_from_location(
736 file=file,
737 format=format,
738 input_source=input_source,
739 location=location,
740 )
742 if file is not None:
743 if TYPE_CHECKING:
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/site-packages/rdflib/parser.py:797, in _create_input_source_from_location(file, format, input_source, location)
795 file = open(filename, "rb")
796 else:
--> 797 input_source = URLInputSource(absolute_location, format)
799 auto_close = True
800 # publicID = publicID or absolute_location # Further to fix
801 # for issue 130
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/site-packages/rdflib/parser.py:594, in URLInputSource.__init__(self, system_id, format)
590 myheaders["Accept"] = ", ".join(acc)
592 req = Request(system_id, None, myheaders) # type: ignore[arg-type]
--> 594 response: addinfourl = _urlopen(req)
595 self.url = response.geturl() # in case redirections took place
596 self.links = self.get_links(response)
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/site-packages/rdflib/_networking.py:106, in _urlopen(request)
95 """
96 This is a shim for `urlopen` that handles HTTP redirects with status code
97 308 (Permanent Redirect).
(...)
103 :return: The response to the request.
104 """
105 try:
--> 106 return urlopen(request)
107 except HTTPError as error:
108 if error.code == 308 and sys.version_info < (3, 11):
109 # HTTP response code 308 (Permanent Redirect) is not supported by python
110 # versions older than 3.11. See <https://bugs.python.org/issue40321> and
111 # <https://github.com/python/cpython/issues/84501> for more details.
112 # This custom error handling should be removed once all supported
113 # versions of Python handles 308.
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/urllib/request.py:216, in urlopen(url, data, timeout, cafile, capath, cadefault, context)
214 else:
215 opener = _opener
--> 216 return opener.open(url, data, timeout)
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/urllib/request.py:525, in OpenerDirector.open(self, fullurl, data, timeout)
523 for processor in self.process_response.get(protocol, []):
524 meth = getattr(processor, meth_name)
--> 525 response = meth(req, response)
527 return response
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/urllib/request.py:634, in HTTPErrorProcessor.http_response(self, request, response)
631 # According to RFC 2616, "2xx" code indicates that the client's
632 # request was successfully received, understood, and accepted.
633 if not (200 <= code < 300):
--> 634 response = self.parent.error(
635 'http', request, response, code, msg, hdrs)
637 return response
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/urllib/request.py:563, in OpenerDirector.error(self, proto, *args)
561 if http_err:
562 args = (dict, 'default', 'http_error_default') + orig_args
--> 563 return self._call_chain(*args)
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/urllib/request.py:496, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)
494 for handler in handlers:
495 func = getattr(handler, meth_name)
--> 496 result = func(*args)
497 if result is not None:
498 return result
File /opt/hostedtoolcache/Python/3.11.11/x64/lib/python3.11/urllib/request.py:643, in HTTPDefaultErrorHandler.http_error_default(self, req, fp, code, msg, hdrs)
642 def http_error_default(self, req, fp, code, msg, hdrs):
--> 643 raise HTTPError(req.full_url, code, msg, hdrs, fp)
HTTPError: HTTP Error 404: Not Found
Next, we’ll explore the model’s size by printing the number of triples in it.
# Print the number of "triples" in the Graph
print(f"Graph g has {len(g)} statements.")
Finally, we’ll print the contents of the model since it’s not that large.
# Print out the entire Graph in the RDF Turtle format
print(g.serialize(format="turtle"))
Model Querying#
After exploring the model to get a sense for what it contains, let’s query the model using RDFLib (this can also be done with Open223 Query). For this tutorial, we’ll query the model for all the VAV terminal’s points, which are instances of the following classes:
# Query the data in g using SPARQL
q = """
PREFIX s223: <http://data.ashrae.org/standard223#>
SELECT ?obj WHERE {
{ ?obj a s223:QuantifiableActuatableProperty . }
UNION
{ ?obj a s223:QuantifiableObservableProperty . }
}
"""
# Apply the query to the graph and iterate through results
for r in g.query(q):
print(r)