Files
IFC-Exporter-Grasshopper/README.md
T
2026-03-19 16:58:08 +01:00

130 lines
6.1 KiB
Markdown

# Speckle-Grasshopper to IFC 4.3 Exporter
A [Speckle Automate](https://automate.speckle.dev/) function that converts Speckle models into IFC 4.3 files using [ifcopenshell](https://ifcopenshell.org/). Built for Grasshopper + Rhino workflows, but compatible with any application that produces Speckle objects matching the expected property structure.
## What It Does
The exporter receives a Speckle model version, walks its nested collection tree, and produces a standards-compliant IFC 4.3 file. Each Speckle object becomes an IFC element with:
- Correct IFC entity classification read from `_properties.Attributes.type`
- Tessellated geometry (IfcPolygonalFaceSet)
- Material colours from Speckle render materials
- All property sets cloned from `_properties.Property Sets`
- All quantity sets cloned from `_properties.Quantities`
- IFC type objects created from `_properties.Element Type Attributes`
- Building storeys derived from `_properties.Building Storey`
- Spatial structure (IfcProject > IfcSite > IfcBuilding > IfcBuildingStorey)
## Object Structure
The exporter expects Speckle objects with the following `_properties` layout:
```
_properties
├── Attributes → IFC element attributes (type, GlobalId, Name, Tag, etc.)
├── Property Sets → dict of {pset_name: {prop_name: value}}
├── Quantities → dict of {qto_name: {qty_name: {name, units, value}}}
├── Building Storey → string, used for storey assignment
├── Element Type Attributes → type class, Name, GlobalId (creates IfcTypeObject)
└── Element Type Property Sets → property sets written on the IfcTypeObject
```
The nested collection tree is expected as: `Root Collection > Collection > ... > Object`
## Pipeline Overview
```
Speckle Model
1. Receive version (specklepy)
2. Build definition map (for instance geometry reuse)
3. Create IFC scaffold (Project → Site → Building)
4. Traverse collection tree
│ For each leaf element:
│ ├── Classify → IFC entity class (from _properties.Attributes.type)
│ ├── Convert geometry → IfcPolygonalFaceSet
│ ├── Create IFC element + placement
│ ├── Clone all properties & quantities
│ ├── Assign to Building Storey (from _properties.Building Storey)
│ └── Assign IFC type object (from Element Type Attributes)
5. Flush spatial containment & type relationships
6. Write .ifc file
```
## Module Structure
| File | Purpose |
|------|---------|
| `main.py` | Entry point, orchestrates the full pipeline |
| `utils/traversal.py` | Walks the Speckle collection tree (Root > Collection* > Object) |
| `utils/mapper.py` | Reads IFC entity class from `_properties.Attributes.type` |
| `utils/geometry.py` | Converts Speckle meshes to IfcPolygonalFaceSet geometry |
| `utils/instances.py` | Handles InstanceProxy objects with shared geometry (IfcMappedItem) |
| `utils/properties.py` | Clones all properties, quantities, and attributes into IFC entities |
| `utils/type_manager.py` | Creates and caches IfcTypeObjects from Element Type Attributes |
| `utils/materials.py` | Maps Speckle render materials to IfcSurfaceStyle colours |
| `utils/writer.py` | Creates the IFC file scaffold and manages storey creation |
| `utils/receiver.py` | Standalone Speckle model receiver utility |
## Classification
IFC entity classification is read directly from `_properties.Attributes.type` on each object. For example, an object with `Attributes.type = "IfcWall"` becomes an `IfcWall` element. If the field is missing, the object falls back to `IfcBuildingElementProxy`.
For instance proxy objects without their own type, the exporter looks up the definition object's `Attributes.type`.
## Property Handling
All properties are cloned generically — no source-application-specific logic:
| Source | IFC Target |
|--------|------------|
| `_properties.Attributes` | Element attributes: GlobalId, Name, Tag, ObjectType, Description, PredefinedType |
| `_properties.Property Sets.*` | IfcPropertySet per sub-dict (e.g. `Pset_WallCommon` → IfcPropertySet with IfcPropertySingleValue entries) |
| `_properties.Quantities.*` | IfcElementQuantity per sub-dict, with automatic unit detection (mm → IfcQuantityLength, m² → IfcQuantityArea, m³ → IfcQuantityVolume) |
| `_properties.Element Type Attributes` | Shared IfcTypeObject (e.g. IfcWallType), cached by GlobalId |
| `_properties.Element Type Property Sets` | Property sets on the IfcTypeObject |
Property values are auto-typed: `bool` → IfcBoolean, `int` → IfcInteger, `float` → IfcReal, `str` → IfcLabel, `list` → comma-joined IfcLabel.
## Geometry Handling
### Direct Meshes (Path B1)
Objects with `displayValue` containing Mesh objects are converted directly:
1. Extract vertices and faces from each mesh in `displayValue`
2. Scale vertices to millimetres based on the mesh's unit declaration
3. Deduplicate vertices via snap grid (0.01mm tolerance) to avoid IFC GEM111 errors
4. Build `IfcPolygonalFaceSet` with `IfcCartesianPointList3D` + `IfcIndexedPolygonalFace`
5. Compute bounding box origin for `IfcLocalPlacement`, offset vertices relative to it
### Instance Objects (Path A / B2)
Speckle `InstanceProxy` objects reference shared definition geometry via `definitionId`. Geometry is built once as an `IfcRepresentationMap`, then each instance references it via `IfcMappedItem` + `IfcCartesianTransformationOperator3DnonUniform`. This avoids duplicating vertex data across hundreds of identical elements.
## Function Inputs
| Input | Description |
|---|---|
| `file_name` | Output IFC filename (timestamp is appended automatically) |
| `IFC_PROJECT_NAME` | Name for the IfcProject entity |
| `IFC_SITE_NAME` | Name for the IfcSite entity |
| `IFC_BUILDING_NAME` | Name for the IfcBuilding entity |
## Resources
- [Speckle Developer Docs](https://speckle.guide/dev/python.html)
- [ifcopenshell Documentation](https://ifcopenshell.org/)
- [IFC 4.3 Schema](https://standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/)