# Aec Core Bim Workflows

> >

- **Type:** Skill
- **Install:** `agentstack add skill-impertio-studio-blender-bonsai-ifcopenshell-sverchok-claude-skill-package-aec-core-bim-workflows`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Impertio-Studio](https://agentstack.voostack.com/s/impertio-studio)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** https://github.com/Impertio-Studio/Blender-Bonsai-ifcOpenshell-Sverchok-Claude-Skill-Package/tree/main/skills/aec-cross-tech/core/aec-core-bim-workflows
- **Website:** https://github.com/OpenAEC-Foundation

## Install

```sh
agentstack add skill-impertio-studio-blender-bonsai-ifcopenshell-sverchok-claude-skill-package-aec-core-bim-workflows
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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

1. **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.
2. **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.
3. **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.
4. **NEVER** mix `IfcStore.get_file()` (Bonsai context) with `ifcopenshell.open()` on the same file simultaneously. Bonsai owns the in-memory IFC graph — opening a second handle creates divergent state.
5. **NEVER** use `blenderbim.*` imports. The package was renamed to `bonsai.*` in v0.8.0 (2024).
6. **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.
7. **NEVER** use `void.add_opening()` in Bonsai v0.8.0+. Use `feature.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_file` on `SvIfcCreateProject` to write directly into Bonsai's active IFC file
- **Warning**: `SvIfcStore` is transient — purged on every full tree update. Persist via Bonsai or `model.write()`
- Refer to: `sverchok-impl-parametric`, `sverchok-impl-ifcsverchok`

---

## Essential Patterns

### Pattern 1: Standalone IFC Creation (No Blender)

```python
# 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)

```python
# 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

```python
# 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)

```python
# 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

```python
# 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

```python
# 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](https://github.com/Impertio-Studio)
- **Source:** [Impertio-Studio/Blender-Bonsai-ifcOpenshell-Sverchok-Claude-Skill-Package](https://github.com/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.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-impertio-studio-blender-bonsai-ifcopenshell-sverchok-claude-skill-package-aec-core-bim-workflows
- Seller: https://agentstack.voostack.com/s/impertio-studio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
