IFC Property Extraction
Read the effective property and quantity values of an IFC element. The hard part
is not finding one IfcPropertySet ; it is resolving the type vs occurrence
layering correctly and handling the IFC2x3 vs IFC4 inverse-attribute split.
This skill is read-side. For the write-side (attaching property sets) see
ifc-impl-data-enrichment. For the entity definitions see ifc-syntax-property-sets
and ifc-syntax-quantities.
Quick Reference
| Goal | Path | Versions |
|---|---|---|
| Occurrence property sets | occurrence.IsDefinedBy to IfcRelDefinesByProperties.RelatingPropertyDefinition |
all |
| Type of an occurrence (IFC4+) | occurrence.IsTypedBy[0].RelatingType |
IFC4, IFC4.3 |
| Type of an occurrence (IFC2x3) | filter occurrence.IsDefinedBy for IfcRelDefinesByType, then .RelatingType |
IFC2x3 |
| Type property sets | typeObject.HasPropertySets (direct explicit attribute, no relationship hop) |
all |
| Properties in a set | IfcPropertySet.HasProperties to IfcPropertySingleValue.NominalValue |
all |
| Quantities of an element | IfcElementQuantity.Quantities to IfcQuantityArea.AreaValue etc. |
all |
| Standard set | Name begins with Pset_ (properties) or Qto_ (quantities, IFC4+) |
all |
Core facts
- ALWAYS read BOTH the type-level sets AND the occurrence-level sets. Type sets are defaults ; occurrence sets override them. Reading one side only returns wrong values.
- INVERSE attributes (
IsDefinedBy,IsTypedBy,Types,ObjectTypeOf) are NOT stored in the STEP file. ALWAYS build them by scanning everyIfcRelDefinesByPropertiesandIfcRelDefinesByTypeinstance and grouping byRelatedObjects. Seeifc-impl-reading-parsing. IfcPropertySetandIfcElementQuantityshare the same attachment relationship (IfcRelDefinesByProperties) and the sameHasPropertySetsslot. ALWAYS branch on the entity type after retrieval.- A property set Name is the lookup key for override resolution. NEVER override by
list position or
#id.
Decision Trees
Which traversal reaches the type
Target IFC version of the file (read FILE_SCHEMA in the header) ?
├─ IFC4 or IFC4.3
│ └─ occurrence.IsTypedBy : SET [0:1] OF IfcRelDefinesByType
│ └─ at most one ; .RelatingType is the IfcTypeObject (or NULL = untyped)
└─ IFC2x3
└─ occurrence.IsDefinedBy : SET OF IfcRelDefines (mixed)
└─ filter members that are IfcRelDefinesByType ; .RelatingType is the type
(IFC2x3 has NO IsTypedBy inverse ; the type relationship lives in IsDefinedBy)
Which inverse holds the occurrence property sets
Target IFC version ?
├─ IFC4 or IFC4.3
│ └─ occurrence.IsDefinedBy : SET [0:?] OF IfcRelDefinesByProperties (properties only)
└─ IFC2x3
└─ occurrence.IsDefinedBy : SET OF IfcRelDefines
└─ keep ONLY the members that are IfcRelDefinesByProperties
(drop the IfcRelDefinesByType members ; those are the type link)
Is a retrieved set a property set or a quantity set
entity is a subtype of IfcPropertySetDefinition. Which one ?
├─ IfcPropertySet -> read .HasProperties (IfcProperty subtypes)
├─ IfcElementQuantity -> read .Quantities (IfcPhysicalQuantity subtypes)
└─ IfcPreDefinedPropertySet (e.g. IfcDoorPanelProperties, IFC2x3-style)
-> read its fixed named attributes, NOT HasProperties
Does the value carry a unit already
Reading IfcPropertySingleValue.NominalValue ?
├─ property.Unit is set -> use property.Unit (explicit local override)
└─ property.Unit is $ (unset) -> the measure type of NominalValue implies the
unit ; resolve it from IfcProject
UnitsInContext (IfcUnitAssignment). See
ifc-syntax-units.
Patterns
Pattern 1 : resolve the effective property sets of an element
The override rule (verified against the IFC4 ADD2 property-set template
documentation) : when a property set with the same Name exists on both the type
and the occurrence, and a property with the same Name exists in both, the
occurrence property value wins. Type values are defaults.
function effectiveProperties(occurrence, schema):
result = {} # psetName -> { propName -> property }
# 1. TYPE LEVEL (defaults) - apply first
typeObject = resolveType(occurrence, schema)
if typeObject != NULL:
for psd in (typeObject.HasPropertySets or []):
mergeSet(result, psd) # see Pattern 4 for expansion
# 2. OCCURRENCE LEVEL (overrides) - apply second so it wins
for rel in occurrence.IsDefinedBy:
if not isInstance(rel, "IfcRelDefinesByProperties"):
continue # IFC2x3 : skips IfcRelDefinesByType members
for psd in expand(rel.RelatingPropertyDefinition):
mergeSet(result, psd)
return result
function mergeSet(result, psd):
if not isInstance(psd, "IfcPropertySet"):
return # IfcElementQuantity handled separately
bucket = result.setdefault(psd.Name, {})
for prop in psd.HasProperties:
bucket[prop.Name] = prop # later write (occurrence) overwrites earlier
ALWAYS apply the type level first and the occurrence level second so the second
write overwrites on a Name collision. NEVER reverse the order.
Pattern 2 : resolve the type of an occurrence (version-split)
function resolveType(occurrence, schema):
if schema in ("IFC4", "IFC4X3"):
# IsTypedBy : SET [0:1] OF IfcRelDefinesByType
if occurrence.IsTypedBy is not empty:
return occurrence.IsTypedBy[0].RelatingType
return NULL
else: # IFC2X3
# no IsTypedBy ; the type link sits inside IsDefinedBy
for rel in occurrence.IsDefinedBy:
if isInstance(rel, "IfcRelDefinesByType"):
return rel.RelatingType
return NULL
IfcRelDefinesByType.RelatingType is the IfcTypeObject ; RelatedObjects are the
occurrences. NEVER swap the direction. An element with no type relationship is
valid : resolveType returns NULL and only occurrence sets apply.
Pattern 3 : read quantities (IfcElementQuantity)
IfcElementQuantity arrives through the SAME IfcRelDefinesByProperties /
HasPropertySets channels as IfcPropertySet. Collect it the same way, then
branch:
for psd in allRetrievedSets(occurrence):
if isInstance(psd, "IfcElementQuantity"):
for q in psd.Quantities: # SET [1:?] OF IfcPhysicalQuantity
value = readQuantityValue(q) # LengthValue / AreaValue / ...
unit = q.Unit or globalUnitFor(q) # IfcPhysicalSimpleQuantity.Unit
Each IfcPhysicalSimpleQuantity subtype names its value attribute after its kind :
IfcQuantityLength.LengthValue, IfcQuantityArea.AreaValue,
IfcQuantityVolume.VolumeValue, IfcQuantityCount.CountValue,
IfcQuantityWeight.WeightValue, IfcQuantityTime.TimeValue,
IfcQuantityNumber.NumberValue (IFC4+). NEVER call .HasProperties on an
IfcElementQuantity : that attribute does not exist on it.
Pattern 4 : expand IfcPropertySetDefinitionSet (IFC4+)
In IFC4 and IFC4.3, IfcRelDefinesByProperties.RelatingPropertyDefinition is the
select IfcPropertySetDefinitionSelect, which is EITHER a single
IfcPropertySetDefinition OR an IfcPropertySetDefinitionSet (a defined type :
SET [1:?] OF IfcPropertySetDefinition). One relationship can therefore carry many
sets at once.
function expand(relating):
if isInstance(relating, "IfcPropertySetDefinitionSet"):
return list(relating) # the SET members
return [relating] # a single IfcPropertySetDefinition
In IFC2x3 RelatingPropertyDefinition is always a single IfcPropertySetDefinition
; expand is a no-op there. NEVER assume one relationship equals one set in IFC4+.
Pattern 5 : resolve a property value unit
function resolveUnit(property, project):
if property.Unit is set:
return property.Unit # explicit local override
measureType = typeOf(property.NominalValue) # e.g. IfcLengthMeasure
unitEnum = unitEnumFor(measureType) # e.g. LENGTHUNIT
for u in project.UnitsInContext.Units:
if u.UnitType == unitEnum:
return u # the project global unit
return NULL # dimensionless or unresolved
A property never invents a unit. It either carries an explicit Unit, or its
measure type maps to an IfcUnitEnum value resolved against the project
IfcUnitAssignment. See ifc-syntax-units for the measure-to-unit table.
Pattern 6 : standard vs custom property sets
psetName.startsWith("Pset_") -> standard property set (documented template)
psetName.startsWith("Qto_") -> standard quantity set (IFC4+)
otherwise -> custom / project-specific set
A custom set wrongly named with the reserved Pset_ or Qto_ prefix is an
authoring anti-pattern, not a separate kind of set. ALWAYS read custom sets the
same way as standard ones ; the prefix only signals whether a published template
governs the contained property names and types. NEVER skip a set because it lacks
the Pset_ prefix.
Worked Order of Operations
- Read
FILE_SCHEMAfrom the STEP header to fix the version branch. - Index every
IfcRelDefinesByPropertiesandIfcRelDefinesByTypeto rebuild theIsDefinedBy/IsTypedByinverses (ifc-impl-reading-parsing). - For the target element :
resolveType(Pattern 2). - Collect type sets from
typeObject.HasPropertySets, occurrence sets fromIsDefinedBy, expanding eachRelatingPropertyDefinition(Pattern 4). - Split retrieved sets into property sets and quantity sets by entity type.
- Merge property sets type-first then occurrence-second (Pattern 1).
- For each value, resolve the unit (Pattern 5).
Reference Links
references/methods.md: exact attributes, cardinalities, version matrix, the measure-to-unit-enum table.references/examples.md: worked STEP snippets and the full resolution walk-through.references/anti-patterns.md: the failure modes and why each one is wrong.
Related skills
ifc-syntax-property-sets:IfcPropertySet, theIfcPropertyvalue tree.ifc-syntax-quantities:IfcElementQuantity, the seven quantity subtypes.ifc-syntax-units:IfcUnitAssignment, measure-to-unit resolution.ifc-impl-reading-parsing: rebuilding INVERSE attributes from a STEP file.ifc-impl-data-enrichment: the write-side, attaching property sets.ifc-errors-property-set-mistakes:WHERE-rule-backed property failures.
Verified sources
All entity names, attributes, and cardinalities verified 2026-05-20 via WebFetch
against the buildingSMART specifications listed in SOURCES.md :
IFC 4.3.2 spec, IFC4 ADD2 TC1 spec, IFC2x3 TC1 spec.