From d2652bf78374b09dcfb6d6b725fa0a3f9d5e6858 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Mon, 17 Nov 2025 14:24:49 -0500 Subject: [PATCH 01/14] create schema-specific resource files for conversion-based-units (IVS-680) --- .../IFC2X3/valid_ConversionBasedUnits.csv | 13 +++++++ .../IFC4/valid_ConversionBasedUnits.csv | 33 ++++++++++++++++++ .../IFC4X3/valid_ConversionBasedUnits.csv | 34 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 features/resources/IFC2X3/valid_ConversionBasedUnits.csv create mode 100644 features/resources/IFC4/valid_ConversionBasedUnits.csv create mode 100644 features/resources/IFC4X3/valid_ConversionBasedUnits.csv diff --git a/features/resources/IFC2X3/valid_ConversionBasedUnits.csv b/features/resources/IFC2X3/valid_ConversionBasedUnits.csv new file mode 100644 index 000000000..0adf38707 --- /dev/null +++ b/features/resources/IFC2X3/valid_ConversionBasedUnits.csv @@ -0,0 +1,13 @@ +UnitType,Name,ConversionFactor,SIUnitPrefix,SIUnitName,Description +LENGTHUNIT,inch,25.4,MILLI,METRE +LENGTHUNIT,foot,304.8,MILLI,METRE +LENGTHUNIT,yard,914.,MILLI,METRE +LENGTHUNIT,mile,1609.,None,METRE +AREAUNIT,acre,4046.86,None,METRE +VOLUMEUNIT,litre,0.001,None,METRE +VOLUMEUNIT,pint UK,0.000568,None,METRE +VOLUMEUNIT,pint US,0.000473,None,METRE +VOLUMEUNIT,gallon UK,0.004546,None,METRE +VOLUMEUNIT,gallon US,0.003785,None,METRE +MASSUNIT,ounce,28.35,None,GRAM +MASSUNIT,pound,0.454,KILO,GRAM \ No newline at end of file diff --git a/features/resources/IFC4/valid_ConversionBasedUnits.csv b/features/resources/IFC4/valid_ConversionBasedUnits.csv new file mode 100644 index 000000000..fa88652a7 --- /dev/null +++ b/features/resources/IFC4/valid_ConversionBasedUnits.csv @@ -0,0 +1,33 @@ +UnitType,Name,ConversionFactor,SIUnitPrefix,SIUnitName,Description +LENGTHUNIT,inch,25.4,MILLI,METRE +LENGTHUNIT,foot,304.8,MILLI,METRE +LENGTHUNIT,yard,914.,MILLI,METRE +LENGTHUNIT,mile,1609.,None,METRE +AREAUNIT,square inch,0.0006452,None,METRE +AREAUNIT,square foot,0.09290,None,METRE +AREAUNIT,square yard,0.83612736,None,METRE +AREAUNIT,acre,4046.86,None,METRE +AREAUNIT,square mile,2588881.,None,METRE +VOLUMEUNIT,cubic inch,0.00001639,None,METRE +VOLUMEUNIT,cubic foot,0.02832,None,METRE +VOLUMEUNIT,cubic yard,0.7636,None,METRE +VOLUMEUNIT,litre,0.001,None,METRE +VOLUMEUNIT,fluid ounce UK,0.0000284130625,None,METRE +VOLUMEUNIT,fluid ounce US,0.00002957353,None,METRE +VOLUMEUNIT,pint UK,0.000568,None,METRE +VOLUMEUNIT,pint US,0.000473,None,METRE +VOLUMEUNIT,gallon UK,0.004546,None,METRE +VOLUMEUNIT,gallon US,0.003785,None,METRE +PLANEANGLEUNIT,degree,π/180,None,RADIAM +MASSUNIT,ounce,28.35,None,GRAM +MASSUNIT,pound,0.454,KILO,GRAM +MASSUNIT,ton UK,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton +MASSUNIT,ton US,907.18474,KILO,GRAM,also known as short ton or net ton +FORCEUNIT,lbf,4.4482216153,None,NEWTON,also known as pound-force +FORCEUNIT,kip,4448.2216153,None,NEWTON,also known as kilopound-force +PRESSUREUNIT,psi,6894.7572932,None,PASCAL,also known as pound-force per square inch +PRESSUREUNIT,ksi,6894757.2932,None,PASCAL,also known as kilopound-force per square inch +TIMEUNIT,minute,60.,None,SECOND +TIMEUNIT,hour,3600.,None,SECOND +TIMEUNIT,day, 86400.,None,SECOND +ENERGYUNIT,btu,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file diff --git a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv new file mode 100644 index 000000000..add7a01ff --- /dev/null +++ b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv @@ -0,0 +1,34 @@ +UnitType,Name,ConversionFactor,SIUnitPrefix,SIUnitName,Description +LENGTHUNIT,inch,25.4,MILLI,METRE +LENGTHUNIT,foot,304.8,MILLI,METRE +LENGTHUNIT,US survey foot,304.80060960122,MILLI,METRE,the approximate value of 1200/3937 meters +LENGTHUNIT,yard,914.,MILLI,METRE +LENGTHUNIT,mile,1609.,None,METRE +AREAUNIT,square inch,0.0006452,None,METRE +AREAUNIT,square foot,0.09290,None,METRE +AREAUNIT,square yard,0.83612736,None,METRE +AREAUNIT,acre,4046.86,None,METRE +AREAUNIT,square mile,2588881.,None,METRE +VOLUMEUNIT,cubic inch,0.00001639,None,METRE +VOLUMEUNIT,cubic foot,0.02832,None,METRE +VOLUMEUNIT,cubic yard,0.7636,None,METRE +VOLUMEUNIT,litre,0.001,None,METRE +VOLUMEUNIT,fluid ounce UK,0.0000284130625,None,METRE +VOLUMEUNIT,fluid ounce US,0.00002957353,None,METRE +VOLUMEUNIT,pint UK,0.000568,None,METRE +VOLUMEUNIT,pint US,0.000473,None,METRE +VOLUMEUNIT,gallon UK,0.004546,None,METRE +VOLUMEUNIT,gallon US,0.003785,None,METRE +PLANEANGLEUNIT,degree,π/180,None,RADIAM +MASSUNIT,ounce,28.35,None,GRAM +MASSUNIT,pound,0.454,KILO,GRAM +MASSUNIT,ton UK,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton +MASSUNIT,ton US,907.18474,KILO,GRAM,also known as short ton or net ton +FORCEUNIT,lbf,4.4482216153,None,NEWTON,also known as pound-force +FORCEUNIT,kip,4448.2216153,None,NEWTON,also known as kilopound-force +PRESSUREUNIT,psi,6894.7572932,None,PASCAL,also known as pound-force per square inch +PRESSUREUNIT,ksi,6894757.2932,None,PASCAL,also known as kilopound-force per square inch +TIMEUNIT,minute,60.,None,SECOND +TIMEUNIT,hour,3600.,None,SECOND +TIMEUNIT,day, 86400.,None,SECOND +ENERGYUNIT,btu,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file From 7c8ba3c5da6df37557af58f0500e7f73ffc1eda4 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Thu, 20 Nov 2025 15:40:49 -0500 Subject: [PATCH 02/14] initial WIP commit for PJS001 gherkin logic (IVS-680) --- ...001_Correct-conversion-based-units.feature | 30 +++++++++++++++++++ features/steps/steps.py | 2 +- ...and_qtys.py => propertysets_qtys_units.py} | 0 features/steps/thens/values.py | 4 +++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 features/rules/PJS/PJS001_Correct-conversion-based-units.feature rename features/steps/steps/{propertysets_and_qtys.py => propertysets_qtys_units.py} (100%) diff --git a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature new file mode 100644 index 000000000..d77a33b9b --- /dev/null +++ b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature @@ -0,0 +1,30 @@ +@implementer-agreement +@PJS +@version1 +Feature: PJS001 - Correct conversion based units + + The rule verifies that conversion-based units used per Concept Template 4.1.9.9 + (https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/concepts/Project_Context/Project_Units/content.html) + have names and corresponding conversion factors per the table of recommended values for each schema version. + IFC 4X3: https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcConversionBasedUnit.htm + IFC 4: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcconversionbasedunit.htm + IFC 2X3: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/ifcmeasureresource/lexical/ifcconversionbasedunit.htm + + Background: Selection of conversion-based units in default unit assignment + Given an .IfcProject. + Given its attribute .UnitsInContext. + Given an .IfcConversionBasedUnit. + + Scenario: Validating correct names for area, length, and unit + Given .UnitType. ^is^ 'AREAUNIT' or 'LENGTHUNIT' or 'VOLUMEUNIT' + Given its attribute .Name. + + Then the value must be in 'valid_ConversionBasedUnits.csv' + + """ + Scenario: Validating correct conversion factors + Given its attribute .ConversionFactor. + + Then the factor must be in 'valid_ConversionBasedUnits.csv' + """ + diff --git a/features/steps/steps.py b/features/steps/steps.py index 5854c4984..b15e74d30 100644 --- a/features/steps/steps.py +++ b/features/steps/steps.py @@ -1,4 +1,4 @@ from givens import attributes, entities, relationships, values from thens import alignment, attributes, geometry, nesting, reference, relations, values, existence from steps import attribute_selection, attribute_value, entity_selection, model_traversal, representation, \ - propertysets_and_qtys, crs + propertysets_qtys_units, crs diff --git a/features/steps/steps/propertysets_and_qtys.py b/features/steps/steps/propertysets_qtys_units.py similarity index 100% rename from features/steps/steps/propertysets_and_qtys.py rename to features/steps/steps/propertysets_qtys_units.py diff --git a/features/steps/thens/values.py b/features/steps/thens/values.py index e14d091ef..f15b754ae 100644 --- a/features/steps/thens/values.py +++ b/features/steps/thens/values.py @@ -29,6 +29,10 @@ def read_csv_values(schema, csv_file): @gherkin_ifc.step("The {i:value_or_type} must be in '{csv_file}.csv'") @gherkin_ifc.step("The {i:values_or_types} must be in '{csv_file}.csv'") def step_impl(context, inst, i, csv_file): + """ + This implementation supports basic reading from CSV resources that have a single field and no header. + It validates a value against a single field, but does not support CSV resources with multiple fields per row. + """ if not inst: return [] From 683cbc961610a21ca75df4cdcf65a7f7bb84da56 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Wed, 26 Nov 2025 10:27:14 -0500 Subject: [PATCH 03/14] re-order tables so that name comes first (IVS-680) --- .../IFC2X3/valid_ConversionBasedUnits.csv | 26 +++---- .../IFC4/valid_ConversionBasedUnits.csv | 66 +++++++++--------- .../IFC4X3/valid_ConversionBasedUnits.csv | 68 +++++++++---------- 3 files changed, 80 insertions(+), 80 deletions(-) diff --git a/features/resources/IFC2X3/valid_ConversionBasedUnits.csv b/features/resources/IFC2X3/valid_ConversionBasedUnits.csv index 0adf38707..543504d79 100644 --- a/features/resources/IFC2X3/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC2X3/valid_ConversionBasedUnits.csv @@ -1,13 +1,13 @@ -UnitType,Name,ConversionFactor,SIUnitPrefix,SIUnitName,Description -LENGTHUNIT,inch,25.4,MILLI,METRE -LENGTHUNIT,foot,304.8,MILLI,METRE -LENGTHUNIT,yard,914.,MILLI,METRE -LENGTHUNIT,mile,1609.,None,METRE -AREAUNIT,acre,4046.86,None,METRE -VOLUMEUNIT,litre,0.001,None,METRE -VOLUMEUNIT,pint UK,0.000568,None,METRE -VOLUMEUNIT,pint US,0.000473,None,METRE -VOLUMEUNIT,gallon UK,0.004546,None,METRE -VOLUMEUNIT,gallon US,0.003785,None,METRE -MASSUNIT,ounce,28.35,None,GRAM -MASSUNIT,pound,0.454,KILO,GRAM \ No newline at end of file +Name,UnitType,ConversionFactor,SIUnitPrefix,SIUnitName,Description +inch,LENGTHUNIT,25.4,MILLI,METRE +foot,LENGTHUNIT,304.8,MILLI,METRE +yard,LENGTHUNIT,914.,MILLI,METRE +mile,LENGTHUNIT,1609.,None,METRE +acre,AREAUNIT,4046.86,None,METRE +litre,VOLUMEUNIT,0.001,None,METRE +pint UK,VOLUMEUNIT,0.000568,None,METRE +pint US,VOLUMEUNIT,0.000473,None,METRE +gallon UK,VOLUMEUNIT,0.004546,None,METRE +gallon US,VOLUMEUNIT,0.003785,None,METRE +ounce,MASSUNIT,28.35,None,GRAM +pound,MASSUNIT,0.454,KILO,GRAM \ No newline at end of file diff --git a/features/resources/IFC4/valid_ConversionBasedUnits.csv b/features/resources/IFC4/valid_ConversionBasedUnits.csv index fa88652a7..b06540657 100644 --- a/features/resources/IFC4/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC4/valid_ConversionBasedUnits.csv @@ -1,33 +1,33 @@ -UnitType,Name,ConversionFactor,SIUnitPrefix,SIUnitName,Description -LENGTHUNIT,inch,25.4,MILLI,METRE -LENGTHUNIT,foot,304.8,MILLI,METRE -LENGTHUNIT,yard,914.,MILLI,METRE -LENGTHUNIT,mile,1609.,None,METRE -AREAUNIT,square inch,0.0006452,None,METRE -AREAUNIT,square foot,0.09290,None,METRE -AREAUNIT,square yard,0.83612736,None,METRE -AREAUNIT,acre,4046.86,None,METRE -AREAUNIT,square mile,2588881.,None,METRE -VOLUMEUNIT,cubic inch,0.00001639,None,METRE -VOLUMEUNIT,cubic foot,0.02832,None,METRE -VOLUMEUNIT,cubic yard,0.7636,None,METRE -VOLUMEUNIT,litre,0.001,None,METRE -VOLUMEUNIT,fluid ounce UK,0.0000284130625,None,METRE -VOLUMEUNIT,fluid ounce US,0.00002957353,None,METRE -VOLUMEUNIT,pint UK,0.000568,None,METRE -VOLUMEUNIT,pint US,0.000473,None,METRE -VOLUMEUNIT,gallon UK,0.004546,None,METRE -VOLUMEUNIT,gallon US,0.003785,None,METRE -PLANEANGLEUNIT,degree,π/180,None,RADIAM -MASSUNIT,ounce,28.35,None,GRAM -MASSUNIT,pound,0.454,KILO,GRAM -MASSUNIT,ton UK,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton -MASSUNIT,ton US,907.18474,KILO,GRAM,also known as short ton or net ton -FORCEUNIT,lbf,4.4482216153,None,NEWTON,also known as pound-force -FORCEUNIT,kip,4448.2216153,None,NEWTON,also known as kilopound-force -PRESSUREUNIT,psi,6894.7572932,None,PASCAL,also known as pound-force per square inch -PRESSUREUNIT,ksi,6894757.2932,None,PASCAL,also known as kilopound-force per square inch -TIMEUNIT,minute,60.,None,SECOND -TIMEUNIT,hour,3600.,None,SECOND -TIMEUNIT,day, 86400.,None,SECOND -ENERGYUNIT,btu,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file +Name,UnitType,ConversionFactor,SIUnitPrefix,SIUnitName,Description +inch,LENGTHUNIT,25.4,MILLI,METRE +foot,LENGTHUNIT,304.8,MILLI,METRE +yard,LENGTHUNIT,914.,MILLI,METRE +mile,LENGTHUNIT,1609.,None,METRE +square inch,AREAUNIT,0.0006452,None,METRE +square foot,AREAUNIT,0.09290,None,METRE +square yard,AREAUNIT,0.83612736,None,METRE +acre,AREAUNIT,4046.86,None,METRE +square mile,AREAUNIT,2588881.,None,METRE +cubic inch,VOLUMEUNIT,0.00001639,None,METRE +cubic foot,VOLUMEUNIT,0.02832,None,METRE +cubic yard,VOLUMEUNIT,0.7636,None,METRE +litre,VOLUMEUNIT,0.001,None,METRE +fluid ounce UK,VOLUMEUNIT,0.0000284130625,None,METRE +fluid ounce US,VOLUMEUNIT,0.00002957353,None,METRE +pint UK,VOLUMEUNIT,0.000568,None,METRE +pint US,VOLUMEUNIT,0.000473,None,METRE +gallon UK,VOLUMEUNIT,0.004546,None,METRE +gallon US,VOLUMEUNIT,0.003785,None,METRE +degree,PLANEANGLEUNIT,π/180,None,RADIAM +ounce,MASSUNIT,28.35,None,GRAM +pound,MASSUNIT,0.454,KILO,GRAM +ton UK,MASSUNIT,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton +ton US,MASSUNIT,907.18474,KILO,GRAM,also known as short ton or net ton +lbf,FORCEUNIT,4.4482216153,None,NEWTON,also known as pound-force +kip,FORCEUNIT,4448.2216153,None,NEWTON,also known as kilopound-force +psi,PRESSUREUNIT,6894.7572932,None,PASCAL,also known as pound-force per square inch +ksi,PRESSUREUNIT,6894757.2932,None,PASCAL,also known as kilopound-force per square inch +minute,TIMEUNIT,60.,None,SECOND +hour,TIMEUNIT,3600.,None,SECOND +day,TIMEUNIT,86400.,None,SECOND +btu,ENERGYUNIT,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file diff --git a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv index add7a01ff..8985092aa 100644 --- a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv @@ -1,34 +1,34 @@ -UnitType,Name,ConversionFactor,SIUnitPrefix,SIUnitName,Description -LENGTHUNIT,inch,25.4,MILLI,METRE -LENGTHUNIT,foot,304.8,MILLI,METRE -LENGTHUNIT,US survey foot,304.80060960122,MILLI,METRE,the approximate value of 1200/3937 meters -LENGTHUNIT,yard,914.,MILLI,METRE -LENGTHUNIT,mile,1609.,None,METRE -AREAUNIT,square inch,0.0006452,None,METRE -AREAUNIT,square foot,0.09290,None,METRE -AREAUNIT,square yard,0.83612736,None,METRE -AREAUNIT,acre,4046.86,None,METRE -AREAUNIT,square mile,2588881.,None,METRE -VOLUMEUNIT,cubic inch,0.00001639,None,METRE -VOLUMEUNIT,cubic foot,0.02832,None,METRE -VOLUMEUNIT,cubic yard,0.7636,None,METRE -VOLUMEUNIT,litre,0.001,None,METRE -VOLUMEUNIT,fluid ounce UK,0.0000284130625,None,METRE -VOLUMEUNIT,fluid ounce US,0.00002957353,None,METRE -VOLUMEUNIT,pint UK,0.000568,None,METRE -VOLUMEUNIT,pint US,0.000473,None,METRE -VOLUMEUNIT,gallon UK,0.004546,None,METRE -VOLUMEUNIT,gallon US,0.003785,None,METRE -PLANEANGLEUNIT,degree,π/180,None,RADIAM -MASSUNIT,ounce,28.35,None,GRAM -MASSUNIT,pound,0.454,KILO,GRAM -MASSUNIT,ton UK,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton -MASSUNIT,ton US,907.18474,KILO,GRAM,also known as short ton or net ton -FORCEUNIT,lbf,4.4482216153,None,NEWTON,also known as pound-force -FORCEUNIT,kip,4448.2216153,None,NEWTON,also known as kilopound-force -PRESSUREUNIT,psi,6894.7572932,None,PASCAL,also known as pound-force per square inch -PRESSUREUNIT,ksi,6894757.2932,None,PASCAL,also known as kilopound-force per square inch -TIMEUNIT,minute,60.,None,SECOND -TIMEUNIT,hour,3600.,None,SECOND -TIMEUNIT,day, 86400.,None,SECOND -ENERGYUNIT,btu,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file +Name,UnitType,ConversionFactor,SIUnitPrefix,SIUnitName,Description +inch,LENGTHUNIT,25.4,MILLI,METRE +foot,LENGTHUNIT,304.8,MILLI,METRE +US survey foot,LENGTHUNIT,304.80060960122,MILLI,METRE,the approximate value of 1200/3937 meters +yard,LENGTHUNIT,914.,MILLI,METRE +mile,LENGTHUNIT,1609.,None,METRE +square inch,AREAUNIT,0.0006452,None,METRE +square foot,AREAUNIT,0.09290,None,METRE +square yard,AREAUNIT,0.83612736,None,METRE +acre,AREAUNIT,4046.86,None,METRE +square mile,AREAUNIT,2588881.,None,METRE +cubic inch,VOLUMEUNIT,0.00001639,None,METRE +cubic foot,VOLUMEUNIT,0.02832,None,METRE +cubic yard,VOLUMEUNIT,0.7636,None,METRE +litre,VOLUMEUNIT,0.001,None,METRE +fluid ounce UK,VOLUMEUNIT,0.0000284130625,None,METRE +fluid ounce US,VOLUMEUNIT,0.00002957353,None,METRE +pint UK,VOLUMEUNIT,0.000568,None,METRE +pint US,VOLUMEUNIT,0.000473,None,METRE +gallon UK,VOLUMEUNIT,0.004546,None,METRE +gallon US,VOLUMEUNIT,0.003785,None,METRE +degree,PLANEANGLEUNIT,π/180,None,RADIAM +ounce,MASSUNIT,28.35,None,GRAM +pound,MASSUNIT,0.454,KILO,GRAM +ton UK,MASSUNIT,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton +ton US,MASSUNIT,907.18474,KILO,GRAM,also known as short ton or net ton +lbf,FORCEUNIT,4.4482216153,None,NEWTON,also known as pound-force +kip,FORCEUNIT,4448.2216153,None,NEWTON,also known as kilopound-force +psi,PRESSUREUNIT,6894.7572932,None,PASCAL,also known as pound-force per square inch +ksi,PRESSUREUNIT,6894757.2932,None,PASCAL,also known as kilopound-force per square inch +minute,TIMEUNIT,60.,None,SECOND +hour,TIMEUNIT,3600.,None,SECOND +day,TIMEUNIT,86400.,None,SECOND +btu,ENERGYUNIT,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file From de71fa06ef8a949489eb50456f949eb079ebdd8c Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Wed, 26 Nov 2025 14:29:46 -0500 Subject: [PATCH 04/14] pass files and fail files for scenario 1 (IVS-680) --- .../fail-pjs001-scenario01-degree_ifc2x3.ifc | 33 +++++++++++++++++ ...l-pjs001-scenario01-fluid_oz_uk_ifc2x3.ifc | 32 ++++++++++++++++ .../fail-pjs001-scenario01-furlong_ifc4x3.ifc | 21 +++++++++++ ...l-pjs001-scenario01-survey_foot_ifc2x3.ifc | 30 +++++++++++++++ .../PJS/pjs001/na-pjs001-second_ifc4.ifc | 24 ++++++++++++ .../PJS/pjs001/pass-pjs001-degree_ifc4.ifc | 25 +++++++++++++ .../pjs001/pass-pjs001-fluid_oz_uk_ifc4x3.ifc | 25 +++++++++++++ .../PJS/pjs001/pass-pjs001-fortnight_ifc4.ifc | 26 +++++++++++++ .../PJS/pjs001/pass-pjs001-ft_ifc2x3.ifc | 29 +++++++++++++++ .../PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4.ifc | 37 +++++++++++++++++++ .../pjs001/pass-pjs001-ft_sy_cyd_ifc4x3.ifc | 37 +++++++++++++++++++ .../pass-pjs001-us_survey_foot_ifc4x3.ifc | 25 +++++++++++++ 12 files changed, 344 insertions(+) create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario01-degree_ifc2x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario01-fluid_oz_uk_ifc2x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario01-furlong_ifc4x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario01-survey_foot_ifc2x3.ifc create mode 100644 test/files/PJS/pjs001/na-pjs001-second_ifc4.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-degree_ifc4.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-fluid_oz_uk_ifc4x3.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-fortnight_ifc4.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-ft_ifc2x3.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4x3.ifc create mode 100644 test/files/PJS/pjs001/pass-pjs001-us_survey_foot_ifc4x3.ifc diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario01-degree_ifc2x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario01-degree_ifc2x3.ifc new file mode 100644 index 000000000..c6e1cad92 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario01-degree_ifc2x3.ifc @@ -0,0 +1,33 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]'),'2;1'); +FILE_NAME('fail-pjs001-scenario01-degree_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org'); +#3=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#1),(#2)); +#4=IFCAPPLICATION(#3,'0.0.0','IfcOpenShell','IfcOpenShell'); +#5=IFCPERSON('FW','Whalbanger','Frank',$,$,$,$,$); +#6=IFCORGANIZATION('SBB','Soggy Bottom Boys',$,$,$); +#7=IFCPERSONANDORGANIZATION(#5,#6,$); +#8=IFCOWNERHISTORY(#7,#4,.READWRITE.,.ADDED.,1763516380,#7,#4,1763516380); +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',#8,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); + +/* degree definition */ +#16=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.017453292519943295),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.PLANEANGLEUNIT.,'degree',#18); + +/* unit assignment */ +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario01-fluid_oz_uk_ifc2x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario01-fluid_oz_uk_ifc2x3.ifc new file mode 100644 index 000000000..64d8fb629 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario01-fluid_oz_uk_ifc2x3.ifc @@ -0,0 +1,32 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]'),'2;1'); +FILE_NAME('fail-pjs001-fluid_oz_uk_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org'); +#3=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#1),(#2)); +#4=IFCAPPLICATION(#3,'0.0.0','IfcOpenShell','IfcOpenShell'); +#5=IFCPERSON('FW','Whalbanger','Frank',$,$,$,$,$); +#6=IFCORGANIZATION('SBB','Soggy Bottom Boys',$,$,$); +#7=IFCPERSONANDORGANIZATION(#5,#6,$); +#8=IFCOWNERHISTORY(#7,#4,.READWRITE.,.ADDED.,1763516380,#7,#4,1763516380); +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',#8,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); + +/* volume unit definition */ +#16=IFCDIMENSIONALEXPONENTS(3,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.0000284130625),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.VOLUMEUNIT.,'fluid ounce UK',#18); + +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario01-furlong_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario01-furlong_ifc4x3.ifc new file mode 100644 index 000000000..f76126267 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario01-furlong_ifc4x3.ifc @@ -0,0 +1,21 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('fail-pjs001-scenario01-furlong_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); +#8=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#9=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#10=IFCMEASUREWITHUNIT(IFCREAL(201.168),#9); +#11=IFCCONVERSIONBASEDUNIT(#8,.LENGTHUNIT.,'furlong',#10); +#20=IFCUNITASSIGNMENT((#11)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario01-survey_foot_ifc2x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario01-survey_foot_ifc2x3.ifc new file mode 100644 index 000000000..1b3f31358 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario01-survey_foot_ifc2x3.ifc @@ -0,0 +1,30 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]'),'2;1'); +FILE_NAME('fail-pjs001-survey_foot_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org'); +#3=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#1),(#2)); +#4=IFCAPPLICATION(#3,'0.0.0','IfcOpenShell','IfcOpenShell'); +#5=IFCPERSON('FW','Whalbanger','Frank',$,$,$,$,$); +#6=IFCORGANIZATION('SBB','Soggy Bottom Boys',$,$,$); +#7=IFCPERSONANDORGANIZATION(#5,#6,$); +#8=IFCOWNERHISTORY(#7,#4,.READWRITE.,.ADDED.,1763516380,#7,#4,1763516380); +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',#8,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); + +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(304.80060960122),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'US survey foot',#18); +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/na-pjs001-second_ifc4.ifc b/test/files/PJS/pjs001/na-pjs001-second_ifc4.ifc new file mode 100644 index 000000000..d7a3a8990 --- /dev/null +++ b/test/files/PJS/pjs001/na-pjs001-second_ifc4.ifc @@ -0,0 +1,24 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); +FILE_NAME('na-pjs001-second_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCPROJECT('1jjdBaLuz2vQsw16Lk$9EB',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + + +/* second definition */ +#16=IFCDIMENSIONALEXPONENTS(0,0,1,0,0,0,0); +#17=IFCSIUNIT(*,.TIMEUNIT.,$,.SECOND.); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#17)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-degree_ifc4.ifc b/test/files/PJS/pjs001/pass-pjs001-degree_ifc4.ifc new file mode 100644 index 000000000..0877ed2b8 --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-degree_ifc4.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); +FILE_NAME('pass-pjs001-degree_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',$,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); + +/* degree definition */ +#16=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.017453292519943295),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.PLANEANGLEUNIT.,'degree',#18); + +/* unit assignment */ +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-fluid_oz_uk_ifc4x3.ifc b/test/files/PJS/pjs001/pass-pjs001-fluid_oz_uk_ifc4x3.ifc new file mode 100644 index 000000000..31b404599 --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-fluid_oz_uk_ifc4x3.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('pass-pjs001-fluid_oz_uk_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* volume unit definition */ +#16=IFCDIMENSIONALEXPONENTS(3,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.0000284130625),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.VOLUMEUNIT.,'fluid ounce UK',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-fortnight_ifc4.ifc b/test/files/PJS/pjs001/pass-pjs001-fortnight_ifc4.ifc new file mode 100644 index 000000000..aa9f0f04a --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-fortnight_ifc4.ifc @@ -0,0 +1,26 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); +FILE_NAME('pass-pjs001-fortnight_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCPROJECT('1jjdBaLuz2vQsw16Lk$9EB',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + + +/* fortnight definition */ +#16=IFCDIMENSIONALEXPONENTS(0,0,1,0,0,0,0); +#17=IFCSIUNIT(*,.TIMEUNIT.,$,.SECOND.); +#18=IFCMEASUREWITHUNIT(IFCREAL(1209600),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.TIMEUNIT.,'fortnight',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-ft_ifc2x3.ifc b/test/files/PJS/pjs001/pass-pjs001-ft_ifc2x3.ifc new file mode 100644 index 000000000..772c02060 --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-ft_ifc2x3.ifc @@ -0,0 +1,29 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]'),'2;1'); +FILE_NAME('pass-pjs001-ft_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org'); +#3=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#1),(#2)); +#4=IFCAPPLICATION(#3,'0.0.0','IfcOpenShell','IfcOpenShell'); +#5=IFCPERSON('FW','Whalbanger','Frank',$,$,$,$,$); +#6=IFCORGANIZATION('SBB','Soggy Bottom Boys',$,$,$); +#7=IFCPERSONANDORGANIZATION(#5,#6,$); +#8=IFCOWNERHISTORY(#7,#4,.READWRITE.,.ADDED.,1763516380,#7,#4,1763516380); +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',#8,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.3048),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'foot',#18); +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4.ifc b/test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4.ifc new file mode 100644 index 000000000..80e066e3b --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4.ifc @@ -0,0 +1,37 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); +FILE_NAME('pass-pjs001-ft_sy_cyd_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCPROJECT('1jjdBaLuz2vQsw16Lk$9EB',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* foot definition */ +#8=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#9=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#10=IFCMEASUREWITHUNIT(IFCREAL(0.3048),#9); +#11=IFCCONVERSIONBASEDUNIT(#8,.LENGTHUNIT.,'foot',#10); + +/* square yard definition */ +#12=IFCDIMENSIONALEXPONENTS(2,0,0,0,0,0,0); +#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#14=IFCMEASUREWITHUNIT(IFCREAL(0.83612736),#13); +#15=IFCCONVERSIONBASEDUNIT(#12,.AREAUNIT.,'square yard',#14); + +/* cubic yard definition */ +#16=IFCDIMENSIONALEXPONENTS(3,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.7636),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.VOLUMEUNIT.,'cubic yard',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19,#11,#15)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4x3.ifc b/test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4x3.ifc new file mode 100644 index 000000000..aa907e8da --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-ft_sy_cyd_ifc4x3.ifc @@ -0,0 +1,37 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('pass-pjs001-ft_sy_cyd_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* foot definition */ +#8=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#9=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#10=IFCMEASUREWITHUNIT(IFCREAL(0.3048),#9); +#11=IFCCONVERSIONBASEDUNIT(#8,.LENGTHUNIT.,'foot',#10); + +/* square yard definition */ +#12=IFCDIMENSIONALEXPONENTS(2,0,0,0,0,0,0); +#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#14=IFCMEASUREWITHUNIT(IFCREAL(0.83612736),#13); +#15=IFCCONVERSIONBASEDUNIT(#12,.AREAUNIT.,'square yard',#14); + +/* cubic yard definition */ +#16=IFCDIMENSIONALEXPONENTS(3,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.7636),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.VOLUMEUNIT.,'cubic yard',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19,#11,#15)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/pass-pjs001-us_survey_foot_ifc4x3.ifc b/test/files/PJS/pjs001/pass-pjs001-us_survey_foot_ifc4x3.ifc new file mode 100644 index 000000000..a9c70a44c --- /dev/null +++ b/test/files/PJS/pjs001/pass-pjs001-us_survey_foot_ifc4x3.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('pass-pjs001-us_survey_foot_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* US survey foot definition */ +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(304.80060960122),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'US survey foot',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; From b5c37d028f8dcde5724790739bb302e6a7c25d96 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Wed, 26 Nov 2025 14:30:54 -0500 Subject: [PATCH 05/14] pass files and fail files for scenario 1 (IVS-680) --- .../IFC4/valid_ConversionBasedUnits.csv | 2 +- .../IFC4X3/valid_ConversionBasedUnits.csv | 2 +- ...PJS001_Correct-conversion-based-units.feature | 16 ++++------------ features/steps/validation_handling.py | 3 +++ 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/features/resources/IFC4/valid_ConversionBasedUnits.csv b/features/resources/IFC4/valid_ConversionBasedUnits.csv index b06540657..3773500bd 100644 --- a/features/resources/IFC4/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC4/valid_ConversionBasedUnits.csv @@ -18,7 +18,7 @@ pint UK,VOLUMEUNIT,0.000568,None,METRE pint US,VOLUMEUNIT,0.000473,None,METRE gallon UK,VOLUMEUNIT,0.004546,None,METRE gallon US,VOLUMEUNIT,0.003785,None,METRE -degree,PLANEANGLEUNIT,π/180,None,RADIAM +degree,PLANEANGLEUNIT,π/180,None,RADIAN ounce,MASSUNIT,28.35,None,GRAM pound,MASSUNIT,0.454,KILO,GRAM ton UK,MASSUNIT,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton diff --git a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv index 8985092aa..133935af8 100644 --- a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv @@ -19,7 +19,7 @@ pint UK,VOLUMEUNIT,0.000568,None,METRE pint US,VOLUMEUNIT,0.000473,None,METRE gallon UK,VOLUMEUNIT,0.004546,None,METRE gallon US,VOLUMEUNIT,0.003785,None,METRE -degree,PLANEANGLEUNIT,π/180,None,RADIAM +degree,PLANEANGLEUNIT,π/180,None,RADIAN ounce,MASSUNIT,28.35,None,GRAM pound,MASSUNIT,0.454,KILO,GRAM ton UK,MASSUNIT,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton diff --git a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature index d77a33b9b..2531f1c6f 100644 --- a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature +++ b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature @@ -11,20 +11,12 @@ Feature: PJS001 - Correct conversion based units IFC 2X3: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/ifcmeasureresource/lexical/ifcconversionbasedunit.htm Background: Selection of conversion-based units in default unit assignment - Given an .IfcProject. - Given its attribute .UnitsInContext. Given an .IfcConversionBasedUnit. - Scenario: Validating correct names for area, length, and unit - Given .UnitType. ^is^ 'AREAUNIT' or 'LENGTHUNIT' or 'VOLUMEUNIT' - Given its attribute .Name. + Scenario: Validating correct names for area, length, and volume units + Given .UnitType. ^is^ 'AREAUNIT' or 'LENGTHUNIT' or 'VOLUMEUNIT' or 'PLANEANGLEUNIT' + Then its attribute .Name. must be defined [according to the table] 'valid_ConversionBasedUnits' - Then the value must be in 'valid_ConversionBasedUnits.csv' - - """ Scenario: Validating correct conversion factors - Given its attribute .ConversionFactor. - - Then the factor must be in 'valid_ConversionBasedUnits.csv' - """ + Then its attribute .ConversionFactor. must be defined [according to the table] 'valid_ConversionBasedUnits' diff --git a/features/steps/validation_handling.py b/features/steps/validation_handling.py index 70fae0a57..8c962fa96 100644 --- a/features/steps/validation_handling.py +++ b/features/steps/validation_handling.py @@ -223,6 +223,9 @@ def apply_then_operation(fn, inst, context, current_path, depth=0, **kwargs): if 'npath' in inspect.getargs(fn.__code__).args: kwargs = kwargs | {'npath': current_path} top_level_index = current_path[0] if current_path else None + max_index = len(current_path) - 1 + if top_level_index > max_index: + top_level_index = max_index activation_inst = inst if not current_path or activation_instances[top_level_index] is None else activation_instances[top_level_index] # TODO: refactor into a more general solution that works for all rules if context.is_global_rule and ( From 2943fba5d7b37bc0532115b83b8a567de4c369eb Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Wed, 26 Nov 2025 14:32:11 -0500 Subject: [PATCH 06/14] updated step implementation so CI/CD passes (IVS-680) --- .../steps/steps/propertysets_qtys_units.py | 76 ++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/features/steps/steps/propertysets_qtys_units.py b/features/steps/steps/propertysets_qtys_units.py index 04c0d1f47..48cc54f94 100644 --- a/features/steps/steps/propertysets_qtys_units.py +++ b/features/steps/steps/propertysets_qtys_units.py @@ -1,12 +1,15 @@ +from dataclasses import dataclass import functools import itertools import json import operator import os +import math import re from typing import List import ifcopenshell +from ifcopenshell.util.unit import convert from validation_handling import gherkin_ifc from utils import ifc, misc, system from . import ValidationOutcome, OutcomeSeverity @@ -19,6 +22,35 @@ 'QTO_OCCURRENCEDRIVEN': 'IfcObject', } +@dataclass +class ConversionBasedUnitDefinition: + """ + used to hold data from table of conversion-based units defined in the IFC spec + Ref: IFC2X3 - https://standards.buildingsmart.org/IFC/RELEASE/IFC4/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcconversionbasedunit.htm + Ref: IFC4 - https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/ifcmeasureresource/lexical/ifcconversionbasedunit.htm + Ref: IFC4X3 - https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcConversionBasedUnit.htm + """ + Name: str + UnitType: str + ConversionFactor: str + SIUnitName: str + SIUnitPrefix: str = None + Description: str = None + + def __post_init__(self): + # capture π in table definitions + if "π" in self.ConversionFactor: + denom = self.ConversionFactor.split("/")[-1] + self.ConversionFactor = math.pi / float(denom) + if not isinstance(self.ConversionFactor, float): + self.ConversionFactor = float(self.ConversionFactor) + if self.SIUnitPrefix == "None": + self.SIUnitPrefix = None + if self.Description == "None": + self.Description = None + if self.Description and len(self.Description) == 0: + self.Description = None + @functools.cache def get_predefined_type(inst): @@ -62,9 +94,8 @@ def upper_case_if_string(v): except AttributeError: return v - @functools.cache -def get_pset_definitions(schema, table): +def get_table_definition(schema, table): schema_specific_path = system.get_abs_path(f"resources/{schema.upper()}/{table}.csv") if os.path.exists(schema_specific_path): @@ -73,8 +104,11 @@ def get_pset_definitions(schema, table): tbl_path = system.get_abs_path(f"resources/{table}.csv") tbl = system.get_csv(tbl_path, return_type='dict') - return {d['property_set_name']: d for d in tbl} - + if table == "valid_ConversionBasedUnits": + name_key = "Name" + else: + name_key = "property_set_name" + return {d[name_key]: d for d in tbl} class AlwaysEqualDict(dict): """ @@ -169,7 +203,7 @@ def normalize_pset(name: str) -> str: @gherkin_ifc.step( "The .{inst_type:property_set_or_element_quantity}. attribute .Name. must use standard values [according to the table] '{table}'") def step_impl(context, inst, table, inst_type=None): - property_set_definitions = get_pset_definitions(context.model.schema, table) + property_set_definitions = get_table_definition(context.model.schema, table) name = normalize_pset(getattr(inst, 'Name', 'Attribute not found')) if name not in property_set_definitions.keys(): @@ -179,7 +213,7 @@ def step_impl(context, inst, table, inst_type=None): @gherkin_ifc.step( "Each associated .{inst_type:property_or_physical_quantity}. must be named [according to the table] '{table}'") def step_impl(context, inst, table, inst_type=None): - property_set_definitions = get_pset_definitions(context.model.schema, table) + property_set_definitions = get_table_definition(context.model.schema, table) name = normalize_pset(getattr(inst, 'Name', 'Attribute not found')) accepted_values = establish_accepted_pset_values(name, context.model.schema, table, @@ -287,7 +321,7 @@ def schema_has_declaration_name(s): @gherkin_ifc.step( "The .{inst_type:property_set_or_element_quantity}. must be related to a valid entity type [according to the table] '{table}'") def step_impl(context, inst, table, inst_type=None): - property_set_definitions = get_pset_definitions(context.model.schema, table) + property_set_definitions = get_table_definition(context.model.schema, table) name = normalize_pset(getattr(inst, 'Name', 'Attribute not found')) accepted_values = establish_accepted_pset_values(name, context.model.schema, table, @@ -306,7 +340,7 @@ def step_impl(context, inst, table, inst_type=None): @gherkin_ifc.step( "Each associated .{inst_type:property_or_physical_quantity}. must be of valid entity type [according to the table] '{table}'") def step_impl(context, inst, table, inst_type=None): - property_set_definitions = get_pset_definitions(context.model.schema, table) + property_set_definitions = get_table_definition(context.model.schema, table) name = normalize_pset(getattr(inst, 'Name', 'Attribute not found')) accepted_values = establish_accepted_pset_values(name, context.model.schema, table, @@ -334,7 +368,7 @@ def step_impl(context, inst, table, inst_type=None): @gherkin_ifc.step( "Each associated .{inst_type:property_or_physical_quantity}. value must be of valid data type [according to the table] '{table}'") def step_impl(context, inst, table, inst_type=None): - property_set_definitions = get_pset_definitions(context.model.schema, table) + property_set_definitions = get_table_definition(context.model.schema, table) name = normalize_pset(getattr(inst, 'Name', 'Attribute not found')) accepted_values = establish_accepted_pset_values(name, context.model.schema, table, @@ -375,3 +409,27 @@ def step_impl(context, inst, table, inst_type=None): # not a universal error. This is more IDS territory. # if not values: # yield ValidationOutcome(inst=inst, expected= {"oneOf": accepted_data_type['instance']}, observed = {'value':None}, severity=OutcomeSeverity.ERROR) + +@gherkin_ifc.step( + "Its attribute .{attr_name}. must be defined [according to the table] 'valid_ConversionBasedUnits'" +) +def step_impl(context, inst, attr_name): + table = "valid_ConversionBasedUnits" + unit_definitions = get_table_definition(context.model.schema, table) + accepted_names = list(unit_definitions.keys()) + match attr_name.upper(): + case "NAME": + attr_value = getattr(inst, attr_name) + if attr_value not in accepted_names: + yield ValidationOutcome(inst=inst, expected=accepted_names, observed=attr_value, severity=OutcomeSeverity.ERROR) + case "CONVERSIONFACTOR": + unit_name = inst.Name + if unit_name in accepted_names: + inst_factor = inst.ConversionFactor.ValueComponent.wrappedValue + inst_si_unit = inst.ConversionFactor.UnitComponent + conv_unit_def = ConversionBasedUnitDefinition(**unit_definitions[unit_name]) + expected_factor = ifcopenshell.util.unit.convert(value=conv_unit_def.ConversionFactor,from_unit=conv_unit_def.SIUnitName,from_prefix=conv_unit_def.SIUnitPrefix,to_unit=inst_si_unit.Name,to_prefix=inst_si_unit.Prefix) + if not math.isclose(a=inst_factor, b=expected_factor, rel_tol=1e-06, abs_tol=1e-06): + yield ValidationOutcome(inst=inst, expected=expected_factor, observed=inst_factor, severity=OutcomeSeverity.ERROR) + else: + print(f"{unit_name=} not found in table") From 3bd7d17280bd3acd318b85b6d02ec43d4d832dcd Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Wed, 26 Nov 2025 14:45:40 -0500 Subject: [PATCH 07/14] scenario 2 unit test files (IVS-680) --- ...js001-scenario01_us_survey_inch_ifc4x3.ifc | 25 +++++++++++++ .../fail-pjs001-scenario02_degree_ifc4.ifc | 25 +++++++++++++ ...l-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc | 25 +++++++++++++ .../fail-pjs001-scenario02_ft_ifc2x3.ifc | 29 +++++++++++++++ .../fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc | 37 +++++++++++++++++++ ...js001-scenario02_us_survey_foot_ifc4x3.ifc | 25 +++++++++++++ 6 files changed, 166 insertions(+) create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc new file mode 100644 index 000000000..feb79a22c --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* US survey foot definition */ +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(25.4),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'US survey inch',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc new file mode 100644 index 000000000..4c1e97559 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); +FILE_NAME('fail-pjs001-scenario02_degree_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',$,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); + +/* degree definition */ +#16=IFCDIMENSIONALEXPONENTS(0,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.010101),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.PLANEANGLEUNIT.,'degree',#18); + +/* unit assignment */ +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc new file mode 100644 index 000000000..627e0809b --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* volume unit definition */ +#16=IFCDIMENSIONALEXPONENTS(3,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(28.1337),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.VOLUMEUNIT.,'fluid ounce UK',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc new file mode 100644 index 000000000..dd3cbab3a --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc @@ -0,0 +1,29 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]'),'2;1'); +FILE_NAME('fail-pjs001-scenario02_ft_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC2X3')); +ENDSEC; +DATA; +#1=IFCACTORROLE(.USERDEFINED.,'CONTRIBUTOR',$); +#2=IFCTELECOMADDRESS(.USERDEFINED.,$,'WEBPAGE',$,$,$,$,'https://ifcopenshell.org'); +#3=IFCORGANIZATION('IfcOpenShell','IfcOpenShell','IfcOpenShell is an open source software library that helps users and software developers to work with IFC data.',(#1),(#2)); +#4=IFCAPPLICATION(#3,'0.0.0','IfcOpenShell','IfcOpenShell'); +#5=IFCPERSON('FW','Whalbanger','Frank',$,$,$,$,$); +#6=IFCORGANIZATION('SBB','Soggy Bottom Boys',$,$,$); +#7=IFCPERSONANDORGANIZATION(#5,#6,$); +#8=IFCOWNERHISTORY(#7,#4,.READWRITE.,.ADDED.,1763516380,#7,#4,1763516380); +#9=IFCPROJECT('3qYCxaONHFGe86oex3KHpw',#8,'PJS001 Unit Test',$,$,$,$,(#14),#28); +#10=IFCCARTESIANPOINT((0.,0.,0.)); +#11=IFCDIRECTION((0.,0.,1.)); +#12=IFCDIRECTION((1.,0.,0.)); +#13=IFCAXIS2PLACEMENT3D(#10,#11,#12); +#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#13,$); +#15=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#14,$,.MODEL_VIEW.,$); +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.305),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'foot',#18); +#28=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc new file mode 100644 index 000000000..27a9d4953 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc @@ -0,0 +1,37 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); +FILE_NAME('fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4')); +ENDSEC; +DATA; +#1=IFCPROJECT('1jjdBaLuz2vQsw16Lk$9EB',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* foot definition */ +#8=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#9=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#10=IFCMEASUREWITHUNIT(IFCREAL(1.0),#9); +#11=IFCCONVERSIONBASEDUNIT(#8,.LENGTHUNIT.,'foot',#10); + +/* square yard definition */ +#12=IFCDIMENSIONALEXPONENTS(2,0,0,0,0,0,0); +#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#14=IFCMEASUREWITHUNIT(IFCREAL(0.83612736),#13); +#15=IFCCONVERSIONBASEDUNIT(#12,.AREAUNIT.,'square yard',#14); + +/* cubic yard definition */ +#16=IFCDIMENSIONALEXPONENTS(3,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(0.7636),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.VOLUMEUNIT.,'cubic yard',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19,#11,#15)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc new file mode 100644 index 000000000..865c24da0 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* US survey foot definition */ +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(304.8),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'US survey foot',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21; From f55574e95453a309c0b3755ece20a25cb9993372 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Fri, 28 Nov 2025 22:28:12 -0500 Subject: [PATCH 08/14] Clarify scope of activation (entire model, not just context assignment) Co-authored-by: Thomas Krijnen --- .../rules/PJS/PJS001_Correct-conversion-based-units.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature index 2531f1c6f..85b0d0871 100644 --- a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature +++ b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature @@ -10,7 +10,7 @@ Feature: PJS001 - Correct conversion based units IFC 4: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcconversionbasedunit.htm IFC 2X3: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/ifcmeasureresource/lexical/ifcconversionbasedunit.htm - Background: Selection of conversion-based units in default unit assignment + Background: Selection of conversion-based units from model Given an .IfcConversionBasedUnit. Scenario: Validating correct names for area, length, and volume units From e6924de5fa03f505aa74c84467ba5655cf68303e Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Fri, 28 Nov 2025 22:32:35 -0500 Subject: [PATCH 09/14] Update features/steps/steps/propertysets_qtys_units.py Co-authored-by: Thomas Krijnen --- features/steps/steps/propertysets_qtys_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/steps/propertysets_qtys_units.py b/features/steps/steps/propertysets_qtys_units.py index 48cc54f94..f3a762350 100644 --- a/features/steps/steps/propertysets_qtys_units.py +++ b/features/steps/steps/propertysets_qtys_units.py @@ -22,7 +22,7 @@ 'QTO_OCCURRENCEDRIVEN': 'IfcObject', } -@dataclass +@dataclass(frozen=True,slots=True,kw_only=True,repr=True,eq=True) class ConversionBasedUnitDefinition: """ used to hold data from table of conversion-based units defined in the IFC spec From 89389c183cd69e9c6ffc51bdce0afeac60c3280b Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Fri, 28 Nov 2025 22:34:54 -0500 Subject: [PATCH 10/14] Update features/steps/steps/propertysets_qtys_units.py Co-authored-by: Thomas Krijnen --- features/steps/steps/propertysets_qtys_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/steps/propertysets_qtys_units.py b/features/steps/steps/propertysets_qtys_units.py index f3a762350..3ddcc254c 100644 --- a/features/steps/steps/propertysets_qtys_units.py +++ b/features/steps/steps/propertysets_qtys_units.py @@ -429,7 +429,7 @@ def step_impl(context, inst, attr_name): inst_si_unit = inst.ConversionFactor.UnitComponent conv_unit_def = ConversionBasedUnitDefinition(**unit_definitions[unit_name]) expected_factor = ifcopenshell.util.unit.convert(value=conv_unit_def.ConversionFactor,from_unit=conv_unit_def.SIUnitName,from_prefix=conv_unit_def.SIUnitPrefix,to_unit=inst_si_unit.Name,to_prefix=inst_si_unit.Prefix) - if not math.isclose(a=inst_factor, b=expected_factor, rel_tol=1e-06, abs_tol=1e-06): + if not math.isclose(a=inst_factor, b=expected_factor, rel_tol=1e-06, abs_tol=0.): yield ValidationOutcome(inst=inst, expected=expected_factor, observed=inst_factor, severity=OutcomeSeverity.ERROR) else: print(f"{unit_name=} not found in table") From bb1f0af99aa11d6ffe62b18a7c8d238b09616114 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Sat, 29 Nov 2025 14:06:40 -0500 Subject: [PATCH 11/14] header -> "Name" for consistency with unit definitions (IVS-680) --- features/resources/IFC2X3/pset_definitions.csv | 2 +- features/resources/IFC4/pset_definitions.csv | 2 +- features/resources/IFC4/qto_definitions.csv | 2 +- features/resources/IFC4X3/pset_definitions.csv | 2 +- features/resources/IFC4X3/qto_definitions.csv | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/resources/IFC2X3/pset_definitions.csv b/features/resources/IFC2X3/pset_definitions.csv index 184e1f937..d59b7401f 100644 --- a/features/resources/IFC2X3/pset_definitions.csv +++ b/features/resources/IFC2X3/pset_definitions.csv @@ -1,4 +1,4 @@ -property_set_name,applicable_entities,applicable_type_value,property_definitions +Name,applicable_entities,applicable_type_value,property_definitions Pset_ActionRequest,['IfcActionRequest'],,"[{'property_name': 'RequestSourceType', 'property_type': 'IfcPropertyEnumeratedValue', 'data_type': {'instance': 'PEnum_RequestSourceType', 'values': ['Email', 'Fax', 'Phone', 'Post', 'Verbal', 'Other', 'NotKnown', 'Unset']}}, {'property_name': 'RequestSourceLabel', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}, {'property_name': 'RequestSourceName', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcPerson'}}, {'property_name': 'RequestDescription', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcText', 'unit_type': 'None'}}, {'property_name': 'RequestComments', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcText', 'unit_type': 'None'}}, {'property_name': 'Status', 'property_type': 'IfcPropertyEnumeratedValue', 'data_type': {'instance': 'PEnum_RequestStatus', 'values': ['Hold', 'NoAction', 'Schedule', 'Urgent', 'Other', 'NotKnown', 'Unset']}}]" Pset_ActorCommon,['IfcActor'],,"[{'property_name': 'NumberOfActors', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcCountMeasure', 'unit_type': 'None'}}, {'property_name': 'Category', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}, {'property_name': 'SkillLevel', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}]" Pset_ActuatorTypeCommon,['IfcActuatorType'],,"[{'property_name': 'FailPosition', 'property_type': 'IfcPropertyEnumeratedValue', 'data_type': {'instance': 'PEnum_FailPosition', 'values': ['FailOpen', 'FailClosed', 'NotKnown', 'Unset']}}, {'property_name': 'ManualOverride', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcBoolean', 'unit_type': 'None'}}]" diff --git a/features/resources/IFC4/pset_definitions.csv b/features/resources/IFC4/pset_definitions.csv index 42f300825..a6b4e3067 100644 --- a/features/resources/IFC4/pset_definitions.csv +++ b/features/resources/IFC4/pset_definitions.csv @@ -1,4 +1,4 @@ -property_set_name,template_type,applicable_entities,applicable_type_value,property_definitions +Name,template_type,applicable_entities,applicable_type_value,property_definitions Pset_ActionRequest,PSET_TYPEDRIVENOVERRIDE,['IfcActionRequest'],IfcActionRequest,"[{'property_name': 'RequestSourceLabel', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}, {'property_name': 'RequestSourceName', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcPerson'}}, {'property_name': 'RequestComments', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcText', 'unit_type': 'None'}}]" Pset_ActorCommon,PSET_TYPEDRIVENOVERRIDE,['IfcActor'],IfcActor,"[{'property_name': 'NumberOfActors', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcCountMeasure', 'unit_type': 'None'}}, {'property_name': 'Category', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}, {'property_name': 'SkillLevel', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}]" Pset_ActuatorPHistory,PSET_PERFORMANCEDRIVEN,['IfcActuator'],IfcActuator,"[{'property_name': 'Position', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcTimeSeries'}}, {'property_name': 'Quality', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcTimeSeries'}}, {'property_name': 'Status', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcTimeSeries'}}]" diff --git a/features/resources/IFC4/qto_definitions.csv b/features/resources/IFC4/qto_definitions.csv index 5f22da717..38985d4bb 100644 --- a/features/resources/IFC4/qto_definitions.csv +++ b/features/resources/IFC4/qto_definitions.csv @@ -1,4 +1,4 @@ -property_set_name,template_type,applicable_entities,applicable_type_value,property_definitions +Name,template_type,applicable_entities,applicable_type_value,property_definitions Qto_BuildingBaseQuantities,QTO_OCCURRENCEDRIVEN,['IfcBuilding'],IfcBuilding,"[{'property_name': 'Height', 'property_type': 'IfcQuantityLength'}, {'property_name': 'EavesHeight', 'property_type': 'IfcQuantityLength'}, {'property_name': 'FootprintArea', 'property_type': 'IfcQuantityArea'}, {'property_name': 'GrossFloorArea', 'property_type': 'IfcQuantityArea'}, {'property_name': 'NetFloorArea', 'property_type': 'IfcQuantityArea'}, {'property_name': 'GrossVolume', 'property_type': 'IfcQuantityVolume'}, {'property_name': 'NetVolume', 'property_type': 'IfcQuantityVolume'}]" Qto_BuildingStoreyBaseQuantities,QTO_OCCURRENCEDRIVEN,['IfcBuildingStorey'],IfcBuildingStorey,"[{'property_name': 'GrossHeight', 'property_type': 'IfcQuantityLength'}, {'property_name': 'NetHeigtht', 'property_type': 'IfcQuantityLength'}, {'property_name': 'GrossPerimeter', 'property_type': 'IfcQuantityLength'}, {'property_name': 'GrossFloorArea', 'property_type': 'IfcQuantityArea'}, {'property_name': 'NetFloorArea', 'property_type': 'IfcQuantityArea'}, {'property_name': 'GrossVolume', 'property_type': 'IfcQuantityVolume'}, {'property_name': 'NetVolume', 'property_type': 'IfcQuantityVolume'}]" Qto_OpeningElementBaseQuantities,QTO_OCCURRENCEDRIVEN,['IfcOpeningElement'],IfcOpeningElement,"[{'property_name': 'Width', 'property_type': 'IfcQuantityLength'}, {'property_name': 'Height', 'property_type': 'IfcQuantityLength'}, {'property_name': 'Depth', 'property_type': 'IfcQuantityLength'}, {'property_name': 'Area', 'property_type': 'IfcQuantityArea'}, {'property_name': 'Volume', 'property_type': 'IfcQuantityVolume'}]" diff --git a/features/resources/IFC4X3/pset_definitions.csv b/features/resources/IFC4X3/pset_definitions.csv index f4d31b416..b186dcc7f 100644 --- a/features/resources/IFC4X3/pset_definitions.csv +++ b/features/resources/IFC4X3/pset_definitions.csv @@ -1,4 +1,4 @@ -property_set_name,template_type,applicable_entities,applicable_type_value,property_definitions +Name,template_type,applicable_entities,applicable_type_value,property_definitions Pset_ActionRequest,PSET_OCCURRENCEDRIVEN,['IfcActionRequest'],IfcActionRequest,"[{'property_name': 'RequestSourceLabel', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}, {'property_name': 'RequestSourceName', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcPerson'}}, {'property_name': 'RequestComments', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcText', 'unit_type': 'None'}}]" Pset_ActorCommon,PSET_OCCURRENCEDRIVEN,['IfcActor'],IfcActor,"[{'property_name': 'NumberOfActors', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcCountMeasure', 'unit_type': 'None'}}, {'property_name': 'ActorCategory', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}, {'property_name': 'SkillLevel', 'property_type': 'IfcPropertySingleValue', 'data_type': {'instance': 'IfcLabel', 'unit_type': 'None'}}]" Pset_ActuatorPHistory,PSET_PERFORMANCEDRIVEN,['IfcActuator'],IfcActuator,"[{'property_name': 'PositionHistory', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcTimeSeries'}}, {'property_name': 'QualityHistory', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcTimeSeries'}}, {'property_name': 'StatusHistory', 'property_type': 'IfcPropertyReferenceValue', 'data_type': {'instance': 'IfcTimeSeries'}}]" diff --git a/features/resources/IFC4X3/qto_definitions.csv b/features/resources/IFC4X3/qto_definitions.csv index 55c1ae530..2cc2bdfd5 100644 --- a/features/resources/IFC4X3/qto_definitions.csv +++ b/features/resources/IFC4X3/qto_definitions.csv @@ -1,4 +1,4 @@ -property_set_name,template_type,applicable_entities,applicable_type_value,property_definitions +Name,template_type,applicable_entities,applicable_type_value,property_definitions Qto_ActuatorBaseQuantities,QTO_TYPEDRIVENOVERRIDE,"['IfcActuator', 'IfcActuatorType']","IfcActuator,IfcActuatorType","[{'property_name': 'GrossWeight', 'property_type': 'IfcQuantityWeight'}]" Qto_AirTerminalBaseQuantities,QTO_TYPEDRIVENOVERRIDE,"['IfcAirTerminal', 'IfcAirTerminalType']","IfcAirTerminal,IfcAirTerminalType","[{'property_name': 'GrossWeight', 'property_type': 'IfcQuantityWeight'}, {'property_name': 'Perimeter', 'property_type': 'IfcQuantityLength'}, {'property_name': 'TotalSurfaceArea', 'property_type': 'IfcQuantityArea'}]" Qto_AirTerminalBoxTypeBaseQuantities,QTO_TYPEDRIVENOVERRIDE,"['IfcAirTerminalBox', 'IfcAirTerminalBoxType']","IfcAirTerminalBox,IfcAirTerminalBoxType","[{'property_name': 'GrossWeight', 'property_type': 'IfcQuantityWeight'}]" From b433f72d42b8fe4dc2c4fb71a823662d057c26c9 Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Sat, 29 Nov 2025 14:18:52 -0500 Subject: [PATCH 12/14] clean up file names in test suite (IVS-680) --- ...4x3.ifc => fail-pjs001-scenario01-us_survey_inch_ifc4x3.ifc} | 2 +- ...2_degree_ifc4.ifc => fail-pjs001-scenario02-degree_ifc4.ifc} | 2 +- ...ifc4x3.ifc => fail-pjs001-scenario02-fluid_oz_uk_ifc4x3.ifc} | 2 +- ...rio02_ft_ifc2x3.ifc => fail-pjs001-scenario02-ft_ifc2x3.ifc} | 2 +- ...y_cyd_ifc4.ifc => fail-pjs001-scenario02-ft_sy_cyd_ifc4.ifc} | 2 +- ...4x3.ifc => fail-pjs001-scenario02-us_survey_foot_ifc4x3.ifc} | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename test/files/PJS/pjs001/{fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc => fail-pjs001-scenario01-us_survey_inch_ifc4x3.ifc} (93%) rename test/files/PJS/pjs001/{fail-pjs001-scenario02_degree_ifc4.ifc => fail-pjs001-scenario02-degree_ifc4.ifc} (93%) rename test/files/PJS/pjs001/{fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc => fail-pjs001-scenario02-fluid_oz_uk_ifc4x3.ifc} (93%) rename test/files/PJS/pjs001/{fail-pjs001-scenario02_ft_ifc2x3.ifc => fail-pjs001-scenario02-ft_ifc2x3.ifc} (95%) rename test/files/PJS/pjs001/{fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc => fail-pjs001-scenario02-ft_sy_cyd_ifc4.ifc} (95%) rename test/files/PJS/pjs001/{fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc => fail-pjs001-scenario02-us_survey_foot_ifc4x3.ifc} (93%) diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario01-us_survey_inch_ifc4x3.ifc similarity index 93% rename from test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc rename to test/files/PJS/pjs001/fail-pjs001-scenario01-us_survey_inch_ifc4x3.ifc index feb79a22c..436d4787d 100644 --- a/test/files/PJS/pjs001/fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc +++ b/test/files/PJS/pjs001/fail-pjs001-scenario01-us_survey_inch_ifc4x3.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); -FILE_NAME('fail-pjs001-scenario01_us_survey_inch_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_NAME('fail-pjs001-scenario01-us_survey_inch_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); FILE_SCHEMA(('IFC4X3_ADD2')); ENDSEC; DATA; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02-degree_ifc4.ifc similarity index 93% rename from test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc rename to test/files/PJS/pjs001/fail-pjs001-scenario02-degree_ifc4.ifc index 4c1e97559..216a9fb44 100644 --- a/test/files/PJS/pjs001/fail-pjs001-scenario02_degree_ifc4.ifc +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02-degree_ifc4.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); -FILE_NAME('fail-pjs001-scenario02_degree_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_NAME('fail-pjs001-scenario02-degree_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); FILE_SCHEMA(('IFC4')); ENDSEC; DATA; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02-fluid_oz_uk_ifc4x3.ifc similarity index 93% rename from test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc rename to test/files/PJS/pjs001/fail-pjs001-scenario02-fluid_oz_uk_ifc4x3.ifc index 627e0809b..ada5fcee9 100644 --- a/test/files/PJS/pjs001/fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02-fluid_oz_uk_ifc4x3.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); -FILE_NAME('fail-pjs001-scenario02_fluid_oz_uk_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_NAME('fail-pjs001-scenario02-fluid_oz_uk_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); FILE_SCHEMA(('IFC4X3_ADD2')); ENDSEC; DATA; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02-ft_ifc2x3.ifc similarity index 95% rename from test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc rename to test/files/PJS/pjs001/fail-pjs001-scenario02-ft_ifc2x3.ifc index dd3cbab3a..a0eddc422 100644 --- a/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_ifc2x3.ifc +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02-ft_ifc2x3.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [CoordinationView_V2.0]'),'2;1'); -FILE_NAME('fail-pjs001-scenario02_ft_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_NAME('fail-pjs001-scenario02-ft_ifc2x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); FILE_SCHEMA(('IFC2X3')); ENDSEC; DATA; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02-ft_sy_cyd_ifc4.ifc similarity index 95% rename from test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc rename to test/files/PJS/pjs001/fail-pjs001-scenario02-ft_sy_cyd_ifc4.ifc index 27a9d4953..c608f6d35 100644 --- a/test/files/PJS/pjs001/fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02-ft_sy_cyd_ifc4.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [ReferenceView_V1.2]'),'2;1'); -FILE_NAME('fail-pjs001-scenario02_ft_sy_cyd_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_NAME('fail-pjs001-scenario02-ft_sy_cyd_ifc4.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); FILE_SCHEMA(('IFC4')); ENDSEC; DATA; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario02-us_survey_foot_ifc4x3.ifc similarity index 93% rename from test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc rename to test/files/PJS/pjs001/fail-pjs001-scenario02-us_survey_foot_ifc4x3.ifc index 865c24da0..13c9101af 100644 --- a/test/files/PJS/pjs001/fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc +++ b/test/files/PJS/pjs001/fail-pjs001-scenario02-us_survey_foot_ifc4x3.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); -FILE_NAME('fail-pjs001-scenario02_us_survey_foot_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_NAME('fail-pjs001-scenario02-us_survey_foot_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); FILE_SCHEMA(('IFC4X3_ADD2')); ENDSEC; DATA; From d0f5ef181290c10217c8684282c19fdf81743a9e Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Sat, 29 Nov 2025 14:49:18 -0500 Subject: [PATCH 13/14] address review comments (IVS-680) --- .../IFC2X3/valid_ConversionBasedUnits.csv | 16 ++--- .../IFC4/valid_ConversionBasedUnits.csv | 52 +++++++------- .../IFC4X3/valid_ConversionBasedUnits.csv | 52 +++++++------- ...001_Correct-conversion-based-units.feature | 7 +- .../steps/steps/propertysets_qtys_units.py | 69 ++++++++++++------- 5 files changed, 109 insertions(+), 87 deletions(-) diff --git a/features/resources/IFC2X3/valid_ConversionBasedUnits.csv b/features/resources/IFC2X3/valid_ConversionBasedUnits.csv index 543504d79..c1f6a545c 100644 --- a/features/resources/IFC2X3/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC2X3/valid_ConversionBasedUnits.csv @@ -2,12 +2,12 @@ Name,UnitType,ConversionFactor,SIUnitPrefix,SIUnitName,Description inch,LENGTHUNIT,25.4,MILLI,METRE foot,LENGTHUNIT,304.8,MILLI,METRE yard,LENGTHUNIT,914.,MILLI,METRE -mile,LENGTHUNIT,1609.,None,METRE -acre,AREAUNIT,4046.86,None,METRE -litre,VOLUMEUNIT,0.001,None,METRE -pint UK,VOLUMEUNIT,0.000568,None,METRE -pint US,VOLUMEUNIT,0.000473,None,METRE -gallon UK,VOLUMEUNIT,0.004546,None,METRE -gallon US,VOLUMEUNIT,0.003785,None,METRE -ounce,MASSUNIT,28.35,None,GRAM +mile,LENGTHUNIT,1609.,,METRE +acre,AREAUNIT,4046.86,,SQUARE_METRE +litre,VOLUMEUNIT,0.001,,CUBIC_METRE +pint UK,VOLUMEUNIT,0.000568,,CUBIC_METRE +pint US,VOLUMEUNIT,0.000473,,CUBIC_METRE +gallon UK,VOLUMEUNIT,0.00454,,CUBIC_METRE +gallon US,VOLUMEUNIT,0.003785,,CUBIC_METRE +ounce,MASSUNIT,28.35,,GRAM pound,MASSUNIT,0.454,KILO,GRAM \ No newline at end of file diff --git a/features/resources/IFC4/valid_ConversionBasedUnits.csv b/features/resources/IFC4/valid_ConversionBasedUnits.csv index 3773500bd..94e64bd73 100644 --- a/features/resources/IFC4/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC4/valid_ConversionBasedUnits.csv @@ -2,32 +2,32 @@ Name,UnitType,ConversionFactor,SIUnitPrefix,SIUnitName,Description inch,LENGTHUNIT,25.4,MILLI,METRE foot,LENGTHUNIT,304.8,MILLI,METRE yard,LENGTHUNIT,914.,MILLI,METRE -mile,LENGTHUNIT,1609.,None,METRE -square inch,AREAUNIT,0.0006452,None,METRE -square foot,AREAUNIT,0.09290,None,METRE -square yard,AREAUNIT,0.83612736,None,METRE -acre,AREAUNIT,4046.86,None,METRE -square mile,AREAUNIT,2588881.,None,METRE -cubic inch,VOLUMEUNIT,0.00001639,None,METRE -cubic foot,VOLUMEUNIT,0.02832,None,METRE -cubic yard,VOLUMEUNIT,0.7636,None,METRE -litre,VOLUMEUNIT,0.001,None,METRE -fluid ounce UK,VOLUMEUNIT,0.0000284130625,None,METRE -fluid ounce US,VOLUMEUNIT,0.00002957353,None,METRE -pint UK,VOLUMEUNIT,0.000568,None,METRE -pint US,VOLUMEUNIT,0.000473,None,METRE -gallon UK,VOLUMEUNIT,0.004546,None,METRE -gallon US,VOLUMEUNIT,0.003785,None,METRE -degree,PLANEANGLEUNIT,π/180,None,RADIAN -ounce,MASSUNIT,28.35,None,GRAM +mile,LENGTHUNIT,1609.,,METRE +square inch,AREAUNIT,0.0006452,,SQUARE_METRE +square foot,AREAUNIT,0.09290,,SQUARE_METRE +square yard,AREAUNIT,0.83612736,,SQUARE_METRE +acre,AREAUNIT,4046.86,,SQUARE_METRE +square mile,AREAUNIT,2588881.,,SQUARE_METRE +cubic inch,VOLUMEUNIT,0.00001639,,CUBIC_METRE +cubic foot,VOLUMEUNIT,0.02832,,CUBIC_METRE +cubic yard,VOLUMEUNIT,0.7636,,CUBIC_METRE +litre,VOLUMEUNIT,0.001,,CUBIC_METRE +fluid ounce UK,VOLUMEUNIT,0.0000284130625,,CUBIC_METRE +fluid ounce US,VOLUMEUNIT,0.00002957353,,CUBIC_METRE +pint UK,VOLUMEUNIT,0.000568,,CUBIC_METRE +pint US,VOLUMEUNIT,0.000473,,CUBIC_METRE +gallon UK,VOLUMEUNIT,0.004546,,CUBIC_METRE +gallon US,VOLUMEUNIT,0.003785,,CUBIC_METRE +degree,PLANEANGLEUNIT,0.017453292519943295,,RADIAN +ounce,MASSUNIT,28.35,,GRAM pound,MASSUNIT,0.454,KILO,GRAM ton UK,MASSUNIT,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton ton US,MASSUNIT,907.18474,KILO,GRAM,also known as short ton or net ton -lbf,FORCEUNIT,4.4482216153,None,NEWTON,also known as pound-force -kip,FORCEUNIT,4448.2216153,None,NEWTON,also known as kilopound-force -psi,PRESSUREUNIT,6894.7572932,None,PASCAL,also known as pound-force per square inch -ksi,PRESSUREUNIT,6894757.2932,None,PASCAL,also known as kilopound-force per square inch -minute,TIMEUNIT,60.,None,SECOND -hour,TIMEUNIT,3600.,None,SECOND -day,TIMEUNIT,86400.,None,SECOND -btu,ENERGYUNIT,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file +lbf,FORCEUNIT,4.4482216153,,NEWTON,also known as pound-force +kip,FORCEUNIT,4448.2216153,,NEWTON,also known as kilopound-force +psi,PRESSUREUNIT,6894.7572932,,PASCAL,also known as pound-force per square inch +ksi,PRESSUREUNIT,6894757.2932,,PASCAL,also known as kilopound-force per square inch +minute,TIMEUNIT,60.,,SECOND +hour,TIMEUNIT,3600.,,SECOND +day,TIMEUNIT,86400.,,SECOND +btu,ENERGYUNIT,1055.056,,JOULE, also known as British Thermal Unit \ No newline at end of file diff --git a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv index 133935af8..9a678676d 100644 --- a/features/resources/IFC4X3/valid_ConversionBasedUnits.csv +++ b/features/resources/IFC4X3/valid_ConversionBasedUnits.csv @@ -3,32 +3,32 @@ inch,LENGTHUNIT,25.4,MILLI,METRE foot,LENGTHUNIT,304.8,MILLI,METRE US survey foot,LENGTHUNIT,304.80060960122,MILLI,METRE,the approximate value of 1200/3937 meters yard,LENGTHUNIT,914.,MILLI,METRE -mile,LENGTHUNIT,1609.,None,METRE -square inch,AREAUNIT,0.0006452,None,METRE -square foot,AREAUNIT,0.09290,None,METRE -square yard,AREAUNIT,0.83612736,None,METRE -acre,AREAUNIT,4046.86,None,METRE -square mile,AREAUNIT,2588881.,None,METRE -cubic inch,VOLUMEUNIT,0.00001639,None,METRE -cubic foot,VOLUMEUNIT,0.02832,None,METRE -cubic yard,VOLUMEUNIT,0.7636,None,METRE -litre,VOLUMEUNIT,0.001,None,METRE -fluid ounce UK,VOLUMEUNIT,0.0000284130625,None,METRE -fluid ounce US,VOLUMEUNIT,0.00002957353,None,METRE -pint UK,VOLUMEUNIT,0.000568,None,METRE -pint US,VOLUMEUNIT,0.000473,None,METRE -gallon UK,VOLUMEUNIT,0.004546,None,METRE -gallon US,VOLUMEUNIT,0.003785,None,METRE -degree,PLANEANGLEUNIT,π/180,None,RADIAN -ounce,MASSUNIT,28.35,None,GRAM +mile,LENGTHUNIT,1609.,,METRE +square inch,AREAUNIT,0.0006452,,SQUARE_METRE +square foot,AREAUNIT,0.09290,,SQUARE_METRE +square yard,AREAUNIT,0.83612736,,SQUARE_METRE +acre,AREAUNIT,4046.86,,SQUARE_METRE +square mile,AREAUNIT,2588881.,,SQUARE_METRE +cubic inch,VOLUMEUNIT,0.00001639,,CUBIC_METRE +cubic foot,VOLUMEUNIT,0.02832,,CUBIC_METRE +cubic yard,VOLUMEUNIT,0.7636,,CUBIC_METRE +litre,VOLUMEUNIT,0.001,,CUBIC_METRE +fluid ounce UK,VOLUMEUNIT,0.0000284130625,,CUBIC_METRE +fluid ounce US,VOLUMEUNIT,0.00002957353,,CUBIC_METRE +pint UK,VOLUMEUNIT,0.000568,,CUBIC_METRE +pint US,VOLUMEUNIT,0.000473,,CUBIC_METRE +gallon UK,VOLUMEUNIT,0.004546,,CUBIC_METRE +gallon US,VOLUMEUNIT,0.003785,,CUBIC_METRE +degree,PLANEANGLEUNIT,0.017453292519943295,,RADIAN +ounce,MASSUNIT,28.35,,GRAM pound,MASSUNIT,0.454,KILO,GRAM ton UK,MASSUNIT,1016.0469088,KILO,GRAM,also known as long ton or gross ton or shipper's ton ton US,MASSUNIT,907.18474,KILO,GRAM,also known as short ton or net ton -lbf,FORCEUNIT,4.4482216153,None,NEWTON,also known as pound-force -kip,FORCEUNIT,4448.2216153,None,NEWTON,also known as kilopound-force -psi,PRESSUREUNIT,6894.7572932,None,PASCAL,also known as pound-force per square inch -ksi,PRESSUREUNIT,6894757.2932,None,PASCAL,also known as kilopound-force per square inch -minute,TIMEUNIT,60.,None,SECOND -hour,TIMEUNIT,3600.,None,SECOND -day,TIMEUNIT,86400.,None,SECOND -btu,ENERGYUNIT,1055.056,None,JOULE, also known as British Thermal Unit \ No newline at end of file +lbf,FORCEUNIT,4.4482216153,,NEWTON,also known as pound-force +kip,FORCEUNIT,4448.2216153,,NEWTON,also known as kilopound-force +psi,PRESSUREUNIT,6894.7572932,,PASCAL,also known as pound-force per square inch +ksi,PRESSUREUNIT,6894757.2932,,PASCAL,also known as kilopound-force per square inch +minute,TIMEUNIT,60.,,SECOND +hour,TIMEUNIT,3600.,,SECOND +day,TIMEUNIT,86400.,,SECOND +btu,ENERGYUNIT,1055.056,,JOULE, also known as British Thermal Unit \ No newline at end of file diff --git a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature index 85b0d0871..48229f920 100644 --- a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature +++ b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature @@ -10,8 +10,11 @@ Feature: PJS001 - Correct conversion based units IFC 4: https://standards.buildingsmart.org/IFC/RELEASE/IFC4/FINAL/HTML/schema/ifcmeasureresource/lexical/ifcconversionbasedunit.htm IFC 2X3: https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/FINAL/HTML/ifcmeasureresource/lexical/ifcconversionbasedunit.htm - Background: Selection of conversion-based units from model - Given an .IfcConversionBasedUnit. + Background: Selection of conversion-based units in default unit assignment + Given an .IfcProject. + Given its attribute .UnitsInContext. + Given its attribute .Units. + Given [its entity type] ^is^ 'IfcConversionBasedUnit' Scenario: Validating correct names for area, length, and volume units Given .UnitType. ^is^ 'AREAUNIT' or 'LENGTHUNIT' or 'VOLUMEUNIT' or 'PLANEANGLEUNIT' diff --git a/features/steps/steps/propertysets_qtys_units.py b/features/steps/steps/propertysets_qtys_units.py index 3ddcc254c..550482060 100644 --- a/features/steps/steps/propertysets_qtys_units.py +++ b/features/steps/steps/propertysets_qtys_units.py @@ -6,7 +6,7 @@ import os import math import re -from typing import List +from typing import List, Dict import ifcopenshell from ifcopenshell.util.unit import convert @@ -22,7 +22,8 @@ 'QTO_OCCURRENCEDRIVEN': 'IfcObject', } -@dataclass(frozen=True,slots=True,kw_only=True,repr=True,eq=True) + +@dataclass(frozen=True, slots=True, kw_only=True, repr=True, eq=True) class ConversionBasedUnitDefinition: """ used to hold data from table of conversion-based units defined in the IFC spec @@ -32,24 +33,36 @@ class ConversionBasedUnitDefinition: """ Name: str UnitType: str - ConversionFactor: str + ConversionFactor: float SIUnitName: str SIUnitPrefix: str = None Description: str = None - def __post_init__(self): - # capture π in table definitions - if "π" in self.ConversionFactor: - denom = self.ConversionFactor.split("/")[-1] - self.ConversionFactor = math.pi / float(denom) - if not isinstance(self.ConversionFactor, float): - self.ConversionFactor = float(self.ConversionFactor) - if self.SIUnitPrefix == "None": - self.SIUnitPrefix = None - if self.Description == "None": - self.Description = None - if self.Description and len(self.Description) == 0: - self.Description = None + +@functools.cache +def load_conversion_unit_def_from_table(table_definition: Dict) -> ConversionBasedUnitDefinition: + conv_factor = table_definition["ConversionFactor"] + if not isinstance(conv_factor, float): + conv_factor = float(conv_factor) + if table_definition['SIUnitPrefix'] == "None": + si_unit_prefix = None + else: + si_unit_prefix = table_definition['SIUnitPrefix'] + if table_definition['Description'] == "None": + descr = None + elif table_definition['Description'] and len(table_definition['Description']) == 0: + descr = None + else: + descr = table_definition['Description'] + + return ConversionBasedUnitDefinition( + Name=table_definition["Name"], + UnitType=table_definition["UnitType"], + ConversionFactor=conv_factor, + SIUnitName=table_definition["SIUnitName"], + SIUnitPrefix=si_unit_prefix, + Description=descr, + ) @functools.cache @@ -94,6 +107,7 @@ def upper_case_if_string(v): except AttributeError: return v + @functools.cache def get_table_definition(schema, table): schema_specific_path = system.get_abs_path(f"resources/{schema.upper()}/{table}.csv") @@ -104,11 +118,8 @@ def get_table_definition(schema, table): tbl_path = system.get_abs_path(f"resources/{table}.csv") tbl = system.get_csv(tbl_path, return_type='dict') - if table == "valid_ConversionBasedUnits": - name_key = "Name" - else: - name_key = "property_set_name" - return {d[name_key]: d for d in tbl} + return {d["Name"]: d for d in tbl} + class AlwaysEqualDict(dict): """ @@ -410,6 +421,7 @@ def step_impl(context, inst, table, inst_type=None): # if not values: # yield ValidationOutcome(inst=inst, expected= {"oneOf": accepted_data_type['instance']}, observed = {'value':None}, severity=OutcomeSeverity.ERROR) + @gherkin_ifc.step( "Its attribute .{attr_name}. must be defined [according to the table] 'valid_ConversionBasedUnits'" ) @@ -421,15 +433,22 @@ def step_impl(context, inst, attr_name): case "NAME": attr_value = getattr(inst, attr_name) if attr_value not in accepted_names: - yield ValidationOutcome(inst=inst, expected=accepted_names, observed=attr_value, severity=OutcomeSeverity.ERROR) + yield ValidationOutcome(inst=inst, expected=accepted_names, observed=attr_value, + severity=OutcomeSeverity.ERROR) case "CONVERSIONFACTOR": unit_name = inst.Name if unit_name in accepted_names: inst_factor = inst.ConversionFactor.ValueComponent.wrappedValue inst_si_unit = inst.ConversionFactor.UnitComponent - conv_unit_def = ConversionBasedUnitDefinition(**unit_definitions[unit_name]) - expected_factor = ifcopenshell.util.unit.convert(value=conv_unit_def.ConversionFactor,from_unit=conv_unit_def.SIUnitName,from_prefix=conv_unit_def.SIUnitPrefix,to_unit=inst_si_unit.Name,to_prefix=inst_si_unit.Prefix) + conv_data = {**unit_definitions[unit_name]} + conv_unit_def = load_conversion_unit_def_from_table(conv_data) + expected_factor = ifcopenshell.util.unit.convert(value=conv_unit_def.ConversionFactor, + from_unit=conv_unit_def.SIUnitName, + from_prefix=conv_unit_def.SIUnitPrefix, + to_unit=inst_si_unit.Name, + to_prefix=inst_si_unit.Prefix) if not math.isclose(a=inst_factor, b=expected_factor, rel_tol=1e-06, abs_tol=0.): - yield ValidationOutcome(inst=inst, expected=expected_factor, observed=inst_factor, severity=OutcomeSeverity.ERROR) + yield ValidationOutcome(inst=inst, expected=expected_factor, observed=inst_factor, + severity=OutcomeSeverity.ERROR) else: print(f"{unit_name=} not found in table") From 24ec01823476ae8b30dddb4f5243afa147dde9bd Mon Sep 17 00:00:00 2001 From: Scott Lecher Date: Sat, 29 Nov 2025 16:12:16 -0500 Subject: [PATCH 14/14] add scenarios per review comments (IVS-680) --- ...001_Correct-conversion-based-units.feature | 17 +++++++++ .../steps/steps/propertysets_qtys_units.py | 6 +-- ...js001-scenario03-us_survey_foot_ifc4x3.ifc | 37 +++++++++++++++++++ ...js001-scenario04-us_survey_foot_ifc4x3.ifc | 25 +++++++++++++ 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario03-us_survey_foot_ifc4x3.ifc create mode 100644 test/files/PJS/pjs001/fail-pjs001-scenario04-us_survey_foot_ifc4x3.ifc diff --git a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature index 48229f920..aff373b40 100644 --- a/features/rules/PJS/PJS001_Correct-conversion-based-units.feature +++ b/features/rules/PJS/PJS001_Correct-conversion-based-units.feature @@ -23,3 +23,20 @@ Feature: PJS001 - Correct conversion based units Scenario: Validating correct conversion factors Then its attribute .ConversionFactor. must be defined [according to the table] 'valid_ConversionBasedUnits' + Scenario: Validating that the conversion is based on SI units + Given its attribute .ConversionFactor. + Given its attribute .UnitComponent. + Then [its entity type] ^is^ 'IfcSIUnit' + + Scenario Outline: Validating that the conversion is based on the correct SI unit + Given .UnitType. ^is^ '' + Given its attribute .ConversionFactor. + Given its attribute .UnitComponent. + Then the value of attribute .Name. must be '' + + Examples: + | UnitType | CorrespondingSIUnit | + | AREAUNIT | SQUARE_METRE | + | LENGTHUNIT | METRE | + | VOLUMEUNIT | CUBIC_METRE | + | PLANEANGLEUNIT | RADIAN | \ No newline at end of file diff --git a/features/steps/steps/propertysets_qtys_units.py b/features/steps/steps/propertysets_qtys_units.py index 550482060..922698ae7 100644 --- a/features/steps/steps/propertysets_qtys_units.py +++ b/features/steps/steps/propertysets_qtys_units.py @@ -39,15 +39,13 @@ class ConversionBasedUnitDefinition: Description: str = None -@functools.cache def load_conversion_unit_def_from_table(table_definition: Dict) -> ConversionBasedUnitDefinition: conv_factor = table_definition["ConversionFactor"] + si_unit_prefix = table_definition["SIUnitPrefix"] if not isinstance(conv_factor, float): conv_factor = float(conv_factor) - if table_definition['SIUnitPrefix'] == "None": + if (si_unit_prefix == "None") or (len(si_unit_prefix) == 0): si_unit_prefix = None - else: - si_unit_prefix = table_definition['SIUnitPrefix'] if table_definition['Description'] == "None": descr = None elif table_definition['Description'] and len(table_definition['Description']) == 0: diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario03-us_survey_foot_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario03-us_survey_foot_ifc4x3.ifc new file mode 100644 index 000000000..df4635a9d --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario03-us_survey_foot_ifc4x3.ifc @@ -0,0 +1,37 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('fail-pjs001-scenario03-us_survey_foot_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* foot definition */ +#8=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#9=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.); +#10=IFCMEASUREWITHUNIT(IFCREAL(0.3048),#9); +#11=IFCCONVERSIONBASEDUNIT(#8,.LENGTHUNIT.,'foot',#10); + +/* square yard definition */ +#12=IFCDIMENSIONALEXPONENTS(2,0,0,0,0,0,0); +#13=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.); +#14=IFCMEASUREWITHUNIT(IFCREAL(0.83612736),#13); +#15=IFCCONVERSIONBASEDUNIT(#12,.AREAUNIT.,'square yard',#14); + +/* US survey foot definition */ +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.); +#18=IFCMEASUREWITHUNIT(IFCREAL(304.80060960122),#11); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'US survey foot',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19,#15)); +ENDSEC; +END-ISO-10303-21; diff --git a/test/files/PJS/pjs001/fail-pjs001-scenario04-us_survey_foot_ifc4x3.ifc b/test/files/PJS/pjs001/fail-pjs001-scenario04-us_survey_foot_ifc4x3.ifc new file mode 100644 index 000000000..a89b0ccc9 --- /dev/null +++ b/test/files/PJS/pjs001/fail-pjs001-scenario04-us_survey_foot_ifc4x3.ifc @@ -0,0 +1,25 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('ViewDefinition [Alignment-basedView]'),'2;1'); +FILE_NAME('fail-pjs001-scenario04-us_survey_foot_ifc4x3.ifc','2025-11-18T20:39:40-05:00',(''),(''),'IfcOpenShell 0.0.0','redacted - redacted - 3.14159','Nobody'); +FILE_SCHEMA(('IFC4X3_ADD2')); +ENDSEC; +DATA; +#1=IFCPROJECT('0k7XaScqT91O1q2GuOZQU8',$,'PJS001 Unit Test',$,$,$,$,(#6),#20); +#2=IFCCARTESIANPOINT((0.,0.,0.)); +#3=IFCDIRECTION((0.,0.,1.)); +#4=IFCDIRECTION((1.,0.,0.)); +#5=IFCAXIS2PLACEMENT3D(#2,#3,#4); +#6=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,#5,$); +#7=IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body',$,*,*,*,*,#6,$,.MODEL_VIEW.,$); + +/* US survey foot definition */ +#16=IFCDIMENSIONALEXPONENTS(1,0,0,0,0,0,0); +#17=IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.GRAM.); +#18=IFCMEASUREWITHUNIT(IFCREAL(304.80060960122),#17); +#19=IFCCONVERSIONBASEDUNIT(#16,.LENGTHUNIT.,'US survey foot',#18); + +/* unit assignment */ +#20=IFCUNITASSIGNMENT((#19)); +ENDSEC; +END-ISO-10303-21;