Skip to main content

IfcOpenShell: Minimal Console Application in Python

This is a first, very basic example of a minimal console IFC application, written in Python and using the ifcopenshell library.

First steps - preparation

Create a file called minimal.py.

Let's start with importing the necessary Python libraries and provide a main function, so we can run a program from a terminal. The only thing this does right now is reading a file from the arguments and open it using ifcopenshell. There is no error catching, so you best provide it with a valid file and format.

python minimal.py IfcOpenHouse.ifc

This is the starting point of the source code in the Python file.

import sys
import ifcopenshell

# Our Main function
def main():
    ifc_file = ifcopenshell.open(sys.argv[1])

if __name__ == "__main__":
    main()    

Print the Spatial Hierarchy

We will ask for the one and only IfcProject, which returns a list of items, but it should actually only contain a single item. Nonetheless, we prefer to write this as such so whenever you query another class, it will still work when multiple items are returned.

Our print_hierarchyfunction has two arguments:

  • entity is a reference to any IFC entity, which in this case is the IfcProject. From that entity we get its Name attribute and its class using the method .is_a(), which will print IfcProjectin this example. We place everything into a formatted string, using the print method Python provides.

  • level is an integer which adds an indent before the string. At zero, there is no indent, but when we increase it, we repeatedly print .  (a dot and two spaces).

import sys
import ifcopenshell

# Our Print Hierarchy function
def print_hierarchy(entity, level):
    print("{0}{1} [{2}]".format('.  ' * level, entity.Name, entity.is_a()))

# Our Main function
def main():
    ifc_file = ifcopenshell.open(sys.argv[1])
    items = ifc_file.by_type('IfcProject')
    print_hierarchy(items[0], 0)

if __name__ == "__main__":
    main()    

When you run it now, you'll get a list of the one and only project instance.

IfcOpenHouse [IfcProject]

Follow the two main relations

To get the actual spatial hierarchy, we will not only print the object, but recursively call the same function for our all our children. There are two relations in the IFC-scheme we can follow here.

We place them inside the print_hierarchy function, so be sure to respect the indentation.

Is Decomposed By for regular objects

The first thing we need to do is to check the decomposition of the entity. This can be retrieved from the .IsDecomposedBy attribute, which refers to a list of elements. And thus we need a for loop to run through them. We check first to see if we have an entity of class IfcObjectDefinition as otherwise it would not have this attribute.

Actually, this is not entirely true. In IFC such lists are kept inside an IfcRelationship class and we don't know in advance how many of these we may have. So the call to .IsDecomposedBy does not return elements, but rather a list of relations, from which we can get to the actual related objects. So in the second nested for loop, we will ask the related objects from the attribute .RelatedObjects. And then we feed them into our print_hierarchy function. This is the recursive step, so we increase the level with 1 to get a nicely formatted indent.

# Our Print Hierarchy function (recursive)
def print_hierarchy(entity, level):
    print("{0}{1} [{2}]".format('.  ' * level, entity.Name, entity.is_a()))
    
    if entity.is_a('IfcObjectDefinition'):
        for rel in entity.IsDecomposedBy:
            related_objects = rel.RelatedObjects
            for item in related_objects:
                print_hierarchy(item, level + 1)

When we run the script now, we get a nicely indented result:

IfcOpenHouse [IfcProject]
.  None [IfcSite]
.  .  None [IfcBuilding]
.  .  .  None [IfcBuildingStorey]

Notice that this particular example does not have names for the Site and Building and Building Storey entities, so their name is returned as None.

ContainsElements for Spatial Structure Elements

We can expand this in a very similar way to also retrieve the Spatially contained elements. That way we can get to the actual elements which reside on each Building Storey.

If the entity is of class IfcSpatialStructureElement then it has an attribute .ContainsElements which, in a very similar way, returns a relationship from which we can get to the related elements via the attribute .RelatedElements, which is again a list of elements. Beware that in this case, the wording is slightly different.

    if entity.is_a('IfcSpatialStructureElement'):
        # using IfcRelContainedInSpatialElement to get contained elements
        for rel in entity.ContainsElements:
            contained_elements = rel.RelatedElements
            for element in contained_elements:
                print_hierarchy(element, level + 1)

Run the script again and now we get a much deeper output. We get all elements on the Building Storey. Not only that, but we also get their children, as they use the same .IsDecomposedBy relationship. So our recursion effectively gets us through both the .ContainsElements and .IsDecomposedBy relationships.

IfcOpenHouse [IfcProject]
.  None [IfcSite]
.  .  None [IfcBuilding]
.  .  .  None [IfcBuildingStorey]
.  .  .  .  South wall [IfcWallStandardCase]
.  .  .  .  Footing [IfcFooting]
.  .  .  .  Roof [IfcRoof]
.  .  .  .  .  South roof [IfcSlab]
.  .  .  .  .  North roof [IfcSlab]
.  .  .  .  North wall [IfcWallStandardCase]
.  .  .  .  East wall [IfcWallStandardCase]
.  .  .  .  West wall [IfcWallStandardCase]
.  .  .  .  None [IfcStairFlight]
.  .  .  .  None [IfcDoor]
.  .  .  .  None [IfcWindow]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcPlate]
.  .  .  .  None [IfcWindow]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcPlate]
.  .  .  .  None [IfcWindow]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcPlate]
.  .  .  .  None [IfcWindow]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcPlate]
.  .  .  .  None [IfcWindow]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcMember]
.  .  .  .  .  None [IfcPlate]

That's it. You can now test this code with other files using the same code. Here is the full listing or you can download the file here.

import sys
import ifcopenshell


# Our Print Hierarchy function (recursive)
def print_hierarchy(entity, level):
    print("{0}{1} [{2}]".format('.  ' * level, entity.Name, entity.is_a()))

    # using IfcRelAggregates to get spatial decomposition of spatial structure elements
    if entity.is_a('IfcObjectDefinition'):
        for rel in entity.IsDecomposedBy:
            related_objects = rel.RelatedObjects
            for item in related_objects:
                print_hierarchy(item, level + 1)

    # only spatial elements can contain building elements
    if entity.is_a('IfcSpatialStructureElement'):
        # using IfcRelContainedInSpatialElement to get contained elements
        for rel in entity.ContainsElements:
            contained_elements = rel.RelatedElements
            for element in contained_elements:
                print_hierarchy(element, level + 1)

# Our Main function
def main():
    ifc_file = ifcopenshell.open(sys.argv[1])
    items = ifc_file.by_type('IfcProject')
    print_hierarchy(items[0], 0)


if __name__ == "__main__":
    main()

Comments

Popular posts from this blog

Improve usage of BIM during early design phases

When I was collecting ideas for a book chapter on BIM (that seemed to never have emerged after that), I collected 10 ideas, which I believe still reflect good recommendations to improve the usage of BIM during the early design phases. These ideas are related to BIM software, but you can apply them in any flavor, as long as you can model with Building Elements, Spaces and have control over representation. Introduction This article gives an overview of several recommendations and tips, to better apply BIM applications and BIM methodologies, in the context of the early design phases. Many of these tips are applicable in any BIM application and they are based on experience gathered from teaching, researching and using BIM software. Sometimes they could help software developers to improve the workflow of their particular BIM implementation. Tip 1 : Gradually increase the amount of information In the early design phases, the architect makes assumptions and lays out the main design in...

PythonOCC : Open Source interactive CAD shell (and how to run it on OSX)

What is PythonOCC? PythonOCC is an Open Source (LGPL) Python-wrapper for OpenCASCADE. So what is OpenCASCADE (OCC)? This is an advanced Open Source (custom license) modeling kernel, comparable to commercial engines, such as ACIS or Parasolid, which are used in quite some commercial CAD programs. When you want to develop CAD software, you could use OCC and write programs in C++. And why using Python? With this wrapper, you can create CAD and geometry scripts in Python, which is an interpreted Object-oriented scripting language. You can write almost "on-the-fly" and seriously reduce the implementation effort, by skipping the compiling-phase. You can even interact with a running program in the Python interpreter. Want to read more about this? The OpenCASCADE official website  (currently Linux and Windows are officially supported) The PythonOCC website/blog  (beware that the core of the actions happen in the development repositories). So far so good. Now the nasty, techn...

Getting BIM data into Unity (Part 8 - Strategies to tackle IFC)

This is part 8 of a series of posts about getting BIM data into Unity. In this post, we’ll discuss IFC as a transfer format towards Unity. As with the previous post, this is not a coding post, although hints and examples are provided. Open BIM and IFC Everybody who ever met me or heard me present on a conference or BIM-lecture will not be surprised to hear that I’m a strong believer in the Industry Foundation Classes (IFC), an open standard, with already two versions published as an ISO standard, being IFC2x2 and IFC4 (but surprisingly not IFC2x3 which is widely used). In the ideal world, this would be the format to use to transfer BIM data into another environment, such as Unity. So what are our options? Looking in the Unity Asset Store Assimp is a library which supports multiple formats, including IFC. https://assetstore.unity.com/packages/tools/modeling/trilib-unity-model-loader-package-91777   I did a few attempts, but alas without any success. It is po...