diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py
index 18731bf443..bf452c8e2b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py
@@ -22,3 +22,6 @@ One common use is spatial elements, such as how a site has multiple buildings,
and a building has multiple storeys. Another is for regular elements, such as
how a wall is made out of members and coverings.
"""
+
+from .assign_object import assign_object
+from .unassign_object import unassign_object
diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py
index 3e9f866435..c1ffdc5a16 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py
@@ -23,148 +23,144 @@ import ifcopenshell.util.placement
from typing import Union
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- relating_object: ifcopenshell.entity_instance,
- ):
- """Assigns object as an aggregate to the products
+def assign_object(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ relating_object: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """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
- 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.
+ 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.
- Typically used when you want to describe how large spaces are made up of
- smaller spaces. For example large spatial elements (e.g. sites,
- buidings) can be made out of smaller spatial elements (e.g. storeys,
- spaces).
+ Typically used when you want to describe how large spaces are made up of
+ smaller spaces. For example large spatial elements (e.g. sites,
+ buidings) can be made out of smaller spatial elements (e.g. storeys,
+ spaces).
- The largest space (typically the IfcSite) can then be aggregated in a
- project. It is requirement for all spatial structures to be directly or
- indirectly aggregated back to the IfcProject to create a hierarchy of
- spaces.
+ The largest space (typically the IfcSite) can then be aggregated in a
+ project. It is requirement for all spatial structures to be directly or
+ indirectly aggregated back to the IfcProject to create a hierarchy of
+ spaces.
- The other common usecase is when larger physical products are made up of
- smaller physical products. For example, a stair might be made out of a
- flight, a landing, a railing and so on. Or a wall might be made out of
- stud members, and coverings.
+ The other common usecase is when larger physical products are made up of
+ smaller physical products. For example, a stair might be made out of a
+ flight, a landing, a railing and so on. Or a wall might be made out of
+ stud members, and coverings.
- As a product may only have a single location in the "spatial
- decomposition" tree, assigning an aggregate relationship will remove any
- previous aggregation, containment, or nesting relationships it may have.
+ As a product may only have a single location in the "spatial
+ decomposition" tree, assigning an aggregate relationship will remove any
+ previous aggregation, containment, or nesting relationships it may have.
- IFC placements follow a convention where the placement is relative to
- its parent in the spatial hierarchy. If your product has a placement,
- its placement will be recalculated to follow this convention.
+ IFC placements follow a convention where the placement is relative to
+ its parent in the spatial hierarchy. If your product has a placement,
+ its placement will be recalculated to follow this convention.
- :param products: The list of parts of the aggregate, typically of IfcElement or
- IfcSpatialStructureElement subclass
- :type product: list[ifcopenshell.entity_instance]
- :param relating_object: The whole of the aggregate, typically an
- IfcElement or IfcSpatialStructureElement subclass
- :type relating_object: ifcopenshell.entity_instance
- :return: The IfcRelAggregate relationship instance
- or `None` if `products` was empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param products: The list of parts of the aggregate, typically of IfcElement or
+ IfcSpatialStructureElement subclass
+ :type product: list[ifcopenshell.entity_instance]
+ :param relating_object: The whole of the aggregate, typically an
+ IfcElement or IfcSpatialStructureElement subclass
+ :type relating_object: ifcopenshell.entity_instance
+ :return: The IfcRelAggregate relationship instance
+ or `None` if `products` was empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
- element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite")
- subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding")
+ project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
+ element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite")
+ 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, products=[element], relating_object=project)
+ # The project contains a site (note that project aggregation is a special case in IFC)
+ ifcopenshell.api.run("aggregate.assign_object", model, products=[element], relating_object=project)
- # The site has a building
- ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element)
- """
- self.file = file
- self.settings = {
- "products": products,
- "relating_object": relating_object,
- }
+ # The site has a building
+ ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element)
+ """
+ settings = {
+ "products": products,
+ "relating_object": relating_object,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- if not self.settings["products"]:
- return
+ if not settings["products"]:
+ return
- 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)
+ products = set(settings["products"])
+ relating_object = settings["relating_object"]
+ is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None)
- previous_aggregates_rels: set[ifcopenshell.entity_instance] = set()
- products_without_aggregates: list[ifcopenshell.entity_instance] = []
- products_with_aggregates: list[ifcopenshell.entity_instance] = []
+ previous_aggregates_rels: set[ifcopenshell.entity_instance] = set()
+ products_without_aggregates: list[ifcopenshell.entity_instance] = []
+ products_with_aggregates: list[ifcopenshell.entity_instance] = []
- # check if there is anything to change
- for product in products:
- product_rel = next(iter(product.Decomposes), None)
+ # check if there is anything to change
+ for product in products:
+ product_rel = next(iter(product.Decomposes), None)
- if product_rel is None:
- products_without_aggregates.append(product)
- continue
+ 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)
+ # 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 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
+ 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)
+ # 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", 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 = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": decomposes})
- else:
- history = decomposes.OwnerHistory
- self.file.remove(decomposes)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
-
- # assign elements to a new aggregate
- if is_decomposed_by:
- is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_decomposed_by})
+ # unassign elements from previous aggregates
+ for decomposes in previous_aggregates_rels:
+ related_objects = set(decomposes.RelatedObjects) - products
+ if related_objects:
+ decomposes.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": decomposes})
else:
- is_decomposed_by = self.file.create_entity(
- "IfcRelAggregates",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": list(products),
- "RelatingObject": relating_object,
- }
+ history = decomposes.OwnerHistory
+ file.remove(decomposes)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+
+ # assign elements to a new aggregate
+ if is_decomposed_by:
+ is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_decomposed_by})
+ else:
+ is_decomposed_by = file.create_entity(
+ "IfcRelAggregates",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": list(products),
+ "RelatingObject": relating_object,
+ }
+ )
+
+ # 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",
+ file,
+ product=product,
+ matrix=ifcopenshell.util.placement.get_local_placement(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
+ 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 c766a4f019..8a8e35a948 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py
@@ -21,60 +21,57 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]):
- """Unassigns products from their aggregate
+def unassign_object(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None:
+ """Unassigns products from their aggregate
- A product (i.e. a smaller part of a whole) may be aggregated into zero
- or one larger space or element. This function will remove that
- aggregation relationship.
+ A product (i.e. a smaller part of a whole) may be aggregated into zero
+ or one larger space or element. This function will remove that
+ aggregation relationship.
- As all physical IFC model elements must be part of a hierarchical tree
- called the "spatial decomposition", using this function will remove the
- product from that tree. This is a dangerous operation and may result in
- the product no longer being visible in IFC applications.
+ As all physical IFC model elements must be part of a hierarchical tree
+ called the "spatial decomposition", using this function will remove the
+ product from that tree. This is a dangerous operation and may result in
+ the product no longer being visible in IFC applications.
- If the product is not part of an aggregation relationship, nothing will
- happen.
+ If the product is not part of an aggregation relationship, nothing will
+ happen.
- :param products: The list of parts of the aggregate, typically of IfcElements or
- IfcSpatialStructureElement subclass
- :type product: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
+ :param products: The list of parts of the aggregate, typically of IfcElements or
+ IfcSpatialStructureElement subclass
+ :type product: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- 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, products=[subelement1], relating_object=element)
- ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element)
- # nothing is returned
- ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement1])
- # nothing is returned, relationship is removed
- ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2])
- """
- self.file = file
- self.settings = {"products": products}
+ 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, products=[subelement1], relating_object=element)
+ ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element)
+ # nothing is returned
+ ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement1])
+ # nothing is returned, relationship is removed
+ ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2])
+ """
+ settings = {"products": products}
- def execute(self) -> None:
- products = set(self.settings["products"])
- rels = set(
- rel
- for product in products
- if (rel := next((rel for rel in product.Decomposes if rel.is_a("IfcRelAggregates")), None))
- )
+ products = set(settings["products"])
+ rels = set(
+ rel
+ for product in products
+ if (rel := next((rel for rel in product.Decomposes if rel.is_a("IfcRelAggregates")), None))
+ )
- for rel in rels:
- related_objects = set(rel.RelatedObjects) - products
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for rel in rels:
+ related_objects = set(rel.RelatedObjects) - products
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py
index e0caddbe3c..31a605de5d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py
@@ -15,3 +15,5 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .edit_attributes import edit_attributes
diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py
index 1e2cd98bc5..05dbff5a5c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py
@@ -19,64 +19,49 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, product=None, attributes=None):
- """Edit the attributes of a product
+def edit_attributes(file, product=None, attributes=None) -> None:
+ """Edit the attributes of a product
- All IFC entities have attributes. Normally they can be edited directly,
- by simply assigning a new value to them. In some scenarios, you may wish
- to also ensure that ownership history is updated. This function provides
- that convenience.
+ All IFC entities have attributes. Normally they can be edited directly,
+ by simply assigning a new value to them. In some scenarios, you may wish
+ to also ensure that ownership history is updated. This function provides
+ that convenience.
- :param product: The product you want to edit. This may be any rooted IFC
- entity.
- :type product: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param product: The product you want to edit. This may be any rooted IFC
+ entity.
+ :type product: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- ifcopenshell.api.run("attribute.edit_attributes", model,
- product=element, attributes={"Name": "Waldo"})
- """
- self.file = file
- self.settings = {"product": product, "attributes": attributes or {}}
+ element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ ifcopenshell.api.run("attribute.edit_attributes", model,
+ product=element, attributes={"Name": "Waldo"})
+ """
+ settings = {"product": product, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["product"], name, value)
- if hasattr(self.settings["product"], "PredefinedType"):
- if hasattr(self.settings["product"], "ElementType"):
- if (
- self.settings["product"].ElementType is None
- and self.settings["product"].PredefinedType == "USERDEFINED"
- ):
- self.settings["product"].PredefinedType = "NOTDEFINED"
- elif (
- self.settings["product"].ElementType
- and self.settings["product"].PredefinedType != "USERDEFINED"
- ):
- self.settings["product"].PredefinedType = "USERDEFINED"
- elif hasattr(self.settings["product"], "ObjectType"):
- relating_type = ifcopenshell.util.element.get_type(self.settings["product"])
- # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
- if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None):
- self.settings["product"].ObjectType = None
- self.settings["product"].PredefinedType = None
- elif (
- self.settings["product"].ObjectType is None
- and self.settings["product"].PredefinedType == "USERDEFINED"
- ):
- self.settings["product"].PredefinedType = "NOTDEFINED"
- elif (
- self.settings["product"].ObjectType
- and self.settings["product"].PredefinedType != "USERDEFINED"
- ):
- self.settings["product"].PredefinedType = "USERDEFINED"
- if hasattr(self.settings["product"], "OwnerHistory"):
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": self.settings["product"]})
+ for name, value in settings["attributes"].items():
+ setattr(settings["product"], name, value)
+ if hasattr(settings["product"], "PredefinedType"):
+ if hasattr(settings["product"], "ElementType"):
+ if settings["product"].ElementType is None and settings["product"].PredefinedType == "USERDEFINED":
+ settings["product"].PredefinedType = "NOTDEFINED"
+ elif settings["product"].ElementType and settings["product"].PredefinedType != "USERDEFINED":
+ settings["product"].PredefinedType = "USERDEFINED"
+ elif hasattr(settings["product"], "ObjectType"):
+ relating_type = ifcopenshell.util.element.get_type(settings["product"])
+ # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818
+ if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None):
+ settings["product"].ObjectType = None
+ settings["product"].PredefinedType = None
+ elif settings["product"].ObjectType is None and settings["product"].PredefinedType == "USERDEFINED":
+ settings["product"].PredefinedType = "NOTDEFINED"
+ elif settings["product"].ObjectType and settings["product"].PredefinedType != "USERDEFINED":
+ settings["product"].PredefinedType = "USERDEFINED"
+ if hasattr(settings["product"], "OwnerHistory"):
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": settings["product"]})
diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py
index c2a0c1900d..fff4c4e7f5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py
@@ -19,3 +19,8 @@
"""Boundaries are primarily used for representing virtual interfaces between
spaces for energy analysis.
"""
+
+from .assign_connection_geometry import assign_connection_geometry
+from .copy_boundary import copy_boundary
+from .edit_attributes import edit_attributes
+from .remove_boundary import remove_boundary
diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py
index 8f23a60bb6..b184810642 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py
@@ -19,68 +19,80 @@
import ifcopenshell.util.unit
+def assign_connection_geometry(
+ file,
+ rel_space_boundary=None,
+ outer_boundary=None,
+ inner_boundaries=None,
+ location=None,
+ axis=None,
+ ref_direction=None,
+ unit_scale=None,
+) -> None:
+ """Create and assign a connection geometry to a space boundary relationship
+
+ A space boundary may optionally have a plane that represents how that
+ space is adjacent to another space, known as the connection geometry.
+ You may specify this plane in terms of an outer boundary polyline, zero
+ or more inner boundaries (such as for windows), and a positional matrix
+ for the orientation of the plane.
+
+ :param rel_space_boundary: The space boundary relationship to assign the
+ connection geometry to.
+ :type rel_space_boundary: ifcopenshell.entity_instance
+ :param outer_boundary: A list of 2D points representing an open
+ polyline. The last point will connect to the first point. Each
+ point is represented by an interable of 2 floats. The coordinates of
+ the points are relative to the positional matrix arguments.
+ :type outer_boundary: list[list[float]]
+ :param inner_boundaries: A list of zero or more inner boundaries to use
+ for the plane. Each boundary is represented by an open polyline, as
+ defined by the outer_boundary argument.
+ :type inner_boundaries: list[list[list[float]]], optional
+ :param location: The local origin of the connection geometry, defined as
+ an XYZ coordinate relative to the placement of the space that is
+ being bounded.
+ :type location: list[float]
+ :param axis: The local X axis of the connection geometry, defined as an
+ XYZ vector relative to the placement of the space that is being
+ bounded.
+ :type axis: list[float]
+ :param ref_direction: The local Z axis of the connection geometry,
+ defined as an XYZ vector relative to the placement of the space that
+ is being bounded. The Y vector is automatically derived using the
+ right hand rule.
+ :type ref_direction: list[float]
+ :param unit_scale: The unit scale as calculated by
+ ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
+ will be automatically calculated for you.
+ :type unit_scale: float, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ ifcopenshell.api.run("boundary.assign_connection_geometry", model,
+ rel_space_boundary=element,
+ outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)],
+ location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.],
+ )
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.rel_space_boundary = rel_space_boundary
+ usecase.outer_boundary = outer_boundary
+ usecase.inner_boundaries = inner_boundaries or ()
+ usecase.location = location
+ usecase.axis = axis
+ usecase.ref_direction = ref_direction
+ usecase.unit_scale = unit_scale
+ usecase.ifc_vertices = []
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, rel_space_boundary=None, outer_boundary=None, inner_boundaries=None, location=None, axis=None, ref_direction=None, unit_scale=None):
- """Create and assign a connection geometry to a space boundary relationship
-
- A space boundary may optionally have a plane that represents how that
- space is adjacent to another space, known as the connection geometry.
- You may specify this plane in terms of an outer boundary polyline, zero
- or more inner boundaries (such as for windows), and a positional matrix
- for the orientation of the plane.
-
- :param rel_space_boundary: The space boundary relationship to assign the
- connection geometry to.
- :type rel_space_boundary: ifcopenshell.entity_instance
- :param outer_boundary: A list of 2D points representing an open
- polyline. The last point will connect to the first point. Each
- point is represented by an interable of 2 floats. The coordinates of
- the points are relative to the positional matrix arguments.
- :type outer_boundary: list[list[float]]
- :param inner_boundaries: A list of zero or more inner boundaries to use
- for the plane. Each boundary is represented by an open polyline, as
- defined by the outer_boundary argument.
- :type inner_boundaries: list[list[list[float]]], optional
- :param location: The local origin of the connection geometry, defined as
- an XYZ coordinate relative to the placement of the space that is
- being bounded.
- :type location: list[float]
- :param axis: The local X axis of the connection geometry, defined as an
- XYZ vector relative to the placement of the space that is being
- bounded.
- :type axis: list[float]
- :param ref_direction: The local Z axis of the connection geometry,
- defined as an XYZ vector relative to the placement of the space that
- is being bounded. The Y vector is automatically derived using the
- right hand rule.
- :type ref_direction: list[float]
- :param unit_scale: The unit scale as calculated by
- ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
- will be automatically calculated for you.
- :type unit_scale: float, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- ifcopenshell.api.run("boundary.assign_connection_geometry", model,
- rel_space_boundary=element,
- outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)],
- location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.],
- )
- """
- self.file = file
- self.rel_space_boundary = rel_space_boundary
- self.outer_boundary = outer_boundary
- self.inner_boundaries = inner_boundaries or ()
- self.location = location
- self.axis = axis
- self.ref_direction = ref_direction
- self.unit_scale = unit_scale
- self.ifc_vertices = []
-
def execute(self):
if self.unit_scale is None:
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py
index 2f8b092c51..b051bae828 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py
@@ -19,29 +19,26 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, boundary=None):
- """Copies a space boundary
+def copy_boundary(file, boundary=None) -> None:
+ """Copies a space boundary
- :param boundary: The IfcRelSpaceBoundary you want to copy.
- :type boundary: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param boundary: The IfcRelSpaceBoundary you want to copy.
+ :type boundary: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- # A boring boundary with no geometry. Note that this boundary is
- # invalid and does not relate to any space or building element.
- boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary")
+ # A boring boundary with no geometry. Note that this boundary is
+ # invalid and does not relate to any space or building element.
+ boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary")
- # And now we have two
- boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary)
- """
- self.file = file
- self.settings = {"boundary": boundary}
+ # And now we have two
+ boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary)
+ """
+ settings = {"boundary": boundary}
- def execute(self):
- result = ifcopenshell.util.element.copy(self.file, self.settings["boundary"])
- if result.ConnectionGeometry:
- result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(self.file, result.ConnectionGeometry)
- return result
+ result = ifcopenshell.util.element.copy(file, settings["boundary"])
+ if result.ConnectionGeometry:
+ result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry)
+ return result
diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py
index 4be540f7a2..663c656dbb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py
@@ -17,45 +17,49 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, entity=None, relating_space=None, related_building_element=None, parent_boundary=None, corresponding_boundary=None):
- """Modify the relationships of a space boundary relationship
+def edit_attributes(
+ file,
+ entity=None,
+ relating_space=None,
+ related_building_element=None,
+ parent_boundary=None,
+ corresponding_boundary=None,
+) -> None:
+ """Modify the relationships of a space boundary relationship
- Currently this function is quite minimal and offers no advantage to
- manual assignment of the space boundary attributes.
+ Currently this function is quite minimal and offers no advantage to
+ manual assignment of the space boundary attributes.
- :param entity: The IfcRelSpaceBoundary to modify
- :type entity: ifcopenshell.entity_instance
- :param relating_space: The IfcSpace or IfcExternalSpatialElement that
- the space boundary is related to.
- :type relating_space: ifcopenshell.entity_instance
- :param related_building_element: The IfcElement that defines the
- boundary, typically an IfcWall.
- :type relating_space: ifcopenshell.entity_instance
- :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if
- this is an inner boundary. This can apply to 1st and 2nd level
- boundaries.
- :type parent_boundary: ifcopenshell.entity_instance,
- optional
- :param corresponding_boundary: The other IfcRelSpaceBoundary on the
- other side of the related element. The pair together represents a
- thermal boundary. This only applies to 2nd level boundaries.
- :type corresponding_boundary: ifcopenshell.entity_instance,
- optional
- :return: None
- :rtype: None
- """
- self.file = file
- self.entity = entity
- self.relating_space = relating_space
- self.related_building_element = related_building_element
- self.parent_boundary = parent_boundary
- self.corresponding_boundary = corresponding_boundary
+ :param entity: The IfcRelSpaceBoundary to modify
+ :type entity: ifcopenshell.entity_instance
+ :param relating_space: The IfcSpace or IfcExternalSpatialElement that
+ the space boundary is related to.
+ :type relating_space: ifcopenshell.entity_instance
+ :param related_building_element: The IfcElement that defines the
+ boundary, typically an IfcWall.
+ :type relating_space: ifcopenshell.entity_instance
+ :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if
+ this is an inner boundary. This can apply to 1st and 2nd level
+ boundaries.
+ :type parent_boundary: ifcopenshell.entity_instance,
+ optional
+ :param corresponding_boundary: The other IfcRelSpaceBoundary on the
+ other side of the related element. The pair together represents a
+ thermal boundary. This only applies to 2nd level boundaries.
+ :type corresponding_boundary: ifcopenshell.entity_instance,
+ optional
+ :return: None
+ :rtype: None
+ """
+ entity = entity
+ relating_space = relating_space
+ related_building_element = related_building_element
+ parent_boundary = parent_boundary
+ corresponding_boundary = corresponding_boundary
- def execute(self):
- self.entity.RelatingSpace = self.relating_space
- self.entity.RelatedBuildingElement = self.related_building_element
- if hasattr(self.entity, "ParentBoundary"):
- self.entity.ParentBoundary = self.parent_boundary
- if hasattr(self.entity, "CorrespondingBoundary"):
- self.entity.CorrespondingBoundary = self.corresponding_boundary
+ entity.RelatingSpace = relating_space
+ entity.RelatedBuildingElement = related_building_element
+ if hasattr(entity, "ParentBoundary"):
+ entity.ParentBoundary = parent_boundary
+ if hasattr(entity, "CorrespondingBoundary"):
+ entity.CorrespondingBoundary = corresponding_boundary
diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py
index 6744da820c..dadf44e3c7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py
@@ -20,36 +20,33 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, boundary=None):
- """Removes a space boundary
+def remove_boundary(file, boundary=None) -> None:
+ """Removes a space boundary
- The relating space or related building element is untouched. Only the
- boundary and its connection geometry is removed.
+ The relating space or related building element is untouched. Only the
+ boundary and its connection geometry is removed.
- :param boundary: The IfcRelSpaceBoundary you want to remove.
- :type boundary: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param boundary: The IfcRelSpaceBoundary you want to remove.
+ :type boundary: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- # A boring boundary with no geometry. Note that this boundary is
- # invalid and does not relate to any space or building element.
- boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary")
+ # A boring boundary with no geometry. Note that this boundary is
+ # invalid and does not relate to any space or building element.
+ boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary")
- # Let's remove it!
- ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary)
- """
- self.file = file
- self.settings = {"boundary": boundary}
+ # Let's remove it!
+ ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary)
+ """
+ settings = {"boundary": boundary}
- def execute(self):
- geometry = self.settings["boundary"].ConnectionGeometry
- if geometry:
- self.settings["boundary"].ConnectionGeometry = None
- ifcopenshell.util.element.remove_deep2(self.file, geometry)
- history = self.settings["boundary"].OwnerHistory
- self.file.remove(self.settings["boundary"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ geometry = settings["boundary"].ConnectionGeometry
+ if geometry:
+ settings["boundary"].ConnectionGeometry = None
+ ifcopenshell.util.element.remove_deep2(file, geometry)
+ history = settings["boundary"].OwnerHistory
+ file.remove(settings["boundary"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py
index e0caddbe3c..6616ff6f89 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py
@@ -15,3 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_classification import add_classification
+from .add_reference import add_reference
+from .edit_classification import edit_classification
+from .edit_reference import edit_reference
+from .remove_classification import remove_classification
+from .remove_reference import remove_reference
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py
index 580c036a5f..a6e251fcf2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py
@@ -22,67 +22,72 @@ import ifcopenshell.util.date
from typing import Union
+def add_classification(
+ file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]
+) -> ifcopenshell.entity_instance:
+ """Adds a new classification system to the project
+
+ External classification systems such as Uniclass or Omniclass are
+ ways of categorising elements in the AEC industry, typically
+ standardised or nominated by governments or companies. A system
+ typically contains a series of hierarchical reference codes and labels
+ like Pr_12_23_34.
+
+ Classifications may be applied to many things, not just physical
+ elements, such as doors and windows, spatial elements, tasks, cost
+ items, or even resources.
+
+ Prior to assigning classificaion references, you need to add the name
+ and metadata of the classification system that you will use in your
+ project. Classification systems may be revised over time, so this
+ metadata includes the edition date.
+
+ Common classification systems are provided as an IFC library which may
+ be downloaded from https://github.com/Moult/IfcClassification for your
+ convenience. It is advised to use these to ensure that the
+ classification metadata is standardised.
+
+ Adding a classification system will not add the entire hierarchy of
+ references available in the classification. References need to be added
+ separately. Typically, you'd only add the references that you use in
+ your project, see ifcopenshell.api.classification.add_reference for more
+ information.
+
+ :param classification: If a string is provided, it is assumed to be the
+ name of your classification system. This is necessary if you are
+ creating your own custom classification system. Alternatively, you
+ may provide an entity_instance of an IfcClassification from an IFC
+ classification library. The latter approach is preferred if you are
+ using a commonly known system such as Uniclass, as this will ensure
+ all metadata is added correctly.
+ :type classification: str,ifcopenshell.entity_instance
+ :return: The added IfcClassification element
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # Option 1: adding a custom clasification from scratch
+ ifcopenshell.api.run("classification.add_classification", model,
+ classification="MyCustomClassification")
+
+ # Option 2: adding a popular classification from a library
+ library = ifcopenshell.open("/path/to/Uniclass.ifc")
+ classification = library.by_type("IfcClassification")[0]
+ ifcopenshell.api.run("classification.add_classification", model,
+ classification=classification)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "classification": classification,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]):
- """Adds a new classification system to the project
-
- External classification systems such as Uniclass or Omniclass are
- ways of categorising elements in the AEC industry, typically
- standardised or nominated by governments or companies. A system
- typically contains a series of hierarchical reference codes and labels
- like Pr_12_23_34.
-
- Classifications may be applied to many things, not just physical
- elements, such as doors and windows, spatial elements, tasks, cost
- items, or even resources.
-
- Prior to assigning classificaion references, you need to add the name
- and metadata of the classification system that you will use in your
- project. Classification systems may be revised over time, so this
- metadata includes the edition date.
-
- Common classification systems are provided as an IFC library which may
- be downloaded from https://github.com/Moult/IfcClassification for your
- convenience. It is advised to use these to ensure that the
- classification metadata is standardised.
-
- Adding a classification system will not add the entire hierarchy of
- references available in the classification. References need to be added
- separately. Typically, you'd only add the references that you use in
- your project, see ifcopenshell.api.classification.add_reference for more
- information.
-
- :param classification: If a string is provided, it is assumed to be the
- name of your classification system. This is necessary if you are
- creating your own custom classification system. Alternatively, you
- may provide an entity_instance of an IfcClassification from an IFC
- classification library. The latter approach is preferred if you are
- using a commonly known system such as Uniclass, as this will ensure
- all metadata is added correctly.
- :type classification: str,ifcopenshell.entity_instance
- :return: The added IfcClassification element
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # Option 1: adding a custom clasification from scratch
- ifcopenshell.api.run("classification.add_classification", model,
- classification="MyCustomClassification")
-
- # Option 2: adding a popular classification from a library
- library = ifcopenshell.open("/path/to/Uniclass.ifc")
- classification = library.by_type("IfcClassification")[0]
- ifcopenshell.api.run("classification.add_classification", model,
- classification=classification)
- """
- self.file = file
- self.settings = {
- "classification": classification,
- }
-
- def execute(self) -> ifcopenshell.entity_instance:
+ def execute(self):
if isinstance(self.settings["classification"], str):
classification = self.file.createIfcClassification(Name=self.settings["classification"])
self.relate_to_project(classification)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py
index db1bab41bf..979bfc40dd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py
@@ -23,117 +23,119 @@ import ifcopenshell.util.schema
from typing import Optional, Union
+def add_reference(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ reference: Optional[ifcopenshell.entity_instance] = None,
+ identification: Optional[str] = None,
+ name: Optional[str] = None,
+ classification: Optional[ifcopenshell.entity_instance] = None,
+ is_lightweight=True,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Adds a new classification reference and assigns it to the list of products
+
+ A classification reference is a single entry such as "Pr_12_23_34" that
+ is part of an external classification system (such as Uniclass or
+ Omniclass).
+
+ References can be added to almost any object in IFC, including physical
+ objects, object types, properties, tasks, costs, resources, or even
+ resources such as profiles, documents, libraries, and so on.
+
+ Classification references can be added in two ways. Option 1) specify a
+ custom arbitrary reference, where you have to manually specify the
+ identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products").
+ Option 2) add a reference from an IFC classification library. The latter
+ is preferred if you are using a common classification system such as
+ Uniclass, as the library will be prepopulated with all the valid
+ classifications already.
+
+ Objects are allowed to have multiple classification references from
+ multiple classification systems. This means that adding a new reference
+ will not remove existing references.
+
+ References can be inherited from types. This means that if an
+ IfcWallType has a classification reference of Pr_12_23_34, then all
+ IfcWall occurrences of that type automatically get the same
+ classification of Pr_12_23_34. This means that it is more efficient to
+ assign to types where possible. If a classification reference is
+ assigned to both the type and an occurrence, then the assignment at the
+ occurrence will override the type classification.
+
+ :param product: The list of IFC objects, properties, or resources you want to
+ associate the classification reference to.
+ :type product: list[ifcopenshell.entity_instance]
+ :param reference: The classification reference entity taken from an
+ IFC classification library. If you supply this parameter, you will
+ use option 2.
+ :type reference: ifcopenshell.entity_instance, optional
+ :param identification: If you choose option 1 and do not specify a
+ reference, you may manually specify an identification code. The code
+ is typically a short identifier and may have punctuation to separate
+ the levels of hierarchy in the classificaion (e.g. Pr_12_23_34).
+ :type identification: str, optional
+ :param name: If you choose option 1 and do not specify a reference, you
+ may manually specify a name. The name is typically human readable.
+ :type name: str, optional
+ :param classification: The IfcClassification entity in your IFC model
+ (not the library, if you are doing option 2) that the reference is
+ part of.
+ :type classification: ifcopenshell.entity_instance
+ :param is_lightweight: If you are doing option 2, choose whether or not
+ to only add that particular reference (lighweight) or also add all
+ of its parent references in the classification hierarchy (not
+ lighweight). For example, adding a lightweight reference to
+ Pr_12_23_34 will only add Pr_12_23_34, but adding a heavy reference
+ to Pr_12_23_34 will also add Pr_12_23 and Pr_12. These parent
+ references merely help describe the "tree" of classifications, but
+ is generally unnecessary. Using lightweight classifications are
+ recommended and is the default.
+ :type is_lightweight: bool, optional
+
+ :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
+
+ :return: The newly added IfcClassificationReference
+ or `None` if `products` was empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
+
+ Example:
+
+ .. code:: python
+
+ # Option 1: adding and assigning a new reference from scratch
+ wall_type = model.by_type("IfcWallType")[0]
+ classification = ifcopenshell.api.run("classification.add_classification",
+ model, classification="MyCustomClassification")
+ ifcopenshell.api.run("classification.add_reference", model,
+ products=[wall_type], classification=classification,
+ identification="W_01", name="Interior Walls")
+
+ # Option 2: adding a popular classification from a library
+ library = ifcopenshell.open("/path/to/Uniclass.ifc")
+ lib_classification = library.by_type("IfcClassification")[0]
+ classification = ifcopenshell.api.run("classification.add_classification",
+ model, classification=lib_classification)
+ reference = [r for r in library.by_type("IfcClassificationReference")
+ if r.Identification == "XYZ"][0]
+ ifcopenshell.api.run("classification.add_reference", model,
+ products=[wall_type], classification=classification,
+ reference=reference)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "products": products,
+ "reference": reference,
+ "identification": identification,
+ "name": name,
+ "classification": classification,
+ "is_lightweight": is_lightweight,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- reference: Optional[ifcopenshell.entity_instance] = None,
- identification: Optional[str] = None,
- name: Optional[str] = None,
- classification: Optional[ifcopenshell.entity_instance] = None,
- is_lightweight=True,
- ):
- """Adds a new classification reference and assigns it to the list of products
-
- A classification reference is a single entry such as "Pr_12_23_34" that
- is part of an external classification system (such as Uniclass or
- Omniclass).
-
- References can be added to almost any object in IFC, including physical
- objects, object types, properties, tasks, costs, resources, or even
- resources such as profiles, documents, libraries, and so on.
-
- Classification references can be added in two ways. Option 1) specify a
- custom arbitrary reference, where you have to manually specify the
- identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products").
- Option 2) add a reference from an IFC classification library. The latter
- is preferred if you are using a common classification system such as
- Uniclass, as the library will be prepopulated with all the valid
- classifications already.
-
- Objects are allowed to have multiple classification references from
- multiple classification systems. This means that adding a new reference
- will not remove existing references.
-
- References can be inherited from types. This means that if an
- IfcWallType has a classification reference of Pr_12_23_34, then all
- IfcWall occurrences of that type automatically get the same
- classification of Pr_12_23_34. This means that it is more efficient to
- assign to types where possible. If a classification reference is
- assigned to both the type and an occurrence, then the assignment at the
- occurrence will override the type classification.
-
- :param product: The list of IFC objects, properties, or resources you want to
- associate the classification reference to.
- :type product: list[ifcopenshell.entity_instance]
- :param reference: The classification reference entity taken from an
- IFC classification library. If you supply this parameter, you will
- use option 2.
- :type reference: ifcopenshell.entity_instance, optional
- :param identification: If you choose option 1 and do not specify a
- reference, you may manually specify an identification code. The code
- is typically a short identifier and may have punctuation to separate
- the levels of hierarchy in the classificaion (e.g. Pr_12_23_34).
- :type identification: str, optional
- :param name: If you choose option 1 and do not specify a reference, you
- may manually specify a name. The name is typically human readable.
- :type name: str, optional
- :param classification: The IfcClassification entity in your IFC model
- (not the library, if you are doing option 2) that the reference is
- part of.
- :type classification: ifcopenshell.entity_instance
- :param is_lightweight: If you are doing option 2, choose whether or not
- to only add that particular reference (lighweight) or also add all
- of its parent references in the classification hierarchy (not
- lighweight). For example, adding a lightweight reference to
- Pr_12_23_34 will only add Pr_12_23_34, but adding a heavy reference
- to Pr_12_23_34 will also add Pr_12_23 and Pr_12. These parent
- references merely help describe the "tree" of classifications, but
- is generally unnecessary. Using lightweight classifications are
- recommended and is the default.
- :type is_lightweight: bool, optional
-
- :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
-
- :return: The newly added IfcClassificationReference
- or `None` if `products` was empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
-
- Example:
-
- .. code:: python
-
- # Option 1: adding and assigning a new reference from scratch
- wall_type = model.by_type("IfcWallType")[0]
- classification = ifcopenshell.api.run("classification.add_classification",
- model, classification="MyCustomClassification")
- ifcopenshell.api.run("classification.add_reference", model,
- products=[wall_type], classification=classification,
- identification="W_01", name="Interior Walls")
-
- # Option 2: adding a popular classification from a library
- library = ifcopenshell.open("/path/to/Uniclass.ifc")
- lib_classification = library.by_type("IfcClassification")[0]
- classification = ifcopenshell.api.run("classification.add_classification",
- model, classification=lib_classification)
- reference = [r for r in library.by_type("IfcClassificationReference")
- if r.Identification == "XYZ"][0]
- ifcopenshell.api.run("classification.add_reference", model,
- products=[wall_type], classification=classification,
- reference=reference)
- """
- self.file = file
- self.settings = {
- "products": products,
- "reference": reference,
- "identification": identification,
- "name": name,
- "classification": classification,
- "is_lightweight": is_lightweight,
- }
-
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
+ def execute(self):
if not self.settings["products"]:
return
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py
index 9925c59f54..7568a11d5e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, classification=None, attributes=None):
- """Edits the attributes of an IfcClassification
+def edit_classification(file, classification=None, attributes=None) -> None:
+ """Edits the attributes of an IfcClassification
- For more information about the attributes and data types of an
- IfcClassification, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcClassification, consult the IFC documentation.
- :param classification: The IfcClassification entity you want to edit
- :type classification: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param classification: The IfcClassification entity you want to edit
+ :type classification: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- classification = model.by_type("IfcClassification")[0]
- # Change the name of the classification system to "Foo"
- ifcopenshell.api.run("classification.edit_classification", model,
- classification=classification, attributes={"Name": "Foo"})
- """
- self.file = file
- self.settings = {"classification": classification, "attributes": attributes or {}}
+ classification = model.by_type("IfcClassification")[0]
+ # Change the name of the classification system to "Foo"
+ ifcopenshell.api.run("classification.edit_classification", model,
+ classification=classification, attributes={"Name": "Foo"})
+ """
+ settings = {"classification": classification, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["classification"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["classification"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py
index 4acf396adb..dc5096f38c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, reference=None, attributes=None):
- """Edits the attributes of an IfcClassificationReference
+def edit_reference(file, reference=None, attributes=None) -> None:
+ """Edits the attributes of an IfcClassificationReference
- For more information about the attributes and data types of an
- IfcClassificationReference, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcClassificationReference, consult the IFC documentation.
- :param reference: The IfcClassificationReference entity you want to edit
- :type reference: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param reference: The IfcClassificationReference entity you want to edit
+ :type reference: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- reference = model.by_type("IfcClassification")[0]
- # Change the name of the reference to "Foo"
- ifcopenshell.api.run("classification.edit_reference", model,
- reference=reference, attributes={"Name": "Foo"})
- """
- self.file = file
- self.settings = {"reference": reference, "attributes": attributes or {}}
+ reference = model.by_type("IfcClassification")[0]
+ # Change the name of the reference to "Foo"
+ ifcopenshell.api.run("classification.edit_reference", model,
+ reference=reference, attributes={"Name": "Foo"})
+ """
+ settings = {"reference": reference, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["reference"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["reference"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py
index 42a5dcacd0..42ec050d61 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py
@@ -20,30 +20,33 @@ import ifcopenshell
import ifcopenshell.util.element
+def remove_classification(file, classification=None) -> None:
+ """Removes an IfcClassification from the project and all references
+
+ The classification and all of its relationships, children references,
+ and relationships between objectse and child references are completely
+ removed from a project.
+
+ :param classification: The IfcClassification entity you want to remove
+ :type classification: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ classification = model.by_type("IfcClassification")[0]
+ ifcopenshell.api.run("classification.remove_classification", model,
+ classification=classification)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"classification": classification}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, classification=None):
- """Removes an IfcClassification from the project and all references
-
- The classification and all of its relationships, children references,
- and relationships between objectse and child references are completely
- removed from a project.
-
- :param classification: The IfcClassification entity you want to remove
- :type classification: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- classification = model.by_type("IfcClassification")[0]
- ifcopenshell.api.run("classification.remove_classification", model,
- classification=classification)
- """
- self.file = file
- self.settings = {"classification": classification}
-
def execute(self):
references = self.get_references(self.settings["classification"])
for reference in references:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py
index ea61fb002d..bad8406582 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py
@@ -21,107 +21,102 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- reference: ifcopenshell.entity_instance,
- products: list[ifcopenshell.entity_instance],
- ):
- """Removes a classification reference from the list of products
+def remove_reference(
+ file: ifcopenshell.file,
+ reference: ifcopenshell.entity_instance,
+ products: list[ifcopenshell.entity_instance],
+) -> None:
+ """Removes a classification reference from the list of products
- If the classification reference is no longer associated to any products,
- the classification reference itself is also removed.
+ If the classification reference is no longer associated to any products,
+ the classification reference itself is also removed.
- :param reference: The IfcClassificationReference entity of the
- relationship you want to remove.
- :type reference: ifcopenshell.entity_instance
- :param product: The list fo object entities of the relationship you want to
- remove.
- :type product: list[ifcopenshell.entity_instance]
+ :param reference: The IfcClassificationReference entity of the
+ relationship you want to remove.
+ :type reference: ifcopenshell.entity_instance
+ :param product: The list fo object entities of the relationship you want to
+ remove.
+ :type product: list[ifcopenshell.entity_instance]
- :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
+ :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements.
- :return: None
- :rtype: None
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- wall_type = model.by_type("IfcWallType")[0]
- classification = ifcopenshell.api.run("classification.add_classification",
- model, classification="MyCustomClassification")
- reference = ifcopenshell.api.run("classification.add_reference", model,
- products=[wall_type], classification=classification,
- identification="W_01", name="Interior Walls")
- ifcopenshell.api.run("classification.remove_reference", model,
- reference=reference, products=[wall_type])
- """
- self.file = file
- self.settings = {"reference": reference, "products": products}
+ wall_type = model.by_type("IfcWallType")[0]
+ classification = ifcopenshell.api.run("classification.add_classification",
+ model, classification="MyCustomClassification")
+ reference = ifcopenshell.api.run("classification.add_reference", model,
+ products=[wall_type], classification=classification,
+ identification="W_01", name="Interior Walls")
+ ifcopenshell.api.run("classification.remove_reference", model,
+ reference=reference, products=[wall_type])
+ """
+ settings = {"reference": reference, "products": products}
- def execute(self) -> None:
- is_ifc2x3 = self.file.schema == "IFC2X3"
- products = set(self.settings["products"])
- referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
- products -= products.difference(referenced)
+ is_ifc2x3 = file.schema == "IFC2X3"
+ products = set(settings["products"])
+ referenced = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
+ products -= products.difference(referenced)
- # all products are already unassigned from a reference
- if not products:
- return
+ # all products are already unassigned from a reference
+ if not products:
+ return
- rooted_products: set[ifcopenshell.entity_instance] = set()
- non_rooted_products: set[ifcopenshell.entity_instance] = set()
- for product in self.settings["products"]:
- if product.is_a("IfcRoot"):
- rooted_products.add(product)
+ rooted_products: set[ifcopenshell.entity_instance] = set()
+ non_rooted_products: set[ifcopenshell.entity_instance] = set()
+ for product in settings["products"]:
+ if product.is_a("IfcRoot"):
+ rooted_products.add(product)
+ else:
+ non_rooted_products.add(product)
+
+ if non_rooted_products and is_ifc2x3:
+ raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.")
+
+ if rooted_products:
+ reference_rels: set[ifcopenshell.entity_instance] = set()
+ for product in rooted_products:
+ reference_rels.update(product.HasAssociations)
+
+ reference_rels = {
+ rel
+ for rel in reference_rels
+ if rel.is_a("IfcRelAssociatesClassification") and rel.RelatingClassification == settings["reference"]
+ }
+
+ for rel in reference_rels:
+ related_objects = set(rel.RelatedObjects) - rooted_products
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else:
- non_rooted_products.add(product)
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
- if non_rooted_products and is_ifc2x3:
- raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.")
+ if non_rooted_products:
+ reference_rels: set[ifcopenshell.entity_instance] = set()
+ for product in non_rooted_products:
+ rels = getattr(product, "HasExternalReferences", None)
+ if rels is None:
+ rels = getattr(product, "HasExternalReference", [])
+ reference_rels.update(rels)
- if rooted_products:
- reference_rels: set[ifcopenshell.entity_instance] = set()
- for product in rooted_products:
- reference_rels.update(product.HasAssociations)
+ reference_rels = {rel for rel in reference_rels if rel.RelatingReference == settings["reference"]}
+ for rel in reference_rels:
+ related_objects = set(rel.RelatedResourceObjects) - non_rooted_products
+ if related_objects:
+ rel.RelatedResourceObjects = list(related_objects)
+ else:
+ file.remove(rel)
- reference_rels = {
- rel
- for rel in reference_rels
- if rel.is_a("IfcRelAssociatesClassification")
- and rel.RelatingClassification == self.settings["reference"]
- }
-
- for rel in reference_rels:
- related_objects = set(rel.RelatedObjects) - rooted_products
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
-
- if non_rooted_products:
- reference_rels: set[ifcopenshell.entity_instance] = set()
- for product in non_rooted_products:
- rels = getattr(product, "HasExternalReferences", None)
- if rels is None:
- rels = getattr(product, "HasExternalReference", [])
- reference_rels.update(rels)
-
- reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]}
- for rel in reference_rels:
- related_objects = set(rel.RelatedResourceObjects) - non_rooted_products
- if related_objects:
- rel.RelatedResourceObjects = list(related_objects)
- else:
- self.file.remove(rel)
-
- # TODO: we only handle lightweight classifications here
- referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
- if not referenced_elements:
- self.file.remove(self.settings["reference"])
+ # TODO: we only handle lightweight classifications here
+ referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
+ if not referenced_elements:
+ file.remove(settings["reference"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py
index e0caddbe3c..7309050851 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py
@@ -15,3 +15,13 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_metric import add_metric
+from .add_metric_reference import add_metric_reference
+from .add_objective import add_objective
+from .assign_constraint import assign_constraint
+from .edit_metric import edit_metric
+from .edit_objective import edit_objective
+from .remove_constraint import remove_constraint
+from .remove_metric import remove_metric
+from .unassign_constraint import unassign_constraint
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py
index b84e2f3cc9..ab0870b528 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py
@@ -19,44 +19,41 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, objective=None):
- """Add a new metric benchmark
+def add_metric(file, objective=None) -> None:
+ """Add a new metric benchmark
- Qualitative constraints may have a series of quantitative benchmarks
- linked to it known as metrics. Metrics may be parametrically linked to
- computed model properties or quantities. Metrics need to be satisfied
- to meet the objective of the constraint.
+ Qualitative constraints may have a series of quantitative benchmarks
+ linked to it known as metrics. Metrics may be parametrically linked to
+ computed model properties or quantities. Metrics need to be satisfied
+ to meet the objective of the constraint.
- :param objective: The IfcObjective that this metric is a benchmark of.
- :type objective: ifcopenshell.entity_instance
- :return: The newly created IfcMetric entity
- :rtype: ifcopenshell.entity_instance
+ :param objective: The IfcObjective that this metric is a benchmark of.
+ :type objective: ifcopenshell.entity_instance
+ :return: The newly created IfcMetric entity
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- objective = ifcopenshell.api.run("constraint.add_objective", model)
- metric = ifcopenshell.api.run("constraint.add_metric", model,
- objective=objective)
- """
- self.file = file
- self.settings = {
- "objective": objective,
+ objective = ifcopenshell.api.run("constraint.add_objective", model)
+ metric = ifcopenshell.api.run("constraint.add_metric", model,
+ objective=objective)
+ """
+ settings = {
+ "objective": objective,
+ }
+
+ metric = file.create_entity(
+ "IfcMetric",
+ **{
+ "Name": "Unnamed",
+ "ConstraintGrade": "NOTDEFINED",
+ "Benchmark": "EQUALTO",
}
-
- def execute(self):
- metric = self.file.create_entity(
- "IfcMetric",
- **{
- "Name": "Unnamed",
- "ConstraintGrade": "NOTDEFINED",
- "Benchmark": "EQUALTO",
- }
- )
- if self.settings["objective"]:
- benchmark_values = list(self.settings["objective"].BenchmarkValues or [])
- benchmark_values.append(metric)
- self.settings["objective"].BenchmarkValues = benchmark_values
- return metric
+ )
+ if settings["objective"]:
+ benchmark_values = list(settings["objective"].BenchmarkValues or [])
+ benchmark_values.append(metric)
+ settings["objective"].BenchmarkValues = benchmark_values
+ return metric
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py
index 072c71ecb0..a3c37392e2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py
@@ -18,28 +18,26 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, metric=None, reference_path=None):
- """
- Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute"
- Used to reference a value of an attribute of an instance through a metric objective entity.
- """
- self.file = file
- self.settings = {"metric": metric, "reference_path": reference_path}
- def execute(self):
- if self.settings["reference_path"]:
- attributes = self.settings["reference_path"].split(".")
- references_created = []
- for i in range(len(attributes)):
- if i == 0:
- reference = self.file.create_entity("IfcReference")
- reference.AttributeIdentifier = attributes[i]
- self.settings["metric"].ReferencePath = reference
- references_created.append(reference)
- else:
- reference = self.file.create_entity("IfcReference")
- reference.AttributeIdentifier = attributes[i]
- references_created[i-1].InnerReference = reference
- references_created.append(reference)
- return references_created
\ No newline at end of file
+def add_metric_reference(file, metric=None, reference_path=None) -> None:
+ """
+ Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute"
+ Used to reference a value of an attribute of an instance through a metric objective entity.
+ """
+ settings = {"metric": metric, "reference_path": reference_path}
+
+ if settings["reference_path"]:
+ attributes = settings["reference_path"].split(".")
+ references_created = []
+ for i in range(len(attributes)):
+ if i == 0:
+ reference = file.create_entity("IfcReference")
+ reference.AttributeIdentifier = attributes[i]
+ settings["metric"].ReferencePath = reference
+ references_created.append(reference)
+ else:
+ reference = file.create_entity("IfcReference")
+ reference.AttributeIdentifier = attributes[i]
+ references_created[i - 1].InnerReference = reference
+ references_created.append(reference)
+ return references_created
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py
index 40fb46dfd2..efce0bc080 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py
@@ -19,34 +19,31 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file):
- """Add a new objective constraint
+def add_objective(file) -> None:
+ """Add a new objective constraint
- Parametric constraints may be defined by the user. The constraint is defined
- by first creating an objective describing the purpose of the constraint and
- whether it is a hard or soft constraint. Later on, metrics may be added to
- check whether the constraint has been met by connecting it to properties and
- quantities. See ifcopenshell.api.constraint.add_metric for more information.
+ Parametric constraints may be defined by the user. The constraint is defined
+ by first creating an objective describing the purpose of the constraint and
+ whether it is a hard or soft constraint. Later on, metrics may be added to
+ check whether the constraint has been met by connecting it to properties and
+ quantities. See ifcopenshell.api.constraint.add_metric for more information.
- :return: The newly created IfcObjective entity
- :rtype: ifcopenshell.entity_instance
+ :return: The newly created IfcObjective entity
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a new objective for code compliance requirements
- objective = ifcopenshell.api.run("constraint.add_objective", model)
- objective.ConstraintGrade = "ADVISORY"
- objective.ObjectiveQualifier = "CODECOMPLIANCE"
- # Note: the objective right now is purely qualitative and for
- # information purposes. You may wish to add quantiative metrics.
- """
- self.file = file
- self.settings = {}
+ # Create a new objective for code compliance requirements
+ objective = ifcopenshell.api.run("constraint.add_objective", model)
+ objective.ConstraintGrade = "ADVISORY"
+ objective.ObjectiveQualifier = "CODECOMPLIANCE"
+ # Note: the objective right now is purely qualitative and for
+ # information purposes. You may wish to add quantiative metrics.
+ """
+ settings = {}
- def execute(self):
- return self.file.create_entity(
- "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"}
- )
+ return file.create_entity(
+ "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"}
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py
index dfc826faf8..89a80d9ab3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py
@@ -21,39 +21,41 @@ import ifcopenshell.api
from typing import Union
+def assign_constraint(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ constraint: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Assigns a constraint to a list of products
+
+ This assigns a relationship between a product and a constraint, so that
+ when a product's properties and quantities do not match the requirements
+ of the constraint's metrics, results can be flagged.
+
+ It is assumed (but not explicit in the IFC documentation) that
+ constraints are inherited from the type. This way, it is not necessary
+ to create lots of constraint assignments.
+
+ :param products: The list of products the constraint applies to. This is anything
+ which can have properties or quantities.
+ :type products: list[ifcopenshell.entity_instance]
+ :param constraint: The IfcObjective constraint
+ :type constraint: ifcopenshell.entity_instance
+ :return: The new or updated IfcRelAssociatesConstraint relationship
+ or `None` if `products` was an empty list.
+ :rtype: ifcopenshell.entity_instance
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "products": products,
+ "constraint": constraint,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- constraint: ifcopenshell.entity_instance,
- ):
- """Assigns a constraint to a list of products
-
- This assigns a relationship between a product and a constraint, so that
- when a product's properties and quantities do not match the requirements
- of the constraint's metrics, results can be flagged.
-
- It is assumed (but not explicit in the IFC documentation) that
- constraints are inherited from the type. This way, it is not necessary
- to create lots of constraint assignments.
-
- :param products: The list of products the constraint applies to. This is anything
- which can have properties or quantities.
- :type products: list[ifcopenshell.entity_instance]
- :param constraint: The IfcObjective constraint
- :type constraint: ifcopenshell.entity_instance
- :return: The new or updated IfcRelAssociatesConstraint relationship
- or `None` if `products` was an empty list.
- :rtype: ifcopenshell.entity_instance
- """
- self.file = file
- self.settings = {
- "products": products,
- "constraint": constraint,
- }
-
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
+ def execute(self):
products = set(self.settings["products"])
if not products:
return
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py
index 72fead7d88..b1ba5699ca 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, metric=None, attributes=None):
- """Edit the attributes of a metric
+def edit_metric(file, metric=None, attributes=None) -> None:
+ """Edit the attributes of a metric
- For more information about the attributes and data types of an
- IfcMetric, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMetric, consult the IFC documentation.
- :param metric: The IfcMetric you want to edit.
- :type metric: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param metric: The IfcMetric you want to edit.
+ :type metric: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- objective = ifcopenshell.api.run("constraint.add_objective", model)
- metric = ifcopenshell.api.run("constraint.add_metric", model,
- objective=objective)
- ifcopenshell.api.run("constraint.edit_metric", model,
- metric=metric, attributes={"ConstraintGrade": "HARD"})
- """
- self.file = file
- self.settings = {"metric": metric, "attributes": attributes or {}}
+ objective = ifcopenshell.api.run("constraint.add_objective", model)
+ metric = ifcopenshell.api.run("constraint.add_metric", model,
+ objective=objective)
+ ifcopenshell.api.run("constraint.edit_metric", model,
+ metric=metric, attributes={"ConstraintGrade": "HARD"})
+ """
+ settings = {"metric": metric, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["metric"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["metric"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py
index dff4985539..6ce5c597e8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, objective=None, attributes=None):
- """Edit the attributes of a objective
+def edit_objective(file, objective=None, attributes=None) -> None:
+ """Edit the attributes of a objective
- For more information about the attributes and data types of an
- IfcObjective, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcObjective, consult the IFC documentation.
- :param objective: The IfcObjective you want to edit.
- :type objective: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param objective: The IfcObjective you want to edit.
+ :type objective: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- objective = ifcopenshell.api.run("constraint.add_objective", model)
- ifcopenshell.api.run("constraint.edit_objective", model,
- objective=objective, attributes={"ConstraintGrade": "HARD"})
- """
- self.file = file
- self.settings = {"objective": objective, "attributes": attributes or {}}
+ objective = ifcopenshell.api.run("constraint.add_objective", model)
+ ifcopenshell.api.run("constraint.edit_objective", model,
+ objective=objective, attributes={"ConstraintGrade": "HARD"})
+ """
+ settings = {"objective": objective, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["objective"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["objective"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py
index e7dab1afb0..30b61fb5c7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py
@@ -20,36 +20,33 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, constraint=None):
- """Remove a constraint (typically an objective)
+def remove_constraint(file, constraint=None) -> None:
+ """Remove a constraint (typically an objective)
- Removes a constraint definition and all of its associations to any
- products. Typically this would be an IfcObjective, although technically
- you can associate IfcMetrics ith products too, though the meaning may be
- unclear.
+ Removes a constraint definition and all of its associations to any
+ products. Typically this would be an IfcObjective, although technically
+ you can associate IfcMetrics ith products too, though the meaning may be
+ unclear.
- :param constraint: The IfcObjective you want to remove.
- :type constraint: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param constraint: The IfcObjective you want to remove.
+ :type constraint: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- objective = ifcopenshell.api.run("constraint.add_objective", model)
- ifcopenshell.api.run("constraint.remove_constraint", model,
- constraint=objective)
- """
- self.file = file
- self.settings = {"constraint": constraint}
+ objective = ifcopenshell.api.run("constraint.add_objective", model)
+ ifcopenshell.api.run("constraint.remove_constraint", model,
+ constraint=objective)
+ """
+ settings = {"constraint": constraint}
- def execute(self):
- self.file.remove(self.settings["constraint"])
- for rel in self.file.by_type("IfcRelAssociatesConstraint"):
- if not rel.RelatingConstraint:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ file.remove(settings["constraint"])
+ for rel in file.by_type("IfcRelAssociatesConstraint"):
+ if not rel.RelatingConstraint:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py
index 6eaf012fa2..49203da3c5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py
@@ -17,31 +17,34 @@
# along with IfcOpenShell. If not, see .
+def remove_metric(file, metric=None) -> None:
+ """Remove a metric benchmark
+
+ Removes a metric benchmark and all of its associations to any products
+ and objectives.
+
+ :param metric: The IfcMetric you want to remove.
+ :type metric: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ objective = ifcopenshell.api.run("constraint.add_objective", model)
+ metric = ifcopenshell.api.run("constraint.add_metric", model,
+ objective=objective)
+ ifcopenshell.api.run("constraint.remove_metric", model,
+ metric=metric)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"metric": metric}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, metric=None):
- """Remove a metric benchmark
-
- Removes a metric benchmark and all of its associations to any products
- and objectives.
-
- :param metric: The IfcMetric you want to remove.
- :type metric: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- objective = ifcopenshell.api.run("constraint.add_objective", model)
- metric = ifcopenshell.api.run("constraint.add_metric", model,
- objective=objective)
- ifcopenshell.api.run("constraint.remove_metric", model,
- metric=metric)
- """
- self.file = file
- self.settings = {"metric": metric}
-
def execute(self):
if self.settings["metric"].ReferencePath:
reference = self.settings["metric"].ReferencePath
diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py
index 3b6471713e..138964e265 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py
@@ -21,31 +21,33 @@ import ifcopenshell.api
import ifcopenshell.util.element
+def unassign_constraint(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ constraint: ifcopenshell.entity_instance,
+) -> None:
+ """Unassigns a constraint from a list of products
+
+ The constraint will not be deleted and is available to be assigned to
+ other products.
+
+ :param products: The list of products the constraint applies to.
+ :type products: list[ifcopenshell.entity_instance]
+ :param constraint: The IfcObjective constraint
+ :type constraint: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "products": products,
+ "constraint": constraint,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- constraint: ifcopenshell.entity_instance,
- ):
- """Unassigns a constraint from a list of products
-
- The constraint will not be deleted and is available to be assigned to
- other products.
-
- :param products: The list of products the constraint applies to.
- :type products: list[ifcopenshell.entity_instance]
- :param constraint: The IfcObjective constraint
- :type constraint: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {
- "products": products,
- "constraint": constraint,
- }
-
def execute(self):
products = set(self.settings["products"])
if not products:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py
index e0caddbe3c..1edb3ee252 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py
@@ -15,3 +15,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_context import add_context
+from .edit_context import edit_context
+from .remove_context import remove_context
diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py
index 02156daf0b..95a0929ab6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py
@@ -17,168 +17,171 @@
# along with IfcOpenShell. If not, see .
+def add_context(file, context_type=None, context_identifier=None, target_view=None, parent=None) -> None:
+ """Adds a new geometric representation context
+
+ In IFC, physical objects may have zero, one, or multiple geometric
+ representations associated with it. For example, a building storey might
+ not have any geometry, but simply be a coordinate in space.
+ Alternatively, a wall might have a 3D body representation in the form of
+ a cuboid. As a final example, a door might also have a 3D body
+ representation of a 3D door panel and door frame, but may additionally
+ have a 2D door plan view representation of the door swing, and even a 2D
+ elevation view of the door, a 3D box representing the disabled clearance
+ zone of the door, a 2D profile representing the profile of the door to
+ cut out in a wall, and so on. In this situation, a door will have
+ multiple geometric representations.
+
+ To distinguish between the different purposes of multiple geometric
+ representations, each geometric representation must belong to a
+ geometric representation "context". There are typically always 2
+ contexts, one for 3D representations and one for 2D representations.
+ These 2 contexts then have subcontexts for things like the 3D body
+ representation, clearance representations, annotation representations,
+ and so on. Each representation of a physical IFC product (e.g. a door)
+ must be assigned to one of these subcontexts. Therefore setting up
+ appropriate contexts is critical prior to authoring any IFC model which
+ contains geometry.
+
+ There are two steps to setting up appropriate subcontexts. First, a 2D
+ and/or 3D context must be added. These must be always called the "Model"
+ context for 3D and the "Plan" context for 2D (even if the 2D geometry is
+ not a plan view). Then, one or more subcontexts are added using either
+ the "Model" or "Plan" as their parent. These subcontexts are further
+ distinguished using an "identifier" and "target view". The "identifier"
+ describes the purpose of the representation, and the "target view"
+ describes the typical diagrammatic presentation that context's geometry
+ should be viewed in. The most common identifiers you might use are:
+
+ - Body: for the actual shape of the object
+ - Box: the bounding box of the object (useful for shape analytics)
+ - Axis: the parametric line determining the shape of the object
+ - Profile: the elevation silhouette of the object, useful for cutting
+ out holes for the object to fit into host elements
+ - Footprint: the plan view silhouette of the object, useful for certain
+ quantity take-off rules
+ - Clearance: the clearance zone of the object
+ - Annotation: symbolic annotations typically used in diagrams or
+ drawings
+
+ The most common "target views" you might use are:
+
+ - MODEL_VIEW: for 3D geometry you might see in a BIM viewer
+ - PLAN_VIEW: for 2D geometry you might see in a plan representation
+ - ELEVATION_VIEW: for 2D geometry you might see in an elevation representation
+ - SECTION_VIEW: for 2D geometry you might see in a section representation
+ - GRAPH_VIEW: for 2D or 3D line or frame or path connectivity diagrams
+ you might use for structural frame analysis, axis-based parametric
+ modeling
+ - SKETCH_VIEW: for viewing abstract high-level representations such as
+ in bubble diagrams of spatial topology
+
+ This may sound like a lot, but after a few typical contexts are set up
+ at the beginning, it becomes easy to navigate and isolate geometry for
+ different purposes. There is also the concept of a target scale, which
+ represents the zoom level detail of geometry, but this is not currently
+ supported by this API. Setting up all these contexts are also optional,
+ and you may only use a single Model context and Body subcontext for
+ simple models, but this simplification sacrifices the ability of more
+ parametric or analytical usecases.
+
+ :param context_type: The type of the context, must be one of "Model" or
+ "Plan" only.
+ :type context_type: str
+ :param context_identifier: The identifier of the context, chosen from
+ one of the common identifiers above or consult the IFC documentation
+ (under the IfcShapeRepresentation page) for more details. Optional
+ for contexts, but mandatory for subcontexts.
+ :type context_identifier: str, optional
+ :param target_view: the target view of the context, chosen from one of
+ the common target views above or consult the IFC documentation
+ (under the IfcShapeRepresentation page) for more details. Optional
+ for contexts, but mandatory for subcontexts.
+ :type target_view: str, optional
+ :param parent: the parent context. Must be left as None (the default)
+ for contexts, and only set for subcontexts. Note that there are only
+ contexts and subcontexts, a subcontext cannot have any children.
+ :type parent: ifcopenshell.entity_instance, optional
+ :return: the newly created IfcGeometricRepresentationContext or
+ IfcGeometricRepresentationSubContext entity
+ :rtype: ifcopenshell.entity_instance, optional
+
+ Example:
+
+ .. code:: python
+
+ # If we plan to store 3D geometry in our IFC model, we have to setup
+ # a "Model" context.
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+
+ # And/Or, if we plan to store 2D geometry, we need a "Plan" context
+ plan = ifcopenshell.api.run("context.add_context", model, context_type="Plan")
+
+ # Now we setup the subcontexts with each of the geometric "purposes"
+ # we plan to store in our model. "Body" is by far the most important
+ # and common context, as most IFC models are assumed to be viewable
+ # in 3D.
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+
+ # The 3D Axis subcontext is important if any "axis-based" parametric
+ # geometry is going to be created. For example, a beam, or column
+ # may be drawn using a single 3D axis line, and for this we need an
+ # Axis subcontext.
+ ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=model3d)
+
+ # The 3D Box subcontext is useful for clash detection or shape
+ # analysis, or even lazy-loading of large models.
+ ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model3d)
+
+ # It's also important to have a 2D Axis subcontext for things like
+ # walls and claddings which can be drawn using a 2D axis line.
+ ifcopenshell.api.run("context.add_context", model,
+ context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent=plan)
+
+ # A 2D annotation subcontext for plan views are important for door
+ # swings, window cuts, and symbols for equipment like GPOs, fire
+ # extinguishers, and so on.
+ ifcopenshell.api.run("context.add_context", model,
+ context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan)
+
+ # You may also create 2D annotation subcontexts for sections and
+ # elevation views.
+ ifcopenshell.api.run("context.add_context", model,
+ context_type="Plan", context_identifier="Annotation", target_view="SECTION_VIEW", parent=plan)
+ ifcopenshell.api.run("context.add_context", model,
+ context_type="Plan", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent=plan)
+
+ # Let's create a new wall. The wall does not have any geometry yet.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # Let's use the "3D Body" representation we created earlier to add a
+ # new wall-like body geometry, 5 meters long, 3 meters high, and
+ # 200mm thick
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+
+ # Assign our new body geometry back to our wall
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
+
+ # Place our wall at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "context_type": context_type,
+ "parent": parent,
+ "context_identifier": context_identifier,
+ "target_view": target_view,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, context_type=None, context_identifier=None, target_view=None, parent=None):
- """Adds a new geometric representation context
-
- In IFC, physical objects may have zero, one, or multiple geometric
- representations associated with it. For example, a building storey might
- not have any geometry, but simply be a coordinate in space.
- Alternatively, a wall might have a 3D body representation in the form of
- a cuboid. As a final example, a door might also have a 3D body
- representation of a 3D door panel and door frame, but may additionally
- have a 2D door plan view representation of the door swing, and even a 2D
- elevation view of the door, a 3D box representing the disabled clearance
- zone of the door, a 2D profile representing the profile of the door to
- cut out in a wall, and so on. In this situation, a door will have
- multiple geometric representations.
-
- To distinguish between the different purposes of multiple geometric
- representations, each geometric representation must belong to a
- geometric representation "context". There are typically always 2
- contexts, one for 3D representations and one for 2D representations.
- These 2 contexts then have subcontexts for things like the 3D body
- representation, clearance representations, annotation representations,
- and so on. Each representation of a physical IFC product (e.g. a door)
- must be assigned to one of these subcontexts. Therefore setting up
- appropriate contexts is critical prior to authoring any IFC model which
- contains geometry.
-
- There are two steps to setting up appropriate subcontexts. First, a 2D
- and/or 3D context must be added. These must be always called the "Model"
- context for 3D and the "Plan" context for 2D (even if the 2D geometry is
- not a plan view). Then, one or more subcontexts are added using either
- the "Model" or "Plan" as their parent. These subcontexts are further
- distinguished using an "identifier" and "target view". The "identifier"
- describes the purpose of the representation, and the "target view"
- describes the typical diagrammatic presentation that context's geometry
- should be viewed in. The most common identifiers you might use are:
-
- - Body: for the actual shape of the object
- - Box: the bounding box of the object (useful for shape analytics)
- - Axis: the parametric line determining the shape of the object
- - Profile: the elevation silhouette of the object, useful for cutting
- out holes for the object to fit into host elements
- - Footprint: the plan view silhouette of the object, useful for certain
- quantity take-off rules
- - Clearance: the clearance zone of the object
- - Annotation: symbolic annotations typically used in diagrams or
- drawings
-
- The most common "target views" you might use are:
-
- - MODEL_VIEW: for 3D geometry you might see in a BIM viewer
- - PLAN_VIEW: for 2D geometry you might see in a plan representation
- - ELEVATION_VIEW: for 2D geometry you might see in an elevation representation
- - SECTION_VIEW: for 2D geometry you might see in a section representation
- - GRAPH_VIEW: for 2D or 3D line or frame or path connectivity diagrams
- you might use for structural frame analysis, axis-based parametric
- modeling
- - SKETCH_VIEW: for viewing abstract high-level representations such as
- in bubble diagrams of spatial topology
-
- This may sound like a lot, but after a few typical contexts are set up
- at the beginning, it becomes easy to navigate and isolate geometry for
- different purposes. There is also the concept of a target scale, which
- represents the zoom level detail of geometry, but this is not currently
- supported by this API. Setting up all these contexts are also optional,
- and you may only use a single Model context and Body subcontext for
- simple models, but this simplification sacrifices the ability of more
- parametric or analytical usecases.
-
- :param context_type: The type of the context, must be one of "Model" or
- "Plan" only.
- :type context_type: str
- :param context_identifier: The identifier of the context, chosen from
- one of the common identifiers above or consult the IFC documentation
- (under the IfcShapeRepresentation page) for more details. Optional
- for contexts, but mandatory for subcontexts.
- :type context_identifier: str, optional
- :param target_view: the target view of the context, chosen from one of
- the common target views above or consult the IFC documentation
- (under the IfcShapeRepresentation page) for more details. Optional
- for contexts, but mandatory for subcontexts.
- :type target_view: str, optional
- :param parent: the parent context. Must be left as None (the default)
- for contexts, and only set for subcontexts. Note that there are only
- contexts and subcontexts, a subcontext cannot have any children.
- :type parent: ifcopenshell.entity_instance, optional
- :return: the newly created IfcGeometricRepresentationContext or
- IfcGeometricRepresentationSubContext entity
- :rtype: ifcopenshell.entity_instance, optional
-
- Example:
-
- .. code:: python
-
- # If we plan to store 3D geometry in our IFC model, we have to setup
- # a "Model" context.
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
-
- # And/Or, if we plan to store 2D geometry, we need a "Plan" context
- plan = ifcopenshell.api.run("context.add_context", model, context_type="Plan")
-
- # Now we setup the subcontexts with each of the geometric "purposes"
- # we plan to store in our model. "Body" is by far the most important
- # and common context, as most IFC models are assumed to be viewable
- # in 3D.
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
-
- # The 3D Axis subcontext is important if any "axis-based" parametric
- # geometry is going to be created. For example, a beam, or column
- # may be drawn using a single 3D axis line, and for this we need an
- # Axis subcontext.
- ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=model3d)
-
- # The 3D Box subcontext is useful for clash detection or shape
- # analysis, or even lazy-loading of large models.
- ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model3d)
-
- # It's also important to have a 2D Axis subcontext for things like
- # walls and claddings which can be drawn using a 2D axis line.
- ifcopenshell.api.run("context.add_context", model,
- context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent=plan)
-
- # A 2D annotation subcontext for plan views are important for door
- # swings, window cuts, and symbols for equipment like GPOs, fire
- # extinguishers, and so on.
- ifcopenshell.api.run("context.add_context", model,
- context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan)
-
- # You may also create 2D annotation subcontexts for sections and
- # elevation views.
- ifcopenshell.api.run("context.add_context", model,
- context_type="Plan", context_identifier="Annotation", target_view="SECTION_VIEW", parent=plan)
- ifcopenshell.api.run("context.add_context", model,
- context_type="Plan", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent=plan)
-
- # Let's create a new wall. The wall does not have any geometry yet.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # Let's use the "3D Body" representation we created earlier to add a
- # new wall-like body geometry, 5 meters long, 3 meters high, and
- # 200mm thick
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
-
- # Assign our new body geometry back to our wall
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
-
- # Place our wall at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- """
- self.file = file
- self.settings = {
- "context_type": context_type,
- "parent": parent,
- "context_identifier": context_identifier,
- "target_view": target_view,
- }
-
def execute(self):
if not self.settings["parent"]:
if self.settings["context_type"] == "Plan":
diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py
index 50f4612c75..30f6d642b1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py
@@ -17,37 +17,34 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, context, attributes):
- """Edits the attributes of an IfcGeometricRepresentationContext
+def edit_context(file, context, attributes) -> None:
+ """Edits the attributes of an IfcGeometricRepresentationContext
- For more information about the attributes and data types of an
- IfcGeometricRepresentationContext, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcGeometricRepresentationContext, consult the IFC documentation.
- :param context: The IfcGeometricRepresentationContext entity you want to edit
- :type context: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param context: The IfcGeometricRepresentationContext entity you want to edit
+ :type context: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- # Revit had a bug where they incorrectly called the body representation a "Facetation"
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model
- )
+ model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ # Revit had a bug where they incorrectly called the body representation a "Facetation"
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model
+ )
- # Let's fix it!
- ifcopenshell.api.run("context.edit_context", model,
- context=body, attributes={"ContextIdentifier": "Body"})
- """
- self.file = file
- self.settings = {"context": context, "attributes": attributes or {}}
+ # Let's fix it!
+ ifcopenshell.api.run("context.edit_context", model,
+ context=body, attributes={"ContextIdentifier": "Body"})
+ """
+ settings = {"context": context, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["context"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["context"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py
index b0025efbdb..9ac30483cf 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py
@@ -19,49 +19,46 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, context=None):
- """Removes an IfcGeometricRepresentationContext
+def remove_context(file, context=None) -> None:
+ """Removes an IfcGeometricRepresentationContext
- Any representation geometry that is assigned to the context is also
- removed. If a context is removed, then any subcontexts are also removed.
+ Any representation geometry that is assigned to the context is also
+ removed. If a context is removed, then any subcontexts are also removed.
- :param context: The IfcGeometricRepresentationContext entity to remove
- :type context: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param context: The IfcGeometricRepresentationContext entity to remove
+ :type context: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- # Revit had a bug where they incorrectly called the body representation a "Facetation"
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model
- )
+ model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ # Revit had a bug where they incorrectly called the body representation a "Facetation"
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model
+ )
- # Let's just get rid of it completely
- ifcopenshell.api.run("context.remove_context", model, context=body)
- """
- self.file = file
- self.settings = {"context": context}
+ # Let's just get rid of it completely
+ ifcopenshell.api.run("context.remove_context", model, context=body)
+ """
+ settings = {"context": context}
- def execute(self):
- for subcontext in self.settings["context"].HasSubContexts:
- ifcopenshell.api.run("context.remove_context", self.file, context=subcontext)
+ for subcontext in settings["context"].HasSubContexts:
+ ifcopenshell.api.run("context.remove_context", file, context=subcontext)
- if getattr(self.settings["context"], "ParentContext", None):
- new = self.settings["context"].ParentContext
- for inverse in self.file.get_inverse(self.settings["context"]):
- if inverse.is_a("IfcCoordinateOperation"):
- inverse.SourceCRS = inverse.TargetCRS
- ifcopenshell.util.element.remove_deep(self.file, inverse)
- else:
- ifcopenshell.util.element.replace_attribute(inverse, self.settings["context"], new)
- self.file.remove(self.settings["context"])
- else:
- representations_in_context = self.settings["context"].RepresentationsInContext
- self.file.remove(self.settings["context"])
- for element in representations_in_context:
- ifcopenshell.api.run("geometry.remove_representation", self.file, representation=element)
+ if getattr(settings["context"], "ParentContext", None):
+ new = settings["context"].ParentContext
+ for inverse in file.get_inverse(settings["context"]):
+ if inverse.is_a("IfcCoordinateOperation"):
+ inverse.SourceCRS = inverse.TargetCRS
+ ifcopenshell.util.element.remove_deep(file, inverse)
+ else:
+ ifcopenshell.util.element.replace_attribute(inverse, settings["context"], new)
+ file.remove(settings["context"])
+ else:
+ representations_in_context = settings["context"].RepresentationsInContext
+ file.remove(settings["context"])
+ for element in representations_in_context:
+ ifcopenshell.api.run("geometry.remove_representation", file, representation=element)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py
index e0caddbe3c..792f5eec35 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py
@@ -15,3 +15,6 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .assign_control import assign_control
+from .unassign_control import unassign_control
diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py
index 4d1ecb128d..93a4fe2f35 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py
@@ -20,87 +20,81 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_control=None, related_object=None):
- """Assigns a planning control or constraint to an object
+def assign_control(file, relating_control=None, related_object=None) -> None:
+ """Assigns a planning control or constraint to an object
- IFC can describe concepts that control other objects. For example, a
- planning calendar controls the availability of working days for
- construction planning. As another example, a cost item might constrain
- or limit the ability to procure and build a product.
+ IFC can describe concepts that control other objects. For example, a
+ planning calendar controls the availability of working days for
+ construction planning. As another example, a cost item might constrain
+ or limit the ability to procure and build a product.
- This usecase lets you assign controls following the rules of the IFC
- specification. This is an advanced topic and assumes knowledge of the
- IFC concepts to determine what is allowed to control what. In the
- future, this API will likely be deprecated in favour of multiple usecase
- specific APIs.
+ This usecase lets you assign controls following the rules of the IFC
+ specification. This is an advanced topic and assumes knowledge of the
+ IFC concepts to determine what is allowed to control what. In the
+ future, this API will likely be deprecated in favour of multiple usecase
+ specific APIs.
- :param relating_control: The IfcControl entity that is creating the
- control or constraint
- :type relating_control: ifcopenshell.entity_instance
- :param related_object: The IfcObjectDefinition that is being controlled
- :type related_object: ifcopenshell.entity_instance
- :return: The newly created IfcRelAssignsToControl. If relationship already
- existed before and wasn't changed then returns None.
- :rtype: ifcopenshell.entity_instance, None
+ :param relating_control: The IfcControl entity that is creating the
+ control or constraint
+ :type relating_control: ifcopenshell.entity_instance
+ :param related_object: The IfcObjectDefinition that is being controlled
+ :type related_object: ifcopenshell.entity_instance
+ :return: The newly created IfcRelAssignsToControl. If relationship already
+ existed before and wasn't changed then returns None.
+ :rtype: ifcopenshell.entity_instance, None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # One common usecase is to assign a calendar to a task
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model)
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule)
+ # One common usecase is to assign a calendar to a task
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model)
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule)
- # All subtasks will inherit this calendar, so assigning a single
- # calendar to the root task effectively defines a "default" calendar
- ifcopenshell.api.run("control.assign_control", model,
- relating_control=calendar, related_object=task)
+ # All subtasks will inherit this calendar, so assigning a single
+ # calendar to the root task effectively defines a "default" calendar
+ ifcopenshell.api.run("control.assign_control", model,
+ relating_control=calendar, related_object=task)
- # Another common example might be relating a cost item and a product
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- cost_item = ifcopenshell.api.run("cost.add_cost_item", model,
- cost_schedule=schedule)
- ifcopenshell.api.run("control.assign_control", model,
- relating_control=cost_item, related_object=wall)
- """
- self.file = file
- self.settings = {
- "relating_control": relating_control,
- "related_object": related_object,
- }
+ # Another common example might be relating a cost item and a product
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ cost_item = ifcopenshell.api.run("cost.add_cost_item", model,
+ cost_schedule=schedule)
+ ifcopenshell.api.run("control.assign_control", model,
+ relating_control=cost_item, related_object=wall)
+ """
+ settings = {
+ "relating_control": relating_control,
+ "related_object": related_object,
+ }
- def execute(self):
- if self.settings["related_object"].HasAssignments:
- for assignment in self.settings["related_object"].HasAssignments:
- if (
- assignment.is_a("IfcRelAssignsToControl")
- and assignment.RelatingControl == self.settings["relating_control"]
- ):
- return
-
- controls = None
- if self.settings["relating_control"].Controls:
- controls = self.settings["relating_control"].Controls[0]
-
- if controls:
- if self.settings["related_object"] in controls.RelatedObjects:
+ if settings["related_object"].HasAssignments:
+ for assignment in settings["related_object"].HasAssignments:
+ if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == settings["relating_control"]:
return
- related_objects = set(controls.RelatedObjects)
- related_objects.add(self.settings["related_object"])
- controls.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": controls})
- else:
- controls = self.file.create_entity(
- "IfcRelAssignsToControl",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": [self.settings["related_object"]],
- "RelatingControl": self.settings["relating_control"],
- },
- )
- return controls
+
+ controls = None
+ if settings["relating_control"].Controls:
+ controls = settings["relating_control"].Controls[0]
+
+ if controls:
+ if settings["related_object"] in controls.RelatedObjects:
+ return
+ related_objects = set(controls.RelatedObjects)
+ related_objects.add(settings["related_object"])
+ controls.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": controls})
+ else:
+ controls = file.create_entity(
+ "IfcRelAssignsToControl",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["related_object"]],
+ "RelatingControl": settings["relating_control"],
+ },
+ )
+ return controls
diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py
index 72996ad62a..0463689c5f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py
@@ -21,54 +21,51 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_control=None, related_object=None):
- """Unassigns a planning control or constraint to an object
+def unassign_control(file, relating_control=None, related_object=None) -> None:
+ """Unassigns a planning control or constraint to an object
- :param relating_control: The IfcControl entity that is creating the
- control or constraint
- :type relating_control: ifcopenshell.entity_instance
- :param related_object: The IfcObjectDefinition that is being controlled
- :type related_object: ifcopenshell.entity_instance
- :return: If the control still is related to other objects, the
- IfcRelAssignsToControl is returned, otherwise None.
- :rtype: ifcopenshell.entity_instance, None
+ :param relating_control: The IfcControl entity that is creating the
+ control or constraint
+ :type relating_control: ifcopenshell.entity_instance
+ :param related_object: The IfcObjectDefinition that is being controlled
+ :type related_object: ifcopenshell.entity_instance
+ :return: If the control still is related to other objects, the
+ IfcRelAssignsToControl is returned, otherwise None.
+ :rtype: ifcopenshell.entity_instance, None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's relate a cost item and a product
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- cost_item = ifcopenshell.api.run("cost.add_cost_item", model,
- cost_schedule=schedule)
- ifcopenshell.api.run("control.assign_control", model,
- relating_control=cost_item, related_object=wall)
+ # Let's relate a cost item and a product
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ cost_item = ifcopenshell.api.run("cost.add_cost_item", model,
+ cost_schedule=schedule)
+ ifcopenshell.api.run("control.assign_control", model,
+ relating_control=cost_item, related_object=wall)
- # And now let's change our mind
- ifcopenshell.api.run("control.unassign_control", model,
- relating_control=cost_item, related_object=wall)
- """
+ # And now let's change our mind
+ ifcopenshell.api.run("control.unassign_control", model,
+ relating_control=cost_item, related_object=wall)
+ """
- self.file = file
- self.settings = {
- "relating_control": relating_control,
- "related_object": related_object,
- }
+ settings = {
+ "relating_control": relating_control,
+ "related_object": related_object,
+ }
- def execute(self):
- for rel in self.settings["related_object"].HasAssignments or []:
- if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != self.settings["relating_control"]:
- continue
- if len(rel.RelatedObjects) == 1:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_objects = list(rel.RelatedObjects)
- related_objects.remove(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- return rel
+ for rel in settings["related_object"].HasAssignments or []:
+ if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != settings["relating_control"]:
+ continue
+ if len(rel.RelatedObjects) == 1:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_objects = list(rel.RelatedObjects)
+ related_objects.remove(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py
index e0caddbe3c..4cf5fc63c6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py
@@ -15,3 +15,23 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_cost_item import add_cost_item
+from .add_cost_item_quantity import add_cost_item_quantity
+from .add_cost_schedule import add_cost_schedule
+from .add_cost_value import add_cost_value
+from .assign_cost_item_quantity import assign_cost_item_quantity
+from .assign_cost_value import assign_cost_value
+from .calculate_cost_item_resource_value import calculate_cost_item_resource_value
+from .copy_cost_item import copy_cost_item
+from .copy_cost_item_values import copy_cost_item_values
+from .edit_cost_item import edit_cost_item
+from .edit_cost_item_quantity import edit_cost_item_quantity
+from .edit_cost_schedule import edit_cost_schedule
+from .edit_cost_value import edit_cost_value
+from .edit_cost_value_formula import edit_cost_value_formula
+from .remove_cost_item import remove_cost_item
+from .remove_cost_item_quantity import remove_cost_item_quantity
+from .remove_cost_schedule import remove_cost_schedule
+from .remove_cost_value import remove_cost_value
+from .unassign_cost_item_quantity import unassign_cost_item_quantity
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py
index 26a0bba442..f270446cfd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py
@@ -19,55 +19,52 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, cost_schedule=None, cost_item=None):
- """Add a new cost item
+def add_cost_item(file, cost_schedule=None, cost_item=None) -> None:
+ """Add a new cost item
- A cost item represents a single line item in a cost schedule. Cost items
- may then be broken down into cost subitems.
+ A cost item represents a single line item in a cost schedule. Cost items
+ may then be broken down into cost subitems.
- :param cost_schedule: If the cost item is to be added as a root or top
- level cost item to a cost schedule, the IfcCostSchedule may be
- specified. This is mutually exlclusive to the cost_item parameter.
- :type cost_schedule: ifcopenshell.entity_instance
- :param cost_item: If the cost item is to be added as a subitem to an
- existing cost item, the parent IfcCostItem may be specified. This is
- mutually exclusive to the cost_schedule parameter.
- :type cost_item: ifcopenshell.entity_instance
- :return: The newly created IfcCostItem
- :rtype: ifcopenshell.entity_instance
+ :param cost_schedule: If the cost item is to be added as a root or top
+ level cost item to a cost schedule, the IfcCostSchedule may be
+ specified. This is mutually exlclusive to the cost_item parameter.
+ :type cost_schedule: ifcopenshell.entity_instance
+ :param cost_item: If the cost item is to be added as a subitem to an
+ existing cost item, the parent IfcCostItem may be specified. This is
+ mutually exclusive to the cost_schedule parameter.
+ :type cost_item: ifcopenshell.entity_instance
+ :return: The newly created IfcCostItem
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # The very first cost item must be in a cost schedule
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ # The very first cost item must be in a cost schedule
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- # You may add cost items as top level item in the schedule
- item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ # You may add cost items as top level item in the schedule
+ item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # Alternatively you may add them as subitems
- item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1)
- """
- self.file = file
- self.settings = {"cost_schedule": cost_schedule, "cost_item": cost_item}
+ # Alternatively you may add them as subitems
+ item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1)
+ """
+ settings = {"cost_schedule": cost_schedule, "cost_item": cost_item}
- def execute(self):
- cost_item = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcCostItem")
+ cost_item = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcCostItem")
- if self.settings["cost_schedule"]:
- self.file.create_entity(
- "IfcRelAssignsToControl",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": [cost_item],
- "RelatingControl": self.settings["cost_schedule"],
- }
- )
- elif self.settings["cost_item"]:
- ifcopenshell.api.run(
- "nest.assign_object", self.file, related_objects=[cost_item], relating_object=self.settings["cost_item"]
- )
- return cost_item
+ if settings["cost_schedule"]:
+ file.create_entity(
+ "IfcRelAssignsToControl",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [cost_item],
+ "RelatingControl": settings["cost_schedule"],
+ }
+ )
+ elif settings["cost_item"]:
+ ifcopenshell.api.run(
+ "nest.assign_object", file, related_objects=[cost_item], relating_object=settings["cost_item"]
+ )
+ return cost_item
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py
index fabe443460..47a9b3efb9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py
@@ -19,73 +19,70 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, cost_item=None, ifc_class="IfcQuantityCount"):
- """Adds a new quantity associated with a cost item
+def add_cost_item_quantity(file, cost_item=None, ifc_class="IfcQuantityCount") -> None:
+ """Adds a new quantity associated with a cost item
- Cost items calculate their subtotal by multiplying the sum of the cost
- item's "values" by the sum of the cost item's "quantities". The
- quantities may be either parametrically linked to quantities measured on
- physical product, or manually specified.
+ Cost items calculate their subtotal by multiplying the sum of the cost
+ item's "values" by the sum of the cost item's "quantities". The
+ quantities may be either parametrically linked to quantities measured on
+ physical product, or manually specified.
- The quantity must be of a particular type, common examples are:
+ The quantity must be of a particular type, common examples are:
- - IfcQuantityCount: to count the total occurrences of a product, useful
- for things like doors, windows, and furniture
- - IfcQuantityNumber: any other generic numeric quantity
- - IfcQuantityLength
- - IfcQuantityArea
- - IfcQuantityVolume
- - IfcQuantityWeight
- - IfcQuantityTime
+ - IfcQuantityCount: to count the total occurrences of a product, useful
+ for things like doors, windows, and furniture
+ - IfcQuantityNumber: any other generic numeric quantity
+ - IfcQuantityLength
+ - IfcQuantityArea
+ - IfcQuantityVolume
+ - IfcQuantityWeight
+ - IfcQuantityTime
- A cost item must not mix quantities of different types.
+ A cost item must not mix quantities of different types.
- If an IfcQuantityCount is used, then this API will automatically count
- all products that this cost item controls (see
- ifcopenshell.api.controls.assign_control) and prefill that quantity.
+ If an IfcQuantityCount is used, then this API will automatically count
+ all products that this cost item controls (see
+ ifcopenshell.api.controls.assign_control) and prefill that quantity.
- For all other quantity types, the quantity is left as zero and the user
- must either manually specify the quantity or parametrically link it
- using another API call.
+ For all other quantity types, the quantity is left as zero and the user
+ must either manually specify the quantity or parametrically link it
+ using another API call.
- :param cost_item: The IfcCostItem to add the quantity to
- :type cost_item: ifcopenshell.entity_instance
- :param ifc_class: The type of quantity to add
- :type ifc_class: str, optional
- :return: The newly created quantity entity, chosen from the ifc_class
- parameter
- :rtype: ifcopenshell.entity_instance
+ :param cost_item: The IfcCostItem to add the quantity to
+ :type cost_item: ifcopenshell.entity_instance
+ :param ifc_class: The type of quantity to add
+ :type ifc_class: str, optional
+ :return: The newly created quantity entity, chosen from the ifc_class
+ parameter
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- chair = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- ifcopenshell.api.run("control.assign_control", model,
- relating_control=cost_item, related_object=chair)
+ chair = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ ifcopenshell.api.run("control.assign_control", model,
+ relating_control=cost_item, related_object=chair)
- # Let's assume we want to count the amount of chairs to calculate our cost item
- # Because this is an IfcQuantityCount the count will be automatically set to "1" chair
- ifcopenshell.api.run("cost.add_cost_item_quantity", model,
- cost_item=item, ifc_class="IfcQuantityCount")
- """
- self.file = file
- self.settings = {"cost_item": cost_item, "ifc_class": ifc_class}
+ # Let's assume we want to count the amount of chairs to calculate our cost item
+ # Because this is an IfcQuantityCount the count will be automatically set to "1" chair
+ ifcopenshell.api.run("cost.add_cost_item_quantity", model,
+ cost_item=item, ifc_class="IfcQuantityCount")
+ """
+ settings = {"cost_item": cost_item, "ifc_class": ifc_class}
- def execute(self):
- quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed")
- quantity[3] = 0.0
- # This is a bold assumption
- # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564
- if self.settings["ifc_class"] == "IfcQuantityCount" and self.settings["cost_item"].Controls:
- count = 0
- for rel in self.settings["cost_item"].Controls:
- count += len(rel.RelatedObjects)
- quantity[3] = count
- quantities = list(self.settings["cost_item"].CostQuantities or [])
- quantities.append(quantity)
- self.settings["cost_item"].CostQuantities = quantities
- return quantity
+ quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
+ quantity[3] = 0.0
+ # This is a bold assumption
+ # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564
+ if settings["ifc_class"] == "IfcQuantityCount" and settings["cost_item"].Controls:
+ count = 0
+ for rel in settings["cost_item"].Controls:
+ count += len(rel.RelatedObjects)
+ quantity[3] = count
+ quantities = list(settings["cost_item"].CostQuantities or [])
+ quantities.append(quantity)
+ settings["cost_item"].CostQuantities = quantities
+ return quantity
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py
index fa97a893f3..d72566ae1f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py
@@ -21,48 +21,45 @@ import ifcopenshell.util.date
from datetime import datetime
-class Usecase:
- def __init__(self, file, name=None, predefined_type="NOTDEFINED"):
- """Add a new cost schedule
+def add_cost_schedule(file, name=None, predefined_type="NOTDEFINED") -> None:
+ """Add a new cost schedule
- A cost schedule is a group of cost items which typically represent a
- cost plan or breakdown of the project. This may be used as an estimate,
- bid, or actual cost.
+ A cost schedule is a group of cost items which typically represent a
+ cost plan or breakdown of the project. This may be used as an estimate,
+ bid, or actual cost.
- Alternatively, a cost schedule may also represent a schedule of rates,
- which include cost items which capture unit rates for different elements
- or processes.
+ Alternatively, a cost schedule may also represent a schedule of rates,
+ which include cost items which capture unit rates for different elements
+ or processes.
- As such, creating a cost schedule is necessary prior to creating and
- managing any cost items.
+ As such, creating a cost schedule is necessary prior to creating and
+ managing any cost items.
- :param name: The name of the cost schedule.
- :type name: str, optional
- :param predefined_type: The predefined type of the cost schedule, chosen
- from a valid type in the IFC documentation for
- IfcCostScheduleTypeEnum
- :type predefined_type: str, optional
- :return: The newly created IfcCostSchedule entity
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the cost schedule.
+ :type name: str, optional
+ :param predefined_type: The predefined type of the cost schedule, chosen
+ from a valid type in the IFC documentation for
+ IfcCostScheduleTypeEnum
+ :type predefined_type: str, optional
+ :return: The newly created IfcCostSchedule entity
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- # Now that we have a cost schedule, we may add cost items to it
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- """
- self.file = file
- self.settings = {"name": name, "predefined_type": predefined_type}
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ # Now that we have a cost schedule, we may add cost items to it
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ """
+ settings = {"name": name, "predefined_type": predefined_type}
- def execute(self):
- cost_schedule = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcCostSchedule",
- predefined_type=self.settings["predefined_type"],
- name=self.settings["name"],
- )
- cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
- return cost_schedule
+ cost_schedule = ifcopenshell.api.run(
+ "root.create_entity",
+ file,
+ ifc_class="IfcCostSchedule",
+ predefined_type=settings["predefined_type"],
+ name=settings["name"],
+ )
+ cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
+ return cost_schedule
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py
index e7a481e8b2..b6fe5f5698 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py
@@ -17,95 +17,92 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, parent=None):
- """Adds a new value or subvalue to a cost item
+def add_cost_value(file, parent=None) -> None:
+ """Adds a new value or subvalue to a cost item
- A cost item's subtotal can be specified in two ways.
+ A cost item's subtotal can be specified in two ways.
- Option 1 is by simply manually specifying the subtotal value, which
- represents the full cost of that cost item. This option occurs when a
- cost item has no quantities associated with it.
+ Option 1 is by simply manually specifying the subtotal value, which
+ represents the full cost of that cost item. This option occurs when a
+ cost item has no quantities associated with it.
- Option 2 is by specifying a unit cost value of the cost item, which is
- then multiplied by the associated quantity of the cost item, to give us
- the subtotal. This option occurs when a cost item has quantities
- associated with it.
+ Option 2 is by specifying a unit cost value of the cost item, which is
+ then multiplied by the associated quantity of the cost item, to give us
+ the subtotal. This option occurs when a cost item has quantities
+ associated with it.
- For either option 1 (full cost value) or option 2 (unit cost value), the
- cost value may be specified as a single number, or as a sum of
- subcomponents or formulas (e.g. multiplication by wastage factor, or
- adding taxes or other adjustments).
+ For either option 1 (full cost value) or option 2 (unit cost value), the
+ cost value may be specified as a single number, or as a sum of
+ subcomponents or formulas (e.g. multiplication by wastage factor, or
+ adding taxes or other adjustments).
- This function lets you add a single top level unit value to a cost item,
- or alternatively price subcomponents by using the "parent" parameter.
+ This function lets you add a single top level unit value to a cost item,
+ or alternatively price subcomponents by using the "parent" parameter.
- More advanced usage, which involves summing, subcategory-filtered costs,
- and formulas are possible but not yet documented.
+ More advanced usage, which involves summing, subcategory-filtered costs,
+ and formulas are possible but not yet documented.
- :param parent: A parent IfcCostItem, if specifying a price directly to a
- cost item, or a top-level price component. Alternatively, this can
- be set to a IfcCostValue, if specifying price subcomponents.
- :type parent: ifcopenshell.entity_instance
- :return: The newly created IfcCostValue
- :rtype: ifcopenshell.entity_instance
+ :param parent: A parent IfcCostItem, if specifying a price directly to a
+ cost item, or a top-level price component. Alternatively, this can
+ be set to a IfcCostValue, if specifying price subcomponents.
+ :type parent: ifcopenshell.entity_instance
+ :return: The newly created IfcCostValue
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # We always need a schedule first prior to adding any cost items
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ # We always need a schedule first prior to adding any cost items
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- # Option 1: This cost item will have a full cost of 42.0
- item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 42.0})
+ # Option 1: This cost item will have a full cost of 42.0
+ item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 42.0})
- # Option 2: This cost item will have a unit cost of 5.0 per unit
- # area, multiplied by the quantity of area specified explicitly as
- # 3.0, giving us a subtotal cost of 15.0.
- item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item2)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5.0})
- quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model,
- cost_item=item2, ifc_class="IfcQuantityVolume")
- ifcopenshell.api.run("cost.edit_cost_item_quantity", model,
- physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
+ # Option 2: This cost item will have a unit cost of 5.0 per unit
+ # area, multiplied by the quantity of area specified explicitly as
+ # 3.0, giving us a subtotal cost of 15.0.
+ item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item2)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5.0})
+ quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model,
+ cost_item=item2, ifc_class="IfcQuantityVolume")
+ ifcopenshell.api.run("cost.edit_cost_item_quantity", model,
+ physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
- # A cost value may also be specified in terms of the sum of its
- # subcomponents. In this case, it's broken down into 2 subvalues.
- item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1)
- subvalue1 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value)
- subvalue2 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value)
+ # A cost value may also be specified in terms of the sum of its
+ # subcomponents. In this case, it's broken down into 2 subvalues.
+ item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1)
+ subvalue1 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value)
+ subvalue2 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value)
- # This specifies that the value is the sum of all subitems
- # regardless of their cost category. The first subvalue is 2.0 and
- # the second is 3.0, giving a total value of 5.0.
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, attributes={"Category": "*"})
- ifcopenshell.api.run("cost.edit_cost_value", model,
- cost_value=subvalue1, attributes={"AppliedValue": 2.0})
- ifcopenshell.api.run("cost.edit_cost_value", model,
- cost_value=subvalue2, attributes={"AppliedValue": 3.0})
- """
- self.file = file
- self.settings = {"parent": parent}
+ # This specifies that the value is the sum of all subitems
+ # regardless of their cost category. The first subvalue is 2.0 and
+ # the second is 3.0, giving a total value of 5.0.
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, attributes={"Category": "*"})
+ ifcopenshell.api.run("cost.edit_cost_value", model,
+ cost_value=subvalue1, attributes={"AppliedValue": 2.0})
+ ifcopenshell.api.run("cost.edit_cost_value", model,
+ cost_value=subvalue2, attributes={"AppliedValue": 3.0})
+ """
+ settings = {"parent": parent}
- def execute(self):
- value = self.file.create_entity("IfcCostValue")
- if self.settings["parent"].is_a("IfcCostItem"):
- values = list(self.settings["parent"].CostValues or [])
- values.append(value)
- self.settings["parent"].CostValues = values
- elif self.settings["parent"].is_a("IfcConstructionResource"):
- values = list(self.settings["parent"].BaseCosts or [])
- values.append(value)
- self.settings["parent"].BaseCosts = values
- elif self.settings["parent"].is_a("IfcCostValue"):
- values = list(self.settings["parent"].Components or [])
- values.append(value)
- self.settings["parent"].Components = values
- return value
+ value = file.create_entity("IfcCostValue")
+ if settings["parent"].is_a("IfcCostItem"):
+ values = list(settings["parent"].CostValues or [])
+ values.append(value)
+ settings["parent"].CostValues = values
+ elif settings["parent"].is_a("IfcConstructionResource"):
+ values = list(settings["parent"].BaseCosts or [])
+ values.append(value)
+ settings["parent"].BaseCosts = values
+ elif settings["parent"].is_a("IfcCostValue"):
+ values = list(settings["parent"].Components or [])
+ values.append(value)
+ settings["parent"].Components = values
+ return value
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py
index d4c6b6be69..6c13162ed7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py
@@ -19,82 +19,82 @@
import ifcopenshell.api
+def assign_cost_item_quantity(file, cost_item=None, products=None, prop_name="") -> None:
+ """Adds a cost item quantity that is parametrically connected to a product
+
+ A cost item may have its subtotal calculated by multiplying a unit value
+ by a quantity associated with the cost item. That quantity may be either
+ manually specified or parametrically connected to a quantity on a
+ product. This API function lets you create that parametric connection.
+
+ For example, you may wish to have a cost item linked to the "NetVolume"
+ quantity on all IfcSlabs. Each quantity has a name which you can
+ specify. If the quantity is updated in-place (which should occur for
+ Native IFC applications) then the quantity for the cost item will
+ automatically update as well. If the quantity is deleted and then
+ re-added, then the parametric relationship is also lost.
+
+ This API also automatically assigns a control relationship between the
+ cost item and the product, so it is not necessary to use
+ ifcopenshell.api.control.assign_control.
+
+ :param cost_item: The IfcCostItem to assign parametric quantities to
+ :type cost_item: ifcopenshell.entity_instance
+ :param products: The IfcObjects to assign parametric quantities to
+ :type products: list[ifcopenshell.entity_instance]
+ :param prop_name: The name of the quantity. If this is not specified,
+ then it is assumed that there is no calculated quantity, and the
+ number of objects are counted instead.
+ :type prop_name: str, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+
+ # Let's imagine a unit cost of 5.0 per unit volume
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5.0})
+
+ slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab")
+ # Usually the quantity would be automatically calculated via a
+ # graphical authoring application but let's assign a manual quantity
+ # for now.
+ qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities")
+ ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0})
+
+ # Now let's parametrically link the slab's quantity to the cost
+ # item. If the slab is edited in the future and 42.0 changes, then
+ # the updated value will also automatically be applied to the cost
+ # item.
+ ifcopenshell.api.run("cost.assign_cost_item_quantity", model,
+ cost_item=item, products=[slab], prop_name="NetVolume")
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "cost_item": cost_item,
+ "products": products or [],
+ "prop_name": prop_name,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, cost_item=None, products=None, prop_name=""):
- """Adds a cost item quantity that is parametrically connected to a product
-
- A cost item may have its subtotal calculated by multiplying a unit value
- by a quantity associated with the cost item. That quantity may be either
- manually specified or parametrically connected to a quantity on a
- product. This API function lets you create that parametric connection.
-
- For example, you may wish to have a cost item linked to the "NetVolume"
- quantity on all IfcSlabs. Each quantity has a name which you can
- specify. If the quantity is updated in-place (which should occur for
- Native IFC applications) then the quantity for the cost item will
- automatically update as well. If the quantity is deleted and then
- re-added, then the parametric relationship is also lost.
-
- This API also automatically assigns a control relationship between the
- cost item and the product, so it is not necessary to use
- ifcopenshell.api.control.assign_control.
-
- :param cost_item: The IfcCostItem to assign parametric quantities to
- :type cost_item: ifcopenshell.entity_instance
- :param products: The IfcObjects to assign parametric quantities to
- :type products: list[ifcopenshell.entity_instance]
- :param prop_name: The name of the quantity. If this is not specified,
- then it is assumed that there is no calculated quantity, and the
- number of objects are counted instead.
- :type prop_name: str, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
-
- # Let's imagine a unit cost of 5.0 per unit volume
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5.0})
-
- slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab")
- # Usually the quantity would be automatically calculated via a
- # graphical authoring application but let's assign a manual quantity
- # for now.
- qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities")
- ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0})
-
- # Now let's parametrically link the slab's quantity to the cost
- # item. If the slab is edited in the future and 42.0 changes, then
- # the updated value will also automatically be applied to the cost
- # item.
- ifcopenshell.api.run("cost.assign_cost_item_quantity", model,
- cost_item=item, products=[slab], prop_name="NetVolume")
- """
- self.file = file
- self.settings = {
- "cost_item": cost_item,
- "products": products or [],
- "prop_name": prop_name,
- }
-
def execute(self):
if self.settings["prop_name"]:
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for product in self.settings["products"]:
- self.assign_cost_control(
- related_object=product, cost_item=self.settings["cost_item"]
- )
+ self.assign_cost_control(related_object=product, cost_item=self.settings["cost_item"])
if self.settings["prop_name"]:
if (
self.settings["cost_item"].CostQuantities
- and self.settings["cost_item"].CostQuantities[0].Name.lower()
- != self.settings["prop_name"].lower()
+ and self.settings["cost_item"].CostQuantities[0].Name.lower() != self.settings["prop_name"].lower()
) or not product.is_a("IfcObject"):
continue
self.add_quantity_from_related_object(product)
@@ -120,10 +120,7 @@ class Usecase:
if not qto.is_a("IfcElementQuantity"):
return
for prop in qto.Quantities:
- if (
- prop.is_a("IfcPhysicalSimpleQuantity")
- and prop.Name.lower() == self.settings["prop_name"].lower()
- ):
+ if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower():
self.quantities.add(prop)
def update_cost_item_count(self):
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py
index fb89fe7432..18bb05694f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py
@@ -19,60 +19,57 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, cost_item=None, cost_rate=None):
- """Assigns a cost value to a cost item from a schedule of rates
+def assign_cost_value(file, cost_item=None, cost_rate=None) -> None:
+ """Assigns a cost value to a cost item from a schedule of rates
- Instead of assigning cost values from scratch for each cost item in a
- cost schedule, the cost values may instead be assigned from a schedule
- of rates.
+ Instead of assigning cost values from scratch for each cost item in a
+ cost schedule, the cost values may instead be assigned from a schedule
+ of rates.
- A schedule of rates is just another cost schedule which have cost values
- but no quantities. This API will allow you to "copy" the values from a
- cost item in the schedule of rates into another cost item in your own
- cost schedule. When the schedule of rates value is updated, then your
- cost item values will also be updated. You can think of the schedule of
- rates as a "template" to quickly populate your rates from.
+ A schedule of rates is just another cost schedule which have cost values
+ but no quantities. This API will allow you to "copy" the values from a
+ cost item in the schedule of rates into another cost item in your own
+ cost schedule. When the schedule of rates value is updated, then your
+ cost item values will also be updated. You can think of the schedule of
+ rates as a "template" to quickly populate your rates from.
- :param cost_item: The IfcCostItem that you want to copy the values to
- :type cost_item: ifcopenshell.entity_instance
- :param cost_rate: The IfcCostItem that you want to copy the values from
- :type cost_rate: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param cost_item: The IfcCostItem that you want to copy the values to
+ :type cost_item: ifcopenshell.entity_instance
+ :param cost_rate: The IfcCostItem that you want to copy the values from
+ :type cost_rate: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a schedule of rates with a single rate in it of 5.0
- rate_tables = ifcopenshell.api.run("cost.add_cost_schedule", model,
- predefined_type="SCHEDULEOFRATES")
- rate = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=rate)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5.0})
+ # Let's create a schedule of rates with a single rate in it of 5.0
+ rate_tables = ifcopenshell.api.run("cost.add_cost_schedule", model,
+ predefined_type="SCHEDULEOFRATES")
+ rate = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=rate)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5.0})
- # And this schedule will be for our actual cost plan / estimate / etc
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ # And this schedule will be for our actual cost plan / estimate / etc
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # Now the cost item has the same rate as the one from the schedule of rate's item
- ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate)
- """
- self.file = file
- self.settings = {"cost_item": cost_item, "cost_rate": cost_rate}
+ # Now the cost item has the same rate as the one from the schedule of rate's item
+ ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate)
+ """
+ settings = {"cost_item": cost_item, "cost_rate": cost_rate}
- def execute(self):
- if self.settings["cost_item"].CostValues:
- [
- ifcopenshell.api.run(
- "cost.remove_cost_value",
- self.file,
- parent=self.settings["cost_item"],
- cost_value=cost_value,
- )
- for cost_value in self.settings["cost_item"].CostValues
- ]
- # This is an assumption, and not part of the official IFC documentation
- self.settings["cost_item"].CostValues = self.settings["cost_rate"].CostValues
+ if settings["cost_item"].CostValues:
+ [
+ ifcopenshell.api.run(
+ "cost.remove_cost_value",
+ file,
+ parent=settings["cost_item"],
+ cost_value=cost_value,
+ )
+ for cost_value in settings["cost_item"].CostValues
+ ]
+ # This is an assumption, and not part of the official IFC documentation
+ settings["cost_item"].CostValues = settings["cost_rate"].CostValues
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py
index b82e5948a6..c977be1b19 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py
@@ -21,100 +21,97 @@ import ifcopenshell.util.date
import ifcopenshell.util.resource
-class Usecase:
- def __init__(self, file, cost_item=None):
- """Calculates the total cost of all resources associated with a cost item
+def calculate_cost_item_resource_value(file, cost_item=None) -> None:
+ """Calculates the total cost of all resources associated with a cost item
- A cost item may have construction resources (e.g. equipment, material,
- etc) assigned to it. Construction resources may be assigned directly to
- the cost item, or assigned first to a task, and the task is then
- assigned to the cost item.
+ A cost item may have construction resources (e.g. equipment, material,
+ etc) assigned to it. Construction resources may be assigned directly to
+ the cost item, or assigned first to a task, and the task is then
+ assigned to the cost item.
- The cost of a resource is calculated by the total sum of all of its base
- costs. If no quantity is provided, that sum is considered to be the
- total cost. Otherwise, it is considered to be a unit cost, and is then
- multiplied by the resource quantity. The quantity is either stored as a
- base quantity (such as a volume) for a things like material resources,
- or as a duration as a daily rate for labour resources.
+ The cost of a resource is calculated by the total sum of all of its base
+ costs. If no quantity is provided, that sum is considered to be the
+ total cost. Otherwise, it is considered to be a unit cost, and is then
+ multiplied by the resource quantity. The quantity is either stored as a
+ base quantity (such as a volume) for a things like material resources,
+ or as a duration as a daily rate for labour resources.
- The final calculated cost is set as the cost item's value. Any
- previously existing values are removed.
+ The final calculated cost is set as the cost item's value. Any
+ previously existing values are removed.
- :param cost_item: The IfcCostItem to calculate
- :type cost_item: ifccopenshell.entity_instance.entity_instance
- :return: None
- :rtype: None
+ :param cost_item: The IfcCostItem to calculate
+ :type cost_item: ifccopenshell.entity_instance.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # First, we need a cost schedule and item
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ # First, we need a cost schedule and item
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # Let's imagine we have our own formworking crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Let's imagine we have our own formworking crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # ... and they need concrete
- concrete = ifcopenshell.api.run("resource.add_resource", model,
- ifc_class="IfcConstructionMaterialResource", parent_resource=crew)
- ifcopenshell.api.run("control.assign_control", model,
- relating_control=item, related_object=concrete)
- # ... which has a unit price of 42.0 per m3
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 42.0})
- # ... and a volume of 200m3
- quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=concrete, ifc_class="IfcQuantityVolume")
- ifcopenshell.api.run("resource.edit_resource_quantity", model,
- physical_quantity=quantity, "attributes": {"VolumeValue": 200.0})
+ # ... and they need concrete
+ concrete = ifcopenshell.api.run("resource.add_resource", model,
+ ifc_class="IfcConstructionMaterialResource", parent_resource=crew)
+ ifcopenshell.api.run("control.assign_control", model,
+ relating_control=item, related_object=concrete)
+ # ... which has a unit price of 42.0 per m3
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 42.0})
+ # ... and a volume of 200m3
+ quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=concrete, ifc_class="IfcQuantityVolume")
+ ifcopenshell.api.run("resource.edit_resource_quantity", model,
+ physical_quantity=quantity, "attributes": {"VolumeValue": 200.0})
- # Let's say they also need some equipment
- equipment = ifcopenshell.api.run("resource.add_resource", model,
- ifc_class="IfcConstructionEquipmentResource", parent_resource=crew)
- ifcopenshell.api.run("control.assign_control", model,
- relating_control=item, related_object=equipment)
- # ... with a fixed price of 50,000
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 42.0})
+ # Let's say they also need some equipment
+ equipment = ifcopenshell.api.run("resource.add_resource", model,
+ ifc_class="IfcConstructionEquipmentResource", parent_resource=crew)
+ ifcopenshell.api.run("control.assign_control", model,
+ relating_control=item, related_object=equipment)
+ # ... with a fixed price of 50,000
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 42.0})
- # (42 * 200) + 50000 = 58400 is our calculated cost
- ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item)
- """
- self.file = file
- self.settings = {"cost_item": cost_item}
+ # (42 * 200) + 50000 = 58400 is our calculated cost
+ ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item)
+ """
+ settings = {"cost_item": cost_item}
- def execute(self):
- for cost_value in self.settings["cost_item"].CostValues or []:
- ifcopenshell.api.run(
- "cost.remove_cost_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value
- )
+ for cost_value in settings["cost_item"].CostValues or []:
+ ifcopenshell.api.run("cost.remove_cost_value", file, parent=settings["cost_item"], cost_value=cost_value)
- resources = []
- for rel in self.settings["cost_item"].Controls or []:
- for related_object in rel.RelatedObjects:
- if related_object.is_a("IfcConstructionResource"):
- resources.append(related_object)
- elif related_object.is_a("IfcTask"):
- for rel2 in related_object.OperatesOn or []:
- for related_object2 in rel2.RelatedObjects:
- if related_object2.is_a("IfcConstructionResource"):
- resources.append(related_object2)
+ resources = []
+ for rel in settings["cost_item"].Controls or []:
+ for related_object in rel.RelatedObjects:
+ if related_object.is_a("IfcConstructionResource"):
+ resources.append(related_object)
+ elif related_object.is_a("IfcTask"):
+ for rel2 in related_object.OperatesOn or []:
+ for related_object2 in rel2.RelatedObjects:
+ if related_object2.is_a("IfcConstructionResource"):
+ resources.append(related_object2)
- for resource in resources:
- cost, unit = ifcopenshell.util.resource.get_cost(resource)
- if not cost:
- cost, unit = ifcopenshell.util.resource.get_parent_cost(resource) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data.
- quantity = ifcopenshell.util.resource.get_quantity(resource)
- if not cost or not quantity:
- continue
- if unit and "day" in unit:
- quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar
- quantity = round(quantity, 2)
- formula = "{}*{}".format(cost, quantity)
- cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.settings["cost_item"])
- cost_value.Name = resource.Name
- ifcopenshell.api.run("cost.edit_cost_value_formula", self.file, cost_value=cost_value, formula=formula)
\ No newline at end of file
+ for resource in resources:
+ cost, unit = ifcopenshell.util.resource.get_cost(resource)
+ if not cost:
+ cost, unit = ifcopenshell.util.resource.get_parent_cost(
+ resource
+ ) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data.
+ quantity = ifcopenshell.util.resource.get_quantity(resource)
+ if not cost or not quantity:
+ continue
+ if unit and "day" in unit:
+ quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar
+ quantity = round(quantity, 2)
+ formula = "{}*{}".format(cost, quantity)
+ cost_value = ifcopenshell.api.run("cost.add_cost_value", file, parent=settings["cost_item"])
+ cost_value.Name = resource.Name
+ ifcopenshell.api.run("cost.edit_cost_value_formula", file, cost_value=cost_value, formula=formula)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py
index ec2d147731..13927088ed 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py
@@ -21,35 +21,38 @@ import ifcopenshell.api
import ifcopenshell.util.element
+def copy_cost_item(file, cost_item=None) -> None:
+ """Copies all cost items and related relationships
+
+ The following relationships are also duplicated:
+
+ * The copy will have the same attributes and property sets as the original cost item
+ * The copy will be assigned to the parent cost schedule
+ * The copy will have duplicated nested cost items
+
+ :param cost_item: The cost item to be duplicated
+ :type cost_item: ifcopenshell.entity_instance
+ :return: The duplicated cost item or the list of duplicated cost items if the latter has children
+ :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
+
+ Example:
+ .. code:: python
+
+ # We have a cost item
+ cost_item = CostItem(name="Design new feature", deadline="2023-03-01")
+
+ # And now we have two
+ duplicated_cost_item = project.duplicate_cost_item(cost_item)
+
+
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"cost_item": cost_item}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, cost_item=None):
- """Copies all cost items and related relationships
-
- The following relationships are also duplicated:
-
- * The copy will have the same attributes and property sets as the original cost item
- * The copy will be assigned to the parent cost schedule
- * The copy will have duplicated nested cost items
-
- :param cost_item: The cost item to be duplicated
- :type cost_item: ifcopenshell.entity_instance
- :return: The duplicated cost item or the list of duplicated cost items if the latter has children
- :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
-
- Example:
- .. code:: python
-
- # We have a cost item
- cost_item = CostItem(name="Design new feature", deadline="2023-03-01")
-
- # And now we have two
- duplicated_cost_item = project.duplicate_cost_item(cost_item)
-
-
- """
- self.file = file
- self.settings = {"cost_item": cost_item}
-
def execute(self):
self.new_cost_items = []
self.duplicate_cost_item(self.settings["cost_item"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py
index 6bc0677b28..8ccb0a4158 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py
@@ -20,45 +20,42 @@ import ifcopenshell.util.element
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, source=None, destination=None):
- """Copies all cost values from one cost item to another
+def copy_cost_item_values(file, source=None, destination=None) -> None:
+ """Copies all cost values from one cost item to another
- Any previously existing values will be removed. The entire value is
- copied, including all components and formulas. However they are not
- parametrically linked, so if one value changes, the other will not.
+ Any previously existing values will be removed. The entire value is
+ copied, including all components and formulas. However they are not
+ parametrically linked, so if one value changes, the other will not.
- :param source: The IfcCostItem to copy cost values from
- :type source: ifcopenshell.entity_instance
- :param destination: The IfcCostItem to copy cost values from
- :type destination: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param source: The IfcCostItem to copy cost values from
+ :type source: ifcopenshell.entity_instance
+ :param destination: The IfcCostItem to copy cost values from
+ :type destination: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Assume we have a schedule with multiple items in it
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ # Assume we have a schedule with multiple items in it
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # One of the items has a value
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5000.0})
+ # One of the items has a value
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5000.0})
- # Let's copy the value from one item to another
- ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2)
- """
- self.file = file
- self.settings = {"source": source, "destination": destination}
+ # Let's copy the value from one item to another
+ ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2)
+ """
+ settings = {"source": source, "destination": destination}
- def execute(self):
- for cost_value in self.settings["destination"].CostValues or []:
- ifcopenshell.api.run("cost.remove_cost_item_value", self.file, cost_value=cost_value)
- copied_cost_values = []
- for cost_value in self.settings["source"].CostValues or []:
- copied_cost_values.append(ifcopenshell.util.element.copy_deep(self.file, cost_value))
- self.settings["destination"].CostValues = copied_cost_values
+ for cost_value in settings["destination"].CostValues or []:
+ ifcopenshell.api.run("cost.remove_cost_item_value", file, cost_value=cost_value)
+ copied_cost_values = []
+ for cost_value in settings["source"].CostValues or []:
+ copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value))
+ settings["destination"].CostValues = copied_cost_values
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py
index cc0a187177..2bf72a5d57 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, cost_item=None, attributes=None):
- """Edits the attributes of an IfcCostItem
+def edit_cost_item(file, cost_item=None, attributes=None) -> None:
+ """Edits the attributes of an IfcCostItem
- For more information about the attributes and data types of an
- IfcCostItem, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcCostItem, consult the IFC documentation.
- :param cost_item: The IfcCostItem entity you want to edit
- :type cost_item: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param cost_item: The IfcCostItem entity you want to edit
+ :type cost_item: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"})
- """
- self.file = file
- self.settings = {"cost_item": cost_item, "attributes": attributes or {}}
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"})
+ """
+ settings = {"cost_item": cost_item, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["cost_item"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["cost_item"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py
index 3ba4e9f498..178816a593 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py
@@ -17,39 +17,36 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, physical_quantity=None, attributes=None):
- """Edits the attributes of an IfcPhysicalQuantity
+def edit_cost_item_quantity(file, physical_quantity=None, attributes=None) -> None:
+ """Edits the attributes of an IfcPhysicalQuantity
- For more information about the attributes and data types of an
- IfcPhysicalQuantity, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcPhysicalQuantity, consult the IFC documentation.
- :param physical_quantity: The IfcPhysicalQuantity entity you want to edit
- :type physical_quantity: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param physical_quantity: The IfcPhysicalQuantity entity you want to edit
+ :type physical_quantity: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # This cost item will have a unit cost of 5 and a volume of 3
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5.0})
- quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model,
- cost_item=item, ifc_class="IfcQuantityVolume")
- ifcopenshell.api.run("cost.edit_cost_item_quantity", model,
- physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
- """
- self.file = file
- self.settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}}
+ # This cost item will have a unit cost of 5 and a volume of 3
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5.0})
+ quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model,
+ cost_item=item, ifc_class="IfcQuantityVolume")
+ ifcopenshell.api.run("cost.edit_cost_item_quantity", model,
+ physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
+ """
+ settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["physical_quantity"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["physical_quantity"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py
index bdfb856cc1..3e47f3a430 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, cost_schedule=None, attributes=None):
- """Edits the attributes of an IfcCostSchedule
+def edit_cost_schedule(file, cost_schedule=None, attributes=None) -> None:
+ """Edits the attributes of an IfcCostSchedule
- For more information about the attributes and data types of an
- IfcCostSchedule, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcCostSchedule, consult the IFC documentation.
- :param cost_schedule: The IfcCostSchedule entity you want to edit
- :type cost_schedule: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param cost_schedule: The IfcCostSchedule entity you want to edit
+ :type cost_schedule: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- ifcopenshell.api.run("cost.edit_cost_schedule", model,
- cost_schedule=schedule, attributes={"Name": "Foo"})
- """
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ ifcopenshell.api.run("cost.edit_cost_schedule", model,
+ cost_schedule=schedule, attributes={"Name": "Foo"})
+ """
- self.file = file
- self.settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}}
+ settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["cost_schedule"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["cost_schedule"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py
index 75ce055eb1..430b4272aa 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py
@@ -21,48 +21,45 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, cost_value=None, attributes=None):
- """Edits the attributes of an IfcCostValue
+def edit_cost_value(file, cost_value=None, attributes=None) -> None:
+ """Edits the attributes of an IfcCostValue
- For more information about the attributes and data types of an
- IfcCostValue, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcCostValue, consult the IFC documentation.
- :param cost_value: The IfcCostValue entity you want to edit
- :type cost_value: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param cost_value: The IfcCostValue entity you want to edit
+ :type cost_value: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # This cost item will have a total cost of 42
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 42.0})
- """
- self.file = file
- self.settings = {"cost_value": cost_value, "attributes": attributes or {}}
+ # This cost item will have a total cost of 42
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 42.0})
+ """
+ settings = {"cost_value": cost_value, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- if name == "AppliedValue" and value is not None:
- # TODO: support all applied value select types
- value = self.file.createIfcMonetaryMeasure(value)
- elif name == "UnitBasis":
- old_unit_basis = self.settings["cost_value"].UnitBasis
- if value:
- value_component = self.file.create_entity(
- ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType),
- value["ValueComponent"],
- )
- value = self.file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"])
- if old_unit_basis and len(self.file.get_inverse(old_unit_basis)) == 0:
- ifcopenshell.util.element.remove_deep(self.file, old_unit_basis)
- setattr(self.settings["cost_value"], name, value)
+ for name, value in settings["attributes"].items():
+ if name == "AppliedValue" and value is not None:
+ # TODO: support all applied value select types
+ value = file.createIfcMonetaryMeasure(value)
+ elif name == "UnitBasis":
+ old_unit_basis = settings["cost_value"].UnitBasis
+ if value:
+ value_component = file.create_entity(
+ ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType),
+ value["ValueComponent"],
+ )
+ value = file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"])
+ if old_unit_basis and len(file.get_inverse(old_unit_basis)) == 0:
+ ifcopenshell.util.element.remove_deep(file, old_unit_basis)
+ setattr(settings["cost_value"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py
index 8dada5dc98..eac443ba40 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py
@@ -22,37 +22,40 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element
+def edit_cost_value_formula(file, cost_value=None, formula=None) -> None:
+ """Sets a cost value based on a formula, similar to formulas in spreadsheets
+
+ Costs may be made up of many components (e.g. labour, material, waste
+ factor, taxes, etc). This can be easily represented in the form of a
+ formula similar thta would be used in spreadsheet applications.
+
+ For more information, see ifcopenshell.util.cost
+
+ :param cost_value: The IfcCostValue to set the values of
+ :type cost_value: ifcopenshell.entity_instance
+ :param formula: The formula following the language of ifcopenshell.util.cost
+ :type formula: str
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value,
+ formula="5000 * 1.19")
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"cost_value": cost_value, "formula": formula or {}}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, cost_value=None, formula=None):
- """Sets a cost value based on a formula, similar to formulas in spreadsheets
-
- Costs may be made up of many components (e.g. labour, material, waste
- factor, taxes, etc). This can be easily represented in the form of a
- formula similar thta would be used in spreadsheet applications.
-
- For more information, see ifcopenshell.util.cost
-
- :param cost_value: The IfcCostValue to set the values of
- :type cost_value: ifcopenshell.entity_instance
- :param formula: The formula following the language of ifcopenshell.util.cost
- :type formula: str
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
-
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value,
- formula="5000 * 1.19")
- """
- self.file = file
- self.settings = {"cost_value": cost_value, "formula": formula or {}}
-
def execute(self):
try:
data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py
index 5596ceff13..e52fd655cb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py
@@ -21,48 +21,45 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, cost_item=None):
- """Removes a cost item
+def remove_cost_item(file, cost_item=None) -> None:
+ """Removes a cost item
- All associated relationships with the cost item are also removed,
- however the related resources, products, and tasks themselves are
- retained.
+ All associated relationships with the cost item are also removed,
+ however the related resources, products, and tasks themselves are
+ retained.
- :param cost_item: The IfcCostItem entity you want to remove
- :type cost_item: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param cost_item: The IfcCostItem entity you want to remove
+ :type cost_item: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item)
- """
- self.file = file
- self.settings = {"cost_item": cost_item}
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item)
+ """
+ settings = {"cost_item": cost_item}
- def execute(self):
- # TODO: do a deep purge
- for inverse in self.file.get_inverse(self.settings["cost_item"]):
- if inverse.is_a("IfcRelNests"):
- if inverse.RelatingObject == self.settings["cost_item"]:
- for related_object in inverse.RelatedObjects:
- ifcopenshell.api.run("cost.remove_cost_item", self.file, cost_item=related_object)
- elif inverse.RelatedObjects == (self.settings["cost_item"],):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelAssignsToControl"):
+ # TODO: do a deep purge
+ for inverse in file.get_inverse(settings["cost_item"]):
+ if inverse.is_a("IfcRelNests"):
+ if inverse.RelatingObject == settings["cost_item"]:
+ for related_object in inverse.RelatedObjects:
+ ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object)
+ elif inverse.RelatedObjects == (settings["cost_item"],):
history = inverse.OwnerHistory
- self.file.remove(inverse)
+ file.remove(inverse)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["cost_item"].OwnerHistory
- self.file.remove(self.settings["cost_item"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelAssignsToControl"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["cost_item"].OwnerHistory
+ file.remove(settings["cost_item"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py
index fae8e1cd37..eed3a7adb3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py
@@ -17,40 +17,37 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, cost_item=None, physical_quantity=None):
- """Removes a quantity assigned to a cost item
+def remove_cost_item_quantity(file, cost_item=None, physical_quantity=None) -> None:
+ """Removes a quantity assigned to a cost item
- If the quantity is part of a product (e.g. wall), then the quantity will
- still exist and merely the relationship to the cost item will be
- removed.
+ If the quantity is part of a product (e.g. wall), then the quantity will
+ still exist and merely the relationship to the cost item will be
+ removed.
- :param cost_item: The IfcCostItem that the quantity is assigned to
- :type cost_item: ifcopenshell.entity_instance
- :param physical_quantity: The IfcPhysicalQuantity to remove
- :type physical_quantity: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param cost_item: The IfcCostItem that the quantity is assigned to
+ :type cost_item: ifcopenshell.entity_instance
+ :param physical_quantity: The IfcPhysicalQuantity to remove
+ :type physical_quantity: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model,
- cost_item=item, ifc_class="IfcQuantityVolume")
- # Let's change our mind and delete it
- ifcopenshell.api.run("cost.remove_cost_item", model,
- cost_item=item, physical_quantity=quantity)
- """
- self.file = file
- self.settings = {"cost_item": cost_item, "physical_quantity": physical_quantity}
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model,
+ cost_item=item, ifc_class="IfcQuantityVolume")
+ # Let's change our mind and delete it
+ ifcopenshell.api.run("cost.remove_cost_item", model,
+ cost_item=item, physical_quantity=quantity)
+ """
+ settings = {"cost_item": cost_item, "physical_quantity": physical_quantity}
- def execute(self):
- if len(self.file.get_inverse(self.settings["physical_quantity"])) == 1:
- self.file.remove(self.settings["physical_quantity"])
- return
- quantities = list(self.settings["cost_item"].CostQuantities or [])
- quantities.remove(self.settings["physical_quantity"])
- self.settings["cost_item"].CostQuantities = quantities
+ if len(file.get_inverse(settings["physical_quantity"])) == 1:
+ file.remove(settings["physical_quantity"])
+ return
+ quantities = list(settings["cost_item"].CostQuantities or [])
+ quantities.remove(settings["physical_quantity"])
+ settings["cost_item"].CostQuantities = quantities
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py
index 51feebb76e..7b73859bb0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py
@@ -21,41 +21,36 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, cost_schedule=None):
- """Removes a cost schedule
+def remove_cost_schedule(file, cost_schedule=None) -> None:
+ """Removes a cost schedule
- All associated relationships with the cost schedule are also removed,
- including all cost items.
+ All associated relationships with the cost schedule are also removed,
+ including all cost items.
- :param cost_schedule: The IfcCostSchedule entity you want to remove
- :type cost_schedule: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param cost_schedule: The IfcCostSchedule entity you want to remove
+ :type cost_schedule: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule)
- """
- self.file = file
- self.settings = {"cost_schedule": cost_schedule}
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule)
+ """
+ settings = {"cost_schedule": cost_schedule}
- def execute(self):
- # TODO: do a deep purge
- for inverse in self.file.get_inverse(self.settings["cost_schedule"]):
- if inverse.is_a("IfcRelAssignsToControl"):
- [
- ifcopenshell.api.run(
- "cost.remove_cost_item", self.file, cost_item=related_object
- )
- for related_object in inverse.RelatedObjects
- if related_object.is_a("IfcCostItem")
- ]
- history = self.settings["cost_schedule"].OwnerHistory
- self.file.remove(self.settings["cost_schedule"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ # TODO: do a deep purge
+ for inverse in file.get_inverse(settings["cost_schedule"]):
+ if inverse.is_a("IfcRelAssignsToControl"):
+ [
+ ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object)
+ for related_object in inverse.RelatedObjects
+ if related_object.is_a("IfcCostItem")
+ ]
+ history = settings["cost_schedule"].OwnerHistory
+ file.remove(settings["cost_schedule"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py
index 4af7322899..757877bf9d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py
@@ -17,51 +17,48 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, parent=None, cost_value=None):
- """Removes a cost value
+def remove_cost_value(file, parent=None, cost_value=None) -> None:
+ """Removes a cost value
- The cost value may be assigned either to a cost item, a construction
- resource, or another cost value (i.e. it is a subcomponent of a cost)
+ The cost value may be assigned either to a cost item, a construction
+ resource, or another cost value (i.e. it is a subcomponent of a cost)
- :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue
- that the IfcCostValue is assigned to.
- :type parent: ifcopenshell.entity_instance
- :param cost_value: The IfcCostValue that you want to remove
- :type parent: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue
+ that the IfcCostValue is assigned to.
+ :type parent: ifcopenshell.entity_instance
+ :param cost_value: The IfcCostValue that you want to remove
+ :type parent: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
- # This cost item will have a unit cost of 5 and a volume of 3
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5.0})
+ # This cost item will have a unit cost of 5 and a volume of 3
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5.0})
- ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value)
- """
- self.file = file
- self.settings = {"parent": parent, "cost_value": cost_value}
+ ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value)
+ """
+ settings = {"parent": parent, "cost_value": cost_value}
- def execute(self):
- if len(self.file.get_inverse(self.settings["cost_value"])) == 1:
- self.file.remove(self.settings["cost_value"])
- # TODO deep purge
- elif self.settings["parent"].is_a("IfcCostItem"):
- values = list(self.settings["parent"].CostValues)
- values.remove(self.settings["cost_value"])
- self.settings["parent"].CostValues = values if values else None
- elif self.settings["parent"].is_a("IfcConstructionResource"):
- values = list(self.settings["parent"].BaseCosts)
- values.remove(self.settings["cost_value"])
- self.settings["parent"].BaseCosts = values if values else None
- elif self.settings["parent"].is_a("IfcCostValue"):
- components = list(self.settings["parent"].Components)
- components.remove(self.settings["cost_value"])
- self.settings["parent"].Components = components if components else None
+ if len(file.get_inverse(settings["cost_value"])) == 1:
+ file.remove(settings["cost_value"])
+ # TODO deep purge
+ elif settings["parent"].is_a("IfcCostItem"):
+ values = list(settings["parent"].CostValues)
+ values.remove(settings["cost_value"])
+ settings["parent"].CostValues = values if values else None
+ elif settings["parent"].is_a("IfcConstructionResource"):
+ values = list(settings["parent"].BaseCosts)
+ values.remove(settings["cost_value"])
+ settings["parent"].BaseCosts = values if values else None
+ elif settings["parent"].is_a("IfcCostValue"):
+ components = list(settings["parent"].Components)
+ components.remove(settings["cost_value"])
+ settings["parent"].Components = components if components else None
diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py
index 091029f594..c7c5fc69fd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py
@@ -19,56 +19,59 @@
import ifcopenshell.api
+def unassign_cost_item_quantity(file, cost_item=None, products=None) -> None:
+ """Removes quantities of a cost item that are calculated on products
+
+ A cost item may have quantities that are parametrically calculated on
+ physical products. This lets you remove those quantities. This means
+ that any future changes in the physical product's dimensions will not
+ have any impact on the cost item.
+
+ :param cost_item: The IfcCostItem to remove quantities from
+ :type cost_item: ifcopenshell.entity_instance
+ :param products: A list of IfcProducts that may have parametrically
+ connected quantities to the cost item
+ :type products: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
+ item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
+
+ # Let's imagine a unit cost of 5.0 per unit volume
+ value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
+ ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
+ attributes={"AppliedValue": 5.0})
+
+ slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab")
+ # Usually the quantity would be automatically calculated via a
+ # graphical authoring application but let's assign a manual quantity
+ # for now.
+ qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities")
+ ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0})
+
+ # Now let's parametrically link the slab's quantity to the cost
+ # item. If the slab is edited in the future and 42.0 changes, then
+ # the updated value will also automatically be applied to the cost
+ # item.
+ ifcopenshell.api.run("cost.assign_cost_item_quantity", model,
+ cost_item=item, products=[slab], prop_name="NetVolume")
+
+ # Let's change our mind and remove the parametric connection
+ ifcopenshell.api.run("cost.unassign_cost_item_quantity", model,
+ cost_item=item, products=[slab])
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"cost_item": cost_item, "products": products or []}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, cost_item=None, products=None):
- """Removes quantities of a cost item that are calculated on products
-
- A cost item may have quantities that are parametrically calculated on
- physical products. This lets you remove those quantities. This means
- that any future changes in the physical product's dimensions will not
- have any impact on the cost item.
-
- :param cost_item: The IfcCostItem to remove quantities from
- :type cost_item: ifcopenshell.entity_instance
- :param products: A list of IfcProducts that may have parametrically
- connected quantities to the cost item
- :type products: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- schedule = ifcopenshell.api.run("cost.add_cost_schedule", model)
- item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule)
-
- # Let's imagine a unit cost of 5.0 per unit volume
- value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item)
- ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value,
- attributes={"AppliedValue": 5.0})
-
- slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab")
- # Usually the quantity would be automatically calculated via a
- # graphical authoring application but let's assign a manual quantity
- # for now.
- qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities")
- ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0})
-
- # Now let's parametrically link the slab's quantity to the cost
- # item. If the slab is edited in the future and 42.0 changes, then
- # the updated value will also automatically be applied to the cost
- # item.
- ifcopenshell.api.run("cost.assign_cost_item_quantity", model,
- cost_item=item, products=[slab], prop_name="NetVolume")
-
- # Let's change our mind and remove the parametric connection
- ifcopenshell.api.run("cost.unassign_cost_item_quantity", model,
- cost_item=item, products=[slab])
- """
- self.file = file
- self.settings = {"cost_item": cost_item, "products": products or []}
-
def execute(self):
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for quantity in self.settings["cost_item"].CostQuantities or []:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py
index e0caddbe3c..b1affe3a71 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py
@@ -15,3 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_information import add_information
+from .add_reference import add_reference
+from .assign_document import assign_document
+from .edit_information import edit_information
+from .edit_reference import edit_reference
+from .remove_information import remove_information
+from .remove_reference import remove_reference
+from .unassign_document import unassign_document
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py
index 68c9c53759..fc60134477 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py
@@ -19,69 +19,62 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, parent=None):
- """Adds a new document information to the project
+def add_information(file, parent=None) -> None:
+ """Adds a new document information to the project
- An IFC document information is a document associated with the project.
- It may be a drawing, specification, schedule, certificate, warranty
- guarantee, manual, contract, and so on. They are often used for drawings
- and facility management purposes.
+ An IFC document information is a document associated with the project.
+ It may be a drawing, specification, schedule, certificate, warranty
+ guarantee, manual, contract, and so on. They are often used for drawings
+ and facility management purposes.
- A document may also be a subdocument of a larger document, this is
- useful for superseding documents or tracking older versions. The parent
- is considered the latest version and the children are older revisions.
+ A document may also be a subdocument of a larger document, this is
+ useful for superseding documents or tracking older versions. The parent
+ is considered the latest version and the children are older revisions.
- :param parent: The parent document, if necessary.
- :type parent: ifcopenshell.entity_instance, optional
- :return: The newly created IfcDocumentInformation entity
- :rtype: ifcopenshell.entity_instance
+ :param parent: The parent document, if necessary.
+ :type parent: ifcopenshell.entity_instance, optional
+ :return: The newly created IfcDocumentInformation entity
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- # A document typically has a unique drawing or document name (which
- # follows a coding system depending on the project), as well as a
- # title. This should match what is shown on the titleblock or title
- # page of the document. At a minimum you'd also want to specify a
- # URI location. The location may be on local, or on a CDE, or any
- # other platform.
- ifcopenshell.api.run("document.edit_information", model,
- information=document,
- attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
- "Location": "A-GA-6100 - Overall Plan.pdf"})
- """
- self.file = file
- self.settings = {"parent": parent}
+ document = ifcopenshell.api.run("document.add_information", model)
+ # A document typically has a unique drawing or document name (which
+ # follows a coding system depending on the project), as well as a
+ # title. This should match what is shown on the titleblock or title
+ # page of the document. At a minimum you'd also want to specify a
+ # URI location. The location may be on local, or on a CDE, or any
+ # other platform.
+ ifcopenshell.api.run("document.edit_information", model,
+ information=document,
+ attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
+ "Location": "A-GA-6100 - Overall Plan.pdf"})
+ """
+ settings = {"parent": parent}
- def execute(self):
- id_attribute = "DocumentId" if self.file.schema == "IFC2X3" else "Identification"
- information = self.file.create_entity(
- "IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"}
+ id_attribute = "DocumentId" if file.schema == "IFC2X3" else "Identification"
+ information = file.create_entity("IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"})
+ parent = settings["parent"]
+ if not parent and file.by_type("IfcProject"):
+ parent = file.by_type("IfcProject")[0]
+ if parent.is_a("IfcProject") or parent.is_a("IfcContext"):
+ file.create_entity(
+ "IfcRelAssociatesDocument",
+ GlobalId=ifcopenshell.guid.new(),
+ OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
+ RelatingDocument=information,
+ RelatedObjects=[parent],
)
- parent = self.settings["parent"]
- if not parent and self.file.by_type("IfcProject"):
- parent = self.file.by_type("IfcProject")[0]
- if parent.is_a("IfcProject") or parent.is_a("IfcContext"):
- self.file.create_entity(
- "IfcRelAssociatesDocument",
- GlobalId=ifcopenshell.guid.new(),
- OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
- RelatingDocument=information,
- RelatedObjects=[parent],
+ elif parent.is_a("IfcDocumentInformation"):
+ if parent.IsPointer:
+ rel = parent.IsPointer[0]
+ documents = set(rel.RelatedDocuments)
+ documents.add(information)
+ rel.RelatedDocuments = list(documents)
+ else:
+ file.create_entity(
+ "IfcDocumentInformationRelationship", RelatingDocument=parent, RelatedDocuments=[information]
)
- elif parent.is_a("IfcDocumentInformation"):
- if parent.IsPointer:
- rel = parent.IsPointer[0]
- documents = set(rel.RelatedDocuments)
- documents.add(information)
- rel.RelatedDocuments = list(documents)
- else:
- self.file.create_entity(
- "IfcDocumentInformationRelationship",
- RelatingDocument=parent,
- RelatedDocuments=[information]
- )
- return information
+ return information
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py
index 80cf91d8a1..3b96b6d666 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py
@@ -19,62 +19,57 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file: ifcopenshell.file, information: ifcopenshell.entity_instance):
- """Creates a new reference to a document to assign to products
+def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
+ """Creates a new reference to a document to assign to products
- A document may be associated with physical products, tasks, cost items,
- and so on. For example, spaces, storeys, and buildings may have a list
- of associated drawings so you can see which drawings (e.g. plans,
- sections, details) are documenting that location. Alternatively,
- equipment may have associated training manuals, operation and
- maintenance manuals or detailed assembly drawings. Resources may be
- training certification required, schedules may have gantt charts or bid
- documents, and so on.
+ A document may be associated with physical products, tasks, cost items,
+ and so on. For example, spaces, storeys, and buildings may have a list
+ of associated drawings so you can see which drawings (e.g. plans,
+ sections, details) are documenting that location. Alternatively,
+ equipment may have associated training manuals, operation and
+ maintenance manuals or detailed assembly drawings. Resources may be
+ training certification required, schedules may have gantt charts or bid
+ documents, and so on.
- In order to associate a document with an object, a reference to that
- document needs to be created. It could be a reference to the entire
- document, or a reference to a particular page or chapter. See
- ifcopenshell.api.document.assign_document for more information.
+ In order to associate a document with an object, a reference to that
+ document needs to be created. It could be a reference to the entire
+ document, or a reference to a particular page or chapter. See
+ ifcopenshell.api.document.assign_document for more information.
- :param information: The IfcDocumentInformation that the reference will
- be created for
- :type information: ifcopenshell.entity_instance
- :return: The newly created IfcDocumentReference entity
- :rtype: ifcopenshell.entity_instance
+ :param information: The IfcDocumentInformation that the reference will
+ be created for
+ :type information: ifcopenshell.entity_instance
+ :return: The newly created IfcDocumentReference entity
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- ifcopenshell.api.run("document.edit_information", model,
- information=document,
- attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
- "Location": "A-GA-6100 - Overall Plan.pdf"})
+ document = ifcopenshell.api.run("document.add_information", model)
+ ifcopenshell.api.run("document.edit_information", model,
+ information=document,
+ attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
+ "Location": "A-GA-6100 - Overall Plan.pdf"})
- # In this case, we don't specify any more information, and so the
- # reference is for the entire document, as opposed to a single page or
- # chapter or section.
- reference = ifcopenshell.api.run("document.add_reference", model, information=document)
+ # In this case, we don't specify any more information, and so the
+ # reference is for the entire document, as opposed to a single page or
+ # chapter or section.
+ reference = ifcopenshell.api.run("document.add_reference", model, information=document)
- # Alternatively, we can specify a single section, such as by a
- # subheading code.
- reference2 = ifcopenshell.api.run("document.add_reference", model, information=document)
- ifcopenshell.api.run("document.edit_reference", model,
- reference=reference2, attributes={"Identification": "2.1.15"})
- """
- self.file = file
- self.settings = {"information": information}
+ # Alternatively, we can specify a single section, such as by a
+ # subheading code.
+ reference2 = ifcopenshell.api.run("document.add_reference", model, information=document)
+ ifcopenshell.api.run("document.edit_reference", model,
+ reference=reference2, attributes={"Identification": "2.1.15"})
+ """
+ settings = {"information": information}
- def execute(self) -> ifcopenshell.entity_instance:
- if self.file.schema == "IFC2X3":
- reference = self.file.create_entity("IfcDocumentReference", ItemReference="X")
- if self.settings["information"]:
- references = list(self.settings["information"].DocumentReferences or [])
- references.append(reference)
- self.settings["information"].DocumentReferences = references
- return reference
- return self.file.create_entity(
- "IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X"
- )
+ if file.schema == "IFC2X3":
+ reference = file.create_entity("IfcDocumentReference", ItemReference="X")
+ if settings["information"]:
+ references = list(settings["information"].DocumentReferences or [])
+ references.append(reference)
+ settings["information"].DocumentReferences = references
+ return reference
+ return file.create_entity("IfcDocumentReference", ReferencedDocument=settings["information"], Identification="X")
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py
index f9b433213a..5818347a90 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py
@@ -22,93 +22,85 @@ import ifcopenshell.util.element
from typing import Union
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- document: ifcopenshell.entity_instance,
- ):
- """Assigns a document to a list of products
+def assign_document(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ document: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Assigns a document to a list of products
- An object may be assigned to zero, one, or multiple documents. Almost
- any object or property may be assigned to a document, though typically
- we'd only use it for spaces, types, physical products and schedules.
- Adding a new assignment is typically done using a document reference and
- an object. IFC technically allows association with a document
- information and an object, but this is not encouraged because it is not
- consistent with other external relationships (such as classification
- systems or libraries).
+ An object may be assigned to zero, one, or multiple documents. Almost
+ any object or property may be assigned to a document, though typically
+ we'd only use it for spaces, types, physical products and schedules.
+ Adding a new assignment is typically done using a document reference and
+ an object. IFC technically allows association with a document
+ information and an object, but this is not encouraged because it is not
+ consistent with other external relationships (such as classification
+ systems or libraries).
- :param product: The list of objects to associate the document to. This could be
- almost any sensible object in IFC.
- :type product: list[ifcopenshell.entity_instance]
- :param document: The IfcDocumentReference to associate to, or
- alternatively an IfcDocumentInformation, though this is not
- recommended.
- :type document: ifcopenshell.entity_instance
- :return: The IfcRelAssociatesDocument relationship
- or `None` if `products` was an empty list or all products were
- already assigned to the `document`.
- :rtype: ifcopenshell.entity_instance
+ :param product: The list of objects to associate the document to. This could be
+ almost any sensible object in IFC.
+ :type product: list[ifcopenshell.entity_instance]
+ :param document: The IfcDocumentReference to associate to, or
+ alternatively an IfcDocumentInformation, though this is not
+ recommended.
+ :type document: ifcopenshell.entity_instance
+ :return: The IfcRelAssociatesDocument relationship
+ or `None` if `products` was an empty list or all products were
+ already assigned to the `document`.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- ifcopenshell.api.run("document.edit_information", model,
- information=document,
- attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
- "Location": "A-GA-6100 - Overall Plan.pdf"})
- reference = ifcopenshell.api.run("document.add_reference", model, information=document)
+ document = ifcopenshell.api.run("document.add_information", model)
+ ifcopenshell.api.run("document.edit_information", model,
+ information=document,
+ attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
+ "Location": "A-GA-6100 - Overall Plan.pdf"})
+ reference = ifcopenshell.api.run("document.add_reference", model, information=document)
- # Let's imagine storey represents an IfcBuildingStorey for the ground floor
- ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
- """
- self.file = file
- self.settings = {
- "products": products,
- "document": document,
- }
+ # Let's imagine storey represents an IfcBuildingStorey for the ground floor
+ ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
+ """
+ settings = {
+ "products": products,
+ "document": document,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
- # NOTE: reuses code from `library.assign_reference`
+ # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
+ # NOTE: reuses code from `library.assign_reference`
- referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"])
- products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
- products = products - referenced_elements
+ referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["document"])
+ products: set[ifcopenshell.entity_instance] = set(settings["products"])
+ products = products - referenced_elements
- if not products:
- return
+ if not products:
+ return
- if self.file.schema == "IFC2X3":
- rel = next(
- (
- r
- for r in self.file.by_type("IfcRelAssociatesDocument")
- if r.RelatingDocument == self.settings["document"]
- ),
- None,
- )
- else:
- ifc_class = self.settings["document"].is_a()
- if ifc_class == "IfcDocumentReference":
- rel = next(iter(self.settings["document"].DocumentRefForObjects), None)
- elif ifc_class == "IfcDocumentInformation":
- rel = next(iter(self.settings["document"].DocumentInfoForObjects), None)
+ if file.schema == "IFC2X3":
+ rel = next(
+ (r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == settings["document"]),
+ None,
+ )
+ else:
+ ifc_class = settings["document"].is_a()
+ if ifc_class == "IfcDocumentReference":
+ rel = next(iter(settings["document"].DocumentRefForObjects), None)
+ elif ifc_class == "IfcDocumentInformation":
+ rel = next(iter(settings["document"].DocumentInfoForObjects), None)
- if not rel:
- return self.file.create_entity(
- "IfcRelAssociatesDocument",
- GlobalId=ifcopenshell.guid.new(),
- OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
- RelatedObjects=list(products),
- RelatingDocument=self.settings["document"],
- )
+ if not rel:
+ return file.create_entity(
+ "IfcRelAssociatesDocument",
+ GlobalId=ifcopenshell.guid.new(),
+ OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
+ RelatedObjects=list(products),
+ RelatingDocument=settings["document"],
+ )
- related_objects = set(rel.RelatedObjects) | products
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
- return rel
+ related_objects = set(rel.RelatedObjects) | products
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py
index 96c0120120..478c1c11da 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py
@@ -19,38 +19,34 @@ import ifcopenshell
from typing import Any, Optional
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- information: ifcopenshell.entity_instance,
- attributes: Optional[dict[str, Any]] = None,
- ):
- """Edits the attributes of an IfcDocumentInformation
+def edit_information(
+ file: ifcopenshell.file,
+ information: ifcopenshell.entity_instance,
+ attributes: Optional[dict[str, Any]] = None,
+) -> None:
+ """Edits the attributes of an IfcDocumentInformation
- For more information about the attributes and data types of an
- IfcDocumentInformation, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcDocumentInformation, consult the IFC documentation.
- :param reference: The IfcDocumentInformation entity you want to edit
- :type reference: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param reference: The IfcDocumentInformation entity you want to edit
+ :type reference: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- ifcopenshell.api.run("document.edit_information", model,
- information=document,
- attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
- "Location": "A-GA-6100 - Overall Plan.pdf"})
- """
- self.file = file
- self.settings = {"information": information, "attributes": attributes or {}}
+ document = ifcopenshell.api.run("document.add_information", model)
+ ifcopenshell.api.run("document.edit_information", model,
+ information=document,
+ attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
+ "Location": "A-GA-6100 - Overall Plan.pdf"})
+ """
+ settings = {"information": information, "attributes": attributes or {}}
- def execute(self) -> None:
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["information"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["information"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py
index d88afdfc2f..fb705fbbc2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py
@@ -19,41 +19,37 @@ import ifcopenshell
from typing import Any, Optional
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- reference: ifcopenshell.entity_instance,
- attributes: Optional[dict[str, Any]] = None,
- ):
- """Edits the attributes of an IfcDocumentReference
+def edit_reference(
+ file: ifcopenshell.file,
+ reference: ifcopenshell.entity_instance,
+ attributes: Optional[dict[str, Any]] = None,
+) -> None:
+ """Edits the attributes of an IfcDocumentReference
- For more information about the attributes and data types of an
- IfcDocumentReference, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcDocumentReference, consult the IFC documentation.
- :param reference: The IfcDocumentReference entity you want to edit
- :type reference: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param reference: The IfcDocumentReference entity you want to edit
+ :type reference: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- ifcopenshell.api.run("document.edit_information", model,
- information=document,
- attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
- "Location": "A-GA-6100 - Overall Plan.pdf"})
- reference = ifcopenshell.api.run("document.add_reference", model, information=document)
- ifcopenshell.api.run("document.edit_reference", model,
- reference=reference, attributes={"Identification": "2.1.15"})
- """
- self.file = file
- self.settings = {"reference": reference, "attributes": attributes or {}}
+ document = ifcopenshell.api.run("document.add_information", model)
+ ifcopenshell.api.run("document.edit_information", model,
+ information=document,
+ attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
+ "Location": "A-GA-6100 - Overall Plan.pdf"})
+ reference = ifcopenshell.api.run("document.add_reference", model, information=document)
+ ifcopenshell.api.run("document.edit_reference", model,
+ reference=reference, attributes={"Identification": "2.1.15"})
+ """
+ settings = {"reference": reference, "attributes": attributes or {}}
- def execute(self) -> None:
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["reference"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["reference"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py
index 56f57df283..86531252e9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py
@@ -22,45 +22,42 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, information=None):
- """Removes a document information
+def remove_information(file, information=None) -> None:
+ """Removes a document information
- All references and associations are also removed.
+ All references and associations are also removed.
- :param information: The IfcDocumentInformation to remove
- :type information: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param information: The IfcDocumentInformation to remove
+ :type information: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add a document
- document = ifcopenshell.api.run("document.add_information", model)
- # ... and remove it!
- ifcopenshell.api.run("document.remove_information", model, information=document)
- """
- self.file = file
- self.settings = {"information": information}
+ # Add a document
+ document = ifcopenshell.api.run("document.add_information", model)
+ # ... and remove it!
+ ifcopenshell.api.run("document.remove_information", model, information=document)
+ """
+ settings = {"information": information}
- def execute(self):
- for reference in self.settings["information"].HasDocumentReferences or []:
- ifcopenshell.api.run("document.remove_reference", self.file, reference=reference)
+ for reference in settings["information"].HasDocumentReferences or []:
+ ifcopenshell.api.run("document.remove_reference", file, reference=reference)
- for rel in self.settings["information"].IsPointer or []:
- for information in rel.RelatedDocuments:
- ifcopenshell.api.run("document.remove_information", self.file, information=information)
+ for rel in settings["information"].IsPointer or []:
+ for information in rel.RelatedDocuments:
+ ifcopenshell.api.run("document.remove_information", file, information=information)
- for rel in self.settings["information"].IsPointedTo or []:
- if rel.RelatedDocuments == (self.settings["information"],):
- # This relationship is non-rooted
- self.file.remove(rel)
+ for rel in settings["information"].IsPointedTo or []:
+ if rel.RelatedDocuments == (settings["information"],):
+ # This relationship is non-rooted
+ file.remove(rel)
- for rel in self.settings["information"].DocumentInfoForObjects or []:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- self.file.remove(self.settings["information"])
+ for rel in settings["information"].DocumentInfoForObjects or []:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ file.remove(settings["information"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py
index 61fd6810c1..5321b480f7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py
@@ -20,32 +20,29 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance):
- """Remove a document reference
+def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None:
+ """Remove a document reference
- All associations with objects are removed.
+ All associations with objects are removed.
- :param reference: The IfcDocumentReference to remove
- :type reference: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param reference: The IfcDocumentReference to remove
+ :type reference: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- reference = ifcopenshell.api.run("document.add_reference", model, information=document)
- ifcopenshell.api.run("document.remove_reference", model, reference=reference)
- """
- self.file = file
- self.settings = {"reference": reference}
+ document = ifcopenshell.api.run("document.add_information", model)
+ reference = ifcopenshell.api.run("document.add_reference", model, information=document)
+ ifcopenshell.api.run("document.remove_reference", model, reference=reference)
+ """
+ settings = {"reference": reference}
- def execute(self) -> None:
- for rel in self.settings["reference"].DocumentRefForObjects or []:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- self.file.remove(self.settings["reference"])
+ for rel in settings["reference"].DocumentRefForObjects or []:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ file.remove(settings["reference"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py
index c4728d5511..44c0543501 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py
@@ -21,69 +21,65 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- document: ifcopenshell.entity_instance,
- ):
- """Unassigns a document and an association to the list of products
+def unassign_document(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ document: ifcopenshell.entity_instance,
+) -> None:
+ """Unassigns a document and an association to the list of products
- :param product: The list of objects that the document reference or information is
- related to.
- :type product: list[ifcopenshell.entity_instance]
- :param document: The IfcDocumentReference (typically) or in rare cases
- the IfcDocumentInformation that is associated with the product
- :type document: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param product: The list of objects that the document reference or information is
+ related to.
+ :type product: list[ifcopenshell.entity_instance]
+ :param document: The IfcDocumentReference (typically) or in rare cases
+ the IfcDocumentInformation that is associated with the product
+ :type document: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- document = ifcopenshell.api.run("document.add_information", model)
- ifcopenshell.api.run("document.edit_information", model,
- information=document,
- attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
- "Location": "A-GA-6100 - Overall Plan.pdf"})
- reference = ifcopenshell.api.run("document.add_reference", model, information=document)
+ document = ifcopenshell.api.run("document.add_information", model)
+ ifcopenshell.api.run("document.edit_information", model,
+ information=document,
+ attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
+ "Location": "A-GA-6100 - Overall Plan.pdf"})
+ reference = ifcopenshell.api.run("document.add_reference", model, information=document)
- # Let's imagine storey represents an IfcBuildingStorey for the ground floor
- ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
+ # Let's imagine storey represents an IfcBuildingStorey for the ground floor
+ ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference)
- # Now let's change our mind and remove the association
- ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference)
- """
- self.file = file
- self.settings = {
- "products": products,
- "document": document,
- }
+ # Now let's change our mind and remove the association
+ ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference)
+ """
+ settings = {
+ "products": products,
+ "document": document,
+ }
- def execute(self):
- # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
- # NOTE: reuses code from `library.un assign_reference`
+ # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
+ # NOTE: reuses code from `library.un assign_reference`
- reference_rels: set[ifcopenshell.entity_instance] = set()
- products = set(self.settings["products"])
- for product in products:
- reference_rels.update(product.HasAssociations)
+ reference_rels: set[ifcopenshell.entity_instance] = set()
+ products = set(settings["products"])
+ for product in products:
+ reference_rels.update(product.HasAssociations)
- reference_rels = {
- rel
- for rel in reference_rels
- if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"]
- }
+ reference_rels = {
+ rel
+ for rel in reference_rels
+ if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == settings["document"]
+ }
- for rel in reference_rels:
- related_objects = set(rel.RelatedObjects) - products
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for rel in reference_rels:
+ related_objects = set(rel.RelatedObjects) - products
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py
index e0caddbe3c..dd010e886e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py
@@ -15,3 +15,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .assign_product import assign_product
+from .edit_text_literal import edit_text_literal
+from .unassign_product import unassign_product
diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py
index 051dd985b3..b35d0bd564 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py
@@ -20,95 +20,92 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_product=None, related_object=None):
- """Associates a product and an object, typically for annotation
+def assign_product(file, relating_product=None, related_object=None) -> None:
+ """Associates a product and an object, typically for annotation
- Warning: this is an experimental API.
+ Warning: this is an experimental API.
- When you want to draw attention to a feature or characteristic (such as
- a dimension, material, or name) or of a product (e.g. wall, slab,
- furniture, etc), an annotation object is created. This annotation is
- then associated with the product so that it can reference attributes,
- properties, and relationships.
+ When you want to draw attention to a feature or characteristic (such as
+ a dimension, material, or name) or of a product (e.g. wall, slab,
+ furniture, etc), an annotation object is created. This annotation is
+ then associated with the product so that it can reference attributes,
+ properties, and relationships.
- For example, an annotation of a line will be associated with a grid
- axis, such that when that grid axis moves, the annotation of that grid
- axis (which is typically truncated to the extents of a drawing) will
- also move.
+ For example, an annotation of a line will be associated with a grid
+ axis, such that when that grid axis moves, the annotation of that grid
+ axis (which is typically truncated to the extents of a drawing) will
+ also move.
- Another example might be a label of a furniture product, which might
- have some text of the name of the furniture to be shown on drawings or
- in 3D.
+ Another example might be a label of a furniture product, which might
+ have some text of the name of the furniture to be shown on drawings or
+ in 3D.
- :param relating_product: The IfcProduct the object is related to
- :type relating_product: ifcopenshell.entity_instance
- :param related_object: The object (typically IfcAnnotation) that the
- product is related to
- :type related_object: ifcopenshell.entity_instance
- :return: The created IfcRelAssignsToProduct relationship
- :rtype: ifcopenshell.entity_instance
+ :param relating_product: The IfcProduct the object is related to
+ :type relating_product: ifcopenshell.entity_instance
+ :param related_object: The object (typically IfcAnnotation) that the
+ product is related to
+ :type related_object: ifcopenshell.entity_instance
+ :return: The created IfcRelAssignsToProduct relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation")
- ifcopenshell.api.run("drawing.assign_product", model,
- relating_product=furniture, related_object=annotation)
- """
- self.file = file
- self.settings = {
- "relating_product": relating_product,
- "related_object": related_object,
- }
+ furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation")
+ ifcopenshell.api.run("drawing.assign_product", model,
+ relating_product=furniture, related_object=annotation)
+ """
+ settings = {
+ "relating_product": relating_product,
+ "related_object": related_object,
+ }
- def execute(self):
- is_grid_axis = self.settings["relating_product"].is_a("IfcGridAxis")
+ is_grid_axis = settings["relating_product"].is_a("IfcGridAxis")
- if is_grid_axis:
- if self.settings["related_object"].HasAssignments:
- for rel in self.settings["related_object"].HasAssignments:
- if rel.is_a("IfcRelAssignsToProduct") and rel.Name == self.settings["relating_product"].AxisTag:
- return
- elif self.settings["related_object"].HasAssignments:
- for rel in self.settings["related_object"].HasAssignments:
- if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == self.settings["relating_product"]:
+ if is_grid_axis:
+ if settings["related_object"].HasAssignments:
+ for rel in settings["related_object"].HasAssignments:
+ if rel.is_a("IfcRelAssignsToProduct") and rel.Name == settings["relating_product"].AxisTag:
return
+ elif settings["related_object"].HasAssignments:
+ for rel in settings["related_object"].HasAssignments:
+ if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == settings["relating_product"]:
+ return
- referenced_by = None
+ referenced_by = None
- if is_grid_axis:
- axis = self.settings["relating_product"]
- grid = None
- for attribute in ("PartOfW", "PartOfV", "PartOfU"):
- if getattr(axis, attribute, None):
- grid = getattr(axis, attribute)[0]
- self.settings["relating_product"] = grid
- for rel in grid.ReferencedBy:
- if rel.Name == axis.AxisTag:
- referenced_by = rel
- break
- elif self.settings["relating_product"].ReferencedBy:
- referenced_by = self.settings["relating_product"].ReferencedBy[0]
+ if is_grid_axis:
+ axis = settings["relating_product"]
+ grid = None
+ for attribute in ("PartOfW", "PartOfV", "PartOfU"):
+ if getattr(axis, attribute, None):
+ grid = getattr(axis, attribute)[0]
+ settings["relating_product"] = grid
+ for rel in grid.ReferencedBy:
+ if rel.Name == axis.AxisTag:
+ referenced_by = rel
+ break
+ elif settings["relating_product"].ReferencedBy:
+ referenced_by = settings["relating_product"].ReferencedBy[0]
- if referenced_by:
- related_objects = list(referenced_by.RelatedObjects)
- related_objects.append(self.settings["related_object"])
- referenced_by.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by})
- else:
- referenced_by = self.file.create_entity(
- "IfcRelAssignsToProduct",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": [self.settings["related_object"]],
- "RelatingProduct": self.settings["relating_product"],
- }
- )
+ if referenced_by:
+ related_objects = list(referenced_by.RelatedObjects)
+ related_objects.append(settings["related_object"])
+ referenced_by.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by})
+ else:
+ referenced_by = file.create_entity(
+ "IfcRelAssignsToProduct",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["related_object"]],
+ "RelatingProduct": settings["relating_product"],
+ }
+ )
- if is_grid_axis:
- referenced_by.Name = axis.AxisTag
- return referenced_by
+ if is_grid_axis:
+ referenced_by.Name = axis.AxisTag
+ return referenced_by
diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py
index 00b8bc4f82..f1aadc25b0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, text_literal=None, attributes=None):
- """Edits the attributes of an IfcTextLiteral
+def edit_text_literal(file, text_literal=None, attributes=None) -> None:
+ """Edits the attributes of an IfcTextLiteral
- For more information about the attributes and data types of an
- IfcTextLiteral, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcTextLiteral, consult the IFC documentation.
- :param reference: The IfcTextLiteral entity you want to edit
- :type reference: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param reference: The IfcTextLiteral entity you want to edit
+ :type reference: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- text = model.createIfcTextLiteral()
- ifcopenshell.api.run("drawing.edit_text_literal", model,
- text_literal=text, attributes={"Literal": "MY ANNOTATION"})
- """
- self.file = file
- self.settings = {"text_literal": text_literal, "attributes": attributes or {}}
+ text = model.createIfcTextLiteral()
+ ifcopenshell.api.run("drawing.edit_text_literal", model,
+ text_literal=text, attributes={"Literal": "MY ANNOTATION"})
+ """
+ settings = {"text_literal": text_literal, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["text_literal"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["text_literal"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py
index 91ee7ded3d..8254bffdce 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py
@@ -21,54 +21,51 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_product=None, related_object=None):
- """Unassigns a product and an object (typically an annotation)
+def unassign_product(file, relating_product=None, related_object=None) -> None:
+ """Unassigns a product and an object (typically an annotation)
- Smart annotation objects can be associated with products so that they
- can annotate attributes and properties. This function lets you remove
- the association, so that you may change the assocation with another
- object later or leave the annotation as a "dumb" annotation.
+ Smart annotation objects can be associated with products so that they
+ can annotate attributes and properties. This function lets you remove
+ the association, so that you may change the assocation with another
+ object later or leave the annotation as a "dumb" annotation.
- :param relating_product: The IfcProduct the object is related to
- :type relating_product: ifcopenshell.entity_instance
- :param related_object: The object (typically IfcAnnotation) that the
- product is related to
- :type related_object: ifcopenshell.entity_instance
- :return: The created IfcRelAssignsToProduct relationship
- :rtype: ifcopenshell.entity_instance
+ :param relating_product: The IfcProduct the object is related to
+ :type relating_product: ifcopenshell.entity_instance
+ :param related_object: The object (typically IfcAnnotation) that the
+ product is related to
+ :type related_object: ifcopenshell.entity_instance
+ :return: The created IfcRelAssignsToProduct relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation")
- ifcopenshell.api.run("drawing.assign_product", model,
- relating_product=furniture, related_object=annotation)
+ furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation")
+ ifcopenshell.api.run("drawing.assign_product", model,
+ relating_product=furniture, related_object=annotation)
- # Let's change our mind and remove the relationship
- ifcopenshell.api.run("drawing.unassign_product", model,
- relating_product=furniture, related_object=annotation)
- """
- self.file = file
- self.settings = {
- "relating_product": relating_product,
- "related_object": related_object,
- }
+ # Let's change our mind and remove the relationship
+ ifcopenshell.api.run("drawing.unassign_product", model,
+ relating_product=furniture, related_object=annotation)
+ """
+ settings = {
+ "relating_product": relating_product,
+ "related_object": related_object,
+ }
- def execute(self):
- for rel in self.settings["related_object"].HasAssignments or []:
- if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]:
- continue
- if len(rel.RelatedObjects) == 1:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_objects = list(rel.RelatedObjects)
- related_objects.remove(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- return rel
+ for rel in settings["related_object"].HasAssignments or []:
+ if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
+ continue
+ if len(rel.RelatedObjects) == 1:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_objects = list(rel.RelatedObjects)
+ related_objects.remove(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py
index e0caddbe3c..1caaa312ba 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py
@@ -15,3 +15,30 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_axis_representation import add_axis_representation
+from .add_boolean import add_boolean
+from .add_door_representation import add_door_representation
+from .add_footprint_representation import add_footprint_representation
+from .add_mesh_representation import add_mesh_representation
+from .add_profile_representation import add_profile_representation
+from .add_railing_representation import add_railing_representation
+
+try:
+ from .add_representation import add_representation
+except ModuleNotFoundError as e:
+ print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}")
+from .add_slab_representation import add_slab_representation
+from .add_wall_representation import add_wall_representation
+from .add_window_representation import add_window_representation
+from .assign_representation import assign_representation
+from .connect_element import connect_element
+from .connect_path import connect_path
+from .create_2pt_wall import create_2pt_wall
+from .disconnect_element import disconnect_element
+from .disconnect_path import disconnect_path
+from .edit_object_placement import edit_object_placement
+from .map_representation import map_representation
+from .remove_boolean import remove_boolean
+from .remove_representation import remove_representation
+from .unassign_representation import unassign_representation
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py
index 8a09531a26..d8180d287f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py
@@ -19,61 +19,64 @@
import ifcopenshell.util.unit
+def add_axis_representation(file, context=None, axis=None) -> None:
+ """Adds a new axis representation
+
+ Certain objects are typically "axis-based", such as walls, beams,
+ and columns. This means you can represent them abstractly by simply
+ drawing a single line either in 2D (such as for walls) or 3D (for beams
+ and columns). Humans can understand this axis-based representation as
+ being a simplification of a layered extrusion or a profile that is being
+ extruded along that axis and joined to other elements.
+
+ Using an axis-based representation makes it easy for users and computers
+ to analyse connectivity and spatial relationships, as well as makes it
+ easy to parametrically edit these objects by simply stretching the start
+ or end of the axis.
+
+ For now, only simple straight line axes are supported, represented by a
+ start and end coordinate. The order is important. For walls, the start
+ must be at the minimum local X ordinate, and the end at the maximum
+ local X ordinate. For beams and columns, the start is at the minimum
+ local Z ordinate, and the end of the maximum local Z ordinate. The first
+ coordinate is the "start" and the second coordinate is the "end". This
+ stat and end is then used to determine any parametric junctions with
+ other elements.
+
+ Using an axis-representation is optional, but highly recommended for
+ "standard" representations of walls, beams, columns, and other
+ structural members. A rule of thumb is that if you can draw it as a line
+ on paper, you can probably represent it using an axis.
+
+ :param context: The IfcGeometricRepresentationContext that the
+ representation is part of. This must be either a
+ Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D).
+ :type context: ifcopenshell.entity_instance
+ :param axis: The axis, as a list of two coordinates, the coordinates
+ being either a list of 2 or 3 float coordinates depending on whether
+ the axis is 2D or 3D.
+ :type axis: list[list[float]]
+ :return: The newly created IfcShapeRepresentation entity
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW")
+ axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
+ context=context, axis=[(0.0, 0.0), (1.0, 0.0)])
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "context": context,
+ "axis": axis or [],
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, context=None, axis=None):
- """Adds a new axis representation
-
- Certain objects are typically "axis-based", such as walls, beams,
- and columns. This means you can represent them abstractly by simply
- drawing a single line either in 2D (such as for walls) or 3D (for beams
- and columns). Humans can understand this axis-based representation as
- being a simplification of a layered extrusion or a profile that is being
- extruded along that axis and joined to other elements.
-
- Using an axis-based representation makes it easy for users and computers
- to analyse connectivity and spatial relationships, as well as makes it
- easy to parametrically edit these objects by simply stretching the start
- or end of the axis.
-
- For now, only simple straight line axes are supported, represented by a
- start and end coordinate. The order is important. For walls, the start
- must be at the minimum local X ordinate, and the end at the maximum
- local X ordinate. For beams and columns, the start is at the minimum
- local Z ordinate, and the end of the maximum local Z ordinate. The first
- coordinate is the "start" and the second coordinate is the "end". This
- stat and end is then used to determine any parametric junctions with
- other elements.
-
- Using an axis-representation is optional, but highly recommended for
- "standard" representations of walls, beams, columns, and other
- structural members. A rule of thumb is that if you can draw it as a line
- on paper, you can probably represent it using an axis.
-
- :param context: The IfcGeometricRepresentationContext that the
- representation is part of. This must be either a
- Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D).
- :type context: ifcopenshell.entity_instance
- :param axis: The axis, as a list of two coordinates, the coordinates
- being either a list of 2 or 3 float coordinates depending on whether
- the axis is 2D or 3D.
- :type axis: list[list[float]]
- :return: The newly created IfcShapeRepresentation entity
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW")
- axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
- context=context, axis=[(0.0, 0.0), (1.0, 0.0)])
- """
- self.file = file
- self.settings = {
- "context": context,
- "axis": axis or [],
- }
-
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
is_2d = len(self.settings["axis"][0]) == 2
@@ -82,9 +85,13 @@ class Usecase:
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
else:
if is_2d:
- curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False)
+ curve = self.file.createIfcIndexedPolyCurve(
+ self.file.createIfcCartesianPointList2D(points), None, False
+ )
else:
- curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(points), None, False)
+ curve = self.file.createIfcIndexedPolyCurve(
+ self.file.createIfcCartesianPointList3D(points), None, False
+ )
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py
index 66a5bf3baf..3ec590a6f3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py
@@ -20,24 +20,27 @@ import ifcopenshell.util.unit
import numpy as np
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "representation": None,
- "operator": "DIFFERENCE",
- # IfcHalfSpaceSolid, Mesh
- "type": "IfcHalfSpaceSolid",
- # The XY plane is the clipping boundary and +Z is removed.
- "matrix": None, # A matrix to define a clipping Ifchalfspacesolid.
- "blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type
- "blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type
- "should_force_faceted_brep": False,
- "should_force_triangulation": False,
- }
- for key, value in settings.items():
- self.settings[key] = value
+def add_boolean(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "representation": None,
+ "operator": "DIFFERENCE",
+ # IfcHalfSpaceSolid, Mesh
+ "type": "IfcHalfSpaceSolid",
+ # The XY plane is the clipping boundary and +Z is removed.
+ "matrix": None, # A matrix to define a clipping Ifchalfspacesolid.
+ "blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type
+ "blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type
+ "should_force_faceted_brep": False,
+ "should_force_triangulation": False,
+ }
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
if self.settings["type"] == "IfcHalfSpaceSolid":
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py
index 4da8d5b1be..da7678aac4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py
@@ -63,11 +63,7 @@ def create_ifc_door_lining(
points = [p.xz for p in points]
door_lining = builder.polyline(points, closed=True)
- door_lining = builder.extrude(
- door_lining,
- size.y,
- **builder.extrude_kwargs("Y")
- )
+ door_lining = builder.extrude(door_lining, size.y, **builder.extrude_kwargs("Y"))
builder.translate(door_lining, position)
return door_lining
@@ -79,75 +75,78 @@ def create_ifc_box(builder: ShapeBuilder, size: Vector, position: Vector = V(0,
return box
-class Usecase:
- def __init__(self, file, **settings):
- """units in settings expected to be in ifc project units"""
- self.file = file
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm
- self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
- self.settings.update(
- {
- "context": None, # IfcGeometricRepresentationContext
- "overall_height": self.convert_si_to_unit(2.0),
- "overall_width": self.convert_si_to_unit(0.9),
- # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL,
- # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT,
- # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING,
- # DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT,
- # FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT,
- # LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL,
- # ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT,
- # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT
- "operation_type": "SINGLE_SWING_LEFT", # door type
- "lining_properties": {
- "LiningDepth": self.convert_si_to_unit(0.050),
- "LiningThickness": self.convert_si_to_unit(0.050),
- # offset from the outer side of the wall (by Y-axis)
- "LiningOffset": self.convert_si_to_unit(0.0),
- # offset from the wall
- "LiningToPanelOffsetX": self.convert_si_to_unit(0.025),
- # offset from the X-axis (unlike windows)
- "LiningToPanelOffsetY": self.convert_si_to_unit(0.025),
- # transom - vertical distance between door and window panels
- "TransomThickness": self.convert_si_to_unit(0.000),
- # TransomOffset - distance from the bottom door opening
- # to the beginning of the transom
- # unlike windows TransomOffset which goes to the center of the transom
- "TransomOffset": self.convert_si_to_unit(1.525),
- "ShapeAspectStyle": None, # DEPRECATED
- # Casing cover wall faces around the opening
- # on the left, right and upper sides
- # Casing should be either on both sides of the wall or no casing
- # If `LiningOffset` is present then therefore casing is not possible on outer wall
- # therefore there will be no casing on inner wall either
- "CasingDepth": self.convert_si_to_unit(0.005),
- "CasingThickness": self.convert_si_to_unit(0.075), # by Z-axis
- # Threshold covers the bottom side of the opening
- "ThresholdDepth": self.convert_si_to_unit(0.1),
- "ThresholdThickness": self.convert_si_to_unit(0.025), # by Z-axis
- # offset by Y-axis
- "ThresholdOffset": self.convert_si_to_unit(0.000),
- },
- "panel_properties": {
- "PanelDepth": self.convert_si_to_unit(0.035), # by Y
- "PanelWidth": 1.0, # as ratio to the clear door opening
- "FrameDepth": self.convert_si_to_unit(0.035), # by Y
- "FrameThickness": self.convert_si_to_unit(0.035), # by X
- # LEFT, MIDDLE, RIGHT, NOTDEFINED
- "PanelPosition": ..., # NEVER USED
- # defines the basic ways to describe how door panels operate
- # basically how it opens
- "PanelOperation": None, # NEVER USED
- "ShapeAspectStyle": None, # DEPRECATED
- },
- }
- )
- for key, value in settings.items():
- self.settings[key] = value
+def add_door_representation(file, **usecase_settings) -> None:
+ """units in usecase_settings expected to be in ifc project units"""
+ usecase = Usecase()
+ usecase.file = file
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm
+ usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
+ usecase.settings.update(
+ {
+ "context": None, # IfcGeometricRepresentationContext
+ "overall_height": usecase.convert_si_to_unit(2.0),
+ "overall_width": usecase.convert_si_to_unit(0.9),
+ # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL,
+ # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT,
+ # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING,
+ # DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT,
+ # FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT,
+ # LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL,
+ # ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT,
+ # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT
+ "operation_type": "SINGLE_SWING_LEFT", # door type
+ "lining_properties": {
+ "LiningDepth": usecase.convert_si_to_unit(0.050),
+ "LiningThickness": usecase.convert_si_to_unit(0.050),
+ # offset from the outer side of the wall (by Y-axis)
+ "LiningOffset": usecase.convert_si_to_unit(0.0),
+ # offset from the wall
+ "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025),
+ # offset from the X-axis (unlike windows)
+ "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025),
+ # transom - vertical distance between door and window panels
+ "TransomThickness": usecase.convert_si_to_unit(0.000),
+ # TransomOffset - distance from the bottom door opening
+ # to the beginning of the transom
+ # unlike windows TransomOffset which goes to the center of the transom
+ "TransomOffset": usecase.convert_si_to_unit(1.525),
+ "ShapeAspectStyle": None, # DEPRECATED
+ # Casing cover wall faces around the opening
+ # on the left, right and upper sides
+ # Casing should be either on both sides of the wall or no casing
+ # If `LiningOffset` is present then therefore casing is not possible on outer wall
+ # therefore there will be no casing on inner wall either
+ "CasingDepth": usecase.convert_si_to_unit(0.005),
+ "CasingThickness": usecase.convert_si_to_unit(0.075), # by Z-axis
+ # Threshold covers the bottom side of the opening
+ "ThresholdDepth": usecase.convert_si_to_unit(0.1),
+ "ThresholdThickness": usecase.convert_si_to_unit(0.025), # by Z-axis
+ # offset by Y-axis
+ "ThresholdOffset": usecase.convert_si_to_unit(0.000),
+ },
+ "panel_properties": {
+ "PanelDepth": usecase.convert_si_to_unit(0.035), # by Y
+ "PanelWidth": 1.0, # as ratio to the clear door opening
+ "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y
+ "FrameThickness": usecase.convert_si_to_unit(0.035), # by X
+ # LEFT, MIDDLE, RIGHT, NOTDEFINED
+ "PanelPosition": ..., # NEVER USED
+ # defines the basic ways to describe how door panels operate
+ # basically how it opens
+ "PanelOperation": None, # NEVER USED
+ "ShapeAspectStyle": None, # DEPRECATED
+ },
+ }
+ )
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
builder = ShapeBuilder(self.file)
overall_height = self.settings["overall_height"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py
index afdf95155a..976b48e5ce 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py
@@ -19,20 +19,17 @@
import ifcopenshell.util.unit
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "context": None, # IfcGeometricRepresentationContext
- "curves": [], # A list of IFC curves to include in the curve set
- }
- for key, value in settings.items():
- self.settings[key] = value
+def add_footprint_representation(file, **usecase_settings) -> None:
+ settings = {
+ "context": None, # IfcGeometricRepresentationContext
+ "curves": [], # A list of IFC curves to include in the curve set
+ }
+ for key, value in usecase_settings.items():
+ settings[key] = value
- def execute(self):
- return self.file.createIfcShapeRepresentation(
- self.settings["context"],
- self.settings["context"].ContextIdentifier,
- "GeometricCurveSet",
- [self.file.createIfcGeometricCurveSet(self.settings["curves"])],
- )
+ return file.createIfcShapeRepresentation(
+ settings["context"],
+ settings["context"].ContextIdentifier,
+ "GeometricCurveSet",
+ [file.createIfcGeometricCurveSet(settings["curves"])],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py
index ac2167a70e..fbe42063d9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py
@@ -19,25 +19,28 @@
import ifcopenshell.util.unit
-class Usecase:
- def __init__(self, file: ifcopenshell.file, **settings):
- self.file = file
- self.settings = {
- "context": None, # IfcGeometricRepresentationContext
- # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
- # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
- "vertices": None, # A list of coordinates
- # ... where itemN = [(0, 1), (1, 2), (v1, v2), ...]
- "edges": None, # A list of edges, represented by vertex index pairs
- # ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...]
- "faces": None, # A list of polygons, represented by vertex indices
- "coordinate_offset": None, # Optionally apply a vector offset to all coordinates
- "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
- "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets
- }
- for key, value in settings.items():
- self.settings[key] = value
+def add_mesh_representation(file: ifcopenshell.file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "context": None, # IfcGeometricRepresentationContext
+ # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
+ # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
+ "vertices": None, # A list of coordinates
+ # ... where itemN = [(0, 1), (1, 2), (v1, v2), ...]
+ "edges": None, # A list of edges, represented by vertex index pairs
+ # ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...]
+ "faces": None, # A list of polygons, represented by vertex indices
+ "coordinate_offset": None, # Optionally apply a vector offset to all coordinates
+ "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
+ "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets
+ }
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
if self.settings["unit_scale"] is None:
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py
index c38aaf87df..025f09f0fc 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py
@@ -21,22 +21,25 @@ import ifcopenshell.util.unit
from ifcopenshell.util.data import Clipping
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "context": None, # IfcGeometricRepresentationContext
- "profile": None,
- "depth": 1.0,
- "cardinal_point": 5,
- # Planes are defined either by Clipping objects
- # or by dictionaries of arguments for `Clipping.parse`
- "clippings": [], # A list of planes that define clipping half space solids
- "placement_zx_axes": (None, None),
- }
- for key, value in settings.items():
- self.settings[key] = value
+def add_profile_representation(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "context": None, # IfcGeometricRepresentationContext
+ "profile": None,
+ "depth": 1.0,
+ "cardinal_point": 5,
+ # Planes are defined either by Clipping objects
+ # or by dictionaries of arguments for `Clipping.parse`
+ "clippings": [], # A list of planes that define clipping half space solids
+ "placement_zx_axes": (None, None),
+ }
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py
index b597633678..9de884c8c2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py
@@ -31,39 +31,42 @@ def mm(x):
return x / 1000
+def add_railing_representation(file, **usecase_settings) -> None:
+ """
+ units in usecase_settings expected to be in ifc project units
+
+ `railing_path` is a list of point coordinates for the railing path,
+ coordinates are expected to be at the top of the railing, not at the center
+
+ `railing_path` is expected to be a list of Vector objects
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
+ usecase.settings.update(
+ {
+ "context": None, # IfcGeometricRepresentationContext
+ "railing_type": "WALL_MOUNTED_HANDRAIL",
+ "railing_path": usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]),
+ "use_manual_supports": False,
+ "support_spacing": usecase.convert_si_to_unit(mm(1000)),
+ "railing_diameter": usecase.convert_si_to_unit(mm(50)),
+ "clear_width": usecase.convert_si_to_unit(mm(40)),
+ "terminal_type": "180",
+ "height": usecase.convert_si_to_unit(mm(1000)),
+ "looped_path": False,
+ }
+ )
+
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+
+ if usecase.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL":
+ raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.')
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, **settings):
- """
- units in settings expected to be in ifc project units
-
- `railing_path` is a list of point coordinates for the railing path,
- coordinates are expected to be at the top of the railing, not at the center
-
- `railing_path` is expected to be a list of Vector objects
- """
- self.file = file
- self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
- self.settings.update(
- {
- "context": None, # IfcGeometricRepresentationContext
- "railing_type": "WALL_MOUNTED_HANDRAIL",
- "railing_path": self.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]),
- "use_manual_supports": False,
- "support_spacing": self.convert_si_to_unit(mm(1000)),
- "railing_diameter": self.convert_si_to_unit(mm(50)),
- "clear_width": self.convert_si_to_unit(mm(40)),
- "terminal_type": "180",
- "height": self.convert_si_to_unit(mm(1000)),
- "looped_path": False,
- }
- )
-
- for key, value in settings.items():
- self.settings[key] = value
-
- if self.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL":
- raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.')
-
def execute(self):
arc_points = []
items_3d = []
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py
index 155bfb2bea..6c9a8da058 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py
@@ -28,37 +28,40 @@ X_AXIS = Vector((1, 0, 0))
EPSILON = 1e-6
-class Usecase:
- def __init__(self, file: ifcopenshell.file, **settings):
- # TODO: This usecase currently depends on Blender's data model
- self.file = file
- self.settings = {
- "context": None, # IfcGeometricRepresentationContext
- "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
- "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
- "coordinate_offset": None, # Optionally apply a vector offset to all coordinates
- "total_items": 1, # How many representation items to create
- "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
- "should_force_faceted_brep": False, # If we should force faceted breps for meshes
- "should_force_triangulation": False, # If we should force triangulation for meshes
- "should_generate_uvs": False, # If UV coordinates should also be generated
- # Possible IFC representation classes:
- # IfcExtrudedAreaSolid/IfcRectangleProfileDef
- # IfcExtrudedAreaSolid/IfcCircleProfileDef
- # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef
- # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids
- # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage
- # IfcGeometricCurveSet/IfcTextLiteral
- # IfcTextLiteral
- "ifc_representation_class": None, # Whether to cast a mesh into a particular class
- "profile_set_usage": None, # The material profile set if the extrusion requires it
- "text_literal": None, # The text literal if the representation requires it
- }
- self.ifc_vertices = []
- for key, value in settings.items():
- self.settings[key] = value
+def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopenshell.entity_instance:
+ usecase = Usecase()
+ # TODO: This usecase currently depends on Blender's data model
+ usecase.file = file
+ usecase.settings = {
+ "context": None, # IfcGeometricRepresentationContext
+ "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
+ "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
+ "coordinate_offset": None, # Optionally apply a vector offset to all coordinates
+ "total_items": 1, # How many representation items to create
+ "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
+ "should_force_faceted_brep": False, # If we should force faceted breps for meshes
+ "should_force_triangulation": False, # If we should force triangulation for meshes
+ "should_generate_uvs": False, # If UV coordinates should also be generated
+ # Possible IFC representation classes:
+ # IfcExtrudedAreaSolid/IfcRectangleProfileDef
+ # IfcExtrudedAreaSolid/IfcCircleProfileDef
+ # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef
+ # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids
+ # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage
+ # IfcGeometricCurveSet/IfcTextLiteral
+ # IfcTextLiteral
+ "ifc_representation_class": None, # Whether to cast a mesh into a particular class
+ "profile_set_usage": None, # The material profile set if the extrusion requires it
+ "text_literal": None, # The text literal if the representation requires it
+ }
+ usecase.ifc_vertices = []
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
- def execute(self) -> ifcopenshell.entity_instance:
+
+class Usecase:
+ def execute(self):
self.is_manifold = None
if (
isinstance(self.settings["geometry"], bpy.types.Mesh)
@@ -374,10 +377,12 @@ class Usecase:
return items
def create_plane(self, polygon):
- return self.file.createIfcPlane(Position=self.file.createIfcAxis2Placement3D(
- Location=self.file.createIfcCartesianPoint(polygon.center),
- Axis=self.file.createIfcDirection(polygon.normal),
- ))
+ return self.file.createIfcPlane(
+ Position=self.file.createIfcAxis2Placement3D(
+ Location=self.file.createIfcCartesianPoint(polygon.center),
+ Axis=self.file.createIfcDirection(polygon.normal),
+ )
+ )
def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]:
items = []
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py
index 2edd68d5c1..7514ade38f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py
@@ -20,20 +20,23 @@ import ifcopenshell.util.unit
from math import sin, cos
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "context": None, # IfcGeometricRepresentationContext
- "depth": 0.2,
- "x_angle": 0, # Radians
- # Planes are defined either by Clipping objects
- # or by dictionaries of arguments for `Clipping.parse`
- "clippings": [], # A list of planes that define clipping half space solids
- }
- for key, value in settings.items():
- self.settings[key] = value
+def add_slab_representation(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "context": None, # IfcGeometricRepresentationContext
+ "depth": 0.2,
+ "x_angle": 0, # Radians
+ # Planes are defined either by Clipping objects
+ # or by dictionaries of arguments for `Clipping.parse`
+ "clippings": [], # A list of planes that define clipping half space solids
+ }
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
return self.file.createIfcShapeRepresentation(
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py
index 6aa5466f58..504a89078b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py
@@ -21,25 +21,28 @@ from math import sin, cos
from ifcopenshell.util.data import Clipping
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "context": None, # IfcGeometricRepresentationContext
- "length": 1.0,
- "height": 3.0,
- "offset": 0.0,
- "thickness": 0.2,
- # Sloped walls along the wall's X axis, provided in radians
- "x_angle": 0,
- # Planes are defined either by Clipping objects
- # or by dictionaries of arguments for `Clipping.parse`
- "clippings": [], # A list of planes that define clipping half space solids
- "booleans": [], # Any existing IfcBooleanResults
- }
- for key, value in settings.items():
- self.settings[key] = value
+def add_wall_representation(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "context": None, # IfcGeometricRepresentationContext
+ "length": 1.0,
+ "height": 3.0,
+ "offset": 0.0,
+ "thickness": 0.2,
+ # Sloped walls along the wall's X axis, provided in radians
+ "x_angle": 0,
+ # Planes are defined either by Clipping objects
+ # or by dictionaries of arguments for `Clipping.parse`
+ "clippings": [], # A list of planes that define clipping half space solids
+ "booleans": [], # Any existing IfcBooleanResults
+ }
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py
index 94b97636aa..b0a46e69ab 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py
@@ -56,12 +56,7 @@ def create_ifc_window_frame_simple(
th_left, th_up, th_right, th_bottom = thickness
def get_extruded_profile(profile):
- return builder.extrude(
- profile,
- size.y,
- position=position,
- **builder.extrude_kwargs("Y")
- )
+ return builder.extrude(profile, size.y, position=position, **builder.extrude_kwargs("Y"))
# if all lining sides are present then we can just use two rectangles
# as inner and outer curves of the profile
@@ -207,12 +202,7 @@ def create_ifc_window(
glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0)
glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0])
- glass = builder.extrude(
- glass_rect,
- glass_thickness,
- position=glass_position,
- **builder.extrude_kwargs("Y")
- )
+ glass = builder.extrude(glass_rect, glass_thickness, position=glass_position, **builder.extrude_kwargs("Y"))
output_items = [lining_items, frame_extruded_items, [glass]]
builder.translate(chain(*output_items), position)
@@ -220,73 +210,76 @@ def create_ifc_window(
return output_items
-class Usecase:
- def __init__(self, file, **settings):
- """units in settings expected to be in ifc project units"""
- self.file = file
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm
- # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
- self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
- self.settings.update(
- {
- "context": None, # IfcGeometricRepresentationContext
- # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
- # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
- # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
- "partition_type": "SINGLE_PANEL",
- "overall_height": self.convert_si_to_unit(0.9),
- "overall_width": self.convert_si_to_unit(0.6),
- "lining_properties": {
- "LiningDepth": self.convert_si_to_unit(0.050),
- "LiningThickness": self.convert_si_to_unit(0.050),
- "LiningOffset": self.convert_si_to_unit(0.050), # offset to the wall
- # offset from the wall
- "LiningToPanelOffsetX": self.convert_si_to_unit(0.025),
- # offset from the lining
- # that way it allows you to define overall_depth constant between all panels
- # and still have panels with different size:
- # overall_depth = lining_depth + offset_y
- # full offset from X axis = overall_depth - frame_depth
- "LiningToPanelOffsetY": self.convert_si_to_unit(0.025),
- # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
- # TriplePanelLeft, TriplePanelRight
- # mullion - horizontal distance between panels
- "MullionThickness": self.convert_si_to_unit(0.050),
- # distance from the first lining to the mullion center
- "FirstMullionOffset": self.convert_si_to_unit(0.3),
- # applies to TriplePanelVertical
- # distance from the first lining to the second mullion center
- "SecondMullionOffset": self.convert_si_to_unit(0.45),
- # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
- # TriplePanelLeft, TriplePanelRight
- # works similar way to mullion
- "TransomThickness": self.convert_si_to_unit(0.050),
- "FirstTransomOffset": self.convert_si_to_unit(0.3),
- # applies to TriplePanelHorizontal
- "SecondTransomOffset": self.convert_si_to_unit(0.6),
+def add_window_representation(file, **usecase_settings) -> None:
+ """units in usecase_settings expected to be in ifc project units"""
+ usecase = Usecase()
+ usecase.file = file
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm
+ # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
+ usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
+ usecase.settings.update(
+ {
+ "context": None, # IfcGeometricRepresentationContext
+ # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
+ # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
+ # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
+ "partition_type": "SINGLE_PANEL",
+ "overall_height": usecase.convert_si_to_unit(0.9),
+ "overall_width": usecase.convert_si_to_unit(0.6),
+ "lining_properties": {
+ "LiningDepth": usecase.convert_si_to_unit(0.050),
+ "LiningThickness": usecase.convert_si_to_unit(0.050),
+ "LiningOffset": usecase.convert_si_to_unit(0.050), # offset to the wall
+ # offset from the wall
+ "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025),
+ # offset from the lining
+ # that way it allows you to define overall_depth constant between all panels
+ # and still have panels with different size:
+ # overall_depth = lining_depth + offset_y
+ # full offset from X axis = overall_depth - frame_depth
+ "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025),
+ # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
+ # TriplePanelLeft, TriplePanelRight
+ # mullion - horizontal distance between panels
+ "MullionThickness": usecase.convert_si_to_unit(0.050),
+ # distance from the first lining to the mullion center
+ "FirstMullionOffset": usecase.convert_si_to_unit(0.3),
+ # applies to TriplePanelVertical
+ # distance from the first lining to the second mullion center
+ "SecondMullionOffset": usecase.convert_si_to_unit(0.45),
+ # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
+ # TriplePanelLeft, TriplePanelRight
+ # works similar way to mullion
+ "TransomThickness": usecase.convert_si_to_unit(0.050),
+ "FirstTransomOffset": usecase.convert_si_to_unit(0.3),
+ # applies to TriplePanelHorizontal
+ "SecondTransomOffset": usecase.convert_si_to_unit(0.6),
+ "ShapeAspectStyle": None, # DEPRECATED
+ },
+ "panel_properties": [
+ {
+ "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y
+ "FrameThickness": usecase.convert_si_to_unit(0.035), # by X
+ # BOTTOM, LEFT, MIDDLE, RIGHT, TOP
+ "PanelPosition": ..., # NEVER USED
+ # defines the basic ways to describe how window panels operate
+ # how it's hanged, how it opens
+ "OperationType": None, # NEVER USED
"ShapeAspectStyle": None, # DEPRECATED
},
- "panel_properties": [
- {
- "FrameDepth": self.convert_si_to_unit(0.035), # by Y
- "FrameThickness": self.convert_si_to_unit(0.035), # by X
- # BOTTOM, LEFT, MIDDLE, RIGHT, TOP
- "PanelPosition": ..., # NEVER USED
- # defines the basic ways to describe how window panels operate
- # how it's hanged, how it opens
- "OperationType": None, # NEVER USED
- "ShapeAspectStyle": None, # DEPRECATED
- },
- ],
- }
- )
+ ],
+ }
+ )
- for key, value in settings.items():
- self.settings[key] = value
- self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]]
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]]
+ return usecase.execute()
+
+class Usecase:
def execute(self):
builder = ShapeBuilder(self.file)
overall_height = self.settings["overall_height"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py
index 41d7f9422b..1df964d845 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py
@@ -20,13 +20,16 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {"product": None, "representation": None}
- for key, value in settings.items():
- self.settings[key] = value
+def assign_representation(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"product": None, "representation": None}
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
if self.settings["product"].is_a("IfcProduct"):
product_type = ifcopenshell.util.element.get_type(self.settings["product"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py
index 9339752d4c..64df2e59d6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py
@@ -21,44 +21,41 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "relating_element": None,
- "related_element": None,
- "description": None,
- }
- for key, value in settings.items():
- self.settings[key] = value
+def connect_element(file, **usecase_settings) -> None:
+ settings = {
+ "relating_element": None,
+ "related_element": None,
+ "description": None,
+ }
+ for key, value in usecase_settings.items():
+ settings[key] = value
- def execute(self):
- incompatible_connections = []
+ incompatible_connections = []
- for rel in self.settings["relating_element"].ConnectedFrom:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]:
- incompatible_connections.append(rel)
+ for rel in settings["relating_element"].ConnectedFrom:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
+ incompatible_connections.append(rel)
- for rel in self.settings["related_element"].ConnectedTo:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]:
- incompatible_connections.append(rel)
+ for rel in settings["related_element"].ConnectedTo:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
+ incompatible_connections.append(rel)
- if incompatible_connections:
- for connection in set(incompatible_connections):
- history = connection.OwnerHistory
- self.file.remove(connection)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if incompatible_connections:
+ for connection in set(incompatible_connections):
+ history = connection.OwnerHistory
+ file.remove(connection)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
- for rel in self.settings["relating_element"].ConnectedTo:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]:
- rel.Description = self.settings["description"]
- return rel
+ for rel in settings["relating_element"].ConnectedTo:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
+ rel.Description = settings["description"]
+ return rel
- return self.file.createIfcRelConnectsElements(
- ifcopenshell.guid.new(),
- OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
- Description=self.settings["description"],
- RelatingElement=self.settings["relating_element"],
- RelatedElement=self.settings["related_element"],
- )
+ return file.createIfcRelConnectsElements(
+ ifcopenshell.guid.new(),
+ OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
+ Description=settings["description"],
+ RelatingElement=settings["relating_element"],
+ RelatedElement=settings["related_element"],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py
index 1a610a7d2c..7cc60c4ef1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py
@@ -21,76 +21,73 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "relating_element": None,
- "related_element": None,
- "relating_connection": "NOTDEFINED",
- "related_connection": "NOTDEFINED",
- "description": None,
- }
- for key, value in settings.items():
- self.settings[key] = value
+def connect_path(file, **usecase_settings) -> None:
+ settings = {
+ "relating_element": None,
+ "related_element": None,
+ "relating_connection": "NOTDEFINED",
+ "related_connection": "NOTDEFINED",
+ "description": None,
+ }
+ for key, value in usecase_settings.items():
+ settings[key] = value
- def execute(self):
- incompatible_connections = []
- for rel in self.settings["relating_element"].ConnectedTo:
- if not rel.is_a("IfcRelConnectsPathElements"):
- continue
- if rel.RelatedElement == self.settings["related_element"]:
- incompatible_connections.append(rel)
- elif (
- rel.RelatingConnectionType in ["ATSTART", "ATEND"]
- and rel.RelatingConnectionType == self.settings["relating_connection"]
- ):
- incompatible_connections.append(rel)
+ incompatible_connections = []
+ for rel in settings["relating_element"].ConnectedTo:
+ if not rel.is_a("IfcRelConnectsPathElements"):
+ continue
+ if rel.RelatedElement == settings["related_element"]:
+ incompatible_connections.append(rel)
+ elif (
+ rel.RelatingConnectionType in ["ATSTART", "ATEND"]
+ and rel.RelatingConnectionType == settings["relating_connection"]
+ ):
+ incompatible_connections.append(rel)
- for rel in self.settings["relating_element"].ConnectedFrom:
- if not rel.is_a("IfcRelConnectsPathElements"):
- continue
- if (
- rel.RelatedConnectionType in ["ATSTART", "ATEND"]
- and rel.RelatedConnectionType == self.settings["relating_connection"]
- ):
- incompatible_connections.append(rel)
+ for rel in settings["relating_element"].ConnectedFrom:
+ if not rel.is_a("IfcRelConnectsPathElements"):
+ continue
+ if (
+ rel.RelatedConnectionType in ["ATSTART", "ATEND"]
+ and rel.RelatedConnectionType == settings["relating_connection"]
+ ):
+ incompatible_connections.append(rel)
- for rel in self.settings["related_element"].ConnectedFrom:
- if not rel.is_a("IfcRelConnectsPathElements"):
- continue
- if (
- rel.RelatedConnectionType in ["ATSTART", "ATEND"]
- and rel.RelatedConnectionType == self.settings["related_connection"]
- ):
- incompatible_connections.append(rel)
+ for rel in settings["related_element"].ConnectedFrom:
+ if not rel.is_a("IfcRelConnectsPathElements"):
+ continue
+ if (
+ rel.RelatedConnectionType in ["ATSTART", "ATEND"]
+ and rel.RelatedConnectionType == settings["related_connection"]
+ ):
+ incompatible_connections.append(rel)
- for rel in self.settings["related_element"].ConnectedTo:
- if not rel.is_a("IfcRelConnectsPathElements"):
- continue
- if rel.RelatedElement == self.settings["relating_element"]:
- incompatible_connections.append(rel)
- elif (
- rel.RelatingConnectionType in ["ATSTART", "ATEND"]
- and rel.RelatingConnectionType == self.settings["related_connection"]
- ):
- incompatible_connections.append(rel)
+ for rel in settings["related_element"].ConnectedTo:
+ if not rel.is_a("IfcRelConnectsPathElements"):
+ continue
+ if rel.RelatedElement == settings["relating_element"]:
+ incompatible_connections.append(rel)
+ elif (
+ rel.RelatingConnectionType in ["ATSTART", "ATEND"]
+ and rel.RelatingConnectionType == settings["related_connection"]
+ ):
+ incompatible_connections.append(rel)
- if incompatible_connections:
- for connection in set(incompatible_connections):
- history = connection.OwnerHistory
- self.file.remove(connection)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if incompatible_connections:
+ for connection in set(incompatible_connections):
+ history = connection.OwnerHistory
+ file.remove(connection)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
- return self.file.createIfcRelConnectsPathElements(
- ifcopenshell.guid.new(),
- OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
- Description=self.settings["description"],
- RelatingElement=self.settings["relating_element"],
- RelatedElement=self.settings["related_element"],
- RelatingConnectionType=self.settings["relating_connection"],
- RelatedConnectionType=self.settings["related_connection"],
- RelatingPriorities=[],
- RelatedPriorities=[],
- )
+ return file.createIfcRelConnectsPathElements(
+ ifcopenshell.guid.new(),
+ OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
+ Description=settings["description"],
+ RelatingElement=settings["relating_element"],
+ RelatedElement=settings["related_element"],
+ RelatingConnectionType=settings["relating_connection"],
+ RelatedConnectionType=settings["related_connection"],
+ RelatingPriorities=[],
+ RelatedPriorities=[],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py
index e228730974..cd508d4a3e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py
@@ -21,20 +21,25 @@ import ifcopenshell.api
import ifcopenshell.util.unit
-class Usecase:
- def __init__(self, file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True):
- self.file = file
- self.settings = {
- "element": element,
- "context": context,
- "p1": p1,
- "p2": p2,
- "elevation": elevation,
- "height": height,
- "thickness": thickness,
- "is_si": is_si
- }
+def create_2pt_wall(
+ file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True
+) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "element": element,
+ "context": context,
+ "p1": p1,
+ "p2": p2,
+ "elevation": elevation,
+ "height": height,
+ "thickness": thickness,
+ "is_si": is_si,
+ }
+ return usecase.execute()
+
+class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -44,9 +49,9 @@ class Usecase:
length = float(np.linalg.norm(self.settings["p2"] - self.settings["p1"]))
if not self.settings["is_si"]:
- length=self.convert_unit_to_si(length)
- self.settings["height"]=self.convert_unit_to_si(self.settings["height"])
- self.settings["thickness"]=self.convert_unit_to_si(self.settings["thickness"])
+ length = self.convert_unit_to_si(length)
+ self.settings["height"] = self.convert_unit_to_si(self.settings["height"])
+ self.settings["thickness"] = self.convert_unit_to_si(self.settings["thickness"])
self.settings["p1"][0] = self.convert_unit_to_si(self.settings["p1"][0])
self.settings["p1"][1] = self.convert_unit_to_si(self.settings["p1"][1])
self.settings["elevation"] = self.convert_unit_to_si(self.settings["elevation"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py
index 680b3e85e7..1e1eaaa82b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py
@@ -20,38 +20,35 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "relating_element": None,
- "related_element": None,
- }
- for key, value in settings.items():
- self.settings[key] = value
+def disconnect_element(file, **usecase_settings) -> None:
+ settings = {
+ "relating_element": None,
+ "related_element": None,
+ }
+ for key, value in usecase_settings.items():
+ settings[key] = value
- def execute(self):
- incompatible_connections = []
+ incompatible_connections = []
- for rel in self.settings["relating_element"].ConnectedTo:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]:
- incompatible_connections.append(rel)
+ for rel in settings["relating_element"].ConnectedTo:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
+ incompatible_connections.append(rel)
- for rel in self.settings["relating_element"].ConnectedFrom:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]:
- incompatible_connections.append(rel)
+ for rel in settings["relating_element"].ConnectedFrom:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
+ incompatible_connections.append(rel)
- for rel in self.settings["related_element"].ConnectedTo:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]:
- incompatible_connections.append(rel)
+ for rel in settings["related_element"].ConnectedTo:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
+ incompatible_connections.append(rel)
- for rel in self.settings["related_element"].ConnectedFrom:
- if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["relating_element"]:
- incompatible_connections.append(rel)
+ for rel in settings["related_element"].ConnectedFrom:
+ if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["relating_element"]:
+ incompatible_connections.append(rel)
- if incompatible_connections:
- for connection in set(incompatible_connections):
- history = connection.OwnerHistory
- self.file.remove(connection)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if incompatible_connections:
+ for connection in set(incompatible_connections):
+ history = connection.OwnerHistory
+ file.remove(connection)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py
index 8e52c7e4ff..14bbaf9e7c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py
@@ -21,38 +21,35 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {
- "relating_element": None,
- "related_element": None,
- "element": None,
- "connection_type": None,
- }
- for key, value in settings.items():
- self.settings[key] = value
+def disconnect_path(file, **usecase_settings) -> None:
+ settings = {
+ "relating_element": None,
+ "related_element": None,
+ "element": None,
+ "connection_type": None,
+ }
+ for key, value in usecase_settings.items():
+ settings[key] = value
- def execute(self):
- if self.settings["connection_type"] and self.settings["element"]:
- connections = [
- r
- for r in self.settings["element"].ConnectedTo
- if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == self.settings["connection_type"]
- ] + [
- r
- for r in self.settings["element"].ConnectedFrom
- if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == self.settings["connection_type"]
- ]
- else:
- connections = [
- r
- for r in self.settings["relating_element"].ConnectedTo
- if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == self.settings["related_element"]
- ]
+ if settings["connection_type"] and settings["element"]:
+ connections = [
+ r
+ for r in settings["element"].ConnectedTo
+ if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == settings["connection_type"]
+ ] + [
+ r
+ for r in settings["element"].ConnectedFrom
+ if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == settings["connection_type"]
+ ]
+ else:
+ connections = [
+ r
+ for r in settings["relating_element"].ConnectedTo
+ if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == settings["related_element"]
+ ]
- for connection in set(connections):
- history = connection.OwnerHistory
- self.file.remove(connection)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for connection in set(connections):
+ history = connection.OwnerHistory
+ file.remove(connection)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py
index 2f442388b6..768468e03a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py
@@ -27,24 +27,26 @@ from typing import Optional, Union
NPArrayOfFloats = npt.NDArray[np.float64]
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- product: ifcopenshell.entity_instance,
- matrix: Optional[NPArrayOfFloats] = None,
- is_si=True,
- should_transform_children=False,
- ):
- self.file = file
- self.settings = {
- "product": product,
- "matrix": matrix if matrix is not None else np.eye(4),
- "is_si": is_si,
- "should_transform_children": should_transform_children,
- }
+def edit_object_placement(
+ file: ifcopenshell.file,
+ product: ifcopenshell.entity_instance,
+ matrix: Optional[NPArrayOfFloats] = None,
+ is_si=True,
+ should_transform_children=False,
+) -> ifcopenshell.entity_instance:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "product": product,
+ "matrix": matrix if matrix is not None else np.eye(4),
+ "is_si": is_si,
+ "should_transform_children": should_transform_children,
+ }
+ return usecase.execute()
- def execute(self) -> ifcopenshell.entity_instance:
+
+class Usecase:
+ def execute(self):
if not hasattr(self.settings["product"], "ObjectPlacement"):
return
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py
index 1e4b4e2207..83e1e1e821 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py
@@ -17,14 +17,17 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {"representation": None}
- self.ifc_vertices = []
- for key, value in settings.items():
- self.settings[key] = value
+def map_representation(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"representation": None}
+ usecase.ifc_vertices = []
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
mapping_source = self.get_mapping_source()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py
index 85ca854233..5d81203a5d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py
@@ -19,13 +19,16 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {"item": None}
- for key, value in settings.items():
- self.settings[key] = value
+def remove_boolean(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"item": None}
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
item = None
for inverse in self.file.get_inverse(self.settings["item"]):
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py
index d7893ea317..7aafb751e0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py
@@ -19,62 +19,57 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, representation: ifcopenshell.entity_instance):
- """Remove a representation.
+def remove_representation(file: ifcopenshell.file, representation: ifcopenshell.entity_instance) -> None:
+ """Remove a representation.
- Also purges representation items and their related elements
- like IfcStyledItem, tessellated facesets colours and UV map.
+ Also purges representation items and their related elements
+ like IfcStyledItem, tessellated facesets colours and UV map.
- :param representation: IfcRepresentation to remove.
- Note that it's expected that IfcRepresentation won't be in use
- before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect)
- otherwise representation won't be removed.
- :type representation: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"representation": representation}
+ :param representation: IfcRepresentation to remove.
+ Note that it's expected that IfcRepresentation won't be in use
+ before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect)
+ otherwise representation won't be removed.
+ :type representation: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"representation": representation}
- def execute(self) -> None:
- styled_items = set()
- presentation_layer_assignments = set()
- textures = set()
- colours = set()
- for subelement in self.file.traverse(self.settings["representation"]):
- if subelement.is_a("IfcRepresentationItem"):
- [styled_items.add(s) for s in subelement.StyledByItem or []]
- # IFC2X3 is using LayerAssignments
- for s in (
- subelement.LayerAssignment
- if hasattr(subelement, "LayerAssignment")
- else subelement.LayerAssignments
- ):
- presentation_layer_assignments.add(s)
- # IfcTessellatedFaceSet inverses
- [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []]
- [colours.add(t) for t in getattr(subelement, "HasColours", []) or []]
- elif subelement.is_a("IfcRepresentation"):
- for layer in subelement.LayerAssignments:
- presentation_layer_assignments.add(layer)
+ styled_items = set()
+ presentation_layer_assignments = set()
+ textures = set()
+ colours = set()
+ for subelement in file.traverse(settings["representation"]):
+ if subelement.is_a("IfcRepresentationItem"):
+ [styled_items.add(s) for s in subelement.StyledByItem or []]
+ # IFC2X3 is using LayerAssignments
+ for s in (
+ subelement.LayerAssignment if hasattr(subelement, "LayerAssignment") else subelement.LayerAssignments
+ ):
+ presentation_layer_assignments.add(s)
+ # IfcTessellatedFaceSet inverses
+ [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []]
+ [colours.add(t) for t in getattr(subelement, "HasColours", []) or []]
+ elif subelement.is_a("IfcRepresentation"):
+ for layer in subelement.LayerAssignments:
+ presentation_layer_assignments.add(layer)
- ifcopenshell.util.element.remove_deep2(
- self.file,
- self.settings["representation"],
- also_consider=list(styled_items | presentation_layer_assignments | colours),
- do_not_delete=self.file.by_type("IfcGeometricRepresentationContext"),
- )
+ ifcopenshell.util.element.remove_deep2(
+ file,
+ settings["representation"],
+ also_consider=list(styled_items | presentation_layer_assignments | colours),
+ do_not_delete=file.by_type("IfcGeometricRepresentationContext"),
+ )
- for texture in textures:
- ifcopenshell.util.element.remove_deep2(self.file, texture)
- for colour in colours:
- ifcopenshell.util.element.remove_deep2(self.file, colour)
+ for texture in textures:
+ ifcopenshell.util.element.remove_deep2(file, texture)
+ for colour in colours:
+ ifcopenshell.util.element.remove_deep2(file, colour)
- to_delete = getattr(self.file, "to_delete", ())
- for element in styled_items:
- if not element.Item or element.Item in to_delete:
- self.file.remove(element)
- for element in presentation_layer_assignments:
- if all(item in to_delete for item in element.AssignedItems):
- self.file.remove(element)
+ to_delete = getattr(file, "to_delete", ())
+ for element in styled_items:
+ if not element.Item or element.Item in to_delete:
+ file.remove(element)
+ for element in presentation_layer_assignments:
+ if all(item in to_delete for item in element.AssignedItems):
+ file.remove(element)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py
index 389aad88d6..83b1570ac6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py
@@ -20,13 +20,16 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, **settings):
- self.file = file
- self.settings = {"product": None, "representation": None}
- for key, value in settings.items():
- self.settings[key] = value
+def unassign_representation(file, **usecase_settings) -> None:
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"product": None, "representation": None}
+ for key, value in usecase_settings.items():
+ usecase.settings[key] = value
+ return usecase.execute()
+
+class Usecase:
def execute(self):
if self.settings["product"].is_a("IfcProduct"):
self.unassign_product_representation(self.settings["product"], self.settings["representation"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py
index e0caddbe3c..1aa858db17 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py
@@ -15,3 +15,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_georeferencing import add_georeferencing
+from .edit_georeferencing import edit_georeferencing
+from .remove_georeferencing import remove_georeferencing
diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py
index a957970a91..5da8819e47 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py
@@ -17,48 +17,45 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file):
- """Add empty georeferencing entities to a model
+def add_georeferencing(file) -> None:
+ """Add empty georeferencing entities to a model
- By default, models are not georeferenced. Georeferencing requires two
- entities: a definition of the projected coordinated reference system
- (CRS) used, and the transformation parameters between any local coordinate
- system and that projected CRS if any.
+ By default, models are not georeferenced. Georeferencing requires two
+ entities: a definition of the projected coordinated reference system
+ (CRS) used, and the transformation parameters between any local coordinate
+ system and that projected CRS if any.
- This function will create the entities to store the projected CRS and
- map conversion transformation, but will leave all the parameters blank.
- It is this the users responsibility to specify the correct
- georeferencing parameters. See
- ifcopenshell.api.georeference.edit_georeferencing.
+ This function will create the entities to store the projected CRS and
+ map conversion transformation, but will leave all the parameters blank.
+ It is this the users responsibility to specify the correct
+ georeferencing parameters. See
+ ifcopenshell.api.georeference.edit_georeferencing.
- :return: None
- :rtype: None
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("georeference.add_georeferencing", model)
- """
- self.file = file
+ ifcopenshell.api.run("georeference.add_georeferencing", model)
+ """
- def execute(self):
- source_crs = None
- for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
- if context.ContextType == "Model":
- source_crs = context
- break
- if not source_crs:
- return
- projected_crs = self.file.create_entity("IfcProjectedCRS", **{"Name": ""})
- self.file.create_entity(
- "IfcMapConversion",
- **{
- "SourceCRS": source_crs,
- "TargetCRS": projected_crs,
- "Eastings": 0,
- "Northings": 0,
- "OrthogonalHeight": 0,
- }
- )
+ source_crs = None
+ for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
+ if context.ContextType == "Model":
+ source_crs = context
+ break
+ if not source_crs:
+ return
+ projected_crs = file.create_entity("IfcProjectedCRS", **{"Name": ""})
+ file.create_entity(
+ "IfcMapConversion",
+ **{
+ "SourceCRS": source_crs,
+ "TargetCRS": projected_crs,
+ "Eastings": 0,
+ "Northings": 0,
+ "OrthogonalHeight": 0,
+ }
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py
index 77554e1f7f..f4118d96e1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py
@@ -17,77 +17,80 @@
# along with IfcOpenShell. If not, see .
+def edit_georeferencing(file, map_conversion=None, projected_crs=None, true_north=None) -> None:
+ """Edits the attributes of a map conversion, projected CRS, and true north
+
+ Setting the correct georeferencing parameters is a complex topic and
+ should ideally be done with three parties present: the lead architect,
+ surveyor, and a third-party digital engineer with expertise in IFC to
+ moderate. For more information, read the BlenderBIM Add-on documentation
+ for Georeferencing:
+ https://docs.blenderbim.org/users/georeferencing.html
+
+ For more information about the attributes and data types of an
+ IfcMapConversion, consult the IFC documentation.
+
+ For more information about the attributes and data types of an
+ IfcProjectedCRS, consult the IFC documentation.
+
+ True north is defined as a unitised 2D vector pointing to true north.
+ Note that true north is not part of georeferencing, and is only
+ optionally provided as a reference value, typically for solar analysis.
+
+ See ifcopenshell.util.geolocation for more utilities to convert to and
+ from local and map coordinates to check your results.
+
+ :param map_conversion: The IfcMapConversion dictionary of attribute
+ names and values you want to edit.
+ :type map_conversion: dict, optional
+ :param projected_crs: The IfcProjectedCRS dictionary of attribute
+ names and values you want to edit.
+ :type projected_crs: dict, optional
+ :param true_north: A unitised 2D vector, where each ordinate is a float
+ :type true_north: list[float]
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ ifcopenshell.api.run("georeference.add_georeferencing", model)
+ # This is the simplest scenario, a defined CRS (GDA2020 / MGA Zone
+ # 56, typically used in Sydney, Australia) but with no local
+ # coordinates. This is only recommended for horizontal construction
+ # projects, not for vertical construction (such as buildings).
+ ifcopenshell.api.run("georeference.edit_georeferencing", model,
+ projected_crs={"Name": "EPSG:7856"})
+
+ # For buildings, it is almost always recommended to specify map
+ # conversion parameters to a false origin and orientation to project
+ # north. See the diagram in the BlenderBIM Add-on Georeferencing
+ # documentation for correct calculation of the X Axis Abcissa and
+ # Ordinate.
+ ifcopenshell.api.run("georeference.edit_georeferencing", model,
+ projected_crs={"Name": "EPSG:7856"},
+ map_conversion={
+ "Eastings": 335087.17, # The architect nominates a false origin
+ "Northings": 6251635.41, # The architect nominates a false origin
+ # Note: this is the angle difference between Project North
+ # and Grid North. Remember: True North should never be used!
+ "XAxisAbscissa": cos(radians(-30)), # The architect nominates a project north
+ "XAxisOrdinate": sin(radians(-30)), # The architect nominates a project north
+ "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor!
+ })
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "map_conversion": map_conversion or {},
+ "projected_crs": projected_crs or {},
+ "true_north": true_north or [],
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, map_conversion=None, projected_crs=None, true_north=None):
- """Edits the attributes of a map conversion, projected CRS, and true north
-
- Setting the correct georeferencing parameters is a complex topic and
- should ideally be done with three parties present: the lead architect,
- surveyor, and a third-party digital engineer with expertise in IFC to
- moderate. For more information, read the BlenderBIM Add-on documentation
- for Georeferencing:
- https://docs.blenderbim.org/users/georeferencing.html
-
- For more information about the attributes and data types of an
- IfcMapConversion, consult the IFC documentation.
-
- For more information about the attributes and data types of an
- IfcProjectedCRS, consult the IFC documentation.
-
- True north is defined as a unitised 2D vector pointing to true north.
- Note that true north is not part of georeferencing, and is only
- optionally provided as a reference value, typically for solar analysis.
-
- See ifcopenshell.util.geolocation for more utilities to convert to and
- from local and map coordinates to check your results.
-
- :param map_conversion: The IfcMapConversion dictionary of attribute
- names and values you want to edit.
- :type map_conversion: dict, optional
- :param projected_crs: The IfcProjectedCRS dictionary of attribute
- names and values you want to edit.
- :type projected_crs: dict, optional
- :param true_north: A unitised 2D vector, where each ordinate is a float
- :type true_north: list[float]
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- ifcopenshell.api.run("georeference.add_georeferencing", model)
- # This is the simplest scenario, a defined CRS (GDA2020 / MGA Zone
- # 56, typically used in Sydney, Australia) but with no local
- # coordinates. This is only recommended for horizontal construction
- # projects, not for vertical construction (such as buildings).
- ifcopenshell.api.run("georeference.edit_georeferencing", model,
- projected_crs={"Name": "EPSG:7856"})
-
- # For buildings, it is almost always recommended to specify map
- # conversion parameters to a false origin and orientation to project
- # north. See the diagram in the BlenderBIM Add-on Georeferencing
- # documentation for correct calculation of the X Axis Abcissa and
- # Ordinate.
- ifcopenshell.api.run("georeference.edit_georeferencing", model,
- projected_crs={"Name": "EPSG:7856"},
- map_conversion={
- "Eastings": 335087.17, # The architect nominates a false origin
- "Northings": 6251635.41, # The architect nominates a false origin
- # Note: this is the angle difference between Project North
- # and Grid North. Remember: True North should never be used!
- "XAxisAbscissa": cos(radians(-30)), # The architect nominates a project north
- "XAxisOrdinate": sin(radians(-30)), # The architect nominates a project north
- "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor!
- })
- """
- self.file = file
- self.settings = {
- "map_conversion": map_conversion or {},
- "projected_crs": projected_crs or {},
- "true_north": true_north or [],
- }
-
def execute(self):
map_conversion = self.file.by_type("IfcMapConversion")[0]
projected_crs = self.file.by_type("IfcProjectedCRS")[0]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py
index 2ac32a0c6e..3d3941ed7c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py
@@ -17,29 +17,26 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file):
- """Remove georeferencing data
+def remove_georeferencing(file) -> None:
+ """Remove georeferencing data
- All georeferencing parameters such as projected CRS and map conversion
- data will be lost.
+ All georeferencing parameters such as projected CRS and map conversion
+ data will be lost.
- :return: None
- :rtype: None
+ :return: None
+ :rtype: None
- Example:
+ Example:
- ifcopenshell.api.run("georeference.add_georeferencing", model)
- # Let's change our mind
- ifcopenshell.api.run("georeference.remove_georeferencing", model)
- """
- self.file = file
+ ifcopenshell.api.run("georeference.add_georeferencing", model)
+ # Let's change our mind
+ ifcopenshell.api.run("georeference.remove_georeferencing", model)
+ """
- def execute(self):
- map_conversion = self.file.by_type("IfcMapConversion")[0]
- projected_crs = self.file.by_type("IfcProjectedCRS")[0]
- if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1:
- # TODO: go deeper for conversion units
- self.file.remove(projected_crs.MapUnit)
- self.file.remove(projected_crs)
- self.file.remove(map_conversion)
+ map_conversion = file.by_type("IfcMapConversion")[0]
+ projected_crs = file.by_type("IfcProjectedCRS")[0]
+ if projected_crs.MapUnit and len(file.get_inverse(projected_crs.MapUnit)) == 1:
+ # TODO: go deeper for conversion units
+ file.remove(projected_crs.MapUnit)
+ file.remove(projected_crs)
+ file.remove(map_conversion)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py
index e0caddbe3c..c66e86a668 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py
@@ -15,3 +15,7 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .create_axis_curve import create_axis_curve
+from .create_grid_axis import create_grid_axis
+from .remove_grid_axis import remove_grid_axis
diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py
index 21fead74e5..2f3520c662 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py
@@ -22,46 +22,49 @@ import ifcopenshell.util.placement
from mathutils import Matrix # For now, we depend on Blender
+def create_axis_curve(file, axis_curve=None, grid_axis=None) -> None:
+ """Adds curve geometry to a grid axis to represent the axis extents
+
+ This currently depends on the Blender geometry kernel to function.
+
+ An IFC grid will have a minimum of two axes (typically perpendicular). Each
+ axis will then have a line which represents the extents of the axis.
+
+ :param axis_curve: The Blender object that contains a mesh data block with a
+ single edge.
+ :type axis_curve: bpy.types.Object
+ :param grid_axis: The IfcGridAxis element to add geometry to.
+ :type grid_axis: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # A pretty standard rectangular grid, with only two axes.
+ grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid")
+ axis_a = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="A", uvw_axes="UAxes", grid=grid)
+ axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="1", uvw_axes="VAxes", grid=grid)
+
+ # Assume you have these Blender objects in your active Blender session
+ obj1 = bpy.data.objects.get("AxisA")
+ obj2 = bpy.data.objects.get("Axis1")
+ ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a)
+ ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "axis_curve": axis_curve, # A Blender object
+ "grid_axis": grid_axis,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, axis_curve=None, grid_axis=None):
- """Adds curve geometry to a grid axis to represent the axis extents
-
- This currently depends on the Blender geometry kernel to function.
-
- An IFC grid will have a minimum of two axes (typically perpendicular). Each
- axis will then have a line which represents the extents of the axis.
-
- :param axis_curve: The Blender object that contains a mesh data block with a
- single edge.
- :type axis_curve: bpy.types.Object
- :param grid_axis: The IfcGridAxis element to add geometry to.
- :type grid_axis: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # A pretty standard rectangular grid, with only two axes.
- grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid")
- axis_a = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="A", uvw_axes="UAxes", grid=grid)
- axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="1", uvw_axes="VAxes", grid=grid)
-
- # Assume you have these Blender objects in your active Blender session
- obj1 = bpy.data.objects.get("AxisA")
- obj2 = bpy.data.objects.get("Axis1")
- ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a)
- ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1)
- """
- self.file = file
- self.settings = {
- "axis_curve": axis_curve, # A Blender object
- "grid_axis": grid_axis,
- }
-
def execute(self):
existing_curve = self.settings["grid_axis"].AxisCurve
if existing_curve and len(self.file.get_inverse(existing_curve)) == 1:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py
index de667bb5bc..f089b43b98 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py
@@ -17,69 +17,66 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None):
- """Adds a new grid axis to a grid
+def create_grid_axis(file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None) -> None:
+ """Adds a new grid axis to a grid
- An IFC grid will typically have a minimum of two axes which will be
- perpendicular to one another. Grids may be rectangular (typically
- perpendicular lines), radial (where one set of axes is a circle and the
- other is a line), or triangular (three sets of axes, each at a different
- angle to one another).
+ An IFC grid will typically have a minimum of two axes which will be
+ perpendicular to one another. Grids may be rectangular (typically
+ perpendicular lines), radial (where one set of axes is a circle and the
+ other is a line), or triangular (three sets of axes, each at a different
+ angle to one another).
- For a simple rectangular grid, the "UAxes" are a set of one or more
- horizontal axes, which are typically labeled with the convention of A,
- B, C, etc. The "VAxes" is another set of one or more vertical axes,
- typically labeled with the convention of 1, 2, 3, etc. These axes are
- horizontal or vertical relative to project north.
+ For a simple rectangular grid, the "UAxes" are a set of one or more
+ horizontal axes, which are typically labeled with the convention of A,
+ B, C, etc. The "VAxes" is another set of one or more vertical axes,
+ typically labeled with the convention of 1, 2, 3, etc. These axes are
+ horizontal or vertical relative to project north.
- For a radial grid, the "UAxes" are straight lines, typically radiating
- from a central point. The "VAxes" are circular perimeters, with the
- center of these circles being the same central point.
+ For a radial grid, the "UAxes" are straight lines, typically radiating
+ from a central point. The "VAxes" are circular perimeters, with the
+ center of these circles being the same central point.
- For a triangular grid, the UAxes, VAxes, and WAxes are all sets of one
- or more straight lines.
+ For a triangular grid, the UAxes, VAxes, and WAxes are all sets of one
+ or more straight lines.
- :param axis_tag: The name of the axis, that would typically be labeled
- on drawings or described on site during coordination, such as A, B,
- C, 1, 2, 3, etc. Defaults to "A".
- :type axis_tag: str, optional
- :param same_sense: Determines whether the direction of the axis's line
- is reversed. True means the direction the geometry is defined in
- represents the direction of the axis. False means the direction is
- reversed. Leave as True if unsure. Defaults to "True".
- :type same_sense: bool, optional
- :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on
- which set of axes the new axis you are adding should belong to.
- Defaults to "UAxes".
- :type uvw_axes: str, optional
- :param grid: The IfcGrid you are adding the axis to.
- :type grid: ifcopenshell.entity_instance
- :return: The newly created IfcGridAxis
- :rtype: ifcopenshell.entity_instance
+ :param axis_tag: The name of the axis, that would typically be labeled
+ on drawings or described on site during coordination, such as A, B,
+ C, 1, 2, 3, etc. Defaults to "A".
+ :type axis_tag: str, optional
+ :param same_sense: Determines whether the direction of the axis's line
+ is reversed. True means the direction the geometry is defined in
+ represents the direction of the axis. False means the direction is
+ reversed. Leave as True if unsure. Defaults to "True".
+ :type same_sense: bool, optional
+ :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on
+ which set of axes the new axis you are adding should belong to.
+ Defaults to "UAxes".
+ :type uvw_axes: str, optional
+ :param grid: The IfcGrid you are adding the axis to.
+ :type grid: ifcopenshell.entity_instance
+ :return: The newly created IfcGridAxis
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- # A pretty standard rectangular grid, with only two axes.
- grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid")
- axis_a = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="A", uvw_axes="UAxes", grid=grid)
- axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="1", uvw_axes="VAxes", grid=grid)
- """
- self.file = file
- self.settings = {
- "axis_tag": axis_tag or "A",
- "same_sense": same_sense or True,
- "uvw_axes": uvw_axes or "UAxes", # Choose which axes
- "grid": grid,
- }
+ # A pretty standard rectangular grid, with only two axes.
+ grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid")
+ axis_a = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="A", uvw_axes="UAxes", grid=grid)
+ axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="1", uvw_axes="VAxes", grid=grid)
+ """
+ settings = {
+ "axis_tag": axis_tag or "A",
+ "same_sense": same_sense or True,
+ "uvw_axes": uvw_axes or "UAxes", # Choose which axes
+ "grid": grid,
+ }
- def execute(self):
- element = self.file.create_entity(
- "IfcGridAxis", **{"AxisTag": self.settings["axis_tag"], "SameSense": self.settings["same_sense"]}
- )
- axes = list(getattr(self.settings["grid"], self.settings["uvw_axes"]) or [])
- axes.append(element)
- setattr(self.settings["grid"], self.settings["uvw_axes"], axes)
- return element
+ element = file.create_entity(
+ "IfcGridAxis", **{"AxisTag": settings["axis_tag"], "SameSense": settings["same_sense"]}
+ )
+ axes = list(getattr(settings["grid"], settings["uvw_axes"]) or [])
+ axes.append(element)
+ setattr(settings["grid"], settings["uvw_axes"], axes)
+ return element
diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py
index b380778a67..51032ed52f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py
@@ -19,36 +19,33 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, axis=None):
- """Removes a grid axis from a grid
+def remove_grid_axis(file, axis=None) -> None:
+ """Removes a grid axis from a grid
- :param axis: The IfcGridAxis you want to remove.
- :type axis: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param axis: The IfcGridAxis you want to remove.
+ :type axis: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- # A pretty standard rectangular grid, with only two axes.
- grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid")
- axis_a = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="A", uvw_axes="UAxes", grid=grid)
- axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="1", uvw_axes="VAxes", grid=grid)
+ # A pretty standard rectangular grid, with only two axes.
+ grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid")
+ axis_a = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="A", uvw_axes="UAxes", grid=grid)
+ axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="1", uvw_axes="VAxes", grid=grid)
- # Let's create a third so we can remove it later
- axis_2 = ifcopenshell.api.run("grid.create_grid_axis", model,
- axis_tag="2", uvw_axes="VAxes", grid=grid)
+ # Let's create a third so we can remove it later
+ axis_2 = ifcopenshell.api.run("grid.create_grid_axis", model,
+ axis_tag="2", uvw_axes="VAxes", grid=grid)
- # Let's remove it!
- ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2)
- """
- self.file = file
- self.settings = {"axis": axis}
+ # Let's remove it!
+ ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2)
+ """
+ settings = {"axis": axis}
- def execute(self):
- if len(self.file.get_inverse(self.settings["axis"].AxisCurve)) == 1:
- ifcopenshell.util.element.remove_deep(self.file, self.settings["axis"].AxisCurve)
- self.file.remove(self.settings["axis"].AxisCurve)
- self.file.remove(self.settings["axis"])
+ if len(file.get_inverse(settings["axis"].AxisCurve)) == 1:
+ ifcopenshell.util.element.remove_deep(file, settings["axis"].AxisCurve)
+ file.remove(settings["axis"].AxisCurve)
+ file.remove(settings["axis"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py
index e0caddbe3c..5b729b0dfc 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py
@@ -15,3 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_group import add_group
+from .assign_group import assign_group
+from .edit_group import edit_group
+from .remove_group import remove_group
+from .unassign_group import unassign_group
+from .update_group_products import update_group_products
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py
index 298ec42ba6..1d576f7173 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py
@@ -20,44 +20,41 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, Name="Unnamed", Description=None):
- """Adds a new group
+def add_group(file, Name="Unnamed", Description=None) -> None:
+ """Adds a new group
- An IFC group is an arbitrary collection of products, which are typically
- physical. It may be used when there is no other more specific group
- which may be used. Other types of groups include distribution systems,
- which group together products that are connected and circulate a medium
- (such as fluid or electricity), or zones, which group together spaces,
- or structural load groups, which group together loads for structural
- analysis, or inventories, which are groups of assets.
+ An IFC group is an arbitrary collection of products, which are typically
+ physical. It may be used when there is no other more specific group
+ which may be used. Other types of groups include distribution systems,
+ which group together products that are connected and circulate a medium
+ (such as fluid or electricity), or zones, which group together spaces,
+ or structural load groups, which group together loads for structural
+ analysis, or inventories, which are groups of assets.
- :param Name: The name of the group. Defaults to "Unnamed"
- :type Name: str, optional
- :param Description: The description of the purpose of the group.
- :type Description: str, optional
- :return: The newly created IfcGroup
- :rtype: ifcopenshell.entity_instance
+ :param Name: The name of the group. Defaults to "Unnamed"
+ :type Name: str, optional
+ :param Description: The description of the purpose of the group.
+ :type Description: str, optional
+ :return: The newly created IfcGroup
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
- """
- self.file = file
- self.settings = {
- "Name": Name or "Unnamed",
- "Description": Description,
+ ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
+ """
+ settings = {
+ "Name": Name or "Unnamed",
+ "Description": Description,
+ }
+
+ return file.create_entity(
+ "IfcGroup",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "Name": settings["Name"],
+ "Description": settings["Description"],
}
-
- def execute(self):
- return self.file.create_entity(
- "IfcGroup",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "Name": self.settings["Name"],
- "Description": self.settings["Description"],
- }
- )
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py
index d312a95bd6..c5afd5b9b0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py
@@ -21,56 +21,53 @@ import ifcopenshell.api
from typing import Union
-class Usecase:
- def __init__(
- self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance
- ):
- """Assigns products to a group
+def assign_group(
+ file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Assigns products to a group
- If a product is already assigned to the group, it will not be assigned
- twice.
+ If a product is already assigned to the group, it will not be assigned
+ twice.
- :param products: A list of IfcProduct elements to assign to the group
- :type products: list[ifcopenshell.entity_instance]
- :param group: The IfcGroup to assign the products to
- :type group: ifcopenshell.entity_instance
- :return: The IfcRelAssignsToGroup relationship
- or `None` if `products` was empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param products: A list of IfcProduct elements to assign to the group
+ :type products: list[ifcopenshell.entity_instance]
+ :param group: The IfcGroup to assign the products to
+ :type group: ifcopenshell.entity_instance
+ :return: The IfcRelAssignsToGroup relationship
+ or `None` if `products` was empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- group = ifcopenshell.api.run("group.add_group", model, Name="Furniture")
- ifcopenshell.api.run("group.assign_group", model,
- products=model.by_type("IfcFurniture"), group=group)
- """
- self.file = file
- self.settings = {
- "products": products,
- "group": group,
- }
+ group = ifcopenshell.api.run("group.add_group", model, Name="Furniture")
+ ifcopenshell.api.run("group.assign_group", model,
+ products=model.by_type("IfcFurniture"), group=group)
+ """
+ settings = {
+ "products": products,
+ "group": group,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- if not self.settings["products"]:
- return
+ if not settings["products"]:
+ return
- if not self.settings["group"].IsGroupedBy:
- return self.file.create_entity(
- "IfcRelAssignsToGroup",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": self.settings["products"],
- "RelatingGroup": self.settings["group"],
- }
- )
- rel = self.settings["group"].IsGroupedBy[0]
- related_objects = set(rel.RelatedObjects) or set()
- products = set(self.settings["products"])
- if products.issubset(related_objects):
- return rel
- rel.RelatedObjects = list(related_objects | products)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
+ if not settings["group"].IsGroupedBy:
+ return file.create_entity(
+ "IfcRelAssignsToGroup",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": settings["products"],
+ "RelatingGroup": settings["group"],
+ }
+ )
+ rel = settings["group"].IsGroupedBy[0]
+ related_objects = set(rel.RelatedObjects) or set()
+ products = set(settings["products"])
+ if products.issubset(related_objects):
return rel
+ rel.RelatedObjects = list(related_objects | products)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py
index 87fa9dcf12..1eb0c8d6f4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, group=None, attributes=None):
- """Edits the attributes of an IfcGroup
+def edit_group(file, group=None, attributes=None) -> None:
+ """Edits the attributes of an IfcGroup
- For more information about the attributes and data types of an
- IfcGroup, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcGroup, consult the IFC documentation.
- :param group: The IfcGroup entity you want to edit
- :type group: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param group: The IfcGroup entity you want to edit
+ :type group: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
- ifcopenshell.api.run("group.edit_group", model,
- group=group, attributes={"Description": "All furniture and joinery included in the unit"})
- """
- self.file = file
- self.settings = {"group": group, "attributes": attributes or {}}
+ group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
+ ifcopenshell.api.run("group.edit_group", model,
+ group=group, attributes={"Description": "All furniture and joinery included in the unit"})
+ """
+ settings = {"group": group, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["group"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["group"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py
index c87b36e316..05e85f3fc0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py
@@ -21,53 +21,50 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, group=None):
- """Removes a group
+def remove_group(file, group=None) -> None:
+ """Removes a group
- All products assigned to the group will remain, but the relationship to
- the group will be removed.
+ All products assigned to the group will remain, but the relationship to
+ the group will be removed.
- :param group: The IfcGroup entity you want to remove
- :type group: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param group: The IfcGroup entity you want to remove
+ :type group: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
- ifcopenshell.api.run("group.remove_group", model, group=group)
- """
- self.file = file
- self.settings = {"group": group}
+ group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A")
+ ifcopenshell.api.run("group.remove_group", model, group=group)
+ """
+ settings = {"group": group}
- def execute(self):
- for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["group"])]:
- try:
- inverse = self.file.by_id(inverse_id)
- except:
- continue
- if inverse.is_a("IfcRelDefinesByProperties"):
- ifcopenshell.api.run(
- "pset.remove_pset",
- self.file,
- product=self.settings["group"],
- pset=inverse.RelatingPropertyDefinition,
- )
- elif inverse.is_a("IfcRelAssignsToGroup"):
- if inverse.RelatingGroup == self.settings["group"]:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["group"].OwnerHistory
- self.file.remove(self.settings["group"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for inverse_id in [i.id() for i in file.get_inverse(settings["group"])]:
+ try:
+ inverse = file.by_id(inverse_id)
+ except:
+ continue
+ if inverse.is_a("IfcRelDefinesByProperties"):
+ ifcopenshell.api.run(
+ "pset.remove_pset",
+ file,
+ product=settings["group"],
+ pset=inverse.RelatingPropertyDefinition,
+ )
+ elif inverse.is_a("IfcRelAssignsToGroup"):
+ if inverse.RelatingGroup == settings["group"]:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["group"].OwnerHistory
+ file.remove(settings["group"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py
index 9229281a69..c486cceab6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py
@@ -21,48 +21,47 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance):
- """Unassigns products from a group
+def unassign_group(
+ file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance
+) -> None:
+ """Unassigns products from a group
- If the product isn't assigned to the group, nothing will happen.
+ If the product isn't assigned to the group, nothing will happen.
- :param products: A list of IfcProduct elements to unassign from the group
- :type products: list[ifcopenshell.entity_instance]
- :param group: The IfcGroup to unassign from
- :type group: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param products: A list of IfcProduct elements to unassign from the group
+ :type products: list[ifcopenshell.entity_instance]
+ :param group: The IfcGroup to unassign from
+ :type group: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- group = ifcopenshell.api.run("group.add_group", model, Name="Furniture")
- furniture = model.by_type("IfcFurniture")
- ifcopenshell.api.run("group.assign_group", model, products=furniture, group=group)
+ group = ifcopenshell.api.run("group.add_group", model, Name="Furniture")
+ furniture = model.by_type("IfcFurniture")
+ ifcopenshell.api.run("group.assign_group", model, products=furniture, group=group)
- bad_furniture = furniture[0]
- ifcopenshell.api.run("group.unassign_group", model, products=[bad_furniture], group=group)
- """
- self.file = file
- self.settings = {
- "products": products,
- "group": group,
- }
+ bad_furniture = furniture[0]
+ ifcopenshell.api.run("group.unassign_group", model, products=[bad_furniture], group=group)
+ """
+ settings = {
+ "products": products,
+ "group": group,
+ }
- def execute(self) -> None:
- if not self.settings["group"].IsGroupedBy:
- return
- rel = self.settings["group"].IsGroupedBy[0]
- related_objects = set(rel.RelatedObjects) or set()
- products = set(self.settings["products"])
- related_objects -= products
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if not settings["group"].IsGroupedBy:
+ return
+ rel = settings["group"].IsGroupedBy[0]
+ related_objects = set(rel.RelatedObjects) or set()
+ products = set(settings["products"])
+ related_objects -= products
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py
index 61b96c2ba6..f526666fa2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py
@@ -20,51 +20,48 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, group=None, products=None):
- """Sets a group products to be an explicit list of products
+def update_group_products(file, group=None, products=None) -> None:
+ """Sets a group products to be an explicit list of products
- Any previous products assigned to that group will have their assignment
- removed.
+ Any previous products assigned to that group will have their assignment
+ removed.
- :param products: A list of IfcProduct elements to assign to the group
- :type products: list[ifcopenshell.entity_instance]
- :param group: The IfcGroup to assign the products to
- :type group: ifcopenshell.entity_instance
- :return: The IfcRelAssignsToGroup relationship
- :rtype: ifcopenshell.entity_instance
+ :param products: A list of IfcProduct elements to assign to the group
+ :type products: list[ifcopenshell.entity_instance]
+ :param group: The IfcGroup to assign the products to
+ :type group: ifcopenshell.entity_instance
+ :return: The IfcRelAssignsToGroup relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- group = ifcopenshell.api.run("group.add_group", model, Name="Furniture")
- ifcopenshell.api.run("group.update_group_products", model,
- products=model.by_type("IfcFurniture"), group=group)
- """
- self.file = file
- self.settings = {
- "group": group,
- "products": products,
- }
+ group = ifcopenshell.api.run("group.add_group", model, Name="Furniture")
+ ifcopenshell.api.run("group.update_group_products", model,
+ products=model.by_type("IfcFurniture"), group=group)
+ """
+ settings = {
+ "group": group,
+ "products": products,
+ }
- def execute(self):
- if not self.settings["group"].IsGroupedBy:
- return self.file.create_entity(
- "IfcRelAssignsToGroup",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": self.settings["products"],
- "RelatingGroup": self.settings["group"],
- }
- )
- else:
- # assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes
- # where the cardinality is 0:? - vulevukusej
- rel = self.settings["group"].IsGroupedBy[0]
- existing_sub_groups = [g for g in rel.RelatedObjects if g.is_a("IfcGroup")]
+ if not settings["group"].IsGroupedBy:
+ return file.create_entity(
+ "IfcRelAssignsToGroup",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": settings["products"],
+ "RelatingGroup": settings["group"],
+ }
+ )
+ else:
+ # assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes
+ # where the cardinality is 0:? - vulevukusej
+ rel = settings["group"].IsGroupedBy[0]
+ existing_sub_groups = [g for g in rel.RelatedObjects if g.is_a("IfcGroup")]
- rel.RelatedObjects = self.settings["products"]
- for g in existing_sub_groups:
- rel.RelatedObjects.add(g)
+ rel.RelatedObjects = settings["products"]
+ for g in existing_sub_groups:
+ rel.RelatedObjects.add(g)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py
index e0caddbe3c..03145bf34a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py
@@ -15,3 +15,9 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_layer import add_layer
+from .assign_layer import assign_layer
+from .edit_layer import edit_layer
+from .remove_layer import remove_layer
+from .unassign_layer import unassign_layer
diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py
index 8638a76b22..5379ff1b3a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, Name=None):
- """Adds a new layer
+def add_layer(file, Name=None) -> None:
+ """Adds a new layer
- An IFC layer is like a CAD layer. Portions of an object's geometry
- (typically portions of its 2D linework) can be assigned to layers, which
- can provide stylistic information such as line weights, colours, or
- simply be used for filtering.
+ An IFC layer is like a CAD layer. Portions of an object's geometry
+ (typically portions of its 2D linework) can be assigned to layers, which
+ can provide stylistic information such as line weights, colours, or
+ simply be used for filtering.
- Layers have historically been used to organise CAD data and included in
- ISO standards such as ISO 13567 or by the AIA. This alllows IFC data to
- be compatible with older, 2D-oriented, layer-based workflows.
+ Layers have historically been used to organise CAD data and included in
+ ISO standards such as ISO 13567 or by the AIA. This alllows IFC data to
+ be compatible with older, 2D-oriented, layer-based workflows.
- Some software that are still based on layers, such as Tekla or ArchiCAD
- may also use this layer information for filtering.
+ Some software that are still based on layers, such as Tekla or ArchiCAD
+ may also use this layer information for filtering.
- :param Name: The name of the layer. Defaults to "Unnamed".
- :type Name: str, optional
- :return: The newly created IfcPresentationLayerAssignment element
- :rtype: ifcopenshell.entity_instance
+ :param Name: The name of the layer. Defaults to "Unnamed".
+ :type Name: str, optional
+ :return: The newly created IfcPresentationLayerAssignment element
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N")
- """
- self.file = file
- self.settings = {"Name": Name or "Unnamed"}
+ ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N")
+ """
+ settings = {"Name": Name or "Unnamed"}
- def execute(self):
- return self.file.create_entity("IfcPresentationLayerAssignment", Name=self.settings["Name"])
+ return file.create_entity("IfcPresentationLayerAssignment", Name=settings["Name"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py
index 93a863d66f..70926625c3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py
@@ -19,64 +19,61 @@
import ifcopenshell
-class Usecase:
- def __init__(
- self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance
- ):
- """Assigns representation items to a layer
+def assign_layer(
+ file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance
+) -> None:
+ """Assigns representation items to a layer
- In IFC, instead of objects being assigned to layers, representation
- items are assigned to layers. Representation items are portions of the
- object's representation. For example, this allows a single IFC Window
- element to have portions of its 2D linework (e.g. the cross section of
- its frame) assigned to one layer, and another portion (e.g. the glazing
- panels) assigned to another layer.
+ In IFC, instead of objects being assigned to layers, representation
+ items are assigned to layers. Representation items are portions of the
+ object's representation. For example, this allows a single IFC Window
+ element to have portions of its 2D linework (e.g. the cross section of
+ its frame) assigned to one layer, and another portion (e.g. the glazing
+ panels) assigned to another layer.
- :param items: The list of IfcRepresentationItems to assign to the layer. This
- should be the items from the object's IfcShapeRepresentation.
- :type items: list[ifcopenshell.entity_instance]
- :param layer: The IfcPresentationLayerAssignment layer to assign the
- item to.
- :type layer: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param items: The list of IfcRepresentationItems to assign to the layer. This
+ should be the items from the object's IfcShapeRepresentation.
+ :type items: list[ifcopenshell.entity_instance]
+ :param layer: The IfcPresentationLayerAssignment layer to assign the
+ item to.
+ :type layer: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Remember, all geometry needs to specify the context it is part of first.
- # See ifcopenshell.api.context.add_context for details.
- model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
- )
+ # Remember, all geometry needs to specify the context it is part of first.
+ # See ifcopenshell.api.context.add_context for details.
+ model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
+ )
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- # Now let's create a layer that contains walls
- layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
+ # Now let's create a layer that contains walls
+ layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
- # And assign our wall representation item (in this example, there is
- # only one item) to the layer.
- ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer)
- """
- self.file = file
- self.settings = {
- "items": items,
- "layer": layer,
- }
+ # And assign our wall representation item (in this example, there is
+ # only one item) to the layer.
+ ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer)
+ """
+ settings = {
+ "items": items,
+ "layer": layer,
+ }
- def execute(self) -> None:
- # support AssignedItems == None since layer might just got created
- layer = self.settings["layer"]
- assigned_items = set(layer.AssignedItems or [])
- items = set(self.settings["items"])
- if items.issubset(assigned_items):
- return
- layer.AssignedItems = list(assigned_items | items)
+ # support AssignedItems == None since layer might just got created
+ layer = settings["layer"]
+ assigned_items = set(layer.AssignedItems or [])
+ items = set(settings["items"])
+ if items.issubset(assigned_items):
+ return
+ layer.AssignedItems = list(assigned_items | items)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py
index c2b1cbc99a..9d96156cfc 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, layer=None, attributes=None):
- """Edits the attributes of an IfcPresentationLayerAssignment
+def edit_layer(file, layer=None, attributes=None) -> None:
+ """Edits the attributes of an IfcPresentationLayerAssignment
- For more information about the attributes and data types of an
- IfcPresentationLayerAssignment, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcPresentationLayerAssignment, consult the IFC documentation.
- :param layer: The IfcPresentationLayerAssignment entity you want to edit
- :type layer: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param layer: The IfcPresentationLayerAssignment entity you want to edit
+ :type layer: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
- ifcopenshell.api.run("layer.edit_layer", model,
- layer=layer, attributes={"Description": "All walls, based on the AIA standard."})
- """
- self.file = file
- self.settings = {"layer": layer, "attributes": attributes or {}}
+ layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
+ ifcopenshell.api.run("layer.edit_layer", model,
+ layer=layer, attributes={"Description": "All walls, based on the AIA standard."})
+ """
+ settings = {"layer": layer, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["layer"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["layer"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py
index 8e83e475ab..790b396174 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py
@@ -17,27 +17,24 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, layer=None):
- """Removes a layer
+def remove_layer(file, layer=None) -> None:
+ """Removes a layer
- All representation items assigned to the layer will remain, but the
- relationship to the layer will be removed.
+ All representation items assigned to the layer will remain, but the
+ relationship to the layer will be removed.
- :param layer: The IfcPresentationLayerAssignment entity to remove
- :type layer: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param layer: The IfcPresentationLayerAssignment entity to remove
+ :type layer: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
- ifcopenshell.api.run("layer.remove_layer", model, layer=layer)
- """
- self.file = file
- self.settings = {"layer": layer}
+ layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
+ ifcopenshell.api.run("layer.remove_layer", model, layer=layer)
+ """
+ settings = {"layer": layer}
- def execute(self):
- self.file.remove(self.settings["layer"])
+ file.remove(settings["layer"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py
index f9d6a024a3..9418a28ad6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py
@@ -20,68 +20,65 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance
- ):
- """Unassigns representation items from a layer
+def unassign_layer(
+ file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance
+) -> None:
+ """Unassigns representation items from a layer
- If the representation item isn't assigned to the layer, nothing will
- happen.
- If after unassignment layer won't have any assigned items it will be
- removed to keep IFC valid.
+ If the representation item isn't assigned to the layer, nothing will
+ happen.
+ If after unassignment layer won't have any assigned items it will be
+ removed to keep IFC valid.
- :param items: A list IfcRepresentationItem elements to unassign
- :type items: list[ifcopenshell.entity_instance]
- :param layer: The IfcPresentationLayerAssignment to unassign from
- :type layer: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param items: A list IfcRepresentationItem elements to unassign
+ :type items: list[ifcopenshell.entity_instance]
+ :param layer: The IfcPresentationLayerAssignment to unassign from
+ :type layer: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Remember, all geometry needs to specify the context it is part of first.
- # See ifcopenshell.api.context.add_context for details.
- model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
- )
+ # Remember, all geometry needs to specify the context it is part of first.
+ # See ifcopenshell.api.context.add_context for details.
+ model = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
+ )
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- # Now let's create a layer that contains walls
- layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
+ # Now let's create a layer that contains walls
+ layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL")
- # And assign our wall representation item (in this example, there is
- # only one item) to the layer.
- ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer)
+ # And assign our wall representation item (in this example, there is
+ # only one item) to the layer.
+ ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer)
- # Let's undo it!
- ifcopenshell.api.run("layer.unassign_layer", model, items=[representation.Items[0]], layer=layer)
- """
- self.file = file
- self.settings = {
- "items": items,
- "layer": layer,
- }
+ # Let's undo it!
+ ifcopenshell.api.run("layer.unassign_layer", model, items=[representation.Items[0]], layer=layer)
+ """
+ settings = {
+ "items": items,
+ "layer": layer,
+ }
- def execute(self):
- layer = self.settings["layer"]
- assigned_items = set(layer.AssignedItems) or set()
- items = set(self.settings["items"])
- if not items.issubset(assigned_items):
- return
- assigned_items = list(assigned_items - items)
+ layer = settings["layer"]
+ assigned_items = set(layer.AssignedItems) or set()
+ items = set(settings["items"])
+ if not items.issubset(assigned_items):
+ return
+ assigned_items = list(assigned_items - items)
- # keep IFC valid in case if there are no items left
- if assigned_items:
- layer.AssignedItems = assigned_items
- else:
- self.file.remove(layer)
+ # keep IFC valid in case if there are no items left
+ if assigned_items:
+ layer.AssignedItems = assigned_items
+ else:
+ file.remove(layer)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py
index e0caddbe3c..dbb74de3d4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py
@@ -15,3 +15,12 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_library import add_library
+from .add_reference import add_reference
+from .assign_reference import assign_reference
+from .edit_library import edit_library
+from .edit_reference import edit_reference
+from .remove_library import remove_library
+from .remove_reference import remove_reference
+from .unassign_reference import unassign_reference
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py
index f20494ac5f..16cd00b914 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py
@@ -21,48 +21,45 @@ import ifcopenshell.util.schema
import ifcopenshell.util.date
-class Usecase:
- def __init__(self, file, name=None):
- """Adds a new library to the project
+def add_library(file, name=None) -> None:
+ """Adds a new library to the project
- A library is an external data source that is related to the project. It
- may be a database, a spreadsheet, an API, or even a stack of papers in a
- filing cabinet. This allows IFC data to store relationships to these
- external data sources.
+ A library is an external data source that is related to the project. It
+ may be a database, a spreadsheet, an API, or even a stack of papers in a
+ filing cabinet. This allows IFC data to store relationships to these
+ external data sources.
- For example, you may have a list of laser scans of a site stored in an
- online platform, which can be queried using an API. Or, you might have a
- database of live building sensor data. So long as there is a clear
- identifier you can use to link the two datasets together, you can create
- a relationship.
+ For example, you may have a list of laser scans of a site stored in an
+ online platform, which can be queried using an API. Or, you might have a
+ database of live building sensor data. So long as there is a clear
+ identifier you can use to link the two datasets together, you can create
+ a relationship.
- Note that IFC does not store any instructions on how to access the
- library. It does not specify whether a HTTP request or database
- connection needs to be made or what protocol the library operates with.
- Until this is fleshed out further, it is the users responsibility to
- name the libraries consistently and use appropriate identifiers. For
- example, if you are linking IFC data and Brickschema data, use a full
- URI for the identifier with no abbreviation (e.g.
- 'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01').
+ Note that IFC does not store any instructions on how to access the
+ library. It does not specify whether a HTTP request or database
+ connection needs to be made or what protocol the library operates with.
+ Until this is fleshed out further, it is the users responsibility to
+ name the libraries consistently and use appropriate identifiers. For
+ example, if you are linking IFC data and Brickschema data, use a full
+ URI for the identifier with no abbreviation (e.g.
+ 'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01').
- A library will then contain a list of references within that library.
- These references will then be related to IFC elements. For example, a
- library will represent an external database, and a reference will point
- to a particular table and row within that database.
+ A library will then contain a list of references within that library.
+ These references will then be related to IFC elements. For example, a
+ library will represent an external database, and a reference will point
+ to a particular table and row within that database.
- :param name: The name of the library
- :type name: str
- :return: The newly created IfcLibraryInformation
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the library
+ :type name: str
+ :return: The newly created IfcLibraryInformation
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- """
- self.file = file
- self.settings = {"name": name}
+ ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ """
+ settings = {"name": name}
- def execute(self):
- return self.file.create_entity("IfcLibraryInformation", Name=self.settings["name"])
+ return file.create_entity("IfcLibraryInformation", Name=settings["name"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py
index 84f6605cf0..626d413b3d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py
@@ -19,48 +19,45 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file: ifcopenshell.file, library: ifcopenshell.entity_instance):
- """Adds a new reference to a library
+def add_reference(file: ifcopenshell.file, library: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
+ """Adds a new reference to a library
- A library represents an external data source, such as a database,
- spreadsheet, API, or something else that contains information related to
- the IFC project. Within a library, there will be one or more references,
- such as reference to a particular table or row in a database, or a sheet
- and row or column in a spreadsheet, a URI in a linked data Brickschema
- file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP
- address in a network, and so on.
+ A library represents an external data source, such as a database,
+ spreadsheet, API, or something else that contains information related to
+ the IFC project. Within a library, there will be one or more references,
+ such as reference to a particular table or row in a database, or a sheet
+ and row or column in a spreadsheet, a URI in a linked data Brickschema
+ file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP
+ address in a network, and so on.
- These references can then be related to IFC elements. You cannot relate
- an IFC element directly to a library, it must be related to one of the
- library's references.
+ These references can then be related to IFC elements. You cannot relate
+ an IFC element directly to a library, it must be related to one of the
+ library's references.
- :param library: The IfcLibraryInformation element to add a reference to
- :type library: ifcopenshell.entity_instance
- :return: The newly created IfcLibraryReference element
- :rtype: ifcopenshell.entity_instance
+ :param library: The IfcLibraryInformation element to add a reference to
+ :type library: ifcopenshell.entity_instance
+ :return: The newly created IfcLibraryReference element
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- # Let's create a reference to a single AHU in our Brickschema dataset
- reference = ifcopenshell.api.run("library.add_reference", model, library=library)
- ifcopenshell.api.run("library.edit_reference", model,
- reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
- """
- self.file = file
- self.settings = {
- "library": library,
- }
+ # Let's create a reference to a single AHU in our Brickschema dataset
+ reference = ifcopenshell.api.run("library.add_reference", model, library=library)
+ ifcopenshell.api.run("library.edit_reference", model,
+ reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
+ """
+ settings = {
+ "library": library,
+ }
- def execute(self) -> ifcopenshell.entity_instance:
- if self.file.schema == "IFC2X3":
- reference = self.file.createIfcLibraryReference()
- references = list(self.settings["library"].LibraryReference or [])
- references.append(reference)
- self.settings["library"].LibraryReference = references
- return reference
- return self.file.createIfcLibraryReference(ReferencedLibrary=self.settings["library"])
+ if file.schema == "IFC2X3":
+ reference = file.createIfcLibraryReference()
+ references = list(settings["library"].LibraryReference or [])
+ references.append(reference)
+ settings["library"].LibraryReference = references
+ return reference
+ return file.createIfcLibraryReference(ReferencedLibrary=settings["library"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py
index 8a0880ccf0..6cbd208865 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py
@@ -22,82 +22,75 @@ import ifcopenshell.util.element
from typing import Union
-class Usecase:
- def __init__(
- self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
- ):
- """Associates a list products with a library reference
+def assign_reference(
+ file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Associates a list products with a library reference
- A product may be associated with zero, one, or many references across
- multiple libraries. See ifcopenshell.api.library.add_reference for more
- detail about how references work.
+ A product may be associated with zero, one, or many references across
+ multiple libraries. See ifcopenshell.api.library.add_reference for more
+ detail about how references work.
- :param products: The list of IfcProducts you want to associate with the reference
- :type products: list[ifcopenshell.entity_instance]
- :param reference: The IfcLibraryReference you want the product to be
- associated with.
- :type reference: ifcopenshell.entity_instance
- :return: The IfcRelAssociatesLibrary relationship entity
- or `None` if `products` was an empty list or all products were
- already assigned to the `reference`.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param products: The list of IfcProducts you want to associate with the reference
+ :type products: list[ifcopenshell.entity_instance]
+ :param reference: The IfcLibraryReference you want the product to be
+ associated with.
+ :type reference: ifcopenshell.entity_instance
+ :return: The IfcRelAssociatesLibrary relationship entity
+ or `None` if `products` was an empty list or all products were
+ already assigned to the `reference`.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- # Let's create a reference to a single AHU in our Brickschema dataset
- reference = ifcopenshell.api.run("library.add_reference", model, library=library)
- ifcopenshell.api.run("library.edit_reference", model,
- reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
+ # Let's create a reference to a single AHU in our Brickschema dataset
+ reference = ifcopenshell.api.run("library.add_reference", model, library=library)
+ ifcopenshell.api.run("library.edit_reference", model,
+ reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
- # Let's assume we have an AHU in our model.
- ahu = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
+ # Let's assume we have an AHU in our model.
+ ahu = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
- # And now assign the IFC model's AHU with its Brickschema counterpart
- ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
- """
- self.file = file
- self.settings = {
- "products": products,
- "reference": reference,
- }
+ # And now assign the IFC model's AHU with its Brickschema counterpart
+ ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
+ """
+ settings = {
+ "products": products,
+ "reference": reference,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
+ # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
- referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
- products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
- products = products - referenced_elements
+ referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
+ products: set[ifcopenshell.entity_instance] = set(settings["products"])
+ products = products - referenced_elements
- if not products:
- return
+ if not products:
+ return
- if self.file.schema == "IFC2X3":
- rel = next(
- (
- r
- for r in self.file.by_type("IfcRelAssociatesLibrary")
- if r.RelatingLibrary == self.settings["reference"]
- ),
- None,
- )
- else:
- rel = next(iter(self.settings["reference"].LibraryRefForObjects), None)
+ if file.schema == "IFC2X3":
+ rel = next(
+ (r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == settings["reference"]),
+ None,
+ )
+ else:
+ rel = next(iter(settings["reference"].LibraryRefForObjects), None)
- if not rel:
- return self.file.create_entity(
- "IfcRelAssociatesLibrary",
- GlobalId=ifcopenshell.guid.new(),
- OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
- RelatedObjects=list(products),
- RelatingLibrary=self.settings["reference"],
- )
+ if not rel:
+ return file.create_entity(
+ "IfcRelAssociatesLibrary",
+ GlobalId=ifcopenshell.guid.new(),
+ OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
+ RelatedObjects=list(products),
+ RelatingLibrary=settings["reference"],
+ )
- related_objects = set(rel.RelatedObjects) | products
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
- return rel
+ related_objects = set(rel.RelatedObjects) | products
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py
index aca508cc38..5a53869a3f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, library=None, attributes=None):
- """Edits the attributes of an IfcLibraryInformation
+def edit_library(file, library=None, attributes=None) -> None:
+ """Edits the attributes of an IfcLibraryInformation
- For more information about the attributes and data types of an
- IfcLibraryInformation, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcLibraryInformation, consult the IFC documentation.
- :param library: The IfcLibraryInformation entity you want to edit
- :type library: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param library: The IfcLibraryInformation entity you want to edit
+ :type library: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- ifcopenshell.api.run("library.edit_library", model, library=library,
- attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."})
- """
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ ifcopenshell.api.run("library.edit_library", model, library=library,
+ attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."})
+ """
- self.file = file
- self.settings = {"library": library, "attributes": attributes or {}}
+ settings = {"library": library, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["library"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["library"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py
index 35a1be4709..1d2487820a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, reference=None, attributes=None):
- """Edits the attributes of an IfcLibraryReference
+def edit_reference(file, reference=None, attributes=None) -> None:
+ """Edits the attributes of an IfcLibraryReference
- For more information about the attributes and data types of an
- IfcLibraryReference, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcLibraryReference, consult the IFC documentation.
- :param reference: The IfcLibraryReference entity you want to edit
- :type reference: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param reference: The IfcLibraryReference entity you want to edit
+ :type reference: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- # Let's create a reference to a single AHU in our Brickschema dataset
- reference = ifcopenshell.api.run("library.add_reference", model, library=library)
- ifcopenshell.api.run("library.edit_reference", model,
- reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
- """
- self.file = file
- self.settings = {"reference": reference, "attributes": attributes or {}}
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ # Let's create a reference to a single AHU in our Brickschema dataset
+ reference = ifcopenshell.api.run("library.add_reference", model, library=library)
+ ifcopenshell.api.run("library.edit_reference", model,
+ reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
+ """
+ settings = {"reference": reference, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["reference"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["reference"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py
index e921016c4d..ba5244537c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py
@@ -20,35 +20,32 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, library=None):
- """Removes a library
+def remove_library(file, library=None) -> None:
+ """Removes a library
- All references along with their relationships will also be removed. Any
- products which have relationships to this library will not be removed.
+ All references along with their relationships will also be removed. Any
+ products which have relationships to this library will not be removed.
- :param library: The IfcLibraryInformation entity you want to remove
- :type library: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param library: The IfcLibraryInformation entity you want to remove
+ :type library: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- ifcopenshell.api.run("library.remove_library", model, library=library)
- """
- self.file = file
- self.settings = {"library": library}
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ ifcopenshell.api.run("library.remove_library", model, library=library)
+ """
+ settings = {"library": library}
- def execute(self):
- for reference in set(self.settings["library"].HasLibraryReferences or []):
- self.file.remove(reference)
- self.file.remove(self.settings["library"])
- for rel in self.file.by_type("IfcRelAssociatesLibrary"):
- if not rel.RelatingLibrary:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for reference in set(settings["library"].HasLibraryReferences or []):
+ file.remove(reference)
+ file.remove(settings["library"])
+ for rel in file.by_type("IfcRelAssociatesLibrary"):
+ if not rel.RelatingLibrary:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py
index d34973f6b2..0b5ad42d1a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py
@@ -20,34 +20,31 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance):
- """Removes a library reference
+def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None:
+ """Removes a library reference
- Any products which have relationships to this reference will not be
- removed.
+ Any products which have relationships to this reference will not be
+ removed.
- :param reference: The IfcLibraryReference entity you want to remove
- :type reference: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param reference: The IfcLibraryReference entity you want to remove
+ :type reference: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- reference = ifcopenshell.api.run("library.add_reference", model, library=library)
- # Let's change our mind and remove it.
- ifcopenshell.api.run("library.remove_reference", model, reference=reference)
- """
- self.file = file
- self.settings = {"reference": reference}
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ reference = ifcopenshell.api.run("library.add_reference", model, library=library)
+ # Let's change our mind and remove it.
+ ifcopenshell.api.run("library.remove_reference", model, reference=reference)
+ """
+ settings = {"reference": reference}
- def execute(self) -> None:
- for rel in self.settings["reference"].LibraryRefForObjects:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- self.file.remove(self.settings["reference"])
+ for rel in settings["reference"].LibraryRefForObjects:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ file.remove(settings["reference"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py
index 420b7fa0d4..c2f2837ad0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py
@@ -21,70 +21,66 @@ import ifcopenshell.util.element
import ifcopenshell.api
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- reference: ifcopenshell.entity_instance,
- products: list[ifcopenshell.entity_instance],
- ):
- """Unassigns a product of products from a reference
+def unassign_reference(
+ file: ifcopenshell.file,
+ reference: ifcopenshell.entity_instance,
+ products: list[ifcopenshell.entity_instance],
+) -> None:
+ """Unassigns a product of products from a reference
- If the product isn't assigned to the reference, nothing will happen.
+ If the product isn't assigned to the reference, nothing will happen.
- :param reference: The IfcLibraryReference to unassign from
- :type reference: ifcopenshell.entity_instance
- :param products: A list of IfcProduct elements to unassign from the reference
- :type products: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
+ :param reference: The IfcLibraryReference to unassign from
+ :type reference: ifcopenshell.entity_instance
+ :param products: A list of IfcProduct elements to unassign from the reference
+ :type products: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
+ library = ifcopenshell.api.run("library.add_library", model, name="Brickschema")
- # Let's create a reference to a single AHU in our Brickschema dataset
- reference = ifcopenshell.api.run("library.add_reference", model, library=library)
- ifcopenshell.api.run("library.edit_reference", model,
- reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
+ # Let's create a reference to a single AHU in our Brickschema dataset
+ reference = ifcopenshell.api.run("library.add_reference", model, library=library)
+ ifcopenshell.api.run("library.edit_reference", model,
+ reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
- # Let's assume we have an AHU in our model.
- ahu = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
+ # Let's assume we have an AHU in our model.
+ ahu = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
- # And now assign the IFC model's AHU with its Brickschema counterpart
- ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
+ # And now assign the IFC model's AHU with its Brickschema counterpart
+ ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
- # Let's change our mind and unassign it.
- ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
- """
+ # Let's change our mind and unassign it.
+ ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu])
+ """
- self.file = file
- self.settings = {"reference": reference, "products": products}
+ settings = {"reference": reference, "products": products}
- def execute(self):
- # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
+ # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
- reference_rels: set[ifcopenshell.entity_instance] = set()
- products = set(self.settings["products"])
- for product in products:
- reference_rels.update(product.HasAssociations)
+ reference_rels: set[ifcopenshell.entity_instance] = set()
+ products = set(settings["products"])
+ for product in products:
+ reference_rels.update(product.HasAssociations)
- reference_rels = {
- rel
- for rel in reference_rels
- if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"]
- }
+ reference_rels = {
+ rel
+ for rel in reference_rels
+ if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == settings["reference"]
+ }
- for rel in reference_rels:
- related_objects = set(rel.RelatedObjects) - products
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for rel in reference_rels:
+ related_objects = set(rel.RelatedObjects) - products
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py
index e0caddbe3c..2831915f76 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py
@@ -15,3 +15,28 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_constituent import add_constituent
+from .add_layer import add_layer
+from .add_list_item import add_list_item
+from .add_material import add_material
+from .add_material_set import add_material_set
+from .add_profile import add_profile
+from .assign_material import assign_material
+from .assign_profile import assign_profile
+from .copy_material import copy_material
+from .edit_assigned_material import edit_assigned_material
+from .edit_constituent import edit_constituent
+from .edit_layer import edit_layer
+from .edit_layer_usage import edit_layer_usage
+from .edit_material import edit_material
+from .edit_profile import edit_profile
+from .edit_profile_usage import edit_profile_usage
+from .remove_constituent import remove_constituent
+from .remove_layer import remove_layer
+from .remove_list_item import remove_list_item
+from .remove_material import remove_material
+from .remove_material_set import remove_material_set
+from .remove_profile import remove_profile
+from .reorder_set_item import reorder_set_item
+from .unassign_material import unassign_material
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py
index 278eb50872..b1f3f76073 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py
@@ -17,75 +17,72 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, constituent_set=None, material=None):
- """Adds a new constituent to a constituent set
+def add_constituent(file, constituent_set=None, material=None) -> None:
+ """Adds a new constituent to a constituent set
- A constituent describes how a portion of an object is made out of a
- material whereas other portions of the object is made out of other
- materials. For example, a window might be made out of an aluminium frame
- and a glass panel. The aluminium used for the frame is one constituent
- of the material, and glass would be another constituent. Another example
- might be concrete, where one constituent might be cement, and another
- constituent might be binder. In the case of the window, the constituent
- is represented explicitly by the geometry of the window frame and the
- geometry of the window panel. In the case of a concrete slab, the
- constituents might be represented in terms of percentages.
+ A constituent describes how a portion of an object is made out of a
+ material whereas other portions of the object is made out of other
+ materials. For example, a window might be made out of an aluminium frame
+ and a glass panel. The aluminium used for the frame is one constituent
+ of the material, and glass would be another constituent. Another example
+ might be concrete, where one constituent might be cement, and another
+ constituent might be binder. In the case of the window, the constituent
+ is represented explicitly by the geometry of the window frame and the
+ geometry of the window panel. In the case of a concrete slab, the
+ constituents might be represented in terms of percentages.
- Constituents are not available in IFC2X3.
+ Constituents are not available in IFC2X3.
- :param constituent_set: The IfcMaterialConstituentSet that the
- constituent is part of. The constituent set represents a group of
- constituents. See ifcopenshell.api.material.add_material_set for
- information on how to add a constituent set.
- :type constituent_set: ifcopenshell.entity_instance
- :param material: The IfcMaterial that the constituent is made out of.
- :type material: ifcopenshell.entity_instance
- :return: The newly created IfcMaterialConstituent
- :rtype: ifcopenshell.entity_instance
+ :param constituent_set: The IfcMaterialConstituentSet that the
+ constituent is part of. The constituent set represents a group of
+ constituents. See ifcopenshell.api.material.add_material_set for
+ information on how to add a constituent set.
+ :type constituent_set: ifcopenshell.entity_instance
+ :param material: The IfcMaterial that the constituent is made out of.
+ :type material: ifcopenshell.entity_instance
+ :return: The newly created IfcMaterialConstituent
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a window type that has an aluminium frame
- # and a glass glazing panel. Notice we are assigning to the type
- # only, as all occurrences of that type will automatically inherit
- # the material.
- window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType")
+ # Let's imagine we have a window type that has an aluminium frame
+ # and a glass glazing panel. Notice we are assigning to the type
+ # only, as all occurrences of that type will automatically inherit
+ # the material.
+ window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType")
- # First, let's create a constituent set. This will later be assigned
- # to our window element.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialConstituentSet")
+ # First, let's create a constituent set. This will later be assigned
+ # to our window element.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialConstituentSet")
- # Let's create a few materials, it's important to also give them
- # categories. This makes it easy for model recipients to do things
- # like "show me everything made out of aluminium / concrete / steel
- # / glass / etc". The IFC specification states a list of categories
- # you can use.
- aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
+ # Let's create a few materials, it's important to also give them
+ # categories. This makes it easy for model recipients to do things
+ # like "show me everything made out of aluminium / concrete / steel
+ # / glass / etc". The IFC specification states a list of categories
+ # you can use.
+ aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
- # Now let's use those materials as two constituents in our set.
- ifcopenshell.api.run("material.add_constituent", model,
- constituent_set=material_set, material=aluminium)
- ifcopenshell.api.run("material.add_constituent", model,
- constituent_set=material_set, material=glass)
+ # Now let's use those materials as two constituents in our set.
+ ifcopenshell.api.run("material.add_constituent", model,
+ constituent_set=material_set, material=aluminium)
+ ifcopenshell.api.run("material.add_constituent", model,
+ constituent_set=material_set, material=glass)
- # Great! Let's assign our material set to our window type.
- # We're technically not done here, we might want to add geometry to
- # our window too, but to keep this example simple, geometry is
- # optional and it is enough to say that this window is made out of
- # aluminium and glass.
- ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set)
- """
- self.file = file
- self.settings = {"constituent_set": constituent_set, "material": material}
+ # Great! Let's assign our material set to our window type.
+ # We're technically not done here, we might want to add geometry to
+ # our window too, but to keep this example simple, geometry is
+ # optional and it is enough to say that this window is made out of
+ # aluminium and glass.
+ ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set)
+ """
+ settings = {"constituent_set": constituent_set, "material": material}
- def execute(self):
- constituents = list(self.settings["constituent_set"].MaterialConstituents or [])
- constituent = self.file.create_entity("IfcMaterialConstituent", **{"Material": self.settings["material"]})
- constituents.append(constituent)
- self.settings["constituent_set"].MaterialConstituents = constituents
- return constituent
+ constituents = list(settings["constituent_set"].MaterialConstituents or [])
+ constituent = file.create_entity("IfcMaterialConstituent", **{"Material": settings["material"]})
+ constituents.append(constituent)
+ settings["constituent_set"].MaterialConstituents = constituents
+ return constituent
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py
index aa572f07bd..68885715ee 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py
@@ -17,75 +17,70 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, layer_set=None, material=None):
- """Adds a new layer to a layer set
+def add_layer(file, layer_set=None, material=None) -> None:
+ """Adds a new layer to a layer set
- A layer represents a portion of material within a layered build up,
- defined by a thickness. Typical layered construction includes walls and
- slabs, where a wall might include a layer of finish, a layer of
- structure, a layer of insulation, and so on. It is recommended to define
- layered construction this way where it is unnecessary to define the
- exact geometry of how the wall or slab will be built, and it will
- instead be determined on site by a trade.
+ A layer represents a portion of material within a layered build up,
+ defined by a thickness. Typical layered construction includes walls and
+ slabs, where a wall might include a layer of finish, a layer of
+ structure, a layer of insulation, and so on. It is recommended to define
+ layered construction this way where it is unnecessary to define the
+ exact geometry of how the wall or slab will be built, and it will
+ instead be determined on site by a trade.
- Layers are defined in a particular order and thickness, so that it is
- clear which layer comes next.
+ Layers are defined in a particular order and thickness, so that it is
+ clear which layer comes next.
- :param layer_set: The IfcMaterialLayerSet that the layer is part of. The
- layer set represents a group of layers. See
- ifcopenshell.api.material.add_material_set for more information on
- how to add a layer set.
- :type layer_set: ifcopenshell.entity_instance
- :param material: The IfcMaterial that the layer is made out of.
- :type material: ifcopenshell.entity_instance
- :return: The newly created IfcMaterialLayer
- :rtype: ifcopenshell.entity_instance
+ :param layer_set: The IfcMaterialLayerSet that the layer is part of. The
+ layer set represents a group of layers. See
+ ifcopenshell.api.material.add_material_set for more information on
+ how to add a layer set.
+ :type layer_set: ifcopenshell.entity_instance
+ :param material: The IfcMaterial that the layer is made out of.
+ :type material: ifcopenshell.entity_instance
+ :return: The newly created IfcMaterialLayer
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a wall type that has two layers of
- # gypsum with steel studs inside. Notice we are assigning to
- # the type only, as all occurrences of that type will automatically
- # inherit the material.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
+ # Let's imagine we have a wall type that has two layers of
+ # gypsum with steel studs inside. Notice we are assigning to
+ # the type only, as all occurrences of that type will automatically
+ # inherit the material.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
- # First, let's create a material set. This will later be assigned
- # to our wall type element.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
+ # First, let's create a material set. This will later be assigned
+ # to our wall type element.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
- # Let's create a few materials, it's important to also give them
- # categories. This makes it easy for model recipients to do things
- # like "show me everything made out of aluminium / concrete / steel
- # / glass / etc". The IFC specification states a list of categories
- # you can use.
- gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Let's create a few materials, it's important to also give them
+ # categories. This makes it easy for model recipients to do things
+ # like "show me everything made out of aluminium / concrete / steel
+ # / glass / etc". The IFC specification states a list of categories
+ # you can use.
+ gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Now let's use those materials as three layers in our set, such
- # that the steel studs are sandwiched by the gypsum. Let's imagine
- # we're setting the layer thickness in millimeters.
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
+ # Now let's use those materials as three layers in our set, such
+ # that the steel studs are sandwiched by the gypsum. Let's imagine
+ # we're setting the layer thickness in millimeters.
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
- # Great! Let's assign our material set to our wall type.
- ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set)
- """
- self.file = file
- self.settings = {"layer_set": layer_set, "material": material}
+ # Great! Let's assign our material set to our wall type.
+ ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set)
+ """
+ settings = {"layer_set": layer_set, "material": material}
- def execute(self):
- layers = list(self.settings["layer_set"].MaterialLayers or [])
- layer = self.file.create_entity(
- "IfcMaterialLayer", **{"Material": self.settings["material"], "LayerThickness": 1.0}
- )
- layers.append(layer)
- self.settings["layer_set"].MaterialLayers = layers
- return layer
+ layers = list(settings["layer_set"].MaterialLayers or [])
+ layer = file.create_entity("IfcMaterialLayer", **{"Material": settings["material"], "LayerThickness": 1.0})
+ layers.append(layer)
+ settings["layer_set"].MaterialLayers = layers
+ return layer
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py
index 9a12ed044b..7eaa8159ce 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py
@@ -19,70 +19,67 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, material_list=None, material=None):
- """Adds a new material in a list of materials
+def add_list_item(file, material_list=None, material=None) -> None:
+ """Adds a new material in a list of materials
- In IFC2X3, if you wanted an object to have multiple materials (i.e. a
- composite material) you would assign the object to a material list,
- which would contain a list of materials. For example, a window might
- have a list of 2 materials, one being aluminium for the frame, and
- another being glass for the panel.
+ In IFC2X3, if you wanted an object to have multiple materials (i.e. a
+ composite material) you would assign the object to a material list,
+ which would contain a list of materials. For example, a window might
+ have a list of 2 materials, one being aluminium for the frame, and
+ another being glass for the panel.
- In IFC4 and above, this is deprecated and should not be used. Instead,
- you should use constituent sets instead, which achieve the same thing
- but are more powerful as they allow you to define the properties of the
- constituents too.
+ In IFC4 and above, this is deprecated and should not be used. Instead,
+ you should use constituent sets instead, which achieve the same thing
+ but are more powerful as they allow you to define the properties of the
+ constituents too.
- However if you're stuck on IFC2X3, you have my condolences as well as
- this function.
+ However if you're stuck on IFC2X3, you have my condolences as well as
+ this function.
- :param material_list: The IfcMaterialList the material should be added
- to.
- :type material_list: ifcopenshell.entity_instance
- :param material: The IfcMaterial to add to the list
- :type material: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param material_list: The IfcMaterialList the material should be added
+ to.
+ :type material_list: ifcopenshell.entity_instance
+ :param material: The IfcMaterial to add to the list
+ :type material: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a window type that has an aluminium frame
- # and a glass glazing panel. Notice we are assigning to the type
- # only, as all occurrences of that type will automatically inherit
- # the material.
- window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType")
+ # Let's imagine we have a window type that has an aluminium frame
+ # and a glass glazing panel. Notice we are assigning to the type
+ # only, as all occurrences of that type will automatically inherit
+ # the material.
+ window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType")
- # First, let's create a list. This will later be assigned to our
- # window element.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialList")
+ # First, let's create a list. This will later be assigned to our
+ # window element.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialList")
- # Let's create a few materials, it's important to also give them
- # categories. This makes it easy for model recipients to do things
- # like "show me everything made out of aluminium / concrete / steel
- # / glass / etc". The IFC specification states a list of categories
- # you can use.
- aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
+ # Let's create a few materials, it's important to also give them
+ # categories. This makes it easy for model recipients to do things
+ # like "show me everything made out of aluminium / concrete / steel
+ # / glass / etc". The IFC specification states a list of categories
+ # you can use.
+ aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
- # Now let's use those materials as two items in our list.
- ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium)
- ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass)
+ # Now let's use those materials as two items in our list.
+ ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium)
+ ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass)
- # Great! Let's assign our material set to our window type.
- # We're technically not done here, we might want to add geometry to
- # our window too, but to keep this example simple, geometry is
- # optional and it is enough to say that this window is made out of
- # aluminium and glass.
- ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set)
- """
- self.file = file
- self.settings = {"material_list": material_list, "material": material}
+ # Great! Let's assign our material set to our window type.
+ # We're technically not done here, we might want to add geometry to
+ # our window too, but to keep this example simple, geometry is
+ # optional and it is enough to say that this window is made out of
+ # aluminium and glass.
+ ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set)
+ """
+ settings = {"material_list": material_list, "material": material}
- def execute(self):
- materials = list(self.settings["material_list"].Materials or [])
- materials.append(self.settings["material"])
- self.settings["material_list"].Materials = materials
+ materials = list(settings["material_list"].Materials or [])
+ materials.append(settings["material"])
+ settings["material_list"].Materials = materials
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py
index bac5a3ac0a..534d9e911c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py
@@ -17,64 +17,61 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, name=None, category=None):
- """Adds a new material
+def add_material(file, name=None, category=None) -> None:
+ """Adds a new material
- A material in IFC represents a physical material, such as timber, steel,
- concrete, aluminium, etc. It may also contain physical properties used
- for structural or lighting simulation. Note that unlike the computer
- graphics industry, a material by itself does not define any colour or
- lighting information. Colours in IFC are known as "styles", and an IFC
- material may or may not have any style information associated with it.
- See ifcopenshell.api.style for more information.
+ A material in IFC represents a physical material, such as timber, steel,
+ concrete, aluminium, etc. It may also contain physical properties used
+ for structural or lighting simulation. Note that unlike the computer
+ graphics industry, a material by itself does not define any colour or
+ lighting information. Colours in IFC are known as "styles", and an IFC
+ material may or may not have any style information associated with it.
+ See ifcopenshell.api.style for more information.
- A material is typically given a code name which is used by architects in
- elevations and details when tagging finishes. Materials are also useful
- to structural engineers in specifying the exact types of concrete and
- steel to be used in structural simulations.
+ A material is typically given a code name which is used by architects in
+ elevations and details when tagging finishes. Materials are also useful
+ to structural engineers in specifying the exact types of concrete and
+ steel to be used in structural simulations.
- In addition, materials can belong to a category. Specifying this
- category is critical to allow model recipients to make simple queries
- like "show me all concrete / steel" elements in the model. Without
- standardised category naming of all materials, this type of query
- becomes a bespoke and inefficient task. A list of categories are:
- 'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood',
- 'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to
- specify their own category instead if none of these categories are
- appropriate.
+ In addition, materials can belong to a category. Specifying this
+ category is critical to allow model recipients to make simple queries
+ like "show me all concrete / steel" elements in the model. Without
+ standardised category naming of all materials, this type of query
+ becomes a bespoke and inefficient task. A list of categories are:
+ 'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood',
+ 'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to
+ specify their own category instead if none of these categories are
+ appropriate.
- Note that categories are not available in IFC2X3. This shortcoming is
- one of the big reasons projects should upgrade to IFC4.
+ Note that categories are not available in IFC2X3. This shortcoming is
+ one of the big reasons projects should upgrade to IFC4.
- :param name: The name of the material, typically tagged in a finishes
- drawing or schedule.
- :type name: str
- :param category: The category of the material.
- :type category: str, optional
- :return: The newly created IfcMaterial
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the material, typically tagged in a finishes
+ drawing or schedule.
+ :type name: str
+ :param category: The category of the material.
+ :type category: str, optional
+ :return: The newly created IfcMaterial
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create two materials with their respective categories
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Let's create two materials with their respective categories
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Let's imagine an urban concrete bench which is purely made out of concrete
- concrete_bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType")
+ # Let's imagine an urban concrete bench which is purely made out of concrete
+ concrete_bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType")
- # Assign the concrete material to that bench. Note that no colour
- # "Style" has been specified.
- ifcopenshell.api.run("material.assign_material", model, products=[concrete_bench], material=concrete)
- """
- self.file = file
- self.settings = {"name": name or "Unnamed", "category": category}
+ # Assign the concrete material to that bench. Note that no colour
+ # "Style" has been specified.
+ ifcopenshell.api.run("material.assign_material", model, products=[concrete_bench], material=concrete)
+ """
+ settings = {"name": name or "Unnamed", "category": category}
- def execute(self):
- material = self.file.create_entity("IfcMaterial", **{"Name": self.settings["name"] or "Unnamed"})
- if self.settings["category"]:
- material.Category = self.settings["category"]
- return material
+ material = file.create_entity("IfcMaterial", **{"Name": settings["name"] or "Unnamed"})
+ if settings["category"]:
+ material.Category = settings["category"]
+ return material
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py
index 277aa258f2..99cfafad41 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py
@@ -17,97 +17,94 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, name="Unnamed", set_type="IfcMaterialConstituentSet"):
- """Adds a new material set
+def add_material_set(file, name="Unnamed", set_type="IfcMaterialConstituentSet") -> None:
+ """Adds a new material set
- IFC allows you to state that objects are made out of multiple materials.
- These are known generically as material sets, but may also be called
- layered materials, composite materials, or other names in software.
+ IFC allows you to state that objects are made out of multiple materials.
+ These are known generically as material sets, but may also be called
+ layered materials, composite materials, or other names in software.
- There are three types of material sets:
+ There are three types of material sets:
- - A layer set, used for layered construction such as walls, where the
- element is parametrically made out of extruded layers, each layer
- having a thickness defined. Even though this is known as a layer
- "set" it is still recommended to use it for all standared layered
- construction as it describes the intent of the element to be layered
- construction and thus can be used for parametric editing.
- - A profile set, used for profiled construction such as beams or
- columns, where the element is parametrically made out of one or more
- extruded profiles, where each profile may be parametric from a
- standard section (e.g. standardised steel profile) or an arbitrary
- shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note
- that even though this is called a profile "set", it should still be
- used even if there is only a single profile. This is not available in
- IFC2X3.
- - A constituent set, used for arbitrary composite construction where
- the object is made out of multiple materials. The constituents may be
- explicitly defined via a shape, such as a window where the frame
- geometry is made from one material and the panel geometry is made
- from another material. Alternatively, the constituents may be
- represented in terms of percentages, such as in mixtures like
- concrete where there might be a percentage constituent of cement and
- another percentage constituent of binder. This is not available in
- IFC2X3.
+ - A layer set, used for layered construction such as walls, where the
+ element is parametrically made out of extruded layers, each layer
+ having a thickness defined. Even though this is known as a layer
+ "set" it is still recommended to use it for all standared layered
+ construction as it describes the intent of the element to be layered
+ construction and thus can be used for parametric editing.
+ - A profile set, used for profiled construction such as beams or
+ columns, where the element is parametrically made out of one or more
+ extruded profiles, where each profile may be parametric from a
+ standard section (e.g. standardised steel profile) or an arbitrary
+ shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note
+ that even though this is called a profile "set", it should still be
+ used even if there is only a single profile. This is not available in
+ IFC2X3.
+ - A constituent set, used for arbitrary composite construction where
+ the object is made out of multiple materials. The constituents may be
+ explicitly defined via a shape, such as a window where the frame
+ geometry is made from one material and the panel geometry is made
+ from another material. Alternatively, the constituents may be
+ represented in terms of percentages, such as in mixtures like
+ concrete where there might be a percentage constituent of cement and
+ another percentage constituent of binder. This is not available in
+ IFC2X3.
- There is also a fourth material set known as a material list, which is a
- legacy type of set used by IFC2X3. It should not be used on IFC4 and
- above, and constituent sets should be used instead.
+ There is also a fourth material set known as a material list, which is a
+ legacy type of set used by IFC2X3. It should not be used on IFC4 and
+ above, and constituent sets should be used instead.
- :param name: The name of the material set, which may be purely
- descriptive or annotated in drawings. Defaults to "Unnamed".
- :type name: str, optional
- :param set_type: What type of set you want to create, chosen from
- IfcMaterialLayerSet, IfcMaterialProfileSet,
- IfcMaterialConstituentSet, or IfcMaterialList. Defaults to
- IfcMaterialConstituentSet.
- :type set_type: str, optional
- :return: The newly created material set element
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the material set, which may be purely
+ descriptive or annotated in drawings. Defaults to "Unnamed".
+ :type name: str, optional
+ :param set_type: What type of set you want to create, chosen from
+ IfcMaterialLayerSet, IfcMaterialProfileSet,
+ IfcMaterialConstituentSet, or IfcMaterialList. Defaults to
+ IfcMaterialConstituentSet.
+ :type set_type: str, optional
+ :return: The newly created material set element
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a wall type that has two layers of
- # gypsum with steel studs inside. Notice we are assigning to
- # the type only, as all occurrences of that type will automatically
- # inherit the material.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
+ # Let's imagine we have a wall type that has two layers of
+ # gypsum with steel studs inside. Notice we are assigning to
+ # the type only, as all occurrences of that type will automatically
+ # inherit the material.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
- # First, let's create a material set. This will later be assigned
- # to our wall type element.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
+ # First, let's create a material set. This will later be assigned
+ # to our wall type element.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
- # Let's create a few materials, it's important to also give them
- # categories. This makes it easy for model recipients to do things
- # like "show me everything made out of aluminium / concrete / steel
- # / glass / etc". The IFC specification states a list of categories
- # you can use.
- gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Let's create a few materials, it's important to also give them
+ # categories. This makes it easy for model recipients to do things
+ # like "show me everything made out of aluminium / concrete / steel
+ # / glass / etc". The IFC specification states a list of categories
+ # you can use.
+ gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Now let's use those materials as three layers in our set, such
- # that the steel studs are sandwiched by the gypsum. Let's imagine
- # we're setting the layer thickness in millimeters.
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
+ # Now let's use those materials as three layers in our set, such
+ # that the steel studs are sandwiched by the gypsum. Let's imagine
+ # we're setting the layer thickness in millimeters.
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
- # Great! Let's assign our material set to our wall type.
- ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set)
- """
- self.file = file
- self.settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"}
+ # Great! Let's assign our material set to our wall type.
+ ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set)
+ """
+ settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"}
- def execute(self):
- if self.settings["set_type"] == "IfcMaterialLayerSet":
- return self.file.create_entity("IfcMaterialLayerSet", LayerSetName=self.settings["name"] or "Unnamed")
- elif self.settings["set_type"] == "IfcMaterialList":
- return self.file.create_entity("IfcMaterialList")
- return self.file.create_entity(self.settings["set_type"], Name=self.settings["name"] or "Unnamed")
+ if settings["set_type"] == "IfcMaterialLayerSet":
+ return file.create_entity("IfcMaterialLayerSet", LayerSetName=settings["name"] or "Unnamed")
+ elif settings["set_type"] == "IfcMaterialList":
+ return file.create_entity("IfcMaterialList")
+ return file.create_entity(settings["set_type"], Name=settings["name"] or "Unnamed")
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py
index ff2cf3bed5..3a23dd44a9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py
@@ -19,86 +19,82 @@ import ifcopenshell
from typing import Optional
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- profile_set: ifcopenshell.entity_instance,
- material: Optional[ifcopenshell.entity_instance] = None,
- profile: Optional[ifcopenshell.entity_instance] = None,
- ):
- """Add a new profile item to a profile set
+def add_profile(
+ file: ifcopenshell.file,
+ profile_set: ifcopenshell.entity_instance,
+ material: Optional[ifcopenshell.entity_instance] = None,
+ profile: Optional[ifcopenshell.entity_instance] = None,
+) -> ifcopenshell.entity_instance:
+ """Add a new profile item to a profile set
- A profile item in a profile set represents an extruded 2D profile curve
- that is extruded along the axis of the element. Most commonly there will
- only be a single profile item in a profile set. For example, a beam will
- have a material profile set containing a single profile item, which may
- have a steel material and a I-beam shaped profile curve.
+ A profile item in a profile set represents an extruded 2D profile curve
+ that is extruded along the axis of the element. Most commonly there will
+ only be a single profile item in a profile set. For example, a beam will
+ have a material profile set containing a single profile item, which may
+ have a steel material and a I-beam shaped profile curve.
- Note that the "profile item" represents a single extrusion in the
- profile set, whereas the "profile curve" represents a 2D curve used by a
- "profile item".
+ Note that the "profile item" represents a single extrusion in the
+ profile set, whereas the "profile curve" represents a 2D curve used by a
+ "profile item".
- In some cases, a profiled element (i.e. beam, column) may be a composite
- beam or column and include multiple extrusions. This is rare. The order
- of the profiles does not matter.
+ In some cases, a profiled element (i.e. beam, column) may be a composite
+ beam or column and include multiple extrusions. This is rare. The order
+ of the profiles does not matter.
- :param profile_set: The IfcMaterialProfileSet that the profile is part of. The
- profile set represents a group of profile items. See
- ifcopenshell.api.material.add_material_set for more information on
- how to add a profile set.
- :type profile_set: ifcopenshell.entity_instance
- :param material: The IfcMaterial that the profile item is made out of.
- :type material: ifcopenshell.entity_instance, optional
- :param profile: The IfcProfileDef that represents the 2D cross section
- of the the profile item.
- :type profile: ifcopenshell.entity_instance, optional
- :return: The newly created IfcMaterialProfile
- :rtype: ifcopenshell.entity_instance
+ :param profile_set: The IfcMaterialProfileSet that the profile is part of. The
+ profile set represents a group of profile items. See
+ ifcopenshell.api.material.add_material_set for more information on
+ how to add a profile set.
+ :type profile_set: ifcopenshell.entity_instance
+ :param material: The IfcMaterial that the profile item is made out of.
+ :type material: ifcopenshell.entity_instance, optional
+ :param profile: The IfcProfileDef that represents the 2D cross section
+ of the the profile item.
+ :type profile: ifcopenshell.entity_instance, optional
+ :return: The newly created IfcMaterialProfile
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a steel I-beam. Notice we are assigning to
- # the type only, as all occurrences of that type will automatically
- # inherit the material.
- beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1")
+ # Let's imagine we have a steel I-beam. Notice we are assigning to
+ # the type only, as all occurrences of that type will automatically
+ # inherit the material.
+ beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1")
- # First, let's create a material set. This will later be assigned
- # to our beam type element.
- material_set = ifcopenshell.api.run("material.add_profile_set", model,
- name="B1", set_type="IfcMaterialProfileSet")
+ # First, let's create a material set. This will later be assigned
+ # to our beam type element.
+ material_set = ifcopenshell.api.run("material.add_profile_set", model,
+ name="B1", set_type="IfcMaterialProfileSet")
- # Create a steel material.
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Create a steel material.
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Create an I-beam profile curve. Notice how we name our profiles
- # based on standardised steel profile names.
- hea100 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
- OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
- )
+ # Create an I-beam profile curve. Notice how we name our profiles
+ # based on standardised steel profile names.
+ hea100 = file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
+ OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
+ )
- # Define that steel material and cross section as a single profile
- # item. If this were a composite beam, we might add multiple profile
- # items instead, but this is rarely the case in most construction.
- ifcopenshell.api.run("material.add_profile", model,
- profile_set=material_set, material=steel, profile=hea100)
+ # Define that steel material and cross section as a single profile
+ # item. If this were a composite beam, we might add multiple profile
+ # items instead, but this is rarely the case in most construction.
+ ifcopenshell.api.run("material.add_profile", model,
+ profile_set=material_set, material=steel, profile=hea100)
- # Great! Let's assign our material set to our beam type.
- ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set)
- """
- self.file = file
- self.settings = {"profile_set": profile_set, "material": material, "profile": profile}
+ # Great! Let's assign our material set to our beam type.
+ ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set)
+ """
+ settings = {"profile_set": profile_set, "material": material, "profile": profile}
- def execute(self) -> ifcopenshell.entity_instance:
- profiles = list(self.settings["profile_set"].MaterialProfiles or [])
- profile = self.file.create_entity("IfcMaterialProfile")
- if self.settings["material"]:
- profile.Material = self.settings["material"]
- if self.settings["profile"]:
- profile.Profile = self.settings["profile"]
- profiles.append(profile)
- self.settings["profile_set"].MaterialProfiles = profiles
- return profile
+ profiles = list(settings["profile_set"].MaterialProfiles or [])
+ profile = file.create_entity("IfcMaterialProfile")
+ if settings["material"]:
+ profile.Material = settings["material"]
+ if settings["profile"]:
+ profile.Profile = settings["profile"]
+ profiles.append(profile)
+ settings["profile_set"].MaterialProfiles = profiles
+ return profile
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py
index a65a644866..9098ca72fc 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py
@@ -23,133 +23,135 @@ import ifcopenshell.util.representation
from typing import Optional, Union
+def assign_material(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ type: str = "IfcMaterial",
+ material: Optional[ifcopenshell.entity_instance] = None,
+) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]:
+ """Assigns a material to the list of products
+
+ Will unassign previously assigned material.
+
+ When a material is assigned to a product, it means that the product is
+ made out of that material. In its simplest form, a single material may
+ be assigned to a product, meaning that the entire product is made out of
+ that one material. Alternatively, a material set may be assigned to a
+ product, meaning that the product is made out of a set of materials.
+ There are three types of sets, including layered construction, profiled
+ materials, and arbitrary material constituents. See
+ ifcopenshell.api.material.add_material_set for details.
+
+ Materials are typically assigned to the element types rather than
+ individual occurrences of elements. Individual occurrences would then
+ inherit the material from the type.
+
+ If the type has a material set, then the geometry of the occurrences
+ must comply with the material set. For example, if the type has a
+ constituent set, then it is expected that all occurrences also inherit
+ the geometry of the type, which is made out of those constituents.
+ Alternatively, if the type has a layer set, then all occurrences must
+ have geometry that has a thickness equal to the sum of all layers. If a
+ type has a profile set, then all occurrences must has the same profile
+ extruded along its axis.
+
+ For layers and profiles assigned to types, the occurrences must be
+ assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage.
+ This allows individual occurrences to override the layered or profiled
+ construction offset from a reference line.
+
+ :param products: The list of IfcProducts to assign the material or material set
+ to.
+ :type products: list[ifcopenshell.entity_instance]
+ :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet",
+ "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage",
+ "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or
+ "IfcMaterialList". Note that "Set Usages" may only be assigned to
+ occurrences, not types. Defaults to "IfcMaterial".
+ :type type: str
+ :param material: The IfcMaterial or material set you are assigning here.
+ If type is Usage then no need to provide `material`, it will be deduced
+ from the element type automatically.
+ :type material: ifcopenshell.entity_instance, optional
+ :return: IfcRelAssociatesMaterial entity
+ or a list of IfcRelAssociatesMaterial entities
+ (possible if `type` is Usage
+ and `products` require different Usages)
+ or `None` if `products` was empty list.
+ :rtype: Union[
+ ifcopenshell.entity_instance,
+ list[ifcopenshell.entity_instance], None]
+
+ Example:
+
+ .. code:: python
+
+ # Let's start with a simple concrete material
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+
+ # Let's imagine a concrete bench made out of a single concrete
+ # material. Let's assign it to the type.
+ bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType")
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[bench_type], type="IfcMaterial", material=concrete)
+
+ # Let's imagine there are a two occurrences of this bench. It's not
+ # necessary to assign any material to these benches as they
+ # automatically inherit the material from the type.
+ bench1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ bench2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[bench1], relating_type=bench_type)
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[bench2], relating_type=bench_type)
+
+ # If we have a concrete wall, we should use a layer set. Again,
+ # let's start with a wall type, not occurrences.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
+
+ # Even though there is only one layer in our layer set, we still use
+ # a layer set because it makes it clear that this is a layered
+ # construction. Let's say it's a 200mm thick concrete layer.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="CON200", set_type="IfcMaterialLayerSet")
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200})
+
+ # Our wall type now has the layer set assigned to it
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[wall_type], type="IfcMaterialLayerSet", material=material_set)
+
+ # Let's imagine an occurrence of this wall type.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type)
+
+ # Our wall occurrence needs to have a "set usage" which describes
+ # how the layers relate to a reference line (typically a 2D line
+ # representing the extents of the wall). Usages are special since
+ # they automatically detect the inherited material set from the
+ # type. You'd write similar code for a profile set.
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[wall], type="IfcMaterialLayerSetUsage")
+
+ # To be complete, let's create the wall's axis and body
+ # representation. Notice how the axis guides the walls "reference
+ # line" which determines where layers are extruded from, and the
+ # body has a thickness of 200mm, same as our total layer set
+ # thickness.
+ axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
+ context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)])
+ body = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body_context, length=5000, height=3000, thickness=200)
+ ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=axis)
+ ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=body)
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"products": products, "type": type, "material": material}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- type: str = "IfcMaterial",
- material: Optional[ifcopenshell.entity_instance] = None,
- ):
- """Assigns a material to the list of products
-
- Will unassign previously assigned material.
-
- When a material is assigned to a product, it means that the product is
- made out of that material. In its simplest form, a single material may
- be assigned to a product, meaning that the entire product is made out of
- that one material. Alternatively, a material set may be assigned to a
- product, meaning that the product is made out of a set of materials.
- There are three types of sets, including layered construction, profiled
- materials, and arbitrary material constituents. See
- ifcopenshell.api.material.add_material_set for details.
-
- Materials are typically assigned to the element types rather than
- individual occurrences of elements. Individual occurrences would then
- inherit the material from the type.
-
- If the type has a material set, then the geometry of the occurrences
- must comply with the material set. For example, if the type has a
- constituent set, then it is expected that all occurrences also inherit
- the geometry of the type, which is made out of those constituents.
- Alternatively, if the type has a layer set, then all occurrences must
- have geometry that has a thickness equal to the sum of all layers. If a
- type has a profile set, then all occurrences must has the same profile
- extruded along its axis.
-
- For layers and profiles assigned to types, the occurrences must be
- assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage.
- This allows individual occurrences to override the layered or profiled
- construction offset from a reference line.
-
- :param products: The list of IfcProducts to assign the material or material set
- to.
- :type products: list[ifcopenshell.entity_instance]
- :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet",
- "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage",
- "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or
- "IfcMaterialList". Note that "Set Usages" may only be assigned to
- occurrences, not types. Defaults to "IfcMaterial".
- :type type: str
- :param material: The IfcMaterial or material set you are assigning here.
- If type is Usage then no need to provide `material`, it will be deduced
- from the element type automatically.
- :type material: ifcopenshell.entity_instance, optional
- :return: IfcRelAssociatesMaterial entity
- or a list of IfcRelAssociatesMaterial entities
- (possible if `type` is Usage
- and `products` require different Usages)
- or `None` if `products` was empty list.
- :rtype: Union[
- ifcopenshell.entity_instance,
- list[ifcopenshell.entity_instance], None]
-
- Example:
-
- .. code:: python
-
- # Let's start with a simple concrete material
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
-
- # Let's imagine a concrete bench made out of a single concrete
- # material. Let's assign it to the type.
- bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType")
- ifcopenshell.api.run("material.assign_material", model,
- products=[bench_type], type="IfcMaterial", material=concrete)
-
- # Let's imagine there are a two occurrences of this bench. It's not
- # necessary to assign any material to these benches as they
- # automatically inherit the material from the type.
- bench1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- bench2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- ifcopenshell.api.run("type.assign_type", model, related_objects=[bench1], relating_type=bench_type)
- ifcopenshell.api.run("type.assign_type", model, related_objects=[bench2], relating_type=bench_type)
-
- # If we have a concrete wall, we should use a layer set. Again,
- # let's start with a wall type, not occurrences.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
-
- # Even though there is only one layer in our layer set, we still use
- # a layer set because it makes it clear that this is a layered
- # construction. Let's say it's a 200mm thick concrete layer.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="CON200", set_type="IfcMaterialLayerSet")
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200})
-
- # Our wall type now has the layer set assigned to it
- ifcopenshell.api.run("material.assign_material", model,
- products=[wall_type], type="IfcMaterialLayerSet", material=material_set)
-
- # Let's imagine an occurrence of this wall type.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type)
-
- # Our wall occurrence needs to have a "set usage" which describes
- # how the layers relate to a reference line (typically a 2D line
- # representing the extents of the wall). Usages are special since
- # they automatically detect the inherited material set from the
- # type. You'd write similar code for a profile set.
- ifcopenshell.api.run("material.assign_material", model,
- products=[wall], type="IfcMaterialLayerSetUsage")
-
- # To be complete, let's create the wall's axis and body
- # representation. Notice how the axis guides the walls "reference
- # line" which determines where layers are extruded from, and the
- # body has a thickness of 200mm, same as our total layer set
- # thickness.
- axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
- context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)])
- body = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body_context, length=5000, height=3000, thickness=200)
- ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=axis)
- ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=body)
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- """
- self.file = file
- self.settings = {"products": products, "type": type, "material": material}
-
- def execute(self) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]:
+ def execute(self):
self.products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
if not self.products:
return
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py
index 4d1678a0ae..15c7e4d779 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py
@@ -19,78 +19,81 @@
import ifcopenshell.util.representation
+def assign_profile(file, material_profile=None, profile=None) -> None:
+ """Changes the profile curve of a material profile item in a profile set
+
+ In addition to changing the profile curve, it will also change the
+ profile curve used in any body representation extrusions.
+
+ :param material_profile: The IfcMaterialProfile to change the profile
+ curve of. See ifcopenshell.api.material.add_profile to see how to
+ create profiles.
+ :type material_profile: ifcopenshell.entity_instance
+ :param profile: The IfcProfileDef to set the profile item's curve to.
+ :type profile: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we have a steel I-beam. Notice we are assigning to
+ # the type only, as all occurrences of that type will automatically
+ # inherit the material.
+ beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1")
+
+ # First, let's create a material set. This will later be assigned
+ # to our beam type element.
+ material_set = ifcopenshell.api.run("material.add_profile_set", model,
+ name="B1", set_type="IfcMaterialProfileSet")
+
+ # Create a steel material.
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+
+ # Create an I-beam profile curve. Notice how we name our profiles
+ # based on standardised steel profile names.
+ hea100 = usecase.file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
+ OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
+ )
+
+ # Define that steel material and cross section as a single profile
+ # item. If this were a composite beam, we might add multiple profile
+ # items instead, but this is rarely the case in most construction.
+ profile_item = ifcopenshell.api.run("material.add_profile", model,
+ profile_set=material_set, material=steel, profile=hea100)
+
+ # Great! Let's assign our material set to our beam type.
+ ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set)
+
+ # Let's create an occurrence of this beam.
+ beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01")
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[beam], type="IfcMaterialProfileSetUsage")
+
+ # Let's give a 1000mm long beam body representation.
+ body = ifcopenshell.api.run("geometry.add_profile_representation",
+ context=body_context, profile=hea100, depth=1000)
+ ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body)
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam)
+
+ # Now let's change the profile to a HEA200 standard profile instead.
+ # This will automatically change the body representation that we
+ # just added as well to a HEA200 profile.
+ hea200 = usecase.file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA",
+ OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18,
+ )
+ ifcopenshell.api.run("material.assign_profile", model, material_profile=profile_item, profile=hea200)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"material_profile": material_profile, "profile": profile}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, material_profile=None, profile=None):
- """Changes the profile curve of a material profile item in a profile set
-
- In addition to changing the profile curve, it will also change the
- profile curve used in any body representation extrusions.
-
- :param material_profile: The IfcMaterialProfile to change the profile
- curve of. See ifcopenshell.api.material.add_profile to see how to
- create profiles.
- :type material_profile: ifcopenshell.entity_instance
- :param profile: The IfcProfileDef to set the profile item's curve to.
- :type profile: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Let's imagine we have a steel I-beam. Notice we are assigning to
- # the type only, as all occurrences of that type will automatically
- # inherit the material.
- beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1")
-
- # First, let's create a material set. This will later be assigned
- # to our beam type element.
- material_set = ifcopenshell.api.run("material.add_profile_set", model,
- name="B1", set_type="IfcMaterialProfileSet")
-
- # Create a steel material.
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
-
- # Create an I-beam profile curve. Notice how we name our profiles
- # based on standardised steel profile names.
- hea100 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
- OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
- )
-
- # Define that steel material and cross section as a single profile
- # item. If this were a composite beam, we might add multiple profile
- # items instead, but this is rarely the case in most construction.
- profile_item = ifcopenshell.api.run("material.add_profile", model,
- profile_set=material_set, material=steel, profile=hea100)
-
- # Great! Let's assign our material set to our beam type.
- ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set)
-
- # Let's create an occurrence of this beam.
- beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01")
- ifcopenshell.api.run("material.assign_material", model,
- products=[beam], type="IfcMaterialProfileSetUsage")
-
- # Let's give a 1000mm long beam body representation.
- body = ifcopenshell.api.run("geometry.add_profile_representation",
- context=body_context, profile=hea100, depth=1000)
- ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body)
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam)
-
- # Now let's change the profile to a HEA200 standard profile instead.
- # This will automatically change the body representation that we
- # just added as well to a HEA200 profile.
- hea200 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA",
- OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18,
- )
- ifcopenshell.api.run("material.assign_profile", model, material_profile=profile_item, profile=hea200)
- """
- self.file = file
- self.settings = {"material_profile": material_profile, "profile": profile}
-
def execute(self):
# TODO: handle composite profiles
old_profile = self.settings["material_profile"].Profile
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py
index 8dac43b8e0..c862e9cb4b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py
@@ -20,45 +20,42 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, material=None):
- """Copies a material
+def copy_material(file, material=None) -> None:
+ """Copies a material
- All material psets and styles are copied. The copied material is not
- associated to any elements.
+ All material psets and styles are copied. The copied material is not
+ associated to any elements.
- :param material: The IfcMaterial to copy
- :type material: ifcopenshell.entity_instance
- :return: The new copy of the material
- :rtype: ifcopenshell.entity_instance
+ :param material: The IfcMaterial to copy
+ :type material: ifcopenshell.entity_instance
+ :return: The new copy of the material
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
- # Let's duplicate the concrete material
- concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete)
- """
- self.file = file
- self.settings = {"material": material}
+ # Let's duplicate the concrete material
+ concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete)
+ """
+ settings = {"material": material}
- def execute(self):
- if self.settings["material"].is_a("IfcMaterial"):
- new = ifcopenshell.util.element.copy(self.file, self.settings["material"])
- for inverse in self.file.get_inverse(self.settings["material"]):
- if inverse.is_a("IfcMaterialProperties"):
- # Properties must not be shared between objects for convenience of authoring
- inverse = ifcopenshell.util.element.copy(self.file, inverse)
- properties = []
- for pset in inverse.Properties:
- properties.append(ifcopenshell.util.element.copy_deep(self.file, pset))
- inverse.Properties = properties
- inverse.Material = new
- elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
- inverse = ifcopenshell.util.element.copy_deep(
- self.file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"]
- )
- inverse.RepresentedMaterial = new
- return new
+ if settings["material"].is_a("IfcMaterial"):
+ new = ifcopenshell.util.element.copy(file, settings["material"])
+ for inverse in file.get_inverse(settings["material"]):
+ if inverse.is_a("IfcMaterialProperties"):
+ # Properties must not be shared between objects for convenience of authoring
+ inverse = ifcopenshell.util.element.copy(file, inverse)
+ properties = []
+ for pset in inverse.Properties:
+ properties.append(ifcopenshell.util.element.copy_deep(file, pset))
+ inverse.Properties = properties
+ inverse.Material = new
+ elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
+ inverse = ifcopenshell.util.element.copy_deep(
+ file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"]
+ )
+ inverse.RepresentedMaterial = new
+ return new
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py
index 3e3a03dbd8..3102d5a645 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, element=None, attributes=None):
- """Edits the attributes of an IfcMaterial
+def edit_assigned_material(file, element=None, attributes=None) -> None:
+ """Edits the attributes of an IfcMaterial
- For more information about the attributes and data types of an
- IfcMaterial, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMaterial, consult the IFC documentation.
- :param element: The IfcMaterial entity you want to edit
- :type element: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param element: The IfcMaterial entity you want to edit
+ :type element: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
- ifcopenshell.api.run("material.edit_assigned_material", model,
- element=concrete, attributes={"Description": "40MPA concrete with broom finish"})
- """
- self.file = file
- self.settings = {"element": element, "attributes": attributes or {}}
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+ ifcopenshell.api.run("material.edit_assigned_material", model,
+ element=concrete, attributes={"Description": "40MPA concrete with broom finish"})
+ """
+ settings = {"element": element, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["element"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["element"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py
index ef036527a3..998bef98bd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py
@@ -17,52 +17,49 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, constituent=None, attributes=None, material=None):
- """Edits the attributes of an IfcMaterialConstituent
+def edit_constituent(file, constituent=None, attributes=None, material=None) -> None:
+ """Edits the attributes of an IfcMaterialConstituent
- For more information about the attributes and data types of an
- IfcMaterialConstituent, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMaterialConstituent, consult the IFC documentation.
- :param constituent: The IfcMaterialConstituent entity you want to edit
- :type constituent: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :param material: The IfcMaterial entity you want to change the constituent to
- :type material: ifcopenshell.entity_instance, optional
- :return: None
- :rtype: None
+ :param constituent: The IfcMaterialConstituent entity you want to edit
+ :type constituent: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :param material: The IfcMaterial entity you want to change the constituent to
+ :type material: ifcopenshell.entity_instance, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's add two materials
- aluminium1 = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- aluminium2 = ifcopenshell.api.run("material.add_material", model, name="AL02", category="aluminium")
- glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
+ # Let's add two materials
+ aluminium1 = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ aluminium2 = ifcopenshell.api.run("material.add_material", model, name="AL02", category="aluminium")
+ glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialConstituentSet")
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialConstituentSet")
- # Set up two constituents, one for the frame and the other for the glazing.
- framing = ifcopenshell.api.run("material.add_constituent", model,
- constituent_set=material_set, material=aluminium1)
- glazing = ifcopenshell.api.run("material.add_constituent", model,
- constituent_set=material_set, material=glass)
+ # Set up two constituents, one for the frame and the other for the glazing.
+ framing = ifcopenshell.api.run("material.add_constituent", model,
+ constituent_set=material_set, material=aluminium1)
+ glazing = ifcopenshell.api.run("material.add_constituent", model,
+ constituent_set=material_set, material=glass)
- # Let's make sure this constituent refers to the framing of the
- # window and uses the second aluminium material instead.
- ifcopenshell.api.run("material.edit_constituent", model,
- constituent=framing, attributes={"Name": "Framing"}, material=aluminium2)
+ # Let's make sure this constituent refers to the framing of the
+ # window and uses the second aluminium material instead.
+ ifcopenshell.api.run("material.edit_constituent", model,
+ constituent=framing, attributes={"Name": "Framing"}, material=aluminium2)
- ifcopenshell.api.run("material.edit_constituent", model,
- constituent=constituent, attributes={"Name": "Glazing"})
- """
- self.file = file
- self.settings = {"constituent": constituent, "attributes": attributes or {}, "material": material}
+ ifcopenshell.api.run("material.edit_constituent", model,
+ constituent=constituent, attributes={"Name": "Glazing"})
+ """
+ settings = {"constituent": constituent, "attributes": attributes or {}, "material": material}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["constituent"], name, value)
- self.settings["constituent"].Material = self.settings["material"]
+ for name, value in settings["attributes"].items():
+ setattr(settings["constituent"], name, value)
+ settings["constituent"].Material = settings["material"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py
index 3e31194452..78ce15132f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py
@@ -17,51 +17,48 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, layer=None, attributes=None, material=None):
- """Edits the attributes of an IfcMaterialLayer
+def edit_layer(file, layer=None, attributes=None, material=None) -> None:
+ """Edits the attributes of an IfcMaterialLayer
- For more information about the attributes and data types of an
- IfcMaterialLayer, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMaterialLayer, consult the IFC documentation.
- :param layer: The IfcMaterialLayer entity you want to edit
- :type layer: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :param material: The IfcMaterial entity you want the layer to be made
- from.
- :type material: ifcopenshell.entity_instance, optional
- :return: None
- :rtype: None
+ :param layer: The IfcMaterialLayer entity you want to edit
+ :type layer: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :param material: The IfcMaterial entity you want the layer to be made
+ from.
+ :type material: ifcopenshell.entity_instance, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create two materials typically used for steel stud partition
- # walls with gypsum lining.
- gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Let's create two materials typically used for steel stud partition
+ # walls with gypsum lining.
+ gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Create a material layer set to contain our layers.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
+ # Create a material layer set to contain our layers.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
- # Now let's use those materials as three layers in our set, such
- # that the steel studs are sandwiched by the gypsum. Let's imagine
- # we're setting the layer thickness in millimeters.
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
- """
- self.file = file
- self.settings = {"layer": layer, "attributes": attributes or {}, "material": material}
+ # Now let's use those materials as three layers in our set, such
+ # that the steel studs are sandwiched by the gypsum. Let's imagine
+ # we're setting the layer thickness in millimeters.
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13})
+ """
+ settings = {"layer": layer, "attributes": attributes or {}, "material": material}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["layer"], name, value)
- if self.settings["material"]:
- self.settings["layer"].Material = self.settings["material"]
+ for name, value in settings["attributes"].items():
+ setattr(settings["layer"], name, value)
+ if settings["material"]:
+ settings["layer"].Material = settings["material"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py
index a30fa39f21..9204728004 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py
@@ -17,66 +17,63 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, usage=None, attributes=None):
- """Edits the attributes of an IfcMaterialLayerSetUsage
+def edit_layer_usage(file, usage=None, attributes=None) -> None:
+ """Edits the attributes of an IfcMaterialLayerSetUsage
- This is typically used to change the offset from the reference line to
- the layers.
+ This is typically used to change the offset from the reference line to
+ the layers.
- For more information about the attributes and data types of an
- IfcMaterialLayerSetUsage, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMaterialLayerSetUsage, consult the IFC documentation.
- :param usage: The IfcMaterialLayerSetUsage entity you want to edit
- :type usage: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param usage: The IfcMaterialLayerSetUsage entity you want to edit
+ :type usage: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's start with a simple concrete material
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+ # Let's start with a simple concrete material
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
- # If we have a concrete wall, we should use a layer set. Again,
- # let's start with a wall type, not occurrences.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
+ # If we have a concrete wall, we should use a layer set. Again,
+ # let's start with a wall type, not occurrences.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
- # Even though there is only one layer in our layer set, we still use
- # a layer set because it makes it clear that this is a layered
- # construction. Let's say it's a 200mm thick concrete layer.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="CON200", set_type="IfcMaterialLayerSet")
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200})
+ # Even though there is only one layer in our layer set, we still use
+ # a layer set because it makes it clear that this is a layered
+ # construction. Let's say it's a 200mm thick concrete layer.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="CON200", set_type="IfcMaterialLayerSet")
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200})
- # Our wall type now has the layer set assigned to it
- ifcopenshell.api.run("material.assign_material", model,
- products=[wall_type], type="IfcMaterialLayerSet", material=material_set)
+ # Our wall type now has the layer set assigned to it
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[wall_type], type="IfcMaterialLayerSet", material=material_set)
- # Let's imagine an occurrence of this wall type.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type)
+ # Let's imagine an occurrence of this wall type.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type)
- # Our wall occurrence needs to have a "set usage" which describes
- # how the layers relate to a reference line (typically a 2D line
- # representing the extents of the wall). Usages are special since
- # they automatically detect the inherited material set from the
- # type. You'd write similar code for a profile set.
- rel = ifcopenshell.api.run("material.assign_material", model,
- products=[wall], type="IfcMaterialLayerSetUsage")
+ # Our wall occurrence needs to have a "set usage" which describes
+ # how the layers relate to a reference line (typically a 2D line
+ # representing the extents of the wall). Usages are special since
+ # they automatically detect the inherited material set from the
+ # type. You'd write similar code for a profile set.
+ rel = ifcopenshell.api.run("material.assign_material", model,
+ products=[wall], type="IfcMaterialLayerSetUsage")
- # Let's change the offset from the reference line to be 200mm
- # instead of the default of 0mm.
- ifcopenshell.api.run("material.edit_layer_usage", model,
- usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200})
- """
- self.file = file
- self.settings = {"usage": usage, "attributes": attributes or {}}
+ # Let's change the offset from the reference line to be 200mm
+ # instead of the default of 0mm.
+ ifcopenshell.api.run("material.edit_layer_usage", model,
+ usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200})
+ """
+ settings = {"usage": usage, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["usage"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["usage"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py
index c712af15ac..87b3f2c7fb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py
@@ -17,13 +17,10 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, material=None, attributes=None):
- """Edits the attributes of an IfcMaterial"""
-
- self.file = file
- self.settings = {"material": material, "attributes": attributes or {}}
+def edit_material(file, material=None, attributes=None) -> None:
+ """Edits the attributes of an IfcMaterial"""
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["material"], name, value)
+ settings = {"material": material, "attributes": attributes or {}}
+
+ for name, value in settings["attributes"].items():
+ setattr(settings["material"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py
index 6fc781f9a6..aa1310dbca 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py
@@ -17,72 +17,69 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, profile=None, attributes=None, profile_def=None, material=None):
- """Edits the attributes of an IfcMaterialProfile
+def edit_profile(file, profile=None, attributes=None, profile_def=None, material=None) -> None:
+ """Edits the attributes of an IfcMaterialProfile
- For more information about the attributes and data types of an
- IfcMaterialProfile, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMaterialProfile, consult the IFC documentation.
- :param profile: The IfcMaterialProfile entity you want to edit
- :type profile: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :param profile_def: The IfcProfileDef entity the profile curve should be
- extruded from.
- :type profile_def: ifcopenshell.entity_instance, optional
- :param material: The IfcMaterial entity you want to change the profile
- to be made from.
- :type material: ifcopenshell.entity_instance, optional
- :return: None
- :rtype: None
+ :param profile: The IfcMaterialProfile entity you want to edit
+ :type profile: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :param profile_def: The IfcProfileDef entity the profile curve should be
+ extruded from.
+ :type profile_def: ifcopenshell.entity_instance, optional
+ :param material: The IfcMaterial entity you want to change the profile
+ to be made from.
+ :type material: ifcopenshell.entity_instance, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a material set to store our profiles.
- material_set = ifcopenshell.api.run("material.add_profile_set", model,
- name="B1", set_type="IfcMaterialProfileSet")
+ # Let's create a material set to store our profiles.
+ material_set = ifcopenshell.api.run("material.add_profile_set", model,
+ name="B1", set_type="IfcMaterialProfileSet")
- # Create a couple steel materials.
- steel1 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- steel2 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Create a couple steel materials.
+ steel1 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ steel2 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Create some I-shaped profiles. Notice how we name our profiles based
- # on standardised steel profile names.
- hea100 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
- OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
- )
- hea200 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA",
- OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18,
- )
+ # Create some I-shaped profiles. Notice how we name our profiles based
+ # on standardised steel profile names.
+ hea100 = file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
+ OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
+ )
+ hea200 = file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA",
+ OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18,
+ )
- # Define that steel material and cross section as a single profile
- # item. If this were a composite beam, we might add multiple profile
- # items instead, but this is rarely the case in most construction.
- profile_item = ifcopenshell.api.run("material.add_profile", model,
- profile_set=material_set, material=steel1, profile=hea100)
+ # Define that steel material and cross section as a single profile
+ # item. If this were a composite beam, we might add multiple profile
+ # items instead, but this is rarely the case in most construction.
+ profile_item = ifcopenshell.api.run("material.add_profile", model,
+ profile_set=material_set, material=steel1, profile=hea100)
- # Edit our profile item to use a HEA200 profile instead made out of
- # another type of steel.
- ifcopenshell.api.run("material.edit_profile", model,
- profile=profile_item, profile_def=hea200, material=steel2)
- """
- self.file = file
- self.settings = {
- "profile": profile,
- "attributes": attributes or {},
- "profile_def": profile_def,
- "material": material,
- }
+ # Edit our profile item to use a HEA200 profile instead made out of
+ # another type of steel.
+ ifcopenshell.api.run("material.edit_profile", model,
+ profile=profile_item, profile_def=hea200, material=steel2)
+ """
+ settings = {
+ "profile": profile,
+ "attributes": attributes or {},
+ "profile_def": profile_def,
+ "material": material,
+ }
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["profile"], name, value)
- if self.settings["material"]:
- self.settings["profile"].Material = self.settings["material"]
- if self.settings["profile_def"]:
- self.settings["profile"].Profile = self.settings["profile_def"]
+ for name, value in settings["attributes"].items():
+ setattr(settings["profile"], name, value)
+ if settings["material"]:
+ settings["profile"].Material = settings["material"]
+ if settings["profile_def"]:
+ settings["profile"].Profile = settings["profile_def"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py
index afd9007c7b..8ad5192570 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py
@@ -20,79 +20,82 @@ import ifcopenshell.geom
import ifcopenshell.util.representation
+def edit_profile_usage(file, usage=None, attributes=None) -> None:
+ """Edits the attributes of an IfcMaterialProfileSetUsage
+
+ This is typically used to change the cardinal point of the profile.
+ The cardinal point represents whether the profile is extruded along the
+ center of the axis line, at a corner, at a shear center, at the bottom,
+ top, etc.
+
+ For more information about the attributes and data types of an
+ IfcMaterialProfileSetUsage, consult the IFC documentation.
+
+ :param usage: The IfcMaterialProfileSetUsage entity you want to edit
+ :type usage: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we have a steel I-beam. Notice we are assigning to
+ # the type only, as all occurrences of that type will automatically
+ # inherit the material.
+ beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1")
+
+ # First, let's create a material set. This will later be assigned
+ # to our beam type element.
+ material_set = ifcopenshell.api.run("material.add_profile_set", model,
+ name="B1", set_type="IfcMaterialProfileSet")
+
+ # Create a steel material.
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+
+ # Create an I-beam profile curve. Notice how we name our profiles
+ # based on standardised steel profile names.
+ hea100 = usecase.file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
+ OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
+ )
+
+ # Define that steel material and cross section as a single profile
+ # item. If this were a composite beam, we might add multiple profile
+ # items instead, but this is rarely the case in most construction.
+ profile_item = ifcopenshell.api.run("material.add_profile", model,
+ profile_set=material_set, material=steel, profile=hea100)
+
+ # Great! Let's assign our material set to our beam type.
+ ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set)
+
+ # Let's create an occurrence of this beam.
+ beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01")
+ rel = ifcopenshell.api.run("material.assign_material", model,
+ products=[beam], type="IfcMaterialProfileSetUsage")
+
+ # Let's give a 1000mm long beam body representation.
+ body = ifcopenshell.api.run("geometry.add_profile_representation",
+ context=body_context, profile=hea100, depth=1000)
+ ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body)
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam)
+
+ # Let's change the cardinal point to be the top center of the axis
+ # line. This is represented by the number "8". Consult the IFC
+ # documentation for all the numbers you can use.
+ ifcopenshell.api.run("material.edit_profile_usage", model,
+ usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8})
+ """
+ usecase = Usecase()
+
+ usecase.file = file
+ usecase.settings = {"usage": usage, "attributes": attributes or {}}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, usage=None, attributes=None):
- """Edits the attributes of an IfcMaterialProfileSetUsage
-
- This is typically used to change the cardinal point of the profile.
- The cardinal point represents whether the profile is extruded along the
- center of the axis line, at a corner, at a shear center, at the bottom,
- top, etc.
-
- For more information about the attributes and data types of an
- IfcMaterialProfileSetUsage, consult the IFC documentation.
-
- :param usage: The IfcMaterialProfileSetUsage entity you want to edit
- :type usage: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Let's imagine we have a steel I-beam. Notice we are assigning to
- # the type only, as all occurrences of that type will automatically
- # inherit the material.
- beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1")
-
- # First, let's create a material set. This will later be assigned
- # to our beam type element.
- material_set = ifcopenshell.api.run("material.add_profile_set", model,
- name="B1", set_type="IfcMaterialProfileSet")
-
- # Create a steel material.
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
-
- # Create an I-beam profile curve. Notice how we name our profiles
- # based on standardised steel profile names.
- hea100 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
- OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
- )
-
- # Define that steel material and cross section as a single profile
- # item. If this were a composite beam, we might add multiple profile
- # items instead, but this is rarely the case in most construction.
- profile_item = ifcopenshell.api.run("material.add_profile", model,
- profile_set=material_set, material=steel, profile=hea100)
-
- # Great! Let's assign our material set to our beam type.
- ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set)
-
- # Let's create an occurrence of this beam.
- beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01")
- rel = ifcopenshell.api.run("material.assign_material", model,
- products=[beam], type="IfcMaterialProfileSetUsage")
-
- # Let's give a 1000mm long beam body representation.
- body = ifcopenshell.api.run("geometry.add_profile_representation",
- context=body_context, profile=hea100, depth=1000)
- ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body)
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam)
-
- # Let's change the cardinal point to be the top center of the axis
- # line. This is represented by the number "8". Consult the IFC
- # documentation for all the numbers you can use.
- ifcopenshell.api.run("material.edit_profile_usage", model,
- usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8})
- """
-
- self.file = file
- self.settings = {"usage": usage, "attributes": attributes or {}}
-
def execute(self):
self.cardinal_point = self.settings["attributes"].get("CardinalPoint")
if self.cardinal_point and self.cardinal_point != self.settings["usage"].CardinalPoint:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py
index 2fb919d10d..02256e0695 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py
@@ -17,42 +17,39 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, constituent=None):
- """Removes a constituent from a constituent set
+def remove_constituent(file, constituent=None) -> None:
+ """Removes a constituent from a constituent set
- Note that it is invalid to have zero items in a set, so you should leave
- at least one constituent to ensure a valid IFC dataset.
+ Note that it is invalid to have zero items in a set, so you should leave
+ at least one constituent to ensure a valid IFC dataset.
- :param constituent: The IfcMaterialConstituent entity you want to remove
- :type constituent: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param constituent: The IfcMaterialConstituent entity you want to remove
+ :type constituent: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a material set for windows made out of aluminium and glass.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialConstituentSet")
+ # Create a material set for windows made out of aluminium and glass.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialConstituentSet")
- aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
+ aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
- # Now let's use those materials as two constituents in our set.
- framing = ifcopenshell.api.run("material.add_constituent", model,
- constituent_set=material_set, material=aluminium)
- glazing = ifcopenshell.api.run("material.add_constituent", model,
- constituent_set=material_set, material=glass)
+ # Now let's use those materials as two constituents in our set.
+ framing = ifcopenshell.api.run("material.add_constituent", model,
+ constituent_set=material_set, material=aluminium)
+ glazing = ifcopenshell.api.run("material.add_constituent", model,
+ constituent_set=material_set, material=glass)
- # Let's remove the glass constituent. Note that we should not remove
- # the framing, at this would mean there are no constituents which is
- # invalid.
- ifcopenshell.api.run("material.remove_constituent", model, constituent=glazing)
- """
- self.file = file
- self.settings = {"constituent": constituent}
+ # Let's remove the glass constituent. Note that we should not remove
+ # the framing, at this would mean there are no constituents which is
+ # invalid.
+ ifcopenshell.api.run("material.remove_constituent", model, constituent=glazing)
+ """
+ settings = {"constituent": constituent}
- def execute(self):
- self.file.remove(self.settings["constituent"])
+ file.remove(settings["constituent"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py
index f068641533..fda5cf2151 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py
@@ -17,45 +17,42 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, layer=None):
- """Removes a layer from a layer set
+def remove_layer(file, layer=None) -> None:
+ """Removes a layer from a layer set
- Note that it is invalid to have zero items in a set, so you should leave
- at least one layer to ensure a valid IFC dataset.
+ Note that it is invalid to have zero items in a set, so you should leave
+ at least one layer to ensure a valid IFC dataset.
- :param layer: The IfcMaterialLayer entity you want to remove
- :type layer: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param layer: The IfcMaterialLayer entity you want to remove
+ :type layer: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a material set for steel stud partition walls.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialConstituentSet")
+ # Create a material set for steel stud partition walls.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialConstituentSet")
- gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Now let's use those materials as three layers in our set, such
- # that the steel studs are sandwiched by the gypsum. Let's imagine
- # we're setting the layer thickness in millimeters.
- layer1 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer1, attributes={"LayerThickness": 13})
- layer2 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer2, attributes={"LayerThickness": 92})
- layer3 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer3, attributes={"LayerThickness": 13})
+ # Now let's use those materials as three layers in our set, such
+ # that the steel studs are sandwiched by the gypsum. Let's imagine
+ # we're setting the layer thickness in millimeters.
+ layer1 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer1, attributes={"LayerThickness": 13})
+ layer2 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer2, attributes={"LayerThickness": 92})
+ layer3 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer3, attributes={"LayerThickness": 13})
- # Let's remove the last layer, such that the wall might be clad only
- # one one side such as to line a services riser.
- ifcopenshell.api.run("material.remove_layer", model, layer=layer3)
- """
- self.file = file
- self.settings = {"layer": layer}
+ # Let's remove the last layer, such that the wall might be clad only
+ # one one side such as to line a services riser.
+ ifcopenshell.api.run("material.remove_layer", model, layer=layer3)
+ """
+ settings = {"layer": layer}
- def execute(self):
- self.file.remove(self.settings["layer"])
+ file.remove(settings["layer"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py
index 276d9e64d2..a41b6ec7a8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py
@@ -19,44 +19,41 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, material_list=None, material_index=0):
- """Removes an item in an material list
+def remove_list_item(file, material_list=None, material_index=0) -> None:
+ """Removes an item in an material list
- Note that it is invalid to have zero items in a list, so you should leave
- at least one item to ensure a valid IFC dataset.
+ Note that it is invalid to have zero items in a list, so you should leave
+ at least one item to ensure a valid IFC dataset.
- :param material_list: The IfcMaterialList entity you want to remove an
- item from.
- :type material_list: ifcopenshell.entity_instance
- :param material_index: The index of the material you want to remove from
- the list. Starts counting at 0. Defaults to 0.
- :type material_index: int, optional
- :return: None
- :rtype: None
+ :param material_list: The IfcMaterialList entity you want to remove an
+ item from.
+ :type material_list: ifcopenshell.entity_instance
+ :param material_index: The index of the material you want to remove from
+ the list. Starts counting at 0. Defaults to 0.
+ :type material_index: int, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a material list for aluminium windows.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialMaterialList")
+ # Create a material list for aluminium windows.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialMaterialList")
- aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
+ aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
- # Now let's use those materials as two items in our list.
- ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium)
- ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass)
+ # Now let's use those materials as two items in our list.
+ ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium)
+ ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass)
- # Let's remove the glass
- ifcopenshell.api.run("material.remove_list_item", model, material_list=material_set, material_index=1)
- """
- self.file = file
- self.settings = {"material_list": material_list, "material_index": material_index}
+ # Let's remove the glass
+ ifcopenshell.api.run("material.remove_list_item", model, material_list=material_set, material_index=1)
+ """
+ settings = {"material_list": material_list, "material_index": material_index}
- def execute(self):
- materials = list(self.settings["material_list"].Materials)
- materials.pop(self.settings["material_index"])
- self.settings["material_list"].Materials = materials
+ materials = list(settings["material_list"].Materials)
+ materials.pop(settings["material_index"])
+ settings["material_list"].Materials = materials
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py
index ffdf9693d8..a59fee6caf 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py
@@ -20,57 +20,54 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, material=None):
- """Removes a material
+def remove_material(file, material=None) -> None:
+ """Removes a material
- If the material is used in a material set, the corresponding layer,
- profile, or constituent is also removed. Note that this may result in a
- material set with zero items in it, which is invalid, so the user must
- take care of this situation themselves.
+ If the material is used in a material set, the corresponding layer,
+ profile, or constituent is also removed. Note that this may result in a
+ material set with zero items in it, which is invalid, so the user must
+ take care of this situation themselves.
- :param material: The IfcMaterial entity you want to remove
- :type material: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param material: The IfcMaterial entity you want to remove
+ :type material: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a material
- aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ # Create a material
+ aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- # ... and remove it
- ifcopenshell.api.run("material.remove_material", model, material=aluminium)
- """
- self.file = file
- self.settings = {"material": material}
+ # ... and remove it
+ ifcopenshell.api.run("material.remove_material", model, material=aluminium)
+ """
+ settings = {"material": material}
- def execute(self):
- inverse_elements = self.file.get_inverse(self.settings["material"])
- self.file.remove(self.settings["material"])
- # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set
- # This can lead to invalid material sets, but we assume the user will deal with it
- for inverse in inverse_elements:
- if inverse.is_a("IfcMaterialConstituent"):
- self.file.remove(inverse)
- elif inverse.is_a("IfcMaterialLayer"):
- self.file.remove(inverse)
- elif inverse.is_a("IfcMaterialProfile"):
- self.file.remove(inverse)
- elif inverse.is_a("IfcRelAssociatesMaterial"):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcMaterialProperties"):
- for prop in inverse.Properties or []:
- self.file.remove(prop)
- self.file.remove(inverse)
- elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
- for representation in inverse.Representations:
- for item in representation.Items:
- self.file.remove(item)
- self.file.remove(representation)
- self.file.remove(inverse)
+ inverse_elements = file.get_inverse(settings["material"])
+ file.remove(settings["material"])
+ # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set
+ # This can lead to invalid material sets, but we assume the user will deal with it
+ for inverse in inverse_elements:
+ if inverse.is_a("IfcMaterialConstituent"):
+ file.remove(inverse)
+ elif inverse.is_a("IfcMaterialLayer"):
+ file.remove(inverse)
+ elif inverse.is_a("IfcMaterialProfile"):
+ file.remove(inverse)
+ elif inverse.is_a("IfcRelAssociatesMaterial"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcMaterialProperties"):
+ for prop in inverse.Properties or []:
+ file.remove(prop)
+ file.remove(inverse)
+ elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
+ for representation in inverse.Representations:
+ for item in representation.Items:
+ file.remove(item)
+ file.remove(representation)
+ file.remove(inverse)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py
index 79093789aa..5ede76c1c2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py
@@ -20,65 +20,62 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, material=None):
- """Removes a material set
+def remove_material_set(file, material=None) -> None:
+ """Removes a material set
- All set items, such as layers, profiles, or constituents will also be
- removed. However, the materials and profile curves used by the layers,
- profiles and constituents will not be removed.
+ All set items, such as layers, profiles, or constituents will also be
+ removed. However, the materials and profile curves used by the layers,
+ profiles and constituents will not be removed.
- :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
- IfcMaterialProfileSet entity you want to remove.
- :type material: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
+ IfcMaterialProfileSet entity you want to remove.
+ :type material: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a material set
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
+ # Create a material set
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
- # Create some materials
- gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Create some materials
+ gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Add some layers
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ # Add some layers
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- # Completely delete the set and all layers. The gypsum and steel
- # material still exist, though.
- ifcopenshell.api.run("material.remove_material_set", model, material=material_set)
- """
+ # Completely delete the set and all layers. The gypsum and steel
+ # material still exist, though.
+ ifcopenshell.api.run("material.remove_material_set", model, material=material_set)
+ """
- self.file = file
- self.settings = {"material": material}
+ settings = {"material": material}
- def execute(self):
- inverse_elements = self.file.get_inverse(self.settings["material"])
- if self.settings["material"].is_a("IfcMaterialLayerSet"):
- set_items = self.settings["material"].MaterialLayers or []
- elif self.settings["material"].is_a("IfcMaterialProfileSet"):
- set_items = self.settings["material"].MaterialProfiles or []
- elif self.settings["material"].is_a("IfcMaterialConstituentSet"):
- set_items = self.settings["material"].MaterialConstituents or []
- elif self.settings["material"].is_a("IfcMaterialList"):
- set_items = []
- for set_item in set_items:
- self.file.remove(set_item)
- self.file.remove(self.settings["material"])
- for inverse in inverse_elements:
- if inverse.is_a("IfcRelAssociatesMaterial"):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcMaterialProperties"):
- for prop in inverse.Properties or []:
- self.file.remove(prop)
- self.file.remove(inverse)
+ inverse_elements = file.get_inverse(settings["material"])
+ if settings["material"].is_a("IfcMaterialLayerSet"):
+ set_items = settings["material"].MaterialLayers or []
+ elif settings["material"].is_a("IfcMaterialProfileSet"):
+ set_items = settings["material"].MaterialProfiles or []
+ elif settings["material"].is_a("IfcMaterialConstituentSet"):
+ set_items = settings["material"].MaterialConstituents or []
+ elif settings["material"].is_a("IfcMaterialList"):
+ set_items = []
+ for set_item in set_items:
+ file.remove(set_item)
+ file.remove(settings["material"])
+ for inverse in inverse_elements:
+ if inverse.is_a("IfcRelAssociatesMaterial"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcMaterialProperties"):
+ for prop in inverse.Properties or []:
+ file.remove(prop)
+ file.remove(inverse)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py
index 9c930866ed..858b6f1289 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py
@@ -21,58 +21,55 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, profile=None):
- """Removes a profile item from a profile set
+def remove_profile(file, profile=None) -> None:
+ """Removes a profile item from a profile set
- Note that it is invalid to have zero items in a set, so you should leave
- at least one profile to ensure a valid IFC dataset.
+ Note that it is invalid to have zero items in a set, so you should leave
+ at least one profile to ensure a valid IFC dataset.
- :param profile: The IfcMaterialProfile entity you want to remove
- :type profile: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param profile: The IfcMaterialProfile entity you want to remove
+ :type profile: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # First, let's create a material set.
- material_set = ifcopenshell.api.run("material.add_profile_set", model,
- name="B1", set_type="IfcMaterialProfileSet")
+ # First, let's create a material set.
+ material_set = ifcopenshell.api.run("material.add_profile_set", model,
+ name="B1", set_type="IfcMaterialProfileSet")
- # Create a steel material.
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+ # Create a steel material.
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
- # Create an I-beam profile curve. Notice how we name our profiles
- # based on standardised steel profile names.
- hea100 = self.file.create_entity(
- "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
- OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
- )
+ # Create an I-beam profile curve. Notice how we name our profiles
+ # based on standardised steel profile names.
+ hea100 = file.create_entity(
+ "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA",
+ OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12,
+ )
- # Define that steel material and cross section as a single profile item.
- ifcopenshell.api.run("material.add_profile", model,
- profile_set=material_set, material=steel, profile=hea100)
+ # Define that steel material and cross section as a single profile item.
+ ifcopenshell.api.run("material.add_profile", model,
+ profile_set=material_set, material=steel, profile=hea100)
- # Imagine a welded square along the length of the profile.
- welded_square = ifcopenshell.api.run("profile.add_arbitrary_profile", model,
- profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)])
- weld_profile = ifcopenshell.api.run("material.add_profile", model,
- profile_set=material_set, material=steel, profile=welded_square)
+ # Imagine a welded square along the length of the profile.
+ welded_square = ifcopenshell.api.run("profile.add_arbitrary_profile", model,
+ profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)])
+ weld_profile = ifcopenshell.api.run("material.add_profile", model,
+ profile_set=material_set, material=steel, profile=welded_square)
- # Let's remove our welded square.
- ifcopenshell.api.run("material.remove_profile", model, profile=weld_profile)
- """
+ # Let's remove our welded square.
+ ifcopenshell.api.run("material.remove_profile", model, profile=weld_profile)
+ """
- self.file = file
- self.settings = {"profile": profile}
+ settings = {"profile": profile}
- def execute(self):
- subelements = set()
- for attribute in self.settings["profile"]:
- if isinstance(attribute, ifcopenshell.entity_instance):
- subelements.add(attribute)
- self.file.remove(self.settings["profile"])
- for subelement in subelements:
- ifcopenshell.util.element.remove_deep2(self.file, subelement)
+ subelements = set()
+ for attribute in settings["profile"]:
+ if isinstance(attribute, ifcopenshell.entity_instance):
+ subelements.add(attribute)
+ file.remove(settings["profile"])
+ for subelement in subelements:
+ ifcopenshell.util.element.remove_deep2(file, subelement)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py
index 442fec8b5e..481050ff63 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py
@@ -17,56 +17,53 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, material_set=None, old_index=0, new_index=0):
- """Reorders an item in a material set
+def reorder_set_item(file, material_set=None, old_index=0, new_index=0) -> None:
+ """Reorders an item in a material set
- In some material sets, the order have meaning, like in a layer set. In
- other cases, it is purely for human convenience.
+ In some material sets, the order have meaning, like in a layer set. In
+ other cases, it is purely for human convenience.
- :param material_set: The IfcMaterialSet which you want to reorder an
- item in.
- :type material_set: ifcopenshell.entity_instance
- :param old_index: The index of the item you want to move. This starts
- counting from 0.
- :type old_index: int
- :param new_index: The index of the new position the item will move to.
- This starts counting from 0.
- :type new_index: int
- :return: None
- :rtype: None
+ :param material_set: The IfcMaterialSet which you want to reorder an
+ item in.
+ :type material_set: ifcopenshell.entity_instance
+ :param old_index: The index of the item you want to move. This starts
+ counting from 0.
+ :type old_index: int
+ :param new_index: The index of the new position the item will move to.
+ This starts counting from 0.
+ :type new_index: int
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="Window", set_type="IfcMaterialList")
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="Window", set_type="IfcMaterialList")
- aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
- glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
+ aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium")
+ glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass")
- # Now let's use those materials as two items in our list.
- ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium)
- ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass)
+ # Now let's use those materials as two items in our list.
+ ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium)
+ ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass)
- # Switch the order around, this has no meaning for a list, so this
- # is just for fun.
- ifcopenshell.api.run("material.reorder_set_item", model,
- material_set=material_set, old_index=0, new_index=1)
- """
- self.file = file
- self.settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index}
+ # Switch the order around, this has no meaning for a list, so this
+ # is just for fun.
+ ifcopenshell.api.run("material.reorder_set_item", model,
+ material_set=material_set, old_index=0, new_index=1)
+ """
+ settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index}
- def execute(self):
- if self.settings["material_set"].is_a("IfcMaterialConstituentSet"):
- set_name = "MaterialConstituents"
- elif self.settings["material_set"].is_a("IfcMaterialLayerSet"):
- set_name = "MaterialLayers"
- elif self.settings["material_set"].is_a("IfcMaterialProfileSet"):
- set_name = "MaterialProfiles"
- elif self.settings["material_set"].is_a("IfcMaterialList"):
- set_name = "Materials"
- items = list(getattr(self.settings["material_set"], set_name) or [])
- items.insert(self.settings["new_index"], items.pop(self.settings["old_index"]))
- setattr(self.settings["material_set"], set_name, items)
+ if settings["material_set"].is_a("IfcMaterialConstituentSet"):
+ set_name = "MaterialConstituents"
+ elif settings["material_set"].is_a("IfcMaterialLayerSet"):
+ set_name = "MaterialLayers"
+ elif settings["material_set"].is_a("IfcMaterialProfileSet"):
+ set_name = "MaterialProfiles"
+ elif settings["material_set"].is_a("IfcMaterialList"):
+ set_name = "Materials"
+ items = list(getattr(settings["material_set"], set_name) or [])
+ items.insert(settings["new_index"], items.pop(settings["old_index"]))
+ setattr(settings["material_set"], set_name, items)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py
index 5f88963c36..93a7f11aea 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py
@@ -21,41 +21,44 @@ import ifcopenshell.api
import ifcopenshell.util.element
+def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None:
+ """Removes any material relationship with the list of products
+
+ A product can only have one material assigned to it, which is why it is
+ not necessary to specify the material to unassign. The material is not
+ removed, only the relationship is removed.
+
+ If the product does not have a material, nothing happens.
+
+ :param products: The list IfcProducts that may or may not have a material
+ :type product: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+
+ # Let's imagine a concrete bench made out of concrete.
+ bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType")
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[bench_type], type="IfcMaterial", material=concrete)
+
+ # Let's change our mind and remove the concrete assignment. The
+ # concrete material still exists, but the bench is no longer made
+ # out of concrete now.
+ ifcopenshell.api.run("material.unassign_material", model, products=[bench_type])
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"products": products}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]):
- """Removes any material relationship with the list of products
-
- A product can only have one material assigned to it, which is why it is
- not necessary to specify the material to unassign. The material is not
- removed, only the relationship is removed.
-
- If the product does not have a material, nothing happens.
-
- :param products: The list IfcProducts that may or may not have a material
- :type product: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
-
- # Let's imagine a concrete bench made out of concrete.
- bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType")
- ifcopenshell.api.run("material.assign_material", model,
- products=[bench_type], type="IfcMaterial", material=concrete)
-
- # Let's change our mind and remove the concrete assignment. The
- # concrete material still exists, but the bench is no longer made
- # out of concrete now.
- ifcopenshell.api.run("material.unassign_material", model, products=[bench_type])
- """
- self.file = file
- self.settings = {"products": products}
-
- def execute(self) -> None:
+ def execute(self):
self.products = set(self.settings["products"])
if not self.products:
return
diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py
index e0caddbe3c..242e12eb11 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py
@@ -15,3 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .assign_object import assign_object
+from .change_nest import change_nest
+from .reorder_nesting import reorder_nesting
+from .unassign_object import unassign_object
diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py
index 1c9ab8c9b0..7bf76e5c08 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py
@@ -22,156 +22,152 @@ import ifcopenshell.util.element
from typing import Union
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- related_objects: list[ifcopenshell.entity_instance],
- relating_object: ifcopenshell.entity_instance,
- ):
- """Assigns objects as nested children to a parent host
+def assign_object(
+ file: ifcopenshell.file,
+ related_objects: list[ifcopenshell.entity_instance],
+ relating_object: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Assigns objects as nested children to a parent host
- 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.
+ 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.
- Another type of "decomposition" relationship is known as "nesting".
- Nesting is used when an child object is physically attached to a parent
- host object, through a physical predetermined connection point. The
- child object must be specifically designed to attach to a other objects
- at specific positions with a particular form factor. Examples include
- faucets which must always be attached through a predrilled hole in a
- basin. Alternatively, it could be a modular attachment with a
- correlating male and female joint that must join at a particular point.
- Because there is a strict connection point, when the parent moves, all
- nested children must move with the parent. Another example might be a
- predrilled hole in a door panel where hardware must fit through.
+ Another type of "decomposition" relationship is known as "nesting".
+ Nesting is used when an child object is physically attached to a parent
+ host object, through a physical predetermined connection point. The
+ child object must be specifically designed to attach to a other objects
+ at specific positions with a particular form factor. Examples include
+ faucets which must always be attached through a predrilled hole in a
+ basin. Alternatively, it could be a modular attachment with a
+ correlating male and female joint that must join at a particular point.
+ Because there is a strict connection point, when the parent moves, all
+ nested children must move with the parent. Another example might be a
+ predrilled hole in a door panel where hardware must fit through.
- Nesting relationships are not very commonly used in most design and
- construction models. Its main usecase is in modular construction, kit of
- parts, or fabrication models.
+ Nesting relationships are not very commonly used in most design and
+ construction models. Its main usecase is in modular construction, kit of
+ parts, or fabrication models.
- As a product may only have a single location in the "spatial
- decomposition" tree, assigning an nesting relationship will remove any
- previous aggregation, containment, or nesting relationships it may have.
+ As a product may only have a single location in the "spatial
+ decomposition" tree, assigning an nesting relationship will remove any
+ previous aggregation, containment, or nesting relationships it may have.
- IFC placements follow a convention where the placement is relative to
- its parent in the spatial hierarchy. If your product has a placement,
- its placement will be recalculated to follow this convention.
+ IFC placements follow a convention where the placement is relative to
+ its parent in the spatial hierarchy. If your product has a placement,
+ its placement will be recalculated to follow this convention.
- For physical connections which are part of a distribution system, such
- as a plug connecting into a GPO, or a duct connecting to an AHU, or two
- pipe segments connecting with a bend, tee, or wye fitting, you should
- not nest the two objects directly. Instead, you should nest a connection
- port, which determines the type of compatible distribution flow that can
- be connected to it. To do this, do not use this function, but instead
- use the more specific functions in the ifcopenshell.api.system module.
+ For physical connections which are part of a distribution system, such
+ as a plug connecting into a GPO, or a duct connecting to an AHU, or two
+ pipe segments connecting with a bend, tee, or wye fitting, you should
+ not nest the two objects directly. Instead, you should nest a connection
+ port, which determines the type of compatible distribution flow that can
+ be connected to it. To do this, do not use this function, but instead
+ use the more specific functions in the ifcopenshell.api.system module.
- Note that nesting relationships may also be used by non-physical
- elements, such as cost items or tasks. In this context, nesting means
- that there is an implied order to the child cost items or tasks (i.e.
- task 1 should be shown before task 2). It is not necessary to use this
- function for nesting non-physical elements. Instead, it is recommended
- to instead just use the relevant API functions, like
- ifcopenshell.api.cost.add_cost_item or
- ifcopenshell.api.sequence.add_task.
+ Note that nesting relationships may also be used by non-physical
+ elements, such as cost items or tasks. In this context, nesting means
+ that there is an implied order to the child cost items or tasks (i.e.
+ task 1 should be shown before task 2). It is not necessary to use this
+ function for nesting non-physical elements. Instead, it is recommended
+ to instead just use the relevant API functions, like
+ ifcopenshell.api.cost.add_cost_item or
+ ifcopenshell.api.sequence.add_task.
- :param related_objects: The list of children of the nesting relationship,
- typically IfcElements.
- :type related_objects: list[ifcopenshell.entity_instance]
- :param relating_object: The host parent of the nesting relationship,
- typically an IfcElement.
- :type relating_object: ifcopenshell.entity_instance
- :return: The IfcRelNests relationship instance
- or `None` if `related_objects` was empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param related_objects: The list of children of the nesting relationship,
+ typically IfcElements.
+ :type related_objects: list[ifcopenshell.entity_instance]
+ :param relating_object: The host parent of the nesting relationship,
+ typically an IfcElement.
+ :type relating_object: ifcopenshell.entity_instance
+ :return: The IfcRelNests relationship instance
+ or `None` if `related_objects` was empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Faucets are designed to attach onto a sink through a predrilled hole.
- sink = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcSanitaryTerminal", predefined_type="SINK")
- faucet = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcValve", predefined_type="FAUCET")
- ifcopenshell.api.run("nest.assign_object", model, related_objects=[faucet], relating_object=sink)
- """
- self.file = file
- self.settings = {"related_objects": related_objects, "relating_object": relating_object}
+ # Faucets are designed to attach onto a sink through a predrilled hole.
+ sink = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcSanitaryTerminal", predefined_type="SINK")
+ faucet = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcValve", predefined_type="FAUCET")
+ ifcopenshell.api.run("nest.assign_object", model, related_objects=[faucet], relating_object=sink)
+ """
+ settings = {"related_objects": related_objects, "relating_object": relating_object}
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- if not self.settings["related_objects"]:
- return
+ if not settings["related_objects"]:
+ return
- ifc2x3 = self.file.schema == "IFC2X3"
+ ifc2x3 = file.schema == "IFC2X3"
- related_objects = set(self.settings["related_objects"])
- relating_object = self.settings["relating_object"]
+ related_objects = set(settings["related_objects"])
+ relating_object = settings["relating_object"]
+ if ifc2x3:
+ is_nested_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelNests")), None)
+ else:
+ is_nested_by = next((i for i in relating_object.IsNestedBy), None)
+
+ previous_nests_rels: set[ifcopenshell.entity_instance] = set()
+ objects_without_nests: list[ifcopenshell.entity_instance] = []
+ objects_with_nests: list[ifcopenshell.entity_instance] = []
+
+ # check if there is anything to change
+ for object in related_objects:
if ifc2x3:
- is_nested_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelNests")), None)
+ object_rel = next((i for i in object.Decomposes if i.is_a("IfcRelNests")), None)
else:
- is_nested_by = next((i for i in relating_object.IsNestedBy), None)
+ object_rel = next(iter(object.Nests), None)
- previous_nests_rels: set[ifcopenshell.entity_instance] = set()
- objects_without_nests: list[ifcopenshell.entity_instance] = []
- objects_with_nests: list[ifcopenshell.entity_instance] = []
+ if object_rel is None:
+ objects_without_nests.append(object)
+ continue
- # check if there is anything to change
- for object in related_objects:
- if ifc2x3:
- object_rel = next((i for i in object.Decomposes if i.is_a("IfcRelNests")), None)
- else:
- object_rel = next(iter(object.Nests), None)
+ # either is_nested_by is None or product is part of different rel
+ if object_rel != is_nested_by:
+ previous_nests_rels.add(object_rel)
+ objects_with_nests.append(object)
- if object_rel is None:
- objects_without_nests.append(object)
- continue
-
- # either is_nested_by is None or product is part of different rel
- if object_rel != is_nested_by:
- previous_nests_rels.add(object_rel)
- objects_with_nests.append(object)
-
- # products with already assigned nestings will be skipped
-
- objects_to_change = objects_without_nests + objects_with_nests
- # nothing to change
- if not objects_to_change:
- return is_nested_by
-
- # NOTE: An object can both be nested and assigned to a container or an aggregate.
-
- # unassign elements from previous nests
- for nests in previous_nests_rels:
- cur_related_objects = set(nests.RelatedObjects) - related_objects
- if cur_related_objects:
- nests.RelatedObjects = list(cur_related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": nests})
- else:
- history = nests.OwnerHistory
- self.file.remove(nests)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
-
- # assign elements to a new nesting
- if is_nested_by:
- is_nested_by.RelatedObjects = list(set(is_nested_by.RelatedObjects) | related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_nested_by})
- else:
- is_nested_by = self.file.create_entity(
- "IfcRelNests",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": list(related_objects),
- "RelatingObject": relating_object,
- }
- )
-
- # NOTE: Creating a nesting relationship doesn't localize the object's placement,
- # unlike assigning it to an aggregate or a container.
+ # products with already assigned nestings will be skipped
+ objects_to_change = objects_without_nests + objects_with_nests
+ # nothing to change
+ if not objects_to_change:
return is_nested_by
+
+ # NOTE: An object can both be nested and assigned to a container or an aggregate.
+
+ # unassign elements from previous nests
+ for nests in previous_nests_rels:
+ cur_related_objects = set(nests.RelatedObjects) - related_objects
+ if cur_related_objects:
+ nests.RelatedObjects = list(cur_related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests})
+ else:
+ history = nests.OwnerHistory
+ file.remove(nests)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+
+ # assign elements to a new nesting
+ if is_nested_by:
+ is_nested_by.RelatedObjects = list(set(is_nested_by.RelatedObjects) | related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_nested_by})
+ else:
+ is_nested_by = file.create_entity(
+ "IfcRelNests",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": list(related_objects),
+ "RelatingObject": relating_object,
+ }
+ )
+
+ # NOTE: Creating a nesting relationship doesn't localize the object's placement,
+ # unlike assigning it to an aggregate or a container.
+
+ return is_nested_by
diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py b/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py
index 6d83a9e4e8..22ba7d0fc3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py
@@ -21,29 +21,26 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, item=None, new_parent=None):
- """Assigns a cost item to a new parent cost item"""
- self.file = file
- self.settings = {"item": item, "new_parent": new_parent}
+def change_nest(file, item=None, new_parent=None) -> None:
+ """Assigns a cost item to a new parent cost item"""
+ settings = {"item": item, "new_parent": new_parent}
- def execute(self):
- if not self.settings["item"].Nests:
- return
- nests = self.settings["item"].Nests[0]
- related_objects = list(nests.RelatedObjects)
- related_objects.remove(self.settings["item"])
- if related_objects:
- nests.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": nests})
- else:
- history = nests.OwnerHistory
- self.file.remove(nests)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- ifcopenshell.api.run(
- "nest.assign_object",
- self.file,
- related_objects=[self.settings["item"]],
- relating_object=self.settings["new_parent"],
- )
+ if not settings["item"].Nests:
+ return
+ nests = settings["item"].Nests[0]
+ related_objects = list(nests.RelatedObjects)
+ related_objects.remove(settings["item"])
+ if related_objects:
+ nests.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests})
+ else:
+ history = nests.OwnerHistory
+ file.remove(nests)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ ifcopenshell.api.run(
+ "nest.assign_object",
+ file,
+ related_objects=[settings["item"]],
+ relating_object=settings["new_parent"],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py b/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py
index cdc23c07a5..63b591ee28 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py
@@ -17,20 +17,17 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, item=None, old_index=0, new_index=0):
- """Reorders an item in a nesting set"""
- self.file = file
- self.settings = {"item": item, "old_index":old_index, "new_index": new_index}
+def reorder_nesting(file, item=None, old_index=0, new_index=0) -> None:
+ """Reorders an item in a nesting set"""
+ settings = {"item": item, "old_index": old_index, "new_index": new_index}
- def execute(self):
- if not self.settings["item"].Nests:
- return
- nesting_set = self.settings["item"].Nests[0]
- if not self.settings["old_index"]:
- old_index = nesting_set.RelatedObjects.index(self.settings["item"])
- else:
- old_index = self.settings["old_index"]
- items = list(getattr(nesting_set, "RelatedObjects") or [])
- items.insert(self.settings["new_index"], items.pop(old_index))
- setattr(nesting_set, "RelatedObjects", items)
+ if not settings["item"].Nests:
+ return
+ nesting_set = settings["item"].Nests[0]
+ if not settings["old_index"]:
+ old_index = nesting_set.RelatedObjects.index(settings["item"])
+ else:
+ old_index = settings["old_index"]
+ items = list(getattr(nesting_set, "RelatedObjects") or [])
+ items.insert(settings["new_index"], items.pop(old_index))
+ setattr(nesting_set, "RelatedObjects", items)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py
index b9f48b4b5b..42e35777ad 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py
@@ -21,57 +21,54 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]):
- """Unassigns related_objects from their nests.
+def unassign_object(file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]) -> None:
+ """Unassigns related_objects from their nests.
- An object (the whole within a decomposition) is Nested by zero or one more smaller objects.
- This function will remove this nesting relationship.
+ An object (the whole within a decomposition) is Nested by zero or one more smaller objects.
+ This function will remove this nesting relationship.
- If the object is not part of a nesting relationship, nothing will happen.
+ If the object is not part of a nesting relationship, nothing will happen.
- :param related_objects: The list of children of the nesting relationship,
- typically IfcElements.
- :type related_objects: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
+ :param related_objects: The list of children of the nesting relationship,
+ typically IfcElements.
+ :type related_objects: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- task = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTasks")
- subtask1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask")
- subtask2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask")
- ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask1], relating_object=task)
- ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask2], relating_object=task)
- # nothing is returned
- rel = ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask1])
- # nothing is returned, relationship is removed
- ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask2])
- """
- self.file = file
- self.settings = {"related_objects": related_objects}
+ task = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTasks")
+ subtask1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask")
+ subtask2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask")
+ ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask1], relating_object=task)
+ ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask2], relating_object=task)
+ # nothing is returned
+ rel = ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask1])
+ # nothing is returned, relationship is removed
+ ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask2])
+ """
+ settings = {"related_objects": related_objects}
- def execute(self) -> None:
- related_objects = set(self.settings["related_objects"])
- ifc2x3 = self.file.schema == "IFC2X3"
- if ifc2x3:
- rels = set(
- rel
- for object in related_objects
- if (rel := next((rel for rel in object.Decomposes if rel.is_a("IfcRelNests")), None))
- )
+ related_objects = set(settings["related_objects"])
+ ifc2x3 = file.schema == "IFC2X3"
+ if ifc2x3:
+ rels = set(
+ rel
+ for object in related_objects
+ if (rel := next((rel for rel in object.Decomposes if rel.is_a("IfcRelNests")), None))
+ )
+ else:
+ rels = set(rel for object in related_objects if (rel := next((rel for rel in object.Nests), None)))
+
+ for rel in rels:
+ related_objects = set(rel.RelatedObjects) - related_objects
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else:
- rels = set(rel for object in related_objects if (rel := next((rel for rel in object.Nests), None)))
-
- for rel in rels:
- related_objects = set(rel.RelatedObjects) - related_objects
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py
index e0caddbe3c..755fa4fc5f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py
@@ -15,3 +15,27 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_actor import add_actor
+from .add_address import add_address
+from .add_application import add_application
+from .add_organisation import add_organisation
+from .add_person import add_person
+from .add_person_and_organisation import add_person_and_organisation
+from .add_role import add_role
+from .assign_actor import assign_actor
+from .create_owner_history import create_owner_history
+from .edit_actor import edit_actor
+from .edit_address import edit_address
+from .edit_organisation import edit_organisation
+from .edit_person import edit_person
+from .edit_role import edit_role
+from .remove_actor import remove_actor
+from .remove_address import remove_address
+from .remove_application import remove_application
+from .remove_organisation import remove_organisation
+from .remove_person import remove_person
+from .remove_person_and_organisation import remove_person_and_organisation
+from .remove_role import remove_role
+from .unassign_actor import unassign_actor
+from .update_owner_history import update_owner_history
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py
index c3ff65c3d1..124561f136 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py
@@ -21,49 +21,46 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, actor=None, ifc_class="IfcActor"):
- """Adds a new actor
+def add_actor(file, actor=None, ifc_class="IfcActor") -> None:
+ """Adds a new actor
- An actor is a person or an organisation who has a responsibility or role
- to play in a project. Actor roles include design consultants,
- architects, engineers, cost planners, suppliers, manufacturers,
- warrantors, owners, subcontractors, etc.
+ An actor is a person or an organisation who has a responsibility or role
+ to play in a project. Actor roles include design consultants,
+ architects, engineers, cost planners, suppliers, manufacturers,
+ warrantors, owners, subcontractors, etc.
- Actors may either be project actors, who are responsible for the
- delivery of the project, or occupants, who are responsible for the
- consumption of the project.
+ Actors may either be project actors, who are responsible for the
+ delivery of the project, or occupants, who are responsible for the
+ consumption of the project.
- Identifying and managing actors is critical for asset management, and
- identifying liability for legal submissions.
+ Identifying and managing actors is critical for asset management, and
+ identifying liability for legal submissions.
- :param actor: Most commonly, an IfcOrganization (in compliance with GDPR
- requirements for non personally identifiable information), or an
- IfcPerson if it is a sole individual, or an IfcPersonAndOrganization
- if a specific person is liable within an organisation and must be
- legally nominated.
- :type actor: ifcopenshell.entity_instance
- :param ifc_class: Either "IfcActor" or "IfcOccupant".
- :type ifc_class: str, optional
- :return: The newly created IfcActor or IfcOccupant
- :rtype: ifcopenshell.entity_instance
+ :param actor: Most commonly, an IfcOrganization (in compliance with GDPR
+ requirements for non personally identifiable information), or an
+ IfcPerson if it is a sole individual, or an IfcPersonAndOrganization
+ if a specific person is liable within an organisation and must be
+ legally nominated.
+ :type actor: ifcopenshell.entity_instance
+ :param ifc_class: Either "IfcActor" or "IfcOccupant".
+ :type ifc_class: str, optional
+ :return: The newly created IfcActor or IfcOccupant
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Setup an organisation with a single role
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT")
+ # Setup an organisation with a single role
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT")
- # Assign that organisation to a newly created actor
- actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
- """
- self.file = file
- self.settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"}
+ # Assign that organisation to a newly created actor
+ actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
+ """
+ settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"}
- def execute(self):
- actor = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=self.settings["ifc_class"])
- actor.TheActor = self.settings["actor"]
- return actor
+ actor = ifcopenshell.api.run("root.create_entity", file, ifc_class=settings["ifc_class"])
+ actor.TheActor = settings["actor"]
+ return actor
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py
index b184408fa9..a214c86d81 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py
@@ -17,58 +17,53 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, assigned_object=None, ifc_class="IfcPostalAddress"):
- """Add a new telecom or postal address to an organisation or person
+def add_address(file, assigned_object=None, ifc_class="IfcPostalAddress") -> None:
+ """Add a new telecom or postal address to an organisation or person
- A person or organisation may have associated contact details such as
- phone numbers, mailing addresses, websites, email addresses, and instant
- messaging handles. This information is critical in recording the contact
- information of manufacturers and suppliers for facility management, or
- liable actors.
+ A person or organisation may have associated contact details such as
+ phone numbers, mailing addresses, websites, email addresses, and instant
+ messaging handles. This information is critical in recording the contact
+ information of manufacturers and suppliers for facility management, or
+ liable actors.
- There are two types of addresses, postal addresses for physical snail
- mail, and telecom addresses for telephone or internet contact numbers
- and addresses.
+ There are two types of addresses, postal addresses for physical snail
+ mail, and telecom addresses for telephone or internet contact numbers
+ and addresses.
- :param assigned_object: The IfcOrganization or IfcPerson the contact
- address belongs to.
- :type assigned_object: ifcopenshell.entity_instance
- :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults
- to IfcPostalAddress.
- :type ifc_class: str, optional
- :return: The new IfcPostalAddress or IfcTelecomAddress
- :rtype: ifcopenshell.entity_instance
+ :param assigned_object: The IfcOrganization or IfcPerson the contact
+ address belongs to.
+ :type assigned_object: ifcopenshell.entity_instance
+ :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults
+ to IfcPostalAddress.
+ :type ifc_class: str, optional
+ :return: The new IfcPostalAddress or IfcTelecomAddress
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model)
+ organisation = ifcopenshell.api.run("owner.add_organisation", model)
- # A snail mail address
- postal = ifcopenshell.api.run("owner.add_address", model,
- assigned_object=organisation, ifc_class="IfcPostalAddress")
- ifcopenshell.api.run("owner.edit_address", model, address=postal,
- attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"],
- "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"})
+ # A snail mail address
+ postal = ifcopenshell.api.run("owner.add_address", model,
+ assigned_object=organisation, ifc_class="IfcPostalAddress")
+ ifcopenshell.api.run("owner.edit_address", model, address=postal,
+ attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"],
+ "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"})
- # A phone or internet address
- telecom = ifcopenshell.api.run("owner.add_address", model,
- assigned_object=organisation, ifc_class="IfcTelecomAddress")
- ifcopenshell.api.run("owner.edit_address", model, address=telecom,
- attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"],
- "ElectronicMailAddresses": ["bobthebuilder@example.com"],
- "WWWHomePageURL": "https://thinkmoult.com"})
- """
- self.file = file
- self.settings = {"assigned_object": assigned_object, "ifc_class": ifc_class}
+ # A phone or internet address
+ telecom = ifcopenshell.api.run("owner.add_address", model,
+ assigned_object=organisation, ifc_class="IfcTelecomAddress")
+ ifcopenshell.api.run("owner.edit_address", model, address=telecom,
+ attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"],
+ "ElectronicMailAddresses": ["bobthebuilder@example.com"],
+ "WWWHomePageURL": "https://thinkmoult.com"})
+ """
+ settings = {"assigned_object": assigned_object, "ifc_class": ifc_class}
- def execute(self):
- address = self.file.create_entity(self.settings["ifc_class"], "OFFICE")
- addresses = (
- list(self.settings["assigned_object"].Addresses) if self.settings["assigned_object"].Addresses else []
- )
- addresses.append(address)
- self.settings["assigned_object"].Addresses = addresses
- return address
+ address = file.create_entity(settings["ifc_class"], "OFFICE")
+ addresses = list(settings["assigned_object"].Addresses) if settings["assigned_object"].Addresses else []
+ addresses.append(address)
+ settings["assigned_object"].Addresses = addresses
+ return address
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py
index 92861a3417..07a59db0d7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py
@@ -19,50 +19,52 @@
import ifcopenshell.api
+def add_application(
+ file,
+ application_developer=None,
+ version=None,
+ application_full_name="IfcOpenShell",
+ application_identifier="IfcOpenShell",
+) -> None:
+ """Adds a new application
+
+ IFC data may be associated with an authoring application to identify
+ which application was responsible for editing or authoring the data. An
+ application is defined by the developing organisation, as well as a full
+ name and identifier. This is akin to how web browsers have an
+ identification string.
+
+ :param application_developer: The IfcOrganization responsible for
+ creating the application. Defaults to generating an IfcOpenShell
+ organisation if none is provided.
+ :type application_developer: ifcopenshell.entity_instance, optional
+ :param version: The version of the application. Defaults to the
+ ifcopenshell.version data if not specified.
+ :type version: str, optional
+ :param application_full_name: The name of the application
+ :type application_full_name: str, optional
+ :param application_identifier: An identification string for the
+ application intended for computers to read.
+ :type application_identifier: str, optional
+
+ Example:
+
+ .. code:: python
+
+ application = ifcopenshell.api.run("owner.add_application", model)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "application_developer": application_developer,
+ "version": version or ifcopenshell.version,
+ "application_full_name": application_full_name,
+ "application_identifier": application_identifier,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file,
- application_developer=None,
- version=None,
- application_full_name="IfcOpenShell",
- application_identifier="IfcOpenShell",
- ):
- """Adds a new application
-
- IFC data may be associated with an authoring application to identify
- which application was responsible for editing or authoring the data. An
- application is defined by the developing organisation, as well as a full
- name and identifier. This is akin to how web browsers have an
- identification string.
-
- :param application_developer: The IfcOrganization responsible for
- creating the application. Defaults to generating an IfcOpenShell
- organisation if none is provided.
- :type application_developer: ifcopenshell.entity_instance, optional
- :param version: The version of the application. Defaults to the
- ifcopenshell.version data if not specified.
- :type version: str, optional
- :param application_full_name: The name of the application
- :type application_full_name: str, optional
- :param application_identifier: An identification string for the
- application intended for computers to read.
- :type application_identifier: str, optional
-
- Example:
-
- .. code:: python
-
- application = ifcopenshell.api.run("owner.add_application", model)
- """
- self.file = file
- self.settings = {
- "application_developer": application_developer,
- "version": version or ifcopenshell.version,
- "application_full_name": application_full_name,
- "application_identifier": application_identifier,
- }
-
def execute(self):
if not self.settings["application_developer"]:
self.settings["application_developer"] = self.create_application_organisation()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py
index 2127acd570..354d66f76a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py
@@ -18,38 +18,37 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file: ifcopenshell.file, identification: str = "APTR", name: str = "Aperture Science"):
- """Adds a new organisation
+def add_organisation(
+ file: ifcopenshell.file, identification: str = "APTR", name: str = "Aperture Science"
+) -> ifcopenshell.entity_instance:
+ """Adds a new organisation
- Organisations are the main way to identify manufacturers, suppliers, and
- other actors who do not have a single representative or must not have
- any personally identifiable information.
+ Organisations are the main way to identify manufacturers, suppliers, and
+ other actors who do not have a single representative or must not have
+ any personally identifiable information.
- :param identification: The short code identifying the organisation.
- Sometimes used in drawing naming schemes. Otherise used as a
- canonicalised way of computers to identify the organisation. Like
- their stock name.
- :type identification: str, optional
- :param name: The legal name of the organisation
- :type name: str, optional
- :return: The newly created IfcOrganization
- :rtype: ifcopenshell.entity_instance
+ :param identification: The short code identifying the organisation.
+ Sometimes used in drawing naming schemes. Otherise used as a
+ canonicalised way of computers to identify the organisation. Like
+ their stock name.
+ :type identification: str, optional
+ :param name: The legal name of the organisation
+ :type name: str, optional
+ :return: The newly created IfcOrganization
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- """
- self.file = file
- self.settings = {"identification": identification, "name": name}
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ """
+ settings = {"identification": identification, "name": name}
- def execute(self) -> ifcopenshell.entity_instance:
- data = {"Name": self.settings["name"]}
- if self.file.schema == "IFC2X3":
- data["Id"] = self.settings["identification"]
- else:
- data["Identification"] = self.settings["identification"]
- return self.file.create_entity("IfcOrganization", **data)
+ data = {"Name": settings["name"]}
+ if file.schema == "IFC2X3":
+ data["Id"] = settings["identification"]
+ else:
+ data["Identification"] = settings["identification"]
+ return file.create_entity("IfcOrganization", **data)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py
index a607d8af29..5d571920d4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py
@@ -18,47 +18,43 @@
import ifcopenshell
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.entity_instance,
- identification: str = "HSeldon",
- family_name: str = "Seldon",
- given_name: str = "Hari",
- ):
- """Adds a new person
+def add_person(
+ file: ifcopenshell.entity_instance,
+ identification: str = "HSeldon",
+ family_name: str = "Seldon",
+ given_name: str = "Hari",
+) -> None:
+ """Adds a new person
- Persons are used to identify a legal or liable representative of an
- organisation or point of contact.
+ Persons are used to identify a legal or liable representative of an
+ organisation or point of contact.
- :param identification: The computer readable unique identification of
- the person. For example, their username in a CDE or alias.
- :type identification: str, optional
- :param family_name: The family name
- :type family_name: str, optional
- :param given_name: The given name
- :type given_name: str, optional
- :return: The newly created IfcPerson
- :rtype: ifcopenshell.entity_instance
+ :param identification: The computer readable unique identification of
+ the person. For example, their username in a CDE or alias.
+ :type identification: str, optional
+ :param family_name: The family name
+ :type family_name: str, optional
+ :param given_name: The given name
+ :type given_name: str, optional
+ :return: The newly created IfcPerson
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("owner.add_person", model,
- identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
- """
- self.file = file
- self.settings = {
- "identification": identification,
- "family_name": family_name,
- "given_name": given_name,
- }
+ ifcopenshell.api.run("owner.add_person", model,
+ identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
+ """
+ settings = {
+ "identification": identification,
+ "family_name": family_name,
+ "given_name": given_name,
+ }
- def execute(self) ->ifcopenshell.entity_instance:
- data = {"FamilyName": self.settings["family_name"], "GivenName": self.settings["given_name"]}
- if self.file.schema == "IFC2X3":
- data["Id"] = self.settings["identification"]
- else:
- data["Identification"] = self.settings["identification"]
- return self.file.create_entity("IfcPerson", **data)
+ data = {"FamilyName": settings["family_name"], "GivenName": settings["given_name"]}
+ if file.schema == "IFC2X3":
+ data["Id"] = settings["identification"]
+ else:
+ data["Identification"] = settings["identification"]
+ return file.create_entity("IfcPerson", **data)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py
index a5987b4b76..7f07c1991c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py
@@ -18,40 +18,36 @@
import ifcopenshell
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.entity_instance,
- person: ifcopenshell.entity_instance,
- organisation: ifcopenshell.entity_instance,
- ):
- """Adds a paired person and organisation
+def add_person_and_organisation(
+ file: ifcopenshell.entity_instance,
+ person: ifcopenshell.entity_instance,
+ organisation: ifcopenshell.entity_instance,
+) -> ifcopenshell.entity_instance:
+ """Adds a paired person and organisation
- A person and an organisation may be paired to create a representative
- belonging to a company.
+ A person and an organisation may be paired to create a representative
+ belonging to a company.
- :param person: The IfcPerson being the representative of the
- organisation.
- :type person: ifcopenshell.entity_instance
- :param organisation: The IfcOrganization itself.
- :type organisation: ifcopenshell.entity_instance
- :return: The newly created IfcPersonAndOrganization
- :rtype: ifcopenshell.entity_instance
+ :param person: The IfcPerson being the representative of the
+ organisation.
+ :type person: ifcopenshell.entity_instance
+ :param organisation: The IfcOrganization it
+ :type organisation: ifcopenshell.entity_instance
+ :return: The newly created IfcPersonAndOrganization
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- person = ifcopenshell.api.run("owner.add_person", model,
- identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le")
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
+ person = ifcopenshell.api.run("owner.add_person", model,
+ identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le")
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
- ifcopenshell.api.run("owner.add_person_and_organisation", model,
- person=person, organisation=organisation)
- """
- self.file = file
- self.settings = {"person": person, "organisation": organisation}
+ ifcopenshell.api.run("owner.add_person_and_organisation", model,
+ person=person, organisation=organisation)
+ """
+ settings = {"person": person, "organisation": organisation}
- def execute(self) -> ifcopenshell.entity_instance:
- return self.file.createIfcPersonAndOrganization(self.settings["person"], self.settings["organisation"])
+ return file.createIfcPersonAndOrganization(settings["person"], settings["organisation"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py
index 3b1369877c..415ed4efde 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py
@@ -17,47 +17,44 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, assigned_object=None, role="ARCHITECT"):
- """Adds and assigns a new role
+def add_role(file, assigned_object=None, role="ARCHITECT") -> None:
+ """Adds and assigns a new role
- People and organisations must play one or more roles on a project. Roles
- include architects, engineers, subcontractors, clients, manufacturers,
- etc. Typically these roles and their corresponding responsibilities will
- be outlined in contractual documents.
+ People and organisations must play one or more roles on a project. Roles
+ include architects, engineers, subcontractors, clients, manufacturers,
+ etc. Typically these roles and their corresponding responsibilities will
+ be outlined in contractual documents.
- This function will both add and assign the role to the person or
- organisation.
+ This function will both add and assign the role to the person or
+ organisation.
- :param assigned_object: The IfcPerson or IfcOrganization the role should
- be assigned to.
- :type assigned_object: ifcopenshell.entity_instance
- :param role: The type of role, taken from the IFC documentation for
- IfcActorRole, or a custom name.
- :type role: str, optional
- :return: The newly created IfcActorRole
- :rtype: ifcopenshell.entity_instance
+ :param assigned_object: The IfcPerson or IfcOrganization the role should
+ be assigned to.
+ :type assigned_object: ifcopenshell.entity_instance
+ :param role: The type of role, taken from the IFC documentation for
+ IfcActorRole, or a custom name.
+ :type role: str, optional
+ :return: The newly created IfcActorRole
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT")
- """
- self.file = file
- self.settings = {"assigned_object": assigned_object, "role": role}
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT")
+ """
+ settings = {"assigned_object": assigned_object, "role": role}
- def execute(self):
- element = self.file.createIfcActorRole("ARCHITECT")
- if self.settings["role"]:
- try:
- element.Role = self.settings["role"]
- except:
- element.Role = "USERDEFINED"
- element.UserDefinedRole = self.settings["role"]
- roles = list(self.settings["assigned_object"].Roles) if self.settings["assigned_object"].Roles else []
- roles.append(element)
- self.settings["assigned_object"].Roles = roles
- return element
+ element = file.createIfcActorRole("ARCHITECT")
+ if settings["role"]:
+ try:
+ element.Role = settings["role"]
+ except:
+ element.Role = "USERDEFINED"
+ element.UserDefinedRole = settings["role"]
+ roles = list(settings["assigned_object"].Roles) if settings["assigned_object"].Roles else []
+ roles.append(element)
+ settings["assigned_object"].Roles = roles
+ return element
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py
index 1085adeb34..361c32bf2a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py
@@ -20,88 +20,85 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_actor=None, related_object=None):
- """Assigns an actor to an object
+def assign_actor(file, relating_actor=None, related_object=None) -> None:
+ """Assigns an actor to an object
- An actor may be assigned to objects which implies that the actor is
- responsible for. This is most commonly used in facility management for
- indicating the manufacturers, suppliers, and warrantors for product
- types.
+ An actor may be assigned to objects which implies that the actor is
+ responsible for. This is most commonly used in facility management for
+ indicating the manufacturers, suppliers, and warrantors for product
+ types.
- Here are a list of objects you may assign an actor to:
+ Here are a list of objects you may assign an actor to:
- * IfcControl: Indicates project directives issued by the actor.
- * IfcGroup: Indicates groups for which the actor is responsible.
- * IfcProduct: Indicates products for which the actor is responsible.
- * IfcProcess: Indicates processes for which the actor is responsible.
- * IfcResource: Indicates resources for which the actor is responsible to
- allocate, manage, or delegate. This is not the actor actually using
- the resource or performing the work. For that type of actor, see
- ifcopenshell.api.resource.assign_resource.
+ * IfcControl: Indicates project directives issued by the actor.
+ * IfcGroup: Indicates groups for which the actor is responsible.
+ * IfcProduct: Indicates products for which the actor is responsible.
+ * IfcProcess: Indicates processes for which the actor is responsible.
+ * IfcResource: Indicates resources for which the actor is responsible to
+ allocate, manage, or delegate. This is not the actor actually using
+ the resource or performing the work. For that type of actor, see
+ ifcopenshell.api.resource.assign_resource.
- :param relating_actor: The IfcActor who is responsible for the object.
- :type relating_actor: ifcopenshell.entity_instance
- :param related_object: The object the actor is responsible for.
- :type related_object: ifcopenshell.entity_instance
- :return: The newly created IfcRelAssignsToActor relationship.
- :rtype: ifcopenshell.entity_instance
+ :param relating_actor: The IfcActor who is responsible for the object.
+ :type relating_actor: ifcopenshell.entity_instance
+ :param related_object: The object the actor is responsible for.
+ :type related_object: ifcopenshell.entity_instance
+ :return: The newly created IfcRelAssignsToActor relationship.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # We need to procure and install 2 of this particular pump type in our facility.
- pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType")
+ # We need to procure and install 2 of this particular pump type in our facility.
+ pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType")
- # Define who the manufacturer is
- manufacturer = ifcopenshell.api.run("owner.add_organisation", model,
- identification="PWP", name="Pumps With Power")
- ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER")
+ # Define who the manufacturer is
+ manufacturer = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="PWP", name="Pumps With Power")
+ ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER")
- # To help our facility manager, it's nice to provide contact details
- # of the manufacturer so they know how to call when the pump breaks.
- telecom = ifcopenshell.api.run("owner.add_address", model,
- assigned_object=organisation, ifc_class="IfcTelecomAddress")
- ifcopenshell.api.run("owner.edit_address", model, address=telecom,
- attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"],
- "ElectronicMailAddresses": ["contact@example.com"],
- "WWWHomePageURL": "https://example.com"})
+ # To help our facility manager, it's nice to provide contact details
+ # of the manufacturer so they know how to call when the pump breaks.
+ telecom = ifcopenshell.api.run("owner.add_address", model,
+ assigned_object=organisation, ifc_class="IfcTelecomAddress")
+ ifcopenshell.api.run("owner.edit_address", model, address=telecom,
+ attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"],
+ "ElectronicMailAddresses": ["contact@example.com"],
+ "WWWHomePageURL": "https://example.com"})
- # Make the manufacturer responsible for that pump type.
- ifcopenshell.api.run("owner.assign_actor", model,
- relating_actor=manufacturer, related_object=pump_type)
- """
- self.file = file
- self.settings = {
- "relating_actor": relating_actor,
- "related_object": related_object,
- }
+ # Make the manufacturer responsible for that pump type.
+ ifcopenshell.api.run("owner.assign_actor", model,
+ relating_actor=manufacturer, related_object=pump_type)
+ """
+ settings = {
+ "relating_actor": relating_actor,
+ "related_object": related_object,
+ }
- def execute(self):
- if self.settings["related_object"].HasAssignments:
- for rel in self.settings["related_object"].HasAssignments:
- if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == self.settings["relating_actor"]:
- return
+ if settings["related_object"].HasAssignments:
+ for rel in settings["related_object"].HasAssignments:
+ if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == settings["relating_actor"]:
+ return
- rel = None
+ rel = None
- if self.settings["relating_actor"].IsActingUpon:
- rel = self.settings["relating_actor"].IsActingUpon[0]
+ if settings["relating_actor"].IsActingUpon:
+ rel = settings["relating_actor"].IsActingUpon[0]
- if rel:
- related_objects = list(rel.RelatedObjects)
- related_objects.append(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- rel = self.file.create_entity(
- "IfcRelAssignsToActor",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": [self.settings["related_object"]],
- "RelatingActor": self.settings["relating_actor"],
- }
- )
- return rel
+ if rel:
+ related_objects = list(rel.RelatedObjects)
+ related_objects.append(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ rel = file.create_entity(
+ "IfcRelAssignsToActor",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["related_object"]],
+ "RelatingActor": settings["relating_actor"],
+ }
+ )
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py
index 94183e0c6b..2b4729897f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py
@@ -22,100 +22,97 @@ import ifcopenshell.api.owner.settings
from typing import Union
-class Usecase:
- def __init__(self, file: ifcopenshell.entity_instance):
- """Creates a new owner history indicating an element was added
+def create_owner_history(file: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
+ """Creates a new owner history indicating an element was added
- Any object in IFC with a unique ID and name (such as physical products,
- tasks, calendars, etc) may have an owner associated with it. An owner is
- a liable person and/or organisation which a bit of metadata indicating
- whether they have created the object, edited the object, when the change
- was made, and which application they used.
+ Any object in IFC with a unique ID and name (such as physical products,
+ tasks, calendars, etc) may have an owner associated with it. An owner is
+ a liable person and/or organisation which a bit of metadata indicating
+ whether they have created the object, edited the object, when the change
+ was made, and which application they used.
- IFC does not offer a comprehensive specification for version control and
- change tracking, as this is completely out of scope. However this
- similar ability allows IFC to satisfy legal requirements where object
- ownership, responsibilities, and permissions must be specified.
- Recording the owner is mandatory in IFC2X3 but optional in IFC4. It is
- not recommended to store this ownership data in IFC4 unless a legal
- requirement is in place.
+ IFC does not offer a comprehensive specification for version control and
+ change tracking, as this is completely out of scope. However this
+ similar ability allows IFC to satisfy legal requirements where object
+ ownership, responsibilities, and permissions must be specified.
+ Recording the owner is mandatory in IFC2X3 but optional in IFC4. It is
+ not recommended to store this ownership data in IFC4 unless a legal
+ requirement is in place.
- Because owner tracking is mandatory in IFC2X3, be aware that some
- configuration may be required to work correctly. Read on.
+ Because owner tracking is mandatory in IFC2X3, be aware that some
+ configuration may be required to work correctly. Read on.
- To track the owner, at a minimum we have to know the application that
- the element was authored from, as well as the user (person and
- organisation) that made the change. The IfcOpenShell API is a low level
- software library and will not know what application the API is being
- called from, and nor does it have the responsibility to manage the
- "active user" making edits, which may be as simple as hardcoding it to
- "Bob" or even be as complex as integration with a CDE's authentication
- system. As a result, the developer responsible to integrate with
- IfcOpenShell is expected to overload the
- ifcopenshell.api.owner.settings.get_user and
- ifcopenshell.api.owner.settings.get_application functions.
+ To track the owner, at a minimum we have to know the application that
+ the element was authored from, as well as the user (person and
+ organisation) that made the change. The IfcOpenShell API is a low level
+ software library and will not know what application the API is being
+ called from, and nor does it have the responsibility to manage the
+ "active user" making edits, which may be as simple as hardcoding it to
+ "Bob" or even be as complex as integration with a CDE's authentication
+ system. As a result, the developer responsible to integrate with
+ IfcOpenShell is expected to overload the
+ ifcopenshell.api.owner.settings.get_user and
+ ifcopenshell.api.owner.settings.get_application functions.
- It is not necessary to call this function directly if you are already
- using other API calls. It is a low level function only available if you
- are writing your own advanced scripts and want to take advantage of the
- easier ownership tracking.
+ It is not necessary to call this function directly if you are already
+ using other API calls. It is a low level function only available if you
+ are writing your own advanced scripts and want to take advantage of the
+ easier ownership tracking.
- :return: The newly created IfcOwnerHistory element or `None` if it's
- not IFC2X3 and user or application is not found in the current project.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :return: The newly created IfcOwnerHistory element or `None` if it's
+ not IFC2X3 and user or application is not found in the current project.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we're writing a small script, not large enough to be
- # its own fully branded application. In this case, let's use the
- # default application which is prepopulated with "IfcOpenShell" as
- # the name and version.
- application = ifcopenshell.api.run("owner.add_application", model)
+ # Let's imagine we're writing a small script, not large enough to be
+ # its own fully branded application. In this case, let's use the
+ # default application which is prepopulated with "IfcOpenShell" as
+ # the name and version.
+ application = ifcopenshell.api.run("owner.add_application", model)
- # Let's imagine we run this as an automated QA process in an
- # architectural firm. However, the results must be signed off by the
- # registered architect who is liable for the project.
- person = ifcopenshell.api.run("owner.add_person", model,
- identification="LPARTEE", family_name="Partee", given_name="Leeable")
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- user = ifcopenshell.api.run("owner.add_person_and_organisation", model,
- person=person, organisation=organisation)
+ # Let's imagine we run this as an automated QA process in an
+ # architectural firm. However, the results must be signed off by the
+ # registered architect who is liable for the project.
+ person = ifcopenshell.api.run("owner.add_person", model,
+ identification="LPARTEE", family_name="Partee", given_name="Leeable")
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ user = ifcopenshell.api.run("owner.add_person_and_organisation", model,
+ person=person, organisation=organisation)
- # Let's configure our owner settings to hardcode always returning
- # the application and user. In theory, you could build complex user
- # access control lookup functions here, but this is simple enough.
- ifcopenshell.api.owner.settings.get_user = lambda x: user
- ifcopenshell.api.owner.settings.get_application = lambda x: application
+ # Let's configure our owner settings to hardcode always returning
+ # the application and user. In theory, you could build complex user
+ # access control lookup functions here, but this is simple enough.
+ ifcopenshell.api.owner.settings.get_user = lambda x: user
+ ifcopenshell.api.owner.settings.get_application = lambda x: application
- # We've finished our ownership setup. Now let's start our script and
- # create a space. Notice we don't actually call
- # create_owner_history at all. This is already automatically handled
- # by the API when necessary. Under the hood, the API is actually
- # running this code on the IfcSpace element:
- # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model)
- space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace")
- """
- self.file = file
- self.settings = {}
+ # We've finished our ownership setup. Now let's start our script and
+ # create a space. Notice we don't actually call
+ # create_owner_history at all. This is already automatically handled
+ # by the API when necessary. Under the hood, the API is actually
+ # running this code on the IfcSpace element:
+ # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model)
+ space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace")
+ """
+ settings = {}
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- user = ifcopenshell.api.owner.settings.get_user(self.file)
- if self.file.schema != "IFC2X3" and not user:
- return
- application = ifcopenshell.api.owner.settings.get_application(self.file)
- if self.file.schema != "IFC2X3" and not application:
- return
- return self.file.create_entity(
- "IfcOwnerHistory",
- OwningUser=user,
- OwningApplication=application,
- State="READWRITE",
- ChangeAction="ADDED",
- LastModifiedDate=int(time.time()),
- LastModifyingUser=user,
- LastModifyingApplication=application,
- CreationDate=int(time.time()),
- )
+ user = ifcopenshell.api.owner.settings.get_user(file)
+ if file.schema != "IFC2X3" and not user:
+ return
+ application = ifcopenshell.api.owner.settings.get_application(file)
+ if file.schema != "IFC2X3" and not application:
+ return
+ return file.create_entity(
+ "IfcOwnerHistory",
+ OwningUser=user,
+ OwningApplication=application,
+ State="READWRITE",
+ ChangeAction="ADDED",
+ LastModifiedDate=int(time.time()),
+ LastModifyingUser=user,
+ LastModifyingApplication=application,
+ CreationDate=int(time.time()),
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py
index eb6491b359..e1125ab212 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py
@@ -17,40 +17,37 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, actor=None, attributes=None):
- """Edits the attributes of an IfcActor
+def edit_actor(file, actor=None, attributes=None) -> None:
+ """Edits the attributes of an IfcActor
- For more information about the attributes and data types of an
- IfcActor, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcActor, consult the IFC documentation.
- :param actor: The IfcActor entity you want to edit
- :type actor: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param actor: The IfcActor entity you want to edit
+ :type actor: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Setup an organisation with a single role
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation)
- ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"})
+ # Setup an organisation with a single role
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation)
+ ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"})
- # Assign that organisation to a newly created actor
- actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
+ # Assign that organisation to a newly created actor
+ actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
- # Edit the description of the attribute.
- ifcopenshell.api.run("actor.edit_actor", model,
- actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."})
- """
- self.file = file
- self.settings = {"actor": actor, "attributes": attributes or {}}
+ # Edit the description of the attribute.
+ ifcopenshell.api.run("actor.edit_actor", model,
+ actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."})
+ """
+ settings = {"actor": actor, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["actor"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["actor"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py
index 0f48af25d5..ba74f0ede6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py
@@ -17,42 +17,39 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, address=None, attributes=None):
- """Edits the attributes of an IfcAddress
+def edit_address(file, address=None, attributes=None) -> None:
+ """Edits the attributes of an IfcAddress
- For more information about the attributes and data types of an
- IfcAddress, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcAddress, consult the IFC documentation.
- :param address: The IfcAddress entity you want to edit
- :type address: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param address: The IfcAddress entity you want to edit
+ :type address: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A snail mail address
- postal = ifcopenshell.api.run("owner.add_address", model,
- assigned_object=organisation, ifc_class="IfcPostalAddress")
- ifcopenshell.api.run("owner.edit_address", model, address=postal,
- attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"],
- "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"})
+ # A snail mail address
+ postal = ifcopenshell.api.run("owner.add_address", model,
+ assigned_object=organisation, ifc_class="IfcPostalAddress")
+ ifcopenshell.api.run("owner.edit_address", model, address=postal,
+ attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"],
+ "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"})
- # A phone or internet address
- telecom = ifcopenshell.api.run("owner.add_address", model,
- assigned_object=organisation, ifc_class="IfcTelecomAddress")
- ifcopenshell.api.run("owner.edit_address", model, address=telecom,
- attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"],
- "ElectronicMailAddresses": ["bobthebuilder@example.com"],
- "WWWHomePageURL": "https://thinkmoult.com"})
- """
- self.file = file
- self.settings = {"address": address, "attributes": attributes or {}}
+ # A phone or internet address
+ telecom = ifcopenshell.api.run("owner.add_address", model,
+ assigned_object=organisation, ifc_class="IfcTelecomAddress")
+ ifcopenshell.api.run("owner.edit_address", model, address=telecom,
+ attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"],
+ "ElectronicMailAddresses": ["bobthebuilder@example.com"],
+ "WWWHomePageURL": "https://thinkmoult.com"})
+ """
+ settings = {"address": address, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["address"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["address"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py
index 19c8d1e431..012e9152ba 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, organisation=None, attributes=None):
- """Edits the attributes of an IfcOrganization
+def edit_organisation(file, organisation=None, attributes=None) -> None:
+ """Edits the attributes of an IfcOrganization
- For more information about the attributes and data types of an
- IfcOrganization, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcOrganization, consult the IFC documentation.
- :param organisation: The IfcOrganization entity you want to edit
- :type organisation: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param organisation: The IfcOrganization entity you want to edit
+ :type organisation: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects With Ballpens")
- ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation,
- attributes={"name": "Architects Without Ballpens"})
- """
- self.file = file
- self.settings = {"organisation": organisation, "attributes": attributes or {}}
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects With Ballpens")
+ ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation,
+ attributes={"name": "Architects Without Ballpens"})
+ """
+ settings = {"organisation": organisation, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["organisation"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["organisation"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py
index 19eedc23db..a8fdb56168 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, person=None, attributes=None):
- """Edits the attributes of an IfcPerson
+def edit_person(file, person=None, attributes=None) -> None:
+ """Edits the attributes of an IfcPerson
- For more information about the attributes and data types of an
- IfcPerson, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcPerson, consult the IFC documentation.
- :param person: The IfcPerson entity you want to edit
- :type person: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param person: The IfcPerson entity you want to edit
+ :type person: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- person = ifcopenshell.api.run("owner.add_person", model,
- identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
- ifcopenshell.api.run("owner.edit_person", model, person=person,
- attributes={"MiddleNames": ["The"], "FamilyName": "Builder"})
- """
- self.file = file
- self.settings = {"person": person, "attributes": attributes or {}}
+ person = ifcopenshell.api.run("owner.add_person", model,
+ identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
+ ifcopenshell.api.run("owner.edit_person", model, person=person,
+ attributes={"MiddleNames": ["The"], "FamilyName": "Builder"})
+ """
+ settings = {"person": person, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["person"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["person"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py
index 160f6f6d91..6934af27e0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py
@@ -17,36 +17,33 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, role=None, attributes=None):
- """Edits the attributes of an IfcActorRole
+def edit_role(file, role=None, attributes=None) -> None:
+ """Edits the attributes of an IfcActorRole
- For more information about the attributes and data types of an
- IfcActorRole, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcActorRole, consult the IFC documentation.
- :param role: The IfcActorRole entity you want to edit
- :type role: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param role: The IfcActorRole entity you want to edit
+ :type role: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- person = ifcopenshell.api.run("owner.add_person", model,
- identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
+ person = ifcopenshell.api.run("owner.add_person", model,
+ identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
- # By default, the role is an architect
- role = ifcopenshell.api.run("owner.add_role", model, assigned_object=person)
+ # By default, the role is an architect
+ role = ifcopenshell.api.run("owner.add_role", model, assigned_object=person)
- # But Bob is not an architect
- ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"})
- """
- self.file = file
- self.settings = {"role": role, "attributes": attributes or {}}
+ # But Bob is not an architect
+ ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"})
+ """
+ settings = {"role": role, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["role"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["role"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py
index ac99299a6f..49feb54179 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py
@@ -20,36 +20,33 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, actor=None):
- """Removes an actor
+def remove_actor(file, actor=None) -> None:
+ """Removes an actor
- :param actor: The IfcActor to remove.
- :type actor: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param actor: The IfcActor to remove.
+ :type actor: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Setup an organisation with a single role
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation)
- ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"})
+ # Setup an organisation with a single role
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation)
+ ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"})
- # Assign that organisation to a newly created actor
- actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
+ # Assign that organisation to a newly created actor
+ actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
- # Actually we need ballpens on this project
- ifcopenshell.api.run("owner.remove_actor", model, actor=actor)
- """
- self.file = file
- self.settings = {"actor": actor}
+ # Actually we need ballpens on this project
+ ifcopenshell.api.run("owner.remove_actor", model, actor=actor)
+ """
+ settings = {"actor": actor}
- def execute(self):
- history = self.settings["actor"].OwnerHistory
- self.file.remove(self.settings["actor"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ history = settings["actor"].OwnerHistory
+ file.remove(settings["actor"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py
index 728ffb45f5..5fba84583b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py
@@ -17,35 +17,32 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, address=None):
- """Removes an address
+def remove_address(file, address=None) -> None:
+ """Removes an address
- Naturally, any organisations or people using that address will have the
- relationship removed.
+ Naturally, any organisations or people using that address will have the
+ relationship removed.
- :param address: The IfcAddress to remove.
- :type address: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param address: The IfcAddress to remove.
+ :type address: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model)
- address = ifcopenshell.api.run("owner.add_address", model,
- assigned_object=organisation, ifc_class="IfcPostalAddress")
+ organisation = ifcopenshell.api.run("owner.add_organisation", model)
+ address = ifcopenshell.api.run("owner.add_address", model,
+ assigned_object=organisation, ifc_class="IfcPostalAddress")
- # Change our mind and delete it
- ifcopenshell.api.run("owner.remove_address", model, address=address)
- """
- self.file = file
- self.settings = {"address": address}
+ # Change our mind and delete it
+ ifcopenshell.api.run("owner.remove_address", model, address=address)
+ """
+ settings = {"address": address}
- def execute(self):
- for inverse in self.file.get_inverse(self.settings["address"]):
- if inverse.is_a() in ("IfcOrganization", "IfcPerson"):
- if inverse.Addresses == (self.settings["address"],):
- inverse.Addresses = None
- self.file.remove(self.settings["address"])
+ for inverse in file.get_inverse(settings["address"]):
+ if inverse.is_a() in ("IfcOrganization", "IfcPerson"):
+ if inverse.Addresses == (settings["address"],):
+ inverse.Addresses = None
+ file.remove(settings["address"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py
index 7e21c07943..16973cfcc4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py
@@ -17,27 +17,24 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, application=None):
- """Removes an application
+def remove_application(file, application=None) -> None:
+ """Removes an application
- Warning: removing an application may invalidate ownership histories.
- Check whether or not the application is used anywhere prior to removal.
+ Warning: removing an application may invalidate ownership histories.
+ Check whether or not the application is used anywhere prior to removal.
- :param address: The IfcApplication to remove.
- :type address: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param address: The IfcApplication to remove.
+ :type address: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- application = ifcopenshell.api.run("owner.add_application", model)
- ifcopenshell.api.run("owner.remove_address", model, application=application)
- """
- self.file = file
- self.settings = {"application": application}
+ application = ifcopenshell.api.run("owner.add_application", model)
+ ifcopenshell.api.run("owner.remove_address", model, application=application)
+ """
+ settings = {"application": application}
- def execute(self):
- self.file.remove(self.settings["application"])
+ file.remove(settings["application"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py
index e9e2c77e9e..42111e017e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py
@@ -19,53 +19,50 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, organisation=None):
- """Remove an organisation
+def remove_organisation(file, organisation=None) -> None:
+ """Remove an organisation
- All roles and addresses assigned to the organisation will also be
- removed.
+ All roles and addresses assigned to the organisation will also be
+ removed.
- :param organisation: The IfcOrganization to remove
- :type organisation: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param organisation: The IfcOrganization to remove
+ :type organisation: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- ifcopenshell.api.run("owner.remove_organisation", model, organisation=organisation)
- """
- self.file = file
- self.settings = {"organisation": organisation}
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ ifcopenshell.api.run("owner.remove_organisation", model, organisation=organisation)
+ """
+ settings = {"organisation": organisation}
- def execute(self):
- for role in self.settings["organisation"].Roles or []:
- if len(self.file.get_inverse(role)) == 1:
- ifcopenshell.api.run("owner.remove_role", self.file, role=role)
- for address in self.settings["organisation"].Addresses or []:
- if len(self.file.get_inverse(address)) == 1:
- ifcopenshell.api.run("owner.remove_address", self.file, address=address)
- for inverse in self.file.get_inverse(self.settings["organisation"]):
- if inverse.is_a("IfcOrganizationRelationship"):
- if inverse.RelatingOrganization == self.settings["organisation"]:
- self.file.remove(inverse)
- elif inverse.RelatedOrganizations == (self.settings["organisation"],):
- self.file.remove(inverse)
- elif inverse.is_a("IfcDocumentInformation"):
- if inverse.Editors == (self.settings["organisation"],):
- inverse.Editors = None
- elif inverse.is_a("IfcPersonAndOrganization"):
- ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=inverse)
- elif inverse.is_a("IfcActor"):
- ifcopenshell.api.run("root.remove_product", self.file, product=inverse)
- elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"):
- if inverse.RelatedResourceObjects == (self.settings["organisation"],):
- self.file.remove(inverse)
- elif inverse.is_a("IfcApplication"):
- ifcopenshell.api.run("owner.remove_application", self.file, application=inverse)
+ for role in settings["organisation"].Roles or []:
+ if len(file.get_inverse(role)) == 1:
+ ifcopenshell.api.run("owner.remove_role", file, role=role)
+ for address in settings["organisation"].Addresses or []:
+ if len(file.get_inverse(address)) == 1:
+ ifcopenshell.api.run("owner.remove_address", file, address=address)
+ for inverse in file.get_inverse(settings["organisation"]):
+ if inverse.is_a("IfcOrganizationRelationship"):
+ if inverse.RelatingOrganization == settings["organisation"]:
+ file.remove(inverse)
+ elif inverse.RelatedOrganizations == (settings["organisation"],):
+ file.remove(inverse)
+ elif inverse.is_a("IfcDocumentInformation"):
+ if inverse.Editors == (settings["organisation"],):
+ inverse.Editors = None
+ elif inverse.is_a("IfcPersonAndOrganization"):
+ ifcopenshell.api.run("owner.remove_person_and_organisation", file, person_and_organisation=inverse)
+ elif inverse.is_a("IfcActor"):
+ ifcopenshell.api.run("root.remove_product", file, product=inverse)
+ elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"):
+ if inverse.RelatedResourceObjects == (settings["organisation"],):
+ file.remove(inverse)
+ elif inverse.is_a("IfcApplication"):
+ ifcopenshell.api.run("owner.remove_application", file, application=inverse)
- self.file.remove(self.settings["organisation"])
+ file.remove(settings["organisation"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py
index 8e1ba7a972..aac583b22e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py
@@ -19,51 +19,48 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, person=None):
- """Remove an person
+def remove_person(file, person=None) -> None:
+ """Remove an person
- All roles and addresses assigned to the person will also be
- removed.
+ All roles and addresses assigned to the person will also be
+ removed.
- :param person: The IfcPerson to remove
- :type person: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param person: The IfcPerson to remove
+ :type person: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("owner.add_person", model,
- identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
- ifcopenshell.api.run("owner.remove_person", model, person=person)
- """
- self.file = file
- self.settings = {"person": person}
+ ifcopenshell.api.run("owner.add_person", model,
+ identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
+ ifcopenshell.api.run("owner.remove_person", model, person=person)
+ """
+ settings = {"person": person}
- def execute(self):
- for role in self.settings["person"].Roles or []:
- if len(self.file.get_inverse(role)) == 1:
- ifcopenshell.api.run("owner.remove_role", self.file, role=role)
- for address in self.settings["person"].Addresses or []:
- if len(self.file.get_inverse(address)) == 1:
- ifcopenshell.api.run("owner.remove_address", self.file, address=address)
- for inverse in self.file.get_inverse(self.settings["person"]):
- if inverse.is_a("IfcWorkControl"):
- if inverse.Creators == (self.settings["person"],):
- inverse.Creators = None
- elif inverse.is_a("IfcInventory"):
- if inverse.ResponsiblePersons == (self.settings["person"],):
- inverse.ResponsiblePersons = None
- elif inverse.is_a("IfcDocumentInformation"):
- if inverse.Editors == (self.settings["person"],):
- inverse.Editors = None
- elif inverse.is_a("IfcPersonAndOrganization"):
- ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=inverse)
- elif inverse.is_a("IfcActor"):
- ifcopenshell.api.run("root.remove_product", self.file, product=inverse)
- elif inverse.is_a("IfcResourceLevelRelationship"):
- if inverse.RelatedResourceObjects == (self.settings["person"],):
- self.file.remove(inverse)
- self.file.remove(self.settings["person"])
+ for role in settings["person"].Roles or []:
+ if len(file.get_inverse(role)) == 1:
+ ifcopenshell.api.run("owner.remove_role", file, role=role)
+ for address in settings["person"].Addresses or []:
+ if len(file.get_inverse(address)) == 1:
+ ifcopenshell.api.run("owner.remove_address", file, address=address)
+ for inverse in file.get_inverse(settings["person"]):
+ if inverse.is_a("IfcWorkControl"):
+ if inverse.Creators == (settings["person"],):
+ inverse.Creators = None
+ elif inverse.is_a("IfcInventory"):
+ if inverse.ResponsiblePersons == (settings["person"],):
+ inverse.ResponsiblePersons = None
+ elif inverse.is_a("IfcDocumentInformation"):
+ if inverse.Editors == (settings["person"],):
+ inverse.Editors = None
+ elif inverse.is_a("IfcPersonAndOrganization"):
+ ifcopenshell.api.run("owner.remove_person_and_organisation", file, person_and_organisation=inverse)
+ elif inverse.is_a("IfcActor"):
+ ifcopenshell.api.run("root.remove_product", file, product=inverse)
+ elif inverse.is_a("IfcResourceLevelRelationship"):
+ if inverse.RelatedResourceObjects == (settings["person"],):
+ file.remove(inverse)
+ file.remove(settings["person"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py
index fc85722e68..8473e04b87 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py
@@ -19,45 +19,42 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, person_and_organisation=None):
- """Removes a person and organisation
+def remove_person_and_organisation(file, person_and_organisation=None) -> None:
+ """Removes a person and organisation
- Note that the underlying person and organisation is not removed, only
- the "person and organisation" group.
+ Note that the underlying person and organisation is not removed, only
+ the "person and organisation" group.
- :param person_and_organisation: The IfcPersonAndOrganization to remove.
- :type person_and_organisation: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param person_and_organisation: The IfcPersonAndOrganization to remove.
+ :type person_and_organisation: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- person = ifcopenshell.api.run("owner.add_person", model,
- identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le")
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
+ person = ifcopenshell.api.run("owner.add_person", model,
+ identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le")
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
- user = ifcopenshell.api.run("owner.add_person_and_organisation", model,
- person=person, organisation=organisation)
+ user = ifcopenshell.api.run("owner.add_person_and_organisation", model,
+ person=person, organisation=organisation)
- ifcopenshell.api.run("owner.remove_person_and_organisation", model, person_and_organisation=user)
- """
- self.file = file
- self.settings = {"person_and_organisation": person_and_organisation}
+ ifcopenshell.api.run("owner.remove_person_and_organisation", model, person_and_organisation=user)
+ """
+ settings = {"person_and_organisation": person_and_organisation}
- def execute(self):
- for inverse in self.file.get_inverse(self.settings["person_and_organisation"]):
- if inverse.is_a("IfcDocumentInformation"):
- if inverse.Editors == (self.settings["person_and_organisation"],):
- inverse.Editors = None
- elif inverse.is_a("IfcActor"):
- ifcopenshell.api.run("root.remove_product", self.file, product=inverse)
- elif inverse.is_a("IfcResourceLevelRelationship"):
- if inverse.RelatedResourceObjects == (self.settings["person_and_organisation"],):
- self.file.remove(inverse)
- elif inverse.is_a("IfcOwnerHistory"):
- self.file.remove(inverse)
- self.file.remove(self.settings["person_and_organisation"])
+ for inverse in file.get_inverse(settings["person_and_organisation"]):
+ if inverse.is_a("IfcDocumentInformation"):
+ if inverse.Editors == (settings["person_and_organisation"],):
+ inverse.Editors = None
+ elif inverse.is_a("IfcActor"):
+ ifcopenshell.api.run("root.remove_product", file, product=inverse)
+ elif inverse.is_a("IfcResourceLevelRelationship"):
+ if inverse.RelatedResourceObjects == (settings["person_and_organisation"],):
+ file.remove(inverse)
+ elif inverse.is_a("IfcOwnerHistory"):
+ file.remove(inverse)
+ file.remove(settings["person_and_organisation"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py
index 5de9915c33..08bf39881c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py
@@ -17,38 +17,35 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, role=None):
- """Removes a role
+def remove_role(file, role=None) -> None:
+ """Removes a role
- People and organisations using the role will be untouched. This may
- leave some of them without roles.
+ People and organisations using the role will be untouched. This may
+ leave some of them without roles.
- :param role: The IfcActorRole to remove.
- :type role: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param role: The IfcActorRole to remove.
+ :type role: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="AWB", name="Architects Without Ballpens")
- role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT")
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="AWB", name="Architects Without Ballpens")
+ role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT")
- # After running this, the organisation will have no role again
- ifcopenshell.api.run("owner.remove_role", model, role=role)
- """
- self.file = file
- self.settings = {"role": role}
+ # After running this, the organisation will have no role again
+ ifcopenshell.api.run("owner.remove_role", model, role=role)
+ """
+ settings = {"role": role}
- def execute(self):
- for inverse in self.file.get_inverse(self.settings["role"]):
- if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"):
- if inverse.Roles == (self.settings["role"],):
- inverse.Roles = None
- elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"):
- if inverse.RelatedResourceObjects == (self.settings["organisation"],):
- self.file.remove(inverse)
- self.file.remove(self.settings["role"])
+ for inverse in file.get_inverse(settings["role"]):
+ if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"):
+ if inverse.Roles == (settings["role"],):
+ inverse.Roles = None
+ elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"):
+ if inverse.RelatedResourceObjects == (settings["organisation"],):
+ file.remove(inverse)
+ file.remove(settings["role"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py
index 711732bcdc..c57d0172f5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py
@@ -21,58 +21,55 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_actor=None, related_object=None):
- """Unassigns an actor to an object
+def unassign_actor(file, relating_actor=None, related_object=None) -> None:
+ """Unassigns an actor to an object
- This means that the actor is no longer responsible for the object.
+ This means that the actor is no longer responsible for the object.
- :param relating_actor: The IfcActor who is responsible for the object.
- :type relating_actor: ifcopenshell.entity_instance
- :param related_object: The object the actor is responsible for.
- :type related_object: ifcopenshell.entity_instance
- :return: The updated IfcRelAssignsToActor relationship or none if there
- is no more valid relationship.
- :rtype: None, ifcopenshell.entity_instance
+ :param relating_actor: The IfcActor who is responsible for the object.
+ :type relating_actor: ifcopenshell.entity_instance
+ :param related_object: The object the actor is responsible for.
+ :type related_object: ifcopenshell.entity_instance
+ :return: The updated IfcRelAssignsToActor relationship or none if there
+ is no more valid relationship.
+ :rtype: None, ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # We need to procure and install 2 of this particular pump type in our facility.
- pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType")
+ # We need to procure and install 2 of this particular pump type in our facility.
+ pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType")
- # Define who the manufacturer is
- manufacturer = ifcopenshell.api.run("owner.add_organisation", model,
- identification="PWP", name="Pumps With Power")
- ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER")
+ # Define who the manufacturer is
+ manufacturer = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="PWP", name="Pumps With Power")
+ ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER")
- # Make the manufacturer responsible for that pump type.
- ifcopenshell.api.run("owner.assign_actor", model,
- relating_actor=manufacturer, related_object=pump_type)
+ # Make the manufacturer responsible for that pump type.
+ ifcopenshell.api.run("owner.assign_actor", model,
+ relating_actor=manufacturer, related_object=pump_type)
- # Undo the assignment
- ifcopenshell.api.run("owner.unassign_actor", model,
- relating_actor=manufacturer, related_object=pump_type)
- """
- self.file = file
- self.settings = {
- "relating_actor": relating_actor,
- "related_object": related_object,
- }
+ # Undo the assignment
+ ifcopenshell.api.run("owner.unassign_actor", model,
+ relating_actor=manufacturer, related_object=pump_type)
+ """
+ settings = {
+ "relating_actor": relating_actor,
+ "related_object": related_object,
+ }
- def execute(self):
- for rel in self.settings["related_object"].HasAssignments or []:
- if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != self.settings["relating_actor"]:
- continue
- if len(rel.RelatedObjects) == 1:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_objects = list(rel.RelatedObjects)
- related_objects.remove(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- return rel
+ for rel in settings["related_object"].HasAssignments or []:
+ if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != settings["relating_actor"]:
+ continue
+ if len(rel.RelatedObjects) == 1:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_objects = list(rel.RelatedObjects)
+ related_objects.remove(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py
index 797d1b8b57..95a356ef2f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py
@@ -24,71 +24,70 @@ import ifcopenshell.util.element
from typing import Union
-class Usecase:
- def __init__(self, file: ifcopenshell.file, element: ifcopenshell.entity_instance):
- """Updates the owner that is assigned to an object
+def update_owner_history(
+ file: ifcopenshell.file, element: ifcopenshell.entity_instance
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Updates the owner that is assigned to an object
- This ensures that the owner is tracked to have modified the object last,
- including the time when the change occured. See
- ifcopenshell.api.owner.create_owner_history for details.
+ This ensures that the owner is tracked to have modified the object last,
+ including the time when the change occured. See
+ ifcopenshell.api.owner.create_owner_history for details.
- :param element: The IfcRoot element to update the ownership details on
- when a change is made.
- :type element: ifcopenshell.entity_instance
- :return: The updated IfcOwnerHistory element.
- :rtype: ifcopenshell.entity_instance
+ :param element: The IfcRoot element to update the ownership details on
+ when a change is made.
+ :type element: ifcopenshell.entity_instance
+ :return: The updated IfcOwnerHistory element.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # See ifcopenshell.api.owner.create_owner_history for setup
- # [ ... example setup code ... ]
+ # See ifcopenshell.api.owner.create_owner_history for setup
+ # [ ... example setup code ... ]
- # We've finished our ownership setup. Now let's start our script and
- # create a space. Notice we don't actually call
- # create_owner_history at all. This is already automatically handled
- # by the API when necessary. Under the hood, the API is actually
- # running this code on the IfcSpace element:
- # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model)
- space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace")
+ # We've finished our ownership setup. Now let's start our script and
+ # create a space. Notice we don't actually call
+ # create_owner_history at all. This is already automatically handled
+ # by the API when necessary. Under the hood, the API is actually
+ # running this code on the IfcSpace element:
+ # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model)
+ space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace")
- # Any edits we make will have ownership tracking automatically
- # applied. There is no need to run any owner.update_owner_history
- # API calls either.
- ifcopenshell.api.run("attribute.edit_attributes", model, product=space, attributes={"Name": "Lobby"})
- """
- self.file = file
- self.settings = {"element": element}
+ # Any edits we make will have ownership tracking automatically
+ # applied. There is no need to run any owner.update_owner_history
+ # API calls either.
+ ifcopenshell.api.run("attribute.edit_attributes", model, product=space, attributes={"Name": "Lobby"})
+ """
+ settings = {"element": element}
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- element = self.settings["element"]
- if not element.is_a("IfcRoot"):
- return
- user = ifcopenshell.api.owner.settings.get_user(self.file)
- if not user:
- return
- application = ifcopenshell.api.owner.settings.get_application(self.file)
- if not application:
- return
+ element = settings["element"]
+ if not element.is_a("IfcRoot"):
+ return
+ user = ifcopenshell.api.owner.settings.get_user(file)
+ if not user:
+ return
+ application = ifcopenshell.api.owner.settings.get_application(file)
+ if not application:
+ return
- # 1 IfcRoot IfcOwnerHistory
- owner_history = element[1]
- if not owner_history:
- owner_history = ifcopenshell.api.run("owner.create_owner_history", self.file)
- element[1] = owner_history
- return owner_history
-
- if self.file.get_total_inverses(owner_history) > 1:
- owner_history = ifcopenshell.util.element.copy(self.file, owner_history)
- element[1] = owner_history
-
- # 3 IfcOwnerHistory ChangeAction
- owner_history[3] = "MODIFIED"
- # 4 IfcOwnerHistory LastModifiedDate
- owner_history[4] = int(time.time())
- # 5 IfcOwnerHistory LastModifyingUser
- owner_history[5] = user
- # 6 IfcOwnerHistory LastModifyingApplication
- owner_history[6] = application
+ # 1 IfcRoot IfcOwnerHistory
+ owner_history = element[1]
+ if not owner_history:
+ owner_history = ifcopenshell.api.run("owner.create_owner_history", file)
+ element[1] = owner_history
return owner_history
+
+ if file.get_total_inverses(owner_history) > 1:
+ owner_history = ifcopenshell.util.element.copy(file, owner_history)
+ element[1] = owner_history
+
+ # 3 IfcOwnerHistory ChangeAction
+ owner_history[3] = "MODIFIED"
+ # 4 IfcOwnerHistory LastModifiedDate
+ owner_history[4] = int(time.time())
+ # 5 IfcOwnerHistory LastModifyingUser
+ owner_history[5] = user
+ # 6 IfcOwnerHistory LastModifyingApplication
+ owner_history[6] = application
+ return owner_history
diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py
index e0caddbe3c..7decc6750b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py
@@ -15,3 +15,9 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_arbitrary_profile import add_arbitrary_profile
+from .add_arbitrary_profile_with_voids import add_arbitrary_profile_with_voids
+from .add_parameterized_profile import add_parameterized_profile
+from .edit_profile import edit_profile
+from .remove_profile import remove_profile
diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py
index 8cced9d934..9acc800b89 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py
@@ -19,39 +19,42 @@
import ifcopenshell.util.unit
+def add_arbitrary_profile(file, profile=None, name=None) -> None:
+ """Adds a new arbitrary polyline-based profile
+
+ The profile is represented as a polyline defined by a list of
+ coordinates. Only straight segments are allowed. Coordinates must be
+ provided in SI meters.
+
+ To represent a closed curve, the first and last coordinate must be
+ identical.
+
+ :param profile: A list of coordinates
+ :type profile: list[list[float]]
+ :param name: If the profile is semantically significant (i.e. to be
+ managed and reused by the user) then it must be named. Otherwise,
+ this may be left as none.
+ :type name: str, optional
+ :return: The newly created IfcArbitraryClosedProfileDef
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # A 10mm by 100mm rectangle, such that might be used as a wooden
+ # skirting board or kick plate.
+ square = ifcopenshell.api.run("profile.add_arbitrary_profile", model,
+ profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)],
+ name="SK01 Profile")
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"profile": profile, "name": name}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, profile=None, name=None):
- """Adds a new arbitrary polyline-based profile
-
- The profile is represented as a polyline defined by a list of
- coordinates. Only straight segments are allowed. Coordinates must be
- provided in SI meters.
-
- To represent a closed curve, the first and last coordinate must be
- identical.
-
- :param profile: A list of coordinates
- :type profile: list[list[float]]
- :param name: If the profile is semantically significant (i.e. to be
- managed and reused by the user) then it must be named. Otherwise,
- this may be left as none.
- :type name: str, optional
- :return: The newly created IfcArbitraryClosedProfileDef
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # A 10mm by 100mm rectangle, such that might be used as a wooden
- # skirting board or kick plate.
- square = ifcopenshell.api.run("profile.add_arbitrary_profile", model,
- profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)],
- name="SK01 Profile")
- """
- self.file = file
- self.settings = {"profile": profile, "name": name}
-
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
points = [self.convert_si_to_unit(p) for p in self.settings["profile"]]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py
index 7c1af83294..e1a6fe9cdb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py
@@ -19,46 +19,49 @@
import ifcopenshell.util.unit
+def add_arbitrary_profile_with_voids(file, outer_profile=None, inner_profiles=None, name=None) -> None:
+ """Adds a new arbitrary polyline-based profile with voids
+
+ The outer profile is represented as a polyline defined by a list of
+ coordinates. Only straight segments are allowed. Coordinates must be
+ provided in SI meters.
+
+ To represent a closed curve, the first and last coordinate must be
+ identical.
+
+ The inner profiles are represented as a list of polylines.
+ Every polyline in defined by a list of coordinates.
+ Only straight segments are allowed. Coordinates must be
+ provided in SI meters.
+
+ :param outer_profile: A list of coordinates
+ :type profile: list[float]
+ :param inner_profiles: A list of polylines
+ :type profile: list[list[float]]
+ :param name: If the profile is semantically significant (i.e. to be
+ managed and reused by the user) then it must be named. Otherwise,
+ this may be left as none.
+ :type name: str, optional
+ :return: The newly created IfcArbitraryProfileDefWithVoids
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # A 400mm by 400mm square with a 200mm by 200mm hole in it.
+ square_with_hole = ifcopenshell.api.run("profile.add_arbitrary_profile_with_voids", model,
+ outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)],
+ inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]],
+ name="SK01 Hole Profile")
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, outer_profile=None, inner_profiles=None, name=None):
- """Adds a new arbitrary polyline-based profile with voids
-
- The outer profile is represented as a polyline defined by a list of
- coordinates. Only straight segments are allowed. Coordinates must be
- provided in SI meters.
-
- To represent a closed curve, the first and last coordinate must be
- identical.
-
- The inner profiles are represented as a list of polylines.
- Every polyline in defined by a list of coordinates.
- Only straight segments are allowed. Coordinates must be
- provided in SI meters.
-
- :param outer_profile: A list of coordinates
- :type profile: list[float]
- :param inner_profiles: A list of polylines
- :type profile: list[list[float]]
- :param name: If the profile is semantically significant (i.e. to be
- managed and reused by the user) then it must be named. Otherwise,
- this may be left as none.
- :type name: str, optional
- :return: The newly created IfcArbitraryProfileDefWithVoids
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # A 400mm by 400mm square with a 200mm by 200mm hole in it.
- square_with_hole = ifcopenshell.api.run("profile.add_arbitrary_profile_with_voids", model,
- outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)],
- inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]],
- name="SK01 Hole Profile")
- """
- self.file = file
- self.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name}
-
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
outer_points = [self.convert_si_to_unit(p) for p in self.settings["outer_profile"]]
@@ -69,7 +72,9 @@ class Usecase:
outer_curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in outer_points])
inner_curves = []
for inner_point in inner_points:
- inner_curves.append(self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point]))
+ inner_curves.append(
+ self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point])
+ )
else:
outer_curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(outer_points))
inner_curves = []
diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py
index 0201095feb..c0a6348656 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, ifc_class=None):
- """Adds a new parameterised profile
+def add_parameterized_profile(file, ifc_class=None) -> None:
+ """Adds a new parameterised profile
- IFC offers parameterised profiles for common standardised hot roll
- steel sections and common concrete forms. A full list is available on
- the IFC documentation as subclasses of IfcParameterizedProfileDef.
+ IFC offers parameterised profiles for common standardised hot roll
+ steel sections and common concrete forms. A full list is available on
+ the IFC documentation as subclasses of IfcParameterizedProfileDef.
- Currently, this API has no benefit over directly calling
- ifcopenshell.file.create_entity.
+ Currently, this API has no benefit over directly calling
+ ifcopenshell.file.create_entity.
- :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd
- like to create.
- :type ifc_class: str
- :return: The newly created element depending on the specified ifc_class.
- :rtype: ifcopenshell.entity_instance
+ :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd
+ like to create.
+ :type ifc_class: str
+ :return: The newly created element depending on the specified ifc_class.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- circle = ifcopenshell.api.run("profile.add_parameterized_profile", model,
- ifc_class="IfcCircleProfileDef")
- circle.Radius = 1.
- """
- self.file = file
- self.settings = {"ifc_class": ifc_class}
+ circle = ifcopenshell.api.run("profile.add_parameterized_profile", model,
+ ifc_class="IfcCircleProfileDef")
+ circle.Radius = 1.
+ """
+ settings = {"ifc_class": ifc_class}
- def execute(self):
- return self.file.create_entity(self.settings["ifc_class"])
+ return file.create_entity(settings["ifc_class"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py
index 759a5d4c7d..4d525a5d32 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py
@@ -17,34 +17,31 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, profile=None, attributes=None):
- """Edits the attributes of an IfcProfileDef
+def edit_profile(file, profile=None, attributes=None) -> None:
+ """Edits the attributes of an IfcProfileDef
- For more information about the attributes and data types of an
- IfcProfileDef, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcProfileDef, consult the IFC documentation.
- :param profile: The IfcProfileDef entity you want to edit
- :type profile: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param profile: The IfcProfileDef entity you want to edit
+ :type profile: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- circle = ifcopenshell.api.run("profile.add_parameterized_profile", model,
- ifc_class="IfcCircleProfileDef")
- circle = 1.
+ circle = ifcopenshell.api.run("profile.add_parameterized_profile", model,
+ ifc_class="IfcCircleProfileDef")
+ circle = 1.
- ifcopenshell.api.run("profile.edit_profile", model,
- profile=circle, attributes={"ProfileName": "1000mm Dia"})
- """
- self.file = file
- self.settings = {"profile": profile, "attributes": attributes or {}}
+ ifcopenshell.api.run("profile.edit_profile", model,
+ profile=circle, attributes={"ProfileName": "1000mm Dia"})
+ """
+ settings = {"profile": profile, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["profile"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["profile"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py
index 47d3391e00..58f3eebedb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py
@@ -20,32 +20,29 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, profile=None):
- """Removes a profile
+def remove_profile(file, profile=None) -> None:
+ """Removes a profile
- :param profile: The IfcProfileDef to remove.
- :type profile: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param profile: The IfcProfileDef to remove.
+ :type profile: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- circle = ifcopenshell.api.run("profile.add_parameterized_profile", model,
- ifc_class="IfcCircleProfileDef")
- circle = 1.
- ifcopenshell.api.run("profile.remove_profile", model, profile=circle)
- """
- self.file = file
- self.settings = {"profile": profile}
+ circle = ifcopenshell.api.run("profile.add_parameterized_profile", model,
+ ifc_class="IfcCircleProfileDef")
+ circle = 1.
+ ifcopenshell.api.run("profile.remove_profile", model, profile=circle)
+ """
+ settings = {"profile": profile}
- def execute(self):
- subelements = set()
- for attribute in self.settings["profile"]:
- if isinstance(attribute, ifcopenshell.entity_instance):
- subelements.add(attribute)
- self.file.remove(self.settings["profile"])
- for subelement in subelements:
- ifcopenshell.util.element.remove_deep2(self.file, subelement)
+ subelements = set()
+ for attribute in settings["profile"]:
+ if isinstance(attribute, ifcopenshell.entity_instance):
+ subelements.add(attribute)
+ file.remove(settings["profile"])
+ for subelement in subelements:
+ ifcopenshell.util.element.remove_deep2(file, subelement)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py
index e0caddbe3c..e9c21fbddd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py
@@ -15,3 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .append_asset import append_asset
+from .assign_declaration import assign_declaration
+from .create_file import create_file
+from .unassign_declaration import unassign_declaration
diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py
index 3e88c7c82a..fc101347ee 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py
@@ -21,100 +21,103 @@ import ifcopenshell.api
import ifcopenshell.api.owner.settings
+def append_asset(file, library=None, element=None, reuse_identities=None) -> None:
+ """Appends an asset from a library into the active project
+
+ A BIM library asset may be a type product (e.g. wall type), product
+ (e.g. pump), material, profile, or cost schedule.
+
+ This copies the asset from the specified library file into the active
+ project. It handles all details like ensuring that product materials,
+ styles, properties, quantities, and so on are preserved.
+
+ If an asset contains geometry, the geometric contexts are also
+ intelligentely transplanted such that existing equivalent contexts are
+ reused.
+
+ Do not mix units.
+
+ :param library: The file object containing the asset.
+ :type library: ifcopenshell.file
+ :param element: An element in the library file of the asset. It may be
+ an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or
+ IfcProfileDef.
+ :type element: ifcopenshell.entity_instance
+ :param reuse_identities: Optional dictionary of mapped entities' identities to the
+ already created elements. It will be used to avoid creating
+ duplicated inverse elements during multiple `project.append_asset` calls. If you want
+ to add just 1 asset or if added assets won't have any shared elements, then it can be left empty.
+ :type reuse_identities: dict[int, ifcopenshell.entity_instance]
+ :return: The appended element
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # Programmatically generate a library. You could do this visually too.
+ library = ifcopenshell.api.run("project.create_file")
+ root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library")
+ context = ifcopenshell.api.run("root.create_entity", library,
+ ifc_class="IfcProjectLibrary", name="Demo Library")
+ ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root)
+
+ # Assign units for our example library
+ unit = ifcopenshell.api.run("unit.add_si_unit", library,
+ unit_type="LENGTHUNIT", name="METRE", prefix="MILLI")
+ ifcopenshell.api.run("unit.assign_unit", library, units=[unit])
+
+ # Let's create a single asset of a 200mm thick concrete wall
+ wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01")
+ concrete = ifcopenshell.api.run("material.add_material", usecase.file, name="CON", category="concrete")
+ rel = ifcopenshell.api.run("material.assign_material", library,
+ products=[wall_type], type="IfcMaterialLayerSet")
+ layer = ifcopenshell.api.run("material.add_layer", library,
+ layer_set=rel.RelatingMaterial, material=concrete)
+ layer.Name = "Structure"
+ layer.LayerThickness = 200
+
+ # Mark our wall type as a reusable asset in our library.
+ ifcopenshell.api.run("project.assign_declaration", library,
+ definitions=[wall_type], relating_context=context)
+
+ # Let's imagine we're starting a new project
+ model = ifcopenshell.api.run("project.create_file")
+ project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test")
+
+ # Now we can easily append our wall type from our libary
+ wall_type = ifcopenshell.api.run("project.append_asset", model, library=library, element=wall_type)
+
+ Example of adding multiple assets and avoiding duplicated inverses:
+
+ .. code:: python
+
+ # since occurrences of IfcWindow of the same type
+ # might have shared inverses (e.g. IfcStyledItem)
+ # we provide a dictionary that will be populated with newly created items
+ # and reused to avoid duplicated elements
+ reuse_identities = dict()
+
+ for element in ifcopenshell.util.selector.filter_elements(model, "IfcWindow"):
+ ifcopenshell.api.run(
+ "project.append_asset",
+ model, library=library,
+ element=wall_type
+ reuse_identities=reuse_identities
+ )
+
+ """
+ usecase = Usecase()
+ usecase.file: ifcopenshell.file = file
+ usecase.settings = {
+ "library": library,
+ "element": element,
+ "reuse_identities": {} if reuse_identities is None else reuse_identities,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, library=None, element=None, reuse_identities=None):
- """Appends an asset from a library into the active project
-
- A BIM library asset may be a type product (e.g. wall type), product
- (e.g. pump), material, profile, or cost schedule.
-
- This copies the asset from the specified library file into the active
- project. It handles all details like ensuring that product materials,
- styles, properties, quantities, and so on are preserved.
-
- If an asset contains geometry, the geometric contexts are also
- intelligentely transplanted such that existing equivalent contexts are
- reused.
-
- Do not mix units.
-
- :param library: The file object containing the asset.
- :type library: ifcopenshell.file
- :param element: An element in the library file of the asset. It may be
- an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or
- IfcProfileDef.
- :type element: ifcopenshell.entity_instance
- :param reuse_identities: Optional dictionary of mapped entities' identities to the
- already created elements. It will be used to avoid creating
- duplicated inverse elements during multiple `project.append_asset` calls. If you want
- to add just 1 asset or if added assets won't have any shared elements, then it can be left empty.
- :type reuse_identities: dict[int, ifcopenshell.entity_instance]
- :return: The appended element
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # Programmatically generate a library. You could do this visually too.
- library = ifcopenshell.api.run("project.create_file")
- root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library")
- context = ifcopenshell.api.run("root.create_entity", library,
- ifc_class="IfcProjectLibrary", name="Demo Library")
- ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root)
-
- # Assign units for our example library
- unit = ifcopenshell.api.run("unit.add_si_unit", library,
- unit_type="LENGTHUNIT", name="METRE", prefix="MILLI")
- ifcopenshell.api.run("unit.assign_unit", library, units=[unit])
-
- # Let's create a single asset of a 200mm thick concrete wall
- wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01")
- concrete = ifcopenshell.api.run("material.add_material", self.file, name="CON", category="concrete")
- rel = ifcopenshell.api.run("material.assign_material", library,
- products=[wall_type], type="IfcMaterialLayerSet")
- layer = ifcopenshell.api.run("material.add_layer", library,
- layer_set=rel.RelatingMaterial, material=concrete)
- layer.Name = "Structure"
- layer.LayerThickness = 200
-
- # Mark our wall type as a reusable asset in our library.
- ifcopenshell.api.run("project.assign_declaration", library,
- definitions=[wall_type], relating_context=context)
-
- # Let's imagine we're starting a new project
- model = ifcopenshell.api.run("project.create_file")
- project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test")
-
- # Now we can easily append our wall type from our libary
- wall_type = ifcopenshell.api.run("project.append_asset", model, library=library, element=wall_type)
-
- Example of adding multiple assets and avoiding duplicated inverses:
-
- .. code:: python
-
- # since occurrences of IfcWindow of the same type
- # might have shared inverses (e.g. IfcStyledItem)
- # we provide a dictionary that will be populated with newly created items
- # and reused to avoid duplicated elements
- reuse_identities = dict()
-
- for element in ifcopenshell.util.selector.filter_elements(model, "IfcWindow"):
- ifcopenshell.api.run(
- "project.append_asset",
- model, library=library,
- element=wall_type
- reuse_identities=reuse_identities
- )
-
- """
- self.file: ifcopenshell.file = file
- self.settings = {
- "library": library,
- "element": element,
- "reuse_identities": {} if reuse_identities is None else reuse_identities,
- }
-
def execute(self):
# mapping of old element ids to new elements
self.added_elements: dict[int, ifcopenshell.entity_instance] = {}
diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py
index e6e919b77b..776a26b8f5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py
@@ -22,129 +22,125 @@ import ifcopenshell.util.element
from typing import Union
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.entity_instance,
- definitions: list[ifcopenshell.entity_instance],
- relating_context: ifcopenshell.entity_instance,
- ):
- """Declares the list of elements to the project
+def assign_declaration(
+ file: ifcopenshell.entity_instance,
+ definitions: list[ifcopenshell.entity_instance],
+ relating_context: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Declares the list of elements to the project
- All data in a model must be directly or indirectly related to the
- project. Most data is indirectly related, existing instead within the
- spatial decomposition tree. Other data, such as types, may be declared
- at the top level.
+ All data in a model must be directly or indirectly related to the
+ project. Most data is indirectly related, existing instead within the
+ spatial decomposition tree. Other data, such as types, may be declared
+ at the top level.
- Most of the time, the API handles declaration automatically for you.
- There is one scenario where you might want to explicitly declare objects
- to the project, and that's when you want to organise objects into
- project libraries for future use (such as an assets library). Assigning
- a declaration lets you say that an object belongs to a library.
+ Most of the time, the API handles declaration automatically for you.
+ There is one scenario where you might want to explicitly declare objects
+ to the project, and that's when you want to organise objects into
+ project libraries for future use (such as an assets library). Assigning
+ a declaration lets you say that an object belongs to a library.
- :param definitions: The list of objects you want to declare. Typically a list of assets.
- :type definitions: list[ifcopenshell.entity_instance]
- :param relating_context: The IfcProject, or more commonly the
- IfcProjectLibrary that you want the object to be part of.
- :type relating_context: ifcopenshell.entity_instance
- :return: The new IfcRelDeclares relationship or None if all definitions
- were already declared / do not support declaration.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param definitions: The list of objects you want to declare. Typically a list of assets.
+ :type definitions: list[ifcopenshell.entity_instance]
+ :param relating_context: The IfcProject, or more commonly the
+ IfcProjectLibrary that you want the object to be part of.
+ :type relating_context: ifcopenshell.entity_instance
+ :return: The new IfcRelDeclares relationship or None if all definitions
+ were already declared / do not support declaration.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Programmatically generate a library. You could do this visually too.
- library = ifcopenshell.api.run("project.create_file")
- root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library")
- context = ifcopenshell.api.run("root.create_entity", library,
- ifc_class="IfcProjectLibrary", name="Demo Library")
+ # Programmatically generate a library. You could do this visually too.
+ library = ifcopenshell.api.run("project.create_file")
+ root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library")
+ context = ifcopenshell.api.run("root.create_entity", library,
+ ifc_class="IfcProjectLibrary", name="Demo Library")
- # It's necessary to say our library is part of our project.
- ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root)
+ # It's necessary to say our library is part of our project.
+ ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root)
- # Assign units for our example library
- unit = ifcopenshell.api.run("unit.add_si_unit", library,
- unit_type="LENGTHUNIT", name="METRE", prefix="MILLI")
- ifcopenshell.api.run("unit.assign_unit", library, units=[unit])
+ # Assign units for our example library
+ unit = ifcopenshell.api.run("unit.add_si_unit", library,
+ unit_type="LENGTHUNIT", name="METRE", prefix="MILLI")
+ ifcopenshell.api.run("unit.assign_unit", library, units=[unit])
- # Let's create a single asset of a 200mm thick concrete wall
- wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01")
- concrete = ifcopenshell.api.run("material.add_material", self.file, name="CON", category="concrete")
- rel = ifcopenshell.api.run("material.assign_material", library,
- products=[wall_type], type="IfcMaterialLayerSet")
- layer = ifcopenshell.api.run("material.add_layer", library,
- layer_set=rel.RelatingMaterial, material=concrete)
- layer.Name = "Structure"
- layer.LayerThickness = 200
+ # Let's create a single asset of a 200mm thick concrete wall
+ wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01")
+ concrete = ifcopenshell.api.run("material.add_material", file, name="CON", category="concrete")
+ rel = ifcopenshell.api.run("material.assign_material", library,
+ products=[wall_type], type="IfcMaterialLayerSet")
+ layer = ifcopenshell.api.run("material.add_layer", library,
+ layer_set=rel.RelatingMaterial, material=concrete)
+ layer.Name = "Structure"
+ layer.LayerThickness = 200
- # Mark our wall type as a reusable asset in our library.
- ifcopenshell.api.run("project.assign_declaration", library,
- definitions=[wall_type], relating_context=context)
+ # Mark our wall type as a reusable asset in our library.
+ ifcopenshell.api.run("project.assign_declaration", library,
+ definitions=[wall_type], relating_context=context)
- # All done, just for fun let's save our asset library to disk for later use.
- library.write("/path/to/my-library.ifc")
- """
- self.file = file
- self.settings = {
- "definitions": definitions,
- "relating_context": relating_context,
- }
+ # All done, just for fun let's save our asset library to disk for later use.
+ library.write("/path/to/my-library.ifc")
+ """
+ settings = {
+ "definitions": definitions,
+ "relating_context": relating_context,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- relating_context = self.settings["relating_context"]
- all_declares = relating_context.Declares
- definitions = set(self.settings["definitions"])
+ relating_context = settings["relating_context"]
+ all_declares = relating_context.Declares
+ definitions = set(settings["definitions"])
- previous_declares_rels: set[ifcopenshell.entity_instance] = set()
- objects_without_contexts: list[ifcopenshell.entity_instance] = []
- objects_with_contexts: list[ifcopenshell.entity_instance] = []
+ previous_declares_rels: set[ifcopenshell.entity_instance] = set()
+ objects_without_contexts: list[ifcopenshell.entity_instance] = []
+ objects_with_contexts: list[ifcopenshell.entity_instance] = []
- # check if there is anything to change
- for definition in definitions:
- has_context = getattr(definition, "HasContext", None)
- if has_context is None:
- continue
+ # check if there is anything to change
+ for definition in definitions:
+ has_context = getattr(definition, "HasContext", None)
+ if has_context is None:
+ continue
- object_rel = next(iter(has_context), None)
- if object_rel is None:
- objects_without_contexts.append(definition)
- continue
+ object_rel = next(iter(has_context), None)
+ if object_rel is None:
+ objects_without_contexts.append(definition)
+ continue
- # either rel doesn't exist or product is part of different rel
- if object_rel not in all_declares:
- previous_declares_rels.add(object_rel)
- objects_with_contexts.append(definition)
+ # either rel doesn't exist or product is part of different rel
+ if object_rel not in all_declares:
+ previous_declares_rels.add(object_rel)
+ objects_with_contexts.append(definition)
- objects_to_change = objects_without_contexts + objects_with_contexts
- # nothing to change
- if not objects_to_change:
- return None
+ objects_to_change = objects_without_contexts + objects_with_contexts
+ # nothing to change
+ if not objects_to_change:
+ return None
- for has_context in previous_declares_rels:
- related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts
- if related_definitions:
- has_context.RelatedDefinitions = related_definitions
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": has_context})
- else:
- history = has_context.OwnerHistory
- self.file.remove(has_context)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
-
- declares = next(iter(all_declares), None)
- if declares:
- declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change))
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": declares})
+ for has_context in previous_declares_rels:
+ related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts
+ if related_definitions:
+ has_context.RelatedDefinitions = related_definitions
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": has_context})
else:
- declares = self.file.create_entity(
- "IfcRelDeclares",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedDefinitions": list(objects_to_change),
- "RelatingContext": relating_context,
- }
- )
- return declares
+ history = has_context.OwnerHistory
+ file.remove(has_context)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+
+ declares = next(iter(all_declares), None)
+ if declares:
+ declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change))
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": declares})
+ else:
+ declares = file.create_entity(
+ "IfcRelDeclares",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedDefinitions": list(objects_to_change),
+ "RelatingContext": relating_context,
+ }
+ )
+ return declares
diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py
index 1b3e644cbd..6db9e87c01 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py
@@ -20,52 +20,46 @@ import datetime
import ifcopenshell
-class Usecase:
- def __init__(self, version: str = "IFC4"):
- """Create a blank IFC model file object
+def create_file(version: str = "IFC4") -> ifcopenshell.file:
+ """Create a blank IFC model file object
- Create a new IFC file object based on the nominated schema version. The
- schema version you choose determines what type of IFC data you can store
- in this model. The file is blank and contains no entities.
+ Create a new IFC file object based on the nominated schema version. The
+ schema version you choose determines what type of IFC data you can store
+ in this model. The file is blank and contains no entities.
- It also sets up header data for STEP file serialisation, such as the
- current timestamp, IfcOpenShell as the preprocessor, and defaults to a
- DesignTransferView MVD.
+ It also sets up header data for STEP file serialisation, such as the
+ current timestamp, IfcOpenShell as the preprocessor, and defaults to a
+ DesignTransferView MVD.
- :param version: The schema version of the IFC file. Choose from
- "IFC2X3", "IFC4", or "IFC4X3". If you have loaded in a custom
- schema, you may specify that schema identifier here too.
- :type version: str, optional
- :return: The created IFC file object.
- :rtype: ifcopenshell.file
+ :param version: The schema version of the IFC file. Choose from
+ "IFC2X3", "IFC4", or "IFC4X3". If you have loaded in a custom
+ schema, you may specify that schema identifier here too.
+ :type version: str, optional
+ :return: The created IFC file object.
+ :rtype: ifcopenshell.file
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Start a new model.
- model = ifcopenshell.api.run("project.create_file")
+ # Start a new model.
+ model = ifcopenshell.api.run("project.create_file")
- # It's currently a blank model, so typically the first thing we do
- # is create a project in it.
- project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test")
+ # It's currently a blank model, so typically the first thing we do
+ # is create a project in it.
+ project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test")
- # ... and off we go!
- """
- self.settings = {"version": version}
+ # ... and off we go!
+ """
+ settings = {"version": version}
- def execute(self) -> ifcopenshell.file:
- self.file = ifcopenshell.file(schema=self.settings["version"])
- self.file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe
- self.file.wrapped_data.header.file_name.time_stamp = (
- datetime.datetime.utcnow()
- .replace(tzinfo=datetime.timezone.utc)
- .astimezone()
- .replace(microsecond=0)
- .isoformat()
- )
- self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
- self.file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version)
- self.file.wrapped_data.header.file_name.authorization = "Nobody"
- self.file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",)
- return self.file
+ file = ifcopenshell.file(schema=settings["version"])
+ file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe
+ file.wrapped_data.header.file_name.time_stamp = (
+ datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()
+ )
+ file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version)
+ file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version)
+ file.wrapped_data.header.file_name.authorization = "Nobody"
+ file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",)
+ return file
diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py
index 8f8e726570..38a0ae4e25 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py
@@ -21,59 +21,55 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- definitions: list[ifcopenshell.entity_instance],
- relating_context: ifcopenshell.entity_instance,
- ):
- """Unassigns a list of objects from a project or project library
+def unassign_declaration(
+ file: ifcopenshell.file,
+ definitions: list[ifcopenshell.entity_instance],
+ relating_context: ifcopenshell.entity_instance,
+) -> None:
+ """Unassigns a list of objects from a project or project library
- Typically used to remove an asset from a project library.
+ Typically used to remove an asset from a project library.
- :param definitions: The list of objects you want to undeclare.
- Typically a list of assets.
- :type definitions: list[ifcopenshell.entity_instance]
- :param relating_context: The IfcProject, or more commonly the
- IfcProjectLibrary that you want the object to no longer be part of.
- :type relating_context: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param definitions: The list of objects you want to undeclare.
+ Typically a list of assets.
+ :type definitions: list[ifcopenshell.entity_instance]
+ :param relating_context: The IfcProject, or more commonly the
+ IfcProjectLibrary that you want the object to no longer be part of.
+ :type relating_context: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Programmatically generate a library. You could do this visually too.
- library = ifcopenshell.api.run("project.create_file")
- root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library")
- context = ifcopenshell.api.run("root.create_entity", library,
- ifc_class="IfcProjectLibrary", name="Demo Library")
+ # Programmatically generate a library. You could do this visually too.
+ library = ifcopenshell.api.run("project.create_file")
+ root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library")
+ context = ifcopenshell.api.run("root.create_entity", library,
+ ifc_class="IfcProjectLibrary", name="Demo Library")
- # It's necessary to say our library is part of our project.
- ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root)
+ # It's necessary to say our library is part of our project.
+ ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root)
- # Remove the library from our project
- ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root)
- """
- self.file = file
- self.settings = {
- "definitions": definitions,
- "relating_context": relating_context,
- }
+ # Remove the library from our project
+ ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root)
+ """
+ settings = {
+ "definitions": definitions,
+ "relating_context": relating_context,
+ }
- def execute(self):
- definitions = set(self.settings["definitions"])
- rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))}
+ definitions = set(settings["definitions"])
+ rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))}
- for rel in rels:
- related_definitions = set(rel.RelatedDefinitions) - definitions
- if related_definitions:
- rel.RelatedDefinitions = list(related_definitions)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for rel in rels:
+ related_definitions = set(rel.RelatedDefinitions) - definitions
+ if related_definitions:
+ rel.RelatedDefinitions = list(related_definitions)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py
index e0caddbe3c..c3e01e30df 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py
@@ -15,3 +15,9 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_pset import add_pset
+from .add_qto import add_qto
+from .edit_pset import edit_pset
+from .edit_qto import edit_qto
+from .remove_pset import remove_pset
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py
index 1fd1a0c61a..6cb72e435c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py
@@ -19,133 +19,127 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, product=None, name=None):
- """Adds a new property set to a product
+def add_pset(file, product=None, name=None) -> None:
+ """Adds a new property set to a product
- Products, such as physical objects or types in IFC may have properties
- associated with them. These properties are typically simple key value
- metadata with data types. For example, a wall type may have a property
- called FireRating with a text value of "2HR". Properties are grouped
- into property sets, so that related properties are grouped together.
+ Products, such as physical objects or types in IFC may have properties
+ associated with them. These properties are typically simple key value
+ metadata with data types. For example, a wall type may have a property
+ called FireRating with a text value of "2HR". Properties are grouped
+ into property sets, so that related properties are grouped together.
- If a property is assigned to a type, the property is inherited by all
- occurrences of that type. For example, a wall type with a FireRating
- property of "2HR" automatically implies that all walls of that wall type
- also have a FireRating of "2HR". It is not necessary to explictly define
- the property again for each occurrence. This also means that properties
- are typically defined on types. If the same property is defined at an
- occurrence, this overrides the property defined on the type.
+ If a property is assigned to a type, the property is inherited by all
+ occurrences of that type. For example, a wall type with a FireRating
+ property of "2HR" automatically implies that all walls of that wall type
+ also have a FireRating of "2HR". It is not necessary to explictly define
+ the property again for each occurrence. This also means that properties
+ are typically defined on types. If the same property is defined at an
+ occurrence, this overrides the property defined on the type.
- buildingSMART has come up with a long list of standardised properties
- for the most common properties required internationally. This solves the
- age-old question of "where do I store my FireRating data for walls"? The
- answer, in this case, is in the "FireRating" property with an "IfcLabel"
- data type grouped in the "Pset_WallCommon" property set. It is
- recommended to view the list of standardised buildingSMART properties
- and see if any suit your needs first. If none are appropriate, then you
- are free to create your own custom properties.
+ buildingSMART has come up with a long list of standardised properties
+ for the most common properties required internationally. This solves the
+ age-old question of "where do I store my FireRating data for walls"? The
+ answer, in this case, is in the "FireRating" property with an "IfcLabel"
+ data type grouped in the "Pset_WallCommon" property set. It is
+ recommended to view the list of standardised buildingSMART properties
+ and see if any suit your needs first. If none are appropriate, then you
+ are free to create your own custom properties.
- This function adds a blank named property set. One you have a property
- set you may add properties using ifcopenshell.api.pset.edit_pset.
+ This function adds a blank named property set. One you have a property
+ set you may add properties using ifcopenshell.api.pset.edit_pset.
- See also ifcopenshell.api.pset.add_qto if you want to add quantification
- data, rather than arbitrary metadata.
+ See also ifcopenshell.api.pset.add_qto if you want to add quantification
+ data, rather than arbitrary metadata.
- :param product: The IfcObject that you want to assign a property set to.
- :type product: ifcopenshell.entity_instance
- :param name: The name of the property set. Property sets that are
- standardised by buildingSMART typically have a prefix of "Pset_",
- like "Pset_WallCommon". If you create your own, you must not use
- that prefix. It is recommended to use your own prefix tailored to
- your project, company, or local government requirement.
- :type name: str
- :return: The newly created IfcPropertySet
- :rtype: ifcopenshell.entity_instance
+ :param product: The IfcObject that you want to assign a property set to.
+ :type product: ifcopenshell.entity_instance
+ :param name: The name of the property set. Property sets that are
+ standardised by buildingSMART typically have a prefix of "Pset_",
+ like "Pset_WallCommon". If you create your own, you must not use
+ that prefix. It is recommended to use your own prefix tailored to
+ your project, company, or local government requirement.
+ :type name: str
+ :return: The newly created IfcPropertySet
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a new wall type.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
+ # Let's imagine we have a new wall type.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
- # Note that this only creates and assigns an empty property set. We
- # still need to add properties into the property set. Having blank
- # property sets are invalid.
- pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon")
+ # Note that this only creates and assigns an empty property set. We
+ # still need to add properties into the property set. Having blank
+ # property sets are invalid.
+ pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon")
- # Add a fire rating property standardised by buildingSMART.
- ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"FireRating": "2HR"})
- """
- self.file = file
- self.settings = {"product": product, "name": name}
+ # Add a fire rating property standardised by buildingSMART.
+ ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"FireRating": "2HR"})
+ """
+ settings = {"product": product, "name": name}
- def execute(self):
- if self.settings["product"].is_a("IfcObject") or self.settings["product"].is_a("IfcContext"):
- for rel in self.settings["product"].IsDefinedBy or []:
- if (
- rel.is_a("IfcRelDefinesByProperties")
- and rel.RelatingPropertyDefinition.Name == self.settings["name"]
- ):
- return rel.RelatingPropertyDefinition
+ if settings["product"].is_a("IfcObject") or settings["product"].is_a("IfcContext"):
+ for rel in settings["product"].IsDefinedBy or []:
+ if rel.is_a("IfcRelDefinesByProperties") and rel.RelatingPropertyDefinition.Name == settings["name"]:
+ return rel.RelatingPropertyDefinition
- pset = self.file.create_entity(
- "IfcPropertySet",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "Name": self.settings["name"],
- }
- )
- self.file.create_entity(
- "IfcRelDefinesByProperties",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": [self.settings["product"]],
- "RelatingPropertyDefinition": pset,
- }
- )
- return pset
- elif self.settings["product"].is_a("IfcTypeObject"):
- for definition in self.settings["product"].HasPropertySets or []:
- if definition.Name == self.settings["name"]:
- return definition
+ pset = file.create_entity(
+ "IfcPropertySet",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "Name": settings["name"],
+ }
+ )
+ file.create_entity(
+ "IfcRelDefinesByProperties",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["product"]],
+ "RelatingPropertyDefinition": pset,
+ }
+ )
+ return pset
+ elif settings["product"].is_a("IfcTypeObject"):
+ for definition in settings["product"].HasPropertySets or []:
+ if definition.Name == settings["name"]:
+ return definition
- pset = self.file.create_entity(
- "IfcPropertySet",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "Name": self.settings["name"],
- }
- )
- has_property_sets = list(self.settings["product"].HasPropertySets or [])
- has_property_sets.append(pset)
- self.settings["product"].HasPropertySets = has_property_sets
- return pset
- elif self.settings["product"].is_a("IfcMaterialDefinition"):
- for definition in self.settings["product"].HasProperties or []:
- if definition.Name == self.settings["name"]:
- return definition
+ pset = file.create_entity(
+ "IfcPropertySet",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "Name": settings["name"],
+ }
+ )
+ has_property_sets = list(settings["product"].HasPropertySets or [])
+ has_property_sets.append(pset)
+ settings["product"].HasPropertySets = has_property_sets
+ return pset
+ elif settings["product"].is_a("IfcMaterialDefinition"):
+ for definition in settings["product"].HasProperties or []:
+ if definition.Name == settings["name"]:
+ return definition
- return self.file.create_entity(
- "IfcMaterialProperties",
- **{
- "Name": self.settings["name"],
- "Material": self.settings["product"],
- }
- )
- elif self.settings["product"].is_a("IfcProfileDef"):
- for definition in self.settings["product"].HasProperties or []:
- if definition.Name == self.settings["name"]:
- return definition
+ return file.create_entity(
+ "IfcMaterialProperties",
+ **{
+ "Name": settings["name"],
+ "Material": settings["product"],
+ }
+ )
+ elif settings["product"].is_a("IfcProfileDef"):
+ for definition in settings["product"].HasProperties or []:
+ if definition.Name == settings["name"]:
+ return definition
- return self.file.create_entity(
- "IfcProfileProperties",
- **{
- "Name": self.settings["name"],
- "ProfileDefinition": self.settings["product"],
- }
- )
+ return file.create_entity(
+ "IfcProfileProperties",
+ **{
+ "Name": settings["name"],
+ "ProfileDefinition": settings["product"],
+ }
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py
index a3c7b54299..0215ab31ad 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py
@@ -20,65 +20,68 @@ import ifcopenshell
import ifcopenshell.api
+def add_qto(file, product=None, name=None) -> None:
+ """Adds a new quantity set to a product
+
+ Products, such as physical objects or types in IFC may have quantities
+ associated with them. These quantities are typically simple key value
+ metadata with data types. For example, a wall type may have a quantity
+ called NetSideArea with a area value of "4.2". Quantities are grouped
+ into quantity sets, so that related quantities are grouped together.
+
+ Quantities are similar to, but different from properties in that they
+ may store a method of measurement or formula. Quantities may also have
+ parametric relationships to other calculated values, such as cost
+ schedules, resource utilisation, or construction task durations.
+
+ buildingSMART has come up with a long list of standardised quantities
+ for the most common quantities required internationally. This solves the
+ age-old question of "what's the standard way of storing quantity
+ take-off data"? It is recommended to view the list of standardised
+ buildingSMART quantities and see if any suit your needs first. If none
+ are appropriate, then you are free to create your own custom quantities.
+
+ This function adds a blank named quantity set. One you have a quantity
+ set you may add quantities using ifcopenshell.api.pset.edit_qto.
+
+ See also ifcopenshell.api.pset.add_qto if you want to arbitrary
+ metadata, rather than quantification data.
+
+ :param product: The IfcObject that you want to assign a quantity set to.
+ :type product: ifcopenshell.entity_instance
+ :param name: The name of the quantity set. Quantity sets that are
+ standardised by buildingSMART typically have a prefix of "Qto_",
+ like "Qto_WallBaseQuantities". If you create your own, you must not
+ use that prefix. It is recommended to use your own prefix tailored
+ to your project, company, or local government requirement.
+ :type name: str
+ :return: The newly created IfcElementQuantity
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we have a new wall.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # Note that this only creates and assigns an empty quantity set. We
+ # still need to add quantities into the property set. Having blank
+ # quantity sets are invalid.
+ qto = ifcopenshell.api.run("pset.add_qto", model, product=wall_type, name="Qto_WallBaseQuantities")
+
+ # Add a side area property standardised by buildingSMART. This
+ # allows quantity take-off to occur, even though no geometry has
+ # even been modelled!
+ ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetSideArea": 4.2})
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"product": product, "name": name}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, product=None, name=None):
- """Adds a new quantity set to a product
-
- Products, such as physical objects or types in IFC may have quantities
- associated with them. These quantities are typically simple key value
- metadata with data types. For example, a wall type may have a quantity
- called NetSideArea with a area value of "4.2". Quantities are grouped
- into quantity sets, so that related quantities are grouped together.
-
- Quantities are similar to, but different from properties in that they
- may store a method of measurement or formula. Quantities may also have
- parametric relationships to other calculated values, such as cost
- schedules, resource utilisation, or construction task durations.
-
- buildingSMART has come up with a long list of standardised quantities
- for the most common quantities required internationally. This solves the
- age-old question of "what's the standard way of storing quantity
- take-off data"? It is recommended to view the list of standardised
- buildingSMART quantities and see if any suit your needs first. If none
- are appropriate, then you are free to create your own custom quantities.
-
- This function adds a blank named quantity set. One you have a quantity
- set you may add quantities using ifcopenshell.api.pset.edit_qto.
-
- See also ifcopenshell.api.pset.add_qto if you want to arbitrary
- metadata, rather than quantification data.
-
- :param product: The IfcObject that you want to assign a quantity set to.
- :type product: ifcopenshell.entity_instance
- :param name: The name of the quantity set. Quantity sets that are
- standardised by buildingSMART typically have a prefix of "Qto_",
- like "Qto_WallBaseQuantities". If you create your own, you must not
- use that prefix. It is recommended to use your own prefix tailored
- to your project, company, or local government requirement.
- :type name: str
- :return: The newly created IfcElementQuantity
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # Let's imagine we have a new wall.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # Note that this only creates and assigns an empty quantity set. We
- # still need to add quantities into the property set. Having blank
- # quantity sets are invalid.
- qto = ifcopenshell.api.run("pset.add_qto", model, product=wall_type, name="Qto_WallBaseQuantities")
-
- # Add a side area property standardised by buildingSMART. This
- # allows quantity take-off to occur, even though no geometry has
- # even been modelled!
- ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetSideArea": 4.2})
- """
- self.file = file
- self.settings = {"product": product, "name": name}
-
def execute(self):
if self.settings["product"].is_a("IfcObject") or self.settings["product"].is_a("IfcContext"):
for rel in self.settings["product"].IsDefinedBy or []:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py
index 0eb711b803..c4955e1605 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py
@@ -20,141 +20,144 @@ import ifcopenshell
import ifcopenshell.util.pset
+def edit_pset(file, pset=None, name=None, properties=None, pset_template=None, should_purge=False) -> None:
+ """Edits a property set and its properties
+
+ At its simplest usage, this may be used to edit the name of a property
+ set. It may also be used to add, edit, or remove properties, either
+ arbitrarily or using a property set template.
+
+ A list of properties are provided as a dictionary, where the keys are
+ property names, and values are property values. Keys that don't already
+ exist are interpreted as properties to be added. Keys that already exist
+ are interpreted as properties to be edited. A "None" value may specify a
+ property to be deleted.
+
+ Properties must have a data type. There are lots of data types in IFCs,
+ not just simple unitless data types like integers, booleans, text, but
+ also distinguishing between types of text, like labels versus
+ descriptive text. There are also lots of unit-based data types like
+ areas, volumes, lengths, power, density, flow rates, pressure, etc.
+
+ To ensure the appropriate data type is used for properties, a property
+ set template may be used. These can be seen as "property
+ specifications". A default selection is provided by buildingSMART, so
+ that all buildingSMART defined standard properties have exactly the same
+ data types and exactly the right property names without fear of invalid
+ data or typos. The built-in buildingSMART templates are always loaded.
+ However, you may also specify your own templates. If you try to add a
+ non-standard property that does not exist in either your own template or
+ in the built-in buildingSMART template, then you have the responsibility
+ to ensure that data types are always consistent and correct.
+
+ :param pset: The IfcPropertySet to edit.
+ :type pset: ifcopenshell.entity_instance
+ :param name: A new name for the property set. If no name is specified,
+ the property set name is not changed.
+ :type name: str, optional
+ :param properties: A dictionary of properties. The keys must be a string
+ of the name of the property. The data type of the value will be
+ determined by the property set template. If no property set
+ template is found, the data types of the Python values will
+ influence the IFC data type of the property. String values will
+ become IfcLabel, float values will become IfcReal, booleans will
+ become IfcBoolean, and integers will become IfcInteger. If more
+ control is desired, you may explicitly specify IFC data objects
+ directly. Note that provided `properties` might be mutated in the process.
+ :type properties: dict
+ :param pset_template: If a property set template is provided, this will
+ be used to determine data types. If no user-defined template is
+ provided, the built-in buildingSMART templates will be loaded.
+ :type pset_template: ifcopenshell.entity_instance
+ :param should_purge: If left as False, properties set to None will be
+ left as None but not removed. If set to true, properties set to None
+ will actually be removed.
+ :type should_purge: bool, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we have a new wall type.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
+
+ # This is a standard buildingSMART property set.
+ pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon")
+
+ # In this scenario, we don't specify any pset_template because it is
+ # part of the built-in buildingSMART templates, and so the
+ # FireRating will automatically be an IfcLabel, and the thermal
+ # transmittance value will automatically be an
+ # IfcThermalTransmittanceMeasure. Neither of these properties exist
+ # yet, so they will be created.
+ ifcopenshell.api.run("pset.edit_pset", model,
+ pset=pset, properties={"FireRating": "2HR", "ThermalTransmittance": 42.3})
+
+ # We can edit existing properties. In this case, "FireRating" is
+ # edited from "2HR" to "1HR". Combustible is new, and will be added.
+ # The existing "ThermalTransmittance" property will be left
+ # unchanged.
+ ifcopenshell.api.run("pset.edit_pset", model,
+ pset=pset, properties={"FireRating": "1HR", "Combustible": False})
+
+ # Setting to None will change the value but not delete the property.
+ ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"Combustible": None})
+
+ # If you actually want to delete the property, enable purging.
+ ifcopenshell.api.run("pset.edit_pset", model, pset=pset,
+ properties={"Combustible": None}, should_purge=True)
+
+ # What if we wanted to manage our own properties? Let's create our
+ # own "Company Standard" property set templates. Notice how we
+ # prefix our property set with "Foo_", if our company name was "Foo"
+ # this would make sense.
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar")
+
+ # Let's imagine we want all model authors to specify two properties,
+ # one being a length measurement and another being a boolean.
+ prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model,
+ pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure")
+ prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model,
+ pset_template=template, name="DemoB", primary_measure_type="IfcBoolean")
+
+ # Now we can use our property set template to add our properties,
+ # and the data types will always match our template.
+ pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar")
+ ifcopenshell.api.run("pset.edit_pset", model,
+ pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template)
+
+ # Here's a third scenario where we want to add arbitrary properties
+ # that are not standardised by anything, not even our own custom
+ # templates.
+ pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Custom_Pset")
+ ifcopenshell.api.run("pset.edit_pset", model,
+ pset=pset, properties={
+ # Basic Python data types are mapped to a sensible default
+ "SomeLabel": "Foo",
+ "SomeNumber": 12.3,
+ # But we can always specify exactly what we're after too
+ "ExplicitLength": model.createIfcLengthMeasure(42.3)
+ })
+
+ # Editing existing properties will retain their current data types
+ # if possible. So this will still be a length measure.
+ ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"ExplicitLength": 12.3})
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "pset": pset,
+ "name": name,
+ "properties": properties or {},
+ "pset_template": pset_template,
+ "should_purge": should_purge,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, pset=None, name=None, properties=None, pset_template=None, should_purge=False):
- """Edits a property set and its properties
-
- At its simplest usage, this may be used to edit the name of a property
- set. It may also be used to add, edit, or remove properties, either
- arbitrarily or using a property set template.
-
- A list of properties are provided as a dictionary, where the keys are
- property names, and values are property values. Keys that don't already
- exist are interpreted as properties to be added. Keys that already exist
- are interpreted as properties to be edited. A "None" value may specify a
- property to be deleted.
-
- Properties must have a data type. There are lots of data types in IFCs,
- not just simple unitless data types like integers, booleans, text, but
- also distinguishing between types of text, like labels versus
- descriptive text. There are also lots of unit-based data types like
- areas, volumes, lengths, power, density, flow rates, pressure, etc.
-
- To ensure the appropriate data type is used for properties, a property
- set template may be used. These can be seen as "property
- specifications". A default selection is provided by buildingSMART, so
- that all buildingSMART defined standard properties have exactly the same
- data types and exactly the right property names without fear of invalid
- data or typos. The built-in buildingSMART templates are always loaded.
- However, you may also specify your own templates. If you try to add a
- non-standard property that does not exist in either your own template or
- in the built-in buildingSMART template, then you have the responsibility
- to ensure that data types are always consistent and correct.
-
- :param pset: The IfcPropertySet to edit.
- :type pset: ifcopenshell.entity_instance
- :param name: A new name for the property set. If no name is specified,
- the property set name is not changed.
- :type name: str, optional
- :param properties: A dictionary of properties. The keys must be a string
- of the name of the property. The data type of the value will be
- determined by the property set template. If no property set
- template is found, the data types of the Python values will
- influence the IFC data type of the property. String values will
- become IfcLabel, float values will become IfcReal, booleans will
- become IfcBoolean, and integers will become IfcInteger. If more
- control is desired, you may explicitly specify IFC data objects
- directly. Note that provided `properties` might be mutated in the process.
- :type properties: dict
- :param pset_template: If a property set template is provided, this will
- be used to determine data types. If no user-defined template is
- provided, the built-in buildingSMART templates will be loaded.
- :type pset_template: ifcopenshell.entity_instance
- :param should_purge: If left as False, properties set to None will be
- left as None but not removed. If set to true, properties set to None
- will actually be removed.
- :type should_purge: bool, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Let's imagine we have a new wall type.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
-
- # This is a standard buildingSMART property set.
- pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon")
-
- # In this scenario, we don't specify any pset_template because it is
- # part of the built-in buildingSMART templates, and so the
- # FireRating will automatically be an IfcLabel, and the thermal
- # transmittance value will automatically be an
- # IfcThermalTransmittanceMeasure. Neither of these properties exist
- # yet, so they will be created.
- ifcopenshell.api.run("pset.edit_pset", model,
- pset=pset, properties={"FireRating": "2HR", "ThermalTransmittance": 42.3})
-
- # We can edit existing properties. In this case, "FireRating" is
- # edited from "2HR" to "1HR". Combustible is new, and will be added.
- # The existing "ThermalTransmittance" property will be left
- # unchanged.
- ifcopenshell.api.run("pset.edit_pset", model,
- pset=pset, properties={"FireRating": "1HR", "Combustible": False})
-
- # Setting to None will change the value but not delete the property.
- ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"Combustible": None})
-
- # If you actually want to delete the property, enable purging.
- ifcopenshell.api.run("pset.edit_pset", model, pset=pset,
- properties={"Combustible": None}, should_purge=True)
-
- # What if we wanted to manage our own properties? Let's create our
- # own "Company Standard" property set templates. Notice how we
- # prefix our property set with "Foo_", if our company name was "Foo"
- # this would make sense.
- template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar")
-
- # Let's imagine we want all model authors to specify two properties,
- # one being a length measurement and another being a boolean.
- prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model,
- pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure")
- prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model,
- pset_template=template, name="DemoB", primary_measure_type="IfcBoolean")
-
- # Now we can use our property set template to add our properties,
- # and the data types will always match our template.
- pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar")
- ifcopenshell.api.run("pset.edit_pset", model,
- pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template)
-
- # Here's a third scenario where we want to add arbitrary properties
- # that are not standardised by anything, not even our own custom
- # templates.
- pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Custom_Pset")
- ifcopenshell.api.run("pset.edit_pset", model,
- pset=pset, properties={
- # Basic Python data types are mapped to a sensible default
- "SomeLabel": "Foo",
- "SomeNumber": 12.3,
- # But we can always specify exactly what we're after too
- "ExplicitLength": model.createIfcLengthMeasure(42.3)
- })
-
- # Editing existing properties will retain their current data types
- # if possible. So this will still be a length measure.
- ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"ExplicitLength": 12.3})
- """
- self.file = file
- self.settings = {
- "pset": pset,
- "name": name,
- "properties": properties or {},
- "pset_template": pset_template,
- "should_purge": should_purge,
- }
-
def execute(self):
self.update_pset_name()
self.load_pset_template()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py
index cd5a3bca05..ba5b7c93e0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py
@@ -20,97 +20,100 @@ import ifcopenshell
import ifcopenshell.util.pset
+def edit_qto(file, qto=None, name=None, properties=None, pset_template=None) -> None:
+ """Edits a quantity set and its quantities
+
+ At its simplest usage, this may be used to edit the name of a quantity
+ set. It may also be used to add, edit, or remove quantities.
+
+ See ifcopenshell.api.pset.edit_pset for documentation on how this is
+ intended to be used.
+
+ One major difference is that quantities set to None are always purged.
+ It is not allowed to have None quantities in IFC.
+
+ :param qto: The IfcElementQuantity to edit.
+ :type qto: ifcopenshell.entity_instance
+ :param name: A new name for the quantity set. If no name is specified,
+ the quantity set name is not changed.
+ :type name: str, optional
+ :param properties: A dictionary of properties. The keys must be a string
+ of the name of the quantity. The data type of the value will be
+ determined by the quantity set template. If no quantity set
+ template is found, the data types of the Python values will
+ influence the IFC data type of the quantity. String values will
+ become IfcLabel, float values will become IfcReal, booleans will
+ become IfcBoolean, and integers will become IfcInteger. If more
+ control is desired, you may explicitly specify IFC data objects
+ directly.
+ :type properties: dict
+ :param pset_template: If a quantity set template is provided, this will
+ be used to determine data types. If no user-defined template is
+ provided, the built-in buildingSMART templates will be loaded.
+ :type pset_template: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we have a new wall type.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # This is a standard buildingSMART property set.
+ qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Qto_WallBaseQuantities")
+
+ # In this scenario, we don't specify any pset_template because it is
+ # part of the built-in buildingSMART templates, and so the Length
+ # will automatically be an IfcLengthMeasure, and the NetVolume will
+ # automatically be an IfcVolumeMeasure. Neither of these properties
+ # exist yet, so they will be created.
+ ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": 12, "NetVolume": 7.2})
+
+ # Setting to None will delete the quantity.
+ ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": None})
+
+ # What if we wanted to manage our own properties? Let's create our
+ # own "Company Standard" property set templates. Notice how we
+ # prefix our property set with "Foo_", if our company name was "Foo"
+ # this would make sense. In this example, we say that our template
+ # only applies to walls and is for quantities.
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model,
+ name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall")
+
+ # Let's imagine we want all model authors to specify a length
+ # measurement for the portion of a wall that is overhanging.
+ prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
+ name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure")
+
+ # Now we can use our property set template to add our properties,
+ # and the data types will always match our template.
+ qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall")
+ ifcopenshell.api.run("pset.edit_qto", model,
+ qto=qto, properties={"OverhangLength": 42.3}, pset_template=template)
+
+ # Here's a third scenario where we want to add arbitrary quantities
+ # that are not standardised by anything, not even our own custom
+ # templates.
+ qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Custom_Qto")
+ ifcopenshell.api.run("pset.edit_qto", model,
+ qto=qto, properties={
+ "SomeLength": model.createIfcLengthMeasure(42.3),
+ "SomeArea": model.createIfcAreaMeasure(21.0)
+ })
+
+ # Editing existing quantities will retain their current data types
+ # if possible. So this will still be a length measure.
+ ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3})
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, qto=None, name=None, properties=None, pset_template=None):
- """Edits a quantity set and its quantities
-
- At its simplest usage, this may be used to edit the name of a quantity
- set. It may also be used to add, edit, or remove quantities.
-
- See ifcopenshell.api.pset.edit_pset for documentation on how this is
- intended to be used.
-
- One major difference is that quantities set to None are always purged.
- It is not allowed to have None quantities in IFC.
-
- :param qto: The IfcElementQuantity to edit.
- :type qto: ifcopenshell.entity_instance
- :param name: A new name for the quantity set. If no name is specified,
- the quantity set name is not changed.
- :type name: str, optional
- :param properties: A dictionary of properties. The keys must be a string
- of the name of the quantity. The data type of the value will be
- determined by the quantity set template. If no quantity set
- template is found, the data types of the Python values will
- influence the IFC data type of the quantity. String values will
- become IfcLabel, float values will become IfcReal, booleans will
- become IfcBoolean, and integers will become IfcInteger. If more
- control is desired, you may explicitly specify IFC data objects
- directly.
- :type properties: dict
- :param pset_template: If a quantity set template is provided, this will
- be used to determine data types. If no user-defined template is
- provided, the built-in buildingSMART templates will be loaded.
- :type pset_template: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Let's imagine we have a new wall type.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # This is a standard buildingSMART property set.
- qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Qto_WallBaseQuantities")
-
- # In this scenario, we don't specify any pset_template because it is
- # part of the built-in buildingSMART templates, and so the Length
- # will automatically be an IfcLengthMeasure, and the NetVolume will
- # automatically be an IfcVolumeMeasure. Neither of these properties
- # exist yet, so they will be created.
- ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": 12, "NetVolume": 7.2})
-
- # Setting to None will delete the quantity.
- ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": None})
-
- # What if we wanted to manage our own properties? Let's create our
- # own "Company Standard" property set templates. Notice how we
- # prefix our property set with "Foo_", if our company name was "Foo"
- # this would make sense. In this example, we say that our template
- # only applies to walls and is for quantities.
- template = ifcopenshell.api.run("pset_template.add_pset_template", model,
- name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall")
-
- # Let's imagine we want all model authors to specify a length
- # measurement for the portion of a wall that is overhanging.
- prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
- name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure")
-
- # Now we can use our property set template to add our properties,
- # and the data types will always match our template.
- qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall")
- ifcopenshell.api.run("pset.edit_qto", model,
- qto=qto, properties={"OverhangLength": 42.3}, pset_template=template)
-
- # Here's a third scenario where we want to add arbitrary quantities
- # that are not standardised by anything, not even our own custom
- # templates.
- qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Custom_Qto")
- ifcopenshell.api.run("pset.edit_qto", model,
- qto=qto, properties={
- "SomeLength": model.createIfcLengthMeasure(42.3),
- "SomeArea": model.createIfcAreaMeasure(21.0)
- })
-
- # Editing existing quantities will retain their current data types
- # if possible. So this will still be a length measure.
- ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3})
- """
- self.file = file
- self.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template}
-
def execute(self):
self.qto_idx = 5
if self.settings["qto"].is_a("IfcPhysicalComplexQuantity"):
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py
index da77accbb6..50ef427bb4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py
@@ -20,68 +20,65 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, product=None, pset=None):
- """Removes a property set from a product
+def remove_pset(file, product=None, pset=None) -> None:
+ """Removes a property set from a product
- All properties that are part of this property set are also removed.
+ All properties that are part of this property set are also removed.
- :param product: The IfcObject to remove the property set from.
- :type product: ifcopenshell.entity_instance
- :param pset: The IfcPropertySet or IfcElementQuantity to remove.
- :type pset: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param product: The IfcObject to remove the property set from.
+ :type product: ifcopenshell.entity_instance
+ :param pset: The IfcPropertySet or IfcElementQuantity to remove.
+ :type pset: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we have a new wall type with a property set.
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
- pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon")
+ # Let's imagine we have a new wall type with a property set.
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
+ pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon")
- # Remove it!
- ifcopenshell.api.run("pset.remove_pset", model, product=wall_type, pset=pset)
- """
- self.file = file
- self.settings = {"product": product, "pset": pset}
+ # Remove it!
+ ifcopenshell.api.run("pset.remove_pset", model, product=wall_type, pset=pset)
+ """
+ settings = {"product": product, "pset": pset}
- def execute(self):
- to_purge = []
- should_remove_pset = True
- for inverse in self.file.get_inverse(self.settings["pset"]):
- if inverse.is_a("IfcRelDefinesByProperties"):
- if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1:
- to_purge.append(inverse)
- else:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["product"])
- inverse.RelatedObjects = related_objects
- should_remove_pset = False
- if should_remove_pset:
- properties = [] # Predefined psets have no properties
- if self.settings["pset"].is_a("IfcPropertySet"):
- properties = self.settings["pset"].HasProperties or []
- elif self.settings["pset"].is_a("IfcQuantitySet"):
- properties = self.settings["pset"].Quantities or []
- elif self.settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"):
- properties = self.settings["pset"].Properties or []
- for prop in properties:
- if self.file.get_total_inverses(prop) != 1:
- continue
- if prop.is_a("IfcPropertyEnumeratedValue"):
- enumeration = prop.EnumerationReference
- if enumeration and self.file.get_total_inverses(enumeration) == 1:
- self.file.remove(enumeration)
- self.file.remove(prop)
- # IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory
- history = getattr(self.settings["pset"], "OwnerHistory", None)
- self.file.remove(self.settings["pset"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- for element in to_purge:
- history = getattr(element, "OwnerHistory", None)
- self.file.remove(element)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ to_purge = []
+ should_remove_pset = True
+ for inverse in file.get_inverse(settings["pset"]):
+ if inverse.is_a("IfcRelDefinesByProperties"):
+ if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1:
+ to_purge.append(inverse)
+ else:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["product"])
+ inverse.RelatedObjects = related_objects
+ should_remove_pset = False
+ if should_remove_pset:
+ properties = [] # Predefined psets have no properties
+ if settings["pset"].is_a("IfcPropertySet"):
+ properties = settings["pset"].HasProperties or []
+ elif settings["pset"].is_a("IfcQuantitySet"):
+ properties = settings["pset"].Quantities or []
+ elif settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"):
+ properties = settings["pset"].Properties or []
+ for prop in properties:
+ if file.get_total_inverses(prop) != 1:
+ continue
+ if prop.is_a("IfcPropertyEnumeratedValue"):
+ enumeration = prop.EnumerationReference
+ if enumeration and file.get_total_inverses(enumeration) == 1:
+ file.remove(enumeration)
+ file.remove(prop)
+ # IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory
+ history = getattr(settings["pset"], "OwnerHistory", None)
+ file.remove(settings["pset"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ for element in to_purge:
+ history = getattr(element, "OwnerHistory", None)
+ file.remove(element)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py
index e0caddbe3c..1c5963479c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py
@@ -15,3 +15,10 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_prop_template import add_prop_template
+from .add_pset_template import add_pset_template
+from .edit_prop_template import edit_prop_template
+from .edit_pset_template import edit_pset_template
+from .remove_prop_template import remove_prop_template
+from .remove_pset_template import remove_pset_template
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py
index 5a9dc42355..109c830c94 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py
@@ -19,95 +19,91 @@
import ifcopenshell
-class Usecase:
- def __init__(
- self,
- file,
- pset_template=None,
- name="NewProperty",
- description=None,
- template_type="P_SINGLEVALUE",
- primary_measure_type="IfcLabel",
- ):
- """Adds new property templates to a property set template
+def add_prop_template(
+ file,
+ pset_template=None,
+ name="NewProperty",
+ description=None,
+ template_type="P_SINGLEVALUE",
+ primary_measure_type="IfcLabel",
+) -> None:
+ """Adds new property templates to a property set template
- Assuming you first have a property set template, this allows you to add
- templates for properties within that property set. A property template
- lets you specify the name, description, and data type of a property.
- When the template is provided to a model author, this gives them clear
- instructions about the intention of the property and exactly which data
- type to use.
+ Assuming you first have a property set template, this allows you to add
+ templates for properties within that property set. A property template
+ lets you specify the name, description, and data type of a property.
+ When the template is provided to a model author, this gives them clear
+ instructions about the intention of the property and exactly which data
+ type to use.
- Types of properties and quantities include:
+ Types of properties and quantities include:
- * P_SINGLEVALUE - a single value, the most common type of property.
- * P_ENUMERATEDVALUE - the property value may one or more values chosen
- from a preset list of values.
- * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value.
- * P_LISTVALUE - the property has a list of values.
- * P_TABLEVALUE - the property has a table of values.
- * P_REFERENCEVALUE - the property is a parametric reference to another
- value. This is only for advanced users.
- * Q_LENGTH - the quantity is a length.
- * Q_AREA - the quantity is an area.
- * Q_VOLUME - the quantity is a volume.
- * Q_COUNT - the quantity is counting a item.
- * Q_WEIGHT - the quantity is a weight.
- * Q_TIME - the quantity is a time duration.
+ * P_SINGLEVALUE - a single value, the most common type of property.
+ * P_ENUMERATEDVALUE - the property value may one or more values chosen
+ from a preset list of values.
+ * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value.
+ * P_LISTVALUE - the property has a list of values.
+ * P_TABLEVALUE - the property has a table of values.
+ * P_REFERENCEVALUE - the property is a parametric reference to another
+ value. This is only for advanced users.
+ * Q_LENGTH - the quantity is a length.
+ * Q_AREA - the quantity is an area.
+ * Q_VOLUME - the quantity is a volume.
+ * Q_COUNT - the quantity is counting a item.
+ * Q_WEIGHT - the quantity is a weight.
+ * Q_TIME - the quantity is a time duration.
- :param pset_template: The property set template to add the property
- template to.
- :type pset_template: ifcopenshell.entity_instance
- :param name: The name of the property
- :type name: str,optional
- :param description: A few words describing what the property stores.
- :type description: str,optional
- :param primary_measure_type: The data type of the property. Consult the
- IFC documentation for the full list of data types.
- :param primary_measure_type: str,optional
- :return: The newly created IfcSimplePropertyTemplate.
- :rtype: ifcopenshell.entity_instance
+ :param pset_template: The property set template to add the property
+ template to.
+ :type pset_template: ifcopenshell.entity_instance
+ :param name: The name of the property
+ :type name: str,optional
+ :param description: A few words describing what the property stores.
+ :type description: str,optional
+ :param primary_measure_type: The data type of the property. Consult the
+ IFC documentation for the full list of data types.
+ :param primary_measure_type: str,optional
+ :return: The newly created IfcSimplePropertyTemplate.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a simple template that may be applied to all types
- template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
+ # Create a simple template that may be applied to all types
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
- # Here's one example property
- ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
- name="HighVoltage", description="Whether there is a risk of high voltage.",
- primary_measure_type="IfcBoolean")
+ # Here's one example property
+ ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
+ name="HighVoltage", description="Whether there is a risk of high voltage.",
+ primary_measure_type="IfcBoolean")
- # Here's another
- ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
- name="ChemicalType", description="The class of chemical spillage.",
- primary_measure_type="IfcLabel")
- """
- self.file = file
- self.settings = {
- "pset_template": pset_template,
- "name": name,
- "description": description,
- "template_type": template_type,
- "primary_measure_type": primary_measure_type,
+ # Here's another
+ ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
+ name="ChemicalType", description="The class of chemical spillage.",
+ primary_measure_type="IfcLabel")
+ """
+ settings = {
+ "pset_template": pset_template,
+ "name": name,
+ "description": description,
+ "template_type": template_type,
+ "primary_measure_type": primary_measure_type,
+ }
+
+ prop_template = file.create_entity(
+ "IfcSimplePropertyTemplate",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "Name": settings["name"],
+ "Description": settings["description"],
+ "PrimaryMeasureType": settings["primary_measure_type"],
+ "TemplateType": settings["template_type"],
+ "AccessState": "READWRITE",
+ "Enumerators": None,
}
-
- def execute(self):
- prop_template = self.file.create_entity(
- "IfcSimplePropertyTemplate",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "Name": self.settings["name"],
- "Description": self.settings["description"],
- "PrimaryMeasureType": self.settings["primary_measure_type"],
- "TemplateType": self.settings["template_type"],
- "AccessState": "READWRITE",
- "Enumerators": None,
- }
- )
- has_property_templates = list(self.settings["pset_template"].HasPropertyTemplates or [])
- has_property_templates.append(prop_template)
- self.settings["pset_template"].HasPropertyTemplates = has_property_templates
- return prop_template
+ )
+ has_property_templates = list(settings["pset_template"].HasPropertyTemplates or [])
+ has_property_templates.append(prop_template)
+ settings["pset_template"].HasPropertyTemplates = has_property_templates
+ return prop_template
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py
index 242f7b7510..9a22b6a97a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py
@@ -19,95 +19,91 @@
import ifcopenshell
-class Usecase:
- def __init__(
- self,
- file,
- name="New_Pset",
- template_type="PSET_TYPEDRIVENOVERRIDE",
- applicable_entity="IfcObject,IfcTypeObject",
- ):
- """Adds a new property set template
+def add_pset_template(
+ file,
+ name="New_Pset",
+ template_type="PSET_TYPEDRIVENOVERRIDE",
+ applicable_entity="IfcObject,IfcTypeObject",
+) -> None:
+ """Adds a new property set template
- This creates a new template for property sets. A template defines what
- the name of the property set should be, what properties it can have,
- what entities (e.g. wall) the property set can be assigned to, whether
- it should be assigned at a type or occurrence level, the data types of
- the properties, and descriptions of the properties. This template can
- then be used as a project, company, or local government standard.
+ This creates a new template for property sets. A template defines what
+ the name of the property set should be, what properties it can have,
+ what entities (e.g. wall) the property set can be assigned to, whether
+ it should be assigned at a type or occurrence level, the data types of
+ the properties, and descriptions of the properties. This template can
+ then be used as a project, company, or local government standard.
- buildingSMART itself ships a catalogue of property sets using these
- templates, ensuring that internationally common properties (e.g. fire
- rating of a wall) are all implemented exactly the same way across all
- vendors and projects. Naturally, not everything can be standardised
- internationally, so this allows you to create your own templates.
+ buildingSMART itself ships a catalogue of property sets using these
+ templates, ensuring that internationally common properties (e.g. fire
+ rating of a wall) are all implemented exactly the same way across all
+ vendors and projects. Naturally, not everything can be standardised
+ internationally, so this allows you to create your own templates.
- You may either create a property template to store properties, or a
- quantity template to store quantities. For convenience, we will always
- call them "property templates" as they are conceptually very similar.
+ You may either create a property template to store properties, or a
+ quantity template to store quantities. For convenience, we will always
+ call them "property templates" as they are conceptually very similar.
- This function only creates a template for the property set, not the
- properties themselves within the property set. At this level, you are
- allowed to define the name of the property set, whether it is type or
- occurrence based, and which entities it applies to.
+ This function only creates a template for the property set, not the
+ properties themselves within the property set. At this level, you are
+ allowed to define the name of the property set, whether it is type or
+ occurrence based, and which entities it applies to.
- See the documentation for IfcPropertySetTemplate for instructions on
- the types of template type and list of applicable entities.
+ See the documentation for IfcPropertySetTemplate for instructions on
+ the types of template type and list of applicable entities.
- The types of property set templates are:
+ The types of property set templates are:
- * PSET_TYPEDRIVENONLY - assigned only to types
- * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both,
- the occurrence overrides the type.
- * PSET_OCCURRENCEDRIVEN - assigned to occurrences only.
- * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is
- only recommended for advanced users.
- * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities.
- * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for
- quantities. If both, the occurrence overrides the type.
- * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for
- quantities.
+ * PSET_TYPEDRIVENONLY - assigned only to types
+ * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both,
+ the occurrence overrides the type.
+ * PSET_OCCURRENCEDRIVEN - assigned to occurrences only.
+ * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is
+ only recommended for advanced users.
+ * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities.
+ * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for
+ quantities. If both, the occurrence overrides the type.
+ * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for
+ quantities.
- By default, this creates a template that can be applied to types, but
- overridden by occurrences, and is applicable to everything.
+ By default, this creates a template that can be applied to types, but
+ overridden by occurrences, and is applicable to everything.
- :param name: The name of the property set
- :type name: str,optional
- :param template_type: Choose from one of PSET_TYPEDRIVENONLY,
- PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN,
- PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE,
- QTO_OCCURRENCEDRIVEN, NOTDEFINED
- :type template_type: str,optional
- :param applicable_entity: The entity that this template is allowed to be
- applied to. For example, IfcWall means that the property set may be
- assigned to walls only. IfcTypeObject, the default, means that the
- property set may be assigned to any type.
- :type applicable_entity: str,optional
- :return: The newly created IfcPropertySetTemplate
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the property set
+ :type name: str,optional
+ :param template_type: Choose from one of PSET_TYPEDRIVENONLY,
+ PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN,
+ PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE,
+ QTO_OCCURRENCEDRIVEN, NOTDEFINED
+ :type template_type: str,optional
+ :param applicable_entity: The entity that this template is allowed to be
+ applied to. For example, IfcWall means that the property set may be
+ assigned to walls only. IfcTypeObject, the default, means that the
+ property set may be assigned to any type.
+ :type applicable_entity: str,optional
+ :return: The newly created IfcPropertySetTemplate
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a simple template that may be applied to all types
- ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
+ # Create a simple template that may be applied to all types
+ ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
- # Note that we aren't finished yet. Our property set template
- # doesn't have any properties in it. Let's add a minimum of one
- # property.
- ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
- name="HighVoltage", description="Whether there is a risk of high voltage.",
- primary_measure_type="IfcBoolean")
- """
- self.file = file
- self.settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity}
+ # Note that we aren't finished yet. Our property set template
+ # doesn't have any properties in it. Let's add a minimum of one
+ # property.
+ ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template,
+ name="HighVoltage", description="Whether there is a risk of high voltage.",
+ primary_measure_type="IfcBoolean")
+ """
+ settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity}
- def execute(self):
- return self.file.create_entity(
- "IfcPropertySetTemplate",
- GlobalId=ifcopenshell.guid.new(),
- Name=self.settings["name"],
- TemplateType=self.settings["template_type"],
- ApplicableEntity=self.settings["applicable_entity"],
- )
+ return file.create_entity(
+ "IfcPropertySetTemplate",
+ GlobalId=ifcopenshell.guid.new(),
+ Name=settings["name"],
+ TemplateType=settings["template_type"],
+ ApplicableEntity=settings["applicable_entity"],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py
index 6b2633992f..dcc6c9b3ae 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py
@@ -17,36 +17,33 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, prop_template=None, attributes=None):
- """Edits the attributes of an IfcSimplePropertyTemplate
+def edit_prop_template(file, prop_template=None, attributes=None) -> None:
+ """Edits the attributes of an IfcSimplePropertyTemplate
- For more information about the attributes and data types of an
- IfcSimplePropertyTemplate, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcSimplePropertyTemplate, consult the IFC documentation.
- :param prop_template: The IfcSimplePropertyTemplate entity you want to edit
- :type prop_template: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param prop_template: The IfcSimplePropertyTemplate entity you want to edit
+ :type prop_template: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
- # Here's a property with just default values.
- prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template)
+ # Here's a property with just default values.
+ prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template)
- # Let's edit it to give the actual values we need.
- ifcopenshell.api.run("pset_template.edit_prop_template", model,
- prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"})
- """
- self.file = file
- self.settings = {"prop_template": prop_template, "attributes": attributes or {}}
+ # Let's edit it to give the actual values we need.
+ ifcopenshell.api.run("pset_template.edit_prop_template", model,
+ prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"})
+ """
+ settings = {"prop_template": prop_template, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["prop_template"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["prop_template"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py
index 303618f509..8a0581efdc 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py
@@ -17,34 +17,31 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, pset_template=None, attributes=None):
- """Edits the attributes of an IfcPropertySetTemplate
+def edit_pset_template(file, pset_template=None, attributes=None) -> None:
+ """Edits the attributes of an IfcPropertySetTemplate
- For more information about the attributes and data types of an
- IfcPropertySetTemplate, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcPropertySetTemplate, consult the IFC documentation.
- :param pset_template: The IfcPropertySetTemplate entity you want to edit
- :type pset_template: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param pset_template: The IfcPropertySetTemplate entity you want to edit
+ :type pset_template: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Whoops! We named it with a buildingSMART reserved "Pset_" prefix!
- template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors")
+ # Whoops! We named it with a buildingSMART reserved "Pset_" prefix!
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors")
- # Let's fix it to prefix with our company code instead.
- ifcopenshell.api.run("pset_template.edit_pset_template", model,
- pset_template=template, attributes={"Name": "ABC_RiskFactors"})
- """
- self.file = file
- self.settings = {"pset_template": pset_template, "attributes": attributes or {}}
+ # Let's fix it to prefix with our company code instead.
+ ifcopenshell.api.run("pset_template.edit_pset_template", model,
+ pset_template=template, attributes={"Name": "ABC_RiskFactors"})
+ """
+ settings = {"pset_template": pset_template, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["pset_template"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["pset_template"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py
index 6479e6ffc2..7a247ac383 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py
@@ -19,41 +19,38 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, prop_template=None):
- """Removes a property template
+def remove_prop_template(file, prop_template=None) -> None:
+ """Removes a property template
- Note that a property set template should always have at least one
- property template to be valid, so take care when removing property
- templates.
+ Note that a property set template should always have at least one
+ property template to be valid, so take care when removing property
+ templates.
- :param prop_template: The IfcSimplePropertyTemplate to remove.
- :type prop_template: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param prop_template: The IfcSimplePropertyTemplate to remove.
+ :type prop_template: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
- # Here's two propertes with just default values.
- prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template)
- prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template)
+ # Here's two propertes with just default values.
+ prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template)
+ prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template)
- # Let's remove the second one.
- ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2)
- """
- self.file = file
- self.settings = {"prop_template": prop_template}
+ # Let's remove the second one.
+ ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2)
+ """
+ settings = {"prop_template": prop_template}
- def execute(self):
- for inverse in self.file.get_inverse(self.settings["prop_template"]):
- if len(inverse.HasPropertyTemplates) == 1:
- inverse.HasPropertyTemplates = []
- else:
- has_property_templates = list(inverse.HasPropertyTemplates)
- has_property_templates.remove(self.settings["prop_template"])
- inverse.HasPropertyTemplates = has_property_templates
- ifcopenshell.util.element.remove_deep(self.file, self.settings["prop_template"])
+ for inverse in file.get_inverse(settings["prop_template"]):
+ if len(inverse.HasPropertyTemplates) == 1:
+ inverse.HasPropertyTemplates = []
+ else:
+ has_property_templates = list(inverse.HasPropertyTemplates)
+ has_property_templates.remove(settings["prop_template"])
+ inverse.HasPropertyTemplates = has_property_templates
+ ifcopenshell.util.element.remove_deep(file, settings["prop_template"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py
index 4cb2c2695a..c567a55033 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py
@@ -19,30 +19,27 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, pset_template=None):
- """Removes a property set template
+def remove_pset_template(file, pset_template=None) -> None:
+ """Removes a property set template
- All property templates within the property set template are also removed
- along with it.
+ All property templates within the property set template are also removed
+ along with it.
- :param pset_template: The IfcPropertySetTemplate to remove.
- :type pset_template: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param pset_template: The IfcPropertySetTemplate to remove.
+ :type pset_template: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a template.
- template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
+ # Create a template.
+ template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors")
- # Let's remove the template.
- ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template)
- """
- self.file = file
- self.settings = {"pset_template": pset_template}
+ # Let's remove the template.
+ ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template)
+ """
+ settings = {"pset_template": pset_template}
- def execute(self):
- ifcopenshell.util.element.remove_deep(self.file, self.settings["pset_template"])
+ ifcopenshell.util.element.remove_deep(file, settings["pset_template"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py
index e0caddbe3c..8fcff6a7fc 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py
@@ -15,3 +15,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_resource import add_resource
+from .add_resource_quantity import add_resource_quantity
+from .add_resource_time import add_resource_time
+from .assign_resource import assign_resource
+from .calculate_resource_usage import calculate_resource_usage
+from .calculate_resource_work import calculate_resource_work
+from .edit_resource import edit_resource
+from .edit_resource_quantity import edit_resource_quantity
+from .edit_resource_time import edit_resource_time
+from .remove_resource import remove_resource
+from .remove_resource_quantity import remove_resource_quantity
+from .unassign_resource import unassign_resource
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py
index 03a67ab648..e2a1dab308 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py
@@ -19,93 +19,89 @@
import ifcopenshell.api
-class Usecase:
- def __init__(
- self,
+def add_resource(
+ file,
+ parent_resource=None,
+ ifc_class="IfcCrewResource",
+ name=None,
+ predefined_type="NOTDEFINED",
+) -> None:
+ """Add a new construction resource
+
+ Construction resources may be managed and connected to cost schedules
+ and construction schedules. This allows calculations to be done on
+ resource utilisation, cost optimisation (e.g. labour rates), and
+ optioneering on build strategies.
+
+ There are typically two types of resources. Crew resources are resources
+ where you manage your own crew and you have full control over the
+ equipment, labour, products, and materials used by your crew.
+ Alternatively, there are subcontractor resources, where you simply
+ delegate all the details to a subcontractor and it is not decomposed
+ into further levels of detail.
+
+ This means when adding resources, you'd first either add a crew or
+ subcontract resource. If it is a crew resource, you'd then add child
+ resources to that crew, such as equipment (cranes, excavators, hoists,
+ etc), material (wood, concrete, etc), and labour (rigging crews,
+ formworkers, etc).
+
+ :param parent_resource: If this is a child resource (typically to a crew
+ resource), then nominate the parent IfcConstructionResource here.
+ :type parent_resource: ifcopenshell.entity_instance
+ :param ifc_class: The class of resource chosen from
+ IfcConstructionEquipmentResource, IfcConstructionMaterialResource,
+ IfcConstructionProductResource, IfcCrewResource, IfcLaborResource,
+ or IfcSubContractResource.
+ :type ifc_class: str,optional
+ :param name: The name of the resource
+ :type name: str,optional
+ :param predefined_type: Consult the IFC documentation for the valid
+ predefined types for each type of resource class.
+ :type predefined_type: str,optional
+ :return: The newly created resource depending on the nominated IFC
+ class.
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+
+ # Add some labour to our crew.
+ ifcopenshell.api.run("resource.add_resource", model, parent_resource=crew, ifc_class="IfcLaborResource")
+ """
+ settings = {
+ "parent_resource": parent_resource,
+ "ifc_class": ifc_class,
+ "name": name,
+ "predefined_type": predefined_type,
+ }
+
+ resource = ifcopenshell.api.run(
+ "root.create_entity",
file,
- parent_resource=None,
- ifc_class="IfcCrewResource",
- name=None,
- predefined_type="NOTDEFINED",
- ):
- """Add a new construction resource
-
- Construction resources may be managed and connected to cost schedules
- and construction schedules. This allows calculations to be done on
- resource utilisation, cost optimisation (e.g. labour rates), and
- optioneering on build strategies.
-
- There are typically two types of resources. Crew resources are resources
- where you manage your own crew and you have full control over the
- equipment, labour, products, and materials used by your crew.
- Alternatively, there are subcontractor resources, where you simply
- delegate all the details to a subcontractor and it is not decomposed
- into further levels of detail.
-
- This means when adding resources, you'd first either add a crew or
- subcontract resource. If it is a crew resource, you'd then add child
- resources to that crew, such as equipment (cranes, excavators, hoists,
- etc), material (wood, concrete, etc), and labour (rigging crews,
- formworkers, etc).
-
- :param parent_resource: If this is a child resource (typically to a crew
- resource), then nominate the parent IfcConstructionResource here.
- :type parent_resource: ifcopenshell.entity_instance
- :param ifc_class: The class of resource chosen from
- IfcConstructionEquipmentResource, IfcConstructionMaterialResource,
- IfcConstructionProductResource, IfcCrewResource, IfcLaborResource,
- or IfcSubContractResource.
- :type ifc_class: str,optional
- :param name: The name of the resource
- :type name: str,optional
- :param predefined_type: Consult the IFC documentation for the valid
- predefined types for each type of resource class.
- :type predefined_type: str,optional
- :return: The newly created resource depending on the nominated IFC
- class.
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
-
- # Add some labour to our crew.
- ifcopenshell.api.run("resource.add_resource", model, parent_resource=crew, ifc_class="IfcLaborResource")
- """
- self.file = file
- self.settings = {
- "parent_resource": parent_resource,
- "ifc_class": ifc_class,
- "name": name,
- "predefined_type": predefined_type,
- }
-
- def execute(self):
- resource = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class=self.settings["ifc_class"],
- predefined_type=self.settings["predefined_type"],
- name=self.settings["name"] or "Unnamed",
+ ifc_class=settings["ifc_class"],
+ predefined_type=settings["predefined_type"],
+ name=settings["name"] or "Unnamed",
+ )
+ # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ?
+ # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550
+ if settings["parent_resource"]:
+ ifcopenshell.api.run(
+ "nest.assign_object",
+ file,
+ related_objects=[resource],
+ relating_object=settings["parent_resource"],
)
- # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ?
- # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550
- if self.settings["parent_resource"]:
- ifcopenshell.api.run(
- "nest.assign_object",
- self.file,
- related_objects=[resource],
- relating_object=self.settings["parent_resource"],
- )
- else:
- context = self.file.by_type("IfcContext")[0]
- ifcopenshell.api.run(
- "project.assign_declaration",
- self.file,
- definitions=[resource],
- relating_context=context,
- )
- return resource
+ else:
+ context = file.by_type("IfcContext")[0]
+ ifcopenshell.api.run(
+ "project.assign_declaration",
+ file,
+ definitions=[resource],
+ relating_context=context,
+ )
+ return resource
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py
index 4e5ef0c0a0..6600a06ae2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py
@@ -19,58 +19,55 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, resource=None, ifc_class="IfcQuantityCount"):
- """Adds a quantity to a resource
+def add_resource_quantity(file, resource=None, ifc_class="IfcQuantityCount") -> None:
+ """Adds a quantity to a resource
- The quantity of a resource represents the "unit quantity" of that
- resource. For example, labour might be hired on a daily basis (8 hours).
- There are different types of quantities (e.g. volume, count, or time).
- Which quantity is used depends on the type of resource. Material
- resources may be quantified in terms of length, area, volume, or weight.
- Equipment and labour resources are quantified in terms of time. Products
- resources are quantified in terms of counts.
+ The quantity of a resource represents the "unit quantity" of that
+ resource. For example, labour might be hired on a daily basis (8 hours).
+ There are different types of quantities (e.g. volume, count, or time).
+ Which quantity is used depends on the type of resource. Material
+ resources may be quantified in terms of length, area, volume, or weight.
+ Equipment and labour resources are quantified in terms of time. Products
+ resources are quantified in terms of counts.
- This base quantity is then used in other calculations.
+ This base quantity is then used in other calculations.
- :param resource: The IfcConstructionResource to add a quantity to.
- :type resource: ifcopenshell.entity_instance
- :param ifc_class: The type of quantity to add, chosen from
- IfcQuantityArea (for material), IfcQuantityCount (for products),
- IfcQuantityLength (for material), IfcQuantityTime (for equipment or
- labour), IfcQuantityVolume (for material), and IfcQuantityWeight
- (for material).
- :type ifc_class: str,optional
- :return: The newly created quantity depending on the IFC class
- :rtype: ifcopenshell.entity_instance
+ :param resource: The IfcConstructionResource to add a quantity to.
+ :type resource: ifcopenshell.entity_instance
+ :param ifc_class: The type of quantity to add, chosen from
+ IfcQuantityArea (for material), IfcQuantityCount (for products),
+ IfcQuantityLength (for material), IfcQuantityTime (for equipment or
+ labour), IfcQuantityVolume (for material), and IfcQuantityWeight
+ (for material).
+ :type ifc_class: str,optional
+ :return: The newly created quantity depending on the IFC class
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Add some labour to our crew.
- labour = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcLaborResource")
+ # Add some labour to our crew.
+ labour = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcLaborResource")
- # Labour resource is quantified in terms of time.
- quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=labour, ifc_class="IfcQuantityTime")
+ # Labour resource is quantified in terms of time.
+ quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=labour, ifc_class="IfcQuantityTime")
- # Store the time used in hours
- ifcopenshell.api.run("resource.edit_resource_quantity", model,
- physical_quantity=quantity, attributes={"TimeValue": 8.0})
- """
- self.file = file
- self.settings = {"resource": resource, "ifc_class": ifc_class}
+ # Store the time used in hours
+ ifcopenshell.api.run("resource.edit_resource_quantity", model,
+ physical_quantity=quantity, attributes={"TimeValue": 8.0})
+ """
+ settings = {"resource": resource, "ifc_class": ifc_class}
- def execute(self):
- quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed")
- quantity[3] = 0.0
- old_quantity = self.settings["resource"].BaseQuantity
- self.settings["resource"].BaseQuantity = quantity
- if old_quantity:
- ifcopenshell.util.element.remove_deep(self.file, old_quantity)
- return quantity
+ quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
+ quantity[3] = 0.0
+ old_quantity = settings["resource"].BaseQuantity
+ settings["resource"].BaseQuantity = quantity
+ if old_quantity:
+ ifcopenshell.util.element.remove_deep(file, old_quantity)
+ return quantity
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py
index 8627e319a7..3066441330 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py
@@ -19,50 +19,47 @@
import ifcopenshell.util.date
-class Usecase:
- def __init__(self, file, resource=None):
- """Adds the time that a resource is used for
+def add_resource_time(file, resource=None) -> None:
+ """Adds the time that a resource is used for
- For labour and equipment resources, the total duration that the resource
- is used for may be stored. This may either be input manually or
- calculated parametrically. This is known as the resource time, and may
- be used to calculate other parameters like resource utilisation.
+ For labour and equipment resources, the total duration that the resource
+ is used for may be stored. This may either be input manually or
+ calculated parametrically. This is known as the resource time, and may
+ be used to calculate other parameters like resource utilisation.
- :param resource: The IfcConstructionResource to record time for.
- :type resource: ifcopenshell.entity_instance
- :return: The newly created IfcResourceTime
- :rtype: ifcopenshell.entity_instance
+ :param resource: The IfcConstructionResource to record time for.
+ :type resource: ifcopenshell.entity_instance
+ :return: The newly created IfcResourceTime
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Add some labour to our crew.
- labour = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcLaborResource")
+ # Add some labour to our crew.
+ labour = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcLaborResource")
- # Labour resource is quantified in terms of time.
- quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=labour, ifc_class="IfcQuantityTime")
+ # Labour resource is quantified in terms of time.
+ quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=labour, ifc_class="IfcQuantityTime")
- # Store the unit time used in hours
- ifcopenshell.api.run("resource.edit_resource_quantity", model,
- physical_quantity=quantity, attributes={"TimeValue": 8.0})
+ # Store the unit time used in hours
+ ifcopenshell.api.run("resource.edit_resource_quantity", model,
+ physical_quantity=quantity, attributes={"TimeValue": 8.0})
- # Let's imagine we've used the resource for 2 days.
- time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
- ifcopenshell.api.run("resource.edit_resource_time", model,
- resource_time=time, attributes={"ScheduleWork": "PT16H"})
- """
- self.file = file
- self.settings = {
- "resource": resource,
- }
+ # Let's imagine we've used the resource for 2 days.
+ time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
+ ifcopenshell.api.run("resource.edit_resource_time", model,
+ resource_time=time, attributes={"ScheduleWork": "PT16H"})
+ """
+ settings = {
+ "resource": resource,
+ }
- def execute(self):
- resource_time = self.file.create_entity("IfcResourceTime")
- self.settings["resource"].Usage = resource_time
- return resource_time
+ resource_time = file.create_entity("IfcResourceTime")
+ settings["resource"].Usage = resource_time
+ return resource_time
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py
index f71ec00260..44d856ef0f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py
@@ -20,101 +20,93 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_resource=None, related_object=None):
- """Assigns a resource to an object
+def assign_resource(file, relating_resource=None, related_object=None) -> None:
+ """Assigns a resource to an object
- Two types of objects are typically assigned to resources: products and
- actors.
+ Two types of objects are typically assigned to resources: products and
+ actors.
- If a product is assigned to a resource, that means that the product
- represents the resource on site. This may be represented via material
- handling zones on a construction site, or equipment like cranes and
- their physical locations.
+ If a product is assigned to a resource, that means that the product
+ represents the resource on site. This may be represented via material
+ handling zones on a construction site, or equipment like cranes and
+ their physical locations.
- If an actor is assigned to a resource, that means that the actor (person
- or organisation) is the actor consuming the resource (e.g. if the
- resource is material or equipment) or the actor performing the work
- (e.g. if the resource is a labour resource).
+ If an actor is assigned to a resource, that means that the actor (person
+ or organisation) is the actor consuming the resource (e.g. if the
+ resource is material or equipment) or the actor performing the work
+ (e.g. if the resource is a labour resource).
- :param relating_resource: The IfcResource to assign the object to.
- :type relating_resource: ifcopenshell.entity_instance
- :param related_object: The IfcProduct or IfcActor to assign to the
- object.
- :type related_object: ifcopenshell.entity_instance
- :return: The newly created IfcRelAssignsToResource
- :rtype: ifcopenshell.entity_instance
+ :param relating_resource: The IfcResource to assign the object to.
+ :type relating_resource: ifcopenshell.entity_instance
+ :param related_object: The IfcProduct or IfcActor to assign to the
+ object.
+ :type related_object: ifcopenshell.entity_instance
+ :return: The newly created IfcRelAssignsToResource
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Add some a tower crane to our crew.
- crane = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01")
+ # Add some a tower crane to our crew.
+ crane = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01")
- # Our tower crane will be placed via this physical product.
- product = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcBuildingElementProxy", predefined_type="CRANE")
+ # Our tower crane will be placed via this physical product.
+ product = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcBuildingElementProxy", predefined_type="CRANE")
- # Let's place our crane at some X, Y coordinates.
- matrix = numpy.eye(4)
- matrix[0][3], matrix[1][3] = 3.0, 4.0
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=crane, matrix=matrix)
+ # Let's place our crane at some X, Y coordinates.
+ matrix = numpy.eye(4)
+ matrix[0][3], matrix[1][3] = 3.0, 4.0
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=crane, matrix=matrix)
- # Let's assign our crane to the resource. The crane now represents
- # the resource.
- ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=product)
+ # Let's assign our crane to the resource. The crane now represents
+ # the resource.
+ ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=product)
- # Setup an organisation actor who will operate the crane
- organisation = ifcopenshell.api.run("owner.add_organisation", model,
- identification="UCO", name="Unionised Crane Operators Pty Ltd")
- role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="CREW")
- actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
+ # Setup an organisation actor who will operate the crane
+ organisation = ifcopenshell.api.run("owner.add_organisation", model,
+ identification="UCO", name="Unionised Crane Operators Pty Ltd")
+ role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="CREW")
+ actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation)
- # This means that UCO is now our crane operator.
- ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=actor)
- """
- self.file = file
- self.settings = {
- "relating_resource": relating_resource,
- "related_object": related_object,
- }
+ # This means that UCO is now our crane operator.
+ ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=actor)
+ """
+ settings = {
+ "relating_resource": relating_resource,
+ "related_object": related_object,
+ }
- def execute(self):
- if self.settings["related_object"].HasAssignments:
- for assignment in self.settings["related_object"].HasAssignments:
- if (
- assignment.is_a("IfclRelAssignsToResource")
- and assignment.RelatingResource
- == self.settings["relating_resource"]
- ):
- return
+ if settings["related_object"].HasAssignments:
+ for assignment in settings["related_object"].HasAssignments:
+ if (
+ assignment.is_a("IfclRelAssignsToResource")
+ and assignment.RelatingResource == settings["relating_resource"]
+ ):
+ return
- resource_of = None
- if self.settings["relating_resource"].ResourceOf:
- resource_of = self.settings["relating_resource"].ResourceOf[0]
+ resource_of = None
+ if settings["relating_resource"].ResourceOf:
+ resource_of = settings["relating_resource"].ResourceOf[0]
- if resource_of:
- related_objects = list(resource_of.RelatedObjects)
- related_objects.append(self.settings["related_object"])
- resource_of.RelatedObjects = related_objects
- ifcopenshell.api.run(
- "owner.update_owner_history", self.file, **{"element": resource_of}
- )
- else:
- resource_of = self.file.create_entity(
- "IfcRelAssignsToResource",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run(
- "owner.create_owner_history", self.file
- ),
- "RelatedObjects": [self.settings["related_object"]],
- "RelatingResource": self.settings["relating_resource"],
- }
- )
- return resource_of
+ if resource_of:
+ related_objects = list(resource_of.RelatedObjects)
+ related_objects.append(settings["related_object"])
+ resource_of.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": resource_of})
+ else:
+ resource_of = file.create_entity(
+ "IfcRelAssignsToResource",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["related_object"]],
+ "RelatingResource": settings["relating_resource"],
+ }
+ )
+ return resource_of
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py
index 08602c9f0f..fc63b2d9d4 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py
@@ -23,42 +23,29 @@ import ifcopenshell.util.element
import ifcopenshell.util.resource
-class Usecase:
- def __init__(self, file, resource=None):
- """Calculates the number of resources required to perform scheduled work on a task.
- """
- self.file = file
- self.settings = {"resource": resource}
+def calculate_resource_usage(file, resource=None) -> None:
+ """Calculates the number of resources required to perform scheduled work on a task."""
+ settings = {"resource": resource}
- def execute(self):
- if ifcopenshell.util.constraint.is_attribute_locked(self.settings["resource"], "Usage.ScheduleUsage"):
- return
- if (
- not self.settings["resource"].Usage
- or not self.settings["resource"].Usage.ScheduleWork
- ):
- return
+ if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleUsage"):
+ return
+ if not settings["resource"].Usage or not settings["resource"].Usage.ScheduleWork:
+ return
- task = ifcopenshell.util.resource.get_task_assignments(
- self.settings["resource"]
- )
- if not task or not task.TaskTime:
- return
+ task = ifcopenshell.util.resource.get_task_assignments(settings["resource"])
+ if not task or not task.TaskTime:
+ return
- if not task.TaskTime.DurationType or task.TaskTime.DurationType == "WORKTIME":
- hours_per_day = 8
- else:
- hours_per_day = 24
+ if not task.TaskTime.DurationType or task.TaskTime.DurationType == "WORKTIME":
+ hours_per_day = 8
+ else:
+ hours_per_day = 24
- task_duration = ifcopenshell.util.date.ifc2datetime(
- task.TaskTime.ScheduleDuration
- )
- seconds = task_duration.days * hours_per_day * 60 * 60
- seconds += task_duration.seconds
+ task_duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration)
+ seconds = task_duration.days * hours_per_day * 60 * 60
+ seconds += task_duration.seconds
- person_hours = ifcopenshell.util.date.ifc2datetime(
- self.settings["resource"].Usage.ScheduleWork
- )
+ person_hours = ifcopenshell.util.date.ifc2datetime(settings["resource"].Usage.ScheduleWork)
- required_resources = person_hours.total_seconds() / seconds
- self.settings["resource"].Usage.ScheduleUsage = float(required_resources)
+ required_resources = person_hours.total_seconds() / seconds
+ settings["resource"].Usage.ScheduleUsage = float(required_resources)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py
index 746f0d88c0..dd5621d386 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py
@@ -23,52 +23,49 @@ import ifcopenshell.util.element
import ifcopenshell.util.resource
-class Usecase:
- def __init__(self, file, resource=None):
- """Calculates the work that a resource is used for
+def calculate_resource_work(file, resource=None) -> None:
+ """Calculates the work that a resource is used for
- This is an unofficial parametric calculation that may be done on a
- resource based on careful analysis of the relationships between the
- costing, scheduling, and resource domains in IFC.
+ This is an unofficial parametric calculation that may be done on a
+ resource based on careful analysis of the relationships between the
+ costing, scheduling, and resource domains in IFC.
- A resource may store a productivity rate in a property set called
- EPset_Productivity. This stores three properties:
+ A resource may store a productivity rate in a property set called
+ EPset_Productivity. This stores three properties:
- * BaseQuantityConsumed - a duration that the resource is consumed for.
- * BaseQuantityProducedName - what quantity the resource can produce,
- such as area or volume.
- * BaseQuantityProducedValue - what value of that quantity the resource
- can produce during that duration.
+ * BaseQuantityConsumed - a duration that the resource is consumed for.
+ * BaseQuantityProducedName - what quantity the resource can produce,
+ such as area or volume.
+ * BaseQuantityProducedValue - what value of that quantity the resource
+ can produce during that duration.
- For example, a labour or equipment resource might produce 100m3 of
- NetVolume every day (i.e. 8 hours are consumed).
+ For example, a labour or equipment resource might produce 100m3 of
+ NetVolume every day (i.e. 8 hours are consumed).
- Then, if a resource is assigned to a construction task, and that
- construction task is assigned to concrete slabs totalling 200m3, we can
- calculate that the resource consumes 16 hours of work.
+ Then, if a resource is assigned to a construction task, and that
+ construction task is assigned to concrete slabs totalling 200m3, we can
+ calculate that the resource consumes 16 hours of work.
- This calculated work is stored against the resource as scheduled work
- under the resource time data.
+ This calculated work is stored against the resource as scheduled work
+ under the resource time data.
- :param resource: The IfcConstructionResource that you want to calculate
- the work performed.
- :type resource: ifcopenshell.entity_instance
- :return None:
- :rtype: None:
- """
- self.file = file
- self.settings = {"resource": resource}
+ :param resource: The IfcConstructionResource that you want to calculate
+ the work performed.
+ :type resource: ifcopenshell.entity_instance
+ :return None:
+ :rtype: None:
+ """
+ settings = {"resource": resource}
- def execute(self):
- if ifcopenshell.util.constraint.is_attribute_locked(self.settings["resource"], "Usage.ScheduleWork"):
- return
- amount_worked = ifcopenshell.util.resource.get_resource_required_work(self.settings["resource"])
- if not amount_worked:
- return
- if not self.settings["resource"].Usage:
- ifcopenshell.api.run(
- "resource.add_resource_time",
- self.file,
- resource=self.settings["resource"],
- )
- self.settings["resource"].Usage.ScheduleWork = amount_worked
+ if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleWork"):
+ return
+ amount_worked = ifcopenshell.util.resource.get_resource_required_work(settings["resource"])
+ if not amount_worked:
+ return
+ if not settings["resource"].Usage:
+ ifcopenshell.api.run(
+ "resource.add_resource_time",
+ file,
+ resource=settings["resource"],
+ )
+ settings["resource"].Usage.ScheduleWork = amount_worked
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py
index c28f4c0661..2ab8ac669a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, resource=None, attributes=None):
- """Edits the attributes of an IfcResource
+def edit_resource(file, resource=None, attributes=None) -> None:
+ """Edits the attributes of an IfcResource
- For more information about the attributes and data types of an
- IfcResource, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcResource, consult the IFC documentation.
- :param resource: The IfcResource entity you want to edit
- :type resource: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param resource: The IfcResource entity you want to edit
+ :type resource: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Change the name of the resource to "Zone A Crew"
- ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"})
- """
- self.file = file
- self.settings = {"resource": resource, "attributes": attributes or {}}
+ # Change the name of the resource to "Zone A Crew"
+ ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"})
+ """
+ settings = {"resource": resource, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["resource"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["resource"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py
index 0785caa02e..b4d016c7ad 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py
@@ -17,45 +17,42 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, physical_quantity=None, attributes=None):
- """Edits the attributes of an IFC quantity
+def edit_resource_quantity(file, physical_quantity=None, attributes=None) -> None:
+ """Edits the attributes of an IFC quantity
- For more information about the attributes and data types of an
- IfC quantity, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfC quantity, consult the IFC documentation.
- :param physical_quantity: The IfC quantity entity you want to edit
- :type physical_quantity: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param physical_quantity: The IfC quantity entity you want to edit
+ :type physical_quantity: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Add some labour to our crew.
- labour = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcLaborResource")
+ # Add some labour to our crew.
+ labour = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcLaborResource")
- # Labour resource is quantified in terms of time.
- ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=labour, ifc_class="IfcQuantityTime")
+ # Labour resource is quantified in terms of time.
+ ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=labour, ifc_class="IfcQuantityTime")
- # Store the time used in hours
- ifcopenshell.api.run("resource.edit_resource_quantity", model,
- physical_quantity=time, attributes={"TimeValue": 8.0})
- """
- self.file = file
- self.settings = {
- "physical_quantity": physical_quantity,
- "attributes": attributes or {},
- }
+ # Store the time used in hours
+ ifcopenshell.api.run("resource.edit_resource_quantity", model,
+ physical_quantity=time, attributes={"TimeValue": 8.0})
+ """
+ settings = {
+ "physical_quantity": physical_quantity,
+ "attributes": attributes or {},
+ }
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["physical_quantity"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["physical_quantity"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py
index c9db827a89..9ec41a60c5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py
@@ -20,47 +20,50 @@ import datetime
import ifcopenshell
+def edit_resource_time(file, resource_time=None, attributes=None) -> None:
+ """Edits the attributes of an IfcResourceTime
+
+ For more information about the attributes and data types of an
+ IfcResourceTime, consult the IFC documentation.
+
+ :param resource_time: The IfcResourceTime entity you want to edit
+ :type resource_time: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+
+ # Add some labour to our crew.
+ labour = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcLaborResource")
+
+ # Labour resource is quantified in terms of time.
+ ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=labour, ifc_class="IfcQuantityTime")
+
+ # Store the unit time used in hours
+ ifcopenshell.api.run("resource.edit_resource_quantity", model,
+ physical_quantity=time, attributes={"TimeValue": 8.0})
+
+ # Let's imagine we've used the resource for 2 days.
+ time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
+ ifcopenshell.api.run("resource.edit_resource_time", model,
+ resource_time=time, attributes={"ScheduleWork": "P16H"})
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"resource_time": resource_time, "attributes": attributes or {}}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, resource_time=None, attributes=None):
- """Edits the attributes of an IfcResourceTime
-
- For more information about the attributes and data types of an
- IfcResourceTime, consult the IFC documentation.
-
- :param resource_time: The IfcResourceTime entity you want to edit
- :type resource_time: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
-
- # Add some labour to our crew.
- labour = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcLaborResource")
-
- # Labour resource is quantified in terms of time.
- ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=labour, ifc_class="IfcQuantityTime")
-
- # Store the unit time used in hours
- ifcopenshell.api.run("resource.edit_resource_quantity", model,
- physical_quantity=time, attributes={"TimeValue": 8.0})
-
- # Let's imagine we've used the resource for 2 days.
- time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
- ifcopenshell.api.run("resource.edit_resource_time", model,
- resource_time=time, attributes={"ScheduleWork": "P16H"})
- """
- self.file = file
- self.settings = {"resource_time": resource_time, "attributes": attributes or {}}
-
def execute(self):
self.resource = self.get_resource()
@@ -70,43 +73,25 @@ class Usecase:
and "ScheduleFinish" in self.settings["attributes"].keys()
):
del self.settings["attributes"]["ScheduleFinish"]
- if (
- self.settings["attributes"].get("ActualWork", None)
- and "ActualFinish" in self.settings["attributes"].keys()
- ):
+ if self.settings["attributes"].get("ActualWork", None) and "ActualFinish" in self.settings["attributes"].keys():
del self.settings["attributes"]["ActualFinish"]
for name, value in self.settings["attributes"].items():
- metrics = ifcopenshell.util.constraint.get_metric_constraints(
- self.resource, "Usage." + name
- )
+ metrics = ifcopenshell.util.constraint.get_metric_constraints(self.resource, "Usage." + name)
if metrics and ifcopenshell.util.constraint.is_hard_constraint(metrics[0]):
continue
if value:
if "Start" in name or "Finish" in name or name == "StatusTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
- elif (
- name == "ScheduleWork"
- or name == "ActualWork"
- or name == "RemainingTime"
- ):
+ elif name == "ScheduleWork" or name == "ActualWork" or name == "RemainingTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["resource_time"], name, value)
- if (
- name == "ScheduleUsage"
- and ifcopenshell.util.constraint.get_metric_constraints(
- self.resource, "Usage.ScheduleWork"
- )
+ if name == "ScheduleUsage" and ifcopenshell.util.constraint.get_metric_constraints(
+ self.resource, "Usage.ScheduleWork"
):
task = ifcopenshell.util.resource.get_task_assignments(self.resource)
if task:
- ifcopenshell.api.run(
- "sequence.calculate_task_duration", self.file, task=task
- )
+ ifcopenshell.api.run("sequence.calculate_task_duration", self.file, task=task)
def get_resource(self):
- return [
- e
- for e in self.file.get_inverse(self.settings["resource_time"])
- if e.is_a("IfcResource")
- ][0]
+ return [e for e in self.file.get_inverse(self.settings["resource_time"]) if e.is_a("IfcResource")][0]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py
index db8153a3e0..cfbdd25fd9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py
@@ -21,71 +21,68 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, resource=None):
- """Removes a resource and all relationships
+def remove_resource(file, resource=None) -> None:
+ """Removes a resource and all relationships
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Fire our crew
- ifcopenshell.api.run("resource.remove_resource", model, resource=crew)
- """
- self.file = file
- self.settings = {"resource": resource}
+ # Fire our crew
+ ifcopenshell.api.run("resource.remove_resource", model, resource=crew)
+ """
+ settings = {"resource": resource}
- def execute(self):
- # TODO: review deep purge
- for inverse in self.file.get_inverse(self.settings["resource"]):
- if inverse.is_a("IfcRelNests"):
- if inverse.RelatingObject == self.settings["resource"]:
- for related_object in inverse.RelatedObjects:
- ifcopenshell.api.run(
- "resource.remove_resource",
- self.file,
- resource=related_object,
- )
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelAssignsToControl"):
- if len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- else:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["resource"])
- inverse.RelatedObjects = related_objects
- elif inverse.is_a("IfcRelAssignsToResource"):
- if inverse.RelatingResource == self.settings["resource"]:
- for related_object in inverse.RelatedObjects:
- ifcopenshell.api.run(
- "resource.unassign_resource",
- self.file,
- related_object=related_object,
- resource=self.settings["resource"],
- )
- elif inverse.RelatedObjects == tuple(self.settings["resource"]):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- if self.settings["resource"].Usage:
- self.file.remove(self.settings["resource"].Usage)
- if self.settings["resource"].BaseQuantity:
- ifcopenshell.api.run(
- "resource.remove_resource_quantity",
- self.file,
- resource=self.settings["resource"],
- )
- history = self.settings["resource"].OwnerHistory
- self.file.remove(self.settings["resource"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ # TODO: review deep purge
+ for inverse in file.get_inverse(settings["resource"]):
+ if inverse.is_a("IfcRelNests"):
+ if inverse.RelatingObject == settings["resource"]:
+ for related_object in inverse.RelatedObjects:
+ ifcopenshell.api.run(
+ "resource.remove_resource",
+ file,
+ resource=related_object,
+ )
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelAssignsToControl"):
+ if len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ else:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["resource"])
+ inverse.RelatedObjects = related_objects
+ elif inverse.is_a("IfcRelAssignsToResource"):
+ if inverse.RelatingResource == settings["resource"]:
+ for related_object in inverse.RelatedObjects:
+ ifcopenshell.api.run(
+ "resource.unassign_resource",
+ file,
+ related_object=related_object,
+ resource=settings["resource"],
+ )
+ elif inverse.RelatedObjects == tuple(settings["resource"]):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ if settings["resource"].Usage:
+ file.remove(settings["resource"].Usage)
+ if settings["resource"].BaseQuantity:
+ ifcopenshell.api.run(
+ "resource.remove_resource_quantity",
+ file,
+ resource=settings["resource"],
+ )
+ history = settings["resource"].OwnerHistory
+ file.remove(settings["resource"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py
index afafa9a193..221d94c5a6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py
@@ -19,34 +19,31 @@
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, resource=None):
- """Removes the base quantity of a resource
+def remove_resource_quantity(file, resource=None) -> None:
+ """Removes the base quantity of a resource
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Add some labour to our crew.
- labour = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcLaborResource")
+ # Add some labour to our crew.
+ labour = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcLaborResource")
- # Labour resource is quantified in terms of time.
- ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=labour, ifc_class="IfcQuantityTime")
+ # Labour resource is quantified in terms of time.
+ ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=labour, ifc_class="IfcQuantityTime")
- # Let's say we only want to store the resource but no quantities,
- # let's clean up our mess and remove the quantity.
- ifcopenshell.api.run("resource.remove_resource_quantity", model, resource=labour)
- """
- self.file = file
- self.settings = {"resource": resource}
+ # Let's say we only want to store the resource but no quantities,
+ # let's clean up our mess and remove the quantity.
+ ifcopenshell.api.run("resource.remove_resource_quantity", model, resource=labour)
+ """
+ settings = {"resource": resource}
- def execute(self):
- old_quantity = self.settings["resource"].BaseQuantity
- self.settings["resource"].BaseQuantity = None
- if old_quantity:
- ifcopenshell.util.element.remove_deep(self.file, old_quantity)
+ old_quantity = settings["resource"].BaseQuantity
+ settings["resource"].BaseQuantity = None
+ if old_quantity:
+ ifcopenshell.util.element.remove_deep(file, old_quantity)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py
index ceed0dbf2a..7b1a59f519 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py
@@ -21,65 +21,57 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_resource=None, related_object=None):
- """Removes the relationship between a resource and object
+def unassign_resource(file, relating_resource=None, related_object=None) -> None:
+ """Removes the relationship between a resource and object
- :param relating_resource: The IfcResource to assign the object to.
- :type relating_resource: ifcopenshell.entity_instance
- :param related_object: The IfcProduct or IfcActor to assign to the
- object.
- :type related_object: ifcopenshell.entity_instance
- :return: The newly created IfcRelAssignsToResource
- :rtype: ifcopenshell.entity_instance
+ :param relating_resource: The IfcResource to assign the object to.
+ :type relating_resource: ifcopenshell.entity_instance
+ :param related_object: The IfcProduct or IfcActor to assign to the
+ object.
+ :type related_object: ifcopenshell.entity_instance
+ :return: The newly created IfcRelAssignsToResource
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
- # Add some a tower crane to our crew.
- crane = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01")
+ # Add some a tower crane to our crew.
+ crane = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01")
- # Our tower crane will be placed via this physical product.
- product = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcBuildingElementProxy", predefined_type="CRANE")
+ # Our tower crane will be placed via this physical product.
+ product = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcBuildingElementProxy", predefined_type="CRANE")
- # Let's assign our crane to the resource. The crane now represents
- # the resource.
- ifcopenshell.api.run("resource.assign_resource", model,
- relating_resource=crane, related_object=product)
+ # Let's assign our crane to the resource. The crane now represents
+ # the resource.
+ ifcopenshell.api.run("resource.assign_resource", model,
+ relating_resource=crane, related_object=product)
- # Undo it.
- ifcopenshell.api.run("resource.unassign_resource", model,
- relating_resource=crane, related_object=product)
- """
- self.file = file
- self.settings = {
- "relating_resource": relating_resource,
- "related_object": related_object,
- }
+ # Undo it.
+ ifcopenshell.api.run("resource.unassign_resource", model,
+ relating_resource=crane, related_object=product)
+ """
+ settings = {
+ "relating_resource": relating_resource,
+ "related_object": related_object,
+ }
- def execute(self):
- for rel in self.settings["related_object"].HasAssignments or []:
- if (
- not rel.is_a("IfcRelAssignsToResource")
- or rel.RelatingResource != self.settings["relating_resource"]
- ):
- continue
- if len(rel.RelatedObjects) == 1:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_objects = list(rel.RelatedObjects)
- related_objects.remove(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run(
- "owner.update_owner_history", self.file, **{"element": rel}
- )
- return rel
+ for rel in settings["related_object"].HasAssignments or []:
+ if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != settings["relating_resource"]:
+ continue
+ if len(rel.RelatedObjects) == 1:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_objects = list(rel.RelatedObjects)
+ related_objects.remove(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py
index e0caddbe3c..309f87cfff 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py
@@ -15,3 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .copy_class import copy_class
+from .create_entity import create_entity
+from .reassign_class import reassign_class
+from .remove_product import remove_product
diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py
index 389930d409..976010c3c3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py
@@ -21,52 +21,55 @@ import ifcopenshell.util.system
import ifcopenshell.util.element
+def copy_class(file, product=None) -> None:
+ """Copies a product
+
+ The following relationships are also duplicated:
+
+ * The copy will have the same object placement coordinates as the
+ original.
+ * The copy will have duplicated property sets, properties, and quantities
+ * The copy will have all nested distribution ports copied too
+ * The copy will be part of the same aggregate
+ * The copy will be contained in the same spatial structure
+ * The copy, if it is an occurrence, will have the same type
+ * Voids are duplicated too
+ * The copy will have the same material as the original. Parametric
+ material set usages will be copied.
+ * The copy will be part of the same groups as the original.
+
+ Be warned that:
+
+ * Representations are _not_ copied. Copying representations is an
+ expensive operation so for now the user is responsible for handling
+ representations.
+ * Filled voids are not copied, as there is no guarantee that the filling
+ will also be copied.
+ * Path connectivity is not copied, as there is no guarantee that the
+ connections are still valid.
+
+ :param product: The IfcProduct to copy.
+ :type param: ifcopenshell.entity_instance
+ :return: The copied product
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # We have a wall
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # And now we have two
+ wall_copy = ifcopenshell.api.run("root.copy_class", model, product=wall)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"product": product}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, product=None):
- """Copies a product
-
- The following relationships are also duplicated:
-
- * The copy will have the same object placement coordinates as the
- original.
- * The copy will have duplicated property sets, properties, and quantities
- * The copy will have all nested distribution ports copied too
- * The copy will be part of the same aggregate
- * The copy will be contained in the same spatial structure
- * The copy, if it is an occurrence, will have the same type
- * Voids are duplicated too
- * The copy will have the same material as the original. Parametric
- material set usages will be copied.
- * The copy will be part of the same groups as the original.
-
- Be warned that:
-
- * Representations are _not_ copied. Copying representations is an
- expensive operation so for now the user is responsible for handling
- representations.
- * Filled voids are not copied, as there is no guarantee that the filling
- will also be copied.
- * Path connectivity is not copied, as there is no guarantee that the
- connections are still valid.
-
- :param product: The IfcProduct to copy.
- :type param: ifcopenshell.entity_instance
- :return: The copied product
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # We have a wall
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # And now we have two
- wall_copy = ifcopenshell.api.run("root.copy_class", model, product=wall)
- """
- self.file = file
- self.settings = {"product": product}
-
def execute(self):
result = ifcopenshell.util.element.copy(self.file, self.settings["product"])
self.copy_direct_attributes(result)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py
index 7619eec067..5bb64d411a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py
@@ -21,63 +21,65 @@ import ifcopenshell.api
from typing import Optional
+def create_entity(
+ file: ifcopenshell.file,
+ ifc_class: str = "IfcBuildingElementProxy",
+ predefined_type: Optional[str] = None,
+ name: Optional[str] = None,
+) -> ifcopenshell.entity_instance:
+ """Create a new rooted product
+
+ This is a critical function used to create almost any rooted product or
+ product type. If you want to create walls, spaces, buildings, wall
+ types, and so on, use this function.
+
+ Just specify the class you want to create, as well as the predefined
+ type and name. It will handle the storage of the predefined type and
+ check whether the predefined type is built-in or custom. It will also
+ generate a valid GlobalId and store ownership history. It will also
+ handle some edge cases for default validity where users might forget to
+ populate some mandatory attributes. For example, doors must define an
+ operation type but many people forget.
+
+ :param ifc_class: Any rooted IFC class.
+ :type ifc_class: str,optional
+ :param predefined_type: Any built-in or user-defined predefined type that
+ is applicable to that IFC class. For user-defined predefined types
+ just enter in any value and the API will handle it automatically.
+ :type predefined_type: str,optional
+ :param name: The name of the new element.
+ :type name: str,optional
+ :return: The newly created element based on the specified IFC class.
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # We have a project.
+ ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
+
+ # We have a building.
+ ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding")
+
+ # We have a wall.
+ ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # We have a wall type.
+ ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "ifc_class": ifc_class,
+ "predefined_type": predefined_type,
+ "name": name,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- ifc_class: str = "IfcBuildingElementProxy",
- predefined_type: Optional[str] = None,
- name: Optional[str] = None,
- ):
- """Create a new rooted product
-
- This is a critical function used to create almost any rooted product or
- product type. If you want to create walls, spaces, buildings, wall
- types, and so on, use this function.
-
- Just specify the class you want to create, as well as the predefined
- type and name. It will handle the storage of the predefined type and
- check whether the predefined type is built-in or custom. It will also
- generate a valid GlobalId and store ownership history. It will also
- handle some edge cases for default validity where users might forget to
- populate some mandatory attributes. For example, doors must define an
- operation type but many people forget.
-
- :param ifc_class: Any rooted IFC class.
- :type ifc_class: str,optional
- :param predefined_type: Any built-in or user-defined predefined type that
- is applicable to that IFC class. For user-defined predefined types
- just enter in any value and the API will handle it automatically.
- :type predefined_type: str,optional
- :param name: The name of the new element.
- :type name: str,optional
- :return: The newly created element based on the specified IFC class.
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # We have a project.
- ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
-
- # We have a building.
- ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding")
-
- # We have a wall.
- ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # We have a wall type.
- ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType")
- """
- self.file = file
- self.settings = {
- "ifc_class": ifc_class,
- "predefined_type": predefined_type,
- "name": name,
- }
-
- def execute(self) -> ifcopenshell.entity_instance:
+ def execute(self):
element = self.file.create_entity(
self.settings["ifc_class"],
**{
diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py
index 1035a6b17c..c124786a83 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py
@@ -22,61 +22,63 @@ import ifcopenshell.util.schema
import ifcopenshell.util.element
+def reassign_class(
+ file,
+ product=None,
+ ifc_class="IfcBuildingElementProxy",
+ predefined_type=None,
+) -> None:
+ """Changes the class of a product
+
+ If you ever created a wall then realised it's meant to be something
+ else, this function lets you change the IFC class whilst retaining all
+ other geometry and relationships.
+
+ This is especially useful when dealing with poorly classified data from
+ proprietary software with limited IFC capabilities.
+
+ If you are reassigning a type, the occurrence classes are also
+ reassigned to maintain validity.
+
+ Vice versa, if you are reassigning an occurrence, the type is also
+ reassigned in IFC4 and up. In IFC2X3, this may not occur if the type
+ cannot be unambiguously derived, so you are required to manually check
+ this.
+
+ :param product: The IfcProduct that you want to change the class of.
+ :type product: ifcopenshell.entity_instance
+ :param ifc_class: The new IFC class you want to change it to.
+ :type ifc_class: str,optional
+ :param predefined_type: In case you want to change the predefined type
+ too. User defined types are also allowed, just type what you want.
+ :type predefined_type: str,optional
+ :return: The newly modified product.
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # We have a wall.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # Oh, did I say wall? I meant slab.
+ slab = ifcopenshell.api.run("root.reassign_class", model, product=wall, ifc_class="IfcSlab")
+
+ # Warning: this will crash since wall doesn't exist any more.
+ print(wall) # Kaboom.
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "product": product,
+ "ifc_class": ifc_class,
+ "predefined_type": predefined_type,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file,
- product=None,
- ifc_class="IfcBuildingElementProxy",
- predefined_type=None,
- ):
- """Changes the class of a product
-
- If you ever created a wall then realised it's meant to be something
- else, this function lets you change the IFC class whilst retaining all
- other geometry and relationships.
-
- This is especially useful when dealing with poorly classified data from
- proprietary software with limited IFC capabilities.
-
- If you are reassigning a type, the occurrence classes are also
- reassigned to maintain validity.
-
- Vice versa, if you are reassigning an occurrence, the type is also
- reassigned in IFC4 and up. In IFC2X3, this may not occur if the type
- cannot be unambiguously derived, so you are required to manually check
- this.
-
- :param product: The IfcProduct that you want to change the class of.
- :type product: ifcopenshell.entity_instance
- :param ifc_class: The new IFC class you want to change it to.
- :type ifc_class: str,optional
- :param predefined_type: In case you want to change the predefined type
- too. User defined types are also allowed, just type what you want.
- :type predefined_type: str,optional
- :return: The newly modified product.
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # We have a wall.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # Oh, did I say wall? I meant slab.
- slab = ifcopenshell.api.run("root.reassign_class", model, product=wall, ifc_class="IfcSlab")
-
- # Warning: this will crash since wall doesn't exist any more.
- print(wall) # Kaboom.
- """
- self.file = file
- self.settings = {
- "product": product,
- "ifc_class": ifc_class,
- "predefined_type": predefined_type,
- }
-
def execute(self):
element = self.reassign_class(self.settings["product"], self.settings["ifc_class"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py
index cecb5544f3..6a1c48b404 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py
@@ -20,212 +20,207 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, product: ifcopenshell.entity_instance):
- """Removes a product
+def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instance) -> None:
+ """Removes a product
- This is effectively a smart delete function that not only removes a
- product, but also all of its relationships. It is always recommended to
- use this function to prevent orphaned data in your IFC model.
+ This is effectively a smart delete function that not only removes a
+ product, but also all of its relationships. It is always recommended to
+ use this function to prevent orphaned data in your IFC model.
- This is intended to be used for removing:
+ This is intended to be used for removing:
- - IfcAnnotation
- - IfcElement
- - IfcElementType
- - IfcSpatialElement
- - IfcSpatialElementType
+ - IfcAnnotation
+ - IfcElement
+ - IfcElementType
+ - IfcSpatialElement
+ - IfcSpatialElementType
- For example, geometric representations are removed. Placement
- coordinates are also removed. Properties are removed. Material, type,
- containment, aggregation, and nesting relationships are removed (but
- naturally, the materials, types, containers, etc themselves remain).
+ For example, geometric representations are removed. Placement
+ coordinates are also removed. Properties are removed. Material, type,
+ containment, aggregation, and nesting relationships are removed (but
+ naturally, the materials, types, containers, etc themselves remain).
- :param product: The element to remove.
- :type product: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param product: The element to remove.
+ :type product: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # We have a wall.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # We have a wall.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # No we don't.
- ifcopenshell.api.run("root.remove_product", model, product=wall)
- """
- self.file = file
- self.settings = {"product": product}
+ # No we don't.
+ ifcopenshell.api.run("root.remove_product", model, product=wall)
+ """
+ settings = {"product": product}
- def execute(self) -> None:
- representations = []
- if self.settings["product"].is_a("IfcProduct"):
- if self.settings["product"].Representation:
- representations = self.settings["product"].Representation.Representations or []
- else:
- representations = []
+ representations = []
+ if settings["product"].is_a("IfcProduct"):
+ if settings["product"].Representation:
+ representations = settings["product"].Representation.Representations or []
+ else:
+ representations = []
- # remove object placements
- object_placement = self.settings["product"].ObjectPlacement
- if object_placement:
- if self.file.get_total_inverses(object_placement) == 1:
- self.settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work
- ifcopenshell.util.element.remove_deep2(self.file, object_placement)
+ # remove object placements
+ object_placement = settings["product"].ObjectPlacement
+ if object_placement:
+ if file.get_total_inverses(object_placement) == 1:
+ settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work
+ ifcopenshell.util.element.remove_deep2(file, object_placement)
- elif self.settings["product"].is_a("IfcTypeProduct"):
- representations = [rm.MappedRepresentation for rm in self.settings["product"].RepresentationMaps or []]
+ elif settings["product"].is_a("IfcTypeProduct"):
+ representations = [rm.MappedRepresentation for rm in settings["product"].RepresentationMaps or []]
- # remove psets
- psets = self.settings["product"].HasPropertySets or []
- for pset in psets:
- if self.file.get_total_inverses(pset) != 1:
- continue
- ifcopenshell.api.run(
- "pset.remove_pset",
- self.file,
- product=self.settings["product"],
- pset=pset,
- )
-
- for representation in representations:
- ifcopenshell.api.run(
- "geometry.unassign_representation",
- self.file,
- **{"product": self.settings["product"], "representation": representation}
- )
- ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation})
- for opening in getattr(self.settings["product"], "HasOpenings", []) or []:
- ifcopenshell.api.run("void.remove_opening", self.file, opening=opening.RelatedOpeningElement)
-
- if self.settings["product"].is_a("IfcGrid"):
- for axis in (
- self.settings["product"].UAxes + self.settings["product"].VAxes + (self.settings["product"].WAxes or ())
- ):
- 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:
- inverse = self.file.by_id(inverse_id)
- except:
+ # remove psets
+ psets = settings["product"].HasPropertySets or []
+ for pset in psets:
+ if file.get_total_inverses(pset) != 1:
continue
- if inverse.is_a("IfcRelDefinesByProperties"):
- ifcopenshell.api.run(
- "pset.remove_pset",
- self.file,
- product=self.settings["product"],
- pset=inverse.RelatingPropertyDefinition,
- )
- elif inverse.is_a("IfcRelAssociatesMaterial"):
- ifcopenshell.api.run("material.unassign_material", self.file, products=[self.settings["product"]])
- elif inverse.is_a("IfcRelDefinesByType"):
- if inverse.RelatingType == self.settings["product"]:
- ifcopenshell.api.run("type.unassign_type", self.file, related_objects=inverse.RelatedObjects)
- else:
- ifcopenshell.api.run("type.unassign_type", self.file, related_objects=[self.settings["product"]])
- elif inverse.is_a("IfcRelSpaceBoundary"):
- ifcopenshell.api.run("boundary.remove_boundary", self.file, boundary=inverse)
- elif inverse.is_a("IfcRelFillsElement"):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelVoidsElement"):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelServicesBuildings"):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- 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)
- # 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"],):
+ ifcopenshell.api.run(
+ "pset.remove_pset",
+ file,
+ product=settings["product"],
+ pset=pset,
+ )
+
+ for representation in representations:
+ ifcopenshell.api.run(
+ "geometry.unassign_representation",
+ file,
+ **{"product": settings["product"], "representation": representation}
+ )
+ ifcopenshell.api.run("geometry.remove_representation", file, **{"representation": representation})
+ for opening in getattr(settings["product"], "HasOpenings", []) or []:
+ ifcopenshell.api.run("void.remove_opening", file, opening=opening.RelatedOpeningElement)
+
+ if settings["product"].is_a("IfcGrid"):
+ for axis in settings["product"].UAxes + settings["product"].VAxes + (settings["product"].WAxes or ()):
+ ifcopenshell.api.run("grid.remove_grid_axis", file, axis=axis)
+
+ def element_exists(element_id):
+ try:
+ 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 file.get_inverse(settings["product"])]:
+ try:
+ inverse = file.by_id(inverse_id)
+ except:
+ continue
+ if inverse.is_a("IfcRelDefinesByProperties"):
+ ifcopenshell.api.run(
+ "pset.remove_pset",
+ file,
+ product=settings["product"],
+ pset=inverse.RelatingPropertyDefinition,
+ )
+ elif inverse.is_a("IfcRelAssociatesMaterial"):
+ ifcopenshell.api.run("material.unassign_material", file, products=[settings["product"]])
+ elif inverse.is_a("IfcRelDefinesByType"):
+ if inverse.RelatingType == settings["product"]:
+ ifcopenshell.api.run("type.unassign_type", file, related_objects=inverse.RelatedObjects)
+ else:
+ ifcopenshell.api.run("type.unassign_type", file, related_objects=[settings["product"]])
+ elif inverse.is_a("IfcRelSpaceBoundary"):
+ ifcopenshell.api.run("boundary.remove_boundary", file, boundary=inverse)
+ elif inverse.is_a("IfcRelFillsElement"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelVoidsElement"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelServicesBuildings"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelNests"):
+ if inverse.RelatingObject == settings["product"]:
+ inverse_id = inverse.id()
+ for subelement in inverse.RelatedObjects:
+ if subelement.is_a("IfcDistributionPort"):
+ ifcopenshell.api.run("root.remove_product", file, product=subelement)
+ # IfcRelNests could have been already deleted after removing one of the products
+ if element_exists(inverse_id):
history = inverse.OwnerHistory
- self.file.remove(inverse)
+ 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
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelContainedInSpatialStructure"):
- if inverse.RelatingStructure == self.settings["product"] or len(inverse.RelatedElements) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelConnectsElements"):
- if inverse.is_a("IfcRelConnectsWithRealizingElements"):
- if self.settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any(
- el for el in inverse.RealizingElements if el != self.settings["product"]
- ):
- continue
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.RelatedObjects == (settings["product"],):
history = inverse.OwnerHistory
- self.file.remove(inverse)
+ file.remove(inverse)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelConnectsPorts"):
- if self.settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort):
- # if it's not RelatingPort/RelatedPort then it's optional RealizingElement
- # so we keep the relationship
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelAggregates"):
+ if inverse.RelatingObject == settings["product"] or len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelContainedInSpatialStructure"):
+ if inverse.RelatingStructure == settings["product"] or len(inverse.RelatedElements) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelConnectsElements"):
+ if inverse.is_a("IfcRelConnectsWithRealizingElements"):
+ if settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any(
+ el for el in inverse.RealizingElements if el != settings["product"]
+ ):
continue
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelConnectsPorts"):
+ if settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort):
+ # if it's not RelatingPort/RelatedPort then it's optional RealizingElement
+ # so we keep the relationship
+ continue
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelAssignsToGroup"):
+ if len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
- self.file.remove(inverse)
+ file.remove(inverse)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelAssignsToGroup"):
- if len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelAssignsToProduct"):
- if inverse.RelatingProduct == self.settings["product"]:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelFlowControlElements"):
- if inverse.RelatingFlowElement == self.settings["product"]:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.RelatedControlElements == (self.settings["product"],):
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["product"].OwnerHistory
- self.file.remove(self.settings["product"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelAssignsToProduct"):
+ if inverse.RelatingProduct == settings["product"]:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelFlowControlElements"):
+ if inverse.RelatingFlowElement == settings["product"]:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.RelatedControlElements == (settings["product"],):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["product"].OwnerHistory
+ file.remove(settings["product"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py
index e0caddbe3c..90cb5f4922 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py
@@ -15,3 +15,47 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_task import add_task
+from .add_task_time import add_task_time
+from .add_time_period import add_time_period
+from .add_work_calendar import add_work_calendar
+from .add_work_plan import add_work_plan
+from .add_work_schedule import add_work_schedule
+from .add_work_time import add_work_time
+from .assign_lag_time import assign_lag_time
+from .assign_process import assign_process
+from .assign_product import assign_product
+from .assign_recurrence_pattern import assign_recurrence_pattern
+from .assign_sequence import assign_sequence
+from .assign_workplan import assign_workplan
+from .calculate_task_duration import calculate_task_duration
+from .cascade_schedule import cascade_schedule
+from .create_baseline import create_baseline
+from .duplicate_task import duplicate_task
+from .edit_lag_time import edit_lag_time
+from .edit_recurrence_pattern import edit_recurrence_pattern
+from .edit_sequence import edit_sequence
+from .edit_task import edit_task
+from .edit_task_time import edit_task_time
+from .edit_work_calendar import edit_work_calendar
+from .edit_work_plan import edit_work_plan
+from .edit_work_schedule import edit_work_schedule
+from .edit_work_time import edit_work_time
+from .get_related_products import get_related_products
+
+try:
+ from .recalculate_schedule import recalculate_schedule
+except ModuleNotFoundError as e:
+ print(f"Note: API not available due to missing dependencies: sequence.recalculate_schedule - {e}")
+from .remove_task import remove_task
+from .remove_time_period import remove_time_period
+from .remove_work_calendar import remove_work_calendar
+from .remove_work_plan import remove_work_plan
+from .remove_work_schedule import remove_work_schedule
+from .remove_work_time import remove_work_time
+from .unassign_lag_time import unassign_lag_time
+from .unassign_process import unassign_process
+from .unassign_product import unassign_product
+from .unassign_recurrence_pattern import unassign_recurrence_pattern
+from .unassign_sequence import unassign_sequence
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py
index 60ab48c6df..7d1a91423f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py
@@ -20,169 +20,159 @@ import ifcopenshell.api
import ifcopenshell
-class Usecase:
- def __init__(
- self,
+def add_task(
+ file,
+ work_schedule=None,
+ parent_task=None,
+ name=None,
+ description=None,
+ identification=None,
+ predefined_type="NOTDEFINED",
+) -> None:
+ """Adds a new task
+
+ Tasks are typically used for two purposes: construction scheduling and
+ facility management.
+
+ In construction scheduling, a task represents a job to be done in a work
+ schedule. Tasks are organised in a hierarchical manner known as a work
+ breakdown structure (WBS) and have lots of sequential relationships
+ (e.g. this task must finish before the next task can start) and date
+ information (e.g. durations, start dates). This is often represented as
+ a gantt chart and used to analyse critical paths to try and reduce
+ project time to stay on-time and within budget.
+
+ In facility management, a task represents a maintenance task to maintain
+ a piece of equipment. Tasks are broken down into a punch list, or simply
+ a bulleted or ordered sequence of tasks to be performed (e.g. turn off
+ equipment, check power connection, etc) in order to maintain the
+ equipment. Tasks will also typically have recurring scheduled dates in
+ line with the maintenance schedule. These maintenance tasks and
+ procedures are typically published as part of an operations and
+ maintenance manual.
+
+ All tasks must be grouped in a work schedule, either directly as a root
+ or top-level task, or indirectly as a child or subtask of a parent task.
+ In construction scheduling, tasks may be nested many times to create the
+ work breakdown structure, and the "leaf" tasks (i.e. tasks with no more
+ subtasks) are considered to be the activities with dates, whereas all
+ parent tasks are part of the breakdown structure used for categorisation
+ purposes. In facility management, top-level tasks represent the overall
+ maintenance job to be performed, and child tasks represent an ordered
+ list of things to do for that maintenance. These form a 2-level
+ hierarchy. No further child tasks are recommended.
+
+ :param work_schedule: The work schedule to group the task in, if the
+ task is to be a top-level or root task. This is mutually exclusive
+ with the parent_task parameter.
+ :type work_schedule: ifcopenshell.entity_instance
+ :param parent_task: The parent task, if the task is to be a subtask or
+ child task. This is mutually exclusive with the work_schedule
+ parameter.
+ :type parent_task: ifcopenshell.entity_instance
+ :param name: The name of the task.
+ :type name: str,optional
+ :param description: The description of the task.
+ :type description: str,optional
+ :param identification: The identification code of the task.
+ :type identification: str,optional
+ :param predefined_type: The predefined type of the task. Common ones
+ include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the
+ IFC documentation for IfcTaskTypeEnum for more information.
+ :type predefined_type: str
+ :return: The newly created IfcTask
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+
+ # Add a root task to represent the design milestones, and major
+ # project phases.
+ ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Milestones", identification="A")
+ ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Design", identification="B")
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
+
+ # Let's start creating our work breakdown structure.
+ ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Early Works", identification="C1")
+ ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Substructure", identification="C2")
+ superstructure = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Superstructure", identification="C3")
+
+ # Notice how the leaf task is the actual activity
+ ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
+
+ # Let's imagine we are digitising an operations and maintenance
+ # manual for the mechanical discipline.
+ maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance")
+
+ # Imagine we have to clean the condenser coils for a chiller every
+ # month. Like the schedule above, to keep things simple we won't
+ # show scheduling times and calendars. This root task represents the
+ # overall maintenance task.
+ cleaning = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=maintenance, name="Condenser coil cleaning")
+
+ # These subtasks represent the punch list of maintenance tasks.
+ ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1",
+ description="Prior to work, wear safety shoes, gloves, and goggles.")
+ ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2",
+ description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.")
+ ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
+ description="Switch OFF the chiller unit.")
+ ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
+ description="Open the isolator switch.")
+ ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
+ description="Setup the water pressure by tapping to a water supply and connecting to a ...")
+ """
+ settings = {
+ "work_schedule": work_schedule,
+ "parent_task": parent_task,
+ "name": name,
+ "description": description,
+ "identification": identification,
+ "predefined_type": predefined_type,
+ }
+
+ task = ifcopenshell.api.run(
+ "root.create_entity",
file,
- work_schedule=None,
- parent_task=None,
- name=None,
- description=None,
- identification=None,
- predefined_type="NOTDEFINED",
- ):
- """Adds a new task
-
- Tasks are typically used for two purposes: construction scheduling and
- facility management.
-
- In construction scheduling, a task represents a job to be done in a work
- schedule. Tasks are organised in a hierarchical manner known as a work
- breakdown structure (WBS) and have lots of sequential relationships
- (e.g. this task must finish before the next task can start) and date
- information (e.g. durations, start dates). This is often represented as
- a gantt chart and used to analyse critical paths to try and reduce
- project time to stay on-time and within budget.
-
- In facility management, a task represents a maintenance task to maintain
- a piece of equipment. Tasks are broken down into a punch list, or simply
- a bulleted or ordered sequence of tasks to be performed (e.g. turn off
- equipment, check power connection, etc) in order to maintain the
- equipment. Tasks will also typically have recurring scheduled dates in
- line with the maintenance schedule. These maintenance tasks and
- procedures are typically published as part of an operations and
- maintenance manual.
-
- All tasks must be grouped in a work schedule, either directly as a root
- or top-level task, or indirectly as a child or subtask of a parent task.
- In construction scheduling, tasks may be nested many times to create the
- work breakdown structure, and the "leaf" tasks (i.e. tasks with no more
- subtasks) are considered to be the activities with dates, whereas all
- parent tasks are part of the breakdown structure used for categorisation
- purposes. In facility management, top-level tasks represent the overall
- maintenance job to be performed, and child tasks represent an ordered
- list of things to do for that maintenance. These form a 2-level
- hierarchy. No further child tasks are recommended.
-
- :param work_schedule: The work schedule to group the task in, if the
- task is to be a top-level or root task. This is mutually exclusive
- with the parent_task parameter.
- :type work_schedule: ifcopenshell.entity_instance
- :param parent_task: The parent task, if the task is to be a subtask or
- child task. This is mutually exclusive with the work_schedule
- parameter.
- :type parent_task: ifcopenshell.entity_instance
- :param name: The name of the task.
- :type name: str,optional
- :param description: The description of the task.
- :type description: str,optional
- :param identification: The identification code of the task.
- :type identification: str,optional
- :param predefined_type: The predefined type of the task. Common ones
- include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the
- IFC documentation for IfcTaskTypeEnum for more information.
- :type predefined_type: str
- :return: The newly created IfcTask
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
-
- # Add a root task to represent the design milestones, and major
- # project phases.
- ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Milestones", identification="A")
- ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Design", identification="B")
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
-
- # Let's start creating our work breakdown structure.
- ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Early Works", identification="C1")
- ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Substructure", identification="C2")
- superstructure = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Superstructure", identification="C3")
-
- # Notice how the leaf task is the actual activity
- ifcopenshell.api.run("sequence.add_task", model,
- parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
-
- # Let's imagine we are digitising an operations and maintenance
- # manual for the mechanical discipline.
- maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance")
-
- # Imagine we have to clean the condenser coils for a chiller every
- # month. Like the schedule above, to keep things simple we won't
- # show scheduling times and calendars. This root task represents the
- # overall maintenance task.
- cleaning = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=maintenance, name="Condenser coil cleaning")
-
- # These subtasks represent the punch list of maintenance tasks.
- ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1",
- description="Prior to work, wear safety shoes, gloves, and goggles.")
- ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2",
- description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.")
- ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
- description="Switch OFF the chiller unit.")
- ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
- description="Open the isolator switch.")
- ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3",
- description="Setup the water pressure by tapping to a water supply and connecting to a ...")
- """
- self.file = file
- self.settings = {
- "work_schedule": work_schedule,
- "parent_task": parent_task,
- "name": name,
- "description": description,
- "identification": identification,
- "predefined_type": predefined_type,
- }
-
- def execute(self):
- task = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcTask",
- name=self.settings["name"],
- predefined_type=self.settings["predefined_type"],
+ ifc_class="IfcTask",
+ name=settings["name"],
+ predefined_type=settings["predefined_type"],
+ )
+ if settings["description"]:
+ task.Description = settings["description"]
+ if settings["identification"]:
+ task.Identification = settings["identification"]
+ task.IsMilestone = False
+ if settings["work_schedule"]:
+ file.create_entity(
+ "IfcRelAssignsToControl",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [task],
+ "RelatingControl": settings["work_schedule"],
+ }
)
- if self.settings["description"]:
- task.Description = self.settings["description"]
- if self.settings["identification"]:
- task.Identification = self.settings["identification"]
- task.IsMilestone = False
- if self.settings["work_schedule"]:
- self.file.create_entity(
- "IfcRelAssignsToControl",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run(
- "owner.create_owner_history", self.file
- ),
- "RelatedObjects": [task],
- "RelatingControl": self.settings["work_schedule"],
- }
- )
- elif self.settings["parent_task"]:
- rel = ifcopenshell.api.run(
- "nest.assign_object",
- self.file,
- related_objects=[task],
- relating_object=self.settings["parent_task"],
- )
- if self.settings["parent_task"].Identification:
- task.Identification = (
- self.settings["parent_task"].Identification
- + "."
- + str(len(rel.RelatedObjects))
- )
- return task
+ elif settings["parent_task"]:
+ rel = ifcopenshell.api.run(
+ "nest.assign_object",
+ file,
+ related_objects=[task],
+ relating_object=settings["parent_task"],
+ )
+ if settings["parent_task"].Identification:
+ task.Identification = settings["parent_task"].Identification + "." + str(len(rel.RelatedObjects))
+ return task
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py
index 7c4381c361..bbd51c2e68 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py
@@ -17,55 +17,52 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, task=None, is_recurring=False):
- """Adds a task time to a task
+def add_task_time(file, task=None, is_recurring=False) -> None:
+ """Adds a task time to a task
- Some tasks, such as activities within a work breakdown structure or
- overall maintenance tasks will have time related information. This
- includes start dates, durations, end dates, and possible recurring times
- (especially for maintenance tasks).
+ Some tasks, such as activities within a work breakdown structure or
+ overall maintenance tasks will have time related information. This
+ includes start dates, durations, end dates, and possible recurring times
+ (especially for maintenance tasks).
- :param task: The task to add time data to.
- :type task: ifcopenshell.entity_instance
- :param is_recurring: Whether or not the time should recur.
- :type is_recurring: bool
- :return: The newly created IfcTaskTime.
- :rtype: ifcopenshell.entity_instance
+ :param task: The task to add time data to.
+ :type task: ifcopenshell.entity_instance
+ :param is_recurring: Whether or not the time should recur.
+ :type is_recurring: bool
+ :return: The newly created IfcTaskTime.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Create a portion of a work breakdown structure.
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
- superstructure = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Superstructure", identification="C3")
- task = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
+ # Create a portion of a work breakdown structure.
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
+ superstructure = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Superstructure", identification="C3")
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=superstructure, name="Ground Floor FRP", identification="C3.1")
- # Add time data. Note that time data is blank by default.
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
+ # Add time data. Note that time data is blank by default.
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
- # Let's say our task starts on the first of January when everybody
- # is still drunk from the new years celebration, and lasts for 2
- # days. Note we don't need to specify the end date, as that is
- # derived from the start plus the duration. In this simple example,
- # no calendar has been specified, so we are working 24/7. Yikes!
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- """
- self.file = file
- self.settings = {"task": task, "is_recurring": is_recurring}
+ # Let's say our task starts on the first of January when everybody
+ # is still drunk from the new years celebration, and lasts for 2
+ # days. Note we don't need to specify the end date, as that is
+ # derived from the start plus the duration. In this simple example,
+ # no calendar has been specified, so we are working 24/7. Yikes!
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ """
+ settings = {"task": task, "is_recurring": is_recurring}
- def execute(self):
- if self.settings["is_recurring"]:
- task_time = self.file.create_entity("IfcTaskTimeRecurring")
- else:
- task_time = self.file.create_entity("IfcTaskTime")
- self.settings["task"].TaskTime = task_time
- return task_time
+ if settings["is_recurring"]:
+ task_time = file.create_entity("IfcTaskTimeRecurring")
+ else:
+ task_time = file.create_entity("IfcTaskTime")
+ settings["task"].TaskTime = task_time
+ return task_time
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py
index 35a022a9ea..479524a4b1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py
@@ -23,79 +23,72 @@ from datetime import datetime
from datetime import timedelta
-class Usecase:
- def __init__(self, file, recurrence_pattern=None, start_time=None, end_time=None):
- """Adds a time period to a recurrence pattern
+def add_time_period(file, recurrence_pattern=None, start_time=None, end_time=None) -> None:
+ """Adds a time period to a recurrence pattern
- A recurring time may be an all-day event, or only during certain time
- periods of the day. For example, you might say that every 1st of January
- recurring is a public holiday, which is an all-day event. Alternatively,
- you might say that you work every (i.e. recurringly) Monday to Friday,
- from 9am to 5pm. The 9am to 5pm is the time period.
+ A recurring time may be an all-day event, or only during certain time
+ periods of the day. For example, you might say that every 1st of January
+ recurring is a public holiday, which is an all-day event. Alternatively,
+ you might say that you work every (i.e. recurringly) Monday to Friday,
+ from 9am to 5pm. The 9am to 5pm is the time period.
- There may also be multiple recurrence patterns, such as from 9am to
- 12pm, and then another from 1pm to 5pm (to indicate an hour break for
- lunch).
+ There may also be multiple recurrence patterns, such as from 9am to
+ 12pm, and then another from 1pm to 5pm (to indicate an hour break for
+ lunch).
- :param recurrence_pattern: The IfcRecurrencePattern to add the time
- period to. See ifcopenshell.api.sequence.assign_recurrence_pattern.
- :type recurrence_pattern: ifcopenshell.entity_instance
- :param start_time: The start time of the time period, in a format
- compatible with IfcTime, such as an ISO format time string or a
- datetime.time object.
- :type start_time: str,datetime.time
- :param end_time: The end time of the time period, in a format
- compatible with IfcTime, such as an ISO format time string or a
- datetime.time object.
- :type end_time: str,datetime.time
- :return: The newly created IfcTimePeriod
- :rtype: ifcopenshell.entity_instance
+ :param recurrence_pattern: The IfcRecurrencePattern to add the time
+ period to. See ifcopenshell.api.sequence.assign_recurrence_pattern.
+ :type recurrence_pattern: ifcopenshell.entity_instance
+ :param start_time: The start time of the time period, in a format
+ compatible with IfcTime, such as an ISO format time string or a
+ datetime.time object.
+ :type start_time: str,datetime.time
+ :param end_time: The end time of the time period, in a format
+ compatible with IfcTime, such as an ISO format time string or a
+ datetime.time object.
+ :type end_time: str,datetime.time
+ :return: The newly created IfcTimePeriod
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
+ # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
- # The morning work session, lunch, then the afternoon work session.
- ifcopenshell.api.run("sequence.add_time_period", model,
- recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
- ifcopenshell.api.run("sequence.add_time_period", model,
- recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
- """
- self.file = file
- self.settings = {
- "recurrence_pattern": recurrence_pattern,
- "start_time": start_time,
- "end_time": end_time,
- }
+ # The morning work session, lunch, then the afternoon work session.
+ ifcopenshell.api.run("sequence.add_time_period", model,
+ recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
+ ifcopenshell.api.run("sequence.add_time_period", model,
+ recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
+ """
+ settings = {
+ "recurrence_pattern": recurrence_pattern,
+ "start_time": start_time,
+ "end_time": end_time,
+ }
- def execute(self):
- time_period = self.file.create_entity("IfcTimePeriod")
- time_period.StartTime = ifcopenshell.util.date.datetime2ifc(
- self.settings["start_time"], "IfcTime"
- )
- time_period.EndTime = ifcopenshell.util.date.datetime2ifc(
- self.settings["end_time"], "IfcTime"
- )
- time_periods = list(self.settings["recurrence_pattern"].TimePeriods or [])
- time_periods.append(time_period)
- self.settings["recurrence_pattern"].TimePeriods = time_periods
+ time_period = file.create_entity("IfcTimePeriod")
+ time_period.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcTime")
+ time_period.EndTime = ifcopenshell.util.date.datetime2ifc(settings["end_time"], "IfcTime")
+ time_periods = list(settings["recurrence_pattern"].TimePeriods or [])
+ time_periods.append(time_period)
+ settings["recurrence_pattern"].TimePeriods = time_periods
- ifcopenshell.util.sequence.is_working_day.cache_clear()
- ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
+ ifcopenshell.util.sequence.is_working_day.cache_clear()
+ ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
- return time_period
+ return time_period
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py
index 373d628be6..250a1b2fe0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py
@@ -19,80 +19,77 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, name="Unnamed", predefined_type="NOTDEFINED"):
- """Add a work calendar
+def add_work_calendar(file, name="Unnamed", predefined_type="NOTDEFINED") -> None:
+ """Add a work calendar
- A work calendar defines when work is allowed to occur and when the
- holidays are. This is a fundamental concept in construction planning.
- Every task in a work schedule will have an associated calendar. Some
- task and resources work 24/7, whereas others work Monday to Friday, or
- 5.5 day weeks, etc. This is important, as tasks durations may only occur
- during working times in a work calendar.
+ A work calendar defines when work is allowed to occur and when the
+ holidays are. This is a fundamental concept in construction planning.
+ Every task in a work schedule will have an associated calendar. Some
+ task and resources work 24/7, whereas others work Monday to Friday, or
+ 5.5 day weeks, etc. This is important, as tasks durations may only occur
+ during working times in a work calendar.
- Work calendars can also be used to associate with events, such as
- indicating that during certain days and times of the year, motion
- sensors should turn on the lights, and other smart building controls.
+ Work calendars can also be used to associate with events, such as
+ indicating that during certain days and times of the year, motion
+ sensors should turn on the lights, and other smart building controls.
- :param name: The name of the calendar. Typically something like
- "5 Day Working Week" or "24/7".
- :type name: str, optional
- :param predefined_type: The type of calendar, typically used to more
- specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or
- THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage.
- :return: The newly created IfcWorkCalendar
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the calendar. Typically something like
+ "5 Day Working Week" or "24/7".
+ :type name: str, optional
+ :param predefined_type: The type of calendar, typically used to more
+ specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or
+ THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage.
+ :return: The newly created IfcWorkCalendar
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Add a root task to represent the construction tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Add a root task to represent the construction tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
- ifcopenshell.api.run("sequence.add_time_period", model,
- recurrence_pattern=pattern, start_time="09:00", end_time="17:00")
+ # State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
+ ifcopenshell.api.run("sequence.add_time_period", model,
+ recurrence_pattern=pattern, start_time="09:00", end_time="17:00")
- # We associate the calendar with the construction root task. All
- # subtasks underneath the construction work task will also inherit
- # this calendar by default (though you can override them).
- ifcopenshell.api.run("control.assign_control", model, relating_control=calendar, related_object=task)
- """
- self.file = file
- self.settings = {"name": name, "predefined_type": predefined_type}
+ # We associate the calendar with the construction root task. All
+ # subtasks underneath the construction work task will also inherit
+ # this calendar by default (though you can override them).
+ ifcopenshell.api.run("control.assign_control", model, relating_control=calendar, related_object=task)
+ """
+ settings = {"name": name, "predefined_type": predefined_type}
- def execute(self):
- work_calendar = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcWorkCalendar",
- predefined_type=self.settings["predefined_type"],
- name=self.settings["name"],
- )
- context = self.file.by_type("IfcContext")[0]
- ifcopenshell.api.run(
- "project.assign_declaration",
- self.file,
- definitions=[work_calendar],
- relating_context=context,
- )
- return work_calendar
+ work_calendar = ifcopenshell.api.run(
+ "root.create_entity",
+ file,
+ ifc_class="IfcWorkCalendar",
+ predefined_type=settings["predefined_type"],
+ name=settings["name"],
+ )
+ context = file.by_type("IfcContext")[0]
+ ifcopenshell.api.run(
+ "project.assign_declaration",
+ file,
+ definitions=[work_calendar],
+ relating_context=context,
+ )
+ return work_calendar
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py
index f6fba71315..858d944d66 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py
@@ -21,70 +21,63 @@ import ifcopenshell.util.date
from datetime import datetime
-class Usecase:
- def __init__(self, file, name=None, predefined_type="NOTDEFINED", start_time=None):
- """Add a new work plan
+def add_work_plan(file, name=None, predefined_type="NOTDEFINED", start_time=None) -> None:
+ """Add a new work plan
- A work plan is a group of work schedules. Since work schedules may have
- different purposes, such as for maintenance or construction scheduling,
- baseline comparison, or phasing, work plans can be used to group related
- work schedules. At a minimum, it is recommended to use work plans to
- indicate whether the work schedules are for facility management or for
- construction scheduling.
+ A work plan is a group of work schedules. Since work schedules may have
+ different purposes, such as for maintenance or construction scheduling,
+ baseline comparison, or phasing, work plans can be used to group related
+ work schedules. At a minimum, it is recommended to use work plans to
+ indicate whether the work schedules are for facility management or for
+ construction scheduling.
- :param name: The name of the work plan. Recommended to be "Maintenance"
- or "Construction" for the two main purposes.
- :type name: str, optional
- :param predefined_type: The type of work plan, used for baselining.
- Leave as "NOTDEFINED" if unsure.
- :type predefined_type: str
- :param start_time: The earliest start time when the schedules grouped
- within the work plan are relevant.
- :type start_time: str,datetime.time
- :return: The newly created IfcWorkPlan
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the work plan. Recommended to be "Maintenance"
+ or "Construction" for the two main purposes.
+ :type name: str, optional
+ :param predefined_type: The type of work plan, used for baselining.
+ Leave as "NOTDEFINED" if unsure.
+ :type predefined_type: str
+ :param start_time: The earliest start time when the schedules grouped
+ within the work plan are relevant.
+ :type start_time: str,datetime.time
+ :return: The newly created IfcWorkPlan
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
- # This is one of our schedules in our work plan.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
- name="Construction Schedule A", work_plan=work_plan)
- """
- self.file = file
- self.settings = {
- "name": name,
- "predefined_type": predefined_type,
- "start_time": start_time or datetime.now(),
- }
+ # This is one of our schedules in our work plan.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
+ name="Construction Schedule A", work_plan=work_plan)
+ """
+ settings = {
+ "name": name,
+ "predefined_type": predefined_type,
+ "start_time": start_time or datetime.now(),
+ }
- def execute(self):
- work_plan = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcWorkPlan",
- predefined_type=self.settings["predefined_type"],
- name=self.settings["name"],
- )
- work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(
- datetime.now(), "IfcDateTime"
- )
- user = ifcopenshell.api.owner.settings.get_user(self.file)
- if user:
- work_plan.Creators = [user.ThePerson]
- work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(
- self.settings["start_time"], "IfcDateTime"
- )
+ work_plan = ifcopenshell.api.run(
+ "root.create_entity",
+ file,
+ ifc_class="IfcWorkPlan",
+ predefined_type=settings["predefined_type"],
+ name=settings["name"],
+ )
+ work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
+ user = ifcopenshell.api.owner.settings.get_user(file)
+ if user:
+ work_plan.Creators = [user.ThePerson]
+ work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
- context = self.file.by_type("IfcContext")[0]
- ifcopenshell.api.run(
- "project.assign_declaration",
- self.file,
- definitions=[work_plan],
- relating_context=context,
- )
- return work_plan
+ context = file.by_type("IfcContext")[0]
+ ifcopenshell.api.run(
+ "project.assign_declaration",
+ file,
+ definitions=[work_plan],
+ relating_context=context,
+ )
+ return work_plan
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 47e96a8fa3..21f508999c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py
@@ -21,104 +21,96 @@ import ifcopenshell.util.date
from datetime import datetime
-class Usecase:
- def __init__(
- self,
+def add_work_schedule(
+ file,
+ name="Unnamed",
+ predefined_type="NOTDEFINED",
+ object_type=None,
+ start_time=None,
+ work_plan=None,
+) -> None:
+ """Add a new work schedule
+
+ A work schedule is a group of tasks, where the tasks are typically
+ either for maintenance or for construction scheduling.
+
+ :param name: The name of the work schedule.
+ :type name: str
+ :param predefined_type: The type of schedule, chosen from ACTUAL,
+ BASELINE, and PLANNED. Typically you would start with PLANNED, then
+ convert to a BASELINE when changes are made with separate schedules,
+ then have a parallel ACTUAL schedule.
+ :type predefined_type: str
+ :param start_time: The earlier start time when the schedule is relevant.
+ May be represented with an ISO standard string.
+ :type start_time: str,datetime.time,optional
+ :param work_plan: The IfcWorkPlan the schedule will be part of. If not
+ provided, the schedule will not be grouped in a work plan and would
+ exist as a top level schedule in the project. This is not
+ recommended.
+ :type work_plan: ifcopenshell.entity_instance,optional
+ :return: The newly created IfcWorkSchedule
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+
+ # Let's imagine this is one of our schedules in our work plan.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
+ name="Construction Schedule A", work_plan=work_plan)
+
+ # Add a root task to represent the design milestones, and major
+ # project phases.
+ ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Milestones", identification="A")
+ ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Design", identification="B")
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
+ """
+ settings = {
+ "name": name,
+ "predefined_type": predefined_type,
+ "object_type": object_type,
+ "start_time": start_time or datetime.now(),
+ "work_plan": work_plan,
+ }
+
+ work_schedule = ifcopenshell.api.run(
+ "root.create_entity",
file,
- name="Unnamed",
- predefined_type="NOTDEFINED",
- object_type=None,
- start_time=None,
- work_plan=None,
- ):
- """Add a new work schedule
-
- A work schedule is a group of tasks, where the tasks are typically
- either for maintenance or for construction scheduling.
-
- :param name: The name of the work schedule.
- :type name: str
- :param predefined_type: The type of schedule, chosen from ACTUAL,
- BASELINE, and PLANNED. Typically you would start with PLANNED, then
- convert to a BASELINE when changes are made with separate schedules,
- then have a parallel ACTUAL schedule.
- :type predefined_type: str
- :param start_time: The earlier start time when the schedule is relevant.
- May be represented with an ISO standard string.
- :type start_time: str,datetime.time,optional
- :param work_plan: The IfcWorkPlan the schedule will be part of. If not
- provided, the schedule will not be grouped in a work plan and would
- exist as a top level schedule in the project. This is not
- recommended.
- :type work_plan: ifcopenshell.entity_instance,optional
- :return: The newly created IfcWorkSchedule
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
-
- # Let's imagine this is one of our schedules in our work plan.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
- name="Construction Schedule A", work_plan=work_plan)
-
- # Add a root task to represent the design milestones, and major
- # project phases.
- ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Milestones", identification="A")
- ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Design", identification="B")
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
- """
- self.file = file
- self.settings = {
- "name": name,
- "predefined_type": predefined_type,
- "object_type": object_type,
- "start_time": start_time or datetime.now(),
- "work_plan": work_plan,
- }
-
- def execute(self):
- work_schedule = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcWorkSchedule",
- predefined_type=self.settings["predefined_type"],
- name=self.settings["name"],
+ ifc_class="IfcWorkSchedule",
+ predefined_type=settings["predefined_type"],
+ name=settings["name"],
+ )
+ work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
+ user = ifcopenshell.api.owner.settings.get_user(file)
+ if user:
+ work_schedule.Creators = [user.ThePerson]
+ work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
+ if settings["object_type"]:
+ work_schedule.ObjectType = settings["object_type"]
+ if settings["work_plan"]:
+ ifcopenshell.api.run(
+ "aggregate.assign_object",
+ file,
+ **{
+ "products": [work_schedule],
+ "relating_object": settings["work_plan"],
+ }
)
- work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(
- datetime.now(), "IfcDateTime"
+ else:
+ # TODO: this is an ambiguity by buildingSMART
+ # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
+ context = file.by_type("IfcContext")[0]
+ ifcopenshell.api.run(
+ "project.assign_declaration",
+ file,
+ definitions=[work_schedule],
+ relating_context=context,
)
- user = ifcopenshell.api.owner.settings.get_user(self.file)
- if user:
- work_schedule.Creators = [user.ThePerson]
- work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(
- self.settings["start_time"], "IfcDateTime"
- )
- if self.settings["object_type"]:
- work_schedule.ObjectType = self.settings["object_type"]
- if self.settings["work_plan"]:
- ifcopenshell.api.run(
- "aggregate.assign_object",
- self.file,
- **{
- "products": [work_schedule],
- "relating_object": self.settings["work_plan"],
- }
- )
- else:
- # TODO: this is an ambiguity by buildingSMART
- # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
- context = self.file.by_type("IfcContext")[0]
- ifcopenshell.api.run(
- "project.assign_declaration",
- self.file,
- definitions=[work_schedule],
- relating_context=context,
- )
- return work_schedule
+ return work_schedule
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py
index 0d75914949..86d4666ad9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py
@@ -17,69 +17,66 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, work_calendar=None, time_type="WorkingTimes"):
- """Add either working times or holiday times to a calendar
+def add_work_time(file, work_calendar=None, time_type="WorkingTimes") -> None:
+ """Add either working times or holiday times to a calendar
- A calendar defines when work occurs by defining working times and
- holiday times. First, the working times are defined, then the holidays
- may override the working times. For this reason, holidays are also known
- as exception times. For example, you might define the working times as
- every Monday to Friday, then define a few holidays in the year, such as
- the 1st of January. If the 1st of January is on a weekday, it will
- override the work time.
+ A calendar defines when work occurs by defining working times and
+ holiday times. First, the working times are defined, then the holidays
+ may override the working times. For this reason, holidays are also known
+ as exception times. For example, you might define the working times as
+ every Monday to Friday, then define a few holidays in the year, such as
+ the 1st of January. If the 1st of January is on a weekday, it will
+ override the work time.
- :param work_calendar: The IfcWorkCalendar to add the work or holiday
- time definition to.
- :type work_calendar: ifcopenshell.entity_instance
- :param time_type: Either WorkingTimes or ExceptionTimes, depending on
- what you want to define.
- :type time_type: str
- :return: The newly created IfcWorkTime
- :rtype: ifcopenshell.entity_instance
+ :param work_calendar: The IfcWorkCalendar to add the work or holiday
+ time definition to.
+ :type work_calendar: ifcopenshell.entity_instance
+ :param time_type: Either WorkingTimes or ExceptionTimes, depending on
+ what you want to define.
+ :type time_type: str
+ :return: The newly created IfcWorkTime
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
+ # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
- # Let's set some holidays
- holidays = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="ExceptionTimes")
+ # Let's set some holidays
+ holidays = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="ExceptionTimes")
- # We create a yearly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH")
+ # We create a yearly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH")
- # The holiday is every 1st of January
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]})
- """
- self.file = file
- self.settings = {"work_calendar": work_calendar, "time_type": time_type}
+ # The holiday is every 1st of January
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]})
+ """
+ settings = {"work_calendar": work_calendar, "time_type": time_type}
- def execute(self):
- work_time = self.file.create_entity("IfcWorkTime")
- if self.settings["time_type"] == "WorkingTimes":
- working_times = list(self.settings["work_calendar"].WorkingTimes or [])
- working_times.append(work_time)
- self.settings["work_calendar"].WorkingTimes = working_times
- elif self.settings["time_type"] == "ExceptionTimes":
- exception_times = list(self.settings["work_calendar"].ExceptionTimes or [])
- exception_times.append(work_time)
- self.settings["work_calendar"].ExceptionTimes = exception_times
- return work_time
+ work_time = file.create_entity("IfcWorkTime")
+ if settings["time_type"] == "WorkingTimes":
+ working_times = list(settings["work_calendar"].WorkingTimes or [])
+ working_times.append(work_time)
+ settings["work_calendar"].WorkingTimes = working_times
+ elif settings["time_type"] == "ExceptionTimes":
+ exception_times = list(settings["work_calendar"].ExceptionTimes or [])
+ exception_times.append(work_time)
+ settings["work_calendar"].ExceptionTimes = exception_times
+ return work_time
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py
index 87f2882055..0ba89d0346 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py
@@ -19,88 +19,78 @@
import ifcopenshell.util.date
-class Usecase:
- def __init__(self, file, rel_sequence=None, lag_value=None, duration_type="WORKTIME"):
- """Assign a lag time to a sequence relationship between tasks
+def assign_lag_time(file, rel_sequence=None, lag_value=None, duration_type="WORKTIME") -> None:
+ """Assign a lag time to a sequence relationship between tasks
- A task sequence (e.g. finish to start) may optionally have a lag time
- defined. This is a fundamental concept in construction scheduling. The
- lag is defined as a duration, and the duration is typically either
- calendar based (i.e. follows the working times and holidays of the
- calendar) or elapsed time based (i.e. 24/7).
+ A task sequence (e.g. finish to start) may optionally have a lag time
+ defined. This is a fundamental concept in construction scheduling. The
+ lag is defined as a duration, and the duration is typically either
+ calendar based (i.e. follows the working times and holidays of the
+ calendar) or elapsed time based (i.e. 24/7).
- A sequence may only have a single lag time defined. Negative lag times
- are allowed.
+ A sequence may only have a single lag time defined. Negative lag times
+ are allowed.
- :param rel_sequence: The IfcRelSequence to assign the lag time to.
- :type rel_sequence: ifcopenshell.entity_instance
- :param lag_value: An ISO standardised duration string.
- :type lag_value: str
- :param duration_type: Choose from WORKTIME for the associated
- calendar-based lag times (this is the most common scenario and is
- recommended as a default), or ELAPSEDTIME to not follow the
- calendar. You may also choose NOTDEFINED but the behaviour of this
- is unclear.
- :type duration_type: str
- :return: The newly created IfcLagTime
- :rtype: ifcopenshell.entity_instance
+ :param rel_sequence: The IfcRelSequence to assign the lag time to.
+ :type rel_sequence: ifcopenshell.entity_instance
+ :param lag_value: An ISO standardised duration string.
+ :type lag_value: str
+ :param duration_type: Choose from WORKTIME for the associated
+ calendar-based lag times (this is the most common scenario and is
+ recommended as a default), or ELAPSEDTIME to not follow the
+ calendar. You may also choose NOTDEFINED but the behaviour of this
+ is unclear.
+ :type duration_type: str
+ :return: The newly created IfcLagTime
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's imagine a root construction task
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Let's imagine a root construction task
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's imagine we're doing a typically formwork, reinforcement,
- # pour sequence. Let's start with the formwork. It'll take us 2
- # days.
- formwork = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Formwork", identification="C.1")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ # Let's imagine we're doing a typically formwork, reinforcement,
+ # pour sequence. Let's start with the formwork. It'll take us 2
+ # days.
+ formwork = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Formwork", identification="C.1")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- # Now let's do the reinforcement. It'll take us another 2 days.
- reinforcement = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Reinforcement", identification="C.2")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ # Now let's do the reinforcement. It'll take us another 2 days.
+ reinforcement = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Reinforcement", identification="C.2")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- # Now let's say the formwork must finish before the reinforcement
- # can start. This is a typical finish to start relationship (FS).
- sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=formwork, related_process=reinforcement)
+ # Now let's say the formwork must finish before the reinforcement
+ # can start. This is a typical finish to start relationship (FS).
+ sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=formwork, related_process=reinforcement)
- # Now typically there would be no lag time between formwork and
- # reinforcement, but let's pretend that we had to allow 1 day gap
- # for whatever reason.
- ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
- """
- self.file = file
- self.settings = {
- "rel_sequence": rel_sequence,
- "lag_value": lag_value,
- "duration_type": duration_type,
- }
+ # Now typically there would be no lag time between formwork and
+ # reinforcement, but let's pretend that we had to allow 1 day gap
+ # for whatever reason.
+ ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
+ """
+ settings = {
+ "rel_sequence": rel_sequence,
+ "lag_value": lag_value,
+ "duration_type": duration_type,
+ }
- def execute(self):
- lag_value = self.file.createIfcDuration(
- ifcopenshell.util.date.datetime2ifc(self.settings["lag_value"], "IfcDuration")
- )
- lag_time = self.file.create_entity(
- "IfcLagTime", DurationType=self.settings["duration_type"], LagValue=lag_value
- )
- if self.settings["rel_sequence"].is_a("IfcRelSequence"):
- if (
- self.settings["rel_sequence"].TimeLag
- and len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1
- ):
- self.file.remove(self.settings["rel_sequence"].TimeLag)
- self.settings["rel_sequence"].TimeLag = lag_time
+ lag_value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(settings["lag_value"], "IfcDuration"))
+ lag_time = file.create_entity("IfcLagTime", DurationType=settings["duration_type"], LagValue=lag_value)
+ if settings["rel_sequence"].is_a("IfcRelSequence"):
+ if settings["rel_sequence"].TimeLag and len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1:
+ file.remove(settings["rel_sequence"].TimeLag)
+ settings["rel_sequence"].TimeLag = lag_time
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py
index 5aa2210d42..12f8cfe78c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py
@@ -20,109 +20,99 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_process=None, related_object=None):
- """Assigns an object to be related to a process, typically a construction task
+def assign_process(file, relating_process=None, related_object=None) -> None:
+ """Assigns an object to be related to a process, typically a construction task
- Processes work using the ICOM (Input, Controls, Outputs, Mechanisms)
- paradigm in IFC. This process model is commonly used in modeling
- manufacturing functions.
+ Processes work using the ICOM (Input, Controls, Outputs, Mechanisms)
+ paradigm in IFC. This process model is commonly used in modeling
+ manufacturing functions.
- For example, processes (such as tasks) consume Inputs and transform them
- into Outputs. The process may only occur within the limits of Controls
- (e.g. cost items) and may require Mechanisms (ISO9000 calls them
- Mechanisms, whereas IFC calls them resources, such as raw materials,
- labour, or equipment).
+ For example, processes (such as tasks) consume Inputs and transform them
+ into Outputs. The process may only occur within the limits of Controls
+ (e.g. cost items) and may require Mechanisms (ISO9000 calls them
+ Mechanisms, whereas IFC calls them resources, such as raw materials,
+ labour, or equipment).
- +----------+
- | Controls |
- +----------+
- |
- V
- +--------+ +---------+ +---------+
- | Inputs | --> | Process | --> | Outputs |
- +--------+ +---------+ +---------+
- ^
- |
- +-----------+
- | Resources |
- +-----------+
+ +----------+
+ | Controls |
+ +----------+
+ |
+ V
+ +--------+ +---------+ +---------+
+ | Inputs | --> | Process | --> | Outputs |
+ +--------+ +---------+ +---------+
+ ^
+ |
+ +-----------+
+ | Resources |
+ +-----------+
- There are three main scenarios where an object may be related to a
- task: defining inputs, controls, and resources of a process.
+ There are three main scenarios where an object may be related to a
+ task: defining inputs, controls, and resources of a process.
- For inputs, a product (i.e. wall) may be defined as an input to a task,
- such as when the task is to demolish the wall (i.e. the wall is an
- input, and there is no output).
+ For inputs, a product (i.e. wall) may be defined as an input to a task,
+ such as when the task is to demolish the wall (i.e. the wall is an
+ input, and there is no output).
- For controls, a cost item may be defined as a control to a task.
+ For controls, a cost item may be defined as a control to a task.
- For resources, any construction resource may be assigned to a task.
+ For resources, any construction resource may be assigned to a task.
- :param relating_process: The IfcProcess (typically IfcTask) that the
- input, control, or resource is related to.
- :type relating_process: ifcopenshell.entity_instance
- :param related_object: The IfcProduct (for input), IfcCostItem (for
- control) or IfcConstructionResource (for resource).
- :type related_object: ifcopenshell.entity_instance
- :return: The newly created IfcRelAssignsToProcess relationship
- :rtype: ifcopenshell.entity_instance
+ :param relating_process: The IfcProcess (typically IfcTask) that the
+ input, control, or resource is related to.
+ :type relating_process: ifcopenshell.entity_instance
+ :param related_object: The IfcProduct (for input), IfcCostItem (for
+ control) or IfcConstructionResource (for resource).
+ :type related_object: ifcopenshell.entity_instance
+ :return: The newly created IfcRelAssignsToProcess relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's create a construction task. Note that the predefined type is
- # important to distinguish types of tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
+ # Let's create a construction task. Note that the predefined type is
+ # important to distinguish types of tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
- # Let's say we have a wall somewhere.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Let's say we have a wall somewhere.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's demolish that wall!
- ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
- """
- self.file = file
- self.settings = {
- "relating_process": relating_process,
- "related_object": related_object,
- }
+ # Let's demolish that wall!
+ ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
+ """
+ settings = {
+ "relating_process": relating_process,
+ "related_object": related_object,
+ }
- def execute(self):
- if self.settings["related_object"].HasAssignments:
- for assignment in self.settings["related_object"].HasAssignments:
- if (
- assignment.is_a("IfcRelAssignsToProcess")
- and assignment.RelatingProcess == self.settings["relating_process"]
- ):
- return
+ if settings["related_object"].HasAssignments:
+ for assignment in settings["related_object"].HasAssignments:
+ if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == settings["relating_process"]:
+ return
- operates_on = None
- if self.settings["relating_process"].OperatesOn:
- operates_on = self.settings["relating_process"].OperatesOn[0]
+ operates_on = None
+ if settings["relating_process"].OperatesOn:
+ operates_on = settings["relating_process"].OperatesOn[0]
- if operates_on:
- related_objects = list(operates_on.RelatedObjects)
- related_objects.append(self.settings["related_object"])
- operates_on.RelatedObjects = related_objects
- ifcopenshell.api.run(
- "owner.update_owner_history", self.file, **{"element": operates_on}
- )
- else:
- operates_on = self.file.create_entity(
- "IfcRelAssignsToProcess",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run(
- "owner.create_owner_history", self.file
- ),
- "RelatedObjects": [self.settings["related_object"]],
- "RelatingProcess": self.settings["relating_process"],
- }
- )
- return operates_on
+ if operates_on:
+ related_objects = list(operates_on.RelatedObjects)
+ related_objects.append(settings["related_object"])
+ operates_on.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": operates_on})
+ else:
+ operates_on = file.create_entity(
+ "IfcRelAssignsToProcess",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["related_object"]],
+ "RelatingProcess": settings["relating_process"],
+ }
+ )
+ return operates_on
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py
index 3431a71f19..bd9b90d2da 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py
@@ -20,85 +20,75 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_product=None, related_object=None):
- """Assigns a product to be produced as a result of a process
+def assign_product(file, relating_product=None, related_object=None) -> None:
+ """Assigns a product to be produced as a result of a process
- A construction task may result in products (e.g. a wall) being
- constructed. These task "Outputs" are defined in IFC through product
- relationships.
+ A construction task may result in products (e.g. a wall) being
+ constructed. These task "Outputs" are defined in IFC through product
+ relationships.
- Not all tasks have Outputs. For example, maintenance tasks will
- typically not have any outputs.
+ Not all tasks have Outputs. For example, maintenance tasks will
+ typically not have any outputs.
- See ifcopenshell.api.sequence.assign_process for Inputs and other types
- of process relationships that can be described in manufacturing
- process modeling.
+ See ifcopenshell.api.sequence.assign_process for Inputs and other types
+ of process relationships that can be described in manufacturing
+ process modeling.
- :param relating_product: The IfcProduct that was constructed as a result
- of the task.
- :type relating_product: ifcopenshell.entity_instance
- :param related_object: The IfcProcess (typically IfcTask) of the
- construction task.
- :type related_object: ifcopenshell.entity_instance
- :return: The newly created IfcRelAssignsToProduct relationship
- :rtype: ifcopenshell.entity_instance
+ :param relating_product: The IfcProduct that was constructed as a result
+ of the task.
+ :type relating_product: ifcopenshell.entity_instance
+ :param related_object: The IfcProcess (typically IfcTask) of the
+ construction task.
+ :type related_object: ifcopenshell.entity_instance
+ :return: The newly created IfcRelAssignsToProduct relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's create a construction task. Note that the predefined type is
- # important to distinguish types of tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
+ # Let's create a construction task. Note that the predefined type is
+ # important to distinguish types of tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
- # Let's say we have a wall somewhere.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Let's say we have a wall somewhere.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's construct that wall!
- ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task)
- """
- self.file = file
- self.settings = {
- "relating_product": relating_product,
- "related_object": related_object,
- }
+ # Let's construct that wall!
+ ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task)
+ """
+ settings = {
+ "relating_product": relating_product,
+ "related_object": related_object,
+ }
- def execute(self):
- if self.settings["related_object"].HasAssignments:
- for assignment in self.settings["related_object"].HasAssignments:
- if (
- assignment.is_a("IfcRelAssignsToProduct")
- and assignment.RelatingProduct == self.settings["relating_product"]
- ):
- return
+ if settings["related_object"].HasAssignments:
+ for assignment in settings["related_object"].HasAssignments:
+ if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == settings["relating_product"]:
+ return
- referenced_by = None
- if self.settings["relating_product"].ReferencedBy:
- referenced_by = self.settings["relating_product"].ReferencedBy[0]
+ referenced_by = None
+ if settings["relating_product"].ReferencedBy:
+ referenced_by = settings["relating_product"].ReferencedBy[0]
- if referenced_by:
- related_objects = list(referenced_by.RelatedObjects)
- related_objects.append(self.settings["related_object"])
- referenced_by.RelatedObjects = related_objects
- ifcopenshell.api.run(
- "owner.update_owner_history", self.file, **{"element": referenced_by}
- )
- else:
- referenced_by = self.file.create_entity(
- "IfcRelAssignsToProduct",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run(
- "owner.create_owner_history", self.file
- ),
- "RelatedObjects": [self.settings["related_object"]],
- "RelatingProduct": self.settings["relating_product"],
- }
- )
- return referenced_by
+ if referenced_by:
+ related_objects = list(referenced_by.RelatedObjects)
+ related_objects.append(settings["related_object"])
+ referenced_by.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by})
+ else:
+ referenced_by = file.create_entity(
+ "IfcRelAssignsToProduct",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["related_object"]],
+ "RelatingProduct": settings["relating_product"],
+ }
+ )
+ return referenced_by
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py
index a3243f1057..177d90fb8d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py
@@ -17,112 +17,101 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, parent=None, recurrence_type="WEEKLY"):
- """Define a time to recur at a particular interval
+def assign_recurrence_pattern(file, parent=None, recurrence_type="WEEKLY") -> None:
+ """Define a time to recur at a particular interval
- There are two scenarios where you might want to define a recurring time
- pattern.
+ There are two scenarios where you might want to define a recurring time
+ pattern.
- You might want a task to be scheduled at a recurring interval,
- this is common for maintenance tasks which need to be performed monthly,
- every 6 months, every year, etc.
+ You might want a task to be scheduled at a recurring interval,
+ this is common for maintenance tasks which need to be performed monthly,
+ every 6 months, every year, etc.
- Alternatively, you might be defining a work calendar, which defines
- working days or holidays. The working days might be every week from
- monday to friday ("every" week means it recurs every week), or the
- holidays might be the same every year.
+ Alternatively, you might be defining a work calendar, which defines
+ working days or holidays. The working days might be every week from
+ monday to friday ("every" week means it recurs every week), or the
+ holidays might be the same every year.
- The types of recurrence are:
+ The types of recurrence are:
- - DAILY: every Nth (interval) day for up to X (Occurrences) occurrences.
- e.g. Every day, every 2 days, every day up to 5 times, etc
- - WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X
- (Occurrences) occurrences. e.g. Every Monday, every weekday, every
- other saturday, etc
- - MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth
- (Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of
- the Month.
- - MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent)
- of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g.
- Every second Tuesday of the Month.
- - YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND
- (MonthComponent) month of every Yth (Interval) Year up to Z
- (Occurrences) occurrences. e.g. every 25th of December.
- - YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of
- every JFMAMJJASOND (MonthComponent) month of every Yth (Interval)
- Year up to Z (Occurrences) occurrences. e.g. every third Wednesday
- of January.
+ - DAILY: every Nth (interval) day for up to X (Occurrences) occurrences.
+ e.g. Every day, every 2 days, every day up to 5 times, etc
+ - WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X
+ (Occurrences) occurrences. e.g. Every Monday, every weekday, every
+ other saturday, etc
+ - MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth
+ (Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of
+ the Month.
+ - MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent)
+ of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g.
+ Every second Tuesday of the Month.
+ - YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND
+ (MonthComponent) month of every Yth (Interval) Year up to Z
+ (Occurrences) occurrences. e.g. every 25th of December.
+ - YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of
+ every JFMAMJJASOND (MonthComponent) month of every Yth (Interval)
+ Year up to Z (Occurrences) occurrences. e.g. every third Wednesday
+ of January.
- These recurrence patterns are fairly standard in all calendar and
- scheduling applications.
+ These recurrence patterns are fairly standard in all calendar and
+ scheduling applications.
- :param parent: Either an IfcTaskTimeRecurring if you are defining a
- recurring schedule for a task, or IfcWorkTime if you are defining a
- recurring pattern for a workdays or holidays in a calendar.
- :type parent: ifcopenshell.entity_instance
- :param recurrence_type: One of the types of recurrences.
- :type recurrence_type: str
- :return: The newly created IfcRecurrencePattern
- :rtype: ifcopenshell.entity_instance
+ :param parent: Either an IfcTaskTimeRecurring if you are defining a
+ recurring schedule for a task, or IfcWorkTime if you are defining a
+ recurring pattern for a workdays or holidays in a calendar.
+ :type parent: ifcopenshell.entity_instance
+ :param recurrence_type: One of the types of recurrences.
+ :type recurrence_type: str
+ :return: The newly created IfcRecurrencePattern
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
+ # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
- # Let's imagine we are creating a maintenance schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance")
+ # Let's imagine we are creating a maintenance schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance")
- # Now let's imagine we have a task to maintain the chillers
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Chiller maintenance")
+ # Now let's imagine we have a task to maintain the chillers
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Chiller maintenance")
- # Because it is a maintenance task, we must schedule a recurring time
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True)
+ # Because it is a maintenance task, we must schedule a recurring time
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True)
- # We create a monthly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH")
+ # We create a monthly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH")
- # Specifically, the maintenance task must occur every 6 months
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6})
- """
- self.file = file
- self.settings = {"parent": parent, "recurrence_type": recurrence_type}
+ # Specifically, the maintenance task must occur every 6 months
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6})
+ """
+ settings = {"parent": parent, "recurrence_type": recurrence_type}
- def execute(self):
- recurrence = self.file.createIfcRecurrencePattern(
- self.settings["recurrence_type"]
- )
+ recurrence = file.createIfcRecurrencePattern(settings["recurrence_type"])
- if self.settings["parent"].is_a("IfcWorkTime"):
- if (
- self.settings["parent"].RecurrencePattern
- and len(
- self.file.get_inverse(self.settings["parent"].RecurrencePattern)
- )
- == 1
- ):
- self.file.remove(self.settings["parent"].RecurrencePattern)
- self.settings["parent"].RecurrencePattern = recurrence
- elif self.settings["parent"].is_a("IfcTaskTimeRecurring"):
- if len(self.file.get_inverse(self.settings["parent"].Recurrence)) == 1:
- self.file.remove(self.settings["parent"].Recurrence)
- self.settings["parent"].Recurrence = recurrence
- return recurrence
+ if settings["parent"].is_a("IfcWorkTime"):
+ if settings["parent"].RecurrencePattern and len(file.get_inverse(settings["parent"].RecurrencePattern)) == 1:
+ file.remove(settings["parent"].RecurrencePattern)
+ settings["parent"].RecurrencePattern = recurrence
+ elif settings["parent"].is_a("IfcTaskTimeRecurring"):
+ if len(file.get_inverse(settings["parent"].Recurrence)) == 1:
+ file.remove(settings["parent"].Recurrence)
+ settings["parent"].Recurrence = recurrence
+ return recurrence
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py
index e1c1100760..ed5103758c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py
@@ -20,119 +20,111 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(
- self,
- file,
- relating_process=None,
- related_process=None,
- sequence_type="FINISH_START",
- ):
- """Assign a sequential relationship between tasks
+def assign_sequence(
+ file,
+ relating_process=None,
+ related_process=None,
+ sequence_type="FINISH_START",
+) -> None:
+ """Assign a sequential relationship between tasks
- Tasks in construction sequencing typically have sequence relationships
- between them, indicating that one task must happen after another. This
- is used to automatically compute new start and end dates and cascade
- changes when dates are changed. This is also used to calculate critical
- paths and floats.
+ Tasks in construction sequencing typically have sequence relationships
+ between them, indicating that one task must happen after another. This
+ is used to automatically compute new start and end dates and cascade
+ changes when dates are changed. This is also used to calculate critical
+ paths and floats.
- There are four types of sequence relationships, known as finish to
- start, finish to finish, start to start, and start to finish, sometimes
- abbreviated as a (FS, FF, SS, and SF). The most common is the finish to
- start relationship, indicating that the previous task must finish before
- the next task can start.
+ There are four types of sequence relationships, known as finish to
+ start, finish to finish, start to start, and start to finish, sometimes
+ abbreviated as a (FS, FF, SS, and SF). The most common is the finish to
+ start relationship, indicating that the previous task must finish before
+ the next task can start.
- You must not create cyclical task sequences. This makes the computer
- unhappy.
+ You must not create cyclical task sequences. This makes the computer
+ unhappy.
- Note that "previous" or "next" does not necessarily mean the task
- chronologically happens before or after. They simply indicate the order
- of the sequence relationship. For this reason, they are often called
- predecessor and successor tasks in the planning profession.
+ Note that "previous" or "next" does not necessarily mean the task
+ chronologically happens before or after. They simply indicate the order
+ of the sequence relationship. For this reason, they are often called
+ predecessor and successor tasks in the planning profession.
- :param relating_process: The previous / predecessor task.
- :type relating_process: ifcopenshell.entity_instance
- :param related_process: The next / successor task.
- :type related_process: ifcopenshell.entity_instance
- :param sequence_type: Choose from FINISH_START, FINISH_FINISH,
- START_START, or START_FINISH.
- :return: The newly created IfcRelSequence
- :rtype: ifcopenshell.entity_instance
+ :param relating_process: The previous / predecessor task.
+ :type relating_process: ifcopenshell.entity_instance
+ :param related_process: The next / successor task.
+ :type related_process: ifcopenshell.entity_instance
+ :param sequence_type: Choose from FINISH_START, FINISH_FINISH,
+ START_START, or START_FINISH.
+ :return: The newly created IfcRelSequence
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's imagine a root construction task
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Let's imagine a root construction task
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's imagine we're doing a typically formwork, reinforcement,
- # pour sequence. Let's start with the formwork. It'll take us 2
- # days.
- formwork = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Formwork", identification="C.1")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ # Let's imagine we're doing a typically formwork, reinforcement,
+ # pour sequence. Let's start with the formwork. It'll take us 2
+ # days.
+ formwork = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Formwork", identification="C.1")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- # Now let's do the reinforcement. It'll take us another 2 days.
- reinforcement = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Reinforcement", identification="C.2")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ # Now let's do the reinforcement. It'll take us another 2 days.
+ reinforcement = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Reinforcement", identification="C.2")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- # Now the pour itself. It'll only take 1 day.
- pour = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Reinforcement", identification="C.3")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"})
+ # Now the pour it It'll only take 1 day.
+ pour = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Reinforcement", identification="C.3")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"})
- # Now let's say the formwork must finish before the reinforcement
- # can start, and the reinforcement must finish before the pour can
- # start. This is a typical finish to start relationship (FS).
- ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=formwork, related_process=reinforcement)
- ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=reinforcement, related_process=pour)
+ # Now let's say the formwork must finish before the reinforcement
+ # can start, and the reinforcement must finish before the pour can
+ # start. This is a typical finish to start relationship (FS).
+ ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=formwork, related_process=reinforcement)
+ ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=reinforcement, related_process=pour)
- # Notice how we set all the scheduled start dates arbitrarily at
- # 2000-01-01. This is because we can ask IfcOpenShell to
- # automatically cascade the dates, starting from any task. This will
- # update the reinforcement date to be 2000-01-03 and the pour date
- # to be 2000-01-05.
- ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork)
- """
- self.file = file
- self.settings = {
- "relating_process": relating_process,
- "related_process": related_process,
- "sequence_type": sequence_type,
+ # Notice how we set all the scheduled start dates arbitrarily at
+ # 2000-01-01. This is because we can ask IfcOpenShell to
+ # automatically cascade the dates, starting from any task. This will
+ # update the reinforcement date to be 2000-01-03 and the pour date
+ # to be 2000-01-05.
+ ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork)
+ """
+ settings = {
+ "relating_process": relating_process,
+ "related_process": related_process,
+ "sequence_type": sequence_type,
+ }
+
+ for rel in settings["related_process"].IsSuccessorFrom or []:
+ if rel.RelatingProcess == settings["relating_process"]:
+ return rel
+ rel = file.create_entity(
+ "IfcRelSequence",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatingProcess": settings["relating_process"],
+ "RelatedProcess": settings["related_process"],
+ "SequenceType": settings["sequence_type"],
}
-
- def execute(self):
- for rel in self.settings["related_process"].IsSuccessorFrom or []:
- if rel.RelatingProcess == self.settings["relating_process"]:
- return rel
- rel = self.file.create_entity(
- "IfcRelSequence",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run(
- "owner.create_owner_history", self.file
- ),
- "RelatingProcess": self.settings["relating_process"],
- "RelatedProcess": self.settings["related_process"],
- "SequenceType": self.settings["sequence_type"],
- }
- )
- ifcopenshell.api.run(
- "sequence.cascade_schedule", self.file, task=self.settings["relating_process"]
- )
- return rel
+ )
+ ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["relating_process"])
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py
index a1eaed71be..634f2af494 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py
@@ -20,52 +20,49 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, work_schedule=None, work_plan=None):
- """Assigns a work schedule to a work plan
+def assign_workplan(file, work_schedule=None, work_plan=None) -> None:
+ """Assigns a work schedule to a work plan
- Typically, work schedules would be assigned to a work plan at creation.
- However you may also delay this and do it manually afterwards.
+ Typically, work schedules would be assigned to a work plan at creation.
+ However you may also delay this and do it manually afterwards.
- :param work_schedule: The IfcWorkSchedule that will be assigned to the
- work plan.
- :type work_schedule: ifcopenshell.entity_instance
- :param work_plan: The IfcWorkPlan for the schedule to be assigned to.
- :type work_plan: ifcopenshell.entity_instance
- :return: The IfcRelAggregates relationship
- :rtype: ifcopenshell.entity_instance
+ :param work_schedule: The IfcWorkSchedule that will be assigned to the
+ work plan.
+ :type work_schedule: ifcopenshell.entity_instance
+ :param work_plan: The IfcWorkPlan for the schedule to be assigned to.
+ :type work_plan: ifcopenshell.entity_instance
+ :return: The IfcRelAggregates relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
- # Alternatively, if you create a schedule without a work plan ...
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Alternatively, if you create a schedule without a work plan ...
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # ... you can assign the work plan afterwards.
- ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan)
- """
- self.file = file
- self.settings = {"work_schedule": work_schedule, "work_plan": work_plan}
+ # ... you can assign the work plan afterwards.
+ ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan)
+ """
+ settings = {"work_schedule": work_schedule, "work_plan": work_plan}
- def execute(self):
- # TODO: this is an ambiguity by buildingSMART
- # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
- ifcopenshell.api.run(
- "project.unassign_declaration",
- self.file,
- definitions=[self.settings["work_schedule"]],
- relating_context=self.file.by_type("IfcContext")[0],
- )
- rel_aggregates = ifcopenshell.api.run(
- "aggregate.assign_object",
- self.file,
- **{
- "products": [self.settings["work_schedule"]],
- "relating_object": self.settings["work_plan"],
- }
- )
- return rel_aggregates
+ # TODO: this is an ambiguity by buildingSMART
+ # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
+ ifcopenshell.api.run(
+ "project.unassign_declaration",
+ file,
+ definitions=[settings["work_schedule"]],
+ relating_context=file.by_type("IfcContext")[0],
+ )
+ rel_aggregates = ifcopenshell.api.run(
+ "aggregate.assign_object",
+ file,
+ **{
+ "products": [settings["work_schedule"]],
+ "relating_object": settings["work_plan"],
+ }
+ )
+ return rel_aggregates
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py
index 6698ab85a2..22c9ec7dda 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py
@@ -22,68 +22,71 @@ import ifcopenshell.util.date
import ifcopenshell.util.element
+def calculate_task_duration(file, task=None) -> None:
+ """Calculates the task duration based on resource usage
+
+ If a task has labour or equipment resources assigned to it, its duration
+ may be parametrically derived from the scheduled work of the resource.
+ For example, a labour resource with scheduled work of 10 working days
+ and a resource utilisation of 200% (i.e. two labour teams) will imply
+ that the task duration is 5 working days.
+
+ If this data is not available, such as if the task has no resources,
+ then nothing happens.
+
+ :param task: The IfcTask to calculate the duration for.
+ :type task: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Add our own crew
+ crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
+
+ # Add some labour to our crew.
+ labour = ifcopenshell.api.run("resource.add_resource", model,
+ parent_resource=crew, ifc_class="IfcLaborResource")
+
+ # Labour resource is quantified in terms of time.
+ quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
+ resource=labour, ifc_class="IfcQuantityTime")
+
+ # Store the unit time used in hours
+ ifcopenshell.api.run("resource.edit_resource_quantity", model,
+ physical_quantity=quantity, attributes={"TimeValue": 8.0})
+
+ # Let's imagine we've used the resource for 10 days with a
+ # utilisation of 200%.
+ time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
+ ifcopenshell.api.run("resource.edit_resource_time", model,
+ resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2})
+
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+
+ # Let's create a construction task. Note that the predefined type is
+ # important to distinguish types of tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Foundations", identification="A")
+
+ # Assign our resource to the task.
+ ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour)
+
+ # Now we can calculate the task duration based on the resource. This
+ # will set task.TaskTime.ScheduleDuration to be P5D.
+ ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"task": task}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, task=None):
- """Calculates the task duration based on resource usage
-
- If a task has labour or equipment resources assigned to it, its duration
- may be parametrically derived from the scheduled work of the resource.
- For example, a labour resource with scheduled work of 10 working days
- and a resource utilisation of 200% (i.e. two labour teams) will imply
- that the task duration is 5 working days.
-
- If this data is not available, such as if the task has no resources,
- then nothing happens.
-
- :param task: The IfcTask to calculate the duration for.
- :type task: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Add our own crew
- crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource")
-
- # Add some labour to our crew.
- labour = ifcopenshell.api.run("resource.add_resource", model,
- parent_resource=crew, ifc_class="IfcLaborResource")
-
- # Labour resource is quantified in terms of time.
- quantity = ifcopenshell.api.run("resource.add_resource_quantity", model,
- resource=labour, ifc_class="IfcQuantityTime")
-
- # Store the unit time used in hours
- ifcopenshell.api.run("resource.edit_resource_quantity", model,
- physical_quantity=quantity, attributes={"TimeValue": 8.0})
-
- # Let's imagine we've used the resource for 10 days with a
- # utilisation of 200%.
- time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour)
- ifcopenshell.api.run("resource.edit_resource_time", model,
- resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2})
-
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
-
- # Let's create a construction task. Note that the predefined type is
- # important to distinguish types of tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Foundations", identification="A")
-
- # Assign our resource to the task.
- ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour)
-
- # Now we can calculate the task duration based on the resource. This
- # will set task.TaskTime.ScheduleDuration to be P5D.
- ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task)
- """
- self.file = file
- self.settings = {"task": task}
-
def execute(self):
self.seconds_per_workday = self.calculate_seconds_per_workday()
duration = self.calculate_max_resource_usage_duration()
@@ -93,9 +96,7 @@ class Usecase:
def calculate_seconds_per_workday(self):
def get_work_schedule(task):
for rel in task.HasAssignments or []:
- if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a(
- "IfcWorkSchedule"
- ):
+ if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"):
return rel.RelatingControl
for rel in task.Nests or []:
return get_work_schedule(rel.RelatingObject)
@@ -111,9 +112,7 @@ class Usecase:
or "WorkDayDuration" not in psets["Pset_WorkControlCommon"]
):
return default_seconds_per_workday
- work_day_duration = ifcopenshell.util.date.ifc2datetime(
- psets["Pset_WorkControlCommon"]["WorkDayDuration"]
- )
+ work_day_duration = ifcopenshell.util.date.ifc2datetime(psets["Pset_WorkControlCommon"]["WorkDayDuration"])
return work_day_duration.seconds
def calculate_max_resource_usage_duration(self):
@@ -133,23 +132,15 @@ class Usecase:
if not resource.Usage or not resource.Usage.ScheduleWork:
return
schedule_usage = resource.Usage.ScheduleUsage or 1
- schedule_duration = ifcopenshell.util.date.ifc2datetime(
- resource.Usage.ScheduleWork
- )
+ schedule_duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork)
if is_hourly_work(resource.Usage.ScheduleWork):
- schedule_seconds = (
- schedule_duration.days * 24 * 60 * 60
- ) + schedule_duration.seconds
+ schedule_seconds = (schedule_duration.days * 24 * 60 * 60) + schedule_duration.seconds
else:
partial_days = schedule_duration.seconds / (24 * 60 * 60)
- schedule_seconds = (
- schedule_duration.days + partial_days
- ) * self.seconds_per_workday
+ schedule_seconds = (schedule_duration.days + partial_days) * self.seconds_per_workday
return math.ceil((schedule_seconds / self.seconds_per_workday) / schedule_usage)
def set_task_duration(self, duration):
if not self.settings["task"].TaskTime:
- ifcopenshell.api.run(
- "sequence.add_task_time", self.file, task=self.settings["task"]
- )
+ ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.settings["task"])
self.settings["task"].TaskTime.ScheduleDuration = f"P{duration}D"
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py
index 2a720b9fa1..0a2aea7190 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py
@@ -21,90 +21,93 @@ import ifcopenshell.util.date
import ifcopenshell.util.sequence
+def cascade_schedule(file, task=None) -> None:
+ """Cascades start and end dates of tasks based on durations
+
+ Given a start task with a start date and duration, the end date, and the
+ start and end of all successor tasks with durations may be automatically
+ computed.
+
+ Using this automatic computation is recommended is an alternative to
+ manually specifying dates. It is useful for doing edits and cascading
+ changes.
+
+ Dates can only cascade from predecessor to successors, not backwards.
+ Cyclical relationships are invalid and will result in a recursion error
+ being raised.
+
+ Note that there may be differences between how different planning
+ software calculate start and end dates. Some may consider Monday 5pm to
+ be equivalent to be Tuesday 8am, for instance.
+
+ :param task: The start task to begin cascading from.
+ :type task: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Define a convenience function to add a task chained to a predecessor
+ def add_task(model, name, predecessor, work_schedule):
+ # Add a construction task
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION")
+
+ # Give it a time
+ task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
+
+ # Arbitrarily set the task's scheduled time duration to be 1 week
+ ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time,
+ attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"})
+
+ # If a predecessor exists, create a finish to start relationship
+ if predecessor:
+ ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=predecessor, related_process=task)
+
+ return task
+
+ # Open an existing IFC4 model you have of a building
+ model = ifcopenshell.open("/path/to/existing/model.ifc")
+
+ # Create a new construction schedule
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction")
+
+ # Let's imagine a starting task for site establishment.
+ task = add_task(model, "Site establishment", None, schedule)
+ start_task = task
+
+ # Get all our storeys sorted by elevation ascending.
+ storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s))
+
+ # For each storey ...
+ for storey in storeys:
+
+ # Add a construction task to construct that storey, using our convenience function
+ task = add_task(model, f"Construct {storey.Name}", task, schedule)
+
+ # Assign all the products in that storey to the task as construction outputs.
+ for product in get_decomposition(storey):
+ ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task)
+
+ # Ask the computer to calculate all the dates for us from the start task.
+ # For example, if the first task started on the 1st of January and took a
+ # week, the next task will start on the 8th of January. This saves us
+ # manually doing date calculations.
+ ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task)
+
+ # Calculate the critical path and floats.
+ ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"task": task}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, task=None):
- """Cascades start and end dates of tasks based on durations
-
- Given a start task with a start date and duration, the end date, and the
- start and end of all successor tasks with durations may be automatically
- computed.
-
- Using this automatic computation is recommended is an alternative to
- manually specifying dates. It is useful for doing edits and cascading
- changes.
-
- Dates can only cascade from predecessor to successors, not backwards.
- Cyclical relationships are invalid and will result in a recursion error
- being raised.
-
- Note that there may be differences between how different planning
- software calculate start and end dates. Some may consider Monday 5pm to
- be equivalent to be Tuesday 8am, for instance.
-
- :param task: The start task to begin cascading from.
- :type task: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Define a convenience function to add a task chained to a predecessor
- def add_task(model, name, predecessor, work_schedule):
- # Add a construction task
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION")
-
- # Give it a time
- task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task)
-
- # Arbitrarily set the task's scheduled time duration to be 1 week
- ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time,
- attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"})
-
- # If a predecessor exists, create a finish to start relationship
- if predecessor:
- ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=predecessor, related_process=task)
-
- return task
-
- # Open an existing IFC4 model you have of a building
- model = ifcopenshell.open("/path/to/existing/model.ifc")
-
- # Create a new construction schedule
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction")
-
- # Let's imagine a starting task for site establishment.
- task = add_task(model, "Site establishment", None, schedule)
- start_task = task
-
- # Get all our storeys sorted by elevation ascending.
- storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s))
-
- # For each storey ...
- for storey in storeys:
-
- # Add a construction task to construct that storey, using our convenience function
- task = add_task(model, f"Construct {storey.Name}", task, schedule)
-
- # Assign all the products in that storey to the task as construction outputs.
- for product in get_decomposition(storey):
- ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task)
-
- # Ask the computer to calculate all the dates for us from the start task.
- # For example, if the first task started on the 1st of January and took a
- # week, the next task will start on the 8th of January. This saves us
- # manually doing date calculations.
- ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task)
-
- # Calculate the critical path and floats.
- ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule)
- """
- self.file = file
- self.settings = {"task": task}
-
def execute(self):
self.calendar_cache = {}
self.cascade_task(self.settings["task"], is_first_task=True)
@@ -135,14 +138,10 @@ class Usecase:
finishes = []
starts = []
- for rel in ifcopenshell.util.sequence.get_sequence_assignment(
- task, "predecessor"
- ):
+ for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor"):
predecessor = rel.RelatingProcess
predecessor_duration = (
- ifcopenshell.util.date.ifc2datetime(
- predecessor.TaskTime.ScheduleDuration
- )
+ ifcopenshell.util.date.ifc2datetime(predecessor.TaskTime.ScheduleDuration)
if predecessor.TaskTime and predecessor.TaskTime.ScheduleDuration
else datetime.timedelta()
)
@@ -154,14 +153,16 @@ class Usecase:
duration_type = "WORKTIME"
if rel.TimeLag:
# updated to handle IfcRatioMeasure as a TimeLag value
- days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ days += (
+ self.get_lag_time_days(rel.TimeLag)
+ if rel.TimeLag.LagValue.is_a("IfcDuration")
+ else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ )
duration_type = rel.TimeLag.DurationType
if days:
starts.append(
datetime.datetime.combine(
- self.offset_date(
- finish, days, duration_type, self.get_calendar(task)
- ),
+ self.offset_date(finish, days, duration_type, self.get_calendar(task)),
datetime.time(9),
)
)
@@ -183,18 +184,14 @@ class Usecase:
if not start:
continue
if rel.TimeLag:
- days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ days = (
+ self.get_lag_time_days(rel.TimeLag)
+ if rel.TimeLag.LagValue.is_a("IfcDuration")
+ else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ )
duration_type = rel.TimeLag.DurationType
- starts.append(
- self.offset_date(
- start, days, duration_type, self.get_calendar(task)
- )
- )
- starts.append(
- self.offset_date(
- start, days, duration_type, self.get_calendar(predecessor)
- )
- )
+ starts.append(self.offset_date(start, days, duration_type, self.get_calendar(task)))
+ starts.append(self.offset_date(start, days, duration_type, self.get_calendar(predecessor)))
else:
starts.append(start)
elif rel.SequenceType == "FINISH_FINISH":
@@ -202,18 +199,14 @@ class Usecase:
if not finish:
continue
if rel.TimeLag:
- days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ days = (
+ self.get_lag_time_days(rel.TimeLag)
+ if rel.TimeLag.LagValue.is_a("IfcDuration")
+ else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ )
duration_type = rel.TimeLag.DurationType
- finishes.append(
- self.offset_date(
- finish, days, duration_type, self.get_calendar(task)
- )
- )
- finishes.append(
- self.offset_date(
- finish, days, duration_type, self.get_calendar(predecessor)
- )
- )
+ finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(task)))
+ finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(predecessor)))
else:
finishes.append(finish)
elif rel.SequenceType == "START_FINISH":
@@ -223,14 +216,16 @@ class Usecase:
days = -1
duration_type = "WORKTIME"
if rel.TimeLag:
- days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ days += (
+ self.get_lag_time_days(rel.TimeLag)
+ if rel.TimeLag.LagValue.is_a("IfcDuration")
+ else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue
+ )
duration_type = rel.TimeLag.DurationType
if days or rel.TimeLag:
finishes.append(
datetime.datetime.combine(
- self.offset_date(
- start, days, duration_type, self.get_calendar(task)
- ),
+ self.offset_date(start, days, duration_type, self.get_calendar(task)),
datetime.time(17),
)
)
@@ -263,9 +258,7 @@ class Usecase:
if task.TaskTime.ScheduleStart == start_ifc and not is_first_task:
return
task.TaskTime.ScheduleStart = start_ifc
- task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(
- potential_finish, "IfcDateTime"
- )
+ task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(potential_finish, "IfcDateTime")
else:
finish_ifc = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime")
if task.TaskTime.ScheduleFinish == finish_ifc and not is_first_task:
@@ -328,15 +321,11 @@ class Usecase:
def get_calendar(self, task):
if task.id() not in self.calendar_cache:
- self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(
- task
- )
+ self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(task)
return self.calendar_cache[task.id()]
def offset_date(self, date, days, duration_type, calendar):
- return ifcopenshell.util.sequence.offset_date(
- date, datetime.timedelta(days=days), duration_type, calendar
- )
+ return ifcopenshell.util.sequence.offset_date(date, datetime.timedelta(days=days), duration_type, calendar)
def get_task_time_attribute(self, task, attribute):
if task.TaskTime:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py
index c0ebe4f72f..a06fe8d722 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py
@@ -21,38 +21,41 @@ import ifcopenshell.util.system
import ifcopenshell.util.element
+def create_baseline(file, work_schedule=None, name=None) -> None:
+ """Creates a baseline for your Work Schedule
+
+ Using a IfcWorkSchdule having PredefinedType=PLANNED,
+ We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE
+ and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline.
+
+ The following relationships are also baselined:
+
+ * Same Tasks & attributes
+ * Same Task Relationships
+ * Same Construction Resources
+ * Same Resource Relationships
+
+ :param work_schedule: The planned work_schedule to baseline
+ :type work_schedule: ifcopenshell.entity_instance
+ :return: The baseline work_schedule
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+ .. code:: python
+
+ # We have a Work Schedule
+ planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01")
+
+ # And now we have a baseline for our Work Schedule
+ baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1")
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"work_schedule": work_schedule, "name": name}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, work_schedule=None, name=None):
- """Creates a baseline for your Work Schedule
-
- Using a IfcWorkSchdule having PredefinedType=PLANNED,
- We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE
- and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline.
-
- The following relationships are also baselined:
-
- * Same Tasks & attributes
- * Same Task Relationships
- * Same Construction Resources
- * Same Resource Relationships
-
- :param work_schedule: The planned work_schedule to baseline
- :type work_schedule: ifcopenshell.entity_instance
- :return: The baseline work_schedule
- :rtype: ifcopenshell.entity_instance
-
- Example:
- .. code:: python
-
- # We have a Work Schedule
- planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01")
-
- # And now we have a baseline for our Work Schedule
- baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1")
- """
- self.file = file
- self.settings = {"work_schedule": work_schedule, "name": name}
-
def execute(self):
result = self.create_baseline_work_schedule(self.settings["work_schedule"])
return result
@@ -92,17 +95,13 @@ class Usecase:
related_objects = list(referenced_by.RelatedObjects)
related_objects.append(related_object)
referenced_by.RelatedObjects = related_objects
- ifcopenshell.api.run(
- "owner.update_owner_history", self.file, **{"element": referenced_by}
- )
+ ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by})
else:
referenced_by = self.file.create_entity(
"IfcRelDefinesByObject",
**{
"GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run(
- "owner.create_owner_history", self.file
- ),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
"RelatedObjects": [related_object],
"RelatingObject": relating_object,
}
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py
index 91d5ed1b99..14016794cd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py
@@ -22,33 +22,36 @@ import ifcopenshell.util.element
import ifcopenshell.util.sequence
+def duplicate_task(file, task=None) -> None:
+ """Duplicates a task in the project
+
+ The following relationships are also duplicated:
+
+ * The copy will have the same attributes and property sets as the original task
+ * The copy will be assigned to the parent task or work schedule
+ * The copy will have duplicated nested tasks
+
+ :param task: The task to be duplicated
+ :type task: ifcopenshell.entity_instance
+ :return: The duplicated task or the list of duplicated tasks if the latter has children
+ :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
+
+ Example:
+ .. code:: python
+
+ # We have a task
+ original_task = Task(name="Design new feature", deadline="2023-03-01")
+
+ # And now we have two
+ duplicated_task = project.duplicate_task(original_task)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"task": task}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, task=None):
- """Duplicates a task in the project
-
- The following relationships are also duplicated:
-
- * The copy will have the same attributes and property sets as the original task
- * The copy will be assigned to the parent task or work schedule
- * The copy will have duplicated nested tasks
-
- :param task: The task to be duplicated
- :type task: ifcopenshell.entity_instance
- :return: The duplicated task or the list of duplicated tasks if the latter has children
- :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance
-
- Example:
- .. code:: python
-
- # We have a task
- original_task = Task(name="Design new feature", deadline="2023-03-01")
-
- # And now we have two
- duplicated_task = project.duplicate_task(original_task)
- """
- self.file = file
- self.settings = {"task": task}
-
def execute(self):
self.tracker = {"current": [], "duplicate": []}
self.duplicate_task(self.settings["task"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py
index f5779b058c..4b77daf9e8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py
@@ -20,79 +20,68 @@ import ifcopenshell.api
import ifcopenshell.util.date
-class Usecase:
- def __init__(self, file, lag_time=None, attributes=None):
- """Edits the attributes of an IfcLagTime
+def edit_lag_time(file, lag_time=None, attributes=None) -> None:
+ """Edits the attributes of an IfcLagTime
- For more information about the attributes and data types of an
- IfcLagTime, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcLagTime, consult the IFC documentation.
- :param lag_time: The IfcLagTime entity you want to edit
- :type lag_time: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param lag_time: The IfcLagTime entity you want to edit
+ :type lag_time: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's imagine a root construction task
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Let's imagine a root construction task
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's imagine we're doing a typically formwork, reinforcement,
- # pour sequence. Let's start with the formwork. It'll take us 2
- # days.
- formwork = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Formwork", identification="C.1")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ # Let's imagine we're doing a typically formwork, reinforcement,
+ # pour sequence. Let's start with the formwork. It'll take us 2
+ # days.
+ formwork = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Formwork", identification="C.1")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- # Now let's do the reinforcement. It'll take us another 2 days.
- reinforcement = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Reinforcement", identification="C.2")
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ # Now let's do the reinforcement. It'll take us another 2 days.
+ reinforcement = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Reinforcement", identification="C.2")
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- # Now let's say the formwork must finish before the reinforcement
- # can start. This is a typical finish to start relationship (FS).
- sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=formwork, related_process=reinforcement)
+ # Now let's say the formwork must finish before the reinforcement
+ # can start. This is a typical finish to start relationship (FS).
+ sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=formwork, related_process=reinforcement)
- # Now typically there would be no lag time between formwork and
- # reinforcement, but let's pretend that we had to allow 1 day gap
- # for whatever reason.
- lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
+ # Now typically there would be no lag time between formwork and
+ # reinforcement, but let's pretend that we had to allow 1 day gap
+ # for whatever reason.
+ lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D")
- # Or, let's make it 2 days instead.
- ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"})
- """
- self.file = file
- self.settings = {"lag_time": lag_time, "attributes": attributes or {}}
+ # Or, let's make it 2 days instead.
+ ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"})
+ """
+ settings = {"lag_time": lag_time, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- if name == "LagValue" and value is not None:
- if isinstance(value, float):
- value = self.file.createIfcRatioMeasure(value)
- else:
- value = self.file.createIfcDuration(
- ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
- )
- setattr(self.settings["lag_time"], name, value)
- for rel in [
- r
- for r in self.file.get_inverse(self.settings["lag_time"])
- if r.is_a("IfcRelSequence")
- ]:
- ifcopenshell.api.run(
- "sequence.cascade_schedule", self.file, task=rel.RelatedProcess
- )
+ for name, value in settings["attributes"].items():
+ if name == "LagValue" and value is not None:
+ if isinstance(value, float):
+ value = file.createIfcRatioMeasure(value)
+ else:
+ value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(value, "IfcDuration"))
+ setattr(settings["lag_time"], name, value)
+ for rel in [r for r in file.get_inverse(settings["lag_time"]) if r.is_a("IfcRelSequence")]:
+ ifcopenshell.api.run("sequence.cascade_schedule", file, task=rel.RelatedProcess)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py
index 21292233ad..2863102b3d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py
@@ -20,48 +20,45 @@ import ifcopenshell
import ifcopenshell.util.sequence
-class Usecase:
- def __init__(self, file, recurrence_pattern=None, attributes=None):
- """Edits the attributes of an IfcRecurrencePattern
+def edit_recurrence_pattern(file, recurrence_pattern=None, attributes=None) -> None:
+ """Edits the attributes of an IfcRecurrencePattern
- For more information about the attributes and data types of an
- IfcRecurrencePattern, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcRecurrencePattern, consult the IFC documentation.
- :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit
- :type recurrence_pattern: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit
+ :type recurrence_pattern: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
- """
- self.file = file
- self.settings = {
- "recurrence_pattern": recurrence_pattern,
- "attributes": attributes or {},
- }
+ # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
+ """
+ settings = {
+ "recurrence_pattern": recurrence_pattern,
+ "attributes": attributes or {},
+ }
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["recurrence_pattern"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["recurrence_pattern"], name, value)
- ifcopenshell.util.sequence.is_working_day.cache_clear()
- ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
+ ifcopenshell.util.sequence.is_working_day.cache_clear()
+ ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py
index c563cb6990..bcbc521ef3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py
@@ -20,55 +20,52 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, rel_sequence=None, attributes=None):
- """Edits the attributes of an IfcRelSequence
+def edit_sequence(file, rel_sequence=None, attributes=None) -> None:
+ """Edits the attributes of an IfcRelSequence
- For more information about the attributes and data types of an
- IfcRelSequence, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcRelSequence, consult the IFC documentation.
- :param rel_sequence: The IfcRelSequence entity you want to edit
- :type rel_sequence: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param rel_sequence: The IfcRelSequence entity you want to edit
+ :type rel_sequence: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's imagine a root construction task
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Let's imagine a root construction task
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's imagine we're building 2 zones, one after another.
- zone1 = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Zone 1", identification="C.1")
- zone2 = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Zone 2", identification="C.2")
+ # Let's imagine we're building 2 zones, one after another.
+ zone1 = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Zone 1", identification="C.1")
+ zone2 = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Zone 2", identification="C.2")
- # Zone 1 finishes, then zone 2 starts.
- sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=zone1, related_process=zone2)
+ # Zone 1 finishes, then zone 2 starts.
+ sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=zone1, related_process=zone2)
- # What if they both started at the same time?
- ifcopenshell.api.run("sequence.edit_sequence", model,
- rel_sequence=sequence, attributes={"SequenceType": "START_START"})
- """
- self.file = file
- self.settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}}
+ # What if they both started at the same time?
+ ifcopenshell.api.run("sequence.edit_sequence", model,
+ rel_sequence=sequence, attributes={"SequenceType": "START_START"})
+ """
+ settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["rel_sequence"], name, value)
- if "SequenceType" in self.settings["attributes"].keys():
- ifcopenshell.api.run(
- "sequence.cascade_schedule",
- self.file,
- task=self.settings["rel_sequence"].RelatedProcess,
- )
+ for name, value in settings["attributes"].items():
+ setattr(settings["rel_sequence"], name, value)
+ if "SequenceType" in settings["attributes"].keys():
+ ifcopenshell.api.run(
+ "sequence.cascade_schedule",
+ file,
+ task=settings["rel_sequence"].RelatedProcess,
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py
index cbdaa18de0..d151926a69 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py
@@ -17,39 +17,36 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, task=None, attributes=None):
- """Edits the attributes of an IfcTask
+def edit_task(file, task=None, attributes=None) -> None:
+ """Edits the attributes of an IfcTask
- For more information about the attributes and data types of an
- IfcTask, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcTask, consult the IFC documentation.
- :param task: The IfcTask entity you want to edit
- :type task: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param task: The IfcTask entity you want to edit
+ :type task: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Add a root task to represent the design milestones, and major
- # project phases.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Milestones", identification="A")
+ # Add a root task to represent the design milestones, and major
+ # project phases.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Milestones", identification="A")
- # Change the identification
- ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"})
- """
- self.file = file
- self.settings = {"task": task, "attributes": attributes or {}}
+ # Change the identification
+ ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"})
+ """
+ settings = {"task": task, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["task"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["task"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py
index 10bec7909c..f81b2b1f90 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py
@@ -23,46 +23,48 @@ import ifcopenshell.util.sequence
from typing import Any, Optional
+def edit_task_time(
+ file: ifcopenshell.file,
+ task_time: ifcopenshell.entity_instance,
+ attributes: Optional[dict[str, Any]] = None,
+) -> None:
+ """Edits the attributes of an IfcTaskTime
+
+ For more information about the attributes and data types of an
+ IfcTaskTime, consult the IFC documentation.
+
+ :param task_time: The IfcTaskTime entity you want to edit
+ :type task_time: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+
+ # Create a task to do formwork
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Formwork", identification="A")
+
+ # Let's say it takes 2 days and starts on the 1st of January, 2000
+ time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
+ ifcopenshell.api.run("sequence.edit_task_time", model,
+ task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"task_time": task_time, "attributes": attributes or {}}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- task_time: ifcopenshell.entity_instance,
- attributes: Optional[dict[str, Any]] = None,
- ):
- """Edits the attributes of an IfcTaskTime
-
- For more information about the attributes and data types of an
- IfcTaskTime, consult the IFC documentation.
-
- :param task_time: The IfcTaskTime entity you want to edit
- :type task_time: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
-
- # Create a task to do formwork
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Formwork", identification="A")
-
- # Let's say it takes 2 days and starts on the 1st of January, 2000
- time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork)
- ifcopenshell.api.run("sequence.edit_task_time", model,
- task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"})
- """
- self.file = file
- self.settings = {"task_time": task_time, "attributes": attributes or {}}
-
- def execute(self) -> None:
+ def execute(self):
self.task = self.get_task()
self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task)
@@ -73,17 +75,13 @@ class Usecase:
):
del self.settings["attributes"]["ScheduleFinish"]
- duration_type = self.settings["attributes"].get(
- "DurationType", self.settings["task_time"].DurationType
- )
+ duration_type = self.settings["attributes"].get("DurationType", self.settings["task_time"].DurationType)
finish = self.settings["attributes"].get("ScheduleFinish", None)
if finish:
if isinstance(finish, str):
finish = datetime.datetime.fromisoformat(finish)
self.settings["attributes"]["ScheduleFinish"] = datetime.datetime.combine(
- ifcopenshell.util.sequence.get_soonest_working_day(
- finish, duration_type, self.calendar
- ),
+ ifcopenshell.util.sequence.get_soonest_working_day(finish, duration_type, self.calendar),
datetime.time(17),
)
start = self.settings["attributes"].get("ScheduleStart", None)
@@ -91,9 +89,7 @@ class Usecase:
if isinstance(start, str):
start = datetime.datetime.fromisoformat(start)
self.settings["attributes"]["ScheduleStart"] = datetime.datetime.combine(
- ifcopenshell.util.sequence.get_soonest_working_day(
- start, duration_type, self.calendar
- ),
+ ifcopenshell.util.sequence.get_soonest_working_day(start, duration_type, self.calendar),
datetime.time(9),
)
@@ -101,11 +97,7 @@ class Usecase:
if value is not None:
if "Start" in name or "Finish" in name or name == "StatusTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
- elif (
- name == "ScheduleDuration"
- or name == "ActualDuration"
- or name == "RemainingTime"
- ):
+ elif name == "ScheduleDuration" or name == "ActualDuration" or name == "RemainingTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["task_time"], name, value)
@@ -115,15 +107,9 @@ class Usecase:
and self.settings["task_time"].ScheduleStart
):
self.calculate_finish()
- elif (
- self.settings["attributes"].get("ScheduleStart", None)
- and self.settings["task_time"].ScheduleDuration
- ):
+ elif self.settings["attributes"].get("ScheduleStart", None) and self.settings["task_time"].ScheduleDuration:
self.calculate_finish()
- elif (
- self.settings["attributes"].get("ScheduleFinish", None)
- and self.settings["task_time"].ScheduleStart
- ):
+ elif self.settings["attributes"].get("ScheduleFinish", None) and self.settings["task_time"].ScheduleStart:
self.calculate_duration()
if self.settings["task_time"].ScheduleDuration and (
@@ -137,57 +123,36 @@ class Usecase:
def calculate_finish(self):
finish = ifcopenshell.util.sequence.get_start_or_finish_date(
- ifcopenshell.util.date.ifc2datetime(
- self.settings["task_time"].ScheduleStart
- ),
- ifcopenshell.util.date.ifc2datetime(
- self.settings["task_time"].ScheduleDuration
- ),
+ ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart),
+ ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleDuration),
self.settings["task_time"].DurationType,
self.calendar,
date_type="FINISH",
)
- self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(
- finish, "IfcDateTime"
- )
+ self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime")
def calculate_duration(self):
- start = ifcopenshell.util.date.ifc2datetime(
- self.settings["task_time"].ScheduleStart
- )
- finish = ifcopenshell.util.date.ifc2datetime(
- self.settings["task_time"].ScheduleFinish
- )
+ start = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart)
+ finish = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleFinish)
current_date = datetime.date(start.year, start.month, start.day)
finish_date = datetime.date(finish.year, finish.month, finish.day)
duration = datetime.timedelta(days=1)
while current_date < finish_date:
- if (
- self.settings["task_time"].DurationType == "ELAPSEDTIME"
- or not self.calendar
- ):
+ if self.settings["task_time"].DurationType == "ELAPSEDTIME" or not self.calendar:
duration += datetime.timedelta(days=1)
elif ifcopenshell.util.sequence.is_working_day(current_date, self.calendar):
duration += datetime.timedelta(days=1)
current_date += datetime.timedelta(days=1)
- self.settings[
- "task_time"
- ].ScheduleDuration = ifcopenshell.util.date.datetime2ifc(
- duration, "IfcDuration"
- )
+ self.settings["task_time"].ScheduleDuration = ifcopenshell.util.date.datetime2ifc(duration, "IfcDuration")
def get_task(self) -> ifcopenshell.entity_instance:
- return next(
- e
- for e in self.file.get_inverse(self.settings["task_time"])
- if e.is_a("IfcTask")
- )
+ return next(e for e in self.file.get_inverse(self.settings["task_time"]) if e.is_a("IfcTask"))
def handle_resource_calculation(self):
resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False)
for resource in resources:
if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleWork"):
ifcopenshell.api.run("resource.calculate_resource_usage", self.file, resource=resource)
- #TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated.
+ # TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated.
# elif ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleUsage"):
# ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=resource)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py
index 12ce1e15d1..4efb35da84 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py
@@ -17,34 +17,31 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, work_calendar=None, attributes=None):
- """Edits the attributes of an IfcWorkCalendar
+def edit_work_calendar(file, work_calendar=None, attributes=None) -> None:
+ """Edits the attributes of an IfcWorkCalendar
- For more information about the attributes and data types of an
- IfcWorkCalendar, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcWorkCalendar, consult the IFC documentation.
- :param work_calendar: The IfcWorkCalendar entity you want to edit
- :type work_calendar: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param work_calendar: The IfcWorkCalendar entity you want to edit
+ :type work_calendar: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
- # Let's give it a description
- ifcopenshell.api.run("sequence.edit_work_calendar", model,
- work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"})
- """
- self.file = file
- self.settings = {"work_calendar": work_calendar, "attributes": attributes or {}}
+ # Let's give it a description
+ ifcopenshell.api.run("sequence.edit_work_calendar", model,
+ work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"})
+ """
+ settings = {"work_calendar": work_calendar, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["work_calendar"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["work_calendar"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py
index 669ef0193c..e2bbcae33f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py
@@ -19,39 +19,36 @@
import ifcopenshell.util.date
-class Usecase:
- def __init__(self, file, work_plan=None, attributes=None):
- """Edits the attributes of an IfcWorkPlan
+def edit_work_plan(file, work_plan=None, attributes=None) -> None:
+ """Edits the attributes of an IfcWorkPlan
- For more information about the attributes and data types of an
- IfcWorkPlan, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcWorkPlan, consult the IFC documentation.
- :param work_plan: The IfcWorkPlan entity you want to edit
- :type work_plan: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param work_plan: The IfcWorkPlan entity you want to edit
+ :type work_plan: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
- # Let's give it a description
- ifcopenshell.api.run("sequence.edit_work_plan", model,
- work_plan=work_plan, attributes={"Description": "Construction of phase 1"})
- """
- self.file = file
- self.settings = {"work_plan": work_plan, "attributes": attributes or {}}
+ # Let's give it a description
+ ifcopenshell.api.run("sequence.edit_work_plan", model,
+ work_plan=work_plan, attributes={"Description": "Construction of phase 1"})
+ """
+ settings = {"work_plan": work_plan, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- if value:
- if "Date" in name or "Time" in name:
- value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
- elif name == "Duration" or name == "TotalFloat":
- value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
- setattr(self.settings["work_plan"], name, value)
+ for name, value in settings["attributes"].items():
+ if value:
+ if "Date" in name or "Time" in name:
+ value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
+ elif name == "Duration" or name == "TotalFloat":
+ value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
+ setattr(settings["work_plan"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py
index cd7ca163b2..49e6b053ac 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py
@@ -19,43 +19,40 @@
import ifcopenshell.util.date
-class Usecase:
- def __init__(self, file, work_schedule=None, attributes=None):
- """Edits the attributes of an IfcWorkSchedule
+def edit_work_schedule(file, work_schedule=None, attributes=None) -> None:
+ """Edits the attributes of an IfcWorkSchedule
- For more information about the attributes and data types of an
- IfcWorkSchedule, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcWorkSchedule, consult the IFC documentation.
- :param work_schedule: The IfcWorkSchedule entity you want to edit
- :type work_schedule: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param work_schedule: The IfcWorkSchedule entity you want to edit
+ :type work_schedule: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
- # Let's imagine this is one of our schedules in our work plan.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
- name="Construction Schedule A", work_plan=work_plan)
+ # Let's imagine this is one of our schedules in our work plan.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
+ name="Construction Schedule A", work_plan=work_plan)
- # Let's give it a description
- ifcopenshell.api.run("sequence.edit_work_schedule", model,
- work_schedule=work_schedule, attributes={"Description": "3 crane design option"})
- """
- self.file = file
- self.settings = {"work_schedule": work_schedule, "attributes": attributes or {}}
+ # Let's give it a description
+ ifcopenshell.api.run("sequence.edit_work_schedule", model,
+ work_schedule=work_schedule, attributes={"Description": "3 crane design option"})
+ """
+ settings = {"work_schedule": work_schedule, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- if value:
- if "Date" in name or "Time" in name:
- value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
- elif name == "Duration" or name == "TotalFloat":
- value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
- setattr(self.settings["work_schedule"], name, value)
+ for name, value in settings["attributes"].items():
+ if value:
+ if "Date" in name or "Time" in name:
+ value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
+ elif name == "Duration" or name == "TotalFloat":
+ value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
+ setattr(settings["work_schedule"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py
index d62c3a5357..ac0a05dad0 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py
@@ -20,54 +20,50 @@ import ifcopenshell.util.date
from typing import Any, Optional
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- work_time: ifcopenshell.entity_instance,
- attributes: Optional[dict[str, Any]] = None,
- ):
- """Edits the attributes of an IfcWorkTime
+def edit_work_time(
+ file: ifcopenshell.file,
+ work_time: ifcopenshell.entity_instance,
+ attributes: Optional[dict[str, Any]] = None,
+) -> None:
+ """Edits the attributes of an IfcWorkTime
- For more information about the attributes and data types of an
- IfcWorkTime, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcWorkTime, consult the IFC documentation.
- :param work_time: The IfcWorkTime entity you want to edit
- :type work_time: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param work_time: The IfcWorkTime entity you want to edit
+ :type work_time: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # If we don't specify any recurring time periods in our work time,
- # we need to specify a start and end date of the work time. It
- # starts at 0:00 on the start date and 24:00 at the end date.
- ifcopenshell.api.run("sequence.edit_work_time", model,
- work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"})
- """
- self.file = file
- self.settings = {"work_time": work_time, "attributes": attributes or {}}
+ # If we don't specify any recurring time periods in our work time,
+ # we need to specify a start and end date of the work time. It
+ # starts at 0:00 on the start date and 24:00 at the end date.
+ ifcopenshell.api.run("sequence.edit_work_time", model,
+ work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"})
+ """
+ settings = {"work_time": work_time, "attributes": attributes or {}}
- def execute(self) -> None:
- for name, value in self.settings["attributes"].items():
- if name in ("Start", "StartDate"):
- value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
- # 4 IfcWorktime Start
- self.settings["work_time"][4] = value
- elif name in ("Finish", "FinishDate"):
- value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
- # 5 IfcWorktime Finish
- self.settings["work_time"][5] = value
- else:
- setattr(self.settings["work_time"], name, value)
+ for name, value in settings["attributes"].items():
+ if name in ("Start", "StartDate"):
+ value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
+ # 4 IfcWorktime Start
+ settings["work_time"][4] = value
+ elif name in ("Finish", "FinishDate"):
+ value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
+ # 5 IfcWorktime Finish
+ settings["work_time"][5] = value
+ else:
+ setattr(settings["work_time"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py
index eb8af300d1..df402c31ed 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py
@@ -19,61 +19,58 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, relating_product=None, related_object=None):
- """Gets the related products being output by a task
+def get_related_products(file, relating_product=None, related_object=None) -> None:
+ """Gets the related products being output by a task
- This API function will be removed in the future and migrated to a
- utility module.
+ This API function will be removed in the future and migrated to a
+ utility module.
- :param relating_product: One of the products already output by the task.
- :type relating_product: ifcopenshell.entity_instance
- :param related_object: The IfcTask that you want to get all the related
- products for.
- :type related_object: ifcopenshell.entity_instance
- :return: A set of IfcProducts output by the IfcTask.
- :rtype: set[ifcopenshell.entity_instance]
+ :param relating_product: One of the products already output by the task.
+ :type relating_product: ifcopenshell.entity_instance
+ :param related_object: The IfcTask that you want to get all the related
+ products for.
+ :type related_object: ifcopenshell.entity_instance
+ :return: A set of IfcProducts output by the IfcTask.
+ :rtype: set[ifcopenshell.entity_instance]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's create a construction task. Note that the predefined type is
- # important to distinguish types of tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
+ # Let's create a construction task. Note that the predefined type is
+ # important to distinguish types of tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
- # Let's say we have a wall somewhere.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Let's say we have a wall somewhere.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's construct that wall!
- ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
+ # Let's construct that wall!
+ ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
- # This will give us a set with that wall in it.
- products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task)
- """
- self.file = file
- self.settings = {
- "relating_product": relating_product,
- "related_object": related_object,
- }
+ # This will give us a set with that wall in it.
+ products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task)
+ """
+ settings = {
+ "relating_product": relating_product,
+ "related_object": related_object,
+ }
- def execute(self):
- products = set()
- related_object = None
- if self.settings["related_object"]:
- related_object = self.settings["related_object"]
- elif self.settings["relating_product"]:
- for reference in self.settings["relating_product"].ReferencedBy:
- if reference.is_a("IfcRelAssignsToProduct"):
- related_object = reference.RelatedObjects[0]
- if related_object:
- assignments = self.settings["related_object"].HasAssignments
- for assignment in assignments:
- if assignment.is_a("IfcRelAssignsToProduct"):
- products.add(assignment.RelatingProduct.id())
- return products
+ products = set()
+ related_object = None
+ if settings["related_object"]:
+ related_object = settings["related_object"]
+ elif settings["relating_product"]:
+ for reference in settings["relating_product"].ReferencedBy:
+ if reference.is_a("IfcRelAssignsToProduct"):
+ related_object = reference.RelatedObjects[0]
+ if related_object:
+ assignments = settings["related_object"].HasAssignments
+ for assignment in assignments:
+ if assignment.is_a("IfcRelAssignsToProduct"):
+ products.add(assignment.RelatingProduct.id())
+ return products
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py
index da07337f7b..d54bf01579 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py
@@ -23,35 +23,38 @@ import ifcopenshell.util.date
import ifcopenshell.util.sequence
+def recalculate_schedule(file, work_schedule=None) -> None:
+ """Calculate the critical path and floats for a work schedule
+
+ This implements critical path analysis, using the forward pass and
+ backward pass method. When run, any tasks that have no float will be
+ marked as critical, and both the total and free floats will be
+ populated for all task times.
+
+ Cyclical relationships are detected and will result in a recursion
+ error.
+
+ :param work_schedule: The IfcWorkSchedule to perform the calculation on.
+ :type work_schedule: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # See the example for ifcopenshell.api.sequence.cascade_schedule for
+ # details of how to set up a basic set of tasks and calculate the
+ # critical path. Typically cascade_schedule is run prior to ensure
+ # that dates are correct.
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"work_schedule": work_schedule}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, work_schedule=None):
- """Calculate the critical path and floats for a work schedule
-
- This implements critical path analysis, using the forward pass and
- backward pass method. When run, any tasks that have no float will be
- marked as critical, and both the total and free floats will be
- populated for all task times.
-
- Cyclical relationships are detected and will result in a recursion
- error.
-
- :param work_schedule: The IfcWorkSchedule to perform the calculation on.
- :type work_schedule: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # See the example for ifcopenshell.api.sequence.cascade_schedule for
- # details of how to set up a basic set of tasks and calculate the
- # critical path. Typically cascade_schedule is run prior to ensure
- # that dates are correct.
- """
- self.file = file
- self.settings = {"work_schedule": work_schedule}
-
def execute(self):
# The method implemented is the same as shown here:
# https://www.youtube.com/watch?v=qTErIV6OqLg
@@ -84,9 +87,7 @@ class Usecase:
break # We have an infinite loop due to a cyclic graph
if is_cyclic:
- raise RecursionError(
- "Task graph is cyclic and so critical path method cannot be performed."
- )
+ raise RecursionError("Task graph is cyclic and so critical path method cannot be performed.")
return
self.pending_nodes = set(self.g.nodes)
@@ -112,9 +113,7 @@ class Usecase:
self.g = nx.DiGraph()
self.edges = []
self.g.add_node("start", duration=0, duration_type="ELAPSEDTIME", calendar=None)
- self.g.add_node(
- "finish", duration=0, duration_type="ELAPSEDTIME", calendar=None
- )
+ self.g.add_node("finish", duration=0, duration_type="ELAPSEDTIME", calendar=None)
for rel in self.settings["work_schedule"].Controls:
for related_object in rel.RelatedObjects:
if not related_object.is_a("IfcTask"):
@@ -129,9 +128,7 @@ class Usecase:
return
if task.TaskTime and task.TaskTime.ScheduleDuration:
- duration = ifcopenshell.util.date.ifc2datetime(
- task.TaskTime.ScheduleDuration
- ).days
+ duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration).days
duration_type = task.TaskTime.DurationType
else:
duration = 0
@@ -150,11 +147,11 @@ class Usecase:
rel.RelatingProcess.id(),
task.id(),
{
- "lag_time": 0
- if not rel.TimeLag
- else ifcopenshell.util.date.ifc2datetime(
- rel.TimeLag.LagValue.wrappedValue
- ).days,
+ "lag_time": (
+ 0
+ if not rel.TimeLag
+ else ifcopenshell.util.date.ifc2datetime(rel.TimeLag.LagValue.wrappedValue).days
+ ),
"type": self.sequence_type_map[rel.SequenceType],
},
)
@@ -162,16 +159,20 @@ class Usecase:
]
)
- predecessor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor")]
- successor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor")]
+ predecessor_types = [
+ rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor")
+ ]
+ successor_types = [
+ rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor")
+ ]
if not predecessor_types:
self.edges.append(("start", task.id(), {"lag_time": 0, "type": "FS"}))
if task.TaskTime and task.TaskTime.ScheduleStart:
- self.start_dates.append(
- ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart)
- )
- self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart) # we assume this task is constrained to start on this date
+ self.start_dates.append(ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart))
+ self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime(
+ task.TaskTime.ScheduleStart
+ ) # we assume this task is constrained to start on this date
if not successor_types:
self.edges.append((task.id(), "finish", {"lag_time": 0, "type": "FF"}))
@@ -188,25 +189,13 @@ class Usecase:
self.file,
task_time=task.TaskTime,
attributes={
- "FreeFloat": ifcopenshell.util.date.datetime2ifc(
- data["free_float"], "IfcDuration"
- ),
- "TotalFloat": ifcopenshell.util.date.datetime2ifc(
- data["total_float"], "IfcDuration"
- ),
+ "FreeFloat": ifcopenshell.util.date.datetime2ifc(data["free_float"], "IfcDuration"),
+ "TotalFloat": ifcopenshell.util.date.datetime2ifc(data["total_float"], "IfcDuration"),
"IsCritical": data["total_float"].days == 0,
- "EarlyStart": ifcopenshell.util.date.datetime2ifc(
- data["early_start"], "IfcDateTime"
- ),
- "EarlyFinish": ifcopenshell.util.date.datetime2ifc(
- data["early_finish"], "IfcDateTime"
- ),
- "LateStart": ifcopenshell.util.date.datetime2ifc(
- data["late_start"], "IfcDateTime"
- ),
- "LateFinish": ifcopenshell.util.date.datetime2ifc(
- data["late_finish"], "IfcDateTime"
- ),
+ "EarlyStart": ifcopenshell.util.date.datetime2ifc(data["early_start"], "IfcDateTime"),
+ "EarlyFinish": ifcopenshell.util.date.datetime2ifc(data["early_finish"], "IfcDateTime"),
+ "LateStart": ifcopenshell.util.date.datetime2ifc(data["late_start"], "IfcDateTime"),
+ "LateFinish": ifcopenshell.util.date.datetime2ifc(data["late_finish"], "IfcDateTime"),
},
)
@@ -246,11 +235,7 @@ class Usecase:
if edge["lag_time"]:
days += edge["lag_time"]
if days:
- starts.append(
- datetime.datetime.combine(
- self.offset_date(finish, days, data), datetime.time(9)
- )
- )
+ starts.append(datetime.datetime.combine(self.offset_date(finish, days, data), datetime.time(9)))
starts.append(
datetime.datetime.combine(
self.offset_date(finish, days, predecessor_data),
@@ -265,9 +250,7 @@ class Usecase:
return
if edge["lag_time"]:
starts.append(self.offset_date(start, edge["lag_time"], data))
- starts.append(
- self.offset_date(start, edge["lag_time"], predecessor_data)
- )
+ starts.append(self.offset_date(start, edge["lag_time"], predecessor_data))
else:
starts.append(start)
elif edge["type"] == "FF":
@@ -275,12 +258,8 @@ class Usecase:
if finish is None:
return
if edge["lag_time"]:
- finishes.append(
- self.offset_date(finish, edge["lag_time"], data)
- )
- finishes.append(
- self.offset_date(finish, edge["lag_time"], predecessor_data)
- )
+ finishes.append(self.offset_date(finish, edge["lag_time"], data))
+ finishes.append(self.offset_date(finish, edge["lag_time"], predecessor_data))
else:
finishes.append(finish)
elif edge["type"] == "SF":
@@ -292,9 +271,7 @@ class Usecase:
days += edge["lag_time"]
if days or edge["lag_time"]:
finishes.append(
- datetime.datetime.combine(
- self.offset_date(start, days, data), datetime.time(17)
- )
+ datetime.datetime.combine(self.offset_date(start, days, data), datetime.time(17))
)
finishes.append(
datetime.datetime.combine(
@@ -317,9 +294,7 @@ class Usecase:
if potential_finish > data["early_finish"]:
data["early_finish"] = potential_finish
else:
- data[
- "early_start"
- ] = ifcopenshell.util.sequence.get_start_or_finish_date(
+ data["early_start"] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["early_finish"],
datetime.timedelta(days=data["duration"]),
data["duration_type"],
@@ -375,9 +350,7 @@ class Usecase:
days += edge["lag_time"]
if days or edge["lag_time"]:
finishes.append(
- datetime.datetime.combine(
- self.offset_date(start, -days, data), datetime.time(17)
- )
+ datetime.datetime.combine(self.offset_date(start, -days, data), datetime.time(17))
)
finishes.append(
datetime.datetime.combine(
@@ -402,9 +375,7 @@ class Usecase:
return
if edge["lag_time"]:
starts.append(self.offset_date(start, -edge["lag_time"], data))
- starts.append(
- self.offset_date(start, -edge["lag_time"], successor_data)
- )
+ starts.append(self.offset_date(start, -edge["lag_time"], successor_data))
else:
starts.append(start)
free_floats.append(
@@ -421,12 +392,8 @@ class Usecase:
if finish is None:
return
if edge["lag_time"]:
- finishes.append(
- self.offset_date(finish, -edge["lag_time"], data)
- )
- finishes.append(
- self.offset_date(finish, -edge["lag_time"], successor_data)
- )
+ finishes.append(self.offset_date(finish, -edge["lag_time"], data))
+ finishes.append(self.offset_date(finish, -edge["lag_time"], successor_data))
else:
finishes.append(finish)
free_floats.append(
@@ -447,9 +414,7 @@ class Usecase:
days += edge["lag_time"]
if days:
starts.append(
- datetime.datetime.combine(
- self.offset_date(finish, -days, data), datetime.time(9)
- )
+ datetime.datetime.combine(self.offset_date(finish, -days, data), datetime.time(9))
)
starts.append(
datetime.datetime.combine(
@@ -471,13 +436,8 @@ class Usecase:
if starts and finishes:
data["late_start"] = min(starts)
data["late_finish"] = min(finishes)
- if (
- self.offset_date(data["late_start"], data["duration"], data)
- < data["late_finish"]
- ):
- data[
- "late_finish"
- ] = ifcopenshell.util.sequence.get_start_or_finish_date(
+ if self.offset_date(data["late_start"], data["duration"], data) < data["late_finish"]:
+ data["late_finish"] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["late_start"],
datetime.timedelta(days=data["duration"]),
data["duration_type"],
@@ -485,9 +445,7 @@ class Usecase:
date_type="FINISH",
)
else:
- data[
- "late_start"
- ] = ifcopenshell.util.sequence.get_start_or_finish_date(
+ data["late_start"] = ifcopenshell.util.sequence.get_start_or_finish_date(
data["late_finish"],
datetime.timedelta(days=data["duration"]),
data["duration_type"],
@@ -528,9 +486,7 @@ class Usecase:
data["total_float"] = data["late_finish"] - data["early_finish"]
# If the float is within the span of a single day, it may show as a 8 hours
if data["total_float"].seconds == 60 * 60 * 8:
- data["total_float"] = datetime.timedelta(
- days=data["total_float"].days + 1
- )
+ data["total_float"] = datetime.timedelta(days=data["total_float"].days + 1)
data["free_float"] = min(free_floats) if free_floats else None
# If the float is within the span of a single day, it may show as a 8 hours
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py
index 6b7fac4f75..d49da8324e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py
@@ -21,124 +21,121 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, task=None):
- """Removes a task
+def remove_task(file, task=None) -> None:
+ """Removes a task
- All subtasks are also removed recursively. Any relationships such as
- sequences or controls are also removed.
+ All subtasks are also removed recursively. Any relationships such as
+ sequences or controls are also removed.
- :param task: The IfcTask to remove.
- :type task: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param task: The IfcTask to remove.
+ :type task: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Add a root task to represent the design milestones, and major
- # project phases.
- ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Milestones", identification="A")
- design = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Design", identification="B")
- ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Add a root task to represent the design milestones, and major
+ # project phases.
+ ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Milestones", identification="A")
+ design = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Design", identification="B")
+ ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Ah, let's delete the design section, who needs it anyway we'll
- # just fix it on site.
- ifcopenshell.api.run("sequence.remove_task", model, task=design)
- """
- self.file = file
- self.settings = {"task": task}
+ # Ah, let's delete the design section, who needs it anyway we'll
+ # just fix it on site.
+ ifcopenshell.api.run("sequence.remove_task", model, task=design)
+ """
+ settings = {"task": task}
- def execute(self):
- # TODO: do a deep purge
- ifcopenshell.api.run(
- "project.unassign_declaration",
- self.file,
- definitions=[self.settings["task"]],
- relating_context=self.file.by_type("IfcContext")[0],
- )
- if self.settings["task"].TaskTime:
- self.file.remove(self.settings["task"].TaskTime)
- for inverse in self.file.get_inverse(self.settings["task"]):
- if inverse.is_a("IfcRelSequence"):
+ # TODO: do a deep purge
+ ifcopenshell.api.run(
+ "project.unassign_declaration",
+ file,
+ definitions=[settings["task"]],
+ relating_context=file.by_type("IfcContext")[0],
+ )
+ if settings["task"].TaskTime:
+ file.remove(settings["task"].TaskTime)
+ for inverse in file.get_inverse(settings["task"]):
+ if inverse.is_a("IfcRelSequence"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelNests"):
+ if inverse.RelatingObject == settings["task"]:
+ for related_object in inverse.RelatedObjects:
+ ifcopenshell.api.run("sequence.remove_task", file, task=related_object)
+ elif not inverse.RelatedObjects:
history = inverse.OwnerHistory
- self.file.remove(inverse)
+ file.remove(inverse)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelNests"):
- if inverse.RelatingObject == self.settings["task"]:
- for related_object in inverse.RelatedObjects:
- ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object)
- elif not inverse.RelatedObjects:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif self.settings["task"] in inverse.RelatedObjects:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["task"])
- if not related_objects:
- self.file.remove(inverse)
- else:
- inverse.RelatedObjects = related_objects
- elif inverse.is_a("IfcRelAssignsToControl"):
- if inverse.RelatingControl == self.settings["task"] or len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif settings["task"] in inverse.RelatedObjects:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["task"])
+ if not related_objects:
+ file.remove(inverse)
else:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["task"])
inverse.RelatedObjects = related_objects
- elif inverse.is_a("IfcRelDefinesByProperties"):
- ifcopenshell.api.run(
- "pset.remove_pset",
- self.file,
- product=self.settings["task"],
- pset=inverse.RelatingPropertyDefinition,
- )
- elif inverse.is_a("IfcRelAssignsToProcess"):
- if inverse.RelatingProcess == self.settings["task"] or len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif inverse.is_a("IfcRelAssignsToProduct"):
- if inverse.RelatingProduct == self.settings["task"] or len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- else:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["task"])
- inverse.RelatedObjects = related_objects
- elif inverse.is_a("IfcRelAssignsToObject"):
- if inverse.RelatingObject == self.settings["task"] or len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- else:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["task"])
- inverse.RelatedObjects = related_objects
- elif inverse.is_a("IfcRelAssignsToProcess"):
+ elif inverse.is_a("IfcRelAssignsToControl"):
+ if inverse.RelatingControl == settings["task"] or len(inverse.RelatedObjects) == 1:
history = inverse.OwnerHistory
- self.file.remove(inverse)
+ file.remove(inverse)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ ifcopenshell.util.element.remove_deep2(file, history)
+ else:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["task"])
+ inverse.RelatedObjects = related_objects
+ elif inverse.is_a("IfcRelDefinesByProperties"):
+ ifcopenshell.api.run(
+ "pset.remove_pset",
+ file,
+ product=settings["task"],
+ pset=inverse.RelatingPropertyDefinition,
+ )
+ elif inverse.is_a("IfcRelAssignsToProcess"):
+ if inverse.RelatingProcess == settings["task"] or len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif inverse.is_a("IfcRelAssignsToProduct"):
+ if inverse.RelatingProduct == settings["task"] or len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ else:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["task"])
+ inverse.RelatedObjects = related_objects
+ elif inverse.is_a("IfcRelAssignsToObject"):
+ if inverse.RelatingObject == settings["task"] or len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ else:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["task"])
+ inverse.RelatedObjects = related_objects
+ elif inverse.is_a("IfcRelAssignsToProcess"):
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
- history = self.settings["task"].OwnerHistory
- self.file.remove(self.settings["task"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ history = settings["task"].OwnerHistory
+ file.remove(settings["task"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py
index 32effdf4ac..672606c421 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py
@@ -19,45 +19,42 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, time_period=None):
- """Removes a time period
+def remove_time_period(file, time_period=None) -> None:
+ """Removes a time period
- :param time_period: The IfcTimePeriod to remove.
- :type time_period: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param time_period: The IfcTimePeriod to remove.
+ :type time_period: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
- ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
- recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
+ # State that we work from weekdays 1 to 5 (i.e. Monday to Friday)
+ ifcopenshell.api.run("sequence.edit_recurrence_pattern", model,
+ recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]})
- # The morning work session, lunch, then the afternoon work session.
- morning = ifcopenshell.api.run("sequence.add_time_period", model,
- recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
- afternoon = ifcopenshell.api.run("sequence.add_time_period", model,
- recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
+ # The morning work session, lunch, then the afternoon work session.
+ morning = ifcopenshell.api.run("sequence.add_time_period", model,
+ recurrence_pattern=pattern, start_time="09:00", end_time="12:00")
+ afternoon = ifcopenshell.api.run("sequence.add_time_period", model,
+ recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
- # Let's take the afternoon off!
- ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon)
- """
- self.file = file
- self.settings = {"time_period": time_period}
+ # Let's take the afternoon off!
+ ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon)
+ """
+ settings = {"time_period": time_period}
- def execute(self):
- self.file.remove(self.settings["time_period"])
+ file.remove(settings["time_period"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py
index e233bef26d..22a362c42d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py
@@ -20,49 +20,46 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, work_calendar=None):
- """Removes a work calendar
+def remove_work_calendar(file, work_calendar=None) -> None:
+ """Removes a work calendar
- All relationships are also removed, such as if a task is set to use that
- calendar.
+ All relationships are also removed, such as if a task is set to use that
+ calendar.
- :param work_calendar: The IfcWorkCalendar to remove
- :type work_calendar: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param work_calendar: The IfcWorkCalendar to remove
+ :type work_calendar: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week")
- # And remove it immediately
- ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar)
- """
- self.file = file
- self.settings = {"work_calendar": work_calendar}
+ # And remove it immediately
+ ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar)
+ """
+ settings = {"work_calendar": work_calendar}
- def execute(self):
- # TODO: do a deep purge
- ifcopenshell.api.run(
- "project.unassign_declaration",
- self.file,
- definitions=[self.settings["work_calendar"]],
- relating_context=self.file.by_type("IfcContext")[0],
- )
- if self.settings["work_calendar"].Controls:
- for rel in self.settings["work_calendar"].Controls:
- for related_object in rel.RelatedObjects:
- ifcopenshell.api.run(
- "control.unassign_control",
- self.file,
- relating_control=self.settings["work_calendar"],
- related_object=related_object,
- )
- history = self.settings["work_calendar"].OwnerHistory
- self.file.remove(self.settings["work_calendar"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ # TODO: do a deep purge
+ ifcopenshell.api.run(
+ "project.unassign_declaration",
+ file,
+ definitions=[settings["work_calendar"]],
+ relating_context=file.by_type("IfcContext")[0],
+ )
+ if settings["work_calendar"].Controls:
+ for rel in settings["work_calendar"].Controls:
+ for related_object in rel.RelatedObjects:
+ ifcopenshell.api.run(
+ "control.unassign_control",
+ file,
+ relating_control=settings["work_calendar"],
+ related_object=related_object,
+ )
+ history = settings["work_calendar"].OwnerHistory
+ file.remove(settings["work_calendar"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py
index bbd631829d..28675fe6a5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py
@@ -20,40 +20,37 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, work_plan=None):
- """Removes a work plan
+def remove_work_plan(file, work_plan=None) -> None:
+ """Removes a work plan
- Note that schedules that are grouped under the work plan are not
- removed.
+ Note that schedules that are grouped under the work plan are not
+ removed.
- :param work_plan: The IfcWorkPlan to remove.
- :type work_plan: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param work_plan: The IfcWorkPlan to remove.
+ :type work_plan: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
- # And remove it immediately
- ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan)
- """
- self.file = file
- self.settings = {"work_plan": work_plan}
+ # And remove it immediately
+ ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan)
+ """
+ settings = {"work_plan": work_plan}
- def execute(self):
- # TODO: do a deep purge
- ifcopenshell.api.run(
- "project.unassign_declaration",
- self.file,
- definitions=[self.settings["work_plan"]],
- relating_context=self.file.by_type("IfcContext")[0],
- )
- history = self.settings["work_plan"].OwnerHistory
- self.file.remove(self.settings["work_plan"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ # TODO: do a deep purge
+ ifcopenshell.api.run(
+ "project.unassign_declaration",
+ file,
+ definitions=[settings["work_plan"]],
+ relating_context=file.by_type("IfcContext")[0],
+ )
+ history = settings["work_plan"].OwnerHistory
+ file.remove(settings["work_plan"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py
index 66b69c8804..ec06a9146b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py
@@ -21,69 +21,66 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, work_schedule=None):
- """Removes a work schedule
+def remove_work_schedule(file, work_schedule=None) -> None:
+ """Removes a work schedule
- All tasks in the work schedule are also removed recursively.
+ All tasks in the work schedule are also removed recursively.
- :param work_schedule: The IfcWorkSchedule to remove.
- :type work_schedule: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param work_schedule: The IfcWorkSchedule to remove.
+ :type work_schedule: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # This will hold all our construction schedules
- work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
+ # This will hold all our construction schedules
+ work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction")
- # Let's imagine this is one of our schedules in our work plan.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
- name="Construction Schedule A", work_plan=work_plan)
+ # Let's imagine this is one of our schedules in our work plan.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model,
+ name="Construction Schedule A", work_plan=work_plan)
- # And remove it immediately
- ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule)
- """
- self.file = file
- self.settings = {"work_schedule": work_schedule}
+ # And remove it immediately
+ ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule)
+ """
+ settings = {"work_schedule": work_schedule}
- def execute(self):
- # TODO: do a deep purge
- ifcopenshell.api.run(
- "project.unassign_declaration",
- self.file,
- definitions=[self.settings["work_schedule"]],
- relating_context=self.file.by_type("IfcContext")[0],
- )
- if self.settings["work_schedule"].Declares:
- for rel in self.settings["work_schedule"].Declares:
- for work_schedule in rel.RelatedObjects:
- ifcopenshell.api.run(
- "sequence.remove_work_schedule",
- self.file,
- work_schedule=work_schedule,
- )
- for inverse in self.file.get_inverse(self.settings["work_schedule"]):
- if inverse.is_a("IfcRelDefinesByObject"):
- if inverse.RelatingObject == self.settings["work_schedule"] or len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- else:
- related_objects = list(inverse.RelatedObjects)
- related_objects.remove(self.settings["work_schedule"])
- inverse.RelatedObjects = related_objects
- elif inverse.is_a("IfcRelAssignsToControl"):
- [
- ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object)
- for related_object in inverse.RelatedObjects
- if related_object.is_a("IfcTask")
- ]
+ # TODO: do a deep purge
+ ifcopenshell.api.run(
+ "project.unassign_declaration",
+ file,
+ definitions=[settings["work_schedule"]],
+ relating_context=file.by_type("IfcContext")[0],
+ )
+ if settings["work_schedule"].Declares:
+ for rel in settings["work_schedule"].Declares:
+ for work_schedule in rel.RelatedObjects:
+ ifcopenshell.api.run(
+ "sequence.remove_work_schedule",
+ file,
+ work_schedule=work_schedule,
+ )
+ for inverse in file.get_inverse(settings["work_schedule"]):
+ if inverse.is_a("IfcRelDefinesByObject"):
+ if inverse.RelatingObject == settings["work_schedule"] or len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ else:
+ related_objects = list(inverse.RelatedObjects)
+ related_objects.remove(settings["work_schedule"])
+ inverse.RelatedObjects = related_objects
+ elif inverse.is_a("IfcRelAssignsToControl"):
+ [
+ ifcopenshell.api.run("sequence.remove_task", file, task=related_object)
+ for related_object in inverse.RelatedObjects
+ if related_object.is_a("IfcTask")
+ ]
- history = self.settings["work_schedule"].OwnerHistory
- self.file.remove(self.settings["work_schedule"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ history = settings["work_schedule"].OwnerHistory
+ file.remove(settings["work_schedule"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py
index 3898e3655a..ab4587ce6a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, work_time=None):
- """Removes a work time
+def remove_work_time(file, work_time=None) -> None:
+ """Removes a work time
- :param work_time: The IfcWorkTime to remove.
- :type work_time: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param work_time: The IfcWorkTime to remove.
+ :type work_time: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # And remove it immediately
- ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time)
- """
- self.file = file
- self.settings = {"work_time": work_time}
+ # And remove it immediately
+ ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time)
+ """
+ settings = {"work_time": work_time}
- def execute(self):
- self.file.remove(self.settings["work_time"])
+ file.remove(settings["work_time"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py
index cca278da44..cac8f95372 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py
@@ -19,57 +19,54 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, rel_sequence=None):
- """Removes any lag time in a sequence
+def unassign_lag_time(file, rel_sequence=None) -> None:
+ """Removes any lag time in a sequence
- The schedule is cascaded afterwards.
+ The schedule is cascaded afterwards.
- :param rel_sequence: The sequence to remove the lag time from.
- :type rel_sequence: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param rel_sequence: The sequence to remove the lag time from.
+ :type rel_sequence: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's imagine a root construction task
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Let's imagine a root construction task
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's imagine we're building 2 zones, one after another.
- zone1 = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Zone 1", identification="C.1")
- zone2 = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Zone 2", identification="C.2")
+ # Let's imagine we're building 2 zones, one after another.
+ zone1 = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Zone 1", identification="C.1")
+ zone2 = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Zone 2", identification="C.2")
- # Zone 1 finishes, then zone 2 starts.
- sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
- relating_process=zone1, related_process=zone2)
+ # Zone 1 finishes, then zone 2 starts.
+ sequence = ifcopenshell.api.run("sequence.assign_sequence", model,
+ relating_process=zone1, related_process=zone2)
- # What if you had to wait 1 week before you could start zone 2?
- ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W")
+ # What if you had to wait 1 week before you could start zone 2?
+ ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W")
- # What if you didn't?
- ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence)
- """
- self.file = file
- self.settings = {
- "rel_sequence": rel_sequence,
- }
+ # What if you didn't?
+ ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence)
+ """
+ settings = {
+ "rel_sequence": rel_sequence,
+ }
- def execute(self):
- if len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1:
- self.file.remove(self.settings["rel_sequence"].TimeLag)
- else:
- self.settings["rel_sequence"].TimeLag = None
- ifcopenshell.api.run(
- "sequence.cascade_schedule",
- self.file,
- task=self.settings["rel_sequence"].RelatedProcess,
- )
+ if len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1:
+ file.remove(settings["rel_sequence"].TimeLag)
+ else:
+ settings["rel_sequence"].TimeLag = None
+ ifcopenshell.api.run(
+ "sequence.cascade_schedule",
+ file,
+ task=settings["rel_sequence"].RelatedProcess,
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py
index dfc12068d3..f7e141afc1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py
@@ -21,59 +21,56 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_process=None, related_object=None):
- """Unassigns a process and object relationship
+def unassign_process(file, relating_process=None, related_object=None) -> None:
+ """Unassigns a process and object relationship
- See ifcopenshell.api.sequence.assign_process for details.
+ See ifcopenshell.api.sequence.assign_process for details.
- :param relating_process: The IfcTask in the relationship.
- :type relating_process: ifcopenshell.entity_instance
- :param related_object: The related object.
- :type related_object: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param relating_process: The IfcTask in the relationship.
+ :type relating_process: ifcopenshell.entity_instance
+ :param related_object: The related object.
+ :type related_object: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's create a construction task. Note that the predefined type is
- # important to distinguish types of tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
+ # Let's create a construction task. Note that the predefined type is
+ # important to distinguish types of tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION")
- # Let's say we have a wall somewhere.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Let's say we have a wall somewhere.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's demolish that wall!
- ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
+ # Let's demolish that wall!
+ ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall)
- # Change our mind.
- ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall)
- """
- self.file = file
- self.settings = {
- "relating_process": relating_process,
- "related_object": related_object,
- }
+ # Change our mind.
+ ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall)
+ """
+ settings = {
+ "relating_process": relating_process,
+ "related_object": related_object,
+ }
- def execute(self):
- for rel in self.settings["related_object"].HasAssignments or []:
- if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != self.settings["relating_process"]:
- continue
- if len(rel.RelatedObjects) == 1:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_objects = list(rel.RelatedObjects)
- related_objects.remove(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
- return rel
+ for rel in settings["related_object"].HasAssignments or []:
+ if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != settings["relating_process"]:
+ continue
+ if len(rel.RelatedObjects) == 1:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_objects = list(rel.RelatedObjects)
+ related_objects.remove(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py
index 31d9edb0e1..23f9281c95 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py
@@ -21,59 +21,56 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_product=None, related_object=None):
- """Unassigns a product and object relationship
+def unassign_product(file, relating_product=None, related_object=None) -> None:
+ """Unassigns a product and object relationship
- See ifcopenshell.api.sequence.assign_product for details.
+ See ifcopenshell.api.sequence.assign_product for details.
- :param relating_product: The IfcProduct in the relationship.
- :type relating_product: ifcopenshell.entity_instance
- :param related_object: The IfcTask in the relationship.
- :type related_object: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param relating_product: The IfcProduct in the relationship.
+ :type relating_product: ifcopenshell.entity_instance
+ :param related_object: The IfcTask in the relationship.
+ :type related_object: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's create a construction task. Note that the predefined type is
- # important to distinguish types of tasks.
- task = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
+ # Let's create a construction task. Note that the predefined type is
+ # important to distinguish types of tasks.
+ task = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION")
- # Let's say we have a wall somewhere.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Let's say we have a wall somewhere.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's construct that wall!
- ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
+ # Let's construct that wall!
+ ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task)
- # Change our mind.
- ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task)
- """
- self.file = file
- self.settings = {
- "relating_product": relating_product,
- "related_object": related_object,
- }
+ # Change our mind.
+ ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task)
+ """
+ settings = {
+ "relating_product": relating_product,
+ "related_object": related_object,
+ }
- def execute(self):
- for rel in self.settings["related_object"].HasAssignments or []:
- if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]:
- continue
- if len(rel.RelatedObjects) == 1:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_objects = list(rel.RelatedObjects)
- related_objects.remove(self.settings["related_object"])
- rel.RelatedObjects = related_objects
- ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
- return rel
+ for rel in settings["related_object"].HasAssignments or []:
+ if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
+ continue
+ if len(rel.RelatedObjects) == 1:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_objects = list(rel.RelatedObjects)
+ related_objects.remove(settings["related_object"])
+ rel.RelatedObjects = related_objects
+ ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py
index fc74c69a95..46c99207f5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py
@@ -17,40 +17,37 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, recurrence_pattern=None):
- """Unassigns a recurrence pattern
+def unassign_recurrence_pattern(file, recurrence_pattern=None) -> None:
+ """Unassigns a recurrence pattern
- Note that a recurring task time must have a recurrence pattern, so if
- you remove it, be sure to clean up after yourself.
+ Note that a recurring task time must have a recurrence pattern, so if
+ you remove it, be sure to clean up after your
- :param recurrence_pattern: The IfcRecurrencePattern to remove.
- :type recurrence_pattern: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param recurrence_pattern: The IfcRecurrencePattern to remove.
+ :type recurrence_pattern: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's create a new calendar.
- calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
+ # Let's create a new calendar.
+ calendar = ifcopenshell.api.run("sequence.add_work_calendar", model)
- # Let's start defining the times that we work during the week.
- work_time = ifcopenshell.api.run("sequence.add_work_time", model,
- work_calendar=calendar, time_type="WorkingTimes")
+ # Let's start defining the times that we work during the week.
+ work_time = ifcopenshell.api.run("sequence.add_work_time", model,
+ work_calendar=calendar, time_type="WorkingTimes")
- # We create a weekly recurrence
- pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
- parent=work_time, recurrence_type="WEEKLY")
+ # We create a weekly recurrence
+ pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model,
+ parent=work_time, recurrence_type="WEEKLY")
- # Change our mind, let's just maintain it whenever we feel like it.
- ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern)
- """
- self.file = file
- self.settings = {"recurrence_pattern": recurrence_pattern}
+ # Change our mind, let's just maintain it whenever we feel like it.
+ ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern)
+ """
+ settings = {"recurrence_pattern": recurrence_pattern}
- def execute(self):
- for time_period in self.settings["recurrence_pattern"].TimePeriods or []:
- self.file.remove(time_period)
- self.file.remove(self.settings["recurrence_pattern"])
+ for time_period in settings["recurrence_pattern"].TimePeriods or []:
+ file.remove(time_period)
+ file.remove(settings["recurrence_pattern"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py
index c7286909bd..f10b11f893 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py
@@ -21,53 +21,50 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_process=None, related_process=None):
- """Removes a sequence relationship between tasks
+def unassign_sequence(file, relating_process=None, related_process=None) -> None:
+ """Removes a sequence relationship between tasks
- :param relating_process: The previous / predecessor task.
- :type relating_process: ifcopenshell.entity_instance
- :param related_process: The next / successor task.
- :type related_process: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param relating_process: The previous / predecessor task.
+ :type relating_process: ifcopenshell.entity_instance
+ :param related_process: The next / successor task.
+ :type related_process: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Let's imagine we are creating a construction schedule. All tasks
- # need to be part of a work schedule.
- schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
+ # Let's imagine we are creating a construction schedule. All tasks
+ # need to be part of a work schedule.
+ schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A")
- # Let's imagine a root construction task
- construction = ifcopenshell.api.run("sequence.add_task", model,
- work_schedule=schedule, name="Construction", identification="C")
+ # Let's imagine a root construction task
+ construction = ifcopenshell.api.run("sequence.add_task", model,
+ work_schedule=schedule, name="Construction", identification="C")
- # Let's imagine we're building 2 zones, one after another.
- zone1 = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Zone 1", identification="C.1")
- zone2 = ifcopenshell.api.run("sequence.add_task", model,
- parent_task=construction, name="Zone 2", identification="C.2")
+ # Let's imagine we're building 2 zones, one after another.
+ zone1 = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Zone 1", identification="C.1")
+ zone2 = ifcopenshell.api.run("sequence.add_task", model,
+ parent_task=construction, name="Zone 2", identification="C.2")
- # Zone 1 finishes, then zone 2 starts.
- ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2)
+ # Zone 1 finishes, then zone 2 starts.
+ ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2)
- # Let's make them unrelated
- ifcopenshell.api.run("sequence.unassign_sequence", model,
- relating_process=zone1, related_process=zone2)
- """
- self.file = file
- self.settings = {
- "relating_process": relating_process,
- "related_process": related_process,
- }
+ # Let's make them unrelated
+ ifcopenshell.api.run("sequence.unassign_sequence", model,
+ relating_process=zone1, related_process=zone2)
+ """
+ settings = {
+ "relating_process": relating_process,
+ "related_process": related_process,
+ }
- def execute(self):
- for rel in self.settings["related_process"].IsSuccessorFrom or []:
- if rel.RelatingProcess == self.settings["relating_process"]:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- ifcopenshell.api.run("sequence.cascade_schedule", self.file, task=self.settings["related_process"])
+ for rel in settings["related_process"].IsSuccessorFrom or []:
+ if rel.RelatingProcess == settings["relating_process"]:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["related_process"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py
index e0caddbe3c..22891f5c83 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py
@@ -15,3 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .assign_container import assign_container
+from .dereference_structure import dereference_structure
+from .reference_structure import reference_structure
+from .unassign_container import unassign_container
diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py
index 9edf7ccba8..298fe3dfdb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py
@@ -23,163 +23,159 @@ import ifcopenshell.util.placement
from typing import Union
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- relating_structure: ifcopenshell.entity_instance,
- ):
- """Assigns products to be contained hierarchically in a space
+def assign_container(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ relating_structure: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Assigns products 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.
+ 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.
+ 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".
+ 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.
+ 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.
+ 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 location in the "spatial
- decomposition" tree, assigning an aggregate relationship will remove any
- previous aggregation, containment, or nesting relationships it may have.
+ As a product may only have a single location in the "spatial
+ decomposition" tree, assigning an aggregate relationship will remove any
+ previous aggregation, containment, or nesting relationships it may have.
- :param products: A list of physical IfcElements existing in the space.
- :type products: list[ifcopenshell.entity_instance]
- :param relating_structure: The IfcSpatialStructureElement element, such
- as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
- exists in.
- :return: The IfcRelContainedInSpatialStructure relationship instance
- or `None` if `products` was empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param products: A list of physical IfcElements existing in the space.
+ :type products: list[ifcopenshell.entity_instance]
+ :param relating_structure: The IfcSpatialStructureElement element, such
+ as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
+ exists in.
+ :return: The IfcRelContainedInSpatialStructure relationship instance
+ or `None` if `products` was empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- 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")
+ 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, products=[site], relating_object=project)
+ # The project contains a site (note that project aggregation is a special case in IFC)
+ 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, 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)
+ # The site has a building, the building has a storey, and the storey has a space
+ 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 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")
+ # 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, products=[wall], relating_structure=storey)
- ifcopenshell.api.run("spatial.assign_container", model, products=[furniture], relating_structure=space)
- """
- self.file = file
- self.settings = {
- "products": products,
- "relating_structure": relating_structure,
- }
+ # The wall is in the storey, and the furniture is in the space
+ ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey)
+ ifcopenshell.api.run("spatial.assign_container", model, products=[furniture], relating_structure=space)
+ """
+ settings = {
+ "products": products,
+ "relating_structure": relating_structure,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- if not self.settings["products"]:
- return
+ if not settings["products"]:
+ return
- products = set(self.settings["products"])
- relating_structure = self.settings["relating_structure"]
- structure_rel = next(iter(relating_structure.ContainsElements), None)
+ products = set(settings["products"])
+ relating_structure = settings["relating_structure"]
+ structure_rel = next(iter(relating_structure.ContainsElements), None)
- previous_containers_rels: set[ifcopenshell.entity_instance] = set()
- products_without_containers: list[ifcopenshell.entity_instance] = []
- products_with_containers: list[ifcopenshell.entity_instance] = []
+ previous_containers_rels: set[ifcopenshell.entity_instance] = set()
+ products_without_containers: list[ifcopenshell.entity_instance] = []
+ products_with_containers: list[ifcopenshell.entity_instance] = []
- # check if there is anything to change
- for product in products:
- product_rel = next(iter(product.ContainedInStructure), None)
+ # check if there is anything to change
+ for product in products:
+ product_rel = next(iter(product.ContainedInStructure), None)
- if product_rel is None:
- products_without_containers.append(product)
- continue
+ if product_rel is None:
+ 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)
+ # 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 with already assigned containers will be skipped
- products_to_change = products_without_containers + products_with_containers
- # nothing to change
- if not products_to_change:
- return structure_rel
+ products_to_change = products_without_containers + products_with_containers
+ # nothing to change
+ if not products_to_change:
+ return structure_rel
- # can be either only aggregated or only contained at the same time
- ifcopenshell.api.run("aggregate.unassign_object", self.file, products=products_without_containers)
+ # can be either only aggregated or only contained at the same time
+ ifcopenshell.api.run("aggregate.unassign_object", file, products=products_without_containers)
- # unassign elements from previous containers
- for rel in previous_containers_rels:
- related_elements = set(rel.RelatedElements) - products
- if related_elements:
- rel.RelatedElements = list(related_elements)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
-
- # assign elements to a new container
- if structure_rel:
- structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": structure_rel})
+ # unassign elements from previous containers
+ for rel in previous_containers_rels:
+ related_elements = set(rel.RelatedElements) - products
+ if related_elements:
+ rel.RelatedElements = list(related_elements)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else:
- structure_rel = self.file.create_entity(
- "IfcRelContainedInSpatialStructure",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedElements": list(products),
- "RelatingStructure": self.settings["relating_structure"],
- }
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+
+ # assign elements to a new container
+ if structure_rel:
+ structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": structure_rel})
+ else:
+ structure_rel = file.create_entity(
+ "IfcRelContainedInSpatialStructure",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedElements": list(products),
+ "RelatingStructure": settings["relating_structure"],
+ }
+ )
+
+ # localize placement relative to a new container 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",
+ file,
+ product=product,
+ matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement),
+ is_si=False,
)
- # localize placement relative to a new container 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 structure_rel
+ return structure_rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py
index 6902018b46..50eb72305b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py
@@ -21,70 +21,66 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- relating_structure: ifcopenshell.entity_instance,
- ):
- """Dereferences a list of products and space
+def dereference_structure(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ relating_structure: ifcopenshell.entity_instance,
+) -> None:
+ """Dereferences a list of products and space
- :param products: The list of physical IfcElements that exists in the space.
- :type products: list[ifcopenshell.entity_instance]
- :param relating_structure: The IfcSpatialStructureElement element, such
- as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
- exists in.
- :return: None
- :rtype: None
+ :param products: The list of physical IfcElements that exists in the space.
+ :type products: list[ifcopenshell.entity_instance]
+ :param relating_structure: The IfcSpatialStructureElement element, such
+ as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
+ exists in.
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- 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")
+ 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, products=[site], relating_object=project)
+ # The project contains a site (note that project aggregation is a special case in IFC)
+ 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, 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)
+ # The site has a building, the building has a storey, and the storey has a space
+ 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")
+ # 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, products=[column], relating_structure=storey1)
+ # The column is contained in the lowermost storey
+ ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
- # And referenced in the others
- ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2)
- ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3)
+ # And referenced in the others
+ ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2)
+ 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, products=[column], relating_structure=storey3)
- """
- self.file = file
- self.settings = {"products": products, "relating_structure": relating_structure}
+ # Actually, it only goes up to storey 2.
+ ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3)
+ """
+ settings = {"products": products, "relating_structure": 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 = related_elements - products
- if related_elements:
- rel.RelatedElements = list(related_elements)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ products = set(settings["products"])
+ for rel in settings["relating_structure"].ReferencesElements:
+ related_elements = set(rel.RelatedElements)
+ if not related_elements.intersection(products):
+ continue
+ related_elements = related_elements - products
+ if related_elements:
+ rel.RelatedElements = list(related_elements)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py
index 32ef580b96..48b5c6bc1b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py
@@ -22,103 +22,99 @@ import ifcopenshell.util.element
from typing import Union
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- relating_structure: ifcopenshell.entity_instance,
- ):
- """Denote that a list products is related to a list of spatial structures
+def reference_structure(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ relating_structure: ifcopenshell.entity_instance,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Denote that a list products is related to a list of spatial structures
- 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.
+ 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.
+ 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.
+ Referencing is non-hierarchical, so a door may be referenced in multiple
+ spaces simultaneously.
- :param products: The list of physical IfcElements that exists in the space.
- :type products: list[ifcopenshell.entity_instance]
- :param relating_structure: The IfcSpatialStructureElement element, such
- as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
- exists in.
- :type relating_structure: ifcopenshell.entity_instance
- :return: The IfcRelReferencedInSpatialStructure relationship instance
- or `None` if `products` was an empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
+ :param products: The list of physical IfcElements that exists in the space.
+ :type products: list[ifcopenshell.entity_instance]
+ :param relating_structure: The IfcSpatialStructureElement element, such
+ as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
+ exists in.
+ :type relating_structure: ifcopenshell.entity_instance
+ :return: The IfcRelReferencedInSpatialStructure relationship instance
+ or `None` if `products` was an empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- 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")
+ 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, products=[site], relating_object=project)
+ # The project contains a site (note that project aggregation is a special case in IFC)
+ 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, 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)
+ # The site has a building, the building has a storey, and the storey has a space
+ 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")
+ # 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, products=[column], relating_structure=storey1)
+ # The column is contained in the lowermost storey
+ ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1)
- # And referenced in the others
- ifcopenshell.api.run(
- "spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3]
- )
- """
- self.file = file
- self.settings = {
- "products": products,
- "relating_structure": relating_structure,
- }
+ # And referenced in the others
+ ifcopenshell.api.run(
+ "spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3]
+ )
+ """
+ settings = {
+ "products": products,
+ "relating_structure": relating_structure,
+ }
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
- structure = self.settings["relating_structure"]
- products = set(self.settings["products"])
+ structure = settings["relating_structure"]
+ products = set(settings["products"])
- if not products:
- return
+ if not products:
+ return
- referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure)
- products_to_assign = products - referenced
- rel = next(iter(structure.ReferencesElements), None)
-
- if not products_to_assign:
- return rel
-
- if rel is None:
- rel = self.file.create_entity(
- "IfcRelReferencedInSpatialStructure",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedElements": list(products_to_assign),
- "RelatingStructure": structure,
- }
- )
- else:
- related_elements = set(rel.RelatedElements) | products_to_assign
- rel.RelatedElements = list(related_elements)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
+ referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure)
+ products_to_assign = products - referenced
+ rel = next(iter(structure.ReferencesElements), None)
+ if not products_to_assign:
return rel
+
+ if rel is None:
+ rel = file.create_entity(
+ "IfcRelReferencedInSpatialStructure",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedElements": list(products_to_assign),
+ "RelatingStructure": structure,
+ }
+ )
+ else:
+ related_elements = set(rel.RelatedElements) | products_to_assign
+ rel.RelatedElements = list(related_elements)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py
index d1418d3be5..b6afbc13c9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py
@@ -21,56 +21,53 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]):
- """Unassigns a container from products.
+def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None:
+ """Unassigns a container from products.
- :param product: A list of IfcProducts to remove the containment from.
- :type product: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
+ :param product: A list of IfcProducts to remove the containment from.
+ :type product: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- 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")
+ 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, products=[site], relating_object=project)
+ # The project contains a site (note that project aggregation is a special case in IFC)
+ 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, products=[building], relating_object=site)
- ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building)
+ # The site has a building, the building has a storey, and the storey has a space
+ 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")
+ # 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, products=[wall], relating_structure=storey)
+ # The wall is in the storey
+ ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey)
- # Not anymore!
- ifcopenshell.api.run("spatial.unassign_container", model, products=[wall])
- """
- self.file = file
- self.settings = {
- "products": products,
- }
+ # Not anymore!
+ ifcopenshell.api.run("spatial.unassign_container", model, products=[wall])
+ """
+ settings = {
+ "products": products,
+ }
- def execute(self) -> None:
- products = set(self.settings["products"])
- rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None)))
+ products = set(settings["products"])
+ rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None)))
- for rel in rels:
- related_elements = set(rel.RelatedElements) - products
- if related_elements:
- rel.RelatedElements = list(related_elements)
- ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for rel in rels:
+ related_elements = set(rel.RelatedElements) - products
+ if related_elements:
+ rel.RelatedElements = list(related_elements)
+ ifcopenshell.api.run("owner.update_owner_history", file, element=rel)
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py
index e0caddbe3c..3bdaf8c004 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py
@@ -15,3 +15,25 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_structural_activity import add_structural_activity
+from .add_structural_analysis_model import add_structural_analysis_model
+from .add_structural_boundary_condition import add_structural_boundary_condition
+from .add_structural_load import add_structural_load
+from .add_structural_load_case import add_structural_load_case
+from .add_structural_load_group import add_structural_load_group
+from .add_structural_member_connection import add_structural_member_connection
+from .assign_structural_analysis_model import assign_structural_analysis_model
+from .edit_structural_analysis_model import edit_structural_analysis_model
+from .edit_structural_boundary_condition import edit_structural_boundary_condition
+from .edit_structural_connection_cs import edit_structural_connection_cs
+from .edit_structural_item_axis import edit_structural_item_axis
+from .edit_structural_load import edit_structural_load
+from .edit_structural_load_case import edit_structural_load_case
+from .remove_structural_analysis_model import remove_structural_analysis_model
+from .remove_structural_boundary_condition import remove_structural_boundary_condition
+from .remove_structural_connection_condition import remove_structural_connection_condition
+from .remove_structural_load import remove_structural_load
+from .remove_structural_load_case import remove_structural_load_case
+from .remove_structural_load_group import remove_structural_load_group
+from .unassign_structural_analysis_model import unassign_structural_analysis_model
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py
index faf1daf366..4be210fcf1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py
@@ -19,65 +19,61 @@
import ifcopenshell.api
-class Usecase:
- def __init__(
- self,
+def add_structural_activity(
+ file,
+ ifc_class="IfcStructuralPlanarAction",
+ predefined_type="CONST",
+ global_or_local="GLOBAL_COORDS",
+ applied_load=None,
+ structural_member=None,
+) -> None:
+ """Adds a new structural activity
+
+ A structural activity is either a structural action or a reaction. It
+ may be applied to a point, a curve, or a planar surface, and may be a
+ constant load, linear, etc.
+
+ The activity must be defined using an applied load, and associated with
+ a structural member.
+
+ :param ifc_class: Choose from any subtype of IfcStructuralActivity.
+ :type ifc_class: str
+ :param predefined_type: View the IFC documentation for what valid
+ predefined types may be chosen.
+ :type predefined_type: str
+ :param global_or_local: The location coordinates of the load is always
+ defined locally relative to the structural member the activity is
+ assigned to. However, the directions of the applied load may either
+ be specified globally or locally depending on how this argument is
+ set. Choose from GLOBAL_COORDS or LOCAL_COORDS.
+ :type global_or_local: str
+ :param applied_load: The IfcStructuralLoad that is applied in this
+ activity.
+ :type applied_load: ifcopenshell.entity_instance
+ :param structural_member: The IfcStructuralMember that the load is
+ applied to.
+ :type structural_member: ifcopenshell.entity_instance
+ :return: The newly created entity based on the ifc_class
+ :rtype: ifcopenshell.entity_instance
+ """
+ settings = {
+ "ifc_class": ifc_class,
+ "predefined_type": predefined_type,
+ "global_or_local": global_or_local,
+ "applied_load": applied_load,
+ "structural_member": structural_member,
+ }
+
+ activity = ifcopenshell.api.run(
+ "root.create_entity",
file,
- ifc_class="IfcStructuralPlanarAction",
- predefined_type="CONST",
- global_or_local="GLOBAL_COORDS",
- applied_load=None,
- structural_member=None,
- ):
- """Adds a new structural activity
+ ifc_class=settings["ifc_class"],
+ predefined_type=settings["predefined_type"],
+ )
+ activity.AppliedLoad = settings["applied_load"]
+ activity.GlobalOrLocal = settings["global_or_local"]
- A structural activity is either a structural action or a reaction. It
- may be applied to a point, a curve, or a planar surface, and may be a
- constant load, linear, etc.
-
- The activity must be defined using an applied load, and associated with
- a structural member.
-
- :param ifc_class: Choose from any subtype of IfcStructuralActivity.
- :type ifc_class: str
- :param predefined_type: View the IFC documentation for what valid
- predefined types may be chosen.
- :type predefined_type: str
- :param global_or_local: The location coordinates of the load is always
- defined locally relative to the structural member the activity is
- assigned to. However, the directions of the applied load may either
- be specified globally or locally depending on how this argument is
- set. Choose from GLOBAL_COORDS or LOCAL_COORDS.
- :type global_or_local: str
- :param applied_load: The IfcStructuralLoad that is applied in this
- activity.
- :type applied_load: ifcopenshell.entity_instance
- :param structural_member: The IfcStructuralMember that the load is
- applied to.
- :type structural_member: ifcopenshell.entity_instance
- :return: The newly created entity based on the ifc_class
- :rtype: ifcopenshell.entity_instance
- """
- self.file = file
- self.settings = {
- "ifc_class": ifc_class,
- "predefined_type": predefined_type,
- "global_or_local": global_or_local,
- "applied_load": applied_load,
- "structural_member": structural_member,
- }
-
- def execute(self):
- activity = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class=self.settings["ifc_class"],
- predefined_type=self.settings["predefined_type"],
- )
- activity.AppliedLoad = self.settings["applied_load"]
- activity.GlobalOrLocal = self.settings["global_or_local"]
-
- rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralActivity")
- rel.RelatingElement = self.settings["structural_member"]
- rel.RelatedStructuralActivity = activity
- return activity
+ rel = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcRelConnectsStructuralActivity")
+ rel.RelatingElement = settings["structural_member"]
+ rel.RelatedStructuralActivity = activity
+ return activity
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py
index 39181f9a4c..837fd29cce 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py
@@ -20,30 +20,27 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file):
- """Add a new structural analysis model
+def add_structural_analysis_model(file) -> None:
+ """Add a new structural analysis model
- A structural analysis model is a group of all the loads, reactions,
- structural members, and structural connections required to describe a
- structural analysis model.
+ A structural analysis model is a group of all the loads, reactions,
+ structural members, and structural connections required to describe a
+ structural analysis model.
- A 3D analytical model is assumed.
+ A 3D analytical model is assumed.
- :return: The newly created IfcStructuralAnalysisModel
- :rtype: ifcopenshell.entity_instance
+ :return: The newly created IfcStructuralAnalysisModel
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a fresh blank structural analysis
- analysis = ifcopenshell.api.run("structural.add_structural_analysis_model", model)
- """
- self.file = file
- self.settings = {}
+ # Create a fresh blank structural analysis
+ analysis = ifcopenshell.api.run("structural.add_structural_analysis_model", model)
+ """
+ settings = {}
- def execute(self):
- return ifcopenshell.api.run(
- "root.create_entity", self.file, ifc_class="IfcStructuralAnalysisModel", predefined_type="LOADING_3D"
- )
+ return ifcopenshell.api.run(
+ "root.create_entity", file, ifc_class="IfcStructuralAnalysisModel", predefined_type="LOADING_3D"
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py
index 1cd9dfef9f..5aef16efed 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py
@@ -17,55 +17,50 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, name=None, connection=None, ifc_class="IfcBoundaryNodeCondition"):
- """Adds a new structural boundary condition to a structural connection
+def add_structural_boundary_condition(file, name=None, connection=None, ifc_class="IfcBoundaryNodeCondition") -> None:
+ """Adds a new structural boundary condition to a structural connection
- The type of boundary condition depends on the connection. Point
- connections will have a node condition, curve connections will have an
- edge condition, and surface connections will have a face condition.
+ The type of boundary condition depends on the connection. Point
+ connections will have a node condition, curve connections will have an
+ edge condition, and surface connections will have a face condition.
- :param name: The name of the boundary condition.
- :type name: str,optional
- :param connection: The IfcStructuralConnection to apply the boundary
- condition to. This will determine the type of condition that is
- created. If no connection is supplied, an orphan boundary condition
- will be created using the ifc_class that you specify.
- :type connection: ifcopenshell.entity_instance,optional
- :param ifc_class: The class of IfcBoundaryCondition to create, only
- relevant if you do not specify a connection and want to create an
- orphaned boundary condition.
- :type ifc_class: str,optional
- :return: The newly created IfcBoundaryCondition
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the boundary condition.
+ :type name: str,optional
+ :param connection: The IfcStructuralConnection to apply the boundary
+ condition to. This will determine the type of condition that is
+ created. If no connection is supplied, an orphan boundary condition
+ will be created using the ifc_class that you specify.
+ :type connection: ifcopenshell.entity_instance,optional
+ :param ifc_class: The class of IfcBoundaryCondition to create, only
+ relevant if you do not specify a connection and want to create an
+ orphaned boundary condition.
+ :type ifc_class: str,optional
+ :return: The newly created IfcBoundaryCondition
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("structural.add_structural_boundary_condition", model, connection=connection)
- """
- self.file = file
- self.settings = {"name": name, "connection": connection, "ifc_class": ifc_class}
+ ifcopenshell.api.run("structural.add_structural_boundary_condition", model, connection=connection)
+ """
+ settings = {"name": name, "connection": connection, "ifc_class": ifc_class}
- def execute(self):
- if self.settings["connection"]:
- # assign boundary condition to a connection
- if self.settings["connection"].is_a("IfcRelConnectsStructuralMember"):
- related_connection = self.settings["connection"].RelatedStructuralConnection
- else:
- related_connection = self.settings["connection"]
-
- if related_connection.is_a("IfcStructuralPointConnection"):
- boundary_class = "IfcBoundaryNodeCondition"
- elif related_connection.is_a("IfcStructuralCurveConnection"):
- boundary_class = "IfcBoundaryEdgeCondition"
- elif related_connection.is_a("IfcStructuralSurfaceConnection"):
- boundary_class = "IfcBoundaryFaceCondition"
-
- self.settings["connection"].AppliedCondition = self.file.create_entity(
- boundary_class, Name=self.settings["name"]
- )
+ if settings["connection"]:
+ # assign boundary condition to a connection
+ if settings["connection"].is_a("IfcRelConnectsStructuralMember"):
+ related_connection = settings["connection"].RelatedStructuralConnection
else:
- # add an orphan boundary condition
- return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"])
+ related_connection = settings["connection"]
+
+ if related_connection.is_a("IfcStructuralPointConnection"):
+ boundary_class = "IfcBoundaryNodeCondition"
+ elif related_connection.is_a("IfcStructuralCurveConnection"):
+ boundary_class = "IfcBoundaryEdgeCondition"
+ elif related_connection.is_a("IfcStructuralSurfaceConnection"):
+ boundary_class = "IfcBoundaryFaceCondition"
+
+ settings["connection"].AppliedCondition = file.create_entity(boundary_class, Name=settings["name"])
+ else:
+ # add an orphan boundary condition
+ return file.create_entity(settings["ifc_class"], Name=settings["name"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py
index 6d51d7dc22..3cb06cd513 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py
@@ -19,35 +19,32 @@
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, name=None, ifc_class="IfcStructuralLoadLinearForce"):
- """Adds a new structural load
+def add_structural_load(file, name=None, ifc_class="IfcStructuralLoadLinearForce") -> None:
+ """Adds a new structural load
- Structural loads may be actions or reactions. A simple load might be a
- static and be linear, planar, or a single point. Alternatively, loads
- may be defined as a configuration of multiple loads.
+ Structural loads may be actions or reactions. A simple load might be a
+ static and be linear, planar, or a single point. Alternatively, loads
+ may be defined as a configuration of multiple loads.
- :param name: The name of the load
- :type name: str,optional
- :param ifc_class: The subtype of IfcStructuralLoad to create. Consult
- the IFC documentation to see all the types of loads.
- :type ifc_class: str
- :return: The newly created load entity, depending on the ifc_class
- specified.
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the load
+ :type name: str,optional
+ :param ifc_class: The subtype of IfcStructuralLoad to create. Consult
+ the IFC documentation to see all the types of loads.
+ :type ifc_class: str
+ :return: The newly created load entity, depending on the ifc_class
+ specified.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a simple linear load
- ifcopenshell.api.run("structural.add_structural_load", model)
- """
- self.file = file
- self.settings = {
- "name": name,
- "ifc_class": ifc_class,
- }
+ # Create a simple linear load
+ ifcopenshell.api.run("structural.add_structural_load", model)
+ """
+ settings = {
+ "name": name,
+ "ifc_class": ifc_class,
+ }
- def execute(self):
- return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"])
+ return file.create_entity(settings["ifc_class"], Name=settings["name"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py
index afc4e676db..e3d2c4f6c2 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py
@@ -19,39 +19,34 @@
import ifcopenshell.api
-class Usecase:
- def __init__(
- self, file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED"
- ):
- """Adds a new load case, which is a collection of related load groups
+def add_structural_load_case(file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED") -> None:
+ """Adds a new load case, which is a collection of related load groups
- :param name: The name of the load case
- :type name: str
- :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G,
- or VARIABLE_Q, taken from the Eurocode standard.
- :type action_type: str
- :param action_source: The source of the load case, such as DEAD_LOAD_G,
- LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult
- IfcActionSourceTypeEnum in the IFC documentation.
- :type action_source: str
- :return: The new IfcStructuralLoadCase
- :rtype: ifcopenshell.entity_instance
- """
- self.file = file
- self.settings = {
- "name": name,
- "action_type": action_type,
- "action_source": action_source,
- }
+ :param name: The name of the load case
+ :type name: str
+ :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G,
+ or VARIABLE_Q, taken from the Eurocode standard.
+ :type action_type: str
+ :param action_source: The source of the load case, such as DEAD_LOAD_G,
+ LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult
+ IfcActionSourceTypeEnum in the IFC documentation.
+ :type action_source: str
+ :return: The new IfcStructuralLoadCase
+ :rtype: ifcopenshell.entity_instance
+ """
+ settings = {
+ "name": name,
+ "action_type": action_type,
+ "action_source": action_source,
+ }
- def execute(self):
- load_case = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcStructuralLoadCase",
- predefined_type="LOAD_CASE",
- name=self.settings["name"],
- )
- load_case.ActionType = self.settings["action_type"]
- load_case.ActionSource = self.settings["action_source"]
- return load_case
+ load_case = ifcopenshell.api.run(
+ "root.create_entity",
+ file,
+ ifc_class="IfcStructuralLoadCase",
+ predefined_type="LOAD_CASE",
+ name=settings["name"],
+ )
+ load_case.ActionType = settings["action_type"]
+ load_case.ActionSource = settings["action_source"]
+ return load_case
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py
index 497977fe6e..3f450df5c8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py
@@ -19,39 +19,34 @@
import ifcopenshell.api
-class Usecase:
- def __init__(
- self, file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED"
- ):
- """Adds a new load group, which is a collection of related loads
+def add_structural_load_group(file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED") -> None:
+ """Adds a new load group, which is a collection of related loads
- :param name: The name of the load group
- :type name: str
- :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G,
- or VARIABLE_Q, taken from the Eurocode standard.
- :type action_type: str
- :param action_source: The source of the load case, such as DEAD_LOAD_G,
- LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult
- IfcActionSourceTypeEnum in the IFC documentation.
- :type action_source: str
- :return: The new IfcStructuralLoadCase
- :rtype: ifcopenshell.entity_instance
- """
- self.file = file
- self.settings = {
- "name": name,
- "action_type": action_type,
- "action_source": action_source,
- }
+ :param name: The name of the load group
+ :type name: str
+ :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G,
+ or VARIABLE_Q, taken from the Eurocode standard.
+ :type action_type: str
+ :param action_source: The source of the load case, such as DEAD_LOAD_G,
+ LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult
+ IfcActionSourceTypeEnum in the IFC documentation.
+ :type action_source: str
+ :return: The new IfcStructuralLoadCase
+ :rtype: ifcopenshell.entity_instance
+ """
+ settings = {
+ "name": name,
+ "action_type": action_type,
+ "action_source": action_source,
+ }
- def execute(self):
- load_group = ifcopenshell.api.run(
- "root.create_entity",
- self.file,
- ifc_class="IfcStructuralLoadGroup",
- predefined_type="LOAD_GROUP",
- name=self.settings["name"],
- )
- load_group.ActionType = self.settings["action_type"]
- load_group.ActionSource = self.settings["action_source"]
- return load_group
+ load_group = ifcopenshell.api.run(
+ "root.create_entity",
+ file,
+ ifc_class="IfcStructuralLoadGroup",
+ predefined_type="LOAD_GROUP",
+ name=settings["name"],
+ )
+ load_group.ActionType = settings["action_type"]
+ load_group.ActionSource = settings["action_source"]
+ return load_group
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py
index eda5fc96c2..792c03b66a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py
@@ -20,30 +20,27 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_structural_member=None, related_structural_connection=None):
- """Relates a structural member and a structural connection
+def add_structural_member_connection(file, relating_structural_member=None, related_structural_connection=None) -> None:
+ """Relates a structural member and a structural connection
- :param relating_structural_member: The IfcStructuralMember to have a
- connection added to it.
- :type relating_structural_member: ifcopenshell.entity_instance
- :param related_structural_connection: The IfcStructuralConnection to add
- to the IfcStructuralMember.
- :type related_structural_connection: ifcopenshell.entity_instance
- :return: The IfcRelConnectsStructuralMember relationship
- :rtype: ifcopenshell.entity_instance
- """
- self.file = file
- self.settings = {
- "relating_structural_member": relating_structural_member,
- "related_structural_connection": related_structural_connection,
- }
+ :param relating_structural_member: The IfcStructuralMember to have a
+ connection added to it.
+ :type relating_structural_member: ifcopenshell.entity_instance
+ :param related_structural_connection: The IfcStructuralConnection to add
+ to the IfcStructuralMember.
+ :type related_structural_connection: ifcopenshell.entity_instance
+ :return: The IfcRelConnectsStructuralMember relationship
+ :rtype: ifcopenshell.entity_instance
+ """
+ settings = {
+ "relating_structural_member": relating_structural_member,
+ "related_structural_connection": related_structural_connection,
+ }
- def execute(self):
- for connection in self.settings["related_structural_connection"].ConnectsStructuralMembers or []:
- if connection.RelatingStructuralMember == self.settings["relating_structural_member"]:
- return
- rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralMember")
- rel.RelatingStructuralMember = self.settings["relating_structural_member"]
- rel.RelatedStructuralConnection = self.settings["related_structural_connection"]
- return rel
+ for connection in settings["related_structural_connection"].ConnectsStructuralMembers or []:
+ if connection.RelatingStructuralMember == settings["relating_structural_member"]:
+ return
+ rel = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcRelConnectsStructuralMember")
+ rel.RelatingStructuralMember = settings["relating_structural_member"]
+ rel.RelatedStructuralConnection = settings["related_structural_connection"]
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py
index 61f771c982..19c31e34ff 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py
@@ -20,37 +20,34 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, product=None, structural_analysis_model=None):
- """Assigns a load or structural member to an analysis model
+def assign_structural_analysis_model(file, product=None, structural_analysis_model=None) -> None:
+ """Assigns a load or structural member to an analysis model
- :param product: The structural element that is part of the analysis.
- :type product: ifcopenshell.entity_instance
- :param structural_analysis_model: The IfcStructuralAnalysisModel that
- the structural element is related to.
- :type structural_analysis_model: ifcopenshell.entity_instance
- :return: The IfcRelAssignsToGroup relationship
- :rtype: ifcopenshell.entity_instance
- """
- self.file = file
- self.settings = {
- "product": product,
- "structural_analysis_model": structural_analysis_model,
- }
+ :param product: The structural element that is part of the analysis.
+ :type product: ifcopenshell.entity_instance
+ :param structural_analysis_model: The IfcStructuralAnalysisModel that
+ the structural element is related to.
+ :type structural_analysis_model: ifcopenshell.entity_instance
+ :return: The IfcRelAssignsToGroup relationship
+ :rtype: ifcopenshell.entity_instance
+ """
+ settings = {
+ "product": product,
+ "structural_analysis_model": structural_analysis_model,
+ }
- def execute(self):
- if not self.settings["structural_analysis_model"].IsGroupedBy:
- return self.file.create_entity(
- "IfcRelAssignsToGroup",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedObjects": [self.settings["product"]],
- "RelatingGroup": self.settings["structural_analysis_model"],
- }
- )
- rel = self.settings["structural_analysis_model"].IsGroupedBy[0]
- related_objects = set(rel.RelatedObjects) or set()
- related_objects.add(self.settings["product"])
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
+ if not settings["structural_analysis_model"].IsGroupedBy:
+ return file.create_entity(
+ "IfcRelAssignsToGroup",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedObjects": [settings["product"]],
+ "RelatingGroup": settings["structural_analysis_model"],
+ }
+ )
+ rel = settings["structural_analysis_model"].IsGroupedBy[0]
+ related_objects = set(rel.RelatedObjects) or set()
+ related_objects.add(settings["product"])
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py
index 39c46f6fe7..7c41c59478 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py
@@ -17,24 +17,21 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, structural_analysis_model=None, attributes=None):
- """Edits the attributes of an IfcStructuralAnalysisModel
+def edit_structural_analysis_model(file, structural_analysis_model=None, attributes=None) -> None:
+ """Edits the attributes of an IfcStructuralAnalysisModel
- For more information about the attributes and data types of an
- IfcStructuralAnalysisModel, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcStructuralAnalysisModel, consult the IFC documentation.
- :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit
- :type structural_analysis_model: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}}
+ :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit
+ :type structural_analysis_model: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+ """
+ settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["structural_analysis_model"], name, value)
- return self.settings["structural_analysis_model"]
+ for name, value in settings["attributes"].items():
+ setattr(settings["structural_analysis_model"], name, value)
+ return settings["structural_analysis_model"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py
index e6814c5242..2674a4869e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py
@@ -17,29 +17,26 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, condition=None, attributes=None):
- """Edits the attributes of an IfcBoundaryCondition
+def edit_structural_boundary_condition(file, condition=None, attributes=None) -> None:
+ """Edits the attributes of an IfcBoundaryCondition
- For more information about the attributes and data types of an
- IfcBoundaryCondition, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcBoundaryCondition, consult the IFC documentation.
- :param condition: The IfcBoundaryCondition entity you want to edit
- :type condition: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"condition": condition, "attributes": attributes or {}}
+ :param condition: The IfcBoundaryCondition entity you want to edit
+ :type condition: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+ """
+ settings = {"condition": condition, "attributes": attributes or {}}
- def execute(self):
- for name, data in self.settings["attributes"].items():
- if data["type"] == "string" or data["type"] == "null":
- value = data["value"]
- elif data["type"] == "IfcBoolean":
- value = self.file.createIfcBoolean(data["value"])
- else:
- value = self.file.create_entity(data["type"], data["value"])
- setattr(self.settings["condition"], name, value)
+ for name, data in settings["attributes"].items():
+ if data["type"] == "string" or data["type"] == "null":
+ value = data["value"]
+ elif data["type"] == "IfcBoolean":
+ value = file.createIfcBoolean(data["value"])
+ else:
+ value = file.create_entity(data["type"], data["value"])
+ setattr(settings["condition"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py
index a66bbb989e..89faa62ecd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py
@@ -17,38 +17,35 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, structural_item=None, axis=None, ref_direction=None):
- """Edits the coordinate system of a structural connection
+def edit_structural_connection_cs(file, structural_item=None, axis=None, ref_direction=None) -> None:
+ """Edits the coordinate system of a structural connection
- :param structural_item: The IfcStructuralItem you want to modify.
- :type structural_item: ifcopenshell.entity_instance
- :param axis: The unit Z axis vector defined as a list of 3 floats.
- Defaults to [0., 0., 1.].
- :type axis: list[float]
- :param ref_direction: The unit X axis vector defined as a list of 3
- floats. Defaults to [1., 0., 0.].
- :type ref_direction: list[float]
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {
- "structural_item": structural_item,
- "axis": axis or [0.0, 0.0, 1.0],
- "ref_direction": ref_direction or [1.0, 0.0, 0.0],
- }
+ :param structural_item: The IfcStructuralItem you want to modify.
+ :type structural_item: ifcopenshell.entity_instance
+ :param axis: The unit Z axis vector defined as a list of 3 floats.
+ Defaults to [0., 0., 1.].
+ :type axis: list[float]
+ :param ref_direction: The unit X axis vector defined as a list of 3
+ floats. Defaults to [1., 0., 0.].
+ :type ref_direction: list[float]
+ :return: None
+ :rtype: None
+ """
+ settings = {
+ "structural_item": structural_item,
+ "axis": axis or [0.0, 0.0, 1.0],
+ "ref_direction": ref_direction or [1.0, 0.0, 0.0],
+ }
- def execute(self):
- if self.settings["structural_item"].ConditionCoordinateSystem is None:
- point = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
- ccs = self.file.createIfcAxis2Placement3D(point, None, None)
- self.settings["structural_item"].ConditionCoordinateSystem = ccs
+ if settings["structural_item"].ConditionCoordinateSystem is None:
+ point = file.createIfcCartesianPoint((0.0, 0.0, 0.0))
+ ccs = file.createIfcAxis2Placement3D(point, None, None)
+ settings["structural_item"].ConditionCoordinateSystem = ccs
- ccs = self.settings["structural_item"].ConditionCoordinateSystem
- if ccs.Axis and len(self.file.get_inverse(ccs.Axis)) == 1:
- self.file.remove(ccs.Axis)
- ccs.Axis = self.file.createIfcDirection(self.settings["axis"])
- if ccs.RefDirection and len(self.file.get_inverse(ccs.RefDirection)) == 1:
- self.file.remove(ccs.RefDirection)
- ccs.RefDirection = self.file.createIfcDirection(self.settings["ref_direction"])
+ ccs = settings["structural_item"].ConditionCoordinateSystem
+ if ccs.Axis and len(file.get_inverse(ccs.Axis)) == 1:
+ file.remove(ccs.Axis)
+ ccs.Axis = file.createIfcDirection(settings["axis"])
+ if ccs.RefDirection and len(file.get_inverse(ccs.RefDirection)) == 1:
+ file.remove(ccs.RefDirection)
+ ccs.RefDirection = file.createIfcDirection(settings["ref_direction"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py
index ec4b163aca..dbb2541371 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py
@@ -17,22 +17,19 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, structural_item=None, axis=None):
- """Edits the coordinate system of a structural connection
+def edit_structural_item_axis(file, structural_item=None, axis=None) -> None:
+ """Edits the coordinate system of a structural connection
- :param structural_item: The IfcStructuralItem you want to modify.
- :type structural_item: ifcopenshell.entity_instance
- :param axis: The unit Z axis vector defined as a list of 3 floats.
- Defaults to [0., 0., 1.].
- :type axis: list[float]
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"structural_item": structural_item, "axis": axis or [0.0, 0.0, 1.0]}
+ :param structural_item: The IfcStructuralItem you want to modify.
+ :type structural_item: ifcopenshell.entity_instance
+ :param axis: The unit Z axis vector defined as a list of 3 floats.
+ Defaults to [0., 0., 1.].
+ :type axis: list[float]
+ :return: None
+ :rtype: None
+ """
+ settings = {"structural_item": structural_item, "axis": axis or [0.0, 0.0, 1.0]}
- def execute(self):
- if len(self.file.get_inverse(self.settings["structural_item"].Axis)) == 1:
- self.file.remove(self.settings["structural_item"].Axis)
- self.settings["structural_item"].Axis = self.file.createIfcDirection(self.settings["axis"])
+ if len(file.get_inverse(settings["structural_item"].Axis)) == 1:
+ file.remove(settings["structural_item"].Axis)
+ settings["structural_item"].Axis = file.createIfcDirection(settings["axis"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py
index 3adba0ade9..2c577deb83 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py
@@ -17,23 +17,20 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, structural_load=None, attributes=None):
- """Edits the attributes of an IfcStructuralLoad
+def edit_structural_load(file, structural_load=None, attributes=None) -> None:
+ """Edits the attributes of an IfcStructuralLoad
- For more information about the attributes and data types of an
- IfcStructuralLoad, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcStructuralLoad, consult the IFC documentation.
- :param structural_load: The IfcStructuralLoad entity you want to edit
- :type structural_load: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"structural_load": structural_load, "attributes": attributes or {}}
+ :param structural_load: The IfcStructuralLoad entity you want to edit
+ :type structural_load: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+ """
+ settings = {"structural_load": structural_load, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["structural_load"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["structural_load"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py
index cffce454bf..4c84573795 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py
@@ -17,23 +17,20 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, load_case=None, attributes=None):
- """Edits the attributes of an IfcStructuralLoadCase
+def edit_structural_load_case(file, load_case=None, attributes=None) -> None:
+ """Edits the attributes of an IfcStructuralLoadCase
- For more information about the attributes and data types of an
- IfcStructuralLoadCase, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcStructuralLoadCase, consult the IFC documentation.
- :param load_case: The IfcStructuralLoadCase entity you want to edit
- :type load_case: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"load_case": load_case, "attributes": attributes or {}}
+ :param load_case: The IfcStructuralLoadCase entity you want to edit
+ :type load_case: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+ """
+ settings = {"load_case": load_case, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["load_case"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["load_case"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py
index 4ebff6cc3d..b238562b18 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py
@@ -20,28 +20,25 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, structural_analysis_model=None):
- """Removes an analysis model
+def remove_structural_analysis_model(file, structural_analysis_model=None) -> None:
+ """Removes an analysis model
- Note that the contents of an analysis model are currently preserved.
+ Note that the contents of an analysis model are currently preserved.
- :param structural_analysis_model: The IfcStructuralAnalysisModel to
- remove.
- :type structural_analysis_model: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"structural_analysis_model": structural_analysis_model}
+ :param structural_analysis_model: The IfcStructuralAnalysisModel to
+ remove.
+ :type structural_analysis_model: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"structural_analysis_model": structural_analysis_model}
- def execute(self):
- for rel in self.settings["structural_analysis_model"].IsGroupedBy or []:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["structural_analysis_model"].OwnerHistory
- self.file.remove(self.settings["structural_analysis_model"])
+ for rel in settings["structural_analysis_model"].IsGroupedBy or []:
+ history = rel.OwnerHistory
+ file.remove(rel)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["structural_analysis_model"].OwnerHistory
+ file.remove(settings["structural_analysis_model"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py
index 02cfb79e3c..7aa4f6bd74 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py
@@ -17,31 +17,28 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, connection=None, boundary_condition=None):
- """Removes a condition from a connection, or an orphased boundary condition
+def remove_structural_boundary_condition(file, connection=None, boundary_condition=None) -> None:
+ """Removes a condition from a connection, or an orphased boundary condition
- :param connection: The IfcStructuralConnection to remove the condition
- from. If omitted, it is assumed to be an orphaned condition.
- :type connection: ifcopenshell.entity_instance,optional
- :param boundary_condition: The IfcBoundaryCondition to remove.
- :type boundary_condition: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"connection": connection, "boundary_condition": boundary_condition}
+ :param connection: The IfcStructuralConnection to remove the condition
+ from. If omitted, it is assumed to be an orphaned condition.
+ :type connection: ifcopenshell.entity_instance,optional
+ :param boundary_condition: The IfcBoundaryCondition to remove.
+ :type boundary_condition: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"connection": connection, "boundary_condition": boundary_condition}
- def execute(self):
- if self.settings["connection"]:
- # remove boundary condition from a connection
- if not self.settings["connection"].AppliedCondition:
- return
- if len(self.file.get_inverse(self.settings["connection"].AppliedCondition)) == 1:
- self.file.remove(self.settings["connection"].AppliedCondition)
- self.settings["connection"].AppliedCondition = None
- else:
- # remove the boundary condition
- for conn in self.file.get_inverse(self.settings["boundary_condition"]):
- conn.AppliedCondition = None
- self.file.remove(self.settings["boundary_condition"])
+ if settings["connection"]:
+ # remove boundary condition from a connection
+ if not settings["connection"].AppliedCondition:
+ return
+ if len(file.get_inverse(settings["connection"].AppliedCondition)) == 1:
+ file.remove(settings["connection"].AppliedCondition)
+ settings["connection"].AppliedCondition = None
+ else:
+ # remove the boundary condition
+ for conn in file.get_inverse(settings["boundary_condition"]):
+ conn.AppliedCondition = None
+ file.remove(settings["boundary_condition"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py
index 28ce9fc4c9..21ed51f712 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py
@@ -21,28 +21,25 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relation=None):
- """Removes a relationship between a connection and a condition
+def remove_structural_connection_condition(file, relation=None) -> None:
+ """Removes a relationship between a connection and a condition
- The condition and the member itself is preserved.
+ The condition and the member itself is preserved.
- :param relation: The IfcRelConnectsStructuralMember to remove.
- :type relation: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"relation": relation}
+ :param relation: The IfcRelConnectsStructuralMember to remove.
+ :type relation: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"relation": relation}
- def execute(self):
- if self.settings["relation"].AppliedCondition:
- ifcopenshell.api.run(
- "structural.remove_structural_boundary_condition",
- self.file,
- connection=self.settings["relation"].RelatedStructuralConnection
- )
- history = self.settings["relation"].OwnerHistory
- self.file.remove(self.settings["relation"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if settings["relation"].AppliedCondition:
+ ifcopenshell.api.run(
+ "structural.remove_structural_boundary_condition",
+ file,
+ connection=settings["relation"].RelatedStructuralConnection,
+ )
+ history = settings["relation"].OwnerHistory
+ file.remove(settings["relation"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py
index 55b83a7f1b..afe97029ab 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py
@@ -17,17 +17,14 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, structural_load=None):
- """Removes a structural load
+def remove_structural_load(file, structural_load=None) -> None:
+ """Removes a structural load
- :param structural_load: The IfcStructuralLoad to remove.
- :type structural_load: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"structural_load": structural_load}
+ :param structural_load: The IfcStructuralLoad to remove.
+ :type structural_load: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"structural_load": structural_load}
- def execute(self):
- self.file.remove(self.settings["structural_load"])
+ file.remove(settings["structural_load"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py
index e331309239..de317ed354 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py
@@ -21,25 +21,22 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, load_case=None):
- """Removes a structural load case
+def remove_structural_load_case(file, load_case=None) -> None:
+ """Removes a structural load case
- :param load_case: The IfcStructuralLoadCase to remove.
- :type load_case: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"load_case": load_case}
+ :param load_case: The IfcStructuralLoadCase to remove.
+ :type load_case: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"load_case": load_case}
- def execute(self):
- # TODO: do a deep purge
- for rel in self.settings["load_case"].IsGroupedBy or []:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["load_case"].OwnerHistory
- self.file.remove(self.settings["load_case"])
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ # TODO: do a deep purge
+ for rel in settings["load_case"].IsGroupedBy or []:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["load_case"].OwnerHistory
+ file.remove(settings["load_case"])
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py
index 93500aba1b..541dd87811 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py
@@ -21,27 +21,24 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, load_group=None):
- """Removes a structural load group
+def remove_structural_load_group(file, load_group=None) -> None:
+ """Removes a structural load group
- :param load_group: The IfcStructuralLoadGroup to remove.
- :type load_group: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"load_group": load_group}
+ :param load_group: The IfcStructuralLoadGroup to remove.
+ :type load_group: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {"load_group": load_group}
- def execute(self):
- # TODO: do a deep purge
- for inverse in self.file.get_inverse(self.settings["load_group"]):
- if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["load_group"].OwnerHistory
- self.file.remove(self.settings["load_group"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ # TODO: do a deep purge
+ for inverse in file.get_inverse(settings["load_group"]):
+ if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["load_group"].OwnerHistory
+ file.remove(settings["load_group"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py
index 5a86a6a9f3..b4dedc2832 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py
@@ -21,35 +21,32 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, product=None, structural_analysis_model=None):
- """Removes a relationship between a structural element and the analysis model
+def unassign_structural_analysis_model(file, product=None, structural_analysis_model=None) -> None:
+ """Removes a relationship between a structural element and the analysis model
- :param product: The structural element that is part of the analysis.
- :type product: ifcopenshell.entity_instance
- :param structural_analysis_model: The IfcStructuralAnalysisModel that
- the structural element is related to.
- :type structural_analysis_model: ifcopenshell.entity_instance
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {
- "product": product,
- "structural_analysis_model": structural_analysis_model,
- }
+ :param product: The structural element that is part of the analysis.
+ :type product: ifcopenshell.entity_instance
+ :param structural_analysis_model: The IfcStructuralAnalysisModel that
+ the structural element is related to.
+ :type structural_analysis_model: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+ """
+ settings = {
+ "product": product,
+ "structural_analysis_model": structural_analysis_model,
+ }
- def execute(self):
- if not self.settings["structural_analysis_model"].IsGroupedBy:
- return
- rel = self.settings["structural_analysis_model"].IsGroupedBy[0]
- related_objects = set(rel.RelatedObjects) or set()
- related_objects.remove(self.settings["product"])
- if len(related_objects):
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if not settings["structural_analysis_model"].IsGroupedBy:
+ return
+ rel = settings["structural_analysis_model"].IsGroupedBy[0]
+ related_objects = set(rel.RelatedObjects) or set()
+ related_objects.remove(settings["product"])
+ if len(related_objects):
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
+ else:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py
index e0caddbe3c..df9fd47518 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py
@@ -15,3 +15,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_style import add_style
+from .add_surface_style import add_surface_style
+from .add_surface_textures import add_surface_textures
+from .assign_material_style import assign_material_style
+from .assign_representation_styles import assign_representation_styles
+from .edit_presentation_style import edit_presentation_style
+from .edit_surface_style import edit_surface_style
+from .remove_style import remove_style
+from .remove_styled_representation import remove_styled_representation
+from .remove_surface_style import remove_surface_style
+from .unassign_material_style import unassign_material_style
+from .unassign_representation_styles import unassign_representation_styles
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py
index 650043039f..599feaef3d 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py
@@ -17,48 +17,45 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, name=None, ifc_class="IfcSurfaceStyle"):
- """Add a new presentation style
+def add_style(file, name=None, ifc_class="IfcSurfaceStyle") -> None:
+ """Add a new presentation style
- A presentation style is a container of visual settings (called
- presentation items) that affect the appearance of objects. There are
- four types of style:
+ A presentation style is a container of visual settings (called
+ presentation items) that affect the appearance of objects. There are
+ four types of style:
- - Surface styles, which give 3D objects (which have surfaces / faces)
- their colours and textures. This is the most common type of style.
- - Curve styles, which give 2D and 3D curves, lines, polylines, their
- stroke thickness and colour.
- - Fill area styles, which gives 2D polygons and flat 3D planes their
- colours, hatch patterns, tiled patterns, and pattern scales.
- - Text styles, which gives text their font family, weight, variant,
- size, indentation, alignment, decoration, spacing, and transformation.
+ - Surface styles, which give 3D objects (which have surfaces / faces)
+ their colours and textures. This is the most common type of style.
+ - Curve styles, which give 2D and 3D curves, lines, polylines, their
+ stroke thickness and colour.
+ - Fill area styles, which gives 2D polygons and flat 3D planes their
+ colours, hatch patterns, tiled patterns, and pattern scales.
+ - Text styles, which gives text their font family, weight, variant,
+ size, indentation, alignment, decoration, spacing, and transformation.
- Once you have created a presentation style object, you can further
- define the properties of your style using other API functions by adding
- presentation items, such as ifcopenshell.api.style.add_surface_style.
+ Once you have created a presentation style object, you can further
+ define the properties of your style using other API functions by adding
+ presentation items, such as ifcopenshell.api.style.add_surface_style.
- :param name: The name of the style. Used to easily identify it using a
- style library.
- :type name: str,optional
- :param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle,
- IfcFillAreaStyle, or IfcTextStyle.
- :type ifc_class: str
- :return: The newly created style element, based on the provided
- ifc_class.
- :rtype: ifcopenshell.entity_instance
+ :param name: The name of the style. Used to easily identify it using a
+ style library.
+ :type name: str,optional
+ :param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle,
+ IfcFillAreaStyle, or IfcTextStyle.
+ :type ifc_class: str
+ :return: The newly created style element, based on the provided
+ ifc_class.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
- """
- self.file = file
- self.settings = {"name": name, "ifc_class": ifc_class}
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
+ """
+ settings = {"name": name, "ifc_class": ifc_class}
- def execute(self):
- if self.settings["ifc_class"] == "IfcSurfaceStyle":
- # Name is filled out because Revit treats this incorrectly as the material name
- return self.file.createIfcSurfaceStyle(self.settings["name"], "BOTH")
+ if settings["ifc_class"] == "IfcSurfaceStyle":
+ # Name is filled out because Revit treats this incorrectly as the material name
+ return file.createIfcSurfaceStyle(settings["name"], "BOTH")
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py
index 32064811f9..c8f9c415d1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py
@@ -20,117 +20,112 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None):
- """Adds a new presentation item to a surface style
+def add_surface_style(file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None) -> None:
+ """Adds a new presentation item to a surface style
- A surface style can have multiple different types of presentation items
- assigned to it:
+ A surface style can have multiple different types of presentation items
+ assigned to it:
- - Shading, this is the simplest item, which defines a single basic
- colour and transparency that can be used to display the object on a
- screen. It is an indicative colour of what the object would be in real
- life. It is commonly incorrectly abused to colour code systems for MEP
- equipment or object types for structural steel. If you just want to
- give something a colour, this is what you need.
- - Rendering, this is an advanced extension of shading, which includes
- the definition of a shader for a rendering engine. You may select the
- reflectance / lighting model such as PHYSICAL, for PBR style
- rendering, or FLAT, for flat shading, or PHONG for older biased
- rendering workflows. Based on the chosen lighting model, you may then
- specify the appropriate colour maps, such as diffuse colours,
- specularity, emissive component, etc. These lighting models are fully
- compatible with glTF and X3D. This should be used if your model is
- prepared to be rendered by a rendering engine which is compatible with
- glTF / X3D shader descriptions. If you are doing archviz or 3D
- rendering, this is what you need.
- - Textures, this is a special type of Rendering presentation item that
- uses image textures instead of single colours. Textures may be either
- mapped using a bounding box stretch mapping, or with UV coordinates
- for mesh-like geometry.
- - Lighting, this is used to define photometrically accurate colour
- parameters used in lighting simulation. If you are a simulationist,
- this is what you need.
- - Reflectance, this is a special type of Lighting presentation item
- which includes some lesser used photometric properties, typically
- required for advanced materials like glazing.
- - External, this is for any other surface style defined using an
- external URI. This is relevant if you are using a third-party non-glTF
- compatible shader definition such as for Cycles, Renderman, V-Ray,
- etc, or a complex lighting simulation definition, such as for
- Radiance.
+ - Shading, this is the simplest item, which defines a single basic
+ colour and transparency that can be used to display the object on a
+ screen. It is an indicative colour of what the object would be in real
+ life. It is commonly incorrectly abused to colour code systems for MEP
+ equipment or object types for structural steel. If you just want to
+ give something a colour, this is what you need.
+ - Rendering, this is an advanced extension of shading, which includes
+ the definition of a shader for a rendering engine. You may select the
+ reflectance / lighting model such as PHYSICAL, for PBR style
+ rendering, or FLAT, for flat shading, or PHONG for older biased
+ rendering workflows. Based on the chosen lighting model, you may then
+ specify the appropriate colour maps, such as diffuse colours,
+ specularity, emissive component, etc. These lighting models are fully
+ compatible with glTF and X3D. This should be used if your model is
+ prepared to be rendered by a rendering engine which is compatible with
+ glTF / X3D shader descriptions. If you are doing archviz or 3D
+ rendering, this is what you need.
+ - Textures, this is a special type of Rendering presentation item that
+ uses image textures instead of single colours. Textures may be either
+ mapped using a bounding box stretch mapping, or with UV coordinates
+ for mesh-like geometry.
+ - Lighting, this is used to define photometrically accurate colour
+ parameters used in lighting simulation. If you are a simulationist,
+ this is what you need.
+ - Reflectance, this is a special type of Lighting presentation item
+ which includes some lesser used photometric properties, typically
+ required for advanced materials like glazing.
+ - External, this is for any other surface style defined using an
+ external URI. This is relevant if you are using a third-party non-glTF
+ compatible shader definition such as for Cycles, Renderman, V-Ray,
+ etc, or a complex lighting simulation definition, such as for
+ Radiance.
- Shading is sufficient for the majority of basic models.
+ Shading is sufficient for the majority of basic models.
- The attributes you specify will depend on the type of presentation item
- you are adding. An example is shown below, but for full details please
- refer to the IFC documentation.
+ The attributes you specify will depend on the type of presentation item
+ you are adding. An example is shown below, but for full details please
+ refer to the IFC documentation.
- :param style: The IfcSurfaceStyle you want to add to presentation item
- to. See ifcopenshell.api.style.add_style.
- :type style: ifcopenshell.entity_instance
- :param ifc_class: Choose from IfcSurfaceStyleShading,
- IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
- IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
- IfcExternallyDefinedSurfaceStyle.
- :type ifc_class: str
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: The newly created presentation item based on the provided
- ifc_class.
- :rtype: ifcopenshell.entity_instance
+ :param style: The IfcSurfaceStyle you want to add to presentation item
+ to. See ifcopenshell.api.style.add_style.
+ :type style: ifcopenshell.entity_instance
+ :param ifc_class: Choose from IfcSurfaceStyleShading,
+ IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
+ IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
+ IfcExternallyDefinedSurfaceStyle.
+ :type ifc_class: str
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: The newly created presentation item based on the provided
+ ifc_class.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
- # Create a simple shading colour and transparency.
- ifcopenshell.api.run("style.add_surface_style", model,
- style=style, ifc_class="IfcSurfaceStyleShading", attributes={
- "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
- "Transparency": 0., # 0 is opaque, 1 is transparent
- })
+ # Create a simple shading colour and transparency.
+ ifcopenshell.api.run("style.add_surface_style", model,
+ style=style, ifc_class="IfcSurfaceStyleShading", attributes={
+ "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
+ "Transparency": 0., # 0 is opaque, 1 is transparent
+ })
- # Alternatively, create a rendering style.
- ifcopenshell.api.run("style.add_surface_style", model,
- style=style, ifc_class="IfcSurfaceStyleRendering", attributes={
- # A surface colour and transparency is still supplied for
- # viewport display only. This will supersede the shading
- # presentation item.
- "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
- "Transparency": 0., # 0 is opaque, 1 is transparent
+ # Alternatively, create a rendering style.
+ ifcopenshell.api.run("style.add_surface_style", model,
+ style=style, ifc_class="IfcSurfaceStyleRendering", attributes={
+ # A surface colour and transparency is still supplied for
+ # viewport display only. This will supersede the shading
+ # presentation item.
+ "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
+ "Transparency": 0., # 0 is opaque, 1 is transparent
- # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
- # model. In IFC4X3, you may choose PHYSICAL directly.
- "ReflectanceMethod": "NOTDEFINED",
+ # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
+ # model. In IFC4X3, you may choose PHYSICAL directly.
+ "ReflectanceMethod": "NOTDEFINED",
- # For PBR shading, you may specify these parameters:
- "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
- "SpecularColour": 0.1, # Metallic factor
- "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
- })
- """
- self.file = file
- self.settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}}
+ # For PBR shading, you may specify these parameters:
+ "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
+ "SpecularColour": 0.1, # Metallic factor
+ "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
+ })
+ """
+ settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}}
- def execute(self):
- style_item = self.file.create_entity(self.settings["ifc_class"])
- ifcopenshell.api.run(
- "style.edit_surface_style", self.file, style=style_item, attributes=self.settings["attributes"]
- )
- styles = list(self.settings["style"].Styles or [])
+ style_item = file.create_entity(settings["ifc_class"])
+ ifcopenshell.api.run("style.edit_surface_style", file, style=style_item, attributes=settings["attributes"])
+ styles = list(settings["style"].Styles or [])
- select_class = self.settings["ifc_class"]
- if select_class == "IfcSurfaceStyleRendering":
- select_class = "IfcSurfaceStyleShading"
- duplicate_items = [s for s in styles if s.is_a(select_class)]
- for duplicate_item in duplicate_items:
- ifcopenshell.api.run("style.remove_surface_style", self.file, style=duplicate_item)
+ select_class = settings["ifc_class"]
+ if select_class == "IfcSurfaceStyleRendering":
+ select_class = "IfcSurfaceStyleShading"
+ duplicate_items = [s for s in styles if s.is_a(select_class)]
+ for duplicate_item in duplicate_items:
+ ifcopenshell.api.run("style.remove_surface_style", file, style=duplicate_item)
- styles = list(self.settings["style"].Styles or [])
- styles.append(style_item)
- self.settings["style"].Styles = styles
- return style_item
+ styles = list(settings["style"].Styles or [])
+ styles.append(style_item)
+ settings["style"].Styles = styles
+ return style_item
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py
index 88aabfe801..9b6fbda053 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py
@@ -20,39 +20,42 @@ import ifcopenshell
import ifcopenshell.api
+def add_surface_textures(file, material=None, uv_maps=None, textures=None) -> None:
+ """Add surface texture based on a Blender material definition or texture data.
+
+ :param material: The Blender material definition with a node tree that
+ is compatible with glTF. See one of the valid combinations here:
+ https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html
+ :type material: bpy.types.Material
+ :param uv_maps: A list of IfcIndexedTextureMap for any
+ IfcTessellatedFaceSets that the representation has, obtained from
+ the HasTextures attribute.
+ :type uv_maps: list[ifcopenshell.entity_instance]
+ :param textures: A list of dictionaries containing:
+
+ 1. Attributes to create IfcImageTexture.
+ 2. One additional parameter `uv_mode` to map IfcImageTexture to correct
+ IfcTextureCoordinate type.
+
+ Possible `uv_mode` values:
+
+ * `UV` - use IfcTextureCoordinate from `uv_maps` parameter;
+ * `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV
+ based on geometry);
+ * `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV
+ based on camera position)
+ :type textures: list[dict]
+ :return: A list of IfcImageTexture
+ :rtype: list[ifcopenshell.entity_instance]
+ """
+ usecase = Usecase()
+ # TODO: This usecase currently depends on Blender's data model
+ usecase.file = file
+ usecase.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, material=None, uv_maps=None, textures=None):
- """Add surface texture based on a Blender material definition or texture data.
-
- :param material: The Blender material definition with a node tree that
- is compatible with glTF. See one of the valid combinations here:
- https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html
- :type material: bpy.types.Material
- :param uv_maps: A list of IfcIndexedTextureMap for any
- IfcTessellatedFaceSets that the representation has, obtained from
- the HasTextures attribute.
- :type uv_maps: list[ifcopenshell.entity_instance]
- :param textures: A list of dictionaries containing:
-
- 1. Attributes to create IfcImageTexture.
- 2. One additional parameter `uv_mode` to map IfcImageTexture to correct
- IfcTextureCoordinate type.
-
- Possible `uv_mode` values:
-
- * `UV` - use IfcTextureCoordinate from `uv_maps` parameter;
- * `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV
- based on geometry);
- * `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV
- based on camera position)
- :type textures: list[dict]
- :return: A list of IfcImageTexture
- :rtype: list[ifcopenshell.entity_instance]
- """
- # TODO: This usecase currently depends on Blender's data model
- self.file = file
- self.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []}
-
def execute(self):
if self.file.schema == "IFC2X3":
# TODO: research how compatible IFC2X3 and IFC4 textures are
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py
index 06d3a6339a..630a842bf6 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py
@@ -21,91 +21,96 @@ import ifcopenshell.api
import ifcopenshell.util.element
+def assign_material_style(
+ file, material=None, style=None, context=None, should_use_presentation_style_assignment=False
+) -> None:
+ """Assigns a style to a material
+
+ A style may either be assigned directly to an object's representation,
+ or to a material which is then associated with the object. If both
+ exist, then the style assigned directly to the object's representation
+ takes precedence. It is recommended to use materials and assign styles
+ to materials. This API function provides that capability.
+
+ :param material: The IfcMaterial which you want to assign the style to.
+ :type material: ifcopenshell.entity_instance
+ :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
+ you want to assign to the material. This will then be applied to all
+ objects that have that material.
+ :type style: ifcopenshell.entity_instance
+ :param context: The IfcGeometricRepresentationSubContext at which this
+ style should be used. Typically this is the Model BODY context.
+ :type context: ifcopenshell.entity_instance
+ :param should_use_presentation_style_assignment: This is a technical
+ detail to accomodate a bug in Revit. This should always be left as
+ the default of False, unless you are finding that colours aren't
+ showing up in Revit. In that case, set it to True, but keep in mind
+ that this is no longer a valid IFC. Blame Autodesk.
+ :type should_use_presentation_style_assignment: bool
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # A model context is needed to store 3D geometry
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+
+ # Specifically, we want to store body geometry
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+
+ # Let's create a new wall. The wall does not have any geometry yet.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # Let's use the "3D Body" representation we created earlier to add a
+ # new wall-like body geometry, 5 meters long, 3 meters high, and
+ # 200mm thick
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+
+ # Assign our new body geometry back to our wall
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
+
+ # Place our wall at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+
+ # Let's prepare a concrete material. Note that our concrete material
+ # does not have any colours (styles) at this point.
+ concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
+
+ # Assign our concrete material to our wall
+ ifcopenshell.api.run("material.assign_material", model,
+ products=[wall], type="IfcMaterial", material=concrete)
+
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
+
+ # Create a simple grey shading colour and transparency.
+ ifcopenshell.api.run("style.add_surface_style", model,
+ style=style, ifc_class="IfcSurfaceStyleShading", attributes={
+ "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
+ "Transparency": 0., # 0 is opaque, 1 is transparent
+ })
+
+ # Now any element (like our wall) with a concrete material will have
+ # a grey colour applied.
+ ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "material": material,
+ "style": style,
+ "context": context,
+ "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, material=None, style=None, context=None, should_use_presentation_style_assignment=False):
- """Assigns a style to a material
-
- A style may either be assigned directly to an object's representation,
- or to a material which is then associated with the object. If both
- exist, then the style assigned directly to the object's representation
- takes precedence. It is recommended to use materials and assign styles
- to materials. This API function provides that capability.
-
- :param material: The IfcMaterial which you want to assign the style to.
- :type material: ifcopenshell.entity_instance
- :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
- you want to assign to the material. This will then be applied to all
- objects that have that material.
- :type style: ifcopenshell.entity_instance
- :param context: The IfcGeometricRepresentationSubContext at which this
- style should be used. Typically this is the Model BODY context.
- :type context: ifcopenshell.entity_instance
- :param should_use_presentation_style_assignment: This is a technical
- detail to accomodate a bug in Revit. This should always be left as
- the default of False, unless you are finding that colours aren't
- showing up in Revit. In that case, set it to True, but keep in mind
- that this is no longer a valid IFC. Blame Autodesk.
- :type should_use_presentation_style_assignment: bool
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # A model context is needed to store 3D geometry
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
-
- # Specifically, we want to store body geometry
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
-
- # Let's create a new wall. The wall does not have any geometry yet.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # Let's use the "3D Body" representation we created earlier to add a
- # new wall-like body geometry, 5 meters long, 3 meters high, and
- # 200mm thick
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
-
- # Assign our new body geometry back to our wall
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
-
- # Place our wall at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
-
- # Let's prepare a concrete material. Note that our concrete material
- # does not have any colours (styles) at this point.
- concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
-
- # Assign our concrete material to our wall
- ifcopenshell.api.run("material.assign_material", model,
- products=[wall], type="IfcMaterial", material=concrete)
-
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
-
- # Create a simple grey shading colour and transparency.
- ifcopenshell.api.run("style.add_surface_style", model,
- style=style, ifc_class="IfcSurfaceStyleShading", attributes={
- "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
- "Transparency": 0., # 0 is opaque, 1 is transparent
- })
-
- # Now any element (like our wall) with a concrete material will have
- # a grey colour applied.
- ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body)
- """
- self.file = file
- self.settings = {
- "material": material,
- "style": style,
- "context": context,
- "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
- }
-
def execute(self):
self.style = self.settings["style"]
if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py
index c6236c8a4a..e2f5daf766 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py
@@ -17,98 +17,100 @@
# along with IfcOpenShell. If not, see .
+def assign_representation_styles(
+ file,
+ shape_representation=None,
+ styles=None,
+ replace_previous_same_type_style=True,
+ should_use_presentation_style_assignment=False,
+) -> None:
+ """Assigns a style directly to an object representation
+
+ A style may either be assigned directly to an object's representation,
+ or to a material which is then associated with the object. If both
+ exist, then the style assigned directly to the object's representation
+ takes precedence. It is recommended to use materials and assign styles
+ to materials. However, sometimes you may want to assign colours directly
+ to the object representation as an override. This API function provides
+ that capability.
+
+ If you want to assign styles to a material instead (recommended), then
+ please see ifcopenshell.api.style.assign_material_style.
+
+ :param shape_representation: The IfcShapeRepresentation of the object
+ that you want to assign styles to. This implicitly defines the
+ context at which the styles should be used.
+ :type shape_representation: ifcopenshell.entity_instance
+ :param styles: A list of presentation styles, typically IfcSurfaceStyle.
+ The number of items in the list should correlate with the number of
+ items in the shape_representation's Items attribute. If you have
+ more items than styles, the last style is used.
+ :type styles: list[ifcopenshell.entity_instance]
+ :param replace_previous_same_type_style: Remove previously assigned styles
+ of the same type as currently assign style`. Defaults to `True`.
+ :type replace_previous_same_type_style: bool
+ :param should_use_presentation_style_assignment: This is a technical
+ detail to accomodate a bug in Revit. This should always be left as
+ the default of False, unless you are finding that colours aren't
+ showing up in Revit. In that case, set it to True, but keep in mind
+ that this is no longer a valid IFC. Blame Autodesk.
+ :type should_use_presentation_style_assignment: bool
+ :return: List of created IfcStyledItems
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # A model context is needed to store 3D geometry
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+
+ # Specifically, we want to store body geometry
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+
+ # Let's create a new wall. The wall does not have any geometry yet.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # Let's use the "3D Body" representation we created earlier to add a
+ # new wall-like body geometry, 5 meters long, 3 meters high, and
+ # 200mm thick
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+
+ # Assign our new body geometry back to our wall
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
+
+ # Place our wall at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
+
+ # Create a simple grey shading colour and transparency.
+ ifcopenshell.api.run("style.add_surface_style", model,
+ style=style, ifc_class="IfcSurfaceStyleShading", attributes={
+ "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
+ "Transparency": 0., # 0 is opaque, 1 is transparent
+ })
+
+ # Now specifically our wall only will be coloured grey.
+ ifcopenshell.api.run("style.assign_representation_styles", model,
+ shape_representation=representation, styles=[style])
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "shape_representation": shape_representation,
+ "styles": styles or [],
+ "replace_previous_same_type_style": replace_previous_same_type_style,
+ "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file,
- shape_representation=None,
- styles=None,
- replace_previous_same_type_style=True,
- should_use_presentation_style_assignment=False,
- ):
- """Assigns a style directly to an object representation
-
- A style may either be assigned directly to an object's representation,
- or to a material which is then associated with the object. If both
- exist, then the style assigned directly to the object's representation
- takes precedence. It is recommended to use materials and assign styles
- to materials. However, sometimes you may want to assign colours directly
- to the object representation as an override. This API function provides
- that capability.
-
- If you want to assign styles to a material instead (recommended), then
- please see ifcopenshell.api.style.assign_material_style.
-
- :param shape_representation: The IfcShapeRepresentation of the object
- that you want to assign styles to. This implicitly defines the
- context at which the styles should be used.
- :type shape_representation: ifcopenshell.entity_instance
- :param styles: A list of presentation styles, typically IfcSurfaceStyle.
- The number of items in the list should correlate with the number of
- items in the shape_representation's Items attribute. If you have
- more items than styles, the last style is used.
- :type styles: list[ifcopenshell.entity_instance]
- :param replace_previous_same_type_style: Remove previously assigned styles
- of the same type as currently assign style`. Defaults to `True`.
- :type replace_previous_same_type_style: bool
- :param should_use_presentation_style_assignment: This is a technical
- detail to accomodate a bug in Revit. This should always be left as
- the default of False, unless you are finding that colours aren't
- showing up in Revit. In that case, set it to True, but keep in mind
- that this is no longer a valid IFC. Blame Autodesk.
- :type should_use_presentation_style_assignment: bool
- :return: List of created IfcStyledItems
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # A model context is needed to store 3D geometry
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
-
- # Specifically, we want to store body geometry
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
-
- # Let's create a new wall. The wall does not have any geometry yet.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # Let's use the "3D Body" representation we created earlier to add a
- # new wall-like body geometry, 5 meters long, 3 meters high, and
- # 200mm thick
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
-
- # Assign our new body geometry back to our wall
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
-
- # Place our wall at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
-
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
-
- # Create a simple grey shading colour and transparency.
- ifcopenshell.api.run("style.add_surface_style", model,
- style=style, ifc_class="IfcSurfaceStyleShading", attributes={
- "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
- "Transparency": 0., # 0 is opaque, 1 is transparent
- })
-
- # Now specifically our wall only will be coloured grey.
- ifcopenshell.api.run("style.assign_representation_styles", model,
- shape_representation=representation, styles=[style])
- """
- self.file = file
- self.settings = {
- "shape_representation": shape_representation,
- "styles": styles or [],
- "replace_previous_same_type_style": replace_previous_same_type_style,
- "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
- }
-
def execute(self):
if not self.settings["styles"]:
return []
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py
index 877d0f89c2..268acfdc2e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, style=None, attributes=None):
- """Edits the attributes of an IfcPresentationStyle
+def edit_presentation_style(file, style=None, attributes=None) -> None:
+ """Edits the attributes of an IfcPresentationStyle
- For more information about the attributes and data types of an
- IfcPresentationStyle, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcPresentationStyle, consult the IFC documentation.
- :param style: The IfcPresentationStyle entity you want to edit
- :type style: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param style: The IfcPresentationStyle entity you want to edit
+ :type style: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
- # Change the name of the style to "Foo"
- ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"})
- """
- self.file = file
- self.settings = {"style": style, "attributes": attributes or {}}
+ # Change the name of the style to "Foo"
+ ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"})
+ """
+ settings = {"style": style, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["style"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["style"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py
index 20c1002fdf..b8c7a30be8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py
@@ -17,61 +17,64 @@
# along with IfcOpenShell. If not, see .
+def edit_surface_style(file, style=None, attributes=None) -> None:
+ """Edits the attributes of an IfcPresentationItem
+
+ For more information about the attributes and data types of an
+ IfcPresentationItem, consult the IFC documentation.
+
+ The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading,
+ IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
+ IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
+ IfcExternallyDefinedSurfaceStyle.
+
+ To represent a colour, a nested dictionary should be used. See the
+ example below.
+
+ :param style: The IfcPresentationStyle entity you want to edit
+ :type style: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
+
+ # Create a blank rendering style.
+ rendering = ifcopenshell.api.run("style.add_surface_style", model,
+ style=style, ifc_class="IfcSurfaceStyleRendering")
+
+ # Edit the attributes of the rendering style.
+ ifcopenshell.api.run("style.edit_surface_style", model,
+ style=rendering, attributes={
+ # A surface colour and transparency is still supplied for
+ # viewport display only. This will supersede the shading
+ # presentation item.
+ "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
+ "Transparency": 0., # 0 is opaque, 1 is transparent
+
+ # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
+ # model. In IFC4X3, you may choose PHYSICAL directly.
+ "ReflectanceMethod": "NOTDEFINED",
+
+ # For PBR shading, you may specify these parameters:
+ "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
+ "SpecularColour": 0.1, # Metallic factor
+ "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
+ })
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"style": style, "attributes": attributes or {}}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, style=None, attributes=None):
- """Edits the attributes of an IfcPresentationItem
-
- For more information about the attributes and data types of an
- IfcPresentationItem, consult the IFC documentation.
-
- The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading,
- IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
- IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
- IfcExternallyDefinedSurfaceStyle.
-
- To represent a colour, a nested dictionary should be used. See the
- example below.
-
- :param style: The IfcPresentationStyle entity you want to edit
- :type style: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
-
- # Create a blank rendering style.
- rendering = ifcopenshell.api.run("style.add_surface_style", model,
- style=style, ifc_class="IfcSurfaceStyleRendering")
-
- # Edit the attributes of the rendering style.
- ifcopenshell.api.run("style.edit_surface_style", model,
- style=rendering, attributes={
- # A surface colour and transparency is still supplied for
- # viewport display only. This will supersede the shading
- # presentation item.
- "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
- "Transparency": 0., # 0 is opaque, 1 is transparent
-
- # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
- # model. In IFC4X3, you may choose PHYSICAL directly.
- "ReflectanceMethod": "NOTDEFINED",
-
- # For PBR shading, you may specify these parameters:
- "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
- "SpecularColour": 0.1, # Metallic factor
- "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
- })
- """
- self.file = file
- self.settings = {"style": style, "attributes": attributes or {}}
-
def execute(self):
attributes = {}
for attribute in self.settings["style"].wrapped_data.declaration().as_entity().all_attributes():
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py
index 40692982bb..453f511f2a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py
@@ -19,30 +19,33 @@
import ifcopenshell.util.element
+def remove_style(file, style=None) -> None:
+ """Removes a presentation style
+
+ All of the presentation items of the style will also be removed.
+
+ :param style: The IfcPresentationStyle to remove.
+ :type style: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
+
+ # Not anymore!
+ ifcopenshell.api.run("style.remove_style", model, style=style)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"style": style}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, style=None):
- """Removes a presentation style
-
- All of the presentation items of the style will also be removed.
-
- :param style: The IfcPresentationStyle to remove.
- :type style: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
-
- # Not anymore!
- ifcopenshell.api.run("style.remove_style", model, style=style)
- """
- self.file = file
- self.settings = {"style": style}
-
def execute(self):
self.purge_styled_items(self.settings["style"])
for style in self.settings["style"].Styles or []:
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py
index ab061e182c..62ab7e4973 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py
@@ -17,38 +17,35 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, representation=None):
- """Removes a styled representation
+def remove_styled_representation(file, representation=None) -> None:
+ """Removes a styled representation
- Styled representations are typically associated with materials. This
- removes the representation but not the underlying styles.
+ Styled representations are typically associated with materials. This
+ removes the representation but not the underlying styles.
- :param representation: The IfcStyledRepresentation to remove.
- :type representation: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param representation: The IfcStyledRepresentation to remove.
+ :type representation: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Remove a styled representation
- ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation)
- """
- self.file = file
- self.settings = {"representation": representation}
+ # Remove a styled representation
+ ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation)
+ """
+ settings = {"representation": representation}
- def execute(self):
- for inverse in self.file.get_inverse(self.settings["representation"]):
- if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
- self.file.remove(inverse)
+ for inverse in file.get_inverse(settings["representation"]):
+ if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
+ file.remove(inverse)
- for item in self.settings["representation"].Items:
- if item.is_a("IfcStyledItem") and self.file.get_total_inverses(item) == 1:
- for style in item.Styles:
- if style.is_a("IfcPresentationStyleAssignment"):
- self.file.remove(style)
- self.file.remove(item)
+ for item in settings["representation"].Items:
+ if item.is_a("IfcStyledItem") and file.get_total_inverses(item) == 1:
+ for style in item.Styles:
+ if style.is_a("IfcPresentationStyleAssignment"):
+ file.remove(style)
+ file.remove(item)
- self.file.remove(self.settings["representation"])
+ file.remove(settings["representation"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py
index ce214dbf21..9621d5b51a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py
@@ -20,50 +20,47 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, style=None):
- """Removes a presentation item from a presentation style
+def remove_surface_style(file, style=None) -> None:
+ """Removes a presentation item from a presentation style
- :param style: The IfcPresentationItem to remove.
- :type style: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param style: The IfcPresentationItem to remove.
+ :type style: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a new surface style
- style = ifcopenshell.api.run("style.add_style", model)
+ # Create a new surface style
+ style = ifcopenshell.api.run("style.add_style", model)
- # Create a simple shading colour and transparency.
- shading = ifcopenshell.api.run("style.add_surface_style", model,
- style=style, ifc_class="IfcSurfaceStyleShading", attributes={
- "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
- "Transparency": 0., # 0 is opaque, 1 is transparent
- })
+ # Create a simple shading colour and transparency.
+ shading = ifcopenshell.api.run("style.add_surface_style", model,
+ style=style, ifc_class="IfcSurfaceStyleShading", attributes={
+ "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
+ "Transparency": 0., # 0 is opaque, 1 is transparent
+ })
- # Remove the shading item
- ifcopenshell.api.run("style.remove_surface_style", model, style=shading)
- """
- self.file = file
- self.settings = {"style": style}
+ # Remove the shading item
+ ifcopenshell.api.run("style.remove_surface_style", model, style=shading)
+ """
+ settings = {"style": style}
- def execute(self):
- to_delete = set()
- if self.settings["style"].is_a("IfcSurfaceStyleWithTextures"):
- for texture in self.settings["style"].Textures or []:
- if texture.IsMappedBy:
- for coordinate in texture.IsMappedBy:
- to_delete.add(coordinate)
- else:
- to_delete.add(texture)
+ to_delete = set()
+ if settings["style"].is_a("IfcSurfaceStyleWithTextures"):
+ for texture in settings["style"].Textures or []:
+ if texture.IsMappedBy:
+ for coordinate in texture.IsMappedBy:
+ to_delete.add(coordinate)
+ else:
+ to_delete.add(texture)
- for attribute in self.settings["style"]:
- if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id():
- to_delete.add(attribute)
+ for attribute in settings["style"]:
+ if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id():
+ to_delete.add(attribute)
- self.file.remove(self.settings["style"])
+ file.remove(settings["style"])
- for element in to_delete:
- ifcopenshell.util.element.remove_deep2(self.file, element)
+ for element in to_delete:
+ ifcopenshell.util.element.remove_deep2(file, element)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py
index f1e2e7e85b..59b37a1935 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py
@@ -20,78 +20,75 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, material=None, style=None, context=None):
- """Unassigns a style to a material
+def unassign_material_style(file, material=None, style=None, context=None) -> None:
+ """Unassigns a style to a material
- This does the inverse of assign_material_style.
+ This does the inverse of assign_material_style.
- :param material: The IfcMaterial which you want to unassign the style from.
- :type material: ifcopenshell.entity_instance
- :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
- you want to unassign from material. This will then be applied to all
- objects that have that material.
- :type style: ifcopenshell.entity_instance
- :param context: The IfcGeometricRepresentationSubContext at which this
- style should be unassigned. Typically this is the Model BODY context.
- :type context: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param material: The IfcMaterial which you want to unassign the style from.
+ :type material: ifcopenshell.entity_instance
+ :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
+ you want to unassign from material. This will then be applied to all
+ objects that have that material.
+ :type style: ifcopenshell.entity_instance
+ :param context: The IfcGeometricRepresentationSubContext at which this
+ style should be unassigned. Typically this is the Model BODY context.
+ :type context: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body)
- """
- self.file = file
- self.settings = {
- "material": material,
- "style": style,
- "context": context,
- }
+ ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body)
+ """
+ settings = {
+ "material": material,
+ "style": style,
+ "context": context,
+ }
- def execute(self):
- for definition in self.settings["material"].HasRepresentation:
- for representation in definition.Representations:
- if not representation.is_a("IfcStyledRepresentation"):
- continue
- if representation.ContextOfItems != self.settings["context"]:
- continue
- for item in representation.Items:
- if not item.is_a("IfcStyledItem"):
- continue
- styles = [s for s in item.Styles if s != self.settings["style"]]
- if not styles:
- self.file.remove(item)
- elif len(styles) != len(item.Styles):
- item.Styles = styles
- if not representation.Items:
- self.file.remove(representation)
- if not definition.Representations:
- self.file.remove(definition)
-
- # handle material constituents and shape aspects
- material_constituents_names = []
- for inverse in self.file.get_inverse(self.settings["material"]):
- if inverse.is_a("IfcMaterialConstituent") and inverse.Name:
- material_constituents_names.append(inverse.Name)
- if not material_constituents_names:
- return
-
- elements = ifcopenshell.util.element.get_elements_by_material(self.file, self.settings["material"])
- shape_aspects = []
- for element in elements:
- shape_aspects += ifcopenshell.util.element.get_shape_aspects(element)
-
- for shape_aspect in shape_aspects:
- if shape_aspect.Name not in material_constituents_names:
+ for definition in settings["material"].HasRepresentation:
+ for representation in definition.Representations:
+ if not representation.is_a("IfcStyledRepresentation"):
continue
+ if representation.ContextOfItems != settings["context"]:
+ continue
+ for item in representation.Items:
+ if not item.is_a("IfcStyledItem"):
+ continue
+ styles = [s for s in item.Styles if s != settings["style"]]
+ if not styles:
+ file.remove(item)
+ elif len(styles) != len(item.Styles):
+ item.Styles = styles
+ if not representation.Items:
+ file.remove(representation)
+ if not definition.Representations:
+ file.remove(definition)
- for rep in shape_aspect.ShapeRepresentations:
- ifcopenshell.api.run(
- "style.unassign_representation_styles",
- self.file,
- shape_representation=rep,
- styles=[self.settings["style"]],
- )
+ # handle material constituents and shape aspects
+ material_constituents_names = []
+ for inverse in file.get_inverse(settings["material"]):
+ if inverse.is_a("IfcMaterialConstituent") and inverse.Name:
+ material_constituents_names.append(inverse.Name)
+ if not material_constituents_names:
+ return
+
+ elements = ifcopenshell.util.element.get_elements_by_material(file, settings["material"])
+ shape_aspects = []
+ for element in elements:
+ shape_aspects += ifcopenshell.util.element.get_shape_aspects(element)
+
+ for shape_aspect in shape_aspects:
+ if shape_aspect.Name not in material_constituents_names:
+ continue
+
+ for rep in shape_aspect.ShapeRepresentations:
+ ifcopenshell.api.run(
+ "style.unassign_representation_styles",
+ file,
+ shape_representation=rep,
+ styles=[settings["style"]],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py
index 83f60fe3d4..14f52e9c6e 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py
@@ -17,43 +17,48 @@
# along with IfcOpenShell. If not, see .
+def unassign_representation_styles(
+ file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False
+) -> None:
+ """Unassigns styles directly assigned to an object representation
+
+ This does the inverse of assign_representation_styles.
+
+ :param shape_representation: The IfcShapeRepresentation of the object
+ that you want to unassign styles from.
+ :type shape_representation: ifcopenshell.entity_instance
+ :param styles: A list of presentation styles, typically IfcSurfaceStyle.
+ The number of items in the list should correlate with the number of
+ items in the shape_representation's Items attribute. If you have
+ more items than styles, the last style is used.
+ :type styles: list[ifcopenshell.entity_instance]
+ :param should_use_presentation_style_assignment: This is a technical
+ detail to accomodate a bug in Revit. This should always be left as
+ the default of False, unless you are finding that colours aren't
+ showing up in Revit. In that case, set it to True, but keep in mind
+ that this is no longer a valid IFC. Blame Autodesk.
+ :type should_use_presentation_style_assignment: bool
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ ifcopenshell.api.run("style.unassign_representation_styles", model,
+ shape_representation=representation, styles=[style])
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "shape_representation": shape_representation,
+ "styles": styles or [],
+ "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False):
- """Unassigns styles directly assigned to an object representation
-
- This does the inverse of assign_representation_styles.
-
- :param shape_representation: The IfcShapeRepresentation of the object
- that you want to unassign styles from.
- :type shape_representation: ifcopenshell.entity_instance
- :param styles: A list of presentation styles, typically IfcSurfaceStyle.
- The number of items in the list should correlate with the number of
- items in the shape_representation's Items attribute. If you have
- more items than styles, the last style is used.
- :type styles: list[ifcopenshell.entity_instance]
- :param should_use_presentation_style_assignment: This is a technical
- detail to accomodate a bug in Revit. This should always be left as
- the default of False, unless you are finding that colours aren't
- showing up in Revit. In that case, set it to True, but keep in mind
- that this is no longer a valid IFC. Blame Autodesk.
- :type should_use_presentation_style_assignment: bool
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- ifcopenshell.api.run("style.unassign_representation_styles", model,
- shape_representation=representation, styles=[style])
- """
- self.file = file
- self.settings = {
- "shape_representation": shape_representation,
- "styles": styles or [],
- "should_use_presentation_style_assignment": should_use_presentation_style_assignment,
- }
-
def execute(self):
if not self.settings["styles"]:
return []
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py
index e0caddbe3c..14213ca168 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py
@@ -15,3 +15,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_port import add_port
+from .add_system import add_system
+from .assign_flow_control import assign_flow_control
+from .assign_port import assign_port
+from .assign_system import assign_system
+from .connect_port import connect_port
+from .disconnect_port import disconnect_port
+from .edit_system import edit_system
+from .remove_system import remove_system
+from .unassign_flow_control import unassign_flow_control
+from .unassign_port import unassign_port
+from .unassign_system import unassign_system
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py
index f792ecc2fa..a3664cffbb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py
@@ -20,44 +20,41 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, element=None):
- """Adds a new distribution port to an element
+def add_port(file, element=None) -> None:
+ """Adds a new distribution port to an element
- A distribution port represents a connection point on an element, where
- a distribution element may be connected to another distribution element.
- For example, a duct segment will typically have two ports, one at either
- end, because you can attach another segment or fitting to either end of
- the duct segment.
+ A distribution port represents a connection point on an element, where
+ a distribution element may be connected to another distribution element.
+ For example, a duct segment will typically have two ports, one at either
+ end, because you can attach another segment or fitting to either end of
+ the duct segment.
- This will both add a distribution port and automatically assign it to a
- distribution element.
+ This will both add a distribution port and automatically assign it to a
+ distribution element.
- :param element: The IfcDistributionElement you want to add a
- distribution port to.
- :type element: ifcopenshell.entity_instance
- :return: The newly created IfcDistributionPort
- :rtype: ifcopenshell.entity_instance
+ :param element: The IfcDistributionElement you want to add a
+ distribution port to.
+ :type element: ifcopenshell.entity_instance
+ :return: The newly created IfcDistributionPort
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a duct
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+ # Create a duct
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
- # Create 2 ports, one for either end.
- port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
- port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
- """
- self.file = file
- self.settings = {
- "element": element,
- }
+ # Create 2 ports, one for either end.
+ port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ """
+ settings = {
+ "element": element,
+ }
- def execute(self):
- port = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcDistributionPort")
- if self.settings["element"]:
- ifcopenshell.api.run("system.assign_port", self.file, element=self.settings["element"], port=port)
- return port
+ port = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcDistributionPort")
+ if settings["element"]:
+ ifcopenshell.api.run("system.assign_port", file, element=settings["element"], port=port)
+ return port
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py
index 26c8cfe8fc..75027ee55a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py
@@ -20,45 +20,42 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem"):
- """Add a new distribution system
+def add_system(file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem") -> ifcopenshell.entity_instance:
+ """Add a new distribution system
- A distribution system is a group of distribution elements, like ducts,
- pipes, pumps, filters, fans, and so on that distribute a medium (air,
- liquid, or electricity) throughout a facility. Systems may be
- hierarchical, with larger systems composed of smaller subsystems.
+ A distribution system is a group of distribution elements, like ducts,
+ pipes, pumps, filters, fans, and so on that distribute a medium (air,
+ liquid, or electricity) throughout a facility. Systems may be
+ hierarchical, with larger systems composed of smaller subsystems.
- :param ifc_class: The type of system, chosen from IfcDistributionSystem
- for mechanical, electrical, communications, plumbing, fire, or
- security systems. Alternatively you may choose IfcBuildingSystem for
- specialised building facade systems or similar. For IFC2X3, choose
- IfcSystem.
- :type ifc_class: str
- :return: The newly created IfcSystem.
- :rtype: ifcopenshell.entity_instance
+ :param ifc_class: The type of system, chosen from IfcDistributionSystem
+ for mechanical, electrical, communications, plumbing, fire, or
+ security systems. Alternatively you may choose IfcBuildingSystem for
+ specialised building facade systems or similar. For IFC2X3, choose
+ IfcSystem.
+ :type ifc_class: str
+ :return: The newly created IfcSystem.
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
- """
- self.file = file
- self.settings = {"ifc_class": ifc_class}
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
+ """
+ settings = {"ifc_class": ifc_class}
- def execute(self) -> ifcopenshell.entity_instance:
- ifc_class = self.settings["ifc_class"]
- # workaround for failing default argument in ifc2x3
- if self.file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem":
- ifc_class = "IfcSystem"
+ ifc_class = settings["ifc_class"]
+ # workaround for failing default argument in ifc2x3
+ if file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem":
+ ifc_class = "IfcSystem"
- return self.file.create_entity(
- ifc_class,
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "Name": "Unnamed",
- }
- )
+ return file.create_entity(
+ ifc_class,
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "Name": "Unnamed",
+ }
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py
index aae80ab6eb..2254a43dec 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py
@@ -20,66 +20,63 @@ import ifcopenshell
import ifcopenshell.api
-class Usecase:
- def __init__(self, file, relating_flow_element=None, related_flow_control=None):
- """Assigns to the flow element control element that either sense or control
- some aspect of the flow element.
+def assign_flow_control(file, relating_flow_element=None, related_flow_control=None) -> None:
+ """Assigns to the flow element control element that either sense or control
+ some aspect of the flow element.
- Note that control can be assigned only to the one flow element.
+ Note that control can be assigned only to the one flow element.
- :param related_flow_control: IfcDistributionControlElement
- which may be used to impart control on the flow element
- :type related_flow_control: ifcopenshell.entity_instance
- :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed
- :type relating_flow_element: ifcopenshell.entity_instance
- :return: Matching or newly created IfcRelFlowControlElements. If control
- is already assigned to some other element method will return None.
- :rtype: ifcopenshell.entity_instance, None
+ :param related_flow_control: IfcDistributionControlElement
+ which may be used to impart control on the flow element
+ :type related_flow_control: ifcopenshell.entity_instance
+ :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed
+ :type relating_flow_element: ifcopenshell.entity_instance
+ :return: Matching or newly created IfcRelFlowControlElements. If control
+ is already assigned to some other element method will return None.
+ :rtype: ifcopenshell.entity_instance, None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- flow_element = model.createIfcFlowSegment()
- flow_control = model.createIfcController()
- relation = ifcopenshell.api.run(
- "system.assign_flow_control", model,
- related_flow_control=flow_control, relating_flow_element=flow_element
- )
- """
- self.file = file
- self.settings = {
- "relating_flow_element": relating_flow_element,
- "related_flow_control": related_flow_control,
- }
+ flow_element = model.createIfcFlowSegment()
+ flow_control = model.createIfcController()
+ relation = ifcopenshell.api.run(
+ "system.assign_flow_control", model,
+ related_flow_control=flow_control, relating_flow_element=flow_element
+ )
+ """
+ settings = {
+ "relating_flow_element": relating_flow_element,
+ "related_flow_control": related_flow_control,
+ }
- def execute(self):
- if self.settings["related_flow_control"].AssignedToFlowElement:
- # only 1 control per 1 flow element is possible
- assignment = self.settings["related_flow_control"].AssignedToFlowElement[0]
- if assignment.RelatingFlowElement == self.settings["relating_flow_element"]:
- return assignment
- # return None if this control is already assigned to another flow element
- return
+ if settings["related_flow_control"].AssignedToFlowElement:
+ # only 1 control per 1 flow element is possible
+ assignment = settings["related_flow_control"].AssignedToFlowElement[0]
+ if assignment.RelatingFlowElement == settings["relating_flow_element"]:
+ return assignment
+ # return None if this control is already assigned to another flow element
+ return
- if self.settings["relating_flow_element"].HasControlElements:
- assignment = self.settings["relating_flow_element"].HasControlElements[0]
- if self.settings["related_flow_control"] in assignment.RelatedControlElements:
- return assignment
-
- related_flow_controls = set(assignment.RelatedControlElements)
- related_flow_controls.add(self.settings["related_flow_control"])
- assignment.RelatedControlElements = list(related_flow_controls)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment})
+ if settings["relating_flow_element"].HasControlElements:
+ assignment = settings["relating_flow_element"].HasControlElements[0]
+ if settings["related_flow_control"] in assignment.RelatedControlElements:
return assignment
- assignment = self.file.create_entity(
- "IfcRelFlowControlElements",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatedControlElements": [self.settings["related_flow_control"]],
- "RelatingFlowElement": self.settings["relating_flow_element"],
- },
- )
+ related_flow_controls = set(assignment.RelatedControlElements)
+ related_flow_controls.add(settings["related_flow_control"])
+ assignment.RelatedControlElements = list(related_flow_controls)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": assignment})
return assignment
+
+ assignment = file.create_entity(
+ "IfcRelFlowControlElements",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatedControlElements": [settings["related_flow_control"]],
+ "RelatingFlowElement": settings["relating_flow_element"],
+ },
+ )
+ return assignment
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py
index 728a935395..c424f8eb4b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py
@@ -21,46 +21,49 @@ import ifcopenshell.api
import ifcopenshell.util.placement
+def assign_port(file, element=None, port=None) -> None:
+ """Assigns a port to an element
+
+ If you have an orphaned port, you may assign it to a distribution
+ element using this function. Ports should typically not be orphaned, but
+ it may be useful when patching up models.
+
+ :param element: The IfcDistributionElement to assign the port to.
+ :type element: ifcopenshell.entity_instance
+ :param port: The IfcDistributionPort you want to assign.
+ :type port: ifcopenshell.entity_instance
+ :return: The IfcRelNests relationship, or the
+ IfcRelConnectsPortToElement for IFC2X3.
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # Create a duct
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+
+ # Create 2 ports, one for either end.
+ port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
+
+ # Unassign one port for some weird reason.
+ ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1)
+
+ # Reassign it back
+ ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "element": element,
+ "port": port,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, element=None, port=None):
- """Assigns a port to an element
-
- If you have an orphaned port, you may assign it to a distribution
- element using this function. Ports should typically not be orphaned, but
- it may be useful when patching up models.
-
- :param element: The IfcDistributionElement to assign the port to.
- :type element: ifcopenshell.entity_instance
- :param port: The IfcDistributionPort you want to assign.
- :type port: ifcopenshell.entity_instance
- :return: The IfcRelNests relationship, or the
- IfcRelConnectsPortToElement for IFC2X3.
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # Create a duct
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
-
- # Create 2 ports, one for either end.
- port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
- port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
-
- # Unassign one port for some weird reason.
- ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1)
-
- # Reassign it back
- ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1)
- """
- self.file = file
- self.settings = {
- "element": element,
- "port": port,
- }
-
def execute(self):
if self.file.schema == "IFC2X3":
return self.execute_ifc2x3()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py
index 20f5a8519f..e0b19c5165 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py
@@ -21,51 +21,47 @@ import ifcopenshell.api
import ifcopenshell.util.system
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- system: ifcopenshell.entity_instance,
- ):
- """Assigns distribution elements to a system
+def assign_system(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ system: ifcopenshell.entity_instance,
+) -> None:
+ """Assigns distribution elements to a system
- Note that it is not necessary to assign distribution ports to a system.
+ Note that it is not necessary to assign distribution ports to a system.
- :param products: The list of IfcDistributionElements to assign to the system.
- :type products: list[ifcopenshell.entity_instance]
- :param system: The IfcSystem you want to assign the element to.
- :type system: ifcopenshell.entity_instance
- :return: The IfcRelAssignsToGroup relationship
- or `None` if `products` was empty list.
- :rtype: [ifcopenshell.entity_instance, None]
+ :param products: The list of IfcDistributionElements to assign to the system.
+ :type products: list[ifcopenshell.entity_instance]
+ :param system: The IfcSystem you want to assign the element to.
+ :type system: ifcopenshell.entity_instance
+ :return: The IfcRelAssignsToGroup relationship
+ or `None` if `products` was empty list.
+ :rtype: [ifcopenshell.entity_instance, None]
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
- # Create a duct
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+ # Create a duct
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
- # This duct is part of the system
- ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
- """
- self.file = file
- self.settings = {
- "products": products,
- "system": system,
- }
+ # This duct is part of the system
+ ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
+ """
+ settings = {
+ "products": products,
+ "system": system,
+ }
- def execute(self):
- system = self.settings["system"]
- products = self.settings["products"]
+ system = settings["system"]
+ products = settings["products"]
- if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products):
- raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}")
+ if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products):
+ raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}")
- rel = ifcopenshell.api.run("group.assign_group", self.file, products=products, group=system)
- return rel
+ rel = ifcopenshell.api.run("group.assign_group", file, products=products, group=system)
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py
index 7d23dde1a7..f5773c5a21 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py
@@ -21,84 +21,87 @@ import ifcopenshell.api
import ifcopenshell.util.element
+def connect_port(file, port1=None, port2=None, direction="NOTDEFINED", element=None) -> None:
+ """Connects two ports together
+
+ A distribution element (e.g. a duct) may be connected to another
+ distribution element (e.g. a fitting) by connecting a port at one of the
+ duct to a port at the same end of the fitting.
+
+ Ports may only have one connection, so you cannot have multiple things
+ connected to the same port. Nor can you have incompatible port
+ connections, such as an electrical port connected to an airflow port.
+
+ Port connectivity may be explicit or implicit. Explicit connections are
+ where the port connectivity is described for every single distribution
+ element in detail. For example, a duct segment would have port
+ connections to a duct fitting, which would have port connections to
+ another duct segment, all the way from a fan to an air terminal exactly
+ as constructed on site. Implicit connections only consider the key
+ distribution control elements (e.g. the fan and the terminal) and ignore
+ all of the details of the duct segments and fittings in between.
+ Generally, explicit connectivity is preferred for later detailed design,
+ and implicit connectivity is preferred for early phase design.
+
+ :param port1: The port of the first distribution element to connect.
+ :type port1: ifcopenshell.entity_instance
+ :param port2: The port of the second distribution element to connect.
+ :type port2: ifcopenshell.entity_instance
+ :param direction: The directionality of distribution flow through the
+ port connection. NOTDEFINED means that the direction has not yet
+ been determined. This is useful during preliminary system design.
+ SOURCE means that the flow is from the first element to the second
+ element. SINK means that the flow is from the second element to the
+ first element. SOURCEANDSINK means that flow is bi-directional
+ between the first and second element. SOURCEANDSINK is a relatively
+ rare scenario.
+ :type direction: str
+ :param element: Optionally set an element through which the port
+ connectivity is made, such as a segment or fitting. This is only to
+ be used for implicit port connectivity where the segments and
+ fittings are less important.
+ :type element: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
+
+ # Create a duct and a 90 degree bend fitting
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+ fitting = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctFitting", predefined_type="BEND")
+
+ # The duct and fitting is part of the system
+ ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
+ ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system)
+
+ # Create 2 ports, one for either end of both the duct and fitting.
+ duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting)
+ fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting)
+
+ # Connect the duct and fitting together. At this point, we have not
+ # yet determined the direction of the flow, so we leave direction as
+ # NOTDEFINED.
+ ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "port1": port1,
+ "port2": port2,
+ "direction": direction,
+ "element": element,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, port1=None, port2=None, direction="NOTDEFINED", element=None):
- """Connects two ports together
-
- A distribution element (e.g. a duct) may be connected to another
- distribution element (e.g. a fitting) by connecting a port at one of the
- duct to a port at the same end of the fitting.
-
- Ports may only have one connection, so you cannot have multiple things
- connected to the same port. Nor can you have incompatible port
- connections, such as an electrical port connected to an airflow port.
-
- Port connectivity may be explicit or implicit. Explicit connections are
- where the port connectivity is described for every single distribution
- element in detail. For example, a duct segment would have port
- connections to a duct fitting, which would have port connections to
- another duct segment, all the way from a fan to an air terminal exactly
- as constructed on site. Implicit connections only consider the key
- distribution control elements (e.g. the fan and the terminal) and ignore
- all of the details of the duct segments and fittings in between.
- Generally, explicit connectivity is preferred for later detailed design,
- and implicit connectivity is preferred for early phase design.
-
- :param port1: The port of the first distribution element to connect.
- :type port1: ifcopenshell.entity_instance
- :param port2: The port of the second distribution element to connect.
- :type port2: ifcopenshell.entity_instance
- :param direction: The directionality of distribution flow through the
- port connection. NOTDEFINED means that the direction has not yet
- been determined. This is useful during preliminary system design.
- SOURCE means that the flow is from the first element to the second
- element. SINK means that the flow is from the second element to the
- first element. SOURCEANDSINK means that flow is bi-directional
- between the first and second element. SOURCEANDSINK is a relatively
- rare scenario.
- :type direction: str
- :param element: Optionally set an element through which the port
- connectivity is made, such as a segment or fitting. This is only to
- be used for implicit port connectivity where the segments and
- fittings are less important.
- :type element: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
-
- # Create a duct and a 90 degree bend fitting
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
- fitting = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctFitting", predefined_type="BEND")
-
- # The duct and fitting is part of the system
- ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
- ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system)
-
- # Create 2 ports, one for either end of both the duct and fitting.
- duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
- duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
- fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting)
- fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting)
-
- # Connect the duct and fitting together. At this point, we have not
- # yet determined the direction of the flow, so we leave direction as
- # NOTDEFINED.
- ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1)
- """
- self.file = file
- self.settings = {
- "port1": port1,
- "port2": port2,
- "direction": direction,
- "element": element,
- }
-
def execute(self):
# Note: there are a number of ambiguities with port connectivity. We
# assume system topology is represented by a directed graph. In other
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py
index 071074e9e7..15d5890493 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py
@@ -21,63 +21,60 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, port=None):
- """Disconnects a port from any other port
+def disconnect_port(file, port=None) -> None:
+ """Disconnects a port from any other port
- A port may only be connected to one other port, so the other port is not
- needed to be specified.
+ A port may only be connected to one other port, so the other port is not
+ needed to be specified.
- :param port: The IfcDistributionPort to disconnect.
- :type port: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param port: The IfcDistributionPort to disconnect.
+ :type port: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
- # Create a duct and a 90 degree bend fitting
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
- fitting = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctFitting", predefined_type="BEND")
+ # Create a duct and a 90 degree bend fitting
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+ fitting = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctFitting", predefined_type="BEND")
- # The duct and fitting is part of the system
- ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
- ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system)
+ # The duct and fitting is part of the system
+ ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
+ ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system)
- # Create 2 ports, one for either end of both the duct and fitting.
- duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
- duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
- fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting)
- fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting)
+ # Create 2 ports, one for either end of both the duct and fitting.
+ duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting)
+ fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting)
- # Connect the duct and fitting together. At this point, we have not
- # yet determined the direction of the flow, so we leave direction as
- # NOTDEFINED.
- ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1)
+ # Connect the duct and fitting together. At this point, we have not
+ # yet determined the direction of the flow, so we leave direction as
+ # NOTDEFINED.
+ ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1)
- # Disconnect the port. note we could've equally disconnected
- # fitting_port1 instead of duct_port2
- ifcopenshell.api.run("system.disconnect_port", model, port=duct_port2)
- """
- self.file = file
- self.settings = {
- "port": port,
- }
+ # Disconnect the port. note we could've equally disconnected
+ # fitting_port1 instead of duct_port2
+ ifcopenshell.api.run("system.disconnect_port", model, port=duct_port2)
+ """
+ settings = {
+ "port": port,
+ }
- def execute(self):
- rels = self.settings["port"].ConnectedTo or ()
- rels += self.settings["port"].ConnectedFrom or ()
+ rels = settings["port"].ConnectedTo or ()
+ rels += settings["port"].ConnectedFrom or ()
- for rel in rels:
- rel.RelatingPort.FlowDirection = None
- rel.RelatedPort.FlowDirection = None
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for rel in rels:
+ rel.RelatingPort.FlowDirection = None
+ rel.RelatedPort.FlowDirection = None
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py
index 315c04ccd5..83fd250ddd 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py
@@ -17,34 +17,31 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, system=None, attributes=None):
- """Edits the attributes of an IfcSystem
+def edit_system(file, system=None, attributes=None) -> None:
+ """Edits the attributes of an IfcSystem
- For more information about the attributes and data types of an
- IfcSystem, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcSystem, consult the IFC documentation.
- :param system: The IfcSystem entity you want to edit
- :type system: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param system: The IfcSystem entity you want to edit
+ :type system: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
- # Change the name of the system to "HW" for Hot Water
- ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"})
- """
+ # Change the name of the system to "HW" for Hot Water
+ ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"})
+ """
- self.file = file
- self.settings = {"system": system, "attributes": attributes or {}}
+ settings = {"system": system, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["system"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["system"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py
index f331a5f3e3..a81a06127f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py
@@ -21,55 +21,52 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, system=None):
- """Removes a distribution system
+def remove_system(file, system=None) -> None:
+ """Removes a distribution system
- All the distribution elements within the system are retained.
+ All the distribution elements within the system are retained.
- :param system: The IfcSystem to remove.
- :type system: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param system: The IfcSystem to remove.
+ :type system: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
- # Delete it.
- ifcopenshell.api.run("system.remove_system", model, system=system)
- """
- self.file = file
- self.settings = {"system": system}
+ # Delete it.
+ ifcopenshell.api.run("system.remove_system", model, system=system)
+ """
+ settings = {"system": system}
- def execute(self):
- for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["system"])]:
- try:
- inverse = self.file.by_id(inverse_id)
- except:
- continue
- if inverse.is_a("IfcRelDefinesByProperties"):
- ifcopenshell.api.run(
- "pset.remove_pset",
- self.file,
- product=self.settings["system"],
- pset=inverse.RelatingPropertyDefinition,
- )
- elif inverse.is_a("IfcRelAssignsToGroup"):
- if inverse.RelatingGroup == self.settings["system"]:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- elif len(inverse.RelatedObjects) == 1:
- history = inverse.OwnerHistory
- self.file.remove(inverse)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- history = self.settings["system"].OwnerHistory
- self.file.remove(self.settings["system"])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ for inverse_id in [i.id() for i in file.get_inverse(settings["system"])]:
+ try:
+ inverse = file.by_id(inverse_id)
+ except:
+ continue
+ if inverse.is_a("IfcRelDefinesByProperties"):
+ ifcopenshell.api.run(
+ "pset.remove_pset",
+ file,
+ product=settings["system"],
+ pset=inverse.RelatingPropertyDefinition,
+ )
+ elif inverse.is_a("IfcRelAssignsToGroup"):
+ if inverse.RelatingGroup == settings["system"]:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ elif len(inverse.RelatedObjects) == 1:
+ history = inverse.OwnerHistory
+ file.remove(inverse)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ history = settings["system"].OwnerHistory
+ file.remove(settings["system"])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py
index 04eda27f83..feba961f1b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py
@@ -21,57 +21,54 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, relating_flow_element=None, related_flow_control=None):
- """Unassigns flow control element from the flow element.
+def unassign_flow_control(file, relating_flow_element=None, related_flow_control=None) -> None:
+ """Unassigns flow control element from the flow element.
- :param related_flow_control: IfcDistributionControlElement controling the
- flow element
- :type related_flow_control: ifcopenshell.entity_instance
- :param relating_flow_element: The IfcDistributionFlowElement that is being controlled
- :type relating_flow_element: ifcopenshell.entity_instance
- :return: If the control still is related to other objects, the
- IfcRelFlowControlElements is returned, otherwise None.
- :rtype: ifcopenshell.entity_instance, None
+ :param related_flow_control: IfcDistributionControlElement controling the
+ flow element
+ :type related_flow_control: ifcopenshell.entity_instance
+ :param relating_flow_element: The IfcDistributionFlowElement that is being controlled
+ :type relating_flow_element: ifcopenshell.entity_instance
+ :return: If the control still is related to other objects, the
+ IfcRelFlowControlElements is returned, otherwise None.
+ :rtype: ifcopenshell.entity_instance, None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # assign control to the flow element
- flow_element = self.file.createIfcFlowSegment()
- flow_control = self.file.createIfcController()
- relation = ifcopenshell.api.run(
- "system.assign_flow_control", self.file,
- relating_control=flow_control, related_object=flow_element
- )
+ # assign control to the flow element
+ flow_element = file.createIfcFlowSegment()
+ flow_control = file.createIfcController()
+ relation = ifcopenshell.api.run(
+ "system.assign_flow_control", file,
+ relating_control=flow_control, related_object=flow_element
+ )
- # und unassign it
- ifcopenshell.api.run("system.unassign_flow_control", self.file,
- relating_control=flow_control, related_object=flow_element
- )
- """
+ # und unassign it
+ ifcopenshell.api.run("system.unassign_flow_control", file,
+ relating_control=flow_control, related_object=flow_element
+ )
+ """
- self.file = file
- self.settings = {
- "relating_flow_element": relating_flow_element,
- "related_flow_control": related_flow_control,
- }
+ settings = {
+ "relating_flow_element": relating_flow_element,
+ "related_flow_control": related_flow_control,
+ }
- def execute(self):
- if not self.settings["related_flow_control"].AssignedToFlowElement:
- return
- assignment = self.settings["related_flow_control"].AssignedToFlowElement[0]
- if assignment.RelatingFlowElement != self.settings["relating_flow_element"]:
- return
- if len(assignment.RelatedControlElements) == 1:
- history = assignment.OwnerHistory
- self.file.remove(assignment)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- return
- related_flow_controls = list(assignment.RelatedControlElements)
- related_flow_controls.remove(self.settings["related_flow_control"])
- assignment.RelatedControlElements = related_flow_controls
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment})
- return assignment
+ if not settings["related_flow_control"].AssignedToFlowElement:
+ return
+ assignment = settings["related_flow_control"].AssignedToFlowElement[0]
+ if assignment.RelatingFlowElement != settings["relating_flow_element"]:
+ return
+ if len(assignment.RelatedControlElements) == 1:
+ history = assignment.OwnerHistory
+ file.remove(assignment)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ return
+ related_flow_controls = list(assignment.RelatedControlElements)
+ related_flow_controls.remove(settings["related_flow_control"])
+ assignment.RelatedControlElements = related_flow_controls
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": assignment})
+ return assignment
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py
index e9d82722aa..678c086140 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py
@@ -20,42 +20,45 @@ import ifcopenshell
import ifcopenshell.api
+def unassign_port(file, element=None, port=None) -> None:
+ """Unassigns a port to an element
+
+ Ports are typically always assigned to a distribution element, but in
+ some edge cases you may want to unassign the port to create an orphaned
+ port for cleaning or patchin purposes.
+
+ :param element: The IfcDistributionElement to unassign the port from.
+ :type element: ifcopenshell.entity_instance
+ :param port: The IfcDistributionPort you want to unassign.
+ :type port: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
+
+ Example:
+
+ .. code:: python
+
+ # Create a duct
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+
+ # Create 2 ports, one for either end.
+ port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
+ port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
+
+ # Unassign one port for some weird reason.
+ ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "element": element,
+ "port": port,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(self, file, element=None, port=None):
- """Unassigns a port to an element
-
- Ports are typically always assigned to a distribution element, but in
- some edge cases you may want to unassign the port to create an orphaned
- port for cleaning or patchin purposes.
-
- :param element: The IfcDistributionElement to unassign the port from.
- :type element: ifcopenshell.entity_instance
- :param port: The IfcDistributionPort you want to unassign.
- :type port: ifcopenshell.entity_instance
- :return: None
- :rtype: None
-
- Example:
-
- .. code:: python
-
- # Create a duct
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
-
- # Create 2 ports, one for either end.
- port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
- port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
-
- # Unassign one port for some weird reason.
- ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1)
- """
- self.file = file
- self.settings = {
- "element": element,
- "port": port,
- }
-
def execute(self):
if self.file.schema == "IFC2X3":
return self.execute_ifc2x3()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py
index fbc3dd854b..7bb82aebb7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py
@@ -21,46 +21,40 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- products: list[ifcopenshell.entity_instance],
- system: ifcopenshell.entity_instance,
- ):
- """Unassigns list of products from a system
+def unassign_system(
+ file: ifcopenshell.file,
+ products: list[ifcopenshell.entity_instance],
+ system: ifcopenshell.entity_instance,
+) -> None:
+ """Unassigns list of products from a system
- :param products: The list of IfcDistributionElements to unassign from the system.
- :type products: list[ifcopenshell.entity_instance]
- :param system: The IfcSystem you want to unassign the element from.
- :type system: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param products: The list of IfcDistributionElements to unassign from the system.
+ :type products: list[ifcopenshell.entity_instance]
+ :param system: The IfcSystem you want to unassign the element from.
+ :type system: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A completely empty distribution system
- system = ifcopenshell.api.run("system.add_system", model)
+ # A completely empty distribution system
+ system = ifcopenshell.api.run("system.add_system", model)
- # Create a duct
- duct = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
+ # Create a duct
+ duct = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT")
- # This duct is part of the system
- ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
+ # This duct is part of the system
+ ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system)
- # Not anymore!
- ifcopenshell.api.run("system.unassign_system", model, products=[duct], system=system)
- """
- self.file = file
- self.settings = {
- "products": products,
- "system": system,
- }
+ # Not anymore!
+ ifcopenshell.api.run("system.unassign_system", model, products=[duct], system=system)
+ """
+ settings = {
+ "products": products,
+ "system": system,
+ }
- def execute(self):
- ifcopenshell.api.run(
- "group.unassign_group", self.file, products=self.settings["products"], group=self.settings["system"]
- )
+ ifcopenshell.api.run("group.unassign_group", file, products=settings["products"], group=settings["system"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py
index e0caddbe3c..dddd90a49f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py
@@ -15,3 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .assign_type import assign_type
+from .get_related_objects import get_related_objects
+from .map_type_representations import map_type_representations
+from .unassign_type import unassign_type
diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py
index b8d7cf357a..9d30d6083a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py
@@ -22,166 +22,168 @@ import ifcopenshell.util.element
from typing import Union, Iterable
+def assign_type(
+ file: ifcopenshell.file,
+ related_objects: list[ifcopenshell.entity_instance],
+ relating_type: ifcopenshell.entity_instance,
+ should_map_representations=True,
+) -> Union[ifcopenshell.entity_instance, None]:
+ """Assigns a type to occurrences of an object
+
+ IFC supports the concept of occurrences and types. An occurrence is an
+ actual physical product in the real world: like a wall, a chair, a door,
+ a column, a pump, and so on.
+
+ Most occurrences have a corresponding type. A type describes either a
+ common shape and set of properties of a particular model of equipment,
+ or a construction typology. An occurrence may only have zero or one
+ type.
+
+ For example, architects would typically have a door schedule for
+ individual occurrences of doors and a door types schedule for a handful
+ of door types, described by the door hardware, frame, and panel. Other
+ examples might be window types or wall types. Structural engineers would
+ have a list of column types, beam types, slab types, etc, such as a 400
+ diameter column, a 500 diameter column, and so on. Services consultant
+ might nominate a particular type of sprinkler which have many
+ occurrences, or light fixture types, and so on.
+
+ Types are critical as they communicate to the procurement team what
+ types of equipment and products need to be procured. The individual
+ occurrences of that type tell them how many to procure. Types are also
+ critical in construction as they indicate succinctly how to manufacture
+ or construct something. For example, a wall type is enough information
+ for a builder to understand the build up and construction of a wall.
+ Types are used to help break down cost plans, or isolate portions of an
+ assembly process for construction scheduling. Types are also used in
+ facility maintenance, as occurrences sharing the same type can be
+ repaired in the same way or by replacing the same parts.
+
+ An occurrence of a type inherits all the properties and materials of the
+ type. For example, a 2HR fire rated wall type implies that all
+ wall occurrences of that wall type will also be 2HR fire rated.
+
+ A type may or may not have a geometric representation. If a type does
+ not have any representation, then the occurrences are free to have any
+ representation of their own. However, if a type has a representation,
+ all occurrences must have the same representation. For example, if a
+ light fixture downlight type has a representation of a cylinder, then
+ all occurrences must have exactly the same cylinder as its
+ representation. If you change the cylinder's shape of the type, then all
+ occurrence representations will also change.
+
+ If a type does not have any geometric representation, they may have a
+ parametric material representation. This may be either a parametric
+ layered material or parametric cross-sectional profile material. If this
+ is the case, the occurrence must be constructed out of the parametric
+ material. For example, if a wall type uses a list of parametric layers
+ indicating a thickness of 13mm plasterboard and 90mm stud, then the
+ thickness of every wall occurrence representation must be 103mm. The
+ length of each wall, however, may vary. Similarly, if a beam type has a
+ parametric profile material of an I-beam, then all beam occurrences must
+ also be this I-beam shape, though the length may vary.
+
+ It is highly recommended for every occurrence to have a type. There are
+ some exceptions to the rule, such as in heritage architecture or
+ as-built or dilapidation models, where existing conditions are
+ ambiguous, unknown or are so bespoke as to have no logical type.
+
+ :param related_objects: The IfcElement occurrences.
+ :type related_objects: list[ifcopenshell.entity_instance]
+ :param relating_type: The IfcElementType type.
+ :type relating_type: ifcopenshell.entity_instance
+ :param should_map_representations: If a type has a representation map,
+ IFC requires all occurrences to map those representations. Some IFC
+ vendors might disobey this, or you might want to handle it
+ yourusecase. In this scenario, you may set this to False.
+ This also enabled adding material usages mapping.
+ :type should_map_representations: bool
+ :return: The IfcRelDefinesByType relationship
+ or `None` if `related_objects` was empty list.
+ :rtype: Union[ifcopenshell.entity_instance, None]
+
+ Example:
+
+ .. code:: python
+
+ # A furniture type. This would correlate to a particular model in a
+ # manufacturer's catalogue. Like an Ikea sofa :)
+ furniture_type = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcFurnitureType", name="FUN01")
+
+ # An individual occurrence of a that sofa.
+ furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+
+ # Assign the furniture to the furniture type. If the furniture_type
+ # had a representation, the furniture occurrence will also now have
+ # the exact same representation. This is highly efficient as you
+ # don't need to define the representation for every occurrence.
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type)
+
+ # Let's imagine a parametric material layer set
+ wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
+
+ # First, let's create a material set. This will later be assigned
+ # to our wall type element.
+ material_set = ifcopenshell.api.run("material.add_material_set", model,
+ name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
+
+ # Let's create a few materials, it's important to also give them
+ # categories. This makes it easy for model recipients to do things
+ # like "show me everything made out of aluminium / concrete / steel
+ # / glass / etc". The IFC specification states a list of categories
+ # you can use.
+ gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
+ steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
+
+ # Now let's use those materials as three layers in our set, such
+ # that the steel studs are sandwiched by the gypsum. Let's imagine
+ # we're setting the layer thickness in millimeters.
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .092})
+ layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
+ ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013})
+
+ # Great! Let's assign our material set to our wall type.
+ ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set)
+
+ # Now, let's create a wall.
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+
+ # The wall is a WAL01 wall type.
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type)
+
+ # A bit of preparation, let's create some geometric contexts since
+ # we want to create some geometry for our wall.
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+
+ # Notice how our thickness of 0.118 must equal .013 + .092 + .013 from our type
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.118)
+
+ # Assign our new body geometry back to our wall
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
+
+ # Place our wall at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {
+ "related_objects": related_objects,
+ "relating_type": relating_type,
+ "should_map_representations": should_map_representations,
+ }
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- related_objects: list[ifcopenshell.entity_instance],
- relating_type: ifcopenshell.entity_instance,
- should_map_representations=True,
- ):
- """Assigns a type to occurrences of an object
-
- IFC supports the concept of occurrences and types. An occurrence is an
- actual physical product in the real world: like a wall, a chair, a door,
- a column, a pump, and so on.
-
- Most occurrences have a corresponding type. A type describes either a
- common shape and set of properties of a particular model of equipment,
- or a construction typology. An occurrence may only have zero or one
- type.
-
- For example, architects would typically have a door schedule for
- individual occurrences of doors and a door types schedule for a handful
- of door types, described by the door hardware, frame, and panel. Other
- examples might be window types or wall types. Structural engineers would
- have a list of column types, beam types, slab types, etc, such as a 400
- diameter column, a 500 diameter column, and so on. Services consultant
- might nominate a particular type of sprinkler which have many
- occurrences, or light fixture types, and so on.
-
- Types are critical as they communicate to the procurement team what
- types of equipment and products need to be procured. The individual
- occurrences of that type tell them how many to procure. Types are also
- critical in construction as they indicate succinctly how to manufacture
- or construct something. For example, a wall type is enough information
- for a builder to understand the build up and construction of a wall.
- Types are used to help break down cost plans, or isolate portions of an
- assembly process for construction scheduling. Types are also used in
- facility maintenance, as occurrences sharing the same type can be
- repaired in the same way or by replacing the same parts.
-
- An occurrence of a type inherits all the properties and materials of the
- type. For example, a 2HR fire rated wall type implies that all
- wall occurrences of that wall type will also be 2HR fire rated.
-
- A type may or may not have a geometric representation. If a type does
- not have any representation, then the occurrences are free to have any
- representation of their own. However, if a type has a representation,
- all occurrences must have the same representation. For example, if a
- light fixture downlight type has a representation of a cylinder, then
- all occurrences must have exactly the same cylinder as its
- representation. If you change the cylinder's shape of the type, then all
- occurrence representations will also change.
-
- If a type does not have any geometric representation, they may have a
- parametric material representation. This may be either a parametric
- layered material or parametric cross-sectional profile material. If this
- is the case, the occurrence must be constructed out of the parametric
- material. For example, if a wall type uses a list of parametric layers
- indicating a thickness of 13mm plasterboard and 90mm stud, then the
- thickness of every wall occurrence representation must be 103mm. The
- length of each wall, however, may vary. Similarly, if a beam type has a
- parametric profile material of an I-beam, then all beam occurrences must
- also be this I-beam shape, though the length may vary.
-
- It is highly recommended for every occurrence to have a type. There are
- some exceptions to the rule, such as in heritage architecture or
- as-built or dilapidation models, where existing conditions are
- ambiguous, unknown or are so bespoke as to have no logical type.
-
- :param related_objects: The IfcElement occurrences.
- :type related_objects: list[ifcopenshell.entity_instance]
- :param relating_type: The IfcElementType type.
- :type relating_type: ifcopenshell.entity_instance
- :param should_map_representations: If a type has a representation map,
- IFC requires all occurrences to map those representations. Some IFC
- vendors might disobey this, or you might want to handle it
- yourself. In this scenario, you may set this to False.
- This also enabled adding material usages mapping.
- :type should_map_representations: bool
- :return: The IfcRelDefinesByType relationship
- or `None` if `related_objects` was empty list.
- :rtype: Union[ifcopenshell.entity_instance, None]
-
- Example:
-
- .. code:: python
-
- # A furniture type. This would correlate to a particular model in a
- # manufacturer's catalogue. Like an Ikea sofa :)
- furniture_type = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcFurnitureType", name="FUN01")
-
- # An individual occurrence of a that sofa.
- furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
-
- # Assign the furniture to the furniture type. If the furniture_type
- # had a representation, the furniture occurrence will also now have
- # the exact same representation. This is highly efficient as you
- # don't need to define the representation for every occurrence.
- ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type)
-
- # Let's imagine a parametric material layer set
- wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01")
-
- # First, let's create a material set. This will later be assigned
- # to our wall type element.
- material_set = ifcopenshell.api.run("material.add_material_set", model,
- name="GYP-ST-GYP", set_type="IfcMaterialLayerSet")
-
- # Let's create a few materials, it's important to also give them
- # categories. This makes it easy for model recipients to do things
- # like "show me everything made out of aluminium / concrete / steel
- # / glass / etc". The IFC specification states a list of categories
- # you can use.
- gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum")
- steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel")
-
- # Now let's use those materials as three layers in our set, such
- # that the steel studs are sandwiched by the gypsum. Let's imagine
- # we're setting the layer thickness in millimeters.
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .092})
- layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum)
- ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013})
-
- # Great! Let's assign our material set to our wall type.
- ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set)
-
- # Now, let's create a wall.
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
-
- # The wall is a WAL01 wall type.
- ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type)
-
- # A bit of preparation, let's create some geometric contexts since
- # we want to create some geometry for our wall.
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
-
- # Notice how our thickness of 0.118 must equal .013 + .092 + .013 from our type
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.118)
-
- # Assign our new body geometry back to our wall
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
-
- # Place our wall at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- """
- self.file = file
- self.settings = {
- "related_objects": related_objects,
- "relating_type": relating_type,
- "should_map_representations": should_map_representations,
- }
-
- def execute(self) -> Union[ifcopenshell.entity_instance, None]:
+ def execute(self):
if not self.settings["related_objects"]:
return
diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py
index 0a05de4118..3610f27af5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py
@@ -19,43 +19,40 @@
import ifcopenshell
-class Usecase:
- def __init__(self, file, related_object=None, relating_type=None):
- """Gets all the related occurrences of a type
+def get_related_objects(file, related_object=None, relating_type=None) -> None:
+ """Gets all the related occurrences of a type
- Do not use this function. It will be removed. Use
- ifcopenshell.util.element.get_type or
- ifcopenshell.util.element.get_types instead.
+ Do not use this function. It will be removed. Use
+ ifcopenshell.util.element.get_type or
+ ifcopenshell.util.element.get_types instead.
- :param related_object: The IfcElement occurrence.
- :type related_object: ifcopenshell.entity_instance
- :param relating_type: The IfcElementType type.
- :type relating_type: ifcopenshell.entity_instance
- :return: A list of occurrences of the type.
- :rtype: list[ifcopenshell.entity_instance]
- """
- self.file = file
- self.settings = {
- "related_object": related_object,
- "relating_type": relating_type,
- }
+ :param related_object: The IfcElement occurrence.
+ :type related_object: ifcopenshell.entity_instance
+ :param relating_type: The IfcElementType type.
+ :type relating_type: ifcopenshell.entity_instance
+ :return: A list of occurrences of the type.
+ :rtype: list[ifcopenshell.entity_instance]
+ """
+ settings = {
+ "related_object": related_object,
+ "relating_type": relating_type,
+ }
- def execute(self):
- if self.settings["related_object"]:
- if self.file.schema == "IFC2X3":
- is_defined_by = self.settings["related_object"].IsDefinedBy
- for rel in is_defined_by:
- if rel.is_a("IfcRelDefinesByType"):
- return set([int(o.id()) for o in rel.RelatedObjects])
- else:
- is_typed_by = self.settings["related_object"].IsTypedBy
- if is_typed_by:
- return set([int(o.id()) for o in is_typed_by[0].RelatedObjects])
- elif self.settings["relating_type"]:
- if self.file.schema == "IFC2X3":
- types = self.settings["relating_type"].ObjectTypeOf
- else:
- types = self.settings["relating_type"].Types
- if types:
- return set([int(o.id()) for o in types[0].RelatedObjects])
- return set()
+ if settings["related_object"]:
+ if file.schema == "IFC2X3":
+ is_defined_by = settings["related_object"].IsDefinedBy
+ for rel in is_defined_by:
+ if rel.is_a("IfcRelDefinesByType"):
+ return set([int(o.id()) for o in rel.RelatedObjects])
+ else:
+ is_typed_by = settings["related_object"].IsTypedBy
+ if is_typed_by:
+ return set([int(o.id()) for o in is_typed_by[0].RelatedObjects])
+ elif settings["relating_type"]:
+ if file.schema == "IFC2X3":
+ types = settings["relating_type"].ObjectTypeOf
+ else:
+ types = settings["relating_type"].Types
+ if types:
+ return set([int(o.id()) for o in types[0].RelatedObjects])
+ return set()
diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py
index 064fb148bb..59c1655c6f 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py
@@ -21,99 +21,93 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- related_object: ifcopenshell.entity_instance,
- relating_type: ifcopenshell.entity_instance,
- ):
- """Ensures that all occurrences has the same representation as the type
+def map_type_representations(
+ file: ifcopenshell.file,
+ related_object: ifcopenshell.entity_instance,
+ relating_type: ifcopenshell.entity_instance,
+) -> None:
+ """Ensures that all occurrences has the same representation as the type
- If a type has a representation, all occurrences must have the same
- representation. If the type's representation changes, this function may
- be used to ensure consistency of the occurrence's representations.
+ If a type has a representation, all occurrences must have the same
+ representation. If the type's representation changes, this function may
+ be used to ensure consistency of the occurrence's representations.
- :param related_object: The IfcElement occurrence.
- :type related_object: ifcopenshell.entity_instance
- :param relating_type: The IfcElementType type.
- :type relating_type: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param related_object: The IfcElement occurrence.
+ :type related_object: ifcopenshell.entity_instance
+ :param relating_type: The IfcElementType type.
+ :type relating_type: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A furniture type. This would correlate to a particular model in a
- # manufacturer's catalogue. Like an Ikea sofa :)
- furniture_type = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcFurnitureType", name="FUN01")
+ # A furniture type. This would correlate to a particular model in a
+ # manufacturer's catalogue. Like an Ikea sofa :)
+ furniture_type = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcFurnitureType", name="FUN01")
- # An individual occurrence of a that sofa.
- furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ # An individual occurrence of a that sofa.
+ furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- # Place our furniture at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=furniture)
+ # Place our furniture at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=furniture)
- # Assign the furniture to the furniture type. Right now, the
- # furniture type has no representation, so the furniture may also
- # have no representation, or any arbitrary representation that may
- # vary from occurrence to occurrence.
- ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type)
+ # Assign the furniture to the furniture type. Right now, the
+ # furniture type has no representation, so the furniture may also
+ # have no representation, or any arbitrary representation that may
+ # vary from occurrence to occurrence.
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type)
- # A bit of preparation, let's create some geometric contexts since
- # we want to create some geometry for our furniture type.
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+ # A bit of preparation, let's create some geometric contexts since
+ # we want to create some geometry for our furniture type.
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
- # Let's create a mesh representation of an arbitrary 2m cube.
- representation = ifcopenshell.api.run("geometry.add_sverchok_representation", model, context=body,
- vertices=[[(-1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 0.0), (-1.0, 1.0, 2.0),
- (1.0, -1.0, 0.0), (1.0, -1.0, 2.0), (1.0, 1.0, 0.0), (1.0, 1.0, 2.0)]],
- faces=[[[0, 1, 3, 2], [2, 3, 7, 6], [6, 7, 5, 4], [4, 5, 1, 0], [2, 6, 4, 0], [7, 3, 1, 5]]])
+ # Let's create a mesh representation of an arbitrary 2m cube.
+ representation = ifcopenshell.api.run("geometry.add_sverchok_representation", model, context=body,
+ vertices=[[(-1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 0.0), (-1.0, 1.0, 2.0),
+ (1.0, -1.0, 0.0), (1.0, -1.0, 2.0), (1.0, 1.0, 0.0), (1.0, 1.0, 2.0)]],
+ faces=[[[0, 1, 3, 2], [2, 3, 7, 6], [6, 7, 5, 4], [4, 5, 1, 0], [2, 6, 4, 0], [7, 3, 1, 5]]])
- # Assign our new body geometry back to our furniture type. In this
- # case, since we use the API, all occurrences automatically get the
- # representation mapped, so there is nothing more we need to do.
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=furniture_type, representation=representation)
+ # Assign our new body geometry back to our furniture type. In this
+ # case, since we use the API, all occurrences automatically get the
+ # representation mapped, so there is nothing more we need to do.
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=furniture_type, representation=representation)
- # However, if you were doing some sort of manual IFC patching, like
- # assigning furniture_type.RepresentationMaps directly, then you
- # might make this call:
- # ifcopenshell.api.run("type.map_type_representations", model,
- # related_object=furniture, relating_type=furniture_type)
- """
- self.file = file
- self.settings = {
- "related_object": related_object,
- "relating_type": relating_type,
- }
+ # However, if you were doing some sort of manual IFC patching, like
+ # assigning furniture_type.RepresentationMaps directly, then you
+ # might make this call:
+ # ifcopenshell.api.run("type.map_type_representations", model,
+ # related_object=furniture, relating_type=furniture_type)
+ """
+ settings = {
+ "related_object": related_object,
+ "relating_type": relating_type,
+ }
- def execute(self) -> None:
- if not self.settings["relating_type"].RepresentationMaps:
- return
- representations = []
- if self.settings["related_object"].Representation:
- representations = self.settings["related_object"].Representation.Representations
- for representation in representations:
- ifcopenshell.api.run(
- "geometry.unassign_representation",
- self.file,
- product=self.settings["related_object"],
- representation=representation,
- )
- ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation})
- for representation_map in self.settings["relating_type"].RepresentationMaps:
- representation = representation_map.MappedRepresentation
- mapped_representation = ifcopenshell.api.run(
- "geometry.map_representation", self.file, representation=representation
- )
- ifcopenshell.api.run(
- "geometry.assign_representation",
- self.file,
- product=self.settings["related_object"],
- representation=mapped_representation,
- )
+ if not settings["relating_type"].RepresentationMaps:
+ return
+ representations = []
+ if settings["related_object"].Representation:
+ representations = settings["related_object"].Representation.Representations
+ for representation in representations:
+ ifcopenshell.api.run(
+ "geometry.unassign_representation",
+ file,
+ product=settings["related_object"],
+ representation=representation,
+ )
+ ifcopenshell.api.run("geometry.remove_representation", file, **{"representation": representation})
+ for representation_map in settings["relating_type"].RepresentationMaps:
+ representation = representation_map.MappedRepresentation
+ mapped_representation = ifcopenshell.api.run("geometry.map_representation", file, representation=representation)
+ ifcopenshell.api.run(
+ "geometry.assign_representation",
+ file,
+ product=settings["related_object"],
+ representation=mapped_representation,
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py
index a629100477..cbc41a7785 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py
@@ -21,58 +21,55 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]):
- """Unassigns a type from occurrences
+def unassign_type(file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]) -> None:
+ """Unassigns a type from occurrences
- Note that unassigning a type doesn't automatically remove mapped representations
- and material usages associated with the previously assigned type.
+ Note that unassigning a type doesn't automatically remove mapped representations
+ and material usages associated with the previously assigned type.
- :param related_objects: List of IfcElement occurrences.
- :type related_objects: list[ifcopenshell.entity_instance]
- :return: None
- :rtype: None
+ :param related_objects: List of IfcElement occurrences.
+ :type related_objects: list[ifcopenshell.entity_instance]
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A furniture type. This would correlate to a particular model in a
- # manufacturer's catalogue. Like an Ikea sofa :)
- furniture_type = ifcopenshell.api.run("root.create_entity", model,
- ifc_class="IfcFurnitureType", name="FUN01")
+ # A furniture type. This would correlate to a particular model in a
+ # manufacturer's catalogue. Like an Ikea sofa :)
+ furniture_type = ifcopenshell.api.run("root.create_entity", model,
+ ifc_class="IfcFurnitureType", name="FUN01")
- # An individual occurrence of a that sofa.
- furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
+ # An individual occurrence of a that sofa.
+ furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture")
- # Assign the furniture to the furniture type.
- ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type)
+ # Assign the furniture to the furniture type.
+ ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type)
- # Change our mind. Maybe it's a different type?
- ifcopenshell.api.run("type.unassign_type", model, related_objects=[furniture])
- """
- self.file = file
- self.settings = {"related_objects": related_objects}
+ # Change our mind. Maybe it's a different type?
+ ifcopenshell.api.run("type.unassign_type", model, related_objects=[furniture])
+ """
+ settings = {"related_objects": related_objects}
- def execute(self) -> None:
- related_objects = set(self.settings["related_objects"])
+ related_objects = set(settings["related_objects"])
- if self.file.schema == "IFC2X3":
- rels = set(
- rel
- for object in related_objects
- if (rel := next((rel for rel in object.IsDefinedBy if rel.is_a("IfcRelDefinesByType")), None))
- )
+ if file.schema == "IFC2X3":
+ rels = set(
+ rel
+ for object in related_objects
+ if (rel := next((rel for rel in object.IsDefinedBy if rel.is_a("IfcRelDefinesByType")), None))
+ )
+ else:
+ rels = set(rel for object in related_objects if (rel := next((rel for rel in object.IsTypedBy), None)))
+
+ for rel in rels:
+ related_objects = set(rel.RelatedObjects) - related_objects
+ if related_objects:
+ rel.RelatedObjects = list(related_objects)
+ ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel})
else:
- rels = set(rel for object in related_objects if (rel := next((rel for rel in object.IsTypedBy), None)))
-
- for rel in rels:
- related_objects = set(rel.RelatedObjects) - related_objects
- if related_objects:
- rel.RelatedObjects = list(related_objects)
- ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
- else:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py
index e0caddbe3c..3813724dd7 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py
@@ -15,3 +15,14 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_context_dependent_unit import add_context_dependent_unit
+from .add_conversion_based_unit import add_conversion_based_unit
+from .add_monetary_unit import add_monetary_unit
+from .add_si_unit import add_si_unit
+from .assign_unit import assign_unit
+from .edit_derived_unit import edit_derived_unit
+from .edit_monetary_unit import edit_monetary_unit
+from .edit_named_unit import edit_named_unit
+from .remove_unit import remove_unit
+from .unassign_unit import unassign_unit
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py
index a0d705a94b..5de43a505a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py
@@ -17,48 +17,45 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, unit_type="USERDEFINED", name="THINGAMAJIG", dimensions=None):
- """Add a new arbitrary unit that can only be interpreted in a project specific context
+def add_context_dependent_unit(file, unit_type="USERDEFINED", name="THINGAMAJIG", dimensions=None) -> None:
+ """Add a new arbitrary unit that can only be interpreted in a project specific context
- Occasionally the construction industry uses arbitrary units to quantify
- objects, like "pairs" of door hardware, "palettes" or "boxes" of fixings
- or equipment.
+ Occasionally the construction industry uses arbitrary units to quantify
+ objects, like "pairs" of door hardware, "palettes" or "boxes" of fixings
+ or equipment.
- :param unit_type: Typically should be left as USERDEFINED, unless for
- some bizarre reason you are redefining something you could use a
- sensible normal unit for. In that case, firstly stop whatever you're
- doing and have a hard think about your life, and then if life really
- is going that badly for you, check out the IFC docs for IfcUnitEnum.
- :type unit_type: str
- :param name: Give your unit a name. X what? X bananas?
- :type name: str
- :param dimensions: Units typically measure one of 7 fundamental physical
- dimensions: length, mass, time, electric current, temperature,
- substance amount, or luminous intensity. These are represented as a
- list of 7 integers, representing the exponents of each one of these
- dimensions. For example, a length unit is (1, 0, 0, 0, 0, 0, 0),
- where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per
- second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is
- recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0).
- :type dimensions: list[int]
- :return: The new IfcContextDependentUnit
- :rtype: ifcopenshell.entity_instance
+ :param unit_type: Typically should be left as USERDEFINED, unless for
+ some bizarre reason you are redefining something you could use a
+ sensible normal unit for. In that case, firstly stop whatever you're
+ doing and have a hard think about your life, and then if life really
+ is going that badly for you, check out the IFC docs for IfcUnitEnum.
+ :type unit_type: str
+ :param name: Give your unit a name. X what? X bananas?
+ :type name: str
+ :param dimensions: Units typically measure one of 7 fundamental physical
+ dimensions: length, mass, time, electric current, temperature,
+ substance amount, or luminous intensity. These are represented as a
+ list of 7 integers, representing the exponents of each one of these
+ dimensions. For example, a length unit is (1, 0, 0, 0, 0, 0, 0),
+ where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per
+ second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is
+ recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0).
+ :type dimensions: list[int]
+ :return: The new IfcContextDependentUnit
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Boxes of things
- ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES")
- """
- self.file = file
- self.settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions or (0, 0, 0, 0, 0, 0, 0)}
+ # Boxes of things
+ ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES")
+ """
+ settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions or (0, 0, 0, 0, 0, 0, 0)}
- def execute(self):
- return self.file.create_entity(
- "IfcContextDependentUnit",
- Dimensions=self.file.createIfcDimensionalExponents(*self.settings["dimensions"]),
- UnitType=self.settings["unit_type"],
- Name=self.settings["name"],
- )
+ return file.create_entity(
+ "IfcContextDependentUnit",
+ Dimensions=file.createIfcDimensionalExponents(*settings["dimensions"]),
+ UnitType=settings["unit_type"],
+ Name=settings["name"],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py
index 20b96d3298..b87011b2e1 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py
@@ -21,67 +21,66 @@ import ifcopenshell.util.unit
from typing import Optional
-class Usecase:
- def __init__(self, file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None):
- """Add a conversion based unit
+def add_conversion_based_unit(
+ file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None
+) -> ifcopenshell.entity_instance:
+ """Add a conversion based unit
- If you're in one of those countries who don't use SI units, you're
- probably simply using SI units converted into another unit. If you want
- to use _those_ units, you can create a conversion based unit with this
- function. You can choose from one of: inch, foot, yard, mile, square
- inch, square foot, square yard, acre, square mile, cubic inch, cubic
- foot, cubic yard, litre, fluid ounce UK, fluid ounce US, pint UK, pint
- US, gallon UK, gallon US, degree, ounce, pound, ton UK, ton US, lbf,
- kip, psi, ksi, minute, hour, day, btu, and fahrenheit.
+ If you're in one of those countries who don't use SI units, you're
+ probably simply using SI units converted into another unit. If you want
+ to use _those_ units, you can create a conversion based unit with this
+ function. You can choose from one of: inch, foot, yard, mile, square
+ inch, square foot, square yard, acre, square mile, cubic inch, cubic
+ foot, cubic yard, litre, fluid ounce UK, fluid ounce US, pint UK, pint
+ US, gallon UK, gallon US, degree, ounce, pound, ton UK, ton US, lbf,
+ kip, psi, ksi, minute, hour, day, btu, and fahrenheit.
- :param name: A converted name chosen from the list above.
- :type name: str
- :param conversion_offset: If you want to offset the conversion further
- by a set number, you may specify it here. For example, fahrenheit is
- 1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note
- that this is just an example and you don't actually need to specify
- that for fahrenheit as it's built into this API function. For
- advanced users only.
- :type conversion_offset: float, optional
- :return: The new IfcConversionBasedUnit or
- IfcConversionBasedUnitWithOffset
- :rtype: ifcopenshell.entity_instance
+ :param name: A converted name chosen from the list above.
+ :type name: str
+ :param conversion_offset: If you want to offset the conversion further
+ by a set number, you may specify it here. For example, fahrenheit is
+ 1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note
+ that this is just an example and you don't actually need to specify
+ that for fahrenheit as it's built into this API function. For
+ advanced users only.
+ :type conversion_offset: float, optional
+ :return: The new IfcConversionBasedUnit or
+ IfcConversionBasedUnitWithOffset
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Some common imperial measurements
- length = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="inch")
- area = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="square foot")
+ # Some common imperial measurements
+ length = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="inch")
+ area = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="square foot")
- # Make it our default units, if we are doing an imperial building
- ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
- """
- self.file = file
- self.settings = {"name": name, "conversion_offset": conversion_offset}
+ # Make it our default units, if we are doing an imperial building
+ ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
+ """
+ settings = {"name": name, "conversion_offset": conversion_offset}
- def execute(self) -> ifcopenshell.entity_instance:
- unit_type = ifcopenshell.util.unit.imperial_types.get(self.settings["name"], "USERDEFINED")
- dimensions = ifcopenshell.util.unit.named_dimensions[unit_type]
- exponents = self.file.createIfcDimensionalExponents(*dimensions)
- si_name = ifcopenshell.util.unit.si_type_names[unit_type]
- si_unit = self.file.createIfcSIUnit(UnitType=unit_type, Name=si_name)
+ unit_type = ifcopenshell.util.unit.imperial_types.get(settings["name"], "USERDEFINED")
+ dimensions = ifcopenshell.util.unit.named_dimensions[unit_type]
+ exponents = file.createIfcDimensionalExponents(*dimensions)
+ si_name = ifcopenshell.util.unit.si_type_names[unit_type]
+ si_unit = file.createIfcSIUnit(UnitType=unit_type, Name=si_name)
- conversion_real = ifcopenshell.util.unit.si_conversions.get(self.settings["name"], 1)
- value_component = self.file.create_entity("IfcReal", **{"wrappedValue": conversion_real})
- conversion_factor = self.file.createIfcMeasureWithUnit(value_component, si_unit)
+ conversion_real = ifcopenshell.util.unit.si_conversions.get(settings["name"], 1)
+ value_component = file.create_entity("IfcReal", **{"wrappedValue": conversion_real})
+ conversion_factor = file.createIfcMeasureWithUnit(value_component, si_unit)
- conversion_offset = self.settings["conversion_offset"]
- if not conversion_offset:
- conversion_offset = ifcopenshell.util.unit.si_offsets.get(self.settings["name"], 0)
+ conversion_offset = settings["conversion_offset"]
+ if not conversion_offset:
+ conversion_offset = ifcopenshell.util.unit.si_offsets.get(settings["name"], 0)
- if conversion_offset:
- return self.file.createIfcConversionBasedUnitWithOffset(
- exponents,
- unit_type,
- self.settings["name"],
- conversion_factor,
- conversion_offset,
- )
- return self.file.createIfcConversionBasedUnit(exponents, unit_type, self.settings["name"], conversion_factor)
+ if conversion_offset:
+ return file.createIfcConversionBasedUnitWithOffset(
+ exponents,
+ unit_type,
+ settings["name"],
+ conversion_factor,
+ conversion_offset,
+ )
+ return file.createIfcConversionBasedUnit(exponents, unit_type, settings["name"], conversion_factor)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py
index f15b18ab91..7e345b9a7c 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py
@@ -17,32 +17,29 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, currency="DOLLARYDOO"):
- """Add a new currency
+def add_monetary_unit(file, currency="DOLLARYDOO") -> None:
+ """Add a new currency
- Currency units are useful in cost plans to know in what currency the
- costs are calculated in. The currencies should follow ISO 4217, like
- USD, GBP, AUD, MYR, etc.
+ Currency units are useful in cost plans to know in what currency the
+ costs are calculated in. The currencies should follow ISO 4217, like
+ USD, GBP, AUD, MYR, etc.
- :param currency: The currency code
- :type currency: str
- :return: The newly created IfcMonetaryUnit
- :rtype: ifcopenshell.entity_instance
+ :param currency: The currency code
+ :type currency: str
+ :return: The newly created IfcMonetaryUnit
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # If you do all your cost plans in Zimbabwean dollars then nobody
- # knows how accurate the numbers are.
- zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL")
+ # If you do all your cost plans in Zimbabwean dollars then nobody
+ # knows how accurate the numbers are.
+ zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL")
- # Make it our default currency
- ifcopenshell.api.run("unit.assign_unit", model, units=[zwl])
- """
- self.file = file
- self.settings = {"currency": currency}
+ # Make it our default currency
+ ifcopenshell.api.run("unit.assign_unit", model, units=[zwl])
+ """
+ settings = {"currency": currency}
- def execute(self):
- return self.file.create_entity("IfcMonetaryUnit", self.settings["currency"])
+ return file.create_entity("IfcMonetaryUnit", settings["currency"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py
index 7eb8019632..67ce025dd8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py
@@ -20,48 +20,45 @@ import ifcopenshell.util.unit
from typing import Optional
-class Usecase:
- def __init__(self, file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None):
- """Add a new SI unit
+def add_si_unit(
+ file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None
+) -> ifcopenshell.entity_instance:
+ """Add a new SI unit
- The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT,
- AREAUNIT, DOSEEQUIVALENTUNIT, ELECTRICCAPACITANCEUNIT,
- ELECTRICCHARGEUNIT, ELECTRICCONDUCTANCEUNIT, ELECTRICCURRENTUNIT,
- ELECTRICRESISTANCEUNIT, ELECTRICVOLTAGEUNIT, ENERGYUNIT, FORCEUNIT,
- FREQUENCYUNIT, ILLUMINANCEUNIT, INDUCTANCEUNIT, LENGTHUNIT,
- LUMINOUSFLUXUNIT, LUMINOUSINTENSITYUNIT, MAGNETICFLUXDENSITYUNIT,
- MAGNETICFLUXUNIT, MASSUNIT, PLANEANGLEUNIT, POWERUNIT, PRESSUREUNIT,
- RADIOACTIVITYUNIT, SOLIDANGLEUNIT, THERMODYNAMICTEMPERATUREUNIT,
- TIMEUNIT, VOLUMEUNIT.
+ The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT,
+ AREAUNIT, DOSEEQUIVALENTUNIT, ELECTRICCAPACITANCEUNIT,
+ ELECTRICCHARGEUNIT, ELECTRICCONDUCTANCEUNIT, ELECTRICCURRENTUNIT,
+ ELECTRICRESISTANCEUNIT, ELECTRICVOLTAGEUNIT, ENERGYUNIT, FORCEUNIT,
+ FREQUENCYUNIT, ILLUMINANCEUNIT, INDUCTANCEUNIT, LENGTHUNIT,
+ LUMINOUSFLUXUNIT, LUMINOUSINTENSITYUNIT, MAGNETICFLUXDENSITYUNIT,
+ MAGNETICFLUXUNIT, MASSUNIT, PLANEANGLEUNIT, POWERUNIT, PRESSUREUNIT,
+ RADIOACTIVITYUNIT, SOLIDANGLEUNIT, THERMODYNAMICTEMPERATUREUNIT,
+ TIMEUNIT, VOLUMEUNIT.
- Prefixes supported are ATTO, CENTI, DECA, DECI, EXA, FEMTO, GIGA, HECTO,
- KILO, MEGA, MICRO, MILLI, NANO, PETA, PICO, TERA.
+ Prefixes supported are ATTO, CENTI, DECA, DECI, EXA, FEMTO, GIGA, HECTO,
+ KILO, MEGA, MICRO, MILLI, NANO, PETA, PICO, TERA.
- :param unit_type: A type of unit chosen from the list above. For
- example, choosing LENGTHUNIT will give you a metre.
- :type unit_type: str
- :param prefix: A prefix chosen from the list above, or None for no
- prefix.
- :type prefix: str,optional
- :return: The newly created IfcSIUnit
- :rtype: ifcopenshell.entity_instance
+ :param unit_type: A type of unit chosen from the list above. For
+ example, choosing LENGTHUNIT will give you a metre.
+ :type unit_type: str
+ :param prefix: A prefix chosen from the list above, or None for no
+ prefix.
+ :type prefix: str,optional
+ :return: The newly created IfcSIUnit
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Millimeters and square meters
- length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
- area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT")
+ # Millimeters and square meters
+ length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
+ area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT")
- # Make it our default units, if we are doing a metric building
- ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
- """
- self.file = file
- self.settings = {"unit_type": unit_type, "prefix": prefix}
+ # Make it our default units, if we are doing a metric building
+ ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
+ """
+ settings = {"unit_type": unit_type, "prefix": prefix}
- def execute(self) -> ifcopenshell.entity_instance:
- name = ifcopenshell.util.unit.si_type_names.get(self.settings["unit_type"], None)
- return self.file.create_entity(
- "IfcSIUnit", UnitType=self.settings["unit_type"], Name=name, Prefix=self.settings["prefix"]
- )
+ name = ifcopenshell.util.unit.si_type_names.get(settings["unit_type"], None)
+ return file.create_entity("IfcSIUnit", UnitType=settings["unit_type"], Name=name, Prefix=settings["prefix"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py
index 67305295bd..1e6e1a9cdf 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py
@@ -21,61 +21,63 @@ import ifcopenshell.util.unit
from typing import Optional
+def assign_unit(
+ file: ifcopenshell.file,
+ units: Optional[list[ifcopenshell.entity_instance]] = None,
+ length: Optional[dict] = None,
+ area: Optional[dict] = None,
+ volume: Optional[dict] = None,
+) -> ifcopenshell.entity_instance:
+ """Assign default project units
+
+ Whenever a unitised quantity is specified, such as a length, area,
+ voltage, pressure, etc, these project units are used by default.
+
+ It is also possible to override units for specific properties. For
+ example, generally you might want square metres for area measurements,
+ but you might want square millimeters for the measurements of the cross
+ sectional area of cables in cable trays. However, this function only
+ deals with the default project units.
+
+ :param units: A list of units to assign as project defaults. See
+ ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit,
+ and unit.add_monetary_unit for information on how to create units.
+ :type units: list[ifcopenshell.entity_instance],optional
+ :return: The IfcUnitAssignment element
+ :rtype: ifcopenshell.entity_instance
+
+ Example:
+
+ .. code:: python
+
+ # You need a project before you can assign units.
+ ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
+
+ # Millimeters and square meters
+ length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
+ area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT")
+
+ # Make it our default units, if we are doing a metric building
+ ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
+
+ # Alternatively, you may specify without any arguments to
+ # automatically create millimeters, square meters, and cubic meters
+ # as a convenience for testing purposes. Sorry imperial folks, we
+ # prioritise metric here.
+ ifcopenshell.api.run("unit.assign_unit", model)
+ """
+ usecase = Usecase()
+ usecase.file = file
+ usecase.settings = {"units": units}
+ # This is a convenience function, likely to be deprecated in the future.
+ usecase.settings["length"] = length or {"is_metric": True, "raw": "MILLIMETERS"}
+ usecase.settings["area"] = area or {"is_metric": True, "raw": "METERS"}
+ usecase.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"}
+ return usecase.execute()
+
+
class Usecase:
- def __init__(
- self,
- file: ifcopenshell.file,
- units: Optional[list[ifcopenshell.entity_instance]] = None,
- length: Optional[dict] = None,
- area: Optional[dict] = None,
- volume: Optional[dict] = None,
- ):
- """Assign default project units
-
- Whenever a unitised quantity is specified, such as a length, area,
- voltage, pressure, etc, these project units are used by default.
-
- It is also possible to override units for specific properties. For
- example, generally you might want square metres for area measurements,
- but you might want square millimeters for the measurements of the cross
- sectional area of cables in cable trays. However, this function only
- deals with the default project units.
-
- :param units: A list of units to assign as project defaults. See
- ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit,
- and unit.add_monetary_unit for information on how to create units.
- :type units: list[ifcopenshell.entity_instance],optional
- :return: The IfcUnitAssignment element
- :rtype: ifcopenshell.entity_instance
-
- Example:
-
- .. code:: python
-
- # You need a project before you can assign units.
- ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
-
- # Millimeters and square meters
- length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
- area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT")
-
- # Make it our default units, if we are doing a metric building
- ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
-
- # Alternatively, you may specify without any arguments to
- # automatically create millimeters, square meters, and cubic meters
- # as a convenience for testing purposes. Sorry imperial folks, we
- # prioritise metric here.
- ifcopenshell.api.run("unit.assign_unit", model)
- """
- self.file = file
- self.settings = {"units": units}
- # This is a convenience function, likely to be deprecated in the future.
- self.settings["length"] = length or {"is_metric": True, "raw": "MILLIMETERS"}
- self.settings["area"] = area or {"is_metric": True, "raw": "METERS"}
- self.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"}
-
- def execute(self) -> ifcopenshell.entity_instance:
+ def execute(self):
# We're going to refactor this to split unit creation and assignment
if self.settings["units"]:
units = self.settings["units"]
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py
index 636430159c..ed56b80461 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py
@@ -17,23 +17,20 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, unit=None, attributes=None):
- """Edits the attributes of an IfcDerivedUnit
+def edit_derived_unit(file, unit=None, attributes=None) -> None:
+ """Edits the attributes of an IfcDerivedUnit
- For more information about the attributes and data types of an
- IfcDerivedUnit, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcDerivedUnit, consult the IFC documentation.
- :param unit: The IfcDerivedUnit entity you want to edit
- :type unit: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
- """
- self.file = file
- self.settings = {"unit": unit, "attributes": attributes or {}}
+ :param unit: The IfcDerivedUnit entity you want to edit
+ :type unit: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
+ """
+ settings = {"unit": unit, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["unit"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["unit"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py
index aee4b89305..b4f14f328a 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py
@@ -17,34 +17,31 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, unit=None, attributes=None):
- """Edits the attributes of an IfcMonetaryUnit
+def edit_monetary_unit(file, unit=None, attributes=None) -> None:
+ """Edits the attributes of an IfcMonetaryUnit
- For more information about the attributes and data types of an
- IfcMonetaryUnit, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcMonetaryUnit, consult the IFC documentation.
- :param unit: The IfcMonetaryUnit entity you want to edit
- :type unit: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param unit: The IfcMonetaryUnit entity you want to edit
+ :type unit: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # If you do all your cost plans in Zimbabwean dollars then nobody
- # knows how accurate the numbers are.
- zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL")
+ # If you do all your cost plans in Zimbabwean dollars then nobody
+ # knows how accurate the numbers are.
+ zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL")
- # Ah who are we kidding
- ifcopenshell.api.run("unit.edit_monetary_unit", model, unit=zwl, attributes={"Currency": "USD"})
- """
- self.file = file
- self.settings = {"unit": unit, "attributes": attributes or {}}
+ # Ah who are we kidding
+ ifcopenshell.api.run("unit.edit_monetary_unit", model, unit=zwl, attributes={"Currency": "USD"})
+ """
+ settings = {"unit": unit, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- setattr(self.settings["unit"], name, value)
+ for name, value in settings["attributes"].items():
+ setattr(settings["unit"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py
index da4ff5290f..be0384aabb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py
@@ -17,44 +17,41 @@
# along with IfcOpenShell. If not, see .
-class Usecase:
- def __init__(self, file, unit=None, attributes=None):
- """Edits the attributes of an IfcNamedUnit
+def edit_named_unit(file, unit=None, attributes=None) -> None:
+ """Edits the attributes of an IfcNamedUnit
- Named units include SI units, conversion based units (imperial units),
- and context dependent units.
+ Named units include SI units, conversion based units (imperial units),
+ and context dependent units.
- For more information about the attributes and data types of an
- IfcNamedUnit, consult the IFC documentation.
+ For more information about the attributes and data types of an
+ IfcNamedUnit, consult the IFC documentation.
- :param unit: The IfcNamedUnit entity you want to edit
- :type unit: ifcopenshell.entity_instance
- :param attributes: a dictionary of attribute names and values.
- :type attributes: dict, optional
- :return: None
- :rtype: None
+ :param unit: The IfcNamedUnit entity you want to edit
+ :type unit: ifcopenshell.entity_instance
+ :param attributes: a dictionary of attribute names and values.
+ :type attributes: dict, optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Boxes of things
- unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES")
+ # Boxes of things
+ unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES")
- # Uh, crates? Boxes? Whatever.
- ifcopenshell.api.run("unit.edit_named_unit", model, unit=unit, attibutes={"Name": "CRATES"})
- """
- self.file = file
- self.settings = {"unit": unit, "attributes": attributes or {}}
+ # Uh, crates? Boxes? Whatever.
+ ifcopenshell.api.run("unit.edit_named_unit", model, unit=unit, attibutes={"Name": "CRATES"})
+ """
+ settings = {"unit": unit, "attributes": attributes or {}}
- def execute(self):
- for name, value in self.settings["attributes"].items():
- if name == "Dimensions":
- dimensions = self.settings["unit"].Dimensions
- if len(self.file.get_inverse(dimensions)) > 1:
- self.settings["unit"].Dimensions = self.file.createIfcDimensionalExponents(*value)
- else:
- for i, exponent in enumerate(value):
- dimensions[i] = exponent
- continue
- setattr(self.settings["unit"], name, value)
+ for name, value in settings["attributes"].items():
+ if name == "Dimensions":
+ dimensions = settings["unit"].Dimensions
+ if len(file.get_inverse(dimensions)) > 1:
+ settings["unit"].Dimensions = file.createIfcDimensionalExponents(*value)
+ else:
+ for i, exponent in enumerate(value):
+ dimensions[i] = exponent
+ continue
+ setattr(settings["unit"], name, value)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py
index ba9aff862b..ae2cd192cb 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py
@@ -20,38 +20,35 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, unit=None):
- """Remove a unit
+def remove_unit(file, unit=None) -> None:
+ """Remove a unit
- Be very careful when a unit is removed, as it may mean that previously
- defined quantities in the model completely lose their meaning.
+ Be very careful when a unit is removed, as it may mean that previously
+ defined quantities in the model completely lose their meaning.
- :param unit: The unit element to remove
- :type unit: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param unit: The unit element to remove
+ :type unit: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # What?
- unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="HANDFULS")
+ # What?
+ unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="HANDFULS")
- # Yeah maybe not.
- ifcopenshell.api.run("unit.remove_unit", model, unit=unit)
- """
- self.file = file
- self.settings = {"unit": unit}
+ # Yeah maybe not.
+ ifcopenshell.api.run("unit.remove_unit", model, unit=unit)
+ """
+ settings = {"unit": unit}
- def execute(self):
- unit_assignment = ifcopenshell.util.unit.get_unit_assignment(self.file)
- if unit_assignment and self.settings["unit"] in unit_assignment.Units:
- units = list(unit_assignment.Units)
- units.remove(self.settings["unit"])
- if units:
- unit_assignment.Units = units
- else:
- self.file.remove(unit_assignment)
- ifcopenshell.util.element.remove_deep(self.file, self.settings["unit"])
+ unit_assignment = ifcopenshell.util.unit.get_unit_assignment(file)
+ if unit_assignment and settings["unit"] in unit_assignment.Units:
+ units = list(unit_assignment.Units)
+ units.remove(settings["unit"])
+ if units:
+ unit_assignment.Units = units
+ else:
+ file.remove(unit_assignment)
+ ifcopenshell.util.element.remove_deep(file, settings["unit"])
diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py
index 2da27bd08c..0c0b41c2f9 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py
@@ -19,43 +19,40 @@ import ifcopenshell
from typing import Optional
-class Usecase:
- def __init__(self, file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None):
- """Unassigns units as default units for the project
+def unassign_unit(file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None) -> None:
+ """Unassigns units as default units for the project
- :param units: A list of units to assign as project defaults.
- :type units: list[ifcopenshell.entity_instance],optional
- :return: None
- :rtype: None
+ :param units: A list of units to assign as project defaults.
+ :type units: list[ifcopenshell.entity_instance],optional
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # You need a project before you can assign units.
- ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
+ # You need a project before you can assign units.
+ ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject")
- # Millimeters and square meters
- length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
- area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT")
+ # Millimeters and square meters
+ length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI")
+ area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT")
- # Make it our default units, if we are doing a metric building
- ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
+ # Make it our default units, if we are doing a metric building
+ ifcopenshell.api.run("unit.assign_unit", model, units=[length, area])
- # Actually, we don't need areas.
- ifcopenshell.api.run("unit.unassign_unit", model, units=[area])
- """
- self.file = file
- self.settings = {"units": units}
+ # Actually, we don't need areas.
+ ifcopenshell.api.run("unit.unassign_unit", model, units=[area])
+ """
+ settings = {"units": units}
- def execute(self):
- unit_assignment = self.file.by_type("IfcUnitAssignment")
- if not unit_assignment:
- return
- unit_assignment = unit_assignment[0]
- units = set(unit_assignment.Units or [])
- units = units - set(self.settings["units"])
- if units:
- unit_assignment.Units = list(units)
- return unit_assignment
- self.file.remove(unit_assignment)
+ unit_assignment = file.by_type("IfcUnitAssignment")
+ if not unit_assignment:
+ return
+ unit_assignment = unit_assignment[0]
+ units = set(unit_assignment.Units or [])
+ units = units - set(settings["units"])
+ if units:
+ unit_assignment.Units = list(units)
+ return unit_assignment
+ file.remove(unit_assignment)
diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py
index e0caddbe3c..51e0db158b 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py
@@ -15,3 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see .
+
+from .add_filling import add_filling
+from .add_opening import add_opening
+from .remove_filling import remove_filling
+from .remove_opening import remove_opening
diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py
index a2867450f6..547178fff8 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py
@@ -20,103 +20,100 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, opening=None, element=None):
- """Fill an opening with an element
+def add_filling(file, opening=None, element=None) -> None:
+ """Fill an opening with an element
- Physical elements may have openings in them. For example, a wall might
- have an opening for a door. That opening is then filled by the door.
- This indicates that when the door moves, the opening will move with it.
- Or if the door is removed, then the opening may remain and need to be
- filled.
+ Physical elements may have openings in them. For example, a wall might
+ have an opening for a door. That opening is then filled by the door.
+ This indicates that when the door moves, the opening will move with it.
+ Or if the door is removed, then the opening may remain and need to be
+ filled.
- :param opening: The IfcOpeningElement to fill with the element.
- :type opening: ifcopenshell.entity_instance
- :param element: The IfcElement to be inserted into the opening.
- :type element: ifcopenshell.entity_instance
- :return: The new IfcRelFillsElement relationship
- :rtype: ifcopenshell.entity_instance
+ :param opening: The IfcOpeningElement to fill with the element.
+ :type opening: ifcopenshell.entity_instance
+ :param element: The IfcElement to be inserted into the opening.
+ :type element: ifcopenshell.entity_instance
+ :return: The new IfcRelFillsElement relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A bit of preparation, let's create some geometric contexts since
- # we want to create some geometry for our wall and opening.
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+ # A bit of preparation, let's create some geometric contexts since
+ # we want to create some geometry for our wall and opening.
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
- # Create a wall
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Create a wall
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's use the "3D Body" representation we created earlier to add a
- # new wall-like body geometry, 5 meters long, 3 meters high, and
- # 200mm thick
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
+ # Let's use the "3D Body" representation we created earlier to add a
+ # new wall-like body geometry, 5 meters long, 3 meters high, and
+ # 200mm thick
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
- # Place our wall at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ # Place our wall at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- # Create an opening, such as for a service penetration with fire and
- # acoustic requirements.
- opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
+ # Create an opening, such as for a service penetration with fire and
+ # acoustic requirements.
+ opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
- # Let's create an opening representation of a 950mm x 2100mm door.
- # Notice how the thickness is greater than the wall thickness, this
- # helps resolve floating point resolution errors in 3D.
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=.95, height=2.1, thickness=0.4)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=opening, representation=representation)
+ # Let's create an opening representation of a 950mm x 2100mm door.
+ # Notice how the thickness is greater than the wall thickness, this
+ # helps resolve floating point resolution errors in 3D.
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=.95, height=2.1, thickness=0.4)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=opening, representation=representation)
- # Let's shift our door 1 meter along the wall and 100mm along the
- # wall, to create a nice overlap for the opening boolean.
- matrix = np.identity(4)
- matrix[:,3] = [1, -.1, 0, 0]
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix)
+ # Let's shift our door 1 meter along the wall and 100mm along the
+ # wall, to create a nice overlap for the opening boolean.
+ matrix = np.identity(4)
+ matrix[:,3] = [1, -.1, 0, 0]
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix)
- # The opening will now void the wall.
- ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall)
+ # The opening will now void the wall.
+ ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall)
- # Create a door
- door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor")
+ # Create a door
+ door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor")
- # Let's create a door representation of a 950mm x 2100mm door.
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=.95, height=2.1, thickness=0.05)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=door, representation=representation)
+ # Let's create a door representation of a 950mm x 2100mm door.
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=.95, height=2.1, thickness=0.05)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=door, representation=representation)
- # Let's shift our door 1 meter along the wall and 100mm along the
- # wall, which lines up with our opening.
- matrix = np.identity(4)
- matrix[:,3] = [1, .05, 0, 0]
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix)
+ # Let's shift our door 1 meter along the wall and 100mm along the
+ # wall, which lines up with our opening.
+ matrix = np.identity(4)
+ matrix[:,3] = [1, .05, 0, 0]
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix)
- # The door will now fill the opening.
- ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door)
- """
- self.file = file
- self.settings = {"opening": opening, "element": element}
+ # The door will now fill the opening.
+ ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door)
+ """
+ settings = {"opening": opening, "element": element}
- def execute(self):
- fills_voids = self.settings["element"].FillsVoids
+ fills_voids = settings["element"].FillsVoids
- if fills_voids:
- if fills_voids[0].RelatingOpeningElement == self.settings["opening"]:
- return
- history = fills_voids[0].OwnerHistory
- self.file.remove(fills_voids[0])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if fills_voids:
+ if fills_voids[0].RelatingOpeningElement == settings["opening"]:
+ return
+ history = fills_voids[0].OwnerHistory
+ file.remove(fills_voids[0])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
- self.file.create_entity(
- "IfcRelFillsElement",
- GlobalId=ifcopenshell.guid.new(),
- RelatingOpeningElement=self.settings["opening"],
- RelatedBuildingElement=self.settings["element"],
- )
+ file.create_entity(
+ "IfcRelFillsElement",
+ GlobalId=ifcopenshell.guid.new(),
+ RelatingOpeningElement=settings["opening"],
+ RelatedBuildingElement=settings["element"],
+ )
diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py
index 142eaedd87..d873ac2cd3 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py
@@ -22,118 +22,115 @@ import ifcopenshell.util.element
import ifcopenshell.util.placement
-class Usecase:
- def __init__(
- self, file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
- ):
- """Create an opening in an element
+def add_opening(
+ file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
+) -> ifcopenshell.entity_instance:
+ """Create an opening in an element
- It is often necessary to cut out openings in elements like walls and
- slabs to make space to insert doors, windows, and other services that go
- through these penetrations.
+ It is often necessary to cut out openings in elements like walls and
+ slabs to make space to insert doors, windows, and other services that go
+ through these penetrations.
- Whereas it is possible to simply draw the wall as a rectangle with a
- hole in it for the opening, often these openings have specific meanings.
- For example, an opening might be filled with a window, and so when the
- window moves, the opening should move with it. Alternatively, the
- opening itself might have fire or acoustic requirements, such that any
- service or equipment passing through that space must also comply with
- those requirements. For these types of semantic openings, you should
- have a distinct opening element which voids your regular element. For
- example, your wall will still be a rectangular prism with no hole in it,
- and a separate opening element will have a box representing the extents
- of the opening for a window. The opening element will automatically
- perform a geometric boolean operation to cut out the wall's geometry.
+ Whereas it is possible to simply draw the wall as a rectangle with a
+ hole in it for the opening, often these openings have specific meanings.
+ For example, an opening might be filled with a window, and so when the
+ window moves, the opening should move with it. Alternatively, the
+ opening itself might have fire or acoustic requirements, such that any
+ service or equipment passing through that space must also comply with
+ those requirements. For these types of semantic openings, you should
+ have a distinct opening element which voids your regular element. For
+ example, your wall will still be a rectangular prism with no hole in it,
+ and a separate opening element will have a box representing the extents
+ of the opening for a window. The opening element will automatically
+ perform a geometric boolean operation to cut out the wall's geometry.
- Whenever you have an opening in you project, you should determine
- whether or not the opening is semantic (i.e. should be represented by a
- distinct opening object) or non-semantic (i.e. should simply be
- booleaned or be part of the shape of the object).
+ Whenever you have an opening in you project, you should determine
+ whether or not the opening is semantic (i.e. should be represented by a
+ distinct opening object) or non-semantic (i.e. should simply be
+ booleaned or be part of the shape of the object).
- :param opening: The IfcOpeningElement to cut out the element.
- :type opening: ifcopenshell.entity_instance
- :param element: The IfcElement to insert the opening into.
- :type element: ifcopenshell.entity_instance
- :return: The new IfcRelVoidsElement relationship
- :rtype: ifcopenshell.entity_instance
+ :param opening: The IfcOpeningElement to cut out the element.
+ :type opening: ifcopenshell.entity_instance
+ :param element: The IfcElement to insert the opening into.
+ :type element: ifcopenshell.entity_instance
+ :return: The new IfcRelVoidsElement relationship
+ :rtype: ifcopenshell.entity_instance
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # A bit of preparation, let's create some geometric contexts since
- # we want to create some geometry for our wall and opening.
- model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
- body = ifcopenshell.api.run("context.add_context", model,
- context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
+ # A bit of preparation, let's create some geometric contexts since
+ # we want to create some geometry for our wall and opening.
+ model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
+ body = ifcopenshell.api.run("context.add_context", model,
+ context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
- # Create a wall
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Create a wall
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Let's use the "3D Body" representation we created earlier to add a
- # new wall-like body geometry, 5 meters long, 3 meters high, and
- # 200mm thick
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=5, height=3, thickness=0.2)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=wall, representation=representation)
+ # Let's use the "3D Body" representation we created earlier to add a
+ # new wall-like body geometry, 5 meters long, 3 meters high, and
+ # 200mm thick
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=5, height=3, thickness=0.2)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=wall, representation=representation)
- # Place our wall at the origin
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
+ # Place our wall at the origin
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
- # Create an opening, such as for a service penetration with fire and
- # acoustic requirements.
- opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
+ # Create an opening, such as for a service penetration with fire and
+ # acoustic requirements.
+ opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
- # Let's create an opening representation of a 950mm x 2100mm door.
- # Notice how the thickness is greater than the wall thickness, this
- # helps resolve floating point resolution errors in 3D.
- representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
- context=body, length=.95, height=2.1, thickness=0.4)
- ifcopenshell.api.run("geometry.assign_representation", model,
- product=opening, representation=representation)
+ # Let's create an opening representation of a 950mm x 2100mm door.
+ # Notice how the thickness is greater than the wall thickness, this
+ # helps resolve floating point resolution errors in 3D.
+ representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
+ context=body, length=.95, height=2.1, thickness=0.4)
+ ifcopenshell.api.run("geometry.assign_representation", model,
+ product=opening, representation=representation)
- # Let's shift our door 1 meter along the wall and 100mm along the
- # wall, to create a nice overlap for the opening boolean.
- matrix = np.identity(4)
- matrix[:,3] = [1, -.1, 0, 0]
- ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix)
+ # Let's shift our door 1 meter along the wall and 100mm along the
+ # wall, to create a nice overlap for the opening boolean.
+ matrix = np.identity(4)
+ matrix[:,3] = [1, -.1, 0, 0]
+ ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix)
- # The opening will now void the wall.
- ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall)
- """
- self.file = file
- self.settings = {"opening": opening, "element": element}
+ # The opening will now void the wall.
+ ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall)
+ """
+ settings = {"opening": opening, "element": element}
- def execute(self) -> ifcopenshell.entity_instance:
- voids_elements = self.settings["opening"].VoidsElements
+ voids_elements = settings["opening"].VoidsElements
- if voids_elements:
- if voids_elements[0].RelatingBuildingElement == self.settings["element"]:
- return voids_elements[0]
- history = voids_elements[0].OwnerHistory
- self.file.remove(voids_elements[0])
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
+ if voids_elements:
+ if voids_elements[0].RelatingBuildingElement == settings["element"]:
+ return voids_elements[0]
+ history = voids_elements[0].OwnerHistory
+ file.remove(voids_elements[0])
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
- rel = self.file.create_entity(
- "IfcRelVoidsElement",
- **{
- "GlobalId": ifcopenshell.guid.new(),
- "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
- "RelatingBuildingElement": self.settings["element"],
- "RelatedOpeningElement": self.settings["opening"],
- }
+ rel = file.create_entity(
+ "IfcRelVoidsElement",
+ **{
+ "GlobalId": ifcopenshell.guid.new(),
+ "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file),
+ "RelatingBuildingElement": settings["element"],
+ "RelatedOpeningElement": settings["opening"],
+ }
+ )
+
+ placement = getattr(settings["opening"], "ObjectPlacement", None)
+ if placement and placement.is_a("IfcLocalPlacement"):
+ ifcopenshell.api.run(
+ "geometry.edit_object_placement",
+ file,
+ product=settings["opening"],
+ matrix=ifcopenshell.util.placement.get_local_placement(settings["opening"].ObjectPlacement),
+ is_si=False,
)
- placement = getattr(self.settings["opening"], "ObjectPlacement", None)
- if placement and placement.is_a("IfcLocalPlacement"):
- ifcopenshell.api.run(
- "geometry.edit_object_placement",
- self.file,
- product=self.settings["opening"],
- matrix=ifcopenshell.util.placement.get_local_placement(self.settings["opening"].ObjectPlacement),
- is_si=False,
- )
-
- return rel
+ return rel
diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py
index 6d5ab79752..b4c3188672 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py
@@ -20,47 +20,44 @@ import ifcopenshell
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file, element=None):
- """Remove a filling relationship
+def remove_filling(file, element=None) -> None:
+ """Remove a filling relationship
- If an element is filling an opening, this removes the relationship such
- that the opening and element both still exist, but the element no longer
- fills the opening.
+ If an element is filling an opening, this removes the relationship such
+ that the opening and element both still exist, but the element no longer
+ fills the opening.
- :param element: The element filling an opening.
- :type element: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param element: The element filling an opening.
+ :type element: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create a wall
- wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
+ # Create a wall
+ wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
- # Create an opening, such as for a service penetration with fire and
- # acoustic requirements.
- opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
+ # Create an opening, such as for a service penetration with fire and
+ # acoustic requirements.
+ opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
- # Create a door
- door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor")
+ # Create a door
+ door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor")
- # The door will now fill the opening.
- ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door)
+ # The door will now fill the opening.
+ ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door)
- # Not anymore!
- ifcopenshell.api.run("void.remove_filling", model, element=door)
- """
- self.file = file
- self.settings = {"element": element}
+ # Not anymore!
+ ifcopenshell.api.run("void.remove_filling", model, element=door)
+ """
+ settings = {"element": element}
- def execute(self):
- for rel in self.file.by_type("IfcRelFillsElement"):
- if rel.RelatedBuildingElement == self.settings["element"]:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- break
+ for rel in file.by_type("IfcRelFillsElement"):
+ if rel.RelatedBuildingElement == settings["element"]:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ break
diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py
index 5ffba93e25..58b7782333 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py
@@ -20,44 +20,41 @@ import ifcopenshell.api
import ifcopenshell.util.element
-class Usecase:
- def __init__(self, file: ifcopenshell.entity_instance, opening: ifcopenshell.entity_instance):
- """Remove an opening
+def remove_opening(file: ifcopenshell.entity_instance, opening: ifcopenshell.entity_instance) -> None:
+ """Remove an opening
- Fillings are retained as orphans. Voided elements remain. Openings
- cannot exist by themselves, so not only is the opening relationship
- removed, the opening is also removed.
+ Fillings are retained as orphans. Voided elements remain. Openings
+ cannot exist by themselves, so not only is the opening relationship
+ removed, the opening is also removed.
- :param opening: The IfcOpeningElement to remove.
- :type opening: ifcopenshell.entity_instance
- :return: None
- :rtype: None
+ :param opening: The IfcOpeningElement to remove.
+ :type opening: ifcopenshell.entity_instance
+ :return: None
+ :rtype: None
- Example:
+ Example:
- .. code:: python
+ .. code:: python
- # Create an oprhaned opening. Note that an orphaned opening is
- # invalid, as an opening can only exist when voiding another
- # element.
- opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
+ # Create an oprhaned opening. Note that an orphaned opening is
+ # invalid, as an opening can only exist when voiding another
+ # element.
+ opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement")
- # Remove it. This brings us back to a valid model.
- ifcopenshell.api.run("void.remove_opening", model, opening=opening)
- """
- self.file = file
- self.settings = {"opening": opening}
+ # Remove it. This brings us back to a valid model.
+ ifcopenshell.api.run("void.remove_opening", model, opening=opening)
+ """
+ settings = {"opening": opening}
- def execute(self) -> None:
- for rel in self.settings["opening"].VoidsElements:
+ for rel in settings["opening"].VoidsElements:
+ history = rel.OwnerHistory
+ file.remove(rel)
+ if history:
+ ifcopenshell.util.element.remove_deep2(file, history)
+ if settings["opening"].is_a("IfcOpeningElement"):
+ for rel in settings["opening"].HasFillings:
history = rel.OwnerHistory
- self.file.remove(rel)
+ file.remove(rel)
if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- if self.settings["opening"].is_a("IfcOpeningElement"):
- for rel in self.settings["opening"].HasFillings:
- history = rel.OwnerHistory
- self.file.remove(rel)
- if history:
- ifcopenshell.util.element.remove_deep2(self.file, history)
- ifcopenshell.api.run("root.remove_product", self.file, product=self.settings["opening"])
+ ifcopenshell.util.element.remove_deep2(file, history)
+ ifcopenshell.api.run("root.remove_product", file, product=settings["opening"])