ifcopenshell.util.element optimization

Tried to reduce amount of IFC attributes retrieved by reusing already retrieved values.

E.g. previously get_nest() was retrieving same attribute 3 times on each call, now it does it only once and it's 2 times faster (2.4 ms -> 1.2 ms).
This commit is contained in:
Andrej730
2024-04-09 18:36:55 +05:00
parent ede278db8f
commit 9fe5e75c27
@@ -75,12 +75,12 @@ def get_pset(
if definition.Name == name: if definition.Name == name:
pset = definition pset = definition
break break
elif hasattr(element, "IsDefinedBy"): elif (is_defined_by := getattr(element, "IsDefinedBy", None)) is not None:
if should_inherit: if should_inherit:
element_type = ifcopenshell.util.element.get_type(element) element_type = ifcopenshell.util.element.get_type(element)
if element_type: if element_type:
type_pset = get_pset(element_type, name, prop, should_inherit=False, verbose=verbose) type_pset = get_pset(element_type, name, prop, should_inherit=False, verbose=verbose)
for relationship in element.IsDefinedBy: for relationship in is_defined_by:
if relationship.is_a("IfcRelDefinesByProperties"): if relationship.is_a("IfcRelDefinesByProperties"):
definition = relationship.RelatingPropertyDefinition definition = relationship.RelatingPropertyDefinition
if definition.Name == name: if definition.Name == name:
@@ -157,12 +157,12 @@ def get_psets(
if qtos_only: if qtos_only:
continue continue
psets[definition.Name] = get_property_definition(definition, verbose=verbose) psets[definition.Name] = get_property_definition(definition, verbose=verbose)
elif hasattr(element, "IsDefinedBy"): elif (is_defined_by := getattr(element, "IsDefinedBy", None)) is not None:
if should_inherit: if should_inherit:
element_type = ifcopenshell.util.element.get_type(element) element_type = ifcopenshell.util.element.get_type(element)
if element_type: if element_type:
psets = get_psets(element_type, psets_only=psets_only, qtos_only=qtos_only, should_inherit=False) psets = get_psets(element_type, psets_only=psets_only, qtos_only=qtos_only, should_inherit=False)
for relationship in element.IsDefinedBy: for relationship in is_defined_by:
if relationship.is_a("IfcRelDefinesByProperties"): if relationship.is_a("IfcRelDefinesByProperties"):
definition = relationship.RelatingPropertyDefinition definition = relationship.RelatingPropertyDefinition
if psets_only and not definition.is_a("IfcPropertySet"): if psets_only and not definition.is_a("IfcPropertySet"):
@@ -345,9 +345,12 @@ def get_predefined_type(element: ifcopenshell.entity_instance) -> str:
if element_type: if element_type:
predefined_type = getattr(element_type, "PredefinedType", None) predefined_type = getattr(element_type, "PredefinedType", None)
if predefined_type == "USERDEFINED" or not predefined_type: if predefined_type == "USERDEFINED" or not predefined_type:
predefined_type = getattr(element_type, "ElementType", getattr(element_type, "ProcessType", None)) predefined_type = getattr(element_type, "ElementType", ...)
if predefined_type == ...:
predefined_type = getattr(element_type, "ProcessType", None)
if predefined_type and predefined_type != "NOTDEFINED": if predefined_type and predefined_type != "NOTDEFINED":
return predefined_type return predefined_type
predefined_type = getattr(element, "PredefinedType", None) predefined_type = getattr(element, "PredefinedType", None)
if predefined_type == "USERDEFINED" or not predefined_type: if predefined_type == "USERDEFINED" or not predefined_type:
predefined_type = getattr(element, "ObjectType", None) predefined_type = getattr(element, "ObjectType", None)
@@ -371,10 +374,10 @@ def get_type(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_insta
""" """
if element.is_a("IfcTypeObject"): if element.is_a("IfcTypeObject"):
return element return element
elif hasattr(element, "IsTypedBy") and element.IsTypedBy: elif (is_typed_by := getattr(element, "IsTypedBy", None)) is not None and is_typed_by:
return element.IsTypedBy[0].RelatingType return is_typed_by[0].RelatingType
elif hasattr(element, "IsDefinedBy") and element.IsDefinedBy: # IFC2X3 elif (is_defined_by := getattr(element, "IsDefinedBy", None)) is not None and is_defined_by: # IFC2X3
for relationship in element.IsDefinedBy: for relationship in is_defined_by:
if relationship.is_a("IfcRelDefinesByType"): if relationship.is_a("IfcRelDefinesByType"):
return relationship.RelatingType return relationship.RelatingType
@@ -418,8 +421,8 @@ def get_shape_aspects(element: ifcopenshell.entity_instance) -> list[ifcopenshel
""" """
# IfcProduct # IfcProduct
if hasattr(element, "Representation"): if (representation := getattr(element, "Representation", ...)) != ...:
return element.Representation.HasShapeAspects return representation.HasShapeAspects
# IfcTypeProduct # IfcTypeProduct
shape_aspects = [] shape_aspects = []
@@ -456,18 +459,19 @@ def get_material(
element = ifcopenshell.by_type("IfcWall")[0] element = ifcopenshell.by_type("IfcWall")[0]
material = ifcopenshell.util.element.get_material(element) material = ifcopenshell.util.element.get_material(element)
""" """
if hasattr(element, "HasAssociations") and element.HasAssociations: if (has_associations := getattr(element, "HasAssociations", None)) is not None and has_associations:
for relationship in element.HasAssociations: for relationship in has_associations:
if relationship.is_a("IfcRelAssociatesMaterial"): if relationship.is_a("IfcRelAssociatesMaterial"):
if should_skip_usage: if should_skip_usage:
if relationship.RelatingMaterial.is_a("IfcMaterialLayerSetUsage"): relating_material = relationship.RelatingMaterial
return relationship.RelatingMaterial.ForLayerSet if relating_material.is_a("IfcMaterialLayerSetUsage"):
elif relationship.RelatingMaterial.is_a("IfcMaterialProfileSetUsage"): return relating_material.ForLayerSet
return relationship.RelatingMaterial.ForProfileSet elif relating_material.is_a("IfcMaterialProfileSetUsage"):
return relating_material.ForProfileSet
return relationship.RelatingMaterial return relationship.RelatingMaterial
if should_inherit: if should_inherit:
relating_type = get_type(element) relating_type = get_type(element)
if relating_type != element and hasattr(relating_type, "HasAssociations") and relating_type.HasAssociations: if relating_type != element and (has_associations := getattr(relating_type, "HasAssociations", None)):
return get_material(relating_type, should_skip_usage) return get_material(relating_type, should_skip_usage)
@@ -717,10 +721,10 @@ def get_layers(
""" """
layers = [] layers = []
representations = [] representations = []
if getattr(element, "Representation", None): if representation := getattr(element, "Representation", None):
representations = [element.Representation] representations = [representation]
elif getattr(element, "RepresentationMaps", None): elif representation_maps := getattr(element, "RepresentationMaps", None):
representations = element.RepresentationMaps representations = representation_maps
for representation in representations: for representation in representations:
for subelement in ifc_file.traverse(representation): for subelement in ifc_file.traverse(representation):
if subelement.is_a("IfcShapeRepresentation"): if subelement.is_a("IfcShapeRepresentation"):
@@ -761,8 +765,10 @@ def get_container(
container = ifcopenshell.util.element.get_container(element) container = ifcopenshell.util.element.get_container(element)
""" """
if should_get_direct: if should_get_direct:
if hasattr(element, "ContainedInStructure") and element.ContainedInStructure: if (
container = element.ContainedInStructure[0].RelatingStructure contained_in_structure := getattr(element, "ContainedInStructure", None)
) is not None and contained_in_structure:
container = contained_in_structure[0].RelatingStructure
if not ifc_class: if not ifc_class:
return container return container
if container.is_a(ifc_class): if container.is_a(ifc_class):
@@ -774,8 +780,10 @@ def get_container(
nest = get_nest(element) nest = get_nest(element)
if nest: if nest:
return get_container(nest, should_get_direct) return get_container(nest, should_get_direct)
if hasattr(element, "ContainedInStructure") and element.ContainedInStructure: if (
container = element.ContainedInStructure[0].RelatingStructure contained_in_structure := getattr(element, "ContainedInStructure", None)
) is not None and contained_in_structure:
container = contained_in_structure[0].RelatingStructure
if not ifc_class: if not ifc_class:
return container return container
while container: while container:
@@ -803,9 +811,7 @@ def get_referenced_structures(element: ifcopenshell.entity_instance) -> list[ifc
element = file.by_type("IfcWall")[0] element = file.by_type("IfcWall")[0]
print(ifcopenshell.util.element.get_referenced_structures(element)) print(ifcopenshell.util.element.get_referenced_structures(element))
""" """
if hasattr(element, "ReferencedInStructures"): return [r.RelatingStructure for r in getattr(element, "ReferencedInStructures", [])]
return [r.RelatingStructure for r in element.ReferencedInStructures]
return []
def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) -> list[ifcopenshell.entity_instance]: def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True) -> list[ifcopenshell.entity_instance]:
@@ -831,20 +837,25 @@ def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True)
while queue: while queue:
element = queue.pop() element = queue.pop()
for rel in getattr(element, "ContainsElements", []): for rel in getattr(element, "ContainsElements", []):
queue.extend(rel.RelatedElements) related = rel.RelatedElements
results.extend(rel.RelatedElements) queue.extend(related)
results.extend(related)
for rel in getattr(element, "IsDecomposedBy", []): for rel in getattr(element, "IsDecomposedBy", []):
queue.extend(rel.RelatedObjects) related = rel.RelatedObjects
results.extend(rel.RelatedObjects) queue.extend(related)
results.extend(related)
for rel in getattr(element, "HasOpenings", []): for rel in getattr(element, "HasOpenings", []):
queue.append(rel.RelatedOpeningElement) related = rel.RelatedOpeningElement
results.append(rel.RelatedOpeningElement) queue.append(related)
results.append(related)
for rel in getattr(element, "HasFillings", []): for rel in getattr(element, "HasFillings", []):
queue.append(rel.RelatedBuildingElement) related = rel.RelatedBuildingElement
results.append(rel.RelatedBuildingElement) queue.append(related)
results.append(related)
for rel in getattr(element, "IsNestedBy", []): for rel in getattr(element, "IsNestedBy", []):
queue.extend(rel.RelatedObjects) related = rel.RelatedObjects
results.extend(rel.RelatedObjects) queue.extend(related)
results.extend(related)
if not is_recursive: if not is_recursive:
break break
return results return results
@@ -870,8 +881,9 @@ def get_grouped_by(element: ifcopenshell.entity_instance) -> list[ifcopenshell.e
while queue: while queue:
element = queue.pop() element = queue.pop()
for rel in getattr(element, "IsGroupedBy", []): for rel in getattr(element, "IsGroupedBy", []):
queue.extend(rel.RelatedObjects) related_objects = rel.RelatedObjects
results.extend(rel.RelatedObjects) queue.extend(related_objects)
results.extend(related_objects)
return results return results
@@ -891,9 +903,9 @@ def get_aggregate(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_
element = file.by_type("IfcBeam")[0] element = file.by_type("IfcBeam")[0]
aggregate = ifcopenshell.util.element.get_aggregate(element) aggregate = ifcopenshell.util.element.get_aggregate(element)
""" """
if hasattr(element, "Decomposes") and element.Decomposes: if decomposes := getattr(element, "Decomposes", None):
if element.Decomposes[0].is_a("IfcRelAggregates"): # IFC2X3 if decomposes[0].is_a("IfcRelAggregates"): # IFC2X3
return element.Decomposes[0].RelatingObject return decomposes[0].RelatingObject
def get_nest(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: def get_nest(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
@@ -912,12 +924,12 @@ def get_nest(element: ifcopenshell.entity_instance) -> ifcopenshell.entity_insta
element = file.by_type("IfcBeam")[0] element = file.by_type("IfcBeam")[0]
aggregate = ifcopenshell.util.element.get_nest(element) aggregate = ifcopenshell.util.element.get_nest(element)
""" """
if hasattr(element, "Nests"): if (nests := getattr(element, "Nests", None)) is not None:
if element.Nests: if nests:
return element.Nests[0].RelatingObject return nests[0].RelatingObject
elif hasattr(element, "Decomposes") and element.Decomposes: # IFC2X3 elif (decomposes := getattr(element, "Decomposes", None)) is not None and decomposes: # IFC2X3
if element.Decomposes[0].is_a("IfcRelNests"): if decomposes[0].is_a("IfcRelNests"):
return element.Decomposes[0].RelatingObject return decomposes[0].RelatingObject
def get_parts(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: def get_parts(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
@@ -936,9 +948,9 @@ def get_parts(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity
element = file.by_type("IfcElementAssembly")[0] element = file.by_type("IfcElementAssembly")[0]
parts = ifcopenshell.util.element.get_parts(element) parts = ifcopenshell.util.element.get_parts(element)
""" """
if hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy: if (is_decomposed_by := getattr(element, "IsDecomposedBy", None)) is not None and is_decomposed_by:
if element.IsDecomposedBy[0].is_a("IfcRelAggregates"): if is_decomposed_by[0].is_a("IfcRelAggregates"):
return element.IsDecomposedBy[0].RelatedObjects return is_decomposed_by[0].RelatedObjects
def get_components(element: ifcopenshell.entity_instance, include_ports=False) -> list[ifcopenshell.entity_instance]: def get_components(element: ifcopenshell.entity_instance, include_ports=False) -> list[ifcopenshell.entity_instance]:
@@ -960,14 +972,14 @@ def get_components(element: ifcopenshell.entity_instance, include_ports=False) -
element = file.by_type("IfcElementAssembly")[0] element = file.by_type("IfcElementAssembly")[0]
components = ifcopenshell.util.element.get_components(element) components = ifcopenshell.util.element.get_components(element)
""" """
if hasattr(element, "IsNestedBy"): if (is_nested_by := getattr(element, "IsNestedBy", None)) is not None:
if element.IsNestedBy: if is_nested_by:
if include_ports: if include_ports:
return element.IsNestedBy[0].RelatedObjects return is_nested_by[0].RelatedObjects
return [e for e in element.IsNestedBy[0].RelatedObjects if not e.is_a("IfcPort")] return [e for e in is_nested_by[0].RelatedObjects if not e.is_a("IfcPort")]
elif hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy: elif (is_decomposed_by := getattr(element, "IsDecomposedBy", None)) is not None and is_decomposed_by:
if element.IsDecomposedBy[0].is_a("IfcRelNests"): if is_decomposed_by[0].is_a("IfcRelNests"):
return element.IsDecomposedBy[0].RelatedObjects return is_decomposed_by[0].RelatedObjects
def replace_attribute(element: ifcopenshell.entity_instance, old: Any, new: Any) -> None: def replace_attribute(element: ifcopenshell.entity_instance, old: Any, new: Any) -> None: