|
|
""" |
|
|
Cooling load calculation module for HVAC Load Calculator. |
|
|
This module implements the CLTD/CLF method for calculating cooling 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 |
|
|
|
|
|
|
|
|
from data.building_components import Wall, Roof, Floor, Window, Door, Orientation, ComponentType |
|
|
from data.ashrae_tables import ashrae_tables |
|
|
from utils.psychrometrics import Psychrometrics |
|
|
from utils.heat_transfer import HeatTransfer |
|
|
|
|
|
|
|
|
DATA_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
|
|
|
|
|
|
|
class CoolingLoad: |
|
|
"""Class for calculating cooling loads using the CLTD/CLF method.""" |
|
|
|
|
|
def __init__(self): |
|
|
"""Initialize cooling load calculator.""" |
|
|
self.heat_transfer = HeatTransfer() |
|
|
self.psychrometrics = Psychrometrics() |
|
|
self.ashrae_tables = ashrae_tables |
|
|
|
|
|
def calculate_wall_cooling_load(self, wall: Wall, outdoor_temp: float, indoor_temp: float, |
|
|
month: str, hour: int, latitude: str = "40N", |
|
|
color: str = "Dark") -> float: |
|
|
""" |
|
|
Calculate cooling load through a wall using the CLTD method. |
|
|
|
|
|
Args: |
|
|
wall: Wall object |
|
|
outdoor_temp: Outdoor temperature in °C |
|
|
indoor_temp: Indoor temperature in °C |
|
|
month: Month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec) |
|
|
hour: Hour of the day (0-23) |
|
|
latitude: Latitude (24N, 32N, 40N, 48N, 56N) |
|
|
color: Surface color (Dark, Medium, Light) |
|
|
|
|
|
Returns: |
|
|
Cooling load in W |
|
|
""" |
|
|
|
|
|
u_value = wall.u_value |
|
|
area = wall.area |
|
|
orientation = wall.orientation.value |
|
|
wall_group = wall.wall_group |
|
|
|
|
|
|
|
|
cltd = self.ashrae_tables.calculate_corrected_cltd_wall( |
|
|
wall_group=wall_group, |
|
|
orientation=orientation, |
|
|
hour=hour, |
|
|
color=color, |
|
|
month=month, |
|
|
latitude=latitude, |
|
|
indoor_temp=indoor_temp, |
|
|
outdoor_temp=outdoor_temp |
|
|
) |
|
|
|
|
|
|
|
|
cooling_load = u_value * area * cltd |
|
|
|
|
|
return cooling_load |
|
|
|
|
|
def calculate_roof_cooling_load(self, roof: Roof, outdoor_temp: float, indoor_temp: float, |
|
|
month: str, hour: int, latitude: str = "40N", |
|
|
color: str = "Dark") -> float: |
|
|
""" |
|
|
Calculate cooling load through a roof using the CLTD method. |
|
|
|
|
|
Args: |
|
|
roof: Roof object |
|
|
outdoor_temp: Outdoor temperature in °C |
|
|
indoor_temp: Indoor temperature in °C |
|
|
month: Month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec) |
|
|
hour: Hour of the day (0-23) |
|
|
latitude: Latitude (24N, 32N, 40N, 48N, 56N) |
|
|
color: Surface color (Dark, Medium, Light) |
|
|
|
|
|
Returns: |
|
|
Cooling load in W |
|
|
""" |
|
|
|
|
|
u_value = roof.u_value |
|
|
area = roof.area |
|
|
roof_group = roof.roof_group |
|
|
|
|
|
|
|
|
cltd = self.ashrae_tables.calculate_corrected_cltd_roof( |
|
|
roof_group=roof_group, |
|
|
hour=hour, |
|
|
color=color, |
|
|
month=month, |
|
|
latitude=latitude, |
|
|
indoor_temp=indoor_temp, |
|
|
outdoor_temp=outdoor_temp |
|
|
) |
|
|
|
|
|
|
|
|
cooling_load = u_value * area * cltd |
|
|
|
|
|
return cooling_load |
|
|
|
|
|
def calculate_window_cooling_load(self, window: Window, outdoor_temp: float, indoor_temp: float, |
|
|
month: str, hour: int, latitude: str = "40N_JUL", |
|
|
shading_coefficient: float = 1.0) -> Dict[str, float]: |
|
|
""" |
|
|
Calculate cooling load through a window using the CLTD/SCL method. |
|
|
|
|
|
Args: |
|
|
window: Window object |
|
|
outdoor_temp: Outdoor temperature in °C |
|
|
indoor_temp: Indoor temperature in °C |
|
|
month: Month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec) |
|
|
hour: Hour of the day (0-23) |
|
|
latitude: Latitude and month key (default: "40N_JUL") |
|
|
shading_coefficient: Shading coefficient (0-1) |
|
|
|
|
|
Returns: |
|
|
Dictionary with conduction, solar, and total cooling loads in W |
|
|
""" |
|
|
|
|
|
u_value = window.u_value |
|
|
area = window.area |
|
|
orientation = window.orientation.value |
|
|
shgc = window.shgc |
|
|
|
|
|
|
|
|
delta_t = outdoor_temp - indoor_temp |
|
|
conduction_load = u_value * area * delta_t |
|
|
|
|
|
|
|
|
scl = self.ashrae_tables.get_scl(orientation, hour, latitude) |
|
|
solar_load = area * shgc * shading_coefficient * scl |
|
|
|
|
|
|
|
|
total_load = conduction_load + solar_load |
|
|
|
|
|
return { |
|
|
"conduction": conduction_load, |
|
|
"solar": solar_load, |
|
|
"total": total_load |
|
|
} |
|
|
|
|
|
def calculate_door_cooling_load(self, door: Door, outdoor_temp: float, indoor_temp: float) -> float: |
|
|
""" |
|
|
Calculate cooling load through a door using simple conduction. |
|
|
|
|
|
Args: |
|
|
door: Door object |
|
|
outdoor_temp: Outdoor temperature in °C |
|
|
indoor_temp: Indoor temperature in °C |
|
|
|
|
|
Returns: |
|
|
Cooling load in W |
|
|
""" |
|
|
|
|
|
u_value = door.u_value |
|
|
area = door.area |
|
|
|
|
|
|
|
|
delta_t = outdoor_temp - indoor_temp |
|
|
cooling_load = u_value * area * delta_t |
|
|
|
|
|
return cooling_load |
|
|
|
|
|
def calculate_floor_cooling_load(self, floor: Floor, ground_temp: float, indoor_temp: float) -> float: |
|
|
""" |
|
|
Calculate cooling load through a floor. |
|
|
|
|
|
Args: |
|
|
floor: Floor object |
|
|
ground_temp: Ground or adjacent space temperature in °C |
|
|
indoor_temp: Indoor temperature in °C |
|
|
|
|
|
Returns: |
|
|
Cooling load in W |
|
|
""" |
|
|
|
|
|
u_value = floor.u_value |
|
|
area = floor.area |
|
|
|
|
|
|
|
|
delta_t = ground_temp - indoor_temp |
|
|
cooling_load = u_value * area * delta_t |
|
|
|
|
|
|
|
|
return max(0, cooling_load) |
|
|
|
|
|
def calculate_infiltration_cooling_load(self, flow_rate: float, outdoor_temp: float, indoor_temp: float, |
|
|
outdoor_rh: float, indoor_rh: float) -> Dict[str, float]: |
|
|
""" |
|
|
Calculate sensible and latent cooling 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 cooling loads in W |
|
|
""" |
|
|
|
|
|
sensible_load = self.heat_transfer.infiltration_heat_transfer( |
|
|
flow_rate=flow_rate, |
|
|
delta_t=outdoor_temp - indoor_temp |
|
|
) |
|
|
|
|
|
|
|
|
outdoor_w = self.psychrometrics.relative_humidity_to_humidity_ratio( |
|
|
temperature=outdoor_temp, |
|
|
relative_humidity=outdoor_rh |
|
|
) |
|
|
|
|
|
indoor_w = self.psychrometrics.relative_humidity_to_humidity_ratio( |
|
|
temperature=indoor_temp, |
|
|
relative_humidity=indoor_rh |
|
|
) |
|
|
|
|
|
|
|
|
latent_load = self.heat_transfer.infiltration_latent_heat_transfer( |
|
|
flow_rate=flow_rate, |
|
|
delta_w=outdoor_w - indoor_w |
|
|
) |
|
|
|
|
|
|
|
|
total_load = sensible_load + latent_load |
|
|
|
|
|
return { |
|
|
"sensible": sensible_load, |
|
|
"latent": latent_load, |
|
|
"total": total_load |
|
|
} |
|
|
|
|
|
def calculate_ventilation_cooling_load(self, flow_rate: float, outdoor_temp: float, indoor_temp: float, |
|
|
outdoor_rh: float, indoor_rh: float) -> Dict[str, float]: |
|
|
""" |
|
|
Calculate sensible and latent cooling 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 cooling loads in W |
|
|
""" |
|
|
|
|
|
return self.calculate_infiltration_cooling_load( |
|
|
flow_rate=flow_rate, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp, |
|
|
outdoor_rh=outdoor_rh, |
|
|
indoor_rh=indoor_rh |
|
|
) |
|
|
|
|
|
def calculate_people_cooling_load(self, num_people: int, activity_level: str, hour: int) -> Dict[str, float]: |
|
|
""" |
|
|
Calculate sensible and latent cooling loads due to people. |
|
|
|
|
|
Args: |
|
|
num_people: Number of people |
|
|
activity_level: Activity level (Seated/Resting, Light Work, Medium Work, Heavy Work) |
|
|
hour: Hour of the day (0-23) |
|
|
|
|
|
Returns: |
|
|
Dictionary with sensible, latent, and total cooling loads in W |
|
|
""" |
|
|
|
|
|
if activity_level == "Seated/Resting": |
|
|
sensible_gain_per_person = 70 |
|
|
latent_gain_per_person = 45 |
|
|
elif activity_level == "Light Work": |
|
|
sensible_gain_per_person = 75 |
|
|
latent_gain_per_person = 55 |
|
|
elif activity_level == "Medium Work": |
|
|
sensible_gain_per_person = 75 |
|
|
latent_gain_per_person = 115 |
|
|
elif activity_level == "Heavy Work": |
|
|
sensible_gain_per_person = 80 |
|
|
latent_gain_per_person = 175 |
|
|
else: |
|
|
|
|
|
sensible_gain_per_person = 75 |
|
|
latent_gain_per_person = 55 |
|
|
|
|
|
|
|
|
instantaneous_sensible = num_people * sensible_gain_per_person |
|
|
instantaneous_latent = num_people * latent_gain_per_person |
|
|
|
|
|
|
|
|
clf = self.ashrae_tables.get_clf_people(hour) |
|
|
sensible_load = instantaneous_sensible * clf |
|
|
|
|
|
|
|
|
latent_load = instantaneous_latent |
|
|
|
|
|
|
|
|
total_load = sensible_load + latent_load |
|
|
|
|
|
return { |
|
|
"sensible": sensible_load, |
|
|
"latent": latent_load, |
|
|
"total": total_load |
|
|
} |
|
|
|
|
|
def calculate_lighting_cooling_load(self, power: float, usage_factor: float, |
|
|
special_allowance_factor: float, hour: int) -> float: |
|
|
""" |
|
|
Calculate cooling load due to lighting. |
|
|
|
|
|
Args: |
|
|
power: Lighting power in W |
|
|
usage_factor: Usage factor (0-1) |
|
|
special_allowance_factor: Special allowance factor for fixtures (1.0 for normal fixtures) |
|
|
hour: Hour of the day (0-23) |
|
|
|
|
|
Returns: |
|
|
Cooling load in W |
|
|
""" |
|
|
|
|
|
instantaneous_gain = power * usage_factor * special_allowance_factor |
|
|
|
|
|
|
|
|
clf = self.ashrae_tables.get_clf_lights(hour) |
|
|
cooling_load = instantaneous_gain * clf |
|
|
|
|
|
return cooling_load |
|
|
|
|
|
def calculate_equipment_cooling_load(self, power: float, usage_factor: float, |
|
|
radiation_factor: float, hour: int) -> Dict[str, float]: |
|
|
""" |
|
|
Calculate sensible and latent cooling loads due to equipment. |
|
|
|
|
|
Args: |
|
|
power: Equipment power in W |
|
|
usage_factor: Usage factor (0-1) |
|
|
radiation_factor: Radiation factor (0-1) |
|
|
hour: Hour of the day (0-23) |
|
|
|
|
|
Returns: |
|
|
Dictionary with sensible, latent, and total cooling loads in W |
|
|
""" |
|
|
|
|
|
instantaneous_gain = power * usage_factor |
|
|
|
|
|
|
|
|
radiant_gain = instantaneous_gain * radiation_factor |
|
|
convective_gain = instantaneous_gain * (1 - radiation_factor) |
|
|
|
|
|
|
|
|
clf = self.ashrae_tables.get_clf_equipment(hour) |
|
|
radiant_load = radiant_gain * clf |
|
|
|
|
|
|
|
|
convective_load = convective_gain |
|
|
|
|
|
|
|
|
sensible_load = radiant_load + convective_load |
|
|
|
|
|
|
|
|
latent_load = 0 |
|
|
|
|
|
|
|
|
total_load = sensible_load + latent_load |
|
|
|
|
|
return { |
|
|
"sensible": sensible_load, |
|
|
"latent": latent_load, |
|
|
"total": total_load |
|
|
} |
|
|
|
|
|
def calculate_total_cooling_load(self, building_components: Dict[str, List[Any]], |
|
|
building_info: Dict[str, Any], |
|
|
climate_data: Dict[str, Any], |
|
|
internal_loads: Dict[str, Any], |
|
|
hour: int = 15) -> Dict[str, Any]: |
|
|
""" |
|
|
Calculate total cooling load for a building. |
|
|
|
|
|
Args: |
|
|
building_components: Dictionary with lists of building components |
|
|
building_info: Dictionary with building information |
|
|
climate_data: Dictionary with climate data |
|
|
internal_loads: Dictionary with internal loads information |
|
|
hour: Design hour (default: 15, which is 3 PM) |
|
|
|
|
|
Returns: |
|
|
Dictionary with detailed cooling load results |
|
|
""" |
|
|
|
|
|
results = { |
|
|
"walls": 0, |
|
|
"roofs": 0, |
|
|
"floors": 0, |
|
|
"windows_conduction": 0, |
|
|
"windows_solar": 0, |
|
|
"doors": 0, |
|
|
"infiltration_sensible": 0, |
|
|
"infiltration_latent": 0, |
|
|
"ventilation_sensible": 0, |
|
|
"ventilation_latent": 0, |
|
|
"people_sensible": 0, |
|
|
"people_latent": 0, |
|
|
"lights": 0, |
|
|
"equipment_sensible": 0, |
|
|
"equipment_latent": 0, |
|
|
"detailed_loads": { |
|
|
"walls": [], |
|
|
"roofs": [], |
|
|
"floors": [], |
|
|
"windows": [], |
|
|
"doors": [] |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
outdoor_temp = climate_data.get("design_cooling_temp", 35) |
|
|
indoor_temp = building_info.get("indoor_cooling_temp", 24) |
|
|
outdoor_rh = climate_data.get("design_cooling_rh", 50) |
|
|
indoor_rh = building_info.get("indoor_cooling_rh", 50) |
|
|
month = climate_data.get("design_cooling_month", "Jul") |
|
|
latitude = climate_data.get("latitude", "40N") |
|
|
|
|
|
|
|
|
for wall in building_components.get("walls", []): |
|
|
wall_load = self.calculate_wall_cooling_load( |
|
|
wall=wall, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp, |
|
|
month=month, |
|
|
hour=hour, |
|
|
latitude=latitude |
|
|
) |
|
|
|
|
|
results["walls"] += wall_load |
|
|
|
|
|
|
|
|
results["detailed_loads"]["walls"].append({ |
|
|
"name": wall.name, |
|
|
"orientation": wall.orientation.value, |
|
|
"area": wall.area, |
|
|
"u_value": wall.u_value, |
|
|
"cltd": (wall_load / (wall.area * wall.u_value)) if (wall.area * wall.u_value) > 0 else 0, |
|
|
"load": wall_load / 1000 |
|
|
}) |
|
|
|
|
|
|
|
|
for roof in building_components.get("roofs", []): |
|
|
roof_load = self.calculate_roof_cooling_load( |
|
|
roof=roof, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp, |
|
|
month=month, |
|
|
hour=hour, |
|
|
latitude=latitude |
|
|
) |
|
|
|
|
|
results["roofs"] += roof_load |
|
|
|
|
|
|
|
|
results["detailed_loads"]["roofs"].append({ |
|
|
"name": roof.name, |
|
|
"orientation": "Horizontal", |
|
|
"area": roof.area, |
|
|
"u_value": roof.u_value, |
|
|
"cltd": (roof_load / (roof.area * roof.u_value)) if (roof.area * roof.u_value) > 0 else 0, |
|
|
"load": roof_load / 1000 |
|
|
}) |
|
|
|
|
|
|
|
|
for floor in building_components.get("floors", []): |
|
|
ground_temp = climate_data.get("ground_temp", 15) |
|
|
floor_load = self.calculate_floor_cooling_load( |
|
|
floor=floor, |
|
|
ground_temp=ground_temp, |
|
|
indoor_temp=indoor_temp |
|
|
) |
|
|
|
|
|
results["floors"] += floor_load |
|
|
|
|
|
|
|
|
results["detailed_loads"]["floors"].append({ |
|
|
"name": floor.name, |
|
|
"area": floor.area, |
|
|
"u_value": floor.u_value, |
|
|
"delta_t": ground_temp - indoor_temp, |
|
|
"load": floor_load / 1000 |
|
|
}) |
|
|
|
|
|
|
|
|
for window in building_components.get("windows", []): |
|
|
window_load = self.calculate_window_cooling_load( |
|
|
window=window, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp, |
|
|
month=month, |
|
|
hour=hour, |
|
|
latitude=f"{latitude}_{month.upper()[:3]}", |
|
|
shading_coefficient=window.shading_coefficient |
|
|
) |
|
|
|
|
|
results["windows_conduction"] += window_load["conduction"] |
|
|
results["windows_solar"] += window_load["solar"] |
|
|
|
|
|
|
|
|
results["detailed_loads"]["windows"].append({ |
|
|
"name": window.name, |
|
|
"orientation": window.orientation.value, |
|
|
"area": window.area, |
|
|
"u_value": window.u_value, |
|
|
"shgc": window.shgc, |
|
|
"scl": window_load["solar"] / (window.area * window.shgc * window.shading_coefficient) if (window.area * window.shgc * window.shading_coefficient) > 0 else 0, |
|
|
"conduction_load": window_load["conduction"] / 1000, |
|
|
"solar_load": window_load["solar"] / 1000, |
|
|
"load": window_load["total"] / 1000 |
|
|
}) |
|
|
|
|
|
|
|
|
for door in building_components.get("doors", []): |
|
|
door_load = self.calculate_door_cooling_load( |
|
|
door=door, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp |
|
|
) |
|
|
|
|
|
results["doors"] += door_load |
|
|
|
|
|
|
|
|
results["detailed_loads"]["doors"].append({ |
|
|
"name": door.name, |
|
|
"orientation": door.orientation.value, |
|
|
"area": door.area, |
|
|
"u_value": door.u_value, |
|
|
"delta_t": outdoor_temp - indoor_temp, |
|
|
"load": door_load / 1000 |
|
|
}) |
|
|
|
|
|
|
|
|
if "infiltration" in building_info: |
|
|
infiltration_info = building_info["infiltration"] |
|
|
|
|
|
|
|
|
if "ach" in infiltration_info: |
|
|
volume = building_info.get("volume", building_info.get("floor_area", 100) * 3) |
|
|
flow_rate = self.heat_transfer.air_exchange_rate_to_flow_rate( |
|
|
ach=infiltration_info["ach"], |
|
|
volume=volume |
|
|
) |
|
|
else: |
|
|
flow_rate = infiltration_info.get("flow_rate", 0.05) |
|
|
|
|
|
infiltration_load = self.calculate_infiltration_cooling_load( |
|
|
flow_rate=flow_rate, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp, |
|
|
outdoor_rh=outdoor_rh, |
|
|
indoor_rh=indoor_rh |
|
|
) |
|
|
|
|
|
results["infiltration_sensible"] += infiltration_load["sensible"] |
|
|
results["infiltration_latent"] += infiltration_load["latent"] |
|
|
|
|
|
|
|
|
if "ventilation" in building_info: |
|
|
ventilation_info = building_info["ventilation"] |
|
|
|
|
|
|
|
|
if "ach" in ventilation_info: |
|
|
volume = building_info.get("volume", building_info.get("floor_area", 100) * 3) |
|
|
flow_rate = self.heat_transfer.air_exchange_rate_to_flow_rate( |
|
|
ach=ventilation_info["ach"], |
|
|
volume=volume |
|
|
) |
|
|
else: |
|
|
flow_rate = ventilation_info.get("flow_rate", 0.1) |
|
|
|
|
|
ventilation_load = self.calculate_ventilation_cooling_load( |
|
|
flow_rate=flow_rate, |
|
|
outdoor_temp=outdoor_temp, |
|
|
indoor_temp=indoor_temp, |
|
|
outdoor_rh=outdoor_rh, |
|
|
indoor_rh=indoor_rh |
|
|
) |
|
|
|
|
|
results["ventilation_sensible"] += ventilation_load["sensible"] |
|
|
results["ventilation_latent"] += ventilation_load["latent"] |
|
|
|
|
|
|
|
|
if "people" in internal_loads: |
|
|
people_info = internal_loads["people"] |
|
|
num_people = people_info.get("count", 1) |
|
|
activity_level = people_info.get("activity", "Light Work") |
|
|
|
|
|
people_load = self.calculate_people_cooling_load( |
|
|
num_people=num_people, |
|
|
activity_level=activity_level, |
|
|
hour=hour |
|
|
) |
|
|
|
|
|
results["people_sensible"] += people_load["sensible"] |
|
|
results["people_latent"] += people_load["latent"] |
|
|
|
|
|
|
|
|
if "lighting" in internal_loads: |
|
|
lighting_info = internal_loads["lighting"] |
|
|
power = lighting_info.get("power", 0) |
|
|
usage_factor = lighting_info.get("usage_factor", 1.0) |
|
|
special_allowance_factor = lighting_info.get("special_allowance_factor", 1.0) |
|
|
|
|
|
lighting_load = self.calculate_lighting_cooling_load( |
|
|
power=power, |
|
|
usage_factor=usage_factor, |
|
|
special_allowance_factor=special_allowance_factor, |
|
|
hour=hour |
|
|
) |
|
|
|
|
|
results["lights"] += lighting_load |
|
|
|
|
|
|
|
|
if "equipment" in internal_loads: |
|
|
equipment_info = internal_loads["equipment"] |
|
|
power = equipment_info.get("power", 0) |
|
|
usage_factor = equipment_info.get("usage_factor", 1.0) |
|
|
radiation_factor = equipment_info.get("radiation_factor", 0.3) |
|
|
|
|
|
equipment_load = self.calculate_equipment_cooling_load( |
|
|
power=power, |
|
|
usage_factor=usage_factor, |
|
|
radiation_factor=radiation_factor, |
|
|
hour=hour |
|
|
) |
|
|
|
|
|
results["equipment_sensible"] += equipment_load["sensible"] |
|
|
results["equipment_latent"] += equipment_load["latent"] |
|
|
|
|
|
|
|
|
envelope_load = ( |
|
|
results["walls"] + |
|
|
results["roofs"] + |
|
|
results["floors"] + |
|
|
results["windows_conduction"] + |
|
|
results["windows_solar"] + |
|
|
results["doors"] |
|
|
) |
|
|
|
|
|
infiltration_load = results["infiltration_sensible"] + results["infiltration_latent"] |
|
|
ventilation_load = results["ventilation_sensible"] + results["ventilation_latent"] |
|
|
people_load = results["people_sensible"] + results["people_latent"] |
|
|
equipment_load = results["equipment_sensible"] + results["equipment_latent"] |
|
|
|
|
|
internal_load = people_load + results["lights"] + equipment_load |
|
|
|
|
|
|
|
|
sensible_load = ( |
|
|
envelope_load + |
|
|
results["infiltration_sensible"] + |
|
|
results["ventilation_sensible"] + |
|
|
results["people_sensible"] + |
|
|
results["lights"] + |
|
|
results["equipment_sensible"] |
|
|
) |
|
|
|
|
|
latent_load = ( |
|
|
results["infiltration_latent"] + |
|
|
results["ventilation_latent"] + |
|
|
results["people_latent"] + |
|
|
results["equipment_latent"] |
|
|
) |
|
|
|
|
|
total_load = sensible_load + latent_load |
|
|
|
|
|
|
|
|
sensible_heat_ratio = sensible_load / total_load if total_load > 0 else 1.0 |
|
|
|
|
|
|
|
|
results.update({ |
|
|
"envelope": envelope_load, |
|
|
"infiltration": infiltration_load, |
|
|
"ventilation": ventilation_load, |
|
|
"internal": internal_load, |
|
|
"sensible": sensible_load, |
|
|
"latent": latent_load, |
|
|
"total": total_load, |
|
|
"sensible_heat_ratio": sensible_heat_ratio |
|
|
}) |
|
|
|
|
|
|
|
|
floor_area = building_info.get("floor_area", 100) |
|
|
results["load_per_area"] = total_load / floor_area if floor_area > 0 else 0 |
|
|
|
|
|
return results |
|
|
|