From f0e461c04951816dd7eda2cca3136ff5b0d834a5 Mon Sep 17 00:00:00 2001 From: David Paul Jansen Date: Mon, 6 Jan 2025 12:44:31 +0100 Subject: [PATCH 1/8] only calculate Aggregated TZ attributes if existing and use to_aggregation for all attributes --- .../elements/aggregation/bps_aggregations.py | 319 +++++++----------- 1 file changed, 117 insertions(+), 202 deletions(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index 6107e4a3ce..d38d46ca76 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -138,10 +138,12 @@ def _intensive_calc(self, name) -> ureg.Quantity: 'ratio_conv_rad_lighting', 'maintained_illuminance', 'lighting_efficiency_lumen', base_infiltration', 'max_user_infiltration', 'min_ahu', 'max_ahu', 'persons']""" - prop_sum = sum( - getattr(tz, name) * tz.net_volume for tz in self.elements if - getattr(tz, name) is not None and tz.net_volume is not None) - return prop_sum / self.net_volume + # only calculate intensive calc if all zones have this attribute + if all([getattr(tz, name) is not None for tz in self.elements]): + prop_sum = sum( + getattr(tz, name) * tz.net_volume for tz in self.elements if + getattr(tz, name) is not None and tz.net_volume is not None) + return prop_sum / self.net_volume def _intensive_list_calc(self, name) -> list: """intensive list properties getter - volumetric mean @@ -149,219 +151,132 @@ def _intensive_list_calc(self, name) -> list: 'persons_profile', 'machines_profile', 'lighting_profile', 'max_overheating_infiltration', 'max_summer_infiltration', 'winter_reduction_infiltration']""" - list_attrs = {'heating_profile': 24, 'cooling_profile': 24, - 'persons_profile': 24, - 'machines_profile': 24, 'lighting_profile': 24, - 'max_overheating_infiltration': 2, - 'max_summer_infiltration': 3, - 'winter_reduction_infiltration': 3} - length = list_attrs[name] - aux = [] - for x in range(0, length): - aux.append(sum( - getattr(tz, name)[x] * tz.net_volume for tz in self.elements - if getattr(tz, name) is not None and tz.net_volume is not None) - / self.net_volume) - return aux + if all([getattr(tz, name) is not None for tz in self.elements]): + list_attrs = {'heating_profile': 24, 'cooling_profile': 24, + 'persons_profile': 24, + 'machines_profile': 24, 'lighting_profile': 24, + 'max_overheating_infiltration': 2, + 'max_summer_infiltration': 3, + 'winter_reduction_infiltration': 3} + length = list_attrs[name] + aux = [] + for x in range(0, length): + aux.append(sum( + getattr(tz, name)[x] * tz.net_volume for tz in + self.elements + if getattr(tz, + name) is not None and tz.net_volume is not None) + / self.net_volume) + return aux def _extensive_calc(self, name) -> ureg.Quantity: """extensive properties getter intensive_attributes = ['gross_area', 'net_area', 'volume']""" - return sum(getattr(tz, name) for tz in self.elements if - getattr(tz, name) is not None) + # only calculate intensive calc if all zones have this attribute + if all([getattr(tz, name) is not None for tz in self.elements]): + return sum(getattr(tz, name) for tz in self.elements if + getattr(tz, name) is not None) def _bool_calc(self, name) -> bool: """bool properties getter bool_attributes = ['with_cooling', 'with_heating', 'with_ahu', 'use_maintained_illuminance']""" # todo: log - prop_bool = False - for tz in self.elements: - prop = getattr(tz, name) - if prop is not None: - if prop: - prop_bool = True - break - return prop_bool + # only calculate intensive calc if all zones have this attribute + if all([getattr(tz, name) is not None for tz in self.elements]): + prop_bool = False + for tz in self.elements: + prop = getattr(tz, name) + if prop is not None: + if prop: + prop_bool = True + break + return prop_bool def _get_tz_usage(self, name) -> str: """usage properties getter""" return self.elements[0].usage - usage = attribute.Attribute( - functions=[_get_tz_usage], - ) - # t_set_heat = attribute.Attribute( - # functions=[_intensive_calc], - # unit=ureg.degC - # ) - # todo refactor this to remove redundancy for units + usage = bps.ThermalZone.usage.to_aggregation(_get_tz_usage) t_set_heat = bps.ThermalZone.t_set_heat.to_aggregation(_intensive_calc) - - t_set_cool = attribute.Attribute( - functions=[_intensive_calc], - unit=ureg.degC, - dependant_elements='elements' - ) - t_ground = attribute.Attribute( - functions=[_intensive_calc], - unit=ureg.degC, - dependant_elements='elements' - ) - net_area = attribute.Attribute( - functions=[_extensive_calc], - unit=ureg.meter ** 2, - dependant_elements='elements' - ) - gross_area = attribute.Attribute( - functions=[_extensive_calc], - unit=ureg.meter ** 2, - dependant_elements='elements' - ) - gross_volume = attribute.Attribute( - functions=[_extensive_calc], - unit=ureg.meter ** 3, - dependant_elements='elements' - ) - height = attribute.Attribute( - functions=[_intensive_calc], - unit=ureg.meter, - dependant_elements='elements' - ) - area_per_occupant = attribute.Attribute( - functions=[_intensive_calc], - unit=ureg.meter ** 2, - dependant_elements='elements' - ) - # use conditions - with_cooling = attribute.Attribute( - functions=[_bool_calc], - dependant_elements='elements' - ) - with_heating = attribute.Attribute( - functions=[_bool_calc], - dependant_elements='elements' - ) - with_ahu = attribute.Attribute( - functions=[_bool_calc], - dependant_elements='elements' - ) - heating_profile = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - cooling_profile = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - persons = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - T_threshold_heating = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - activity_degree_persons = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - fixed_heat_flow_rate_persons = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - internal_gains_moisture_no_people = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - T_threshold_cooling = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - ratio_conv_rad_persons = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - machines = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - ratio_conv_rad_machines = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - use_maintained_illuminance = attribute.Attribute( - functions=[_bool_calc], - dependant_elements='elements' - ) - lighting_power = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - fixed_lighting_power = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - ratio_conv_rad_lighting = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - maintained_illuminance = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - lighting_efficiency_lumen = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - use_constant_infiltration = attribute.Attribute( - functions=[_bool_calc], - dependant_elements='elements' - ) - base_infiltration = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - max_user_infiltration = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - max_overheating_infiltration = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - max_summer_infiltration = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - winter_reduction_infiltration = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - min_ahu = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - max_ahu = attribute.Attribute( - functions=[_intensive_calc], - dependant_elements='elements' - ) - with_ideal_thresholds = attribute.Attribute( - functions=[_bool_calc], - dependant_elements='elements' - ) - persons_profile = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - machines_profile = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) - lighting_profile = attribute.Attribute( - functions=[_intensive_list_calc], - dependant_elements='elements' - ) + t_set_cool = bps.ThermalZone.t_set_cool.to_aggregation(_intensive_calc) + t_ground = bps.ThermalZone.t_ground.to_aggregation(_intensive_calc) + net_area = bps.ThermalZone.net_area.to_aggregation(_extensive_calc) + gross_area = bps.ThermalZone.gross_area.to_aggregation(_extensive_calc) + gross_volume = bps.ThermalZone.gross_volume.to_aggregation(_extensive_calc) + height = bps.ThermalZone.height.to_aggregation(_extensive_calc) + area_per_occupant = bps.ThermalZone.area_per_occupant.to_aggregation( + _intensive_calc) + with_cooling = bps.ThermalZone.with_cooling.to_aggregation(_bool_calc) + with_heating = bps.ThermalZone.with_heating.to_aggregation(_bool_calc) + with_ahu = bps.ThermalZone.with_ahu.to_aggregation(_bool_calc) + heating_profile = bps.ThermalZone.heating_profile.to_aggregation( + _intensive_list_calc) + cooling_profile = bps.ThermalZone.cooling_profile.to_aggregation( + _intensive_list_calc) + persons = bps.ThermalZone.persons.to_aggregation(_intensive_calc) + T_threshold_heating = bps.ThermalZone.T_threshold_heating.to_aggregation( + _intensive_calc) + T_threshold_cooling = bps.ThermalZone.T_threshold_cooling.to_aggregation( + _intensive_calc) + ratio_conv_rad_persons = ( + bps.ThermalZone.ratio_conv_rad_persons.to_aggregation( + _intensive_calc)) + machines = bps.ThermalZone.machines.to_aggregation(_intensive_calc) + ratio_conv_rad_machines = ( + bps.ThermalZone.ratio_conv_rad_machines.to_aggregation( + _intensive_calc)) + use_maintained_illuminance = ( + bps.ThermalZone.use_maintained_illuminance.to_aggregation( + _bool_calc)) + activity_degree_persons = ( + bps.ThermalZone.activity_degree_persons.to_aggregation( + _intensive_calc)) + fixed_heat_flow_rate_persons = ( + bps.ThermalZone.fixed_heat_flow_rate_persons. + to_aggregation(_intensive_calc)) + internal_gains_moisture_no_people = ( + bps.ThermalZone.internal_gains_moisture_no_people. + to_aggregation(_intensive_calc)) + fixed_lighting_power = bps.ThermalZone.fixed_lighting_power.to_aggregation( + _intensive_calc) + ratio_conv_rad_lighting = ( + bps.ThermalZone.ratio_conv_rad_lighting.to_aggregation( + _intensive_calc)) + maintained_illuminance = ( + bps.ThermalZone.maintained_illuminance.to_aggregation( + _intensive_calc)) + lighting_efficiency_lumen = ( + bps.ThermalZone.lighting_efficiency_lumen.to_aggregation( + _intensive_calc)) + use_constant_infiltration = ( + bps.ThermalZone.use_constant_infiltration.to_aggregation( + _bool_calc)) + base_infiltration = bps.ThermalZone.base_infiltration.to_aggregation( + _intensive_calc) + max_user_infiltration = ( + bps.ThermalZone.max_user_infiltration.to_aggregation( + _intensive_calc)) + max_overheating_infiltration = ( + bps.ThermalZone.max_overheating_infiltration.to_aggregation( + _intensive_list_calc)) + max_summer_infiltration = ( + bps.ThermalZone.max_summer_infiltration.to_aggregation( + _intensive_list_calc)) + winter_reduction_infiltration = ( + bps.ThermalZone.winter_reduction_infiltration.to_aggregation( + _intensive_list_calc)) + min_ahu = bps.ThermalZone.min_ahu.to_aggregation(_intensive_calc) + max_ahu = bps.ThermalZone.max_ahu.to_aggregation(_intensive_calc) + with_ideal_thresholds = ( + bps.ThermalZone.with_ideal_thresholds.to_aggregation( + _bool_calc)) + persons_profile = bps.ThermalZone.persons_profile.to_aggregation( + _intensive_list_calc) + machines_profile = bps.ThermalZone.machines_profile.to_aggregation( + _intensive_list_calc) + lighting_profile = bps.ThermalZone.lighting_profile.to_aggregation( + _intensive_list_calc) class SBDisaggregationMixin: @@ -450,7 +365,7 @@ def get_id(prefix=""): prefix_length = len(prefix) if prefix_length > 10: raise AttributeError("Max prefix length is 10!") - ifcopenshell_guid = guid.new()[prefix_length+1:] + ifcopenshell_guid = guid.new()[prefix_length + 1:] return f"{prefix}{ifcopenshell_guid}" From a45c95116412e7996a6c180e7a02a7b046ca0f75 Mon Sep 17 00:00:00 2001 From: David Jansen Date: Thu, 15 May 2025 16:32:34 +0200 Subject: [PATCH 2/8] Update bim2sim/elements/aggregation/bps_aggregations.py Co-authored-by: Veronika Richter <48535180+veronikarichter@users.noreply.github.com> --- bim2sim/elements/aggregation/bps_aggregations.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index d38d46ca76..a306cea86e 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -184,14 +184,10 @@ def _bool_calc(self, name) -> bool: # todo: log # only calculate intensive calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): - prop_bool = False for tz in self.elements: prop = getattr(tz, name) if prop is not None: - if prop: - prop_bool = True - break - return prop_bool + return True def _get_tz_usage(self, name) -> str: """usage properties getter""" From 5f2b69dfa9ea09a4fba7c85224c245fb411f5e8c Mon Sep 17 00:00:00 2001 From: David Jansen Date: Thu, 15 May 2025 16:33:05 +0200 Subject: [PATCH 3/8] Update bim2sim/elements/aggregation/bps_aggregations.py Co-authored-by: Veronika Richter <48535180+veronikarichter@users.noreply.github.com> --- bim2sim/elements/aggregation/bps_aggregations.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index a306cea86e..943f0b0347 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -174,8 +174,7 @@ def _extensive_calc(self, name) -> ureg.Quantity: intensive_attributes = ['gross_area', 'net_area', 'volume']""" # only calculate intensive calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): - return sum(getattr(tz, name) for tz in self.elements if - getattr(tz, name) is not None) + return sum(getattr(tz, name) for tz in self.elements)) def _bool_calc(self, name) -> bool: """bool properties getter From f3de6e6979fbbf3564c63144855cc95a61728a65 Mon Sep 17 00:00:00 2001 From: David Paul Jansen Date: Thu, 15 May 2025 16:54:36 +0200 Subject: [PATCH 4/8] improve code for tz aggregation attribute calculation --- .../elements/aggregation/bps_aggregations.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index 943f0b0347..bbac8e9f2f 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -139,10 +139,10 @@ def _intensive_calc(self, name) -> ureg.Quantity: 'lighting_efficiency_lumen', base_infiltration', 'max_user_infiltration', 'min_ahu', 'max_ahu', 'persons']""" # only calculate intensive calc if all zones have this attribute - if all([getattr(tz, name) is not None for tz in self.elements]): + if all([getattr(tz, name) is not None and tz.net_volume is not None for + tz in self.elements]): prop_sum = sum( - getattr(tz, name) * tz.net_volume for tz in self.elements if - getattr(tz, name) is not None and tz.net_volume is not None) + getattr(tz, name) * tz.net_volume for tz in self.elements) return prop_sum / self.net_volume def _intensive_list_calc(self, name) -> list: @@ -151,7 +151,8 @@ def _intensive_list_calc(self, name) -> list: 'persons_profile', 'machines_profile', 'lighting_profile', 'max_overheating_infiltration', 'max_summer_infiltration', 'winter_reduction_infiltration']""" - if all([getattr(tz, name) is not None for tz in self.elements]): + if all([getattr(tz, name) is not None and tz.net_volume is not None + for tz in self.elements]): list_attrs = {'heating_profile': 24, 'cooling_profile': 24, 'persons_profile': 24, 'machines_profile': 24, 'lighting_profile': 24, @@ -161,12 +162,9 @@ def _intensive_list_calc(self, name) -> list: length = list_attrs[name] aux = [] for x in range(0, length): - aux.append(sum( - getattr(tz, name)[x] * tz.net_volume for tz in - self.elements - if getattr(tz, - name) is not None and tz.net_volume is not None) - / self.net_volume) + aux.append(sum(getattr(tz, name)[x] * tz.net_volume for tz in + self.elements if + getattr(tz, name)) / self.net_volume) return aux def _extensive_calc(self, name) -> ureg.Quantity: @@ -174,7 +172,7 @@ def _extensive_calc(self, name) -> ureg.Quantity: intensive_attributes = ['gross_area', 'net_area', 'volume']""" # only calculate intensive calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): - return sum(getattr(tz, name) for tz in self.elements)) + return sum(getattr(tz, name) for tz in self.elements) def _bool_calc(self, name) -> bool: """bool properties getter From 0724db4becf5c0a718a374437b82790456dc893e Mon Sep 17 00:00:00 2001 From: David Paul Jansen Date: Thu, 15 May 2025 20:12:36 +0200 Subject: [PATCH 5/8] refix _bool_calc --- bim2sim/elements/aggregation/bps_aggregations.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index bbac8e9f2f..deee331212 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -181,10 +181,14 @@ def _bool_calc(self, name) -> bool: # todo: log # only calculate intensive calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): + prop_bool = False for tz in self.elements: prop = getattr(tz, name) if prop is not None: - return True + if prop: + prop_bool = True + break + return prop_bool def _get_tz_usage(self, name) -> str: """usage properties getter""" From d9fe3c43cc72c19836d038652e3a447907b65617 Mon Sep 17 00:00:00 2001 From: David Jansen Date: Wed, 21 May 2025 16:32:30 +0200 Subject: [PATCH 6/8] Update bim2sim/elements/aggregation/bps_aggregations.py Co-authored-by: Veronika Richter <48535180+veronikarichter@users.noreply.github.com> --- bim2sim/elements/aggregation/bps_aggregations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index deee331212..2a45454d35 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -170,7 +170,7 @@ def _intensive_list_calc(self, name) -> list: def _extensive_calc(self, name) -> ureg.Quantity: """extensive properties getter intensive_attributes = ['gross_area', 'net_area', 'volume']""" - # only calculate intensive calc if all zones have this attribute + # only calculate extensive calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): return sum(getattr(tz, name) for tz in self.elements) From c11f7f56aa7ced5b9bc99288d82f861140eab36c Mon Sep 17 00:00:00 2001 From: David Paul Jansen Date: Wed, 21 May 2025 16:39:22 +0200 Subject: [PATCH 7/8] extend information about how boolean attributes are handled for aggregations --- bim2sim/elements/aggregation/bps_aggregations.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index 2a45454d35..91f0685349 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -177,7 +177,12 @@ def _extensive_calc(self, name) -> ureg.Quantity: def _bool_calc(self, name) -> bool: """bool properties getter bool_attributes = ['with_cooling', 'with_heating', 'with_ahu', - 'use_maintained_illuminance']""" + 'use_maintained_illuminance'] + + If the attribute is True for at least one zone, the aggregations + attribute will be set to True as well. This is a simplified approach + and might need adjustments in the future depending on the attribute. + """ # todo: log # only calculate intensive calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): From 78291647052c63db2b86f340bf96b3cb802a4957 Mon Sep 17 00:00:00 2001 From: David Jansen Date: Wed, 21 May 2025 23:52:53 +0200 Subject: [PATCH 8/8] Update bim2sim/elements/aggregation/bps_aggregations.py Co-authored-by: Veronika Richter <48535180+veronikarichter@users.noreply.github.com> --- bim2sim/elements/aggregation/bps_aggregations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bim2sim/elements/aggregation/bps_aggregations.py b/bim2sim/elements/aggregation/bps_aggregations.py index 91f0685349..d91a941024 100644 --- a/bim2sim/elements/aggregation/bps_aggregations.py +++ b/bim2sim/elements/aggregation/bps_aggregations.py @@ -184,7 +184,7 @@ def _bool_calc(self, name) -> bool: and might need adjustments in the future depending on the attribute. """ # todo: log - # only calculate intensive calc if all zones have this attribute + # only calculate bool calc if all zones have this attribute if all([getattr(tz, name) is not None for tz in self.elements]): prop_bool = False for tz in self.elements: