Use when querying, traversing, or extracting data from IFC elements -- by_type, by_id, by_guid, inverse references, or property extraction. Prevents the common mistake of manually traversing relationships instead of using the universal property extraction pattern (IsDefinedBy -> HasProperties). Covers get_info(), is_a(), GUID utilities, and attribute access patterns. Keywords: by_type, by_id, by_guid, get_info, is_a, inverse, IsDefinedBy, HasProperties, IFC query, element traversal, property extraction, find all walls, get element by GlobalId, list elements.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Use when querying, traversing, or extracting data from IFC elements -- by_type, by_id, by_guid, inverse references, or property extraction. Prevents the common mistake of manually traversing relationships instead of using the universal property extraction pattern (IsDefinedBy -> HasProperties). Covers get_info(), is_a(), GUID utilities, and attribute access patterns. Keywords: by_type, by_id, by_guid, get_info, is_a, inverse, IsDefinedBy, HasProperties, IFC query, element traversal, property extraction, find all walls, get element by GlobalId, list elements.
license
MIT
compatibility
Designed for Claude Code. Requires IfcOpenShell Python library.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
IfcOpenShell Element Traversal and Querying
Quick Reference
Decision Tree: Finding Elements
Need to find IFC elements?
├── Know the IFC class? (IfcWall, IfcDoor, etc.)
│ └── model.by_type("IfcWall")
│ ├── Need subtypes included? → include_subtypes=True (DEFAULT)
│ └── Need exact type only? → include_subtypes=False
│
├── Know the STEP ID? (#123 in .ifc file)
│ └── model.by_id(123)
│ └── WARNING: STEP IDs are NOT persistent across re-exports
│
├── Know the GlobalId? (22-char IFC GUID)
│ └── model.by_guid("2O2Fr$t4X7Zf8NOew3FLOH")
│ └── GlobalId is ONLY on IfcRoot-derived entities
│
├── Need complex filtering? (by property, container, material)
│ └── ifcopenshell.util.selector.filter_elements(model, query)
│
└── Need ALL entities in the file?
└── for entity in model: ...
Decision Tree: Reading Element Data
Need data from an element?
├── Single attribute? → element.Name, element.GlobalId, etc.
│
├── All attributes as dict? → element.get_info()
│ ├── Need referenced entities expanded? → get_info(recursive=True)
│ │ └── WARNING: EXPENSIVE on large models — avoid in loops
│ └── Need only primitive values? → get_info(scalar_only=True)
│
├── Type checking?
│ ├── Get type name → element.is_a() # returns "IfcWall"
│ └── Check inheritance → element.is_a("IfcElement") # returns True/False
│
├── Property sets?
│ ├── RECOMMENDED → ifcopenshell.util.element.get_psets(element)
│ └── Manual pattern → element.IsDefinedBy traversal (see below)
│
├── Spatial container? → ifcopenshell.util.element.get_container(element)
├── Element type? → ifcopenshell.util.element.get_type(element)
├── Material? → ifcopenshell.util.element.get_material(element)
└── STEP ID? → element.id()
Critical Warnings
ALWAYS use model.by_type() for type-based queries — it is internally cached after the first call per type.
ALWAYS use ifcopenshell.guid.new() to generate GlobalIds. NEVER construct GUID strings manually.
ALWAYS use ifcopenshell.util.element.get_psets() for property extraction in production code. Use the manual IsDefinedBy traversal only when you need fine-grained control.
NEVER use STEP IDs (entity.id()) as persistent identifiers. They change when files are re-exported. Use GlobalId for cross-session identification.
NEVER call get_info(recursive=True) in a loop over many elements — it recursively expands all references and is extremely expensive.
NEVER assume an entity has a GlobalId. Only IfcRoot-derived entities (IfcWall, IfcProject, etc.) have GlobalIds. Low-level entities (IfcCartesianPoint, IfcDirection) do NOT.
ALWAYS handle None values when accessing optional attributes. Many IFC attributes are optional and return None.
ALWAYS check prop.NominalValue is not None before accessing .wrappedValue in the property extraction pattern.
Essential Patterns
Pattern 1: Query Elements by IFC Class
# IfcOpenShell: all schema versionsimport ifcopenshell
model = ifcopenshell.open("model.ifc")
# Get all walls (includes subtypes like IfcWallStandardCase in IFC2X3)
walls = model.by_type("IfcWall")
# Get ONLY exact IfcWall, NOT subtypes
walls_exact = model.by_type("IfcWall", include_subtypes=False)
# Common element queries
storeys = model.by_type("IfcBuildingStorey")
spaces = model.by_type("IfcSpace")
doors = model.by_type("IfcDoor")
windows = model.by_type("IfcWindow")
slabs = model.by_type("IfcSlab")
columns = model.by_type("IfcColumn")
beams = model.by_type("IfcBeam")
Pattern 2: Query by ID and GUID
# IfcOpenShell: all schema versionsimport ifcopenshell
import ifcopenshell.guid
model = ifcopenshell.open("model.ifc")
# By STEP ID (the #123 number in .ifc files)
entity = model.by_id(123)
# By GlobalId (22-character IFC GUID)
entity = model.by_guid("2O2Fr$t4X7Zf8NOew3FLOH")
# Generate a new GlobalId
new_guid = ifcopenshell.guid.new()
# Convert between IFC GUID and standard UUID
standard_uuid = ifcopenshell.guid.expand(new_guid) # → UUID string
ifc_guid = ifcopenshell.guid.compress(standard_uuid) # → 22-char GUID
Pattern 3: Entity Attribute Access and Type Checking
# IfcOpenShell: all schema versions
wall = model.by_type("IfcWall")[0]
# Direct attribute accessprint(wall.Name) # "Wall 001" or Noneprint(wall.GlobalId) # "2O2Fr$t4X7Zf8NOew3FLOH"print(wall.Description) # Optional, may be None# Type checking
wall.is_a() # "IfcWall" (returns type name)
wall.is_a("IfcWall") # True (exact match)
wall.is_a("IfcElement") # True (parent class)
wall.is_a("IfcRoot") # True (ancestor)
wall.is_a("IfcSlab") # False# STEP ID
wall.id() # 123 (integer)# All attributes as dict
info = wall.get_info()
# {"id": 123, "type": "IfcWall", "GlobalId": "...", "Name": "...", ...}
Pattern 4: Inverse References
# IfcOpenShell: all schema versions
wall = model.by_type("IfcWall")[0]
# Get ALL entities that REFERENCE this wall
inverse = model.get_inverse(wall) # returns set# Find which storey contains this wallfor rel in model.get_inverse(wall):
if rel.is_a("IfcRelContainedInSpatialStructure"):
print(f"Wall is in: {rel.RelatingStructure.Name}")
# Find property sets attached to an elementfor rel in model.get_inverse(wall):
if rel.is_a("IfcRelDefinesByProperties"):
pset = rel.RelatingPropertyDefinition
if pset.is_a("IfcPropertySet"):
print(f"PSet: {pset.Name}")
# Find the type object of an elementfor rel in model.get_inverse(wall):
if rel.is_a("IfcRelDefinesByType"):
print(f"Type: {rel.RelatingType.Name}")
Pattern 5: Universal IFC Property Extraction
This is the fundamental pattern for extracting properties from ANY IFC element. It traverses: IsDefinedBy → IfcRelDefinesByProperties → IfcPropertySet → HasProperties → wrappedValue.
# IfcOpenShell: all schema versions# UNIVERSAL PATTERN: Works with IfcOpenShell, Bonsai, web-ifcdefextract_properties(element):
"""Extract all property sets and their values from an IFC element."""
result = {}
for rel in element.IsDefinedBy:
if rel.is_a("IfcRelDefinesByProperties"):
pset = rel.RelatingPropertyDefinition
if pset.is_a("IfcPropertySet"):
props = {}
for prop in pset.HasProperties:
if prop.is_a("IfcPropertySingleValue") and prop.NominalValue:
props[prop.Name] = prop.NominalValue.wrappedValue
result[pset.Name] = props
return result
wall = model.by_type("IfcWall")[0]
all_props = extract_properties(wall)
for pset_name, props in all_props.items():
print(f"\n{pset_name}:")
for name, value in props.items():
print(f" {name}: {value}")
Pattern 6: Recommended: Use util.element Helpers
# IfcOpenShell: all schema versionsimport ifcopenshell.util.element
wall = model.by_type("IfcWall")[0]
# Get ALL property sets as {pset_name: {prop_name: value}}
psets = ifcopenshell.util.element.get_psets(wall)
# Include quantity sets (IfcElementQuantity)
psets_and_qsets = ifcopenshell.util.element.get_psets(wall, psets_only=False)
# Get only quantity sets
qsets = ifcopenshell.util.element.get_psets(wall, qtos_only=True)
# Get element type
wall_type = ifcopenshell.util.element.get_type(wall)
# Get spatial container (typically IfcBuildingStorey)
container = ifcopenshell.util.element.get_container(wall)
# Get material
material = ifcopenshell.util.element.get_material(wall)
# Get all materials as list
materials = ifcopenshell.util.element.get_materials(wall)
# Get parent aggregate
parent = ifcopenshell.util.element.get_aggregate(wall)
# Get decomposition (children)
building = model.by_type("IfcBuilding")[0]
children = ifcopenshell.util.element.get_decomposition(building)
Common Operations
Iterating Over All Entities
# IfcOpenShell: all schema versions# Iterate over ALL entitiesfor entity in model:
pass# Total entity count
total = len(model)
# Count entities by typefrom collections import Counter
type_counts = Counter(entity.is_a() for entity in model)
for ifc_type, count in type_counts.most_common(20):
print(f" {ifc_type}: {count}")
Finding Openings in a Wall
# IfcOpenShell: all schema versionsdefget_openings(model, wall):
"""Get opening elements that void a wall."""
openings = []
for rel in model.get_inverse(wall):
if rel.is_a("IfcRelVoidsElement"):
openings.append(rel.RelatedOpeningElement)
return openings
CSS-like Element Selection
# IfcOpenShell: all schema versionsimport ifcopenshell.util.selector
# Select walls by class
walls = ifcopenshell.util.selector.filter_elements(model, "IfcWall")
# Select by property value
ext_walls = ifcopenshell.util.selector.filter_elements(
model, 'IfcWall, /Pset_WallCommon/.IsExternal = True')
# Select by container
ground_elements = ifcopenshell.util.selector.filter_elements(
model, 'IfcBuildingElement, container="Ground Floor"')
Error Handling for Lookups
# IfcOpenShell: all schema versions# by_id and by_guid raise RuntimeError if not foundtry:
entity = model.by_id(999999)
except RuntimeError:
print("Entity not found")
try:
entity = model.by_guid("nonexistent_guid_12345")
except RuntimeError:
print("Entity not found")
# by_type returns empty tuple if no matches
walls = model.by_type("IfcWall") # () if none exist
Version Notes
Schema-Specific Entity Names
Concept
IFC2X3
IFC4
IFC4X3
Building elements parent
IfcBuildingElement
IfcBuildingElement
IfcBuiltElement
Spatial elements parent
IfcSpatialStructureElement
IfcSpatialElement
IfcSpatialElement
Wall subtype
IfcWallStandardCase
IfcWall (subtype removed)
IfcWall
Facility types
N/A
N/A
IfcBridge, IfcRoad, IfcRailway, IfcMarineFacility
Query API Compatibility
The query methods (by_type, by_id, by_guid, get_inverse, get_info, is_a) are schema-agnostic — they work identically across IFC2X3, IFC4, and IFC4X3. Only the entity class names passed to these methods differ per schema.
GUID Module
ifcopenshell.guid is schema-independent. The same GUID format (22-character base64) is used across all IFC versions.