Batch-Generate a library of IfcWallType with varying thickness

I know the architects around here will think this is dumb but I'm mostly working on existing buildings, and while there are standard wall thickness you want to adhere to when drafting a building, I've personaly witnessed them in all colors, shapes and forms. Even varying thickness along the main axis, which I've yet to learn how to do parametrically in IFC.
So I thought to myself, why not build a library of IfcWallType elements with different thickness I could dynamically load into my projects when I need them ?
I'm posting this to gauge if somehow I've missed something somewhere or if it may be useful to someone, somewhere. :)
Before diving into the code here's the resulting file contents.


and me casually browsing through the library (might be nice to have a search feature in there ?)

And me playing with my new toys

Woops, did I uncover a bug at the end there @Moult ? or is this intended ? Is there a way or a point to Mitre 3 wall segments at once ?
Also me playing with the resulting file in Revit

And finally the code. It's only using very high level functions and operators so naturally it is very slow. It took around 2 minutes to generate the ~500 IfcWallType elements. I'm sure it can be optimised by digging a bit more in the core of ifcopenshell, but that will be the task of another day :).

Cheers

import bpy

from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.material.data import Data as MaterialData


def run():
    file = IfcStore.file
    if file is None:
        bpy.ops.bim.create_project()
        file = IfcStore.file
    for i in range(1, 501):
        add_wall_type(i)


def add_wall_type(depth):
    depth_str = str(depth).zfill(4)
    bpy.ops.object.empty_add(type="PLAIN_AXES")
    obj = bpy.context.active_object
    obj.name = "Wall_" + depth_str
    bpy.context.scene.BIMRootProperties.ifc_product = "IfcElementType"
    bpy.context.scene.BIMRootProperties.ifc_class = "IfcWallType"
    bpy.context.scene.BIMRootProperties.ifc_predefined_type = "STANDARD"
    bpy.ops.bim.assign_class(ifc_class="IfcWallType", predefined_type="STANDARD")
    bpy.ops.bim.add_material(obj="")
    obj.BIMObjectMaterialProperties.material_type = "IfcMaterialLayerSet"

    bpy.ops.bim.assign_material()
    bpy.ops.bim.enable_editing_assigned_material()
    obj.BIMObjectMaterialProperties.material_set_attributes[0].string_value = obj.name

    if not MaterialData.is_loaded:
        MaterialData.load(IfcStore.get_file())

    oprops = obj.BIMObjectProperties
    product_data = MaterialData.products[oprops.ifc_definition_id]
    material_set_id = product_data["id"]
    bpy.ops.bim.add_layer(layer_set=int(material_set_id))

    material_set_data = MaterialData.layer_sets[material_set_id]
    set_items = material_set_data["MaterialLayers"] or []

    layer_0 = set_items[0]
    bpy.ops.bim.enable_editing_material_set_item(material_set_item=int(layer_0))

    obj.BIMObjectMaterialProperties.material_set_item_attributes[2].string_value = depth_str
    obj.BIMObjectMaterialProperties.material_set_item_attributes[0].float_value = depth / 1000

    bpy.ops.bim.disable_editing_material_set_item(obj=obj.name)
    bpy.ops.bim.edit_material_set_item(material_set_item=int(layer_0))
    bpy.ops.bim.disable_editing_assigned_material(obj=obj.name)
    bpy.ops.bim.edit_assigned_material(material_set=material_set_id)

if __name__ == "__main__":
    run()
CoenJesusbilltlang
«1

Comments

  • edited October 2022

    This is amazing!! :-D

    I coincidentally had the same idea, ;-) but from a spreadsheet or csv file. I'm not familiar how you stored this data.

    Some questions:
    1. t's not clear to me where the data of the IfcWallType is defined, where do you get it from? I see from ifcopenshell.api.material.data import Data as MaterialData, but this is a Class you're calling? Where are the hardcoded materials and dimensions? :-)
    2. Is it possible to make it "pure" ifcopenshell, so it's agnostic and the same script for example could be used in FreeCAD?
    3. Could you use the same script to create IfcSlabTypes, IfcCoveringTypes, IfcBeamtypes etc? IfcBeamTypes would be especially cool, but as
    understand there is some library called BOLTS, but I haven't looked into that.
    4. Is it possible to add a predefined IfcClassificationReference? And Pset_???Common Types?

    It took around 2 minutes to generate the ~500

    I don't think performance should matter, since these scripts would generally run only once at the start of a project?

    I know the architects around here will think this is dumb

    Good way to start a post ;-). I think contractors will be very happy with these kind of solutions.

    Gorgious
  • edited October 2022

    Hehe thanks for your input ! There is actually no stored data, apart from the .ifc file it outputs when finished, which can actually be loaded as a library :

    The IfcWallTypes are dynamically generated with an incremented thickness there in the for loop.

    The method I used is pretty simple and actually what most beginner tutorials about the blender python API will tell you to do, I just reverse-engineered the workflow using the UI and copy/pasted the python commands from the console into the script.

    Make sure you have "Developer extras" and "Python Tooltips" enabled in the interface preferences.

    then just have an INFO editor open somewhere, and when you click a button it will print out the API command.

    I just had to dig a bit into the code to get the MaterialLayerSet ID and its Layer ID but thankfully it was pretty straightforward. Mostly just parsing the lines I wanted from this gigantic Panel interface code : https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/blenderbim/blenderbim/bim/module/material/ui.py#L103-L397

    Now I'll try and address your points as well as I can :)

    1. ifcopenshell.api.material.data is a helper class that lets you query for data concerning materials, layers etc. That's as far as my knowledge goes, I just copy/pasted parts where I thought it made sense :) You can see the code there if you want to explore it. Otherwise I just added an IfcMaterialLayerSet material type which looked to me like the most straightforward to ensure a layer with a certain thickness.
    2. Yes, it's 100% possible and I'm sure that it would take half as many lines of codes and execute in a fraction of the time it does now ;)
    3. Yes, if you can do it with the UI you can do it via code, too.
    4. I'm sure it's possible, however I'm really not confident enough in the ifc schema and the interface (yet) to answer about all of these :p
      Here's how you can add a Pset_WallCommon pset to each wall type with default values. Of course instead of having only predefined values, it could fetch the values from a csv file and populate the types accordingly !

      Add these to the top of your file
    import blenderbim.tool as tool
    from blenderbim.bim.module.pset.data import Data as PsetData
    

    and these to the end of the add_wall_type function :

        obj.PsetProperties.pset_name = 'Pset_WallCommon'
        bpy.ops.bim.add_pset(obj=obj.name, obj_type="Object")
        pset = PsetData.psetqtos(tool.Ifc.get().by_id(obj.BIMObjectProperties.ifc_definition_id))[0]
        bpy.ops.bim.enable_pset_editing(pset_id=int(pset["id"]), obj=obj.name, obj_type="Object")
        obj.PsetProperties.properties[7].metadata.bool_value = True
        obj.PsetProperties.properties[2].metadata.string_value = "0.1"
        bpy.context.object.PsetProperties.properties[1].enumerated_value.enumerated_values[3].is_selected = True
        bpy.ops.bim.edit_pset(obj=obj.name, obj_type="Object")
    

    For reference here's how I got the path to the enum value, with the python tooltip . With developer extras you can also right click on the field > Copy Full data Path.

    1. Yeah performance is not the biggest concern, although I'm sure with the right calls to the ifcopenshell API this would be way more streamlined and not depend on Blender.
    2. Hehehehe :P
    Coen
  • edited October 2022

    Here's how you do it via the API: https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/blenderbim/scripts/generate_demo_library.py#L64

    There is no automatic way to mitre multiple right now. Right now axis-based objects (walls, profiles, etc) can only be connected 1:1 at a particular end of the axis (start / end) but 1:N along the path of the axis. We can ease this restriction in the future when connection priorities are implemented.

    Manually of course you can add a boolean half space.

    GorgiousCoen
  • edited October 2022

    Ahh, nice thank you so much !
    Here's the updated code for anyone interested, software agnostic (apart from get_path) and running in ~2 seconds.

    import ifcopenshell.api
    
    class LibraryGenerator:
        def generate(self):
            ifcopenshell.api.pre_listeners = {}  # I don't know if it's useful but I don't want to break anything so I'm keeping this
            ifcopenshell.api.post_listeners = {}
    
            self.file = ifcopenshell.api.run("project.create_file")
            self.project = ifcopenshell.api.run(
                "root.create_entity", self.file, ifc_class="IfcProject", name="BlenderBIM Demo"
            )
            self.library = ifcopenshell.api.run(
                "root.create_entity", self.file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library"
            )
            ifcopenshell.api.run(
                "project.assign_declaration", self.file, definition=self.library, relating_context=self.project
            )
            ifcopenshell.api.run("unit.assign_unit", self.file, length={"is_metric": True, "raw": "METERS"})
            self.material = ifcopenshell.api.run("material.add_material", self.file, name="Unknown")
    
            for thickness in range(1, 501):
                self.create_layer_type("IfcWallType", "Wall_" + str(thickness).zfill(4), thickness / 1000)
    
            self.file.write(get_path("IFC4 Wall Library.ifc"))
    
        def create_layer_type(self, ifc_class, name, thickness):
            element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=ifc_class, name=name)
            rel = ifcopenshell.api.run("material.assign_material", self.file, product=element, type="IfcMaterialLayerSet")
            layer_set = rel.RelatingMaterial
            layer = ifcopenshell.api.run("material.add_layer", self.file, layer_set=layer_set, material=self.material)
            layer.LayerThickness = thickness
            ifcopenshell.api.run("project.assign_declaration", self.file, definition=element, relating_context=self.library)
            return element
    
    
    def get_path(filename):
        import bpy
        from pathlib import Path
        folder = Path(bpy.data.filepath).parent
        return str(folder / filename)
    
    
    if __name__ == "__main__":
        LibraryGenerator().generate()
    
    CoenMartin156131
  • edited October 2022

    @Gorgious
    I simplified your script a lot because I want to understand the basics, so I removed all the OOP functionality:

    import ifcopenshell.api
    
    ifcopenshell.api.post_listeners = {}
    
    ifc_file = ifcopenshell.api.run("project.create_file")
    project = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcProject", name="BlenderBIM Demo")
    library = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library")
    ifcopenshell.api.run( "project.assign_declaration", ifc_file, definition=library, relating_context=project)
    ifcopenshell.api.run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "METERS"})
    
    material = ifcopenshell.api.run("material.add_material", ifc_file, name="Material Name")
    element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class='IfcWallType', name='demo_wall')
    rel = ifcopenshell.api.run("material.assign_material", ifc_file, product=element, type="IfcMaterialLayerSet")
    
    layer_set = rel.RelatingMaterial
    layer = ifcopenshell.api.run("material.add_layer", ifc_file, layer_set=layer_set, material=material)
    layer.LayerThickness = 0.1
    
    ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library)
    
    ifc_file.write("..\\demo2.ifc")
    

    It creates an .ifc file, but when I tried to load it in BlenderBIM I don't see the walltypes appearing.

    EDIT: I see anIfcElementType being defined in the for loop actually 👀
    EDIT 2: Adjusted the script, ifc_class='IfcWallType' instead of ifc_class='IfcWall' as I typed
    It all works now.

    Ace
  • edited October 2022

    @Moult
    Is it possible to assign PropertySets and properties with the ifcopenshell.api? Are there any examples?
    EDIT:
    I see you already posted the answer here last summer:
    https://community.osarch.org/discussion/711/ifcopenshell-how-to-add-a-new-property-and-value-to-an-object

    import ifcopenshell
    import ifcopenshell.api
    ifc = ifcopenshell.open('path\to_your\ifc_file')
    element = ifc.by_type("IfcElement")[0] # Just as an example, you will need to select the elements you want to add properties to yourself.
    pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Your Property Set Name")
    ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"foo": "foobar", "foo2": "foobaz"})
    
  • With added propertyset :-D

    import ifcopenshell.api
    
    
    ifcopenshell.api.post_listeners = {}
    
    ifc_file = ifcopenshell.api.run("project.create_file")
    project = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcProject", name="BlenderBIM Demo")
    library = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library")
    
    ifcopenshell.api.run( "project.assign_declaration", ifc_file, definition=library, relating_context=project)
    ifcopenshell.api.run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "METERS"})
    
    material = ifcopenshell.api.run("material.add_material", ifc_file, name="Material Name")
    
    element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class='IfcWallType', name='demo_wall')
    rel = ifcopenshell.api.run("material.assign_material", ifc_file, product=element, type="IfcMaterialLayerSet")
    
    layer_set = rel.RelatingMaterial
    layer = ifcopenshell.api.run("material.add_layer", ifc_file, layer_set=layer_set, material=material)
    layer.LayerThickness = 0.1
    
    
    pset = ifcopenshell.api.run("pset.add_pset", ifc_file, product=element, name="Pset_Name")
    
    ifcopenshell.api.run("pset.add_pset", ifc_file, product=element, name="Your Property Set Name")
    ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={"foo": "foobar", "foo2": "foobaz"})
    
    ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library)
    
    
    ifc_file.write("...\\demo4.ifc")
    

    brunopostleGorgious
  • Are there any simple examples with batch creating an IfcBeamType with IfMaterialProfileSet and IfcParametrizedProfileDef using the ifcopenshell.api?

  • How to add an IfcClassificationReference as well using the ifcopenshell.api?

  • edited October 2022

    I have created a simple CSV file with crude IFC config data.

    IfcElement;IfcElementType;Name;IfcMaterialLayerSet;Material;Pset_Common;IsExternal;LoadBearing;FireRating;Phase;Width;Length;Height
    IfcWall;IfcWallType;wall name;IfcMaterialLayerSet;concrete;Pset_WallCommon;TRUE;FALSE;90;NEW;100;3000;3000
    IfcWall;IfcWallType;wall name 1;IfcMaterialLayerSet;concrete;Pset_WallCommon;FALSE;TRUE;60;NEW;200;3000;3000
    IfcWall;IfcWallType;wall name 2;IfcMaterialLayerSet;concrete;Pset_WallCommon;FALSE;TRUE;30;NEW;300;3000;3000
    

    A screenshot of the CSV file in excel:

    Now I'm able to read this CSV file with pandas and created a function which makes a library from this CSV file

    import ifcopenshell.api
    import pandas as pd
    
    def get_wall_library(csv_library):
    
        library_data_frame = pd.read_csv(csv_library, ";")
    
    
        return library_data_frame
    
    
    def create_ifcwalltype_library(ifc_element, ifc_element_type, element_name, ifc_material_layer_set, material_name,pset_common, is_external, load_bearing, fire_rating, width):
        ifcopenshell.api.post_listeners = {}
    
        ifc_file = ifcopenshell.api.run("project.create_file")
        project = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcProject", name="BlenderBIM Demo")
        library = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcProjectLibrary", name="BlenderBIM Demo Library")
    
        ifcopenshell.api.run( "project.assign_declaration", ifc_file, definition=library, relating_context=project)
        ifcopenshell.api.run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "MILIMETERS"})
    
        material = ifcopenshell.api.run("material.add_material", ifc_file, name=material_name)
    
        element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class=ifc_element_type, name=element_name)
        rel = ifcopenshell.api.run("material.assign_material", ifc_file, product=element, type="IfcMaterialLayerSet")
    
        layer_set = rel.RelatingMaterial
        layer = ifcopenshell.api.run("material.add_layer", ifc_file, layer_set=layer_set, material=material)
        layer.LayerThickness = width/1000
    
    
        pset = ifcopenshell.api.run("pset.add_pset", ifc_file, product=element, name="Pset_Name")
    
        ifcopenshell.api.run("pset.add_pset", ifc_file, product=element, name="Your Property Set Name")
        ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={"foo": "foobar", "foo2": "foobaz"})
    
        ifcopenshell.api.run("project.assign_declaration", ifc_file, definition=element, relating_context=library)
    
    
        ifc_file.write("...\\demo" + str(element_name) + ".ifc")
    
    
    csv_wall_library = "C:\\Algemeen\\00_prive\\BlenderScripts\\BlenderBIMLibrary\\IfcWallLibrary.csv"
    get_wall_library(csv_library=csv_wall_library)
    
    for index, row in get_wall_library(csv_library=csv_wall_library).iterrows():
        print(row['IfcElement'])
    
        create_ifcwalltype_library( ifc_element=row['IfcElement'],
                                    ifc_element_type=row['IfcElementType'],
                                    element_name=row['Name'],
                                    ifc_material_layer_set=row['IfcMaterialLayerSet'],
                                    material_name=row['Material'],
                                    pset_common=row['Pset_Common'],
                                    is_external=row['IsExternal'],
                                    load_bearing=row['LoadBearing'],
                                    fire_rating=row['FireRating'],
                                    width=row['Width'])
    
    

    It outputs several IFC files which contain the IfcWallTypes.:

    Testing them in BlenderBIM when Loading in the IfcProject Library, can see the PropertySet and such:

    It all works :-)

    1) Need to figure out how to add a style colour to the WallType instances
    2) Need to experiment a bit with Meters and Milimeters
    3) Would be cool if I could use the BlenderBIM spreadsheet writer to create structured Libraris from other IFC files, I am lazy and don't want to re-invent libraries others already made.
    4) Will continue development here.
    5) Need to figure out how to do this for IfcBeamType with all different profiles, or other IfcElementTypes

    Thanks @Gorgious for the inspiration and @Moult for the ifcopenshell.api examlple code
    Any tips are welcome.
    In short, I made an IfcProjectLibrary generator from a CSV file, don't know if this is worth a Youtube video?

    GorgiousCadGiru
  • How do you add a preset style/colour to the IFC library?, I have added this snippet to my code:

        context = ifc_file.createIfcGeometricRepresentationContext()
        style = ifcopenshell.api.run("style.add_style", ifc_file, name=material_name)
        ifcopenshell.api.run(
            "style.add_surface_style",
            ifc_file,
            style=style,
            attributes={
                "SurfaceColour": {
                    "Name": None,
                    "Red": 2.2,
                    "Green": 0.8,
                    "Blue": 0.5,
                },
                "DiffuseColour": {
                    "Name": None,
                    "Red": 2.2,
                    "Green": 0.8,
                    "Blue": 0.5,
                },
                "Transparency": 0.0,
                "ReflectanceMethod": "PLASTIC",
            },
        )
        ifcopenshell.api.run(
            "style.assign_material_style",
            ifc_file,
            material=material,
            style=style,
            context=context,
    

    It creates the IFC files:

    Inspecting an IFC I see this added:

    #8=IFCMATERIAL('brick',$,$);
    #9=IFCWALLTYPE('3yCX3mttD3iOShONsL9xOF',$,'brick_wall_100mm',$,$,(#13),$,$,$,.NOTDEFINED.);
    #10=IFCMATERIALLAYERSET((#12),$,$);
    #11=IFCRELASSOCIATESMATERIAL('0kENWXPufBruMIhLm1Kvv0',$,$,$,(#9),#10);
    #12=IFCMATERIALLAYER(#8,0.3,$,$,$,$,$);
    #13=IFCPROPERTYSET('1U9IZnOXP3Kf$Jhjq2M4ib',$,'Pset_WallCommon',$,(#14,#15,#16));
    #14=IFCPROPERTYSINGLEVALUE('IsExternal',$,IFCBOOLEAN(.F.),$);
    #15=IFCPROPERTYSINGLEVALUE('LoadBearing',$,IFCBOOLEAN(.T.),$);
    #16=IFCPROPERTYSINGLEVALUE('FireRating',$,IFCLABEL('30'),$);
    #17=IFCGEOMETRICREPRESENTATIONCONTEXT($,$,$,$,$,$);
    #18=IFCSURFACESTYLE('brick',.BOTH.,(#19));
    #19=IFCSURFACESTYLERENDERING(#20,0.,#21,$,$,$,$,$,.PLASTIC.);
    #20=IFCCOLOURRGB($,2.2,0.8,0.5);
    #21=IFCCOLOURRGB($,2.2,0.8,0.5);
    #22=IFCSTYLEDITEM($,(#18),'brick');
    #23=IFCSTYLEDREPRESENTATION(#17,$,$,(#22));
    #24=IFCMATERIALDEFINITIONREPRESENTATION($,$,(#23),#8);
    

    When loading it into BlenderBIM it's still grey:

    I expted a nice brick colour, am I missing a step? Do I need to do something in Blender?

  • Disregard the post above, I spoke too soon, I needed to click the Add IfcMaterial too in the IfcProjectLibrary


    How to import all these assets with one click? Do I need to make one IfcProjectLibrary?

  • @Coen said:
    How to import all these assets with one click? Do I need to make one IfcProjectLibrary?

    As far as I am aware... yes
    Could be a feature request for @Moult ?

  • edited October 2022


    Made one library of it, but now I can't seem to import the materials anymore.

    I am beginning to understand my own confusion, I create the same IfcMaterial each time for each IfcElement: Should only create it once and reuse it

    Still with one library you need to click all these elements one by one, what's the reason for that?
    At least colour import works :-)

    updated script:
    https://github.com/C-Claus/BlenderScripts/blob/master/BlenderBIMLibrary/IfcWallTypeLibrary.py

  • @Coen I think this is a bug, importing the wall type drags in the material ok, but fails to bring the material representation. See this issue: https://github.com/IfcOpenShell/IfcOpenShell/issues/2550

    Coen
  • How does one set the LayerSetName with ifcopenshell or the ifcopenshell.api?

    I would like to use it to set predefined hatch patterns with the walltypes, or is there maybe a better way to do it?

  • edited November 2022

    @Coen said:
    How does one set the LayerSetName with ifcopenshell or the ifcopenshell.api?

    I would like to use it to set predefined hatch patterns with the walltypes, or is there maybe a better way to do it?

    Never mind I found it, I now did it like this. Seems to work

        material = ifcopenshell.api.run("material.add_material", ifc_file, name=material_name)
    
    
    
        element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class=ifc_element_type, name=element_name)
        rel = ifcopenshell.api.run("material.assign_material", ifc_file, product=element, type="IfcMaterialLayerSet")
    
    
    
        layer_set = rel.RelatingMaterial
        layer_set.LayerSetName = 'brick'
        layer = ifcopenshell.api.run("material.add_layer", ifc_file, layer_set=layer_set, material=material)
        layer.LayerThickness = width/1000
    


    GorgiousVDobranov
  • How would one create an IfcWallType instance and add it as an IfcWall instance? I managed to make it work with an IfcBeamType instance, but somehow I can't make it work with an IfcWallType instance.

  • @Coen

    Minimal working example would be

    wall_type = element = run("root.create_entity", file, ifc_class="IfcWallType", name="My Wall Type")
    wall_instance = run("root.create_entity", file, ifc_class="IfcWall", name="My Wall Instance")
    run("type.assign_type", file, related_object=wall_instance, relating_type=wall_type)
    

    Of course you'd likely want to

    • Add a representation to the wall
    representation = run(
        "geometry.add_wall_representation",
        file,
        context=body,
        length=5,
        height=3,
        thickness=0.25,
    )
    run("geometry.assign_representation", file, product=wall_type, representation=representation)
    
    • Place the instance wall in the world
    run(
        "geometry.edit_object_placement",
        file,
        product=wall_instance ,
        matrix=my_matrix,
        is_si=False,
    )
    
    • ?? profit
    Coen
  • With this piece of code

    representation = run(
        "geometry.add_wall_representation",
        file,
        context=body,
        length=5,
        height=3,
        thickness=0.25,
    )
    run("geometry.assign_representation", file, product=wall_type, representation=representation
    

    The thickness is defined there, but I defined the thickness in an IfcMaterialLayerSet. Thanks for your snippet. Will try this evening.

  • This is getting frustrating, somehow I can't make a relatively simple thing work. I am hoping by using this forum as a rubber duck I understand what I am doing wrong. What I am trying to do is create an IfcWallType with an IfcMateriaLayerSet and place the IfcWallType instance in the project.

    My steps
    1. Creating an IfcProject

    import ifcopenshell
    from ifcopenshell.api import run
    import numpy
    
    ifc_file = ifcopenshell.file()
    project = run("root.create_entity", ifc_file, ifc_class="IfcProject", name="Demo")
    
    1. Use meters
    run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "METERS"})
    
    1. Define Site, Building and Storey
    # Create a site, building, and storey. Many hierarchies are possible.
    site = run("root.create_entity", ifc_file, ifc_class="IfcSite", name="My Site")
    building = run("root.create_entity", ifc_file, ifc_class="IfcBuilding", name="Building A")
    storey = run("root.create_entity", ifc_file, ifc_class="IfcBuildingStorey", name="Ground Floor")
    
    # Since the site is our top level location, assign it to the project
    # Then place our building on the site, and our storey in the building
    run("aggregate.assign_object", ifc_file, relating_object=project, product=site)
    run("aggregate.assign_object", ifc_file, relating_object=site, product=building)
    run("aggregate.assign_object", ifc_file, relating_object=building, product=storey)
    
    1. Now here I want to define the IfcWallType with an IfcMaterialLayerSet
    #define material
    ifc_material = run("material.add_material", ifc_file, name="brick")
    
    #define ifcwalltype
    ifc_walltype = run("root.create_entity", ifc_file, ifc_class="IfcWallType", name="wall_demo")
    
    #assing material to the ifcwalltype
    relating_material = run("material.assign_material", ifc_file, product=ifc_walltype, type="IfcMaterialLayerSet")
    layer_set = relating_material.RelatingMaterial
    
    #add IfcMaterial to the IfcMaterialLayer
    layer = run("material.add_layer", ifc_file, layer_set=layer_set, material=ifc_material)
    
    #define the LayerThickness
    layer.LayerThickness = 0.2
    
    #make an ifcwalltype instance
    ifc_walltype_instance = run("root.create_entity", ifc_file, ifc_class="IfcWall", relating_type=ifc_walltype)
    
    #ifcwalltype instance needs a represenation, 
    representation = run("geometry.add_wall_representation",ifc_file,context=body,length=5,height=3,thickness=layer.LayerThickness)
    
      #now I need a matrix to define the IfcWallType instance placement and rotation
    matrix_1 = numpy.array(
                (
                    (0.0, 0.0, 1.0, 0.0),
                    (0.0, 1.0, 0.0, 0.0),
                    (1.0, 0.0, 0.0, 0.0),
                    (0.0, 0.0, 0.0, 1.0),
                )
            )
    
    run("geometry.edit_object_placement",ifc_file,product=ifc_walltype_instance ,matrix=matrix_1,is_si=False)
    
    #assing the ifcwalltype instance to a spatial container
    run("spatial.assign_container", ifc_file, relating_structure=storey, product=ifc_walltype_instance)
    
    

    Running the script creates an IFC file. Opening it in BIM Vision, nothing shows up:

    Opening the IFC in BlenderBIM I see the following:

    It made an IfcWallType, and it placed and IfcWall/None in the IfcBuildingStorey
    Manually adding the IfcWallType I see the following

    It didn't get the length of 5 as defined in the script, However it got the material and thickness defined through and IfcMaterialLayerSet

    I am confused where the IfcWall/None is coming from.

    Here is the script in its entirety:

    import ifcopenshell
    from ifcopenshell.api import run
    import numpy
    
    ifc_file = ifcopenshell.file()
    
    project = run("root.create_entity", ifc_file, ifc_class="IfcProject", name="Demo")
    
    run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "METERS"})
    
    context = run("context.add_context", ifc_file, context_type="Model")
    body = run("context.add_context", ifc_file,context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context)
    
    
    # Create a site, building, and storey. Many hierarchies are possible.
    site = run("root.create_entity", ifc_file, ifc_class="IfcSite", name="My Site")
    building = run("root.create_entity", ifc_file, ifc_class="IfcBuilding", name="Building A")
    storey = run("root.create_entity", ifc_file, ifc_class="IfcBuildingStorey", name="Ground Floor")
    
    # Since the site is our top level location, assign it to the project
    # Then place our building on the site, and our storey in the building
    run("aggregate.assign_object", ifc_file, relating_object=project, product=site)
    run("aggregate.assign_object", ifc_file, relating_object=site, product=building)
    run("aggregate.assign_object", ifc_file, relating_object=building, product=storey)
    
    
    ifc_material = run("material.add_material", ifc_file, name="brick")
    
    ifc_walltype = run("root.create_entity", ifc_file, ifc_class="IfcWallType", name="wall_demo")
    relating_material = run("material.assign_material", ifc_file, product=ifc_walltype, type="IfcMaterialLayerSet")
    layer_set = relating_material.RelatingMaterial
    layer = run("material.add_layer", ifc_file, layer_set=layer_set, material=ifc_material)
    layer.LayerThickness = 0.2
    
    
    ifc_walltype_instance = run("root.create_entity", ifc_file, ifc_class="IfcWall", relating_type=ifc_walltype)
    
    representation = run("geometry.add_wall_representation",ifc_file,context=body,length=5,height=3,thickness=layer.LayerThickness,)
    
    matrix_1 = numpy.array(
            (
                (0.0, 0.0, 1.0, 0.0),
                (0.0, 1.0, 0.0, 0.0),
                (1.0, 0.0, 0.0, 0.0),
                (0.0, 0.0, 0.0, 1.0),
            )
        )
    
    run("geometry.edit_object_placement",ifc_file,product=ifc_walltype_instance ,matrix=matrix_1,is_si=False)
    run("spatial.assign_container", ifc_file, relating_structure=storey, product=ifc_walltype_instance)
    
    
    folder_path = "C:\\Algemeen\\00_prive\\BlenderScripts\\BlenderBIM_create_objects\\ifc_library\\" 
    filename = "place_ifcwalltype_instance_1.ifc"
    file_path = (folder_path + filename)
    print (file_path)
    ifc_file.write(file_path)
    
    

    So somewhere in the script I miss placing the IfcWallType instance correctly, as nothing in BIMVision or BlenderBIM shows up.
    Somewhere the length overrides or is not properly defined in my script.

  • edited December 2022

    Yes found it!!! I forgot to assign the representation
    Just needed to add this line at the end of the script

    run("geometry.assign_representation", ifc_file, product=ifc_walltype_instance, representation=representation)
    


    GorgioustheoryshawVDobranov
  • https://github.com/C-Claus/BlenderScripts/blob/master/BlenderBIM_create_objects/create_ifcwalltype_instance.py

    Placed the simple example script here for future reference and maybe other people find it useful.

    theoryshaw
  • edited December 2022

    Congrats, that's how I did it, too ! Thanks for sharing :)

    theoryshawCoen
  • I am stuck again, clueless how to assign a color.
    I've been reading this thread: https://community.osarch.org/discussion/1255/
    It's where @Ace and @Moult explain how a style and ifcmaterial is defined and assigned.
    But I am clueless how to do it with the ifcopenshell.api.
    When I import my ifcwalltype instance from running the script, the ifcwalltype has the ifcmateriallayer assigned, but the instance has no material assigned. I think I am still missing a crucial step.

  • edited December 2022

    @Coen I would try

                style = run("style.add_style", ifc_file, name=ifc_material.Name)
                run(
                    "style.add_surface_style",
                    ifc_file,
                    style=style,
                    attributes={
                        "SurfaceColour": {
                            "Name": ifc_material.Name,
                            "Red": 0.8,
                            "Green": 0.5,
                            "Blue": 0.2,
                        },
                        "Transparency": 0,
                        "ReflectanceMethod": "PLASTIC",
                    },
                )
                run(
                    "style.assign_material_style",
                    ifc_file,
                    material=ifc_material,
                    style=style,
                    context=context,
                )
    
    Coen
  • edited December 2022

    Nice, created this PR with @Gorgious's patch.
    Also fixed the wall to walltype assignment--wasn't working the other way, on my end.
    Also used IfcWall vs. IfcWallStandardCase... as IfcWallStandardCase is deprecated. Not sure if that was your intention, however.
    Also added a little routine, here, to remove materials with your load_ifc_automatically function.

    CoenGorgious
  • @Gorgious

    Thanks, that worked, however when opening the file in BIMvision it appears as green.

    Following the comment in this thread.
    https://community.osarch.org/discussion/comment/13735#Comment_13735
    It seems I should leave the Surface color alone, how would I do something like Ace's comment with python? I just would like to appear it with the same color as it does in BIMVision.

  • @theoryshaw said:
    Nice, created this PR with @Gorgious's patch.
    Also fixed the wall to walltype assignment--wasn't working the other way, on my end.
    Also used IfcWall vs. IfcWallStandardCase... as IfcWallStandardCase is deprecated. Not sure if that was your intention, however.
    Also added a little routine, here, to remove materials with your load_ifc_automatically function.

    Thanks, using IfcWallStandardCase was my intention , did not know it's deprecated.

    Nigel
Sign In or Register to comment.