Model Exploration

Model Exploration#

Note

The purpose of this tutorial is to explore a model by learning the following:

  1. Parsing (loading) an existing model

  2. 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.10/x64/lib/python3.11/site-packages/rdflib/graph.py:1468, in Graph.parse(self, source, publicID, format, location, file, data, **args)
   1373 def parse(
   1374     self,
   1375     source: Optional[
   (...)
   1383     **args: Any,
   1384 ) -> "Graph":
   1385     """
   1386     Parse an RDF source adding the resulting triples to the Graph.
   1387 
   (...)
   1465 
   1466     """
-> 1468     source = create_input_source(
   1469         source=source,
   1470         publicID=publicID,
   1471         location=location,
   1472         file=file,
   1473         data=data,
   1474         format=format,
   1475     )
   1476     if format is None:
   1477         format = source.content_type

File /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/site-packages/rdflib/parser.py:401, in create_input_source(source, publicID, location, file, data, format)
    394         assert data is None
    395         assert source is None
    396     (
    397         absolute_location,
    398         auto_close,
    399         file,
    400         input_source,
--> 401     ) = _create_input_source_from_location(
    402         file=file,
    403         format=format,
    404         input_source=input_source,
    405         location=location,
    406     )
    408 if file is not None:
    409     if TYPE_CHECKING:

File /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/site-packages/rdflib/parser.py:463, in _create_input_source_from_location(file, format, input_source, location)
    461     file = open(filename, "rb")
    462 else:
--> 463     input_source = URLInputSource(absolute_location, format)
    465 auto_close = True
    466 # publicID = publicID or absolute_location  # Further to fix
    467 # for issue 130

File /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/site-packages/rdflib/parser.py:270, in URLInputSource.__init__(self, system_id, format)
    266     myheaders["Accept"] = ", ".join(acc)
    268 req = Request(system_id, None, myheaders)  # type: ignore[arg-type]
--> 270 response: addinfourl = _urlopen(req)
    271 self.url = response.geturl()  # in case redirections took place
    272 self.links = self.get_links(response)

File /opt/hostedtoolcache/Python/3.11.10/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.10/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.10/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.10/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.10/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.10/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.10/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:

Open223 Explore links:

# 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)