Spaces:
Sleeping
Sleeping
""" | |
ASHRAE Heating Load Calculation Module | |
This module implements the ASHRAE method for calculating heating loads in residential buildings. | |
It calculates the heat loss from the building envelope and unwanted ventilation/infiltration. | |
""" | |
import numpy as np | |
import pandas as pd | |
class HeatingLoadCalculator: | |
""" | |
A class to calculate heating loads using the ASHRAE method. | |
""" | |
def __init__(self): | |
"""Initialize the heating load calculator with default values.""" | |
# Specific heat capacity of air × density of air | |
self.air_heat_factor = 0.33 | |
# Default values for internal heat gains (W) | |
self.heat_gain_per_person = 75 | |
self.heat_gain_kitchen = 1000 | |
def calculate_conduction_heat_loss(self, area, u_value, temp_diff): | |
""" | |
Calculate conduction heat loss through building components. | |
Args: | |
area (float): Area of the building component in m² | |
u_value (float): U-value of the component in W/m²°C | |
temp_diff (float): Temperature difference (inside - outside) in °C | |
Returns: | |
float: Heat loss in Watts | |
""" | |
return area * u_value * temp_diff | |
def calculate_wall_solar_heat_gain(self, area, u_value, orientation, daily_range='medium', latitude='medium'): | |
""" | |
Calculate solar heat gain through walls based on orientation. | |
Args: | |
area (float): Area of the wall in m² | |
u_value (float): U-value of the wall in W/m²°C | |
orientation (str): Wall orientation ('north', 'east', 'south', 'west') | |
daily_range (str): Daily temperature range ('low', 'medium', 'high') | |
latitude (str): Latitude category ('low', 'medium', 'high') | |
Returns: | |
float: Heat gain in Watts | |
""" | |
# Solar intensity factors based on orientation - for heating, south-facing walls (northern hemisphere) | |
# or north-facing walls (southern hemisphere) receive more solar gain in winter | |
# These are simplified factors for demonstration | |
orientation_factors = { | |
'north': 0.6, # Higher in southern hemisphere during winter | |
'east': 0.4, | |
'south': 0.2, # Lower in southern hemisphere during winter | |
'west': 0.4, | |
'horizontal': 0.3 | |
} | |
# Adjustments for latitude | |
latitude_factors = { | |
'low': 0.9, # Closer to equator - less winter sun angle | |
'medium': 1.0, # Mid latitudes | |
'high': 1.1 # Closer to poles - more winter sun angle variation | |
} | |
# Adjustments for daily temperature range | |
range_factors = { | |
'low': 0.95, # Less than 8.5°C | |
'medium': 1.0, # Between 8.5°C and 14°C | |
'high': 1.05 # Over 14°C | |
} | |
# Base solar heat gain through walls (W/m²) - lower in winter | |
base_solar_gain = 10.0 | |
# Get factors | |
orientation_factor = orientation_factors.get(orientation.lower(), 0.5) # Default to south if not found | |
latitude_factor = latitude_factors.get(latitude.lower(), 1.0) | |
range_factor = range_factors.get(daily_range.lower(), 1.0) | |
# Calculate solar heat gain | |
solar_gain = area * base_solar_gain * orientation_factor * latitude_factor * range_factor | |
# 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_infiltration_heat_loss(self, volume, air_changes, temp_diff): | |
""" | |
Calculate heat loss due to infiltration and ventilation. | |
Args: | |
volume (float): Volume of the space in m³ | |
air_changes (float): Number of air changes per hour | |
temp_diff (float): Temperature difference (inside - outside) in °C | |
Returns: | |
float: Heat loss in Watts | |
""" | |
return self.air_heat_factor * volume * air_changes * temp_diff | |
def calculate_internal_heat_gain(self, num_people, has_kitchen=False, equipment_watts=0): | |
""" | |
Calculate internal heat gain from people, kitchen, and equipment. | |
Args: | |
num_people (int): Number of occupants | |
has_kitchen (bool): Whether the space includes a kitchen | |
equipment_watts (float): Additional equipment heat gain in Watts | |
Returns: | |
float: Heat gain in Watts | |
""" | |
people_gain = num_people * self.heat_gain_per_person | |
kitchen_gain = self.heat_gain_kitchen if has_kitchen else 0 | |
return people_gain + kitchen_gain + equipment_watts | |
def calculate_annual_heating_energy(self, total_heat_loss, heating_degree_days, correction_factor=1.0): | |
""" | |
Calculate annual heating energy requirement using heating degree days. | |
Args: | |
total_heat_loss (float): Total heat loss in Watts | |
heating_degree_days (float): Number of heating degree days | |
correction_factor (float): Correction factor for occupancy | |
Returns: | |
float: Annual heating energy in kWh | |
""" | |
# Convert W to kW | |
heat_loss_kw = total_heat_loss / 1000 | |
# Calculate annual heating energy (kWh) | |
# 24 hours in a day | |
annual_energy = heat_loss_kw * 24 * heating_degree_days * correction_factor | |
return annual_energy | |
def get_outdoor_design_temperature(self, location): | |
""" | |
Get the outdoor design temperature for a location. | |
Args: | |
location (str): Location name | |
Returns: | |
float: Outdoor design temperature in °C | |
""" | |
# This is a simplified version - in a real implementation, this would use lookup tables | |
# based on the AIRAH Design Data Manual | |
# Example data for Australian locations | |
temperatures = { | |
'sydney': 7.0, | |
'melbourne': 4.0, | |
'brisbane': 9.0, | |
'perth': 7.0, | |
'adelaide': 5.0, | |
'hobart': 2.0, | |
'darwin': 15.0, | |
'canberra': -1.0, | |
'mildura': 4.5 | |
} | |
return temperatures.get(location.lower(), 5.0) # Default to 5°C if location not found | |
def get_heating_degree_days(self, location, base_temp=18): | |
""" | |
Get the heating degree days for a location. | |
Args: | |
location (str): Location name | |
base_temp (int): Base temperature for HDD calculation (default: 18°C) | |
Returns: | |
float: Heating degree days | |
""" | |
# This is a simplified version - in a real implementation, this would use lookup tables | |
# or API data from Bureau of Meteorology | |
# Example data for Australian locations with base temperature of 18°C | |
hdd_data = { | |
'sydney': 740, | |
'melbourne': 1400, | |
'brisbane': 320, | |
'perth': 760, | |
'adelaide': 1100, | |
'hobart': 1800, | |
'darwin': 0, | |
'canberra': 2000, | |
'mildura': 1200 | |
} | |
return hdd_data.get(location.lower(), 1000) # Default to 1000 if location not found | |
def get_occupancy_correction_factor(self, occupancy_type): | |
""" | |
Get the correction factor for occupancy type. | |
Args: | |
occupancy_type (str): Type of occupancy | |
Returns: | |
float: Correction factor | |
""" | |
# Correction factors based on occupancy patterns | |
factors = { | |
'continuous': 1.0, # Continuously heated | |
'intermittent': 0.8, # Heated during occupied hours | |
'night_setback': 0.9, # Temperature setback at night | |
'weekend_off': 0.85, # Heating off during weekends | |
'vacation_home': 0.6 # Occasionally occupied | |
} | |
return factors.get(occupancy_type.lower(), 1.0) # Default to continuous if not found | |
def calculate_total_heating_load(self, building_components, infiltration, internal_gains=None): | |
""" | |
Calculate the total peak heating load. | |
Args: | |
building_components (list): List of dicts with 'area', 'u_value', 'temp_diff', and 'orientation' for each component | |
infiltration (dict): Dict with 'volume', 'air_changes', and 'temp_diff' | |
internal_gains (dict): Dict with 'num_people', 'has_kitchen', and 'equipment_watts' | |
Returns: | |
dict: Dictionary with component heat losses and total heating load in Watts | |
""" | |
# Calculate conduction heat loss through building components | |
component_losses = {} | |
total_conduction_loss = 0 | |
wall_solar_gain = 0 | |
for comp in building_components: | |
name = comp.get('name', f"Component {len(component_losses) + 1}") | |
loss = self.calculate_conduction_heat_loss(comp['area'], comp['u_value'], comp['temp_diff']) | |
component_losses[name] = loss | |
total_conduction_loss += loss | |
# Calculate solar gain for walls based on orientation | |
if 'orientation' in comp: | |
daily_range = comp.get('daily_range', 'medium') | |
latitude = comp.get('latitude', 'medium') | |
solar_gain = self.calculate_wall_solar_heat_gain( | |
comp['area'], | |
comp['u_value'], | |
comp['orientation'], | |
daily_range, | |
latitude | |
) | |
wall_solar_gain += solar_gain | |
# Calculate infiltration heat loss | |
infiltration_loss = self.calculate_infiltration_heat_loss( | |
infiltration['volume'], infiltration['air_changes'], infiltration['temp_diff'] | |
) | |
# Calculate internal heat gain if provided | |
internal_gain = 0 | |
if internal_gains: | |
internal_gain = self.calculate_internal_heat_gain( | |
internal_gains.get('num_people', 0), | |
internal_gains.get('has_kitchen', False), | |
internal_gains.get('equipment_watts', 0) | |
) | |
# Calculate total heating load (subtract solar gain and internal gains as they reduce heating load) | |
total_load = total_conduction_loss + infiltration_loss - wall_solar_gain - internal_gain | |
return { | |
'component_losses': component_losses, | |
'total_conduction_loss': total_conduction_loss, | |
'infiltration_loss': infiltration_loss, | |
'wall_solar_gain': wall_solar_gain, | |
'internal_gain': internal_gain, | |
'total_load': total_load | |
} | |
def calculate_annual_heating_requirement(self, total_load, location, occupancy_type='continuous', base_temp=18): | |
""" | |
Calculate the annual heating energy requirement. | |
Args: | |
total_load (float): Total heating load in Watts | |
location (str): Location name | |
occupancy_type (str): Type of occupancy | |
base_temp (int): Base temperature for HDD calculation | |
Returns: | |
dict: Dictionary with annual heating energy in kWh and related factors | |
""" | |
# Get heating degree days for the location | |
hdd = self.get_heating_degree_days(location, base_temp) | |
# Get correction factor for occupancy | |
correction_factor = self.get_occupancy_correction_factor(occupancy_type) | |
# Calculate annual heating energy | |
annual_energy = self.calculate_annual_heating_energy(total_load, hdd, correction_factor) | |
return { | |
'heating_degree_days': hdd, | |
'correction_factor': correction_factor, | |
'annual_energy_kwh': annual_energy, | |
'annual_energy_mj': annual_energy * 3.6 # Convert kWh to MJ | |
} | |
# Example usage | |
if __name__ == "__main__": | |
calculator = HeatingLoadCalculator() | |
# Example data for a simple room in Mildura | |
building_components = [ | |
{'name': 'Floor', 'area': 50, 'u_value': 1.47, 'temp_diff': 16.5}, # Concrete slab | |
{'name': 'Walls', 'area': 80, 'u_value': 1.5, 'temp_diff': 16.5}, # External walls | |
{'name': 'Ceiling', 'area': 50, 'u_value': 0.9, 'temp_diff': 16.5}, # Ceiling | |
{'name': 'Windows', 'area': 8, 'u_value': 5.8, 'temp_diff': 16.5} # Windows | |
] | |
infiltration = {'volume': 125, 'air_changes': 0.5, 'temp_diff': 16.5} | |
# Calculate peak heating load | |
result = calculator.calculate_total_heating_load(building_components, infiltration) | |
print("Heating Load Calculation Results:") | |
for key, value in result.items(): | |
if key == 'component_losses': | |
print("Component Losses:") | |
for comp, loss in value.items(): | |
print(f" {comp}: {loss:.2f} W") | |
else: | |
print(f"{key}: {value:.2f} W") | |
# Calculate annual heating requirement | |
annual_result = calculator.calculate_annual_heating_requirement(result['total_load'], 'mildura') | |
print("\nAnnual Heating Requirement:") | |
for key, value in annual_result.items(): | |
print(f"{key}: {value:.2f}") | |