From 90d4d5afdadddefcbba3c2dc673bf15e11093bf8 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 4 Jan 2023 16:09:32 +1100 Subject: [PATCH] Write docs for type API module --- .../ifcopenshell/api/type/assign_type.py | 143 +++++++++++++++++- .../api/type/get_related_objects.py | 21 ++- .../api/type/map_type_representations.py | 64 +++++++- .../ifcopenshell/api/type/unassign_type.py | 29 +++- 4 files changed, 238 insertions(+), 19 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py index 6d3991bb8e..152c593033 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py @@ -22,14 +22,147 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, related_object=None, relating_type=None): + """Assigns a type to an occurrence 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 occurence 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_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance.entity_instance + :return: The IfcRelDefinesByType relationship + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # 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_object=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, product=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_object=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_object": None, - "relating_type": None, + "related_object": related_object, + "relating_type": relating_type, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): if self.file.schema == "IFC2X3": 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 5503ff9c42..2e8d562fbf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py @@ -20,14 +20,25 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, related_object=None, relating_type=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. + + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance.entity_instance + :return: A list of occurrences of the type. + :rtype: list[ifcopenshell.entity_instance.entity_instance] + """ self.file = file self.settings = { - "related_object": None, - "relating_type": None, + "related_object": related_object, + "relating_type": relating_type, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): if self.settings["related_object"]: 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 4240b48b03..f07f63eae2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py @@ -22,14 +22,68 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, related_object=None, relating_type=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. + + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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") + + # 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_object=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) + + # 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) + + # 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": None, - "relating_type": None, + "related_object": related_object, + "relating_type": relating_type, } - for key, value in settings.items(): - self.settings[key] = value def execute(self): if not self.settings["relating_type"].RepresentationMaps: diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py index 22c7c27520..9072909ff4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py @@ -21,11 +21,32 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, related_object=None): + """Unassigns a type of an occurrence + + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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. + ifcopenshell.api.run("type.assign_type", model, related_object=furniture, relating_type=furniture_type) + + # Change our mind. Maybe it's a different type? + ifcopenshell.api.run("type.unassign_type", model, related_object=furniture) + """ self.file = file - self.settings = {"related_object": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"related_object": related_object} def execute(self): if self.file.schema == "IFC2X3":