fix: Fix syntax errors, wire config-driven simulation in run.py, and correct fault-rate/availability KPI bugs
This commit is contained in:
139
pvsim/faults.py
139
pvsim/faults.py
@@ -317,6 +317,76 @@ class FaultConfig:
|
||||
|
||||
random_seed: Optional[int] = None
|
||||
|
||||
@classmethod
|
||||
def from_config(
|
||||
cls,
|
||||
config: Dict[str, object]
|
||||
) -> FaultConfig:
|
||||
"""
|
||||
Crea un'istanza di FaultConfig
|
||||
a partire da un dizionario di configurazione.
|
||||
"""
|
||||
|
||||
fault_config = config["faults"]
|
||||
|
||||
return cls(
|
||||
|
||||
enabled=
|
||||
fault_config["enabled"],
|
||||
|
||||
panel_fault_probability=
|
||||
fault_config["panel"]["failure_probability_daily"],
|
||||
|
||||
combiner_fault_probability=
|
||||
fault_config["combiner"]["failure_probability_daily"],
|
||||
|
||||
inverter_fault_probability=
|
||||
fault_config["inverter"]["failure_probability_daily"],
|
||||
|
||||
intermittent_probability=
|
||||
fault_config.get(
|
||||
"intermittent_probability",
|
||||
0.30
|
||||
),
|
||||
|
||||
default_fault_duration_hours=
|
||||
fault_config.get(
|
||||
"default_fault_duration_hours",
|
||||
4.0
|
||||
),
|
||||
|
||||
random_seed=
|
||||
fault_config.get(
|
||||
"random_seed",
|
||||
None
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_json(
|
||||
cls,
|
||||
json_path: str
|
||||
) -> FaultConfig:
|
||||
"""
|
||||
Crea un'istanza di FaultConfig
|
||||
a partire da un file JSON.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
with open(
|
||||
json_path,
|
||||
"r"
|
||||
) as f:
|
||||
|
||||
config = json.load(
|
||||
f
|
||||
)
|
||||
|
||||
return cls.from_config(
|
||||
config
|
||||
)
|
||||
|
||||
|
||||
# ======================================================================
|
||||
# FAULT MANAGER
|
||||
@@ -671,11 +741,40 @@ class FaultManager:
|
||||
# Generazione fault casuale
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@staticmethod
|
||||
def _daily_to_timestep_probability(
|
||||
daily_probability: float,
|
||||
timestep_minutes: float
|
||||
) -> float:
|
||||
"""
|
||||
Converte una probabilità di fault giornaliera
|
||||
nella probabilità equivalente per un singolo timestep.
|
||||
|
||||
Le probabilità configurate (*_probability_daily) sono
|
||||
riferite a un'intera giornata, ma generate_random_fault
|
||||
viene chiamato una volta per timestep: senza questa
|
||||
conversione la probabilità giornaliera verrebbe applicata
|
||||
una volta per ogni timestep, inflazionando il tasso di
|
||||
fault di un fattore pari al numero di timestep al giorno
|
||||
(ad es. 288x per timestep da 5 minuti).
|
||||
"""
|
||||
|
||||
timesteps_per_day = (
|
||||
24 * 60
|
||||
) / timestep_minutes
|
||||
|
||||
return 1 - (
|
||||
1 - daily_probability
|
||||
) ** (
|
||||
1 / timesteps_per_day
|
||||
)
|
||||
|
||||
def generate_random_fault(
|
||||
self,
|
||||
component_id: str,
|
||||
component_level: ComponentLevel,
|
||||
timestamp: datetime
|
||||
timestamp: datetime,
|
||||
timestep_minutes: float = 5.0
|
||||
) -> Optional[FaultEvent]:
|
||||
"""
|
||||
Genera casualmente un fault.
|
||||
@@ -684,7 +783,9 @@ class FaultManager:
|
||||
durante ogni timestep della simulazione.
|
||||
|
||||
La probabilità viene scelta in base
|
||||
al livello gerarchico.
|
||||
al livello gerarchico, e convertita da
|
||||
probabilità giornaliera a probabilità
|
||||
per il singolo timestep.
|
||||
"""
|
||||
|
||||
if not self.config.enabled:
|
||||
@@ -696,7 +797,7 @@ class FaultManager:
|
||||
== ComponentLevel.PANEL
|
||||
):
|
||||
|
||||
probability = (
|
||||
daily_probability = (
|
||||
|
||||
self.config
|
||||
.panel_fault_probability
|
||||
@@ -707,7 +808,7 @@ class FaultManager:
|
||||
== ComponentLevel.COMBINER
|
||||
):
|
||||
|
||||
probability = (
|
||||
daily_probability = (
|
||||
|
||||
self.config
|
||||
.combiner_fault_probability
|
||||
@@ -715,12 +816,19 @@ class FaultManager:
|
||||
|
||||
else:
|
||||
|
||||
probability = (
|
||||
daily_probability = (
|
||||
|
||||
self.config
|
||||
.inverter_fault_probability
|
||||
)
|
||||
|
||||
probability = (
|
||||
self._daily_to_timestep_probability(
|
||||
daily_probability,
|
||||
timestep_minutes
|
||||
)
|
||||
)
|
||||
|
||||
# Nessun fault
|
||||
if (
|
||||
random.random()
|
||||
@@ -905,7 +1013,8 @@ class FaultManager:
|
||||
timestamp: datetime,
|
||||
panel_ids: List[str],
|
||||
combiner_ids: List[str],
|
||||
inverter_ids: List[str]
|
||||
inverter_ids: List[str],
|
||||
timestep_minutes: float = 5.0
|
||||
) -> List[FaultEvent]:
|
||||
"""
|
||||
Esegue la generazione casuale dei fault
|
||||
@@ -931,7 +1040,10 @@ class FaultManager:
|
||||
ComponentLevel.PANEL,
|
||||
|
||||
timestamp=
|
||||
timestamp
|
||||
timestamp,
|
||||
|
||||
timestep_minutes=
|
||||
timestep_minutes
|
||||
)
|
||||
)
|
||||
|
||||
@@ -953,7 +1065,10 @@ class FaultManager:
|
||||
ComponentLevel.COMBINER,
|
||||
|
||||
timestamp=
|
||||
timestamp
|
||||
timestamp,
|
||||
|
||||
timestep_minutes=
|
||||
timestep_minutes
|
||||
)
|
||||
)
|
||||
|
||||
@@ -975,7 +1090,10 @@ class FaultManager:
|
||||
ComponentLevel.INVERTER,
|
||||
|
||||
timestamp=
|
||||
timestamp
|
||||
timestamp,
|
||||
|
||||
timestep_minutes=
|
||||
timestep_minutes
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1130,5 +1248,4 @@ class FaultManager:
|
||||
f"{len(self.faults)}, "
|
||||
f"active="
|
||||
f"{active})"
|
||||
)
|
||||
```
|
||||
)
|
||||
Reference in New Issue
Block a user