Spaces:
Sleeping
Sleeping
| """ | |
| Cooling load calculation module for HVAC Load Calculator. | |
| Based on ASHRAE steady-state calculation methods. | |
| Author: Dr Majed Abuseif | |
| Date: March 2025 | |
| Version: 1.0.0 | |
| """ | |
| from typing import Dict, List, Any, Optional, Tuple | |
| import numpy as np | |
| from datetime import datetime | |
| from data.ashrae_tables import ASHRAETables | |
| from utils.heat_transfer import HeatTransferCalculations | |
| from app.component_selection import Wall, Roof, Window, Door, Orientation | |
| class CoolingLoadCalculator: | |
| """Class for cooling load calculations.""" | |
| def __init__(self): | |
| """Initialize cooling load calculator.""" | |
| self.ashrae_tables = ASHRAETables() | |
| self.heat_transfer = HeatTransferCalculations() | |
| self.hours = list(range(1, 25)) | |
| def calculate_hourly_cooling_loads( | |
| self, | |
| building_components: Dict[str, List[Any]], | |
| outdoor_conditions: Dict[str, Any], | |
| indoor_conditions: Dict[str, Any], | |
| internal_loads: Dict[str, Any], | |
| building_volume: float | |
| ) -> Dict[str, Any]: | |
| """ | |
| Calculate hourly cooling loads for all components. | |
| Args: | |
| building_components: Dictionary of building components | |
| outdoor_conditions: Outdoor weather conditions | |
| indoor_conditions: Indoor design conditions | |
| internal_loads: Internal heat gains | |
| building_volume: Building volume in cubic meters | |
| Returns: | |
| Dictionary containing hourly cooling loads | |
| """ | |
| hourly_loads = { | |
| 'walls': {h: 0.0 for h in self.hours}, | |
| 'roofs': {h: 0.0 for h in self.hours}, | |
| 'windows_conduction': {h: 0.0 for h in self.hours}, | |
| 'windows_solar': {h: 0.0 for h in self.hours}, | |
| 'doors': {h: 0.0 for h in self.hours}, | |
| 'people_sensible': {h: 0.0 for h in self.hours}, | |
| 'people_latent': {h: 0.0 for h in self.hours}, | |
| 'lights': {h: 0.0 for h in self.hours}, | |
| 'equipment_sensible': {h: 0.0 for h in self.hours}, | |
| 'equipment_latent': {h: 0.0 for h in self.hours}, | |
| 'infiltration_sensible': {h: 0.0 for h in self.hours}, | |
| 'infiltration_latent': {h: 0.0 for h in self.hours}, | |
| 'ventilation_sensible': {h: 0.0 for h in self.hours}, | |
| 'ventilation_latent': {h: 0.0 for h in self.hours} | |
| } | |
| try: | |
| # Calculate loads for walls | |
| for wall in building_components.get('walls', []): | |
| for hour in self.hours: | |
| load = self.calculate_wall_cooling_load( | |
| wall=wall, | |
| outdoor_temp=outdoor_conditions['temperature'], | |
| indoor_temp=indoor_conditions['temperature'], | |
| month=outdoor_conditions['month'], | |
| hour=hour, | |
| latitude=outdoor_conditions['latitude'] | |
| ) | |
| hourly_loads['walls'][hour] += load | |
| # Calculate loads for roofs | |
| for roof in building_components.get('roofs', []): | |
| for hour in self.hours: | |
| load = self.calculate_roof_cooling_load( | |
| roof=roof, | |
| outdoor_temp=outdoor_conditions['temperature'], | |
| indoor_temp=indoor_conditions['temperature'], | |
| month=outdoor_conditions['month'], | |
| hour=hour, | |
| latitude=outdoor_conditions['latitude'] | |
| ) | |
| hourly_loads['roofs'][hour] += load | |
| # Calculate loads for windows | |
| for window in building_components.get('windows', []): | |
| for hour in self.hours: | |
| load_dict = self.calculate_window_cooling_load( | |
| window=window, | |
| outdoor_temp=outdoor_conditions['temperature'], | |
| indoor_temp=indoor_conditions['temperature'], | |
| month=outdoor_conditions['month'], | |
| hour=hour, | |
| latitude=outdoor_conditions['latitude'], | |
| shading_coefficient=window.shading_coefficient | |
| ) | |
| hourly_loads['windows_conduction'][hour] += load_dict['conduction'] | |
| hourly_loads['windows_solar'][hour] += load_dict['solar'] | |
| # Calculate loads for doors | |
| for door in building_components.get('doors', []): | |
| for hour in self.hours: | |
| load = self.calculate_door_cooling_load( | |
| door=door, | |
| outdoor_temp=outdoor_conditions['temperature'], | |
| indoor_temp=indoor_conditions['temperature'] | |
| ) | |
| hourly_loads['doors'][hour] += load | |
| # Calculate internal loads | |
| for hour in self.hours: | |
| # People loads | |
| people_load = self.calculate_people_cooling_load( | |
| num_people=internal_loads['people']['number'], | |
| activity_level=internal_loads['people']['activity_level'], | |
| hour=hour | |
| ) | |
| hourly_loads['people_sensible'][hour] += people_load['sensible'] | |
| hourly_loads['people_latent'][hour] += people_load['latent'] | |
| # Lighting loads | |
| lights_load = self.calculate_lights_cooling_load( | |
| power=internal_loads['lights']['power'], | |
| use_factor=internal_loads['lights']['use_factor'], | |
| special_allowance=internal_loads['lights']['special_allowance'], | |
| hour=hour | |
| ) | |
| hourly_loads['lights'][hour] += lights_load | |
| # Equipment loads | |
| equipment_load = self.calculate_equipment_cooling_load( | |
| power=internal_loads['equipment']['power'], | |
| use_factor=internal_loads['equipment']['use_factor'], | |
| radiation_factor=internal_loads['equipment']['radiation_factor'], | |
| hour=hour | |
| ) | |
| hourly_loads['equipment_sensible'][hour] += equipment_load['sensible'] | |
| hourly_loads['equipment_latent'][hour] += equipment_load['latent'] | |
| # Infiltration loads | |
| infiltration_load = self.calculate_infiltration_cooling_load( | |
| flow_rate=internal_loads['infiltration']['flow_rate'], | |
| building_volume=building_volume, | |
| outdoor_temp=outdoor_conditions['temperature'], | |
| outdoor_rh=outdoor_conditions['relative_humidity'], | |
| indoor_temp=indoor_conditions['temperature'], | |
| indoor_rh=indoor_conditions['relative_humidity'] | |
| ) | |
| hourly_loads['infiltration_sensible'][hour] += infiltration_load['sensible'] | |
| hourly_loads['infiltration_latent'][hour] += infiltration_load['latent'] | |
| # Ventilation loads | |
| ventilation_load = self.calculate_ventilation_cooling_load( | |
| flow_rate=internal_loads['ventilation']['flow_rate'], | |
| outdoor_temp=outdoor_conditions['temperature'], | |
| outdoor_rh=outdoor_conditions['relative_humidity'], | |
| indoor_temp=indoor_conditions['temperature'], | |
| indoor_rh=indoor_conditions['relative_humidity'] | |
| ) | |
| hourly_loads['ventilation_sensible'][hour] += ventilation_load['sensible'] | |
| hourly_loads['ventilation_latent'][hour] += ventilation_load['latent'] | |
| return hourly_loads | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_hourly_cooling_loads: {str(e)}") | |
| def calculate_design_cooling_load(self, hourly_loads: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Calculate design cooling load based on peak hourly loads. | |
| Args: | |
| hourly_loads: Dictionary of hourly cooling loads | |
| Returns: | |
| Dictionary containing design cooling loads | |
| """ | |
| try: | |
| design_loads = {} | |
| total_loads = [] | |
| for hour in self.hours: | |
| total_load = sum([ | |
| hourly_loads['walls'][hour], | |
| hourly_loads['roofs'][hour], | |
| hourly_loads['windows_conduction'][hour], | |
| hourly_loads['windows_solar'][hour], | |
| hourly_loads['doors'][hour], | |
| hourly_loads['people_sensible'][hour], | |
| hourly_loads['people_latent'][hour], | |
| hourly_loads['lights'][hour], | |
| hourly_loads['equipment_sensible'][hour], | |
| hourly_loads['equipment_latent'][hour], | |
| hourly_loads['infiltration_sensible'][hour], | |
| hourly_loads['infiltration_latent'][hour], | |
| hourly_loads['ventilation_sensible'][hour], | |
| hourly_loads['ventilation_latent'][hour] | |
| ]) | |
| total_loads.append(total_load) | |
| design_hour = self.hours[np.argmax(total_loads)] | |
| design_loads = { | |
| 'design_hour': design_hour, | |
| 'walls': hourly_loads['walls'][design_hour], | |
| 'roofs': hourly_loads['roofs'][design_hour], | |
| 'windows_conduction': hourly_loads['windows_conduction'][design_hour], | |
| 'windows_solar': hourly_loads['windows_solar'][design_hour], | |
| 'doors': hourly_loads['doors'][design_hour], | |
| 'people_sensible': hourly_loads['people_sensible'][design_hour], | |
| 'people_latent': hourly_loads['people_latent'][design_hour], | |
| 'lights': hourly_loads['lights'][design_hour], | |
| 'equipment_sensible': hourly_loads['equipment_sensible'][design_hour], | |
| 'equipment_latent': hourly_loads['equipment_latent'][design_hour], | |
| 'infiltration_sensible': hourly_loads['infiltration_sensible'][design_hour], | |
| 'infiltration_latent': hourly_loads['infiltration_latent'][design_hour], | |
| 'ventilation_sensible': hourly_loads['ventilation_sensible'][design_hour], | |
| 'ventilation_latent': hourly_loads['ventilation_latent'][design_hour] | |
| } | |
| return design_loads | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_design_cooling_load: {str(e)}") | |
| def calculate_cooling_load_summary(self, design_loads: Dict[str, Any]) -> Dict[str, float]: | |
| """ | |
| Calculate summary of cooling loads. | |
| Args: | |
| design_loads: Dictionary of design cooling loads | |
| Returns: | |
| Dictionary containing cooling load summary | |
| """ | |
| try: | |
| total_sensible = ( | |
| design_loads['walls'] + | |
| design_loads['roofs'] + | |
| design_loads['windows_conduction'] + | |
| design_loads['windows_solar'] + | |
| design_loads['doors'] + | |
| design_loads['people_sensible'] + | |
| design_loads['lights'] + | |
| design_loads['equipment_sensible'] + | |
| design_loads['infiltration_sensible'] + | |
| design_loads['ventilation_sensible'] | |
| ) | |
| total_latent = ( | |
| design_loads['people_latent'] + | |
| design_loads['equipment_latent'] + | |
| design_loads['infiltration_latent'] + | |
| design_loads['ventilation_latent'] | |
| ) | |
| total = total_sensible + total_latent | |
| return { | |
| 'total_sensible': total_sensible, | |
| 'total_latent': total_latent, | |
| 'total': total | |
| } | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_cooling_load_summary: {str(e)}") | |
| def calculate_wall_cooling_load( | |
| self, | |
| wall: Wall, | |
| outdoor_temp: float, | |
| indoor_temp: float, | |
| month: str, | |
| hour: int, | |
| latitude: str | |
| ) -> float: | |
| """ | |
| Calculate cooling load for a wall. | |
| Args: | |
| wall: Wall component | |
| outdoor_temp: Outdoor temperature (°C) | |
| indoor_temp: Indoor temperature (°C) | |
| month: Design month | |
| hour: Hour of the day | |
| latitude: Latitude (e.g., '40N') | |
| Returns: | |
| Cooling load in Watts | |
| """ | |
| try: | |
| cltd = self.ashrae_tables.calculate_corrected_cltd_wall( | |
| wall_group=wall.wall_group, | |
| orientation=wall.orientation.value, | |
| hour=hour, | |
| color='Dark', | |
| month=month, | |
| latitude=latitude, | |
| indoor_temp=indoor_temp, | |
| outdoor_temp=outdoor_temp | |
| ) | |
| load = wall.u_value * wall.area * cltd | |
| return max(load, 0.0) | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_wall_cooling_load: {str(e)}") | |
| def calculate_roof_cooling_load( | |
| self, | |
| roof: Roof, | |
| outdoor_temp: float, | |
| indoor_temp: float, | |
| month: str, | |
| hour: int, | |
| latitude: str | |
| ) -> float: | |
| """ | |
| Calculate cooling load for a roof. | |
| Args: | |
| roof: Roof component | |
| outdoor_temp: Outdoor temperature (°C) | |
| indoor_temp: Indoor temperature (°C) | |
| month: Design month | |
| hour: Hour of the day | |
| latitude: Latitude (e.g., '40N') | |
| Returns: | |
| Cooling load in Watts | |
| """ | |
| try: | |
| cltd = self.ashrae_tables.calculate_corrected_cltd_roof( | |
| roof_group=roof.roof_group, | |
| hour=hour, | |
| color='Dark', | |
| month=month, | |
| latitude=latitude, | |
| indoor_temp=indoor_temp, | |
| outdoor_temp=outdoor_temp | |
| ) | |
| load = roof.u_value * roof.area * cltd | |
| return max(load, 0.0) | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_roof_cooling_load: {str(e)}") | |
| def calculate_window_cooling_load( | |
| self, | |
| window: Window, | |
| outdoor_temp: float, | |
| indoor_temp: float, | |
| month: str, | |
| hour: int, | |
| latitude: str, | |
| shading_coefficient: float | |
| ) -> Dict[str, float]: | |
| """ | |
| Calculate cooling load for a window (conduction and solar). | |
| Args: | |
| window: Window component | |
| outdoor_temp: Outdoor temperature (°C) | |
| indoor_temp: Indoor temperature (°C) | |
| month: Design month | |
| hour: Hour of the day | |
| latitude: Latitude (e.g., '40N') | |
| shading_coefficient: Shading coefficient for drapery | |
| Returns: | |
| Dictionary with conduction and solar loads in Watts | |
| """ | |
| try: | |
| # Conduction load | |
| delta_t = outdoor_temp - indoor_temp | |
| conduction_load = window.u_value * window.area * delta_t | |
| # Solar load | |
| solar_altitude = self.heat_transfer.solar.solar_altitude | |
| scl_latitude = f"{float(latitude[:-1])}_{month.upper()}" | |
| scl = self.ashrae_tables.get_scl( | |
| orientation=window.orientation.value, | |
| hour=hour, | |
| latitude=scl_latitude | |
| ) | |
| solar_load = window.area * window.shgc * scl * shading_coefficient | |
| return { | |
| 'conduction': max(conduction_load, 0.0), | |
| 'solar': max(solar_load, 0.0), | |
| 'total': max(conduction_load + solar_load, 0.0) | |
| } | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_window_cooling_load: {str(e)}") | |
| def calculate_door_cooling_load( | |
| self, | |
| door: Door, | |
| outdoor_temp: float, | |
| indoor_temp: float | |
| ) -> float: | |
| """ | |
| Calculate cooling load for a door. | |
| Args: | |
| door: Door component | |
| outdoor_temp: Outdoor temperature (°C) | |
| indoor_temp: Indoor temperature (°C) | |
| Returns: | |
| Cooling load in Watts | |
| """ | |
| try: | |
| delta_t = outdoor_temp - indoor_temp | |
| load = door.u_value * door.area * delta_t | |
| return max(load, 0.0) | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_door_cooling_load: {str(e)}") | |
| def calculate_people_cooling_load( | |
| self, | |
| num_people: int, | |
| activity_level: str, | |
| hour: int | |
| ) -> Dict[str, float]: | |
| """ | |
| Calculate cooling load from people. | |
| Args: | |
| num_people: Number of people | |
| activity_level: Activity level | |
| hour: Hour of the day | |
| Returns: | |
| Dictionary with sensible and latent loads in Watts | |
| """ | |
| try: | |
| sensible_gain = { | |
| 'Seated/Resting': 70, | |
| 'Light Work': 100, | |
| 'Moderate Work': 150, | |
| 'Heavy Work': 200 | |
| }.get(activity_level, 70) | |
| latent_gain = { | |
| 'Seated/Resting': 45, | |
| 'Light Work': 75, | |
| 'Moderate Work': 120, | |
| 'Heavy Work': 180 | |
| }.get(activity_level, 45) | |
| clf = self.ashrae_tables.get_clf_people(hour, '8h') | |
| sensible_load = num_people * sensible_gain * clf | |
| latent_load = num_people * latent_gain | |
| return { | |
| 'sensible': max(sensible_load, 0.0), | |
| 'latent': max(latent_load, 0.0), | |
| 'total': max(sensible_load + latent_load, 0.0) | |
| } | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_people_cooling_load: {str(e)}") | |
| def calculate_lights_cooling_load( | |
| self, | |
| power: float, | |
| use_factor: float, | |
| special_allowance: float, | |
| hour: int | |
| ) -> float: | |
| """ | |
| Calculate cooling load from lighting. | |
| Args: | |
| power: Lighting power (W) | |
| use_factor: Usage factor | |
| special_allowance: Special allowance factor | |
| hour: Hour of the day | |
| Returns: | |
| Cooling load in Watts | |
| """ | |
| try: | |
| clf = self.ashrae_tables.get_clf_lights(hour, '8h') | |
| load = power * use_factor * special_allowance * clf | |
| return max(load, 0.0) | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_lights_cooling_load: {str(e)}") | |
| def calculate_equipment_cooling_load( | |
| self, | |
| power: float, | |
| use_factor: float, | |
| radiation_factor: float, | |
| hour: int | |
| ) -> Dict[str, float]: | |
| """ | |
| Calculate cooling load from equipment. | |
| Args: | |
| power: Equipment power (W) | |
| use_factor: Usage factor | |
| radiation_factor: Radiation factor | |
| hour: Hour of the day | |
| Returns: | |
| Dictionary with sensible and latent loads in Watts | |
| """ | |
| try: | |
| clf = self.ashrae_tables.get_clf_equipment(hour, '8h') | |
| sensible_load = power * use_factor * radiation_factor * clf | |
| latent_load = power * use_factor * (1 - radiation_factor) | |
| return { | |
| 'sensible': max(sensible_load, 0.0), | |
| 'latent': max(latent_load, 0.0), | |
| 'total': max(sensible_load + latent_load, 0.0) | |
| } | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_equipment_cooling_load: {str(e)}") | |
| def calculate_infiltration_cooling_load( | |
| self, | |
| flow_rate: float, | |
| building_volume: float, | |
| outdoor_temp: float, | |
| outdoor_rh: float, | |
| indoor_temp: float, | |
| indoor_rh: float | |
| ) -> Dict[str, float]: | |
| """ | |
| Calculate cooling load from infiltration. | |
| Args: | |
| flow_rate: Infiltration flow rate (m³/s) | |
| building_volume: Building volume (m³) | |
| outdoor_temp: Outdoor temperature (°C) | |
| outdoor_rh: Outdoor relative humidity (%) | |
| indoor_temp: Indoor temperature (°C) | |
| indoor_rh: Indoor relative humidity (%) | |
| Returns: | |
| Dictionary with sensible and latent loads in Watts | |
| """ | |
| try: | |
| air_changes_per_hour = (flow_rate * 3600) / building_volume | |
| sensible_load = 1.2 * flow_rate * 1000 * (outdoor_temp - indoor_temp) | |
| # Calculate humidity ratio difference | |
| outdoor_w = self.heat_transfer.psychrometrics.calculate_humidity_ratio(outdoor_temp, outdoor_rh) | |
| indoor_w = self.heat_transfer.psychrometrics.calculate_humidity_ratio(indoor_temp, indoor_rh) | |
| latent_load = 2501 * flow_rate * 1000 * (outdoor_w - indoor_w) | |
| return { | |
| 'sensible': max(sensible_load, 0.0), | |
| 'latent': max(latent_load, 0.0), | |
| 'total': max(sensible_load + latent_load, 0.0) | |
| } | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_infiltration_cooling_load: {str(e)}") | |
| def calculate_ventilation_cooling_load( | |
| self, | |
| flow_rate: float, | |
| outdoor_temp: float, | |
| outdoor_rh: float, | |
| indoor_temp: float, | |
| indoor_rh: float | |
| ) -> Dict[str, float]: | |
| """ | |
| Calculate cooling load from ventilation. | |
| Args: | |
| flow_rate: Ventilation flow rate (m³/s) | |
| outdoor_temp: Outdoor temperature (°C) | |
| outdoor_rh: Outdoor relative humidity (%) | |
| indoor_temp: Indoor temperature (°C) | |
| indoor_rh: Indoor relative humidity (%) | |
| Returns: | |
| Dictionary with sensible and latent loads in Watts | |
| """ | |
| try: | |
| sensible_load = 1.2 * flow_rate * 1000 * (outdoor_temp - indoor_temp) | |
| # Calculate humidity ratio difference | |
| outdoor_w = self.heat_transfer.psychrometrics.calculate_humidity_ratio(outdoor_temp, outdoor_rh) | |
| indoor_w = self.heat_transfer.psychrometrics.calculate_humidity_ratio(indoor_temp, indoor_rh) | |
| latent_load = 2501 * flow_rate * 1000 * (outdoor_w - indoor_w) | |
| return { | |
| 'sensible': max(sensible_load, 0.0), | |
| 'latent': max(latent_load, 0.0), | |
| 'total': max(sensible_load + latent_load, 0.0) | |
| } | |
| except Exception as e: | |
| raise Exception(f"Error in calculate_ventilation_cooling_load: {str(e)}") | |
| if __name__ == "__main__": | |
| # Example usage for testing | |
| calculator = CoolingLoadCalculator() | |
| # Dummy inputs | |
| building_components = { | |
| 'walls': [Wall( | |
| name="North Wall", | |
| orientation=Orientation.NORTH, | |
| area=20.0, | |
| u_value=0.5, | |
| wall_group="A" | |
| )], | |
| 'roofs': [Roof( | |
| name="Main Roof", | |
| orientation=Orientation.HORIZONTAL, | |
| area=100.0, | |
| u_value=0.3, | |
| roof_group="1" | |
| )], | |
| 'windows': [Window( | |
| name="South Window", | |
| orientation=Orientation.SOUTH, | |
| area=10.0, | |
| u_value=2.8, | |
| shgc=0.7, | |
| shading_device="Medium drapery", | |
| shading_coefficient=0.8 | |
| )], | |
| 'doors': [Door( | |
| name="Main Door", | |
| orientation=Orientation.NORTH, | |
| area=2.0, | |
| u_value=2.0 | |
| )] | |
| } | |
| outdoor_conditions = { | |
| 'temperature': 35.0, | |
| 'relative_humidity': 50.0, | |
| 'ground_temperature': 20.0, | |
| 'month': 'Jul', | |
| 'latitude': '31.973N', | |
| 'wind_speed': 4.0, | |
| 'day_of_year': 204 | |
| } | |
| indoor_conditions = { | |
| 'temperature': 24.0, | |
| 'relative_humidity': 50.0 | |
| } | |
| internal_loads = { | |
| 'people': { | |
| 'number': 10, | |
| 'activity_level': 'Seated/Resting', | |
| 'operating_hours': '8:00-18:00' | |
| }, | |
| 'lights': { | |
| 'power': 1000.0, | |
| 'use_factor': 0.8, | |
| 'special_allowance': 0.1, | |
| 'hours_operation': '8h' | |
| }, | |
| 'equipment': { | |
| 'power': 500.0, | |
| 'use_factor': 0.7, | |
| 'radiation_factor': 0.3, | |
| 'hours_operation': '8h' | |
| }, | |
| 'infiltration': { | |
| 'flow_rate': 0.05, | |
| 'height': 3.0, | |
| 'crack_length': 10.0 | |
| }, | |
| 'ventilation': { | |
| 'flow_rate': 0.1 | |
| }, | |
| 'operating_hours': '8:00-18:00' | |
| } | |
| building_volume = 300.0 | |
| # Calculate loads | |
| hourly_loads = calculator.calculate_hourly_cooling_loads( | |
| building_components=building_components, | |
| outdoor_conditions=outdoor_conditions, | |
| indoor_conditions=indoor_conditions, | |
| internal_loads=internal_loads, | |
| building_volume=building_volume | |
| ) | |
| design_loads = calculator.calculate_design_cooling_load(hourly_loads) | |
| summary = calculator.calculate_cooling_load_summary(design_loads) | |
| print("Cooling Load Summary:", summary) |