Remodel/packages/engine/tests/unit/test_bess_state.py
Mannu 314127effc [S1-T01 through T11] Solar, Wind, BESS generation simulation + CLI
- Pydantic schemas: SolarConfig, WindConfig, BessConfig, GenerationResult
- Catalog: synthetic 8760h profiles for RJ/KA/GJ solar and wind; LRU-cached loader
- generation/solar.py: 25yr hourly simulation (DC losses, inverter, clipping, soiling, degradation)
- generation/wind.py: power-law shear, piecewise power curve, wake/electrical losses, degradation
- generation/bess_state.py: SOH-based capacity degradation with augmentation schedule
- CLI: remodel --input scenario.json --output gen.parquet (Typer upgraded to 0.25.1 for Click 8.3 compat)
- 43 unit tests, 97.4% coverage; mypy strict + ruff clean
- S1-T10 parity gate: placeholder fixture + skipped integration tests (awaiting Nagasamudra data)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 10:04:21 +05:30

62 lines
1.9 KiB
Python

"""Unit tests for BESS capacity degradation model."""
import pytest
from remodel_engine.generation.bess_state import simulate_bess_capacity
from remodel_engine.schemas.generation import BessConfig
BASE_CFG = BessConfig(
capacity_mwh=500.0,
power_mw=125.0,
rte=0.85,
design_cycles=6000.0,
eol_soh=0.70,
)
def test_output_length() -> None:
cycles = [365.0 * (i + 1) for i in range(25)]
result = simulate_bess_capacity(BASE_CFG, cycles)
assert len(result) == 25
def test_index_is_1_to_25() -> None:
cycles = [365.0 * (i + 1) for i in range(25)]
result = simulate_bess_capacity(BASE_CFG, cycles)
assert list(result.index) == list(range(1, 26))
def test_capacity_decreases_with_cycles() -> None:
cycles = [365.0 * (i + 1) for i in range(25)]
result = simulate_bess_capacity(BASE_CFG, cycles)
assert all(result.iloc[i] >= result.iloc[i + 1] for i in range(24))
def test_zero_cycles_gives_full_capacity() -> None:
"""Zero cumulative cycles → SOH = 1.0 → usable = nameplate."""
result = simulate_bess_capacity(BASE_CFG, [0.0] * 25)
assert all(abs(v - BASE_CFG.capacity_mwh) < 1e-9 for v in result)
def test_soh_floor_at_eol() -> None:
"""Very high cycle count should floor at eol_soh * nameplate."""
extreme_cycles = [100_000.0] * 25
result = simulate_bess_capacity(BASE_CFG, extreme_cycles)
floor = BASE_CFG.capacity_mwh * BASE_CFG.eol_soh
assert all(abs(v - floor) < 1e-9 for v in result)
def test_augmentation_increases_capacity() -> None:
cfg = BessConfig(
capacity_mwh=500.0,
power_mw=125.0,
augmentation_schedule=[(10, 200.0)],
)
cycles = [0.0] * 25
result = simulate_bess_capacity(cfg, cycles)
assert result.iloc[9] > result.iloc[8] # year 10 > year 9
def test_wrong_cycles_length_raises() -> None:
with pytest.raises(ValueError, match="25 entries"):
simulate_bess_capacity(BASE_CFG, [365.0] * 10)