SuperAPI AI Directory

bonsai-syntax-elements

Coding & development

View repository

Updated 10-16 08:50

About this skill

Bonsai IFC Element Access Syntax

Version: Bonsai v0.8.4 | Blender 4.2.0+ | Python 3.11 Module path: bonsai.* — NEVER blenderbim.* Dependencies: bonsai-core-architecture, ifcos-syntax-api

Critical Warnings

  1. ALWAYS use bonsai.* imports. NEVER use blenderbim.* — renamed in 2024 (v0.8.0+).
  2. ALWAYS check IfcStore.get_file() and tool.Ifc.get() for None before any IFC operation.
  3. ALWAYS use tool.Ifc.get_entity(obj) to get an IFC entity from a Blender object. NEVER read obj.BIMProperties.ifc_definition_id and call model.by_id() manually — the tool method handles edge cases.
  4. NEVER assume every Blender object has an IFC entity. get_entity() returns None for non-IFC objects.
  5. NEVER assume every IFC entity has a Blender object. get_object() returns None for entities without scene representation.
  6. ALWAYS run element access code inside Blender (blender --python). bonsai.tool.* depends on bpy and CANNOT run in standalone Python.
  7. NEVER cache IFC entity references across file reloads. Entity instances become stale when the IFC file is reloaded.
  8. ALWAYS use tool.Ifc.link(element, obj) to create bidirectional mappings. NEVER manually populate IfcStore.id_map or IfcStore.guid_map.

Decision Tree: How to Access IFC Elements

What do you have?
│
├── A Blender object (bpy.types.Object)?
│   │
│   ├── Need the IFC entity?
│   │   └── tool.Ifc.get_entity(obj)
│   │       ├── Returns entity_instance → access .GlobalId, .Name, .is_a(), attributes
│   │       └── Returns None → object is NOT linked to IFC
│   │
│   ├── Need just the IFC step ID?
│   │   └── obj.BIMProperties.ifc_definition_id
│   │       ├── Returns int > 0 → linked to IFC entity with that step ID
│   │       └── Returns 0 → NOT linked to IFC
│   │
│   └── Need IFC class name?
│       └── entity = tool.Ifc.get_entity(obj)
│           └── entity.is_a()  # "IfcWall", "IfcSlab", etc.
│
├── An IFC entity (ifcopenshell.entity_instance)?
│   │
│   ├── Need the Blender object?
│   │   └── tool.Ifc.get_object(entity)
│   │       ├── Returns bpy.types.Object → manipulate via Blender API
│   │       └── Returns None → entity has no Blender scene representation
│   │
│   └── Need another entity related to this one?
│       ├── Type definition?
│       │   └── ifcopenshell.util.element.get_type(entity)
│       ├── Spatial container?
│       │   └── ifcopenshell.util.element.get_container(entity)
│       ├── Property sets?
│       │   └── ifcopenshell.util.element.get_psets(entity)
│       └── Material?
│           └── ifcopenshell.util.element.get_material(entity)
│
├── An IFC class name (string)?
│   │
│   └── Need all entities of that class?
│       └── model = tool.Ifc.get()
│           └── model.by_type("IfcWall")  # Returns list of entity_instance
│
├── An IFC GlobalId (string)?
│   │
│   └── model = tool.Ifc.get()
│       └── model.by_guid("2O2Fr$t4X7Zf8...")  # Returns entity_instance
│
├── An IFC step ID (int)?
│   │
│   ├── Need the IFC entity?
│   │   └── model = tool.Ifc.get()
│   │       └── model.by_id(step_id)  # Returns entity_instance
│   │
│   └── Need the Blender object directly?
│       └── IfcStore.id_map.get(step_id)  # Returns bpy.types.Object or None
│
└── Nothing — need to discover elements?
    │
    ├── All elements in the model?
    │   └── model.by_type("IfcElement")  # All physical elements
    │
    ├── All elements in a specific storey?
    │   └── ifcopenshell.util.element.get_decomposition(storey)
    │
    └── All elements matching a query?
        └── Use ifcopenshell.util.selector (CSS-like syntax)

Essential Patterns

Pattern 1: Get the Live IFC File

# Bonsai v0.8.x: inside Blender context

# Method A: via tool.Ifc (preferred in Bonsai tool/core code)
import bonsai.tool as tool

model = tool.Ifc.get()
if model is None:
    raise RuntimeError("No IFC project loaded.")

# Method B: via IfcStore (equivalent, used in operator code)
from bonsai.bim.ifc import IfcStore

model = IfcStore.get_file()
if model is None:
    raise RuntimeError("No IFC project loaded.")

tool.Ifc.get() and IfcStore.get_file() return the same ifcopenshell.file instance. Use tool.Ifc.get() in tool/core code; use IfcStore.get_file() in operator/UI code.

Pattern 2: Blender Object → IFC Entity

# Bonsai v0.8.x: inside Blender context
import bpy
import bonsai.tool as tool

obj = bpy.context.active_object
entity = tool.Ifc.get_entity(obj)

if entity is None:
    print(f"{obj.name} is not linked to IFC")
else:
    print(f"IFC class: {entity.is_a()}")
    print(f"GlobalId:  {entity.GlobalId}")
    print(f"Name:      {entity.Name}")
    print(f"Step ID:   {entity.id()}")

Pattern 3: IFC Entity → Blender Object

# Bonsai v0.8.x: inside Blender context
import bonsai.tool as tool

model = tool.Ifc.get()
wall = model.by_type("IfcWall")[0]
obj = tool.Ifc.get_object(wall)

if obj is None:
    print(f"Wall {wall.Name} has no Blender representation")
else:
    print(f"Blender object: {obj.name}")
    obj.select_set(True)  # Select in viewport

Pattern 4: Query Elements by Type

# Bonsai v0.8.x: inside Blender context
import bonsai.tool as tool

model = tool.Ifc.get()
if model is None:
    raise RuntimeError("No IFC project loaded.")

# All walls
walls = model.by_type("IfcWall")

# All physical elements (walls, slabs, columns, doors, etc.)
elements = model.by_type("IfcElement")

# All spatial elements (sites, buildings, storeys, spaces)
spatial = model.by_type("IfcSpatialElement")  # IFC4+
# For IFC2X3: model.by_type("IfcSpatialStructureElement")

# Specific type with subtypes excluded
walls_only = model.by_type("IfcWall", include_subtypes=False)
# Excludes IfcWallStandardCase (IFC2X3) and subtypes

Pattern 5: Look Up by GlobalId or Step ID

# Bonsai v0.8.x: inside Blender context
import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore

model = tool.Ifc.get()

# By GlobalId (22-character IFC identifier)
entity = model.by_guid("2O2Fr$t4X7Zf8NOew3FLOH")

# By step ID (integer)
entity = model.by_id(42)

# Step ID → Blender object (direct lookup, bypasses entity)
obj = IfcStore.id_map.get(42)

# GlobalId → Blender object (direct lookup)
obj = IfcStore.guid_map.get("2O2Fr$t4X7Zf8NOew3FLOH")

Pattern 6: Navigate Entity Relationships

# Bonsai v0.8.x: inside Blender context
import bonsai.tool as tool
import ifcopenshell.util.element

model = tool.Ifc.get()
wall = model.by_type("IfcWall")[0]

# Get the spatial container (storey/space) of an element
container = ifcopenshell.util.element.get_container(wall)
if container:
    print(f"Wall is in: {container.Name} ({container.is_a()})")

# Get the type definition
wall_type = ifcopenshell.util.element.get_type(wall)
if wall_type:
    print(f"Wall type: {wall_type.Name}")

# Get all property sets
psets = ifcopenshell.util.element.get_psets(wall)
for pset_name, props in psets.items():
    print(f"  {pset_name}: {props}")

# Get material
material = ifcopenshell.util.element.get_material(wall)
if material:
    print(f"Material: {material.is_a()} — {material.Name}")

Pattern 7: Iterate Over All Elements with Blender Objects

# Bonsai v0.8.x: inside Blender context
import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore

model = tool.Ifc.get()

# Method A: Iterate IfcStore.id_map (fastest for Blender-linked elements)
for ifc_id, obj in IfcStore.id_map.items():
    entity = model.by_id(ifc_id)
    print(f"{obj.name} → {entity.is_a()} ({entity.Name})")

# Method B: Iterate IFC entities and look up Blender objects
for wall in model.by_type("IfcWall"):
    obj = tool.Ifc.get_object(wall)
    if obj:
        print(f"{wall.Name} → {obj.name}")

Pattern 8: Check if Object is IFC-Linked

# Bonsai v0.8.x: inside Blender context
import bpy

obj = bpy.context.active_object

# Quick check via property (no tool import needed)
ifc_id = obj.BIMProperties.ifc_definition_id
if ifc_id == 0:
    print("Not linked to IFC")
else:
    print(f"Linked to IFC entity #{ifc_id}")

# Full check with entity retrieval
import bonsai.tool as tool
entity = tool.Ifc.get_entity(obj)
is_ifc_linked = entity is not None

The Blender-IFC Data Bridge

Mapping Architecture

Blender Side                          IFC Side
───────────────────────                ────────────────────────
bpy.types.Object                      ifcopenshell.entity_instance
  .name                    ←→          .Name
  .BIMProperties
    .ifc_definition_id     ────→       .id()  (step ID)
                           ←────       IfcStore.id_map[id]
  .matrix_world            ←→          IfcLocalPlacement
  .data (Mesh)             ←→          IfcShapeRepresentation

           tool.Ifc.get_entity(obj)  ──→
           tool.Ifc.get_object(ent)  ←──
           tool.Ifc.link(ent, obj)   ←→  bidirectional registration

Key Properties on Blender Objects

Property Type Access Description
obj.BIMProperties.ifc_definition_id int Read IFC step ID. 0 = not linked.
obj.name str Read/Write Blender object name (may differ from IFC Name)
obj.matrix_world Matrix Read/Write World-space transform (sync to IFC via edit_object_placement)
obj.data bpy.types.Mesh Read Geometry data (regenerated from IFC representations)

Mapping Lookup Tables

Lookup Data Structure Direction Key → Value
IfcStore.id_map dict IFC → Blender int (step ID) → bpy.types.Object
IfcStore.guid_map dict IFC → Blender str (GlobalId) → bpy.types.Object
obj.BIMProperties.ifc_definition_id Property Blender → IFC bpy.types.Object → int (step ID)

IfcStore Element Access Methods

Quick Reference

Method Input Returns Use When
IfcStore.get_file() — ifcopenshell.file | None Getting the live IFC model
IfcStore.get_element(ifc_id) int bpy.types.Object | None Looking up Blender object by step ID
IfcStore.id_map — dict[int, Object] Iterating all linked objects
IfcStore.guid_map — dict[str, Object] Looking up by GlobalId
tool.Ifc.get() — ifcopenshell.file | None Same as get_file() (tool layer)
tool.Ifc.get_entity(obj) Object entity_instance | None Object → IFC entity
tool.Ifc.get_object(element) entity_instance Object | None IFC entity → Object
tool.Ifc.link(element, obj) Both None Register bidirectional mapping

ifcopenshell.file Query Methods

These methods are called on the ifcopenshell.file instance returned by tool.Ifc.get() or IfcStore.get_file().

Method Signature Returns Description
by_type(type) (type: str, include_subtypes=True) -> list List of entities All entities of a given IFC class
by_id(id) (id: int) -> entity_instance Single entity Entity by step ID. Raises RuntimeError if not found.
by_guid(guid) (guid: str) -> entity_instance Single entity Entity by GlobalId. Raises RuntimeError if not found.

ALWAYS Wrap Lookups in Try/Except When ID Source is External

model = tool.Ifc.get()
try:
    entity = model.by_id(user_provided_id)
except RuntimeError:
    print(f"No entity with step ID {user_provided_id}")

ifcopenshell.util.element: Relationship Navigation

Function Signature Returns Description
get_container(element) (element) -> entity | None Spatial container IfcBuildingStorey, IfcSpace, etc.
get_type(element) (element) -> entity | None Type definition IfcWallType, IfcSlabType, etc.
get_types(type_element) (type_element) -> list Occurrences All instances of a type
get_psets(element) (element, psets_only=False, qtos_only=False, should_inherit=True) -> dict Property dict All psets/qtos as nested dict
get_material(element) (element) -> entity | None Material IfcMaterial, IfcMaterialLayerSet, etc.
get_decomposition(element) (element) -> list Children All decomposed child elements
get_aggregate(element) (element) -> entity | None Parent Aggregating parent element

Common Operations

Select All Elements of a Type in Viewport

# Bonsai v0.8.x: select all IfcDoor objects in Blender viewport
import bpy
import bonsai.tool as tool

model = tool.Ifc.get()
if model is None:
    raise RuntimeError("No IFC project loaded.")

bpy.ops.object.select_all(action='DESELECT')
for door in model.by_type("IfcDoor"):
    obj = tool.Ifc.get_object(door)
    if obj:
        obj.select_set(True)

Get Property Value from Active Object

# Bonsai v0.8.x: read FireRating from active object
import bpy
import bonsai.tool as tool
import ifcopenshell.util.element

obj = bpy.context.active_object
entity = tool.Ifc.get_entity(obj)
if entity:
    psets = ifcopenshell.util.element.get_psets(entity)
    wall_common = psets.get("Pset_WallCommon", {})
    fire_rating = wall_common.get("FireRating")
    print(f"FireRating: {fire_rating}")

List All Elements in a Storey

# Bonsai v0.8.x: list all elements contained in first storey
import bonsai.tool as tool
import ifcopenshell.util.element

model = tool.Ifc.get()
storey = model.by_type("IfcBuildingStorey")[0]
elements = ifcopenshell.util.element.get_decomposition(storey)
for el in elements:
    print(f"  {el.is_a()}: {el.Name} (#{el.id()})")

Version Notes

Bonsai v0.8.0+ (2024 Rename)

Change Before (BlenderBIM) After (Bonsai v0.8.0+)
Module path blenderbim.* bonsai.*
IfcStore import from blenderbim.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
Tool import import blenderbim.tool as tool import bonsai.tool as tool
Core import import blenderbim.core.spatial import bonsai.core.spatial

IfcOpenShell v0.8+ List Parameters

Since IfcOpenShell v0.8, most relationship API functions use list parameters (products, related_objects) instead of singular. This affects tool.Ifc.run() calls:

# CORRECT (v0.8+)
tool.Ifc.run("spatial.assign_container",
    products=[wall], relating_structure=storey)

# WRONG (pre-v0.8 style)
tool.Ifc.run("spatial.assign_container",
    product=wall, relating_structure=storey)  # KeyError

Reference Links

Dependencies

  • bonsai-core-architecture — Three-layer architecture, IfcStore overview, operator pattern
  • ifcos-syntax-api — ifcopenshell.api.run() invocation patterns, module table

Sources

  • Bonsai source: src/bonsai/bonsai/bim/ifc.py (IfcStore)
  • Bonsai source: src/bonsai/bonsai/tool/ifc.py (tool.Ifc implementation)
  • Bonsai source: src/bonsai/bonsai/core/tool.py (@interface definitions)
  • IfcOpenShell API: https://docs.ifcopenshell.org
  • Bonsai Documentation: https://docs.bonsaibim.org