IfcOpenShell Python - Calculate Quantities

Hi everyone,

Still working on a script that exports main quantities to csv. By now, I am working on calculating basic quantities (volume, area, lenght, width...) of walls, slabs, beams...
I saw there was a script here that does exactly what I want but it seems it doesn't work if ifc isn't opened in BlenderBIM (which is what I need) ? (QtoCalculator().get_net_volume() tries to access object.data source here)

Am I right ? Is there a way to achieve what I want without using FreeCAD as I saw here ?

Thanks a lot !!

«1

Comments

  • Hello @arthur595 , i saw that you pointed to my old script, so here i am :-)
    That script is the old script i created but now it's replaced by the the operator that you can find in n-panel under BlenderBim->Quantity Take Off-> Calculate All Quantities (if you want of course to calculate all quantities).
    Btw, your problem is to run that operator (or a qto_calculator function) without actually run the blender ui, right? I think that is possible, because, for example, tests run blender headlessy so maybe if you import the proper libraries you can do that. Of course, the object you are trying to calculate has to be a blender obj... This is all that i know...

  • Thanks @Massimo , I didn't know it had been updated and integrated to the UI, I'll check it out !
    And yes, indeed I would like to run my script without having to load my IFC into Blender. I'll keep searching for libraries, if you or anyone has some info about it, I'll take it :-)
    Thx

  • @arthur595 all i can suggest you is to look at how bbim runs tests (because they run blender headless) or look at "how to run blender headless" (something like this https://caretdashcaret.com/2015/05/19/how-to-run-blender-headless-from-the-command-line-without-the-gui/)

    Coenarthur595
  • edited February 2023

    Actually, my main goal is to avoid importing the IFC to Blender because it can be long in some cases. If I run Blender headless as you suggest, I would still have to import it ? (it would be a bit faster because Blender wouldn't have to render graphics).
    What I don't understand is why this operator (the qto_calculator) needs to have the file imported ? I know it uses the geometry of the objects (vertices, edges and faces) but this data actually exists inside the IFC file, right ? A wall is defined by points inside the IFC file, each point has an (X,Y,Z) position stored too.
    There should be a way to do all this math with only this data ?

    What if I decide to accept losing a bit of time importing IFC to Blender (even with the UI), what is the appropriate python code to use this operator : BlenderBim->Quantity Take Off-> Calculate All Quantities ?

    Sorry for all my questions, but I am a bit confused ! Answer what you can :-)

  • why not directly from ifcopenshell api
    qto = ifcopenshell.util.element.get_psets(element, qtos_only=True)

  • Hi @Arv ,
    Yes I tried this one but it only works if psets are exported to the IFC file. I am trying to make this script to be able to get quantities from any IFC we receive from clients. Sometimes, IFC are very poor and they don't contain any pset.
    That's why I am trying to calculcate this data. But I will definitely check if there is a pset for quantities and use it before doing all the math ;)

  • you could also try the api qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantites")
    check this https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py

  • @Arv said:
    you could also try the api qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantites")
    check this https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py

    That only creates an empty pset according to line 71 : Note that this only creates and assigns an empty quantity set.
    I would still have to do the math I guess.

  • edited February 2023

    If anyone is interested in this, I am working on recreating the base function to guess length, area and volume from IFC Element shapes.
    This is what I have by the end of the day for volume calculation, but I think it doesn't work properly (inspired by this)

    def SignedVol(shape, firstIndex_p1, firstIndex_p2, firstIndex_p3):
        vertices = shape.geometry.verts
    
        p1 = (vertices[3*firstIndex_p1], vertices[3*firstIndex_p1+1], vertices[3*firstIndex_p1+2])
        p2 = (vertices[3*firstIndex_p2], vertices[3*firstIndex_p2+1], vertices[3*firstIndex_p2+2])
        p3 = (vertices[3*firstIndex_p3], vertices[3*firstIndex_p3+1], vertices[3*firstIndex_p3+2])
    
        v321 = p3[0] * p2[1] * p1[2]
        v231 = p2[0] * p3[1] * p1[2]
        v312 = p3[0] * p1[1] * p2[2]
        v132 = p1[0] * p3[1] * p2[2]
        v213 = p2[0] * p1[1] * p3[2]
        v123 = p1[0] * p2[1] * p3[2]
    
        return (-v321 + v231 + v312 - v132 - v213 + v123)/6
    
    
    def VolMesh(elem):
        shape = geom.create_shape(settings , elem )
        faces = shape.geometry.faces
    
        faceCount = int(len(faces)/3)
        print(faceCount)
    
        totVolList = []
    
        for i in range(faceCount):
            totVolList.append(SignedVol(shape, faces[i],faces[i+1],faces[i+2]))
        print(totVolList)
        return sum(totVolList)
    
    MassimoArv
  • I created some time ago a script just to calculate the volume, see here https://community.osarch.org/discussion/1146/ifcopenshell-python-how-to-calculate-a-volume-from-an-ifc-entity#latest

    it was with the modul PythonOCC

    i Hope that helps you futher :)

    MassimoArvarthur595
  • What if I decide to accept losing a bit of time importing IFC to Blender (even with the UI), what is the appropriate python code to use this operator : BlenderBim->Quantity Take Off-> Calculate All Quantities ?

    So, in order to know the exact command to execute, it's sufficient to right click on the operator button (in blender ui) and put the mouse over the "edit source" with the option "python tooltips" activated in blender preferences (submenù interface). In this way you know the command for every operator.
    In this case the command is bpy.ops.bim.calculate_all_quantities()

    Looking at the qto_calculator functions, basically, in order to calculate the quantities (volume, length, height, etc...) qto_calculator uses blender functions or bmesh or shapely, etc.. but it's always needed the blender object to perform the calculations.

    But i think that what you are attempting to do is doable.
    For example, the trick i've done to calculate the gross volume was to recreate another blender object from ifc entity without openings and, in order to do that, i used the ifcopenshell geom functions. Look at this and also the documentation of course.

    Keep going btw, i'm also interested in what you are doing because this could be the beginning of a "native ifc" quantity takeoff method :-)

    Arvarthur595Owura_qu
  • Thank you @Martin156131 , I'll take a look !

    So, in order to know the exact command to execute, it's sufficient to right click on the operator button (in blender ui) and put the mouse over the "edit source" with the option "python tooltips" activated in blender preferences (submenù interface). In this way you know the command for every operator.

    I didn't know that, very usefull thx @Massimo !
    I am still wondering where is the link between the wall object I see in my Blender UI and the ifc wall object defined by wall = model.by_type("IfcWall")[0].
    How do I switch from one to another ?

    Anyway, I'll keep working on that, thanks for your support !

  • edited February 2023

    @arthur595 with script, if obj = bpy.context.active_object you can use entity = blenderbim.tool.Ifc.get_entity(obj) to get the ifc entity from obj.
    There is also the function blenderbim.tool.Ifc.get_object(entity) to do the inverse

    arthur595GorgiousArvOwura_qu
  • @arthur595 some hours ago, moult did a commit that could be useful also for you
    https://github.com/IfcOpenShell/IfcOpenShell/commit/ae564d1b61d3b60c345741144993df47980b77fd
    This is an awesome coincidence! :-)
    Also, he uses the same method as you to calculate the volume...

    arthur595Arv
  • This is a script I've been using to fix / insert missing / incorrect quantities that come from structural models (typically with simple concrete shapes) from Revit: https://gist.github.com/Moult/d25e4fe1c295d5fdafe1c3b2f1adeab1

    Hope it helps :)

  • Thx @Moult very usefull indeed, I am able to do what I want with your logic and code !
    I am still doing some adjustements and I'll try to post my final code on Github ! maybe even my excel dynamic tables if I manage to do smthg ok :-)

  • Slightly related to this thread

    How do you get the NetSideArea of an IfcWall using qsets = ifcopenshell.util.element.get_psets(ifc_product, qtos_only=True) ?
    I tried it on the schependomlaan.ifc as following, but I think I'm using this function in an extremely stupid way:

        qsets = ifcopenshell.util.element.get_psets(ifc_product, qtos_only=True)
    
        if (qsets.keys()):
            for keys in (qsets.keys()):
                if keys == 'BaseQuantities':
                    for keys_quantity in (qsets['BaseQuantities'].keys()):
                        print (keys_quantity)
                        if str(keys_quantity) == 'NetSideArea' :
                            print (keys_quantity)
    

    Couldn't find any documenation.

  • edited February 2023

    @Coen ifcopenshell.util.element.get_psets(ifc_product, qtos_only=True) returns a dictionary so, if you assign it to qsets, you can get the values with qsets[qto_name][quantity_name].
    In your example you can use qtos['BaseQuantities']['NetSideArea'].
    You can use also ifcopenshell.util.element.get_pset(ifc_product, 'BaseQuantities')['NetSideArea'] that documentation says is more efficient than .get_psets. A tip, if you use blender python console, you can use tab key right after ( to see the documentation about that function.
    Btw, please note that IFC provides a Qto_Pset with official quantities and the name begins with 'Qto_'.
    In your example, the 'NetSideArea' should be stored in a pset called 'Qto_WallBaseQuantities'

    Coen
  • There is now a more direct ifcopenshell.util.element.get_pset(ifc_product, "BaseQuantities", "NetSideArea") which immediately fetches the property / quantity instead of needing to manage a full dictionary and check for existence.

    MassimoAceMartin156131Coenarthur595Arv
  • edited February 2023

    Hello,
    Back with some good news ! I have managed to get a first working prototype. For now, I did the most difficult part which is calculating quantities based on the geometry. We could imagine checking if psets already contains the data we need (as Moult just explained) before calculating.
    As promised, here is the files I used !
    Sorry I am not comfortable with Github, that's why I created a zip file. (feel free to use and share this as you wish, if anyone wants to post this to Github i'll be honored)
    This zip file contains :

    • Python script to extract quantities
    • IFC example file I used to extract data
    • Excel file to process data

    [EDIT]
    Just added this to github : https://github.com/arthur-bellemin/ifcopenshell

    Here is an example of what I can get with a larger model. I did compare with the quantities from Revit, it is quite accurate (I would say about 2% diff for length and areas, and less than 1% for volume).

    CoenMartin156131AceArv
  • @arthur595

    For now, I did the most difficult part which is calculating quantities based on the geometry. We could imagine checking if psets already contains the data we need (as Moult just explained) before calculating.

    Nice! If I understand you correctly, the script checks for existing quantities in psets, if they are not present it's going to calculate the quantities from the geometry and assign them to psets? So let's say someone export a Revit model to IFC in which the 'BaseQuantities' are not exported your script can quantify it from geometry ?

  • edited February 2023

    @Coen said:
    Nice! If I understand you correctly, the script checks for existing quantities in psets, if they are not present it's going to calculate the quantities from the geometry and assign them to psets? So let's say someone export a Revit model to IFC in which the 'BaseQuantities' are not exported your script can quantify it from geometry ?

    Not exactly, The script doesn't check for existing quantities in pset (I may improve this later). It does quantify the base quantities from geometry, so wether or not your IFC file contains quantity psets, it will process elements'geometry to determine quantities.
    Updated with github link, hope it works :-)

  • @arthur595 very interesting! It would be nice also to confront the results with calculated quantities given by blenderbim (qto_calculator) and yours ...
    also, do you know that there is an awesome addon that export properties to excel, right? :-)

    Coenarthur595Ace
  • ArvArv
    edited March 2023

    @Moult said:
    There is now a more direct ifcopenshell.util.element.get_pset(ifc_product, "BaseQuantities", "NetSideArea") which immediately fetches the property / quantity instead of needing to manage a full dictionary and check for existence.

    @Moult The latest conda-forge version of ifcopenshell doesn't have ifcopenshell.util.element.get_pset yet. I guess compared to conda, the pypi versions are updated much faster, is that so?

  • edited June 2023

    @Moult said:
    There is now a more direct ifcopenshell.util.element.get_pset(ifc_product, "BaseQuantities", "NetSideArea") which immediately fetches the property / quantity instead of needing to manage a full dictionary and check for existence.

    Why is it that I have the “Qto_WallBaseQuantities” psets with their values in the BlenderBIM ui but when I try to use the code you just posted it returns nothing. I checked the result with the type() function and it confirmed “class ‘NoneType’”. In fact, I have used some other methods including the following;

    wall = file.by_type(‘IfcWall)[0]
    wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities;

    And this also returns an AttributeError (“Quantities”). What am I not doing right?

  • @Owura_qu hey, have you checked the right "qto base name"?
    In this case the right command would be:
    ifcopenshell.util.element.get_pset(product, " Qto_WallBaseQuantities", "NetSideArea")
    Note the "Qto_" at the beginning of qto base name string...

    Owura_qu
  • edited June 2023

    @Massimo said:
    Note the "Qto_" at the beginning of qto base name string...

    Thank you @Massimo for drawing my attention to the "Qto"... it works now ??. However, the other code (wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities) to display all the available quantities in the Qto_WallBaseQuantities set doesn't work. How do I retrieve the list of all the available quantities? Thank you.

  • @Owura_qu good that it worked :-)
    just a clarification, do you want all quantities assigned to an object or all available quantities for an object type defined by the schema?

  • @Owura_qu i think the error relies on the 0 in IsDefinedBy[0].
    It can be that the entity (in this case "wall") has more than one Pset associated to it so, maybe, the one that you want (Qto_WallBaseQuantities) is not the first one (the 0 in the list).
    So in order to resove it, you can insert the right number instead of the 0 or you can use a tool that is designed to retrieve the right Pset.
    In this case, i would prefer to use a tool, so if the relation indices changed the command would be the same.
    I would use
    ifcopenshell.util.element.get_pset(entity, "Qto_WallBaseQuantities")
    an you will get a dictionary with {Quantity : Value} assigned to the object in the "Qto_WallBaseQuantities" pset

  • @Massimo said:
    I would use
    ifcopenshell.util.element.get_pset(entity, "Qto_WallBaseQuantities")
    an you will get a dictionary with {Quantity : Value} assigned to the object in the "Qto_WallBaseQuantities" pset

    @Massimo exactly what I am looking for but instead of using the the utility commands I want to use the relationship approach for a specific need. And concerning the indices as you mentioned I checked and only one item is in there and it’s not about quantities. There is only “EPset_Parametric” which .HasProperties returns (# 24155=IfcPropertySingleValue(‘Engine’, $, IfcLabel (‘BlenderBIM.DumbLayer2’),$),). No related quantities to be found. Thank you.

Sign In or Register to comment.