Coverage for bim2sim/elements/mapping/units.py: 62%
52 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 17:09 +0000
1"""Module contains the unit registry for all params
3To handle different unit definitions in IFC files and get accurate values when
4exporting later to simulation models, we use pint to maintain and check correct
5unit definitions throughout the whole bim2sim chain. To get the correct units
6when loading the IFC we convert IFC units to pint units.
7"""
9from pint import UnitRegistry, set_application_registry
11# to avoid temperature problems
12ureg = UnitRegistry(autoconvert_offset_to_baseunit=True)
13set_application_registry(ureg)
14ureg.define('percent = 0.01*count = %')
15ureg.define('EUR = currency')
16ureg.define('USD = currency')
17ureg.define('GBP = currency')
19# TODO:multiple ifc files
20# ifc units are up2date to ifc 4.3
21ifc_pint_unitmap = {
22 'AMPERE': 'ampere',
23 'BECQUEREL': 'becquerel',
24 'CANDELA': 'candela',
25 'COULOMB': 'coulomb',
26 'CUBIC_METRE': 'cubic metre',
27 'DEGREE_CELSIUS': 'degree_Celsius',
28 'FARAD': 'farad',
29 'GRAM': 'gram',
30 'GRAY': 'gray',
31 'HENRY': 'henry',
32 'HERTZ': 'hertz',
33 'JOULE': 'joule',
34 'KELVIN': 'kelvin',
35 'LUMEN': 'lumen',
36 'LUX': 'lux',
37 'METRE': 'metre',
38 'MOLE': 'mole',
39 'NEWTON': 'newton',
40 'OHM': 'ohm',
41 'PASCAL': 'pascal',
42 'RADIAN': 'radian',
43 'SECOND': 'second',
44 'SIEMENS': 'siemens',
45 'SIEVERT': 'sievert',
46 'SQUARE_METRE': 'square metre',
47 'STERADIAN': 'steradian',
48 'TESLA': 'tesla',
49 'VOLT': 'volt',
50 'WATT': 'watt',
51 'WEBER': 'weber',
52}
55def parse_ifc(unit_entity):
56 """"""
57 unit_type = unit_entity.is_a()
58 if unit_type == 'IfcDerivedUnit':
59 unit = ureg.dimensionless
60 for element in unit_entity.Elements:
61 prefix_string = \
62 element.Unit.Prefix.lower() if element.Unit.Prefix else ''
63 unit_part = ureg.parse_units('{}{}'.format(prefix_string,
64 ifc_pint_unitmap[
65 element.Unit.Name]))
66 if element.Unit.Dimensions:
67 unit_part = unit_part ** element.Dimensions
68 unit = unit * unit_part ** element.Exponent
69 return unit
70 elif unit_type == 'IfcSIUnit':
71 prefix_string = unit_entity.Prefix.lower() if unit_entity.Prefix else ''
72 unit = ureg.parse_units(
73 '{}{}'.format(prefix_string, ifc_pint_unitmap[unit_entity.Name]))
74 return unit
75 elif unit_type in [
76 'IfcConversionBasedUnit',
77 'IfcConversionBasedUnitWithOffset'
78 ]:
79 # we use pint conversions instead IFC ones (ignoring ConversionOffset &
80 # ConversionFactor)
81 unit_component = unit_entity.ConversionFactor.UnitComponent
82 prefix_string = unit_component.Prefix.lower() if \
83 unit_component.Prefix else ''
84 unit = ureg.parse_units(
85 '{}{}'.format(prefix_string, ifc_pint_unitmap[unit_component.Name]))
86 if unit_component.Dimensions:
87 unit = unit ** unit_component.Dimensions
88 return unit
89 elif unit_type == 'IfcMonetaryUnit':
90 currency = unit_entity.Currency
91 try:
92 unit = ureg.parse_units(currency)
93 except:
94 unit = ureg.dimensionless
95 return unit
96 elif unit_type == 'IfcDerivedUnitElement':
97 unit = ureg.dimensionless
98 prefix_string = \
99 unit_type.Unit.Prefix.lower() if unit_type.Unit.Prefix else ''
100 unit_part = ureg.parse_units('{}{}'.format(prefix_string,
101 ifc_pint_unitmap[
102 unit_type.Unit.Name]))
103 if unit_type.Unit.Dimensions:
104 unit_part = unit_part ** unit_type.Dimensions
105 unit = unit * unit_part ** unit_type.Exponent
106 return unit
107 elif unit_type == 'IfcMeasureWithUnit':
108 unit_component = unit_entity.UnitComponent
109 unit = ureg.parse_units(ifc_pint_unitmap[unit_component.Name])
110 if unit_component.Dimensions:
111 unit = unit ** unit_component.Dimensions
112 return unit
113 else:
114 raise NotImplementedError(f"Found {unit_type} and can't convert it to"
115 f"Pint unit in Python.")
116 # TODO: IfcDimensionalExponents,IfcContextDependentUnit