mabuseif commited on
Commit
a851f2c
·
verified ·
1 Parent(s): 28192eb

Update app/climate_data.py

Browse files
Files changed (1) hide show
  1. app/climate_data.py +90 -5
app/climate_data.py CHANGED
@@ -32,6 +32,7 @@ logger = logging.getLogger(__name__)
32
  MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
33
  CLIMATE_ZONES = ["0A", "0B", "1A", "1B", "2A", "2B", "3A", "3B", "3C", "4A", "4B", "4C", "5A", "5B", "5C", "6A", "6B", "7", "8"]
34
  AU_CCH_DIR = "au_cch" # Relative path to au_cch folder
 
35
 
36
  # Location mapping for Australian climate projections
37
  LOCATION_MAPPING = {
@@ -357,9 +358,75 @@ class ClimateDataManager:
357
  "pressure": 101325.0
358
  }
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  def _process_hourly_data(self, df: pd.DataFrame) -> List[Dict[str, Any]]:
361
  """
362
- Process hourly data from EPW DataFrame.
363
 
364
  Args:
365
  df: DataFrame containing EPW data
@@ -372,7 +439,7 @@ class ClimateDataManager:
372
  try:
373
  # Ensure numeric columns
374
  numeric_columns = [
375
- "dry_bulb_temp", "relative_humidity", "atmospheric_pressure",
376
  "global_horizontal_radiation", "direct_normal_radiation",
377
  "diffuse_horizontal_radiation", "wind_speed", "wind_direction"
378
  ]
@@ -391,18 +458,32 @@ class ClimateDataManager:
391
  if pd.isna(row["month"]) or pd.isna(row["day"]) or pd.isna(row["hour"]) or pd.isna(row["dry_bulb_temp"]):
392
  continue # Skip rows with missing critical data
393
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  record = {
395
  "month": int(row["month"]),
396
  "day": int(row["day"]),
397
  "hour": int(row["hour"]),
398
  "dry_bulb": float(row["dry_bulb_temp"]) if not pd.isna(row["dry_bulb_temp"]) else 20.0,
 
399
  "relative_humidity": float(row["relative_humidity"]) if not pd.isna(row["relative_humidity"]) else 50.0,
400
  "atmospheric_pressure": float(row["atmospheric_pressure"]) if not pd.isna(row["atmospheric_pressure"]) else 101325.0,
401
  "global_horizontal_radiation": float(row["global_horizontal_radiation"]) if not pd.isna(row["global_horizontal_radiation"]) else 0.0,
402
  "direct_normal_radiation": float(row["direct_normal_radiation"]) if not pd.isna(row["direct_normal_radiation"]) else 0.0,
403
  "diffuse_horizontal_radiation": float(row["diffuse_horizontal_radiation"]) if not pd.isna(row["diffuse_horizontal_radiation"]) else 0.0,
404
  "wind_speed": float(row["wind_speed"]) if not pd.isna(row["wind_speed"]) else 0.0,
405
- "wind_direction": float(row["wind_direction"]) if not pd.isna(row["wind_direction"]) else 0.0
 
406
  }
407
  hourly_data.append(record)
408
 
@@ -769,13 +850,15 @@ def display_climate_summary(climate_data: Dict[str, Any]):
769
  "Day": record["day"],
770
  "Hour": record["hour"],
771
  "Dry Bulb Temp (°C)": f"{record['dry_bulb']:.1f}",
 
772
  "Relative Humidity (%)": f"{record['relative_humidity']:.1f}",
773
  "Atmospheric Pressure (Pa)": f"{record['atmospheric_pressure']:.1f}",
774
  "Global Horizontal Radiation (W/m²)": f"{record['global_horizontal_radiation']:.1f}",
775
  "Direct Normal Radiation (W/m²)": f"{record['direct_normal_radiation']:.1f}",
776
  "Diffuse Horizontal Radiation (W/m²)": f"{record['diffuse_horizontal_radiation']:.1f}",
777
  "Wind Speed (m/s)": f"{record['wind_speed']:.1f}",
778
- "Wind Direction (°)": f"{record['wind_direction']:.1f}"
 
779
  }
780
  for record in climate_data["hourly_data"]
781
  ]
@@ -812,10 +895,12 @@ def display_climate_help():
812
  EPW (EnergyPlus Weather) files contain hourly weather data for a specific location, including:
813
 
814
  * Dry-bulb temperature
 
815
  * Relative humidity
816
  * Solar radiation (direct and diffuse)
817
  * Wind speed and direction
818
  * Atmospheric pressure
 
819
 
820
  **Where to Find EPW Files:**
821
 
@@ -838,7 +923,7 @@ def display_climate_help():
838
  * Typical/Extreme periods
839
  * Ground temperatures by depth
840
  * Monthly average temperatures and solar radiation
841
- * Hourly data statistics with downloadable tables
842
 
843
  This information will be used throughout the calculation process.
844
  """)
 
32
  MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
33
  CLIMATE_ZONES = ["0A", "0B", "1A", "1B", "2A", "2B", "3A", "3B", "3C", "4A", "4B", "4C", "5A", "5B", "5C", "6A", "6B", "7", "8"]
34
  AU_CCH_DIR = "au_cch" # Relative path to au_cch folder
35
+ SKY_CLEARNESS_CONSTANT = 5.535e-6 # Constant k for Perez model Sky Clearness Index
36
 
37
  # Location mapping for Australian climate projections
38
  LOCATION_MAPPING = {
 
358
  "pressure": 101325.0
359
  }
360
 
361
+ def _calculate_dew_point(self, dry_bulb: float, relative_humidity: float, atmospheric_pressure: float) -> float:
362
+ """
363
+ Calculate dew point temperature using August-Roche-Magnus formula.
364
+
365
+ Args:
366
+ dry_bulb: Dry bulb temperature in °C
367
+ relative_humidity: Relative humidity in %
368
+ atmospheric_pressure: Atmospheric pressure in Pa (not used in simplified formula)
369
+
370
+ Returns:
371
+ Dew point temperature in °C
372
+ """
373
+ try:
374
+ # Step 1: Calculate saturation vapor pressure (hPa)
375
+ es = 6.1078 * 10 ** ((7.5 * dry_bulb) / (237.3 + dry_bulb))
376
+ es = es * 100 # Convert to Pa
377
+
378
+ # Step 2: Calculate actual vapor pressure
379
+ e = (relative_humidity * es) / 100
380
+
381
+ # Step 3: Calculate dew point temperature
382
+ if e <= 0:
383
+ logger.warning(f"Invalid vapor pressure {e} Pa, returning dry bulb temperature {dry_bulb}°C as dew point")
384
+ return dry_bulb
385
+ ln_term = math.log(e / 610.78)
386
+ dew_point = (237.3 * ln_term) / (7.5 - ln_term)
387
+
388
+ # Ensure dew point does not exceed dry bulb temperature
389
+ dew_point = min(dew_point, dry_bulb)
390
+
391
+ return round(dew_point, 1)
392
+ except (ValueError, ZeroDivisionError) as e:
393
+ logger.warning(f"Error calculating dew point: {str(e)}, returning dry bulb temperature {dry_bulb}°C")
394
+ return dry_bulb
395
+
396
+ def _calculate_sky_clearness_index(self, diffuse_horizontal: float, global_horizontal: float, direct_normal: float) -> Optional[float]:
397
+ """
398
+ Calculate Sky Clearness Index using the Perez model.
399
+
400
+ Args:
401
+ diffuse_horizontal: Diffuse horizontal irradiance in W/m²
402
+ global_horizontal: Global horizontal irradiance in W/m²
403
+ direct_normal: Direct normal irradiance in W/m²
404
+
405
+ Returns:
406
+ Sky Clearness Index (dimensionless) or None if undefined (e.g., nighttime)
407
+ """
408
+ try:
409
+ # Handle nighttime or invalid data
410
+ if global_horizontal <= 0 or diffuse_horizontal < 0 or direct_normal < 0:
411
+ return None
412
+
413
+ # Cap diffuse_horizontal to global_horizontal to handle potential measurement errors
414
+ diffuse_horizontal = min(diffuse_horizontal, global_horizontal)
415
+
416
+ # Calculate Sky Clearness Index using Perez model
417
+ k = SKY_CLEARNESS_CONSTANT
418
+ if global_horizontal == 0:
419
+ return None # Avoid division by zero
420
+ epsilon = ((diffuse_horizontal / global_horizontal) + (k * direct_normal)) / (1 + k)
421
+
422
+ return round(epsilon, 3)
423
+ except Exception as e:
424
+ logger.warning(f"Error calculating Sky Clearness Index: {str(e)}, returning None")
425
+ return None
426
+
427
  def _process_hourly_data(self, df: pd.DataFrame) -> List[Dict[str, Any]]:
428
  """
429
+ Process hourly data from EPW DataFrame, including dew point and Sky Clearness Index.
430
 
431
  Args:
432
  df: DataFrame containing EPW data
 
439
  try:
440
  # Ensure numeric columns
441
  numeric_columns = [
442
+ "dry_bulb_temp", "dew_point_temp", "relative_humidity", "atmospheric_pressure",
443
  "global_horizontal_radiation", "direct_normal_radiation",
444
  "diffuse_horizontal_radiation", "wind_speed", "wind_direction"
445
  ]
 
458
  if pd.isna(row["month"]) or pd.isna(row["day"]) or pd.isna(row["hour"]) or pd.isna(row["dry_bulb_temp"]):
459
  continue # Skip rows with missing critical data
460
 
461
+ # Calculate dew point temperature
462
+ dry_bulb = float(row["dry_bulb_temp"]) if not pd.isna(row["dry_bulb_temp"]) else 20.0
463
+ relative_humidity = float(row["relative_humidity"]) if not pd.isna(row["relative_humidity"]) else 50.0
464
+ atmospheric_pressure = float(row["atmospheric_pressure"]) if not pd.isna(row["atmospheric_pressure"]) else 101325.0
465
+ dew_point = self._calculate_dew_point(dry_bulb, relative_humidity, atmospheric_pressure)
466
+
467
+ # Calculate Sky Clearness Index
468
+ diffuse_horizontal = float(row["diffuse_horizontal_radiation"]) if not pd.isna(row["diffuse_horizontal_radiation"]) else 0.0
469
+ global_horizontal = float(row["global_horizontal_radiation"]) if not pd.isna(row["global_horizontal_radiation"]) else 0.0
470
+ direct_normal = float(row["direct_normal_radiation"]) if not pd.isna(row["direct_normal_radiation"]) else 0.0
471
+ sky_clearness_index = self._calculate_sky_clearness_index(diffuse_horizontal, global_horizontal, direct_normal)
472
+
473
  record = {
474
  "month": int(row["month"]),
475
  "day": int(row["day"]),
476
  "hour": int(row["hour"]),
477
  "dry_bulb": float(row["dry_bulb_temp"]) if not pd.isna(row["dry_bulb_temp"]) else 20.0,
478
+ "dew_point": dew_point,
479
  "relative_humidity": float(row["relative_humidity"]) if not pd.isna(row["relative_humidity"]) else 50.0,
480
  "atmospheric_pressure": float(row["atmospheric_pressure"]) if not pd.isna(row["atmospheric_pressure"]) else 101325.0,
481
  "global_horizontal_radiation": float(row["global_horizontal_radiation"]) if not pd.isna(row["global_horizontal_radiation"]) else 0.0,
482
  "direct_normal_radiation": float(row["direct_normal_radiation"]) if not pd.isna(row["direct_normal_radiation"]) else 0.0,
483
  "diffuse_horizontal_radiation": float(row["diffuse_horizontal_radiation"]) if not pd.isna(row["diffuse_horizontal_radiation"]) else 0.0,
484
  "wind_speed": float(row["wind_speed"]) if not pd.isna(row["wind_speed"]) else 0.0,
485
+ "wind_direction": float(row["wind_direction"]) if not pd.isna(row["wind_direction"]) else 0.0,
486
+ "sky_clearness_index": sky_clearness_index if sky_clearness_index is not None else None
487
  }
488
  hourly_data.append(record)
489
 
 
850
  "Day": record["day"],
851
  "Hour": record["hour"],
852
  "Dry Bulb Temp (°C)": f"{record['dry_bulb']:.1f}",
853
+ "Dew Point Temp (°C)": f"{record['dew_point']:.1f}" if record['dew_point'] is not None else "N/A",
854
  "Relative Humidity (%)": f"{record['relative_humidity']:.1f}",
855
  "Atmospheric Pressure (Pa)": f"{record['atmospheric_pressure']:.1f}",
856
  "Global Horizontal Radiation (W/m²)": f"{record['global_horizontal_radiation']:.1f}",
857
  "Direct Normal Radiation (W/m²)": f"{record['direct_normal_radiation']:.1f}",
858
  "Diffuse Horizontal Radiation (W/m²)": f"{record['diffuse_horizontal_radiation']:.1f}",
859
  "Wind Speed (m/s)": f"{record['wind_speed']:.1f}",
860
+ "Wind Direction (°)": f"{record['wind_direction']:.1f}",
861
+ "Sky Clearness Index": f"{record['sky_clearness_index']:.3f}" if record['sky_clearness_index'] is not None else "N/A"
862
  }
863
  for record in climate_data["hourly_data"]
864
  ]
 
895
  EPW (EnergyPlus Weather) files contain hourly weather data for a specific location, including:
896
 
897
  * Dry-bulb temperature
898
+ * Dew point temperature
899
  * Relative humidity
900
  * Solar radiation (direct and diffuse)
901
  * Wind speed and direction
902
  * Atmospheric pressure
903
+ * Sky Clearness Index (calculated)
904
 
905
  **Where to Find EPW Files:**
906
 
 
923
  * Typical/Extreme periods
924
  * Ground temperatures by depth
925
  * Monthly average temperatures and solar radiation
926
+ * Hourly data statistics with downloadable tables (including dew point and Sky Clearness Index)
927
 
928
  This information will be used throughout the calculation process.
929
  """)