mabuseif commited on
Commit
2004b86
·
verified ·
1 Parent(s): f02f762

Update utils/cooling_load.py

Browse files
Files changed (1) hide show
  1. utils/cooling_load.py +120 -19
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
@@ -220,6 +220,7 @@ class CoolingLoadCalculator:
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)},
225
  'lights': {h: 0.0 for h in range(1, 25)},
@@ -237,7 +238,7 @@ class CoolingLoadCalculator:
237
  outdoor_conditions['temperature'],
238
  indoor_conditions['temperature'],
239
  outdoor_conditions.get('relative_humidity', 50.0),
240
- indoor_conditions.get('relative_humidity', 50.0)
241
  )
242
 
243
  latitude = self.validate_latitude(outdoor_conditions.get('latitude', '32N'))
@@ -283,7 +284,10 @@ 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']
@@ -298,6 +302,23 @@ class CoolingLoadCalculator:
298
  )
299
  hourly_loads['doors'][hour + 1] += load
300
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  # Calculate internal loads
302
  for hour in range(24):
303
  # People loads
@@ -382,6 +403,7 @@ class CoolingLoadCalculator:
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],
387
  hourly_loads['lights'][hour],
@@ -403,6 +425,7 @@ class CoolingLoadCalculator:
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],
408
  'lights': hourly_loads['lights'][design_hour],
@@ -439,6 +462,7 @@ class CoolingLoadCalculator:
439
  design_loads['windows_conduction'] +
440
  design_loads['windows_solar'] +
441
  design_loads['doors'] +
 
442
  design_loads['people_sensible'] +
443
  design_loads['lights'] +
444
  design_loads['equipment_sensible'] +
@@ -634,7 +658,10 @@ 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 +675,9 @@ 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
@@ -663,19 +693,10 @@ class CoolingLoadCalculator:
663
  cltd = outdoor_temp - indoor_temp
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:
672
- logger.debug(f"Using drapery shading coefficient: {effective_shading_coefficient}")
673
- except Exception as e:
674
- if self.debug_mode:
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 +729,85 @@ 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,
@@ -1036,7 +1136,8 @@ if __name__ == "__main__":
1036
  'walls': [Wall(id="w1", name="North Wall", area=20.0, u_value=0.5, orientation=Orientation.NORTH, wall_group='A', solar_absorptivity=0.6)],
1037
  'roofs': [Roof(id="r1", name="Main Roof", area=100.0, u_value=0.3, orientation=Orientation.HORIZONTAL, roof_group='A', solar_absorptivity=0.6)],
1038
  'windows': [Window(id="win1", name="South Window", area=10.0, u_value=2.8, orientation=Orientation.SOUTH, shgc=0.7, shading_coefficient=0.8)],
1039
- 'doors': [Door(id="d1", name="Main Door", area=2.0, u_value=2.0, orientation=Orientation.NORTH)]
 
1040
  }
1041
  outdoor_conditions = {
1042
  'temperature': 35.0,
 
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
 
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
+ 'skylights': {h: 0.0 for h in range(1, 25)}, # Added for skylights
224
  'people_sensible': {h: 0.0 for h in range(1, 25)},
225
  'people_latent': {h: 0.0 for h in range(1, 25)},
226
  'lights': {h: 0.0 for h in range(1, 25)},
 
238
  outdoor_conditions['temperature'],
239
  indoor_conditions['temperature'],
240
  outdoor_conditions.get('relative_humidity', 50.0),
241
+ indoor_conditions.get('relative_hierarchy', 50.0)
242
  )
243
 
244
  latitude = self.validate_latitude(outdoor_conditions.get('latitude', '32N'))
 
284
  month=month,
285
  hour=hour,
286
  latitude=latitude,
287
+ shading_coefficient=window.shading_coefficient,
288
+ adjusted_shgc=window.shgc, # Default to base SHGC
289
+ glazing_type=window.glazing_type,
290
+ frame_type=window.frame_type
291
  )
292
  hourly_loads['windows_conduction'][hour + 1] += load_dict['conduction']
293
  hourly_loads['windows_solar'][hour + 1] += load_dict['solar']
 
302
  )
303
  hourly_loads['doors'][hour + 1] += load
304
 
305
+ # Calculate loads for skylights
306
+ for skylight in building_components.get('skylights', []):
307
+ for hour in range(24):
308
+ load_dict = self.calculate_skylight_cooling_load(
309
+ skylight=skylight,
310
+ outdoor_temp=outdoor_conditions['temperature'],
311
+ indoor_temp=indoor_conditions['temperature'],
312
+ month=month,
313
+ hour=hour,
314
+ latitude=latitude,
315
+ shading_coefficient=skylight.shading_coefficient,
316
+ adjusted_shgc=skylight.shgc, # Default to base SHGC
317
+ glazing_type=skylight.glazing_type,
318
+ frame_type=skylight.frame_type
319
+ )
320
+ hourly_loads['skylights'][hour + 1] += load_dict['total']
321
+
322
  # Calculate internal loads
323
  for hour in range(24):
324
  # People loads
 
403
  hourly_loads['windows_conduction'][hour],
404
  hourly_loads['windows_solar'][hour],
405
  hourly_loads['doors'][hour],
406
+ hourly_loads['skylights'][hour], # Added for skylights
407
  hourly_loads['people_sensible'][hour],
408
  hourly_loads['people_latent'][hour],
409
  hourly_loads['lights'][hour],
 
425
  'windows_conduction': hourly_loads['windows_conduction'][design_hour],
426
  'windows_solar': hourly_loads['windows_solar'][design_hour],
427
  'doors': hourly_loads['doors'][design_hour],
428
+ 'skylights': hourly_loads['skylights'][design_hour], # Added for skylights
429
  'people_sensible': hourly_loads['people_sensible'][design_hour],
430
  'people_latent': hourly_loads['people_latent'][design_hour],
431
  'lights': hourly_loads['lights'][design_hour],
 
462
  design_loads['windows_conduction'] +
463
  design_loads['windows_solar'] +
464
  design_loads['doors'] +
465
+ design_loads['skylights'] + # Added for skylights
466
  design_loads['people_sensible'] +
467
  design_loads['lights'] +
468
  design_loads['equipment_sensible'] +
 
658
  month: str,
659
  hour: int,
660
  latitude: str,
661
+ shading_coefficient: float,
662
+ adjusted_shgc: float,
663
+ glazing_type: Any,
664
+ frame_type: Any
665
  ) -> Dict[str, float]:
666
  """
667
  Calculate cooling load for a window (conduction and solar).
 
675
  hour: Hour of the day
676
  latitude: Latitude (e.g., '24N')
677
  shading_coefficient: Default shading coefficient
678
+ adjusted_shgc: Adjusted SHGC accounting for drapery (from Drapery.get_shading_coefficient)
679
+ glazing_type: Window glazing type
680
+ frame_type: Window frame type
681
 
682
  Returns:
683
  Dictionary with conduction, solar, and total loads in Watts
 
693
  cltd = outdoor_temp - indoor_temp
694
  conduction_load = window.u_value * window.area * cltd
695
 
696
+ # Use adjusted_shgc if provided, otherwise fall back to shading_coefficient
697
+ effective_shading_coefficient = adjusted_shgc if adjusted_shgc is not None else shading_coefficient
698
+ if self.debug_mode:
699
+ logger.debug(f"Window {window.name}: adjusted_shgc={adjusted_shgc}, effective_shading_coefficient={effective_shading_coefficient}")
 
 
 
 
 
 
 
 
 
700
 
701
  # Solar load
702
  try:
 
729
  logger.error(f"Error in calculate_window_cooling_load: {str(e)}")
730
  raise Exception(f"Error in calculate_window_cooling_load: {str(e)}")
731
 
732
+ def calculate_skylight_cooling_load(
733
+ self,
734
+ skylight: Skylight,
735
+ outdoor_temp: float,
736
+ indoor_temp: float,
737
+ month: str,
738
+ hour: int,
739
+ latitude: str,
740
+ shading_coefficient: float,
741
+ adjusted_shgc: float,
742
+ glazing_type: Any,
743
+ frame_type: Any
744
+ ) -> Dict[str, float]:
745
+ """
746
+ Calculate cooling load for a skylight (conduction and solar).
747
+ Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 18, Equations 18.12-18.13.
748
+
749
+ Args:
750
+ skylight: Skylight component
751
+ outdoor_temp: Outdoor temperature (°C)
752
+ indoor_temp: Indoor temperature (°C)
753
+ month: Design month
754
+ hour: Hour of the day
755
+ latitude: Latitude (e.g., '24N')
756
+ shading_coefficient: Default shading coefficient
757
+ adjusted_shgc: Adjusted SHGC accounting for drapery (from Drapery.get_shading_coefficient)
758
+ glazing_type: Skylight glazing type
759
+ frame_type: Skylight frame type
760
+
761
+ Returns:
762
+ Dictionary with conduction, solar, and total loads in Watts
763
+ """
764
+ try:
765
+ latitude = self.validate_latitude(latitude)
766
+ month = self.validate_month(month)
767
+ hour = self.validate_hour(hour)
768
+ if self.debug_mode:
769
+ logger.debug(f"calculate_skylight_cooling_load: latitude={latitude}, month={month}, hour={hour}, orientation=Horizontal")
770
+
771
+ # Conduction load
772
+ cltd = outdoor_temp - indoor_temp
773
+ conduction_load = skylight.u_value * skylight.area * cltd
774
+
775
+ # Use adjusted_shgc if provided, otherwise fall back to shading_coefficient
776
+ effective_shading_coefficient = adjusted_shgc if adjusted_shgc is not None else shading_coefficient
777
+ if self.debug_mode:
778
+ logger.debug(f"Skylight {skylight.name}: adjusted_shgc={adjusted_shgc}, effective_shading_coefficient={effective_shading_coefficient}")
779
+
780
+ # Solar load (skylights use horizontal orientation)
781
+ try:
782
+ scl = self.ashrae_tables.get_scl(
783
+ latitude=latitude,
784
+ month=month,
785
+ orientation='Horizontal',
786
+ hour=hour
787
+ )
788
+ except Exception as e:
789
+ if self.debug_mode:
790
+ logger.error(f"get_scl failed for latitude={latitude}, month={month}, orientation=Horizontal: {str(e)}")
791
+ logger.warning("Using default SCL=100 W/m²")
792
+ scl = 100.0
793
+
794
+ solar_load = skylight.area * skylight.shgc * effective_shading_coefficient * scl
795
+
796
+ total_load = conduction_load + solar_load
797
+ if self.debug_mode:
798
+ logger.debug(f"Skylight load: conduction={conduction_load}, solar={solar_load}, total={total_load}, effective_shading_coefficient={effective_shading_coefficient}")
799
+
800
+ return {
801
+ 'conduction': max(conduction_load, 0.0),
802
+ 'solar': max(solar_load, 0.0),
803
+ 'total': max(total_load, 0.0)
804
+ }
805
+
806
+ except Exception as e:
807
+ if self.debug_mode:
808
+ logger.error(f"Error in calculate_skylight_cooling_load: {str(e)}")
809
+ raise Exception(f"Error in calculate_skylight_cooling_load: {str(e)}")
810
+
811
  def calculate_door_cooling_load(
812
  self,
813
  door: Door,
 
1136
  'walls': [Wall(id="w1", name="North Wall", area=20.0, u_value=0.5, orientation=Orientation.NORTH, wall_group='A', solar_absorptivity=0.6)],
1137
  'roofs': [Roof(id="r1", name="Main Roof", area=100.0, u_value=0.3, orientation=Orientation.HORIZONTAL, roof_group='A', solar_absorptivity=0.6)],
1138
  'windows': [Window(id="win1", name="South Window", area=10.0, u_value=2.8, orientation=Orientation.SOUTH, shgc=0.7, shading_coefficient=0.8)],
1139
+ 'doors': [Door(id="d1", name="Main Door", area=2.0, u_value=2.0, orientation=Orientation.NORTH)],
1140
+ 'skylights': [Skylight(id="sky1", name="Roof Skylight", area=5.0, u_value=3.0, shgc=0.6, shading_coefficient=0.9)]
1141
  }
1142
  outdoor_conditions = {
1143
  'temperature': 35.0,