mabuseif commited on
Commit
7c6670b
·
verified ·
1 Parent(s): 7007eef

Update utils/cooling_load.py

Browse files
Files changed (1) hide show
  1. utils/cooling_load.py +120 -7
utils/cooling_load.py CHANGED
@@ -5,7 +5,7 @@ Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 18, Section 18.5.
5
 
6
  Author: Dr Majed Abuseif
7
  Date: April 2025
8
- Version: 1.0.6
9
  """
10
 
11
  from typing import Dict, List, Any, Optional, Tuple
@@ -14,7 +14,7 @@ import logging
14
  from data.ashrae_tables import ASHRAETables
15
  from utils.heat_transfer import HeatTransferCalculations
16
  from utils.psychrometrics import Psychrometrics
17
- from app.component_selection import Wall, Roof, Window, Door, Orientation
18
  from data.drapery import Drapery
19
 
20
  # Set up logging
@@ -219,6 +219,8 @@ class CoolingLoadCalculator:
219
  'roofs': {h: 0.0 for h in range(1, 25)},
220
  'windows_conduction': {h: 0.0 for h in range(1, 25)},
221
  'windows_solar': {h: 0.0 for h in range(1, 25)},
 
 
222
  'doors': {h: 0.0 for h in range(1, 25)},
223
  'people_sensible': {h: 0.0 for h in range(1, 25)},
224
  'people_latent': {h: 0.0 for h in range(1, 25)},
@@ -276,6 +278,7 @@ class CoolingLoadCalculator:
276
  # Calculate loads for windows
277
  for window in building_components.get('windows', []):
278
  for hour in range(24):
 
279
  load_dict = self.calculate_window_cooling_load(
280
  window=window,
281
  outdoor_temp=outdoor_conditions['temperature'],
@@ -283,11 +286,29 @@ class CoolingLoadCalculator:
283
  month=month,
284
  hour=hour,
285
  latitude=latitude,
286
- shading_coefficient=window.shading_coefficient
 
287
  )
288
  hourly_loads['windows_conduction'][hour + 1] += load_dict['conduction']
289
  hourly_loads['windows_solar'][hour + 1] += load_dict['solar']
290
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  # Calculate loads for doors
292
  for door in building_components.get('doors', []):
293
  for hour in range(24):
@@ -381,6 +402,8 @@ class CoolingLoadCalculator:
381
  hourly_loads['roofs'][hour],
382
  hourly_loads['windows_conduction'][hour],
383
  hourly_loads['windows_solar'][hour],
 
 
384
  hourly_loads['doors'][hour],
385
  hourly_loads['people_sensible'][hour],
386
  hourly_loads['people_latent'][hour],
@@ -402,6 +425,8 @@ class CoolingLoadCalculator:
402
  'roofs': hourly_loads['roofs'][design_hour],
403
  'windows_conduction': hourly_loads['windows_conduction'][design_hour],
404
  'windows_solar': hourly_loads['windows_solar'][design_hour],
 
 
405
  'doors': hourly_loads['doors'][design_hour],
406
  'people_sensible': hourly_loads['people_sensible'][design_hour],
407
  'people_latent': hourly_loads['people_latent'][design_hour],
@@ -438,6 +463,8 @@ class CoolingLoadCalculator:
438
  design_loads['roofs'] +
439
  design_loads['windows_conduction'] +
440
  design_loads['windows_solar'] +
 
 
441
  design_loads['doors'] +
442
  design_loads['people_sensible'] +
443
  design_loads['lights'] +
@@ -634,7 +661,8 @@ class CoolingLoadCalculator:
634
  month: str,
635
  hour: int,
636
  latitude: str,
637
- shading_coefficient: float
 
638
  ) -> Dict[str, float]:
639
  """
640
  Calculate cooling load for a window (conduction and solar).
@@ -648,6 +676,7 @@ class CoolingLoadCalculator:
648
  hour: Hour of the day
649
  latitude: Latitude (e.g., '24N')
650
  shading_coefficient: Default shading coefficient
 
651
 
652
  Returns:
653
  Dictionary with conduction, solar, and total loads in Watts
@@ -664,8 +693,8 @@ class CoolingLoadCalculator:
664
  conduction_load = window.u_value * window.area * cltd
665
 
666
  # Determine shading coefficient
667
- effective_shading_coefficient = shading_coefficient
668
- if hasattr(window, 'drapery') and window.drapery and window.drapery.enabled:
669
  try:
670
  effective_shading_coefficient = window.drapery.get_shading_coefficient(window.shgc)
671
  if self.debug_mode:
@@ -675,7 +704,7 @@ class CoolingLoadCalculator:
675
  logger.warning(f"Error getting drapery shading coefficient: {str(e)}. Using default shading_coefficient={shading_coefficient}")
676
  else:
677
  if self.debug_mode:
678
- logger.debug(f"No enabled drapery. Using default shading_coefficient={shading_coefficient}")
679
 
680
  # Solar load
681
  try:
@@ -708,6 +737,90 @@ class CoolingLoadCalculator:
708
  logger.error(f"Error in calculate_window_cooling_load: {str(e)}")
709
  raise Exception(f"Error in calculate_window_cooling_load: {str(e)}")
710
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  def calculate_door_cooling_load(
712
  self,
713
  door: Door,
 
5
 
6
  Author: Dr Majed Abuseif
7
  Date: April 2025
8
+ Version: 1.0.7
9
  """
10
 
11
  from typing import Dict, List, Any, Optional, Tuple
 
14
  from data.ashrae_tables import ASHRAETables
15
  from utils.heat_transfer import HeatTransferCalculations
16
  from utils.psychrometrics import Psychrometrics
17
+ from app.component_selection import Wall, Roof, Window, Door, Skylight, Orientation
18
  from data.drapery import Drapery
19
 
20
  # Set up logging
 
219
  'roofs': {h: 0.0 for h in range(1, 25)},
220
  'windows_conduction': {h: 0.0 for h in range(1, 25)},
221
  'windows_solar': {h: 0.0 for h in range(1, 25)},
222
+ 'skylights_conduction': {h: 0.0 for h in range(1, 25)},
223
+ 'skylights_solar': {h: 0.0 for h in range(1, 25)},
224
  'doors': {h: 0.0 for h in range(1, 25)},
225
  'people_sensible': {h: 0.0 for h in range(1, 25)},
226
  'people_latent': {h: 0.0 for h in range(1, 25)},
 
278
  # Calculate loads for windows
279
  for window in building_components.get('windows', []):
280
  for hour in range(24):
281
+ adjusted_shgc = getattr(window, 'adjusted_shgc', None)
282
  load_dict = self.calculate_window_cooling_load(
283
  window=window,
284
  outdoor_temp=outdoor_conditions['temperature'],
 
286
  month=month,
287
  hour=hour,
288
  latitude=latitude,
289
+ shading_coefficient=window.shading_coefficient,
290
+ adjusted_shgc=adjusted_shgc
291
  )
292
  hourly_loads['windows_conduction'][hour + 1] += load_dict['conduction']
293
  hourly_loads['windows_solar'][hour + 1] += load_dict['solar']
294
 
295
+ # Calculate loads for skylights
296
+ for skylight in building_components.get('skylights', []):
297
+ for hour in range(24):
298
+ adjusted_shgc = getattr(skylight, 'adjusted_shgc', None)
299
+ load_dict = self.calculate_skylight_cooling_load(
300
+ skylight=skylight,
301
+ outdoor_temp=outdoor_conditions['temperature'],
302
+ indoor_temp=indoor_conditions['temperature'],
303
+ month=month,
304
+ hour=hour,
305
+ latitude=latitude,
306
+ shading_coefficient=skylight.shading_coefficient,
307
+ adjusted_shgc=adjusted_shgc
308
+ )
309
+ hourly_loads['skylights_conduction'][hour + 1] += load_dict['conduction']
310
+ hourly_loads['skylights_solar'][hour + 1] += load_dict['solar']
311
+
312
  # Calculate loads for doors
313
  for door in building_components.get('doors', []):
314
  for hour in range(24):
 
402
  hourly_loads['roofs'][hour],
403
  hourly_loads['windows_conduction'][hour],
404
  hourly_loads['windows_solar'][hour],
405
+ hourly_loads['skylights_conduction'][hour],
406
+ hourly_loads['skylights_solar'][hour],
407
  hourly_loads['doors'][hour],
408
  hourly_loads['people_sensible'][hour],
409
  hourly_loads['people_latent'][hour],
 
425
  'roofs': hourly_loads['roofs'][design_hour],
426
  'windows_conduction': hourly_loads['windows_conduction'][design_hour],
427
  'windows_solar': hourly_loads['windows_solar'][design_hour],
428
+ 'skylights_conduction': hourly_loads['skylights_conduction'][design_hour],
429
+ 'skylights_solar': hourly_loads['skylights_solar'][design_hour],
430
  'doors': hourly_loads['doors'][design_hour],
431
  'people_sensible': hourly_loads['people_sensible'][design_hour],
432
  'people_latent': hourly_loads['people_latent'][design_hour],
 
463
  design_loads['roofs'] +
464
  design_loads['windows_conduction'] +
465
  design_loads['windows_solar'] +
466
+ design_loads['skylights_conduction'] +
467
+ design_loads['skylights_solar'] +
468
  design_loads['doors'] +
469
  design_loads['people_sensible'] +
470
  design_loads['lights'] +
 
661
  month: str,
662
  hour: int,
663
  latitude: str,
664
+ shading_coefficient: float,
665
+ adjusted_shgc: Optional[float] = None
666
  ) -> Dict[str, float]:
667
  """
668
  Calculate cooling load for a window (conduction and solar).
 
676
  hour: Hour of the day
677
  latitude: Latitude (e.g., '24N')
678
  shading_coefficient: Default shading coefficient
679
+ adjusted_shgc: Adjusted SHGC from external drapery calculation (optional)
680
 
681
  Returns:
682
  Dictionary with conduction, solar, and total loads in Watts
 
693
  conduction_load = window.u_value * window.area * cltd
694
 
695
  # Determine shading coefficient
696
+ effective_shading_coefficient = adjusted_shgc if adjusted_shgc is not None else shading_coefficient
697
+ if adjusted_shgc is None and hasattr(window, 'drapery') and window.drapery and window.drapery.enabled:
698
  try:
699
  effective_shading_coefficient = window.drapery.get_shading_coefficient(window.shgc)
700
  if self.debug_mode:
 
704
  logger.warning(f"Error getting drapery shading coefficient: {str(e)}. Using default shading_coefficient={shading_coefficient}")
705
  else:
706
  if self.debug_mode:
707
+ logger.debug(f"Using shading coefficient: {effective_shading_coefficient} (adjusted_shgc={adjusted_shgc}, drapery={'enabled' if hasattr(window, 'drapery') and window.drapery and window.drapery.enabled else 'disabled'})")
708
 
709
  # Solar load
710
  try:
 
737
  logger.error(f"Error in calculate_window_cooling_load: {str(e)}")
738
  raise Exception(f"Error in calculate_window_cooling_load: {str(e)}")
739
 
740
+ def calculate_skylight_cooling_load(
741
+ self,
742
+ skylight: Skylight,
743
+ outdoor_temp: float,
744
+ indoor_temp: float,
745
+ month: str,
746
+ hour: int,
747
+ latitude: str,
748
+ shading_coefficient: float,
749
+ adjusted_shgc: Optional[float] = None
750
+ ) -> Dict[str, float]:
751
+ """
752
+ Calculate cooling load for a skylight (conduction and solar).
753
+ Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 18, Equations 18.12-18.13.
754
+
755
+ Args:
756
+ skylight: Skylight component
757
+ outdoor_temp: Outdoor temperature (°C)
758
+ indoor_temp: Indoor temperature (°C)
759
+ month: Design month
760
+ hour: Hour of the day
761
+ latitude: Latitude (e.g., '24N')
762
+ shading_coefficient: Default shading coefficient
763
+ adjusted_shgc: Adjusted SHGC from external drapery calculation (optional)
764
+
765
+ Returns:
766
+ Dictionary with conduction, solar, and total loads in Watts
767
+ """
768
+ try:
769
+ latitude = self.validate_latitude(latitude)
770
+ month = self.validate_month(month)
771
+ hour = self.validate_hour(hour)
772
+ if self.debug_mode:
773
+ logger.debug(f"calculate_skylight_cooling_load: latitude={latitude}, month={month}, hour={hour}, orientation=Horizontal")
774
+
775
+ # Conduction load
776
+ cltd = outdoor_temp - indoor_temp
777
+ conduction_load = skylight.u_value * skylight.area * cltd
778
+
779
+ # Determine shading coefficient
780
+ effective_shading_coefficient = adjusted_shgc if adjusted_shgc is not None else shading_coefficient
781
+ if adjusted_shgc is None and hasattr(skylight, 'drapery') and skylight.drapery and skylight.drapery.enabled:
782
+ try:
783
+ effective_shading_coefficient = skylight.drapery.get_shading_coefficient(skylight.shgc)
784
+ if self.debug_mode:
785
+ logger.debug(f"Using drapery shading coefficient: {effective_shading_coefficient}")
786
+ except Exception as e:
787
+ if self.debug_mode:
788
+ logger.warning(f"Error getting drapery shading coefficient: {str(e)}. Using default shading_coefficient={shading_coefficient}")
789
+ else:
790
+ if self.debug_mode:
791
+ logger.debug(f"Using shading coefficient: {effective_shading_coefficient} (adjusted_shgc={adjusted_shgc}, drapery={'enabled' if hasattr(skylight, 'drapery') and skylight.drapery and skylight.drapery.enabled else 'disabled'})")
792
+
793
+ # Solar load
794
+ try:
795
+ scl = self.ashrae_tables.get_scl(
796
+ latitude=latitude,
797
+ month=month,
798
+ orientation='Horizontal',
799
+ hour=hour
800
+ )
801
+ except Exception as e:
802
+ if self.debug_mode:
803
+ logger.error(f"get_scl failed for latitude={latitude}, month={month}, orientation=Horizontal: {str(e)}")
804
+ logger.warning("Using default SCL=100 W/m²")
805
+ scl = 100.0
806
+
807
+ solar_load = skylight.area * skylight.shgc * effective_shading_coefficient * scl
808
+
809
+ total_load = conduction_load + solar_load
810
+ if self.debug_mode:
811
+ logger.debug(f"Skylight load: conduction={conduction_load}, solar={solar_load}, total={total_load}, effective_shading_coefficient={effective_shading_coefficient}")
812
+
813
+ return {
814
+ 'conduction': max(conduction_load, 0.0),
815
+ 'solar': max(solar_load, 0.0),
816
+ 'total': max(total_load, 0.0)
817
+ }
818
+
819
+ except Exception as e:
820
+ if self.debug_mode:
821
+ logger.error(f"Error in calculate_skylight_cooling_load: {str(e)}")
822
+ raise Exception(f"Error in calculate_skylight_cooling_load: {str(e)}")
823
+
824
  def calculate_door_cooling_load(
825
  self,
826
  door: Door,