diff --git a/src/blenderbim/blenderbim/bim/module/structural/operator.py b/src/blenderbim/blenderbim/bim/module/structural/operator.py index 3662397056..47012097c3 100644 --- a/src/blenderbim/blenderbim/bim/module/structural/operator.py +++ b/src/blenderbim/blenderbim/bim/module/structural/operator.py @@ -473,7 +473,7 @@ class AssignStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator): self.file, **{ "relating_object": self.file.by_id(self.work_plan), - "product": self.file.by_id(self.load_case), + "products": [self.file.by_id(self.load_case)], }, ) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/core/aggregate.py b/src/blenderbim/blenderbim/core/aggregate.py index e74f8d7bb5..09c6f1c4e6 100644 --- a/src/blenderbim/blenderbim/core/aggregate.py +++ b/src/blenderbim/blenderbim/core/aggregate.py @@ -29,7 +29,7 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non if not aggregator.can_aggregate(relating_obj, related_obj): return rel = ifc.run( - "aggregate.assign_object", product=ifc.get_entity(related_obj), relating_object=ifc.get_entity(relating_obj) + "aggregate.assign_object", products=[ifc.get_entity(related_obj)], relating_object=ifc.get_entity(relating_obj) ) collector.assign(relating_obj) collector.assign(related_obj) diff --git a/src/blenderbim/blenderbim/core/sequence.py b/src/blenderbim/blenderbim/core/sequence.py index 2fa2debf5d..970b05e38d 100644 --- a/src/blenderbim/blenderbim/core/sequence.py +++ b/src/blenderbim/blenderbim/core/sequence.py @@ -61,7 +61,7 @@ def remove_work_schedule(ifc, work_schedule=None): def assign_work_schedule(ifc, work_plan=None, work_schedule=None): if work_schedule: - return ifc.run("aggregate.assign_object", relating_object=work_plan, product=work_schedule) + return ifc.run("aggregate.assign_object", relating_object=work_plan, products=[work_schedule]) def unassign_work_schedule(ifc, work_schedule=None): diff --git a/src/blenderbim/scripts/obj2ifc-meshlab.py b/src/blenderbim/scripts/obj2ifc-meshlab.py index 57d658ad90..2ecb5684fb 100644 --- a/src/blenderbim/scripts/obj2ifc-meshlab.py +++ b/src/blenderbim/scripts/obj2ifc-meshlab.py @@ -127,9 +127,9 @@ class Obj2Ifc: self.storey = ifcopenshell.api.run( "root.create_entity", self.file, ifc_class="IfcBuildingStorey", name="My Storey" ) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=site, relating_object=project) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=building, relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[site], relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[self.storey], relating_object=building) ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=site) ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=building) diff --git a/src/blenderbim/scripts/obj2ifc.py b/src/blenderbim/scripts/obj2ifc.py index b056c9539b..2999fbb6b6 100644 --- a/src/blenderbim/scripts/obj2ifc.py +++ b/src/blenderbim/scripts/obj2ifc.py @@ -120,9 +120,9 @@ class Obj2Ifc: self.storey = ifcopenshell.api.run( "root.create_entity", self.file, ifc_class="IfcBuildingStorey", name="My Storey" ) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=site, relating_object=project) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=building, relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=self.storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[site], relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[self.storey], relating_object=building) ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=site) ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=building) diff --git a/src/blenderbim/test/core/test_aggregate.py b/src/blenderbim/test/core/test_aggregate.py index 26de9166a1..2458e48e40 100644 --- a/src/blenderbim/test/core/test_aggregate.py +++ b/src/blenderbim/test/core/test_aggregate.py @@ -38,7 +38,7 @@ class TestAssignObject: ifc.get_entity("relating_obj").should_be_called().will_return("relating_object") ifc.get_entity("related_obj").should_be_called().will_return("related_object") ifc.run( - "aggregate.assign_object", product="related_object", relating_object="relating_object" + "aggregate.assign_object", products=["related_object"], relating_object="relating_object" ).should_be_called().will_return("rel") aggregate.disable_editing("related_obj").should_be_called() collector.assign("relating_obj").should_be_called() diff --git a/src/blenderbim/test/tool/test_brick.py b/src/blenderbim/test/tool/test_brick.py index b70b9b2240..b5d5a1f973 100644 --- a/src/blenderbim/test/tool/test_brick.py +++ b/src/blenderbim/test/tool/test_brick.py @@ -320,8 +320,8 @@ class TestGetParentSpace(NewFile): element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBuildingStorey") subelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSpace") project = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject") - ifcopenshell.api.run("aggregate.assign_object", ifc, product=subelement, relating_object=element) - ifcopenshell.api.run("aggregate.assign_object", ifc, product=element, relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subelement], relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[element], relating_object=project) assert subject.get_parent_space(subelement) == element assert subject.get_parent_space(element) is None diff --git a/src/blenderbim/test/tool/test_collector.py b/src/blenderbim/test/tool/test_collector.py index 1d9353b75b..f9bf18d0f1 100644 --- a/src/blenderbim/test/tool/test_collector.py +++ b/src/blenderbim/test/tool/test_collector.py @@ -68,7 +68,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=tool.Ifc.get().by_type("IfcSite")[0], - product=space_element, + products=[space_element], ) subject.assign(space_obj) assert len(space_obj.users_collection) == 1 @@ -84,7 +84,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=tool.Ifc.get().by_type("IfcSite")[0], - product=space_element, + products=[space_element], ) subject.assign(space_obj) subject.assign(space_obj) @@ -103,7 +103,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=tool.Ifc.get().by_type("IfcSite")[0], - product=space_element, + products=[space_element], ) subject.assign(space_obj) assert len(space_obj.users_collection) == 1 @@ -121,7 +121,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=element, - product=subelement, + products=[subelement], ) subject.assign(element_obj) assert len(element_obj.users_collection) == 1 @@ -161,7 +161,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=tool.Ifc.get().by_type("IfcSite")[0], - product=space_element, + products=[space_element], ) subject.assign(space_obj) assert bpy.context.scene.collection.children.find(space_collection.name) == -1 @@ -180,7 +180,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=element, - product=subelement, + products=[subelement], ) subject.assign(element_obj) subject.assign(subelement_obj) @@ -203,7 +203,7 @@ class TestAssign(NewFile): "aggregate.assign_object", tool.Ifc.get(), relating_object=element, - product=subelement, + products=[subelement], ) subject.assign(subelement_obj) assert subelement_obj.users_collection[0].name == "IfcSite/My Site" diff --git a/src/blenderbim/test/tool/test_misc.py b/src/blenderbim/test/tool/test_misc.py index f5dab4a626..bf7e0f5ce7 100644 --- a/src/blenderbim/test/tool/test_misc.py +++ b/src/blenderbim/test/tool/test_misc.py @@ -86,8 +86,8 @@ class TestGetStoreyHeight(test.bim.bootstrap.NewFile): storey.Elevation = 3000 storey2 = ifc.createIfcBuildingStorey() storey2.Elevation = 5000 - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey, relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey2, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey2], relating_object=building) assert subject.get_storey_height_in_si(storey, 1) == 2.0 def test_getting_a_double_storey_height(self): @@ -103,9 +103,9 @@ class TestGetStoreyHeight(test.bim.bootstrap.NewFile): storey2.Elevation = 5000 storey3 = ifc.createIfcBuildingStorey() storey3.Elevation = 9000 - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey, relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey2, relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey3, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey2], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey3], relating_object=building) assert subject.get_storey_height_in_si(storey, 2) == 6.0 def test_only_considering_storeys_in_the_same_building(self): @@ -116,7 +116,7 @@ class TestGetStoreyHeight(test.bim.bootstrap.NewFile): storey.Elevation = 3000 storey2 = ifc.createIfcBuildingStorey() storey2.Elevation = 5000 - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building) assert subject.get_storey_height_in_si(storey, 1) is None def test_returning_none_if_the_storey_height_is_undefined(self): @@ -124,7 +124,7 @@ class TestGetStoreyHeight(test.bim.bootstrap.NewFile): tool.Ifc.set(ifc) building = ifc.createIfcBuilding() storey = ifc.createIfcBuildingStorey() - ifcopenshell.api.run("aggregate.assign_object", ifc, product=storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[storey], relating_object=building) assert subject.get_storey_height_in_si(storey, 1) is None diff --git a/src/ifcopenshell-python/docs/ifcopenshell-python/code_examples.rst b/src/ifcopenshell-python/docs/ifcopenshell-python/code_examples.rst index 66f708e95e..b43652b975 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell-python/code_examples.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell-python/code_examples.rst @@ -236,9 +236,9 @@ Create a simple model from scratch # Since the site is our top level location, assign it to the project # Then place our building on the site, and our storey in the building - run("aggregate.assign_object", model, relating_object=project, product=site) - run("aggregate.assign_object", model, relating_object=site, product=building) - run("aggregate.assign_object", model, relating_object=building, product=storey) + run("aggregate.assign_object", model, relating_object=project, products=[site]) + run("aggregate.assign_object", model, relating_object=site, products=[building]) + run("aggregate.assign_object", model, relating_object=building, products=[storey]) # Let's create a new wall wall = run("root.create_entity", model, ifc_class="IfcWall") diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 905ebeaaf4..376fd14f3f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -50,6 +50,7 @@ ARGUMENTS_DEPRECATION = { batching_argument_deprecation, prev_argument="product", new_argument="products" ), "group.unassign_group": partial(batching_argument_deprecation, prev_argument="product", new_argument="products"), + "aggregate.assign_object": partial(batching_argument_deprecation, prev_argument="product", new_argument="products"), "layer.assign_layer": partial(batching_argument_deprecation, prev_argument="item", new_argument="items"), "layer.unassign_layer": partial(batching_argument_deprecation, prev_argument="item", new_argument="items"), } diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py index 8a868755be..58bf9daa13 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py @@ -20,11 +20,17 @@ import ifcopenshell import ifcopenshell.api import ifcopenshell.util.element import ifcopenshell.util.placement +from typing import Union class Usecase: - def __init__(self, file, product=None, relating_object=None): - """Assigns an object as an aggregate to a product + def __init__( + self, + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_object: ifcopenshell.entity_instance, + ): + """Assigns object as an aggregate to the products All physical IFC model elements must be part of a hierarchical tree called the "spatial decomposition", where large things are made up of @@ -55,14 +61,15 @@ class Usecase: its parent in the spatial hierarchy. If your product has a placement, its placement will be recalculated to follow this convention. - :param product: The part of the aggregate, typically an IfcElement or + :param products: The list of parts of the aggregate, typically of IfcElement or IfcSpatialStructureElement subclass - :type product: ifcopenshell.entity_instance.entity_instance + :type product: list[ifcopenshell.entity_instance.entity_instance] :param relating_object: The whole of the aggregate, typically an IfcElement or IfcSpatialStructureElement subclass :type relating_object: ifcopenshell.entity_instance.entity_instance :return: The IfcRelAggregate relationship instance - :rtype: ifcopenshell.entity_instance.entity_instance + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] Example: @@ -73,40 +80,59 @@ class Usecase: subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, product=element, relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", model, products=[element], relating_object=project) # The site has a building - ifcopenshell.api.run("aggregate.assign_object", model, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element) """ self.file = file self.settings = { - "product": product, + "products": products, "relating_object": relating_object, } - def execute(self): - decomposes = None - if self.settings["product"].Decomposes: - decomposes = self.settings["product"].Decomposes[0] + def execute(self) -> Union[ifcopenshell.entity_instance, None]: + if not self.settings["products"]: + return - is_decomposed_by = None - for rel in self.settings["relating_object"].IsDecomposedBy: - if rel.is_a("IfcRelAggregates"): - is_decomposed_by = rel - break + products = set(self.settings["products"]) + relating_object = self.settings["relating_object"] + is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None) - if decomposes and decomposes == is_decomposed_by: - return decomposes + previous_aggregates_rels: set[ifcopenshell.entity_instance] = set() + products_without_aggregates: list[ifcopenshell.entity_instance] = [] + products_with_aggregates: list[ifcopenshell.entity_instance] = [] - container = ifcopenshell.util.element.get_container(self.settings["product"], should_get_direct=True) - if container: - ifcopenshell.api.run("spatial.remove_container", self.file, product=self.settings["product"]) + # check if there is anything to change + for product in products: + product_rel = next(iter(product.Decomposes), None) - if decomposes: - related_objects = list(decomposes.RelatedObjects) - related_objects.remove(self.settings["product"]) + if product_rel is None: + products_without_aggregates.append(product) + continue + + # either is_decomposed_by is None or product is part of different rel + if product_rel != is_decomposed_by: + previous_aggregates_rels.add(product_rel) + products_with_aggregates.append(product) + + # products with already assigned aggregates will be skipped + + products_to_change = products_without_aggregates + products_with_aggregates + # nothing to change + if not products_to_change: + return is_decomposed_by + + # can be either only aggregated or only contained at the same time + # some product might not be able to have a container + possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")] + ifcopenshell.api.run("spatial.unassign_container", self.file, products=possibly_contained_products) + + # unassign elements from previous aggregates + for decomposes in previous_aggregates_rels: + related_objects = set(decomposes.RelatedObjects) - products if related_objects: - decomposes.RelatedObjects = related_objects + decomposes.RelatedObjects = list(related_objects) ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": decomposes}) else: history = decomposes.OwnerHistory @@ -114,10 +140,9 @@ class Usecase: if history: ifcopenshell.util.element.remove_deep2(self.file, history) + # assign elements to a new aggregate if is_decomposed_by: - related_objects = set(is_decomposed_by.RelatedObjects) - related_objects.add(self.settings["product"]) - is_decomposed_by.RelatedObjects = list(related_objects) + is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products) ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_decomposed_by}) else: is_decomposed_by = self.file.create_entity( @@ -125,19 +150,21 @@ class Usecase: **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["product"]], - "RelatingObject": self.settings["relating_object"], + "RelatedObjects": list(products), + "RelatingObject": relating_object, } ) - placement = getattr(self.settings["product"], "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=self.settings["product"], - matrix=ifcopenshell.util.placement.get_local_placement(self.settings["product"].ObjectPlacement), - is_si=False, - ) + # localize placement relative to a new aggregate for affected products + for product in products_to_change: + placement = getattr(product, "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + self.file, + product=product, + matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), + is_si=False, + ) return is_decomposed_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py index b4c4c42a79..f92b9e60cf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py @@ -51,8 +51,8 @@ class Usecase: element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") subelement1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") subelement2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", model, product=subelement1, relating_object=element) - ifcopenshell.api.run("aggregate.assign_object", model, product=subelement2, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement1], relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element) # The relationship is returned as element still has subelement2 rel = ifcopenshell.api.run("aggregate.unassign_object", model, product=subelement1) # Nothing is returned, as element is now empty diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py index a9c775d338..3da4d3b10e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py @@ -107,7 +107,7 @@ class Usecase: "aggregate.assign_object", self.file, **{ - "product": work_schedule, + "products": [work_schedule], "relating_object": self.settings["work_plan"], } ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py index b3439e69a6..a9e6f000eb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py @@ -64,7 +64,7 @@ class Usecase: "aggregate.assign_object", self.file, **{ - "product": self.settings["work_schedule"], + "products": [self.settings["work_schedule"]], "relating_object": self.settings["work_plan"], } ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py index 41114e4b1d..2fee48d67c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py @@ -73,7 +73,7 @@ class Usecase: as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element exists in. :return: The IfcRelContainedInSpatialStructure relationship instance - or `None` if nothing was changed. + or `None` if `products` was empty list. :rtype: Union[ifcopenshell.entity_instance.entity_instance, None] Example: @@ -128,13 +128,17 @@ class Usecase: products_without_containers.append(product) continue + # either structure_rel is None or product is part of different rel if product_rel != structure_rel: previous_containers_rels.add(product_rel) products_with_containers.append(product) + # products with already assigned containers will be skipped + + products_to_change = products_without_containers + products_with_containers # nothing to change - if not products_without_containers and not products_with_containers: - return + if not products_to_change: + return structure_rel # can be either only aggregated or only contained at the same time for product in products_without_containers: @@ -142,7 +146,7 @@ class Usecase: if aggregate: ifcopenshell.api.run("aggregate.unassign_object", self.file, product=product) - # unassign elements from previous container + # unassign elements from previous containers for rel in previous_containers_rels: related_elements = set(rel.RelatedElements) - products if related_elements: @@ -170,7 +174,7 @@ class Usecase: ) # localize placement relative to a new container for affected products - for product in products_without_containers + products_with_containers: + for product in products_to_change: placement = getattr(product, "ObjectPlacement", None) if placement and placement.is_a("IfcLocalPlacement"): ifcopenshell.api.run( diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index 1b492634f0..4a62532762 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -45,12 +45,12 @@ class Usecase: storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, product=site, relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, product=building, relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, product=storey, relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, product=space, relating_object=storey) + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) # Create a column, this column spans 3 storeys column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py index ab20b6aa03..187ce20afe 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py @@ -59,12 +59,12 @@ class Usecase: storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, product=site, relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, product=building, relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, product=storey, relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, product=space, relating_object=storey) + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) # Create a column, this column spans 3 storeys column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py index 476dd46315..c2c083bd06 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py @@ -42,11 +42,11 @@ class Usecase: storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, product=site, relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, product=building, relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, product=storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) # Create a wall wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") diff --git a/src/ifcopenshell-python/test/api/aggregate/test_assign_object.py b/src/ifcopenshell-python/test/api/aggregate/test_assign_object.py index 88b93bfd82..c43733f426 100644 --- a/src/ifcopenshell-python/test/api/aggregate/test_assign_object.py +++ b/src/ifcopenshell-python/test/api/aggregate/test_assign_object.py @@ -25,19 +25,23 @@ import ifcopenshell.util.placement class TestAssignObject(test.bootstrap.IFC4): - def test_assigning_a_container(self): + def test_assigning_an_aggregate(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") - subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - rel = ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) - assert ifcopenshell.util.element.get_aggregate(subelement) == element + subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") + subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") + rel = ifcopenshell.api.run( + "aggregate.assign_object", self.file, products=[subelement1, subelement2], relating_object=element + ) + assert ifcopenshell.util.element.get_aggregate(subelement1) == element + assert ifcopenshell.util.element.get_aggregate(subelement2) == element assert rel.is_a("IfcRelAggregates") def test_doing_nothing_if_the_aggregate_is_already_assigned(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) total_elements = len([e for e in self.file]) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert len([e for e in self.file]) == total_elements def test_that_old_aggregate_relationships_are_updated_if_they_still_have_elements(self): @@ -45,20 +49,20 @@ class TestAssignObject(test.bootstrap.IFC4): element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement1, relating_object=element1) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement2, relating_object=element1) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement1], relating_object=element1) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement2], relating_object=element1) rel = subelement1.Decomposes[0] assert len(rel.RelatedObjects) == 2 - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement1, relating_object=element2) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement1], relating_object=element2) assert len(rel.RelatedObjects) == 1 def test_that_old_aggregate_relationships_are_purged_if_no_more_elements_are_contained(self): element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement1, relating_object=element1) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement1], relating_object=element1) rel_id = subelement1.Decomposes[0].id() - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement1, relating_object=element2) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement1], relating_object=element2) with pytest.raises(RuntimeError): self.file.by_id(rel_id) @@ -68,7 +72,7 @@ class TestAssignObject(test.bootstrap.IFC4): element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") element2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element1) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element1) matrix1 = numpy.array( ( (1.0, 0.0, 0.0, 1.0), @@ -94,7 +98,7 @@ class TestAssignObject(test.bootstrap.IFC4): ifcopenshell.api.run( "geometry.edit_object_placement", self.file, product=subelement, matrix=matrix1.copy(), is_si=False ) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element2) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element2) assert subelement.ObjectPlacement.PlacementRelTo.PlacesObject[0] == element2 assert numpy.array_equal(ifcopenshell.util.placement.get_local_placement(subelement.ObjectPlacement), matrix1) @@ -105,7 +109,7 @@ class TestAssignObject(test.bootstrap.IFC4): subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") placement = self.file.createIfcGridPlacement() subelement.ObjectPlacement = placement - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert subelement.ObjectPlacement == placement def test_removing_containment_if_it_exists(self): @@ -115,5 +119,5 @@ class TestAssignObject(test.bootstrap.IFC4): container = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuildingStorey") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") ifcopenshell.api.run("spatial.assign_container", self.file, products=[subelement], relating_structure=container) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert not ifcopenshell.util.element.get_container(subelement, should_get_direct=True) diff --git a/src/ifcopenshell-python/test/api/aggregate/test_unassign_object.py b/src/ifcopenshell-python/test/api/aggregate/test_unassign_object.py index de44b0727e..343fe7deed 100644 --- a/src/ifcopenshell-python/test/api/aggregate/test_unassign_object.py +++ b/src/ifcopenshell-python/test/api/aggregate/test_unassign_object.py @@ -26,7 +26,7 @@ class TestUnassignObject(test.bootstrap.IFC4): def test_unassigning_an_object(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) ifcopenshell.api.run("aggregate.unassign_object", self.file, product=subelement) assert ifcopenshell.util.element.get_aggregate(subelement) is None @@ -34,14 +34,14 @@ class TestUnassignObject(test.bootstrap.IFC4): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement1, relating_object=element) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement2, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement1], relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement2], relating_object=element) ifcopenshell.api.run("aggregate.unassign_object", self.file, product=subelement1) assert len(self.file.by_type("IfcRelAggregates")) == 1 def test_the_rel_is_purged_if_there_are_no_more_decomposed_elements(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) ifcopenshell.api.run("aggregate.unassign_object", self.file, product=subelement) assert len(self.file.by_type("IfcRelAggregates")) == 0 diff --git a/src/ifcopenshell-python/test/api/geometry/test_edit_object_placement.py b/src/ifcopenshell-python/test/api/geometry/test_edit_object_placement.py index 8c9de31134..73985fd627 100644 --- a/src/ifcopenshell-python/test/api/geometry/test_edit_object_placement.py +++ b/src/ifcopenshell-python/test/api/geometry/test_edit_object_placement.py @@ -253,7 +253,7 @@ class TestEditObjectPlacement(test.bootstrap.IFC4): (0.0, 0.0, 0.0, 1.0), ) ) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) ifcopenshell.api.run( "geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy(), is_si=False ) @@ -629,7 +629,7 @@ class TestEditObjectPlacement(test.bootstrap.IFC4): building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") storey = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuildingStorey") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[storey], relating_object=building) wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") ifcopenshell.api.run("spatial.assign_container", self.file, products=[wall], relating_structure=storey) diff --git a/src/ifcopenshell-python/test/api/root/test_copy_class.py b/src/ifcopenshell-python/test/api/root/test_copy_class.py index f8c42cdb74..5fcaf8cb90 100644 --- a/src/ifcopenshell-python/test/api/root/test_copy_class.py +++ b/src/ifcopenshell-python/test/api/root/test_copy_class.py @@ -89,7 +89,7 @@ class TestCopyClass(test.bootstrap.IFC4): def test_copying_a_container_only_and_not_its_decomposition(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuildingStorey") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) new = ifcopenshell.api.run("root.copy_class", self.file, product=element) assert element.IsDecomposedBy assert not new.IsDecomposedBy @@ -97,7 +97,7 @@ class TestCopyClass(test.bootstrap.IFC4): def test_copying_an_aggregate_only_and_not_its_decomposition(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") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) new = ifcopenshell.api.run("root.copy_class", self.file, product=element) assert element.IsDecomposedBy assert not new.IsDecomposedBy @@ -105,7 +105,7 @@ class TestCopyClass(test.bootstrap.IFC4): def test_copying_an_aggregate_decomposition_and_maintaining_the_aggregate_relationship(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") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) new = ifcopenshell.api.run("root.copy_class", self.file, product=subelement) assert new.Decomposes[0].RelatingObject == element 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 4913afd23c..68ddc40a58 100644 --- a/src/ifcopenshell-python/test/api/root/test_remove_product.py +++ b/src/ifcopenshell-python/test/api/root/test_remove_product.py @@ -212,7 +212,7 @@ class TestRemoveProduct(test.bootstrap.IFC4): 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") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[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 @@ -223,7 +223,7 @@ class TestRemoveProduct(test.bootstrap.IFC4): def test_removing_all_aggregate_relationships_of_a_part(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") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[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 diff --git a/src/ifcopenshell-python/test/api/spatial/test_assign_container.py b/src/ifcopenshell-python/test/api/spatial/test_assign_container.py index 26315c8ec2..533db20267 100644 --- a/src/ifcopenshell-python/test/api/spatial/test_assign_container.py +++ b/src/ifcopenshell-python/test/api/spatial/test_assign_container.py @@ -118,6 +118,6 @@ class TestAssignContainer(test.bootstrap.IFC4): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") aggregate = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=aggregate) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=aggregate) ifcopenshell.api.run("spatial.assign_container", self.file, products=[subelement], relating_structure=element) assert not ifcopenshell.util.element.get_aggregate(subelement) diff --git a/src/ifcopenshell-python/test/api/test_api.py b/src/ifcopenshell-python/test/api/test_api.py index 0a2cd611de..9695849875 100644 --- a/src/ifcopenshell-python/test/api/test_api.py +++ b/src/ifcopenshell-python/test/api/test_api.py @@ -83,3 +83,11 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4): ifcopenshell.api.run("layer.unassign_layer", self.file, item=items[2], layer=layer) assert len(layer.AssignedItems) == 2 assert set(layer.AssignedItems) == set(items[:2]) + + @deprecation_check + def test_assigning_an_aggregate(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite") + subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") + rel = ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + assert ifcopenshell.util.element.get_aggregate(subelement) == element + assert rel.is_a("IfcRelAggregates") diff --git a/src/ifcopenshell-python/test/util/test_element.py b/src/ifcopenshell-python/test/util/test_element.py index 6e70c64473..7b5c674da5 100644 --- a/src/ifcopenshell-python/test/util/test_element.py +++ b/src/ifcopenshell-python/test/util/test_element.py @@ -706,7 +706,7 @@ class TestGetContainerIFC4(test.bootstrap.IFC4): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly") building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=building) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert subject.get_container(subelement) == building def test_getting_nothing_if_we_enforce_only_getting_direct_spatial_containers(self): @@ -714,7 +714,7 @@ class TestGetContainerIFC4(test.bootstrap.IFC4): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly") building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=building) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert subject.get_container(subelement, should_get_direct=True) is None @@ -736,7 +736,7 @@ class TestGetDecompositionIFC4(test.bootstrap.IFC4): subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBeam") building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=building) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) results = subject.get_decomposition(building) assert element in results assert subelement in results @@ -756,7 +756,7 @@ class TestGetAggregateIFC4(test.bootstrap.IFC4): def test_getting_the_containing_aggregate_of_a_subelement(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcCovering") - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert subject.get_aggregate(subelement) == element diff --git a/src/ifcopenshell-python/test/util/test_selector.py b/src/ifcopenshell-python/test/util/test_selector.py index ac00bab1a8..3053c1a0a6 100644 --- a/src/ifcopenshell-python/test/util/test_selector.py +++ b/src/ifcopenshell-python/test/util/test_selector.py @@ -236,9 +236,9 @@ class TestFilterElements(test.bootstrap.IFC4): project = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject", name="Project") ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=space) ifcopenshell.api.run("spatial.assign_container", self.file, products=[element2], relating_structure=storey) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=space, relating_object=storey) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=storey, relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=building, relating_object=project) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[space], relating_object=storey) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[building], relating_object=project) assert subject.filter_elements(self.file, "IfcWall, location=NULL") == set() assert subject.filter_elements(self.file, "IfcWall, location=Space") == {element} assert subject.filter_elements(self.file, "IfcWall, location=G") == {element, element2} @@ -469,7 +469,7 @@ class TestSelector(test.bootstrap.IFC4): subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcMember") building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") ifcopenshell.api.run("spatial.assign_container", self.file, products=[element], relating_structure=building) - ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", self.file, products=[subelement], relating_object=element) assert set(subject.Selector.parse(self.file, "@ .IfcBuilding")) == {element, subelement} def test_selecting_elements_from_a_prefiltered_list(self): diff --git a/src/ifcpatch/test/test_ExtractElements.py b/src/ifcpatch/test/test_ExtractElements.py index 79ad7e11da..a6b239ee1a 100644 --- a/src/ifcpatch/test/test_ExtractElements.py +++ b/src/ifcpatch/test/test_ExtractElements.py @@ -42,8 +42,8 @@ class TestExtractElements: building = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcBuilding") storey = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcBuildingStorey") wall = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcWall") - ifcopenshell.api.run("aggregate.assign_object", ifc_file, product=building, relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", ifc_file, product=storey, relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", ifc_file, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", ifc_file, products=[storey], relating_object=building) ifcopenshell.api.run("spatial.assign_container", ifc_file, products=[wall], relating_structure=storey) output = ifcpatch.execute({"file": ifc_file, "recipe": "ExtractElements", "arguments": ["IfcWall"]}) @@ -61,7 +61,7 @@ class TestExtractElements: container = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcBuildingStorey") subelement = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcWall") ifcopenshell.api.run("spatial.assign_container", ifc_file, products=[element], relating_structure=container) - ifcopenshell.api.run("aggregate.assign_object", ifc_file, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", ifc_file, products=[subelement], relating_object=element) output = ifcpatch.execute({"file": ifc_file, "recipe": "ExtractElements", "arguments": ["IfcWall"]}) diff --git a/src/ifcsverchok/__init__.py b/src/ifcsverchok/__init__.py index 7401a90ce9..66a9563c29 100644 --- a/src/ifcsverchok/__init__.py +++ b/src/ifcsverchok/__init__.py @@ -179,7 +179,7 @@ class IFC_Sv_write_file(bpy.types.Operator): if not (spatial.is_a("IfcSite") or spatial.is_a("IfcBuilding")) and (spatial not in elements_in_buildings): elements = ifcopenshell.util.element.get_decomposition(spatial) ifcopenshell.api.run( - "aggregate.assign_object", file, product=spatial, relating_object=file.by_type("IfcBuilding")[0] + "aggregate.assign_object", file, products=[spatial], relating_object=file.by_type("IfcBuilding")[0] ) for building in file.by_type("IfcBuilding"): @@ -202,7 +202,7 @@ class IFC_Sv_write_file(bpy.types.Operator): if len(file.by_type("IfcSite")) == 0: ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcSite", name="My Site") ifcopenshell.api.run( - "aggregate.assign_object", file, product=building, relating_object=file.by_type("IfcSite")[0] + "aggregate.assign_object", file, products=[building], relating_object=file.by_type("IfcSite")[0] ) try: if file.by_type("IfcSite")[0].Decomposes[0].RelatingObject.is_a("IfcProject"): @@ -212,7 +212,7 @@ class IFC_Sv_write_file(bpy.types.Operator): ifcopenshell.api.run( "aggregate.assign_object", file, - product=file.by_type("IfcSite")[0], + products=[file.by_type("IfcSite")[0]], relating_object=file.by_type("IfcProject")[0], ) self.file = file diff --git a/src/ifcsverchok/nodes/ifc/add_spatial_element.py b/src/ifcsverchok/nodes/ifc/add_spatial_element.py index ac94ea1a3c..bf9e4a6dee 100644 --- a/src/ifcsverchok/nodes/ifc/add_spatial_element.py +++ b/src/ifcsverchok/nodes/ifc/add_spatial_element.py @@ -135,7 +135,7 @@ class SvIfcAddSpatialElement(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h ifcopenshell.api.run( "aggregate.assign_object", self.file, - product=items, + products=[items], relating_object=result, ) else: @@ -188,7 +188,7 @@ class SvIfcAddSpatialElement(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.h ifcopenshell.api.run( "aggregate.assign_object", self.file, - product=added_element, + products=[added_element], relating_object=result, ) else: diff --git a/src/ifcsverchok/nodes/ifc/write_file.py b/src/ifcsverchok/nodes/ifc/write_file.py index ba1baae121..b9f03c6e1a 100644 --- a/src/ifcsverchok/nodes/ifc/write_file.py +++ b/src/ifcsverchok/nodes/ifc/write_file.py @@ -93,7 +93,7 @@ class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.Sv ifcopenshell.api.run( "aggregate.assign_object", file, - product=spatial, + products=[spatial], relating_object=file.by_type("IfcBuilding")[0], ) @@ -120,7 +120,7 @@ class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.Sv ifcopenshell.api.run( "aggregate.assign_object", file, - product=building, + products=[building], relating_object=file.by_type("IfcSite")[0], ) try: @@ -131,7 +131,7 @@ class SvIfcWriteFile(bpy.types.Node, SverchCustomTreeNode, ifcsverchok.helper.Sv ifcopenshell.api.run( "aggregate.assign_object", file, - product=file.by_type("IfcSite")[0], + products=[file.by_type("IfcSite")[0]], relating_object=file.by_type("IfcProject")[0], ) self.file = file diff --git a/src/ifctester/test/ids_doc_generator.py b/src/ifctester/test/ids_doc_generator.py index dd002e33c7..3290104cfa 100644 --- a/src/ifctester/test/ids_doc_generator.py +++ b/src/ifctester/test/ids_doc_generator.py @@ -320,9 +320,9 @@ site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite", na building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding", name="Building A") storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey", name="Ground Floor") -ifcopenshell.api.run("aggregate.assign_object", model, relating_object=project, product=site) -ifcopenshell.api.run("aggregate.assign_object", model, relating_object=site, product=building) -ifcopenshell.api.run("aggregate.assign_object", model, relating_object=building, product=storey) +ifcopenshell.api.run("aggregate.assign_object", model, relating_object=project, products=[site]) +ifcopenshell.api.run("aggregate.assign_object", model, relating_object=site, products=[building]) +ifcopenshell.api.run("aggregate.assign_object", model, relating_object=building, products=[storey]) wall_types = [] for i in range(0, 4): diff --git a/src/ifctester/test/test_facet.py b/src/ifctester/test/test_facet.py index ed453af65e..0c93fbdc52 100644 --- a/src/ifctester/test/test_facet.py +++ b/src/ifctester/test/test_facet.py @@ -1428,7 +1428,7 @@ class TestPartOf: subelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall") facet = PartOf(name="IFCELEMENTASSEMBLY", relation="IFCRELAGGREGATES") run("A non aggregated element fails an aggregate relationship", facet=facet, inst=subelement, expected=False) - ifcopenshell.api.run("aggregate.assign_object", ifc, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subelement], relating_object=element) run("The aggregated whole fails an aggregate relationship", facet=facet, inst=element, expected=False) run("The aggregated part passes an aggregate relationship", facet=facet, inst=subelement, expected=True) @@ -1439,7 +1439,7 @@ class TestPartOf: ifc = ifcopenshell.file() element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSlab") subelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBeam") - ifcopenshell.api.run("aggregate.assign_object", ifc, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subelement], relating_object=element) facet = PartOf(name="IFCSLAB", relation="IFCRELAGGREGATES") run("An aggregate may specify the entity of the whole 1/2", facet=facet, inst=subelement, expected=True) facet = PartOf(name="IFCWALL", relation="IFCRELAGGREGATES") @@ -1462,8 +1462,8 @@ class TestPartOf: element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcElementAssembly") subelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSlab") subsubelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBeam") - ifcopenshell.api.run("aggregate.assign_object", ifc, product=subelement, relating_object=element) - ifcopenshell.api.run("aggregate.assign_object", ifc, product=subsubelement, relating_object=subelement) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subelement], relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subsubelement], relating_object=subelement) facet = PartOf(name="IFCELEMENTASSEMBLY", relation="IFCRELAGGREGATES") run("An aggregate entity may pass any ancestral whole passes", facet=facet, inst=subsubelement, expected=True) @@ -1517,7 +1517,7 @@ class TestPartOf: ifc = ifcopenshell.file() element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSlab") subelement = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBeam") - ifcopenshell.api.run("aggregate.assign_object", ifc, product=subelement, relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", ifc, products=[subelement], relating_object=element) container = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcSpace") ifcopenshell.api.run("spatial.assign_container", ifc, products=[element], relating_structure=container) facet = PartOf(relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", name="IFCSPACE")