| name | bonsai-impl-modeling |
| description | Implements Bonsai BIM modeling workflows including placing building elements (walls, slabs, columns, beams), assigning IFC types and predefined types, material layer/profile/constituent assignment, parametric modeling with type libraries, and element copy/array operations. Covers the complete element creation pipeline from type selection through geometric representation to spatial assignment. |
| license | MIT |
| compatibility | Designed for Claude Code. Requires Python 3.x. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
Bonsai Implementation: BIM Modeling
Version: Bonsai v0.8.x | Blender 4.2.0+ | Python 3.11
Module path: bonsai.bim.module.model — NEVER blenderbim.*
Data layer: All IFC operations use ifcopenshell.api.run()
1. Critical Warnings
- ALWAYS use
bonsai.* — NEVER blenderbim.*. The rename occurred in 2024 (v0.8.0).
- ALWAYS check
IfcStore.get_file() for None before any IFC operation.
- ALWAYS verify representation context exists before creating geometry.
- ALWAYS use
spatial.assign_container for placing elements in storeys — NEVER aggregate.assign_object.
- ALWAYS use
feature.add_feature for openings — NEVER void.add_opening (does not exist in v0.8.0+).
- ALWAYS pass lists to
related_objects / products parameters — NEVER single entities.
- ALWAYS call
bpy.ops.bim.edit_object_placement() after moving Blender objects to sync IFC placement.
- NEVER treat Bonsai as an importer/exporter — IFC IS the native document.
2. Element Creation Pipeline
Decision Tree: Which Workflow?
Need to create a building element?
├── Is there an existing IfcElementType for it?
│ ├── YES → Use Type-Based Workflow (§2.1)
│ └── NO → Create type first (§3), then use Type-Based Workflow
│
├── Element category?
│ ├── Wall → Layer-based type (IfcMaterialLayerSet) → §4.1
│ ├── Slab/Plate → Layer-based type (IfcMaterialLayerSet) → §4.1
│ ├── Column/Beam → Profile-based type (IfcMaterialProfileSet) → §4.2
│ ├── Door/Window → Parametric type (mapped geometry) → §4.3
│ └── Custom/Other → Constituent set or direct mesh → §4.4
2.1 Type-Based Element Creation (Standard Workflow)
Every building element in Bonsai follows this pipeline:
Step 1: Get IFC model → IfcStore.get_file()
Step 2: Ensure context exists → ifcopenshell.util.representation.get_context()
Step 3: Get/create type → root.create_entity(ifc_class="IfcWallType")
Step 4: Configure type material → material.add_material_set() + material.assign_material()
Step 5: Create occurrence → root.create_entity(ifc_class="IfcWall")
Step 6: Assign type → type.assign_type(related_objects=[wall], relating_type=wall_type)
Step 7: Create representation → geometry.add_wall_representation() / add_profile_representation()
Step 8: Assign representation → geometry.assign_representation()
Step 9: Place in spatial tree → spatial.assign_container(products=[wall], relating_structure=storey)
Step 10: Set placement → geometry.edit_object_placement()
2.2 Representation Context Setup
ALWAYS verify context exists before creating geometry:
import ifcopenshell.util.representation
body_context = ifcopenshell.util.representation.get_context(
model, "Model", "Body", "MODEL_VIEW")
if not body_context:
model_ctx = ifcopenshell.api.run("context.add_context", model,
context_type="Model")
body_context = ifcopenshell.api.run("context.add_context", model,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model_ctx)
3. Type System
3.1 Creating Element Types
wall_type = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWallType",
predefined_type="STANDARD",
name="200mm Concrete Wall")
3.2 Type-to-Representation Template Mapping
| IFC Type Class | Material Set Type | Representation Template |
|---|
IfcWallType | IfcMaterialLayerSet | LAYERSET_AXIS2 |
IfcSlabType | IfcMaterialLayerSet | LAYERSET_AXIS3 |
IfcPlateType | IfcMaterialLayerSet | LAYERSET_AXIS3 |
IfcColumnType | IfcMaterialProfileSet | PROFILESET |
IfcBeamType | IfcMaterialProfileSet | PROFILESET |
IfcMemberType | IfcMaterialProfileSet | PROFILESET |
IfcFootingType | IfcMaterialProfileSet | PROFILESET |
IfcDoorType | Direct/Constituent | DOOR |
IfcWindowType | Direct/Constituent | WINDOW |
IfcFurnitureType | Direct/Constituent | MESH |
3.3 Predefined Type Enums
IfcWallTypeEnum
STANDARD, SOLIDWALL, PARTITIONING, RETAININGWALL, SHEAR, PARAPET, USERDEFINED, NOTDEFINED
IfcSlabTypeEnum
FLOOR, ROOF, LANDING, BASESLAB, USERDEFINED, NOTDEFINED
IfcColumnTypeEnum
COLUMN, PILASTER, USERDEFINED, NOTDEFINED
IfcBeamTypeEnum
BEAM, JOIST, HOLLOWCORE, LINTEL, SPANDREL, T_BEAM, USERDEFINED, NOTDEFINED
3.4 Type Assignment
ifcopenshell.api.run("type.assign_type", model,
related_objects=[wall1, wall2],
relating_type=wall_type,
should_map_representations=True)
import ifcopenshell.util.element
element_type = ifcopenshell.util.element.get_type(wall1)
occurrences = ifcopenshell.util.element.get_types(wall_type)
3.5 MappedItem Geometry Sharing
When should_map_representations=True:
- Geometry is stored ONCE on the type's
IfcRepresentationMap
- Each occurrence references it via
IfcMappedItem with a per-instance transformation
- Editing the type geometry updates ALL occurrences automatically
4. Material Assignment
4.1 Material Layer Sets (Walls, Slabs, Plates)
concrete = ifcopenshell.api.run("material.add_material", model,
name="Concrete C25", category="concrete")
insulation = ifcopenshell.api.run("material.add_material", model,
name="Mineral Wool", category="ite")
layer_set = ifcopenshell.api.run("material.add_material_set", model,
name="200mm Insulated Wall", set_type="IfcMaterialLayerSet")
layer_c = ifcopenshell.api.run("material.add_layer", model,
layer_set=layer_set, material=concrete)
ifcopenshell.api.run("material.edit_layer", model,
layer=layer_c, attributes={"LayerThickness": 0.150})
layer_i = ifcopenshell.api.run("material.add_layer", model,
layer_set=layer_set, material=insulation)
ifcopenshell.api.run("material.edit_layer", model,
layer=layer_i, attributes={"LayerThickness": 0.050})
ifcopenshell.api.run("material.assign_material", model,
products=[wall_type], material=layer_set)
4.2 Material Profile Sets (Columns, Beams)
steel = ifcopenshell.api.run("material.add_material", model,
name="S355 Steel", category="steel")
profile = model.create_entity("IfcIShapeProfileDef",
ProfileType="AREA", ProfileName="HEA200",
OverallWidth=0.200, OverallDepth=0.190,
WebThickness=0.0065, FlangeThickness=0.010)
profile_set = ifcopenshell.api.run("material.add_material_set", model,
name="HEA 200 Steel", set_type="IfcMaterialProfileSet")
ifcopenshell.api.run("material.add_profile", model,
profile_set=profile_set, material=steel, profile=profile)
ifcopenshell.api.run("material.assign_material", model,
products=[column_type], material=profile_set)
4.3 Material Constituent Sets (Doors, Windows, Custom)
constituent_set = ifcopenshell.api.run("material.add_material_set", model,
name="Door Materials", set_type="IfcMaterialConstituentSet")
wood = ifcopenshell.api.run("material.add_material", model,
name="Oak", category="wood")
glass = ifcopenshell.api.run("material.add_material", model,
name="Float Glass", category="glass")
ifcopenshell.api.run("material.add_constituent", model,
constituent_set=constituent_set, material=wood, name="Frame")
ifcopenshell.api.run("material.add_constituent", model,
constituent_set=constituent_set, material=glass, name="Glazing")
ifcopenshell.api.run("material.assign_material", model,
products=[door_type], material=constituent_set)
4.4 Material Assignment Decision Tree
What element type?
├── Wall / Slab / Plate → IfcMaterialLayerSet → assign to TYPE
├── Column / Beam → IfcMaterialProfileSet → assign to TYPE
├── Door / Window → IfcMaterialConstituentSet → assign to TYPE
├── Generic element → IfcMaterial (single) → assign to TYPE or occurrence
└── Composite → IfcMaterialConstituentSet → assign to TYPE
Rule: ALWAYS assign materials to the TYPE, not to individual occurrences. Occurrences inherit materials from their type via IfcMaterialLayerSetUsage or IfcMaterialProfileSetUsage.
5. Geometry Creation
5.1 Wall Geometry
representation = ifcopenshell.api.run(
"geometry.add_wall_representation", model,
context=body_context,
length=5.0,
height=3.0,
thickness=0.2,
offset=0.0,
x_angle=0.0)
ifcopenshell.api.run("geometry.assign_representation", model,
product=wall, representation=representation)
5.2 Slab Geometry
representation = ifcopenshell.api.run(
"geometry.add_slab_representation", model,
context=body_context,
depth=0.2,
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=representation)
5.3 Profile-Based Geometry (Columns, Beams)
representation = ifcopenshell.api.run(
"geometry.add_profile_representation", model,
context=body_context,
profile=profile_def,
depth=3.0,
cardinal_point=5)
ifcopenshell.api.run("geometry.assign_representation", model,
product=column, representation=representation)
Cardinal Point Reference
| Value | Position |
|---|
| 1 | Bottom left |
| 2 | Bottom centre |
| 3 | Bottom right |
| 4 | Mid-depth left |
| 5 | Mid-depth centre |
| 6 | Mid-depth right |
| 7 | Top left |
| 8 | Top centre |
| 9 | Top right |
| 10 | Geometric centroid |
5.4 Common Profile Definitions
rect_profile = model.create_entity("IfcRectangleProfileDef",
ProfileType="AREA", ProfileName="400x400",
XDim=0.4, YDim=0.4)
i_profile = model.create_entity("IfcIShapeProfileDef",
ProfileType="AREA", ProfileName="HEA200",
OverallWidth=0.200, OverallDepth=0.190,
WebThickness=0.0065, FlangeThickness=0.010)
circle_profile = model.create_entity("IfcCircleProfileDef",
ProfileType="AREA", ProfileName="D300",
Radius=0.15)
profile = ifcopenshell.api.run("profile.add_parameterized_profile", model,
ifc_class="IfcRectangleProfileDef")
6. Element Placement
6.1 Setting Object Placement
import numpy as np
matrix = np.array([
[1.0, 0.0, 0.0, 5.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 3.0],
[0.0, 0.0, 0.0, 1.0]
], dtype=float)
ifcopenshell.api.run("geometry.edit_object_placement", model,
product=wall, matrix=matrix, is_si=True)
6.2 Spatial Container Assignment
storey = model.by_type("IfcBuildingStorey")[0]
ifcopenshell.api.run("spatial.assign_container", model,
products=[wall, column, slab],
relating_structure=storey)
7. Opening and Void Operations
opening = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcOpeningElement", name="Opening-001")
opening_repr = ifcopenshell.api.run(
"geometry.add_wall_representation", model,
context=body_context,
length=1.0, height=2.1, thickness=0.25)
ifcopenshell.api.run("geometry.assign_representation", model,
product=opening, representation=opening_repr)
ifcopenshell.api.run("feature.add_feature", model,
feature=opening, element=wall)
ifcopenshell.api.run("void.add_filling", model,
opening=opening, element=door)
8. Parametric Arrays (Bonsai-Specific)
Bonsai uses BBIM_Array property set for parametric element arrays:
array_data = {
"children": [],
"count": 5,
"x": 3.0,
"y": 0.0,
"z": 0.0,
"use_local_space": True,
"method": "OFFSET"
}
9. Bonsai Operator Quick Reference
| Operator | Purpose |
|---|
bpy.ops.bim.assign_class() | Assign IFC class to Blender object |
bpy.ops.bim.edit_object_placement() | Sync Blender transform to IFC |
bpy.ops.bim.save_project() | Save IFC file |
bpy.ops.bim.add_type_instance() | Place instance of existing type |
Rule: ALWAYS prefer ifcopenshell.api.run() over bpy.ops.bim.* in scripts — the API is more stable and explicit.
10. Reference Links
- API Signatures: See
references/methods.md
- Complete Code Examples: See
references/examples.md
- Anti-Patterns: See
references/anti-patterns.md