From 5e5de0bbd838299a709d6e744a460b66018cab8f Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 12 Feb 2024 11:26:13 +0500 Subject: [PATCH] root.remove_product - fix some cases producing invalid ifc #4313 always remove IfcRelNests 1) if product is RelatingObject 2) if product is the only RelatedObject --- .../ifcopenshell/api/root/remove_product.py | 16 +++++++++++++- .../test/api/root/test_remove_product.py | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py index 4d0ee047be..d5a78bdf27 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py @@ -104,6 +104,13 @@ class Usecase: ): ifcopenshell.api.run("grid.remove_grid_axis", self.file, axis=axis) + def element_exists(element_id): + try: + self.file.by_id(element_id) + return True + except RuntimeError: + return False + # TODO: remove object placement and other relationships for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["product"])]: try: @@ -144,14 +151,21 @@ class Usecase: ifcopenshell.util.element.remove_deep2(self.file, history) elif inverse.is_a("IfcRelNests"): if inverse.RelatingObject == self.settings["product"]: + inverse_id = inverse.id() for subelement in inverse.RelatedObjects: if subelement.is_a("IfcDistributionPort"): ifcopenshell.api.run("root.remove_product", self.file, product=subelement) - if not inverse.RelatedObjects: + # IfcRelNests could have been already deleted after removing one of the products + if element_exists(inverse_id): history = inverse.OwnerHistory self.file.remove(inverse) if history: ifcopenshell.util.element.remove_deep2(self.file, history) + elif inverse.RelatedObjects == (self.settings["product"],): + history = inverse.OwnerHistory + self.file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(self.file, history) elif inverse.is_a("IfcRelAggregates"): if inverse.RelatingObject == self.settings["product"] or len(inverse.RelatedObjects) == 1: history = inverse.OwnerHistory diff --git a/src/ifcopenshell-python/test/api/root/test_remove_product.py b/src/ifcopenshell-python/test/api/root/test_remove_product.py index 5b18ed296b..e221651525 100644 --- a/src/ifcopenshell-python/test/api/root/test_remove_product.py +++ b/src/ifcopenshell-python/test/api/root/test_remove_product.py @@ -187,6 +187,28 @@ class TestRemoveProduct(test.bootstrap.IFC4): assert len(self.file.by_type("IfcRelNests")) == 0 assert len(self.file.by_type("IfcDistributionPort")) == 0 + def test_removing_all_nesting_relationships_of_a_whole(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBeam") + ifcopenshell.api.run("nest.assign_object", self.file, related_object=subelement, relating_object=element) + total_entities = len(list(self.file)) + ifcopenshell.api.run("root.remove_product", self.file, product=element) + assert len(list(self.file)) == total_entities - 2 + assert len(self.file.by_type("IfcRelNests")) == 0 + assert len(self.file.by_type("IfcWall")) == 0 + assert len(self.file.by_type("IfcBeam")) == 1 + + def test_removing_all_nesting_relationships_of_a_part(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBeam") + ifcopenshell.api.run("nest.assign_object", self.file, related_object=subelement, relating_object=element) + total_entities = len(list(self.file)) + ifcopenshell.api.run("root.remove_product", self.file, product=subelement) + assert len(list(self.file)) == total_entities - 2 + assert len(self.file.by_type("IfcRelNests")) == 0 + assert len(self.file.by_type("IfcWall")) == 1 + assert len(self.file.by_type("IfcBeam")) == 0 + def test_removing_all_aggregate_relationships_of_a_whole(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBeam")