Add tests for PR #5257 (using cache for fetching units)

This commit is contained in:
Dion Moult
2025-01-27 14:40:15 +11:00
parent c0aaebcbe9
commit 3582dc43d9
3 changed files with 148 additions and 7 deletions
+1 -2
View File
@@ -222,7 +222,7 @@ class file:
"""
wrapped_data: ifcopenshell_wrapper.file
units: dict[str: ifcopenshell.entity_instance] = None
units: dict[str, entity_instance] = {}
history_size: int = 64
def __init__(
@@ -358,7 +358,6 @@ class file:
:param args: The positional arguments of the IFC class
:param kwargs: The keyword arguments of the IFC class
:returns: An entity instance
:rtype: ifcopenshell.entity_instance
Example:
@@ -413,12 +413,24 @@ def get_unit_assignment(ifc_file: ifcopenshell.file) -> Union[ifcopenshell.entit
def cache_units(ifc_file: ifcopenshell.file) -> None:
"""Cache the default units for performance
Repetitively fetching project units (such as for determining the unit of a
property) can be costly. This enables a cache to make it faster. If the
project units change, you can update the cache by rerunning this function.
:param ifc_file: The IFC file.
"""
ifc_file.units = {}
if assignment := get_unit_assignment(ifc_file):
ifc_file.units = {u.UnitType: u for u in assignment.Units if getattr(u, "UnitType", None)}
def clear_unit_cache(ifc_file: ifcopenshell.file) -> None:
"""Clears the unit cache of the project
:param ifc_file: The IFC file.
"""
ifc_file.units = {}
@@ -472,8 +484,11 @@ def get_property_unit(
elif prop.is_a("IfcPropertySingleValue"):
measure_class = prop.NominalValue.is_a()
elif prop.is_a("IfcPropertyEnumeratedValue"):
if unit := prop.EnumerationReference.Unit:
return unit
if prop.EnumerationReference:
if unit := prop.EnumerationReference.Unit:
return unit
if value := next(iter(prop.EnumerationReference.EnumerationValues or ()), None):
measure_class = value.is_a()
if value := next(iter(prop.EnumerationValues or ()), None):
measure_class = value.is_a()
elif prop.is_a("IfcPropertyListValue"):
@@ -484,7 +499,7 @@ def get_property_unit(
measure_class = value.is_a()
if measure_class and (unit_type := get_measure_unit_type(measure_class)):
return get_project_unit(ifc_file, unit_type)
return get_project_unit(ifc_file, unit_type, use_cache=use_cache)
def get_property_table_unit(
@@ -513,14 +528,14 @@ def get_property_table_unit(
defining_unit = unit
elif value := next(iter(prop.DefiningValues or ()), None):
if unit_type := get_measure_unit_type(value.is_a()):
defining_unit = get_project_unit(ifc_file, unit_type)
defining_unit = get_project_unit(ifc_file, unit_type, use_cache=use_cache)
defined_unit = None
if unit := prop.DefinedUnit:
defined_unit = unit
elif value := next(iter(prop.DefinedValues or ()), None):
if unit_type := get_measure_unit_type(value.is_a()):
defining_unit = get_project_unit(ifc_file, unit_type)
defining_unit = get_project_unit(ifc_file, unit_type, use_cache=use_cache)
return {
"DefiningUnit": defining_unit,
@@ -25,6 +25,133 @@ import ifcopenshell.util.unit as subject
from math import pi
class TestCacheUnits(test.bootstrap.IFC4):
def test_run(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
assert self.file.units == {}
subject.cache_units(self.file)
assert self.file.units == {"LENGTHUNIT": length, "AREAUNIT": area}
class TestClearUnitCache(test.bootstrap.IFC4):
def test_run(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
subject.cache_units(self.file)
subject.clear_unit_cache(self.file)
assert self.file.units == {}
class TestGetProjectUnit(test.bootstrap.IFC4):
def test_run(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
assert subject.get_project_unit(self.file, "LENGTHUNIT") == length
assert subject.get_project_unit(self.file, "AREAUNIT") == area
def test_using_a_cache(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
length2 = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="CENTI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
assert self.file.units == {}
assert subject.get_project_unit(self.file, "LENGTHUNIT", use_cache=True) == length
assert self.file.units == {"LENGTHUNIT": length, "AREAUNIT": area}
ifcopenshell.api.unit.assign_unit(self.file, units=[length2])
assert subject.get_project_unit(self.file, "LENGTHUNIT", use_cache=True) == length
subject.clear_unit_cache(self.file)
assert subject.get_project_unit(self.file, "LENGTHUNIT", use_cache=True) == length2
assert self.file.units == {"LENGTHUNIT": length2, "AREAUNIT": area}
class TestGetPropertyUnit(test.bootstrap.IFC4):
def test_no_unit(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
prop = self.file.createIfcQuantityLength(Name="Foo", LengthValue=42.0)
assert subject.get_property_unit(prop, self.file) is None
def test_simple_quantity(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
length2 = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="CENTI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
prop = self.file.createIfcQuantityLength(Name="Foo", LengthValue=42.0)
assert subject.get_property_unit(prop, self.file) == length
prop.Unit = length2
assert subject.get_property_unit(prop, self.file) == length2
def test_single_value(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
length2 = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="CENTI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
prop = self.file.createIfcPropertySingleValue(Name="Foo", NominalValue=self.file.createIfcLengthMeasure(42.0))
assert subject.get_property_unit(prop, self.file) == length
prop.Unit = length2
assert subject.get_property_unit(prop, self.file) == length2
def test_enumerated_value(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
length2 = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="CENTI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
prop = self.file.createIfcPropertyEnumeratedValue(Name="Foo", EnumerationValues=[self.file.createIfcLengthMeasure(42.0)])
assert subject.get_property_unit(prop, self.file) == length
prop.EnumerationValues = []
prop.EnumerationReference = self.file.createIfcPropertyEnumeration("Foo", [self.file.createIfcAreaMeasure(42.0)])
assert subject.get_property_unit(prop, self.file) == area
prop.EnumerationReference = self.file.createIfcPropertyEnumeration("Foo", [self.file.createIfcAreaMeasure(42.0)], Unit=length2)
assert subject.get_property_unit(prop, self.file) == length2
prop.EnumerationValues = [self.file.createIfcAreaMeasure(42.0)]
assert subject.get_property_unit(prop, self.file) == length2
prop.EnumerationReference.Unit = None
assert subject.get_property_unit(prop, self.file) == area
def test_list_value(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
length2 = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="CENTI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
prop = self.file.createIfcPropertyListValue(Name="Foo", ListValues=[self.file.createIfcLengthMeasure(42.0)])
assert subject.get_property_unit(prop, self.file) == length
prop.Unit = length2
assert subject.get_property_unit(prop, self.file) == length2
prop.Unit = None
prop.ListValues = []
assert subject.get_property_unit(prop, self.file) is None
def test_bounded_value(self):
ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject")
length = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="MILLI")
length2 = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="LENGTHUNIT", prefix="CENTI")
area = ifcopenshell.api.unit.add_si_unit(self.file, unit_type="AREAUNIT")
ifcopenshell.api.unit.assign_unit(self.file, units=[length, area])
prop = self.file.createIfcPropertyBoundedValue(Name="Foo")
assert subject.get_property_unit(prop, self.file) is None
prop.UpperBoundValue = self.file.createIfcLengthMeasure(42.0)
assert subject.get_property_unit(prop, self.file) == length
prop.UpperBoundValue = None
prop.LowerBoundValue = self.file.createIfcLengthMeasure(42.0)
assert subject.get_property_unit(prop, self.file) == length
prop.LowerBoundValue = None
prop.SetPointValue = self.file.createIfcLengthMeasure(42.0)
assert subject.get_property_unit(prop, self.file) == length
prop.Unit = length2
assert subject.get_property_unit(prop, self.file) == length2
class TestConvert(test.bootstrap.IFC4):
def test_run(self):
assert subject.convert(1, None, "METRE", None, "METRE") == 1