ifclite-geom
Native ifc-lite geometry tessellation for Python. It turns an IFC file into per-entity triangle meshes with no Node, no WASM, and no subprocess: the Rust geometry kernel runs directly inside the Python process.
Meshes come back welded, IFC Z-up, in absolute world metres, keyed by IFC STEP id (occurrences only). This is the analysis-ready export, distinct from the render-oriented GLB the viewer uses.
Install
pip install ifclite-geom
Prebuilt wheels ship for CPython 3.9+ on Linux (x86_64, aarch64), macOS (Apple silicon and Intel), and Windows (x64). No Rust toolchain needed.
Quick start
The module is ifclite_geom and exposes three functions, all taking the raw IFC
file as bytes. geometry_data_buffers and geometry_data_json return the
same geometry and differ only in output format; entity_data reads attributes
and property sets instead, without tessellating.
import ifclite_geom
import numpy as np
with open("model.ifc", "rb") as f:
ifc_bytes = f.read()
data = ifclite_geom.geometry_data_buffers(ifc_bytes)
print(data["element_count"], "elements")
print("up axis:", data["up_axis"], "| units:", data["units"])
print("rtc offset:", data["rtc_offset"])
for step_id, el in data["elements"].items():
verts = np.frombuffer(el["vertices"], dtype=np.float64).reshape(-1, 3)
faces = np.frombuffer(el["faces"], dtype=np.uint32 ).reshape(-1, 3)
print(step_id, el["ifc_type"], el["global_id"], verts.shape, faces.shape)
Prefer no numpy dependency? Use the JSON variant, which returns the same data as arrays of numbers:
import ifclite_geom, json
doc = json.loads(ifclite_geom.geometry_data_json(ifc_bytes))
first = next(iter(doc["elements"].values()))
print(first["ifc_type"], first["vertices"][0]) # [x, y, z] in metres
API
geometry_data_buffers(ifc_bytes: bytes, quality: str | None = None) -> dict
The fast path. Vertices and faces come back as raw little-endian byte buffers so
you can hand them straight to numpy.frombuffer with zero parsing.
{
"up_axis": "Z", # always Z (IFC native)
"units": "m", # always metres
"rtc_offset": [x, y, z], # geo-reference offset already folded into vertices
"element_count": 1234,
"elements": {
<step_id:int>: {
"ifc_type": "IfcWall",
"global_id": "3vB2...", # may be None
"name": "Basic Wall:...", # may be None
"color": [r, g, b, a], # 0..1
"vertices": <bytes>, # f64 little-endian, xyz triplets
"faces": <bytes>, # u32 little-endian, triangle indices
},
...
}
}
Decode the buffers with:
verts = np.frombuffer(el["vertices"], dtype=np.float64).reshape(-1, 3) # (V, 3)
faces = np.frombuffer(el["faces"], dtype=np.uint32 ).reshape(-1, 3) # (F, 3)
geometry_data_json(ifc_bytes: bytes, quality: str | None = None) -> str
The same geometry as a readable ifc-lite-geometry-data JSON document (a
string; call json.loads on it). Vertices are [x, y, z] arrays and faces are
[a, b, c] index arrays, so no numpy is required. Each element also carries
global_id and name when the source entity has them.
Tessellation quality
Both geometry functions take an optional quality label:
| label | density |
|---|---|
"lowest" |
quarter |
"low" |
half |
"medium" |
engine default, used when quality is omitted |
"high" |
double |
"highest" |
quadruple |
It scales the segment count on every curved primitive: swept-disk tubes,
cylinders, revolutions, arcs, circular profiles. On curve-heavy elements the
effect is large. A single IfcReinforcingBar authored as an IfcSweptDiskSolid
over a composite arc tessellates to 1056 triangles at "medium" and 96 at
"lowest".
data = ifclite_geom.geometry_data_buffers(ifc_bytes, "lowest")
An unrecognised label raises ValueError rather than silently falling back, so
a typo cannot cost you a 10x triangle budget without saying so. This is the same
knob the browser build exposes as setTessellationQuality and the server as
?tessellation_quality=; the level is model-wide, not per IFC type.
entity_data(ifc_bytes, placements=False, type_properties=True, attributes=True) -> dict
Attributes, property sets and quantity sets. No tessellation runs, so this is cheap compared with the geometry functions.
{
"length_unit_scale": 0.001, # file length unit -> metres
"plane_angle_to_radians": 0.0174,
"project_id": 42, # may be None
"entity_count": 1234,
"entities": {
<step_id:int>: {
"ifc_type": "IfcWall",
"global_id": "3vB2...", # may be None
"name": "WALL 1", # may be None
"description": None,
"object_type": None,
"has_geometry": True,
"placement": None, # see below
"property_sets": [
{"name": "Pset_WallCommon",
"properties": [{"name": "IsExternal", "value": "True",
"value_type": "IFCBOOLEAN"}]},
],
"quantity_sets": [
{"name": "Qto_WallBaseQuantities",
"quantities": [{"name": "Length", "value": 3000.0, "kind": "Length"}]},
],
"attributes": [ # schema-declared entity attributes
{"name": "PredefinedType", "value": "SOLIDWALL", "value_type": "IFCENUM"},
],
},
...
}
}
entities is keyed by IFC STEP id in file order, the same key
geometry_data_buffers uses, so the two join directly. The join is one-way
total: every meshed element has a row, but not every row has an element, so
drive the loop from elements (or use .get()) rather than the other way
round. Besides products with no geometry, an orphan IfcTypeProduct carries
has_geometry: True and still never appears in elements, because the
geometry functions emit occurrences only.
geom = ifclite_geom.geometry_data_buffers(ifc_bytes)
ents = ifclite_geom.entity_data(ifc_bytes)
for step_id, el in geom["elements"].items():
row = ents["entities"].get(step_id)
if row:
print(el["ifc_type"], row["name"], row["property_sets"])
Pass placements=True to also resolve each product's ObjectPlacement into a
list of 16 floats: a column-major 4x4, translation in metres at indices
12/13/14. It is off by default because it costs an extra decode per product.
The matrix is in the same absolute IFC world frame as
geometry_data_buffers vertices, so the two line up directly. Do not fold
rtc_offset into either: the geometry export already adds it back into every
vertex, and the placement is never RTC-rebased. On a georeferenced model both
are large absolute coordinates, and a product's placement origin lands inside
its own mesh bounds.
Units, and two current limits
-
Property and quantity values are in the file's own units, unlike geometry, which is always metres. A millimetre model reports a wall length of
3000. Property values are always strings; quantity values are floats.Converting is per dimension, not one blanket factor:
quantity kind to SI Lengthvalue * length_unit_scaleAreavalue * length_unit_scale ** 2Volumevalue * length_unit_scale ** 3Countunchanged (dimensionless) angles (properties) value * plane_angle_to_radiansOnly the length and plane-angle scales are resolved, so a model that declares an area or volume unit inconsistent with its length unit cannot be reconciled from what is returned here.
-
Only
IfcPropertySingleValueproperties are decoded. Enumerated, list, bounded, table and reference properties are skipped; the pset still appears, with those entries missing.
Entity attributes
Note the two senses of "type" on this page. The section below concerns an
IfcTypeObject, the shared definition an occurrence inherits from. This one
concerns the IFC entity class (IfcWall, IfcReinforcingBar) and the
attributes its schema declares. They are unrelated.
attributes is on by default. These are not property sets and no amount of
pset work surfaces them, because they are declared on the entity itself:
row = ents["entities"][step_id]
{a["name"]: a["value"] for a in row["attributes"]}
# A bar with every attribute set:
# {'Tag': 'TAG-1', 'SteelGrade': 'B500B', 'NominalDiameter': '29',
# 'CrossSectionArea': '660', 'BarLength': '500',
# 'PredefinedType': 'NOTDEFINED', 'BarSurface': 'PLAIN'}
#
# A bar leaving most of them `$`, which is the common case:
# {'NominalDiameter': '29', 'CrossSectionArea': '0',
# 'PredefinedType': 'NOTDEFINED'}
Only what the file sets is returned. An attribute left $ is omitted
rather than reported empty, so the list is usually shorter than the class
declares, and its length varies between two entities of the same class.
Every IFC entity class has its own schema-declared attributes: IfcDoor yields
OverallHeight / OverallWidth, and so on, named and ordered as the schema
declares them. Entries share the {name, value, value_type} shape of a
property, so one code path reads both.
Fields the row already carries (global_id, name, description,
object_type) are not repeated, and reference-valued attributes are omitted
rather than rendered as a dangling #123. Pass attributes=False to skip.
Type-inherited properties
type_properties is on by default. A type attaches its sets through
IfcTypeObject.HasPropertySets and gets no row of its own unless it carries
orphan geometry, so without this the properties authoring tools put on types
are unreachable. Each occurrence therefore also carries what it inherits
through IfcRelDefinesByType, merged per property:
- A type set whose name the occurrence does not use is added whole.
- A type set sharing a name contributes only the properties the occurrence does not already define. On a collision the occurrence wins, and the type-only properties beside it still survive. Replacing the whole set instead would hide them, which is the bug this rule exists to prevent.
quantity_sets inherit on exactly the same terms. A type attaches
IfcElementQuantity definitions through the same HasPropertySets attribute,
so they arrive by the same route and merge by the same rule: a type quantity
set the occurrence does not name is added whole, and a same-named one
contributes only the quantities the occurrence does not already define, so the
occurrence wins a collision. type_properties governs both lists; there is no
separate switch.
# Own sets only, as in 4.3.0. Affects property_sets AND quantity_sets.
ents = ifclite_geom.entity_data(ifc_bytes, type_properties=False)
This mirrors what the browser has done since the same fix landed there, so a property visible in the viewer is now visible here.
Notes
- One mesh per element. Per-material submeshes of an element are merged into a single indexed triangle soup, keyed by its IFC STEP id.
- Coordinates are absolute world metres. The per-element local frame and the
model RTC offset are folded back into every vertex. For geo-referenced models
rtc_offsetis non-zero; subtract it if you want f32-friendly local coordinates. - Welded and indexed. Coincident corners are merged (1 micron grid), so closed-mesh consumers (volume, watertightness checks) work directly.
- Occurrences only. Type-product / RepresentationMap geometry is not emitted, matching what occurrence-based tessellators produce.
- Errors surface as
RuntimeError(pipeline failure) orValueError(an unrecognisedqualitylabel, or JSON serialization failure).
Examples
Runnable scripts live in examples/:
quickstart_numpy.py- load a file and inspect meshes via numpy.dump_json.py- write the JSON document to disk.export_obj.py- write every element to a single Wavefront.obj(numpy only, no extra deps).schedule_csv.py- joinentity_dataagainstgeometry_data_buffersand write a quantity schedule to CSV (stdlib only).
License
MPL-2.0. Part of the ifc-lite project.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ifclite_geom-4.4.0-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: ifclite_geom-4.4.0-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 2.2 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fb865f49a9b74ae62ae0cba22f1602b5a760d7303782b5f220d4340b05a35d3
|
|
| MD5 |
5a5ea4cf952fb3d799a5c5acbd3b0a9e
|
|
| BLAKE2b-256 |
08b3436aaa76d6c1d8feb015a5d63df8d265b32de3d7a167f1c59acd95a15200
|
Provenance
The following attestation bundles were made for ifclite_geom-4.4.0-cp39-abi3-win_amd64.whl:
Publisher:
python-wheels.yml on LTplus-AG/ifc-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifclite_geom-4.4.0-cp39-abi3-win_amd64.whl -
Subject digest:
7fb865f49a9b74ae62ae0cba22f1602b5a760d7303782b5f220d4340b05a35d3 - Sigstore transparency entry: 2373433684
- Sigstore integration time:
-
Permalink:
LTplus-AG/ifc-lite@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Branch / Tag:
refs/tags/ifclite-geom-v4.4.0 - Owner: https://github.com/LTplus-AG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-wheels.yml@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.5 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68e93b24e14d3458ccd305bf3e78452804d8c410bfba3709e96bd804405dacbd
|
|
| MD5 |
e5e49233b7423ff1912e15ca9bd07c0f
|
|
| BLAKE2b-256 |
be3e473c0131860e1273002d343f9ccb4b104ec2fdfe6210c0bab1cbe53b00be
|
Provenance
The following attestation bundles were made for ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
python-wheels.yml on LTplus-AG/ifc-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
68e93b24e14d3458ccd305bf3e78452804d8c410bfba3709e96bd804405dacbd - Sigstore transparency entry: 2373433315
- Sigstore integration time:
-
Permalink:
LTplus-AG/ifc-lite@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Branch / Tag:
refs/tags/ifclite-geom-v4.4.0 - Owner: https://github.com/LTplus-AG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-wheels.yml@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 2.4 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac5d6e92470a9e58f75391128e86a5dea533b05bd61cc6a168e821b5c3a7f376
|
|
| MD5 |
b56ed5599d3d13a22f6d19ae8a8e93cd
|
|
| BLAKE2b-256 |
2dbadb5c37e55cca3b5b5da70873f353244c14b3cee984068c4c654ec5cfba54
|
Provenance
The following attestation bundles were made for ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
python-wheels.yml on LTplus-AG/ifc-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifclite_geom-4.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
ac5d6e92470a9e58f75391128e86a5dea533b05bd61cc6a168e821b5c3a7f376 - Sigstore transparency entry: 2373433394
- Sigstore integration time:
-
Permalink:
LTplus-AG/ifc-lite@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Branch / Tag:
refs/tags/ifclite-geom-v4.4.0 - Owner: https://github.com/LTplus-AG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-wheels.yml@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ifclite_geom-4.4.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: ifclite_geom-4.4.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 2.2 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56e3168912db21bcd3768d9a12d858fa4b77ab8f1c970eddde26009888bfd29b
|
|
| MD5 |
daa4dc68efb2907a806ea2c0d4792b84
|
|
| BLAKE2b-256 |
32fd4b48c58fbce3221783d6271447d2e5ee4ef20adb20fc6d5480aa9ba3f3d3
|
Provenance
The following attestation bundles were made for ifclite_geom-4.4.0-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
python-wheels.yml on LTplus-AG/ifc-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifclite_geom-4.4.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
56e3168912db21bcd3768d9a12d858fa4b77ab8f1c970eddde26009888bfd29b - Sigstore transparency entry: 2373433584
- Sigstore integration time:
-
Permalink:
LTplus-AG/ifc-lite@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Branch / Tag:
refs/tags/ifclite-geom-v4.4.0 - Owner: https://github.com/LTplus-AG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-wheels.yml@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ifclite_geom-4.4.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: ifclite_geom-4.4.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 2.3 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbfc07ac861c8278b6e4fb9457e6524b7591ca4a97a2da0ce62c393410246f87
|
|
| MD5 |
6564e9d67416273779380b8335dd4909
|
|
| BLAKE2b-256 |
93b93021367b817b99fb21de93edea0ba437b74962363cab7d636b2e4e9e8f6c
|
Provenance
The following attestation bundles were made for ifclite_geom-4.4.0-cp39-abi3-macosx_10_12_x86_64.whl:
Publisher:
python-wheels.yml on LTplus-AG/ifc-lite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ifclite_geom-4.4.0-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
bbfc07ac861c8278b6e4fb9457e6524b7591ca4a97a2da0ce62c393410246f87 - Sigstore transparency entry: 2373433503
- Sigstore integration time:
-
Permalink:
LTplus-AG/ifc-lite@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Branch / Tag:
refs/tags/ifclite-geom-v4.4.0 - Owner: https://github.com/LTplus-AG
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-wheels.yml@9c17d7db44ff603f02ce7952d67ded05daabd120 -
Trigger Event:
push
-
Statement type: