HVAC / cooling_load.py
mabuseif's picture
Upload 23 files
2cd3970 verified
"""
Cooling load calculation module for HVAC calculator.
This module provides the core functionality for calculating cooling loads
based on building characteristics, climate data, and internal heat sources.
"""
class CoolingLoadCalculator:
"""
Calculator for determining cooling loads in buildings.
This class implements the ASHRAE method for calculating cooling loads,
taking into account transmission gains, solar gains, internal gains,
and ventilation/infiltration.
"""
def __init__(self, ref_data):
"""
Initialize the cooling load calculator.
Args:
ref_data: Reference data object containing climate data and material properties
"""
self.ref_data = ref_data
self.building_info = {}
self.components = []
self.windows = []
self.internal_sources = {
'people': 0,
'lighting': {'type': '', 'area': 0},
'equipment': []
}
self.custom_heat_sources = []
self.infiltration = {
'volume': 0,
'air_changes': 0,
'temp_diff': 0
}
def set_building_info(self, location, indoor_temp, building_volume, air_changes):
"""
Set basic building information.
Args:
location (str): Location ID for climate data
indoor_temp (float): Indoor design temperature in °C
building_volume (float): Building volume in m³
air_changes (float): Air changes per hour
"""
self.building_info = {
'location': location,
'indoor_temp': indoor_temp,
'volume': building_volume,
'air_changes': air_changes
}
# Set infiltration data based on building info
location_data = self.ref_data.get_location(location)
if location_data:
outdoor_temp = location_data['summer_design_temp']
self.infiltration = {
'volume': building_volume,
'air_changes': air_changes,
'temp_diff': outdoor_temp - indoor_temp
}
def add_component(self, component_type, area, u_value, orientation=None):
"""
Add a building component (wall, roof, floor).
Args:
component_type (str): Type of component ('wall', 'roof', 'floor')
area (float): Area of the component in m²
u_value (float): U-value of the component in W/m²°C
orientation (str, optional): Orientation for walls ('north', 'south', 'east', 'west')
"""
component = {
'type': component_type,
'area': area,
'u_value': u_value
}
if orientation and component_type == 'wall':
component['orientation'] = orientation
self.components.append(component)
def add_window(self, orientation, area, u_value, glass_type, shading):
"""
Add a window.
Args:
orientation (str): Orientation ('north', 'south', 'east', 'west')
area (float): Area of the window in m²
u_value (float): U-value of the window in W/m²°C
glass_type (str): Type of glass
shading (str): Type of shading
"""
window = {
'orientation': orientation,
'area': area,
'u_value': u_value,
'glass_type': glass_type,
'shading': shading
}
self.windows.append(window)
def set_internal_sources(self, people, lighting_type, lighting_area, equipment, custom_heat_sources=None):
"""
Set internal heat sources.
Args:
people (int): Number of occupants
lighting_type (str): Type of lighting
lighting_area (float): Area with lighting in m²
equipment (list): List of equipment items with type and quantity
custom_heat_sources (list, optional): List of custom heat sources with name and watts
"""
self.internal_sources = {
'people': people,
'lighting': {
'type': lighting_type,
'area': lighting_area
},
'equipment': equipment
}
if custom_heat_sources:
self.custom_heat_sources = custom_heat_sources
def calculate_transmission_heat_gain(self, area, u_value, temp_diff):
"""
Calculate heat gain through building components.
Args:
area (float): Area of the component in m²
u_value (float): U-value of the component in W/m²°C
temp_diff (float): Temperature difference (outdoor - indoor) in °C
Returns:
float: Heat gain in Watts
"""
return area * u_value * temp_diff
def calculate_solar_transmission_gain(self, area, u_value, orientation, location):
"""
Calculate solar heat gain through opaque surfaces.
Args:
area (float): Area of the component in m²
u_value (float): U-value of the component in W/m²°C
orientation (str): Orientation of the component
location (str): Location ID for climate data
Returns:
float: Heat gain in Watts
"""
# Get solar intensity based on orientation and location
location_data = self.ref_data.get_location(location)
if not location_data or 'solar_intensity' not in location_data:
return 0
solar_intensity = location_data['solar_intensity'].get(orientation, 0)
# Calculate solar gain
solar_gain = area * solar_intensity * 0.1 # Factor of 0.1 for opaque surfaces
# Factor in the U-value (walls with higher U-values transmit more solar heat)
u_value_factor = min(u_value / 0.5, 2.0) # Normalize against a typical U-value of 0.5
return solar_gain * u_value_factor
def calculate_solar_heat_gain(self, area, shgf, shade_factor=1.0):
"""
Calculate solar heat gain through glazing.
Args:
area (float): Area of the glazing in m²
shgf (float): Solar Heat Gain Factor based on orientation and climate
shade_factor (float): Factor to account for shading (1.0 = no shade, 0.0 = full shade)
Returns:
float: Heat gain in Watts
"""
return area * shgf * shade_factor
def calculate_infiltration_heat_gain(self, volume, air_changes, temp_diff):
"""
Calculate heat gain due to infiltration and ventilation.
Args:
volume (float): Volume of the space in m³
air_changes (float): Air changes per hour
temp_diff (float): Temperature difference (outdoor - indoor) in °C
Returns:
float: Heat gain in Watts
"""
# Constants
air_density = 1.2 # kg/m³
specific_heat = 1000 # J/kg°C
# Calculate air flow rate in m³/s
air_flow = (volume * air_changes) / 3600
# Calculate heat gain
heat_gain = air_density * specific_heat * air_flow * temp_diff
return heat_gain
def calculate_internal_heat_gain(self, people, lighting, equipment, custom_sources=None):
"""
Calculate heat gain from internal sources.
Args:
people (int): Number of occupants
lighting (dict): Lighting information (type and area)
equipment (list): List of equipment items
custom_sources (list, optional): List of custom heat sources
Returns:
dict: Heat gain breakdown in Watts
"""
# People heat gain
people_gain = people * 100 # Approximate heat gain per person in Watts
# Lighting heat gain
lighting_gain = 0
if lighting['type'] and lighting['area'] > 0:
lighting_factors = {
'led': 5,
'fluorescent': 12,
'incandescent': 20
}
lighting_factor = lighting_factors.get(lighting['type'], 0)
lighting_gain = lighting['area'] * lighting_factor
# Equipment heat gain
equipment_gain = 0
equipment_breakdown = {}
for item in equipment:
item_type = item['type']
quantity = item['quantity']
# Get equipment heat gain factors
equipment_data = self.ref_data.get_internal_load('appliances', item_type)
if equipment_data:
item_gain = equipment_data['heat_gain'] * quantity
equipment_gain += item_gain
equipment_breakdown[item_type] = item_gain
# Custom heat sources
custom_gain = 0
if custom_sources:
for source in custom_sources:
custom_gain += source.get('watts', 0)
# Total internal gain
total_gain = people_gain + lighting_gain + equipment_gain + custom_gain
return {
'total': total_gain,
'people': people_gain,
'lighting': lighting_gain,
'equipment': equipment_gain,
'custom': custom_gain,
'equipment_breakdown': equipment_breakdown
}
def calculate_total_cooling_load(self):
"""
Calculate the total cooling load.
Returns:
dict: Cooling load results
"""
# Get location data
location_data = self.ref_data.get_location(self.building_info['location'])
if not location_data:
return {'error': 'Invalid location'}
outdoor_temp = location_data['summer_design_temp']
temp_diff = outdoor_temp - self.building_info['indoor_temp']
# Calculate transmission heat gain through components
transmission_gain = 0
component_gains = {}
for component in self.components:
component_gain = self.calculate_transmission_heat_gain(
component['area'], component['u_value'], temp_diff
)
# Add solar gain for exterior components
if component['type'] in ['wall', 'roof'] and 'orientation' in component:
component_gain += self.calculate_solar_transmission_gain(
component['area'], component['u_value'],
component['orientation'], self.building_info['location']
)
transmission_gain += component_gain
component_gains[f"{component['type']}_{len(component_gains)}"] = component_gain
# Calculate transmission and solar heat gain through windows
window_transmission_gain = 0
window_solar_gain = 0
window_gains = {}
window_solar_gains = {}
for window in self.windows:
# Transmission gain
window_gain = self.calculate_transmission_heat_gain(
window['area'], window['u_value'], temp_diff
)
window_transmission_gain += window_gain
window_gains[f"window_{len(window_gains)}"] = window_gain
# Solar gain
orientation = window['orientation']
glass_type = window['glass_type']
# Get solar heat gain factor
shgf = 0
glass_data = self.ref_data.get_glass_type(glass_type)
if glass_data and 'shgf' in glass_data:
shgf = glass_data['shgf'].get(orientation, 0)
# Get shading factor from reference data
shading_id = window.get('shading', 'none')
shade_factor = 1.0 # Default: no shading (full solar gain)
# Fix for the shading calculation error
shading_data = self.ref_data.get_shading_factor(shading_id)
if shading_data and 'factor' in shading_data:
# Use the factor value from the reference data
# The factor represents how much solar gain is reduced (e.g., 0.4 means 40% reduction)
shade_factor = 1.0 - shading_data['factor']
window_solar_gain += self.calculate_solar_heat_gain(window['area'], shgf, shade_factor)
window_solar_gains[f"window_{orientation}_{len(window_solar_gains)}"] = self.calculate_solar_heat_gain(window['area'], shgf, shade_factor)
# Calculate infiltration heat gain
infiltration_gain = self.calculate_infiltration_heat_gain(
self.infiltration['volume'], self.infiltration['air_changes'], self.infiltration['temp_diff']
)
# Calculate internal heat gain
internal_gain = self.calculate_internal_heat_gain(
self.internal_sources['people'],
self.internal_sources['lighting'],
self.internal_sources['equipment'],
self.custom_heat_sources
)
# Calculate total cooling load
total_load_w = transmission_gain + window_transmission_gain + window_solar_gain + infiltration_gain + internal_gain['total']
# Convert to kW and add safety factor
total_load_kw = total_load_w / 1000
recommended_size_kw = total_load_kw * 1.15 # 15% safety factor
# Calculate percentage breakdown
transmission_percentage = ((transmission_gain + window_transmission_gain) / total_load_w) * 100 if total_load_w > 0 else 0
solar_percentage = (window_solar_gain / total_load_w) * 100 if total_load_w > 0 else 0
infiltration_percentage = (infiltration_gain / total_load_w) * 100 if total_load_w > 0 else 0
internal_percentage = (internal_gain['total'] / total_load_w) * 100 if total_load_w > 0 else 0
# Prepare results
results = {
'total_load_w': total_load_w,
'total_load_kw': total_load_kw,
'recommended_size_kw': recommended_size_kw,
'transmission_gain': {
'total': transmission_gain + window_transmission_gain,
'components': component_gains,
'windows': window_gains
},
'solar_gain': {
'total': window_solar_gain,
'windows': window_solar_gains
},
'ventilation_gain': infiltration_gain,
'internal_gain': internal_gain,
'breakdown_percentage': {
'transmission': transmission_percentage,
'solar': solar_percentage,
'ventilation': infiltration_percentage,
'internal': internal_percentage
}
}
return results