243 lines
5.0 KiB
Python
243 lines
5.0 KiB
Python
```python
|
|
"""
|
|
panel.py
|
|
|
|
Modello del singolo pannello fotovoltaico.
|
|
|
|
Ogni pannello riceve dal simulatore:
|
|
|
|
- Irradianza [W/m²]
|
|
- Temperatura ambiente [°C]
|
|
- Timestamp
|
|
|
|
e restituisce le principali grandezze elettriche.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Dict
|
|
import random
|
|
import math
|
|
|
|
|
|
@dataclass
|
|
class PVPanel:
|
|
|
|
panel_id: str
|
|
|
|
nominal_power: float = 450.0 # W
|
|
area: float = 2.1 # m²
|
|
|
|
vmp: float = 41.5
|
|
imp: float = 10.85
|
|
|
|
voc: float = 49.8
|
|
isc: float = 11.35
|
|
|
|
temp_coeff: float = -0.0035 # -0.35 %/°C
|
|
|
|
nominal_temp: float = 25.0
|
|
|
|
annual_degradation: float = 0.005
|
|
|
|
sensor_noise: float = 0.01
|
|
|
|
# stato pannello
|
|
enabled: bool = True
|
|
soiling_factor: float = 1.0
|
|
degradation: float = 1.0
|
|
|
|
total_energy_Wh: float = 0.0
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def module_temperature(
|
|
self,
|
|
ambient_temperature: float,
|
|
irradiance: float
|
|
) -> float:
|
|
"""
|
|
Stima della temperatura del modulo.
|
|
|
|
Formula semplificata:
|
|
Tmodule = Tamb + irradiance * 0.03
|
|
"""
|
|
|
|
return ambient_temperature + irradiance * 0.03
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def degradation_factor(
|
|
self,
|
|
years: float
|
|
) -> float:
|
|
|
|
return (1.0 - self.annual_degradation) ** years
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def compute_power(
|
|
self,
|
|
irradiance: float,
|
|
ambient_temperature: float,
|
|
years_from_start: float
|
|
) -> float:
|
|
|
|
if not self.enabled:
|
|
return 0.0
|
|
|
|
module_temp = self.module_temperature(
|
|
ambient_temperature,
|
|
irradiance
|
|
)
|
|
|
|
temp_factor = (
|
|
1 +
|
|
self.temp_coeff *
|
|
(module_temp - self.nominal_temp)
|
|
)
|
|
|
|
degr = self.degradation_factor(years_from_start)
|
|
|
|
power = (
|
|
self.nominal_power *
|
|
(irradiance / 1000.0) *
|
|
temp_factor *
|
|
degr *
|
|
self.soiling_factor
|
|
)
|
|
|
|
power = max(power, 0.0)
|
|
|
|
noise = random.gauss(1.0, self.sensor_noise)
|
|
|
|
return power * noise
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def compute_voltage(
|
|
self,
|
|
power: float
|
|
) -> float:
|
|
|
|
if power <= 0:
|
|
return 0.0
|
|
|
|
voltage = self.vmp * (
|
|
0.98 + 0.04 * random.random()
|
|
)
|
|
|
|
return voltage
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def compute_current(
|
|
self,
|
|
power: float,
|
|
voltage: float
|
|
) -> float:
|
|
|
|
if voltage <= 0:
|
|
return 0.0
|
|
|
|
return power / voltage
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def update(
|
|
self,
|
|
timestamp: datetime,
|
|
irradiance: float,
|
|
ambient_temperature: float,
|
|
years_from_start: float,
|
|
timestep_minutes: float
|
|
) -> Dict:
|
|
|
|
power = self.compute_power(
|
|
irradiance,
|
|
ambient_temperature,
|
|
years_from_start
|
|
)
|
|
|
|
voltage = self.compute_voltage(power)
|
|
|
|
current = self.compute_current(
|
|
power,
|
|
voltage
|
|
)
|
|
|
|
module_temp = self.module_temperature(
|
|
ambient_temperature,
|
|
irradiance
|
|
)
|
|
|
|
energy = power * timestep_minutes / 60.0
|
|
|
|
self.total_energy_Wh += energy
|
|
|
|
return {
|
|
|
|
"timestamp": timestamp,
|
|
|
|
"panel_id": self.panel_id,
|
|
|
|
"irradiance_Wm2": irradiance,
|
|
|
|
"ambient_temperature_C": ambient_temperature,
|
|
|
|
"module_temperature_C": module_temp,
|
|
|
|
"voltage_V": voltage,
|
|
|
|
"current_A": current,
|
|
|
|
"power_W": power,
|
|
|
|
"energy_Wh": energy,
|
|
|
|
"total_energy_Wh": self.total_energy_Wh,
|
|
|
|
"enabled": self.enabled,
|
|
|
|
"soiling_factor": self.soiling_factor,
|
|
|
|
"degradation_factor":
|
|
self.degradation_factor(years_from_start)
|
|
|
|
}
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def fail(self):
|
|
|
|
self.enabled = False
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def repair(self):
|
|
|
|
self.enabled = True
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def soil(self, loss=0.9):
|
|
|
|
self.soiling_factor = loss
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def clean(self):
|
|
|
|
self.soiling_factor = 1.0
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def __repr__(self):
|
|
|
|
return (
|
|
f"PVPanel("
|
|
f"{self.panel_id}, "
|
|
f"{self.nominal_power}W)"
|
|
) |