mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Add support for converting IFC spaces, systems, and feed / location / part relationships into Brick. Brick utilities can now get downstream equipment, and system util can now take into account port flow direction.
This commit is contained in:
@@ -91,11 +91,46 @@ def add_brick_relation(brick, brick_uri=None, predicate=None, object=None):
|
||||
|
||||
|
||||
def convert_ifc_to_brick(brick, namespace=None, library=None):
|
||||
spaces = brick.get_convertable_brick_spaces()
|
||||
space_uris = {}
|
||||
for space in spaces:
|
||||
brick_uri = brick.add_brick_from_element(space, namespace, brick.get_brick_class(space))
|
||||
space_uris[space] = brick_uri
|
||||
if library:
|
||||
brick.run_assign_brick_reference(element=space, library=library, brick_uri=brick_uri)
|
||||
|
||||
for space in spaces:
|
||||
parent = brick.get_parent_space(space)
|
||||
if parent:
|
||||
brick.add_relation(space_uris[parent], "hasPart", space_uris[space])
|
||||
|
||||
systems = brick.get_convertable_brick_systems()
|
||||
system_uris = {}
|
||||
for system in systems:
|
||||
brick_uri = brick.add_brick_from_element(system, namespace, brick.get_brick_class(system))
|
||||
space_uris[space] = brick_uri
|
||||
if library:
|
||||
brick.run_assign_brick_reference(element=system, library=library, brick_uri=brick_uri)
|
||||
|
||||
distribution_elements = brick.get_convertable_brick_elements()
|
||||
equipment_uris = {}
|
||||
for element in distribution_elements:
|
||||
brick_uri = brick.add_brick_from_element(element, namespace, brick.get_brick_class(element))
|
||||
equipment_uris[element] = brick_uri
|
||||
space = brick.get_element_container(element)
|
||||
brick.add_relation(brick_uri, "hasLocation", space_uris[space])
|
||||
systems = brick.get_element_systems(element)
|
||||
for system in systems:
|
||||
brick.add_relation(system_uris[system], "hasPart", brick_uri)
|
||||
if library:
|
||||
brick.run_assign_brick_reference(element=element, library=library, brick_uri=brick_uri)
|
||||
|
||||
distribution_elements = brick.get_convertable_brick_elements()
|
||||
for element in distribution_elements:
|
||||
feeds = brick.get_element_feeds(element)
|
||||
for downstream_equipment in feeds:
|
||||
brick.add_relation(equipment_uris[element], "feeds", equipment_uris[downstream_equipment])
|
||||
|
||||
brick.run_refresh_brick_viewer()
|
||||
brick.run_refresh_brick_viewer(split_screen=True)
|
||||
|
||||
@@ -141,4 +176,4 @@ def set_brick_list_root(brick, brick_root=None, split_screen=False):
|
||||
|
||||
def remove_brick_relation(brick, brick_uri=None, predicate=None, object=None):
|
||||
brick.remove_relation(brick_uri, predicate, object)
|
||||
brick.run_refresh_brick_viewer()
|
||||
brick.run_refresh_brick_viewer()
|
||||
|
||||
@@ -96,7 +96,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
cs.add((URIRef(brick), REF.hasExternalReference, bnode))
|
||||
cs.add((bnode, A, REF.IFCReference))
|
||||
cs.add((bnode, REF.hasIfcProjectReference, URIRef(project)))
|
||||
cs.add((bnode, REF.ifcGlobalID , Literal(element.GlobalId)))
|
||||
cs.add((bnode, REF.ifcGlobalID, Literal(element.GlobalId)))
|
||||
if element.Name:
|
||||
cs.add((bnode, REF.ifcName, Literal(element.Name)))
|
||||
|
||||
@@ -108,11 +108,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
bpy.context.scene.BIMBrickProperties.new_brick_relation_type = BrickStore.relationships[0][0]
|
||||
bpy.context.scene.BIMBrickProperties.add_relation_failed = False
|
||||
return
|
||||
query = BrickStore.graph.query(
|
||||
"ASK { <{object_uri}> a ?o . }".replace(
|
||||
"{object_uri}", object
|
||||
)
|
||||
)
|
||||
query = BrickStore.graph.query("ASK { <{object_uri}> a ?o . }".replace("{object_uri}", object))
|
||||
if query:
|
||||
with BrickStore.new_changeset() as cs:
|
||||
cs.add((URIRef(brick_uri), URIRef(predicate), URIRef(object)))
|
||||
@@ -200,7 +196,41 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
|
||||
@classmethod
|
||||
def get_convertable_brick_elements(cls):
|
||||
return ifcopenshell.util.brick.get_brick_elements(tool.Ifc.get())
|
||||
equipment = set(tool.Ifc.get().by_type("IfcDistributionElement"))
|
||||
equipment -= set(tool.Ifc.get().by_type("IfcFlowSegment"))
|
||||
equipment -= set(tool.Ifc.get().by_type("IfcFlowFitting"))
|
||||
return equipment
|
||||
|
||||
@classmethod
|
||||
def get_convertable_brick_spaces(cls):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
||||
return tool.Ifc.get().by_type("IfcSpatialElement")
|
||||
|
||||
@classmethod
|
||||
def get_convertable_brick_systems(cls):
|
||||
systems = set(tool.Ifc.get().by_type("IfcSystem"))
|
||||
systems -= set(tool.Ifc.get().by_type("IfcStructuralAnalysisModel"))
|
||||
systems -= set(tool.Ifc.get().by_type("IfcZone"))
|
||||
return systems
|
||||
|
||||
@classmethod
|
||||
def get_parent_space(cls, space):
|
||||
element = ifcopenshell.util.element.get_aggregate(space)
|
||||
if not element.is_a("IfcProject"):
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def get_element_container(cls, element):
|
||||
return ifcopenshell.util.element.get_container(element)
|
||||
|
||||
@classmethod
|
||||
def get_element_systems(cls, element):
|
||||
return ifcopenshell.util.system.get_element_systems(element)
|
||||
|
||||
@classmethod
|
||||
def get_element_feeds(cls, element):
|
||||
return ifcopenshell.util.brick.get_element_feeds(element)
|
||||
|
||||
@classmethod
|
||||
def get_item_class(cls, item):
|
||||
@@ -389,7 +419,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
def add_namespace(cls, alias, uri):
|
||||
BrickStore.graph.bind(alias, Namespace(uri))
|
||||
BrickStore.load_namespaces()
|
||||
|
||||
|
||||
@classmethod
|
||||
def clear_breadcrumbs(cls, split_screen=False):
|
||||
if split_screen:
|
||||
@@ -403,6 +433,7 @@ class Brick(blenderbim.core.tool.Brick):
|
||||
save = datetime.datetime.fromtimestamp(save)
|
||||
BrickStore.last_saved = f"{save.year}-{save.month}-{save.day} {save.hour}:{save.minute}"
|
||||
|
||||
|
||||
class BrickStore:
|
||||
schema = None # this is now a os path
|
||||
path = None # file path if the project was loaded in
|
||||
@@ -432,7 +463,7 @@ class BrickStore:
|
||||
@classmethod
|
||||
def get_project(cls):
|
||||
return BrickStore.graph.graph_at(graph="PROJECT")
|
||||
|
||||
|
||||
@classmethod
|
||||
def load_sub_roots(cls):
|
||||
query = BrickStore.graph.query(
|
||||
@@ -462,7 +493,18 @@ class BrickStore:
|
||||
@classmethod
|
||||
def load_namespaces(cls):
|
||||
BrickStore.namespaces = []
|
||||
keyword_filter = ["brickschema.org", "schema.org", "w3.org", "purl.org", "rdfs.org", "qudt.org", "ashrae.org", "usefulinc.com", "xmlns.com", "opengis.net"]
|
||||
keyword_filter = [
|
||||
"brickschema.org",
|
||||
"schema.org",
|
||||
"w3.org",
|
||||
"purl.org",
|
||||
"rdfs.org",
|
||||
"qudt.org",
|
||||
"ashrae.org",
|
||||
"usefulinc.com",
|
||||
"xmlns.com",
|
||||
"opengis.net",
|
||||
]
|
||||
for alias, uri in BrickStore.graph.namespaces():
|
||||
ignore_namespace = False
|
||||
for keyword in keyword_filter:
|
||||
@@ -484,7 +526,7 @@ class BrickStore:
|
||||
?class rdfs:subClassOf* brick:{root_class} .
|
||||
FILTER NOT EXISTS {
|
||||
?class owl:deprecated true .
|
||||
}
|
||||
}
|
||||
}
|
||||
""".replace(
|
||||
"{root_class}", root_class
|
||||
|
||||
@@ -20,6 +20,7 @@ import os
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.classification
|
||||
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -31,6 +32,11 @@ with open(os.path.join(cwd, "ifc4_to_brick.json")) as f:
|
||||
|
||||
|
||||
def get_brick_type(element):
|
||||
references = ifcopenshell.util.classification.get_references(element)
|
||||
for reference in references:
|
||||
system = ifcopenshell.util.classification.get_classification(reference)
|
||||
if system.Name == "Brick":
|
||||
return reference.Location
|
||||
result = None
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
if predefined_type:
|
||||
@@ -46,12 +52,50 @@ def get_brick_type(element):
|
||||
result = ifc4_to_brick_map.get(ifc_type_class, None)
|
||||
if result:
|
||||
return f"https://brickschema.org/schema/Brick#{result}"
|
||||
# We choose equipment as a generic fallback
|
||||
return f"https://brickschema.org/schema/Brick#Equipment"
|
||||
# Generic fallback
|
||||
if element.is_a("IfcDistributionElement"):
|
||||
return f"https://brickschema.org/schema/Brick#Equipment"
|
||||
elif element.is_a("IfcSpatialElement") or element.is_a("IfcSpatialStructureElement"):
|
||||
return f"https://brickschema.org/schema/Brick#Location"
|
||||
elif element.is_a("IfcSystem"):
|
||||
return f"https://brickschema.org/schema/Brick#System"
|
||||
|
||||
|
||||
def get_brick_elements(ifc_file):
|
||||
elements = set(ifc_file.by_type("IfcDistributionElement"))
|
||||
elements = elements.difference(ifc_file.by_type("IfcFlowSegment"))
|
||||
elements = elements.difference(ifc_file.by_type("IfcFlowFitting"))
|
||||
return elements
|
||||
def get_element_feeds(element):
|
||||
current_element = element
|
||||
processed_elements = set()
|
||||
downstream_equipment = set()
|
||||
|
||||
# A queue is a list of branches. A branch is a list of elements in
|
||||
# sequence, each one connecting to another element. An element in a
|
||||
# branch may have a child queue. The queue and child queues are
|
||||
# acyclic.
|
||||
|
||||
def extend_branch(element, branch, predecessor=None):
|
||||
processed_elements.add(element)
|
||||
branch_element = {"element": element, "children": [], "predecessor": predecessor}
|
||||
branch.append(branch_element)
|
||||
|
||||
connected = {
|
||||
e
|
||||
for e in ifcopenshell.util.system.get_connected_to(element, flow_direction="SOURCE")
|
||||
if e not in processed_elements
|
||||
}
|
||||
connected.update(
|
||||
[
|
||||
e
|
||||
for e in ifcopenshell.util.system.get_connected_from(element, flow_direction="SOURCE")
|
||||
if e not in processed_elements
|
||||
]
|
||||
)
|
||||
|
||||
for connected_element in connected:
|
||||
if connected_element.is_a("IfcFlowFitting") or connected_element.is_a("IfcFlowSegment"):
|
||||
branch_element["children"].append(extend_branch(connected_element, [], element))
|
||||
else:
|
||||
downstream_equipment.add(connected_element)
|
||||
|
||||
return branch
|
||||
|
||||
extended_branch = extend_branch(current_element, [])
|
||||
return downstream_equipment
|
||||
|
||||
@@ -67,13 +67,21 @@ def get_element_systems(element):
|
||||
return results
|
||||
|
||||
|
||||
def get_ports(element):
|
||||
def get_ports(element, flow_direction=None):
|
||||
results = []
|
||||
for rel in getattr(element, "IsNestedBy", []) or []:
|
||||
results.extend([o for o in rel.RelatedObjects if o.is_a("IfcDistributionPort")])
|
||||
for port in rel.RelatedObjects:
|
||||
if not port.is_a("IfcDistributionPort"):
|
||||
continue
|
||||
if flow_direction and port.FlowDirection != flow_direction:
|
||||
continue
|
||||
results.append(port)
|
||||
# IFC2X3 only, deprecated in IFC4
|
||||
for rel in getattr(element, "HasPorts", []) or []:
|
||||
results.append(rel.RelatingPort)
|
||||
port = rel.RelatingPort
|
||||
if flow_direction and port.FlowDirection != flow_direction:
|
||||
continue
|
||||
results.append(port)
|
||||
return results
|
||||
|
||||
|
||||
@@ -84,9 +92,9 @@ def get_connected_port(port):
|
||||
return rel.RelatingPort
|
||||
|
||||
|
||||
def get_connected_to(element):
|
||||
def get_connected_to(element, flow_direction=None):
|
||||
results = []
|
||||
for port in ifcopenshell.util.system.get_ports(element):
|
||||
for port in ifcopenshell.util.system.get_ports(element, flow_direction=flow_direction):
|
||||
for relConnectsPort in port.ConnectedTo:
|
||||
for disPort in [relConnectsPort.RelatedPort, relConnectsPort.RelatingPort]:
|
||||
if hasattr(disPort, "Nests"):
|
||||
@@ -101,9 +109,9 @@ def get_connected_to(element):
|
||||
return results
|
||||
|
||||
|
||||
def get_connected_from(element):
|
||||
def get_connected_from(element, flow_direction=None):
|
||||
results = []
|
||||
for port in ifcopenshell.util.system.get_ports(element):
|
||||
for port in ifcopenshell.util.system.get_ports(element, flow_direction=flow_direction):
|
||||
for relConnectsPort in port.ConnectedFrom:
|
||||
for disPort in [relConnectsPort.RelatedPort, relConnectsPort.RelatingPort]:
|
||||
if hasattr(disPort, "Nests"):
|
||||
|
||||
Reference in New Issue
Block a user