"""System B dual-ledger scenario engine. Reformulation (creative-thinking F2/F8): System B does not "win" by maximizing a single MW headline. It passes when: 1. Electric and thermal megawatts stay on separate ledgers 2. Accreditation scenarios (not a mixed 895 MW composite) are the firm claims 3. Reference and full scenarios cover the 2030 shortfall 4. $/kW and 20-year bands beat gas without overlapping cost ranges All numeric inputs come from ``regulator_paper_assumptions.REGISTRY``. """ from __future__ import annotations from dataclasses import dataclass from regulator_paper_assumptions import REGISTRY, v # Retired anti-pattern: BESS + hub_thermal_mwth + DSM + intertie RETIRED_COMPOSITE_MW = 895 @dataclass(frozen=True) class SystemBLedger: shortfall_mw: float bess_mw: float dsm_mw: float intertie_mw: float hub_thermal_mwth: float hub_net_electric_mw: float hub_effluent_stage_net_mw: float gas_nameplate_mw: float gas_derate_scenario_mw: float gas_capex_b: float hybrid_capex_gross_b: float hybrid_capex_net_b: float gas_20yr_low_b: float gas_20yr_high_b: float hybrid_20yr_low_b: float hybrid_20yr_high_b: float pdrc_summer_shave_mw: float btes_summer_chiller_shave_mw: float accreditation_mw: dict[str, float] thermal_ledger_mwth: dict[str, float] electric_ledger_mw: dict[str, float] @property def firm_headline_mw(self) -> float: """Primary public figure: reference accreditation (covers shortfall).""" return self.accreditation_mw["reference"] @property def summer_peak_shave_total_mw(self) -> float: """Summer residual benefit only — never added to winter firm MW.""" return self.pdrc_summer_shave_mw + self.btes_summer_chiller_shave_mw @property def savings_band_b(self) -> tuple[float, float]: return ( self.gas_20yr_low_b - self.hybrid_20yr_high_b, self.gas_20yr_high_b - self.hybrid_20yr_low_b, ) def covers_shortfall(self, scenario: str) -> bool: return self.accreditation_mw[scenario] >= self.shortfall_mw def margin_mw(self, scenario: str) -> float: return self.accreditation_mw[scenario] - self.shortfall_mw def cost_per_kw_hybrid_net(self, scenario: str) -> float: mw = self.accreditation_mw[scenario] return self.hybrid_capex_net_b * 1e9 / (mw * 1000) def cost_per_kw_hybrid_gross(self, scenario: str) -> float: mw = self.accreditation_mw[scenario] return self.hybrid_capex_gross_b * 1e9 / (mw * 1000) def cost_per_kw_gas_derate(self) -> float: return self.gas_capex_b * 1e9 / (self.gas_derate_scenario_mw * 1000) def cost_per_kw_gas_nameplate(self) -> float: return self.gas_capex_b * 1e9 / (self.gas_nameplate_mw * 1000) def peak_shave_mw_by_hour(self) -> list[float]: """24-hour electric peak-shave profile; hub enters as net electric MW only. Battery and demand response cover two 2-hour crests (hour-beginning 7, 8 and 17, 18 — 07:00–09:00 and 17:00–19:00). Four hours at ``bess_mw`` exhausts a 4-hour pack with recharge at zero. Inclusive 7–9 / 17–19 is six hours and overdraws the pack. """ profile = [float(self.hub_net_electric_mw)] * 24 peak_add = self.bess_mw + self.dsm_mw for hour in (7, 8, 17, 18): profile[hour] += peak_add return profile def pass_report(self) -> dict: gates = { "no_mixed_thermal_in_electric": ( self.accreditation_mw["full"] == self.bess_mw + self.dsm_mw + self.intertie_mw + self.hub_net_electric_mw ), "retired_895_not_headline": self.firm_headline_mw != RETIRED_COMPOSITE_MW, "reference_covers_shortfall": self.covers_shortfall("reference"), "full_covers_shortfall": self.covers_shortfall("full"), # Pass gate is the NAMEPLATE comparison — the derate (525 MW) is a # clearly-labelled sensitivity, never asserted as fact. "reference_beats_gas_per_kw": ( self.cost_per_kw_hybrid_gross("reference") < self.cost_per_kw_gas_nameplate() ), "lifecycle_bands_non_overlapping": ( self.hybrid_20yr_high_b < self.gas_20yr_low_b ), "hub_net_less_than_thermal": ( self.hub_net_electric_mw < self.hub_thermal_mwth ), "summer_addon_not_winter_firm": ( self.summer_peak_shave_total_mw not in self.accreditation_mw.values() and self.accreditation_mw["full"] == self.bess_mw + self.dsm_mw + self.intertie_mw + self.hub_net_electric_mw ), "btes_winter_not_winter_firm": ( float(v("btes_winter_eligible_mw")) == 0 and float(v("btes_winter_planning_mw")) not in self.accreditation_mw.values() and ( self.accreditation_mw["reference"] + float(v("btes_winter_planning_mw")) ) not in self.accreditation_mw.values() ), } c, r, f = ( int(self.accreditation_mw["conservative"]), int(self.accreditation_mw["reference"]), int(self.accreditation_mw["full"]), ) return { "passed": all(gates.values()), "gates": gates, "headline": ( f"{c}–{f} MW winter resource stack " f"(reference {r} MW planning case before independent ELCC; " f"{int(v('intertie_reference_mw'))} MW undemonstrated winter import; " f"arithmetic margin {int(self.margin_mw('reference'))} MW vs " f"{int(self.shortfall_mw)} MW shortfall)" ), "thermal_mwth": self.hub_thermal_mwth, "margin_reference_mw": self.margin_mw("reference"), "margin_full_mw": self.margin_mw("full"), "cost_per_kw_reference_net": round(self.cost_per_kw_hybrid_net("reference")), "cost_per_kw_gas_derate": round(self.cost_per_kw_gas_derate()), "savings_band_b": self.savings_band_b, "summer_peak_shave_total_mw": self.summer_peak_shave_total_mw, "pdrc_summer_shave_mw": self.pdrc_summer_shave_mw, "btes_summer_chiller_shave_mw": self.btes_summer_chiller_shave_mw, "registry_keys": sorted(REGISTRY.keys()), } def build_ledger() -> SystemBLedger: accreditation = { "conservative": float(v("scenario_conservative_mw")), "reference": float(v("scenario_reference_mw")), "full": float(v("scenario_full_mw")), } return SystemBLedger( shortfall_mw=float(v("shortfall_2030_mw")), bess_mw=float(v("bess_mw")), dsm_mw=float(v("dsm_mw")), intertie_mw=float(v("intertie_mw")), hub_thermal_mwth=float(v("hub_thermal_mwth")), hub_net_electric_mw=float(v("hub_net_electric_mw")), hub_effluent_stage_net_mw=float(v("hub_effluent_stage_net_mw")), gas_nameplate_mw=float(v("gas_nameplate_mw")), gas_derate_scenario_mw=float(v("gas_derate_scenario_mw")), gas_capex_b=float(v("gas_capex_b")), hybrid_capex_gross_b=float(v("hybrid_capex_gross_b")), hybrid_capex_net_b=float(v("hybrid_capex_net_b")), gas_20yr_low_b=float(v("gas_20yr_low_b")), gas_20yr_high_b=float(v("gas_20yr_high_b")), hybrid_20yr_low_b=float(v("hybrid_20yr_low_b")), hybrid_20yr_high_b=float(v("hybrid_20yr_high_b")), pdrc_summer_shave_mw=float(v("pdrc_summer_shave_mw")), btes_summer_chiller_shave_mw=float(v("btes_summer_chiller_shave_mw")), accreditation_mw=accreditation, thermal_ledger_mwth={ "hub_total": float(v("hub_thermal_mwth")), "effluent": float(v("hub_thermal_mwth")), "compute_reject": float(v("hub_contingent_thermal_mwth")), }, electric_ledger_mw={ "bess": float(v("bess_mw")), "dsm": float(v("dsm_mw")), "intertie": float(v("intertie_mw")), "hub_net": float(v("hub_net_electric_mw")), "full": float(v("scenario_full_mw")), "reference": float(v("scenario_reference_mw")), "conservative": float(v("scenario_conservative_mw")), }, ) if __name__ == "__main__": report = build_ledger().pass_report() status = "PASS" if report["passed"] else "FAIL" print(f"System B ledger: {status}") print(f" Headline: {report['headline']}") for name, ok in report["gates"].items(): print(f" [{'OK' if ok else 'NO'}] {name}") print( f" $/kW reference net ${report['cost_per_kw_reference_net']:,} " f"vs gas derate ${report['cost_per_kw_gas_derate']:,}" ) print( f" 20-yr savings band " f"${report['savings_band_b'][0]:.1f}–${report['savings_band_b'][1]:.1f}B" )