Update data/climate_data.py
Browse files- data/climate_data.py +134 -15
data/climate_data.py
CHANGED
|
@@ -41,11 +41,13 @@ class ClimateLocation:
|
|
| 41 |
summer_design_temp_wb: float # 0.4% cooling design wet-bulb temperature (°C)
|
| 42 |
summer_daily_range: float # Mean daily temperature range in summer (°C)
|
| 43 |
wind_speed: float # Mean wind speed (m/s)
|
| 44 |
-
pressure: float #
|
| 45 |
hourly_data: List[Dict] # Hourly data for integration with main.py
|
|
|
|
|
|
|
| 46 |
|
| 47 |
-
def __init__(self, epw_file: pd.DataFrame, **kwargs):
|
| 48 |
-
"""Initialize ClimateLocation with EPW file data."""
|
| 49 |
self.id = kwargs.get("id")
|
| 50 |
self.country = kwargs.get("country")
|
| 51 |
self.state_province = kwargs.get("state_province", "N/A")
|
|
@@ -53,44 +55,63 @@ class ClimateLocation:
|
|
| 53 |
self.latitude = kwargs.get("latitude")
|
| 54 |
self.longitude = kwargs.get("longitude")
|
| 55 |
self.elevation = kwargs.get("elevation")
|
|
|
|
|
|
|
| 56 |
|
|
|
|
| 57 |
months = pd.to_numeric(epw_file[1], errors='coerce').values
|
|
|
|
|
|
|
| 58 |
dry_bulb = pd.to_numeric(epw_file[6], errors='coerce').values
|
| 59 |
humidity = pd.to_numeric(epw_file[8], errors='coerce').values
|
| 60 |
pressure = pd.to_numeric(epw_file[9], errors='coerce').values
|
| 61 |
-
wind_speed = pd.to_numeric(epw_file[21], errors='coerce').values
|
| 62 |
-
wind_direction = pd.to_numeric(epw_file[20], errors='coerce').values
|
| 63 |
global_radiation = pd.to_numeric(epw_file[13], errors='coerce').values
|
|
|
|
|
|
|
| 64 |
|
|
|
|
| 65 |
wet_bulb = ClimateData.calculate_wet_bulb(dry_bulb, humidity)
|
| 66 |
|
|
|
|
| 67 |
self.winter_design_temp = round(np.nanpercentile(dry_bulb, 0.4), 1)
|
| 68 |
self.summer_design_temp_db = round(np.nanpercentile(dry_bulb, 99.6), 1)
|
| 69 |
self.summer_design_temp_wb = round(np.nanpercentile(wet_bulb, 99.6), 1)
|
| 70 |
|
|
|
|
| 71 |
daily_temps = np.nanmean(dry_bulb.reshape(-1, 24), axis=1)
|
| 72 |
self.heating_degree_days = round(np.nansum(np.maximum(18 - daily_temps, 0)))
|
| 73 |
self.cooling_degree_days = round(np.nansum(np.maximum(daily_temps - 18, 0)))
|
| 74 |
|
|
|
|
| 75 |
summer_mask = (months >= 6) & (months <= 8)
|
| 76 |
summer_temps = dry_bulb[summer_mask].reshape(-1, 24)
|
| 77 |
self.summer_daily_range = round(np.nanmean(np.nanmax(summer_temps, axis=1) - np.nanmin(summer_temps, axis=1)), 1)
|
| 78 |
|
|
|
|
| 79 |
self.wind_speed = round(np.nanmean(wind_speed), 1)
|
| 80 |
self.pressure = round(np.nanmean(pressure), 1)
|
|
|
|
|
|
|
| 81 |
self.climate_zone = ClimateData.assign_climate_zone(self.heating_degree_days, self.cooling_degree_days, np.nanmean(humidity))
|
| 82 |
|
| 83 |
-
# Store hourly data
|
| 84 |
self.hourly_data = [
|
| 85 |
{
|
| 86 |
"month": int(months[i]),
|
| 87 |
-
"
|
|
|
|
| 88 |
"dry_bulb": float(dry_bulb[i]),
|
| 89 |
"relative_humidity": float(humidity[i]),
|
|
|
|
| 90 |
"global_horizontal_radiation": float(global_radiation[i]),
|
| 91 |
"wind_speed": float(wind_speed[i]),
|
| 92 |
"wind_direction": float(wind_direction[i])
|
| 93 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
]
|
| 95 |
|
| 96 |
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -112,7 +133,9 @@ class ClimateLocation:
|
|
| 112 |
"summer_daily_range": self.summer_daily_range,
|
| 113 |
"wind_speed": self.wind_speed,
|
| 114 |
"pressure": self.pressure,
|
| 115 |
-
"hourly_data": self.hourly_data
|
|
|
|
|
|
|
| 116 |
}
|
| 117 |
|
| 118 |
class ClimateData:
|
|
@@ -159,7 +182,8 @@ class ClimateData:
|
|
| 159 |
"id", "country", "city", "latitude", "longitude", "elevation",
|
| 160 |
"climate_zone", "heating_degree_days", "cooling_degree_days",
|
| 161 |
"winter_design_temp", "summer_design_temp_db", "summer_design_temp_wb",
|
| 162 |
-
"summer_daily_range", "wind_speed", "pressure", "hourly_data"
|
|
|
|
| 163 |
]
|
| 164 |
|
| 165 |
for field in required_fields:
|
|
@@ -182,18 +206,24 @@ class ClimateData:
|
|
| 182 |
return False
|
| 183 |
if not (0 <= data["wind_speed"] <= 20):
|
| 184 |
return False
|
| 185 |
-
if not (
|
| 186 |
return False
|
| 187 |
|
| 188 |
if not data["hourly_data"] or len(data["hourly_data"]) != 8760:
|
| 189 |
return False
|
| 190 |
for record in data["hourly_data"]:
|
| 191 |
-
if not (1 <= record["month"] <= 12
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
return False
|
| 193 |
if not (-50 <= record["dry_bulb"] <= 50):
|
| 194 |
return False
|
| 195 |
if not (0 <= record["relative_humidity"] <= 100):
|
| 196 |
return False
|
|
|
|
|
|
|
| 197 |
if not (0 <= record["global_horizontal_radiation"] <= 1200):
|
| 198 |
return False
|
| 199 |
if not (0 <= record["wind_speed"] <= 20):
|
|
@@ -201,6 +231,22 @@ class ClimateData:
|
|
| 201 |
if not (0 <= record["wind_direction"] <= 360):
|
| 202 |
return False
|
| 203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
return True
|
| 205 |
|
| 206 |
@staticmethod
|
|
@@ -237,6 +283,8 @@ class ClimateData:
|
|
| 237 |
# Process new EPW file
|
| 238 |
epw_content = uploaded_file.read().decode("utf-8")
|
| 239 |
epw_lines = epw_content.splitlines()
|
|
|
|
|
|
|
| 240 |
header = next(line for line in epw_lines if line.startswith("LOCATION"))
|
| 241 |
header_parts = header.split(",")
|
| 242 |
city = header_parts[1].strip()
|
|
@@ -246,6 +294,45 @@ class ClimateData:
|
|
| 246 |
longitude = float(header_parts[7])
|
| 247 |
elevation = float(header_parts[8])
|
| 248 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
data_start_idx = next(i for i, line in enumerate(epw_lines) if line.startswith("DATA PERIODS")) + 1
|
| 250 |
epw_data = pd.read_csv(StringIO("\n".join(epw_lines[data_start_idx:])), header=None, dtype=str)
|
| 251 |
|
|
@@ -254,13 +341,16 @@ class ClimateData:
|
|
| 254 |
if len(epw_data.columns) != 35:
|
| 255 |
raise ValueError(f"EPW file has {len(epw_data.columns)} columns, expected 35.")
|
| 256 |
|
| 257 |
-
for col in [1, 6, 8, 9, 13, 20, 21]:
|
| 258 |
epw_data[col] = pd.to_numeric(epw_data[col], errors='coerce')
|
| 259 |
if epw_data[col].isna().all():
|
| 260 |
raise ValueError(f"Column {col} contains only non-numeric or missing data.")
|
| 261 |
|
|
|
|
| 262 |
location = ClimateLocation(
|
| 263 |
epw_file=epw_data,
|
|
|
|
|
|
|
| 264 |
id=f"{country[:1].upper()}{city[:3].upper()}",
|
| 265 |
country=country,
|
| 266 |
state_province=state_province,
|
|
@@ -287,9 +377,11 @@ class ClimateData:
|
|
| 287 |
hourly_data = climate_data_dict["hourly_data"]
|
| 288 |
epw_data = pd.DataFrame({
|
| 289 |
1: [d["month"] for d in hourly_data], # Month
|
|
|
|
|
|
|
| 290 |
6: [d["dry_bulb"] for d in hourly_data], # Dry-bulb temperature
|
| 291 |
8: [d["relative_humidity"] for d in hourly_data], # Relative humidity
|
| 292 |
-
9: [
|
| 293 |
13: [d["global_horizontal_radiation"] for d in hourly_data], # Global horizontal radiation
|
| 294 |
20: [d["wind_direction"] for d in hourly_data], # Wind direction
|
| 295 |
21: [d["wind_speed"] for d in hourly_data], # Wind speed
|
|
@@ -298,6 +390,8 @@ class ClimateData:
|
|
| 298 |
# Create ClimateLocation with reconstructed epw_data
|
| 299 |
location = ClimateLocation(
|
| 300 |
epw_file=epw_data,
|
|
|
|
|
|
|
| 301 |
id=climate_data_dict["id"],
|
| 302 |
country=climate_data_dict["country"],
|
| 303 |
state_province=climate_data_dict["state_province"],
|
|
@@ -662,7 +756,32 @@ class ClimateData:
|
|
| 662 |
data = json.load(f)
|
| 663 |
climate_data = cls()
|
| 664 |
for loc_id, loc_dict in data.items():
|
| 665 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 666 |
climate_data.add_location(location)
|
| 667 |
return climate_data
|
| 668 |
|
|
|
|
| 41 |
summer_design_temp_wb: float # 0.4% cooling design wet-bulb temperature (°C)
|
| 42 |
summer_daily_range: float # Mean daily temperature range in summer (°C)
|
| 43 |
wind_speed: float # Mean wind speed (m/s)
|
| 44 |
+
pressure: float # Mean atmospheric pressure (Pa)
|
| 45 |
hourly_data: List[Dict] # Hourly data for integration with main.py
|
| 46 |
+
typical_extreme_periods: Dict[str, Dict] # Typical/extreme periods (summer/winter)
|
| 47 |
+
ground_temperatures: Dict[str, List[float]] # Monthly ground temperatures by depth
|
| 48 |
|
| 49 |
+
def __init__(self, epw_file: pd.DataFrame, typical_extreme_periods: Dict, ground_temperatures: Dict, **kwargs):
|
| 50 |
+
"""Initialize ClimateLocation with EPW file data and header information."""
|
| 51 |
self.id = kwargs.get("id")
|
| 52 |
self.country = kwargs.get("country")
|
| 53 |
self.state_province = kwargs.get("state_province", "N/A")
|
|
|
|
| 55 |
self.latitude = kwargs.get("latitude")
|
| 56 |
self.longitude = kwargs.get("longitude")
|
| 57 |
self.elevation = kwargs.get("elevation")
|
| 58 |
+
self.typical_extreme_periods = typical_extreme_periods
|
| 59 |
+
self.ground_temperatures = ground_temperatures
|
| 60 |
|
| 61 |
+
# Extract columns from EPW data
|
| 62 |
months = pd.to_numeric(epw_file[1], errors='coerce').values
|
| 63 |
+
days = pd.to_numeric(epw_file[2], errors='coerce').values
|
| 64 |
+
hours = pd.to_numeric(epw_file[3], errors='coerce').values
|
| 65 |
dry_bulb = pd.to_numeric(epw_file[6], errors='coerce').values
|
| 66 |
humidity = pd.to_numeric(epw_file[8], errors='coerce').values
|
| 67 |
pressure = pd.to_numeric(epw_file[9], errors='coerce').values
|
|
|
|
|
|
|
| 68 |
global_radiation = pd.to_numeric(epw_file[13], errors='coerce').values
|
| 69 |
+
wind_direction = pd.to_numeric(epw_file[20], errors='coerce').values
|
| 70 |
+
wind_speed = pd.to_numeric(epw_file[21], errors='coerce').values
|
| 71 |
|
| 72 |
+
# Calculate wet-bulb temperature
|
| 73 |
wet_bulb = ClimateData.calculate_wet_bulb(dry_bulb, humidity)
|
| 74 |
|
| 75 |
+
# Calculate design conditions
|
| 76 |
self.winter_design_temp = round(np.nanpercentile(dry_bulb, 0.4), 1)
|
| 77 |
self.summer_design_temp_db = round(np.nanpercentile(dry_bulb, 99.6), 1)
|
| 78 |
self.summer_design_temp_wb = round(np.nanpercentile(wet_bulb, 99.6), 1)
|
| 79 |
|
| 80 |
+
# Calculate degree days
|
| 81 |
daily_temps = np.nanmean(dry_bulb.reshape(-1, 24), axis=1)
|
| 82 |
self.heating_degree_days = round(np.nansum(np.maximum(18 - daily_temps, 0)))
|
| 83 |
self.cooling_degree_days = round(np.nansum(np.maximum(daily_temps - 18, 0)))
|
| 84 |
|
| 85 |
+
# Calculate summer daily temperature range (June–August, Southern Hemisphere)
|
| 86 |
summer_mask = (months >= 6) & (months <= 8)
|
| 87 |
summer_temps = dry_bulb[summer_mask].reshape(-1, 24)
|
| 88 |
self.summer_daily_range = round(np.nanmean(np.nanmax(summer_temps, axis=1) - np.nanmin(summer_temps, axis=1)), 1)
|
| 89 |
|
| 90 |
+
# Calculate mean wind speed and pressure
|
| 91 |
self.wind_speed = round(np.nanmean(wind_speed), 1)
|
| 92 |
self.pressure = round(np.nanmean(pressure), 1)
|
| 93 |
+
|
| 94 |
+
# Assign climate zone
|
| 95 |
self.climate_zone = ClimateData.assign_climate_zone(self.heating_degree_days, self.cooling_degree_days, np.nanmean(humidity))
|
| 96 |
|
| 97 |
+
# Store hourly data with enhanced fields
|
| 98 |
self.hourly_data = [
|
| 99 |
{
|
| 100 |
"month": int(months[i]),
|
| 101 |
+
"day": int(days[i]),
|
| 102 |
+
"hour": int(hours[i]),
|
| 103 |
"dry_bulb": float(dry_bulb[i]),
|
| 104 |
"relative_humidity": float(humidity[i]),
|
| 105 |
+
"atmospheric_pressure": float(pressure[i]),
|
| 106 |
"global_horizontal_radiation": float(global_radiation[i]),
|
| 107 |
"wind_speed": float(wind_speed[i]),
|
| 108 |
"wind_direction": float(wind_direction[i])
|
| 109 |
+
}
|
| 110 |
+
for i in range(len(months))
|
| 111 |
+
if not any(np.isnan([
|
| 112 |
+
months[i], days[i], hours[i], dry_bulb[i], humidity[i],
|
| 113 |
+
pressure[i], global_radiation[i], wind_speed[i], wind_direction[i]
|
| 114 |
+
]))
|
| 115 |
]
|
| 116 |
|
| 117 |
def to_dict(self) -> Dict[str, Any]:
|
|
|
|
| 133 |
"summer_daily_range": self.summer_daily_range,
|
| 134 |
"wind_speed": self.wind_speed,
|
| 135 |
"pressure": self.pressure,
|
| 136 |
+
"hourly_data": self.hourly_data,
|
| 137 |
+
"typical_extreme_periods": self.typical_extreme_periods,
|
| 138 |
+
"ground_temperatures": self.ground_temperatures
|
| 139 |
}
|
| 140 |
|
| 141 |
class ClimateData:
|
|
|
|
| 182 |
"id", "country", "city", "latitude", "longitude", "elevation",
|
| 183 |
"climate_zone", "heating_degree_days", "cooling_degree_days",
|
| 184 |
"winter_design_temp", "summer_design_temp_db", "summer_design_temp_wb",
|
| 185 |
+
"summer_daily_range", "wind_speed", "pressure", "hourly_data",
|
| 186 |
+
"typical_extreme_periods", "ground_temperatures"
|
| 187 |
]
|
| 188 |
|
| 189 |
for field in required_fields:
|
|
|
|
| 206 |
return False
|
| 207 |
if not (0 <= data["wind_speed"] <= 20):
|
| 208 |
return False
|
| 209 |
+
if not (80000 <= data["pressure"] <= 110000):
|
| 210 |
return False
|
| 211 |
|
| 212 |
if not data["hourly_data"] or len(data["hourly_data"]) != 8760:
|
| 213 |
return False
|
| 214 |
for record in data["hourly_data"]:
|
| 215 |
+
if not (1 <= record["month"] <= 12):
|
| 216 |
+
return False
|
| 217 |
+
if not (1 <= record["day"] <= 31):
|
| 218 |
+
return False
|
| 219 |
+
if not (1 <= record["hour"] <= 24):
|
| 220 |
return False
|
| 221 |
if not (-50 <= record["dry_bulb"] <= 50):
|
| 222 |
return False
|
| 223 |
if not (0 <= record["relative_humidity"] <= 100):
|
| 224 |
return False
|
| 225 |
+
if not (80000 <= record["atmospheric_pressure"] <= 110000):
|
| 226 |
+
return False
|
| 227 |
if not (0 <= record["global_horizontal_radiation"] <= 1200):
|
| 228 |
return False
|
| 229 |
if not (0 <= record["wind_speed"] <= 20):
|
|
|
|
| 231 |
if not (0 <= record["wind_direction"] <= 360):
|
| 232 |
return False
|
| 233 |
|
| 234 |
+
# Validate typical/extreme periods
|
| 235 |
+
expected_periods = ["summer_extreme", "summer_typical", "winter_extreme", "winter_typical"]
|
| 236 |
+
if not all(key in data["typical_extreme_periods"] for key in expected_periods):
|
| 237 |
+
return False
|
| 238 |
+
for period in data["typical_extreme_periods"].values():
|
| 239 |
+
for date in ["start", "end"]:
|
| 240 |
+
if not (1 <= period[date]["month"] <= 12 and 1 <= period[date]["day"] <= 31):
|
| 241 |
+
return False
|
| 242 |
+
|
| 243 |
+
# Validate ground temperatures
|
| 244 |
+
if not data["ground_temperatures"]:
|
| 245 |
+
return False
|
| 246 |
+
for depth, temps in data["ground_temperatures"].items():
|
| 247 |
+
if len(temps) != 12 or not all(0 <= t <= 50 for t in temps):
|
| 248 |
+
return False
|
| 249 |
+
|
| 250 |
return True
|
| 251 |
|
| 252 |
@staticmethod
|
|
|
|
| 283 |
# Process new EPW file
|
| 284 |
epw_content = uploaded_file.read().decode("utf-8")
|
| 285 |
epw_lines = epw_content.splitlines()
|
| 286 |
+
|
| 287 |
+
# Parse header
|
| 288 |
header = next(line for line in epw_lines if line.startswith("LOCATION"))
|
| 289 |
header_parts = header.split(",")
|
| 290 |
city = header_parts[1].strip()
|
|
|
|
| 294 |
longitude = float(header_parts[7])
|
| 295 |
elevation = float(header_parts[8])
|
| 296 |
|
| 297 |
+
# Parse TYPICAL/EXTREME PERIODS
|
| 298 |
+
typical_extreme_periods = {}
|
| 299 |
+
for line in epw_lines:
|
| 300 |
+
if line.startswith("TYPICAL/EXTREME PERIODS"):
|
| 301 |
+
parts = line.strip().split(',')
|
| 302 |
+
num_periods = int(parts[1])
|
| 303 |
+
for i in range(num_periods):
|
| 304 |
+
period_name = parts[2 + i*4]
|
| 305 |
+
period_type = parts[3 + i*4]
|
| 306 |
+
start_date = parts[4 + i*4]
|
| 307 |
+
end_date = parts[5 + i*4]
|
| 308 |
+
if period_name in [
|
| 309 |
+
"Summer - Week Nearest Max Temperature For Period",
|
| 310 |
+
"Summer - Week Nearest Average Temperature For Period",
|
| 311 |
+
"Winter - Week Nearest Min Temperature For Period",
|
| 312 |
+
"Winter - Week Nearest Average Temperature For Period"
|
| 313 |
+
]:
|
| 314 |
+
key = f"{'summer' if 'Summer' in period_name else 'winter'}_{'extreme' if 'Max' in period_name else 'typical' if 'Average' in period_name else ''}"
|
| 315 |
+
start_month, start_day = map(int, start_date.split('/'))
|
| 316 |
+
end_month, end_day = map(int, end_date.replace(' ', '').split('/'))
|
| 317 |
+
typical_extreme_periods[key] = {
|
| 318 |
+
"start": {"month": start_month, "day": start_day},
|
| 319 |
+
"end": {"month": end_month, "day": end_day}
|
| 320 |
+
}
|
| 321 |
+
break
|
| 322 |
+
|
| 323 |
+
# Parse GROUND TEMPERATURES
|
| 324 |
+
ground_temperatures = {}
|
| 325 |
+
for line in epw_lines:
|
| 326 |
+
if line.startswith("GROUND TEMPERATURES"):
|
| 327 |
+
parts = line.strip().split(',')
|
| 328 |
+
num_depths = int(parts[1])
|
| 329 |
+
for i in range(num_depths):
|
| 330 |
+
depth = parts[2 + i*16]
|
| 331 |
+
temps = [float(t) for t in parts[6 + i*16:18 + i*16]]
|
| 332 |
+
ground_temperatures[depth] = temps
|
| 333 |
+
break
|
| 334 |
+
|
| 335 |
+
# Read data section
|
| 336 |
data_start_idx = next(i for i, line in enumerate(epw_lines) if line.startswith("DATA PERIODS")) + 1
|
| 337 |
epw_data = pd.read_csv(StringIO("\n".join(epw_lines[data_start_idx:])), header=None, dtype=str)
|
| 338 |
|
|
|
|
| 341 |
if len(epw_data.columns) != 35:
|
| 342 |
raise ValueError(f"EPW file has {len(epw_data.columns)} columns, expected 35.")
|
| 343 |
|
| 344 |
+
for col in [1, 2, 3, 6, 8, 9, 13, 20, 21]:
|
| 345 |
epw_data[col] = pd.to_numeric(epw_data[col], errors='coerce')
|
| 346 |
if epw_data[col].isna().all():
|
| 347 |
raise ValueError(f"Column {col} contains only non-numeric or missing data.")
|
| 348 |
|
| 349 |
+
# Create ClimateLocation
|
| 350 |
location = ClimateLocation(
|
| 351 |
epw_file=epw_data,
|
| 352 |
+
typical_extreme_periods=typical_extreme_periods,
|
| 353 |
+
ground_temperatures=ground_temperatures,
|
| 354 |
id=f"{country[:1].upper()}{city[:3].upper()}",
|
| 355 |
country=country,
|
| 356 |
state_province=state_province,
|
|
|
|
| 377 |
hourly_data = climate_data_dict["hourly_data"]
|
| 378 |
epw_data = pd.DataFrame({
|
| 379 |
1: [d["month"] for d in hourly_data], # Month
|
| 380 |
+
2: [d["day"] for d in hourly_data], # Day
|
| 381 |
+
3: [d["hour"] for d in hourly_data], # Hour
|
| 382 |
6: [d["dry_bulb"] for d in hourly_data], # Dry-bulb temperature
|
| 383 |
8: [d["relative_humidity"] for d in hourly_data], # Relative humidity
|
| 384 |
+
9: [d["atmospheric_pressure"] for d in hourly_data], # Pressure
|
| 385 |
13: [d["global_horizontal_radiation"] for d in hourly_data], # Global horizontal radiation
|
| 386 |
20: [d["wind_direction"] for d in hourly_data], # Wind direction
|
| 387 |
21: [d["wind_speed"] for d in hourly_data], # Wind speed
|
|
|
|
| 390 |
# Create ClimateLocation with reconstructed epw_data
|
| 391 |
location = ClimateLocation(
|
| 392 |
epw_file=epw_data,
|
| 393 |
+
typical_extreme_periods=climate_data_dict["typical_extreme_periods"],
|
| 394 |
+
ground_temperatures=climate_data_dict["ground_temperatures"],
|
| 395 |
id=climate_data_dict["id"],
|
| 396 |
country=climate_data_dict["country"],
|
| 397 |
state_province=climate_data_dict["state_province"],
|
|
|
|
| 756 |
data = json.load(f)
|
| 757 |
climate_data = cls()
|
| 758 |
for loc_id, loc_dict in data.items():
|
| 759 |
+
# Rebuild epw_data from hourly_data
|
| 760 |
+
hourly_data = loc_dict["hourly_data"]
|
| 761 |
+
epw_data = pd.DataFrame({
|
| 762 |
+
1: [d["month"] for d in hourly_data],
|
| 763 |
+
2: [d["day"] for d in hourly_data],
|
| 764 |
+
3: [d["hour"] for d in hourly_data],
|
| 765 |
+
6: [d["dry_bulb"] for d in hourly_data],
|
| 766 |
+
8: [d["relative_humidity"] for d in hourly_data],
|
| 767 |
+
9: [d["atmospheric_pressure"] for d in hourly_data],
|
| 768 |
+
13: [d["global_horizontal_radiation"] for d in hourly_data],
|
| 769 |
+
20: [d["wind_direction"] for d in hourly_data],
|
| 770 |
+
21: [d["wind_speed"] for d in hourly_data],
|
| 771 |
+
})
|
| 772 |
+
location = ClimateLocation(
|
| 773 |
+
epw_file=epw_data,
|
| 774 |
+
typical_extreme_periods=loc_dict["typical_extreme_periods"],
|
| 775 |
+
ground_temperatures=loc_dict["ground_temperatures"],
|
| 776 |
+
id=loc_dict["id"],
|
| 777 |
+
country=loc_dict["country"],
|
| 778 |
+
state_province=loc_dict["state_province"],
|
| 779 |
+
city=loc_dict["city"],
|
| 780 |
+
latitude=loc_dict["latitude"],
|
| 781 |
+
longitude=loc_dict["longitude"],
|
| 782 |
+
elevation=loc_dict["elevation"]
|
| 783 |
+
)
|
| 784 |
+
location.hourly_data = loc_dict["hourly_data"] # Ensure consistency
|
| 785 |
climate_data.add_location(location)
|
| 786 |
return climate_data
|
| 787 |
|