diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py index 4a27619176..63dd26ae10 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py @@ -22,14 +22,81 @@ import ifcopenshell.util.placement class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None, relating_structure=None): + """Assigns a product to be contained hierarchically in a space + + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. See + ifcopenshell.api.aggregate.assign_object for more details about + aggregation. + + The IfcProject will be "decomposed" into spatial structure elements. + These are virtual spaces like stes, buildings, storeys, and spaces (i.e. + rooms). You can't physically touch these spaces, but you can touch the + products contained within these spaces. + + To state that a product is contained in a space, you will use a + "containment" relationship. Containment is a very common relationship + used to create the hierarchical spatial decomposition tree. For example, + you might say that "This wall is on the third building storey", or "this + table is in the living room space". + + The distinguishing factor between aggregation and containment is that + aggregation occurs between objects of the same type (e.g. a large space + is made up of smaller spaces), whereas containment is between two + different types: explicitly saying that a physical product exists within + a virtual space. + + Containment is critical in construction management, to know which + objects are in which spaces, as often you would divide your construction + schedule into storey by storey, or zone by zone. Containment is also + critical in facility management, as it indicates through which space + equipment may be accessed for maintenance purposes. + + As a product may only have a single locaion in the "spatial + decomposition" tree, assigning an aggregate relationship will remove any + previous aggregation, containment, or nesting relationships it may have. + + :param product: The physical IfcElement that exists in the space. + :type product: ifcopenshell.entity_instance.entity_instance + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: The IfcRelContainedInSpatialStructure relationship instance + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + + # 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) + + # 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) + + # Create a wall and furniture + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + + # The wall is in the storey, and the furniture is in the space + ifcopenshell.api.run("spatial.assign_container", model, product=wall, relating_structure=storey) + ifcopenshell.api.run("spatial.assign_container", model, product=furniture, relating_structure=space) + """ self.file = file self.settings = { - "product": None, - "relating_structure": None, + "product": product, + "relating_structure": relating_structure, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): contained_in_structure = self.settings["product"].ContainedInStructure diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index d013cee5a6..0d3e100383 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -21,11 +21,49 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None, relating_structure=None): + """Dereferences the a product and space + + :param product: The physical IfcElement that exists in the space. + :type product: ifcopenshell.entity_instance.entity_instance + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: None + :rtype: None + + Example:: + + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + 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) + + # 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) + + # Create a column, this column spans 3 storeys + column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # The column is contained in the lowermost storey + ifcopenshell.api.run("spatial.assign_container", model, product=column, relating_structure=storey1) + + # And referenced in the others + ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2) + ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3) + + # Actually, it only goes up to storey 2. + ifcopenshell.api.run("spatial.dereference_structure", model, product=column, relating_structure=storey3) + """ self.file = file - self.settings = {"product": None, "relating_structure": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"product": product, "relating_structure": relating_structure} def execute(self): for rel in self.settings["product"].ReferencedInStructures: diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py index 32ca61e316..b98d243f0d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py @@ -21,14 +21,64 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None, relating_structure=None): + """Denote that a product is related to a spatial structure + + This is similar to ifcopenshell.api.spatial.assign_container, except + that containment can only occur between a product and a single spatial + structure element. This is fine if a wall is on level 1, but not + appropriate if you have a multistorey column on multiple levels, or a + door with a to and from space, or a stair going from one floor to + another floor. This is where spatial referencing is used. + + Typically, the product will be contained in the lowermost, constructed + first, or primarily accessible space. For a multistorey column or stair, + the column or stair will therefore be contained in the lowermost storey. + Then, any other storeys will be referenced. + + Referencing is non-hierarchical, so a door may be referenced in multiple + spaces simultaneously. + + :param product: The physical IfcElement that exists in the space. + :type product: ifcopenshell.entity_instance.entity_instance + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: The IfcRelContainedInSpatialStructure relationship instance + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + 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) + + # 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) + + # Create a column, this column spans 3 storeys + column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # The column is contained in the lowermost storey + ifcopenshell.api.run("spatial.assign_container", model, product=column, relating_structure=storey1) + + # And referenced in the others + ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey2) + ifcopenshell.api.run("spatial.reference_structure", model, product=column, relating_structure=storey3) + """ self.file = file self.settings = { - "product": None, - "relating_structure": None, + "product": product, + "relating_structure": relating_structure, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): referenced_in_structures = self.settings["product"].ReferencedInStructures diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py index 3959177f4c..2907fab1ff 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/remove_container.py @@ -21,7 +21,39 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None): + """Unassigns a container from a product. + + Caution: this API function may be replaced by spatial.unassign_container + + :param product: The IfcProduct to remove the containment from. + :type product: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + 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) + + # 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) + + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # The wall is in the storey + ifcopenshell.api.run("spatial.assign_container", model, product=wall, relating_structure=storey) + + # Not anymore! + ifcopenshell.api.run("spatial.remove_container", model, product=wall) + """ self.file = file self.settings = {"product": None} for key, value in settings.items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py index f703311d35..6bf7a7cbd1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py @@ -21,13 +21,41 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None): + """Unassigns a container from a product. + + :param product: The IfcProduct to remove the containment from. + :type product: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + 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) + + # 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) + + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # The wall is in the storey + ifcopenshell.api.run("spatial.assign_container", model, product=wall, relating_structure=storey) + + # Not anymore! + ifcopenshell.api.run("spatial.unassign_container", model, product=wall) + """ self.file = file self.settings = { - "product": None, + "product": product, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): for rel in self.settings["product"].ContainedInStructure or []: