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