HVAC-testing / utils /heating_load.py
mabuseif's picture
Upload 23 files
4af4e9a verified
raw
history blame
23.9 kB
"""
Heating load calculation module for HVAC Load Calculator.
This module implements steady-state methods for calculating heating loads.
"""
from typing import Dict, List, Any, Optional, Tuple
import math
import numpy as np
import pandas as pd
import os
from datetime import datetime, timedelta
from enum import Enum
# Import data models and utilities
from data.building_components import Wall, Roof, Floor, Window, Door, Orientation, ComponentType
from utils.psychrometrics import Psychrometrics
from utils.heat_transfer import HeatTransferCalculations
# Define paths
DATA_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class HeatingLoadCalculator:
"""Class for calculating heating loads using steady-state methods."""
def __init__(self):
"""Initialize heating load calculator."""
self.heat_transfer = HeatTransferCalculations()
self.psychrometrics = Psychrometrics()
def calculate_wall_heating_load(self, wall: Wall, outdoor_temp: float, indoor_temp: float) -> float:
"""
Calculate heating load through a wall using steady-state conduction.
Args:
wall: Wall object
outdoor_temp: Outdoor temperature in °C
indoor_temp: Indoor temperature in °C
Returns:
Heating load in W
"""
# Get wall properties
u_value = wall.u_value
area = wall.area
# Calculate heating load
delta_t = indoor_temp - outdoor_temp
heating_load = u_value * area * delta_t
return heating_load
def calculate_roof_heating_load(self, roof: Roof, outdoor_temp: float, indoor_temp: float) -> float:
"""
Calculate heating load through a roof using steady-state conduction.
Args:
roof: Roof object
outdoor_temp: Outdoor temperature in °C
indoor_temp: Indoor temperature in °C
Returns:
Heating load in W
"""
# Get roof properties
u_value = roof.u_value
area = roof.area
# Calculate heating load
delta_t = indoor_temp - outdoor_temp
heating_load = u_value * area * delta_t
return heating_load
def calculate_floor_heating_load(self, floor: Floor, ground_temp: float, indoor_temp: float) -> float:
"""
Calculate heating load through a floor.
Args:
floor: Floor object
ground_temp: Ground or adjacent space temperature in °C
indoor_temp: Indoor temperature in °C
Returns:
Heating load in W
"""
# Get floor properties
u_value = floor.u_value
area = floor.area
# Calculate heating load
delta_t = indoor_temp - ground_temp
heating_load = u_value * area * delta_t
return heating_load
def calculate_window_heating_load(self, window: Window, outdoor_temp: float, indoor_temp: float) -> float:
"""
Calculate heating load through a window using steady-state conduction.
Args:
window: Window object
outdoor_temp: Outdoor temperature in °C
indoor_temp: Indoor temperature in °C
Returns:
Heating load in W
"""
# Get window properties
u_value = window.u_value
area = window.area
# Calculate heating load
delta_t = indoor_temp - outdoor_temp
heating_load = u_value * area * delta_t
return heating_load
def calculate_door_heating_load(self, door: Door, outdoor_temp: float, indoor_temp: float) -> float:
"""
Calculate heating load through a door using steady-state conduction.
Args:
door: Door object
outdoor_temp: Outdoor temperature in °C
indoor_temp: Indoor temperature in °C
Returns:
Heating load in W
"""
# Get door properties
u_value = door.u_value
area = door.area
# Calculate heating load
delta_t = indoor_temp - outdoor_temp
heating_load = u_value * area * delta_t
return heating_load
def calculate_infiltration_heating_load(self, flow_rate: float, outdoor_temp: float, indoor_temp: float,
outdoor_rh: float, indoor_rh: float) -> Dict[str, float]:
"""
Calculate sensible and latent heating loads due to infiltration.
Args:
flow_rate: Infiltration flow rate in m³/s
outdoor_temp: Outdoor temperature in °C
indoor_temp: Indoor temperature in °C
outdoor_rh: Outdoor relative humidity in %
indoor_rh: Indoor relative humidity in %
Returns:
Dictionary with sensible, latent, and total heating loads in W
"""
# Calculate sensible heating load
sensible_load = self.heat_transfer.infiltration_heat_transfer(
flow_rate=flow_rate,
delta_t=indoor_temp - outdoor_temp
)
# Calculate humidity ratios
w_outdoor = self.psychrometrics.humidity_ratio(outdoor_temp, outdoor_rh)
w_indoor = self.psychrometrics.humidity_ratio(indoor_temp, indoor_rh)
# Calculate latent heating load (only if indoor humidity is higher than outdoor)
delta_w = w_indoor - w_outdoor
if delta_w > 0:
latent_load = self.heat_transfer.infiltration_latent_heat_transfer(
flow_rate=flow_rate,
delta_w=delta_w
)
else:
latent_load = 0
# Calculate total heating load
total_load = sensible_load + latent_load
return {
"sensible": sensible_load,
"latent": latent_load,
"total": total_load
}
def calculate_ventilation_heating_load(self, flow_rate: float, outdoor_temp: float, indoor_temp: float,
outdoor_rh: float, indoor_rh: float) -> Dict[str, float]:
"""
Calculate sensible and latent heating loads due to ventilation.
Args:
flow_rate: Ventilation flow rate in m³/s
outdoor_temp: Outdoor temperature in °C
indoor_temp: Indoor temperature in °C
outdoor_rh: Outdoor relative humidity in %
indoor_rh: Indoor relative humidity in %
Returns:
Dictionary with sensible, latent, and total heating loads in W
"""
# Ventilation load calculation is the same as infiltration
return self.calculate_infiltration_heating_load(
flow_rate=flow_rate,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp,
outdoor_rh=outdoor_rh,
indoor_rh=indoor_rh
)
def calculate_internal_gains_offset(self, people_load: float, lights_load: float,
equipment_load: float, usage_factor: float = 0.7) -> float:
"""
Calculate internal gains offset for heating load.
Args:
people_load: Heat gain from people in W
lights_load: Heat gain from lights in W
equipment_load: Heat gain from equipment in W
usage_factor: Usage factor for internal gains (0-1)
Returns:
Internal gains offset in W
"""
# Calculate total internal gains
total_gains = people_load + lights_load + equipment_load
# Apply usage factor
offset = total_gains * usage_factor
return offset
def calculate_design_heating_load(self, building_components: Dict[str, List[Any]],
outdoor_conditions: Dict[str, Any],
indoor_conditions: Dict[str, Any],
internal_loads: Dict[str, Any],
safety_factor: float = 1.15) -> Dict[str, float]:
"""
Calculate design heating load for a building.
Args:
building_components: Dictionary with lists of building components
outdoor_conditions: Dictionary with outdoor conditions
indoor_conditions: Dictionary with indoor conditions
internal_loads: Dictionary with internal loads
safety_factor: Safety factor for heating load (default: 1.15)
Returns:
Dictionary with design heating loads
"""
# Extract building components
walls = building_components.get("walls", [])
roofs = building_components.get("roofs", [])
floors = building_components.get("floors", [])
windows = building_components.get("windows", [])
doors = building_components.get("doors", [])
# Extract outdoor conditions
outdoor_temp = outdoor_conditions.get("design_temperature", -10.0)
outdoor_rh = outdoor_conditions.get("design_relative_humidity", 80.0)
ground_temp = outdoor_conditions.get("ground_temperature", 10.0)
# Extract indoor conditions
indoor_temp = indoor_conditions.get("temperature", 21.0)
indoor_rh = indoor_conditions.get("relative_humidity", 40.0)
# Extract internal loads
people = internal_loads.get("people", {})
lights = internal_loads.get("lights", {})
equipment = internal_loads.get("equipment", {})
infiltration = internal_loads.get("infiltration", {})
ventilation = internal_loads.get("ventilation", {})
# Initialize loads
loads = {
"walls": 0,
"roofs": 0,
"floors": 0,
"windows": 0,
"doors": 0,
"infiltration_sensible": 0,
"infiltration_latent": 0,
"ventilation_sensible": 0,
"ventilation_latent": 0,
"internal_gains_offset": 0,
"subtotal": 0,
"safety_factor": safety_factor,
"total": 0
}
# Calculate wall loads
for wall in walls:
wall_load = self.calculate_wall_heating_load(
wall=wall,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp
)
loads["walls"] += wall_load
# Calculate roof loads
for roof in roofs:
roof_load = self.calculate_roof_heating_load(
roof=roof,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp
)
loads["roofs"] += roof_load
# Calculate floor loads
for floor in floors:
floor_load = self.calculate_floor_heating_load(
floor=floor,
ground_temp=ground_temp,
indoor_temp=indoor_temp
)
loads["floors"] += floor_load
# Calculate window loads
for window in windows:
window_load = self.calculate_window_heating_load(
window=window,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp
)
loads["windows"] += window_load
# Calculate door loads
for door in doors:
door_load = self.calculate_door_heating_load(
door=door,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp
)
loads["doors"] += door_load
# Calculate infiltration loads
if infiltration:
flow_rate = infiltration.get("flow_rate", 0.0)
infiltration_loads = self.calculate_infiltration_heating_load(
flow_rate=flow_rate,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp,
outdoor_rh=outdoor_rh,
indoor_rh=indoor_rh
)
loads["infiltration_sensible"] = infiltration_loads["sensible"]
loads["infiltration_latent"] = infiltration_loads["latent"]
# Calculate ventilation loads
if ventilation:
flow_rate = ventilation.get("flow_rate", 0.0)
ventilation_loads = self.calculate_ventilation_heating_load(
flow_rate=flow_rate,
outdoor_temp=outdoor_temp,
indoor_temp=indoor_temp,
outdoor_rh=outdoor_rh,
indoor_rh=indoor_rh
)
loads["ventilation_sensible"] = ventilation_loads["sensible"]
loads["ventilation_latent"] = ventilation_loads["latent"]
# Calculate internal gains offset
people_load = people.get("number", 0) * people.get("sensible_gain", 70)
lights_load = lights.get("power", 0) * lights.get("use_factor", 1.0)
equipment_load = equipment.get("power", 0) * equipment.get("use_factor", 1.0)
loads["internal_gains_offset"] = self.calculate_internal_gains_offset(
people_load=people_load,
lights_load=lights_load,
equipment_load=equipment_load,
usage_factor=internal_loads.get("usage_factor", 0.7)
)
# Calculate subtotal
loads["subtotal"] = (
loads["walls"] + loads["roofs"] + loads["floors"] +
loads["windows"] + loads["doors"] +
loads["infiltration_sensible"] + loads["infiltration_latent"] +
loads["ventilation_sensible"] + loads["ventilation_latent"] -
loads["internal_gains_offset"]
)
# Apply safety factor
loads["total"] = loads["subtotal"] * safety_factor
return loads
def calculate_heating_load_summary(self, design_loads: Dict[str, float]) -> Dict[str, float]:
"""
Calculate heating load summary.
Args:
design_loads: Dictionary with design heating loads
Returns:
Dictionary with heating load summary
"""
# Calculate envelope loads
envelope_loads = (
design_loads["walls"] + design_loads["roofs"] + design_loads["floors"] +
design_loads["windows"] + design_loads["doors"]
)
# Calculate ventilation and infiltration loads
ventilation_loads = design_loads["ventilation_sensible"] + design_loads["ventilation_latent"]
infiltration_loads = design_loads["infiltration_sensible"] + design_loads["infiltration_latent"]
# Create summary
summary = {
"envelope_loads": envelope_loads,
"ventilation_loads": ventilation_loads,
"infiltration_loads": infiltration_loads,
"internal_gains_offset": design_loads["internal_gains_offset"],
"subtotal": design_loads["subtotal"],
"safety_factor": design_loads["safety_factor"],
"total": design_loads["total"]
}
return summary
def calculate_monthly_heating_loads(self, design_loads: Dict[str, float],
monthly_temps: Dict[str, float],
design_temp: float, indoor_temp: float) -> Dict[str, float]:
"""
Calculate monthly heating loads based on design load and monthly temperatures.
Args:
design_loads: Dictionary with design heating loads
monthly_temps: Dictionary with monthly average temperatures
design_temp: Design outdoor temperature in °C
indoor_temp: Indoor temperature in °C
Returns:
Dictionary with monthly heating loads
"""
# Calculate design temperature difference
design_delta_t = indoor_temp - design_temp
# Calculate monthly loads
monthly_loads = {}
for month, temp in monthly_temps.items():
# Calculate temperature difference for this month
delta_t = indoor_temp - temp
# Skip months where outdoor temperature is higher than indoor
if delta_t <= 0:
monthly_loads[month] = 0
continue
# Calculate load ratio based on temperature difference
load_ratio = delta_t / design_delta_t
# Calculate monthly load
monthly_loads[month] = design_loads["total"] * load_ratio
return monthly_loads
def calculate_heating_degree_days(self, monthly_temps: Dict[str, float],
base_temp: float = 18.0) -> Dict[str, float]:
"""
Calculate heating degree days for each month.
Args:
monthly_temps: Dictionary with monthly average temperatures
base_temp: Base temperature for degree days in °C (default: 18°C)
Returns:
Dictionary with monthly heating degree days
"""
# Calculate monthly heating degree days
monthly_hdds = {}
for month, temp in monthly_temps.items():
# Calculate degree days
days_in_month = 30 # Approximate
if month in ["Apr", "Jun", "Sep", "Nov"]:
days_in_month = 30
elif month == "Feb":
days_in_month = 28 # Ignore leap years for simplicity
else:
days_in_month = 31
# Calculate daily degree days
daily_hdd = max(0, base_temp - temp)
# Calculate monthly degree days
monthly_hdds[month] = daily_hdd * days_in_month
return monthly_hdds
def calculate_annual_heating_energy(self, monthly_loads: Dict[str, float],
heating_system_efficiency: float = 0.8) -> Dict[str, float]:
"""
Calculate annual heating energy consumption.
Args:
monthly_loads: Dictionary with monthly heating loads in W
heating_system_efficiency: Heating system efficiency (0-1)
Returns:
Dictionary with monthly and annual heating energy in kWh
"""
# Calculate monthly energy consumption
monthly_energy = {}
annual_energy = 0
for month, load in monthly_loads.items():
# Calculate hours in month
hours_in_month = 24 * 30 # Approximate
if month in ["Apr", "Jun", "Sep", "Nov"]:
hours_in_month = 24 * 30
elif month == "Feb":
hours_in_month = 24 * 28 # Ignore leap years for simplicity
else:
hours_in_month = 24 * 31
# Calculate energy in kWh
energy = load * hours_in_month / 1000 / heating_system_efficiency
# Store monthly energy
monthly_energy[month] = energy
# Add to annual total
annual_energy += energy
# Add annual total to results
monthly_energy["annual"] = annual_energy
return monthly_energy
# Create a singleton instance
heating_load_calculator = HeatingLoadCalculator()
# Example usage
if __name__ == "__main__":
# Create sample building components
from data.building_components import Wall, Roof, Window, Door, Orientation, ComponentType
# Create a sample wall
wall = Wall(
id="wall1",
name="Exterior Wall",
component_type=ComponentType.WALL,
u_value=0.5,
area=20.0,
orientation=Orientation.NORTH,
wall_type="Brick",
wall_group="B"
)
# Create a sample roof
roof = Roof(
id="roof1",
name="Flat Roof",
component_type=ComponentType.ROOF,
u_value=0.3,
area=50.0,
orientation=Orientation.HORIZONTAL,
roof_type="Concrete",
roof_group="C"
)
# Create a sample window
window = Window(
id="window1",
name="North Window",
component_type=ComponentType.WINDOW,
u_value=2.8,
area=5.0,
orientation=Orientation.NORTH,
shgc=0.7,
vt=0.8,
window_type="Double Glazed",
glazing_layers=2,
gas_fill="Air",
low_e_coating=False
)
# Define building components
building_components = {
"walls": [wall],
"roofs": [roof],
"windows": [window],
"doors": [],
"floors": []
}
# Define conditions
outdoor_conditions = {
"design_temperature": -10.0,
"design_relative_humidity": 80.0,
"ground_temperature": 10.0
}
indoor_conditions = {
"temperature": 21.0,
"relative_humidity": 40.0
}
# Define internal loads
internal_loads = {
"people": {
"number": 3,
"sensible_gain": 70
},
"lights": {
"power": 500.0,
"use_factor": 0.9
},
"equipment": {
"power": 1000.0,
"use_factor": 0.7
},
"infiltration": {
"flow_rate": 0.05
},
"ventilation": {
"flow_rate": 0.1
},
"usage_factor": 0.7
}
# Calculate design heating load
design_loads = heating_load_calculator.calculate_design_heating_load(
building_components=building_components,
outdoor_conditions=outdoor_conditions,
indoor_conditions=indoor_conditions,
internal_loads=internal_loads
)
# Calculate heating load summary
summary = heating_load_calculator.calculate_heating_load_summary(design_loads)
# Define monthly temperatures
monthly_temps = {
"Jan": -5.0,
"Feb": -3.0,
"Mar": 2.0,
"Apr": 8.0,
"May": 14.0,
"Jun": 18.0,
"Jul": 21.0,
"Aug": 20.0,
"Sep": 16.0,
"Oct": 10.0,
"Nov": 4.0,
"Dec": -2.0
}
# Calculate monthly heating loads
monthly_loads = heating_load_calculator.calculate_monthly_heating_loads(
design_loads=design_loads,
monthly_temps=monthly_temps,
design_temp=outdoor_conditions["design_temperature"],
indoor_temp=indoor_conditions["temperature"]
)
# Calculate heating degree days
hdds = heating_load_calculator.calculate_heating_degree_days(monthly_temps)
# Calculate annual heating energy
energy = heating_load_calculator.calculate_annual_heating_energy(monthly_loads)
# Print results
print("Heating Load Summary:")
print(f"Envelope Loads: {summary['envelope_loads']:.2f} W")
print(f"Ventilation Loads: {summary['ventilation_loads']:.2f} W")
print(f"Infiltration Loads: {summary['infiltration_loads']:.2f} W")
print(f"Internal Gains Offset: {summary['internal_gains_offset']:.2f} W")
print(f"Subtotal: {summary['subtotal']:.2f} W")
print(f"Safety Factor: {summary['safety_factor']:.2f}")
print(f"Total: {summary['total']:.2f} W")
print("\nMonthly Heating Loads:")
for month, load in monthly_loads.items():
print(f"{month}: {load:.2f} W")
print("\nHeating Degree Days:")
for month, hdd in hdds.items():
print(f"{month}: {hdd:.2f} HDD")
print("\nAnnual Heating Energy:")
print(f"Total: {energy['annual']:.2f} kWh")