| name | bonsai-impl-project |
| description | Use when creating, opening, or configuring Bonsai IFC projects -- schema selection (IFC2X3/IFC4/IFC4X3), unit configuration, georeference setup, or project templates. Prevents the common mistake of not setting units before creating geometry (defaulting to incorrect units). Covers the complete project lifecycle from creation to delivery. Keywords: IFC project, schema selection, IFC2X3, IFC4, IFC4X3, units, georeference, coordinate reference system, project template, Bonsai project, CRS, EPSG, WGS84, map coordinates, start new project.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires Python 3.x. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
Bonsai Project Management
Version: Bonsai v0.8.x | IfcOpenShell v0.8.x | IFC2X3 / IFC4 / IFC4X3
Dependency: bonsai-core-architecture (MUST read first)
Module path: bonsai.* — NEVER blenderbim.*
Critical Warnings
- ALWAYS use
ifcopenshell.api.project.create_file() to create new IFC projects. NEVER use ifcopenshell.file() directly — it skips required project setup (IfcProject, IfcUnitAssignment, representation contexts).
- ALWAYS set units via
ifcopenshell.api.unit.assign_unit() immediately after project creation. An IFC file without a unit assignment is invalid per all IFC schemas.
- NEVER describe the Bonsai workflow as "import/export". The IFC file IS the native document.
- ALWAYS use
bonsai.* imports. NEVER use blenderbim.* — renamed in 2024 (v0.8.0+).
- ALWAYS check
IfcStore.get_file() for None before any IFC operation.
- ALWAYS call
ifcopenshell.api.georeference.add_georeferencing() before edit_georeferencing(). Editing non-existent entities raises an error.
- NEVER set Eastings/Northings without confirmed survey data. Wrong values place the building at the wrong location on Earth.
- ALWAYS run scripts via
blender --python. NEVER import bonsai.* from system Python.
Schema Selection Decision Tree
Which IFC schema should I use?
|
+--> Legacy project or government mandate for IFC2X3?
| +--> YES --> schema="IFC2X3"
| +--> NO --> Continue
|
+--> Infrastructure project (roads, bridges, tunnels, rail)?
| +--> YES --> schema="IFC4X3"
| +--> NO --> Continue
|
+--> Default for buildings
+--> schema="IFC4"
| Schema | Use Case | IfcProject Required? | Georeferencing |
|---|
IFC2X3 | Legacy, government compliance | YES | ePSet_MapConversion (property set) |
IFC4 | Buildings (recommended default) | YES | IfcMapConversion + IfcProjectedCRS |
IFC4X3 | Infrastructure + buildings | YES | IfcMapConversion/IfcMapConversionScaled + IfcProjectedCRS |
Project Creation
Decision Tree: How to Create a Project
Creating an IFC project?
|
+--> Inside Blender with Bonsai?
| |
| +--> Quick start?
| | +--> bpy.ops.bim.create_project(template="metric_m")
| |
| +--> Custom setup?
| +--> bpy.ops.bim.create_project() with wizard options
|
+--> Standalone Python (no Blender)?
|
+--> model = ifcopenshell.api.project.create_file(version="IFC4")
+--> ifcopenshell.api.unit.assign_unit(model)
+--> Create IfcSite, IfcBuilding, IfcStorey
+--> model.write("project.ifc")
Standalone Project Creation (No Blender)
import ifcopenshell
import ifcopenshell.api
model = ifcopenshell.api.project.create_file(version="IFC4")
ifcopenshell.api.unit.assign_unit(model,
length={"is_metric": True, "raw": "MILLIMETRE"},
area={"is_metric": True, "raw": "SQUARE_METRE"},
volume={"is_metric": True, "raw": "CUBIC_METRE"})
project = model.by_type("IfcProject")[0]
site = ifcopenshell.api.root.create_entity(model,
ifc_class="IfcSite", name="Default Site")
building = ifcopenshell.api.root.create_entity(model,
ifc_class="IfcBuilding", name="Default Building")
storey = ifcopenshell.api.root.create_entity(model,
ifc_class="IfcBuildingStorey", name="Ground Floor")
ifcopenshell.api.aggregate.assign_object(model,
relating_object=project, products=[site])
ifcopenshell.api.aggregate.assign_object(model,
relating_object=site, products=[building])
ifcopenshell.api.aggregate.assign_object(model,
relating_object=building, products=[storey])
model_context = ifcopenshell.api.context.add_context(model,
context_type="Model")
body_context = ifcopenshell.api.context.add_context(model,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model_context)
plan_context = ifcopenshell.api.context.add_context(model,
context_type="Plan")
model.write("project.ifc")
Bonsai Project Creation (Inside Blender)
import bpy
bpy.ops.bim.create_project(template="metric_m")
bpy.ops.bim.create_project(template="metric_mm")
bpy.ops.bim.create_project(template="imperial_ft")
bpy.ops.bim.create_project(template="demo")
Opening and Saving IFC Files
Decision Tree: File Operations
IFC file operation needed?
|
+--> Open existing file?
| |
| +--> Inside Blender?
| | +--> bpy.ops.bim.load_project(filepath="project.ifc")
| |
| +--> Standalone Python?
| +--> model = ifcopenshell.open("project.ifc")
|
+--> Save project?
| |
| +--> Inside Blender?
| | +--> bpy.ops.bim.save_project(filepath="project.ifc")
| |
| +--> Standalone Python?
| +--> model.write("project.ifc")
|
+--> Save to different file (Save As)?
+--> model.write("new_path.ifc")
Standalone File I/O
import ifcopenshell
model = ifcopenshell.open("/path/to/project.ifc")
print(f"Schema: {model.schema}")
print(f"Project: {model.by_type('IfcProject')[0].Name}")
model.write("/path/to/project.ifc")
model.write("/path/to/project_v2.ifc")
Bonsai File I/O (Inside Blender)
import bpy
from bonsai.bim.ifc import IfcStore
bpy.ops.bim.load_project(filepath="/path/to/project.ifc")
model = IfcStore.get_file()
if model is None:
raise RuntimeError("No IFC project loaded")
bpy.ops.bim.save_project()
bpy.ops.bim.save_project(filepath="/path/to/project_v2.ifc")
Unit Configuration
Decision Tree: Unit Selection
What unit system?
|
+--> Metric (international)?
| |
| +--> Architecture/interiors (mm precision)?
| | +--> length: MILLIMETRE
| |
| +--> Urban/site planning (m precision)?
| +--> length: METRE (or no prefix = default SI)
|
+--> Imperial (US/UK)?
|
+--> length: "foot" via add_conversion_based_unit
+--> OR: "inch" for detail work
Unit Assignment Patterns
import ifcopenshell
import ifcopenshell.api
model = ifcopenshell.api.project.create_file(version="IFC4")
ifcopenshell.api.unit.assign_unit(model)
length_unit = ifcopenshell.api.unit.add_si_unit(model,
unit_type="LENGTHUNIT", prefix=None)
area_unit = ifcopenshell.api.unit.add_si_unit(model,
unit_type="AREAUNIT", prefix=None)
volume_unit = ifcopenshell.api.unit.add_si_unit(model,
unit_type="VOLUMEUNIT", prefix=None)
ifcopenshell.api.unit.assign_unit(model,
units=[length_unit, area_unit, volume_unit])
length_unit = ifcopenshell.api.unit.add_si_unit(model,
unit_type="LENGTHUNIT", prefix="MILLI")
area_unit = ifcopenshell.api.unit.add_si_unit(model,
unit_type="AREAUNIT", prefix=None)
volume_unit = ifcopenshell.api.unit.add_si_unit(model,
unit_type="VOLUMEUNIT", prefix=None)
ifcopenshell.api.unit.assign_unit(model,
units=[length_unit, area_unit, volume_unit])
length_unit = ifcopenshell.api.unit.add_conversion_based_unit(model,
name="foot")
area_unit = ifcopenshell.api.unit.add_conversion_based_unit(model,
name="square foot")
ifcopenshell.api.unit.assign_unit(model,
units=[length_unit, area_unit])
currency = ifcopenshell.api.unit.add_monetary_unit(model,
currency="EUR")
ifcopenshell.api.unit.assign_unit(model, units=[currency])
SI Unit Prefixes
| Prefix | Factor | Typical Use |
|---|
"MILLI" | 10⁻³ | mm — architecture, structural (most common) |
None | 1 | m — site planning, infrastructure |
"CENTI" | 10⁻² | cm — rarely used in IFC |
"KILO" | 10³ | km — large-scale infrastructure |
Conversion-Based Unit Names
| Name | Unit Type | Use |
|---|
"foot" | LENGTHUNIT | US Imperial length |
"inch" | LENGTHUNIT | US Imperial detail |
"yard" | LENGTHUNIT | US Imperial site |
"square foot" | AREAUNIT | US Imperial area |
"cubic foot" | VOLUMEUNIT | US Imperial volume |
"degree" | PLANEANGLEUNIT | Angle measurement |
Georeferencing
Decision Tree: Georeferencing Setup
Does the project need georeferencing?
|
+--> No survey data available?
| +--> Skip georeferencing — add later when data is available
|
+--> Survey data available?
|
+--> Step 1: add_georeferencing()
+--> Step 2: edit_georeferencing() with EPSG code + Eastings/Northings
+--> Step 3 (optional): edit_true_north() for solar analysis
Georeferencing Setup Pattern
import ifcopenshell
import ifcopenshell.api
from math import cos, sin, radians
model = ifcopenshell.api.project.create_file(version="IFC4")
ifcopenshell.api.georeference.add_georeferencing(model)
ifcopenshell.api.georeference.edit_georeferencing(model,
projected_crs={
"Name": "EPSG:28992"
},
coordinate_operation={
"Eastings": 155000.0,
"Northings": 463000.0,
"OrthogonalHeight": 0.0,
"XAxisAbscissa": cos(radians(0.0)),
"XAxisOrdinate": sin(radians(0.0)),
"Scale": 1.0
})
ifcopenshell.api.georeference.edit_true_north(model,
true_north=5.0)
Common EPSG Codes for Construction
| EPSG Code | Name | Region |
|---|
EPSG:28992 | Amersfoort / RD New | Netherlands |
EPSG:32632 | WGS 84 / UTM zone 32N | Central Europe |
EPSG:32633 | WGS 84 / UTM zone 33N | Eastern Europe |
EPSG:27700 | OSGB 1936 / British National Grid | United Kingdom |
EPSG:2154 | RGF93 v1 / Lambert-93 | France |
EPSG:25832 | ETRS89 / UTM zone 32N | Germany, Denmark, Austria |
EPSG:2056 | CH1903+ / LV95 | Switzerland |
EPSG:3857 | WGS 84 / Pseudo-Mercator | Web mapping (NOT for construction) |
EPSG:7856 | GDA2020 / MGA zone 56 | Australia (East) |
EPSG:2263 | NAD83 / New York Long Island (ftUS) | New York, USA |
IFC2X3 Georeferencing (Legacy)
IFC2X3 does NOT have IfcMapConversion or IfcProjectedCRS entities. Instead, georeferencing uses the ePSet_MapConversion property set on IfcProject. The add_georeferencing API handles this automatically — use the same API calls regardless of schema version.
Header Metadata
import ifcopenshell
import ifcopenshell.api
model = ifcopenshell.open("project.ifc")
ifcopenshell.api.project.edit_header(model,
editor="Jane Architect",
organization="ACME Architecture",
application="Bonsai v0.8.4",
application_version="0.8.4")
model.write("project.ifc")
Owner History Setup
import ifcopenshell
import ifcopenshell.api
model = ifcopenshell.api.project.create_file(version="IFC4")
person = ifcopenshell.api.owner.add_person(model,
identification="jdoe",
family_name="Doe",
given_name="Jane")
org = ifcopenshell.api.owner.add_organisation(model,
identification="ACME",
name="ACME Architecture")
person_org = ifcopenshell.api.owner.add_person_and_organisation(model,
person=person, organisation=org)
app = ifcopenshell.api.owner.add_application(model,
application_full_name="My BIM Tool",
application_identifier="MyBIMTool",
version="1.0")
ifcopenshell.api.owner.settings.get_user = lambda ifc: person_org
ifcopenshell.api.owner.settings.get_application = lambda ifc: app
Schema Migration
import ifcopenshell
import ifcopenshell.util.schema
model_2x3 = ifcopenshell.open("legacy.ifc")
migrator = ifcopenshell.util.schema.Migrator()
model_ifc4 = migrator.migrate(model_2x3, "IFC4")
model_ifc4.write("migrated.ifc")
ALWAYS validate the migrated file with IfcTester after migration.
Representation Context Setup
Every IFC project MUST have at least one IfcGeometricRepresentationContext before any geometry can be assigned.
model_context = ifcopenshell.api.context.add_context(model,
context_type="Model")
body_context = ifcopenshell.api.context.add_context(model,
context_type="Model",
context_identifier="Body",
target_view="MODEL_VIEW",
parent=model_context)
plan_context = ifcopenshell.api.context.add_context(model,
context_type="Plan")
annotation_context = ifcopenshell.api.context.add_context(model,
context_type="Plan",
context_identifier="Annotation",
target_view="PLAN_VIEW",
parent=plan_context)
Context Identifiers Reference
| Context Type | Identifier | Target View | Purpose |
|---|
"Model" | "Body" | "MODEL_VIEW" | Primary 3D geometry (REQUIRED) |
"Model" | "Axis" | "GRAPH_VIEW" | Centerline representations |
"Model" | "Box" | "MODEL_VIEW" | Bounding box for clash detection |
"Model" | "FootPrint" | "MODEL_VIEW" | Floor plan footprint |
"Model" | "Clearance" | "MODEL_VIEW" | Clearance volumes |
"Plan" | "Annotation" | "PLAN_VIEW" | 2D annotations |
"Plan" | "Axis" | "PLAN_VIEW" | 2D centerlines |
Project Lifecycle Summary
1. CREATE --> ifcopenshell.api.project.create_file(version="IFC4")
2. UNITS --> ifcopenshell.api.unit.assign_unit(model)
3. SPATIAL --> Create IfcSite, IfcBuilding, IfcBuildingStorey
4. CONTEXT --> ifcopenshell.api.context.add_context(model, ...)
5. GEO --> ifcopenshell.api.georeference.add_georeferencing(model)
6. OWNER --> ifcopenshell.api.owner.add_person/organisation(model)
7. MODEL --> Add elements, properties, geometry
8. SAVE --> model.write("project.ifc")
ALWAYS follow this order. Steps 5 and 6 are optional but recommended for production deliverables.
References
- methods.md — Complete API signatures for project, unit, georeference, context, owner modules
- examples.md — Working code examples for project lifecycle workflows
- anti-patterns.md — What NOT to do in project management
Sources