IFC Model Creation Workflows
Quick Reference
Decision Tree: Starting a New IFC Model
Creating a new IFC model?
├── Production model with proper header/metadata?
│ └── YES → model = ifcopenshell.api.run("project.create_file", version="IFC4")
│
└── Bare-minimum test file?
└── model = ifcopenshell.file(schema="IFC4")
└── WARNING: No header, no project, no units. Add these manually.
Decision Tree: Choosing IFC Schema Version
Which schema version?
├── Building/architecture project?
│ ├── Legacy compatibility needed? → IFC2X3
│ │ └── WARNING: OwnerHistory is REQUIRED on all rooted entities
│ └── Modern project? → IFC4
│
├── Infrastructure project (roads, bridges, tunnels)?
│ └── IFC4X3
│ └── Adds: IfcBridge, IfcRoad, IfcTunnel, IfcAlignment
│
└── Unsure? → Default to IFC4
Decision Tree: Element Creation Order
Building an IFC model from scratch?
Follow this EXACT order:
1. Create file → project.create_file
2. Create IfcProject → root.create_entity
3. Assign units → unit.assign_unit
4. Add contexts → context.add_context (Model + Body subcontext)
5. Create spatial hierarchy:
a. IfcSite → root.create_entity + aggregate.assign_object
b. IfcBuilding → root.create_entity + aggregate.assign_object
c. IfcBuildingStorey → root.create_entity + aggregate.assign_object
6. Create types (optional but recommended):
a. IfcWallType etc. → root.create_entity
b. Assign materials → material.add_material_set + material.assign_material
7. Create elements:
a. Create entity → root.create_entity
b. Add geometry → geometry.add_wall_representation (etc.)
c. Assign geometry → geometry.assign_representation
d. Set placement → geometry.edit_object_placement
e. Assign container → spatial.assign_container
f. Assign type → type.assign_type
8. Add properties → pset.add_pset + pset.edit_pset
9. Write file → model.write("output.ifc")
Critical Warnings
- ALWAYS use
ifcopenshell.api.run() for entity creation. NEVER use model.create_entity() directly — the API handles GlobalId generation, OwnerHistory, and validation automatically.
- ALWAYS create the spatial hierarchy (Project → Site → Building → Storey) before creating physical elements. Elements without spatial containment are invisible in most BIM viewers.
- ALWAYS assign spatial containment via
spatial.assign_container for physical elements (walls, slabs, columns, doors). Without containment, elements float in the model with no spatial context.
- ALWAYS set up geometric contexts (Model → Body subcontext) before creating geometry. Geometry without a context cannot be displayed.
- ALWAYS assign units via
unit.assign_unit immediately after creating the project. Without units, all dimensions are ambiguous.
- NEVER skip
geometry.edit_object_placement after creating an element. Without a placement, the element has no position in 3D space.
- NEVER create physical elements and types with mismatched IFC classes. An IfcWall occurrence MUST use IfcWallType, not IfcSlabType.
- IFC2X3 ONLY: OwnerHistory is REQUIRED on every rooted entity. The
root.create_entity API handles this automatically, but ONLY if an owner/user has been set via owner.set_user. Call owner.set_user before creating any entities in IFC2X3.
Essential Patterns
Pattern 1: Complete IFC4 Model from Scratch
# IfcOpenShell: IFC4
import ifcopenshell
import ifcopenshell.api
# Step 1: Create file with proper header
model = ifcopenshell.api.run("project.create_file", version="IFC4")
# Step 2: Create project
project = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcProject", name="My Project")
# Step 3: Assign SI units (meters, radians)
ifcopenshell.api.run("unit.assign_unit", model)
# Step 4: Set up geometric context
model3d = ifcopenshell.api.run("context.add_context", model,
context_type="Model")
body = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Body",
target_view="MODEL_VIEW", parent=model3d)
# Step 5: Create spatial hierarchy
site = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcSite", name="Default Site")
building = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuilding", name="Building A")
storey = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="Ground Floor")
ifcopenshell.api.run("aggregate.assign_object", model,
products=[site], relating_object=project)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[building], relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[storey], relating_object=building)
# Step 6: Create wall with geometry
wall = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWall", name="Wall 001")
representation = ifcopenshell.api.run("geometry.add_wall_representation",
model, context=body, length=5.0, height=3.0, thickness=0.2)
ifcopenshell.api.run("geometry.assign_representation", model,
product=wall, representation=representation)
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
ifcopenshell.api.run("spatial.assign_container", model,
relating_structure=storey, products=[wall])
# Step 7: Add properties
pset = ifcopenshell.api.run("pset.add_pset", model,
product=wall, name="Pset_WallCommon")
ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={
"IsExternal": True,
"LoadBearing": True,
"FireRating": "REI90"
})
# Step 8: Write
model.write("output.ifc")
Pattern 2: IFC2X3 Model with Required OwnerHistory
# IfcOpenShell: IFC2X3
import ifcopenshell
import ifcopenshell.api
# Create IFC2X3 file
model = ifcopenshell.api.run("project.create_file", version="IFC2X3")
# REQUIRED for IFC2X3: Set up owner BEFORE creating any entities
person = ifcopenshell.api.run("owner.add_person", model,
identification="jdoe", family_name="Doe", given_name="John")
org = ifcopenshell.api.run("owner.add_organisation", model,
identification="ACME", name="ACME Engineering")
user = ifcopenshell.api.run("owner.add_person_and_organisation", model,
person=person, organisation=org)
application = ifcopenshell.api.run("owner.add_application", model)
ifcopenshell.api.run("owner.set_user", model, user=user)
# Now create entities: OwnerHistory is automatically attached
project = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcProject", name="Legacy Project")
ifcopenshell.api.run("unit.assign_unit", model)
site = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcSite", name="Site")
building = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuilding", name="Building")
storey = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="Ground Floor")
ifcopenshell.api.run("aggregate.assign_object", model,
products=[site], relating_object=project)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[building], relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[storey], relating_object=building)
Pattern 3: Spatial Hierarchy with Multiple Storeys
# IfcOpenShell: IFC4
# After project, units, and contexts are set up:
site = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcSite", name="Main Site")
building = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuilding", name="Office Block")
# Create multiple storeys
basement = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="Basement")
ground = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="Ground Floor")
first = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="First Floor")
roof = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="Roof")
# Aggregate: Project → Site → Building → all storeys
ifcopenshell.api.run("aggregate.assign_object", model,
products=[site], relating_object=project)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[building], relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[basement, ground, first, roof], relating_object=building)
# Set storey elevations using object placement
import numpy as np
for storey_obj, elevation in [(basement, -3.0), (ground, 0.0),
(first, 3.5), (roof, 7.0)]:
matrix = np.eye(4)
matrix[2][3] = elevation # Z translation = elevation
ifcopenshell.api.run("geometry.edit_object_placement", model,
product=storey_obj, matrix=matrix)
Pattern 4: Creating Elements with Type Assignment
# IfcOpenShell: IFC4
# Create a wall type with material layers
wall_type = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWallType", name="WT-200 External")
# Set up material layers for the type
layer_set = ifcopenshell.api.run("material.add_material_set", model,
name="WT-200", set_type="IfcMaterialLayerSet")
brick = ifcopenshell.api.run("material.add_material", model,
name="Brick", category="brick")
insulation = ifcopenshell.api.run("material.add_material", model,
name="Insulation", category="mineral wool")
plaster = ifcopenshell.api.run("material.add_material", model,
name="Gypsum Plaster", category="gypsum")
layer1 = ifcopenshell.api.run("material.add_layer", model,
layer_set=layer_set, material=brick)
ifcopenshell.api.run("material.edit_layer", model,
layer=layer1, attributes={"LayerThickness": 0.102})
layer2 = ifcopenshell.api.run("material.add_layer", model,
layer_set=layer_set, material=insulation)
ifcopenshell.api.run("material.edit_layer", model,
layer=layer2, attributes={"LayerThickness": 0.080})
layer3 = ifcopenshell.api.run("material.add_layer", model,
layer_set=layer_set, material=plaster)
ifcopenshell.api.run("material.edit_layer", model,
layer=layer3, attributes={"LayerThickness": 0.018})
ifcopenshell.api.run("material.assign_material", model,
products=[wall_type], type="IfcMaterialLayerSet", material=layer_set)
# Create wall occurrences and assign the type
wall1 = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWall", name="Wall 001")
wall2 = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWall", name="Wall 002")
ifcopenshell.api.run("type.assign_type", model,
related_objects=[wall1, wall2], relating_type=wall_type)
Pattern 5: Creating Openings (Doors and Windows)
# IfcOpenShell: IFC4
# Prerequisites: wall exists, body context exists, storey exists
# Create opening element
opening = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcOpeningElement", name="Door Opening")
opening_rep = ifcopenshell.api.run("geometry.add_wall_representation",
model, context=body, length=0.9, height=2.1, thickness=0.2)
ifcopenshell.api.run("geometry.assign_representation", model,
product=opening, representation=opening_rep)
ifcopenshell.api.run("geometry.edit_object_placement", model,
product=opening)
# Cut opening in wall (boolean subtraction)
ifcopenshell.api.run("void.add_opening", model,
opening=opening, element=wall)
# Create door and fill the opening
door = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcDoor", name="Door 001")
ifcopenshell.api.run("void.add_filling", model,
opening=opening, element=door)
ifcopenshell.api.run("spatial.assign_container", model,
relating_structure=storey, products=[door])
Pattern 6: Property Sets and Quantity Sets
# IfcOpenShell: IFC4
# Add standard property set to wall
pset = ifcopenshell.api.run("pset.add_pset", model,
product=wall, name="Pset_WallCommon")
ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={
"Reference": "WT-200",
"IsExternal": True,
"LoadBearing": True,
"FireRating": "REI60",
"ThermalTransmittance": 0.28
})
# Add quantity set
qto = ifcopenshell.api.run("pset.add_qto", model,
product=wall, name="Qto_WallBaseQuantities")
ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={
"Length": 5.0,
"Height": 3.0,
"Width": 0.2,
"GrossSideArea": 15.0,
"NetSideArea": 13.11,
"GrossVolume": 3.0
})
# Custom property set (use project-specific prefix, NOT "Pset_")
custom_pset = ifcopenshell.api.run("pset.add_pset", model,
product=wall, name="ACME_WallData")
ifcopenshell.api.run("pset.edit_pset", model, pset=custom_pset, properties={
"CostCode": "STR-W-001",
"InstallDate": "2026-03-15",
"Inspector": "J. Doe"
})
Pattern 7: Slab and Column Creation
# IfcOpenShell: IFC4
# Slab with polyline footprint
slab = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcSlab", name="Floor Slab", predefined_type="FLOOR")
slab_rep = ifcopenshell.api.run("geometry.add_slab_representation", model,
context=body, depth=0.25,
polyline=[(0.0, 0.0), (10.0, 0.0), (10.0, 8.0), (0.0, 8.0)])
ifcopenshell.api.run("geometry.assign_representation", model,
product=slab, representation=slab_rep)
ifcopenshell.api.run("geometry.edit_object_placement", model, product=slab)
ifcopenshell.api.run("spatial.assign_container", model,
relating_structure=storey, products=[slab])
# Column with profile
column = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcColumn", name="Column C1")
profile = ifcopenshell.api.run("profile.add_parameterised_profile", model,
ifc_class="IfcRectangleProfileDef", XDim=0.3, YDim=0.3)
col_rep = ifcopenshell.api.run("geometry.add_profile_representation", model,
context=body, profile=profile, depth=3.0)
ifcopenshell.api.run("geometry.assign_representation", model,
product=column, representation=col_rep)
ifcopenshell.api.run("geometry.edit_object_placement", model, product=column)
ifcopenshell.api.run("spatial.assign_container", model,
relating_structure=storey, products=[column])
Version Notes
IFC2X3 vs IFC4+ Entity Differences
| Feature |
IFC2X3 |
IFC4 / IFC4X3 |
| OwnerHistory |
REQUIRED on all rooted entities |
OPTIONAL |
| IfcWallStandardCase |
EXISTS (use for layered walls) |
DEPRECATED (use IfcWall with PredefinedType) |
| IfcBuildingStorey |
Only valid spatial container |
IfcBuildingStorey + IfcSpace + IfcFacility |
| Property set types |
IfcPropertySingleValue only |
Adds IfcPropertyBoundedValue, IfcPropertyTableValue |
| Material sets |
IfcMaterialList (legacy) |
IfcMaterialConstituentSet (preferred) |
IFC4X3 Infrastructure Entities (Not Available in IFC2X3/IFC4)
IfcBridge, IfcBridgePart
IfcRoad, IfcRoadPart
IfcRailway, IfcRailwayPart
IfcTunnel, IfcTunnelPart
IfcAlignment, IfcLinearElement
IfcFacility, IfcFacilityPart
Aggregation Hierarchy by Schema
| Schema |
Standard Hierarchy |
| IFC2X3 |
IfcProject → IfcSite → IfcBuilding → IfcBuildingStorey → IfcSpace |
| IFC4 |
IfcProject → IfcSite → IfcBuilding → IfcBuildingStorey → IfcSpace |
| IFC4X3 |
IfcProject → IfcSite → IfcFacility (or IfcBuilding/IfcBridge/IfcRoad) → IfcFacilityPart (or IfcBuildingStorey) → IfcSpace |
Type-to-Occurrence Mapping (Common Elements)
| Element Type |
Occurrence Class |
Notes |
| IfcWallType |
IfcWall |
IFC2X3: also IfcWallStandardCase |
| IfcSlabType |
IfcSlab |
Use predefined_type: FLOOR, ROOF, BASESLAB, LANDING |
| IfcColumnType |
IfcColumn |
|
| IfcBeamType |
IfcBeam |
|
| IfcDoorType |
IfcDoor |
IFC2X3: IfcDoorStyle (not IfcDoorType) |
| IfcWindowType |
IfcWindow |
IFC2X3: IfcWindowStyle (not IfcWindowType) |
| IfcCurtainWallType |
IfcCurtainWall |
|
Common Operations
Set Up Geometric Contexts
# IfcOpenShell: IFC4
# Root context (REQUIRED before any subcontexts)
model3d = ifcopenshell.api.run("context.add_context", model,
context_type="Model")
# Body subcontext (REQUIRED for 3D solid geometry)
body = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Body",
target_view="MODEL_VIEW", parent=model3d)
# Optional: Axis subcontext (for wall/beam centerlines)
axis = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Axis",
target_view="GRAPH_VIEW", parent=model3d)
# Optional: Plan context (for 2D representations)
plan = ifcopenshell.api.run("context.add_context", model,
context_type="Plan")
plan_body = ifcopenshell.api.run("context.add_context", model,
context_type="Plan", context_identifier="Body",
target_view="PLAN_VIEW", parent=plan)
Position Elements with Transformation Matrix
# IfcOpenShell: IFC4
import numpy as np
# Place wall at specific X, Y, Z coordinates
matrix = np.eye(4)
matrix[0][3] = 5.0 # X offset = 5 meters
matrix[1][3] = 2.0 # Y offset = 2 meters
matrix[2][3] = 0.0 # Z offset = 0 meters (ground level)
ifcopenshell.api.run("geometry.edit_object_placement", model,
product=wall, matrix=matrix)
# Rotate element 90 degrees around Z axis
import math
angle = math.radians(90)
rotation_matrix = np.array([
[math.cos(angle), -math.sin(angle), 0, 0],
[math.sin(angle), math.cos(angle), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
])
ifcopenshell.api.run("geometry.edit_object_placement", model,
product=wall, matrix=rotation_matrix)
Shared Property Sets Across Multiple Elements
# IfcOpenShell: IFC4
# Create pset on first element
pset = ifcopenshell.api.run("pset.add_pset", model,
product=wall1, name="Pset_WallCommon")
ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={
"IsExternal": True, "FireRating": "REI90"
})
# Share same pset with additional elements
ifcopenshell.api.run("pset.assign_pset", model,
products=[wall1, wall2, wall3], pset=pset)
Visual Alternative: IfcSverchok
For node-based visual IFC creation, use IfcSverchok (31 nodes). Key nodes: SvIfcCreateProject, SvIfcCreateEntity, SvIfcApi. Refer to: sverchok-impl-ifcsverchok
Reference Links
- API Method Signatures — Complete signatures for all creation-related API methods
- Working Code Examples — End-to-end model creation examples
- Anti-Patterns — Common creation mistakes and how to avoid them