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 function that converts Speckle models from Grasshopper into IFC 4X3 files. using ifcopenshell.
β οΈ Note on Model UploadsLarge models (greater than 200MB) may fail to upload due to current file size limitations. The team is actively working on resolving this issue.
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
renderMaterialProxiesapplied 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 Attributesand/orproperties.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.
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
Two formats are supported for creating IFC type objects:
- Format A β
Element Type Attributescontains explicit type info (type,Name,GlobalId, etc.) andElement Type Property Setscontains the type's property sets. - Format B β Only
Element Type Property Setsexists (noElement Type Attributes). The type class is derived from the element class (e.g.IfcColumnβIfcColumnType).
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.
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
| 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 |
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.
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.
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
IfcIndexedPolyCurvewithIfcLineIndex/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.
- Extract vertices and faces from each mesh in
displayValue - Scale vertices to millimetres based on the mesh's unit declaration
- Deduplicate vertices via snap grid (0.01mm tolerance) to avoid IFC GEM111 errors
- Round coordinates to 0.001mm precision for compact IFC file output
- Build
IfcPolygonalFaceSetwithIfcCartesianPointList3D+IfcIndexedPolygonalFace - Compute bounding box origin for
IfcLocalPlacement, offset vertices relative to it
- Extract curve segments from the object or its
displayValue - Parse each segment type (Line β start/end, Arc β start/mid/end, Polyline β point sequence)
- Deduplicate points via snap grid (0.01mm tolerance)
- Round coordinates to 0.001mm precision for compact IFC file output
- Build
IfcIndexedPolyCurvewithIfcCartesianPointList3D+IfcLineIndex/IfcArcIndexsegments - Compute bounding box origin for placement, offset points relative to it
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 definitionIds. Direction vectors for transform operators are also cached and reused across instances.
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.
| 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 |