Objectives

The goal is to analyze the structure and characteristics of the IFC (Industry Foundation Classes) format to evaluate its potential applications in construction information modeling. Specifically:

  1. Understand IFC format’s data structure and storage system, particularly focusing on how relationships between architectural elements are expressed.
  2. Analyze the characteristics and usage methods of major IFC processing tools such as ifcopenshell-python and blender-bim.
  3. Compare with existing Landbook rendering work processes to examine the practical applicability and limitations of IFC-based workflows.

Through this, we aim to identify the benefits of the IFC format in terms of data management and interoperability, and derive insights that can be used in future construction information modeling system development.


What is IFC?

IFC (Industry Foundation Classes) is a CAD data exchange schema for describing architectural, building and construction industry data. It is a platform-neutral, open data schema specification that is not controlled by a single vendor or group of vendors. It was created for information sharing between software and is being developed by buildingSMART based on international standards.


How to Read IFC


Loading and Manipulating IFC Files in Blender

  • Sample files
    • https://github.com/myoualid/ifc-101-course/tree/main/episode-01/Resources
Sample file


IFC Structure and Relationships

  • IFC의 spatial structure
    1. Project
      • aggregates
    2. Site
      • aggregates
    3. Facility: building( bridge, road, railway)
      • aggregates
    4. Building storey
      • Contains
    5. Products (building elements)
IFC spatial structure - Sample file
IFC spatial structure diagram



    file = ifcopenshell.open(r"C:\Users\MAD_SCIENTIST_21.ifc")
    project = file.by_type("IfcProject")[0]
    site = project.IsDecomposedBy[0].RelatedObjects[0]
    building = site.IsDecomposedBy[0].RelatedObjects[0]
    building_storeys = building.IsDecomposedBy[0].RelatedObjects

    for building_storey in building_storeys:
        print(building_storey)
        
    sous_sol = building_storeys[0]
    sous_sol.get_info()
    rel_contained_structure = sous_sol.ContainsElements[0]
    rel_contained_structure.RelatedElements

    for element in rel_contained_structure.RelatedElements:
        print(element)

The elements output using this Blender Python script are identical to the objects shown in the right panel in Blender (shown in the IFC spatial structure - sample file image).

출력 결과


IFC Class Inheritance Structure and Properties

IFC classes are broadly divided into rooted classes and non-rooted classes.

rooted class

  • Inherited from the ifcroot class
  • https://standards.buildingsmart.org/IFC/DEV/IFC4_3/RC1/HTML/schema/ifckernel/lexical/ifcroot.htm
  • Cardinality: whether required or not
  • The rest are optional, used for tracking items
  • 3 subclasses:
    • ifcObjectDefinition
    • ifcPropertyDefinition
    • ifcRelationship
  • Looking at attribute inheritance shows which attributes are inherited from which parent
rooted class


code

Left-click an object → object properties to view and modify IFC properties

ifc properties


ifc construction type is a relation - clicking it shows all objects of the same type

ifc construction type


Attributes and Property Sets

Property sets are very important - if they’re not present according to the schema template, they get added as custom properties. There are inherited psets that come from wall types (quantity sets are for numerical properties).

attribute와 property set


code

Let’s access the above content using ifcopenshell code:


    rooted_entities = file.by_type("IfcRoot")

    ifc_building_element_entities = set()
    for entity in rooted_entities:
        if entity.is_a("IfcBuildingElement"):
            ifc_building_element_entities.add(entity.is_a())

    my_wall = file.by_id("1K9fMEc5bCUxo4LlWWYA9b")
    my_wall.Description
    my_wall.OwnerHistory
    my_wall.Name
    my_wall.GlobalId
    my_wall.Description
    my_wall.Tag
    my_wall.PredefinedType
    my_wall.IsTypedBy[0].RelatingType
    # similar construction type 보는 것과 유사한 기능.
    my_wall.IsTypedBy[0].RelatedObjects

    my_wall.is_a()

    my_wall.is_a("IfcRoot")

    my_wall.is_a("IfcBuildingElement")

    my_wall.is_a("IfcProduct")

    my_wall.is_a("IfcWall")


Property Sets and Quantity Sets

The attributes covered above aren’t sufficient to express all information. In IFC, more information can be expressed through Property Sets (Pset) and Quantity Sets (Qset).

Property Set (Pset)

  • A collection of properties defining additional characteristics of objects
  • Standard Psets: Standard property sets defined by buildingSMART (e.g., Pset_WallCommon)
  • Custom Psets: Property sets that can be defined according to user needs
  • Main property types:
    • Single Value: Single values like strings, numbers, booleans
    • Enumerated Value: Selection from predefined list of values
    • Bounded Value: Numerical values with upper and lower bounds
    • List Value: List of multiple values
    • Table Value: 2D data structures

Among the various objects in the isDefinedBy member of IfcWall, there is an object called IfcRelDefinedsByProperties. The code loops through those where the RelatingPropertyDefinition member is an IfcPropertySet. Within the HasProperties of that pset, there are multiple IfcPropertySingleValue objects containing Name, NominalValue, etc. These are stored in a dictionary called props.

→ props are stored in psets.


    from blenderbim.bim.ifc import IfcStore
    file = IfcStore.file
    path = IfcStore.path
    my_wall.IsDefinedBy


    my_wall.IsDefinedBy[0].RelatingPropertyDefinitio
    my_wall.IsDefinedBy[1].RelatingPropertyDefinitio
    my_wall.IsDefinedBy[2].RelatingPropertyDefinitio
    my_wall.IsDefinedBy[3].RelatingPropertyDefinitio
    pset = my_wall.IsDefinedBy[3].RelatingPropertyDefinition
    pset.HasProperties[0].Name
    pset.HasProperties[0].NominalValue.wrappedValue


    psets = {}

    if my_wall.IsDefinedBy:
        for relationship in my_wall.IsDefinedBy:
            if relationship.is_a("IfcRelDefinesByProperties") and relationship.RelatingPropertyDefinition.is_a("IfcPropertySet"):
                pset = relationship.RelatingPropertyDefinition
                props = {}
                for property in pset.HasProperties:
                    if property.is_a("IfcPropertySingleValue"):
                        props[property.Name] = property.NominalValue.wrappedValue
                psets[pset.Name] = props
                print(pset.Name + " was added!")

    psets

Do we have to do this manually? → util has more sophisticated functionality.


    import ifcopenshell.util.element
    ifcopenshell.util.element.get_psets(my_wall, psets_only=True)


Quantity Set (Qset)

  • A set containing physical quantity information of objects
  • Main quantity types:
    • Length
    • Area
    • Volume
    • Weight
    • Count
    • Time

    my_wall.IsDefinedBy[0].RelatingPropertyDefinition.is_a("IfcQuantitySet")
    my_wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities

    my_wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities[0].Name
    my_wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities[0].LengthValue

    my_wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities[3].AreaValue
    my_wall.IsDefinedBy[0].RelatingPropertyDefinition.Quantities[3][3]

    qsets = {}

    if my_wall.IsDefinedBy:
        for relationship in my_wall.IsDefinedBy:
            if relationship.is_a("IfcRelDefinesByProperties") and relationship.RelatingPropertyDefinition.is_a("IfcQuantitySet"):
                qset = relationship.RelatingPropertyDefinition
                quantities = {}
                for quantity in qset.Quantities:
                    if quantity.is_a("IfcPhysicalSimpleQuantity"):
                        quantities[quantity.Name] = quantity[3]
                qsets[qset.Name] = quantities
                print(qset.Name + " was added!")

    qsets


Checking numerical units. The units for each quantity are specified. These quantities can be grouped and exported to CSV, etc.


    project = file.by_type("IfcProject")
    project = project[0]
    for unit in project.UnitsInContext.Units:
        print(unit)


BIM application with python

A guide for developing web app front-end prototypes using streamlit. Provides visualization of viewer, CSV downloads, and statistical figures.

ifc viewer


Prototyping


    import uuid
    import time
    import ifcopenshell
    import ifcopenshell.guid
    import json


    from ifc_builder import IfcBuilder

    O = 0.0, 0.0, 0.0
    X = 1.0, 0.0, 0.0
    Y = 0.0, 1.0, 0.0
    Z = 0.0, 0.0, 1.0


    create_guid = lambda: ifcopenshell.guid.compress(uuid.uuid1().hex)


    # IFC template creation

    filename = "hello_wall.ifc"
    # https://standards.buildingsmart.org/IFC/DEV/IFC4_3/RC2/HTML/schema/ifcdatetimeresource/lexical/ifctimestamp.htm
    # NOTE: 초단위.
    timestamp = int(time.time())
    timestring = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(timestamp))
    creator = "CKC"
    organization = "SWK"
    application, application_version = "IfcOpenShell", "0.7.0"
    project_globalid, project_name = create_guid(), "Hello Wall"

    # A template IFC file to quickly populate entity instances for an IfcProject with its dependencies
    template = (
        """ISO-10303-21;
    HEADER;
    FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
    FILE_NAME('%(filename)s','%(timestring)s',('%(creator)s'),('%(organization)s'),'%(application)s','%(application)s','');
    FILE_SCHEMA(('IFC4'));
    ENDSEC;
    DATA;
    #1=IFCPERSON($,$,'%(creator)s',$,$,$,$,$);
    #2=IFCORGANIZATION($,'%(organization)s',$,$,$);
    #3=IFCPERSONANDORGANIZATION(#1,#2,$);
    #4=IFCAPPLICATION(#2,'%(application_version)s','%(application)s','');
    #5=IFCOWNERHISTORY(#3,#4,$,.ADDED.,$,#3,#4,%(timestamp)s);
    #6=IFCDIRECTION((1.,0.,0.));
    #7=IFCDIRECTION((0.,0.,1.));
    #8=IFCCARTESIANPOINT((0.,0.,0.));
    #9=IFCAXIS2PLACEMENT3D(#8,#7,#6);
    #10=IFCDIRECTION((0.,1.,0.));
    #11=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#9,#10);
    #12=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0);
    #13=IFCSIUNIT(\*,.LENGTHUNIT.,$,.METRE.);
    #14=IFCSIUNIT(\*,.AREAUNIT.,$,.SQUARE_METRE.);
    #15=IFCSIUNIT(\*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
    #16=IFCSIUNIT(\*,.PLANEANGLEUNIT.,$,.RADIAN.);
    #17=IFCMEASUREWITHUNIT(IFCPLANEANGLEMEASURE(0.017453292519943295),#16);
    #18=IFCCONVERSIONBASEDUNIT(#12,.PLANEANGLEUNIT.,'DEGREE',#17);
    #19=IFCUNITASSIGNMENT((#13,#14,#15,#18));
    #20=IFCPROJECT('%(project_globalid)s',#5,'%(project_name)s',$,$,$,$,(#11),#19);
    ENDSEC;
    END-ISO-10303-21;
    """
        % locals()
    )

    def run():
        print(type(template))
        # Write the template to a temporary file
        # temp_handle, temp_filename = tempfile.mkstemp(suffix=".ifc", text=True)
        # print(temp_filename)
        # with open(temp_filename, "w", encoding="utf-8") as f:
        #     f.write(template)
        # os.close(temp_handle)
        temp_filename = "temp.ifc"
        with open(temp_filename, "w", encoding="utf-8") as f:
            f.write(template)
        print(template)
        with open("result-bldg.json", "r", encoding="utf-8") as f:
            bldg_info = json.load(f)

        # Obtain references to instances defined in template
        ifc_file_template = ifcopenshell.open(temp_filename)

        # IFC hierarchy creation
        ifc_builder = IfcBuilder(bldg_info, ifc_file_template)

        # site 생성
        site_placement, site = ifc_builder.add_site("Site")
        # project -> site
        ifc_builder.add_rel_aggregates("Project Container", ifc_builder.project, [site])

        # building 생성
        building_placement, building = ifc_builder.add_building("Building", relative_to=site_placement)
        # site -> building
        ifc_builder.add_rel_aggregates("Site Container", site, [building])

        # storeys 생성
        storey_placement_list = []
        building_storey_list = []
        for i in range(bldg_info["unit_info"]["general"]["floor_count"]):
            storey_placement, building_storey = ifc_builder.add_storey(
                f"Storey_{i+1}", building_placement, elevation=i * 6.0
            )
            storey_placement_list.append(storey_placement)
            building_storey_list.append(building_storey)

        print(building_storey_list)
        # building -> storeys
        ifc_builder.add_rel_aggregates("Building Container", building, building_storey_list)

        ifc_walls = []

        # 외벽과 창문
        for i, (exterior_wall_polyline_each_floor, exterior_wall_area_each_floor, window_area_each_floor) in enumerate(
            zip(
                ifc_builder.ifc_preprocess.exterior_wall_polyline,
                ifc_builder.ifc_preprocess.exterior_wall_area,
                ifc_builder.ifc_preprocess.window_area,
            )
        ):

            for (
                exterior_wall_polyline_in_floor_polygon,
                exterior_wall_area_in_floor_polygon,
                window_area_in_floor_polygon,
            ) in zip(
                exterior_wall_polyline_each_floor,
                exterior_wall_area_each_floor,
                window_area_each_floor,
            ):
                for (exterioir_wall_polyline, exterior_wall_area, window_area_list) in zip(
                    exterior_wall_polyline_in_floor_polygon,
                    exterior_wall_area_in_floor_polygon,
                    window_area_in_floor_polygon,
                ):
                    print(storey_placement_list[i])
                    print(building_storey_list[i])

                    wall, windows = ifc_builder.add_exterior_wall(
                        storey_placement_list[i], exterioir_wall_polyline, exterior_wall_area, window_area_list
                    )

                    ifc_walls.append(wall)

                    ifc_builder.add_rel_contained_in_spatial_structure(
                        "Building Storey Container", building_storey_list[i], wall
                    )
                    print(windows)
                    for window in windows:
                        ifc_builder.add_rel_contained_in_spatial_structure(
                            "Building Storey Container", building_storey_list[i], window
                        )

        # 층 바닥
        for i, floor_slab_polyline_each_floor in enumerate(ifc_builder.ifc_preprocess.floor_slab_polyline):
            ifc_floor_slabs_in_a_floor = []
            for floor_slab_polyline_each_polygon in floor_slab_polyline_each_floor:
                floor_slab = ifc_builder.add_floor_slab(0.2, storey_placement_list[i], floor_slab_polyline_each_polygon)
                # , building_storey_list[i]
                print(floor_slab)
                ifc_floor_slabs_in_a_floor.append(floor_slab)
                ifc_builder.add_rel_contained_in_spatial_structure(
                    "Building Storey Container",
                    building_storey_list[i],
                    floor_slab,
                )

        # 세대 내벽
        ifc_interior_walls = []
        for i, (interior_wall_polyline_each_floor, interior_wall_area_each_floor) in enumerate(
            zip(
                ifc_builder.ifc_preprocess.interior_wall_polyline,
                ifc_builder.ifc_preprocess.interior_wall_area,
            )
        ):
            for (
                interior_wall_polyline,
                interior_wall_area,
            ) in zip(interior_wall_polyline_each_floor, interior_wall_area_each_floor):

                print(storey_placement_list[i])
                print(building_storey_list[i])

                wall = ifc_builder.add_interior_wall(
                    storey_placement_list[i],
                    interior_wall_polyline,
                    interior_wall_area,
                )

                ifc_interior_walls.append(wall)

                ifc_builder.add_rel_contained_in_spatial_structure(
                    "Building Storey Container", building_storey_list[i], wall
                )

        # material 적용 방식중 맞는 방식 확인 필요.
        # ifc_builder.add_material_style(ifc_interior_walls, ifc_builder.wall_style)
        # ifc_builder.add_material_style(ifc_walls, ifc_builder.glass_style)

        for i, flight_info_each_floor in enumerate(ifc_builder.ifc_preprocess.flight_info):
            ifc_stair = ifc_builder.add_stair(
                flight_info_each_floor,
                storey_placement_list[i],
            )
            ifc_builder.add_rel_contained_in_spatial_structure(
                "Building Storey Container", building_storey_list[i], ifc_stair
            )

        print(filename)

        # Write the contents of the file to disk
        ifc_builder.build()


    if __name__ == "__main__":
        run()


results


Conclusion

The IFC format has a much more systematic structure than expected, capable of storing a wide variety of elements. Due to this comprehensive structure, documentation and practical usage can be somewhat complex. The structural characteristic of defining containment and association relationships as entities is particularly impressive and serves as a good reference for future use.

While internet resources on IFC creation using ifcopenshell-python are relatively scarce, blender-bim is used as the main tool instead, and information about other programming language bindings and tools is relatively abundant.

From a rendering work perspective, compared to existing methods, there isn’t much difference in efficiency as necessary elements still need to be created directly. However, even though the same preprocessing is required, there are advantages in terms of being able to store, provide, and manage this information.


References