diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py index a4f4f09b93..0f36891a28 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py @@ -183,6 +183,10 @@ class Usecase: if self.occurrence_class: ifc_class_ = self.occurrence_class else: + # NOTE: in theory we can skip reassignment in IFC2X3 in some cases + # e.g. if occurrence is IfcRoof and we're reassigning to IfcBuildingElementProxyType + # but currently type_to_entity_map doesn't completely match entity_to_type_map, + # see type.py for more details. ifc_class_ = next(iter(ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema))) self.reassign_class(occurrence, ifc_class_, predefined_type) else: diff --git a/src/ifcopenshell-python/ifcopenshell/util/type.py b/src/ifcopenshell-python/ifcopenshell/util/type.py index 8ff0fe1a3c..ab58814267 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/type.py +++ b/src/ifcopenshell-python/ifcopenshell/util/type.py @@ -43,7 +43,18 @@ for schema in mapped_schemas: type_to_entity_map[schema].setdefault(element_type, []).append(element) if schema == "IFC2X3": + # Prioritize IfcBuildingElementProxyType if it's available as it seems to be the most generic type. + # Otherwise classes that don't have a special type in IFC2X3 (e.g. IfcBuildingElementPart, IfcRoof) + # have IfcBeamType as their first matching type, which can be confusing. + for occurrence_type, element_types in entity_to_type_map[schema].items(): + if "IfcBuildingElementProxyType" in element_types: + element_types.sort(key=lambda x: x == "IfcBuildingElementProxyType", reverse=True) + # There is no official mapping for IFC2X3 but this method gets us something that looks correct + # + # NOTE: currently `type_to_entity_map` in IFC2X3 doesn't completely match `entity_to_type_map`, + # e.g. `get_applicabl_types(IfcRoof)` returns `[IfcBuildingElementProxyType, IfcBeamType, ...]` + # but `get_applicable_entities(IfcBuildingElementProxyType)` returns `[IfcBuildingElementProxy`]. for element_type, elements in type_to_entity_map[schema].items(): # need to take both Type (4 symbols) and Style (5 symbols) into account guessed_element = element_type[:-5] if element_type.endswith("Style") else element_type[:-4]