diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index dfadebe5f1..89e4466b6c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -912,6 +912,13 @@ def convert_file_length_units(ifc_file: ifcopenshell.file, target_units: str = " new_value = convert_value(val) setattr(element, attr.name(), new_value) + # IfcGeometricRepresentationContext.Precision is typed as a plain IfcReal + # but is interpreted in the project length unit, so it must be scaled too. + # Subcontexts derive Precision from their parent and cannot be set. + for context in file_patched.by_type("IfcGeometricRepresentationContext", include_subtypes=False): + if context.Precision is not None: + context.Precision = convert_unit(context.Precision, old_length, new_length) + has_map_unit = False if ( ifc_file.schema == "IFC2X3" diff --git a/src/ifcopenshell-python/test/util/test_unit.py b/src/ifcopenshell-python/test/util/test_unit.py index 17f7fd9976..38d78ba024 100644 --- a/src/ifcopenshell-python/test/util/test_unit.py +++ b/src/ifcopenshell-python/test/util/test_unit.py @@ -19,6 +19,7 @@ from math import pi import numpy as np +import pytest import ifcopenshell.api.context import ifcopenshell.api.georeference @@ -258,6 +259,23 @@ class TestConvertFileLengthUnits(test.bootstrap.IFC2X3): assert max(i.id() for i in output) == len(output.wrapped_data.entity_names()) + 1 assert subject.get_full_unit_name(subject.get_project_unit(output, "LENGTHUNIT")) == "METRE" + def test_precision_conversion(self): + # Regression test for #6127: IfcGeometricRepresentationContext.Precision + # is typed IfcReal but interpreted in the project length unit, so it must + # be scaled along with the length measures. + ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") + unit = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI") + ifcopenshell.api.unit.assign_unit(self.file, units=[unit]) + context = ifcopenshell.api.context.add_context(self.file, context_type="Model") + context.Precision = 0.01 + # Subcontexts derive Precision from the parent and must be left alone. + ifcopenshell.api.context.add_context( + self.file, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context + ) + output = subject.convert_file_length_units(self.file, target_units="METER") + new_context = output.by_type("IfcGeometricRepresentationContext", include_subtypes=False)[0] + assert new_context.Precision == pytest.approx(0.00001) + def test_attribute_conversion(self): ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject") unit = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")