|
|
""" |
|
|
Weather data module for HVAC Load Calculator. |
|
|
This module provides access to weather data for cooling load calculations. |
|
|
""" |
|
|
|
|
|
from typing import Dict, List, Any, Optional, Tuple |
|
|
import pandas as pd |
|
|
import numpy as np |
|
|
import datetime |
|
|
|
|
|
class WeatherDataProvider: |
|
|
"""Class for providing weather data for cooling load calculations.""" |
|
|
|
|
|
def __init__(self): |
|
|
"""Initialize weather data provider.""" |
|
|
|
|
|
self.default_monthly_temps = { |
|
|
"Jan": {"avg_temp": 0.0, "daily_range": 8.0, "relative_humidity": 70.0}, |
|
|
"Feb": {"avg_temp": 2.0, "daily_range": 9.0, "relative_humidity": 65.0}, |
|
|
"Mar": {"avg_temp": 7.0, "daily_range": 10.0, "relative_humidity": 60.0}, |
|
|
"Apr": {"avg_temp": 12.0, "daily_range": 11.0, "relative_humidity": 55.0}, |
|
|
"May": {"avg_temp": 17.0, "daily_range": 12.0, "relative_humidity": 50.0}, |
|
|
"Jun": {"avg_temp": 22.0, "daily_range": 13.0, "relative_humidity": 55.0}, |
|
|
"Jul": {"avg_temp": 25.0, "daily_range": 13.0, "relative_humidity": 60.0}, |
|
|
"Aug": {"avg_temp": 24.0, "daily_range": 12.0, "relative_humidity": 65.0}, |
|
|
"Sep": {"avg_temp": 19.0, "daily_range": 11.0, "relative_humidity": 60.0}, |
|
|
"Oct": {"avg_temp": 13.0, "daily_range": 10.0, "relative_humidity": 65.0}, |
|
|
"Nov": {"avg_temp": 7.0, "daily_range": 9.0, "relative_humidity": 70.0}, |
|
|
"Dec": {"avg_temp": 2.0, "daily_range": 8.0, "relative_humidity": 75.0} |
|
|
} |
|
|
|
|
|
|
|
|
self.city_design_conditions = { |
|
|
"New York": { |
|
|
"latitude": "40N", |
|
|
"summer_db": 32.0, |
|
|
"summer_wb": 24.0, |
|
|
"summer_daily_range": 11.0, |
|
|
"winter_db": -10.0, |
|
|
"winter_humidity": 40.0 |
|
|
}, |
|
|
"Los Angeles": { |
|
|
"latitude": "34N", |
|
|
"summer_db": 35.0, |
|
|
"summer_wb": 23.0, |
|
|
"summer_daily_range": 10.0, |
|
|
"winter_db": 5.0, |
|
|
"winter_humidity": 50.0 |
|
|
}, |
|
|
"Chicago": { |
|
|
"latitude": "42N", |
|
|
"summer_db": 33.0, |
|
|
"summer_wb": 25.0, |
|
|
"summer_daily_range": 12.0, |
|
|
"winter_db": -18.0, |
|
|
"winter_humidity": 35.0 |
|
|
}, |
|
|
"Houston": { |
|
|
"latitude": "30N", |
|
|
"summer_db": 36.0, |
|
|
"summer_wb": 26.0, |
|
|
"summer_daily_range": 9.0, |
|
|
"winter_db": 0.0, |
|
|
"winter_humidity": 60.0 |
|
|
}, |
|
|
"Phoenix": { |
|
|
"latitude": "33N", |
|
|
"summer_db": 42.0, |
|
|
"summer_wb": 24.0, |
|
|
"summer_daily_range": 15.0, |
|
|
"winter_db": 2.0, |
|
|
"winter_humidity": 30.0 |
|
|
}, |
|
|
"Miami": { |
|
|
"latitude": "26N", |
|
|
"summer_db": 33.0, |
|
|
"summer_wb": 27.0, |
|
|
"summer_daily_range": 8.0, |
|
|
"winter_db": 10.0, |
|
|
"winter_humidity": 70.0 |
|
|
}, |
|
|
"London": { |
|
|
"latitude": "51N", |
|
|
"summer_db": 28.0, |
|
|
"summer_wb": 20.0, |
|
|
"summer_daily_range": 10.0, |
|
|
"winter_db": -5.0, |
|
|
"winter_humidity": 80.0 |
|
|
}, |
|
|
"Tokyo": { |
|
|
"latitude": "36N", |
|
|
"summer_db": 33.0, |
|
|
"summer_wb": 27.0, |
|
|
"summer_daily_range": 8.0, |
|
|
"winter_db": 0.0, |
|
|
"winter_humidity": 60.0 |
|
|
}, |
|
|
"Sydney": { |
|
|
"latitude": "34S", |
|
|
"summer_db": 31.0, |
|
|
"summer_wb": 22.0, |
|
|
"summer_daily_range": 9.0, |
|
|
"winter_db": 8.0, |
|
|
"winter_humidity": 65.0 |
|
|
}, |
|
|
"Singapore": { |
|
|
"latitude": "1N", |
|
|
"summer_db": 33.0, |
|
|
"summer_wb": 28.0, |
|
|
"summer_daily_range": 7.0, |
|
|
"winter_db": 23.0, |
|
|
"winter_humidity": 85.0 |
|
|
} |
|
|
} |
|
|
|
|
|
def get_monthly_weather_data(self, city: str = None) -> Dict[str, Dict[str, float]]: |
|
|
""" |
|
|
Get monthly weather data for a city. |
|
|
|
|
|
Args: |
|
|
city: City name (optional) |
|
|
|
|
|
Returns: |
|
|
Dictionary of monthly weather data |
|
|
""" |
|
|
|
|
|
if city and city in self.city_design_conditions: |
|
|
city_data = self.city_design_conditions[city] |
|
|
|
|
|
|
|
|
monthly_data = self.default_monthly_temps.copy() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
summer_months = ["Jun", "Jul", "Aug"] if "N" in city_data["latitude"] else ["Dec", "Jan", "Feb"] |
|
|
winter_months = ["Dec", "Jan", "Feb"] if "N" in city_data["latitude"] else ["Jun", "Jul", "Aug"] |
|
|
|
|
|
|
|
|
for month in summer_months: |
|
|
monthly_data[month]["avg_temp"] = city_data["summer_db"] - 3.0 |
|
|
monthly_data[month]["daily_range"] = city_data["summer_daily_range"] |
|
|
monthly_data[month]["relative_humidity"] = 60.0 |
|
|
|
|
|
|
|
|
for month in winter_months: |
|
|
monthly_data[month]["avg_temp"] = city_data["winter_db"] + 5.0 |
|
|
monthly_data[month]["daily_range"] = city_data["summer_daily_range"] * 0.7 |
|
|
monthly_data[month]["relative_humidity"] = city_data["winter_humidity"] |
|
|
|
|
|
|
|
|
transition_months = [m for m in monthly_data.keys() if m not in summer_months and m not in winter_months] |
|
|
|
|
|
|
|
|
all_months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
|
|
transition_months.sort(key=lambda m: all_months.index(m)) |
|
|
|
|
|
|
|
|
summer_avg_temp = sum(monthly_data[m]["avg_temp"] for m in summer_months) / len(summer_months) |
|
|
winter_avg_temp = sum(monthly_data[m]["avg_temp"] for m in winter_months) / len(winter_months) |
|
|
|
|
|
|
|
|
num_transition = len(transition_months) |
|
|
for i, month in enumerate(transition_months): |
|
|
factor = (i + 1) / (num_transition + 1) |
|
|
if "N" in city_data["latitude"]: |
|
|
if all_months.index(month) < all_months.index("Jun"): |
|
|
monthly_data[month]["avg_temp"] = winter_avg_temp + factor * (summer_avg_temp - winter_avg_temp) |
|
|
else: |
|
|
monthly_data[month]["avg_temp"] = summer_avg_temp - factor * (summer_avg_temp - winter_avg_temp) |
|
|
else: |
|
|
if all_months.index(month) < all_months.index("Dec") and all_months.index(month) >= all_months.index("Jun"): |
|
|
monthly_data[month]["avg_temp"] = winter_avg_temp + factor * (summer_avg_temp - winter_avg_temp) |
|
|
else: |
|
|
monthly_data[month]["avg_temp"] = summer_avg_temp - factor * (summer_avg_temp - winter_avg_temp) |
|
|
|
|
|
return monthly_data |
|
|
|
|
|
|
|
|
return self.default_monthly_temps |
|
|
|
|
|
def get_design_conditions(self, city: str) -> Dict[str, Any]: |
|
|
""" |
|
|
Get design conditions for a city. |
|
|
|
|
|
Args: |
|
|
city: City name |
|
|
|
|
|
Returns: |
|
|
Dictionary of design conditions |
|
|
""" |
|
|
if city in self.city_design_conditions: |
|
|
return self.city_design_conditions[city] |
|
|
else: |
|
|
|
|
|
return { |
|
|
"latitude": "40N", |
|
|
"summer_db": 35.0, |
|
|
"summer_wb": 25.0, |
|
|
"summer_daily_range": 11.0, |
|
|
"winter_db": -10.0, |
|
|
"winter_humidity": 50.0 |
|
|
} |
|
|
|
|
|
def get_hourly_temperatures(self, base_temp: float, daily_range: float) -> List[float]: |
|
|
""" |
|
|
Calculate hourly temperatures based on daily range. |
|
|
|
|
|
Args: |
|
|
base_temp: Base temperature (daily average) |
|
|
daily_range: Daily temperature range |
|
|
|
|
|
Returns: |
|
|
List of hourly temperatures |
|
|
""" |
|
|
from utils.ashrae_integration import get_daily_range_percentage |
|
|
|
|
|
hourly_temps = [] |
|
|
for hour in range(1, 25): |
|
|
|
|
|
percentage = get_daily_range_percentage(hour) / 100.0 |
|
|
|
|
|
|
|
|
temp = base_temp - daily_range / 2.0 + daily_range * percentage |
|
|
|
|
|
hourly_temps.append(temp) |
|
|
|
|
|
return hourly_temps |
|
|
|
|
|
def get_hourly_humidity(self, base_humidity: float, hourly_temps: List[float], base_temp: float) -> List[float]: |
|
|
""" |
|
|
Calculate hourly relative humidity based on temperature variation. |
|
|
|
|
|
Args: |
|
|
base_humidity: Base relative humidity (daily average) |
|
|
hourly_temps: Hourly temperatures |
|
|
base_temp: Base temperature (daily average) |
|
|
|
|
|
Returns: |
|
|
List of hourly relative humidity values |
|
|
""" |
|
|
hourly_humidity = [] |
|
|
|
|
|
for temp in hourly_temps: |
|
|
|
|
|
|
|
|
temp_diff = temp - base_temp |
|
|
humidity_adjustment = -temp_diff * 2.0 |
|
|
|
|
|
humidity = base_humidity + humidity_adjustment |
|
|
humidity = max(10.0, min(100.0, humidity)) |
|
|
|
|
|
hourly_humidity.append(humidity) |
|
|
|
|
|
return hourly_humidity |
|
|
|
|
|
|
|
|
weather_data_provider = WeatherDataProvider() |
|
|
|