"""S1-T07: Wind simulation unit tests.""" import numpy as np from remodel_engine.generation.wind import annual_plf, simulate_wind from remodel_engine.schemas.generation import WindConfig SMALL_WIND = WindConfig( location_id="RJ", capacity_mw=100.0, ) def test_output_shape() -> None: df = simulate_wind(SMALL_WIND) assert df.shape == (25 * 8760, 6) assert set(df.columns) == { "year", "hour", "wind_speed_hub", "power_fraction", "ac_power_mw", "degradation_factor" } def test_plf_in_reasonable_range() -> None: """Rajasthan wind PLF should be 25 %-45 %.""" df = simulate_wind(SMALL_WIND) plf = annual_plf(df, SMALL_WIND.capacity_mw) assert all(0.25 <= v <= 0.45 for v in plf), f"PLF out of range: {plf.values}" def test_no_negative_power() -> None: df = simulate_wind(SMALL_WIND) assert (df["ac_power_mw"] >= 0).all() def test_power_never_exceeds_capacity() -> None: df = simulate_wind(SMALL_WIND) assert (df["ac_power_mw"] <= SMALL_WIND.capacity_mw + 1e-9).all() def test_power_fraction_in_01() -> None: df = simulate_wind(SMALL_WIND) assert (df["power_fraction"] >= 0).all() assert (df["power_fraction"] <= 1.0 + 1e-9).all() def test_degradation_monotonically_decreasing() -> None: df = simulate_wind(SMALL_WIND) yearly_deg = df.groupby("year")["degradation_factor"].first().values diffs = np.diff(yearly_deg) assert (diffs < 0).all(), "Wind degradation factors must decrease each year" def test_year1_degradation_is_one() -> None: """Wind degradation starts at 1.0 in year 1 (no LID, unlike solar).""" df = simulate_wind(SMALL_WIND) y1_deg = df[df["year"] == 1]["degradation_factor"].iloc[0] assert abs(y1_deg - 1.0) < 1e-9 def test_zero_wind_speed_gives_zero_power() -> None: """Hours with zero wind (below cut-in) must produce zero power.""" df = simulate_wind(SMALL_WIND) zero_wind = df[df["wind_speed_hub"] < 3.0] if len(zero_wind) > 0: assert (zero_wind["power_fraction"] < 1e-9).all() def test_gale_wind_cutout() -> None: """Wind speeds above cut-out (25 m/s) should produce zero power.""" df = simulate_wind(SMALL_WIND) gale = df[df["wind_speed_hub"] > 25.0] if len(gale) > 0: assert (gale["ac_power_mw"] < 1e-9).all() def test_shear_increases_wind_speed() -> None: """Hub height > ref height → hub wind speed > ref wind speed.""" cfg = WindConfig( location_id="RJ", capacity_mw=100.0, hub_height_m=140.0, ref_height_m=100.0, wind_shear_exponent=0.14, ) df = simulate_wind(cfg) # hub speeds should be larger than reference speeds on average from remodel_engine.catalog.loader import load_wind_profile ref_speeds = load_wind_profile("RJ") hub_speeds = df[df["year"] == 1]["wind_speed_hub"].values assert hub_speeds.mean() > ref_speeds.mean() def test_ka_plf_reasonable() -> None: cfg = WindConfig(location_id="KA", capacity_mw=100.0) df = simulate_wind(cfg) plf = annual_plf(df, cfg.capacity_mw) assert all(0.30 <= v <= 0.55 for v in plf)