IFC format study
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:
- Understand IFC format’s data structure and storage system, particularly focusing on how relationships between architectural elements are expressed.
- Analyze the characteristics and usage methods of major IFC processing tools such as ifcopenshell-python and blender-bim.
- 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.
- https://en.wikipedia.org/wiki/Industry_Foundation_Classes
- https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=silvury14&logNo=10177489179
- https://en.wikipedia.org/wiki/BuildingSMART
How to Read IFC
- UI - blender
- console - blenderBIM (ifcopenshell - python)
Loading and Manipulating IFC Files in Blender
- Sample files
- https://github.com/myoualid/ifc-101-course/tree/main/episode-01/Resources

IFC Structure and Relationships
- IFC의 spatial structure
- Project
- aggregates
- Site
- aggregates
- Facility: building( bridge, road, railway)
- aggregates
- Building storey
- Contains
- Products (building elements)
- Project


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

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

ifc construction type is a relation - clicking it shows all objects of the same 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).

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.

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()




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.