Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions solarfarmer/models/pvsystem/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ class PVSystem:
`dc_ohmic_loss` and `ac_ohmic_loss`.
transformer_stages: int
Number of transformer stages. 0 for ideal behaviour or 1 for one stage (default is 1).
plant_unavailability : float
Plant availability loss (per unit) (default 0.0, i.e., no unavailability loss).
grid_unavailability : float
Grid availability loss (per unit) (default 0.0, i.e., no unavailability loss).

Auxiliary Files
---------------
Expand Down Expand Up @@ -252,6 +256,8 @@ class PVSystem:
bifacial: bool = False
inverter_type: InverterType | None = InverterType.CENTRAL
transformer_stages: int = 1
plant_unavailability: float = 0.0
grid_unavailability: float = 0.0

# Effects and settings
mounting_height: float | None = None # Changed from: mounting_height: float
Expand Down Expand Up @@ -804,6 +810,8 @@ def describe(self, verbose=False) -> None:
print(
f"Transformer Stages: {self.transformer_stages} (0=Ideal/NoLoss, 1=MV/HV transformer)"
)
print(f"Plant Unavailability: {self.plant_unavailability} (per unit)")
print(f"Grid Unavailability: {self.grid_unavailability} (per unit)")

if verbose:
# Losses
Expand Down Expand Up @@ -1117,6 +1125,8 @@ def construct_plant(pvplant: PVSystem) -> str:
)
calculation_options.apply_spectral_mismatch_modifier = pvplant.enable_spectral_modeling
calculation_options.calculate_dhi = pvplant.calculate_dhi_from_ghi
calculation_options.system_availability_loss = pvplant.plant_unavailability
calculation_options.grid_availability_loss = pvplant.grid_unavailability

# Build the full inputs model
inputs = EnergyCalculationInputs(
Expand Down
37 changes: 37 additions & 0 deletions tests/test_pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,43 @@ def test_aux_loss_present_in_payload(self, bern_2d_racks_inputs):
assert payload["pvPlant"]["auxiliaryLosses"]["simpleLossFactor"] == 0.01


class TestPVSystemUnavailability:
"""Tests for plant_unavailability and grid_unavailability properties."""

def test_unavailability_defaults_are_zero(self):
"""plant_unavailability and grid_unavailability must default to 0.0."""
plant = PVSystem()

assert plant.plant_unavailability == 0.0
assert plant.grid_unavailability == 0.0

def test_unavailability_custom_values_are_stored(self):
"""Custom unavailability values must be stored on the instance."""
plant = PVSystem(plant_unavailability=0.03, grid_unavailability=0.01)

assert plant.plant_unavailability == 0.03
assert plant.grid_unavailability == 0.01

def test_unavailability_propagates_to_payload(self, bern_2d_racks_inputs):
"""Non-zero unavailability values must appear in energyCalculationOptions in the payload."""
plant = PVSystem(
latitude=46.9,
longitude=7.4,
plant_unavailability=0.03,
grid_unavailability=0.01,
)
plant.pan_files = {
"CanadianSolar_CS6U-330M_APP": f"{bern_2d_racks_inputs}/CanadianSolar_CS6U-330M_APP.PAN"
}
plant.ond_files = {"Sungrow_SG125HV_APP": f"{bern_2d_racks_inputs}/Sungrow_SG125HV_APP.OND"}

payload = json.loads(construct_plant(plant))
opts = payload["energyCalculationOptions"]

assert opts["systemAvailabilityLoss"] == 0.03
assert opts["gridAvailabilityLoss"] == 0.01


class TestPVSystemBifacial:
"""Tests for bifacial parameter initialization (Phase 1 bug fix)."""

Expand Down
Loading