Skip to main content

Fast native IFC parsing, data extraction, and geometric analytics

Project description

ifcfast — the agent-first IFC parser

PyPI Python versions License: MIT CI

An agent-first IFC parser. Built for AI agents, RPA, and analytics pipelines that need to ask questions of a model without loading a geometry kernel. Complements ifcopenshell — different tradeoffs, different jobs.

pip install ifcfast
import ifcfast

# Bundled demo — no external IFC needed.
m = ifcfast.open(ifcfast.example_path())
m.summary()      # JSON-friendly snapshot: schema, counts, tables, samples
m.schemas        # column-level dtype introspection of every table
m.preview("aggregates", n=3)

# Open your own.
m = ifcfast.open("model.ifc")
m.children(building_guid)          # all storeys
m.ancestors(wall_guid)             # storey → building → site → project
m.products_in(storey_guid)         # every product under this storey
ifcfast demo                       # showcases against bundled IFC
ifcfast index   FILE  --json       # tier-1 summary, machine-parseable
ifcfast schema  FILE  --json       # column-level schema introspection
ifcfast types   FILE  --json       # type-first extraction (TypeBank shape)

Or plug into any MCP-aware agent (Claude Desktop, Cursor, …) in one line:

pip install 'ifcfast[mcp]'
{ "mcpServers": { "ifcfast": { "command": "ifcfast-mcp" } } }

When ifcfast fits

  • Fast cold parse on the audited set: 22 MB ARK in 29 ms, 834 MB MEP in 905 ms (14.3M records). Roughly 20-30× the cold-parse cost of ifcopenshell.open on the same files, with byte-level parity on 234K products across Tekla, Archicad, Revit IFC4 / IFC2X3, and MagiCAD. Numbers and methodology in docs/history/audit/ — different parsers for different jobs; ifcopenshell still handles geometry kernels, authoring, and schema work that ifcfast doesn't touch.
  • Spatial-relationship graph built in. m.contained_in / .aggregates / .storey_building + seven traversal helpers (parent, children, ancestors, descendants, storey_of, building_of, products_in). Unifies aggregates and spatial containment — m.ancestors(wall_guid) reaches the project.
  • Self-describing. m.summary(), m.schemas, m.preview(table) answer "what am I looking at?" without triggering extracts. Every CLI subcommand has --json. Stable shape across releases.
  • Parquet cache. Second open of a 200 MB IFC returns in tens of milliseconds. Cache key invalidates automatically on any edit.
  • No geometry kernel on the hot path. Rust core via PyO3, mmap-based, peaks under 1 GB resident on an 800 MB MEP IFC. That's a deliberate scope cut — kernels live elsewhere when you need them.

See AGENTS.md for the full agent guide and a copy-paste system_prompt() you can drop into your LLM's context.

ifcfast was extracted on 2026-05-13 from the EdvardGK/ifc-workbench scratch repo. See docs/history/origin.md for the trail back and what was renamed.

What it gives you

layer format typical latency on 200 MB IFC
Products (GUID, type, name, storey, parent, tag) dict of parallel lists tier-1 cold: 0.5–2 s
Property sets long-format pandas.DataFrame 137 ms
Element quantities long-format pandas.DataFrame 90 ms
Materials (incl. layer sets) long-format pandas.DataFrame 27 ms
Classifications long-format pandas.DataFrame 17 ms
All data layers (shared scan) bundle 1.3 s
Triangle meshes (extrusion / mapped / face sets / BREP) OBJ / glTF / CSV 2.6 s
Placement-vs-mesh drift report pandas.DataFrame 322 ms
Parquet cache (all of the above) parquet 65 ms hot reload

End-to-end cold parse of a 200 MB IFC: under 5 s. Hot reload from cache: under 100 ms. Memory peak: under 1 GB resident (mmap-based).

Audited at 234,144 products across 5 authoring tools (Tekla, Archicad, Revit IFC4, Revit IFC2X3, MagiCAD, BSProLib) with byte-level parity against ifcopenshell for the fields ifcfast extracts. See docs/history/audit/.

Install

pip install ifcfast

Pre-built abi3 wheels are available for Python 3.10+ on:

  • Linux x86_64 and aarch64 (manylinux2014)
  • macOS x86_64 (10.12+) and arm64 (11.0+)
  • Windows x64

From source (contributors)

Needs Rust 1.95+ and Python 3.10+.

git clone https://github.com/EdvardGK/ifcfast
cd ifcfast
pip install maturin
maturin develop --release      # builds the Rust extension, ~30 s first time

For a release wheel: maturin build --release.

Quick start

import ifcfast

m = ifcfast.open("model.ifc")
print(len(m), "products,", len(m.storeys), "storeys")
print(m.authoring_app, "→", m.schema)

walls = list(m.filter(entity="IfcWall"))

# Long-format data layers (pandas DataFrames, loaded lazily).
m.psets             # 63k+ rows on a 200 MB Archicad file
m.quantities        # author-supplied Qto_*BaseQuantities
m.materials         # (guid, role, layer, name, thickness, category)
m.classifications   # NS 3451 / Uniformat / OmniClass references
m.drift             # placement-vs-mesh drift report

# Standard QTO query — external walls.
external_walls = m.psets[
    (m.psets.pset_name == "Pset_WallCommon")
    & (m.psets.prop_name == "IsExternal")
    & (m.psets.value == "True")
].guid.unique()

# Quality gate — placement bugs.
suspect = m.drift[m.drift.drift_severity == "error"]

The same model can be re-opened cheaply — the second ifcfast.open(...) returns from the parquet cache in tens of milliseconds.

CLI

ifcfast index   model.ifc           # tier-1 parse + counts
ifcfast extract model.ifc           # extract data layers (writes cache)
ifcfast drift   model.ifc --top 20  # placement / mesh drift report
ifcfast cache   model.ifc           # inspect cache for a file

The Rust binary ifcfast-mesh writes OBJ / glTF / CSV directly:

cargo build --release --bin ifcfast-mesh --no-default-features --features mesh
./target/release/ifcfast-mesh model.ifc model.glb

Cache

Parquet files live under ~/.cache/ifcfast/<cache_key>/, where cache_key is sha256(file_size + first 4 MB + last 4 MB) truncated. Any edit to the IFC invalidates the entry automatically.

Override with the IFCFAST_CACHE environment variable, e.g. IFCFAST_CACHE=/srv/cache ifcfast extract model.ifc.

Disk footprint on a 200 MB Archicad IFC: 2.4 MB total zstd-compressed.

Data schemas

All extractors return long-format (one row per fact, no nested fields). Easy to join, easy to filter, easy to flatten to Excel.

Missing values: string columns use pandas StringDtype with nan as the NULL sentinel (chosen for memory and pyarrow round-trip). Cells corresponding to a STEP $ field hold float('nan'), not Python None. Use .isna() to test, not == None or is None:

m.classifications[m.classifications.identification.isna()]   # correct
m.classifications[m.classifications.identification == None]  # always False
[r for r in m.classifications.itertuples() if r.identification is None]  # always False

If you're cross-checking against ifcopenshell (which returns None), normalise NaN→None on the comparison side.

psets

column type description
guid str IfcProduct.GlobalId
pset_name str e.g. Pset_WallCommon
prop_name str e.g. IsExternal
value str | None booleans normalised to True / False / UNKNOWN
value_type str | None IfcBoolean, IfcText, IfcReal, …

quantities

column type description
guid str IfcProduct.GlobalId
qto_name str e.g. Qto_WallBaseQuantities
quantity_name str e.g. NetVolume, GrossArea, Length
value str | None numeric value as string
quantity_type str Area / Length / Volume / Count / Weight / Time
unit_step_id int | None usually None (project default applies)

materials

column type description
guid str IfcProduct.GlobalId
role str direct / list / layer / unknown
layer_index int 0-based for layered materials, -1 otherwise
material_name str | None material label
layer_thickness_mm float | None only set for role="layer"
category str | None IFC4 only

classifications

column type description
guid str IfcProduct.GlobalId
system_name str | None NS 3451, Uniformat II, OmniClass
edition str | None e.g. 2022
identification str | None the actual code
name str | None human label
location str | None URI to spec (rarely populated)
source str | None publisher

drift

column type description
guid, entity, source str identification
triangle_count, surface_area, volume_abs int / float geometric stats
placement_x/y/z float what IfcLocalPlacement says
centroid_x/y/z float where the mesh AABB centre actually is
drift_distance float Euclidean distance, mm
max_extent float largest AABB dimension
drift_ratio float drift_distance / max_extent
drift_severity str ok / warn / error

Severity rule: ok when drift_ratio ≤ 2.0 or drift_distance < 10 mm; error when drift_ratio > 10.0 and drift_distance > 10 mm.

A 100 m wall placed at one end has ratio 0.5 (legitimate). A 50 mm sensor 100 m from its placement has ratio 2000 (clear authoring bug).

Spatial hierarchy & relationships

The tier-1 index exposes three long-format relationship tables and a small set of traversal helpers. No graph library required — the tables are plain pandas.DataFrames with string-guid columns and feed directly into NetworkX, PyArrow or a custom three.js scene if you want.

m = ifcfast.open("model.ifc")

m.contained_in     # IfcRelContainedInSpatialStructure (product → storey)
m.aggregates       # IfcRelAggregates (child → parent, with parent_kind)
m.storey_building  # storey → building (subset of aggregates)

# Traversal helpers — none of these raise on unknown guids.
m.parent(guid)            # unified parent (aggregate, else spatial storey)
m.children(guid)          # direct children: products + sub-decomposition
m.ancestors(guid)         # chain to root (storey → building → site → project)
m.descendants(guid)       # BFS over the unified-children tree
m.storey_of(guid)         # spatial container, or None
m.building_of(guid)       # building that hosts the storey, or None
m.products_in(parent)     # all products under parent (BFS, filtered)

parent_kind on m.aggregates is one of product / storey / building / site / project / space. The tables are persisted in the parquet cache, so hot reloads keep graph access at full speed.

Coverage today: IfcRelAggregates (decomposition), IfcRelContainedInSpatialStructure (spatial), IfcRelVoidsElement (opening ↔ host — m.voids DataFrame), and IfcRelDefinesByType (product ↔ type — populates type_guid / type_name / type_source on each product, plus m.type_objects as the catalogue). IfcRelConnectsElements and other relationship types are still on the next-tier list — file an issue with a sample if you need one. IfcSpace is surfaced as m.spaces (rooms / zones kept separate from "things you build").

Federated floor synthesis

Multi-discipline projects have the same physical floor named differently by ARK / RIB / RIV / RIE authors. ifcfast.federated_floors clusters by elevation across discipline models and applies a project-supplied YAML rule.

# examples/projects/lbk-building-c.yaml
prefix: "C - "
overrides:
  Plan U1: Hav
  C - U1:  Hav
idempotent_labels: [Hav]
apply_drop_leading_zero: true

The module is project-agnostic — project tables live in user config.

Architecture in two paragraphs

The Rust core (crates/core) does one byte-level pass over the IFC's DATA section using a string-aware STEP tokenizer (memchr-accelerated). That pass builds an EntityTable — a step_id → byte_range map of every entity in the file. Each PyO3 entry point (index_ifc, extract_psets, etc.) walks the table once, dispatching on entity type and extracting only the fields that layer needs.

The Python cache (ifcfast.cache) writes each extractor's output as zstd-compressed parquet, keyed by sha256(size + 4 MB head + 4 MB tail) so any IFC edit invalidates automatically. Hot reads are pure pandas / pyarrow — no Rust call needed. There is no ifcopenshell.open() anywhere in the data path; ifcopenshell is an optional dev dep used only for cross-checking parity in tests.

Reveal-all geometry stance

When the mesh pipeline meets a composite solid (IfcBooleanResult, IfcBooleanClippingResult, IfcCsgSolid) it does not perform the boolean. Both operands are emitted as their own visible mesh segments with compound tags like boolean_first_operand|extrusion (the host wall) and boolean_second_operand|halfspace_bounded (the door clip). You see the file as authored — the host volume AND the clip volume, not a curated "wall minus opening" summary. The glTF emitter writes each segment's (start, count, source) into per-node extras.segments so the viewer can colour, split, or filter by role.

Representation types we don't tessellate yet (e.g. IfcRevolvedAreaSolid, IfcSurfaceCurveSweptAreaSolid, IfcCsgPrimitive3D leaves) surface in mesh_stats.by_source as unhandled:IFCXXX entries so you can see exactly what the file contained that we couldn't reveal — never a silent drop.

What it doesn't do

  • Write or modify IFCs. Read-only by construction. (Round-trip editing is the next major milestone — see AGENTS.md "North star".)
  • True boolean / CSG composition. By design — we reveal BOTH operands instead.
  • Schema validation. Trusts the file's syntax. Use bsi-validator for conformance.
  • Curved-surface tessellation for IfcAdvancedBrep — face loops are triangulated as polygons (tagged advanced_brep_approx).
  • Property variants beyond IfcPropertySingleValueIfcPropertyEnumeratedValue, IfcPropertyListValue, IfcPropertyBoundedValue, IfcComplexProperty are skipped. Covers ~90% of psets seen on Revit / Archicad / Tekla / MagiCAD exports.

Layout

crates/core/         Rust extension (PyO3) — tokenizer, indexer, extractors, mesh
  src/
    lib.rs           PyO3 entry points
    lexer.rs         STEP tokenizer
    indexer.rs       tier-1 product / storey index
    entity_table.rs  step_id → byte range lookup
    extractors/      psets, quantities, materials, classifications
    mesh/            extrusion, mapped, face sets, BREP, glTF writer
    bin/             ifcfast-bench, ifcfast-mesh CLIs
python/ifcfast/      Public Python API
  __init__.py        ifcfast.open(), Model, header, classify
  header.py          STEP header reader (tier-0)
  model.py           Model class + native tier-1 driver
  cache.py           parquet cache for index + data layers
  classify.py        element-mode policy (count / measure / linear / skip)
  federated_floors.py multi-discipline floor synthesiser
  cli.py             ifcfast CLI
docs/history/        origin doc + audit issues from ifc-workbench
examples/projects/   project YAMLs for federated_floors
tests/               pytest suite

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ifcfast-0.4.12.tar.gz (206.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ifcfast-0.4.12-cp310-abi3-win_amd64.whl (525.0 kB view details)

Uploaded CPython 3.10+Windows x86-64

ifcfast-0.4.12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (580.1 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

ifcfast-0.4.12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (554.3 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

ifcfast-0.4.12-cp310-abi3-macosx_11_0_arm64.whl (539.2 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

ifcfast-0.4.12-cp310-abi3-macosx_10_12_x86_64.whl (560.6 kB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file ifcfast-0.4.12.tar.gz.

File metadata

  • Download URL: ifcfast-0.4.12.tar.gz
  • Upload date:
  • Size: 206.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ifcfast-0.4.12.tar.gz
Algorithm Hash digest
SHA256 083869884abc46b9a8ce75bd39f9d00bee566ecce44fd37d4062876b5c3ab314
MD5 03f5d301baa16eb6c4df48994dd735b8
BLAKE2b-256 ca52284a8c7fc897522e41f6a4d31c37e7c4614147d7d3170a97b7c16f1a169a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ifcfast-0.4.12.tar.gz:

Publisher: release.yml on EdvardGK/ifcfast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ifcfast-0.4.12-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: ifcfast-0.4.12-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 525.0 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ifcfast-0.4.12-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 0e8ed6c7bfbba1dc0d24e0ab970acee924ceac8f0ff8631eb82473bbc33ac5a6
MD5 f927ac8acb8fda68e3f8572721dcde39
BLAKE2b-256 79ae2f23de1538957cc387e0a4b467f4f831c38ba46a385152e4b5c2bcfe15f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for ifcfast-0.4.12-cp310-abi3-win_amd64.whl:

Publisher: release.yml on EdvardGK/ifcfast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ifcfast-0.4.12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ifcfast-0.4.12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7e000bb893cf0596bdd17fca5cb2c3a0a507eb4560aff017a34e99d838334bd8
MD5 e2fe346f342295f0bee8ab4c715a2648
BLAKE2b-256 9d6221e93600221a02696790ba2870970a5d237c9e606eb4230f0763ba089f5f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ifcfast-0.4.12-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on EdvardGK/ifcfast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ifcfast-0.4.12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for ifcfast-0.4.12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3380756c4dfc1b2f9342710e2360d449bd062f911b7bcb2eb727eff6b0534c78
MD5 cbf4b9ba2524ed83abd68b86317ab80c
BLAKE2b-256 01766e55bf4a972e5619e3ebeb8513429c61b40e4d6a4c2be4218881c93f4d3e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ifcfast-0.4.12-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on EdvardGK/ifcfast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ifcfast-0.4.12-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ifcfast-0.4.12-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1befaf6e01774798abe87d1c8bb3592f2d0bde7b18dce88ae5da2ab9bcf7d21b
MD5 17274fab27c8781232bea84a3c6a7a96
BLAKE2b-256 815dc97819ce5f66c2dea7766170a0dec6e605c4ded88553ece9a1ac494c186c

See more details on using hashes here.

Provenance

The following attestation bundles were made for ifcfast-0.4.12-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on EdvardGK/ifcfast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ifcfast-0.4.12-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for ifcfast-0.4.12-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 bf729539a2192f8a4a2a9815bb09948e41398abc9c9716b5894e91d93aa1ee06
MD5 9659329d7171850f54962a31aee199b6
BLAKE2b-256 6e1e2f9d24ecc6dcf095df193d62d2c4d2bb8b1a9df627774af100842cd6ac9e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ifcfast-0.4.12-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on EdvardGK/ifcfast

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page