IFC format study
Objectives
IFC(Industry Foundation Classes) 포맷의 구조와 특성을 분석하여 건설 정보 모델링에서의 활용 가능성을 평가하는 것이다. 구체적으로는:
- IFC 포맷의 데이터 구조와 저장 체계를 이해하고, 특히 건축 요소들 간의 관계성 표현 방식을 파악한다.
- ifcopenshell-python과 blender-bim 등 주요 IFC 처리 도구들의 특성과 활용 방법을 분석한다.
- 기존 랜드북 렌더링 작업 프로세스와 비교하여 IFC 기반 워크플로우의 실무 적용 가능성과 한계점을 검토한다.
이를 통해 IFC 포맷이 제공하는 데이터 관리 및 상호운용성 측면의 이점을 파악하고, 향후 건설 정보 모델링 시스템 개발에 활용할 수 있는 인사이트를 도출하고자 한다.
What is IFC?
IFC (Industry Foundation Classes)는 건축, 건물 및 건설 산업 데이터를 설명하기 위한 CAD 데이터 교환 데이터 스키마이다. 이는 단일 공급업체 또는 공급업체 그룹에 의해 제어되지 않는 플랫폼 중립적인 개방형 데이터 스키마 사양이다. 소프트 웨어간 정보 공유를 위해서 만들어졌으며 국제표준기반으로 buildingSMART에 의해 개발되고 있다.
- 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
- 예시 파일
- https://github.com/myoualid/ifc-101-course/tree/main/episode-01/Resources

IFC 형식의 구조와 관계들
- 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)
bleder python script로 다음 코드를 이용하여 출력한 요소들은 blender에서 우측 패널에 보이는 object들과 동일하다. (IFC의 spatial structure - 예시 파일 그림의 패널)

IFC Class Inheritance Structure and Properties
크게 rooted class, non-rooted class로 나뉜다.
rooted class
- ifcroot라는 class로부터 상속
- cardinality: 필수인지 아닌지
- 나머지는 optional인데 track item 잘 하기 위한 용도
- 3가지 subclass
- ifcObjectDefinition
- ifcPropertyDefinition
- ifcRelationship
- attribute inheritance 살펴펴보면, 어떤 부모로부터 어떤 attribute를 상속한 것인지 볼 수 있다.

code
object 좌클릭, → object property 누르면 ifc 속성들을 보고 수정할 수 있다.

ifc construction type 은 relation인데 클릭하면 같은 타입을 다 볼 수 있다.

Attributes and Property Sets
property set이 매우 중요한데 schema에 주어진 template 대로 이 부분이 없으면, 지정한 custom으로 add 된다. inherited pset이 있는데, wall type으로부터 온다(quantity set은 수치에 대한 속성).

code
위의 내용들을 ifcopenshell 코드로 접근해보자.
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
위에서 attribute들을 다뤘는데, 이걸로는 정보가 충분히 표현되지 않는다. IFC에서는 property set(Pset)과 quantity set(Qset)을 통해 더 많은 정보를 표현할 수 있다.
Property Set (Pset)
- 객체의 추가적인 특성을 정의하는 속성들의 집합
- 표준 Pset: buildingSMART에서 정의한 표준 속성 집합 (예: Pset_WallCommon)
- Custom Pset: 사용자가 필요에 따라 정의할 수 있는 속성 집합
- 주요 속성 타입:
- Single Value: 문자열, 숫자, 불리언 등의 단일 값
- Enumerated Value: 미리 정의된 값 목록에서 선택
- Bounded Value: 상한값과 하한값이 있는 수치
- List Value: 여러 값의 목록
- Table Value: 2차원 데이터 구조
IfcWall의 isDefinedBy member에 있는 여러 객체 중 IfcRelDefinedsByProperties라는 객체가 있는데, 그 객체의 RelatingPropertyDefinition member가, IfcPropertySet을 인 것들을 대상으로 loop를 돈다. 그 pset의 HasProperties안에는 여러 IfcPropertySingleValue들이 있고, Name과 NominalValue등을 가지고 있다. 이걸 props에 dict로 저장
→ props들은 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
이걸 다 직접 해야하나? → util에 있음. 더 정교한 기능으로 구현되어 있다.
import ifcopenshell.util.element
ifcopenshell.util.element.get_psets(my_wall, psets_only=True)
Quantity Set (Qset)
- 객체의 물리적 수량 정보를 포함하는 집합
- 주요 수량 타입:
- 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
수치단위 확인. 어떤 수치가 어떤 단위인지 명시되어 있다. 이 quantity들을 묶어서 export csv 등이 가능하다.
project = file.by_type("IfcProject")
project = project[0]
for unit in project.UnitsInContext.Units:
print(unit)
BIM application with python
streamlit을 활용한 web app front-end proto 개발 가이드. viewer 와 download csv, 통계 수치 등을 시각화 해준다.

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
IFC 포맷은 예상했던 것보다 훨씬 더 다양한 요소들을 저장할 수 있도록 체계적인 구조를 갖추고 있다. 이러한 포괄적인 구조로 인해 문서화와 실제 사용법이 다소 복잡한 면이 있다. 특히 포함 관계나 연관 관계를 entity로 정의하는 구조적 특징은 매우 인상적이며, 향후 참고할만한 좋은 사례로 보임. ifcopenshell-python을 이용한 IFC 생성에 관한 인터넷 자료는 상대적으로 부족한 편이지만 대신 blender-bim이 주된 도구로 사용되고 있으며, 다른 프로그래밍 언어 바인딩이나 도구들에 대한 정보는 비교적 풍부합니다. 렌더링 작업 측면에서는 기존 방식과 비교했을 때 필요한 요소들을 직접 생성해야 하는 점에서 큰 차이가 없어 작업 효율성 향상을 기대하기는 어려워 보인다. 다만, 동일한 사전 처리 과정이 필요하더라도 이를 저장하고 제공하거나 관리할 수 있다는 점에서 장점이 있을 것으로 판단됨.