{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# GraphModel Overview" ] }, { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "## Intro to Knowledge Graphs\n", "\n", "Knowledge graphs are a way of storing and organizing complex information in a graph structure. In a knowledge graph, data is represented as nodes (entities) and edges (relationships) that connect these nodes. This structure makes it easier to visualize and analyze connections between different pieces of information.\n", "\n", "For example, in a knowledge graph representing a power grid:\n", "\n", "* Graph _nodes_ can represent components like transformers, substations, and generators.\n", "\n", "* Graph _edges_ can represent relationships such as the connection between different components.\n", "\n", "It is very important to understand that in this context graph nodes are not the same as electrical buses / nodes. Every bus, branch, and piece of equipment is a node in the knowledge graph.\n", "\n", "Below is a simple graphical representation an distribution line as a knowledge graph:\n", "\n", "![line-property-graph](./images/4_1_property_graph.svg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Intro to Typed Property Graphs\n", "\n", "The core functionality of the CIMantic Graphs interface is driven by a typed property graph interface in which the graph nodes and edges are strictly typed according to the particular object and its attributes and associations as given by UML structure of the Common Information Model. Typed property graphs are a specific kind of graph used in programming and database systems to model and manage complex data. Within the typed property graph, the following general rules apply:\n", "1. Nodes and Edges:\n", "\n", " * Nodes represent entities (like transformers, substations, or generators).\n", "\n", " * Edges represent the relationships between these entities.\n", "\n", "2. Properties:\n", "\n", " * Both nodes and edges can have properties. Properties are additional pieces of information such as names, capacity, or any other relevant attributes.\n", "\n", " * For example, a node representing an `ACLineSegment` will have properties like name and length.\n", "\n", "3. Types:\n", "\n", " * Each node and edge has a type that defines what kind of entity or relationship it represents.\n", "\n", " * Types help in organizing and validating the data. For example, a node of type `ACLineSegment` might only have properties like name and length, while a node of type `PhotoVoltaicUnit` will have properties like name and rated capacity.\n", "\n", "In the figure above, each of the graph nodes are an object with a strict datatype, such as `ACLineSegment`, `Feeder`, and `Terminal`. Only object types defined within the CIM profile selected by the user are supported within the typed property graph. The graph nodes are linked together with edges of strict typing with specific names for each association. So, the edge `ACLineSegment` --[`ConductingEquipment.Terminals`]--> `list(Terminal)` can only have a list of `Terminal` objects. A different object type, such as `Location` cannot be used in that edge." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## GraphModel Base Class\n", "\n", "Most of the core interfaces for interacting with bus-branch, node-breaker, and distribution feeders models are provided by the `GraphModel` base class, which is the parent software dataclass for the classes used for specific modeling needs. The structure of the GraphModel class and its attributes are explained in more detail below. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Input Arguments\n", "\n", "The following parameters need to be specified when creating a GraphModel representation of the power system:\n", "\n", "* `connection`: An instance of `ConnectionInterface`, such as `XMLFile` or `GridappsdConnection`\n", "\n", "* `container`: An instance of the top-level `EquipmentContainer` such as `Feeder` from which the model will be built\n", "\n", "* `distributed`: Boolean on whether a centralized or distributed graph should be built\n", "\n", "The `GraphModel` class itself is an abstract class and does not contain an `__init__` method. The input arguments need to be passed to a child class of `GraphModel`, such as `FeederModel`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Graph\n", "\n", "The `.graph` attribute of the GraphModel dataclass provides the interface to the typed property graph with all data contained in the network model.\n", "\n", "It is a dictionary typed first by class type (e.g. `cim.ACLineSegment`) and then by UUID of each element within the network model.\n", "\n", "All objects within the graph (if added using the API) are forced to have a unique UUID identifier.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Object Types in the `graph`\n", "\n", "The set of all equipment available in the graph can be obtained as the dictionary keys:\n", "\n", "```python\n", "eq_types = list(network.graph.keys())\n", "```\n", "\n", "This will provide a list of dataclass types, such as\n", "\n", "`[cim.ACLineSegment, cim.ConnectivityNode, cim.PowerTransformer, cim.Terminal]`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Object IDs in the `graph`\n", "\n", "The set of mRIDs of all equipment contained in the graph can be obtained as the dictionary keys of the graph of a particular class type:\n", "\n", "```python\n", "eq_uuids = list(network.graph[cim.ACLineSegment].keys())\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Objects in the `graph`\n", "\n", "Access to graph nodes (objects) is done via a loop or by reference to the UUID, as shown below.\n", "\n", "#### Example 1: Access to objects via loop\n", "\n", "The most direct method to access objects is via a loop, which covers most use cases\n", "\n", "```python\n", "# Find line named 634_645\n", "for line in network.graph[cim.ACLineSegment].values():\n", " if line.name == '634_635':\n", " break\n", "```\n", "\n", "The .get() method can be used for more robust handling of empty fields:\n", "\n", "```python\n", "# Find lines with length > 100:\n", "long_lines = []\n", "for line in network.graph.get(cim.ACLineSegment, {}).values():\n", " if line.length > 100:\n", " long_lines.append(line)\n", "```\n", "\n", "#### Example 2: Access to objects via UUID\n", "\n", "The other method is by directly invoking the mRID of the object as a UUID\n", "\n", "```python\n", "line = network.graph[cim.ACLineSegment][UUID('4c04f838-62aa-475e-aefa-a63b7c889c13')]\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## API Reference\n", "\n", "All classes based on GraphModel inherit the following methods:\n", "\n", "* `add_to_graph(object)` - \n", "\n", "* `add_jsonld_to_graph(str)`\n", "\n", "* `get_all_edges(cim.ClassName)`\n", "\n", "* `get_object(mRID)`\n", "\n", "* `get_from_triple(object,attribute)`\n", "\n", "* `pprint(cim.ClassName)`\n", "\n", "* `upload()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### get_all_edges()\n", "\n", "The `.get_all_edges()` method is the core library method that enables the flexibility of CIM-Graph to query for CIM objects of any class and build the knowledge graph without custom queries.\n", "\n", "The arguments of the method are\n", "\n", "* cim_class (type): The CIM class for which to retrieve edges (e.g. `cim.ACLineSegment`)\n", "\n", "The method does not return any values.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### get_all_attributes()\n", "\n", "\n", "The `.get_all_attributes()` method is similar to get_all_edges(), but does not create any new graph nodes. Instead, edges to new classes are represented as strings.\n", "\n", "The arguments of the method are\n", "\n", "* cim_class (type): The CIM class for which to retrieve edges (e.g. `cim.ACLineSegment`)\n", "\n", "The method does not return any values." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### get_object()\n", "\n", "The `get_object()` method is used to retrieve an object from the database using its mRID. \n", "\n", "The arguments are\n", "\n", "* `mRID` (str): The mRID of the object to be retrieved.\n", "\n", "* `graph` (dict[type, uuid]): Optional -- An existing graph to which the object should be added" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### add_to_graph()\n", "\n", "The `.add_to_graph()` method adds a CIM object to the graph model (at both the type and UUID levels within the graph dictionary).\n", "\n", "The arguments are\n", "\n", "* `" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## UML Diagrams\n", "\n", "This section contains UML class and sequence diagrams summarizing the structure and methods offered by the GraphModel class.\n", "\n", "All diagrams are loaded from flat text using mermaid.js which can be imported using\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from mermaid import Mermaid" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### GraphModel Class Inheritance\n", "\n", "The diagram below shows the dataclass fields and methods offered by the GraphModel base class and its children classes." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with open('./images/4_1_graph_model_inheritance.txt', 'r') as diagram:\n", " diagram_text = diagram.read()\n", "Mermaid(diagram_text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### get_all_edges()\n", "\n", "The `.get_all_edges()` method invokes a similarly named method within the the ConnectionInterface, which performs the database-specific query. For improved processing, CIM-Graph generally uses parallel processing with sets of 100 objects queried for in each batch. The execution workflow is shown below" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open('./images/4_1_get_all_edges.txt', 'r') as diagram:\n", " diagram_text = diagram.read()\n", "Mermaid(diagram_text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### get_all_attributes()\n", "\n", "The get_all_attributes method invokes a similarly named method within the the ConnectionInterface, which performs the database-specific query.The execution workflow is shown below" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "with open('./images/4_1_get_all_attributes.txt', 'r') as diagram:\n", " diagram_text = diagram.read()\n", "Mermaid(diagram_text)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 2 }