mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-10-20 21:01:57 +00:00
fix(devices): constrain the physics port and validate export levels
Defer inactive EV deadline fields to the optimizer port, reject nonfinite export rates, and document the hourly Optimize boundary. Verify converter IDs, rates and LCOS, separate GENETIC0 interpolation, physical boundary flows and independent GENETIC repricing.
This commit is contained in:
+18
-1
@@ -371,7 +371,7 @@ def test_grid_export_rates_default_and_override():
|
||||
) == [1.0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("rates", [[0.0, 0.5], [1.5], [-0.25], []])
|
||||
@pytest.mark.parametrize("rates", [[0.0, 0.5], [1.5], [-0.25], [], [np.nan], [np.inf], [[0.5]], 0.5])
|
||||
def test_grid_export_rates_reject_invalid_values(rates):
|
||||
"""0.0 is not an export level, and rates above the rated power are rejected."""
|
||||
with pytest.raises(ValidationError):
|
||||
@@ -383,3 +383,20 @@ def test_rated_discharge_energy_scales_with_slot_duration(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
expected = battery.max_charge_power_w * battery.slot_duration_h * battery.discharging_efficiency
|
||||
assert battery.rated_discharge_energy_wh() == pytest.approx(expected)
|
||||
|
||||
|
||||
def test_pv_converter_preserves_id_lcos_charge_and_export_rates():
|
||||
settings = BatteriesCommonSettings(
|
||||
device_id="house",
|
||||
charge_rates=[0.0, 0.5, 1.0],
|
||||
grid_export_rates=[1.0, 0.25],
|
||||
levelized_cost_of_storage_amt_kwh=0.123,
|
||||
)
|
||||
assert isinstance(BatteriesCommonSettings.validate_and_sort_charge_rates(None), np.ndarray)
|
||||
parameters = settings.to_genetic_pv_bat_param()
|
||||
assert parameters.device_id == "house"
|
||||
assert parameters.charge_rates == [0.0, 0.5, 1.0]
|
||||
assert parameters.grid_export_rates == [0.25, 1.0]
|
||||
assert parameters.levelized_cost_of_storage_kwh == pytest.approx(0.123)
|
||||
battery = Battery(parameters, prediction_hours=4, slot_duration_h=0.25)
|
||||
assert battery.levelized_cost_of_storage_kwh == pytest.approx(0.123)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.prediction.interpolator import get_eos_load_interpolator
|
||||
@@ -68,3 +69,37 @@ def test_expected_direct_consumption_preserves_forecast_mean_at_high_pv():
|
||||
direct_power_w = interpolator.calculate_expected_direct_consumption(3000.0, 10000.0)
|
||||
|
||||
assert direct_power_w == pytest.approx(3000.0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("load,pv", [(4000.0, 5000.0), (10000.0, 20000.0), (0.0, 0.0)])
|
||||
def test_genetic_interpolator_boundaries_are_finite_and_physical(load, pv):
|
||||
interpolator = get_eos_load_interpolator()
|
||||
fraction = interpolator.calculate_self_consumption(load, pv)
|
||||
direct = interpolator.calculate_expected_direct_consumption(load, pv)
|
||||
assert np.isfinite(fraction)
|
||||
assert 0.0 <= fraction <= 1.0
|
||||
assert np.isfinite(direct)
|
||||
assert 0.0 <= direct <= min(load, pv)
|
||||
|
||||
|
||||
def test_genetic0_inverter_keeps_its_independent_interpolator():
|
||||
from akkudoktoreos.devices.genetic0.genetic0inverter import (
|
||||
Genetic0Inverter,
|
||||
Genetic0InverterParameters,
|
||||
)
|
||||
from akkudoktoreos.optimization.genetic0.genetic0loadinterpolator import (
|
||||
get_genetic0_load_interpolator,
|
||||
)
|
||||
|
||||
inverter = Genetic0Inverter(Genetic0InverterParameters(device_id="legacy", max_power_wh=10000))
|
||||
assert inverter.self_consumption_predictor is get_genetic0_load_interpolator()
|
||||
assert inverter.self_consumption_predictor is not get_eos_load_interpolator()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("load,pv", [(4000.0, 5000.0), (10000.0, 20000.0)])
|
||||
def test_genetic_inverter_boundary_flows_remain_nonnegative(load, pv):
|
||||
from akkudoktoreos.devices.genetic.inverter import Inverter, InverterParameters
|
||||
|
||||
inverter = Inverter(InverterParameters(device_id="boundary", max_power_wh=25000))
|
||||
flows = inverter.process_energy(generation=pv, consumption=load, hour=0)
|
||||
assert all(np.isfinite(value) and value >= 0.0 for value in flows)
|
||||
|
||||
Reference in New Issue
Block a user