HVAC-text-02 / utils /heat_transfer.py
mabuseif's picture
Upload 27 files
ca54a52 verified
"""
Heat transfer calculation module for HVAC Load Calculator.
This module implements heat transfer calculations for conduction, infiltration, and solar effects.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapters 16 and 18.
"""
from typing import Dict, List, Any, Optional, Tuple
import math
import numpy as np
import logging
from dataclasses import dataclass
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Import utility modules
from utils.psychrometrics import Psychrometrics
# Import data modules
from data.building_components import Orientation
class SolarCalculations:
"""Class for solar geometry and radiation calculations."""
def validate_angle(self, angle: float, name: str, min_val: float, max_val: float) -> None:
"""
Validate angle inputs for solar calculations.
Args:
angle: Angle in degrees
name: Name of the angle
min_val: Minimum allowed value
max_val: Maximum allowed value
Raises:
ValueError: If angle is out of range
"""
if not min_val <= angle <= max_val:
raise ValueError(f"{name} {angle}° must be between {min_val}° and {max_val}°")
def solar_declination(self, day_of_year: int) -> float:
"""
Calculate solar declination angle.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 14, Equation 14.6.
Args:
day_of_year: Day of the year (1-365)
Returns:
Declination angle in degrees
"""
if not 1 <= day_of_year <= 365:
raise ValueError("Day of year must be between 1 and 365")
declination = 23.45 * math.sin(math.radians(360 * (284 + day_of_year) / 365))
self.validate_angle(declination, "Declination angle", -23.45, 23.45)
return declination
def solar_hour_angle(self, hour: float) -> float:
"""
Calculate solar hour angle.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 14, Equation 14.7.
Args:
hour: Hour of the day (0-23)
Returns:
Hour angle in degrees
"""
if not 0 <= hour <= 24:
raise ValueError("Hour must be between 0 and 24")
hour_angle = (hour - 12) * 15
self.validate_angle(hour_angle, "Hour angle", -180, 180)
return hour_angle
def solar_altitude(self, latitude: float, declination: float, hour_angle: float) -> float:
"""
Calculate solar altitude angle.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 14, Equation 14.8.
Args:
latitude: Latitude in degrees
declination: Declination angle in degrees
hour_angle: Hour angle in degrees
Returns:
Altitude angle in degrees
"""
self.validate_angle(latitude, "Latitude", -90, 90)
self.validate_angle(declination, "Declination", -23.45, 23.45)
self.validate_angle(hour_angle, "Hour angle", -180, 180)
sin_beta = (math.sin(math.radians(latitude)) * math.sin(math.radians(declination)) +
math.cos(math.radians(latitude)) * math.cos(math.radians(declination)) *
math.cos(math.radians(hour_angle)))
beta = math.degrees(math.asin(sin_beta))
self.validate_angle(beta, "Altitude angle", 0, 90)
return beta
def solar_azimuth(self, latitude: float, declination: float, hour_angle: float, altitude: float) -> float:
"""
Calculate solar azimuth angle.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 14, Equation 14.9.
Args:
latitude: Latitude in degrees
declination: Declination angle in degrees
hour_angle: Hour angle in degrees
altitude: Altitude angle in degrees
Returns:
Azimuth angle in degrees
"""
self.validate_angle(latitude, "Latitude", -90, 90)
self.validate_angle(declination, "Declination", -23.45, 23.45)
self.validate_angle(hour_angle, "Hour angle", -180, 180)
self.validate_angle(altitude, "Altitude", 0, 90)
sin_phi = (math.cos(math.radians(declination)) * math.sin(math.radians(hour_angle)) /
math.cos(math.radians(altitude)))
phi = math.degrees(math.asin(sin_phi))
if hour_angle > 0:
phi = 180 - phi
elif hour_angle < 0:
phi = -180 - phi
self.validate_angle(phi, "Azimuth angle", -180, 180)
return phi
class HeatTransferCalculations:
"""Class for heat transfer calculations."""
def __init__(self):
"""
Initialize heat transfer calculations with psychrometrics and solar calculations.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 16.
"""
self.psychrometrics = Psychrometrics()
self.solar = SolarCalculations()
self.debug_mode = False
def conduction_heat_transfer(self, u_value: float, area: float, delta_t: float) -> float:
"""
Calculate heat transfer via conduction.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 18, Equation 18.1.
Args:
u_value: U-value of the component in W/(m²·K)
area: Area of the component in m²
delta_t: Temperature difference in °C
Returns:
Heat transfer rate in W
"""
if u_value < 0 or area < 0:
raise ValueError("U-value and area must be non-negative")
q = u_value * area * delta_t
return q
def infiltration_heat_transfer(self, flow_rate: float, delta_t: float,
t_db: float, rh: float, p_atm: float = 101325) -> float:
"""
Calculate sensible heat transfer due to infiltration or ventilation.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 18, Equation 18.5.
Args:
flow_rate: Air flow rate in m³/s
delta_t: Temperature difference in °C
t_db: Dry-bulb temperature for air properties in °C
rh: Relative humidity in % (0-100)
p_atm: Atmospheric pressure in Pa
Returns:
Sensible heat transfer rate in W
"""
if flow_rate < 0:
raise ValueError("Flow rate cannot be negative")
# Calculate air density and specific heat using psychrometrics
w = self.psychrometrics.humidity_ratio(t_db, rh, p_atm)
rho = self.psychrometrics.density(t_db, w, p_atm)
c_p = 1006 + 1860 * w # Specific heat of moist air in J/(kg·K)
q = flow_rate * rho * c_p * delta_t
return q
def infiltration_latent_heat_transfer(self, flow_rate: float, delta_w: float,
t_db: float, rh: float, p_atm: float = 101325) -> float:
"""
Calculate latent heat transfer due to infiltration or ventilation.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 18, Equation 18.6.
Args:
flow_rate: Air flow rate in m³/s
delta_w: Humidity ratio difference in kg/kg
t_db: Dry-bulb temperature for air properties in °C
rh: Relative humidity in % (0-100)
p_atm: Atmospheric pressure in Pa
Returns:
Latent heat transfer rate in W
"""
if flow_rate < 0 or delta_w < 0:
raise ValueError("Flow rate and humidity ratio difference cannot be negative")
# Calculate air density and latent heat
w = self.psychrometrics.humidity_ratio(t_db, rh, p_atm)
rho = self.psychrometrics.density(t_db, w, p_atm)
h_fg = 2501000 + 1840 * t_db # Latent heat of vaporization in J/kg
q = flow_rate * rho * h_fg * delta_w
return q
def wind_pressure_difference(self, wind_speed: float) -> float:
"""
Calculate pressure difference due to wind.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 16, Equation 16.3.
Args:
wind_speed: Wind speed in m/s
Returns:
Pressure difference in Pa
"""
if wind_speed < 0:
raise ValueError("Wind speed cannot be negative")
c_p = 0.6 # Wind pressure coefficient
rho_air = 1.2 # Air density at standard conditions in kg/m³
delta_p = 0.5 * c_p * rho_air * wind_speed**2
return delta_p
def stack_pressure_difference(self, height: float, t_inside: float, t_outside: float) -> float:
"""
Calculate pressure difference due to stack effect.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 16, Equation 16.4.
Args:
height: Height of the building in m
t_inside: Inside temperature in K
t_outside: Outside temperature in K
Returns:
Pressure difference in Pa
"""
if height < 0 or t_inside <= 0 or t_outside <= 0:
raise ValueError("Height and temperatures must be positive")
g = 9.81 # Gravitational acceleration in m/s²
rho_air = 1.2 # Air density at standard conditions in kg/m³
delta_p = rho_air * g * height * (1 / t_outside - 1 / t_inside)
return delta_p
def combined_pressure_difference(self, wind_pd: float, stack_pd: float) -> float:
"""
Calculate combined pressure difference from wind and stack effects.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 16, Section 16.2.
Args:
wind_pd: Wind pressure difference in Pa
stack_pd: Stack pressure difference in Pa
Returns:
Combined pressure difference in Pa
"""
delta_p = math.sqrt(wind_pd**2 + stack_pd**2)
return delta_p
def crack_method_infiltration(self, crack_length: float, crack_width: float, delta_p: float) -> float:
"""
Calculate infiltration flow rate using crack method.
Reference: ASHRAE Handbook—Fundamentals (2017), Chapter 16, Equation 16.5.
Args:
crack_length: Length of cracks in m
crack_width: Width of cracks in m
delta_p: Pressure difference across cracks in Pa
Returns:
Infiltration flow rate in m³/s
"""
if crack_length < 0 or crack_width < 0 or delta_p < 0:
raise ValueError("Crack dimensions and pressure difference cannot be negative")
c_d = 0.65 # Discharge coefficient
area = crack_length * crack_width
rho_air = 1.2 # Air density at standard conditions in kg/m³
q = c_d * area * math.sqrt(2 * delta_p / rho_air)
return q
# Example usage
if __name__ == "__main__":
heat_transfer = HeatTransferCalculations()
heat_transfer.debug_mode = True
# Example conduction calculation
u_value = 0.5 # W/(m²·K)
area = 20.0 # m²
delta_t = 26.0 # °C
q_conduction = heat_transfer.conduction_heat_transfer(u_value, area, delta_t)
logger.info(f"Conduction heat transfer: {q_conduction:.2f} W")
# Example infiltration calculation
flow_rate = 0.05 # m³/s
delta_t = 26.0 # °C
t_db = 21.0 # °C
rh = 40.0 # %
p_atm = 101325 # Pa
q_infiltration = heat_transfer.infiltration_heat_transfer(flow_rate, delta_t, t_db, rh, p_atm)
logger.info(f"Infiltration sensible heat transfer: {q_infiltration:.2f} W")
# Example solar calculation
latitude = 40.0 # degrees
day_of_year = 172 # June 21
hour = 12.0 # Noon
declination = heat_transfer.solar.solar_declination(day_of_year)
hour_angle = heat_transfer.solar.solar_hour_angle(hour)
altitude = heat_transfer.solar.solar_altitude(latitude, declination, hour_angle)
azimuth = heat_transfer.solar.solar_azimuth(latitude, declination, hour_angle, altitude)
logger.info(f"Solar altitude: {altitude:.2f}°, Azimuth: {azimuth:.2f}°")