Added project status section indicating active development.
Speckle-Revit to IFC 4.3 Exporter
🚧 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: your@email.com
A Speckle Automate function that converts Speckle Revit models into IFC 4.3 files using ifcopenshell. This exporter is specifically designed for models sent to Speckle from Autodesk Revit and relies on Revit-specific object structures, categories, and parameters.
What It Does
The exporter receives a Speckle model version, walks its object tree, and produces a standards-compliant IFC 4.3 file. Each Speckle object becomes an IFC element with:
- Correct IFC entity classification (IfcWall, IfcSlab, IfcColumn, etc.)
- Tessellated geometry (IfcPolygonalFaceSet)
- Curve geometry for Lines and Arcs (IfcGeometricCurveSet with IfcPolyline)
- Material colours from Speckle render materials
- Revit property sets (Common psets, instance/type parameters, material quantities)
- IFC type objects (IfcWallType, IfcSlabType, etc.) shared across instances
- Spatial structure (IfcProject > IfcSite > IfcBuilding > IfcBuildingStorey)
- IfcSpace elements aggregated under storeys with Room properties
- Automatic skipping of analytical/energy categories (e.g. Energy Analysis, MEP Analytical, Solar Shading)
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 object tree
│ For each leaf element:
│ ├── Classify → IFC entity class (skip analytical categories)
│ ├── Convert geometry → IfcPolygonalFaceSet or IfcGeometricCurveSet
│ ├── Create IFC element + placement
│ ├── Write property sets & quantities
│ └── Assign IFC type object
│
▼
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 (Project > Level > Category > Element) |
utils/mapper.py |
Classifies Speckle objects into IFC entity types |
utils/geometry.py |
Converts Speckle meshes to IfcPolygonalFaceSet and Lines/Arcs to IfcGeometricCurveSet geometry |
utils/instances.py |
Handles InstanceProxy objects with shared geometry (IfcMappedItem) |
utils/properties.py |
Writes IFC property sets and quantities from Revit parameters |
utils/type_manager.py |
Creates and caches IfcTypeObjects (IfcWallType, etc.) |
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 |
Connects to Speckle server and receives model data (uses .env) |
Mapping Logic
Classification of Speckle objects to IFC entity types follows a priority chain. The first match wins.
Priority 1: builtInCategory (OST_ enum)
The most reliable source. Read from obj.properties.builtInCategory, which contains the Revit BuiltInCategory enum value.
Examples:
| builtInCategory | IFC Class |
|---|---|
OST_Walls |
IfcWall |
OST_Floors |
IfcSlab |
OST_StructuralColumns |
IfcColumn |
OST_StructuralFraming |
IfcBeam |
OST_Doors |
IfcDoor |
OST_Windows |
IfcWindow |
OST_Roofs |
IfcRoof |
OST_CurtainWallPanels |
IfcCurtainWall |
OST_DuctCurves |
IfcDuctSegment |
OST_PipeCurves |
IfcPipeSegment |
OST_PipeFitting |
IfcPipeFitting |
OST_PlumbingEquipment |
IfcSanitaryTerminal |
OST_Rebar |
IfcReinforcingBar |
OST_StructConnections |
IfcMechanicalFastener |
OST_LightingFixtures |
IfcLightFixture |
OST_Furniture |
IfcFurnishingElement |
OST_Rooms |
IfcSpace |
The full table covers ~70 Revit categories across Architectural, Structural, MEP (HVAC, Plumbing, Electrical), and Site/Civil disciplines.
Skipped Categories
The following analytical/energy OST categories are automatically skipped (not exported to IFC):
OST_MEPLoadAreaSeparationLines, OST_EnergyAnalysisZones, OST_EnergyAnalysisSurface, OST_SolarShading, OST_MEPAnalyticalPipeSegments, OST_MEPAnalyticalDuctSegments, OST_MEPAnalyticalSpaces, OST_ElectricalConduitAnalyticalLines, OST_MEPLoadBoundaryLines, OST_FlowTerminalSeparationLines
Priority 2: Category name (display string)
The category name from the traversal context (the name of the parent Collection in the Speckle tree). Exact match first, then case-insensitive substring match.
Examples:
| Category Name | IFC Class |
|---|---|
Walls |
IfcWall |
Structural Columns |
IfcColumn |
Plumbing Fixtures |
IfcSanitaryTerminal |
Structural Rebar |
IfcReinforcingBar |
Structural Connections |
IfcMechanicalFastener |
Lighting Fixtures |
IfcLightFixture |
Priority 3: obj.category field
Same lookup as Priority 2, but using the object's own category attribute.
Fallback
If none of the above match, the object is classified as IfcBuildingElementProxy.
Geometry Handling
Direct Meshes (Path B1)
Objects with displayValue containing Mesh objects are converted directly:
- 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
- Build
IfcPolygonalFaceSetwithIfcCartesianPointList3D+IfcIndexedPolygonalFace - 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. The exporter supports two formats:
- Revit format:
definitionIdis a 64-char hex hash; geometry is found by walking the object tree - IFC format:
definitionIdstarts withDEFINITION:; geometry is indefinitionGeometrycollection
Performance optimisation: 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.
Curve Geometry (Path B3)
Objects whose displayValue contains Objects.Geometry.Line or Objects.Geometry.Arc items (and no meshes or instances) are exported as curve geometry:
- Lines →
IfcPolylinewith start and end points - Arcs →
IfcPolylineapproximated with 8 segments, sampled parametrically from the arc's plane origin, radius, and domain angles. Falls back to start/mid/end points if plane data is unavailable.
All curves are wrapped in an IfcGeometricCurveSet inside an IfcShapeRepresentation with RepresentationType="GeometricCurveSet".
Composite Objects (Path B2 — merged instances)
Objects like Windows and Doors may have multiple InstanceProxy items in their displayValue (e.g. frame, glass, sill). These are not separate IFC elements — all instance geometries are merged into a single IfcShapeRepresentation with combined IfcMappedItem entries, producing one IFC element per Speckle object.
Property Sets
The exporter writes property sets matching Revit's native IFC export structure:
| Property Set | Content |
|---|---|
Pset_<Entity>Common |
Standard IFC properties: Reference, IsExternal, LoadBearing, ThermalTransmittance |
Pset_SpaceCommon |
Room-specific: Reference, RoomNumber, RoomName, Category (Occupant) |
RVT_InstanceParameters |
All Revit instance parameters |
RVT_Identity |
Family, Type, ElementId, BuiltInCategory |
Quantities
Quantities follow the IFC standard naming convention: Qto_<EntityType>BaseQuantities and Qto_<MaterialName>BaseQuantities.
| Quantity Set | Content |
|---|---|
Qto_<EntityType>BaseQuantities |
Element-level quantities from Revit computed parameters (area, volume, length, width, height, perimeter) |
Qto_SpaceBaseQuantities |
Room quantities: NetFloorArea, NetVolume |
Qto_<MaterialName>BaseQuantities |
Per-material quantities: GrossArea, GrossVolume, Density |
Element Quantity Mapping
| IFC Quantity | Revit Parameter(s) |
|---|---|
| GrossArea | HOST_AREA_COMPUTED |
| GrossVolume | HOST_VOLUME_COMPUTED |
| Length | CURVE_ELEM_LENGTH, INSTANCE_LENGTH_PARAM |
| Height | WALL_USER_HEIGHT_PARAM, FAMILY_HEIGHT_PARAM, INSTANCE_HEAD_HEIGHT_PARAM |
| Width | INSTANCE_WIDTH_PARAM, FURNITURE_WIDTH, FLOOR_ATTR_THICKNESS_PARAM |
| Perimeter | HOST_PERIMETER_COMPUTED |
Supported Entity Qto Sets
Qto_WallBaseQuantities, Qto_SlabBaseQuantities, Qto_ColumnBaseQuantities, Qto_BeamBaseQuantities, Qto_DoorBaseQuantities, Qto_WindowBaseQuantities, Qto_RoofBaseQuantities, Qto_CoveringBaseQuantities, Qto_RailingBaseQuantities, Qto_StairBaseQuantities, Qto_RampBaseQuantities, Qto_MemberBaseQuantities, Qto_FootingBaseQuantities, Qto_CurtainWallBaseQuantities, Qto_BuildingElementProxyBaseQuantities, Qto_PipeFittingBaseQuantities, Qto_SanitaryTerminalBaseQuantities, Qto_ReinforcingElementBaseQuantities, Qto_MechanicalFastenerBaseQuantities
IfcSpace (Rooms)
Revit Rooms (OST_Rooms) are exported as IfcSpace elements with special handling:
- Spatial relationship: Aggregated under
IfcBuildingStoreyviaIfcRelAggregates(not contained) - Naming: Uses the Speckle object
nameattribute (not Family:Type which is "none:none" for rooms) - IfcSpace.Name: Set to
ROOM_NUMBER - IfcSpace.LongName: Set to
ROOM_NAME - Geometry: Converted from
displayValuemeshes like any other element
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 |
Environment Variables
For local testing via receiver.py, configure a .env file:
| Variable | Description |
|---|---|
SPECKLE_SERVER_URL |
Speckle server URL (default: https://app.speckle.systems) |
SPECKLE_TOKEN |
Personal access token for authentication |
SPECKLE_PROJECT_ID |
Project (stream) ID |
Testing
| Model Name | Revit Size | IFC Size | Conversion Time |
|---|---|---|---|
| Huge confidential model | 450 MB | 391 MB | 2h 30m |
| Snowdon Towers (Architecture) | 93.2 MB | 118 MB | 8m 37s |
| Speckle Tower | 51 MB | 45 MB | 3m |
| Rac Basic Sample Model | 18.8 MB | 12 MB | 12s |