Feature faster unit method ---> Main (#5257)

* feature_faster_unit_method > main: added constants

* feature_faster_unit_method > main: extended `file` class to dynamically save unit information

* feature_faster_unit_method > main: refactored `get_property_unit` method

1. split out case that returns the wrong type (dictionary of units) into its own method

2. cleaned up (but preserved) logic

3. refactored common part of all cases (the method which prioritises unit then value-entity then measure_class)

---------

Co-authored-by: raj-open <raj-open@users.noreply.github.com>
Co-authored-by: Dion Moult <dion@thinkmoult.com>
This commit is contained in:
Raj
2025-01-26 09:04:49 +01:00
committed by GitHub
parent 3b1e893d34
commit 124c02897c
3 changed files with 240 additions and 47 deletions
+72 -1
View File
@@ -24,10 +24,18 @@ import zipfile
import functools
import ifcopenshell
from pathlib import Path
from typing import Optional, Any, Union, Callable, Generator, Literal, TYPE_CHECKING
from typing import Any
from typing import Callable
from typing import Generator
from typing import Optional
from typing import TYPE_CHECKING
from typing import Union
from . import ifcopenshell_wrapper
from .entity_instance import entity_instance
# from .util.constants import IFC_TYPES
from .util.constants import IFC_UNIT_ASSIGNMENT
from .util.unit import get_measure_unit_type
if TYPE_CHECKING:
import ifcopenshell.util.schema
@@ -217,6 +225,7 @@ class file:
"""
wrapped_data: ifcopenshell_wrapper.file
_units: file_units | None = None
history_size: int = 64
def __init__(
@@ -456,6 +465,28 @@ class file:
version.append(int(number.group(1)) if number else 0)
return tuple(version)
@property
def units(self) -> file_units:
"""
A class which dynamically stores unit information
and provides methods to compute unit-entities associated to types.
"""
if self._units is None:
self._units = file_units(self)
return self._units
def unit_by_measure_class(
self,
measure_class: str | None = None,
/,
) -> ifcopenshell.entity_instance | None:
"""
Helper method to obtain unit-entity either directly
or indirectly via measure class.
"""
if measure_class is not None:
return self.units.by_measure_type(measure_class)
def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]:
if attr[0:6] == "create":
return functools.partial(self.create_entity, attr[6:])
@@ -695,3 +726,43 @@ class file:
def to_string(self) -> str:
return self.wrapped_data.to_string()
class file_units:
"""
A class which provides methods to compute unit-entities associated to types.
"""
_unit_assignment: dict[str, entity_instance] = {}
def __init__(self, ifc_file: file):
self._load_unit_assignment(ifc_file)
def _load_unit_assignment(self, ifc_file: file, /):
"""
A mapping as dictionary which associates
to basic UnitTypes a unit-entity in the IFC file.
"""
entities = self.by_type(IFC_UNIT_ASSIGNMENT)
base = next(iter(entities), None)
units = getattr(base, "Units", None) or ()
units_and_types = [
(u, getattr(u, "UnitType", None))
for u in units
if isinstance(u, entity_instance)
]
self._unit_assignment = {t: u for u, t in units_and_types if isinstance(t, str)}
return
def by_type(self, t: str, /) -> entity_instance | None:
"""
Returns the unit entity associated to a basic type
"""
return self._unit_assignment.get(t, None)
def by_measure_type(self, t: str, /) -> entity_instance | None:
"""
Returns the unit entity associated to a measure type
"""
t = get_measure_unit_type(t)
return self.by_type(t)