Spaces:
Sleeping
Sleeping
Update data/climate_data.py
Browse files- data/climate_data.py +50 -23
data/climate_data.py
CHANGED
|
@@ -93,6 +93,36 @@ class ClimateData:
|
|
| 93 |
self.countries = sorted(list(set(loc.country for loc in self.locations.values())))
|
| 94 |
self.country_states = self._group_locations_by_country_state()
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
def display_climate_input(self, session_state: Dict[str, Any]):
|
| 97 |
"""Display form for manual input or EPW upload in Streamlit."""
|
| 98 |
st.title("Climate Data")
|
|
@@ -191,15 +221,18 @@ class ClimateData:
|
|
| 191 |
for col in epw_data.columns:
|
| 192 |
epw_data[col] = pd.to_numeric(epw_data[col], errors='coerce')
|
| 193 |
|
| 194 |
-
# Extract key columns (
|
| 195 |
months = epw_data[1].values # Month
|
| 196 |
-
dry_bulb = epw_data[
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
# Check for critical NaN issues
|
| 201 |
-
if np.all(np.isnan(dry_bulb)) or np.all(np.isnan(humidity)):
|
| 202 |
-
raise ValueError("Dry bulb
|
| 203 |
|
| 204 |
# Calculate HDD and CDD (base 18°C)
|
| 205 |
daily_temps = np.nanmean(dry_bulb.reshape(-1, 24), axis=1)
|
|
@@ -209,8 +242,7 @@ class ClimateData:
|
|
| 209 |
# Design conditions
|
| 210 |
winter_design_temp = round(np.nanpercentile(dry_bulb, 0.4), 1) # 99.6% heating design
|
| 211 |
summer_design_temp_db = round(np.nanpercentile(dry_bulb, 99.6), 1) # 0.4% cooling design DB
|
| 212 |
-
|
| 213 |
-
summer_design_temp_wb = round(wet_bulb[summer_idx], 1) if not np.isnan(wet_bulb[summer_idx]) else 25.0
|
| 214 |
summer_mask = (months >= 6) & (months <= 8)
|
| 215 |
summer_temps = dry_bulb[summer_mask].reshape(-1, 24)
|
| 216 |
summer_daily_range = round(np.nanmean(np.nanmax(summer_temps, axis=1) - np.nanmin(summer_temps, axis=1)), 1)
|
|
@@ -247,7 +279,7 @@ class ClimateData:
|
|
| 247 |
monthly_humidity=monthly_humidity
|
| 248 |
)
|
| 249 |
self.add_location(location)
|
| 250 |
-
st.success("Climate data extracted from EPW file!")
|
| 251 |
self.display_design_conditions(location)
|
| 252 |
self.visualize_data(location, epw_data=epw_data)
|
| 253 |
except Exception as e:
|
|
@@ -333,7 +365,7 @@ class ClimateData:
|
|
| 333 |
|
| 334 |
# Add min/max for EPW data only
|
| 335 |
if epw_data is not None:
|
| 336 |
-
dry_bulb = epw_data[
|
| 337 |
month_col = epw_data[1].values
|
| 338 |
temps_min = []
|
| 339 |
temps_max = []
|
|
@@ -382,7 +414,8 @@ class ClimateData:
|
|
| 382 |
|
| 383 |
# Add min/max for EPW data only
|
| 384 |
if epw_data is not None:
|
| 385 |
-
humidity = epw_data[
|
|
|
|
| 386 |
humidity_min = []
|
| 387 |
humidity_max = []
|
| 388 |
for i in range(1, 13):
|
|
@@ -425,23 +458,17 @@ class ClimateData:
|
|
| 425 |
|
| 426 |
@classmethod
|
| 427 |
def from_json(cls, file_path: str) -> 'ClimateData':
|
| 428 |
-
"""
|
| 429 |
with open(file_path, 'r') as f:
|
| 430 |
data = json.load(f)
|
| 431 |
climate_data = cls()
|
| 432 |
-
climate_data.locations = {}
|
| 433 |
for loc_id, loc_dict in data.items():
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
climate_data.country_states = climate_data._group_locations_by_country_state()
|
| 437 |
return climate_data
|
| 438 |
|
| 439 |
-
|
| 440 |
if __name__ == "__main__":
|
| 441 |
-
if "building_info" not in st.session_state:
|
| 442 |
-
st.session_state.building_info = {"country": "Iceland", "city": "Reykjavik"}
|
| 443 |
-
if "page" not in st.session_state:
|
| 444 |
-
st.session_state.page = "Climate Data"
|
| 445 |
-
|
| 446 |
climate_data = ClimateData()
|
| 447 |
-
|
|
|
|
|
|
| 93 |
self.countries = sorted(list(set(loc.country for loc in self.locations.values())))
|
| 94 |
self.country_states = self._group_locations_by_country_state()
|
| 95 |
|
| 96 |
+
@staticmethod
|
| 97 |
+
def calculate_wet_bulb(dry_bulb: np.ndarray, relative_humidity: np.ndarray) -> np.ndarray:
|
| 98 |
+
"""Calculate Wet Bulb Temperature using Stull (2011) approximation.
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
dry_bulb (np.ndarray): Dry Bulb Temperature (°C)
|
| 102 |
+
relative_humidity (np.ndarray): Relative Humidity (%)
|
| 103 |
+
|
| 104 |
+
Returns:
|
| 105 |
+
np.ndarray: Wet Bulb Temperature (°C)
|
| 106 |
+
"""
|
| 107 |
+
# Ensure inputs are numpy arrays and handle NaN values
|
| 108 |
+
db = np.array(dry_bulb, dtype=float)
|
| 109 |
+
rh = np.array(relative_humidity, dtype=float)
|
| 110 |
+
|
| 111 |
+
# Stull formula
|
| 112 |
+
term1 = db * np.arctan(0.151977 * (rh + 8.313659)**0.5)
|
| 113 |
+
term2 = np.arctan(db + rh)
|
| 114 |
+
term3 = np.arctan(rh - 1.676331)
|
| 115 |
+
term4 = 0.00391838 * rh**1.5 * np.arctan(0.023101 * rh)
|
| 116 |
+
term5 = -4.686035
|
| 117 |
+
|
| 118 |
+
wet_bulb = term1 + term2 - term3 + term4 + term5
|
| 119 |
+
|
| 120 |
+
# Mask invalid values (e.g., RH < 5% or > 99%, or extreme DBT)
|
| 121 |
+
invalid_mask = (rh < 5) | (rh > 99) | (db < -20) | (db > 50) | np.isnan(db) | np.isnan(rh)
|
| 122 |
+
wet_bulb[invalid_mask] = np.nan
|
| 123 |
+
|
| 124 |
+
return wet_bulb
|
| 125 |
+
|
| 126 |
def display_climate_input(self, session_state: Dict[str, Any]):
|
| 127 |
"""Display form for manual input or EPW upload in Streamlit."""
|
| 128 |
st.title("Climate Data")
|
|
|
|
| 221 |
for col in epw_data.columns:
|
| 222 |
epw_data[col] = pd.to_numeric(epw_data[col], errors='coerce')
|
| 223 |
|
| 224 |
+
# Extract key columns (adjusted for your file)
|
| 225 |
months = epw_data[1].values # Month
|
| 226 |
+
dry_bulb = epw_data[6].values # Dry-bulb temperature (°C) - 7th in Excel
|
| 227 |
+
humidity = epw_data[8].values # Relative humidity (%) - 9th in Excel
|
| 228 |
+
pressure = epw_data[9].values # Atmospheric pressure (Pa) - 10th in Excel
|
| 229 |
+
|
| 230 |
+
# Calculate Wet Bulb Temperature
|
| 231 |
+
wet_bulb = self.calculate_wet_bulb(dry_bulb, humidity)
|
| 232 |
|
| 233 |
# Check for critical NaN issues
|
| 234 |
+
if np.all(np.isnan(dry_bulb)) or np.all(np.isnan(humidity)) or np.all(np.isnan(wet_bulb)):
|
| 235 |
+
raise ValueError("Dry bulb, humidity, or calculated wet bulb data is entirely NaN.")
|
| 236 |
|
| 237 |
# Calculate HDD and CDD (base 18°C)
|
| 238 |
daily_temps = np.nanmean(dry_bulb.reshape(-1, 24), axis=1)
|
|
|
|
| 242 |
# Design conditions
|
| 243 |
winter_design_temp = round(np.nanpercentile(dry_bulb, 0.4), 1) # 99.6% heating design
|
| 244 |
summer_design_temp_db = round(np.nanpercentile(dry_bulb, 99.6), 1) # 0.4% cooling design DB
|
| 245 |
+
summer_design_temp_wb = round(np.nanpercentile(wet_bulb, 99.6), 1) # 0.4% cooling design WB
|
|
|
|
| 246 |
summer_mask = (months >= 6) & (months <= 8)
|
| 247 |
summer_temps = dry_bulb[summer_mask].reshape(-1, 24)
|
| 248 |
summer_daily_range = round(np.nanmean(np.nanmax(summer_temps, axis=1) - np.nanmin(summer_temps, axis=1)), 1)
|
|
|
|
| 279 |
monthly_humidity=monthly_humidity
|
| 280 |
)
|
| 281 |
self.add_location(location)
|
| 282 |
+
st.success("Climate data extracted from EPW file with calculated Wet Bulb Temperature!")
|
| 283 |
self.display_design_conditions(location)
|
| 284 |
self.visualize_data(location, epw_data=epw_data)
|
| 285 |
except Exception as e:
|
|
|
|
| 365 |
|
| 366 |
# Add min/max for EPW data only
|
| 367 |
if epw_data is not None:
|
| 368 |
+
dry_bulb = epw_data[6].values # Dry-bulb temperature (°C)
|
| 369 |
month_col = epw_data[1].values
|
| 370 |
temps_min = []
|
| 371 |
temps_max = []
|
|
|
|
| 414 |
|
| 415 |
# Add min/max for EPW data only
|
| 416 |
if epw_data is not None:
|
| 417 |
+
humidity = epw_data[8].values # Relative humidity (%) - adjusted to 9th column
|
| 418 |
+
month_col = epw_data[1].values
|
| 419 |
humidity_min = []
|
| 420 |
humidity_max = []
|
| 421 |
for i in range(1, 13):
|
|
|
|
| 458 |
|
| 459 |
@classmethod
|
| 460 |
def from_json(cls, file_path: str) -> 'ClimateData':
|
| 461 |
+
"""Load climate data from a JSON file."""
|
| 462 |
with open(file_path, 'r') as f:
|
| 463 |
data = json.load(f)
|
| 464 |
climate_data = cls()
|
|
|
|
| 465 |
for loc_id, loc_dict in data.items():
|
| 466 |
+
location = ClimateLocation(**loc_dict)
|
| 467 |
+
climate_data.add_location(location)
|
|
|
|
| 468 |
return climate_data
|
| 469 |
|
| 470 |
+
# Example usage
|
| 471 |
if __name__ == "__main__":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
climate_data = ClimateData()
|
| 473 |
+
session_state = {"building_info": {"country": "Iceland", "city": "Reykjavik"}, "page": "Climate Data"}
|
| 474 |
+
climate_data.display_climate_input(session_state)
|