Source code for models.pv

"""PV system model using pvlib (POA irradiance, Sandia cell temperature, PVWatts DC)."""

from dataclasses import dataclass
import pvlib

REFERENCE_IRRADIANCE = 1000.0
REFERENCE_TEMP = 25.0
TEMP_COEFF_PDC = -0.004

SAPM_TEMP_PARAMS = pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS["sapm"][
    "open_rack_glass_polymer"
]


[docs] @dataclass class PV: """ PV system with fixed tilt/azimuth; production from GHI/DNI/DHI and cell temperature. Attributes: name: System name. capacity: DC capacity at STC (kW). tilt: Panel tilt (degrees); default 30. azimuth: Panel azimuth (degrees); default 180. latitude: Site latitude. longitude: Site longitude. current_production: Last computed production (kW). """ name: str capacity: float tilt: float = 30.0 azimuth: float = 180.0 latitude: float = 0.0 longitude: float = 0.0 current_production: float = 0.0 def __post_init__(self): """Validate capacity, tilt, azimuth, and coordinates.""" if self.capacity <= 0: raise ValueError("Capacity must be positive") if not 0 <= self.tilt <= 90: raise ValueError("Tilt must be 0–90°") if not 0 <= self.azimuth < 360: raise ValueError("Azimuth must be 0–360°") if not -90 <= self.latitude <= 90: raise ValueError("Latitude must be -90–90") if not -180 <= self.longitude <= 180: raise ValueError("Longitude must be -180–180")
[docs] def calculate_production( self, ghi: float, dni: float, dhi: float, temperature: float, timestamp, wind_speed: float = 1.0, ) -> float: """ Compute DC power (kW) at timestamp using POA irradiance, Sandia cell temperature, PVWatts DC. Args: ghi: Global horizontal irradiance (W/m²). dni: Direct normal irradiance (W/m²). dhi: Diffuse horizontal irradiance (W/m²). temperature: Air temperature (°C). timestamp: Time for solar position. wind_speed: Wind speed (m/s) for cell temperature; default 1.0. Returns: DC power in kW; 0 if sun below horizon. Also sets current_production. """ solpos = pvlib.solarposition.get_solarposition( timestamp, self.latitude, self.longitude ) if solpos["apparent_elevation"].iloc[0] <= 0: self.current_production = 0.0 return 0.0 poa = pvlib.irradiance.get_total_irradiance( surface_tilt=self.tilt, surface_azimuth=self.azimuth, solar_zenith=solpos["zenith"].iloc[0], solar_azimuth=solpos["azimuth"].iloc[0], dni=dni, ghi=ghi, dhi=dhi, albedo=0.2, )["poa_global"] cell_temperature = pvlib.temperature.sapm_cell( poa_global=poa, temp_air=temperature, wind_speed=wind_speed, **SAPM_TEMP_PARAMS, ) pdc = pvlib.pvsystem.pvwatts_dc( effective_irradiance=poa, temp_cell=cell_temperature, pdc0=self.capacity * REFERENCE_IRRADIANCE, gamma_pdc=TEMP_COEFF_PDC, temp_ref=REFERENCE_TEMP, ) self.current_production = max(0.0, float(pdc) / 1000.0) return self.current_production
def __repr__(self) -> str: return ( f"PV(name='{self.name}', capacity={self.capacity}kW, " f"tilt={self.tilt}°, azimuth={self.azimuth}°, " f"production={self.current_production:.2f}kW)" )