""" Custom power density calculation using efficiency model. Calculates site-specific power density based on latitude and longitude. """ import os import sys from pathlib import Path import numpy as np from typing import Optional, Tuple # Path to efficiency_model directory (used in _load_interpolator) EFFICIENCY_MODEL_PATH = Path(__file__).parent / "efficiency_model" # Default power densities (baseline kWp/m²) DEFAULT_POWER_DENSITY = 0.18 # Standard baseline POWER_DENSITY_RANGE = (0.101, 0.17) # Expected operating range when lookup is available class EfficencyAwarePowerDensity: """ Calculate site-specific power density based on solar efficiency. Uses the efficiency model to adjust power density for geographic location. """ def __init__(self, efficiency_model=None): """ Initialize with optional pre-loaded efficiency model. Args: efficiency_model: Optional pre-loaded SolarEnergyInterpolator instance """ self.interpolator = efficiency_model self._load_interpolator() def _load_interpolator(self): """Lazy load the efficiency model interpolator""" if self.interpolator is not None: return try: # Add efficiency_model to sys.path temporarily import sys model_path_str = str(EFFICIENCY_MODEL_PATH) if model_path_str not in sys.path: sys.path.insert(0, model_path_str) # Import and initialize with data directory path (no cwd change needed) from efficiencyMap import SolarEnergyInterpolator self.interpolator = SolarEnergyInterpolator(data_dir=model_path_str) except Exception as e: print(f"Warning: Could not load efficiency model: {str(e)}") import traceback traceback.print_exc() self.interpolator = None def get_efficiency(self, latitude: float, longitude: float) -> float: """ Get solar efficiency for a location. Args: latitude: Site latitude (-60 to 65) longitude: Site longitude (-180 to 180) Returns: float: Solar efficiency as percentage (0-100) or 0 if unavailable """ # Ensure interpolator is loaded if self.interpolator is None: self._load_interpolator() if self.interpolator is None: return 0 try: efficiency = self.interpolator.get_solar_efficiency(latitude, longitude) return float(efficiency) if efficiency and efficiency > 0 else 0 except Exception as e: print(f"Warning: Efficiency calculation failed for ({latitude}, {longitude}): {str(e)}") return 0 def calculate_power_density(self, latitude: float, longitude: float, baseline_density: float = DEFAULT_POWER_DENSITY) -> float: """ Calculate site-specific power density based on solar efficiency. High efficiency areas (e.g., deserts) → Higher power density Low efficiency areas (e.g., cloudy regions) → Lower power density Args: latitude: Site latitude longitude: Site longitude baseline_density: Baseline power density in kWp/m² (default: 0.18) Returns: float: Site-specific power density in kWp/m² """ # Get efficiency (0-100%) efficiency = self.get_efficiency(latitude, longitude) if efficiency == 0: # Use baseline if efficiency model unavailable return baseline_density # Map the efficiency percentage directly into the calibrated power-density band. adjusted_density = efficiency / 100.0 # Clamp to the expected operating range. adjusted_density = np.clip(adjusted_density, POWER_DENSITY_RANGE[0], POWER_DENSITY_RANGE[1]) return round(adjusted_density, 4) def get_site_power_density_info(self, latitude: float, longitude: float, baseline_density: float = DEFAULT_POWER_DENSITY) -> dict: """ Get comprehensive power density information for a site. Args: latitude: Site latitude longitude: Site longitude baseline_density: Baseline power density Returns: dict: Power density information including efficiency and adjusted value """ efficiency = self.get_efficiency(latitude, longitude) adjusted_density = self.calculate_power_density(latitude, longitude, baseline_density) return { 'efficiency_percent': efficiency, 'baseline_density_kwp_m2': baseline_density, 'adjusted_density_kwp_m2': adjusted_density, 'density_adjustment_factor': adjusted_density / baseline_density if baseline_density > 0 else 1.0, 'location': f"{latitude:.4f}°, {longitude:.4f}°" } # Initialize global instance try: power_density_calculator = EfficencyAwarePowerDensity() except Exception as e: print(f"Warning: Could not initialize power density calculator: {str(e)}") power_density_calculator = None def get_power_density_for_site(latitude: float, longitude: float, baseline_density: float = DEFAULT_POWER_DENSITY) -> float: """ Convenience function to get power density for a site. Args: latitude: Site latitude longitude: Site longitude baseline_density: Baseline power density (default: 0.18 kWp/m²) Returns: float: Site-specific power density in kWp/m² """ # Create fresh instance to ensure no caching issues try: calculator = EfficencyAwarePowerDensity() return calculator.calculate_power_density(latitude, longitude, baseline_density) except Exception as e: print(f"Error calculating power density: {str(e)}") return baseline_density def get_power_density_info(latitude: float, longitude: float, baseline_density: float = DEFAULT_POWER_DENSITY) -> dict: """ Convenience function to get detailed power density information. Args: latitude: Site latitude longitude: Site longitude baseline_density: Baseline power density Returns: dict: Detailed power density information """ # Create fresh instance to ensure no caching issues try: calculator = EfficencyAwarePowerDensity() return calculator.get_site_power_density_info(latitude, longitude, baseline_density) except Exception as e: print(f"Error getting power density info: {str(e)}") return { 'efficiency_percent': 0, 'baseline_density_kwp_m2': baseline_density, 'adjusted_density_kwp_m2': baseline_density, 'density_adjustment_factor': 1.0, 'location': f"{latitude:.4f}°, {longitude:.4f}°" }