# Speckle β†’ IFC 4.3 Exporter (Grasshopper) ## 🚧 Project Status: WIP Hey there! This project is still under active development, so expect changes, bugs, and incomplete features. If you have any questions or suggestions, don’t hesitate to reach out at: **nikos@speckle.systems** A [Speckle Automate](https://docs.speckle.systems/developers/automate/introduction) function that converts Speckle models from Grasshopper into IFC 4X3 files. using [ifcopenshell](https://ifcopenshell.org/). > ⚠️ **Note on Model Uploads** > > Large models (greater than 200MB) may fail to upload due to current file size limitations. The team is actively working on resolving this issue. ## 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 3D geometry (IfcPolygonalFaceSet) from Mesh, Brep, or BrepX objects - 2D curve geometry (IfcIndexedPolyCurve) from Polycurve, Line, Arc, Polyline objects - Material colours from `renderMaterialProxies` applied as IfcSurfaceStyle - All property sets cloned from `properties.Property Sets` - All quantity sets cloned from `properties.Quantities` (supports both `{name, units, value}` dicts and plain numeric values) - IFC type objects from `properties.Element Type Attributes` and/or `properties.Element Type Property Sets` - Building storeys derived from `properties.Building Storey` - Spatial structure (IfcProject > IfcSite > IfcBuilding > IfcBuildingStorey) Objects that serve as instance definition geometry sources are automatically skipped during export β€” their geometry is shared via IfcRepresentationMap. Screenshot 2026-03-19 165619 ifc-gh ## Object Structure The exporter expects Speckle `DataObject` elements with a `properties` dict: ``` properties β”œβ”€β”€ Attributes β†’ IFC element attributes (type, GlobalId, Name, Tag, etc.) β”œβ”€β”€ Property Sets β†’ {pset_name: {prop_name: value}} β”œβ”€β”€ Quantities β†’ {qto_name: {qty_name: value_or_dict}} β”œβ”€β”€ Building Storey β†’ string, used for storey assignment β”œβ”€β”€ Element Type Attributes β†’ (optional) type class, Name, GlobalId (creates IfcTypeObject) └── Element Type Property Sets β†’ (optional) property sets written on the IfcTypeObject ``` The nested collection tree is expected as: `Root Collection > Collection > ... > DataObject` ### Type Object Handling Two formats are supported for creating IFC type objects: - **Format A** β€” `Element Type Attributes` contains explicit type info (`type`, `Name`, `GlobalId`, etc.) and `Element Type Property Sets` contains the type's property sets. - **Format B** β€” Only `Element Type Property Sets` exists (no `Element Type Attributes`). The type class is derived from the element class (e.g. `IfcColumn` β†’ `IfcColumnType`). ### Quantity Formats Quantities support two value formats: - **Dict format**: `{'name': 'Length', 'units': 'Millimetre', 'value': 3000}` β€” unit is used to select the correct IFC quantity type (IfcQuantityLength, IfcQuantityArea, IfcQuantityVolume, etc.) - **Plain format**: `{'Length': 3000, 'GrossVolume': 0.27}` β€” quantity type is inferred from name keywords (e.g. "Length" β†’ IfcQuantityLength, "Area" β†’ IfcQuantityArea, "Volume" β†’ IfcQuantityVolume). Falls back to IfcQuantityCount if no keyword matches. ## Pipeline Overview ``` Speckle Model β”‚ β–Ό 1. Receive version (specklepy) β”‚ β–Ό 2. Build definition map (for instance geometry reuse + definition source detection) β”‚ β–Ό 3. Create IFC scaffold (Project β†’ Site β†’ Building) β”‚ β–Ό 4. Initialize material manager (parse renderMaterialProxies) β”‚ β–Ό 5. Traverse collection tree β”‚ For each leaf element: β”‚ β”œβ”€β”€ Skip spatial structure types and definition geometry sources β”‚ β”œβ”€β”€ Classify β†’ IFC entity class (from properties.Attributes.type) β”‚ β”œβ”€β”€ Convert geometry β†’ IfcPolygonalFaceSet or IfcIndexedPolyCurve (with material colours) β”‚ β”œβ”€β”€ Create IFC element + placement β”‚ β”œβ”€β”€ Clone all properties & quantities β”‚ β”œβ”€β”€ Assign to Building Storey (from properties.Building Storey) β”‚ └── Assign IFC type object β”‚ β–Ό 6. Flush spatial containment & type relationships β”‚ β–Ό 7. Write .ifc file ``` ## Module Structure | File | Purpose | |------|---------| | `main.py` | Entry point, orchestrates the full pipeline | | `utils/helpers.py` | Shared utilities: safe attribute access (`_get`), unit scale constants, and `resolve_scale` | | `utils/traversal.py` | Walks the Speckle collection tree (Root > Collection* > DataObject) | | `utils/mapper.py` | Reads IFC entity class from `properties.Attributes.type` | | `utils/geometry.py` | Converts Speckle Mesh/Brep/BrepX geometry to IfcPolygonalFaceSet | | `utils/curves.py` | Converts Speckle 2D curve geometry (Polycurve, Line, Arc) to IfcIndexedPolyCurve | | `utils/instances.py` | Handles InstanceProxy objects with shared geometry (IfcMappedItem), content-based geometry dedup | | `utils/properties.py` | Clones all properties, quantities, and attributes into IFC entities | | `utils/type_manager.py` | Creates and caches IfcTypeObjects, supports both explicit and derived type classes | | `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 from `properties.Attributes.type` on each object. For example, `Attributes.type = "IfcWall"` produces an `IfcWall` element. Falls back to `IfcBuildingElementProxy` if missing. 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` β†’ IfcPropertySingleValue entries) | | `properties.Quantities.*` | IfcElementQuantity per sub-dict, with automatic unit detection (mm β†’ IfcQuantityLength, mΒ² β†’ IfcQuantityArea, mΒ³ β†’ IfcQuantityVolume) and name-based inference (Length, Width, Height, Area, Volume, Weight) | | `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 ### Supported Geometry Types The exporter handles geometry found in `displayValue` or directly on the object: **3D Geometry (Mesh)** - **Mesh** β€” converted directly (vertices + faces) - **Brep / BrepX** β€” recursively resolved to their inner tessellated mesh representation via nested `displayValue` **2D Geometry (Curves)** - **Polycurve** β€” segments (Line, Arc, Polyline) converted to `IfcIndexedPolyCurve` with `IfcLineIndex` / `IfcArcIndex` - **Line** β€” start/end points β†’ `IfcLineIndex` - **Arc** β€” start/mid/end points β†’ `IfcArcIndex` Curves are typically found wrapped inside `DataObject.displayValue`, following the same pattern as meshes. The exporter checks for curves as a fallback when no mesh or instance geometry is found. ### 3D Mesh Conversion 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. Round coordinates to 0.001mm precision for compact IFC file output 5. Build `IfcPolygonalFaceSet` with `IfcCartesianPointList3D` + `IfcIndexedPolygonalFace` 6. Compute bounding box origin for `IfcLocalPlacement`, offset vertices relative to it ### 2D Curve Conversion 1. Extract curve segments from the object or its `displayValue` 2. Parse each segment type (Line β†’ start/end, Arc β†’ start/mid/end, Polyline β†’ point sequence) 3. Deduplicate points via snap grid (0.01mm tolerance) 4. Round coordinates to 0.001mm precision for compact IFC file output 5. Build `IfcIndexedPolyCurve` with `IfcCartesianPointList3D` + `IfcLineIndex` / `IfcArcIndex` segments 6. Compute bounding box origin for placement, offset points 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/curve data across hundreds of identical elements. Both mesh and curve definitions are supported. **Content-based geometry deduplication**: Instance definitions with identical vertex/face data and materials are detected via MD5 content hashing and share a single `IfcRepresentationMap`, even if they have different `definitionId`s. Direction vectors for transform operators are also cached and reused across instances. ## Material Handling Materials are read from `root.renderMaterialProxies` and applied as `IfcSurfaceStyle` on geometry items. Each proxy contains a `RenderMaterial` (name, diffuse colour as ARGB packed int, opacity) and a list of object references. The material resolution uses a multi-strategy lookup since `renderMaterialProxies.objects` references different IDs depending on the source format: | Format | What `objects` references | Resolution strategy | |--------|--------------------------|-------------------| | **IFC format** (speckleifc) | Mesh applicationIds directly | Direct lookup by mesh applicationId | | **Grasshopper format** | Inner InstanceProxy applicationIds | Map via definitionId β†’ material | | **Direct mesh/BrepX** | Parent DataObject applicationIds | Fall back to parent object's applicationId | IFC styles are created lazily (only when actually assigned to geometry) to avoid orphaned IfcSurfaceStyle entities. ## 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/)