Remove spatial containment and aggregation when nesting

The nest assign_object API now removes existing spatial containment and
aggregate relationships before creating the nest, matching the behavior
documented in its docstring and consistent with aggregate.assign_object.

Fix #7248

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-03-22 15:26:58 +11:00
parent b8136d4762
commit 75b8d4f218
2 changed files with 26 additions and 1 deletions
@@ -19,7 +19,9 @@
from typing import Union
import ifcopenshell
import ifcopenshell.api.aggregate
import ifcopenshell.api.owner
import ifcopenshell.api.spatial
import ifcopenshell.guid
import ifcopenshell.util.element
@@ -137,7 +139,10 @@ def assign_object(
if not objects_to_change:
return is_nested_by
# NOTE: An object can both be nested and assigned to a container or an aggregate.
# Can be either only nested, aggregated, or contained at the same time.
possibly_contained = [o for o in objects_without_nests if hasattr(o, "ContainedInStructure")]
ifcopenshell.api.spatial.unassign_container(file, products=possibly_contained)
ifcopenshell.api.aggregate.unassign_object(file, products=objects_without_nests)
# unassign elements from previous nests
for nests in previous_nests_rels:
@@ -18,8 +18,10 @@
import pytest
import ifcopenshell.api.aggregate
import ifcopenshell.api.nest
import ifcopenshell.api.root
import ifcopenshell.api.spatial
import ifcopenshell.util.element
import test.bootstrap
@@ -82,6 +84,24 @@ class TestAssignObject(test.bootstrap.IFC4):
ifcopenshell.api.nest.assign_object(self.file, related_objects=subelements[2:3], relating_object=element2)
assert rel.RelatedObjects == tuple(subelements[:2] + subelements[3:])
def test_nesting_removes_spatial_containment(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
storey = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcBuildingStorey")
ifcopenshell.api.spatial.assign_container(self.file, products=[subelement], relating_structure=storey)
assert ifcopenshell.util.element.get_container(subelement) == storey
ifcopenshell.api.nest.assign_object(self.file, related_objects=[subelement], relating_object=element)
assert ifcopenshell.util.element.get_container(subelement) is None
def test_nesting_removes_aggregate(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
assembly = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcElementAssembly")
ifcopenshell.api.aggregate.assign_object(self.file, products=[subelement], relating_object=assembly)
assert ifcopenshell.util.element.get_aggregate(subelement) == assembly
ifcopenshell.api.nest.assign_object(self.file, related_objects=[subelement], relating_object=element)
assert ifcopenshell.util.element.get_aggregate(subelement) is None
class TestAssignObjectIFC2X3(test.bootstrap.IFC2X3, TestAssignObject):
pass