diff --git a/src/blenderbim/blenderbim/core/spatial.py b/src/blenderbim/blenderbim/core/spatial.py index 4532b86dba..171642900a 100644 --- a/src/blenderbim/blenderbim/core/spatial.py +++ b/src/blenderbim/blenderbim/core/spatial.py @@ -42,7 +42,7 @@ def dereference_structure( element: Optional[ifcopenshell.entity_instance] = None, ) -> None: if spatial.can_reference(structure, element): - return ifc.run("spatial.dereference_structure", product=element, relating_structure=structure) + return ifc.run("spatial.dereference_structure", products=[element], relating_structure=structure) def assign_container( diff --git a/src/blenderbim/test/core/test_spatial.py b/src/blenderbim/test/core/test_spatial.py index 35282397fa..9e01aefde4 100644 --- a/src/blenderbim/test/core/test_spatial.py +++ b/src/blenderbim/test/core/test_spatial.py @@ -30,7 +30,7 @@ class TestReferenceStructure: class TestDereferenceStructure: def test_run(self, ifc, spatial): spatial.can_reference("structure", "element").should_be_called().will_return(True) - ifc.run("spatial.dereference_structure", product="element", relating_structure="structure").should_be_called() + ifc.run("spatial.dereference_structure", products=["element"], relating_structure="structure").should_be_called() subject.dereference_structure(ifc, spatial, structure="structure", element="element") diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 0a77fe5da7..7bc04bc3ca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -108,6 +108,9 @@ ARGUMENTS_DEPRECATION = { "spatial.reference_structure": partial( batching_argument_deprecation, prev_argument="product", new_argument="products" ), + "spatial.dereference_structure": partial( + batching_argument_deprecation, prev_argument="product", new_argument="products" + ), } diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index a9d4a1ad83..00cb94d8b3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -22,11 +22,16 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, product=None, relating_structure=None): - """Dereferences the a product and space + def __init__( + self, + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, + ): + """Dereferences a list of products and space - :param product: The physical IfcElement that exists in the space. - :type product: ifcopenshell.entity_instance.entity_instance + :param products: The list of physical IfcElements that exists in the space. + :type products: list[ifcopenshell.entity_instance.entity_instance] :param relating_structure: The IfcSpatialStructureElement element, such as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element exists in. @@ -63,19 +68,20 @@ class Usecase: ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3) # Actually, it only goes up to storey 2. - ifcopenshell.api.run("spatial.dereference_structure", model, product=column, relating_structure=storey3) + ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3) """ self.file = file - self.settings = {"product": product, "relating_structure": relating_structure} + self.settings = {"products": products, "relating_structure": relating_structure} - def execute(self): - for rel in self.settings["product"].ReferencedInStructures: - if rel.RelatingStructure != self.settings["relating_structure"]: + def execute(self) -> None: + products = set(self.settings["products"]) + for rel in self.settings["relating_structure"].ReferencesElements: + related_elements = set(rel.RelatedElements) + if not related_elements.intersection(products): continue - related_elements = list(rel.RelatedElements) - related_elements.remove(self.settings["product"]) + related_elements = related_elements - products if related_elements: - rel.RelatedElements = related_elements + rel.RelatedElements = list(related_elements) ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) else: history = rel.OwnerHistory diff --git a/src/ifcopenshell-python/test/api/spatial/test_dereference_structure.py b/src/ifcopenshell-python/test/api/spatial/test_dereference_structure.py index d4d963222f..474f8bbcaf 100644 --- a/src/ifcopenshell-python/test/api/spatial/test_dereference_structure.py +++ b/src/ifcopenshell-python/test/api/spatial/test_dereference_structure.py @@ -26,28 +26,42 @@ class TestDereferenceStructure(test.bootstrap.IFC4): def test_removing_a_container(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") - ifcopenshell.api.run("spatial.reference_structure", self.file, products=[subelement], relating_structure=element) - ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element) + subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + ifcopenshell.api.run( + "spatial.reference_structure", self.file, products=[subelement, subelement2], relating_structure=element + ) + ifcopenshell.api.run( + "spatial.dereference_structure", self.file, products=[subelement, subelement2], relating_structure=element + ) assert ifcopenshell.util.element.get_referenced_structures(subelement) == [] + assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0 def test_doing_nothing_if_no_container(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") - ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element) + subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + ifcopenshell.api.run( + "spatial.dereference_structure", self.file, products=[subelement, subelement2], relating_structure=element + ) assert ifcopenshell.util.element.get_referenced_structures(subelement) == [] + assert ifcopenshell.util.element.get_referenced_structures(subelement2) == [] def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") - ifcopenshell.api.run("spatial.reference_structure", self.file, products=[subelement1], relating_structure=element) - ifcopenshell.api.run("spatial.reference_structure", self.file, products=[subelement2], relating_structure=element) - ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement1, relating_structure=element) - assert self.file.by_type("IfcRelReferencedInSpatialStructure")[0].RelatedElements == (subelement2,) + subelement3 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + ifcopenshell.api.run( + "spatial.reference_structure", self.file, products=[subelement1], relating_structure=element + ) + ifcopenshell.api.run( + "spatial.reference_structure", self.file, products=[subelement2, subelement3], relating_structure=element + ) + ifcopenshell.api.run( + "spatial.dereference_structure", self.file, products=[subelement1, subelement2], relating_structure=element + ) + assert element.ReferencesElements[0].RelatedElements == (subelement3,) - def test_deleting_the_rel_when_a_container_is_removed_with_no_elements(self): - element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") - ifcopenshell.api.run("spatial.reference_structure", self.file, products=[subelement], relating_structure=element) - ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element) - assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0 + +class TestDereferenceStructureIFC2X3(test.bootstrap.IFC2X3, TestDereferenceStructure): + pass diff --git a/src/ifcopenshell-python/test/api/test_api.py b/src/ifcopenshell-python/test/api/test_api.py index e7cfeaf85f..1615062cc2 100644 --- a/src/ifcopenshell-python/test/api/test_api.py +++ b/src/ifcopenshell-python/test/api/test_api.py @@ -280,3 +280,11 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4): ) assert ifcopenshell.util.element.get_referenced_structures(subelement) == [element] assert rel.is_a("IfcRelReferencedInSpatialStructure") + + @deprecation_check + def test_removing_a_container(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") + subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + ifcopenshell.api.run("spatial.reference_structure", self.file, products=[subelement], relating_structure=element) + ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element) + assert ifcopenshell.util.element.get_referenced_structures(subelement) == []