From cf87852d9ce442e59fe3f08dd44553b59afd41a5 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 7 Dec 2022 17:30:08 +1100 Subject: [PATCH] Add API documentation for the material module --- .../api/material/add_constituent.py | 65 +++++++++- .../ifcopenshell/api/material/add_layer.py | 63 +++++++++- .../api/material/add_list_item.py | 62 +++++++++- .../ifcopenshell/api/material/add_material.py | 60 +++++++++- .../api/material/add_material_set.py | 87 +++++++++++++- .../ifcopenshell/api/material/add_profile.py | 66 ++++++++++- .../api/material/assign_material.py | 112 +++++++++++++++++- .../api/material/assign_profile.py | 70 ++++++++++- .../api/material/copy_material.py | 41 ++++++- .../api/material/edit_assigned_material.py | 24 +++- .../api/material/edit_constituent.py | 44 ++++++- .../ifcopenshell/api/material/edit_layer.py | 42 ++++++- .../api/material/edit_layer_usage.py | 59 ++++++++- .../ifcopenshell/api/material/edit_profile.py | 61 +++++++++- .../api/material/edit_profile_usage.py | 71 ++++++++++- .../api/material/remove_constituent.py | 36 +++++- .../ifcopenshell/api/material/remove_layer.py | 39 +++++- .../api/material/remove_list_item.py | 36 +++++- .../api/material/remove_material.py | 26 +++- .../api/material/remove_material_set.py | 38 +++++- .../api/material/remove_profile.py | 46 ++++++- .../api/material/reorder_set_item.py | 38 +++++- .../api/material/unassign_material.py | 33 +++++- 23 files changed, 1129 insertions(+), 90 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py index b2c251c465..577f803810 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py @@ -18,11 +18,68 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, constituent_set=None, material=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. + + 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.entity_instance + :param material: The IfcMaterial that the constituent is made out of. + :type material: ifcopenshell.entity_instance.entity_instance + :return: The newly created IfcMaterialConstituent + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # 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") + + # 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) + + # 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, product=window_type, material=material_set) + """ self.file = file - self.settings = {"constituent_set": None, "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"constituent_set": constituent_set, "material": material} def execute(self): constituents = list(self.settings["constituent_set"].MaterialConstituents or []) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py index ae7eccb33c..6862995879 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py @@ -18,11 +18,66 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, layer_set=None, material=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. + + 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.entity_instance + :param material: The IfcMaterial that the layer is made out of. + :type material: ifcopenshell.entity_instance.entity_instance + :return: The newly created IfcMaterialLayer + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # 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") + + # 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}) + + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, product=wall_type, material=material_set) + """ self.file = file - self.settings = {"layer_set": None, "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"layer_set": layer_set, "material": material} def execute(self): layers = list(self.settings["layer_set"].MaterialLayers or []) 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 649c6f889c..3922d2edd7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py @@ -20,11 +20,65 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, material_list=None, material=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 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. + + :param material_list: The IfcMaterialList the material should be added + to. + :type material_list: ifcopenshell.entity_instance.entity_instance + :param material: The IfcMaterial to add to the list + :type material: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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") + + # 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) + + # 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, product=window_type, material=material_set) + """ self.file = file - self.settings = {"material_list": None, "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"material_list": material_list, "material": material} def execute(self): materials = list(self.settings["material_list"].Materials or []) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py index dd7e280cbc..9101a66492 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py @@ -18,11 +18,61 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, name=None, category=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 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. + + 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.entity_instance + + Example:: + + # 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") + + # Assign the concrete material to that bench. Note that no colour + # "Style" has been specified. + ifcopenshell.api.run("material.assign_material", model, product=concrete_bench, material=concrete) + """ self.file = file - self.settings = {"name": "Unnamed"} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"name": name or "Unnamed", "category": category} def execute(self): - return self.file.create_entity("IfcMaterial", **{"Name": self.settings["name"] or "Unnamed"}) + material = self.file.create_entity("IfcMaterial", **{"Name": self.settings["name"] or "Unnamed"}) + if self.settings["category"]: + material.Category = self.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 91bc609bf3..d64eb7ce6e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py @@ -18,11 +18,90 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, name="Unnamed", set_type="IfcMaterialConstituentSet"): + """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. + + 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. + + 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.entity_instance + + Example:: + + # 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") + + # 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}) + + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, product=wall_type, material=material_set) + """ self.file = file - self.settings = {"name": "Unnamed", "set_type": "IfcMaterialConstituentSet"} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"} def execute(self): if self.settings["set_type"] == "IfcMaterialLayerSet": diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py index 1c54d5409c..f6b56bb904 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py @@ -18,11 +18,69 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, profile_set=None, material=None, profile=None): + """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. + + 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. + + :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.entity_instance + :param material: The IfcMaterial that the profile item is made out of. + :type material: ifcopenshell.entity_instance.entity_instance + :param profile: The IfcProfileDef that represents the 2D cross section + of the the profile item. + :type profile: ifcopenshell.entity_instance.entity_instance + :return: The newly created IfcMaterialProfile + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # 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. + 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, product=beam_type, material=material_set) + """ self.file = file - self.settings = {"profile_set": None, "material": None, "profile": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"profile_set": profile_set, "material": material, "profile": profile} def execute(self): profiles = list(self.settings["profile_set"].MaterialProfiles or []) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py index 70a5040352..6265e20121 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py @@ -23,17 +23,119 @@ import ifcopenshell.util.representation class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None, type="IfcMaterial", material=None): + """Assigns a material to a product + + 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 product: The IfcProduct to assign the material or material set + to. + :type product: ifcopenshell.entity_instance.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. + :type type: str + :param material: The IfcMaterial or material set you are assigning here. + :type material: ifcopenshell.entity_instance.entity_instance + :return: The IfcRelAssociatesMaterial entity + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # 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, + product=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_object=bench1, relating_type=bench_type) + ifcopenshell.api.run("type.assign_type", model, related_object=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, + product=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_object=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, + product=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) + """ self.file = file - self.settings = {"product": None, "type": "IfcMaterial", "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"product": product, "type": type, "material": material} def execute(self): material = ifcopenshell.util.element.get_material(self.settings["product"]) if material: ifcopenshell.api.run("material.unassign_material", self.file, product=self.settings["product"]) - if self.settings["type"] == "IfcMaterial": + if self.settings["type"] == "IfcMaterial" or ( + self.settings["material"] and not self.settings["material"].is_a("IfcMaterial") + ): return self.assign_ifc_material() elif self.settings["type"] == "IfcMaterialConstituentSet": material_set = self.file.create_entity(self.settings["type"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py index 5325dcda58..e257470348 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py @@ -20,11 +20,73 @@ import ifcopenshell.util.representation class Usecase: - def __init__(self, file, **settings): + 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.entity_instance + :param profile: The IfcProfileDef to set the profile item's curve to. + :type profile: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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, product=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, + product=beam, type="IfcMaterialProfileSetUsage") + + # Let's give a 1000mm long beam body representation. + body = ifcopenshell.api.run("geoemtry.add_profile_representation", + context=body_context, profile=hea100, depth=1000) + ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) + + # 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": None, "profile": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"material_profile": material_profile, "profile": profile} def execute(self): # TODO: handle composite profiles diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py index ab233af1a0..ac286cdba5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py @@ -21,9 +21,46 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, material=None, element=None): + """Copies a material to an element + + This convenience function exists to allow you to easily unassign any + existing material an element has, and assign a new material to it. + Essentially, it is a form a copy / pasting a material from one element + to another. + + It's a bit out of place, and will likely be redesigned or removed in the + future. It's not recommended to use this function, and + ifcopenshell.api.material.assign_material should be used instead. + + :param material: The IfcMaterial to assign to the element. + :type material: ifcopenshell.entity_instance.entity_instance + :param element: The IfcElement or IfcElementType to remove all previous + materials from and assign the new material to. + :type element: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + wood = ifcopenshell.api.run("material.add_material", model, name="TIM01", category="wood") + + # Let's imagine a concrete bench made out of concrete. + bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + ifcopenshell.api.run("material.assign_material", model, + product=bench, type="IfcMaterial", material=concrete) + + # And some street furniture made from wood. + street_furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + ifcopenshell.api.run("material.assign_material", model, + product=street_furniture, type="IfcMaterial", material=wood) + + # Let's copy the concrete over to the street furniture. + ifcopenshell.api.run("material.copy_material", model, material=wood, element=street_furniture) + """ self.file = file - self.settings = {"material": None, "element": None} + self.settings = {"material": material, "element": element} for key, value in settings.items(): self.settings[key] = value 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 7e595bcbfc..3e0a738cca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py @@ -18,11 +18,27 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, element=None, attributes=None): + """Edits the attributes of an IfcMaterial + + 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.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example:: + + 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": None, "attributes": {}} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"element": element, "attributes": attributes or {}} def execute(self): for name, value in self.settings["attributes"].items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py index dfb571760c..2933e4fd4e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py @@ -18,11 +18,47 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, constituent=None, attributes=None, material=None): + """Edits the attributes of an IfcMaterialConstituent + + 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.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.entity_instance, optional + :return: None + :rtype: None + + Example:: + + # 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") + + # 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) + + ifcopenshell.api.run("material.edit_constituent", model, + constituent=constituent, attributes={"Name": "Glazing"}) + """ self.file = file - self.settings = {"constituent": None, "attributes": {}, "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"constituent": constituent, "attributes": attributes or {}, "material": material} def execute(self): for name, value in self.settings["attributes"].items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py index efb552d48a..86640f5343 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py @@ -18,11 +18,45 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, layer=None, attributes=None, material=None): + """Edits the attributes of an IfcMaterialLayer + + 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.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.entity_instance, optional + :return: None + :rtype: None + + Example:: + + # 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") + + # 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": None, "attributes": {}, "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"layer": layer, "attributes": attributes or {}, "material": material} def execute(self): for name, value in self.settings["attributes"].items(): 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 6b830b2345..1b7e9b0549 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py @@ -18,11 +18,62 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, usage=None, attributes=None): + """Edits the attributes of an IfcMaterialLayerSetUsage + + 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. + + :param usage: The IfcMaterialLayerSetUsage entity you want to edit + :type usage: ifcopenshell.entity_instance.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example:: + + # 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") + + # 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, + product=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_object=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, + product=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": None, "attributes": {}} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"usage": usage, "attributes": attributes or {}} def execute(self): for name, value in self.settings["attributes"].items(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py index 4d03fce765..e9cfd95cea 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py @@ -18,11 +18,64 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, profile=None, attributes=None, profile_def=None, material=None): + """Edits the attributes of an IfcMaterialProfile + + 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.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.entity_instance, optional + :param material: The IfcMaterial entity you want to change the profile + to be made from. + :type material: ifcopenshell.entity_instance.entity_instance, optional + :return: None + :rtype: None + + Example:: + + # 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 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, + ) + + # 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": None, "attributes": {}, "profile_def": None, "material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = { + "profile": profile, + "attributes": attributes or {}, + "profile_def": profile_def, + "material": material, + } def execute(self): for name, value in self.settings["attributes"].items(): 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 7602763c87..836171fb90 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py @@ -21,11 +21,74 @@ import ifcopenshell.util.representation class Usecase: - def __init__(self, file, **settings): + 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.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example:: + + # 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, product=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, + product=beam, type="IfcMaterialProfileSetUsage") + + # Let's give a 1000mm long beam body representation. + body = ifcopenshell.api.run("geoemtry.add_profile_representation", + context=body_context, profile=hea100, depth=1000) + ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) + + # 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": None, "attributes": {}} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"usage": usage, "attributes": attributes or {}} def execute(self): self.cardinal_point = self.settings["attributes"].get("CardinalPoint") diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py index 72fa450eb5..32b83e2348 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py @@ -18,11 +18,39 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, constituent=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. + + :param constituent: The IfcMaterialConstituent entity you want to remove + :type constituent: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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") + + # 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": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"constituent": constituent} def execute(self): self.file.remove(self.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 a3c61fa410..024792308f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py @@ -18,11 +18,42 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, layer=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. + + :param layer: The IfcMaterialLayer entity you want to remove + :type layer: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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") + + # 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": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"layer": layer} def execute(self): self.file.remove(self.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 780b333efb..da45d1820f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py @@ -20,11 +20,39 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, material_list=None, material_index=0): + """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. + + :param material_list: The IfcMaterialList entity you want to remove an + item from. + :type material_list: ifcopenshell.entity_instance.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:: + + # 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") + + # 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": None, "material_index": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"material_list": material_list, "material_index": material_index} def execute(self): materials = list(self.settings["material_list"].Materials) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py index bcb71b1da5..f3d689455a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py @@ -20,11 +20,29 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, material=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. + + :param material: The IfcMaterial entity you want to remove + :type material: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"material": material} def execute(self): inverse_elements = self.file.get_inverse(self.settings["material"]) 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 f002b3a9fd..be4c0a82eb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py @@ -20,11 +20,41 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, material=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. + + :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet, + IfcMaterialProfileSet entity you want to remove. + :type material: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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") + + # 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) + """ + self.file = file - self.settings = {"material": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"material": material} def execute(self): inverse_elements = self.file.get_inverse(self.settings["material"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py index 8e1f615ce8..a6b05b2c3f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py @@ -18,11 +18,49 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, profile=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. + + :param profile: The IfcMaterialProfile entity you want to remove + :type profile: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # 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 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. + 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=[(2.5, 2.5), (32.5, 2.5), (32.5, -2.5), (2.5, -2.5)]) + 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) + """ + self.file = file - self.settings = {"profile": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"profile": profile} def execute(self): self.file.remove(self.settings["profile"]) 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 b9e021f11f..e726ac695a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py @@ -18,9 +18,43 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, material_set=None, old_index=0, new_index=0): + """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. + + :param material_set: The IfcMaterialSet which you want to reorder an + item in. + :type material_set: ifcopenshell.entity_instance.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:: + + 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") + + # 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": None, "old_index": 0, "new_index": 0} + self.settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index} for key, value in settings.items(): self.settings[key] = value diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py index 1a971e454e..c76aae1e3b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py @@ -21,11 +21,36 @@ import ifcopenshell.util.element class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, product=None): + """Removes any material relationship with a product + + 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 product: The IfcProduct that may or may not have a material + :type product: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + 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, + product=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, product=bench_type) + """ self.file = file - self.settings = {"product": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"product": product} def execute(self): if self.settings["product"].is_a("IfcTypeObject"):