Spaces:
Sleeping
Sleeping
File size: 12,417 Bytes
4af4e9a 13cdf17 efc6bd6 4af4e9a efc6bd6 4af4e9a efc6bd6 aa6c57f 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 13cdf17 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 13cdf17 4af4e9a 63eff5d 13cdf17 4af4e9a 13cdf17 efc6bd6 13cdf17 4af4e9a efc6bd6 4af4e9a 13cdf17 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 932295a 13cdf17 efc6bd6 13cdf17 efc6bd6 13cdf17 4af4e9a efc6bd6 4af4e9a 13cdf17 4af4e9a efc6bd6 4af4e9a efc6bd6 13cdf17 932295a 13cdf17 efc6bd6 4af4e9a 13cdf17 efc6bd6 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 13cdf17 932295a efc6bd6 13cdf17 4af4e9a 13cdf17 efc6bd6 4af4e9a 13cdf17 efc6bd6 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 13cdf17 932295a efc6bd6 932295a 13cdf17 efc6bd6 932295a efc6bd6 63eff5d 13cdf17 4af4e9a 63eff5d 4af4e9a efc6bd6 13cdf17 efc6bd6 13cdf17 932295a efc6bd6 35bc1fb 4af4e9a efc6bd6 13cdf17 4af4e9a 13cdf17 4af4e9a efc6bd6 4af4e9a efc6bd6 4af4e9a 13cdf17 35bc1fb 4af4e9a 26950bb 13cdf17 efc6bd6 4af4e9a 13cdf17 4af4e9a efc6bd6 35bc1fb efc6bd6 4af4e9a 13cdf17 35bc1fb 4af4e9a 26950bb 13cdf17 efc6bd6 4af4e9a 13cdf17 4af4e9a efc6bd6 35bc1fb efc6bd6 4af4e9a 13cdf17 efc6bd6 4af4e9a 13cdf17 4af4e9a 13cdf17 4af4e9a 13cdf17 efc6bd6 932295a efc6bd6 4af4e9a 13cdf17 efc6bd6 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 13cdf17 26950bb efc6bd6 13cdf17 932295a 13cdf17 efc6bd6 4af4e9a 13cdf17 4af4e9a 13cdf17 4af4e9a efc6bd6 4af4e9a 13cdf17 efc6bd6 4af4e9a efc6bd6 4af4e9a 26950bb 4af4e9a efc6bd6 13cdf17 efc6bd6 4af4e9a 13cdf17 4af4e9a efc6bd6 4af4e9a efc6bd6 26950bb efc6bd6 aa6c57f 35bc1fb efc6bd6 35bc1fb efc6bd6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
"""
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}°") |