Fix malformed LoadBearing property templates in IFC4 psets

Pset_RoofCommon, Pset_RampCommon and Pset_StairCommon each declared their
LoadBearing IfcSimplePropertyTemplate with 11 attributes instead of 12 -
the `$` placeholder for OwnerHistory was dropped in 822107a6ed, shifting
every following attribute by one:

    1 OwnerHistory  'LoadBearing'   <- should be $
    2 Name          'Indicates whether the object is intended to carry...'
    4 TemplateType  'IfcBoolean'    <- not a valid P_* enum

The property therefore surfaced with its description as a name and no
valid template type, so it never showed up in the property set UI.

Restore the missing OwnerHistory placeholder, and add regression tests
covering both the three affected psets and the general well-formedness of
all bundled Pset_*.ifc property templates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ryan Schultz
2026-08-14 08:20:52 -05:00
committed by Thomas Krijnen
parent 84a6a9ac75
commit bdc6d2340f
2 changed files with 32 additions and 3 deletions
@@ -3811,7 +3811,7 @@ DATA;
#3805= IFCLIBRARYREFERENCE($,$,'\X2\662F5426591690E867844EF6\X0\','\X2\8868793A8BE567844EF6662F54268BBE8BA14E3A591690E867844EF6300282E5662FFF0C52198BE567844EF64E3A591690E867844EF6FF0C671D54115EFA7B517269768459164FA73002\X0\','zh-CN',$);
#3806= IFCRELASSOCIATESLIBRARY('0ny33eYS11mR15A8dSdsB_',$,$,$,(#3796),#3805);
#3807= IFCSIMPLEPROPERTYTEMPLATE('3MLW9Uqir2HPZ8iRLwgUEW',$,'ThermalTransmittance','Thermal transmittance coefficient (U-Value) of an element, within the direction of the thermal flow (including all materials).',.P_SINGLEVALUE.,'IfcThermalTransmittanceMeasure',$,$,$,$,$,.READWRITE.);
#3808= IFCSIMPLEPROPERTYTEMPLATE('1s7fxao7L4X9mgLw5vDLbk','LoadBearing','Indicates whether the object is intended to carry loads (TRUE) or not (FALSE).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#3808= IFCSIMPLEPROPERTYTEMPLATE('1s7fxao7L4X9mgLw5vDLbk',$,'LoadBearing','Indicates whether the object is intended to carry loads (TRUE) or not (FALSE).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#3809= IFCSIMPLEPROPERTYTEMPLATE('1LZHC0qVyHuO00025QrE$V',$,'FireRating','Fire rating for this object.\X\0AIt is given according to the national fire safety classification.',.P_SINGLEVALUE.,'IfcLabel','',$,$,$,$,.READWRITE.);
#3810= IFCLIBRARYREFERENCE($,$,'Feuerwiderstandsklasse','Feuerwiderstandasklasse gem\S\d\S\_ der nationalen oder regionalen Brandschutzverordnung.','de-DE',$);
#3811= IFCRELASSOCIATESLIBRARY('2rsNjFcwD3xfKDL2kSNZLX',$,$,$,(#3809),#3810);
@@ -3986,7 +3986,7 @@ DATA;
#3980= IFCRELASSOCIATESLIBRARY('07nF4Mq1D99gQJGAXkyvYq',$,$,$,(#3972),#3979);
#3981= IFCLIBRARYREFERENCE($,$,'\X2\5BFC70ED7CFB6570\X0\','\X2\6750659976845BFC70ED7CFB6570FF08\X0\U\X2\503CFF093002\X0\\X\0A\X2\8868793A7A7F8FC78BE55C4B987688689762768465744F535BFC70ED7CFB6570FF08530562EC6240670967506599FF09\X0\','zh-CN',$);
#3982= IFCRELASSOCIATESLIBRARY('0gCn46liz97xidokY4jCk4',$,$,$,(#3972),#3981);
#3983= IFCSIMPLEPROPERTYTEMPLATE('27gGzXJgHElgJNKZaas8aH','LoadBearing','Indicates whether the object is intended to carry loads (TRUE) or not (FALSE).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#3983= IFCSIMPLEPROPERTYTEMPLATE('27gGzXJgHElgJNKZaas8aH',$,'LoadBearing','Indicates whether the object is intended to carry loads (TRUE) or not (FALSE).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#3984= IFCSIMPLEPROPERTYTEMPLATE('2rXBa0qW4HuO00025QrE$V',$,'FireRating','Fire rating for this object. It is given according to the national fire safety classification.',.P_SINGLEVALUE.,'IfcLabel','',$,$,$,$,.READWRITE.);
#3985= IFCLIBRARYREFERENCE($,$,'Feuerwiderstandsklasse','Feuerwiderstandasklasse gem\S\d\S\_ der nationalen oder regionalen Brandschutzverordnung.','de-DE',$);
#3986= IFCRELASSOCIATESLIBRARY('068HwrG5X1bfDVqpvbqRhT',$,$,$,(#3984),#3985);
@@ -4432,7 +4432,7 @@ DATA;
#4426= IFCLIBRARYREFERENCE($,$,'\X2\662F5426591690E867844EF6\X0\','\X2\8868793A8BE567844EF6662F54268BBE8BA14E3A591690E867844EF6300282E5662FFF0C52198BE567844EF64E3A591690E867844EF6FF0C671D54115EFA7B517269768459164FA73002\X0\','zh-CN',$);
#4427= IFCRELASSOCIATESLIBRARY('0H77FP34b3XRusar_DVCi_',$,$,$,(#4417),#4426);
#4428= IFCSIMPLEPROPERTYTEMPLATE('3YhweyeoP4yfJpfFi81P$3',$,'ThermalTransmittance','Thermal transmittance coefficient (U-Value) of an element, within the direction of the thermal flow (including all materials).',.P_SINGLEVALUE.,'IfcThermalTransmittanceMeasure',$,$,$,$,$,.READWRITE.);
#4429= IFCSIMPLEPROPERTYTEMPLATE('2FnJNsRdfDQ9dzEq8OinWs','LoadBearing','Indicates whether the object is intended to carry loads (TRUE) or not (FALSE).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#4429= IFCSIMPLEPROPERTYTEMPLATE('2FnJNsRdfDQ9dzEq8OinWs',$,'LoadBearing','Indicates whether the object is intended to carry loads (TRUE) or not (FALSE).',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
#4430= IFCSIMPLEPROPERTYTEMPLATE('0P_KM0qWqHuO00025QrE$V',$,'FireRating','Fire rating for this object.\X\0AIt is given according to the national fire safety classification.',.P_SINGLEVALUE.,'IfcLabel','',$,$,$,$,.READWRITE.);
#4431= IFCLIBRARYREFERENCE($,$,'Feuerwiderstandsklasse','Feuerwiderstandasklasse gem\S\d\S\_ der nationalen oder regionalen Brandschutzverordnung.','de-DE',$);
#4432= IFCRELASSOCIATESLIBRARY('2t9U7nMxj0UurpJn97sDMt',$,$,$,(#4430),#4431);
@@ -18,10 +18,39 @@
"""Run this test from src/ifcopenshell-python folder: pytest --durations=0 ifcopenshell/util/test_pset.py"""
import pytest
from ifcopenshell.util import pset
from ifcopenshell.util.pset import ApplicableEntity
class TestPsetTemplateFiles:
@pytest.mark.parametrize("schema", ("IFC2X3", "IFC4", "IFC4X3"))
def test_property_templates_are_well_formed(self, schema):
"""Guard against attribute shifts when the bundled Pset_*.ifc files are patched by hand.
A missing `$` for OwnerHistory silently shifts every following attribute,
which makes the property show up with its description as a name and no
template type - so it's dropped from the UI. See #5708.
"""
template = pset.get_template(schema)
for ifc_file in template.templates:
for property_template in ifc_file.by_type("IfcSimplePropertyTemplate"):
assert property_template.OwnerHistory is None, property_template
assert property_template.Name, property_template
template_type = property_template.TemplateType
assert template_type and template_type.startswith(("P_", "Q_")), property_template
@pytest.mark.parametrize("pset_name", ("Pset_RoofCommon", "Pset_RampCommon", "Pset_StairCommon"))
def test_load_bearing_is_defined_in_ifc4(self, pset_name):
template = pset.get_template("IFC4")
pset_template = template.get_by_name(pset_name)
assert pset_template is not None
properties = {p.Name: p for p in pset_template.HasPropertyTemplates}
assert "LoadBearing" in properties
assert properties["LoadBearing"].PrimaryMeasureType == "IfcBoolean"
class TestPsetQto:
@classmethod
def setup_class(cls):