Skip to content

Instantly share code, notes, and snippets.

@gtfierro
Created July 23, 2022 00:10
Show Gist options
  • Save gtfierro/6c1d9910c6e53231a7a463778db5f07e to your computer and use it in GitHub Desktop.
Save gtfierro/6c1d9910c6e53231a7a463778db5f07e to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Use cases:\n",
"1. Generating control sequences for real BAS controllers using CDL\n",
"2. FDD and other open loop verification\n",
" * only need input and output points\n",
" * only CDL sequences suffice\n",
" * add annotation tags to CDL blocks (elementary CDL block and composite block) --- higher priority\n",
" * May not need to parse the entire modelica elements. Stop at blocks?\n",
" * User specificies which blocks to parse (user input)\n",
" * Output:\n",
" * semantic model with inputs and output \n",
" * eventually these will need to be tied to BACnet points. \n",
" * Questions:\n",
" * how to verify user inputed blocks? \n",
" * What if there is user defined composite block using a bunch of CDL blocks (from `Buildings.Controls.CDL` or `Buildings.Controls.OBC.*` ) ?\n",
" * who does the BACnet point mapping? \n",
" * what about the sequences themselves? Are we storing it somehow? \n",
" * What about documentation? \n",
" * How to specify that some points will be need to undergo transformation before mapping to CDL input/output\n",
" * Do we allow annotations to propogate to lower blocks? Current CDL has a mechanism to propogate annotations.\n",
"3. Developing portable analytics applications\n",
"4. Developing portable advanced control sequences\n",
" * Each algorithm would have a BRICK or SPARQL query. \n",
" * Need Energy model and CDL sequences\n",
" * Output: \n",
" * semantic model with equipment and points (inputs and outputs)\n",
" * Questions:\n",
" * When should we stop parsing? Currently, I stop as soon as the element type does not start with `Buildings.*`\n",
"5. BOPTEST/simulation/Hardware in the loop\n",
" * needs energy model\n",
" * might need external references to BACnet points or other real world variables\n",
" * Output:\n",
" * semantic model with equipment and points (inputs and outputs)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import brickschema\n",
"from brickschema.namespaces import BRICK, RDFS, RDF\n",
"import json\n",
"from element_relationship import Element_Relationship_Extractor\n",
"import rdflib"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"modelica_brick_sensor_type_map = {\n",
" 'TemperatureTwoPort': BRICK['Temperature_Sensor'],\n",
" 'Temperature': BRICK['Temperature_Sensor'],\n",
" 'RelativeTemperature': BRICK['Temperature_Sensor'],\n",
" 'TemperatureWetBulbTwoPort': BRICK['Temperature_Sensor'],\n",
" 'VolumeFlowRate': BRICK['Flow_Sensor'],\n",
" 'RelativeHumidity': BRICK['Humditiy_Sensor'],\n",
" 'RelativeHumidityTwoPort': BRICK['Humditiy_Sensor'],\n",
" 'Pressure': BRICK['Pressure_Sensor'],\n",
" 'RelativePressure': BRICK['Pressure_Sensor']\n",
"}\n",
"\n",
"modelica_brick_heat_exchanger_type_map = {\n",
" 'DryCoilCounterFlow': BRICK['Heating_Coil'],\n",
" 'DryCoilDiscretized': BRICK['Heating_Coil'],\n",
" 'DryCoilEffectivenessNTU': BRICK['Heating_Coil'], #DryCoil could also be for cooling in some climate zones, could be part of a heat exchanger,\n",
" 'WetCoilCounterFlow': BRICK['Cooling_Coil'],\n",
" 'WetCoilDiscretized': BRICK['Cooling_Coil'],\n",
" 'EvaporatorCondenser': BRICK['Heat_Exchanger'], #look at which side of compressor it is on to decide if it is evaporator or condensor\n",
" 'Heater_T': BRICK['Space_Heater'],\n",
"}\n",
"\n",
"modelica_brick_actuator_type_map = {\n",
" 'Dampers.Exponential': BRICK['Damper'],\n",
" 'Dampers.MixingBox': BRICK['Damper'],\n",
" 'Dampers.MixingBoxMinimumFlow': BRICK['Damper'],\n",
" 'Dampers.PressureIndependent': BRICK['Damper'],\n",
" 'Valves.ThreeWayEqualPercentageLinear': BRICK['Valve'], #need more clarity on the brick side for valves\n",
" 'Valves.ThreeWayLinear': BRICK['Valve'],\n",
" 'Valves.ThreeWayTable': BRICK['Valve'],\n",
" 'Valves.TwoWayEqualPercentageLinear': BRICK['Valve'],\n",
" 'Valves.TwoWayLinear': BRICK['Valve'],\n",
" 'Valves.TwoWayPolynomial': BRICK['Valve'],\n",
" 'Valves.TwoWayPressureIndependent': BRICK['Valve'],\n",
" 'Valves.TwoWayQuickOpening': BRICK['Valve'],\n",
" 'Valves.TwoWayTable': BRICK['Valve']\n",
"}\n",
"\n",
"modelica_brick_mover_type_map = { # anything can be pump or fan according to the media\n",
" 'FlowControlled_dp': BRICK['Pump'],\n",
" 'FlowControlled_m_flow': BRICK['Pump'],\n",
" 'SpeedControlled_Nrpm': BRICK['Fan'],\n",
" 'SpeedControlled_y': BRICK['Fan']\n",
"}\n",
"\n",
"modelica_brick_thermal_zone_type_map = {\n",
" 'Detailed.MixedAir': BRICK['HVAC_Zone'],\n",
" 'ReducedOrder.EquivalentAirTemperature': BRICK['HVAC_Zone'],\n",
" 'ReducedOrder.RC': BRICK['HVAC_Zone'],\n",
" 'ReducedOrer.SolarGain': BRICK['HVAC_Zone']\n",
"}\n",
"\n",
"modelica_brick_medium_type_map = {\n",
" 'Air': BRICK['Air'],\n",
" 'Water': BRICK['Water']\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'model': 'Buildings.Examples.MultiZoneOfficeSimpleAir.TestCases.TestCase',\n",
" 'generate_json': True}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"with open(\"config_boptest.json\") as fp:\n",
" config = json.load(fp)\n",
"config\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"ename": "AttributeError",
"evalue": "'NoneType' object has no attribute 'split'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"Input \u001b[0;32mIn [5]\u001b[0m, in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0m element_extractor \u001b[38;5;241m=\u001b[39m \u001b[43mElement_Relationship_Extractor\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconfig_file\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mconfig_boptest.json\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m~/src/NREL/obc/software/brick_export/element_relationship.py:28\u001b[0m, in \u001b[0;36mElement_Relationship_Extractor.__init__\u001b[0;34m(self, mo_file, parent_element_name, config_file)\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39melements \u001b[38;5;241m=\u001b[39m {}\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrelationships \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m---> 28\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmodelica_path \u001b[38;5;241m=\u001b[39m \u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43menviron\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mMODELICAPATH\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msplit\u001b[49m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m:\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m1\u001b[39m]\n\u001b[1;32m 29\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mwithin \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n",
"\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'split'"
]
}
],
"source": [
"element_extractor = Element_Relationship_Extractor(config_file = \"config_boptest.json\");"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'element_extractor' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"Input \u001b[0;32mIn [6]\u001b[0m, in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0m elements, relationships \u001b[38;5;241m=\u001b[39m \u001b[43melement_extractor\u001b[49m\u001b[38;5;241m.\u001b[39mextract_class_definition()\n",
"\u001b[0;31mNameError\u001b[0m: name 'element_extractor' is not defined"
]
}
],
"source": [
"elements, relationships = element_extractor.extract_class_definition()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"brick_graph = brickschema.Graph(load_brick=True)\n",
"bldg_graph = brickschema.Graph()\n",
"BLDG = rdflib.Namespace(\"urn:bldg/\")\n",
"all_points = list(brick_graph.transitive_subjects(object=BRICK['Point'], predicate=RDFS['subClassOf']))\n",
"all_zones = list(brick_graph.transitive_subjects(object=BRICK['Zone'], predicate=RDFS['subClassOf']))\n",
"all_equipment = list(brick_graph.transitive_subjects(object=BRICK['Equipment'], predicate=RDFS['subClassOf']))\n",
"all_systems = list(brick_graph.transitive_subjects(object=BRICK['System'], predicate=RDFS['subClassOf']))"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def get_brick_type(semantic_info):\n",
" if semantic_info != \"\":\n",
" return BRICK[semantic_info.split(\" \")[-1].split(\":\")[1]]\n",
" return \"\"\n",
"\n",
"def get_brick_label(element, semantic_info, brick_type=None):\n",
" ## multiple elements in modelica can be refering to the same brick entity. ex: reaCor and overwriteCor, both refer to VAV \"cor\"\n",
" element_label = element.split(\".\")[-1]\n",
" if semantic_info != \"\" and \".\" in element:\n",
" brick_label_from_annotation = semantic_info.split(\" \")[0]\n",
" if element_label != brick_label_from_annotation:\n",
" brick_label = brick_label_from_annotation\n",
" else:\n",
" brick_label = element_label\n",
" brick_label = element.rsplit(\".\", 1)[0]+\".\"+brick_label\n",
" \n",
" brick_label = brick_label.replace(\".\", \"_\")\n",
"\n",
" # if point, add _u to the end of it --> BOPTEST Only\n",
" if brick_type in all_points:\n",
" brick_label = brick_label+\"_u\"\n",
" if brick_label == \"hvac_hvac\":\n",
" print(element, semantic_info)\n",
" return brick_label\n",
" \n",
" return element.replace(\".\", \"_\")\n",
"\n",
"def add_to_brick_graph(graph, relationship, from_element, to_element):\n",
" if relationship == \"feeds\":\n",
" inverse_relationship = \"isFedBy\"\n",
" elif relationship == \"hasPoint\":\n",
" inverse_relationship = \"isPointOf\"\n",
" elif relationship == \"hasPart\":\n",
" inverse_relationship = \"isPartOf\"\n",
" elif relationship == \"isLocationOf\":\n",
" inverse_relationship = \"hasLocation\"\n",
" else: \n",
" inverse_relationship = \"\"\n",
"\n",
" if from_element in graph:\n",
" if relationship in graph[from_element]:\n",
" if to_element not in graph[from_element][relationship]:\n",
" graph[from_element][relationship].append(to_element)\n",
" else:\n",
" graph[from_element][relationship] = [to_element]\n",
" else:\n",
" graph[from_element] = {relationship: [to_element]} \n",
" \n",
" if to_element in graph:\n",
" if inverse_relationship in graph[to_element]:\n",
" if from_element not in graph[to_element][inverse_relationship]:\n",
" graph[to_element][inverse_relationship].append(from_element)\n",
" else:\n",
" graph[to_element][inverse_relationship] = [from_element]\n",
" else:\n",
" graph[to_element] = {inverse_relationship: [from_element]} \n",
" \n",
" return graph"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'elements' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m graph \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m element \u001b[38;5;129;01min\u001b[39;00m \u001b[43melements\u001b[49m:\n\u001b[1;32m 4\u001b[0m type_specifier \u001b[38;5;241m=\u001b[39m elements[element][\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtype_specifier\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[1;32m 5\u001b[0m semantic \u001b[38;5;241m=\u001b[39m elements[element]\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124msemantic\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
"\u001b[0;31mNameError\u001b[0m: name 'elements' is not defined"
]
}
],
"source": [
"\n",
"graph = {}\n",
"\n",
"for element in elements:\n",
" type_specifier = elements[element]['type_specifier']\n",
" semantic = elements[element].get('semantic', '')\n",
" parent_brick_type = \"\"\n",
" if '.' in element:\n",
" parent = element.rsplit(\".\", 1)[0]\n",
" parent_semantic_info = elements[parent].get(\"semantic\")\n",
" parent_brick_type = get_brick_type(elements[parent].get(\"semantic\"))\n",
" parent_label = get_brick_label(parent, parent_semantic_info, parent_brick_type)\n",
"\n",
" if semantic != \"\":\n",
" brick_type = get_brick_type(semantic)\n",
" element_label = get_brick_label(element, semantic, brick_type)\n",
" \n",
" if element_label not in graph:\n",
" graph[element_label] = {\n",
" \"type\": brick_type\n",
" }\n",
" else:\n",
" graph[element_label][\"type\"] = brick_type\n",
" \n",
" bldg_graph.add((BLDG[element], rdflib.RDF.type, brick_type))\n",
" \n",
" if brick_type in all_points:\n",
" if parent_brick_type in all_equipment or parent_brick_type in all_zones or parent_brick_type in all_systems:\n",
" bldg_graph.add((BLDG[parent], BRICK['hasPoint'], element))\n",
" graph = add_to_brick_graph(graph=graph, relationship='hasPoint', from_element=parent_label, to_element=element_label)\n",
"\n",
" if brick_type in all_zones:\n",
" if parent_brick_type in all_zones:\n",
" bldg_graph.add((BLDG[parent], BRICK['hasPart'], element))\n",
" graph = add_to_brick_graph(graph=graph, relationship='hasPart', from_element=parent_label, to_element=element_label)\n",
" \n",
" if brick_type in all_equipment:\n",
" if parent_brick_type in all_equipment or parent_brick_type in all_systems:\n",
" bldg_graph.add((BLDG[parent], BRICK['hasPart'], element))\n",
" graph = add_to_brick_graph(graph=graph, relationship='hasPart', from_element=parent_label, to_element=element_label)\n",
" \n",
" if parent_brick_type in all_zones:\n",
" bldg_graph.add((BLDG[element], BRICK['hasLocation'], parent))\n",
" graph = add_to_brick_graph(graph=graph, relationship='isLocationOf', from_element=parent_label, to_element=element_label)\n",
" \n",
" if element in relationships:\n",
" from_element_og = element\n",
" from_element = element\n",
" else:\n",
" for fro in relationships:\n",
" if \".\" in fro and fro.rsplit(\".\", 1)[0] == element:\n",
" from_element_og = fro\n",
" from_element = fro.rsplit(\".\", 1)[0]\n",
" break\n",
" \n",
" for to_element_og in relationships[from_element_og]:\n",
" to_semantic = \"\"\n",
" to_element = None\n",
" if to_element_og in elements:\n",
" to_element = to_element_og\n",
" elif to_element_og.rsplit(\".\", 1)[0] in elements:\n",
" to_element = to_element_og.rsplit(\".\", 1)[0]\n",
" to_semantic = elements[to_element].get('semantic')\n",
" \n",
" if to_semantic != \"\":\n",
" to_brick_type = get_brick_type(to_semantic)\n",
" from_element_label = get_brick_label(from_element, semantic, brick_type)\n",
" to_element_label = get_brick_label(to_element, to_semantic, to_brick_type)\n",
" \n",
" if brick_type in all_equipment:\n",
" if to_brick_type in all_equipment:\n",
" if from_element_og != from_element and to_element_og != to_element:\n",
" from_port = from_element_og.rsplit(\".\", 1)[1]\n",
" to_port = to_element_og.rsplit(\".\", 1)[1]\n",
" \n",
" if (from_port.startswith(\"port_b\") or from_port.startswith(\"port2\") or from_port.startswith(\"portb\") or from_port.startswith(\"port_2\")) and \\\n",
" (to_port.startswith(\"port_a\") or to_port.startswith(\"port1\") or to_port.startswith(\"porta\") or to_port.startswith(\"port_1\")):\n",
" bldg_graph.add((BLDG[from_element], BRICK['feeds'], BLDG[to_element]))\n",
" \n",
" graph = add_to_brick_graph(graph=graph, relationship='feeds', from_element=from_element_label, to_element=to_element_label)\n",
" \n",
" if to_brick_type in all_points:\n",
" bldg_graph.add((from_element, BRICK['hasPoint'], to_element))\n",
" graph = add_to_brick_graph(graph=graph, relationship='hasPoint', from_element=from_element_label, to_element=to_element_label)\n",
" print(\"\\n\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"g = brickschema.Graph()\n",
"brickschema.bind_prefixes(g)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"for node in graph:\n",
" for relationship in graph.get(node):\n",
" if relationship == \"type\":\n",
" g_relationship = RDF['type']\n",
" g.add((rdflib.Literal(node), g_relationship, graph.get(node).get(relationship)))\n",
" else: \n",
" for obj in graph.get(node).get(relationship):\n",
" g.add((rdflib.Literal(node), BRICK[relationship], rdflib.Literal(obj)))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Graph identifier=Na769f0e65fb1426586ee7e5a1f475d7a (<class 'brickschema.graph.Graph'>)>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"g.serialize(destination='multizone_simple_air.ttl', format='turtle')"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"# brick_types = {}\n",
"# for element in a:\n",
"# type_specifier = a[element].get('type_specifier')\n",
"# brick_type = None\n",
" \n",
"# if type_specifier.startswith(\"Buildings.Fluid.Sensors\"):\n",
"# brick_type = modelica_brick_sensor_type_map.get(type_specifier.split('Buildings.Fluid.Sensors.')[1], None)\n",
"\n",
"# if type_specifier.startswith(\"Buildings.Fluid.HeatExchangers\"):\n",
"# brick_type = modelica_brick_heat_exchanger_type_map.get(type_specifier.split('Buildings.Fluid.HeatExchangers.')[1], None)\n",
" \n",
"# if type_specifier.startswith(\"Buildings.Fluid.Actuators\"):\n",
"# brick_type = modelica_brick_actuator_type_map.get(type_specifier.split('Buildings.Fluid.Actuators.')[1], None)\n",
" \n",
"# if type_specifier.startswith(\"Buildings.Fluid.Movers\"):\n",
"# brick_type = modelica_brick_mover_type_map.get(type_specifier.split('Buildings.Fluid.Movers.')[1], None)\n",
" \n",
"# if type_specifier.startswith(\"Buildings.ThermalZones\"):\n",
"# brick_type = modelica_brick_thermal_zone_type_map.get(type_specifier.split('Buildings.ThermalZones.')[1], None)\n",
" \n",
"# if type_specifier.startswith(\"Buildings.Media\"):\n",
"# brick_type = modelica_brick_medium_type_map.get(type_specifier.split('Buildings.Media.')[1], None)\n",
" \n",
"# if brick_type is not None:\n",
"# brick_types[element] = brick_type"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment