# Ifc Impl Reading Parsing

> Use when reading or parsing an IFC STEP physical file: indexing instances, resolving #id references, dispatching on FILE_SCHEMA, reading positional attributes, or traversing the entity graph from an element to its relationships. Prevents resolving references in a single pass and failing on forward references, expecting INVERSE attributes to be stored in the file, miscounting positional attribute slots when $ or * appear, skipping the FILE_SCHEMA dispatch, and looking for a wall's properties as a direct attribute. Covers the two-pass parse, the instance line grammar, the $ and * tokens, building the inverse index, and navigating IsDefinedBy, IsTypedBy, ContainedInStructure, and HasAssociations. Keywords: IFC parser, parse IFC file, read .ifc file, STEP physical file, SPF, FILE_SCHEMA, two-pass parse, forward reference, #id reference, instance index, INVERSE attribute, IsDefinedBy, IsTypedBy, ContainedInStructure, HasAssociations, IfcRelDefinesByProperties, positional attributes, dollar token, asterisk token, p

- Skill: `impertio-studio/ifc-impl-reading-parsing` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add impertio-studio/ifc-impl-reading-parsing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/impertio-studio/ifc-impl-reading-parsing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: Impertio-Studio (https://skillmd.com/u/impertio-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/impertio-studio/ifc-impl-reading-parsing

---


# IFC Reading and Parsing

An IFC file in the `.ifc` STEP physical file (SPF) encoding is a flat list
of numbered entity instances. The semantics are a graph: instances point at
each other by `#id`, and relationships are first-class entities. Reading IFC
correctly means two things: indexing every instance before resolving any
reference, and computing INVERSE attributes that the file never stores.

This skill covers the two-pass parse, dispatching on `FILE_SCHEMA`, the
positional attribute model with its `$` and `*` tokens, building the inverse
index, and traversing from an element to its property sets, type, spatial
container, and associations.

Verified against ISO 10303-21 (the SPF encoding) and the IFC 4.3.2
specification (ISO 16739-1:2024), with IFC4 and IFC2x3 deltas noted inline.
Applies to IFC2x3, IFC4, IFC4.3.

## Quick Reference

### The parse contract

A conformant reader ALWAYS runs two passes:

1. Pass 1, index: scan every `#<id>=KEYWORD(...);` line and store the id,
   the entity keyword, and the raw attribute tokens. Resolve NOTHING.
2. Pass 2, resolve: replace each `#id` token with the instance from the
   index, and compute INVERSE attributes.

References ALWAYS may point forward (the referenced `#id` line appears later
in the file). A single-pass reader that resolves on first sight fails on
every forward reference. NEVER resolve references during pass 1.

### The instance line grammar

```
#42=IFCWALL('1Hkj5pQ9X8x9bq0d6FZ7yA',#5,'Wall-01',$,$,#60,#80,$,$);
^   ^       ^------------------------ ordered positional attributes -----^
id  keyword
```

- The instance name is `#` followed by a positive integer. It is locally
  valid within this file ONLY. Numbering is not contiguous and carries no
  semantic meaning. NEVER treat `#id` as a stable cross-file identifier.
- The entity keyword is upper-case (`IFCWALL`, `IFCPROJECT`).
- Attributes are an ordered, unnamed, positional sequence in EXPRESS
  declared order, including every inherited explicit attribute.
- Only explicit attributes are serialized. INVERSE, DERIVED, and re-declared
  attributes are omitted from the value list.

### The $ and * tokens

| Token | Meaning | Reader action |
|-------|---------|---------------|
| `$`   | An unset OPTIONAL explicit attribute | Treat the slot as present with value null. It still occupies one position. |
| `*`   | A slot for an inherited explicit attribute that this subtype re-declared as DERIVED | Skip the value; it is computed by the schema. It still occupies one position. |

ALWAYS count `$` and `*` as occupied positional slots. NEVER drop them when
mapping tokens to attribute names.

### FILE_SCHEMA dispatch

The `HEADER` section ends with `FILE_SCHEMA(('<name>'));`. A reader ALWAYS
dispatches its entity definitions on this value before parsing `DATA`.

| FILE_SCHEMA value | Schema | Notes |
|-------------------|--------|-------|
| `IFC2X3` | IFC2x3 TC1 | `IfcOwnerHistory` mandatory; `IsDefinedBy` mixes type and properties; no `IsTypedBy`. |
| `IFC4` | IFC4 ADD2 TC1 | `IfcOwnerHistory` optional; `IsTypedBy` split from `IsDefinedBy`. |
| `IFC4X3`, `IFC4X3_ADD2` | IFC4.3 ADD2 | `IfcBuildingElement` renamed to `IfcBuiltElement`; infrastructure entities added. |

NEVER assume IFC4 entity names or attribute order on an `IFC2X3` file.

### INVERSE attributes are computed, not stored

INVERSE attributes appear in the schema but NEVER in the file. The reader
computes them in pass 2 by scanning relationship instances and indexing
their forward references back to the referenced objects.

| From | INVERSE attribute | Via relationship | Reaches |
|------|-------------------|------------------|---------|
| `IfcObject` | `IsDefinedBy` | `IfcRelDefinesByProperties` | property and quantity sets |
| `IfcObject` | `IsTypedBy` (IFC4+) | `IfcRelDefinesByType` | the type object |
| `IfcElement` | `ContainedInStructure` | `IfcRelContainedInSpatialStructure` | the spatial container |
| `IfcObjectDefinition` | `HasAssociations` | `IfcRelAssociates*` | material, classification |
| `IfcObjectDefinition` | `IsDecomposedBy` | `IfcRelAggregates` | the parts (this is the whole) |
| `IfcObjectDefinition` | `Decomposes` | `IfcRelAggregates` | the whole (this is a part) |
| `IfcElement` | `HasOpenings` | `IfcRelVoidsElement` | the opening elements |

Full INVERSE catalogue with `FOR` clauses is in `references/methods.md`.

## Decision Trees

### How do I resolve a #id reference?

```
Encountered a #id token while reading an attribute.
|
+- Still in pass 1 (indexing)?
|    -> Store the raw token. Do NOT look it up. The target may not be
|       indexed yet (forward reference).
|
+- In pass 2 (resolving)?
     -> Look up #id in the instance index built by pass 1.
        |
        +- Found    -> substitute the resolved instance.
        +- Not found-> the file is malformed. ALWAYS fail loudly with the
                       missing id. NEVER silently substitute null.
```

### Is this attribute slot $, *, or a value?

```
Reading positional token N of an entity instance.
|
+- Token is $   -> OPTIONAL attribute is unset. Slot value = null.
|                  Advance to slot N+1.
|
+- Token is *   -> Attribute was re-declared DERIVED by this subtype.
|                  Slot value = computed by schema, not in file.
|                  Advance to slot N+1.
|
+- Token is #id        -> a reference. Resolve in pass 2.
+- Token is (..)       -> an aggregate (LIST/SET/BAG/ARRAY). Parse members.
+- Token is .NAME.     -> an enumeration, boolean, or logical value.
+- Token is 'text'     -> a string. Un-escape control directives.
+- Token is TYPE(val)  -> a typed (select/measure) value. Unwrap.
+- Token is a number   -> an integer or real literal.
```

ALWAYS map slot N to the Nth explicit attribute in EXPRESS declared order,
counting inherited attributes first. See `references/methods.md` for the
`IfcRoot` to concrete-entity attribute order.

### Which inverse do I follow to reach properties?

```
I have an IfcElement and want its property sets.
|
+- FILE_SCHEMA is IFC4 or IFC4X3?
|    -> element.IsDefinedBy holds ONLY IfcRelDefinesByProperties.
|       Follow each to RelatingPropertyDefinition.
|       For the type, follow element.IsTypedBy instead.
|
+- FILE_SCHEMA is IFC2X3?
     -> element.IsDefinedBy mixes IfcRelDefinesByProperties AND
        IfcRelDefinesByType. Filter by relationship keyword:
        keep IfcRelDefinesByProperties for properties,
        keep IfcRelDefinesByType for the type.
        There is NO IsTypedBy inverse in IFC2x3.
```

This split is the single most version-sensitive part of reading IFC. See
the pattern below and `references/anti-patterns.md`.

### Which schema version am I parsing?

```
Read FILE_SCHEMA from the HEADER.
|
+- 'IFC2X3'              -> IFC2x3 entity set. IfcBuildingElement abstract.
+- 'IFC4'               -> IFC4 entity set. IfcBuildingElement abstract.
+- 'IFC4X3' / 'IFC4X3_ADD2' -> IFC4.3 entity set. IfcBuiltElement (renamed,
|                              non-abstract). Infrastructure entities exist.
+- anything else        -> unknown schema. ALWAYS fail; NEVER guess.
```

## Patterns

Every snippet uses STEP physical file syntax. Full verified signatures are
in `references/methods.md`; complete runnable instances are in
`references/examples.md`.

### Pattern: two-pass index then resolve

Pass 1 builds the index. Pass 2 resolves references and computes inverses.

```python
# Pass 1: index every instance, resolve nothing.
index = {}                                  # id -> (keyword, raw_tokens)
for line in data_section_lines:
    inst_id, keyword, raw_tokens = split_instance_line(line)
    index[inst_id] = (keyword, raw_tokens)

# Pass 2: resolve #id tokens against the completed index.
for inst_id, (keyword, raw_tokens) in index.items():
    resolved = [resolve(tok, index) for tok in raw_tokens]
```

`resolve` substitutes a `#id` token with `index[id]`. Because pass 1
finished first, a forward reference (a `#id` whose line came later) is
already in the index. ALWAYS complete pass 1 over the whole `DATA` section
before starting pass 2.

### Pattern: dispatch on FILE_SCHEMA

```python
schema = read_file_schema(header)            # 'IFC2X3' | 'IFC4' | 'IFC4X3'
if schema == 'IFC2X3':
    entity_defs = IFC2X3_DEFS
elif schema == 'IFC4':
    entity_defs = IFC4_DEFS
elif schema in ('IFC4X3', 'IFC4X3_ADD2'):
    entity_defs = IFC4X3_DEFS
else:
    raise ValueError(f'unsupported FILE_SCHEMA: {schema}')
```

The schema set determines which entities exist and their attribute order.
NEVER parse `DATA` before this dispatch. NEVER hardcode one schema.

### Pattern: read explicit attributes positionally

For any rooted entity, positions 1 to 4 are ALWAYS the `IfcRoot`
attributes: `GlobalId`, `OwnerHistory`, `Name`, `Description`. The concrete
entity adds its own explicit attributes after every inherited one.

```step
#42=IFCWALL(
  '1Hkj5pQ9X8x9bq0d6FZ7yA',  /* 1 GlobalId       (IfcRoot)    */
  #5,                        /* 2 OwnerHistory   (IfcRoot)    */
  'Wall-01',                 /* 3 Name           (IfcRoot)    */
  $,                         /* 4 Description    (IfcRoot)    */
  $,                         /* 5 ObjectType     (IfcObject)  */
  #60,                       /* 6 ObjectPlacement(IfcProduct) */
  #80,                       /* 7 Representation (IfcProduct) */
  $,                         /* 8 Tag            (IfcElement) */
  $                          /* 9 PredefinedType (IfcWall)    */
);
```

Map each token to its attribute by position, NEVER by guessing. A `$` or `*`
still consumes one position. See `references/methods.md` for the declared
order of every entity in this skill.

### Pattern: build the inverse index

INVERSE attributes are not in the file. Build them in pass 2 by scanning
relationship instances and recording the back-link.

```python
inverse = defaultdict(lambda: defaultdict(list))   # obj_id -> name -> [rels]

for rel_id, (keyword, _) in index.items():
    if keyword == 'IFCRELDEFINESBYPROPERTIES':
        rel = resolved[rel_id]
        for obj_id in rel.RelatedObjects:           # the SET of objects
            inverse[obj_id]['IsDefinedBy'].append(rel_id)
    elif keyword == 'IFCRELCONTAINEDINSPATIALSTRUCTURE':
        rel = resolved[rel_id]
        for obj_id in rel.RelatedElements:
            inverse[obj_id]['ContainedInStructure'].append(rel_id)
    # ... one branch per relationship keyword
```

ALWAYS index relationships by the `Related...` side to populate the inverse
on the related objects. The `Relating...` side is the single anchor.

### Pattern: navigate an element to its property sets

```
element
  -> element.IsDefinedBy            (INVERSE, computed in pass 2)
  -> keep IfcRelDefinesByProperties instances
  -> rel.RelatingPropertyDefinition (an IfcPropertySet or IfcElementQuantity)
  -> read HasProperties / Quantities
```

On IFC4 and IFC4.3, `IsDefinedBy` holds ONLY `IfcRelDefinesByProperties`, so
no filter is needed. On IFC2x3, `IsDefinedBy` ALSO holds
`IfcRelDefinesByType`, so the reader MUST filter by relationship keyword.
NEVER expect a direct `PropertySets` attribute on the element: it does not
exist. See `ifc-impl-property-extraction`.

### Pattern: navigate an element to its type

```
IFC4 / IFC4.3:  element -> element.IsTypedBy   -> IfcRelDefinesByType
                        -> rel.RelatingType    (the IfcWallType, etc.)
IFC2x3:         element -> element.IsDefinedBy -> filter IfcRelDefinesByType
                        -> rel.RelatingType
```

`IsTypedBy` has cardinality `SET [0:1]`: an occurrence has at most one type.
`IsTypedBy` does NOT exist in IFC2x3.

### Pattern: navigate an element to its spatial container

```
element -> element.ContainedInStructure   (INVERSE, SET [0:1])
        -> IfcRelContainedInSpatialStructure
        -> rel.RelatingStructure           (the IfcBuildingStorey, IfcSpace...)
```

`ContainedInStructure` has cardinality `SET [0:1]`: a physical element sits
in at most ONE spatial container. An element with an empty set is an orphan.
See `ifc-impl-spatial-decomposition`.

### Pattern: navigate an element to its associations

```
element -> element.HasAssociations         (INVERSE, on IfcObjectDefinition)
        -> IfcRelAssociatesMaterial         -> rel.RelatingMaterial
        -> IfcRelAssociatesClassification   -> rel.RelatingClassification
```

`HasAssociations` is defined on `IfcObjectDefinition`, so both occurrences
AND type objects expose it. Filter by relationship keyword to separate
material from classification.

## Anti-Patterns

Full detail with the failure mechanism is in `references/anti-patterns.md`.
The ones that matter most:

- Resolving `#id` references in a single pass. Every forward reference
  fails. ALWAYS index the whole `DATA` section first.
- Expecting INVERSE attributes (`IsDefinedBy`, `ContainedInStructure`) to be
  values in the file. They are NEVER serialized; the reader computes them.
- Dropping `$` or `*` when mapping tokens to attribute names. Both occupy a
  positional slot; dropping them shifts every later attribute by one.
- Skipping the `FILE_SCHEMA` dispatch and hardcoding one schema. An
  `IFC2X3` file parsed with IFC4 definitions misreads attributes.
- Looking for the type link in `IsDefinedBy` on an IFC4 file. The type moved
  to `IsTypedBy`; only IFC2x3 mixes them.
- Treating a `#id` as a stable identifier across files. Use `GlobalId` for
  cross-file identity; `#id` is local and meaningless outside one file.

## Reference Links

### This skill

- `references/methods.md`: the instance grammar, header entities, positional
  attribute order for every entity in this skill, and the full INVERSE
  catalogue with `FOR` clauses.
- `references/examples.md`: a complete verified `.ifc` file and worked
  resolution walkthroughs.
- `references/anti-patterns.md`: parsing failure modes and their root cause.

### Related skills

- `ifc-syntax-step-physical-file`: the SPF grammar in full, including string
  escaping and the `HEADER` section.
- `ifc-impl-property-extraction`: the type-versus-occurrence property
  override algorithm once the relationships are traversed.
- `ifc-impl-geometry-extraction`: reading `Representation` and
  `ObjectPlacement` after the graph is resolved.
- `ifc-core-relationships`: the objectified relationship pattern and the
  `Relating` versus `Related` direction convention.

### Official sources

- ISO 10303-21 overview: https://en.wikipedia.org/wiki/ISO_10303-21
- ISO 10303-21 edition 3: https://www.steptools.com/stds/step/IS_final_p21e3.html
- IFC header data guide: https://standards.buildingsmart.org/documents/Implementation/ImplementationGuide_IFCHeaderData_Version_1.0.2.pdf
- IfcElement: https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcElement.htm
- IfcObject: https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcObject.htm
- IfcRoot: https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoot.htm

