Install
$ agentstack add skill-impertio-studio-blender-bonsai-ifcopenshell-sverchok-claude-skill-package-aec-core-bim-workflows ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Cross-Technology BIM Workflows
> Scope: End-to-end workflows combining IfcOpenShell, Bonsai, and Blender > Dependencies: ifcos-impl-creation, bonsai-core-architecture > Version coverage: Blender 3.x-5.x | IFC2X3/IFC4/IFC4X3 | Bonsai v0.8.x
Critical Warnings
- ALWAYS determine the execution context first: standalone Python (IfcOpenShell only) vs Blender Python (bpy available) vs Bonsai-loaded (IfcStore available). The API surface differs per context.
- ALWAYS use
ifcopenshell.api.run()for IFC mutations in ALL contexts. NEVER modify IFC entity attributes directly — this bypasses relationship management and breaks graph integrity. - ALWAYS call
bpy.ops.bim.edit_object_placement()after modifying a Blender object's location/rotation/scale when Bonsai is active. Blender transforms do NOT auto-sync to IFC. - NEVER mix
IfcStore.get_file()(Bonsai context) withifcopenshell.open()on the same file simultaneously. Bonsai owns the in-memory IFC graph — opening a second handle creates divergent state. - NEVER use
blenderbim.*imports. The package was renamed tobonsai.*in v0.8.0 (2024). - ALWAYS set up units (
unit.assign_unit) and geometric contexts (context.add_context) before creating any geometry. Geometry without context or units is ambiguous and may render incorrectly. - NEVER use
void.add_opening()in Bonsai v0.8.0+. Usefeature.add_feature()instead.
Decision Tree: Choose Your Execution Context
What is your workflow?
|
+-- Creating/modifying IFC files WITHOUT Blender?
| +-- Use: Standalone IfcOpenShell (headless Python)
| +-- Import: ifcopenshell, ifcopenshell.api
| +-- File access: ifcopenshell.open(path) or ifcopenshell.api.run("project.create_file")
| +-- Best for: batch processing, CI/CD validation, server-side operations
|
+-- Creating/modifying IFC files IN Blender WITH Bonsai?
| +-- Use: Bonsai-loaded context
| +-- File access: IfcStore.get_file() — NEVER ifcopenshell.open()
| +-- IFC mutations: tool.Ifc.run("command", **kwargs) OR ifcopenshell.api.run()
| +-- Blender sync: bpy.ops.bim.* operators handle bidirectional sync
| +-- Best for: interactive BIM authoring, visual verification
|
+-- Extracting geometry from IFC for Blender visualization (no Bonsai)?
| +-- Use: IfcOpenShell geometry + Blender bpy
| +-- Import: ifcopenshell, ifcopenshell.geom, bpy
| +-- Geometry: ifcopenshell.geom.create_shape(settings, element)
| +-- Mesh: bpy.data.meshes.new() + mesh.from_pydata(verts, [], faces)
| +-- Best for: lightweight IFC viewers, geometry analysis
|
+-- Batch processing multiple IFC files?
+-- Use: Standalone IfcOpenShell (headless) or blender --background --python
+-- If Blender ops needed: blender --background --python script.py
+-- If pure IFC: python script.py (no Blender dependency)
+-- Best for: model validation, property extraction, report generation
Decision Tree: IFC Creation vs Enrichment
Starting from scratch or enriching existing?
|
+-- New IFC model from scratch?
| +-- Follow the 9-step creation pipeline:
| | 1. project.create_file → Create file with schema version
| | 2. root.create_entity (IfcProject) → Create project
| | 3. unit.assign_unit → Set measurement units
| | 4. context.add_context → Set up Model + Body subcontext
| | 5. root.create_entity + aggregate.assign_object → Build spatial hierarchy
| | 6. root.create_entity + type assignment → Create element types
| | 7. root.create_entity + geometry + placement → Create elements
| | 8. spatial.assign_container → Place elements in hierarchy
| | 9. pset.add_pset + pset.edit_pset → Add properties
| +-- Reference: ifcos-impl-creation (Pattern 1)
|
+-- Enriching an existing IFC file?
| +-- Open: model = ifcopenshell.open("existing.ifc")
| +-- Query: elements = model.by_type("IfcWall")
| +-- Add properties: pset.add_pset + pset.edit_pset
| +-- Add classifications: classification.add_reference
| +-- Modify geometry: geometry operations
| +-- Write: model.write("enriched.ifc")
| +-- WARNING: Preserve existing GlobalIds. NEVER recreate entities that already exist.
|
+-- Merging data from multiple IFC files?
+-- Open source: source = ifcopenshell.open("source.ifc")
+-- Open target: target = ifcopenshell.open("target.ifc")
+-- Extract data from source (properties, classifications, types)
+-- Apply to target using ifcopenshell.api.run()
+-- WARNING: Entity IDs are file-scoped. NEVER copy raw IDs between files.
+-- WARNING: GlobalIds MUST be unique within a file. Generate new GUIDs for copied entities.
Sverchok Parametric Design Step
When workflows involve parametric, generative, or data-driven geometry (arrays, facades, repetitive elements), insert a Sverchok step before BIM authoring:
[Sverchok: Parametric Geometry] --> [IfcSverchok: Generate IFC] --> [Bonsai: Review/Enrich]
- Sverchok generates geometry via visual node trees (
SverchCustomTreeType) - IfcSverchok (31 nodes) converts parametric geometry to IFC entities within the node tree
- Enable
use_bonsai_fileonSvIfcCreateProjectto write directly into Bonsai's active IFC file - Warning:
SvIfcStoreis transient — purged on every full tree update. Persist via Bonsai ormodel.write() - Refer to:
sverchok-impl-parametric,sverchok-impl-ifcsverchok
Essential Patterns
Pattern 1: Standalone IFC Creation (No Blender)
# Context: Standalone Python: IfcOpenShell only
# Version: IFC4 | IfcOpenShell 0.8.x
import ifcopenshell
import ifcopenshell.api
import numpy as np
model = ifcopenshell.api.run("project.create_file", version="IFC4")
project = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcProject", name="My Project")
ifcopenshell.api.run("unit.assign_unit", model)
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)
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 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)
wall = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWall", name="Wall 001")
rep = 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=rep)
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
ifcopenshell.api.run("spatial.assign_container", model,
relating_structure=storey, products=[wall])
model.write("output.ifc")
Pattern 2: IFC Geometry Extraction to Blender Mesh (No Bonsai)
# Context: Blender Python (bpy available), NO Bonsai
# Version: Blender 3.x-5.x | IfcOpenShell 0.8.x
import bpy
import ifcopenshell
import ifcopenshell.geom
ifc_file = ifcopenshell.open("model.ifc")
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_WORLD_COORDS, True)
for wall in ifc_file.by_type("IfcWall"):
shape = ifcopenshell.geom.create_shape(settings, wall)
verts = shape.geometry.verts # flat list: [x0,y0,z0, x1,y1,z1, ...]
faces = shape.geometry.faces # flat list: [i0,i1,i2, i3,i4,i5, ...]
# Reshape into Blender format
vertices = [(verts[i], verts[i+1], verts[i+2])
for i in range(0, len(verts), 3)]
triangles = [(faces[i], faces[i+1], faces[i+2])
for i in range(0, len(faces), 3)]
mesh = bpy.data.meshes.new(name=wall.Name or "Wall")
mesh.from_pydata(vertices, [], triangles)
mesh.update()
obj = bpy.data.objects.new(wall.Name or "Wall", mesh)
bpy.context.collection.objects.link(obj)
Pattern 3: Bonsai-Context BIM Authoring
# Context: Blender with Bonsai loaded
# Version: Bonsai v0.8.x | Blender 4.2+
import bpy
from bonsai.bim.ifc import IfcStore
import ifcopenshell.api
# ALWAYS check for active IFC project
model = IfcStore.get_file()
if model is None:
raise RuntimeError("No IFC project loaded. Create or open a project first.")
# Create element via Bonsai's tool layer
# Option A: Use ifcopenshell.api.run() directly
wall = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWall", name="New Wall")
# Option B: Use Bonsai operators (handles Blender sync automatically)
bpy.ops.bim.add_wall()
# After creating elements, ensure spatial containment
storey = model.by_type("IfcBuildingStorey")[0]
ifcopenshell.api.run("spatial.assign_container", model,
relating_structure=storey, products=[wall])
# CRITICAL: If you moved the Blender object, sync placement to IFC
bpy.ops.bim.edit_object_placement(context_override)
# Add property set
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": "REI60"
})
Pattern 4: Batch Property Extraction (Headless)
# Context: Standalone Python or blender --background
# Version: IfcOpenShell 0.8.x | IFC4
import ifcopenshell
import ifcopenshell.util.element
import csv
model = ifcopenshell.open("building.ifc")
with open("wall_report.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["GlobalId", "Name", "IsExternal", "FireRating", "LoadBearing"])
for wall in model.by_type("IfcWall"):
psets = ifcopenshell.util.element.get_psets(wall)
common = psets.get("Pset_WallCommon", {})
writer.writerow([
wall.GlobalId,
wall.Name,
common.get("IsExternal", ""),
common.get("FireRating", ""),
common.get("LoadBearing", ""),
])
Pattern 5: Model Validation Pipeline
# Context: Standalone Python
# Version: IfcOpenShell 0.8.x | IFC4/IFC2X3
import ifcopenshell
import ifcopenshell.util.element
def validate_model(filepath: str) -> list[str]:
errors = []
model = ifcopenshell.open(filepath)
# Check 1: Spatial hierarchy exists
if not model.by_type("IfcProject"):
errors.append("CRITICAL: No IfcProject found")
if not model.by_type("IfcSite"):
errors.append("CRITICAL: No IfcSite found")
if not model.by_type("IfcBuilding"):
errors.append("CRITICAL: No IfcBuilding found")
if not model.by_type("IfcBuildingStorey"):
errors.append("WARNING: No IfcBuildingStorey found")
# Check 2: All physical elements have spatial containment
physical_types = ["IfcWall", "IfcSlab", "IfcColumn", "IfcBeam",
"IfcDoor", "IfcWindow"]
for ifc_type in physical_types:
for element in model.by_type(ifc_type):
container = ifcopenshell.util.element.get_container(element)
if container is None:
errors.append(
f"WARNING: {element.is_a()} '{element.Name}' "
f"(#{element.id()}) has no spatial container")
# Check 3: All elements have geometry
for element in model.by_type("IfcProduct"):
if hasattr(element, "Representation") and element.Representation is None:
if element.is_a() not in ("IfcSite", "IfcBuilding",
"IfcBuildingStorey", "IfcSpace",
"IfcProject"):
errors.append(
f"WARNING: {element.is_a()} '{element.Name}' "
f"(#{element.id()}) has no geometry representation")
# Check 4: Units are assigned
project = model.by_type("IfcProject")[0] if model.by_type("IfcProject") else None
if project and not project.UnitsInContext:
errors.append("CRITICAL: No units assigned to project")
# Check 5: IFC2X3 OwnerHistory requirement
if model.schema == "IFC2X3":
for entity in model.by_type("IfcRoot"):
if entity.OwnerHistory is None:
errors.append(
f"ERROR: IFC2X3 requires OwnerHistory on "
f"{entity.is_a()} '{entity.Name}' (#{entity.id()})")
return errors
Pattern 6: Multi-File Batch Processing
# Context: Standalone Python
# Version: IfcOpenShell 0.8.x
import ifcopenshell
import ifcopenshell.api
from pathlib import Path
def enrich_models(input_dir: str, output_dir: str, classification_system: str):
"""Add classification references to all IFC files in a directory."""
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
for ifc_file in input_path.glob("*.ifc"):
model = ifcopenshell.open(str(ifc_file))
# Add classification system if not present
classifications = model.by_type("IfcClassification")
if not any(c.Name == classification_system for c in classifications):
classification = ifcopenshell.api.run(
"classification.add_classification", model,
classification=classification_system)
else:
classification = next(
c for c in classifications if c.Name == classification_system)
# Process each element
for wall in model.by_type("IfcWall"):
ifcopenshell.api.run("classification.add_reference", model,
products=[wall],
classification=classification,
identification="21.22",
name="Exterior Walls")
output_file = output_path / ifc_file.name
model.write(str(output_file))
Cross-Technology API Integration Points
IfcOpenShell Blender (Without Bonsai)
| IfcOpenShell Operation | Blender Equivalent | Integration Point | |----------------------|-------------------|-------------------| | ifcopenshell.geom.create_shape() | mesh.from_pydata(verts, [], faces) | Geometry extraction: shape.geometry.verts/faces | | element.ObjectPlacement | obj.matrix_world | 4x4 transformation matrix | | model.by_type("IfcWall") | bpy.data.objects filtering | Element enumeration | | IFC materials | bpy.data.materials | Manual mapping required |
IfcOpenShell Bonsai
| IfcOpenShell Operation | Bonsai Equivalent | Notes | |----------------------|------------------|-------| | ifcopenshell.api.run(cmd, model, **kw) | tool.Ifc.run(cmd, **kw) | Bonsai wraps the API; model is implicit | | ifcopenshell.open(path) | IfcStore.get_file() | NEVER open separately when Bonsai is active | | element.id() | obj.BIMProperties.ifc_definition_id | Bidirectional mapping via IfcStore.id_map | | element.GlobalId | IfcStore.guid_map | Bidirectional GUID-to-object lookup |
Bonsai Blender
| Bonsai Operation | Blender Equivalent | Sync Rule | |-----------------|-------------------|-----------| | bpy.ops.bim.add_wall() | Creates mesh + IFC entity | Automatic bidirectional sync | | IFC placement change | obj.location / obj.matrix_world | MUST call bpy.ops.bim.edit_object_placement() | | pset.edit_pset() | BIM property panels | Panel shows live IFC data | | IfcStore.edited_objs | Modified Blender objects | Pending IFC changes queue |
Version Compatibility Matrix
| Feature | IFC2X3 | IFC4 | IFC4X3 | Notes | |---------|--------|------|--------|-------| | OwnerHisto
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Blender-Bonsai-ifcOpenshell-Sverchok-Claude-Skill-Package
- License: MIT
- Homepage: https://github.com/OpenAEC-Foundation
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.