Updating and persisting a material property

edited August 2022 in General

First of all: thanks for the great Tutorials on IfcOpenShell. Without those I wouldn't know where to even begin! Getting that out of the way, I'm very new to the Ifc format and therefore very new to IfcOpenShell.

I have a wall object with a pset attached to the material properties. Now I want to change a value of one of the material properties. In this case it's about "Test2" which should take the value of "Test1". This means I have to a) extract the value of "Test1" (which is 0.629...) and replace the value of "Test2" by exactly that number.

I've managed to do that in Python, however, I don't know how to proceed from there to "persist" or write the ifc file with the new values. This is what my current code looks like:

import ifcopenshell
import ifcopenshell.util
import ifcopenshell.util.element
import ifcopenshell.util.pset
import ifcopenshell.geom

ifc = ifcopenshell.open("./ifc_file.ifc")

materials = ifc.by_type("IfcMaterial")
wall = ifc.by_type("IfcWall")[0]
for material in materials:
        psets = ifcopenshell.util.element.get_psets(material)
        for key, pset in psets.items():
                if key.startswith("HHLA"):
                    print(pset)
                    pset["Test2"] = pset["Test1"]
                    print(pset)

The question is: how exactly do I manage to write an updated ifc file with everything being the same as the old ifc file but the new entries for Test2? When using "write", I end up having the same file which I had before.

If anything is unclear or if there is a much less complicated way to achieve what I want to do, please let me know.

I've uploaded an excerpt of what the IFC file looks like in FZKViewer here: https://community.osarch.org/uploads/editor/1b/zlmpu5jlz2cj.png

Comments

  • This should help :)

    ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Test2": pset["Test1"}})
    # and once you're done...
    ifc.write("/path/to/edited.ifc")
    
    randomluck
  • @Moult said:
    This should help :)

    ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Test2": pset["Test1"]})
    # and once you're done...
    ifc.write("/path/to/edited.ifc")
    

    Hey there! Thanks a lot fo answering this quick and sorry for answering in a very verbose way since I want to make sure that I don't miss the simple way (again) :)

    I'm having a type issue when reading in the material properties which end up being a list which doesn't have an attribtue called is_a (that's what my error message says, which looks as following):

     File "c:\path\main.py", line 39, in <module>
        pset = ifcopenshell.util.element.get_psets(ifc.by_type("IfcMaterial"), psets_only=True)
      File "C:\Program Files\Python310\lib\site-packages\ifcopenshell\util\element.py", line 24, in get_psets
        if element.is_a("IfcTypeObject"):
    AttributeError: 'list' object has no attribute 'is_a'
    

    What I am trying to do is to get the correspondig psets of my materials to use in the api call described in your post (ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset <- I'm looking for the pset to pass to the function here). If I access the materials with ifcopenshell.util.element.get_psets(ifc.by_type("IfcMaterial"), psets_only=True), I'm getting a list which is not the expected data type to pass as pset if I understand the error messages correctly.

    If I keep iterating over the dictionary (as shown below), I keep getting an AttributeError since my dict object doesn't have an attribute called Name which is expected by the edit_pset method.

    propertyname = "Test2"
    for material in materials:
            psets = ifcopenshell.util.element.get_psets(material)
            for key, pset in psets.items():
                    if propertyname in pset.keys():
                            ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Test2": pset["Test1"]})
    

    What is the correct way to query to actually get a data type which is compatible with the edit_pset method? I've tried to find a solution in this thread. There is a good example (coincidentally written by yourself as well) here which seems to work for psets of walls, however, I can't access the psets of my materials in the same way without ending up with an error message.

  • ifc.by_type('IfcMaterial') returns a list of all materials in your project. You can get the psets of a single material by doing:

    for material in ifc.by_type('IfcMaterial'):
        psets = ifcopenshell.util.element.get_psets(material)
    

    get_psets returns a dictionary of psets, whereas the API expects an element. Each pset has an ID, which you can use to fetch the element.

            for key, pset in psets.items():
                    if propertyname in pset.keys():
                            ifcopenshell.api.run("pset.edit_pset", ifc, pset=ifc.by_id(pset["id"]), properties={"Test2": pset["Test1"]})
    

    Also of interest is the ifcopenshell.util.element.get_material(element) function :)

    randomluck
  • Thanks again @Moult - I've been awkward for some reasons. I didn't get right away that when looping over my pset keys, ifc.by_id(pset["id"] would already get the pset keys so I was trying a way more complicated solution by passing the id's by parsing them outside of the loop until I've noticed that I can leave it as is haha. The usual fun and "aha"-moments when digging into a new library :)

    To wrap it up, this is what my solution looks like:


    def modify_mat_pset(input, output, propertyname, newvalue): ifc = ifcopenshell.open(input) materials = ifc.by_type("IfcMaterial") for material in materials: psets = ifcopenshell.util.element.get_psets(material) for key, pset in psets.items(): if propertyname in pset.keys(): ifcopenshell.api.run( "pset.edit_pset", file = ifc, pset = ifc.by_id(pset["id"]), properties = {propertyname:newvalue} ) ifc.write(output)

    The solution, however, is only good with prior knowledge of the property I want to change. It's also not (yet) considered that if there is a property set that shoulnd't be modified which however includes a property with the same name (since I'm querying for the propertyname (if propertyname in pset.keys()).

    Thanks again and I'll take a look at the ifcopenshell.util.element.get_material(element)! :)

    theoryshaw
  • Cheers, glad it works now :)

    randomluck
  • edited March 20

    .

Sign In or Register to comment.