From 75dac31bced06bcde9e8c00c19faea59fff80df1 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 23 Mar 2026 16:21:46 +0000 Subject: [PATCH 01/19] wip Made-with: Cursor --- tests/test_validate_demand_flex.py | 222 +++++++++ utils/post/sensitivity_demand_flex.py | 269 +++++++++++ utils/post/validate_demand_flex.py | 661 ++++++++++++++++++++++++++ 3 files changed, 1152 insertions(+) create mode 100644 tests/test_validate_demand_flex.py create mode 100644 utils/post/sensitivity_demand_flex.py create mode 100644 utils/post/validate_demand_flex.py diff --git a/tests/test_validate_demand_flex.py b/tests/test_validate_demand_flex.py new file mode 100644 index 00000000..8839e427 --- /dev/null +++ b/tests/test_validate_demand_flex.py @@ -0,0 +1,222 @@ +"""Tests for analytical demand-flex validation and sensitivity analysis.""" + +from __future__ import annotations + +import pytest + +from utils.post.validate_demand_flex import ( + SyntheticScenario, + build_synthetic_scenario, + check_achieved_elasticity_near_target, + check_energy_conservation, + check_frozen_residual_identity, + check_mc_delta_monotonic_in_elasticity, + check_mc_delta_negative, + check_nonhp_subclass_rr_unchanged, + check_rr_decreases, + check_shifted_loads_nonnegative, + check_tou_ratio_does_not_increase, + check_tou_subclass_rr_absorbs_delta, + run_all_checks, +) +from utils.post.sensitivity_demand_flex import ( + compute_sweep_point, + results_to_dataframe, + run_sweep, +) + + +@pytest.fixture(scope="module") +def scenario() -> SyntheticScenario: + """Shared synthetic scenario (expensive to build, reuse across tests).""" + return build_synthetic_scenario() + + +# --------------------------------------------------------------------------- +# Individual check tests +# --------------------------------------------------------------------------- + + +class TestEnergyConservation: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_energy_conservation(scenario, elasticity=-0.1) + assert result.passed + assert result.details["rel_diff"] < 1e-8 + + def test_passes_at_various_elasticities(self, scenario: SyntheticScenario) -> None: + for e in [-0.05, -0.10, -0.15, -0.20]: + result = check_energy_conservation(scenario, elasticity=e) + assert result.passed, f"Failed at elasticity={e}" + + +class TestMcDeltaNegative: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_mc_delta_negative(scenario, elasticity=-0.1) + assert result.passed + assert result.details["mc_delta"] < 0 + + def test_larger_elasticity_gives_larger_delta( + self, scenario: SyntheticScenario + ) -> None: + r1 = check_mc_delta_negative(scenario, elasticity=-0.05) + r2 = check_mc_delta_negative(scenario, elasticity=-0.15) + assert abs(r2.details["mc_delta"]) > abs(r1.details["mc_delta"]) + + +class TestFrozenResidualIdentity: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_frozen_residual_identity(scenario, elasticity=-0.1) + assert result.passed + assert result.details["diff"] < 1.0 + + +class TestRrDecreases: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_rr_decreases(scenario, elasticity=-0.1) + assert result.passed + + +class TestTouRatioDoesNotIncrease: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_tou_ratio_does_not_increase(scenario, elasticity=-0.1) + assert result.passed + + def test_ratio_within_tolerance(self, scenario: SyntheticScenario) -> None: + result = check_tou_ratio_does_not_increase(scenario, elasticity=-0.1) + assert result.details["delta"] < 1e-3 + + +class TestNonhpSubclassRrUnchanged: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_nonhp_subclass_rr_unchanged(scenario, elasticity=-0.1) + assert result.passed + + +class TestTouSubclassRrAbsorbsDelta: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_tou_subclass_rr_absorbs_delta(scenario, elasticity=-0.1) + assert result.passed + + +class TestAchievedElasticityNearTarget: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_achieved_elasticity_near_target(scenario, elasticity=-0.1) + assert result.passed or result.status == "WARN" + + +class TestShiftedLoadsNonnegative: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_shifted_loads_nonnegative(scenario, elasticity=-0.1) + assert result.passed + + def test_fails_with_extreme_elasticity(self) -> None: + """Extreme elasticity can push loads negative on thin load buildings.""" + extreme_scenario = build_synthetic_scenario(n_tou_bldgs=3, seed=99) + result = check_shifted_loads_nonnegative(extreme_scenario, elasticity=-5.0) + # With such extreme elasticity on synthetic data, loads may go negative. + # Either outcome (PASS or FAIL) validates the check's logic is exercised. + assert result.name == "shifted_loads_nonnegative" + if result.status == "FAIL": + assert result.details["n_negative"] > 0 + + +class TestMcDeltaMonotonicInElasticity: + def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: + result = check_mc_delta_monotonic_in_elasticity(scenario) + assert result.passed + assert result.details["is_monotonic"] + + def test_custom_elasticities(self, scenario: SyntheticScenario) -> None: + result = check_mc_delta_monotonic_in_elasticity( + scenario, elasticities=[-0.02, -0.08, -0.12] + ) + assert result.passed + + +# --------------------------------------------------------------------------- +# Run-all checks +# --------------------------------------------------------------------------- + + +class TestRunAllChecks: + def test_all_pass_on_synthetic(self, scenario: SyntheticScenario) -> None: + results = run_all_checks(scenario, elasticity=-0.1) + assert len(results) == 10 + for r in results: + assert r.status in ("PASS", "WARN"), f"{r.name} -> {r.status}: {r.message}" + + def test_all_checks_have_unique_names(self, scenario: SyntheticScenario) -> None: + results = run_all_checks(scenario, elasticity=-0.1) + names = [r.name for r in results] + assert len(names) == len(set(names)) + + +# --------------------------------------------------------------------------- +# Sensitivity sweep tests +# --------------------------------------------------------------------------- + + +class TestSensitivitySweep: + def test_sweep_returns_correct_count(self, scenario: SyntheticScenario) -> None: + elasticities = [-0.05, -0.10, -0.15, -0.20] + results = run_sweep(scenario, elasticities) + assert len(results) == len(elasticities) + + def test_sweep_mc_deltas_are_negative(self, scenario: SyntheticScenario) -> None: + results = run_sweep(scenario, [-0.05, -0.10, -0.15]) + for r in results: + assert r.mc_delta_dollars < 0, f"MC delta should be negative at e={r.elasticity}" + + def test_sweep_mc_deltas_are_monotonic(self, scenario: SyntheticScenario) -> None: + results = run_sweep(scenario, [-0.05, -0.10, -0.15, -0.20]) + magnitudes = [abs(r.mc_delta_dollars) for r in results] + for i in range(len(magnitudes) - 1): + assert magnitudes[i] <= magnitudes[i + 1] + + def test_sweep_rr_decreases_monotonically( + self, scenario: SyntheticScenario + ) -> None: + results = run_sweep(scenario, [-0.05, -0.10, -0.15, -0.20]) + for r in results: + assert r.rr_change_dollars < 0 + + def test_sweep_tou_ratio_does_not_increase( + self, scenario: SyntheticScenario + ) -> None: + results = run_sweep(scenario, [-0.05, -0.10, -0.15, -0.20]) + for r in results: + assert r.tou_ratio_change < 1e-3, ( + f"TOU ratio increased significantly at e={r.elasticity}" + ) + + def test_results_to_dataframe(self, scenario: SyntheticScenario) -> None: + results = run_sweep(scenario, [-0.05, -0.10]) + df = results_to_dataframe(results) + assert len(df) == 2 + assert "elasticity" in df.columns + assert "mc_delta_dollars" in df.columns + + def test_single_point(self, scenario: SyntheticScenario) -> None: + result = compute_sweep_point(scenario, -0.10) + assert result.load_shift_kwh > 0 + assert result.min_shifted_load >= 0 + + +# --------------------------------------------------------------------------- +# Edge cases +# --------------------------------------------------------------------------- + + +class TestEdgeCases: + def test_single_building(self) -> None: + s = build_synthetic_scenario(n_tou_bldgs=1, n_nontou_bldgs=1) + results = run_all_checks(s, elasticity=-0.1) + for r in results: + assert r.status in ("PASS", "WARN"), f"{r.name} -> {r.status}: {r.message}" + + def test_zero_elasticity_energy_conservation(self) -> None: + """Zero elasticity should result in no shift and perfect conservation.""" + s = build_synthetic_scenario() + result = check_energy_conservation(s, elasticity=0.0) + # Zero elasticity means Q_target = Q_orig, so no shift + assert result.passed diff --git a/utils/post/sensitivity_demand_flex.py b/utils/post/sensitivity_demand_flex.py new file mode 100644 index 00000000..978a2d4a --- /dev/null +++ b/utils/post/sensitivity_demand_flex.py @@ -0,0 +1,269 @@ +"""Elasticity sensitivity analysis for the demand-flex pipeline. + +Sweeps over a range of elasticity values and computes key demand-flex +metrics at each point: load shift magnitude, MC delta, RR change, +TOU ratio change, per-customer bill impact, and non-negativity margin. + +Can run on synthetic data (for CI) or real utility load/MC data. + +Usage:: + + uv run python -m utils.post.sensitivity_demand_flex \ + --elasticities -0.05,-0.10,-0.15,-0.20 [--synthetic] [--output-dir /tmp] +""" + +from __future__ import annotations + +import argparse +import csv +import sys +from dataclasses import dataclass +from io import StringIO +from pathlib import Path + +import numpy as np +import pandas as pd + +from utils.cairo import ( + assign_hourly_periods, + process_residential_hourly_demand_response_shift, +) +from utils.post.validate_demand_flex import ( + SyntheticScenario, + _apply_shift_to_full_load, + _compute_system_mc, + _extract_tou_hourly, + _shift_loads, + build_synthetic_scenario, +) +from utils.pre.compute_tou import ( + combine_marginal_costs, + compute_tou_cost_causation_ratio, +) + + +@dataclass(slots=True) +class SweepResult: + """Metrics computed at a single elasticity point.""" + + elasticity: float + load_shift_kwh: float + load_shift_pct: float + mc_delta_dollars: float + mc_delta_pct: float + rr_change_dollars: float + rr_change_pct: float + tou_ratio_orig: float + tou_ratio_shifted: float + tou_ratio_change: float + per_customer_bill_change: float + min_shifted_load: float + + +def compute_sweep_point( + scenario: SyntheticScenario, + elasticity: float, +) -> SweepResult: + """Compute all metrics for one elasticity value.""" + # Shift loads + shifted, _ = _shift_loads(scenario, elasticity) + full_shifted = _apply_shift_to_full_load(scenario, shifted) + + # Load shift magnitude + period_map = assign_hourly_periods(scenario.hourly_index, scenario.tou_tariff) + tou_hourly = _extract_tou_hourly(scenario) + peak_periods = set() + rate_df = scenario.period_rate.reset_index() + max_rate = scenario.period_rate.max() + for _, row in rate_df.iterrows(): + if row["rate"] == max_rate: + peak_periods.add(int(row["energy_period"])) + + peak_mask = tou_hourly["energy_period"].isin(peak_periods) + orig_peak_kwh = float(tou_hourly.loc[peak_mask, "electricity_net"].sum()) + shifted_merged = shifted if "energy_period" in shifted.columns else tou_hourly.copy() + if "energy_period" not in shifted.columns: + period_df = period_map.reset_index() + period_df.columns = pd.Index(["time", "energy_period"]) + shifted_merged = shifted.merge(period_df, on="time", how="left") + + shifted_peak_mask = shifted_merged["energy_period"].isin(peak_periods) + shifted_peak_kwh = float(shifted_merged.loc[shifted_peak_mask, "shifted_net"].sum()) + load_shift_kwh = orig_peak_kwh - shifted_peak_kwh + load_shift_pct = (load_shift_kwh / orig_peak_kwh * 100) if orig_peak_kwh > 0 else 0.0 + + # MC delta + all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids + mc_orig = _compute_system_mc( + scenario.load_elec, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_shifted = _compute_system_mc( + full_shifted, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_delta = mc_shifted - mc_orig + mc_delta_pct = (mc_delta / mc_orig * 100) if mc_orig != 0 else 0.0 + + # RR change (frozen residual model) + frozen_residual = scenario.rr_total - mc_orig + new_rr = mc_shifted + frozen_residual + rr_change = new_rr - scenario.rr_total + rr_change_pct = (rr_change / scenario.rr_total * 100) if scenario.rr_total != 0 else 0.0 + + # TOU ratio change + combined_mc = combine_marginal_costs(scenario.bulk_marginal_costs, scenario.dist_mc) + mc_index = pd.DatetimeIndex(combined_mc.index) + peak_hours = scenario.season_specs[0].peak_hours + sample_weights = scenario.customer_metadata[["bldg_id", "weight"]] + bldg_level = scenario.load_elec.index.get_level_values("bldg_id") + tou_set = set(scenario.tou_bldg_ids) + + orig_tou = scenario.load_elec.loc[bldg_level.isin(tou_set)] + orig_w = orig_tou.reset_index().merge(sample_weights, on="bldg_id") + orig_w["electricity_net"] *= orig_w["weight"] + orig_sys = orig_w.groupby("time")["electricity_net"].sum() + orig_load = pd.Series(orig_sys.values[: len(mc_index)], index=mc_index) + + shifted_tou = full_shifted.loc[bldg_level.isin(tou_set)] + shifted_w = shifted_tou.reset_index().merge(sample_weights, on="bldg_id") + shifted_w["electricity_net"] *= shifted_w["weight"] + shifted_sys = shifted_w.groupby("time")["electricity_net"].sum() + shifted_load_s = pd.Series(shifted_sys.values[: len(mc_index)], index=mc_index) + + ratio_orig = compute_tou_cost_causation_ratio(combined_mc, orig_load, peak_hours) + ratio_shifted = compute_tou_cost_causation_ratio(combined_mc, shifted_load_s, peak_hours) + + # Per-customer bill impact (approximate: RR change / total customer weights) + total_tou_weight = float( + scenario.customer_metadata[ + scenario.customer_metadata["bldg_id"].isin(tou_set) + ]["weight"].sum() + ) + per_customer_bill_change = rr_change / total_tou_weight if total_tou_weight > 0 else 0.0 + + # Non-negativity margin + min_shifted_load = float(shifted["shifted_net"].min()) + + return SweepResult( + elasticity=elasticity, + load_shift_kwh=load_shift_kwh, + load_shift_pct=load_shift_pct, + mc_delta_dollars=mc_delta, + mc_delta_pct=mc_delta_pct, + rr_change_dollars=rr_change, + rr_change_pct=rr_change_pct, + tou_ratio_orig=ratio_orig, + tou_ratio_shifted=ratio_shifted, + tou_ratio_change=ratio_shifted - ratio_orig, + per_customer_bill_change=per_customer_bill_change, + min_shifted_load=min_shifted_load, + ) + + +def run_sweep( + scenario: SyntheticScenario, + elasticities: list[float], +) -> list[SweepResult]: + """Run the sensitivity sweep across all elasticity values.""" + return [compute_sweep_point(scenario, e) for e in elasticities] + + +def results_to_dataframe(results: list[SweepResult]) -> pd.DataFrame: + """Convert sweep results to a DataFrame for output/plotting.""" + records = [ + { + "elasticity": r.elasticity, + "load_shift_kwh": r.load_shift_kwh, + "load_shift_pct": r.load_shift_pct, + "mc_delta_dollars": r.mc_delta_dollars, + "mc_delta_pct": r.mc_delta_pct, + "rr_change_dollars": r.rr_change_dollars, + "rr_change_pct": r.rr_change_pct, + "tou_ratio_orig": r.tou_ratio_orig, + "tou_ratio_shifted": r.tou_ratio_shifted, + "tou_ratio_change": r.tou_ratio_change, + "per_customer_bill_change": r.per_customer_bill_change, + "min_shifted_load": r.min_shifted_load, + } + for r in results + ] + return pd.DataFrame(records) + + +def format_table(df: pd.DataFrame) -> str: + """Render the sweep results as a human-readable table.""" + buf = StringIO() + fmt: dict[str, str] = { + "elasticity": "{:.3f}", + "load_shift_kwh": "{:,.0f}", + "load_shift_pct": "{:.2f}%", + "mc_delta_dollars": "${:,.0f}", + "mc_delta_pct": "{:.3f}%", + "rr_change_dollars": "${:,.0f}", + "rr_change_pct": "{:.3f}%", + "tou_ratio_orig": "{:.4f}", + "tou_ratio_shifted": "{:.4f}", + "tou_ratio_change": "{:.4f}", + "per_customer_bill_change": "${:,.2f}", + "min_shifted_load": "{:.4f}", + } + formatted = df.copy() + for col, f in fmt.items(): + if col in formatted.columns: + formatted[col] = formatted[col].map(lambda v, f=f: f.format(v)) + formatted.to_string(buf, index=False) + return buf.getvalue() + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Elasticity sensitivity analysis for demand-flex pipeline." + ) + p.add_argument( + "--elasticities", + type=str, + default="-0.05,-0.10,-0.15,-0.20", + help="Comma-separated elasticity values (default: -0.05,-0.10,-0.15,-0.20).", + ) + p.add_argument( + "--synthetic", + action="store_true", + default=True, + help="Use synthetic data (default).", + ) + p.add_argument( + "--output-dir", + type=str, + default=None, + help="Directory to write CSV output. If omitted, prints to stdout.", + ) + return p.parse_args() + + +def main() -> None: + args = _parse_args() + elasticities = [float(e.strip()) for e in args.elasticities.split(",")] + scenario = build_synthetic_scenario() + results = run_sweep(scenario, elasticities) + df = results_to_dataframe(results) + + print(f"\n{'=' * 72}") + print("DEMAND-FLEX ELASTICITY SENSITIVITY ANALYSIS") + print(f"{'=' * 72}") + print(f" Elasticities: {elasticities}") + print(f" Mode: {'synthetic' if args.synthetic else 'custom'}") + print(f"{'=' * 72}\n") + print(format_table(df)) + print() + + if args.output_dir is not None: + out_path = Path(args.output_dir) + out_path.mkdir(parents=True, exist_ok=True) + csv_path = out_path / "demand_flex_sensitivity.csv" + df.to_csv(csv_path, index=False) + print(f" CSV written to: {csv_path}") + + +if __name__ == "__main__": + main() diff --git a/utils/post/validate_demand_flex.py b/utils/post/validate_demand_flex.py new file mode 100644 index 00000000..a05501c7 --- /dev/null +++ b/utils/post/validate_demand_flex.py @@ -0,0 +1,661 @@ +"""Analytical validation of the demand-flex pipeline's mathematical invariants. + +Operates on load profiles, MC prices, tariff structures, and elasticity +parameters to verify expected outcomes derived from the frozen-residual model +(see context/code/cairo/cairo_demand_flexibility_workflow.md). + +Each check returns a ``CheckResult`` consistent with the CAIRO validation +framework in ``utils/post/validate/``. The script can run on synthetic data +(default, for CI) or user-supplied data for deeper analysis. + +Usage:: + + uv run python -m utils.post.validate_demand_flex [--synthetic] +""" + +from __future__ import annotations + +import argparse +import sys +from dataclasses import dataclass, field +from typing import Any, Literal + +import numpy as np +import pandas as pd + +from utils.cairo import ( + _build_period_shift_targets, + _compute_equivalent_flat_tariff, + _shift_building_hourly_demand, + assign_hourly_periods, + extract_tou_period_rates, + process_residential_hourly_demand_response_shift, +) +from utils.pre.compute_tou import ( + Season, + SeasonTouSpec, + combine_marginal_costs, + compute_tou_cost_causation_ratio, + season_mask, +) + +# --------------------------------------------------------------------------- +# Lightweight CheckResult (avoids importing boto3 via utils.post.validate) +# --------------------------------------------------------------------------- + +CheckStatus = Literal["PASS", "WARN", "FAIL"] + + +@dataclass +class CheckResult: + """Mirrors ``utils.post.validate.checks.CheckResult``.""" + + name: str + status: CheckStatus + message: str + details: dict[str, Any] = field(default_factory=dict) + + @property + def passed(self) -> bool: + return self.status == "PASS" + + +# --------------------------------------------------------------------------- +# Synthetic data generation +# --------------------------------------------------------------------------- + + +@dataclass(slots=True) +class SyntheticScenario: + """All inputs needed to exercise the demand-flex analytical checks.""" + + hourly_index: pd.DatetimeIndex + load_elec: pd.DataFrame # indexed by (bldg_id, time) + customer_metadata: pd.DataFrame + tou_tariff: dict + period_rate: pd.Series + tou_bldg_ids: list[int] + nontou_bldg_ids: list[int] + bulk_marginal_costs: pd.DataFrame + dist_mc: pd.Series + rr_total: float + season_specs: list[SeasonTouSpec] + subclass_rr_ratios: dict[str, float] + + +def build_synthetic_scenario( + *, + n_tou_bldgs: int = 5, + n_nontou_bldgs: int = 5, + seed: int = 42, +) -> SyntheticScenario: + """Build a complete synthetic scenario for testing demand-flex checks.""" + rng = np.random.RandomState(seed) + year = 2025 + hourly_index = pd.date_range( + f"{year}-01-01", periods=8760, freq="h", tz="EST", name="time" + ) + + tou_bldg_ids = list(range(1, n_tou_bldgs + 1)) + nontou_bldg_ids = list(range(n_tou_bldgs + 1, n_tou_bldgs + n_nontou_bldgs + 1)) + all_bldg_ids = tou_bldg_ids + nontou_bldg_ids + + rows: list[dict[str, Any]] = [] + for bldg_id in all_bldg_ids: + base = 2.0 + rng.rand() * 2.0 + for ts in hourly_index: + hour = ts.hour + month = ts.month + seasonal = 1.0 + 0.3 * (1 if month in (6, 7, 8) else 0) + diurnal = 1.0 + 0.5 * max(0, (hour - 8) * (20 - hour)) / 36 + noise = rng.normal(1.0, 0.05) + rows.append( + { + "bldg_id": bldg_id, + "time": ts, + "electricity_net": max(0.1, base * seasonal * diurnal * noise), + } + ) + load_elec = pd.DataFrame(rows).set_index(["bldg_id", "time"]) + + weights = {bldg: 100.0 + rng.rand() * 50 for bldg in all_bldg_ids} + customer_metadata = pd.DataFrame( + [{"bldg_id": b, "weight": w} for b, w in weights.items()] + ) + + # TOU tariff: 2-period (off-peak=0, peak=1), peak hours 16-19 + tou_tariff: dict[str, Any] = { + "items": [ + { + "energyratestructure": [ + [{"rate": 0.08, "adj": 0.0}], # period 0: off-peak + [{"rate": 0.20, "adj": 0.0}], # period 1: peak + ], + "energyweekdayschedule": [ + [0] * 16 + [1] * 4 + [0] * 4 for _ in range(12) + ], + "energyweekendschedule": [ + [0] * 16 + [1] * 4 + [0] * 4 for _ in range(12) + ], + } + ] + } + + period_rate = pd.Series({0: 0.08, 1: 0.20}, name="rate") + period_rate.index.name = "energy_period" + + # MC: higher at peak hours, with summer premium + energy_mc = [] + capacity_mc = [] + for ts in hourly_index: + hour = ts.hour + month = ts.month + summer_premium = 0.02 if month in (6, 7, 8, 9) else 0.0 + peak_premium = 0.03 if 16 <= hour < 20 else 0.0 + energy_mc.append(0.04 + summer_premium + peak_premium + rng.normal(0, 0.002)) + capacity_mc.append(0.01 + peak_premium * 0.5 + rng.normal(0, 0.001)) + bulk_mc = pd.DataFrame( + { + "Marginal Energy Costs ($/kWh)": energy_mc, + "Marginal Capacity Costs ($/kWh)": capacity_mc, + }, + index=hourly_index, + ) + + dist_mc_vals = [0.02 + 0.01 * (1 if 16 <= ts.hour < 20 else 0) for ts in hourly_index] + dist_mc = pd.Series(dist_mc_vals, index=hourly_index, name="Marginal Dist+Sub-Tx Costs ($/kWh)") + + rr_total = 500_000.0 + + season_specs = [ + SeasonTouSpec( + season=Season(name="winter", months=[1, 2, 3, 4, 5, 10, 11, 12]), + base_rate=0.10, + peak_hours=[16, 17, 18, 19], + peak_offpeak_ratio=2.0, + ), + SeasonTouSpec( + season=Season(name="summer", months=[6, 7, 8, 9]), + base_rate=0.12, + peak_hours=[16, 17, 18, 19], + peak_offpeak_ratio=2.5, + ), + ] + + hp_ratio = n_tou_bldgs / len(all_bldg_ids) + subclass_rr_ratios = {"hp": hp_ratio, "non-hp": 1.0 - hp_ratio} + + return SyntheticScenario( + hourly_index=hourly_index, + load_elec=load_elec, + customer_metadata=customer_metadata, + tou_tariff=tou_tariff, + period_rate=period_rate, + tou_bldg_ids=tou_bldg_ids, + nontou_bldg_ids=nontou_bldg_ids, + bulk_marginal_costs=bulk_mc, + dist_mc=dist_mc, + rr_total=rr_total, + season_specs=season_specs, + subclass_rr_ratios=subclass_rr_ratios, + ) + + +# --------------------------------------------------------------------------- +# Analytical helpers +# --------------------------------------------------------------------------- + + +def _compute_system_mc( + load_elec: pd.DataFrame, + bldg_ids: list[int], + customer_metadata: pd.DataFrame, + bulk_mc: pd.DataFrame, + dist_mc: pd.Series, +) -> float: + """Compute weighted total MC dollars for a set of buildings.""" + combined_mc = combine_marginal_costs(bulk_mc, dist_mc) + sample_weights = customer_metadata[["bldg_id", "weight"]] + bldg_level = load_elec.index.get_level_values("bldg_id") + sub = load_elec.loc[bldg_level.isin(set(bldg_ids))] + weighted = sub.reset_index().merge(sample_weights, on="bldg_id") + weighted["electricity_net"] = weighted["electricity_net"] * weighted["weight"] + sys_load_raw = weighted.groupby("time")["electricity_net"].sum() + mc_index = pd.DatetimeIndex(combined_mc.index) + sys_load = pd.Series( + sys_load_raw.values[: len(mc_index)], + index=mc_index, + ) + return float((combined_mc * sys_load).sum()) + + +def _shift_loads( + scenario: SyntheticScenario, + elasticity: float, +) -> tuple[pd.DataFrame, pd.DataFrame]: + """Apply demand-response shifting to the TOU cohort and return shifted loads + tracker.""" + return process_residential_hourly_demand_response_shift( + hourly_load_df=_extract_tou_hourly(scenario), + period_rate=scenario.period_rate, + demand_elasticity=elasticity, + ) + + +def _extract_tou_hourly(scenario: SyntheticScenario) -> pd.DataFrame: + """Extract and annotate TOU-cohort hourly loads for shifting.""" + bldg_level = scenario.load_elec.index.get_level_values("bldg_id") + tou_mask = bldg_level.isin(set(scenario.tou_bldg_ids)) + tou_df = scenario.load_elec.loc[tou_mask, ["electricity_net"]].copy().reset_index() + period_map = assign_hourly_periods(scenario.hourly_index, scenario.tou_tariff) + period_df = period_map.reset_index() + period_df.columns = pd.Index(["time", "energy_period"]) + tou_df = tou_df.merge(period_df, on="time", how="left") + return tou_df + + +def _apply_shift_to_full_load( + scenario: SyntheticScenario, + shifted_tou: pd.DataFrame, +) -> pd.DataFrame: + """Merge shifted TOU rows back into the full load DataFrame.""" + full = scenario.load_elec.copy() + shifted_indexed = shifted_tou.set_index(["bldg_id", "time"]) + full.loc[shifted_indexed.index, "electricity_net"] = shifted_indexed[ + "shifted_net" + ].to_numpy() + return full + + +# --------------------------------------------------------------------------- +# Check implementations +# --------------------------------------------------------------------------- + + +def check_energy_conservation( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """Total kWh before == after shift for TOU buildings.""" + shifted, _ = _shift_loads(scenario, elasticity) + orig_kwh = shifted["electricity_net"].sum() + shifted_kwh = shifted["shifted_net"].sum() + rel_diff = abs(shifted_kwh - orig_kwh) / max(abs(orig_kwh), 1e-12) + tol = 1e-8 + return CheckResult( + name="energy_conservation", + status="PASS" if rel_diff < tol else "FAIL", + message=f"Energy conservation: orig={orig_kwh:.2f}, shifted={shifted_kwh:.2f}, rel_diff={rel_diff:.2e}", + details={"orig_kwh": orig_kwh, "shifted_kwh": shifted_kwh, "rel_diff": rel_diff}, + ) + + +def check_mc_delta_negative( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """Shifting peak->offpeak with negative elasticity must reduce MC dollars.""" + shifted, _ = _shift_loads(scenario, elasticity) + full_shifted = _apply_shift_to_full_load(scenario, shifted) + all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids + mc_orig = _compute_system_mc( + scenario.load_elec, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_shifted = _compute_system_mc( + full_shifted, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_delta = mc_shifted - mc_orig + return CheckResult( + name="mc_delta_negative", + status="PASS" if mc_delta < 0 else "FAIL", + message=f"MC delta: ${mc_delta:,.2f} (orig=${mc_orig:,.2f}, shifted=${mc_shifted:,.2f})", + details={"mc_orig": mc_orig, "mc_shifted": mc_shifted, "mc_delta": mc_delta}, + ) + + +def check_frozen_residual_identity( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """new_RR == full_RR_orig + (MC_shifted - MC_orig).""" + shifted, _ = _shift_loads(scenario, elasticity) + full_shifted = _apply_shift_to_full_load(scenario, shifted) + all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids + mc_orig = _compute_system_mc( + scenario.load_elec, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_shifted = _compute_system_mc( + full_shifted, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + frozen_residual = scenario.rr_total - mc_orig + new_rr = mc_shifted + frozen_residual + expected_rr = scenario.rr_total + (mc_shifted - mc_orig) + diff = abs(new_rr - expected_rr) + tol = 1.0 + return CheckResult( + name="frozen_residual_identity", + status="PASS" if diff < tol else "FAIL", + message=f"new_RR=${new_rr:,.2f} vs expected=${expected_rr:,.2f} (diff=${diff:.4f})", + details={ + "new_rr": new_rr, "expected_rr": expected_rr, + "frozen_residual": frozen_residual, "diff": diff, + }, + ) + + +def check_rr_decreases( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """new_RR < full_RR_orig for non-zero elasticity.""" + shifted, _ = _shift_loads(scenario, elasticity) + full_shifted = _apply_shift_to_full_load(scenario, shifted) + all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids + mc_orig = _compute_system_mc( + scenario.load_elec, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_shifted = _compute_system_mc( + full_shifted, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + new_rr = mc_shifted + (scenario.rr_total - mc_orig) + return CheckResult( + name="rr_decreases", + status="PASS" if new_rr < scenario.rr_total else "FAIL", + message=f"new_RR=${new_rr:,.2f} vs orig=${scenario.rr_total:,.2f} (delta=${new_rr - scenario.rr_total:,.2f})", + details={"new_rr": new_rr, "rr_orig": scenario.rr_total}, + ) + + +def check_tou_ratio_does_not_increase( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """Post-flex TOU cost-causation ratio must not increase significantly. + + With proportional within-period hourly allocation, the demand-weighted MC + averages are preserved by construction. The ratio may decrease slightly + (via receiver-period heterogeneity and seasonal effects) or remain + unchanged, but should never increase beyond floating-point noise. + """ + shifted, _ = _shift_loads(scenario, elasticity) + combined_mc = combine_marginal_costs( + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_index = pd.DatetimeIndex(combined_mc.index) + peak_hours = scenario.season_specs[0].peak_hours + + sample_weights = scenario.customer_metadata[["bldg_id", "weight"]] + bldg_level = scenario.load_elec.index.get_level_values("bldg_id") + tou_set = set(scenario.tou_bldg_ids) + orig_tou = scenario.load_elec.loc[bldg_level.isin(tou_set)] + orig_weighted = orig_tou.reset_index().merge(sample_weights, on="bldg_id") + orig_weighted["electricity_net"] *= orig_weighted["weight"] + orig_sys = orig_weighted.groupby("time")["electricity_net"].sum() + orig_load = pd.Series(orig_sys.values[: len(mc_index)], index=mc_index) + + full_shifted = _apply_shift_to_full_load(scenario, shifted) + shifted_tou = full_shifted.loc[bldg_level.isin(tou_set)] + shifted_weighted = shifted_tou.reset_index().merge(sample_weights, on="bldg_id") + shifted_weighted["electricity_net"] *= shifted_weighted["weight"] + shifted_sys = shifted_weighted.groupby("time")["electricity_net"].sum() + shifted_load = pd.Series(shifted_sys.values[: len(mc_index)], index=mc_index) + + ratio_orig = compute_tou_cost_causation_ratio(combined_mc, orig_load, peak_hours) + ratio_shifted = compute_tou_cost_causation_ratio(combined_mc, shifted_load, peak_hours) + delta = ratio_shifted - ratio_orig + tol = 1e-3 + return CheckResult( + name="tou_ratio_does_not_increase", + status="PASS" if delta < tol else "FAIL", + message=f"TOU ratio: {ratio_orig:.4f} -> {ratio_shifted:.4f} (delta={delta:.6f})", + details={"ratio_orig": ratio_orig, "ratio_shifted": ratio_shifted, "delta": delta}, + ) + + +def check_nonhp_subclass_rr_unchanged( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """Non-TOU subclass RR equals its no-flex baseline.""" + nonhp_baseline = scenario.subclass_rr_ratios["non-hp"] * scenario.rr_total + # Under demand flex, non-TOU RR is held at baseline: + nonhp_flex_rr = nonhp_baseline # by construction in the model + diff = abs(nonhp_flex_rr - nonhp_baseline) + return CheckResult( + name="nonhp_subclass_rr_unchanged", + status="PASS" if diff < 0.01 else "FAIL", + message=f"Non-HP RR: baseline=${nonhp_baseline:,.2f}, flex=${nonhp_flex_rr:,.2f}, diff=${diff:.2f}", + details={"nonhp_baseline": nonhp_baseline, "nonhp_flex_rr": nonhp_flex_rr}, + ) + + +def check_tou_subclass_rr_absorbs_delta( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """TOU subclass RR = baseline_TOU + MC_delta_TOU.""" + shifted, _ = _shift_loads(scenario, elasticity) + full_shifted = _apply_shift_to_full_load(scenario, shifted) + mc_orig_tou = _compute_system_mc( + scenario.load_elec, scenario.tou_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_shifted_tou = _compute_system_mc( + full_shifted, scenario.tou_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_delta_tou = mc_shifted_tou - mc_orig_tou + hp_baseline = scenario.subclass_rr_ratios["hp"] * scenario.rr_total + expected_hp_rr = hp_baseline + mc_delta_tou + + # Under the frozen-residual model: new system RR = RR_orig + MC_delta_system + # Non-HP RR is held at baseline. TOU RR = new_system_RR - non-HP baseline. + all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids + mc_orig_all = _compute_system_mc( + scenario.load_elec, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + mc_shifted_all = _compute_system_mc( + full_shifted, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + new_rr_system = scenario.rr_total + (mc_shifted_all - mc_orig_all) + nonhp_baseline = scenario.subclass_rr_ratios["non-hp"] * scenario.rr_total + actual_hp_rr = new_rr_system - nonhp_baseline + + diff = abs(actual_hp_rr - expected_hp_rr) + # Tolerance is wider because system-level MC delta may differ from + # TOU-subclass MC delta when non-TOU loads don't change. + tol = abs(scenario.rr_total) * 0.001 + return CheckResult( + name="tou_subclass_rr_absorbs_delta", + status="PASS" if diff < tol else "FAIL", + message=f"TOU RR: expected=${expected_hp_rr:,.2f}, actual=${actual_hp_rr:,.2f}, diff=${diff:,.2f}", + details={ + "expected_hp_rr": expected_hp_rr, "actual_hp_rr": actual_hp_rr, + "mc_delta_tou": mc_delta_tou, "diff": diff, + }, + ) + + +def check_achieved_elasticity_near_target( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """Realized elasticity approximates input elasticity within 10% relative.""" + tou_hourly = _extract_tou_hourly(scenario) + _, tracker = process_residential_hourly_demand_response_shift( + hourly_load_df=tou_hourly, + period_rate=scenario.period_rate, + demand_elasticity=elasticity, + ) + # Filter to donor periods (not the receiver which is forced zero-sum) + valid = tracker.dropna(subset=["epsilon"]) + if valid.empty: + return CheckResult( + name="achieved_elasticity_near_target", + status="WARN", + message="No valid realized elasticity values", + details={}, + ) + # Donor periods are those with rate > flat; receiver has rate < flat + flat = float(valid["rate"].min()) + donors = valid[valid["rate"] > flat] + if donors.empty: + return CheckResult( + name="achieved_elasticity_near_target", + status="WARN", + message="No donor periods found for elasticity comparison", + details={}, + ) + avg_epsilon = float(donors["epsilon"].mean()) + rel_error = abs(avg_epsilon - elasticity) / abs(elasticity) if elasticity != 0 else 0 + tol = 0.10 + return CheckResult( + name="achieved_elasticity_near_target", + status="PASS" if rel_error < tol else "FAIL", + message=f"Avg realized epsilon={avg_epsilon:.4f} vs target={elasticity:.4f} (rel_error={rel_error:.2%})", + details={"avg_epsilon": avg_epsilon, "target": elasticity, "rel_error": rel_error}, + ) + + +def check_shifted_loads_nonnegative( + scenario: SyntheticScenario, + elasticity: float, +) -> CheckResult: + """All shifted hourly loads must be >= 0.""" + shifted, _ = _shift_loads(scenario, elasticity) + min_load = float(shifted["shifted_net"].min()) + n_negative = int((shifted["shifted_net"] < 0).sum()) + return CheckResult( + name="shifted_loads_nonnegative", + status="PASS" if n_negative == 0 else "FAIL", + message=f"Min shifted load={min_load:.4f}, negative hours={n_negative}", + details={"min_load": min_load, "n_negative": n_negative}, + ) + + +def check_mc_delta_monotonic_in_elasticity( + scenario: SyntheticScenario, + elasticities: list[float] | None = None, +) -> CheckResult: + """Larger |elasticity| produces larger |MC delta|.""" + if elasticities is None: + elasticities = [-0.05, -0.10, -0.15, -0.20] + all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids + mc_orig = _compute_system_mc( + scenario.load_elec, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + + deltas: list[float] = [] + for e in elasticities: + shifted, _ = _shift_loads(scenario, e) + full_shifted = _apply_shift_to_full_load(scenario, shifted) + mc_shifted = _compute_system_mc( + full_shifted, all_bldg_ids, scenario.customer_metadata, + scenario.bulk_marginal_costs, scenario.dist_mc, + ) + deltas.append(mc_shifted - mc_orig) + + magnitudes = [abs(d) for d in deltas] + is_monotonic = all( + magnitudes[i] <= magnitudes[i + 1] for i in range(len(magnitudes) - 1) + ) + return CheckResult( + name="mc_delta_monotonic_in_elasticity", + status="PASS" if is_monotonic else "FAIL", + message=f"MC deltas by elasticity: {dict(zip(elasticities, [f'${d:,.0f}' for d in deltas]))}", + details={ + "elasticities": elasticities, + "mc_deltas": deltas, + "magnitudes": magnitudes, + "is_monotonic": is_monotonic, + }, + ) + + +# --------------------------------------------------------------------------- +# Runner +# --------------------------------------------------------------------------- + +ALL_SINGLE_ELASTICITY_CHECKS = [ + check_energy_conservation, + check_mc_delta_negative, + check_frozen_residual_identity, + check_rr_decreases, + check_tou_ratio_does_not_increase, + check_nonhp_subclass_rr_unchanged, + check_tou_subclass_rr_absorbs_delta, + check_achieved_elasticity_near_target, + check_shifted_loads_nonnegative, +] + + +def run_all_checks( + scenario: SyntheticScenario, + elasticity: float = -0.1, + sweep_elasticities: list[float] | None = None, +) -> list[CheckResult]: + """Run all analytical demand-flex checks and return results.""" + results: list[CheckResult] = [] + for check_fn in ALL_SINGLE_ELASTICITY_CHECKS: + results.append(check_fn(scenario, elasticity)) + results.append( + check_mc_delta_monotonic_in_elasticity(scenario, sweep_elasticities) + ) + return results + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Analytical validation of demand-flex mathematical invariants." + ) + p.add_argument( + "--synthetic", + action="store_true", + default=True, + help="Run on synthetic data (default).", + ) + p.add_argument( + "--elasticity", + type=float, + default=-0.1, + help="Elasticity parameter for single-point checks (default: -0.1).", + ) + return p.parse_args() + + +def main() -> None: + args = _parse_args() + scenario = build_synthetic_scenario() + results = run_all_checks(scenario, elasticity=args.elasticity) + + print(f"\n{'=' * 60}") + print("DEMAND-FLEX ANALYTICAL VALIDATION") + print(f"{'=' * 60}") + print(f" Elasticity: {args.elasticity}") + print(f" Mode: {'synthetic' if args.synthetic else 'custom'}") + print(f"{'=' * 60}\n") + + for r in results: + status_marker = {"PASS": "+", "WARN": "~", "FAIL": "x"}[r.status] + print(f" [{status_marker}] {r.status}: {r.name}") + print(f" {r.message}") + + by_status = {s: sum(1 for r in results if r.status == s) for s in ("PASS", "WARN", "FAIL")} + print(f"\n{'=' * 60}") + print(f" PASS: {by_status['PASS']} WARN: {by_status['WARN']} FAIL: {by_status['FAIL']}") + print(f"{'=' * 60}\n") + + if by_status["FAIL"]: + sys.exit(1) + + +if __name__ == "__main__": + main() From 20db62c8bb1f12d8e05ceae1d61d2e989cacf415 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 25 Mar 2026 23:35:34 +0000 Subject: [PATCH 02/19] Add per-utility demand-flex elasticity diagnostic script Implements utils/post/diagnose_demand_flex.py: an analytical diagnostic that loads real HP building loads and TOU derivation data to sweep candidate elasticity values (-0.02 to -0.20), compute peak reduction % and rate-arbitrage savings per HP building, and recommend the elasticity that best matches the Arcturus 2.0 "no enabling technology" empirical target for each NY utility. Runs in ~15s per utility without requiring a CAIRO run. Made-with: Cursor --- utils/post/diagnose_demand_flex.py | 770 +++++++++++++++++++++++++++++ 1 file changed, 770 insertions(+) create mode 100644 utils/post/diagnose_demand_flex.py diff --git a/utils/post/diagnose_demand_flex.py b/utils/post/diagnose_demand_flex.py new file mode 100644 index 00000000..572c1f3a --- /dev/null +++ b/utils/post/diagnose_demand_flex.py @@ -0,0 +1,770 @@ +"""Per-utility demand-flex elasticity diagnostic for NY heat-pump rate design. + +For each utility, loads real TOU derivation data, ResStock HP building loads, +and marginal cost profiles to compute: + - Demand shift at each candidate elasticity (kWh moved, peak reduction %) + - Rate arbitrage savings (the primary bill savings mechanism) + - MC-based RR reduction savings (secondary, small) + - Arcturus "no enabling tech" comparison + - Recommended elasticity per utility + +Operates purely analytically (no CAIRO run required). Results can be validated +against actual CAIRO bill outputs from batch ny_20260325b_r1-16. + +Usage: + uv run python -m utils.post.diagnose_demand_flex \ + --state ny \ + --elasticities -0.05,-0.10,-0.15,-0.20 \ + --output-dir /tmp/demand_flex_diagnostic + + # Fast dev iteration with sampled buildings: + uv run python -m utils.post.diagnose_demand_flex \ + --state ny --sample-size 50 --output-dir /tmp/demand_flex_diagnostic +""" + +from __future__ import annotations + +import argparse +import json +import logging +import math +import sys +from dataclasses import dataclass, field +from pathlib import Path + +import numpy as np +import pandas as pd +import polars as pl + +from data.eia.hourly_loads.eia_region_config import get_aws_storage_options +from utils.cairo import ( + _build_period_consumption, + _build_period_shift_targets, + _compute_equivalent_flat_tariff, + assign_hourly_periods, + extract_tou_period_rates, +) +from utils.pre.compute_tou import SeasonTouSpec, load_season_specs + +log = logging.getLogger(__name__) + +# ── Constants ───────────────────────────────────────────────────────────────── + +WINTER_MONTHS = {1, 2, 3, 10, 11, 12} +SUMMER_MONTHS = {4, 5, 6, 7, 8, 9} + +ARCTURUS_INTERCEPT = -0.011 +ARCTURUS_SLOPE = -0.065 + +STATE_CONFIGS: dict[str, dict] = { + "ny": { + "utilities": ("cenhud", "coned", "nimo", "nyseg", "or", "psegli", "rge"), + "path_metadata": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata/state=NY/" + "upgrade=00/metadata-sb.parquet" + ), + "path_utility_assignment": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata_utility/" + "state=NY/utility_assignment.parquet" + ), + "path_loads_dir": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/load_curve_hourly/" + "state=NY/upgrade=00" + ), + "path_tou_derivation_dir": Path( + "rate_design/hp_rates/ny/config/tou_derivation" + ), + "path_tariffs_electric_dir": Path( + "rate_design/hp_rates/ny/config/tariffs/electric" + ), + "mc_base": "s3://data.sb/switchbox/marginal_costs/ny", + "mc_year": 2025, + }, +} + + +# ── Data structures ─────────────────────────────────────────────────────────── + + +@dataclass(frozen=True, slots=True) +class UtilityContext: + """All data needed to diagnose one utility.""" + + utility: str + season_specs: list[SeasonTouSpec] + tou_tariff: dict + hp_bldg_ids: list[int] + hp_weights: dict[int, float] + total_weighted_customers: float + hp_weighted_customers: float + + +@dataclass(slots=True) +class SeasonResult: + """Diagnostic results for one utility × season × elasticity.""" + + utility: str + season: str + elasticity: float + price_ratio: float + base_rate: float + peak_hours: list[int] + n_peak_hours_per_day: int + hp_bldg_count: int + hp_weighted_share: float + hp_peak_kwh_orig: float + hp_peak_kwh_shifted: float + peak_reduction_pct: float + kwh_shifted: float + system_peak_reduction_pct: float + p_flat: float + arcturus_peak_reduction_pct: float + delta_vs_arcturus_pct: float + rate_arbitrage_savings_per_hp: float + delivery_mc_savings_total: float + delivery_mc_savings_per_hp: float + delivery_mc_nonzero_hours: int + delivery_mc_peak_overlap_hours: int + + +@dataclass(slots=True) +class UtilityResult: + """Aggregated diagnostic results for one utility.""" + + utility: str + season_results: list[SeasonResult] = field(default_factory=list) + annual_weighted_ratio: float = 0.0 + annual_arcturus_pct: float = 0.0 + recommended_elasticity: float = 0.0 + recommended_annual_savings: float = 0.0 + + +# ── Arcturus model ──────────────────────────────────────────────────────────── + + +def arcturus_peak_reduction(price_ratio: float) -> float: + """Arcturus 2.0 'no enabling technology' peak reduction prediction. + + Returns a positive fraction (e.g. 0.056 for 5.6% reduction). + """ + if price_ratio <= 1.0: + return 0.0 + return abs(ARCTURUS_INTERCEPT + ARCTURUS_SLOPE * math.log(price_ratio)) + + +# ── Data loading ────────────────────────────────────────────────────────────── + + +def load_hp_metadata( + path_metadata: Path, + path_utility_assignment: Path, +) -> pl.DataFrame: + """Load metadata and return HP buildings with utility assignment and weight.""" + meta = pl.read_parquet(path_metadata).select( + "bldg_id", "postprocess_group.has_hp", "weight" + ) + util = pl.read_parquet(path_utility_assignment).select( + "bldg_id", "sb.electric_utility" + ) + joined = meta.join(util, on="bldg_id", how="inner") + hp = joined.filter(pl.col("postprocess_group.has_hp") == True) # noqa: E712 + log.info( + "Loaded %d HP buildings out of %d total", + hp.height, + joined.height, + ) + return hp + + +def load_building_loads( + bldg_ids: list[int], + loads_dir: Path, + sample_size: int | None = None, +) -> pd.DataFrame: + """Load hourly electric net load for a set of buildings from local parquet. + + Returns a pandas DataFrame with MultiIndex (bldg_id, time) and column + 'electricity_net' in kWh, matching CAIRO's expected format. + """ + if sample_size and sample_size < len(bldg_ids): + rng = np.random.default_rng(42) + bldg_ids = list(rng.choice(bldg_ids, size=sample_size, replace=False)) + + frames: list[pd.DataFrame] = [] + missing = 0 + for bid in bldg_ids: + path = loads_dir / f"{bid}-0.parquet" + if not path.exists(): + missing += 1 + continue + df = pl.read_parquet( + path, + columns=["timestamp", "out.electricity.net.energy_consumption"], + ).to_pandas() + df = df.rename( + columns={ + "timestamp": "time", + "out.electricity.net.energy_consumption": "electricity_net", + } + ) + df["bldg_id"] = bid + frames.append(df) + + if missing: + log.warning("Missing load files for %d of %d buildings", missing, len(bldg_ids)) + if not frames: + raise FileNotFoundError( + f"No load files found in {loads_dir} for {len(bldg_ids)} buildings" + ) + + combined = pd.concat(frames, ignore_index=True) + combined = combined.set_index(["bldg_id", "time"]).sort_index() + log.info( + "Loaded %d buildings x %d hours = %d rows", + len(frames), + 8760, + len(combined), + ) + return combined + + +def load_tou_context( + utility: str, + tou_derivation_dir: Path, + tariffs_electric_dir: Path, +) -> tuple[list[SeasonTouSpec], dict]: + """Load TOU derivation specs and the flex tariff for one utility.""" + deriv_path = tou_derivation_dir / f"{utility}_hp_seasonalTOU_derivation.json" + specs = load_season_specs(deriv_path) + + tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU_flex.json" + if not tariff_path.exists(): + tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU.json" + with open(tariff_path) as f: + tou_tariff = json.load(f) + return specs, tou_tariff + + +def load_delivery_mc( + utility: str, + mc_base: str, + mc_year: int, + storage_options: dict[str, str], +) -> pd.DataFrame: + """Load delivery-only MC (dist+sub-tx + bulk_tx) for one utility. + + Returns DataFrame with columns ['timestamp', 'mc_kwh'] in $/kWh. + """ + dist_path = ( + f"{mc_base}/dist_and_sub_tx/utility={utility}/year={mc_year}/data.parquet" + ) + bulk_path = f"{mc_base}/bulk_tx/utility={utility}/year={mc_year}/data.parquet" + + dist = pl.read_parquet(dist_path, storage_options=storage_options) + if "timestamp" in dist.columns: + ts_dtype = dist.schema["timestamp"] + if isinstance(ts_dtype, pl.Datetime) and ts_dtype.time_zone is not None: + dist = dist.with_columns( + pl.col("timestamp").dt.replace_time_zone(None).alias("timestamp") + ) + dist = dist.select( + "timestamp", + pl.col("mc_total_per_kwh").alias("mc_dist"), + ) + + bulk = pl.read_parquet(bulk_path, storage_options=storage_options) + if "timestamp" in bulk.columns: + ts_dtype = bulk.schema["timestamp"] + if isinstance(ts_dtype, pl.Datetime) and ts_dtype.time_zone is not None: + bulk = bulk.with_columns( + pl.col("timestamp").dt.replace_time_zone(None).alias("timestamp") + ) + bulk = bulk.select( + "timestamp", + pl.col("bulk_tx_cost_enduse").alias("mc_bulk"), + ) + + mc = dist.join(bulk, on="timestamp", how="full", coalesce=True).sort("timestamp") + mc = mc.with_columns( + (pl.col("mc_dist").fill_null(0.0) + pl.col("mc_bulk").fill_null(0.0)).alias( + "mc_kwh" + ) + ) + return mc.select("timestamp", "mc_kwh").to_pandas() + + +# ── Core diagnostic logic ───────────────────────────────────────────────────── + + +def diagnose_season( + utility: str, + season_spec: SeasonTouSpec, + loads_df: pd.DataFrame, + period_rate: pd.Series, + period_map: pd.Series, + elasticity: float, + hp_weights: dict[int, float], + total_weighted_customers: float, + hp_weighted_customers: float, + mc_df: pd.DataFrame | None, +) -> SeasonResult: + """Run diagnostic for one utility × season × elasticity.""" + season_months = set(season_spec.season.months) + season_name = season_spec.season.name + + time_idx = pd.DatetimeIndex(loads_df.index.get_level_values("time").unique()) + month_arr = time_idx.month + + bldg_level = loads_df.index.get_level_values("bldg_id") + time_level = pd.DatetimeIndex(loads_df.index.get_level_values("time")) + month_level = time_level.month + + season_mask = month_level.isin(season_months) + season_df = loads_df.loc[season_mask, ["electricity_net"]].copy().reset_index() + + period_lookup = period_map.reset_index() + period_lookup.columns = pd.Index(["time", "energy_period"]) + season_df = season_df.merge(period_lookup, on="time", how="left") + + period_consumption = _build_period_consumption(season_df) + p_flat = _compute_equivalent_flat_tariff(period_consumption, period_rate) + + targets = _build_period_shift_targets( + period_consumption, period_rate, elasticity, p_flat, receiver_period=None + ) + + peak_periods = set() + offpeak_periods = set() + for _, row in targets.drop_duplicates("energy_period").iterrows(): + ep = int(row["energy_period"]) + rate_val = float(row["rate"]) + if rate_val > p_flat: + peak_periods.add(ep) + else: + offpeak_periods.add(ep) + + peak_orig = float( + targets.loc[targets["energy_period"].isin(peak_periods), "Q_orig"].sum() + ) + peak_shifted = float( + targets.loc[targets["energy_period"].isin(peak_periods), "Q_target"].sum() + ) + peak_reduction_kwh = peak_orig - peak_shifted + peak_reduction_pct = (peak_reduction_kwh / peak_orig * 100) if peak_orig > 0 else 0 + + total_shift = float(targets.loc[targets["load_shift"] < 0, "load_shift"].sum()) + + peak_rate = season_spec.peak_offpeak_ratio * season_spec.base_rate + offpeak_rate = season_spec.base_rate + rate_spread = peak_rate - offpeak_rate + rate_arb_savings_total = abs(total_shift) * rate_spread + + n_bldgs = len(targets["bldg_id"].unique()) + savings_per_hp = rate_arb_savings_total / n_bldgs if n_bldgs > 0 else 0 + + arcturus_pct = arcturus_peak_reduction(season_spec.peak_offpeak_ratio) * 100 + delta_vs_arcturus = peak_reduction_pct - arcturus_pct + + system_peak_pct = ( + peak_reduction_pct * (hp_weighted_customers / total_weighted_customers) + if total_weighted_customers > 0 + else 0 + ) + + delivery_mc_savings = 0.0 + mc_nonzero = 0 + mc_peak_overlap = 0 + if mc_df is not None: + mc_season = mc_df[mc_df["timestamp"].dt.month.isin(season_months)].copy() + mc_nonzero = int((mc_season["mc_kwh"].abs() > 1e-9).sum()) + + peak_hours_set = set(season_spec.peak_hours) + mc_in_peak = mc_season[mc_season["timestamp"].dt.hour.isin(peak_hours_set)] + mc_peak_overlap = int((mc_in_peak["mc_kwh"].abs() > 1e-9).sum()) + + hourly_shift = season_df.copy() + shift_map = targets.set_index(["bldg_id", "energy_period"])["load_shift"] + hourly_shift = hourly_shift.merge( + targets[["bldg_id", "energy_period", "load_shift", "Q_orig"]], + on=["bldg_id", "energy_period"], + how="left", + ) + period_sums = ( + hourly_shift.groupby(["bldg_id", "energy_period"])["electricity_net"] + .transform("sum") + ) + hour_share = np.where( + period_sums != 0, + hourly_shift["electricity_net"] / period_sums, + 0.0, + ) + hourly_shift["hourly_shift_kwh"] = hourly_shift["load_shift"] * hour_share + + weights_series = hourly_shift["bldg_id"].map(hp_weights).fillna(1.0) + hourly_shift["weighted_shift"] = hourly_shift["hourly_shift_kwh"] * weights_series + + system_hourly = ( + hourly_shift.groupby("time")["weighted_shift"].sum().reset_index() + ) + system_hourly.columns = ["timestamp", "sys_shift_kwh"] + system_hourly["timestamp"] = pd.to_datetime(system_hourly["timestamp"]) + + mc_merge = mc_season.merge(system_hourly, on="timestamp", how="left") + mc_merge["sys_shift_kwh"] = mc_merge["sys_shift_kwh"].fillna(0.0) + delivery_mc_savings = -float( + (mc_merge["mc_kwh"] * mc_merge["sys_shift_kwh"]).sum() + ) + + return SeasonResult( + utility=utility, + season=season_name, + elasticity=elasticity, + price_ratio=season_spec.peak_offpeak_ratio, + base_rate=season_spec.base_rate, + peak_hours=list(season_spec.peak_hours), + n_peak_hours_per_day=len(season_spec.peak_hours), + hp_bldg_count=len(targets["bldg_id"].unique()), + hp_weighted_share=hp_weighted_customers / total_weighted_customers * 100 + if total_weighted_customers > 0 + else 0, + hp_peak_kwh_orig=peak_orig, + hp_peak_kwh_shifted=peak_shifted, + peak_reduction_pct=peak_reduction_pct, + kwh_shifted=abs(total_shift), + system_peak_reduction_pct=system_peak_pct, + p_flat=p_flat, + arcturus_peak_reduction_pct=arcturus_pct, + delta_vs_arcturus_pct=delta_vs_arcturus, + rate_arbitrage_savings_per_hp=savings_per_hp, + delivery_mc_savings_total=delivery_mc_savings, + delivery_mc_savings_per_hp=delivery_mc_savings / n_bldgs if n_bldgs > 0 else 0, + delivery_mc_nonzero_hours=mc_nonzero, + delivery_mc_peak_overlap_hours=mc_peak_overlap, + ) + + +def diagnose_utility( + ctx: UtilityContext, + loads_df: pd.DataFrame, + elasticities: list[float], + mc_df: pd.DataFrame | None, +) -> UtilityResult: + """Run full diagnostic for one utility across all seasons and elasticities.""" + result = UtilityResult(utility=ctx.utility) + + rate_df = extract_tou_period_rates(ctx.tou_tariff) + period_rate = rate_df.groupby("energy_period")["rate"].first() + time_idx = pd.DatetimeIndex( + loads_df.index.get_level_values("time").unique().sort_values() + ) + period_map = assign_hourly_periods(time_idx, ctx.tou_tariff) + + for eps in elasticities: + for spec in ctx.season_specs: + sr = diagnose_season( + utility=ctx.utility, + season_spec=spec, + loads_df=loads_df, + period_rate=period_rate, + period_map=period_map, + elasticity=eps, + hp_weights=ctx.hp_weights, + total_weighted_customers=ctx.total_weighted_customers, + hp_weighted_customers=ctx.hp_weighted_customers, + mc_df=mc_df, + ) + result.season_results.append(sr) + + winter_specs = [s for s in ctx.season_specs if s.season.name == "winter"] + summer_specs = [s for s in ctx.season_specs if s.season.name == "summer"] + if winter_specs and summer_specs: + winter_kwh = sum( + sr.hp_peak_kwh_orig + sr.kwh_shifted + for sr in result.season_results + if sr.season == "winter" and sr.elasticity == elasticities[0] + ) + summer_kwh = sum( + sr.hp_peak_kwh_orig + sr.kwh_shifted + for sr in result.season_results + if sr.season == "summer" and sr.elasticity == elasticities[0] + ) + total_kwh = winter_kwh + summer_kwh + if total_kwh > 0: + w_ratio = winter_specs[0].peak_offpeak_ratio + s_ratio = summer_specs[0].peak_offpeak_ratio + result.annual_weighted_ratio = ( + w_ratio * winter_kwh + s_ratio * summer_kwh + ) / total_kwh + else: + result.annual_weighted_ratio = ( + winter_specs[0].peak_offpeak_ratio + + summer_specs[0].peak_offpeak_ratio + ) / 2 + elif ctx.season_specs: + result.annual_weighted_ratio = ctx.season_specs[0].peak_offpeak_ratio + + result.annual_arcturus_pct = ( + arcturus_peak_reduction(result.annual_weighted_ratio) * 100 + ) + + best_eps = elasticities[0] + best_delta = float("inf") + for eps in elasticities: + annual_peak_red = np.mean( + [ + sr.peak_reduction_pct + for sr in result.season_results + if sr.elasticity == eps + ] + ) + delta = abs(annual_peak_red - result.annual_arcturus_pct) + if delta < best_delta: + best_delta = delta + best_eps = eps + + result.recommended_elasticity = best_eps + result.recommended_annual_savings = sum( + sr.rate_arbitrage_savings_per_hp + for sr in result.season_results + if sr.elasticity == best_eps + ) + return result + + +# ── Output formatting ───────────────────────────────────────────────────────── + + +def results_to_dataframe(results: list[UtilityResult]) -> pl.DataFrame: + """Convert all season-level results to a flat Polars DataFrame.""" + rows = [] + for ur in results: + for sr in ur.season_results: + rows.append( + { + "utility": sr.utility, + "season": sr.season, + "elasticity": sr.elasticity, + "price_ratio": round(sr.price_ratio, 4), + "base_rate_kwh": round(sr.base_rate, 6), + "peak_rate_kwh": round( + sr.base_rate * sr.price_ratio, 6 + ), + "n_peak_hours_per_day": sr.n_peak_hours_per_day, + "hp_bldg_count": sr.hp_bldg_count, + "hp_weighted_share_pct": round(sr.hp_weighted_share, 2), + "hp_peak_kwh_orig": round(sr.hp_peak_kwh_orig, 1), + "hp_peak_kwh_shifted": round(sr.hp_peak_kwh_shifted, 1), + "peak_reduction_pct": round(sr.peak_reduction_pct, 3), + "kwh_shifted": round(sr.kwh_shifted, 1), + "system_peak_reduction_pct": round( + sr.system_peak_reduction_pct, 4 + ), + "p_flat": round(sr.p_flat, 6), + "arcturus_peak_reduction_pct": round( + sr.arcturus_peak_reduction_pct, 3 + ), + "delta_vs_arcturus_pct": round(sr.delta_vs_arcturus_pct, 3), + "rate_arbitrage_savings_per_hp": round( + sr.rate_arbitrage_savings_per_hp, 2 + ), + "delivery_mc_savings_total": round( + sr.delivery_mc_savings_total, 2 + ), + "delivery_mc_savings_per_hp": round( + sr.delivery_mc_savings_per_hp, 2 + ), + "delivery_mc_nonzero_hours": sr.delivery_mc_nonzero_hours, + "delivery_mc_peak_overlap_hours": sr.delivery_mc_peak_overlap_hours, + "annual_weighted_ratio": round(ur.annual_weighted_ratio, 4), + "annual_arcturus_pct": round(ur.annual_arcturus_pct, 3), + "recommended_elasticity": ur.recommended_elasticity, + "recommended_annual_savings": round( + ur.recommended_annual_savings, 2 + ), + } + ) + return pl.DataFrame(rows) + + +def print_summary(results: list[UtilityResult]) -> None: + """Print a human-readable summary to stdout.""" + print("\n" + "=" * 80) + print("DEMAND-FLEX ELASTICITY DIAGNOSTIC") + print("=" * 80) + + print("\nCAVEATS:") + print(" - Arcturus uses aggregate heterogeneous response; our model applies") + print(" uniform constant elasticity. Recommended epsilon is approximate.") + print(" - Arcturus log-linear form differs from our power-law model;") + print(" comparison valid at specific ratios, not across full curve.") + print(" - Single annual elasticity is a compromise across seasons.") + + for ur in results: + print(f"\n{'─' * 80}") + print(f"UTILITY: {ur.utility.upper()}") + print( + f" Annual weighted ratio: {ur.annual_weighted_ratio:.3f}" + f" │ Arcturus target: {ur.annual_arcturus_pct:.1f}% peak reduction" + ) + print( + f" RECOMMENDED ELASTICITY: {ur.recommended_elasticity}" + f" │ Annual savings/HP customer: ${ur.recommended_annual_savings:.2f}" + ) + + for eps in sorted({sr.elasticity for sr in ur.season_results}): + print(f"\n ε = {eps}:") + for sr in ur.season_results: + if sr.elasticity != eps: + continue + print( + f" {sr.season:8s} ratio={sr.price_ratio:.2f}" + f" peak_red={sr.peak_reduction_pct:5.2f}%" + f" (arcturus={sr.arcturus_peak_reduction_pct:.2f}%" + f" Δ={sr.delta_vs_arcturus_pct:+.2f}%)" + ) + print( + f" shifted={sr.kwh_shifted:,.0f} kWh" + f" arb_savings=${sr.rate_arbitrage_savings_per_hp:.2f}/hp" + f" mc_savings=${sr.delivery_mc_savings_per_hp:.2f}/hp" + ) + print( + f" mc_nonzero_hrs={sr.delivery_mc_nonzero_hours}" + f" mc_peak_overlap={sr.delivery_mc_peak_overlap_hours}" + f" sys_peak_impact={sr.system_peak_reduction_pct:.3f}%" + ) + + +# ── Main ────────────────────────────────────────────────────────────────────── + + +def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--state", required=True, choices=list(STATE_CONFIGS.keys())) + p.add_argument( + "--elasticities", + default="-0.05,-0.10,-0.15,-0.20", + help="Comma-separated elasticity values (default: -0.05,-0.10,-0.15,-0.20)", + ) + p.add_argument("--output-dir", type=Path, default=None) + p.add_argument( + "--sample-size", + type=int, + default=None, + help="Limit HP buildings per utility for faster dev iteration", + ) + p.add_argument( + "--utilities", + default=None, + help="Comma-separated utility subset (default: all)", + ) + p.add_argument("-v", "--verbose", action="store_true") + return p.parse_args(argv) + + +def main(argv: list[str] | None = None) -> None: + args = _parse_args(argv) + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s %(name)s: %(message)s", + ) + + cfg = STATE_CONFIGS[args.state] + elasticities = [float(e) for e in args.elasticities.split(",")] + utilities = ( + tuple(args.utilities.split(",")) if args.utilities else cfg["utilities"] + ) + storage_options = get_aws_storage_options() + + project_root = Path(__file__).resolve().parents[2] + + log.info("Loading HP metadata...") + hp_meta = load_hp_metadata(cfg["path_metadata"], cfg["path_utility_assignment"]) + + all_meta = pl.read_parquet(cfg["path_utility_assignment"]).select( + "bldg_id", "weight", "sb.electric_utility" + ) + + results: list[UtilityResult] = [] + for utility in utilities: + log.info("=" * 60) + log.info("Diagnosing %s...", utility) + + util_hp = hp_meta.filter(pl.col("sb.electric_utility") == utility) + hp_bldg_ids = util_hp["bldg_id"].to_list() + hp_weights = dict( + zip(util_hp["bldg_id"].to_list(), util_hp["weight"].to_list()) + ) + hp_weighted = float(util_hp["weight"].sum()) + + util_all = all_meta.filter(pl.col("sb.electric_utility") == utility) + total_weighted = float(util_all["weight"].sum()) + + log.info( + " %d HP buildings (%.0f weighted, %.1f%% of %.0f total)", + len(hp_bldg_ids), + hp_weighted, + hp_weighted / total_weighted * 100 if total_weighted else 0, + total_weighted, + ) + + if not hp_bldg_ids: + log.warning(" No HP buildings for %s, skipping", utility) + continue + + tou_deriv_dir = project_root / cfg["path_tou_derivation_dir"] + tariffs_dir = project_root / cfg["path_tariffs_electric_dir"] + specs, tou_tariff = load_tou_context(utility, tou_deriv_dir, tariffs_dir) + log.info( + " TOU specs: %s", + [(s.season.name, f"ratio={s.peak_offpeak_ratio:.2f}") for s in specs], + ) + + log.info(" Loading HP building loads...") + loads_df = load_building_loads( + hp_bldg_ids, cfg["path_loads_dir"], sample_size=args.sample_size + ) + + log.info(" Loading delivery MC data...") + try: + mc_df = load_delivery_mc( + utility, cfg["mc_base"], cfg["mc_year"], storage_options + ) + log.info(" MC data loaded: %d rows", len(mc_df)) + except Exception as e: + log.warning(" Failed to load MC data for %s: %s", utility, e) + mc_df = None + + ctx = UtilityContext( + utility=utility, + season_specs=specs, + tou_tariff=tou_tariff, + hp_bldg_ids=hp_bldg_ids, + hp_weights=hp_weights, + total_weighted_customers=total_weighted, + hp_weighted_customers=hp_weighted, + ) + + ur = diagnose_utility(ctx, loads_df, elasticities, mc_df) + results.append(ur) + log.info( + " Recommended ε=%.2f, annual savings=$%.2f/hp", + ur.recommended_elasticity, + ur.recommended_annual_savings, + ) + + print_summary(results) + + if args.output_dir: + args.output_dir.mkdir(parents=True, exist_ok=True) + df = results_to_dataframe(results) + csv_path = args.output_dir / "demand_flex_diagnostic.csv" + df.write_csv(csv_path) + log.info("Wrote diagnostic CSV: %s", csv_path) + + parquet_path = args.output_dir / "demand_flex_diagnostic.parquet" + df.write_parquet(parquet_path) + log.info("Wrote diagnostic parquet: %s", parquet_path) + + +if __name__ == "__main__": + main() From 4c4b720d8dae0515badf6813e77d9d144a48d456 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 25 Mar 2026 23:35:43 +0000 Subject: [PATCH 03/19] Add demand-flex shift validation script and diagnostic plots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements utils/post/validate_demand_flex_shift.py: reproduces the constant-elasticity demand-response shift analytically using the same functions CAIRO uses, then verifies correctness via three checks: energy conservation (per-building kWh preserved), direction (peak kWh decreases, off-peak increases), and cross-check against CAIRO's saved demand_flex_elasticity_tracker.csv. All 7 NY utilities pass at ε=-0.10. Produces five diagnostic plots per utility: aggregate daily load profile (pre/post with shaded fill), net shift by hour bars, month×hour heatmap, per-building peak reduction distribution, and per-building daily profiles at representative consumption percentiles. Also writes CSV/parquet data outputs for report reuse. Made-with: Cursor --- utils/post/validate_demand_flex_shift.py | 1147 ++++++++++++++++++++++ 1 file changed, 1147 insertions(+) create mode 100644 utils/post/validate_demand_flex_shift.py diff --git a/utils/post/validate_demand_flex_shift.py b/utils/post/validate_demand_flex_shift.py new file mode 100644 index 00000000..269335bd --- /dev/null +++ b/utils/post/validate_demand_flex_shift.py @@ -0,0 +1,1147 @@ +"""Validate demand-flex load shifting and produce diagnostic plots. + +Reproduces the constant-elasticity demand-response shift analytically using the +same functions CAIRO uses, then: + 1. Runs validation checks (energy conservation, direction, magnitude) + 2. Cross-checks against CAIRO's saved elasticity tracker + 3. Produces diagnostic plots (daily profiles, heatmaps, distributions) + 4. Writes data outputs for report reuse + +Usage: + uv run python -m utils.post.validate_demand_flex_shift \ + --utility coned \ + --elasticity -0.10 \ + --output-dir dev_plots/flex + + # With CAIRO tracker cross-check: + uv run python -m utils.post.validate_demand_flex_shift \ + --utility coned \ + --elasticity -0.10 \ + --batch ny_20260325b_r1-16 \ + --output-dir dev_plots/flex +""" + +from __future__ import annotations + +import argparse +import json +import logging +import sys +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import polars as pl + +from utils.cairo import ( + _build_period_consumption, + _build_period_shift_targets, + _compute_equivalent_flat_tariff, + assign_hourly_periods, + extract_tou_period_rates, +) +from utils.pre.compute_tou import SeasonTouSpec, load_season_specs + +log = logging.getLogger(__name__) + +plt.style.use("seaborn-v0_8-whitegrid") +plt.rcParams.update({ + "figure.dpi": 150, + "savefig.dpi": 150, + "savefig.bbox": "tight", + "font.size": 10, + "axes.titlesize": 12, + "axes.labelsize": 10, +}) + +WINTER_MONTHS = {1, 2, 3, 10, 11, 12} +SUMMER_MONTHS = {4, 5, 6, 7, 8, 9} + +STATE_CONFIGS: dict[str, dict] = { + "ny": { + "utilities": ("cenhud", "coned", "nimo", "nyseg", "or", "psegli", "rge"), + "path_metadata": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata/state=NY/" + "upgrade=00/metadata-sb.parquet" + ), + "path_utility_assignment": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata_utility/" + "state=NY/utility_assignment.parquet" + ), + "path_loads_dir": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/load_curve_hourly/" + "state=NY/upgrade=00" + ), + "path_tou_derivation_dir": Path( + "rate_design/hp_rates/ny/config/tou_derivation" + ), + "path_tariffs_electric_dir": Path( + "rate_design/hp_rates/ny/config/tariffs/electric" + ), + }, +} + + +# ── Data loading ────────────────────────────────────────────────────────────── + + +def load_hp_metadata( + path_metadata: Path, + path_utility_assignment: Path, +) -> pl.DataFrame: + """Load metadata and return HP buildings with utility assignment and weight.""" + meta = pl.read_parquet(path_metadata).select( + "bldg_id", "postprocess_group.has_hp", "weight" + ) + util = pl.read_parquet(path_utility_assignment).select( + "bldg_id", "sb.electric_utility" + ) + joined = meta.join(util, on="bldg_id", how="inner") + hp = joined.filter(pl.col("postprocess_group.has_hp") == True) # noqa: E712 + log.info("Loaded %d HP buildings out of %d total", hp.height, joined.height) + return hp + + +def load_building_loads(bldg_ids: list[int], loads_dir: Path) -> pd.DataFrame: + """Load hourly electric net load for buildings from local parquet. + + Returns pandas DataFrame with MultiIndex (bldg_id, time) and column + 'electricity_net' in kWh. + """ + frames: list[pd.DataFrame] = [] + missing = 0 + for bid in bldg_ids: + path = loads_dir / f"{bid}-0.parquet" + if not path.exists(): + missing += 1 + continue + df = pl.read_parquet( + path, + columns=["timestamp", "out.electricity.net.energy_consumption"], + ).to_pandas() + df = df.rename(columns={ + "timestamp": "time", + "out.electricity.net.energy_consumption": "electricity_net", + }) + df["bldg_id"] = bid + frames.append(df) + + if missing: + log.warning("Missing load files for %d of %d buildings", missing, len(bldg_ids)) + if not frames: + raise FileNotFoundError(f"No load files found in {loads_dir}") + + combined = pd.concat(frames, ignore_index=True) + combined = combined.set_index(["bldg_id", "time"]).sort_index() + log.info("Loaded %d buildings, %d rows", len(frames), len(combined)) + return combined + + +def load_tou_context( + utility: str, + tou_derivation_dir: Path, + tariffs_electric_dir: Path, +) -> tuple[list[SeasonTouSpec], dict]: + """Load TOU derivation specs and the flex tariff for one utility.""" + deriv_path = tou_derivation_dir / f"{utility}_hp_seasonalTOU_derivation.json" + specs = load_season_specs(deriv_path) + tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU_flex.json" + if not tariff_path.exists(): + tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU.json" + with open(tariff_path) as f: + tou_tariff = json.load(f) + return specs, tou_tariff + + +# ── Shift reproduction ──────────────────────────────────────────────────────── + + +def reproduce_shift( + loads_df: pd.DataFrame, + period_rate: pd.Series, + period_map: pd.Series, + season_specs: list[SeasonTouSpec], + elasticity: float, +) -> pd.DataFrame: + """Apply constant-elasticity shifting to all buildings, return hourly results. + + Returns a DataFrame indexed like loads_df with columns: + electricity_net_orig, electricity_net_shifted, hourly_shift_kw, + energy_period, hour_of_day, month, season, tou_rate + """ + time_level = pd.DatetimeIndex(loads_df.index.get_level_values("time")) + bldg_level = loads_df.index.get_level_values("bldg_id") + + result_parts: list[pd.DataFrame] = [] + + for spec in season_specs: + season_months = set(spec.season.months) + season_name = spec.season.name + + season_mask = time_level.month.isin(season_months) + season_df = loads_df.loc[season_mask].copy().reset_index() + + period_lookup = period_map.reset_index() + period_lookup.columns = pd.Index(["time", "energy_period"]) + season_df = season_df.merge(period_lookup, on="time", how="left") + + period_consumption = _build_period_consumption(season_df) + p_flat = _compute_equivalent_flat_tariff(period_consumption, period_rate) + + targets = _build_period_shift_targets( + period_consumption, period_rate, elasticity, p_flat, receiver_period=None + ) + + # Distribute period-level shifts to hourly rows proportionally + hourly = season_df.merge( + targets[["bldg_id", "energy_period", "load_shift", "Q_orig"]], + on=["bldg_id", "energy_period"], + how="left", + ) + period_sums = hourly.groupby(["bldg_id", "energy_period"])[ + "electricity_net" + ].transform("sum") + hour_share = np.where( + period_sums != 0, hourly["electricity_net"] / period_sums, 0.0 + ) + hourly["hourly_shift_kw"] = hourly["load_shift"] * hour_share + hourly["electricity_net_shifted"] = ( + hourly["electricity_net"] + hourly["hourly_shift_kw"] + ) + hourly["season"] = season_name + hourly["hour_of_day"] = hourly["time"].dt.hour + hourly["month"] = hourly["time"].dt.month + + rate_map = period_rate.to_dict() + hourly["tou_rate"] = hourly["energy_period"].map(rate_map) + + hourly = hourly.rename(columns={"electricity_net": "electricity_net_orig"}) + result_parts.append( + hourly[ + [ + "bldg_id", + "time", + "electricity_net_orig", + "electricity_net_shifted", + "hourly_shift_kw", + "energy_period", + "hour_of_day", + "month", + "season", + "tou_rate", + ] + ] + ) + + result = pd.concat(result_parts, ignore_index=True).sort_values( + ["bldg_id", "time"] + ) + log.info("Reproduced shift: %d hourly rows", len(result)) + return result + + +# ── Validation checks ───────────────────────────────────────────────────────── + + +def run_validation_checks( + hourly: pd.DataFrame, + season_specs: list[SeasonTouSpec], + period_rate: pd.Series, +) -> dict: + """Run validation checks on the shifted hourly data. Returns summary dict.""" + results: dict = {"energy_conservation": {}, "direction": {}, "magnitude": {}} + p_flat_map: dict[str, float] = {} + + for spec in season_specs: + sn = spec.season.name + sh = hourly[hourly["season"] == sn] + + # Energy conservation: per building + bldg_totals = sh.groupby("bldg_id").agg( + orig_kwh=("electricity_net_orig", "sum"), + shifted_kwh=("electricity_net_shifted", "sum"), + ) + bldg_totals["diff_kwh"] = bldg_totals["shifted_kwh"] - bldg_totals["orig_kwh"] + max_diff = bldg_totals["diff_kwh"].abs().max() + violations = (bldg_totals["diff_kwh"].abs() > 0.01).sum() + results["energy_conservation"][sn] = { + "max_abs_diff_kwh": float(max_diff), + "violations": int(violations), + "pass": violations == 0, + } + + # Direction: peak kWh should decrease, off-peak should increase + rates = period_rate.to_dict() + p_flat = sh.groupby("energy_period").apply( + lambda g: (g["electricity_net_orig"] * g["tou_rate"]).sum() + / g["electricity_net_orig"].sum() + if g["electricity_net_orig"].sum() > 0 + else 0.0, + include_groups=False, + ) + # Use the first building's p_flat as representative + period_consumption_all = sh.groupby("energy_period").agg( + orig=("electricity_net_orig", "sum"), + shifted=("electricity_net_shifted", "sum"), + ) + period_consumption_all["shift"] = ( + period_consumption_all["shifted"] - period_consumption_all["orig"] + ) + + global_p_flat = ( + (period_consumption_all["orig"] * pd.Series(rates)).sum() + / period_consumption_all["orig"].sum() + ) + p_flat_map[sn] = float(global_p_flat) + + direction_ok = True + direction_details = {} + for ep, row in period_consumption_all.iterrows(): + ep_rate = rates.get(int(ep), 0.0) # type: ignore[arg-type] + if ep_rate > global_p_flat: + expected_sign = "decrease" + ok = row["shift"] <= 0.01 + else: + expected_sign = "increase" + ok = row["shift"] >= -0.01 + direction_ok = direction_ok and ok + direction_details[int(ep)] = { # type: ignore[arg-type] + "rate": float(ep_rate), + "is_peak": ep_rate > global_p_flat, + "shift_kwh": float(row["shift"]), + "expected": expected_sign, + "pass": ok, + } + results["direction"][sn] = { + "pass": direction_ok, + "periods": direction_details, + } + + # Magnitude: aggregate peak reduction % + peak_eps = [ + ep + for ep, r in rates.items() + if r > global_p_flat and ep in period_consumption_all.index + ] + if peak_eps: + peak_orig = period_consumption_all.loc[peak_eps, "orig"].sum() + peak_shifted = period_consumption_all.loc[peak_eps, "shifted"].sum() + peak_red_pct = ( + (peak_orig - peak_shifted) / peak_orig * 100 if peak_orig > 0 else 0.0 + ) + else: + peak_red_pct = 0.0 + results["magnitude"][sn] = { + "peak_reduction_pct": float(peak_red_pct), + "ratio": spec.peak_offpeak_ratio, + } + + return results + + +def print_validation_report(checks: dict) -> None: + """Print a human-readable validation report.""" + print("\n" + "=" * 70) + print("DEMAND-FLEX SHIFT VALIDATION") + print("=" * 70) + + for sn in sorted(checks["energy_conservation"]): + ec = checks["energy_conservation"][sn] + status = "PASS" if ec["pass"] else "FAIL" + print(f"\n [{status}] Energy conservation ({sn}):") + print(f" max |orig - shifted| = {ec['max_abs_diff_kwh']:.6f} kWh") + if not ec["pass"]: + print(f" {ec['violations']} buildings exceeded 0.01 kWh tolerance") + + for sn in sorted(checks["direction"]): + dr = checks["direction"][sn] + status = "PASS" if dr["pass"] else "FAIL" + print(f"\n [{status}] Direction check ({sn}):") + for ep, info in sorted(dr["periods"].items()): + peak_label = "PEAK" if info["is_peak"] else "offpk" + ep_status = "ok" if info["pass"] else "FAIL" + print( + f" period {ep} ({peak_label}, ${info['rate']:.4f}/kWh):" + f" shift={info['shift_kwh']:+,.0f} kWh [{ep_status}]" + ) + + for sn in sorted(checks["magnitude"]): + mg = checks["magnitude"][sn] + print( + f"\n Peak reduction ({sn}): {mg['peak_reduction_pct']:.2f}%" + f" (TOU ratio={mg['ratio']:.2f})" + ) + + +# ── Diagnostic plots ────────────────────────────────────────────────────────── + + +def _get_peak_hours(spec: SeasonTouSpec) -> set[int]: + return set(spec.peak_hours) + + +def _season_spec_by_name( + specs: list[SeasonTouSpec], name: str +) -> SeasonTouSpec | None: + for s in specs: + if s.season.name == name: + return s + return None + + +def plot_building_daily_profile( + hourly: pd.DataFrame, + bldg_id: int, + season_specs: list[SeasonTouSpec], + utility: str, + output_dir: Path, +) -> None: + """Plot 1: Per-building daily load profile with TOU overlay for one building.""" + bldg = hourly[hourly["bldg_id"] == bldg_id] + if bldg.empty: + return + + seasons = ["summer", "winter"] + fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharey=True) + + for ax, sn in zip(axes, seasons): + spec = _season_spec_by_name(season_specs, sn) + if spec is None: + continue + peak_hours = _get_peak_hours(spec) + + sdata = bldg[bldg["season"] == sn] + if sdata.empty: + ax.set_title(f"{sn.title()} — no data") + continue + + # Pick a representative weekday (middle of season) + sdata_ts = pd.to_datetime(sdata["time"]) + weekday_mask = sdata_ts.dt.dayofweek < 5 + sdata_wd = sdata[weekday_mask.values] + if sdata_wd.empty: + continue + + dates = sdata_wd["time"].dt.date.unique() + mid_date = dates[len(dates) // 2] + day = sdata_wd[sdata_wd["time"].dt.date == mid_date] + if day.empty: + continue + + hours = day["hour_of_day"].values + orig = day["electricity_net_orig"].values + shifted = day["electricity_net_shifted"].values + rates = day["tou_rate"].values + + # Shade peak hours + for h in range(24): + if h in peak_hours: + ax.axvspan(h - 0.5, h + 0.5, alpha=0.12, color="red", zorder=0) + + ax.plot(hours, orig, "o-", color="#2c3e50", linewidth=1.8, markersize=3, + label="Original", zorder=3) + ax.plot(hours, shifted, "s--", color="#e74c3c", linewidth=1.8, markersize=3, + label="Shifted", zorder=3) + + ax2 = ax.twinx() + ax2.step(hours, rates, where="mid", color="#7f8c8d", linewidth=1.0, + alpha=0.6, label="TOU rate") + ax2.set_ylabel("TOU rate ($/kWh)", color="#7f8c8d") + ax2.tick_params(axis="y", labelcolor="#7f8c8d") + + shift_kwh = float(day["hourly_shift_kw"].sum()) + peak_orig_kwh = float(day.loc[day["hour_of_day"].isin(peak_hours), + "electricity_net_orig"].sum()) + peak_shifted_kwh = float(day.loc[day["hour_of_day"].isin(peak_hours), + "electricity_net_shifted"].sum()) + peak_red = ( + (peak_orig_kwh - peak_shifted_kwh) / peak_orig_kwh * 100 + if peak_orig_kwh > 0 + else 0 + ) + + ax.set_title(f"{sn.title()} ({mid_date})") + ax.set_xlabel("Hour of day") + ax.set_xlim(-0.5, 23.5) + ax.set_xticks(range(0, 24, 3)) + textstr = ( + f"Peak red: {peak_red:.1f}%\n" + f"Net shift: {shift_kwh:+.2f} kWh\n" + f"Ratio: {spec.peak_offpeak_ratio:.2f}" + ) + ax.text( + 0.02, 0.98, textstr, transform=ax.transAxes, fontsize=8, + verticalalignment="top", + bbox={"boxstyle": "round,pad=0.3", "facecolor": "wheat", "alpha": 0.5}, + ) + + axes[0].set_ylabel("Load (kWh)") + axes[0].legend(loc="upper right", fontsize=8) + + fig.suptitle( + f"{utility.upper()} — Building {bldg_id} daily profile", + fontsize=13, fontweight="bold", + ) + fig.tight_layout() + path = output_dir / f"building_daily_profile_{bldg_id}.png" + fig.savefig(path) + plt.close(fig) + log.info("Saved %s", path) + + +def plot_aggregate_daily_profile( + hourly: pd.DataFrame, + season_specs: list[SeasonTouSpec], + utility: str, + output_dir: Path, +) -> None: + """Plot 2: Aggregate average daily load profile by season.""" + fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharey=True) + seasons = ["summer", "winter"] + + for ax, sn in zip(axes, seasons): + spec = _season_spec_by_name(season_specs, sn) + if spec is None: + continue + peak_hours = _get_peak_hours(spec) + + sdata = hourly[hourly["season"] == sn] + if sdata.empty: + continue + + profile = sdata.groupby("hour_of_day").agg( + orig=("electricity_net_orig", "mean"), + shifted=("electricity_net_shifted", "mean"), + ) + hours = profile.index.values + orig = profile["orig"].values + shifted = profile["shifted"].values + + for h in range(24): + if h in peak_hours: + ax.axvspan(h - 0.5, h + 0.5, alpha=0.10, color="red", zorder=0) + + ax.plot(hours, orig, "o-", color="#2c3e50", linewidth=2, markersize=3, + label="Original") + ax.plot(hours, shifted, "s-", color="#e74c3c", linewidth=2, markersize=3, + label="Shifted") + + ax.fill_between( + hours, orig, shifted, + where=orig > shifted, interpolate=True, + alpha=0.25, color="#e74c3c", label="Load removed (peak)", + ) + ax.fill_between( + hours, orig, shifted, + where=shifted > orig, interpolate=True, + alpha=0.25, color="#3498db", label="Load added (off-peak)", + ) + + total_orig_peak = float( + sdata.loc[sdata["hour_of_day"].isin(peak_hours), + "electricity_net_orig"].sum() + ) + total_shifted_peak = float( + sdata.loc[sdata["hour_of_day"].isin(peak_hours), + "electricity_net_shifted"].sum() + ) + n_bldgs = sdata["bldg_id"].nunique() + peak_red = ( + (total_orig_peak - total_shifted_peak) / total_orig_peak * 100 + if total_orig_peak > 0 + else 0 + ) + total_shift = float(sdata["hourly_shift_kw"].sum()) / n_bldgs + + ax.set_title(f"{sn.title()} (mean of {n_bldgs} HP buildings)") + ax.set_xlabel("Hour of day") + ax.set_xlim(-0.5, 23.5) + ax.set_xticks(range(0, 24, 3)) + + textstr = ( + f"Peak red: {peak_red:.1f}%\n" + f"kWh shifted/bldg: {total_shift:+,.1f}\n" + f"Ratio: {spec.peak_offpeak_ratio:.2f}" + ) + ax.text( + 0.02, 0.98, textstr, transform=ax.transAxes, fontsize=8, + verticalalignment="top", + bbox={"boxstyle": "round,pad=0.3", "facecolor": "wheat", "alpha": 0.5}, + ) + + axes[0].set_ylabel("Mean load (kWh)") + axes[0].legend(loc="upper right", fontsize=7) + fig.suptitle( + f"{utility.upper()} — Aggregate daily load profile", + fontsize=13, fontweight="bold", + ) + fig.tight_layout() + path = output_dir / "aggregate_daily_profile.png" + fig.savefig(path) + plt.close(fig) + log.info("Saved %s", path) + + +def plot_net_shift_by_hour( + hourly: pd.DataFrame, + season_specs: list[SeasonTouSpec], + utility: str, + output_dir: Path, +) -> None: + """Plot 3: Net kW shift by hour-of-day, bars colored by sign.""" + fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharey=True) + seasons = ["summer", "winter"] + + for ax, sn in zip(axes, seasons): + spec = _season_spec_by_name(season_specs, sn) + if spec is None: + continue + peak_hours = _get_peak_hours(spec) + + sdata = hourly[hourly["season"] == sn] + if sdata.empty: + continue + + n_bldgs = sdata["bldg_id"].nunique() + mean_shift = sdata.groupby("hour_of_day")["hourly_shift_kw"].mean() + + colors = ["#e74c3c" if v < 0 else "#3498db" for v in mean_shift.values] + ax.bar(mean_shift.index, mean_shift.values, color=colors, width=0.8, + edgecolor="white", linewidth=0.5) + + for h in range(24): + if h in peak_hours: + ax.axvspan(h - 0.5, h + 0.5, alpha=0.06, color="red", zorder=0) + + ax.axhline(0, color="black", linewidth=0.5) + ax.set_title(f"{sn.title()} (mean/building, n={n_bldgs})") + ax.set_xlabel("Hour of day") + ax.set_xlim(-0.5, 23.5) + ax.set_xticks(range(0, 24, 3)) + + net_check = mean_shift.sum() + ax.text( + 0.98, 0.02, + f"Net: {net_check:+.4f} kWh\n(should be ~0)", + transform=ax.transAxes, fontsize=7, ha="right", + bbox={"boxstyle": "round,pad=0.3", "facecolor": "lightyellow", "alpha": 0.7}, + ) + + axes[0].set_ylabel("Mean shift (kWh)") + fig.suptitle( + f"{utility.upper()} — Net load shift by hour", + fontsize=13, fontweight="bold", + ) + fig.tight_layout() + path = output_dir / "net_shift_by_hour.png" + fig.savefig(path) + plt.close(fig) + log.info("Saved %s", path) + + +def plot_shift_heatmap( + hourly: pd.DataFrame, + season_specs: list[SeasonTouSpec], + utility: str, + output_dir: Path, +) -> None: + """Plot 4: Month x hour heatmap of mean kW shift per building.""" + pivot = hourly.groupby(["month", "hour_of_day"])["hourly_shift_kw"].mean() + heatmap_data = pivot.unstack(level="hour_of_day").sort_index() + + fig, ax = plt.subplots(figsize=(14, 5)) + vmax = max(abs(heatmap_data.min().min()), abs(heatmap_data.max().max())) + im = ax.imshow( + heatmap_data.values, + aspect="auto", + cmap="RdBu", + vmin=-vmax, + vmax=vmax, + interpolation="nearest", + ) + + ax.set_yticks(range(len(heatmap_data.index))) + month_names = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ] + ax.set_yticklabels([month_names[m - 1] for m in heatmap_data.index]) + ax.set_xticks(range(24)) + ax.set_xticklabels(range(24), fontsize=8) + ax.set_xlabel("Hour of day") + ax.set_ylabel("Month") + + # Annotate peak hours per season + for spec in season_specs: + peak_hours = _get_peak_hours(spec) + for m in spec.season.months: + row_idx = list(heatmap_data.index).index(m) if m in heatmap_data.index else None + if row_idx is None: + continue + for h in peak_hours: + ax.plot(h, row_idx, "k.", markersize=2, alpha=0.4) + + cbar = fig.colorbar(im, ax=ax, shrink=0.8) + cbar.set_label("Mean shift (kWh/building)") + ax.set_title( + f"{utility.upper()} — Hourly load shift heatmap" + f" (red=removed, blue=added, dots=peak hours)", + fontsize=12, fontweight="bold", + ) + fig.tight_layout() + path = output_dir / "shift_heatmap.png" + fig.savefig(path) + plt.close(fig) + log.info("Saved %s", path) + + +def plot_peak_reduction_distribution( + hourly: pd.DataFrame, + season_specs: list[SeasonTouSpec], + utility: str, + output_dir: Path, +) -> None: + """Plot 5: Histogram of per-building peak reduction % by season.""" + fig, axes = plt.subplots(1, 2, figsize=(14, 5)) + seasons = ["summer", "winter"] + + for ax, sn in zip(axes, seasons): + spec = _season_spec_by_name(season_specs, sn) + if spec is None: + continue + peak_hours = _get_peak_hours(spec) + + sdata = hourly[hourly["season"] == sn] + if sdata.empty: + continue + + peak_data = sdata[sdata["hour_of_day"].isin(peak_hours)] + bldg_peak = peak_data.groupby("bldg_id").agg( + orig=("electricity_net_orig", "sum"), + shifted=("electricity_net_shifted", "sum"), + ) + bldg_peak["reduction_pct"] = np.where( + bldg_peak["orig"] > 0, + (bldg_peak["orig"] - bldg_peak["shifted"]) / bldg_peak["orig"] * 100, + 0.0, + ) + + data_range = ( + bldg_peak["reduction_pct"].max() - bldg_peak["reduction_pct"].min() + ) + n_bins = max(5, min(30, int(data_range / 0.3))) if data_range > 0.01 else 5 + ax.hist( + bldg_peak["reduction_pct"], + bins=n_bins, + color="#3498db", + edgecolor="white", + alpha=0.8, + ) + + mean_red = bldg_peak["reduction_pct"].mean() + median_red = bldg_peak["reduction_pct"].median() + ax.axvline(mean_red, color="#e74c3c", linewidth=2, linestyle="-", + label=f"Mean: {mean_red:.1f}%") + ax.axvline(median_red, color="#2ecc71", linewidth=2, linestyle="--", + label=f"Median: {median_red:.1f}%") + + ax.set_title(f"{sn.title()} (n={len(bldg_peak)} buildings)") + ax.set_xlabel("Peak reduction (%)") + ax.set_ylabel("Count") + ax.legend(fontsize=8) + + textstr = ( + f"Std: {bldg_peak['reduction_pct'].std():.1f}%\n" + f"Range: [{bldg_peak['reduction_pct'].min():.1f}%," + f" {bldg_peak['reduction_pct'].max():.1f}%]" + ) + ax.text( + 0.98, 0.98, textstr, transform=ax.transAxes, fontsize=8, + ha="right", va="top", + bbox={"boxstyle": "round,pad=0.3", "facecolor": "lightyellow", "alpha": 0.7}, + ) + + fig.suptitle( + f"{utility.upper()} — Per-building peak reduction distribution", + fontsize=13, fontweight="bold", + ) + fig.tight_layout() + path = output_dir / "peak_reduction_distribution.png" + fig.savefig(path) + plt.close(fig) + log.info("Saved %s", path) + + +# ── Data outputs ────────────────────────────────────────────────────────────── + + +def write_data_outputs( + hourly: pd.DataFrame, + season_specs: list[SeasonTouSpec], + utility: str, + output_dir: Path, + n_example_buildings: int = 100, +) -> None: + """Write data files for report reuse.""" + # Aggregate hourly profile CSV + agg = ( + hourly.groupby(["season", "hour_of_day"]) + .agg( + mean_load_orig_kw=("electricity_net_orig", "mean"), + mean_load_shifted_kw=("electricity_net_shifted", "mean"), + mean_shift_kw=("hourly_shift_kw", "mean"), + ) + .reset_index() + ) + agg_path = output_dir / f"{utility}_aggregate_hourly_profile.csv" + agg.to_csv(agg_path, index=False) + log.info("Wrote %s", agg_path) + + # Shift heatmap data CSV + hm = ( + hourly.groupby(["month", "hour_of_day"])["hourly_shift_kw"] + .mean() + .reset_index() + ) + hm.columns = ["month", "hour_of_day", "mean_shift_kw"] + hm_path = output_dir / f"{utility}_shift_heatmap_data.csv" + hm.to_csv(hm_path, index=False) + log.info("Wrote %s", hm_path) + + # Per-building peak reduction CSV + reduction_rows = [] + for spec in season_specs: + sn = spec.season.name + peak_hours = _get_peak_hours(spec) + sdata = hourly[hourly["season"] == sn] + peak_data = sdata[sdata["hour_of_day"].isin(peak_hours)] + bldg_peak = peak_data.groupby("bldg_id").agg( + orig=("electricity_net_orig", "sum"), + shifted=("electricity_net_shifted", "sum"), + ) + bldg_peak["reduction_pct"] = np.where( + bldg_peak["orig"] > 0, + (bldg_peak["orig"] - bldg_peak["shifted"]) / bldg_peak["orig"] * 100, + 0.0, + ) + bldg_peak["season"] = sn + reduction_rows.append(bldg_peak.reset_index()) + red_df = pd.concat(reduction_rows, ignore_index=True) + red_path = output_dir / f"{utility}_building_peak_reduction.csv" + red_df.to_csv(red_path, index=False) + log.info("Wrote %s", red_path) + + # Per-building hourly data for a subset + bldg_ids = sorted(hourly["bldg_id"].unique()) + if len(bldg_ids) > n_example_buildings: + rng = np.random.default_rng(42) + bldg_ids = sorted(rng.choice(bldg_ids, size=n_example_buildings, replace=False)) + subset = hourly[hourly["bldg_id"].isin(bldg_ids)] + sub_path = output_dir / f"{utility}_building_hourly_shifted.parquet" + subset.to_parquet(sub_path, index=False) + log.info("Wrote %s (%d buildings)", sub_path, len(bldg_ids)) + + +# ── CAIRO tracker cross-check ──────────────────────────────────────────────── + + +def crosscheck_cairo_tracker( + hourly: pd.DataFrame, + batch: str, + utility: str, + period_rate: pd.Series, + season_specs: list[SeasonTouSpec], + output_dir: Path, +) -> None: + """Download CAIRO elasticity tracker and compare to our analytical results.""" + import subprocess + import tempfile + + s3_prefix = ( + f"s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/{utility}/{batch}/" + ) + + # List run dirs to find the run13 tracker + result = subprocess.run( + ["aws", "s3", "ls", s3_prefix, "--recursive"], + capture_output=True, text=True, timeout=30, + ) + tracker_s3 = None + for line in result.stdout.splitlines(): + if "run13" in line and "demand_flex_elasticity_tracker.csv" in line: + parts = line.strip().split() + key = parts[-1] # relative to bucket root + tracker_s3 = f"s3://data.sb/{key}" + break + + if tracker_s3 is None: + log.warning("Could not find CAIRO tracker for %s/%s", utility, batch) + return + + log.info("Downloading CAIRO tracker: %s", tracker_s3) + with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as tmp: + tmp_path = tmp.name + subprocess.run( + ["aws", "s3", "cp", tracker_s3, tmp_path], + capture_output=True, timeout=30, + ) + cairo_tracker = pd.read_csv(tmp_path) + Path(tmp_path).unlink(missing_ok=True) + + print(f"\n CAIRO tracker cross-check ({utility}, run13):") + print(f" CAIRO tracker: {len(cairo_tracker)} buildings") + print(f" CAIRO columns: {list(cairo_tracker.columns)}") + + # Our analytical tracker: compute per-building per-season-period epsilon + rates = period_rate.to_dict() + our_rows = [] + for spec in season_specs: + sn = spec.season.name + sdata = hourly[hourly["season"] == sn] + if sdata.empty: + continue + + bldg_period = sdata.groupby(["bldg_id", "energy_period"]).agg( + orig=("electricity_net_orig", "sum"), + shifted=("electricity_net_shifted", "sum"), + ).reset_index() + bldg_period["rate"] = bldg_period["energy_period"].map(rates) + + global_p_flat = ( + (sdata["electricity_net_orig"] * sdata["tou_rate"]).sum() + / sdata["electricity_net_orig"].sum() + ) + + valid = ( + (bldg_period["orig"] > 0) + & (bldg_period["shifted"] > 0) + & (bldg_period["rate"] != global_p_flat) + ) + bldg_period["epsilon"] = np.nan + bldg_period.loc[valid, "epsilon"] = np.log( + bldg_period.loc[valid, "shifted"] / bldg_period.loc[valid, "orig"] + ) / np.log(bldg_period.loc[valid, "rate"] / global_p_flat) + + bldg_period["season"] = sn + our_rows.append(bldg_period) + + our_tracker = pd.concat(our_rows, ignore_index=True) + + # Pivot our tracker to match CAIRO format: bldg_id, season_period columns + our_pivot = our_tracker.pivot_table( + index="bldg_id", + columns=["season", "energy_period"], + values="epsilon", + ) + our_pivot.columns = [f"{s}_period_{p}" for s, p in our_pivot.columns] + + # Compare to CAIRO + common_bldgs = set(our_pivot.index) & set(cairo_tracker["bldg_id"]) + if not common_bldgs: + print(" WARNING: no common buildings between analytical and CAIRO tracker") + return + + print(f" Common buildings: {len(common_bldgs)}") + cairo_indexed = cairo_tracker.set_index("bldg_id") + common_cols = set(our_pivot.columns) & set(cairo_indexed.columns) + if not common_cols: + print(f" Our columns: {list(our_pivot.columns)}") + print(f" CAIRO columns: {list(cairo_indexed.columns)}") + print(" WARNING: no matching column names") + return + + diffs = [] + for col in sorted(common_cols): + ours = our_pivot.loc[list(common_bldgs), col].dropna() + theirs = cairo_indexed.loc[list(common_bldgs), col].dropna() + common_idx = ours.index.intersection(theirs.index) + if common_idx.empty: + continue + diff = (ours.loc[common_idx] - theirs.loc[common_idx]).abs() + diffs.append(diff) + print( + f" {col}: max|Δε|={diff.max():.6f}, " + f"mean|Δε|={diff.mean():.6f}, n={len(common_idx)}" + ) + + if diffs: + all_diffs = pd.concat(diffs) + print( + f" Overall: max|Δε|={all_diffs.max():.6f}, " + f"mean|Δε|={all_diffs.mean():.6f}" + ) + + +# ── Main ────────────────────────────────────────────────────────────────────── + + +def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--utility", required=True) + p.add_argument("--state", default="ny", choices=list(STATE_CONFIGS.keys())) + p.add_argument("--elasticity", type=float, default=-0.10) + p.add_argument("--batch", default=None, help="S3 batch for CAIRO cross-check") + p.add_argument("--output-dir", type=Path, required=True) + p.add_argument( + "--n-example-buildings", + type=int, + default=100, + help="Buildings to include in hourly parquet (default 100)", + ) + p.add_argument( + "--n-profile-buildings", + type=int, + default=5, + help="Buildings to generate individual profile plots for (default 5)", + ) + p.add_argument("-v", "--verbose", action="store_true") + return p.parse_args(argv) + + +def main(argv: list[str] | None = None) -> None: + args = _parse_args(argv) + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s %(name)s: %(message)s", + ) + + cfg = STATE_CONFIGS[args.state] + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + project_root = Path(__file__).resolve().parents[2] + + # Load metadata + log.info("Loading HP metadata...") + hp_meta = load_hp_metadata(cfg["path_metadata"], cfg["path_utility_assignment"]) + util_hp = hp_meta.filter(pl.col("sb.electric_utility") == args.utility) + hp_bldg_ids = util_hp["bldg_id"].to_list() + log.info("%d HP buildings for %s", len(hp_bldg_ids), args.utility) + + if not hp_bldg_ids: + log.error("No HP buildings for %s", args.utility) + sys.exit(1) + + # Load TOU context + tou_deriv_dir = project_root / cfg["path_tou_derivation_dir"] + tariffs_dir = project_root / cfg["path_tariffs_electric_dir"] + specs, tou_tariff = load_tou_context(args.utility, tou_deriv_dir, tariffs_dir) + log.info("TOU specs: %s", [(s.season.name, f"ratio={s.peak_offpeak_ratio:.2f}") for s in specs]) + + rate_df = extract_tou_period_rates(tou_tariff) + period_rate = rate_df.groupby("energy_period")["rate"].first() + log.info("Period rates:\n%s", period_rate) + + # Load building loads + log.info("Loading HP building loads...") + loads_df = load_building_loads(hp_bldg_ids, cfg["path_loads_dir"]) + time_idx = pd.DatetimeIndex( + loads_df.index.get_level_values("time").unique().sort_values() + ) + period_map = assign_hourly_periods(time_idx, tou_tariff) + + # Reproduce the shift + log.info("Reproducing demand-flex shift (ε=%.2f)...", args.elasticity) + hourly = reproduce_shift(loads_df, period_rate, period_map, specs, args.elasticity) + + # Validation checks + log.info("Running validation checks...") + checks = run_validation_checks(hourly, specs, period_rate) + print_validation_report(checks) + + # Diagnostic plots + log.info("Generating diagnostic plots...") + + # Plot 2: Aggregate daily profile + plot_aggregate_daily_profile(hourly, specs, args.utility, output_dir) + + # Plot 3: Net shift by hour + plot_net_shift_by_hour(hourly, specs, args.utility, output_dir) + + # Plot 4: Shift heatmap + plot_shift_heatmap(hourly, specs, args.utility, output_dir) + + # Plot 5: Peak reduction distribution + plot_peak_reduction_distribution(hourly, specs, args.utility, output_dir) + + # Plot 1: Per-building profiles for illustrative buildings + annual_load = hourly.groupby("bldg_id")["electricity_net_orig"].sum() + quantiles = annual_load.quantile([0.10, 0.25, 0.50, 0.75, 0.90]) + illustrative_bldgs = [] + for q_val in quantiles.values: + closest = (annual_load - q_val).abs().idxmin() + if closest not in illustrative_bldgs: + illustrative_bldgs.append(closest) + + if len(illustrative_bldgs) < args.n_profile_buildings: + remaining = [b for b in annual_load.index if b not in illustrative_bldgs] + rng = np.random.default_rng(42) + extra = rng.choice( + remaining, + size=min(args.n_profile_buildings - len(illustrative_bldgs), len(remaining)), + replace=False, + ) + illustrative_bldgs.extend(extra) + + for bid in illustrative_bldgs: + plot_building_daily_profile(hourly, int(bid), specs, args.utility, output_dir) + + # Data outputs + log.info("Writing data outputs...") + write_data_outputs( + hourly, specs, args.utility, output_dir, + n_example_buildings=args.n_example_buildings, + ) + + # CAIRO tracker cross-check + if args.batch: + log.info("Cross-checking against CAIRO tracker...") + try: + crosscheck_cairo_tracker( + hourly, args.batch, args.utility, period_rate, specs, output_dir + ) + except Exception as e: + log.warning("CAIRO cross-check failed: %s", e) + + # Validation summary JSON + import json as json_mod + + def _make_serializable(obj: object) -> object: + if isinstance(obj, (np.bool_, np.integer)): + return obj.item() + if isinstance(obj, np.floating): + return float(obj) + if isinstance(obj, dict): + return {k: _make_serializable(v) for k, v in obj.items()} + if isinstance(obj, list): + return [_make_serializable(v) for v in obj] + return obj + + summary_path = output_dir / f"{args.utility}_validation_summary.json" + with open(summary_path, "w") as f: + json_mod.dump( + _make_serializable({ + "utility": args.utility, + "elasticity": args.elasticity, + "n_buildings": len(hp_bldg_ids), + "checks": checks, + }), + f, + indent=2, + ) + log.info("Wrote %s", summary_path) + + all_pass = all( + checks["energy_conservation"][s]["pass"] + and checks["direction"][s]["pass"] + for s in checks["energy_conservation"] + ) + print(f"\n{'=' * 70}") + print(f"Overall: {'ALL CHECKS PASSED' if all_pass else 'SOME CHECKS FAILED'}") + print(f"Plots and data written to: {output_dir}") + print(f"{'=' * 70}") + + +if __name__ == "__main__": + main() From c4006ac4d5e00a613a46dc7605f401c451744746 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 25 Mar 2026 23:35:49 +0000 Subject: [PATCH 04/19] Add elasticity calibration context docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit context/methods/tou_and_rates/demand_flex_elasticity_calibration.md: accessible writeup of the per-utility elasticity calibration methodology, Arcturus 2.0 empirical anchor, two savings mechanisms (rate arbitrage vs RR reduction), results table (ε=-0.10 or -0.12 per utility), and known limitations. context/plans/demand-flex_elasticity_calibration.md: living plan document for the broader demand-flex calibration and validation work. Made-with: Cursor --- .../demand_flex_elasticity_calibration.md | 123 ++++++++ .../demand-flex_elasticity_calibration.md | 264 ++++++++++++++++++ 2 files changed, 387 insertions(+) create mode 100644 context/methods/tou_and_rates/demand_flex_elasticity_calibration.md create mode 100644 context/plans/demand-flex_elasticity_calibration.md diff --git a/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md b/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md new file mode 100644 index 00000000..fdc56fc0 --- /dev/null +++ b/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md @@ -0,0 +1,123 @@ +# Demand-Flex Elasticity Calibration for NY Utilities + +## Overview + +This document describes how we calibrate the demand-flexibility elasticity parameter (epsilon) for each NY utility's TOU rate design. The elasticity controls how much HP customers shift load from peak to off-peak hours in response to TOU price signals. We anchor to empirical evidence from the Arcturus 2.0 meta-analysis. + +## The Arcturus 2.0 empirical anchor + +### What it is + +Arcturus 2.0 (Faruqui et al., 2017) is a meta-analysis of 62 time-varying pricing pilots conducted across North America, Europe, and Australia. Each pilot enrolled residential customers on some form of time-of-use or dynamic pricing and measured how much they reduced peak demand compared to a control group. + +### What they found + +Peak demand reduction is strongly predicted by the peak-to-off-peak price ratio -- the higher the ratio, the more customers shift. They fit regression models separately for pilots with and without enabling technology (smart thermostats, in-home displays, etc.). The "no enabling technology" model, which is the one relevant to us, is: + +$$\text{peak\_reduction} = -0.011 + (-0.065) \times \ln(\text{price\_ratio})$$ + +So at a 2:1 ratio, customers reduced peak demand by about 5.5%. At 4:1, about 10.6%. The relationship is log-linear -- doubling the ratio doesn't double the response; there are diminishing returns. + +### Why "no enabling technology" + +Arcturus separates results into groups based on whether customers had enabling technology. The no-tech group represents customers responding to price signals alone with no automation. This is the conservative baseline. With enabling tech, responses are roughly 2x larger -- but we don't assume HP customers have smart controls, so we anchor to the lower bound. + +### How we use it + +Our model uses a different functional form -- constant elasticity ($Q_{\text{shifted}} = Q \times (P / P_{\text{flat}})^\varepsilon$) rather than Arcturus's log-linear regression. We can't directly equate the two. Instead, we: + +1. Take each utility's actual TOU price ratio +2. Ask Arcturus: "what peak reduction would real customers achieve at this ratio?" +3. Run our constant-elasticity model at a range of epsilon values +4. Pick the epsilon that produces the same peak reduction as Arcturus predicts + +This gives us an empirically grounded elasticity: not a theoretical value, but one that reproduces the aggregate demand response observed in actual pricing pilots at a comparable price ratio. + +### What Arcturus doesn't tell us + +It measures aggregate outcomes across heterogeneous customers -- some shift a lot, some don't shift at all. Our model applies the same epsilon uniformly to every building. The recommended epsilon is therefore an approximation that matches the *average* pilot outcome, not the distribution of individual responses. + +## Diagnostic methodology + +### What varies across utilities + +Each NY utility has different TOU structures derived from their marginal cost profiles: + +- **Peak/off-peak price ratios** range from 1.57 (CenHud winter) to 4.33 (ConEd summer) +- **Peak window widths** are either 3 hours (ConEd, NiMo, NYSEG, RGE) or 5 hours (CenHud, OR, PSEG-LI) +- **HP customer share** ranges from 1.4% (PSEG-LI) to 4.4% (RGE) of weighted customers + +Because HP customers heat with electricity, they consume disproportionately more in winter, when the price ratio is lower. We compute a demand-weighted annual price ratio that reflects this seasonal load distribution, giving us a single Arcturus target per utility. + +### How the diagnostic works + +The diagnostic script (`utils/post/diagnose_demand_flex.py`) does the following for each utility: + +1. Loads the actual hourly electricity consumption for every HP building in the baseline stock (upgrade=00) +2. Loads the TOU derivation data (price ratios, peak hours, base rates per season) +3. At each candidate elasticity (-0.02 through -0.30 in 0.02 steps), applies the same constant-elasticity shifting formula CAIRO uses: $Q_{\text{shifted}} = Q_{\text{orig}} \times (P_{\text{period}} / P_{\text{flat}})^\varepsilon$ +4. Measures the resulting peak reduction percentage +5. Compares to the Arcturus prediction for this utility's price ratio +6. Selects the elasticity whose peak reduction most closely matches Arcturus + +We also compute rate arbitrage savings: the dollars an HP customer saves by consuming less at the peak rate and more at the off-peak rate. + +### Two bill savings mechanisms + +1. **Rate arbitrage** (primary): HP customers on a TOU tariff shift load from expensive peak hours to cheap off-peak hours. Savings = kWh shifted x (peak rate - off-peak rate). This is independent of the revenue requirement change. +2. **RR reduction** (secondary, negligible at delivery level): under frozen residual, shifting reduces total MC, lowering the RR. Delivery MC is nonzero in only 40-113 hours/year, so this effect is effectively zero. + +### Key data + +- **Building loads**: ResStock upgrade=00 HP buildings, local parquet at `/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/load_curve_hourly/state=NY/upgrade=00/` +- **TOU derivation**: `rate_design/hp_rates/ny/config/tou_derivation/{utility}_hp_seasonalTOU_derivation.json` +- **MC data**: `s3://data.sb/switchbox/marginal_costs/ny/{dist_and_sub_tx,bulk_tx}/utility={util}/year=2025/data.parquet` (NOT Cambium -- derived from NYISO LBMP and utility PUC filings) +- **CAIRO ground truth**: `s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/all_utilities/ny_20260325b_r1-16/` + +## Results (March 2026) + +### Recommended elasticities + +| Utility | Recommended ε | Annual savings/HP bldg | Peak window | Annual weighted ratio | Arcturus target | +| ------- | :-----------: | :--------------------: | :---------: | :-------------------: | :-------------: | +| CenHud | -0.12 | $27.58 | 5 hours | 2.14 | 6.1% | +| ConEd | -0.10 | $25.72 | 3 hours | 3.01 | 8.3% | +| NiMo | -0.10 | $12.47 | 3 hours | 2.17 | 6.1% | +| NYSEG | -0.10 | $12.57 | 3 hours | 2.16 | 6.1% | +| OR | -0.12 | $23.60 | 5 hours | 2.17 | 6.1% | +| PSEG-LI | -0.12 | $46.77 | 5 hours | 2.51 | 7.1% | +| RGE | -0.10 | $13.67 | 3 hours | 2.16 | 6.1% | + +### Key takeaways + +1. **Two natural groups**: Utilities with 3-hour peak windows land at ε = -0.10; utilities with 5-hour peak windows land at ε = -0.12. Wider peak windows shift more kWh per unit of epsilon, so less epsilon is needed to match the same Arcturus target. + +2. **Savings are modest but real**: $12-$48 per HP building per year from rate arbitrage alone. This is on top of the much larger savings (~$590 for ConEd) from simply switching to the TOU rate structure in the first place. + +3. **Marginal cost savings are negligible**: Delivery MC is nonzero in only 40-113 hours per year. The frozen-residual RR reduction from load shifting is effectively zero at the delivery level. Bill savings come entirely from the rate structure spread. + +4. **Winter matters less than summer for shifting savings**: Winter price ratios (1.6-2.0) are much lower than summer (2.9-4.3), so the rate spread available for arbitrage is smaller. HP customers' heavy winter load contributes little to shifting savings despite being the season where they consume the most. + +5. **These are conservative estimates**: Arcturus "no enabling technology" represents the low end of observed demand response. With smart thermostats or other enabling technology, larger elasticities would be justified. + +### CAIRO ground truth comparison + +At ε = -0.10 (current setting), CAIRO bill outputs for ConEd show: + +- Run 3+4 (default tariff, no flex): mean elec bill = $2,329/year +- Run 11+12 (TOU, no flex): mean = $1,740/year (TOU structure saves $589) +- Run 15+16 (TOU flex, ε=-0.1): mean = $1,729/year (flex adds $10.51 more savings) + +Note: the $10.51 CAIRO figure is for the counterfactual all-HP world (runs 15+16, upgrade=02). The diagnostic's $25.72 is per HP building in the realistic baseline stock (upgrade=00). These are different scenarios and are not directly comparable. + +## Known limitations + +1. **Uniform vs heterogeneous response**: Arcturus measures aggregate outcomes from diverse customers; our model applies the same epsilon to every HP building. +2. **Functional form mismatch**: Arcturus is log-linear; our model is power-law. The comparison is valid at specific price ratios, not across the full curve. +3. **Annual elasticity is a compromise**: A single epsilon can't perfectly match both winter and summer Arcturus targets simultaneously. We use a demand-weighted annual ratio as the calibration anchor. +4. **Delivery MC is too sparse for meaningful savings**: The frozen-residual channel produces negligible savings because delivery MC is concentrated in ~100 hours/year. Supply MC (NYISO LBMP) would add a broader spread, but supply MCs are zeroed in delivery-only runs. + +## References + +- Faruqui, A., Sergici, S., & Warner, C. (2017). Arcturus 2.0: A meta-analysis of time-varying rates for electricity. *The Electricity Journal*, 30(10), 64-72. +- Simenone, M. et al. (2023). Bill alignment test paper. *Utilities Policy*. diff --git a/context/plans/demand-flex_elasticity_calibration.md b/context/plans/demand-flex_elasticity_calibration.md new file mode 100644 index 00000000..668ffd33 --- /dev/null +++ b/context/plans/demand-flex_elasticity_calibration.md @@ -0,0 +1,264 @@ +--- +name: Demand-flex elasticity calibration +overview: Find the correct elasticity parameter per NY utility by running a fast analytical diagnostic against real TOU structures and MC data, grounded in Arcturus empirical predictions, then validate with targeted CAIRO runs. Results stored at s3://data.sb/switchbox/demand_flex/ with utility partitioning. +todos: + - id: diagnostic-script + content: "Create `utils/post/diagnose_demand_flex.py`: per-utility analytical diagnostic reading real TOU derivation JSONs and MC data, computing demand shift + MC savings + Arcturus comparison for elasticity sweep" + status: in_progress + - id: justfile-recipe + content: Add `diagnose-demand-flex` recipe to shared Justfile + status: pending + - id: run-diagnostic + content: Run the diagnostic for all 7 NY utilities, review results + status: pending + - id: update-ticket + content: Update RDP-175 with evolved scope (per-utility calibration, not just synthetic validation) + status: pending + - id: plan-cairo-sweep + content: Based on diagnostic results, plan the targeted CAIRO sweep (utilities, elasticities, run scope, output paths) + status: pending +isProject: false +--- + +# Demand-Flex Elasticity Calibration: Per-Utility Diagnostic and Sweep + +## Problem statement + +Bill savings under demand flex at `epsilon = -0.1` are very small. We need to: + +1. **Diagnose why** -- is it the elasticity, the MC spread, or both? +2. **Find the right elasticity per utility** -- each utility has different seasonal TOU price ratios, so the same epsilon produces different peak reductions +3. **Quantify the demand shift** -- kWh moved, peak reduction %, seasonal breakdown +4. **Maximize bill savings while remaining realistic** -- anchored to Arcturus "no enabling tech" empirical predictions +5. **Run targeted CAIRO sweeps** to validate and produce final outputs + +**The HP overpaying problem**: HP customers have high winter electricity demand (electric heating) while winter marginal costs are generally lower (capacity costs allocated mainly to summer). Under a flat or default rate structure, they overpay relative to their cost causation in winter. The TOU flex tariff gives them the opportunity to shift load away from peak hours, but: (a) the peak/offpeak spread they can arbitrage is smaller in winter than summer, and (b) we are constrained to a single annual elasticity value in the scenario YAMLs. + +## Key data: TOU price ratios vary widely across utilities + +From the TOU derivation JSONs (`[ny/config/tou_derivation/](rate_design/hp_rates/ny/config/tou_derivation/)`): + + +| Utility | Winter Ratio | Winter Peak Hours | Summer Ratio | Summer Peak Hours | +| -------- | ------------ | ----------------- | ------------ | ----------------- | +| ConEd | 2.0 | 16-18 (3h) | 4.3 | 15-17 (3h) | +| CenHud | 1.6 | 16-20 (5h) | 3.0 | 15-19 (5h) | +| NiMo | 1.8 | 17-19 (3h) | 2.9 | 17-19 (3h) | +| (5 more) | ... | ... | ... | ... | + + +A single `epsilon = -0.1` produces very different peak reductions depending on the ratio. Arcturus "no enabling tech" predictions at these ratios: + +- `peak_reduction = -0.011 + (-0.065) * ln(price_ratio)` +- 1.6:1 -> ~4.2% peak reduction +- 2.0:1 -> ~5.6% +- 3.0:1 -> ~8.2% +- 4.3:1 -> ~10.6% + +## CAIRO model constraints + +From [`context/code/cairo/cairo_demand_flexibility_workflow.md`](context/code/cairo/cairo_demand_flexibility_workflow.md) and [`context/code/cairo/demand_flex_residual_treatment.md`](context/code/cairo/demand_flex_residual_treatment.md): + +- **Constant elasticity**: `Q* = Q * (P_period / P_flat)^epsilon` -- same response for all buildings +- **Single receiver period** per season: all shifted load goes to one off-peak sink +- **Exogenous MC prices**: load shifting changes total MC dollars, not prices +- **Frozen residual**: only the MC component of the RR adjusts; non-MC infrastructure costs held constant +- **Zero-sum**: kWh shifted, not reduced + +There are two distinct bill savings mechanisms: + +1. **RR reduction** (system-level, small): under frozen residual, shifting reduces total MC -> lower RR -> lower rates for all (or for the HP subclass under subclass RR). This is `Sigma_h MC_h * (L_orig_h - L_shifted_h)`. Small because HP share is small. +2. **Rate arbitrage** (per-customer, primary): HP customers on a TOU tariff shift load from expensive peak hours to cheap offpeak hours. Savings = `kWh_shifted * (peak_rate - offpeak_rate)`. This is independent of the RR change -- it's the TOU rate structure creating an incentive. Compared to each utility's current default rate structure, this is where meaningful per-customer savings come from. + +## Tariff calibration feedback loop + +The calibrated tariff customers face is an **output** of the demand-flex process, not a fixed input. The chain: + +```mermaid +flowchart LR + deriv["TOU derivation\n(MC-derived ratios)"] -->|"cp"| flex["_flex.json\n(identical copy)"] + flex -->|"run 13 precalc"| p15["Phase 1.5: shift loads\nusing derivation ratios"] + p15 --> p175["Phase 1.75: recompute\nratios from shifted loads"] + p175 --> cairo["CAIRO precalc:\ncalibrate levels to\nmeet lower RR"] + cairo --> cal["_flex_calibrated.json\n(new ratios + levels)"] + cal -->|"run 15 default"| final["Final bills\n(customers face\ncalibrated tariff)"] +``` + +Example for ConEd (from actual files): + +- **Derivation ratios** (from MC, input to run 13): winter 1.978, summer 4.332 +- **Flex calibrated ratios** (output of run 13, input to run 15): winter 2.014, summer 4.143 +- Summer ratio **compressed by ~4.4%** because shifting load out of peak reduced the demand-weighted peak MC +- Summer peak rate dropped from $0.318 to $0.299/kWh (lower RR + compressed ratio) + +**Implications for the sweep:** + +1. The analytical diagnostic uses derivation ratios (first-order); actual customer outcomes use calibrated ratios (second-order feedback ~5%). Good enough for targeting. +2. The CAIRO sweep **must run the full 13 -> copy-calibrated -> 15 chain** per elasticity, not just run 13 alone. Runs 15-16 produce the actual customer bills. +3. The diagnostic should predict how much Phase 1.75 compresses the ratio at each elasticity (larger elasticity = more shift = more compression). + +## Architecture: two phases + +```mermaid +flowchart TD + subgraph phase1 ["Phase 1: Analytical diagnostic (no CAIRO)"] + TOU["TOU derivation JSONs\n(7 utilities x 2 seasons)"] --> diag["diagnose_demand_flex.py"] + MC["MC data from S3\n(bulk + dist per utility)"] --> diag + ARC["Arcturus model\n(no-tech coefficients)"] --> diag + diag --> table["Per-utility table:\nprice ratios, peak reduction %,\nMC spread, savings ceiling,\nrecommended epsilon"] + diag --> csv["CSV at s3://data.sb/\nswitchbox/demand_flex/\ndiagnostic/"] + end + + subgraph phase2 ["Phase 2: Targeted CAIRO sweep"] + table --> sweep["Run CAIRO at recommended\nepsilon per utility\n(+/- 0.05 bracket)"] + sweep --> outputs["Per-run outputs at\ns3://data.sb/switchbox/\ndemand_flex/{utility}/"] + outputs --> compare["Compare CAIRO bills/BAT\nvs analytical predictions"] + end +``` + + + +## Phase 1: Diagnostic on real load data (`utils/post/diagnose_demand_flex.py`) + +New script. Uses actual ResStock building loads, customer metadata, and MC data for all 7 NY utilities. Focuses on the **runs 13-14 scenario** (precalc with HP/non-HP subclasses), which reflects the realistic world where only HP buildings (`postprocess_group.has_hp=True`) are on the TOU flex tariff and subject to demand shifting. HP buildings are a small fraction of each utility's customer base -- this is the primary reason bill savings are small. + +Runs 15-16 model a counterfactual 100% HP adoption world; calibration should target runs 13-14. + +For each of the 7 NY utilities, for each season, for each elasticity in `[-0.05, -0.10, -0.15, -0.20]`: + +**Demand shift metrics (per utility, per season, per elasticity):** + +- HP building count and weighted customer share (% of utility total) +- HP peak period kWh before and after shift +- Peak reduction (% of HP peak kWh) -- the Arcturus-comparable metric +- kWh shifted peak-to-offpeak (absolute) +- System-level peak impact (% of total utility peak, dampened by HP share) +- Demand-weighted annual price ratio (weighted by HP seasonal kWh; used as the Arcturus calibration anchor since we are constrained to a single annual elasticity) +- Per-season Arcturus peak reduction at per-season price ratios (informational, not used for calibration) +- Annual Arcturus peak reduction at the demand-weighted annual ratio (calibration target) +- Delta: our model's prediction vs Arcturus (identifies over/under-response) +- HP-specific effective TOU ratios vs derivation ratios (flags if derivation used system-wide loads that differ from HP-only loads) + +**Financial metrics (two distinct savings mechanisms):** + +*Rate arbitrage savings (primary, per-HP-customer):* +- kWh shifted from peak to offpeak per HP customer (demand-weighted average) +- Bill savings from rate structure = kWh_shifted x (peak_rate - offpeak_rate) per season +- Annual per-HP-customer bill savings from rate arbitrage +- This uses the TOU derivation rates, not calibrated rates (first-order; CAIRO calibration adjusts these) +- Comparison baseline: the current default rate structure for each utility (varies by utility) + +*RR reduction savings (secondary, system-level):* +- HP subclass MC delta ($) = sum_h(MC_h x shift_h) for HP buildings only (demand-weighted) +- Per-HP-customer RR savings = HP_MC_delta / HP_weighted_count (under subclass RR) +- This is small because HPs are a small share of all customers + +*MC data context (NOT Cambium -- all derived from NYISO LBMP and utility PUC filings):* +- NY uses 5 utility-specific MC components: supply energy/NYISO LBMP (most hours nonzero), supply capacity (96 hours/year), supply ancillary, bulk TX (~80 SCR hours/year), dist+sub-TX (~100 PoP hours/year) +- **Delivery-only runs (13) zero out supply MCs** (`zero.parquet`). Only dist+sub-tx + bulk_tx are active -- extremely peaky, nonzero in only ~100-180 hours. MC-based RR savings from shifting are small because the spread is so concentrated. +- **Delivery+supply runs (14)** include supply energy (NYISO LBMP, nonzero virtually all hours) -- broader MC spread, larger MC savings possible. +- The diagnostic should report nonzero-MC hours and their overlap with TOU peak hours per utility, separately for delivery-only and delivery+supply MC stacks. +- For **rate arbitrage savings** (the primary savings mechanism), the MC stack is irrelevant -- savings come from the peak/offpeak rate spread in the tariff structure itself. + +**Tariff feedback metrics (predicting Phase 1.75 effects):** + +- Predicted post-shift TOU cost-causation ratio per season (Phase 1.75 compression) +- Demand-weighted P_flat computed from actual HP building loads + +**Calibration output:** + +- Recommended elasticity per utility: the epsilon where our model's HP peak reduction matches the Arcturus "no tech" prediction for that utility's seasonal price ratio +- Per-HP-customer bill savings at the recommended epsilon +- Binding constraint per utility: is it the MC spread, the HP share, or the peak volume that limits savings? + +**Data sources (all local or S3, no CAIRO run needed):** + +- **Utility assignment**: `/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata_utility/state=NY/utility_assignment.parquet` -- maps bldg_id to utility +- **Customer metadata**: `/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata/state=NY/upgrade=00/metadata-sb.parquet` -- has `postprocess_group.has_hp`, `weight` +- **HP building hourly loads**: `/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/load_curve_hourly/state=NY/upgrade=00/{bldg_id}_00.parquet` -- local disk, fast reads +- **TOU derivation JSONs**: local at [`ny/config/tou_derivation/`](rate_design/hp_rates/ny/config/tou_derivation/) -- seasonal price ratios and peak hours +- **Delivery MC**: `s3://data.sb/switchbox/marginal_costs/ny/{dist_and_sub_tx,bulk_tx}/utility={util}/year=2025/data.parquet` -- 8760 hourly $/kWh (peaky, ~100-180 nonzero hours) +- **Supply MC** (for delivery+supply analysis only): `s3://data.sb/switchbox/marginal_costs/ny/supply/{energy,capacity}/utility={util}/year=2025/data.parquet` -- NYISO LBMP + capacity; zeroed out in delivery-only runs via `zero.parquet` + +**CLI:** + +``` +uv run python -m utils.post.diagnose_demand_flex \ + --state ny \ + --elasticities -0.05,-0.10,-0.15,-0.20 \ + [--output-dir /tmp/demand_flex_diagnostic] \ + [--sample-size N] # optional: limit HP buildings per utility for faster dev iteration +``` + +**Key implementation details:** + +- Loads utility_assignment + metadata once; groups HP bldg_ids by utility +- Per utility: loads HP building hourly loads from local parquet (e.g. ~2k HP buildings for ConEd, fast from local disk) +- Computes demand-weighted `P_flat` from actual HP loads (same as CAIRO's endogenous computation) +- Applies `process_residential_hourly_demand_response_shift` or equivalent constant-elasticity formula per season +- Loads MC data (dist+sub-tx + bulk_tx) per utility from S3 +- Computes HP subclass MC delta = sum_h(MC_h x HP_load_shift_h) using demand-weighted HP system load +- Applies Arcturus formula: `peak_red = -0.011 + (-0.065) * ln(ratio)` for comparison +- Runs the actual constant-elasticity shifting math at each sweep elasticity, computes realized peak reduction, and interpolates the elasticity whose peak reduction best matches Arcturus (not a simple algebraic back-solve, which is imprecise given the multi-period, zero-sum model with endogenous P_flat) + +## Empirical ground truth from batch ny_20260325b_r1-16 + +CAIRO outputs at `s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/all_utilities/ny_20260325b_r1-16/` provide actual bill numbers at `epsilon = -0.1` for all 7 utilities. Key finding for ConEd: + +- Run 3+4 (default tariff, no flex): mean elec bill = $2,329/year +- Run 11+12 (TOU, no flex): mean = $1,740/year (TOU structure saves $589) +- Run 15+16 (TOU flex, epsilon=-0.1): mean = $1,729/year (flex adds only $10.51 more savings) + +The demand-flex effect at epsilon=-0.1 is ~0.6% of the bill -- confirming the "small savings" concern. The TOU structure change dominates. The diagnostic should: +1. Reproduce this $10.51 analytically (validation) +2. Show how it scales with larger elasticity (sweep) +3. Identify the ceiling (is it MC spread, HP share, or both?) + +The bill comparison is: **run 15+16 vs run 11+12** (flex effect alone) and **run 15+16 vs run 3+4** (total effect). + +## Phase 2: CAIRO sweep (after Phase 1 results reviewed) + +Deferred until Phase 1 results are reviewed. The plan: + +1. **Generate sweep scenario YAMLs**: For each utility, create run definitions at the Phase 1-recommended elasticity (and +/- 0.05 bracket = 3 values per utility). Could be a script that patches the existing `scenarios_*.yaml` or generates separate sweep YAMLs. Alternatively, add an `--elasticity` CLI override to `run_scenario.py` (small change: add to `_parse_args`, override in `_resolve_settings`). + +2. **Run scope per elasticity**: Minimum = **runs 13 + 15** (precalc then default, delivery-only). Run 13 produces the calibrated tariff; run 15 produces the actual customer bills against that tariff. The full `run-13 -> copy-calibrated-tariff -> run-15` chain must execute per elasticity. Supply runs (14, 16) only needed for the final chosen elasticity. + +3. **Output location**: `s3://data.sb/switchbox/demand_flex/ny/{utility}/epsilon={value}/` with Hive-style elasticity partitioning. + +4. **Justfile recipe**: `sweep-demand-flex-elasticity` that takes a utility and elasticity list, runs the 13+15 chain at each value. + +5. **Diagnostic comparison**: After sweep, compare CAIRO bill outputs against Phase 1 analytical predictions. Key metrics to compare: peak reduction %, calibrated ratio compression, per-customer bill savings. The ratio compression from Phase 1.75 is a useful validation point -- the diagnostic can predict it analytically and the CAIRO sweep produces it empirically. + +6. **Compute budget**: 7 utilities x 3 elasticities x 2 runs (13+15) = 42 runs. At ~20 min/run for ConEd (smaller utilities faster), total ~6-10 hours. Parallelizable in delivery/supply pairs per the existing `run-all-parallel-tracks` pattern. + +## Files to create/modify + +- **Create**: `[utils/post/diagnose_demand_flex.py](utils/post/diagnose_demand_flex.py)` -- Phase 1 diagnostic +- **Modify**: `[rate_design/hp_rates/Justfile](rate_design/hp_rates/Justfile)` -- add `diagnose-demand-flex` recipe +- **Update**: Linear ticket RDP-175 with evolved scope + +## Relationship to existing RDP-175 work + +The existing validation/sensitivity scripts (`[utils/post/validate_demand_flex.py](utils/post/validate_demand_flex.py)`, `[utils/post/sensitivity_demand_flex.py](utils/post/sensitivity_demand_flex.py)`) verify mathematical invariants on synthetic data. This diagnostic is complementary -- it operates on real utility configurations and MC data to answer the calibration question. The validation scripts still need the API fix (PR #379 breakage) but that is independent work. + +## Known limitations and caveats + +These are explicitly acknowledged; the diagnostic output should note them. + +1. **Arcturus measures aggregate heterogeneous response; our model applies uniform elasticity.** Arcturus coefficients come from meta-analysis of real TOU pilots where customers have diverse responses. Our model applies the same epsilon to every HP building. The recommended elasticity is therefore an approximation: it targets the same aggregate peak reduction as Arcturus predicts, but the underlying behavioral assumption is different. This should be stated in the diagnostic output. + +2. **Arcturus functional form (log-linear) differs from our model (power law).** Arcturus: `peak_red = a + b * ln(ratio)`. Our model: `Q* = Q * (ratio)^epsilon`. These only agree at specific price ratios, not across the full curve. The diagnostic compares at each utility's actual ratio, which is valid; it does not claim to reproduce the full Arcturus response curve. + +3. **Annual elasticity is a compromise.** Each utility has different winter and summer ratios, but we use a single annual epsilon. The diagnostic computes a demand-weighted annual ratio (weighted by HP seasonal kWh) as the Arcturus anchor. Per-season peak reductions are reported for context -- they will differ from the annual prediction because the same epsilon applied to different seasonal ratios produces different reductions. Winter ratios are lower, so winter peak reduction will be smaller. + +4. **ResStock data I/O.** Loading hourly loads for all HP buildings per utility requires reading hundreds to thousands of local parquet files. This is fast from local disk (~seconds) but the data must be present locally. The `--sample-size` CLI option allows faster development iteration by sampling HP buildings. + +5. **Demand shift is identical in delivery vs supply precalc runs.** The `_flex.json` tariff rates are identical between delivery and supply runs; shifting is driven by tariff rates, not MC values. The MC zeroing only affects Phase 1.75 ratio recomputation and Phase 2 RR calibration. The diagnostic therefore computes one shift per elasticity, then branches into delivery-only vs delivery+supply MC savings. + +## Open questions for Phase 2 + +- For the sweep, each elasticity re-runs precalc (run 13) which recalibrates via Phase 1.75. The pre-calibrated `_flex.json` is always the same (copy of the derivation tariff). Only the calibrated output changes. So the sweep is self-contained per elasticity value. +- How to manage 42 calibrated tariff files? One approach: write to a sweep-specific output directory (not the main `config/tariffs/electric/`), parameterized by elasticity, so sweep artifacts don't pollute the main config. +- Should we sweep delivery+supply runs (13+14 -> 15+16) or delivery-only (13 -> 15)? Delivery-only gives the delivery bill savings; adding supply doubles compute time. Recommend delivery-only for the sweep, full 4-run chain only for the final chosen elasticity. + From 7055076da9fd45d2e51668a94686ce20f04c7491 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 25 Mar 2026 23:35:55 +0000 Subject: [PATCH 05/19] Add demand-flex Justfile recipes and update context index rate_design/hp_rates/ny/Justfile: add validate-demand-flex, validate-demand-flex-all, and diagnose-demand-flex recipes for running the shift validation and elasticity sweep diagnostic from the command line. context/README.md: index the new demand_flex_elasticity_calibration.md context document. Made-with: Cursor --- context/README.md | 5 +++-- rate_design/hp_rates/ny/Justfile | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/context/README.md b/context/README.md index 3aa2a78d..ccc36b94 100644 --- a/context/README.md +++ b/context/README.md @@ -58,8 +58,9 @@ Cost-reflective TOU design: theory and window optimization. | File | Purpose | | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| cost_reflective_tou_rate_design.md | Theory and practice of cost-reflective TOU rate design: demand-weighted MC averages, cost-causation ratios, period selection, assumptions, demand flexibility implications, and partial vs. general equilibrium | -| tou_window_optimization.md | TOU window width ($N$) sweep: welfare-loss metric derivation, HP-demand weighting proof, sweep algorithm, CLI/Justfile, NY results | +| cost_reflective_tou_rate_design.md | Theory and practice of cost-reflective TOU rate design: demand-weighted MC averages, cost-causation ratios, period selection, assumptions, demand flexibility implications, and partial vs. general equilibrium | +| tou_window_optimization.md | TOU window width ($N$) sweep: welfare-loss metric derivation, HP-demand weighting proof, sweep algorithm, CLI/Justfile, NY results | +| demand_flex_elasticity_calibration.md | Per-utility elasticity calibration: Arcturus 2.0 meta-analysis anchor, diagnostic methodology, results table (ε = -0.10 or -0.12 per utility), two savings mechanisms, known limitations | ### methods/marginal_costs/ diff --git a/rate_design/hp_rates/ny/Justfile b/rate_design/hp_rates/ny/Justfile index 106304ad..a3b37015 100644 --- a/rate_design/hp_rates/ny/Justfile +++ b/rate_design/hp_rates/ny/Justfile @@ -252,3 +252,41 @@ validate-all-runs-ny timestamp="": just validate-or "{{ timestamp }}" just validate-psegli "{{ timestamp }}" just validate-rge "{{ timestamp }}" + +# ============================================================================= +# NY-only: demand-flex diagnostics +# ============================================================================= + +path_flex_plots := path_repo / "dev_plots/flex" + +# Validate demand-flex load shifting for one utility: reproduces the shift +# analytically, checks energy conservation and direction, cross-checks against +# CAIRO's elasticity tracker, and generates diagnostic plots. +# just -f ny/Justfile validate-demand-flex coned +# just -f ny/Justfile validate-demand-flex coned -0.12 ny_20260325b_r1-16 +validate-demand-flex utility_arg elasticity="-0.10" batch="": + cd {{ project_root }} && uv run python -m utils.post.validate_demand_flex_shift \ + --utility {{ utility_arg }} \ + --elasticity {{ elasticity }} \ + --output-dir "{{ path_flex_plots }}/{{ utility_arg }}" \ + {{ if batch != "" { "--batch " + batch } else { "" } }} + +validate-demand-flex-all elasticity="-0.10" batch="": + just validate-demand-flex cenhud {{ elasticity }} "{{ batch }}" + just validate-demand-flex coned {{ elasticity }} "{{ batch }}" + just validate-demand-flex nimo {{ elasticity }} "{{ batch }}" + just validate-demand-flex nyseg {{ elasticity }} "{{ batch }}" + just validate-demand-flex or {{ elasticity }} "{{ batch }}" + just validate-demand-flex psegli {{ elasticity }} "{{ batch }}" + just validate-demand-flex rge {{ elasticity }} "{{ batch }}" + +# Run the elasticity sweep diagnostic: tests a range of elasticity values per +# utility and finds the one matching the Arcturus 2.0 empirical target. +# just -f ny/Justfile diagnose-demand-flex +# just -f ny/Justfile diagnose-demand-flex --utilities=coned,nimo +diagnose-demand-flex *args: + cd {{ project_root }} && uv run python -m utils.post.diagnose_demand_flex \ + --state ny \ + --elasticities="-0.02,-0.04,-0.06,-0.08,-0.10,-0.12,-0.14,-0.16,-0.18,-0.20" \ + --output-dir "{{ path_flex_plots }}/diagnostic" \ + {{ args }} From 6dbcf2124e7c44557ebfb4c9e07e04f676d27a3b Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 25 Mar 2026 23:40:17 +0000 Subject: [PATCH 06/19] Update demand-flex context doc and plan with validation results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit context/methods/tou_and_rates/demand_flex_elasticity_calibration.md: - Add Validation section: all-utilities pass results at ε=-0.10 (energy conservation, direction, CAIRO tracker cross-check) - Add per-utility peak reduction table at ε=-0.10 - Document the five diagnostic plot types and what they show - Add invoke instructions for the new Justfile recipes context/plans/demand-flex_elasticity_calibration.md: - Update todos to reflect Phase 1 completion (diagnostic script, validation script, Justfile recipes, results review all done) - Add status table at the top of the plan body - Add pending items: scenario YAML update, optional Phase 2 sweep Made-with: Cursor --- .../demand_flex_elasticity_calibration.md | 61 ++++++++++++++++++- .../demand-flex_elasticity_calibration.md | 42 ++++++++++--- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md b/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md index fdc56fc0..8964c3c8 100644 --- a/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md +++ b/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md @@ -43,7 +43,7 @@ It measures aggregate outcomes across heterogeneous customers -- some shift a lo Each NY utility has different TOU structures derived from their marginal cost profiles: -- **Peak/off-peak price ratios** range from 1.57 (CenHud winter) to 4.33 (ConEd summer) +- **Peak/off-peak price ratios** range from 1.57 (CenHud/OR winter) to 4.33 (ConEd summer) - **Peak window widths** are either 3 hours (ConEd, NiMo, NYSEG, RGE) or 5 hours (CenHud, OR, PSEG-LI) - **HP customer share** ranges from 1.4% (PSEG-LI) to 4.4% (RGE) of weighted customers @@ -55,7 +55,7 @@ The diagnostic script (`utils/post/diagnose_demand_flex.py`) does the following 1. Loads the actual hourly electricity consumption for every HP building in the baseline stock (upgrade=00) 2. Loads the TOU derivation data (price ratios, peak hours, base rates per season) -3. At each candidate elasticity (-0.02 through -0.30 in 0.02 steps), applies the same constant-elasticity shifting formula CAIRO uses: $Q_{\text{shifted}} = Q_{\text{orig}} \times (P_{\text{period}} / P_{\text{flat}})^\varepsilon$ +3. At each candidate elasticity (-0.02 through -0.20 in 0.02 steps), applies the same constant-elasticity shifting formula CAIRO uses: $Q_{\text{shifted}} = Q_{\text{orig}} \times (P_{\text{period}} / P_{\text{flat}})^\varepsilon$ 4. Measures the resulting peak reduction percentage 5. Compares to the Arcturus prediction for this utility's price ratio 6. Selects the elasticity whose peak reduction most closely matches Arcturus @@ -110,9 +110,64 @@ At ε = -0.10 (current setting), CAIRO bill outputs for ConEd show: Note: the $10.51 CAIRO figure is for the counterfactual all-HP world (runs 15+16, upgrade=02). The diagnostic's $25.72 is per HP building in the realistic baseline stock (upgrade=00). These are different scenarios and are not directly comparable. +## Validation (March 2026) + +The shift mechanics were validated against CAIRO's own outputs using `utils/post/validate_demand_flex_shift.py`. The script reproduces the constant-elasticity shift analytically using the same `utils/cairo.py` functions CAIRO uses, then checks three invariants. + +### Validation results at ε = -0.10 + +All 7 NY utilities pass all checks: + +| Check | Result | +| ----- | ------ | +| Energy conservation (per building, per season) | PASS -- max \|orig − shifted\| = 0.000 kWh (machine precision) | +| Direction (peak kWh ↓, off-peak kWh ↑) | PASS -- all utilities, both seasons | +| CAIRO tracker match (per-building realized ε) | max \|Δε\| < 0.015, mean \|Δε\| < 0.003 across all utilities | + +The small tracker differences appear only on the **receiver (off-peak) period**, where CAIRO's zero-sum residual differs slightly from our analytical reproduction due to floating-point accumulation order. Donor (peak) period epsilons match CAIRO exactly to machine precision. + +### Peak reduction at ε = -0.10 across utilities + +| Utility | Summer ratio | Summer peak red | Winter ratio | Winter peak red | +| ------- | :----------: | :-------------: | :----------: | :-------------: | +| CenHud | 3.05 | 6.43% | 1.57 | 3.35% | +| ConEd | 4.33 | 9.83% | 1.98 | 5.44% | +| NiMo | 2.91 | 7.67% | 1.75 | 4.60% | +| NYSEG | 2.93 | 7.68% | 1.75 | 4.60% | +| OR | 3.13 | 6.56% | 1.57 | 3.29% | +| PSEG-LI | 3.80 | 7.40% | 1.67 | 3.69% | +| RGE | 2.87 | 7.54% | 1.75 | 4.58% | + +### What the diagnostic plots show + +Five plots per utility are written to `dev_plots/flex/{utility}/`: + +- **`aggregate_daily_profile.png`**: Mean kW per HP building by hour of day, pre/post shift, for summer and winter. The shaded fill between curves shows where load is removed (red, peak hours) and where it accumulates (blue, off-peak). The shift is visually clear in summer (larger ratio, wider spread); winter shifting is smaller. + +- **`net_shift_by_hour.png`**: Bar chart of mean kWh change per building per hour. Red bars during peak hours, blue bars during off-peak. Bars sum to zero within each season (energy conservation). The plot immediately confirms the model is moving load in the right direction. + +- **`shift_heatmap.png`**: Month × hour heatmap of mean kWh shift per building (red = removed, blue = added). Shows the full seasonal and diurnal pattern: summer months (Jun-Sep) have the darkest red in peak hours; winter months have a shallower but still visible shift. The transition months (Apr, Oct) are visually distinct from both seasons, reflecting which season's TOU schedule applies. + +- **`peak_reduction_distribution.png`**: Histogram of per-building peak reduction %. At ε = -0.10, the distribution collapses to a single value with near-zero variance -- because constant elasticity with a uniform rate ratio produces the same proportional reduction for every building regardless of their load level. This is a known limitation of the model. + +- **`building_daily_profile_{bldg_id}.png`**: Per-building view for 5 illustrative buildings at different consumption percentiles. Shows original and shifted load curves overlaid on a representative summer and winter weekday, with TOU rate structure as a step function on the right axis. Suitable as a report visual; per-building hourly data for 100 buildings is written to `{utility}_building_hourly_shifted.parquet` for further use. + +### Invoking the validation + +```bash +# One utility with CAIRO tracker cross-check: +just -f rate_design/hp_rates/ny/Justfile validate-demand-flex coned -0.10 ny_20260325b_r1-16 + +# All utilities: +just -f rate_design/hp_rates/ny/Justfile validate-demand-flex-all -0.10 ny_20260325b_r1-16 + +# Elasticity sweep diagnostic: +just -f rate_design/hp_rates/ny/Justfile diagnose-demand-flex +``` + ## Known limitations -1. **Uniform vs heterogeneous response**: Arcturus measures aggregate outcomes from diverse customers; our model applies the same epsilon to every HP building. +1. **Uniform vs heterogeneous response**: Arcturus measures aggregate outcomes from diverse customers; our model applies the same epsilon to every HP building. The peak reduction distribution confirms this -- all buildings get the same % reduction, which is unrealistic. 2. **Functional form mismatch**: Arcturus is log-linear; our model is power-law. The comparison is valid at specific price ratios, not across the full curve. 3. **Annual elasticity is a compromise**: A single epsilon can't perfectly match both winter and summer Arcturus targets simultaneously. We use a demand-weighted annual ratio as the calibration anchor. 4. **Delivery MC is too sparse for meaningful savings**: The frozen-residual channel produces negligible savings because delivery MC is concentrated in ~100 hours/year. Supply MC (NYISO LBMP) would add a broader spread, but supply MCs are zeroed in delivery-only runs. diff --git a/context/plans/demand-flex_elasticity_calibration.md b/context/plans/demand-flex_elasticity_calibration.md index 668ffd33..e57ccde3 100644 --- a/context/plans/demand-flex_elasticity_calibration.md +++ b/context/plans/demand-flex_elasticity_calibration.md @@ -4,24 +4,50 @@ overview: Find the correct elasticity parameter per NY utility by running a fast todos: - id: diagnostic-script content: "Create `utils/post/diagnose_demand_flex.py`: per-utility analytical diagnostic reading real TOU derivation JSONs and MC data, computing demand shift + MC savings + Arcturus comparison for elasticity sweep" - status: in_progress - - id: justfile-recipe - content: Add `diagnose-demand-flex` recipe to shared Justfile - status: pending + status: completed + - id: shift-validation-script + content: "Create `utils/post/validate_demand_flex_shift.py`: reproduce shift analytically, check energy conservation + direction, cross-check vs CAIRO tracker, produce diagnostic plots and data outputs" + status: completed + - id: justfile-recipes + content: "Add `validate-demand-flex`, `validate-demand-flex-all`, `diagnose-demand-flex` recipes to ny/Justfile" + status: completed - id: run-diagnostic - content: Run the diagnostic for all 7 NY utilities, review results - status: pending + content: Run the diagnostic and validation for all 7 NY utilities, review results + status: completed - id: update-ticket - content: Update RDP-175 with evolved scope (per-utility calibration, not just synthetic validation) + content: Update RDP-175 / open PR #382 with full scope + status: completed + - id: update-scenario-yamls + content: "Update scenario YAMLs with per-utility recommended elasticities (ε=-0.10 for 3h peak window utilities, ε=-0.12 for 5h)" status: pending - id: plan-cairo-sweep - content: Based on diagnostic results, plan the targeted CAIRO sweep (utilities, elasticities, run scope, output paths) + content: "Optional: targeted CAIRO sweep at recommended elasticities to produce final calibrated outputs; see Phase 2 section below" status: pending isProject: false --- # Demand-Flex Elasticity Calibration: Per-Utility Diagnostic and Sweep +## Status (March 2026) + +**Phase 1 complete.** All analytical work is done and validated. PR #382 closed the GitHub issue. + +| Deliverable | Status | +| ----------- | ------ | +| `utils/post/diagnose_demand_flex.py` -- elasticity sweep diagnostic | Done | +| `utils/post/validate_demand_flex_shift.py` -- shift validation + diagnostic plots | Done | +| `rate_design/hp_rates/ny/Justfile` -- `validate-demand-flex`, `diagnose-demand-flex` recipes | Done | +| `context/methods/tou_and_rates/demand_flex_elasticity_calibration.md` -- methodology writeup | Done | +| Per-utility recommended elasticities (7 utilities) | Done -- see results table | +| Shift validation across all 7 utilities (energy conservation, direction, CAIRO tracker match) | Done -- all pass | +| Diagnostic plots in `dev_plots/flex/{utility}/` | Done | +| Update scenario YAMLs with per-utility elasticities | **Pending** | +| Phase 2 CAIRO sweep at recommended elasticities | Optional / deferred | + +**Recommended elasticities (pending YAML update):** +- ε = -0.10: ConEd, NiMo, NYSEG, RGE (3-hour peak windows) +- ε = -0.12: CenHud, OR, PSEG-LI (5-hour peak windows) + ## Problem statement Bill savings under demand flex at `epsilon = -0.1` are very small. We need to: From 185519cc81451e8c179fbb9429ef893012b930a8 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:38:56 +0000 Subject: [PATCH 07/19] Add seasonal elasticity support to demand-flex runtime utils/cairo.py and utils/demand_flex.py now accept a season-keyed dict for the elasticity parameter in addition to a scalar. The shifting pipeline resolves the per-season value inside _shift_season so summer and winter can have independently calibrated epsilons without any structural change to the call sites. Made-with: Cursor --- utils/cairo.py | 17 +++++++++++++---- utils/demand_flex.py | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/utils/cairo.py b/utils/cairo.py index 1398479e..007226ab 100644 --- a/utils/cairo.py +++ b/utils/cairo.py @@ -979,7 +979,7 @@ def apply_runtime_tou_demand_response( raw_load_elec: pd.DataFrame, tou_bldg_ids: list[int], tou_tariff: dict, - demand_elasticity: float, + demand_elasticity: float | dict[str, float], season_specs: list | None = None, *, inplace: bool = False, @@ -990,7 +990,8 @@ def apply_runtime_tou_demand_response( raw_load_elec: Full electric load DataFrame indexed by `(bldg_id, time)`. tou_bldg_ids: Building IDs assigned to the TOU tariff. tou_tariff: URDB v7 tariff dictionary. - demand_elasticity: Constant demand elasticity parameter. + demand_elasticity: Constant demand elasticity parameter, or a + ``{season_name: elasticity}`` dict for per-season values. season_specs: Optional season definitions for seasonal slicing. inplace: If True, mutate *raw_load_elec* directly instead of copying. The caller is responsible for passing an already-copied DataFrame. @@ -1048,6 +1049,14 @@ def apply_runtime_tou_demand_response( def _shift_season(season_name: str, season_months: set[int]) -> None: """Shift one season's TOU rows in-place on shifted_load_elec.""" + season_eps = ( + demand_elasticity.get(season_name, 0.0) + if isinstance(demand_elasticity, dict) + else demand_elasticity + ) + if season_eps == 0.0: + return + mask = bldg_level.isin(tou_set) & month_level.isin(season_months) season_df = ( shifted_load_elec.loc[mask, ["electricity_net"]].copy().reset_index() @@ -1063,7 +1072,7 @@ def _shift_season(season_name: str, season_months: set[int]) -> None: process_residential_hourly_demand_response_shift( hourly_load_df=season_df, period_rate=period_rate, - demand_elasticity=demand_elasticity, + demand_elasticity=season_eps, ) ) tracker["season"] = season_name @@ -1106,7 +1115,7 @@ def _shift_season(season_name: str, season_months: set[int]) -> None: shifted_total = shifted_load_elec.loc[tou_mask, "electricity_net"].sum() log.info( - "Runtime demand response complete: bldgs=%d, elasticity=%.3f, " + "Runtime demand response complete: bldgs=%d, elasticity=%s, " "original=%.0f, shifted=%.0f, diff=%.2f", len(tou_bldg_ids), demand_elasticity, diff --git a/utils/demand_flex.py b/utils/demand_flex.py index cae697e8..6cc68f04 100644 --- a/utils/demand_flex.py +++ b/utils/demand_flex.py @@ -244,7 +244,7 @@ def _compute_tou_subclass_mc( def apply_demand_flex( *, - elasticity: float, + elasticity: float | dict[str, float], run_type: str, year_run: int, path_tariffs_electric: dict[str, Path], @@ -276,7 +276,7 @@ def apply_demand_flex( f"keys={sorted(path_tariffs_electric)}" ) log.info( - ".... Demand flex enabled (elasticity=%.4f); detected %d TOU tariff(s): %s", + ".... Demand flex enabled (elasticity=%s); detected %d TOU tariff(s): %s", elasticity, len(tou_tariff_keys), tou_tariff_keys, From fee4706faa59f0c9a8366a9c603cb93374d48da7 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:02 +0000 Subject: [PATCH 08/19] Wire seasonal elasticity dict through run_scenario run_scenario.py now passes the elasticity value from the scenario YAML directly to the CAIRO runtime unchanged (dict or scalar), enabling seasonal elasticity to flow from config/periods/*.yaml through create_scenario_yamls into the actual CAIRO run. Made-with: Cursor --- rate_design/hp_rates/run_scenario.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/rate_design/hp_rates/run_scenario.py b/rate_design/hp_rates/run_scenario.py index 1265cad9..3956699c 100644 --- a/rate_design/hp_rates/run_scenario.py +++ b/rate_design/hp_rates/run_scenario.py @@ -109,7 +109,7 @@ class ScenarioSettings: solar_pv_compensation: str = "net_metering" run_includes_supply: bool = False sample_size: int | None = None - elasticity: float = 0.0 + elasticity: float | dict[str, float] = 0.0 # Real (non-zero) supply MC paths used exclusively for Phase 1.75 TOU # cost-causation recalibration. Supplied as CLI args from the Justfile so # delivery-only runs (whose path_supply_energy/capacity_mc point to zeros) @@ -257,7 +257,14 @@ def _build_settings_from_yaml_run( path_tariff_maps_gas, path_config, ) - elasticity = _parse_float(run.get("elasticity", 0.0), "elasticity") + elasticity_raw = run.get("elasticity", 0.0) + if isinstance(elasticity_raw, dict): + elasticity: float | dict[str, float] = { + str(k): _parse_float(v, f"elasticity.{k}") + for k, v in elasticity_raw.items() + } + else: + elasticity = _parse_float(elasticity_raw, "elasticity") sample_size = ( _parse_int(run["sample_size"], "sample_size") if "sample_size" in run else None ) @@ -622,7 +629,10 @@ def run(settings: ScenarioSettings, num_workers: int | None = None) -> None: # supply runs, per-subclass supply costs are derived from run 2 BAT data # (via compute_subclass_rr --run-dir-supply), not from raw Cambium prices. - demand_flex_enabled = settings.elasticity != 0.0 + if isinstance(settings.elasticity, dict): + demand_flex_enabled = any(v != 0.0 for v in settings.elasticity.values()) + else: + demand_flex_enabled = settings.elasticity != 0.0 if demand_flex_enabled: flex = apply_demand_flex( From 42b22322884c52dcd96643a03085ec745112f607 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:14 +0000 Subject: [PATCH 09/19] Add calibrate_demand_flex_elasticity to utils/pre Moves the elasticity calibration script from utils/post to utils/pre since its primary output is config/periods/*.yaml, which is consumed before a CAIRO run. Changes from the original: - Dual Arcturus model: computes both no-tech (slope=-0.065) and with-tech (slope=-0.111, corrected interaction term) recommendations - Seasonal elasticities: matches each season's price ratio against its own Arcturus target independently - --write-periods: writes both elasticity and elasticity_with_tech dicts to config/periods/{utility}.yaml - --compare-batch / --with-tech: compares analytical savings against a completed CAIRO batch, selecting the right reference set - --epsilon-start/--epsilon-end/--epsilon-step: replaces hardcoded list with a configurable sweep range (default -0.04 to -0.50, step -0.02) - ty/ruff fixes: cast for period_rate, .to_series().dt.month.to_numpy() for DatetimeIndex.month, removed dead assignments Made-with: Cursor --- utils/pre/calibrate_demand_flex_elasticity.py | 1413 +++++++++++++++++ 1 file changed, 1413 insertions(+) create mode 100644 utils/pre/calibrate_demand_flex_elasticity.py diff --git a/utils/pre/calibrate_demand_flex_elasticity.py b/utils/pre/calibrate_demand_flex_elasticity.py new file mode 100644 index 00000000..3a5e460c --- /dev/null +++ b/utils/pre/calibrate_demand_flex_elasticity.py @@ -0,0 +1,1413 @@ +"""Per-utility demand-flex elasticity calibration for heat-pump rate design. + +For each utility, loads real TOU derivation data, ResStock HP building loads, +and marginal cost profiles to compute: + - Demand shift at each candidate elasticity (kWh moved, peak reduction %) + - Rate arbitrage savings (the primary bill savings mechanism) + - MC-based RR reduction savings (secondary, small) + - Arcturus "no enabling tech" comparison + - Recommended elasticity per utility + +Operates purely analytically (no CAIRO run required). Results can be validated +against actual CAIRO bill outputs from batch ny_20260325b_r1-16. + +Usage: + uv run python -m utils.pre.calibrate_demand_flex_elasticity \ + --state ny \ + --output-dir /tmp/demand_flex_diagnostic + + # Fast dev iteration with sampled buildings: + uv run python -m utils.pre.calibrate_demand_flex_elasticity \ + --state ny --sample-size 50 --output-dir /tmp/demand_flex_diagnostic +""" + +from __future__ import annotations + +import argparse +import json +import logging +import math +import subprocess +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import cast + +import numpy as np +import pandas as pd +import polars as pl +import yaml + +from data.eia.hourly_loads.eia_region_config import get_aws_storage_options +from utils.cairo import ( + _build_period_consumption, + _build_period_shift_targets, + _compute_equivalent_flat_tariff, + assign_hourly_periods, + extract_tou_period_rates, +) +from utils.pre.compute_tou import SeasonTouSpec, load_season_specs +from utils.scenario_config import get_residential_customer_count_from_utility_stats + +log = logging.getLogger(__name__) + +# ── Constants ───────────────────────────────────────────────────────────────── + +WINTER_MONTHS = {1, 2, 3, 10, 11, 12} +SUMMER_MONTHS = {4, 5, 6, 7, 8, 9} + +ARCTURUS_NO_TECH_INTERCEPT = -0.011 +ARCTURUS_NO_TECH_SLOPE = -0.065 + +ARCTURUS_WITH_TECH_INTERCEPT = -0.011 +ARCTURUS_WITH_TECH_SLOPE = -0.111 + + +def _build_epsilon_range(start: float, end: float, step: float) -> list[float]: + """Build a list of epsilon values from start to end (inclusive) by step.""" + values: list[float] = [] + current = start + while current >= end - 1e-9: + values.append(round(current, 4)) + current += step + return values + + +STATE_CONFIGS: dict[str, dict] = { + "ny": { + "utilities": ("cenhud", "coned", "nimo", "nyseg", "or", "psegli", "rge"), + "path_metadata": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata/state=NY/" + "upgrade=00/metadata-sb.parquet" + ), + "path_utility_assignment": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata_utility/" + "state=NY/utility_assignment.parquet" + ), + "path_loads_dir": Path( + "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/load_curve_hourly/" + "state=NY/upgrade=00" + ), + "path_tou_derivation_dir": Path( + "rate_design/hp_rates/ny/config/tou_derivation" + ), + "path_tariffs_electric_dir": Path( + "rate_design/hp_rates/ny/config/tariffs/electric" + ), + "path_rev_requirement_dir": Path( + "rate_design/hp_rates/ny/config/rev_requirement" + ), + "mc_base": "s3://data.sb/switchbox/marginal_costs/ny", + "mc_year": 2025, + "path_electric_utility_stats": "s3://data.sb/eia/861/electric_utility_stats/year=2024/state=NY/data.parquet", + "path_periods_dir": Path("rate_design/hp_rates/ny/config/periods"), + }, +} + + +# ── Data structures ─────────────────────────────────────────────────────────── + + +@dataclass(frozen=True, slots=True) +class UtilityContext: + """All data needed to diagnose one utility.""" + + utility: str + season_specs: list[SeasonTouSpec] + tou_tariff: dict + hp_bldg_ids: list[int] + hp_weights: dict[int, float] + total_weighted_customers: float + hp_weighted_customers: float + delivery_rr: float = 0.0 + + +@dataclass(slots=True) +class SeasonResult: + """Diagnostic results for one utility × season × elasticity.""" + + utility: str + season: str + elasticity: float + price_ratio: float + base_rate: float + peak_hours: list[int] + n_peak_hours_per_day: int + hp_bldg_count: int + hp_weighted_share: float + hp_peak_kwh_orig: float + hp_peak_kwh_shifted: float + peak_reduction_pct: float + kwh_shifted: float + system_peak_reduction_pct: float + p_flat: float + arcturus_peak_reduction_pct: float + delta_vs_arcturus_pct: float + rate_arbitrage_savings_per_hp: float + delivery_mc_savings_total: float + delivery_mc_savings_per_hp: float + delivery_mc_nonzero_hours: int + delivery_mc_peak_overlap_hours: int + + +@dataclass(slots=True) +class SeasonRecommendation: + """Per-season elasticity recommendation.""" + + season: str + recommended_elasticity: float + arcturus_target_pct: float + peak_reduction_pct: float + price_ratio: float + savings_per_hp: float + + +@dataclass(slots=True) +class UtilityResult: + """Aggregated diagnostic results for one utility.""" + + utility: str + season_results: list[SeasonResult] = field(default_factory=list) + annual_weighted_ratio: float = 0.0 + annual_arcturus_pct: float = 0.0 + recommended_elasticity: float = 0.0 + recommended_annual_savings: float = 0.0 + seasonal_recommendations: list[SeasonRecommendation] = field(default_factory=list) + seasonal_recommendations_with_tech: list[SeasonRecommendation] = field( + default_factory=list + ) + delivery_rr: float = 0.0 + rr_decrease_by_elasticity: dict[float, float] = field(default_factory=dict) + + +# ── Arcturus model ──────────────────────────────────────────────────────────── + + +def arcturus_peak_reduction(price_ratio: float) -> float: + """Arcturus 2.0 'no enabling technology' peak reduction prediction. + + Returns a positive fraction (e.g. 0.056 for 5.6% reduction). + """ + if price_ratio <= 1.0: + return 0.0 + return abs( + ARCTURUS_NO_TECH_INTERCEPT + ARCTURUS_NO_TECH_SLOPE * math.log(price_ratio) + ) + + +def arcturus_peak_reduction_with_tech(price_ratio: float) -> float: + """Arcturus 2.0 'with enabling technology' peak reduction prediction. + + Returns a positive fraction (e.g. 0.13 for 13% reduction). + Roughly 2x the no-tech response at the same ratio. + """ + if price_ratio <= 1.0: + return 0.0 + return abs( + ARCTURUS_WITH_TECH_INTERCEPT + + ARCTURUS_WITH_TECH_SLOPE * math.log(price_ratio) + ) + + +# ── Data loading ────────────────────────────────────────────────────────────── + + +def load_hp_metadata( + path_metadata: Path, + path_utility_assignment: Path, +) -> pl.DataFrame: + """Load metadata and return HP buildings with utility assignment and weight.""" + meta = pl.read_parquet(path_metadata).select( + "bldg_id", "postprocess_group.has_hp", "weight" + ) + util = pl.read_parquet(path_utility_assignment).select( + "bldg_id", "sb.electric_utility" + ) + joined = meta.join(util, on="bldg_id", how="inner") + hp = joined.filter(pl.col("postprocess_group.has_hp") == True) # noqa: E712 + log.info( + "Loaded %d HP buildings out of %d total", + hp.height, + joined.height, + ) + return hp + + +def load_building_loads( + bldg_ids: list[int], + loads_dir: Path, + sample_size: int | None = None, +) -> pd.DataFrame: + """Load hourly electric net load for a set of buildings from local parquet. + + Returns a pandas DataFrame with MultiIndex (bldg_id, time) and column + 'electricity_net' in kWh, matching CAIRO's expected format. + """ + if sample_size and sample_size < len(bldg_ids): + rng = np.random.default_rng(42) + bldg_ids = list(rng.choice(bldg_ids, size=sample_size, replace=False)) + + frames: list[pd.DataFrame] = [] + missing = 0 + for bid in bldg_ids: + path = loads_dir / f"{bid}-0.parquet" + if not path.exists(): + missing += 1 + continue + df = pl.read_parquet( + path, + columns=["timestamp", "out.electricity.net.energy_consumption"], + ).to_pandas() + df = df.rename( + columns={ + "timestamp": "time", + "out.electricity.net.energy_consumption": "electricity_net", + } + ) + df["bldg_id"] = bid + frames.append(df) + + if missing: + log.warning("Missing load files for %d of %d buildings", missing, len(bldg_ids)) + if not frames: + raise FileNotFoundError( + f"No load files found in {loads_dir} for {len(bldg_ids)} buildings" + ) + + combined = pd.concat(frames, ignore_index=True) + combined = combined.set_index(["bldg_id", "time"]).sort_index() + log.info( + "Loaded %d buildings x %d hours = %d rows", + len(frames), + 8760, + len(combined), + ) + return combined + + +def load_tou_context( + utility: str, + tou_derivation_dir: Path, + tariffs_electric_dir: Path, + *, + use_calibrated: bool = False, +) -> tuple[list[SeasonTouSpec], dict]: + """Load TOU derivation specs and the TOU tariff for one utility. + + When *use_calibrated* is True, loads the calibrated tariff + (``*_calibrated.json``) whose rate **levels** match the revenue + requirement. The pre-calibration tariff has correct peak/off-peak + ratios but absolute $/kWh set at MC levels (~1.6× higher than + revenue-neutral for the HP subclass). + """ + deriv_path = tou_derivation_dir / f"{utility}_hp_seasonalTOU_derivation.json" + specs = load_season_specs(deriv_path) + + if use_calibrated: + tariff_path = ( + tariffs_electric_dir / f"{utility}_hp_seasonalTOU_flex_calibrated.json" + ) + if not tariff_path.exists(): + tariff_path = ( + tariffs_electric_dir / f"{utility}_hp_seasonalTOU_calibrated.json" + ) + if not tariff_path.exists(): + raise FileNotFoundError( + f"No calibrated tariff found for {utility}. " + f"Looked for *_flex_calibrated.json and *_calibrated.json in " + f"{tariffs_electric_dir}. Run a precalc CAIRO scenario first " + f"to generate calibrated tariffs, or use --no-calibrated." + ) + else: + tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU_flex.json" + if not tariff_path.exists(): + tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU.json" + + log.info(" Tariff: %s", tariff_path.name) + with open(tariff_path) as f: + tou_tariff = json.load(f) + return specs, tou_tariff + + +def load_delivery_mc( + utility: str, + mc_base: str, + mc_year: int, + storage_options: dict[str, str], +) -> pd.DataFrame: + """Load delivery-only MC (dist+sub-tx + bulk_tx) for one utility. + + Returns DataFrame with columns ['timestamp', 'mc_kwh'] in $/kWh. + """ + dist_path = ( + f"{mc_base}/dist_and_sub_tx/utility={utility}/year={mc_year}/data.parquet" + ) + bulk_path = f"{mc_base}/bulk_tx/utility={utility}/year={mc_year}/data.parquet" + + dist = pl.read_parquet(dist_path, storage_options=storage_options) + if "timestamp" in dist.columns: + ts_dtype = dist.schema["timestamp"] + if isinstance(ts_dtype, pl.Datetime) and ts_dtype.time_zone is not None: + dist = dist.with_columns( + pl.col("timestamp").dt.replace_time_zone(None).alias("timestamp") + ) + dist = dist.select( + "timestamp", + pl.col("mc_total_per_kwh").alias("mc_dist"), + ) + + bulk = pl.read_parquet(bulk_path, storage_options=storage_options) + if "timestamp" in bulk.columns: + ts_dtype = bulk.schema["timestamp"] + if isinstance(ts_dtype, pl.Datetime) and ts_dtype.time_zone is not None: + bulk = bulk.with_columns( + pl.col("timestamp").dt.replace_time_zone(None).alias("timestamp") + ) + bulk = bulk.select( + "timestamp", + pl.col("bulk_tx_cost_enduse").alias("mc_bulk"), + ) + + mc = dist.join(bulk, on="timestamp", how="full", coalesce=True).sort("timestamp") + mc = mc.with_columns( + (pl.col("mc_dist").fill_null(0.0) + pl.col("mc_bulk").fill_null(0.0)).alias( + "mc_kwh" + ) + ) + return mc.select("timestamp", "mc_kwh").to_pandas() + + +# ── Core diagnostic logic ───────────────────────────────────────────────────── + + +def diagnose_season( + utility: str, + season_spec: SeasonTouSpec, + loads_df: pd.DataFrame, + period_rate: pd.Series, + period_map: pd.Series, + elasticity: float, + hp_weights: dict[int, float], + total_weighted_customers: float, + hp_weighted_customers: float, + mc_df: pd.DataFrame | None, +) -> SeasonResult: + """Run diagnostic for one utility × season × elasticity.""" + season_months = set(season_spec.season.months) + season_name = season_spec.season.name + + time_level = pd.DatetimeIndex(loads_df.index.get_level_values("time")) + season_mask = time_level.to_series().dt.month.isin(season_months).to_numpy() + season_df = loads_df.loc[season_mask, ["electricity_net"]].copy().reset_index() + + period_lookup = period_map.reset_index() + period_lookup.columns = pd.Index(["time", "energy_period"]) + season_df = season_df.merge(period_lookup, on="time", how="left") + + period_consumption = _build_period_consumption(season_df) + p_flat = _compute_equivalent_flat_tariff(period_consumption, period_rate) + + targets = _build_period_shift_targets( + period_consumption, period_rate, elasticity, p_flat, receiver_period=None + ) + + peak_periods = set() + offpeak_periods = set() + for _, row in targets.drop_duplicates("energy_period").iterrows(): + ep = int(row["energy_period"]) + rate_val = float(row["rate"]) + if rate_val > p_flat: + peak_periods.add(ep) + else: + offpeak_periods.add(ep) + + peak_orig = float( + targets.loc[targets["energy_period"].isin(peak_periods), "Q_orig"].sum() + ) + peak_shifted = float( + targets.loc[targets["energy_period"].isin(peak_periods), "Q_target"].sum() + ) + peak_reduction_kwh = peak_orig - peak_shifted + peak_reduction_pct = (peak_reduction_kwh / peak_orig * 100) if peak_orig > 0 else 0 + + total_shift = float(targets.loc[targets["load_shift"] < 0, "load_shift"].sum()) + + peak_rates = targets.loc[ + targets["energy_period"].isin(peak_periods), "rate" + ].unique() + offpeak_rates = targets.loc[ + targets["energy_period"].isin(offpeak_periods), "rate" + ].unique() + peak_rate = float(peak_rates.mean()) if len(peak_rates) > 0 else 0.0 + offpeak_rate = float(offpeak_rates.mean()) if len(offpeak_rates) > 0 else 0.0 + rate_spread = peak_rate - offpeak_rate + rate_arb_savings_total = abs(total_shift) * rate_spread + + n_bldgs = len(targets["bldg_id"].unique()) + savings_per_hp = rate_arb_savings_total / n_bldgs if n_bldgs > 0 else 0 + + arcturus_pct = arcturus_peak_reduction(season_spec.peak_offpeak_ratio) * 100 + delta_vs_arcturus = peak_reduction_pct - arcturus_pct + + system_peak_pct = ( + peak_reduction_pct * (hp_weighted_customers / total_weighted_customers) + if total_weighted_customers > 0 + else 0 + ) + + delivery_mc_savings = 0.0 + mc_nonzero = 0 + mc_peak_overlap = 0 + if mc_df is not None: + mc_season = mc_df[mc_df["timestamp"].dt.month.isin(season_months)].copy() + mc_nonzero = int((mc_season["mc_kwh"].abs() > 1e-9).sum()) + + peak_hours_set = set(season_spec.peak_hours) + mc_in_peak = mc_season[mc_season["timestamp"].dt.hour.isin(peak_hours_set)] + mc_peak_overlap = int((mc_in_peak["mc_kwh"].abs() > 1e-9).sum()) + + hourly_shift = season_df.copy() + shift_map = targets.set_index(["bldg_id", "energy_period"])["load_shift"] + hourly_shift = hourly_shift.merge( + targets[["bldg_id", "energy_period", "load_shift", "Q_orig"]], + on=["bldg_id", "energy_period"], + how="left", + ) + period_sums = ( + hourly_shift.groupby(["bldg_id", "energy_period"])["electricity_net"] + .transform("sum") + ) + hour_share = np.where( + period_sums != 0, + hourly_shift["electricity_net"] / period_sums, + 0.0, + ) + hourly_shift["hourly_shift_kwh"] = hourly_shift["load_shift"] * hour_share + + weights_series = hourly_shift["bldg_id"].map(hp_weights).fillna(1.0) + hourly_shift["weighted_shift"] = hourly_shift["hourly_shift_kwh"] * weights_series + + system_hourly = ( + hourly_shift.groupby("time")["weighted_shift"].sum().reset_index() + ) + system_hourly.columns = ["timestamp", "sys_shift_kwh"] + system_hourly["timestamp"] = pd.to_datetime(system_hourly["timestamp"]) + + # At runtime, CAIRO re-indexes ResStock AMY2018 loads to the MC year + # (year_run, e.g. 2025) via _return_loads_combined. Our analytical + # loads are still in the source year, so align to MC year here. + mc_year = int(mc_season["timestamp"].dt.year.iloc[0]) + shift_year = int(system_hourly["timestamp"].dt.year.iloc[0]) + if mc_year != shift_year: + year_offset = pd.Timestamp(f"{mc_year}-01-01") - pd.Timestamp( + f"{shift_year}-01-01" + ) + system_hourly["timestamp"] = system_hourly["timestamp"] + year_offset + + mc_merge = mc_season.merge(system_hourly, on="timestamp", how="left") + mc_merge["sys_shift_kwh"] = mc_merge["sys_shift_kwh"].fillna(0.0) + delivery_mc_savings = -float( + (mc_merge["mc_kwh"] * mc_merge["sys_shift_kwh"]).sum() + ) + + return SeasonResult( + utility=utility, + season=season_name, + elasticity=elasticity, + price_ratio=season_spec.peak_offpeak_ratio, + base_rate=season_spec.base_rate, + peak_hours=list(season_spec.peak_hours), + n_peak_hours_per_day=len(season_spec.peak_hours), + hp_bldg_count=len(targets["bldg_id"].unique()), + hp_weighted_share=hp_weighted_customers / total_weighted_customers * 100 + if total_weighted_customers > 0 + else 0, + hp_peak_kwh_orig=peak_orig, + hp_peak_kwh_shifted=peak_shifted, + peak_reduction_pct=peak_reduction_pct, + kwh_shifted=abs(total_shift), + system_peak_reduction_pct=system_peak_pct, + p_flat=p_flat, + arcturus_peak_reduction_pct=arcturus_pct, + delta_vs_arcturus_pct=delta_vs_arcturus, + rate_arbitrage_savings_per_hp=savings_per_hp, + delivery_mc_savings_total=delivery_mc_savings, + delivery_mc_savings_per_hp=delivery_mc_savings / n_bldgs if n_bldgs > 0 else 0, + delivery_mc_nonzero_hours=mc_nonzero, + delivery_mc_peak_overlap_hours=mc_peak_overlap, + ) + + +def diagnose_utility( + ctx: UtilityContext, + loads_df: pd.DataFrame, + elasticities: list[float], + mc_df: pd.DataFrame | None, +) -> UtilityResult: + """Run full diagnostic for one utility across all seasons and elasticities.""" + result = UtilityResult(utility=ctx.utility, delivery_rr=ctx.delivery_rr) + + rate_df = extract_tou_period_rates(ctx.tou_tariff) + period_rate = cast(pd.Series, rate_df.groupby("energy_period")["rate"].first()) + time_idx = pd.DatetimeIndex( + loads_df.index.get_level_values("time").unique().sort_values() + ) + period_map = assign_hourly_periods(time_idx, ctx.tou_tariff) + + for eps in elasticities: + for spec in ctx.season_specs: + sr = diagnose_season( + utility=ctx.utility, + season_spec=spec, + loads_df=loads_df, + period_rate=period_rate, + period_map=period_map, + elasticity=eps, + hp_weights=ctx.hp_weights, + total_weighted_customers=ctx.total_weighted_customers, + hp_weighted_customers=ctx.hp_weighted_customers, + mc_df=mc_df, + ) + result.season_results.append(sr) + + # RR decrease by elasticity: sum delivery MC savings across seasons + if ctx.delivery_rr > 0: + for eps in elasticities: + total_mc_savings = sum( + sr.delivery_mc_savings_total + for sr in result.season_results + if sr.elasticity == eps + ) + result.rr_decrease_by_elasticity[eps] = total_mc_savings / ctx.delivery_rr + + winter_specs = [s for s in ctx.season_specs if s.season.name == "winter"] + summer_specs = [s for s in ctx.season_specs if s.season.name == "summer"] + if winter_specs and summer_specs: + winter_kwh = sum( + sr.hp_peak_kwh_orig + sr.kwh_shifted + for sr in result.season_results + if sr.season == "winter" and sr.elasticity == elasticities[0] + ) + summer_kwh = sum( + sr.hp_peak_kwh_orig + sr.kwh_shifted + for sr in result.season_results + if sr.season == "summer" and sr.elasticity == elasticities[0] + ) + total_kwh = winter_kwh + summer_kwh + if total_kwh > 0: + w_ratio = winter_specs[0].peak_offpeak_ratio + s_ratio = summer_specs[0].peak_offpeak_ratio + result.annual_weighted_ratio = ( + w_ratio * winter_kwh + s_ratio * summer_kwh + ) / total_kwh + else: + result.annual_weighted_ratio = ( + winter_specs[0].peak_offpeak_ratio + + summer_specs[0].peak_offpeak_ratio + ) / 2 + elif ctx.season_specs: + result.annual_weighted_ratio = ctx.season_specs[0].peak_offpeak_ratio + + result.annual_arcturus_pct = ( + arcturus_peak_reduction(result.annual_weighted_ratio) * 100 + ) + + # Per-season recommendations: each season has its own TOU ratio and + # therefore its own Arcturus target. Find the elasticity that best matches + # the Arcturus peak reduction for that season's ratio. + season_names = sorted({sr.season for sr in result.season_results}) + for sn in season_names: + season_srs = [sr for sr in result.season_results if sr.season == sn] + if not season_srs: + continue + season_ratio = season_srs[0].price_ratio + season_arcturus = arcturus_peak_reduction(season_ratio) * 100 + + best_season_eps = elasticities[0] + best_season_delta = float("inf") + for eps in elasticities: + matching = [sr for sr in season_srs if sr.elasticity == eps] + if not matching: + continue + peak_red = matching[0].peak_reduction_pct + delta = abs(peak_red - season_arcturus) + if delta < best_season_delta: + best_season_delta = delta + best_season_eps = eps + + best_sr = next( + (sr for sr in season_srs if sr.elasticity == best_season_eps), None + ) + result.seasonal_recommendations.append( + SeasonRecommendation( + season=sn, + recommended_elasticity=best_season_eps, + arcturus_target_pct=season_arcturus, + peak_reduction_pct=best_sr.peak_reduction_pct if best_sr else 0.0, + price_ratio=season_ratio, + savings_per_hp=best_sr.rate_arbitrage_savings_per_hp + if best_sr + else 0.0, + ) + ) + + # Per-season recommendations: with enabling technology + for sn in season_names: + season_srs = [sr for sr in result.season_results if sr.season == sn] + if not season_srs: + continue + season_ratio = season_srs[0].price_ratio + season_arcturus_wt = arcturus_peak_reduction_with_tech(season_ratio) * 100 + + best_wt_eps = elasticities[0] + best_wt_delta = float("inf") + for eps in elasticities: + matching = [sr for sr in season_srs if sr.elasticity == eps] + if not matching: + continue + delta = abs(matching[0].peak_reduction_pct - season_arcturus_wt) + if delta < best_wt_delta: + best_wt_delta = delta + best_wt_eps = eps + + best_wt_sr = next( + (sr for sr in season_srs if sr.elasticity == best_wt_eps), None + ) + result.seasonal_recommendations_with_tech.append( + SeasonRecommendation( + season=sn, + recommended_elasticity=best_wt_eps, + arcturus_target_pct=season_arcturus_wt, + peak_reduction_pct=best_wt_sr.peak_reduction_pct + if best_wt_sr + else 0.0, + price_ratio=season_ratio, + savings_per_hp=best_wt_sr.rate_arbitrage_savings_per_hp + if best_wt_sr + else 0.0, + ) + ) + + # Annual recommendation: average peak reduction across seasons + best_eps = elasticities[0] + best_delta = float("inf") + for eps in elasticities: + annual_peak_red = np.mean( + [ + sr.peak_reduction_pct + for sr in result.season_results + if sr.elasticity == eps + ] + ) + delta = abs(annual_peak_red - result.annual_arcturus_pct) + if delta < best_delta: + best_delta = delta + best_eps = eps + + result.recommended_elasticity = best_eps + result.recommended_annual_savings = sum( + sr.rate_arbitrage_savings_per_hp + for sr in result.season_results + if sr.elasticity == best_eps + ) + return result + + +# ── Output formatting ───────────────────────────────────────────────────────── + + +def results_to_dataframe(results: list[UtilityResult]) -> pl.DataFrame: + """Convert all season-level results to a flat Polars DataFrame.""" + rows = [] + for ur in results: + for sr in ur.season_results: + rows.append( + { + "utility": sr.utility, + "season": sr.season, + "elasticity": sr.elasticity, + "price_ratio": round(sr.price_ratio, 4), + "base_rate_kwh": round(sr.base_rate, 6), + "peak_rate_kwh": round( + sr.base_rate * sr.price_ratio, 6 + ), + "n_peak_hours_per_day": sr.n_peak_hours_per_day, + "hp_bldg_count": sr.hp_bldg_count, + "hp_weighted_share_pct": round(sr.hp_weighted_share, 2), + "hp_peak_kwh_orig": round(sr.hp_peak_kwh_orig, 1), + "hp_peak_kwh_shifted": round(sr.hp_peak_kwh_shifted, 1), + "peak_reduction_pct": round(sr.peak_reduction_pct, 3), + "kwh_shifted": round(sr.kwh_shifted, 1), + "system_peak_reduction_pct": round( + sr.system_peak_reduction_pct, 4 + ), + "p_flat": round(sr.p_flat, 6), + "arcturus_peak_reduction_pct": round( + sr.arcturus_peak_reduction_pct, 3 + ), + "delta_vs_arcturus_pct": round(sr.delta_vs_arcturus_pct, 3), + "rate_arbitrage_savings_per_hp": round( + sr.rate_arbitrage_savings_per_hp, 2 + ), + "delivery_mc_savings_total": round( + sr.delivery_mc_savings_total, 2 + ), + "delivery_mc_savings_per_hp": round( + sr.delivery_mc_savings_per_hp, 2 + ), + "delivery_mc_nonzero_hours": sr.delivery_mc_nonzero_hours, + "delivery_mc_peak_overlap_hours": sr.delivery_mc_peak_overlap_hours, + "annual_weighted_ratio": round(ur.annual_weighted_ratio, 4), + "annual_arcturus_pct": round(ur.annual_arcturus_pct, 3), + "recommended_elasticity": ur.recommended_elasticity, + "recommended_annual_savings": round( + ur.recommended_annual_savings, 2 + ), + "seasonal_recommended_elasticity": _seasonal_rec_field( + ur, sr.season, "recommended_elasticity" + ), + "seasonal_arcturus_target_pct": _seasonal_rec_field( + ur, sr.season, "arcturus_target_pct" + ), + "seasonal_recommended_savings": _seasonal_rec_field( + ur, sr.season, "savings_per_hp" + ), + "seasonal_recommended_elasticity_with_tech": _seasonal_rec_field( + ur, sr.season, "recommended_elasticity", with_tech=True + ), + "seasonal_arcturus_target_pct_with_tech": _seasonal_rec_field( + ur, sr.season, "arcturus_target_pct", with_tech=True + ), + "seasonal_recommended_savings_with_tech": _seasonal_rec_field( + ur, sr.season, "savings_per_hp", with_tech=True + ), + } + ) + return pl.DataFrame(rows) + + +def _seasonal_rec_field( + ur: UtilityResult, season: str, field_name: str, *, with_tech: bool = False +) -> float: + """Look up a seasonal recommendation field for the output DataFrame.""" + recs = ( + ur.seasonal_recommendations_with_tech if with_tech else ur.seasonal_recommendations + ) + for rec in recs: + if rec.season == season: + return round(getattr(rec, field_name), 4) + return 0.0 + + +S3_OUTPUTS_BASE = "s3://data.sb/switchbox/cairo/outputs/hp_rates" + +COMPARISON_PAIRS: list[tuple[str, str, str, str]] = [ + ("In-sample delivery", "run9", "run13", "precalc u00, subclasses"), + ("In-sample supply", "run10", "run14", "precalc u00, subclasses + supply"), + ("Out-of-sample delivery", "run11", "run15", "default u02, all-HP"), + ("Out-of-sample supply", "run12", "run16", "default u02, all-HP + supply"), +] + + +def _find_run_dir(batch_prefix: str, run_fragment: str) -> str | None: + """Find the timestamped run directory under a batch prefix on S3.""" + result = subprocess.run( + ["aws", "s3", "ls", batch_prefix], + capture_output=True, + text=True, + check=False, + ) + if result.returncode != 0: + return None + for line in result.stdout.strip().splitlines(): + parts = line.strip().split() + if len(parts) >= 2 and "PRE" in parts: + dirname = parts[-1].rstrip("/") + if run_fragment in dirname: + return f"{batch_prefix}{dirname}" + return None + + +def _read_annual_bills(csv_path: str) -> pl.DataFrame | None: + """Read a CAIRO bill CSV and return Annual rows only.""" + try: + return cast( + pl.DataFrame, + pl.scan_csv(csv_path) + .filter(pl.col("month") == "Annual") + .collect(), + ) + except Exception as e: + log.warning(" Failed to read %s: %s", csv_path, e) + return None + + +@dataclass(frozen=True, slots=True) +class PairResult: + """Savings stats for one comparison pair.""" + + label: str + baseline_run: str + flex_run: str + description: str + mean_savings: float + median_savings: float + n_bldgs: int + + +@dataclass(frozen=True, slots=True) +class UtilityBillComparison: + """All comparison pairs for one utility.""" + + utility: str + analytical_savings: float + analytical_savings_seasonal: float + pairs: list[PairResult] + + +def compare_batch_bills( + state: str, + batch: str, + results: list[UtilityResult], + hp_meta: pl.DataFrame, + *, + with_tech: bool = False, +) -> list[UtilityBillComparison]: + """Compare analytical savings against CAIRO bill outputs across run pairs.""" + comparisons: list[UtilityBillComparison] = [] + + for ur in results: + utility = ur.utility + batch_prefix = f"{S3_OUTPUTS_BASE}/{state}/{utility}/{batch}/" + + ls_result = subprocess.run( + ["aws", "s3", "ls", batch_prefix], + capture_output=True, + text=True, + check=False, + ) + if ls_result.returncode != 0: + log.warning(" %s: batch not found at %s", utility, batch_prefix) + continue + + run_dirs: dict[str, str] = {} + for line in ls_result.stdout.strip().splitlines(): + parts = line.strip().split() + if len(parts) >= 2 and "PRE" in parts: + dirname = parts[-1].rstrip("/") + run_dirs[dirname] = f"{batch_prefix}{dirname}" + + hp_bldg_ids = ( + hp_meta.filter(pl.col("sb.electric_utility") == utility)["bldg_id"] + .to_list() + ) + + pairs: list[PairResult] = [] + for label, base_frag, flex_frag, desc in COMPARISON_PAIRS: + base_dir = next( + (v for k, v in run_dirs.items() if base_frag in k), None + ) + flex_dir = next( + (v for k, v in run_dirs.items() if flex_frag in k), None + ) + if not base_dir or not flex_dir: + log.warning( + " %s: %s or %s not found, skipping %s", + utility, base_frag, flex_frag, label, + ) + continue + + base_df = _read_annual_bills( + f"{base_dir}/bills/elec_bills_year_run.csv" + ) + flex_df = _read_annual_bills( + f"{flex_dir}/bills/elec_bills_year_run.csv" + ) + if base_df is None or flex_df is None: + continue + + joined = ( + base_df.select( + pl.col("bldg_id"), + pl.col("bill_level").alias("bill_baseline"), + ) + .join( + flex_df.select( + pl.col("bldg_id"), + pl.col("bill_level").alias("bill_flex"), + ), + on="bldg_id", + how="inner", + ) + .with_columns( + (pl.col("bill_baseline") - pl.col("bill_flex")).alias( + "savings" + ), + ) + ) + + is_in_sample = "In-sample" in label + if is_in_sample: + joined = joined.filter(pl.col("bldg_id").is_in(hp_bldg_ids)) + + if joined.is_empty(): + log.warning(" %s: no buildings for %s", utility, label) + continue + + pairs.append( + PairResult( + label=label, + baseline_run=base_frag, + flex_run=flex_frag, + description=desc, + mean_savings=float(joined["savings"].mean()), # type: ignore[arg-type] + median_savings=float(joined["savings"].median()), # type: ignore[arg-type] + n_bldgs=joined.height, + ) + ) + + if pairs: + recs = ( + ur.seasonal_recommendations_with_tech + if with_tech + else ur.seasonal_recommendations + ) + seasonal_savings = sum(sr.savings_per_hp for sr in recs) + comparisons.append( + UtilityBillComparison( + utility=utility, + analytical_savings=ur.recommended_annual_savings, + analytical_savings_seasonal=seasonal_savings, + pairs=pairs, + ) + ) + + return comparisons + + +def print_batch_comparison( + batch: str, comparisons: list[UtilityBillComparison] +) -> None: + """Print analytical vs CAIRO bill comparison across all run pairs.""" + print("\n" + "=" * 80) + print(f"CAIRO BILL COMPARISON (batch: {batch})") + print("=" * 80) + + for uc in comparisons: + ref = uc.analytical_savings_seasonal or uc.analytical_savings + label_suffix = "seasonal" if uc.analytical_savings_seasonal else "annual" + print( + f"\n {uc.utility.upper()} " + f"(analytical: ${ref:.2f}/HP {label_suffix}" + f", annual=${uc.analytical_savings:.2f}/HP)" + ) + print( + f" {'Comparison':26s} {'Runs':>10s} {'Mean sav':>9s}" + f" {'Med sav':>8s} {'Δ(mean)':>8s} {'N':>5s} Description" + ) + print( + f" {'─' * 26} {'─' * 10} {'─' * 9}" + f" {'─' * 8} {'─' * 8} {'─' * 5} {'─' * 28}" + ) + for p in uc.pairs: + delta = p.mean_savings - ref + print( + f" {p.label:26s} {p.baseline_run:>4s}→{p.flex_run:<4s}" + f" ${p.mean_savings:>8.2f}" + f" ${p.median_savings:>7.2f}" + f" {delta:>+8.2f}" + f" {p.n_bldgs:>5d} {p.description}" + ) + print() + + +def _print_seasonal_table( + title: str, results: list[UtilityResult], variant: str +) -> None: + """Print a seasonal recommendation table (no_tech or with_tech).""" + print(f" {title}\n") + print( + f" {'Utility':8s} {'Season':8s} {'ε':>6s} {'Savings/HP':>11s}" + f" {'Ratio':>7s} {'Peak red':>8s} {'Arcturus':>8s}" + ) + print( + f" {'─' * 8} {'─' * 8} {'─' * 6} {'─' * 11}" + f" {'─' * 7} {'─' * 8} {'─' * 8}" + ) + for ur in results: + recs = ( + ur.seasonal_recommendations + if variant == "no_tech" + else ur.seasonal_recommendations_with_tech + ) + for rec in recs: + print( + f" {ur.utility:8s} {rec.season:8s} {rec.recommended_elasticity:>6.2f}" + f" ${rec.savings_per_hp:>9.2f}" + f" {rec.price_ratio:>7.3f}" + f" {rec.peak_reduction_pct:>7.1f}%" + f" {rec.arcturus_target_pct:>7.1f}%" + ) + print() + + +def print_summary(results: list[UtilityResult], *, verbose: bool = False) -> None: + """Print a human-readable summary to stdout.""" + print("\n" + "=" * 80) + print("DEMAND-FLEX ELASTICITY CALIBRATION") + print("=" * 80) + + # ── Annual recommendation table ─────────────────────────────────────── + print("\n RECOMMENDED ELASTICITIES (annual)\n") + print( + f" {'Utility':8s} {'ε':>6s} {'Savings/HP':>11s}" + f" {'Peak win':>8s} {'Wt ratio':>8s} {'Arcturus':>8s}" + ) + print(f" {'─' * 8} {'─' * 6} {'─' * 11} {'─' * 8} {'─' * 8} {'─' * 8}") + for ur in results: + n_peak = 0 + for sr in ur.season_results: + if sr.elasticity == ur.recommended_elasticity: + n_peak = sr.n_peak_hours_per_day + break + print( + f" {ur.utility:8s} {ur.recommended_elasticity:>6.2f}" + f" ${ur.recommended_annual_savings:>9.2f}" + f" {n_peak:>5d} hr" + f" {ur.annual_weighted_ratio:>8.3f}" + f" {ur.annual_arcturus_pct:>7.1f}%" + ) + print() + + # ── Seasonal recommendation table ───────────────────────────────────── + _print_seasonal_table( + "RECOMMENDED ELASTICITIES — no enabling technology (seasonal)", results, "no_tech" + ) + _print_seasonal_table( + "RECOMMENDED ELASTICITIES — with enabling technology (seasonal)", + results, + "with_tech", + ) + + # ── RR decrease table ────────────────────────────────────────────────── + has_rr = any(ur.rr_decrease_by_elasticity for ur in results) + if has_rr: + all_eps = sorted( + {e for ur in results for e in ur.rr_decrease_by_elasticity}, + ) + print(" DELIVERY RR DECREASE BY ELASTICITY (delivery MC savings only)\n") + header = f" {'Utility':8s} {'Del. RR':>14s}" + for eps in all_eps: + header += f" {eps:>7.2f}" + print(header) + print( + f" {'─' * 8} {'─' * 14}" + + "".join(f" {'─' * 7}" for _ in all_eps) + ) + for ur in results: + if not ur.rr_decrease_by_elasticity: + continue + row = f" {ur.utility:8s} ${ur.delivery_rr / 1e6:>10.1f}M" + for eps in all_eps: + pct = ur.rr_decrease_by_elasticity.get(eps, 0.0) * 100 + row += f" {pct:>7.4f}%" + print(row) + # Also show absolute dollar savings for the recommended elasticity + print() + print(f" {'Utility':8s} {'ε':>6s} {'Del MC savings':>14s} Note") + print(f" {'─' * 8} {'─' * 6} {'─' * 14} {'─' * 40}") + for ur in results: + if not ur.rr_decrease_by_elasticity: + continue + rec_eps = ur.recommended_elasticity + total_mc = sum( + sr.delivery_mc_savings_total + for sr in ur.season_results + if sr.elasticity == rec_eps + ) + print( + f" {ur.utility:8s} {rec_eps:>6.2f}" + f" ${total_mc:>13,.0f}" + f" delivery only; supply MC savings not included" + ) + print() + + # ── Caveats ─────────────────────────────────────────────────────────── + print(" CAVEATS:") + print(" - Arcturus uses aggregate heterogeneous response; our model applies") + print(" uniform constant elasticity. Recommended epsilon is approximate.") + print(" - Arcturus log-linear form differs from our power-law model;") + print(" comparison valid at specific ratios, not across full curve.") + + # ── Per-utility detail (verbose only) ──────────────────────────────── + if not verbose: + return + + for ur in results: + print(f"\n{'─' * 80}") + print(f"UTILITY: {ur.utility.upper()}") + print( + f" Annual weighted ratio: {ur.annual_weighted_ratio:.3f}" + f" │ Arcturus target: {ur.annual_arcturus_pct:.1f}% peak reduction" + ) + print( + f" RECOMMENDED ELASTICITY: {ur.recommended_elasticity}" + f" │ Annual savings/HP customer: ${ur.recommended_annual_savings:.2f}" + ) + + for eps in sorted({sr.elasticity for sr in ur.season_results}): + print(f"\n ε = {eps}:") + for sr in ur.season_results: + if sr.elasticity != eps: + continue + print( + f" {sr.season:8s} ratio={sr.price_ratio:.2f}" + f" peak_red={sr.peak_reduction_pct:5.2f}%" + f" (arcturus={sr.arcturus_peak_reduction_pct:.2f}%" + f" Δ={sr.delta_vs_arcturus_pct:+.2f}%)" + ) + print( + f" shifted={sr.kwh_shifted:,.0f} kWh" + f" arb_savings=${sr.rate_arbitrage_savings_per_hp:.2f}/hp" + f" mc_savings=${sr.delivery_mc_savings_per_hp:.2f}/hp" + ) + print( + f" mc_nonzero_hrs={sr.delivery_mc_nonzero_hours}" + f" mc_peak_overlap={sr.delivery_mc_peak_overlap_hours}" + f" sys_peak_impact={sr.system_peak_reduction_pct:.3f}%" + ) + + +# ── Main ────────────────────────────────────────────────────────────────────── + + +def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--state", required=True, choices=list(STATE_CONFIGS.keys())) + p.add_argument( + "--epsilon-start", type=float, default=-0.04, + help="Start of elasticity sweep range (default: -0.04)", + ) + p.add_argument( + "--epsilon-end", type=float, default=-0.50, + help="End of elasticity sweep range, inclusive (default: -0.50)", + ) + p.add_argument( + "--epsilon-step", type=float, default=-0.02, + help="Step size for elasticity sweep (default: -0.02)", + ) + p.add_argument("--output-dir", type=Path, default=None) + p.add_argument( + "--sample-size", + type=int, + default=None, + help="Limit HP buildings per utility for faster dev iteration", + ) + p.add_argument( + "--utilities", + default=None, + help="Comma-separated utility subset (default: all)", + ) + p.add_argument( + "--no-calibrated", + dest="calibrated", + action="store_false", + default=True, + help="Use pre-calibration (MC-level) tariff rates instead of calibrated", + ) + p.add_argument( + "--compare-batch", + default=None, + help="CAIRO batch name to compare analytical predictions against actual bills", + ) + p.add_argument( + "--with-tech", + action="store_true", + help="Use with-tech recommendations for batch comparison (match CAIRO runs that used elasticity_with_tech)", + ) + p.add_argument( + "--write-periods", + action="store_true", + help="Write seasonal elasticities into each utility's config/periods YAML", + ) + p.add_argument("-v", "--verbose", action="store_true") + return p.parse_args(argv) + + +def main(argv: list[str] | None = None) -> None: + args = _parse_args(argv) + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s %(name)s: %(message)s", + ) + + cfg = STATE_CONFIGS[args.state] + elasticities = _build_epsilon_range(args.epsilon_start, args.epsilon_end, args.epsilon_step) + utilities = ( + tuple(args.utilities.split(",")) if args.utilities else cfg["utilities"] + ) + storage_options = get_aws_storage_options() + + project_root = Path(__file__).resolve().parents[2] + + log.info("Loading HP metadata...") + hp_meta = load_hp_metadata(cfg["path_metadata"], cfg["path_utility_assignment"]) + + all_meta = pl.read_parquet(cfg["path_utility_assignment"]).select( + "bldg_id", "weight", "sb.electric_utility" + ) + + results: list[UtilityResult] = [] + for utility in utilities: + log.info("=" * 60) + log.info("Diagnosing %s...", utility) + + util_hp = hp_meta.filter(pl.col("sb.electric_utility") == utility) + hp_bldg_ids = util_hp["bldg_id"].to_list() + + util_all = all_meta.filter(pl.col("sb.electric_utility") == utility) + raw_weight_sum = float(util_all["weight"].sum()) + + eia_customer_count = get_residential_customer_count_from_utility_stats( + cfg["path_electric_utility_stats"], + utility, + storage_options=storage_options, + ) + scale = eia_customer_count / raw_weight_sum if raw_weight_sum > 0 else 1.0 + log.info( + " EIA customer count: %d, raw weight sum: %.0f, scale factor: %.4f", + eia_customer_count, raw_weight_sum, scale, + ) + + hp_weights = { + bid: w * scale + for bid, w in zip(util_hp["bldg_id"].to_list(), util_hp["weight"].to_list()) + } + hp_weighted = float(util_hp["weight"].sum()) * scale + total_weighted = raw_weight_sum * scale # == eia_customer_count + + log.info( + " %d HP buildings (%.0f weighted, %.1f%% of %.0f total)", + len(hp_bldg_ids), + hp_weighted, + hp_weighted / total_weighted * 100 if total_weighted else 0, + total_weighted, + ) + + if not hp_bldg_ids: + log.warning(" No HP buildings for %s, skipping", utility) + continue + + tou_deriv_dir = project_root / cfg["path_tou_derivation_dir"] + tariffs_dir = project_root / cfg["path_tariffs_electric_dir"] + specs, tou_tariff = load_tou_context( + utility, tou_deriv_dir, tariffs_dir, use_calibrated=args.calibrated + ) + log.info( + " TOU specs: %s", + [(s.season.name, f"ratio={s.peak_offpeak_ratio:.2f}") for s in specs], + ) + + log.info(" Loading HP building loads...") + loads_df = load_building_loads( + hp_bldg_ids, cfg["path_loads_dir"], sample_size=args.sample_size + ) + + log.info(" Loading delivery MC data...") + try: + mc_df = load_delivery_mc( + utility, cfg["mc_base"], cfg["mc_year"], storage_options + ) + log.info(" MC data loaded: %d rows", len(mc_df)) + except Exception as e: + log.warning(" Failed to load MC data for %s: %s", utility, e) + mc_df = None + + delivery_rr = 0.0 + rr_dir = project_root / cfg.get("path_rev_requirement_dir", "") + rr_path = rr_dir / f"{utility}.yaml" + if rr_path.exists(): + with open(rr_path) as f: + rr_data = yaml.safe_load(f) + delivery_rr = float(rr_data.get("total_delivery_revenue_requirement", 0)) + log.info(" Delivery RR: $%.0fM", delivery_rr / 1e6) + + ctx = UtilityContext( + utility=utility, + season_specs=specs, + tou_tariff=tou_tariff, + hp_bldg_ids=hp_bldg_ids, + hp_weights=hp_weights, + total_weighted_customers=total_weighted, + hp_weighted_customers=hp_weighted, + delivery_rr=delivery_rr, + ) + + ur = diagnose_utility(ctx, loads_df, elasticities, mc_df) + results.append(ur) + log.info( + " Recommended ε=%.2f, annual savings=$%.2f/hp", + ur.recommended_elasticity, + ur.recommended_annual_savings, + ) + + print_summary(results, verbose=args.verbose) + + if args.write_periods: + periods_dir = project_root / cfg["path_periods_dir"] + for ur in results: + if not ur.seasonal_recommendations: + log.warning("No seasonal recommendations for %s, skipping", ur.utility) + continue + periods_path = periods_dir / f"{ur.utility}.yaml" + if periods_path.exists(): + with open(periods_path) as f: + periods_data = yaml.safe_load(f) or {} + else: + periods_data = {} + periods_data["elasticity"] = { + sr.season: sr.recommended_elasticity + for sr in ur.seasonal_recommendations + } + if ur.seasonal_recommendations_with_tech: + periods_data["elasticity_with_tech"] = { + sr.season: sr.recommended_elasticity + for sr in ur.seasonal_recommendations_with_tech + } + with open(periods_path, "w") as f: + yaml.dump(periods_data, f, default_flow_style=False, sort_keys=False) + log.info( + "Wrote elasticity %s and elasticity_with_tech %s to %s", + periods_data["elasticity"], + periods_data.get("elasticity_with_tech"), + periods_path, + ) + + if args.output_dir: + args.output_dir.mkdir(parents=True, exist_ok=True) + df = results_to_dataframe(results) + csv_path = args.output_dir / "demand_flex_diagnostic.csv" + df.write_csv(csv_path) + log.info("Wrote diagnostic CSV: %s", csv_path) + + parquet_path = args.output_dir / "demand_flex_diagnostic.parquet" + df.write_parquet(parquet_path) + log.info("Wrote diagnostic parquet: %s", parquet_path) + + if args.compare_batch: + log.info("Comparing against CAIRO batch: %s", args.compare_batch) + comparisons = compare_batch_bills( + state=args.state, + batch=args.compare_batch, + results=results, + hp_meta=hp_meta, + with_tech=args.with_tech, + ) + if comparisons: + print_batch_comparison(args.compare_batch, comparisons) + else: + log.warning("No comparisons could be made for batch %s", args.compare_batch) + + +if __name__ == "__main__": + main() From f56475ed7cfb43427ec2a8402936f46801b78c2d Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:20 +0000 Subject: [PATCH 10/19] Read no-tech or with-tech elasticity from periods YAML based on enabling_tech _row_to_run now reads enabling_tech from the Google Sheet row and selects elasticity_with_tech from the periods YAML when enabling_tech is empty, true, yes, or 1 (with-tech is the default). Only explicit false/no/0 selects the no-tech elasticity key. This makes enabling technology opt-out rather than opt-in, matching the intended workflow. Made-with: Cursor --- utils/pre/create_scenario_yamls.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/utils/pre/create_scenario_yamls.py b/utils/pre/create_scenario_yamls.py index cf569d66..6e9ebb99 100644 --- a/utils/pre/create_scenario_yamls.py +++ b/utils/pre/create_scenario_yamls.py @@ -325,7 +325,27 @@ def parse_required_float(key: str) -> float: except ValueError: run["sample_size"] = sample_size - run["elasticity"] = parse_required_float("elasticity") + elasticity_raw = require_non_empty("elasticity") + try: + run["elasticity"] = float(elasticity_raw) + except ValueError: + state = str(run["state"]).lower() + config_dir = get_project_root() / "rate_design" / "hp_rates" / state / "config" + periods_path = config_dir / elasticity_raw + if not periods_path.exists(): + raise FileNotFoundError( + f"Elasticity path {elasticity_raw!r} resolved to " + f"{periods_path} which does not exist" + ) + with open(periods_path) as f: + periods_data = yaml.safe_load(f) + + enabling_tech = get_optional("enabling_tech").strip().lower() + use_with_tech = enabling_tech not in ("false", "no", "0") + yaml_key = "elasticity_with_tech" if use_with_tech else "elasticity" + if yaml_key not in periods_data: + raise ValueError(f"No '{yaml_key}' key in {periods_path}") + run["elasticity"] = periods_data[yaml_key] return run From 776b419a01bd2a5911403b880a77b2c8adb532fd Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:26 +0000 Subject: [PATCH 11/19] Add trailing newline to calibrated tariff JSON output json.dumps() does not append a newline, causing every _calibrated.json to fail the end-of-file-fixer pre-commit hook and show spurious diffs. Made-with: Cursor --- utils/mid/copy_calibrated_tariff_from_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/mid/copy_calibrated_tariff_from_run.py b/utils/mid/copy_calibrated_tariff_from_run.py index 2680e6f6..35901864 100644 --- a/utils/mid/copy_calibrated_tariff_from_run.py +++ b/utils/mid/copy_calibrated_tariff_from_run.py @@ -49,7 +49,7 @@ def _read_json(path: Path | S3Path) -> dict[str, Any]: def _write_json(path: Path, payload: dict[str, Any]) -> Path: """Write formatted JSON to local disk.""" path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(payload, indent=2), encoding="utf-8") + path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") return path From 12902a9597e8d9097cb038cd35846fcc8c02d7f4 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:36 +0000 Subject: [PATCH 12/19] Fix ty/ruff errors and add usage examples to validate_demand_flex_shift - Add comprehensive usage examples (just recipes and direct uv run) to the module docstring covering scalar, seasonal, no-tech, with-tech, and CAIRO cross-check invocations - Fix DatetimeIndex.month access: use .to_series().dt.month.to_numpy() to avoid ty unresolved-attribute and pandas alignment errors - Cast period_rate to pd.Series to satisfy ty invalid-assignment check - Remove unused bldg_level assignment and dead p_flat groupby block - Remove stale type: ignore[arg-type] comments - Fix set_xticklabels: pass list[str] instead of range Made-with: Cursor --- utils/post/validate_demand_flex_shift.py | 417 ++++++++++++++++------- 1 file changed, 295 insertions(+), 122 deletions(-) diff --git a/utils/post/validate_demand_flex_shift.py b/utils/post/validate_demand_flex_shift.py index 269335bd..fdd6b453 100644 --- a/utils/post/validate_demand_flex_shift.py +++ b/utils/post/validate_demand_flex_shift.py @@ -7,18 +7,45 @@ 3. Produces diagnostic plots (daily profiles, heatmaps, distributions) 4. Writes data outputs for report reuse -Usage: +Invoke via the Justfile: + + # One utility, scalar elasticity (no CAIRO cross-check): + just -f rate_design/hp_rates/ny/Justfile validate-demand-flex cenhud + + # One utility with seasonal elasticities (matching periods YAML): + just -f rate_design/hp_rates/ny/Justfile validate-demand-flex cenhud "winter=-0.12,summer=-0.14" + + # With CAIRO tracker cross-check (no-tech batch): + just -f rate_design/hp_rates/ny/Justfile validate-demand-flex cenhud "winter=-0.12,summer=-0.14" ny_20260325b_r1-16 + + # With CAIRO tracker cross-check (with-tech batch): + just -f rate_design/hp_rates/ny/Justfile validate-demand-flex cenhud "winter=-0.18,summer=-0.22" ny_20260326_elast_seasonal_tech + + # All utilities (scalar, no batch): + just -f rate_design/hp_rates/ny/Justfile validate-demand-flex-all + +Or directly: + + # Scalar elasticity: + uv run python -m utils.post.validate_demand_flex_shift \ + --utility coned --elasticity -0.10 \ + --output-dir dev_plots/flex/coned + + # Seasonal elasticities (no-tech values from periods YAML): uv run python -m utils.post.validate_demand_flex_shift \ - --utility coned \ - --elasticity -0.10 \ - --output-dir dev_plots/flex + --utility cenhud --elasticity winter=-0.12,summer=-0.14 \ + --output-dir dev_plots/flex/cenhud + + # Seasonal elasticities (with-tech values): + uv run python -m utils.post.validate_demand_flex_shift \ + --utility cenhud --elasticity winter=-0.18,summer=-0.22 \ + --output-dir dev_plots/flex/cenhud # With CAIRO tracker cross-check: uv run python -m utils.post.validate_demand_flex_shift \ - --utility coned \ - --elasticity -0.10 \ - --batch ny_20260325b_r1-16 \ - --output-dir dev_plots/flex + --utility cenhud --elasticity winter=-0.12,summer=-0.14 \ + --batch ny_20260326_elast_seasonal_tech \ + --output-dir dev_plots/flex/cenhud """ from __future__ import annotations @@ -28,6 +55,7 @@ import logging import sys from pathlib import Path +from typing import cast import matplotlib.pyplot as plt import numpy as np @@ -46,14 +74,16 @@ log = logging.getLogger(__name__) plt.style.use("seaborn-v0_8-whitegrid") -plt.rcParams.update({ - "figure.dpi": 150, - "savefig.dpi": 150, - "savefig.bbox": "tight", - "font.size": 10, - "axes.titlesize": 12, - "axes.labelsize": 10, -}) +plt.rcParams.update( + { + "figure.dpi": 150, + "savefig.dpi": 150, + "savefig.bbox": "tight", + "font.size": 10, + "axes.titlesize": 12, + "axes.labelsize": 10, + } +) WINTER_MONTHS = {1, 2, 3, 10, 11, 12} SUMMER_MONTHS = {4, 5, 6, 7, 8, 9} @@ -120,10 +150,12 @@ def load_building_loads(bldg_ids: list[int], loads_dir: Path) -> pd.DataFrame: path, columns=["timestamp", "out.electricity.net.energy_consumption"], ).to_pandas() - df = df.rename(columns={ - "timestamp": "time", - "out.electricity.net.energy_consumption": "electricity_net", - }) + df = df.rename( + columns={ + "timestamp": "time", + "out.electricity.net.energy_consumption": "electricity_net", + } + ) df["bldg_id"] = bid frames.append(df) @@ -162,16 +194,19 @@ def reproduce_shift( period_rate: pd.Series, period_map: pd.Series, season_specs: list[SeasonTouSpec], - elasticity: float, + elasticity: float | dict[str, float], ) -> pd.DataFrame: """Apply constant-elasticity shifting to all buildings, return hourly results. + Args: + elasticity: Scalar applied to all seasons, or a ``{season_name: value}`` + dict for per-season elasticity. + Returns a DataFrame indexed like loads_df with columns: electricity_net_orig, electricity_net_shifted, hourly_shift_kw, energy_period, hour_of_day, month, season, tou_rate """ time_level = pd.DatetimeIndex(loads_df.index.get_level_values("time")) - bldg_level = loads_df.index.get_level_values("bldg_id") result_parts: list[pd.DataFrame] = [] @@ -179,7 +214,15 @@ def reproduce_shift( season_months = set(spec.season.months) season_name = spec.season.name - season_mask = time_level.month.isin(season_months) + season_eps = ( + elasticity.get(season_name, 0.0) + if isinstance(elasticity, dict) + else elasticity + ) + if season_eps == 0.0: + continue + + season_mask = time_level.to_series().dt.month.isin(season_months).to_numpy() season_df = loads_df.loc[season_mask].copy().reset_index() period_lookup = period_map.reset_index() @@ -190,7 +233,7 @@ def reproduce_shift( p_flat = _compute_equivalent_flat_tariff(period_consumption, period_rate) targets = _build_period_shift_targets( - period_consumption, period_rate, elasticity, p_flat, receiver_period=None + period_consumption, period_rate, season_eps, p_flat, receiver_period=None ) # Distribute period-level shifts to hourly rows proportionally @@ -234,9 +277,7 @@ def reproduce_shift( ] ) - result = pd.concat(result_parts, ignore_index=True).sort_values( - ["bldg_id", "time"] - ) + result = pd.concat(result_parts, ignore_index=True).sort_values(["bldg_id", "time"]) log.info("Reproduced shift: %d hourly rows", len(result)) return result @@ -273,14 +314,6 @@ def run_validation_checks( # Direction: peak kWh should decrease, off-peak should increase rates = period_rate.to_dict() - p_flat = sh.groupby("energy_period").apply( - lambda g: (g["electricity_net_orig"] * g["tou_rate"]).sum() - / g["electricity_net_orig"].sum() - if g["electricity_net_orig"].sum() > 0 - else 0.0, - include_groups=False, - ) - # Use the first building's p_flat as representative period_consumption_all = sh.groupby("energy_period").agg( orig=("electricity_net_orig", "sum"), shifted=("electricity_net_shifted", "sum"), @@ -290,15 +323,14 @@ def run_validation_checks( ) global_p_flat = ( - (period_consumption_all["orig"] * pd.Series(rates)).sum() - / period_consumption_all["orig"].sum() - ) + period_consumption_all["orig"] * pd.Series(rates) + ).sum() / period_consumption_all["orig"].sum() p_flat_map[sn] = float(global_p_flat) direction_ok = True direction_details = {} for ep, row in period_consumption_all.iterrows(): - ep_rate = rates.get(int(ep), 0.0) # type: ignore[arg-type] + ep_rate = rates.get(int(ep), 0.0) if ep_rate > global_p_flat: expected_sign = "decrease" ok = row["shift"] <= 0.01 @@ -306,7 +338,7 @@ def run_validation_checks( expected_sign = "increase" ok = row["shift"] >= -0.01 direction_ok = direction_ok and ok - direction_details[int(ep)] = { # type: ignore[arg-type] + direction_details[int(ep)] = { "rate": float(ep_rate), "is_peak": ep_rate > global_p_flat, "shift_kwh": float(row["shift"]), @@ -381,9 +413,7 @@ def _get_peak_hours(spec: SeasonTouSpec) -> set[int]: return set(spec.peak_hours) -def _season_spec_by_name( - specs: list[SeasonTouSpec], name: str -) -> SeasonTouSpec | None: +def _season_spec_by_name(specs: list[SeasonTouSpec], name: str) -> SeasonTouSpec | None: for s in specs: if s.season.name == name: return s @@ -439,22 +469,49 @@ def plot_building_daily_profile( if h in peak_hours: ax.axvspan(h - 0.5, h + 0.5, alpha=0.12, color="red", zorder=0) - ax.plot(hours, orig, "o-", color="#2c3e50", linewidth=1.8, markersize=3, - label="Original", zorder=3) - ax.plot(hours, shifted, "s--", color="#e74c3c", linewidth=1.8, markersize=3, - label="Shifted", zorder=3) + ax.plot( + hours, + orig, + "o-", + color="#2c3e50", + linewidth=1.8, + markersize=3, + label="Original", + zorder=3, + ) + ax.plot( + hours, + shifted, + "s--", + color="#e74c3c", + linewidth=1.8, + markersize=3, + label="Shifted", + zorder=3, + ) ax2 = ax.twinx() - ax2.step(hours, rates, where="mid", color="#7f8c8d", linewidth=1.0, - alpha=0.6, label="TOU rate") + ax2.step( + hours, + rates, + where="mid", + color="#7f8c8d", + linewidth=1.0, + alpha=0.6, + label="TOU rate", + ) ax2.set_ylabel("TOU rate ($/kWh)", color="#7f8c8d") ax2.tick_params(axis="y", labelcolor="#7f8c8d") shift_kwh = float(day["hourly_shift_kw"].sum()) - peak_orig_kwh = float(day.loc[day["hour_of_day"].isin(peak_hours), - "electricity_net_orig"].sum()) - peak_shifted_kwh = float(day.loc[day["hour_of_day"].isin(peak_hours), - "electricity_net_shifted"].sum()) + peak_orig_kwh = float( + day.loc[day["hour_of_day"].isin(peak_hours), "electricity_net_orig"].sum() + ) + peak_shifted_kwh = float( + day.loc[ + day["hour_of_day"].isin(peak_hours), "electricity_net_shifted" + ].sum() + ) peak_red = ( (peak_orig_kwh - peak_shifted_kwh) / peak_orig_kwh * 100 if peak_orig_kwh > 0 @@ -471,7 +528,11 @@ def plot_building_daily_profile( f"Ratio: {spec.peak_offpeak_ratio:.2f}" ) ax.text( - 0.02, 0.98, textstr, transform=ax.transAxes, fontsize=8, + 0.02, + 0.98, + textstr, + transform=ax.transAxes, + fontsize=8, verticalalignment="top", bbox={"boxstyle": "round,pad=0.3", "facecolor": "wheat", "alpha": 0.5}, ) @@ -481,7 +542,8 @@ def plot_building_daily_profile( fig.suptitle( f"{utility.upper()} — Building {bldg_id} daily profile", - fontsize=13, fontweight="bold", + fontsize=13, + fontweight="bold", ) fig.tight_layout() path = output_dir / f"building_daily_profile_{bldg_id}.png" @@ -522,29 +584,55 @@ def plot_aggregate_daily_profile( if h in peak_hours: ax.axvspan(h - 0.5, h + 0.5, alpha=0.10, color="red", zorder=0) - ax.plot(hours, orig, "o-", color="#2c3e50", linewidth=2, markersize=3, - label="Original") - ax.plot(hours, shifted, "s-", color="#e74c3c", linewidth=2, markersize=3, - label="Shifted") + ax.plot( + hours, + orig, + "o-", + color="#2c3e50", + linewidth=2, + markersize=3, + label="Original", + ) + ax.plot( + hours, + shifted, + "s-", + color="#e74c3c", + linewidth=2, + markersize=3, + label="Shifted", + ) ax.fill_between( - hours, orig, shifted, - where=orig > shifted, interpolate=True, - alpha=0.25, color="#e74c3c", label="Load removed (peak)", + hours, + orig, + shifted, + where=orig > shifted, + interpolate=True, + alpha=0.25, + color="#e74c3c", + label="Load removed (peak)", ) ax.fill_between( - hours, orig, shifted, - where=shifted > orig, interpolate=True, - alpha=0.25, color="#3498db", label="Load added (off-peak)", + hours, + orig, + shifted, + where=shifted > orig, + interpolate=True, + alpha=0.25, + color="#3498db", + label="Load added (off-peak)", ) total_orig_peak = float( - sdata.loc[sdata["hour_of_day"].isin(peak_hours), - "electricity_net_orig"].sum() + sdata.loc[ + sdata["hour_of_day"].isin(peak_hours), "electricity_net_orig" + ].sum() ) total_shifted_peak = float( - sdata.loc[sdata["hour_of_day"].isin(peak_hours), - "electricity_net_shifted"].sum() + sdata.loc[ + sdata["hour_of_day"].isin(peak_hours), "electricity_net_shifted" + ].sum() ) n_bldgs = sdata["bldg_id"].nunique() peak_red = ( @@ -565,7 +653,11 @@ def plot_aggregate_daily_profile( f"Ratio: {spec.peak_offpeak_ratio:.2f}" ) ax.text( - 0.02, 0.98, textstr, transform=ax.transAxes, fontsize=8, + 0.02, + 0.98, + textstr, + transform=ax.transAxes, + fontsize=8, verticalalignment="top", bbox={"boxstyle": "round,pad=0.3", "facecolor": "wheat", "alpha": 0.5}, ) @@ -574,7 +666,8 @@ def plot_aggregate_daily_profile( axes[0].legend(loc="upper right", fontsize=7) fig.suptitle( f"{utility.upper()} — Aggregate daily load profile", - fontsize=13, fontweight="bold", + fontsize=13, + fontweight="bold", ) fig.tight_layout() path = output_dir / "aggregate_daily_profile.png" @@ -607,8 +700,14 @@ def plot_net_shift_by_hour( mean_shift = sdata.groupby("hour_of_day")["hourly_shift_kw"].mean() colors = ["#e74c3c" if v < 0 else "#3498db" for v in mean_shift.values] - ax.bar(mean_shift.index, mean_shift.values, color=colors, width=0.8, - edgecolor="white", linewidth=0.5) + ax.bar( + mean_shift.index, + mean_shift.values, + color=colors, + width=0.8, + edgecolor="white", + linewidth=0.5, + ) for h in range(24): if h in peak_hours: @@ -622,16 +721,24 @@ def plot_net_shift_by_hour( net_check = mean_shift.sum() ax.text( - 0.98, 0.02, + 0.98, + 0.02, f"Net: {net_check:+.4f} kWh\n(should be ~0)", - transform=ax.transAxes, fontsize=7, ha="right", - bbox={"boxstyle": "round,pad=0.3", "facecolor": "lightyellow", "alpha": 0.7}, + transform=ax.transAxes, + fontsize=7, + ha="right", + bbox={ + "boxstyle": "round,pad=0.3", + "facecolor": "lightyellow", + "alpha": 0.7, + }, ) axes[0].set_ylabel("Mean shift (kWh)") fig.suptitle( f"{utility.upper()} — Net load shift by hour", - fontsize=13, fontweight="bold", + fontsize=13, + fontweight="bold", ) fig.tight_layout() path = output_dir / "net_shift_by_hour.png" @@ -663,12 +770,22 @@ def plot_shift_heatmap( ax.set_yticks(range(len(heatmap_data.index))) month_names = [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", ] ax.set_yticklabels([month_names[m - 1] for m in heatmap_data.index]) ax.set_xticks(range(24)) - ax.set_xticklabels(range(24), fontsize=8) + ax.set_xticklabels([str(h) for h in range(24)], fontsize=8) ax.set_xlabel("Hour of day") ax.set_ylabel("Month") @@ -676,7 +793,9 @@ def plot_shift_heatmap( for spec in season_specs: peak_hours = _get_peak_hours(spec) for m in spec.season.months: - row_idx = list(heatmap_data.index).index(m) if m in heatmap_data.index else None + row_idx = ( + list(heatmap_data.index).index(m) if m in heatmap_data.index else None + ) if row_idx is None: continue for h in peak_hours: @@ -687,7 +806,8 @@ def plot_shift_heatmap( ax.set_title( f"{utility.upper()} — Hourly load shift heatmap" f" (red=removed, blue=added, dots=peak hours)", - fontsize=12, fontweight="bold", + fontsize=12, + fontweight="bold", ) fig.tight_layout() path = output_dir / "shift_heatmap.png" @@ -727,9 +847,7 @@ def plot_peak_reduction_distribution( 0.0, ) - data_range = ( - bldg_peak["reduction_pct"].max() - bldg_peak["reduction_pct"].min() - ) + data_range = bldg_peak["reduction_pct"].max() - bldg_peak["reduction_pct"].min() n_bins = max(5, min(30, int(data_range / 0.3))) if data_range > 0.01 else 5 ax.hist( bldg_peak["reduction_pct"], @@ -741,10 +859,20 @@ def plot_peak_reduction_distribution( mean_red = bldg_peak["reduction_pct"].mean() median_red = bldg_peak["reduction_pct"].median() - ax.axvline(mean_red, color="#e74c3c", linewidth=2, linestyle="-", - label=f"Mean: {mean_red:.1f}%") - ax.axvline(median_red, color="#2ecc71", linewidth=2, linestyle="--", - label=f"Median: {median_red:.1f}%") + ax.axvline( + mean_red, + color="#e74c3c", + linewidth=2, + linestyle="-", + label=f"Mean: {mean_red:.1f}%", + ) + ax.axvline( + median_red, + color="#2ecc71", + linewidth=2, + linestyle="--", + label=f"Median: {median_red:.1f}%", + ) ax.set_title(f"{sn.title()} (n={len(bldg_peak)} buildings)") ax.set_xlabel("Peak reduction (%)") @@ -757,14 +885,24 @@ def plot_peak_reduction_distribution( f" {bldg_peak['reduction_pct'].max():.1f}%]" ) ax.text( - 0.98, 0.98, textstr, transform=ax.transAxes, fontsize=8, - ha="right", va="top", - bbox={"boxstyle": "round,pad=0.3", "facecolor": "lightyellow", "alpha": 0.7}, + 0.98, + 0.98, + textstr, + transform=ax.transAxes, + fontsize=8, + ha="right", + va="top", + bbox={ + "boxstyle": "round,pad=0.3", + "facecolor": "lightyellow", + "alpha": 0.7, + }, ) fig.suptitle( f"{utility.upper()} — Per-building peak reduction distribution", - fontsize=13, fontweight="bold", + fontsize=13, + fontweight="bold", ) fig.tight_layout() path = output_dir / "peak_reduction_distribution.png" @@ -800,9 +938,7 @@ def write_data_outputs( # Shift heatmap data CSV hm = ( - hourly.groupby(["month", "hour_of_day"])["hourly_shift_kw"] - .mean() - .reset_index() + hourly.groupby(["month", "hour_of_day"])["hourly_shift_kw"].mean().reset_index() ) hm.columns = ["month", "hour_of_day", "mean_shift_kw"] hm_path = output_dir / f"{utility}_shift_heatmap_data.csv" @@ -858,14 +994,14 @@ def crosscheck_cairo_tracker( import subprocess import tempfile - s3_prefix = ( - f"s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/{utility}/{batch}/" - ) + s3_prefix = f"s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/{utility}/{batch}/" # List run dirs to find the run13 tracker result = subprocess.run( ["aws", "s3", "ls", s3_prefix, "--recursive"], - capture_output=True, text=True, timeout=30, + capture_output=True, + text=True, + timeout=30, ) tracker_s3 = None for line in result.stdout.splitlines(): @@ -884,7 +1020,8 @@ def crosscheck_cairo_tracker( tmp_path = tmp.name subprocess.run( ["aws", "s3", "cp", tracker_s3, tmp_path], - capture_output=True, timeout=30, + capture_output=True, + timeout=30, ) cairo_tracker = pd.read_csv(tmp_path) Path(tmp_path).unlink(missing_ok=True) @@ -902,16 +1039,19 @@ def crosscheck_cairo_tracker( if sdata.empty: continue - bldg_period = sdata.groupby(["bldg_id", "energy_period"]).agg( - orig=("electricity_net_orig", "sum"), - shifted=("electricity_net_shifted", "sum"), - ).reset_index() + bldg_period = ( + sdata.groupby(["bldg_id", "energy_period"]) + .agg( + orig=("electricity_net_orig", "sum"), + shifted=("electricity_net_shifted", "sum"), + ) + .reset_index() + ) bldg_period["rate"] = bldg_period["energy_period"].map(rates) global_p_flat = ( - (sdata["electricity_net_orig"] * sdata["tou_rate"]).sum() - / sdata["electricity_net_orig"].sum() - ) + sdata["electricity_net_orig"] * sdata["tou_rate"] + ).sum() / sdata["electricity_net_orig"].sum() valid = ( (bldg_period["orig"] > 0) @@ -976,11 +1116,35 @@ def crosscheck_cairo_tracker( # ── Main ────────────────────────────────────────────────────────────────────── +def _parse_elasticity(value: str) -> float | dict[str, float]: + """Parse elasticity as a scalar or ``season=value,...`` dict. + + Examples: ``-0.12``, ``winter=-0.12,summer=-0.14``. + """ + try: + return float(value) + except ValueError: + pass + pairs = {} + for part in value.split(","): + k, _, v = part.partition("=") + k = k.strip() + v = v.strip() + if not k or not v: + raise argparse.ArgumentTypeError( + f"Invalid elasticity format: {value!r}. " + "Use a number (e.g. -0.12) or season=value pairs " + "(e.g. winter=-0.12,summer=-0.14)." + ) + pairs[k] = float(v) + return pairs + + def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: p = argparse.ArgumentParser(description=__doc__) p.add_argument("--utility", required=True) p.add_argument("--state", default="ny", choices=list(STATE_CONFIGS.keys())) - p.add_argument("--elasticity", type=float, default=-0.10) + p.add_argument("--elasticity", type=_parse_elasticity, default=-0.10) p.add_argument("--batch", default=None, help="S3 batch for CAIRO cross-check") p.add_argument("--output-dir", type=Path, required=True) p.add_argument( @@ -1027,10 +1191,13 @@ def main(argv: list[str] | None = None) -> None: tou_deriv_dir = project_root / cfg["path_tou_derivation_dir"] tariffs_dir = project_root / cfg["path_tariffs_electric_dir"] specs, tou_tariff = load_tou_context(args.utility, tou_deriv_dir, tariffs_dir) - log.info("TOU specs: %s", [(s.season.name, f"ratio={s.peak_offpeak_ratio:.2f}") for s in specs]) + log.info( + "TOU specs: %s", + [(s.season.name, f"ratio={s.peak_offpeak_ratio:.2f}") for s in specs], + ) rate_df = extract_tou_period_rates(tou_tariff) - period_rate = rate_df.groupby("energy_period")["rate"].first() + period_rate = cast(pd.Series, rate_df.groupby("energy_period")["rate"].first()) log.info("Period rates:\n%s", period_rate) # Load building loads @@ -1042,7 +1209,7 @@ def main(argv: list[str] | None = None) -> None: period_map = assign_hourly_periods(time_idx, tou_tariff) # Reproduce the shift - log.info("Reproducing demand-flex shift (ε=%.2f)...", args.elasticity) + log.info("Reproducing demand-flex shift (ε=%s)...", args.elasticity) hourly = reproduce_shift(loads_df, period_rate, period_map, specs, args.elasticity) # Validation checks @@ -1079,7 +1246,9 @@ def main(argv: list[str] | None = None) -> None: rng = np.random.default_rng(42) extra = rng.choice( remaining, - size=min(args.n_profile_buildings - len(illustrative_bldgs), len(remaining)), + size=min( + args.n_profile_buildings - len(illustrative_bldgs), len(remaining) + ), replace=False, ) illustrative_bldgs.extend(extra) @@ -1090,7 +1259,10 @@ def main(argv: list[str] | None = None) -> None: # Data outputs log.info("Writing data outputs...") write_data_outputs( - hourly, specs, args.utility, output_dir, + hourly, + specs, + args.utility, + output_dir, n_example_buildings=args.n_example_buildings, ) @@ -1121,20 +1293,21 @@ def _make_serializable(obj: object) -> object: summary_path = output_dir / f"{args.utility}_validation_summary.json" with open(summary_path, "w") as f: json_mod.dump( - _make_serializable({ - "utility": args.utility, - "elasticity": args.elasticity, - "n_buildings": len(hp_bldg_ids), - "checks": checks, - }), + _make_serializable( + { + "utility": args.utility, + "elasticity": args.elasticity, + "n_buildings": len(hp_bldg_ids), + "checks": checks, + } + ), f, indent=2, ) log.info("Wrote %s", summary_path) all_pass = all( - checks["energy_conservation"][s]["pass"] - and checks["direction"][s]["pass"] + checks["energy_conservation"][s]["pass"] and checks["direction"][s]["pass"] for s in checks["energy_conservation"] ) print(f"\n{'=' * 70}") From 29846ca1ef78a4fed7ca254d4eb7839daf19f0e6 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:43 +0000 Subject: [PATCH 13/19] Remove superseded demand-flex scripts and stale tests diagnose_demand_flex.py, sensitivity_demand_flex.py, and validate_demand_flex.py are replaced by calibrate_demand_flex_elasticity (now in utils/pre) and validate_demand_flex_shift. The corresponding test file is also removed as it tested the deleted validate_demand_flex. Made-with: Cursor --- tests/test_validate_demand_flex.py | 222 -------- utils/post/diagnose_demand_flex.py | 770 -------------------------- utils/post/sensitivity_demand_flex.py | 269 --------- utils/post/validate_demand_flex.py | 661 ---------------------- 4 files changed, 1922 deletions(-) delete mode 100644 tests/test_validate_demand_flex.py delete mode 100644 utils/post/diagnose_demand_flex.py delete mode 100644 utils/post/sensitivity_demand_flex.py delete mode 100644 utils/post/validate_demand_flex.py diff --git a/tests/test_validate_demand_flex.py b/tests/test_validate_demand_flex.py deleted file mode 100644 index 8839e427..00000000 --- a/tests/test_validate_demand_flex.py +++ /dev/null @@ -1,222 +0,0 @@ -"""Tests for analytical demand-flex validation and sensitivity analysis.""" - -from __future__ import annotations - -import pytest - -from utils.post.validate_demand_flex import ( - SyntheticScenario, - build_synthetic_scenario, - check_achieved_elasticity_near_target, - check_energy_conservation, - check_frozen_residual_identity, - check_mc_delta_monotonic_in_elasticity, - check_mc_delta_negative, - check_nonhp_subclass_rr_unchanged, - check_rr_decreases, - check_shifted_loads_nonnegative, - check_tou_ratio_does_not_increase, - check_tou_subclass_rr_absorbs_delta, - run_all_checks, -) -from utils.post.sensitivity_demand_flex import ( - compute_sweep_point, - results_to_dataframe, - run_sweep, -) - - -@pytest.fixture(scope="module") -def scenario() -> SyntheticScenario: - """Shared synthetic scenario (expensive to build, reuse across tests).""" - return build_synthetic_scenario() - - -# --------------------------------------------------------------------------- -# Individual check tests -# --------------------------------------------------------------------------- - - -class TestEnergyConservation: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_energy_conservation(scenario, elasticity=-0.1) - assert result.passed - assert result.details["rel_diff"] < 1e-8 - - def test_passes_at_various_elasticities(self, scenario: SyntheticScenario) -> None: - for e in [-0.05, -0.10, -0.15, -0.20]: - result = check_energy_conservation(scenario, elasticity=e) - assert result.passed, f"Failed at elasticity={e}" - - -class TestMcDeltaNegative: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_mc_delta_negative(scenario, elasticity=-0.1) - assert result.passed - assert result.details["mc_delta"] < 0 - - def test_larger_elasticity_gives_larger_delta( - self, scenario: SyntheticScenario - ) -> None: - r1 = check_mc_delta_negative(scenario, elasticity=-0.05) - r2 = check_mc_delta_negative(scenario, elasticity=-0.15) - assert abs(r2.details["mc_delta"]) > abs(r1.details["mc_delta"]) - - -class TestFrozenResidualIdentity: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_frozen_residual_identity(scenario, elasticity=-0.1) - assert result.passed - assert result.details["diff"] < 1.0 - - -class TestRrDecreases: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_rr_decreases(scenario, elasticity=-0.1) - assert result.passed - - -class TestTouRatioDoesNotIncrease: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_tou_ratio_does_not_increase(scenario, elasticity=-0.1) - assert result.passed - - def test_ratio_within_tolerance(self, scenario: SyntheticScenario) -> None: - result = check_tou_ratio_does_not_increase(scenario, elasticity=-0.1) - assert result.details["delta"] < 1e-3 - - -class TestNonhpSubclassRrUnchanged: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_nonhp_subclass_rr_unchanged(scenario, elasticity=-0.1) - assert result.passed - - -class TestTouSubclassRrAbsorbsDelta: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_tou_subclass_rr_absorbs_delta(scenario, elasticity=-0.1) - assert result.passed - - -class TestAchievedElasticityNearTarget: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_achieved_elasticity_near_target(scenario, elasticity=-0.1) - assert result.passed or result.status == "WARN" - - -class TestShiftedLoadsNonnegative: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_shifted_loads_nonnegative(scenario, elasticity=-0.1) - assert result.passed - - def test_fails_with_extreme_elasticity(self) -> None: - """Extreme elasticity can push loads negative on thin load buildings.""" - extreme_scenario = build_synthetic_scenario(n_tou_bldgs=3, seed=99) - result = check_shifted_loads_nonnegative(extreme_scenario, elasticity=-5.0) - # With such extreme elasticity on synthetic data, loads may go negative. - # Either outcome (PASS or FAIL) validates the check's logic is exercised. - assert result.name == "shifted_loads_nonnegative" - if result.status == "FAIL": - assert result.details["n_negative"] > 0 - - -class TestMcDeltaMonotonicInElasticity: - def test_passes_on_synthetic(self, scenario: SyntheticScenario) -> None: - result = check_mc_delta_monotonic_in_elasticity(scenario) - assert result.passed - assert result.details["is_monotonic"] - - def test_custom_elasticities(self, scenario: SyntheticScenario) -> None: - result = check_mc_delta_monotonic_in_elasticity( - scenario, elasticities=[-0.02, -0.08, -0.12] - ) - assert result.passed - - -# --------------------------------------------------------------------------- -# Run-all checks -# --------------------------------------------------------------------------- - - -class TestRunAllChecks: - def test_all_pass_on_synthetic(self, scenario: SyntheticScenario) -> None: - results = run_all_checks(scenario, elasticity=-0.1) - assert len(results) == 10 - for r in results: - assert r.status in ("PASS", "WARN"), f"{r.name} -> {r.status}: {r.message}" - - def test_all_checks_have_unique_names(self, scenario: SyntheticScenario) -> None: - results = run_all_checks(scenario, elasticity=-0.1) - names = [r.name for r in results] - assert len(names) == len(set(names)) - - -# --------------------------------------------------------------------------- -# Sensitivity sweep tests -# --------------------------------------------------------------------------- - - -class TestSensitivitySweep: - def test_sweep_returns_correct_count(self, scenario: SyntheticScenario) -> None: - elasticities = [-0.05, -0.10, -0.15, -0.20] - results = run_sweep(scenario, elasticities) - assert len(results) == len(elasticities) - - def test_sweep_mc_deltas_are_negative(self, scenario: SyntheticScenario) -> None: - results = run_sweep(scenario, [-0.05, -0.10, -0.15]) - for r in results: - assert r.mc_delta_dollars < 0, f"MC delta should be negative at e={r.elasticity}" - - def test_sweep_mc_deltas_are_monotonic(self, scenario: SyntheticScenario) -> None: - results = run_sweep(scenario, [-0.05, -0.10, -0.15, -0.20]) - magnitudes = [abs(r.mc_delta_dollars) for r in results] - for i in range(len(magnitudes) - 1): - assert magnitudes[i] <= magnitudes[i + 1] - - def test_sweep_rr_decreases_monotonically( - self, scenario: SyntheticScenario - ) -> None: - results = run_sweep(scenario, [-0.05, -0.10, -0.15, -0.20]) - for r in results: - assert r.rr_change_dollars < 0 - - def test_sweep_tou_ratio_does_not_increase( - self, scenario: SyntheticScenario - ) -> None: - results = run_sweep(scenario, [-0.05, -0.10, -0.15, -0.20]) - for r in results: - assert r.tou_ratio_change < 1e-3, ( - f"TOU ratio increased significantly at e={r.elasticity}" - ) - - def test_results_to_dataframe(self, scenario: SyntheticScenario) -> None: - results = run_sweep(scenario, [-0.05, -0.10]) - df = results_to_dataframe(results) - assert len(df) == 2 - assert "elasticity" in df.columns - assert "mc_delta_dollars" in df.columns - - def test_single_point(self, scenario: SyntheticScenario) -> None: - result = compute_sweep_point(scenario, -0.10) - assert result.load_shift_kwh > 0 - assert result.min_shifted_load >= 0 - - -# --------------------------------------------------------------------------- -# Edge cases -# --------------------------------------------------------------------------- - - -class TestEdgeCases: - def test_single_building(self) -> None: - s = build_synthetic_scenario(n_tou_bldgs=1, n_nontou_bldgs=1) - results = run_all_checks(s, elasticity=-0.1) - for r in results: - assert r.status in ("PASS", "WARN"), f"{r.name} -> {r.status}: {r.message}" - - def test_zero_elasticity_energy_conservation(self) -> None: - """Zero elasticity should result in no shift and perfect conservation.""" - s = build_synthetic_scenario() - result = check_energy_conservation(s, elasticity=0.0) - # Zero elasticity means Q_target = Q_orig, so no shift - assert result.passed diff --git a/utils/post/diagnose_demand_flex.py b/utils/post/diagnose_demand_flex.py deleted file mode 100644 index 572c1f3a..00000000 --- a/utils/post/diagnose_demand_flex.py +++ /dev/null @@ -1,770 +0,0 @@ -"""Per-utility demand-flex elasticity diagnostic for NY heat-pump rate design. - -For each utility, loads real TOU derivation data, ResStock HP building loads, -and marginal cost profiles to compute: - - Demand shift at each candidate elasticity (kWh moved, peak reduction %) - - Rate arbitrage savings (the primary bill savings mechanism) - - MC-based RR reduction savings (secondary, small) - - Arcturus "no enabling tech" comparison - - Recommended elasticity per utility - -Operates purely analytically (no CAIRO run required). Results can be validated -against actual CAIRO bill outputs from batch ny_20260325b_r1-16. - -Usage: - uv run python -m utils.post.diagnose_demand_flex \ - --state ny \ - --elasticities -0.05,-0.10,-0.15,-0.20 \ - --output-dir /tmp/demand_flex_diagnostic - - # Fast dev iteration with sampled buildings: - uv run python -m utils.post.diagnose_demand_flex \ - --state ny --sample-size 50 --output-dir /tmp/demand_flex_diagnostic -""" - -from __future__ import annotations - -import argparse -import json -import logging -import math -import sys -from dataclasses import dataclass, field -from pathlib import Path - -import numpy as np -import pandas as pd -import polars as pl - -from data.eia.hourly_loads.eia_region_config import get_aws_storage_options -from utils.cairo import ( - _build_period_consumption, - _build_period_shift_targets, - _compute_equivalent_flat_tariff, - assign_hourly_periods, - extract_tou_period_rates, -) -from utils.pre.compute_tou import SeasonTouSpec, load_season_specs - -log = logging.getLogger(__name__) - -# ── Constants ───────────────────────────────────────────────────────────────── - -WINTER_MONTHS = {1, 2, 3, 10, 11, 12} -SUMMER_MONTHS = {4, 5, 6, 7, 8, 9} - -ARCTURUS_INTERCEPT = -0.011 -ARCTURUS_SLOPE = -0.065 - -STATE_CONFIGS: dict[str, dict] = { - "ny": { - "utilities": ("cenhud", "coned", "nimo", "nyseg", "or", "psegli", "rge"), - "path_metadata": Path( - "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata/state=NY/" - "upgrade=00/metadata-sb.parquet" - ), - "path_utility_assignment": Path( - "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/metadata_utility/" - "state=NY/utility_assignment.parquet" - ), - "path_loads_dir": Path( - "/ebs/data/nrel/resstock/res_2024_amy2018_2_sb/load_curve_hourly/" - "state=NY/upgrade=00" - ), - "path_tou_derivation_dir": Path( - "rate_design/hp_rates/ny/config/tou_derivation" - ), - "path_tariffs_electric_dir": Path( - "rate_design/hp_rates/ny/config/tariffs/electric" - ), - "mc_base": "s3://data.sb/switchbox/marginal_costs/ny", - "mc_year": 2025, - }, -} - - -# ── Data structures ─────────────────────────────────────────────────────────── - - -@dataclass(frozen=True, slots=True) -class UtilityContext: - """All data needed to diagnose one utility.""" - - utility: str - season_specs: list[SeasonTouSpec] - tou_tariff: dict - hp_bldg_ids: list[int] - hp_weights: dict[int, float] - total_weighted_customers: float - hp_weighted_customers: float - - -@dataclass(slots=True) -class SeasonResult: - """Diagnostic results for one utility × season × elasticity.""" - - utility: str - season: str - elasticity: float - price_ratio: float - base_rate: float - peak_hours: list[int] - n_peak_hours_per_day: int - hp_bldg_count: int - hp_weighted_share: float - hp_peak_kwh_orig: float - hp_peak_kwh_shifted: float - peak_reduction_pct: float - kwh_shifted: float - system_peak_reduction_pct: float - p_flat: float - arcturus_peak_reduction_pct: float - delta_vs_arcturus_pct: float - rate_arbitrage_savings_per_hp: float - delivery_mc_savings_total: float - delivery_mc_savings_per_hp: float - delivery_mc_nonzero_hours: int - delivery_mc_peak_overlap_hours: int - - -@dataclass(slots=True) -class UtilityResult: - """Aggregated diagnostic results for one utility.""" - - utility: str - season_results: list[SeasonResult] = field(default_factory=list) - annual_weighted_ratio: float = 0.0 - annual_arcturus_pct: float = 0.0 - recommended_elasticity: float = 0.0 - recommended_annual_savings: float = 0.0 - - -# ── Arcturus model ──────────────────────────────────────────────────────────── - - -def arcturus_peak_reduction(price_ratio: float) -> float: - """Arcturus 2.0 'no enabling technology' peak reduction prediction. - - Returns a positive fraction (e.g. 0.056 for 5.6% reduction). - """ - if price_ratio <= 1.0: - return 0.0 - return abs(ARCTURUS_INTERCEPT + ARCTURUS_SLOPE * math.log(price_ratio)) - - -# ── Data loading ────────────────────────────────────────────────────────────── - - -def load_hp_metadata( - path_metadata: Path, - path_utility_assignment: Path, -) -> pl.DataFrame: - """Load metadata and return HP buildings with utility assignment and weight.""" - meta = pl.read_parquet(path_metadata).select( - "bldg_id", "postprocess_group.has_hp", "weight" - ) - util = pl.read_parquet(path_utility_assignment).select( - "bldg_id", "sb.electric_utility" - ) - joined = meta.join(util, on="bldg_id", how="inner") - hp = joined.filter(pl.col("postprocess_group.has_hp") == True) # noqa: E712 - log.info( - "Loaded %d HP buildings out of %d total", - hp.height, - joined.height, - ) - return hp - - -def load_building_loads( - bldg_ids: list[int], - loads_dir: Path, - sample_size: int | None = None, -) -> pd.DataFrame: - """Load hourly electric net load for a set of buildings from local parquet. - - Returns a pandas DataFrame with MultiIndex (bldg_id, time) and column - 'electricity_net' in kWh, matching CAIRO's expected format. - """ - if sample_size and sample_size < len(bldg_ids): - rng = np.random.default_rng(42) - bldg_ids = list(rng.choice(bldg_ids, size=sample_size, replace=False)) - - frames: list[pd.DataFrame] = [] - missing = 0 - for bid in bldg_ids: - path = loads_dir / f"{bid}-0.parquet" - if not path.exists(): - missing += 1 - continue - df = pl.read_parquet( - path, - columns=["timestamp", "out.electricity.net.energy_consumption"], - ).to_pandas() - df = df.rename( - columns={ - "timestamp": "time", - "out.electricity.net.energy_consumption": "electricity_net", - } - ) - df["bldg_id"] = bid - frames.append(df) - - if missing: - log.warning("Missing load files for %d of %d buildings", missing, len(bldg_ids)) - if not frames: - raise FileNotFoundError( - f"No load files found in {loads_dir} for {len(bldg_ids)} buildings" - ) - - combined = pd.concat(frames, ignore_index=True) - combined = combined.set_index(["bldg_id", "time"]).sort_index() - log.info( - "Loaded %d buildings x %d hours = %d rows", - len(frames), - 8760, - len(combined), - ) - return combined - - -def load_tou_context( - utility: str, - tou_derivation_dir: Path, - tariffs_electric_dir: Path, -) -> tuple[list[SeasonTouSpec], dict]: - """Load TOU derivation specs and the flex tariff for one utility.""" - deriv_path = tou_derivation_dir / f"{utility}_hp_seasonalTOU_derivation.json" - specs = load_season_specs(deriv_path) - - tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU_flex.json" - if not tariff_path.exists(): - tariff_path = tariffs_electric_dir / f"{utility}_hp_seasonalTOU.json" - with open(tariff_path) as f: - tou_tariff = json.load(f) - return specs, tou_tariff - - -def load_delivery_mc( - utility: str, - mc_base: str, - mc_year: int, - storage_options: dict[str, str], -) -> pd.DataFrame: - """Load delivery-only MC (dist+sub-tx + bulk_tx) for one utility. - - Returns DataFrame with columns ['timestamp', 'mc_kwh'] in $/kWh. - """ - dist_path = ( - f"{mc_base}/dist_and_sub_tx/utility={utility}/year={mc_year}/data.parquet" - ) - bulk_path = f"{mc_base}/bulk_tx/utility={utility}/year={mc_year}/data.parquet" - - dist = pl.read_parquet(dist_path, storage_options=storage_options) - if "timestamp" in dist.columns: - ts_dtype = dist.schema["timestamp"] - if isinstance(ts_dtype, pl.Datetime) and ts_dtype.time_zone is not None: - dist = dist.with_columns( - pl.col("timestamp").dt.replace_time_zone(None).alias("timestamp") - ) - dist = dist.select( - "timestamp", - pl.col("mc_total_per_kwh").alias("mc_dist"), - ) - - bulk = pl.read_parquet(bulk_path, storage_options=storage_options) - if "timestamp" in bulk.columns: - ts_dtype = bulk.schema["timestamp"] - if isinstance(ts_dtype, pl.Datetime) and ts_dtype.time_zone is not None: - bulk = bulk.with_columns( - pl.col("timestamp").dt.replace_time_zone(None).alias("timestamp") - ) - bulk = bulk.select( - "timestamp", - pl.col("bulk_tx_cost_enduse").alias("mc_bulk"), - ) - - mc = dist.join(bulk, on="timestamp", how="full", coalesce=True).sort("timestamp") - mc = mc.with_columns( - (pl.col("mc_dist").fill_null(0.0) + pl.col("mc_bulk").fill_null(0.0)).alias( - "mc_kwh" - ) - ) - return mc.select("timestamp", "mc_kwh").to_pandas() - - -# ── Core diagnostic logic ───────────────────────────────────────────────────── - - -def diagnose_season( - utility: str, - season_spec: SeasonTouSpec, - loads_df: pd.DataFrame, - period_rate: pd.Series, - period_map: pd.Series, - elasticity: float, - hp_weights: dict[int, float], - total_weighted_customers: float, - hp_weighted_customers: float, - mc_df: pd.DataFrame | None, -) -> SeasonResult: - """Run diagnostic for one utility × season × elasticity.""" - season_months = set(season_spec.season.months) - season_name = season_spec.season.name - - time_idx = pd.DatetimeIndex(loads_df.index.get_level_values("time").unique()) - month_arr = time_idx.month - - bldg_level = loads_df.index.get_level_values("bldg_id") - time_level = pd.DatetimeIndex(loads_df.index.get_level_values("time")) - month_level = time_level.month - - season_mask = month_level.isin(season_months) - season_df = loads_df.loc[season_mask, ["electricity_net"]].copy().reset_index() - - period_lookup = period_map.reset_index() - period_lookup.columns = pd.Index(["time", "energy_period"]) - season_df = season_df.merge(period_lookup, on="time", how="left") - - period_consumption = _build_period_consumption(season_df) - p_flat = _compute_equivalent_flat_tariff(period_consumption, period_rate) - - targets = _build_period_shift_targets( - period_consumption, period_rate, elasticity, p_flat, receiver_period=None - ) - - peak_periods = set() - offpeak_periods = set() - for _, row in targets.drop_duplicates("energy_period").iterrows(): - ep = int(row["energy_period"]) - rate_val = float(row["rate"]) - if rate_val > p_flat: - peak_periods.add(ep) - else: - offpeak_periods.add(ep) - - peak_orig = float( - targets.loc[targets["energy_period"].isin(peak_periods), "Q_orig"].sum() - ) - peak_shifted = float( - targets.loc[targets["energy_period"].isin(peak_periods), "Q_target"].sum() - ) - peak_reduction_kwh = peak_orig - peak_shifted - peak_reduction_pct = (peak_reduction_kwh / peak_orig * 100) if peak_orig > 0 else 0 - - total_shift = float(targets.loc[targets["load_shift"] < 0, "load_shift"].sum()) - - peak_rate = season_spec.peak_offpeak_ratio * season_spec.base_rate - offpeak_rate = season_spec.base_rate - rate_spread = peak_rate - offpeak_rate - rate_arb_savings_total = abs(total_shift) * rate_spread - - n_bldgs = len(targets["bldg_id"].unique()) - savings_per_hp = rate_arb_savings_total / n_bldgs if n_bldgs > 0 else 0 - - arcturus_pct = arcturus_peak_reduction(season_spec.peak_offpeak_ratio) * 100 - delta_vs_arcturus = peak_reduction_pct - arcturus_pct - - system_peak_pct = ( - peak_reduction_pct * (hp_weighted_customers / total_weighted_customers) - if total_weighted_customers > 0 - else 0 - ) - - delivery_mc_savings = 0.0 - mc_nonzero = 0 - mc_peak_overlap = 0 - if mc_df is not None: - mc_season = mc_df[mc_df["timestamp"].dt.month.isin(season_months)].copy() - mc_nonzero = int((mc_season["mc_kwh"].abs() > 1e-9).sum()) - - peak_hours_set = set(season_spec.peak_hours) - mc_in_peak = mc_season[mc_season["timestamp"].dt.hour.isin(peak_hours_set)] - mc_peak_overlap = int((mc_in_peak["mc_kwh"].abs() > 1e-9).sum()) - - hourly_shift = season_df.copy() - shift_map = targets.set_index(["bldg_id", "energy_period"])["load_shift"] - hourly_shift = hourly_shift.merge( - targets[["bldg_id", "energy_period", "load_shift", "Q_orig"]], - on=["bldg_id", "energy_period"], - how="left", - ) - period_sums = ( - hourly_shift.groupby(["bldg_id", "energy_period"])["electricity_net"] - .transform("sum") - ) - hour_share = np.where( - period_sums != 0, - hourly_shift["electricity_net"] / period_sums, - 0.0, - ) - hourly_shift["hourly_shift_kwh"] = hourly_shift["load_shift"] * hour_share - - weights_series = hourly_shift["bldg_id"].map(hp_weights).fillna(1.0) - hourly_shift["weighted_shift"] = hourly_shift["hourly_shift_kwh"] * weights_series - - system_hourly = ( - hourly_shift.groupby("time")["weighted_shift"].sum().reset_index() - ) - system_hourly.columns = ["timestamp", "sys_shift_kwh"] - system_hourly["timestamp"] = pd.to_datetime(system_hourly["timestamp"]) - - mc_merge = mc_season.merge(system_hourly, on="timestamp", how="left") - mc_merge["sys_shift_kwh"] = mc_merge["sys_shift_kwh"].fillna(0.0) - delivery_mc_savings = -float( - (mc_merge["mc_kwh"] * mc_merge["sys_shift_kwh"]).sum() - ) - - return SeasonResult( - utility=utility, - season=season_name, - elasticity=elasticity, - price_ratio=season_spec.peak_offpeak_ratio, - base_rate=season_spec.base_rate, - peak_hours=list(season_spec.peak_hours), - n_peak_hours_per_day=len(season_spec.peak_hours), - hp_bldg_count=len(targets["bldg_id"].unique()), - hp_weighted_share=hp_weighted_customers / total_weighted_customers * 100 - if total_weighted_customers > 0 - else 0, - hp_peak_kwh_orig=peak_orig, - hp_peak_kwh_shifted=peak_shifted, - peak_reduction_pct=peak_reduction_pct, - kwh_shifted=abs(total_shift), - system_peak_reduction_pct=system_peak_pct, - p_flat=p_flat, - arcturus_peak_reduction_pct=arcturus_pct, - delta_vs_arcturus_pct=delta_vs_arcturus, - rate_arbitrage_savings_per_hp=savings_per_hp, - delivery_mc_savings_total=delivery_mc_savings, - delivery_mc_savings_per_hp=delivery_mc_savings / n_bldgs if n_bldgs > 0 else 0, - delivery_mc_nonzero_hours=mc_nonzero, - delivery_mc_peak_overlap_hours=mc_peak_overlap, - ) - - -def diagnose_utility( - ctx: UtilityContext, - loads_df: pd.DataFrame, - elasticities: list[float], - mc_df: pd.DataFrame | None, -) -> UtilityResult: - """Run full diagnostic for one utility across all seasons and elasticities.""" - result = UtilityResult(utility=ctx.utility) - - rate_df = extract_tou_period_rates(ctx.tou_tariff) - period_rate = rate_df.groupby("energy_period")["rate"].first() - time_idx = pd.DatetimeIndex( - loads_df.index.get_level_values("time").unique().sort_values() - ) - period_map = assign_hourly_periods(time_idx, ctx.tou_tariff) - - for eps in elasticities: - for spec in ctx.season_specs: - sr = diagnose_season( - utility=ctx.utility, - season_spec=spec, - loads_df=loads_df, - period_rate=period_rate, - period_map=period_map, - elasticity=eps, - hp_weights=ctx.hp_weights, - total_weighted_customers=ctx.total_weighted_customers, - hp_weighted_customers=ctx.hp_weighted_customers, - mc_df=mc_df, - ) - result.season_results.append(sr) - - winter_specs = [s for s in ctx.season_specs if s.season.name == "winter"] - summer_specs = [s for s in ctx.season_specs if s.season.name == "summer"] - if winter_specs and summer_specs: - winter_kwh = sum( - sr.hp_peak_kwh_orig + sr.kwh_shifted - for sr in result.season_results - if sr.season == "winter" and sr.elasticity == elasticities[0] - ) - summer_kwh = sum( - sr.hp_peak_kwh_orig + sr.kwh_shifted - for sr in result.season_results - if sr.season == "summer" and sr.elasticity == elasticities[0] - ) - total_kwh = winter_kwh + summer_kwh - if total_kwh > 0: - w_ratio = winter_specs[0].peak_offpeak_ratio - s_ratio = summer_specs[0].peak_offpeak_ratio - result.annual_weighted_ratio = ( - w_ratio * winter_kwh + s_ratio * summer_kwh - ) / total_kwh - else: - result.annual_weighted_ratio = ( - winter_specs[0].peak_offpeak_ratio - + summer_specs[0].peak_offpeak_ratio - ) / 2 - elif ctx.season_specs: - result.annual_weighted_ratio = ctx.season_specs[0].peak_offpeak_ratio - - result.annual_arcturus_pct = ( - arcturus_peak_reduction(result.annual_weighted_ratio) * 100 - ) - - best_eps = elasticities[0] - best_delta = float("inf") - for eps in elasticities: - annual_peak_red = np.mean( - [ - sr.peak_reduction_pct - for sr in result.season_results - if sr.elasticity == eps - ] - ) - delta = abs(annual_peak_red - result.annual_arcturus_pct) - if delta < best_delta: - best_delta = delta - best_eps = eps - - result.recommended_elasticity = best_eps - result.recommended_annual_savings = sum( - sr.rate_arbitrage_savings_per_hp - for sr in result.season_results - if sr.elasticity == best_eps - ) - return result - - -# ── Output formatting ───────────────────────────────────────────────────────── - - -def results_to_dataframe(results: list[UtilityResult]) -> pl.DataFrame: - """Convert all season-level results to a flat Polars DataFrame.""" - rows = [] - for ur in results: - for sr in ur.season_results: - rows.append( - { - "utility": sr.utility, - "season": sr.season, - "elasticity": sr.elasticity, - "price_ratio": round(sr.price_ratio, 4), - "base_rate_kwh": round(sr.base_rate, 6), - "peak_rate_kwh": round( - sr.base_rate * sr.price_ratio, 6 - ), - "n_peak_hours_per_day": sr.n_peak_hours_per_day, - "hp_bldg_count": sr.hp_bldg_count, - "hp_weighted_share_pct": round(sr.hp_weighted_share, 2), - "hp_peak_kwh_orig": round(sr.hp_peak_kwh_orig, 1), - "hp_peak_kwh_shifted": round(sr.hp_peak_kwh_shifted, 1), - "peak_reduction_pct": round(sr.peak_reduction_pct, 3), - "kwh_shifted": round(sr.kwh_shifted, 1), - "system_peak_reduction_pct": round( - sr.system_peak_reduction_pct, 4 - ), - "p_flat": round(sr.p_flat, 6), - "arcturus_peak_reduction_pct": round( - sr.arcturus_peak_reduction_pct, 3 - ), - "delta_vs_arcturus_pct": round(sr.delta_vs_arcturus_pct, 3), - "rate_arbitrage_savings_per_hp": round( - sr.rate_arbitrage_savings_per_hp, 2 - ), - "delivery_mc_savings_total": round( - sr.delivery_mc_savings_total, 2 - ), - "delivery_mc_savings_per_hp": round( - sr.delivery_mc_savings_per_hp, 2 - ), - "delivery_mc_nonzero_hours": sr.delivery_mc_nonzero_hours, - "delivery_mc_peak_overlap_hours": sr.delivery_mc_peak_overlap_hours, - "annual_weighted_ratio": round(ur.annual_weighted_ratio, 4), - "annual_arcturus_pct": round(ur.annual_arcturus_pct, 3), - "recommended_elasticity": ur.recommended_elasticity, - "recommended_annual_savings": round( - ur.recommended_annual_savings, 2 - ), - } - ) - return pl.DataFrame(rows) - - -def print_summary(results: list[UtilityResult]) -> None: - """Print a human-readable summary to stdout.""" - print("\n" + "=" * 80) - print("DEMAND-FLEX ELASTICITY DIAGNOSTIC") - print("=" * 80) - - print("\nCAVEATS:") - print(" - Arcturus uses aggregate heterogeneous response; our model applies") - print(" uniform constant elasticity. Recommended epsilon is approximate.") - print(" - Arcturus log-linear form differs from our power-law model;") - print(" comparison valid at specific ratios, not across full curve.") - print(" - Single annual elasticity is a compromise across seasons.") - - for ur in results: - print(f"\n{'─' * 80}") - print(f"UTILITY: {ur.utility.upper()}") - print( - f" Annual weighted ratio: {ur.annual_weighted_ratio:.3f}" - f" │ Arcturus target: {ur.annual_arcturus_pct:.1f}% peak reduction" - ) - print( - f" RECOMMENDED ELASTICITY: {ur.recommended_elasticity}" - f" │ Annual savings/HP customer: ${ur.recommended_annual_savings:.2f}" - ) - - for eps in sorted({sr.elasticity for sr in ur.season_results}): - print(f"\n ε = {eps}:") - for sr in ur.season_results: - if sr.elasticity != eps: - continue - print( - f" {sr.season:8s} ratio={sr.price_ratio:.2f}" - f" peak_red={sr.peak_reduction_pct:5.2f}%" - f" (arcturus={sr.arcturus_peak_reduction_pct:.2f}%" - f" Δ={sr.delta_vs_arcturus_pct:+.2f}%)" - ) - print( - f" shifted={sr.kwh_shifted:,.0f} kWh" - f" arb_savings=${sr.rate_arbitrage_savings_per_hp:.2f}/hp" - f" mc_savings=${sr.delivery_mc_savings_per_hp:.2f}/hp" - ) - print( - f" mc_nonzero_hrs={sr.delivery_mc_nonzero_hours}" - f" mc_peak_overlap={sr.delivery_mc_peak_overlap_hours}" - f" sys_peak_impact={sr.system_peak_reduction_pct:.3f}%" - ) - - -# ── Main ────────────────────────────────────────────────────────────────────── - - -def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: - p = argparse.ArgumentParser(description=__doc__) - p.add_argument("--state", required=True, choices=list(STATE_CONFIGS.keys())) - p.add_argument( - "--elasticities", - default="-0.05,-0.10,-0.15,-0.20", - help="Comma-separated elasticity values (default: -0.05,-0.10,-0.15,-0.20)", - ) - p.add_argument("--output-dir", type=Path, default=None) - p.add_argument( - "--sample-size", - type=int, - default=None, - help="Limit HP buildings per utility for faster dev iteration", - ) - p.add_argument( - "--utilities", - default=None, - help="Comma-separated utility subset (default: all)", - ) - p.add_argument("-v", "--verbose", action="store_true") - return p.parse_args(argv) - - -def main(argv: list[str] | None = None) -> None: - args = _parse_args(argv) - logging.basicConfig( - level=logging.DEBUG if args.verbose else logging.INFO, - format="%(levelname)s %(name)s: %(message)s", - ) - - cfg = STATE_CONFIGS[args.state] - elasticities = [float(e) for e in args.elasticities.split(",")] - utilities = ( - tuple(args.utilities.split(",")) if args.utilities else cfg["utilities"] - ) - storage_options = get_aws_storage_options() - - project_root = Path(__file__).resolve().parents[2] - - log.info("Loading HP metadata...") - hp_meta = load_hp_metadata(cfg["path_metadata"], cfg["path_utility_assignment"]) - - all_meta = pl.read_parquet(cfg["path_utility_assignment"]).select( - "bldg_id", "weight", "sb.electric_utility" - ) - - results: list[UtilityResult] = [] - for utility in utilities: - log.info("=" * 60) - log.info("Diagnosing %s...", utility) - - util_hp = hp_meta.filter(pl.col("sb.electric_utility") == utility) - hp_bldg_ids = util_hp["bldg_id"].to_list() - hp_weights = dict( - zip(util_hp["bldg_id"].to_list(), util_hp["weight"].to_list()) - ) - hp_weighted = float(util_hp["weight"].sum()) - - util_all = all_meta.filter(pl.col("sb.electric_utility") == utility) - total_weighted = float(util_all["weight"].sum()) - - log.info( - " %d HP buildings (%.0f weighted, %.1f%% of %.0f total)", - len(hp_bldg_ids), - hp_weighted, - hp_weighted / total_weighted * 100 if total_weighted else 0, - total_weighted, - ) - - if not hp_bldg_ids: - log.warning(" No HP buildings for %s, skipping", utility) - continue - - tou_deriv_dir = project_root / cfg["path_tou_derivation_dir"] - tariffs_dir = project_root / cfg["path_tariffs_electric_dir"] - specs, tou_tariff = load_tou_context(utility, tou_deriv_dir, tariffs_dir) - log.info( - " TOU specs: %s", - [(s.season.name, f"ratio={s.peak_offpeak_ratio:.2f}") for s in specs], - ) - - log.info(" Loading HP building loads...") - loads_df = load_building_loads( - hp_bldg_ids, cfg["path_loads_dir"], sample_size=args.sample_size - ) - - log.info(" Loading delivery MC data...") - try: - mc_df = load_delivery_mc( - utility, cfg["mc_base"], cfg["mc_year"], storage_options - ) - log.info(" MC data loaded: %d rows", len(mc_df)) - except Exception as e: - log.warning(" Failed to load MC data for %s: %s", utility, e) - mc_df = None - - ctx = UtilityContext( - utility=utility, - season_specs=specs, - tou_tariff=tou_tariff, - hp_bldg_ids=hp_bldg_ids, - hp_weights=hp_weights, - total_weighted_customers=total_weighted, - hp_weighted_customers=hp_weighted, - ) - - ur = diagnose_utility(ctx, loads_df, elasticities, mc_df) - results.append(ur) - log.info( - " Recommended ε=%.2f, annual savings=$%.2f/hp", - ur.recommended_elasticity, - ur.recommended_annual_savings, - ) - - print_summary(results) - - if args.output_dir: - args.output_dir.mkdir(parents=True, exist_ok=True) - df = results_to_dataframe(results) - csv_path = args.output_dir / "demand_flex_diagnostic.csv" - df.write_csv(csv_path) - log.info("Wrote diagnostic CSV: %s", csv_path) - - parquet_path = args.output_dir / "demand_flex_diagnostic.parquet" - df.write_parquet(parquet_path) - log.info("Wrote diagnostic parquet: %s", parquet_path) - - -if __name__ == "__main__": - main() diff --git a/utils/post/sensitivity_demand_flex.py b/utils/post/sensitivity_demand_flex.py deleted file mode 100644 index 978a2d4a..00000000 --- a/utils/post/sensitivity_demand_flex.py +++ /dev/null @@ -1,269 +0,0 @@ -"""Elasticity sensitivity analysis for the demand-flex pipeline. - -Sweeps over a range of elasticity values and computes key demand-flex -metrics at each point: load shift magnitude, MC delta, RR change, -TOU ratio change, per-customer bill impact, and non-negativity margin. - -Can run on synthetic data (for CI) or real utility load/MC data. - -Usage:: - - uv run python -m utils.post.sensitivity_demand_flex \ - --elasticities -0.05,-0.10,-0.15,-0.20 [--synthetic] [--output-dir /tmp] -""" - -from __future__ import annotations - -import argparse -import csv -import sys -from dataclasses import dataclass -from io import StringIO -from pathlib import Path - -import numpy as np -import pandas as pd - -from utils.cairo import ( - assign_hourly_periods, - process_residential_hourly_demand_response_shift, -) -from utils.post.validate_demand_flex import ( - SyntheticScenario, - _apply_shift_to_full_load, - _compute_system_mc, - _extract_tou_hourly, - _shift_loads, - build_synthetic_scenario, -) -from utils.pre.compute_tou import ( - combine_marginal_costs, - compute_tou_cost_causation_ratio, -) - - -@dataclass(slots=True) -class SweepResult: - """Metrics computed at a single elasticity point.""" - - elasticity: float - load_shift_kwh: float - load_shift_pct: float - mc_delta_dollars: float - mc_delta_pct: float - rr_change_dollars: float - rr_change_pct: float - tou_ratio_orig: float - tou_ratio_shifted: float - tou_ratio_change: float - per_customer_bill_change: float - min_shifted_load: float - - -def compute_sweep_point( - scenario: SyntheticScenario, - elasticity: float, -) -> SweepResult: - """Compute all metrics for one elasticity value.""" - # Shift loads - shifted, _ = _shift_loads(scenario, elasticity) - full_shifted = _apply_shift_to_full_load(scenario, shifted) - - # Load shift magnitude - period_map = assign_hourly_periods(scenario.hourly_index, scenario.tou_tariff) - tou_hourly = _extract_tou_hourly(scenario) - peak_periods = set() - rate_df = scenario.period_rate.reset_index() - max_rate = scenario.period_rate.max() - for _, row in rate_df.iterrows(): - if row["rate"] == max_rate: - peak_periods.add(int(row["energy_period"])) - - peak_mask = tou_hourly["energy_period"].isin(peak_periods) - orig_peak_kwh = float(tou_hourly.loc[peak_mask, "electricity_net"].sum()) - shifted_merged = shifted if "energy_period" in shifted.columns else tou_hourly.copy() - if "energy_period" not in shifted.columns: - period_df = period_map.reset_index() - period_df.columns = pd.Index(["time", "energy_period"]) - shifted_merged = shifted.merge(period_df, on="time", how="left") - - shifted_peak_mask = shifted_merged["energy_period"].isin(peak_periods) - shifted_peak_kwh = float(shifted_merged.loc[shifted_peak_mask, "shifted_net"].sum()) - load_shift_kwh = orig_peak_kwh - shifted_peak_kwh - load_shift_pct = (load_shift_kwh / orig_peak_kwh * 100) if orig_peak_kwh > 0 else 0.0 - - # MC delta - all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids - mc_orig = _compute_system_mc( - scenario.load_elec, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_shifted = _compute_system_mc( - full_shifted, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_delta = mc_shifted - mc_orig - mc_delta_pct = (mc_delta / mc_orig * 100) if mc_orig != 0 else 0.0 - - # RR change (frozen residual model) - frozen_residual = scenario.rr_total - mc_orig - new_rr = mc_shifted + frozen_residual - rr_change = new_rr - scenario.rr_total - rr_change_pct = (rr_change / scenario.rr_total * 100) if scenario.rr_total != 0 else 0.0 - - # TOU ratio change - combined_mc = combine_marginal_costs(scenario.bulk_marginal_costs, scenario.dist_mc) - mc_index = pd.DatetimeIndex(combined_mc.index) - peak_hours = scenario.season_specs[0].peak_hours - sample_weights = scenario.customer_metadata[["bldg_id", "weight"]] - bldg_level = scenario.load_elec.index.get_level_values("bldg_id") - tou_set = set(scenario.tou_bldg_ids) - - orig_tou = scenario.load_elec.loc[bldg_level.isin(tou_set)] - orig_w = orig_tou.reset_index().merge(sample_weights, on="bldg_id") - orig_w["electricity_net"] *= orig_w["weight"] - orig_sys = orig_w.groupby("time")["electricity_net"].sum() - orig_load = pd.Series(orig_sys.values[: len(mc_index)], index=mc_index) - - shifted_tou = full_shifted.loc[bldg_level.isin(tou_set)] - shifted_w = shifted_tou.reset_index().merge(sample_weights, on="bldg_id") - shifted_w["electricity_net"] *= shifted_w["weight"] - shifted_sys = shifted_w.groupby("time")["electricity_net"].sum() - shifted_load_s = pd.Series(shifted_sys.values[: len(mc_index)], index=mc_index) - - ratio_orig = compute_tou_cost_causation_ratio(combined_mc, orig_load, peak_hours) - ratio_shifted = compute_tou_cost_causation_ratio(combined_mc, shifted_load_s, peak_hours) - - # Per-customer bill impact (approximate: RR change / total customer weights) - total_tou_weight = float( - scenario.customer_metadata[ - scenario.customer_metadata["bldg_id"].isin(tou_set) - ]["weight"].sum() - ) - per_customer_bill_change = rr_change / total_tou_weight if total_tou_weight > 0 else 0.0 - - # Non-negativity margin - min_shifted_load = float(shifted["shifted_net"].min()) - - return SweepResult( - elasticity=elasticity, - load_shift_kwh=load_shift_kwh, - load_shift_pct=load_shift_pct, - mc_delta_dollars=mc_delta, - mc_delta_pct=mc_delta_pct, - rr_change_dollars=rr_change, - rr_change_pct=rr_change_pct, - tou_ratio_orig=ratio_orig, - tou_ratio_shifted=ratio_shifted, - tou_ratio_change=ratio_shifted - ratio_orig, - per_customer_bill_change=per_customer_bill_change, - min_shifted_load=min_shifted_load, - ) - - -def run_sweep( - scenario: SyntheticScenario, - elasticities: list[float], -) -> list[SweepResult]: - """Run the sensitivity sweep across all elasticity values.""" - return [compute_sweep_point(scenario, e) for e in elasticities] - - -def results_to_dataframe(results: list[SweepResult]) -> pd.DataFrame: - """Convert sweep results to a DataFrame for output/plotting.""" - records = [ - { - "elasticity": r.elasticity, - "load_shift_kwh": r.load_shift_kwh, - "load_shift_pct": r.load_shift_pct, - "mc_delta_dollars": r.mc_delta_dollars, - "mc_delta_pct": r.mc_delta_pct, - "rr_change_dollars": r.rr_change_dollars, - "rr_change_pct": r.rr_change_pct, - "tou_ratio_orig": r.tou_ratio_orig, - "tou_ratio_shifted": r.tou_ratio_shifted, - "tou_ratio_change": r.tou_ratio_change, - "per_customer_bill_change": r.per_customer_bill_change, - "min_shifted_load": r.min_shifted_load, - } - for r in results - ] - return pd.DataFrame(records) - - -def format_table(df: pd.DataFrame) -> str: - """Render the sweep results as a human-readable table.""" - buf = StringIO() - fmt: dict[str, str] = { - "elasticity": "{:.3f}", - "load_shift_kwh": "{:,.0f}", - "load_shift_pct": "{:.2f}%", - "mc_delta_dollars": "${:,.0f}", - "mc_delta_pct": "{:.3f}%", - "rr_change_dollars": "${:,.0f}", - "rr_change_pct": "{:.3f}%", - "tou_ratio_orig": "{:.4f}", - "tou_ratio_shifted": "{:.4f}", - "tou_ratio_change": "{:.4f}", - "per_customer_bill_change": "${:,.2f}", - "min_shifted_load": "{:.4f}", - } - formatted = df.copy() - for col, f in fmt.items(): - if col in formatted.columns: - formatted[col] = formatted[col].map(lambda v, f=f: f.format(v)) - formatted.to_string(buf, index=False) - return buf.getvalue() - - -def _parse_args() -> argparse.Namespace: - p = argparse.ArgumentParser( - description="Elasticity sensitivity analysis for demand-flex pipeline." - ) - p.add_argument( - "--elasticities", - type=str, - default="-0.05,-0.10,-0.15,-0.20", - help="Comma-separated elasticity values (default: -0.05,-0.10,-0.15,-0.20).", - ) - p.add_argument( - "--synthetic", - action="store_true", - default=True, - help="Use synthetic data (default).", - ) - p.add_argument( - "--output-dir", - type=str, - default=None, - help="Directory to write CSV output. If omitted, prints to stdout.", - ) - return p.parse_args() - - -def main() -> None: - args = _parse_args() - elasticities = [float(e.strip()) for e in args.elasticities.split(",")] - scenario = build_synthetic_scenario() - results = run_sweep(scenario, elasticities) - df = results_to_dataframe(results) - - print(f"\n{'=' * 72}") - print("DEMAND-FLEX ELASTICITY SENSITIVITY ANALYSIS") - print(f"{'=' * 72}") - print(f" Elasticities: {elasticities}") - print(f" Mode: {'synthetic' if args.synthetic else 'custom'}") - print(f"{'=' * 72}\n") - print(format_table(df)) - print() - - if args.output_dir is not None: - out_path = Path(args.output_dir) - out_path.mkdir(parents=True, exist_ok=True) - csv_path = out_path / "demand_flex_sensitivity.csv" - df.to_csv(csv_path, index=False) - print(f" CSV written to: {csv_path}") - - -if __name__ == "__main__": - main() diff --git a/utils/post/validate_demand_flex.py b/utils/post/validate_demand_flex.py deleted file mode 100644 index a05501c7..00000000 --- a/utils/post/validate_demand_flex.py +++ /dev/null @@ -1,661 +0,0 @@ -"""Analytical validation of the demand-flex pipeline's mathematical invariants. - -Operates on load profiles, MC prices, tariff structures, and elasticity -parameters to verify expected outcomes derived from the frozen-residual model -(see context/code/cairo/cairo_demand_flexibility_workflow.md). - -Each check returns a ``CheckResult`` consistent with the CAIRO validation -framework in ``utils/post/validate/``. The script can run on synthetic data -(default, for CI) or user-supplied data for deeper analysis. - -Usage:: - - uv run python -m utils.post.validate_demand_flex [--synthetic] -""" - -from __future__ import annotations - -import argparse -import sys -from dataclasses import dataclass, field -from typing import Any, Literal - -import numpy as np -import pandas as pd - -from utils.cairo import ( - _build_period_shift_targets, - _compute_equivalent_flat_tariff, - _shift_building_hourly_demand, - assign_hourly_periods, - extract_tou_period_rates, - process_residential_hourly_demand_response_shift, -) -from utils.pre.compute_tou import ( - Season, - SeasonTouSpec, - combine_marginal_costs, - compute_tou_cost_causation_ratio, - season_mask, -) - -# --------------------------------------------------------------------------- -# Lightweight CheckResult (avoids importing boto3 via utils.post.validate) -# --------------------------------------------------------------------------- - -CheckStatus = Literal["PASS", "WARN", "FAIL"] - - -@dataclass -class CheckResult: - """Mirrors ``utils.post.validate.checks.CheckResult``.""" - - name: str - status: CheckStatus - message: str - details: dict[str, Any] = field(default_factory=dict) - - @property - def passed(self) -> bool: - return self.status == "PASS" - - -# --------------------------------------------------------------------------- -# Synthetic data generation -# --------------------------------------------------------------------------- - - -@dataclass(slots=True) -class SyntheticScenario: - """All inputs needed to exercise the demand-flex analytical checks.""" - - hourly_index: pd.DatetimeIndex - load_elec: pd.DataFrame # indexed by (bldg_id, time) - customer_metadata: pd.DataFrame - tou_tariff: dict - period_rate: pd.Series - tou_bldg_ids: list[int] - nontou_bldg_ids: list[int] - bulk_marginal_costs: pd.DataFrame - dist_mc: pd.Series - rr_total: float - season_specs: list[SeasonTouSpec] - subclass_rr_ratios: dict[str, float] - - -def build_synthetic_scenario( - *, - n_tou_bldgs: int = 5, - n_nontou_bldgs: int = 5, - seed: int = 42, -) -> SyntheticScenario: - """Build a complete synthetic scenario for testing demand-flex checks.""" - rng = np.random.RandomState(seed) - year = 2025 - hourly_index = pd.date_range( - f"{year}-01-01", periods=8760, freq="h", tz="EST", name="time" - ) - - tou_bldg_ids = list(range(1, n_tou_bldgs + 1)) - nontou_bldg_ids = list(range(n_tou_bldgs + 1, n_tou_bldgs + n_nontou_bldgs + 1)) - all_bldg_ids = tou_bldg_ids + nontou_bldg_ids - - rows: list[dict[str, Any]] = [] - for bldg_id in all_bldg_ids: - base = 2.0 + rng.rand() * 2.0 - for ts in hourly_index: - hour = ts.hour - month = ts.month - seasonal = 1.0 + 0.3 * (1 if month in (6, 7, 8) else 0) - diurnal = 1.0 + 0.5 * max(0, (hour - 8) * (20 - hour)) / 36 - noise = rng.normal(1.0, 0.05) - rows.append( - { - "bldg_id": bldg_id, - "time": ts, - "electricity_net": max(0.1, base * seasonal * diurnal * noise), - } - ) - load_elec = pd.DataFrame(rows).set_index(["bldg_id", "time"]) - - weights = {bldg: 100.0 + rng.rand() * 50 for bldg in all_bldg_ids} - customer_metadata = pd.DataFrame( - [{"bldg_id": b, "weight": w} for b, w in weights.items()] - ) - - # TOU tariff: 2-period (off-peak=0, peak=1), peak hours 16-19 - tou_tariff: dict[str, Any] = { - "items": [ - { - "energyratestructure": [ - [{"rate": 0.08, "adj": 0.0}], # period 0: off-peak - [{"rate": 0.20, "adj": 0.0}], # period 1: peak - ], - "energyweekdayschedule": [ - [0] * 16 + [1] * 4 + [0] * 4 for _ in range(12) - ], - "energyweekendschedule": [ - [0] * 16 + [1] * 4 + [0] * 4 for _ in range(12) - ], - } - ] - } - - period_rate = pd.Series({0: 0.08, 1: 0.20}, name="rate") - period_rate.index.name = "energy_period" - - # MC: higher at peak hours, with summer premium - energy_mc = [] - capacity_mc = [] - for ts in hourly_index: - hour = ts.hour - month = ts.month - summer_premium = 0.02 if month in (6, 7, 8, 9) else 0.0 - peak_premium = 0.03 if 16 <= hour < 20 else 0.0 - energy_mc.append(0.04 + summer_premium + peak_premium + rng.normal(0, 0.002)) - capacity_mc.append(0.01 + peak_premium * 0.5 + rng.normal(0, 0.001)) - bulk_mc = pd.DataFrame( - { - "Marginal Energy Costs ($/kWh)": energy_mc, - "Marginal Capacity Costs ($/kWh)": capacity_mc, - }, - index=hourly_index, - ) - - dist_mc_vals = [0.02 + 0.01 * (1 if 16 <= ts.hour < 20 else 0) for ts in hourly_index] - dist_mc = pd.Series(dist_mc_vals, index=hourly_index, name="Marginal Dist+Sub-Tx Costs ($/kWh)") - - rr_total = 500_000.0 - - season_specs = [ - SeasonTouSpec( - season=Season(name="winter", months=[1, 2, 3, 4, 5, 10, 11, 12]), - base_rate=0.10, - peak_hours=[16, 17, 18, 19], - peak_offpeak_ratio=2.0, - ), - SeasonTouSpec( - season=Season(name="summer", months=[6, 7, 8, 9]), - base_rate=0.12, - peak_hours=[16, 17, 18, 19], - peak_offpeak_ratio=2.5, - ), - ] - - hp_ratio = n_tou_bldgs / len(all_bldg_ids) - subclass_rr_ratios = {"hp": hp_ratio, "non-hp": 1.0 - hp_ratio} - - return SyntheticScenario( - hourly_index=hourly_index, - load_elec=load_elec, - customer_metadata=customer_metadata, - tou_tariff=tou_tariff, - period_rate=period_rate, - tou_bldg_ids=tou_bldg_ids, - nontou_bldg_ids=nontou_bldg_ids, - bulk_marginal_costs=bulk_mc, - dist_mc=dist_mc, - rr_total=rr_total, - season_specs=season_specs, - subclass_rr_ratios=subclass_rr_ratios, - ) - - -# --------------------------------------------------------------------------- -# Analytical helpers -# --------------------------------------------------------------------------- - - -def _compute_system_mc( - load_elec: pd.DataFrame, - bldg_ids: list[int], - customer_metadata: pd.DataFrame, - bulk_mc: pd.DataFrame, - dist_mc: pd.Series, -) -> float: - """Compute weighted total MC dollars for a set of buildings.""" - combined_mc = combine_marginal_costs(bulk_mc, dist_mc) - sample_weights = customer_metadata[["bldg_id", "weight"]] - bldg_level = load_elec.index.get_level_values("bldg_id") - sub = load_elec.loc[bldg_level.isin(set(bldg_ids))] - weighted = sub.reset_index().merge(sample_weights, on="bldg_id") - weighted["electricity_net"] = weighted["electricity_net"] * weighted["weight"] - sys_load_raw = weighted.groupby("time")["electricity_net"].sum() - mc_index = pd.DatetimeIndex(combined_mc.index) - sys_load = pd.Series( - sys_load_raw.values[: len(mc_index)], - index=mc_index, - ) - return float((combined_mc * sys_load).sum()) - - -def _shift_loads( - scenario: SyntheticScenario, - elasticity: float, -) -> tuple[pd.DataFrame, pd.DataFrame]: - """Apply demand-response shifting to the TOU cohort and return shifted loads + tracker.""" - return process_residential_hourly_demand_response_shift( - hourly_load_df=_extract_tou_hourly(scenario), - period_rate=scenario.period_rate, - demand_elasticity=elasticity, - ) - - -def _extract_tou_hourly(scenario: SyntheticScenario) -> pd.DataFrame: - """Extract and annotate TOU-cohort hourly loads for shifting.""" - bldg_level = scenario.load_elec.index.get_level_values("bldg_id") - tou_mask = bldg_level.isin(set(scenario.tou_bldg_ids)) - tou_df = scenario.load_elec.loc[tou_mask, ["electricity_net"]].copy().reset_index() - period_map = assign_hourly_periods(scenario.hourly_index, scenario.tou_tariff) - period_df = period_map.reset_index() - period_df.columns = pd.Index(["time", "energy_period"]) - tou_df = tou_df.merge(period_df, on="time", how="left") - return tou_df - - -def _apply_shift_to_full_load( - scenario: SyntheticScenario, - shifted_tou: pd.DataFrame, -) -> pd.DataFrame: - """Merge shifted TOU rows back into the full load DataFrame.""" - full = scenario.load_elec.copy() - shifted_indexed = shifted_tou.set_index(["bldg_id", "time"]) - full.loc[shifted_indexed.index, "electricity_net"] = shifted_indexed[ - "shifted_net" - ].to_numpy() - return full - - -# --------------------------------------------------------------------------- -# Check implementations -# --------------------------------------------------------------------------- - - -def check_energy_conservation( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """Total kWh before == after shift for TOU buildings.""" - shifted, _ = _shift_loads(scenario, elasticity) - orig_kwh = shifted["electricity_net"].sum() - shifted_kwh = shifted["shifted_net"].sum() - rel_diff = abs(shifted_kwh - orig_kwh) / max(abs(orig_kwh), 1e-12) - tol = 1e-8 - return CheckResult( - name="energy_conservation", - status="PASS" if rel_diff < tol else "FAIL", - message=f"Energy conservation: orig={orig_kwh:.2f}, shifted={shifted_kwh:.2f}, rel_diff={rel_diff:.2e}", - details={"orig_kwh": orig_kwh, "shifted_kwh": shifted_kwh, "rel_diff": rel_diff}, - ) - - -def check_mc_delta_negative( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """Shifting peak->offpeak with negative elasticity must reduce MC dollars.""" - shifted, _ = _shift_loads(scenario, elasticity) - full_shifted = _apply_shift_to_full_load(scenario, shifted) - all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids - mc_orig = _compute_system_mc( - scenario.load_elec, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_shifted = _compute_system_mc( - full_shifted, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_delta = mc_shifted - mc_orig - return CheckResult( - name="mc_delta_negative", - status="PASS" if mc_delta < 0 else "FAIL", - message=f"MC delta: ${mc_delta:,.2f} (orig=${mc_orig:,.2f}, shifted=${mc_shifted:,.2f})", - details={"mc_orig": mc_orig, "mc_shifted": mc_shifted, "mc_delta": mc_delta}, - ) - - -def check_frozen_residual_identity( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """new_RR == full_RR_orig + (MC_shifted - MC_orig).""" - shifted, _ = _shift_loads(scenario, elasticity) - full_shifted = _apply_shift_to_full_load(scenario, shifted) - all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids - mc_orig = _compute_system_mc( - scenario.load_elec, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_shifted = _compute_system_mc( - full_shifted, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - frozen_residual = scenario.rr_total - mc_orig - new_rr = mc_shifted + frozen_residual - expected_rr = scenario.rr_total + (mc_shifted - mc_orig) - diff = abs(new_rr - expected_rr) - tol = 1.0 - return CheckResult( - name="frozen_residual_identity", - status="PASS" if diff < tol else "FAIL", - message=f"new_RR=${new_rr:,.2f} vs expected=${expected_rr:,.2f} (diff=${diff:.4f})", - details={ - "new_rr": new_rr, "expected_rr": expected_rr, - "frozen_residual": frozen_residual, "diff": diff, - }, - ) - - -def check_rr_decreases( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """new_RR < full_RR_orig for non-zero elasticity.""" - shifted, _ = _shift_loads(scenario, elasticity) - full_shifted = _apply_shift_to_full_load(scenario, shifted) - all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids - mc_orig = _compute_system_mc( - scenario.load_elec, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_shifted = _compute_system_mc( - full_shifted, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - new_rr = mc_shifted + (scenario.rr_total - mc_orig) - return CheckResult( - name="rr_decreases", - status="PASS" if new_rr < scenario.rr_total else "FAIL", - message=f"new_RR=${new_rr:,.2f} vs orig=${scenario.rr_total:,.2f} (delta=${new_rr - scenario.rr_total:,.2f})", - details={"new_rr": new_rr, "rr_orig": scenario.rr_total}, - ) - - -def check_tou_ratio_does_not_increase( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """Post-flex TOU cost-causation ratio must not increase significantly. - - With proportional within-period hourly allocation, the demand-weighted MC - averages are preserved by construction. The ratio may decrease slightly - (via receiver-period heterogeneity and seasonal effects) or remain - unchanged, but should never increase beyond floating-point noise. - """ - shifted, _ = _shift_loads(scenario, elasticity) - combined_mc = combine_marginal_costs( - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_index = pd.DatetimeIndex(combined_mc.index) - peak_hours = scenario.season_specs[0].peak_hours - - sample_weights = scenario.customer_metadata[["bldg_id", "weight"]] - bldg_level = scenario.load_elec.index.get_level_values("bldg_id") - tou_set = set(scenario.tou_bldg_ids) - orig_tou = scenario.load_elec.loc[bldg_level.isin(tou_set)] - orig_weighted = orig_tou.reset_index().merge(sample_weights, on="bldg_id") - orig_weighted["electricity_net"] *= orig_weighted["weight"] - orig_sys = orig_weighted.groupby("time")["electricity_net"].sum() - orig_load = pd.Series(orig_sys.values[: len(mc_index)], index=mc_index) - - full_shifted = _apply_shift_to_full_load(scenario, shifted) - shifted_tou = full_shifted.loc[bldg_level.isin(tou_set)] - shifted_weighted = shifted_tou.reset_index().merge(sample_weights, on="bldg_id") - shifted_weighted["electricity_net"] *= shifted_weighted["weight"] - shifted_sys = shifted_weighted.groupby("time")["electricity_net"].sum() - shifted_load = pd.Series(shifted_sys.values[: len(mc_index)], index=mc_index) - - ratio_orig = compute_tou_cost_causation_ratio(combined_mc, orig_load, peak_hours) - ratio_shifted = compute_tou_cost_causation_ratio(combined_mc, shifted_load, peak_hours) - delta = ratio_shifted - ratio_orig - tol = 1e-3 - return CheckResult( - name="tou_ratio_does_not_increase", - status="PASS" if delta < tol else "FAIL", - message=f"TOU ratio: {ratio_orig:.4f} -> {ratio_shifted:.4f} (delta={delta:.6f})", - details={"ratio_orig": ratio_orig, "ratio_shifted": ratio_shifted, "delta": delta}, - ) - - -def check_nonhp_subclass_rr_unchanged( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """Non-TOU subclass RR equals its no-flex baseline.""" - nonhp_baseline = scenario.subclass_rr_ratios["non-hp"] * scenario.rr_total - # Under demand flex, non-TOU RR is held at baseline: - nonhp_flex_rr = nonhp_baseline # by construction in the model - diff = abs(nonhp_flex_rr - nonhp_baseline) - return CheckResult( - name="nonhp_subclass_rr_unchanged", - status="PASS" if diff < 0.01 else "FAIL", - message=f"Non-HP RR: baseline=${nonhp_baseline:,.2f}, flex=${nonhp_flex_rr:,.2f}, diff=${diff:.2f}", - details={"nonhp_baseline": nonhp_baseline, "nonhp_flex_rr": nonhp_flex_rr}, - ) - - -def check_tou_subclass_rr_absorbs_delta( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """TOU subclass RR = baseline_TOU + MC_delta_TOU.""" - shifted, _ = _shift_loads(scenario, elasticity) - full_shifted = _apply_shift_to_full_load(scenario, shifted) - mc_orig_tou = _compute_system_mc( - scenario.load_elec, scenario.tou_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_shifted_tou = _compute_system_mc( - full_shifted, scenario.tou_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_delta_tou = mc_shifted_tou - mc_orig_tou - hp_baseline = scenario.subclass_rr_ratios["hp"] * scenario.rr_total - expected_hp_rr = hp_baseline + mc_delta_tou - - # Under the frozen-residual model: new system RR = RR_orig + MC_delta_system - # Non-HP RR is held at baseline. TOU RR = new_system_RR - non-HP baseline. - all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids - mc_orig_all = _compute_system_mc( - scenario.load_elec, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - mc_shifted_all = _compute_system_mc( - full_shifted, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - new_rr_system = scenario.rr_total + (mc_shifted_all - mc_orig_all) - nonhp_baseline = scenario.subclass_rr_ratios["non-hp"] * scenario.rr_total - actual_hp_rr = new_rr_system - nonhp_baseline - - diff = abs(actual_hp_rr - expected_hp_rr) - # Tolerance is wider because system-level MC delta may differ from - # TOU-subclass MC delta when non-TOU loads don't change. - tol = abs(scenario.rr_total) * 0.001 - return CheckResult( - name="tou_subclass_rr_absorbs_delta", - status="PASS" if diff < tol else "FAIL", - message=f"TOU RR: expected=${expected_hp_rr:,.2f}, actual=${actual_hp_rr:,.2f}, diff=${diff:,.2f}", - details={ - "expected_hp_rr": expected_hp_rr, "actual_hp_rr": actual_hp_rr, - "mc_delta_tou": mc_delta_tou, "diff": diff, - }, - ) - - -def check_achieved_elasticity_near_target( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """Realized elasticity approximates input elasticity within 10% relative.""" - tou_hourly = _extract_tou_hourly(scenario) - _, tracker = process_residential_hourly_demand_response_shift( - hourly_load_df=tou_hourly, - period_rate=scenario.period_rate, - demand_elasticity=elasticity, - ) - # Filter to donor periods (not the receiver which is forced zero-sum) - valid = tracker.dropna(subset=["epsilon"]) - if valid.empty: - return CheckResult( - name="achieved_elasticity_near_target", - status="WARN", - message="No valid realized elasticity values", - details={}, - ) - # Donor periods are those with rate > flat; receiver has rate < flat - flat = float(valid["rate"].min()) - donors = valid[valid["rate"] > flat] - if donors.empty: - return CheckResult( - name="achieved_elasticity_near_target", - status="WARN", - message="No donor periods found for elasticity comparison", - details={}, - ) - avg_epsilon = float(donors["epsilon"].mean()) - rel_error = abs(avg_epsilon - elasticity) / abs(elasticity) if elasticity != 0 else 0 - tol = 0.10 - return CheckResult( - name="achieved_elasticity_near_target", - status="PASS" if rel_error < tol else "FAIL", - message=f"Avg realized epsilon={avg_epsilon:.4f} vs target={elasticity:.4f} (rel_error={rel_error:.2%})", - details={"avg_epsilon": avg_epsilon, "target": elasticity, "rel_error": rel_error}, - ) - - -def check_shifted_loads_nonnegative( - scenario: SyntheticScenario, - elasticity: float, -) -> CheckResult: - """All shifted hourly loads must be >= 0.""" - shifted, _ = _shift_loads(scenario, elasticity) - min_load = float(shifted["shifted_net"].min()) - n_negative = int((shifted["shifted_net"] < 0).sum()) - return CheckResult( - name="shifted_loads_nonnegative", - status="PASS" if n_negative == 0 else "FAIL", - message=f"Min shifted load={min_load:.4f}, negative hours={n_negative}", - details={"min_load": min_load, "n_negative": n_negative}, - ) - - -def check_mc_delta_monotonic_in_elasticity( - scenario: SyntheticScenario, - elasticities: list[float] | None = None, -) -> CheckResult: - """Larger |elasticity| produces larger |MC delta|.""" - if elasticities is None: - elasticities = [-0.05, -0.10, -0.15, -0.20] - all_bldg_ids = scenario.tou_bldg_ids + scenario.nontou_bldg_ids - mc_orig = _compute_system_mc( - scenario.load_elec, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - - deltas: list[float] = [] - for e in elasticities: - shifted, _ = _shift_loads(scenario, e) - full_shifted = _apply_shift_to_full_load(scenario, shifted) - mc_shifted = _compute_system_mc( - full_shifted, all_bldg_ids, scenario.customer_metadata, - scenario.bulk_marginal_costs, scenario.dist_mc, - ) - deltas.append(mc_shifted - mc_orig) - - magnitudes = [abs(d) for d in deltas] - is_monotonic = all( - magnitudes[i] <= magnitudes[i + 1] for i in range(len(magnitudes) - 1) - ) - return CheckResult( - name="mc_delta_monotonic_in_elasticity", - status="PASS" if is_monotonic else "FAIL", - message=f"MC deltas by elasticity: {dict(zip(elasticities, [f'${d:,.0f}' for d in deltas]))}", - details={ - "elasticities": elasticities, - "mc_deltas": deltas, - "magnitudes": magnitudes, - "is_monotonic": is_monotonic, - }, - ) - - -# --------------------------------------------------------------------------- -# Runner -# --------------------------------------------------------------------------- - -ALL_SINGLE_ELASTICITY_CHECKS = [ - check_energy_conservation, - check_mc_delta_negative, - check_frozen_residual_identity, - check_rr_decreases, - check_tou_ratio_does_not_increase, - check_nonhp_subclass_rr_unchanged, - check_tou_subclass_rr_absorbs_delta, - check_achieved_elasticity_near_target, - check_shifted_loads_nonnegative, -] - - -def run_all_checks( - scenario: SyntheticScenario, - elasticity: float = -0.1, - sweep_elasticities: list[float] | None = None, -) -> list[CheckResult]: - """Run all analytical demand-flex checks and return results.""" - results: list[CheckResult] = [] - for check_fn in ALL_SINGLE_ELASTICITY_CHECKS: - results.append(check_fn(scenario, elasticity)) - results.append( - check_mc_delta_monotonic_in_elasticity(scenario, sweep_elasticities) - ) - return results - - -def _parse_args() -> argparse.Namespace: - p = argparse.ArgumentParser( - description="Analytical validation of demand-flex mathematical invariants." - ) - p.add_argument( - "--synthetic", - action="store_true", - default=True, - help="Run on synthetic data (default).", - ) - p.add_argument( - "--elasticity", - type=float, - default=-0.1, - help="Elasticity parameter for single-point checks (default: -0.1).", - ) - return p.parse_args() - - -def main() -> None: - args = _parse_args() - scenario = build_synthetic_scenario() - results = run_all_checks(scenario, elasticity=args.elasticity) - - print(f"\n{'=' * 60}") - print("DEMAND-FLEX ANALYTICAL VALIDATION") - print(f"{'=' * 60}") - print(f" Elasticity: {args.elasticity}") - print(f" Mode: {'synthetic' if args.synthetic else 'custom'}") - print(f"{'=' * 60}\n") - - for r in results: - status_marker = {"PASS": "+", "WARN": "~", "FAIL": "x"}[r.status] - print(f" [{status_marker}] {r.status}: {r.name}") - print(f" {r.message}") - - by_status = {s: sum(1 for r in results if r.status == s) for s in ("PASS", "WARN", "FAIL")} - print(f"\n{'=' * 60}") - print(f" PASS: {by_status['PASS']} WARN: {by_status['WARN']} FAIL: {by_status['FAIL']}") - print(f"{'=' * 60}\n") - - if by_status["FAIL"]: - sys.exit(1) - - -if __name__ == "__main__": - main() From 96cf35fce3f45c2a553f36c6eb65c5e1e5ba73fa Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:39:49 +0000 Subject: [PATCH 14/19] Update Justfiles for calibrate-demand-flex-elasticity recipe - Move module reference from utils.post to utils.pre - Add comprehensive usage examples for all invocation patterns (default sweep, utility subset, custom range, --write-periods, --compare-batch with and without --with-tech) Made-with: Cursor --- rate_design/hp_rates/Justfile | 4 ++-- rate_design/hp_rates/ny/Justfile | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/rate_design/hp_rates/Justfile b/rate_design/hp_rates/Justfile index 33740647..73c06ee7 100644 --- a/rate_design/hp_rates/Justfile +++ b/rate_design/hp_rates/Justfile @@ -689,8 +689,8 @@ run-all-sequential: : "${RDP_BATCH:?Set RDP_BATCH before running (e.g. ny_20260305c_r1-8)}" export RDP_BATCH echo ">> run-all-sequential [${RDP_BATCH}]" >&2 - # just run-1 - # just run-2 + just run-1 + just run-2 just compute-rev-requirements just run-3 just run-4 diff --git a/rate_design/hp_rates/ny/Justfile b/rate_design/hp_rates/ny/Justfile index a3b37015..43289c99 100644 --- a/rate_design/hp_rates/ny/Justfile +++ b/rate_design/hp_rates/ny/Justfile @@ -263,7 +263,8 @@ path_flex_plots := path_repo / "dev_plots/flex" # analytically, checks energy conservation and direction, cross-checks against # CAIRO's elasticity tracker, and generates diagnostic plots. # just -f ny/Justfile validate-demand-flex coned -# just -f ny/Justfile validate-demand-flex coned -0.12 ny_20260325b_r1-16 + +# just -f ny/Justfile validate-demand-flex coned -0.12 ny_20260325b_r1-16 validate-demand-flex utility_arg elasticity="-0.10" batch="": cd {{ project_root }} && uv run python -m utils.post.validate_demand_flex_shift \ --utility {{ utility_arg }} \ @@ -280,13 +281,29 @@ validate-demand-flex-all elasticity="-0.10" batch="": just validate-demand-flex psegli {{ elasticity }} "{{ batch }}" just validate-demand-flex rge {{ elasticity }} "{{ batch }}" -# Run the elasticity sweep diagnostic: tests a range of elasticity values per +# Run the elasticity calibration sweep: tests a range of elasticity values per # utility and finds the one matching the Arcturus 2.0 empirical target. -# just -f ny/Justfile diagnose-demand-flex -# just -f ny/Justfile diagnose-demand-flex --utilities=coned,nimo -diagnose-demand-flex *args: - cd {{ project_root }} && uv run python -m utils.post.diagnose_demand_flex \ +# +# Sweep all utilities (default range -0.04 to -0.50, step -0.02): +# just -f ny/Justfile calibrate-demand-flex-elasticity +# +# Subset of utilities: +# just -f ny/Justfile calibrate-demand-flex-elasticity --utilities=coned,nimo +# +# Custom range: +# just -f ny/Justfile calibrate-demand-flex-elasticity --epsilon-start -0.04 --epsilon-end -0.30 --epsilon-step -0.01 +# +# Write no-tech + with-tech seasonal elasticities to config/periods/*.yaml: +# just -f ny/Justfile calibrate-demand-flex-elasticity --write-periods +# +# Compare analytical predictions against a CAIRO batch (no-tech): +# just -f ny/Justfile calibrate-demand-flex-elasticity --utilities=cenhud --compare-batch ny_20260326_batch +# +# Compare against a CAIRO batch that used with-tech elasticities: + +# just -f ny/Justfile calibrate-demand-flex-elasticity --utilities=cenhud --compare-batch ny_20260326_batch --with-tech +calibrate-demand-flex-elasticity *args: + cd {{ project_root }} && uv run python -m utils.pre.calibrate_demand_flex_elasticity \ --state ny \ - --elasticities="-0.02,-0.04,-0.06,-0.08,-0.10,-0.12,-0.14,-0.16,-0.18,-0.20" \ --output-dir "{{ path_flex_plots }}/diagnostic" \ {{ args }} From e62be88d2171a985af2762dc290b678bba427017 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:40:01 +0000 Subject: [PATCH 15/19] Write calibrated seasonal elasticities to all NY utility periods YAMLs Full calibration sweep (epsilon -0.04 to -0.50, step -0.02) across all 7 NY utilities. Each periods YAML now has: elasticity: {summer: ..., winter: ...} # Arcturus no-tech match elasticity_with_tech: {summer: ..., winter: ...} # Arcturus with-tech match No-tech summer epsilons range from -0.10 (NiMo/NYSEG/RGE) to -0.14 (CenHud/OR/PSEG-LI). With-tech values are ~1.5-1.7x larger. Seasonal elasticities independently match each season's price ratio against the Arcturus target rather than using a single annual approximation. Made-with: Cursor --- rate_design/hp_rates/ny/config/periods/cenhud.yaml | 14 +++++++++++++- rate_design/hp_rates/ny/config/periods/coned.yaml | 14 +++++++++++++- rate_design/hp_rates/ny/config/periods/nimo.yaml | 14 +++++++++++++- rate_design/hp_rates/ny/config/periods/nyseg.yaml | 14 +++++++++++++- rate_design/hp_rates/ny/config/periods/or.yaml | 14 +++++++++++++- rate_design/hp_rates/ny/config/periods/psegli.yaml | 14 +++++++++++++- rate_design/hp_rates/ny/config/periods/rge.yaml | 14 +++++++++++++- .../config/rev_requirement/cenhud_hp_vs_nonhp.yaml | 14 +++++++------- 8 files changed, 98 insertions(+), 14 deletions(-) diff --git a/rate_design/hp_rates/ny/config/periods/cenhud.yaml b/rate_design/hp_rates/ny/config/periods/cenhud.yaml index 88decd52..27aab5e4 100644 --- a/rate_design/hp_rates/ny/config/periods/cenhud.yaml +++ b/rate_design/hp_rates/ny/config/periods/cenhud.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 5 +elasticity: + summer: -0.14 + winter: -0.12 +elasticity_with_tech: + summer: -0.22 + winter: -0.18 diff --git a/rate_design/hp_rates/ny/config/periods/coned.yaml b/rate_design/hp_rates/ny/config/periods/coned.yaml index c6a02550..ba87659f 100644 --- a/rate_design/hp_rates/ny/config/periods/coned.yaml +++ b/rate_design/hp_rates/ny/config/periods/coned.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 3 +elasticity: + summer: -0.12 + winter: -0.1 +elasticity_with_tech: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/periods/nimo.yaml b/rate_design/hp_rates/ny/config/periods/nimo.yaml index c6a02550..3eeebc77 100644 --- a/rate_design/hp_rates/ny/config/periods/nimo.yaml +++ b/rate_design/hp_rates/ny/config/periods/nimo.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 3 +elasticity: + summer: -0.1 + winter: -0.1 +elasticity_with_tech: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/periods/nyseg.yaml b/rate_design/hp_rates/ny/config/periods/nyseg.yaml index c6a02550..3eeebc77 100644 --- a/rate_design/hp_rates/ny/config/periods/nyseg.yaml +++ b/rate_design/hp_rates/ny/config/periods/nyseg.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 3 +elasticity: + summer: -0.1 + winter: -0.1 +elasticity_with_tech: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/periods/or.yaml b/rate_design/hp_rates/ny/config/periods/or.yaml index 88decd52..260a1b3b 100644 --- a/rate_design/hp_rates/ny/config/periods/or.yaml +++ b/rate_design/hp_rates/ny/config/periods/or.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 5 +elasticity: + summer: -0.14 + winter: -0.12 +elasticity_with_tech: + summer: -0.24 + winter: -0.2 diff --git a/rate_design/hp_rates/ny/config/periods/psegli.yaml b/rate_design/hp_rates/ny/config/periods/psegli.yaml index 88decd52..27aab5e4 100644 --- a/rate_design/hp_rates/ny/config/periods/psegli.yaml +++ b/rate_design/hp_rates/ny/config/periods/psegli.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 5 +elasticity: + summer: -0.14 + winter: -0.12 +elasticity_with_tech: + summer: -0.22 + winter: -0.18 diff --git a/rate_design/hp_rates/ny/config/periods/rge.yaml b/rate_design/hp_rates/ny/config/periods/rge.yaml index c6a02550..3eeebc77 100644 --- a/rate_design/hp_rates/ny/config/periods/rge.yaml +++ b/rate_design/hp_rates/ny/config/periods/rge.yaml @@ -1,2 +1,14 @@ -winter_months: [10, 11, 12, 1, 2, 3] +winter_months: + - 10 + - 11 + - 12 + - 1 + - 2 + - 3 tou_window_hours: 3 +elasticity: + summer: -0.1 + winter: -0.1 +elasticity_with_tech: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/rev_requirement/cenhud_hp_vs_nonhp.yaml b/rate_design/hp_rates/ny/config/rev_requirement/cenhud_hp_vs_nonhp.yaml index 0c2f2cfd..688ec905 100644 --- a/rate_design/hp_rates/ny/config/rev_requirement/cenhud_hp_vs_nonhp.yaml +++ b/rate_design/hp_rates/ny/config/rev_requirement/cenhud_hp_vs_nonhp.yaml @@ -1,15 +1,15 @@ utility: cenhud group_col: has_hp cross_subsidy_col: BAT_percustomer -source_run_dir: s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/cenhud/ny_20260325b_r1-16/20260325_143137_ny_cenhud_run1_up00_precalc__default +source_run_dir: s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/cenhud/ny_20260326_elast_seasonal_tech/20260326_194621_ny_cenhud_run1_up00_precalc__default total_delivery_revenue_requirement: 418151641.73 total_delivery_and_supply_revenue_requirement: 646688375.32 subclass_revenue_requirements: non-hp: - delivery: 401392812.5614301 - supply: 209823391.6038903 - total: 611216204.1653204 + delivery: 401392812.56143004 + supply: 209823391.60389048 + total: 611216204.1653205 hp: - delivery: 16758829.168569982 - supply: 18713341.98610972 - total: 35472171.1546797 + delivery: 16758829.16856999 + supply: 18713341.986109704 + total: 35472171.15467969 From ce75b14e574ef1730726ed076870562e207cdc95 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:40:08 +0000 Subject: [PATCH 16/19] Regenerate all NY utility scenario YAMLs with seasonal elasticity Scenario YAMLs regenerated from the Google Sheet via create_scenario_yamls. Demand-flex runs now carry a seasonal elasticity dict read from the utility's periods YAML (elasticity_with_tech when enabling_tech is empty or true, elasticity otherwise). This replaces the previous scalar elasticity value and enables summer/winter-specific load shifting in CAIRO. Made-with: Cursor --- .../ny/config/scenarios/scenarios_cenhud.yaml | 16 ++++++++++++---- .../ny/config/scenarios/scenarios_coned.yaml | 16 ++++++++++++---- .../ny/config/scenarios/scenarios_nimo.yaml | 16 ++++++++++++---- .../ny/config/scenarios/scenarios_nyseg.yaml | 16 ++++++++++++---- .../ny/config/scenarios/scenarios_or.yaml | 16 ++++++++++++---- .../ny/config/scenarios/scenarios_psegli.yaml | 16 ++++++++++++---- .../ny/config/scenarios/scenarios_rge.yaml | 16 ++++++++++++---- 7 files changed, 84 insertions(+), 28 deletions(-) diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_cenhud.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_cenhud.yaml index 822df69b..d2a5391d 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_cenhud.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_cenhud.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 14: run_name: ny_cenhud_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 15: run_name: ny_cenhud_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 16: run_name: ny_cenhud_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_coned.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_coned.yaml index 2906b563..06d3b3f7 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_coned.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_coned.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 14: run_name: ny_coned_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 15: run_name: ny_coned_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 16: run_name: ny_coned_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_nimo.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_nimo.yaml index fa11460a..c2121415 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_nimo.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_nimo.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 14: run_name: ny_nimo_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 15: run_name: ny_nimo_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 16: run_name: ny_nimo_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_nyseg.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_nyseg.yaml index d9dc213c..25b4c5bd 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_nyseg.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_nyseg.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 14: run_name: ny_nyseg_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 15: run_name: ny_nyseg_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 16: run_name: ny_nyseg_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_or.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_or.yaml index a0a63682..dd98650e 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_or.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_or.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.24 + winter: -0.2 14: run_name: ny_or_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.24 + winter: -0.2 15: run_name: ny_or_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.24 + winter: -0.2 16: run_name: ny_or_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.24 + winter: -0.2 diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_psegli.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_psegli.yaml index 0c224780..9c335d10 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_psegli.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_psegli.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 14: run_name: ny_psegli_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 15: run_name: ny_psegli_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 16: run_name: ny_psegli_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.22 + winter: -0.18 diff --git a/rate_design/hp_rates/ny/config/scenarios/scenarios_rge.yaml b/rate_design/hp_rates/ny/config/scenarios/scenarios_rge.yaml index 11bb7b11..1c052501 100644 --- a/rate_design/hp_rates/ny/config/scenarios/scenarios_rge.yaml +++ b/rate_design/hp_rates/ny/config/scenarios/scenarios_rge.yaml @@ -367,7 +367,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 14: run_name: ny_rge_run14_up00_precalc_supply__hp_seasonalTOU_flex_vs_default state: NY @@ -396,7 +398,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 15: run_name: ny_rge_run15_up02_default__hp_seasonalTOU_flex state: NY @@ -424,7 +428,9 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 16: run_name: ny_rge_run16_up02_default_supply__hp_seasonalTOU_flex state: NY @@ -452,4 +458,6 @@ runs: year_run: 2025 year_dollar_conversion: 2025 process_workers: 8 - elasticity: -0.1 + elasticity: + summer: -0.18 + winter: -0.16 From 27be1e409258e91ce380a5da906a98c1c5e1f315 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:40:15 +0000 Subject: [PATCH 17/19] Update CenHud calibrated tariffs from ny_20260326_elast_seasonal_tech run Promoted calibrated tariff JSONs from the latest CenHud CAIRO run. All files now end with a trailing newline (fixed in copy_calibrated_tariff_from_run). Made-with: Cursor --- .../tariffs/electric/cenhud_hp_seasonal.json | 4 ++-- .../cenhud_hp_seasonalTOU_calibrated.json | 8 +++---- ...cenhud_hp_seasonalTOU_flex_calibrated.json | 8 +++---- ...hp_seasonalTOU_flex_supply_calibrated.json | 8 +++---- ...nhud_hp_seasonalTOU_supply_calibrated.json | 8 +++---- .../cenhud_hp_seasonal_calibrated.json | 4 ++-- .../electric/cenhud_hp_seasonal_supply.json | 2 +- .../cenhud_hp_seasonal_supply_calibrated.json | 4 ++-- .../cenhud_nonhp_default_calibrated.json | 24 +++++++++---------- ...enhud_nonhp_default_supply_calibrated.json | 24 +++++++++---------- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal.json index ef78d278..be33570c 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal.json @@ -640,14 +640,14 @@ "energyratestructure": [ [ { - "rate": 0.14529932053295455, + "rate": 0.14529932053295458, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.025720769425837828, + "rate": 0.025720769425837883, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_calibrated.json index 5fffd7c7..c0780052 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_calibrated.json @@ -640,28 +640,28 @@ "energyratestructure": [ [ { - "rate": 0.05243093636450794, + "rate": 0.05243093636450798, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.08219835809492648, + "rate": 0.08219835809492654, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.04661356362420692, + "rate": 0.04661356362420695, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.14213873955929415, + "rate": 0.14213873955929424, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_calibrated.json index 7e3165eb..a803ad23 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_calibrated.json @@ -640,28 +640,28 @@ "energyratestructure": [ [ { - "rate": 0.05303207464995336, + "rate": 0.05386936607137937, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.08227295922468561, + "rate": 0.08357155100573309, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.04710194035878263, + "rate": 0.04670889280445623, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.14205945212208843, + "rate": 0.14086000803039866, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_supply_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_supply_calibrated.json index 2e35f27c..b3d4abac 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_supply_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_flex_supply_calibrated.json @@ -640,28 +640,28 @@ "energyratestructure": [ [ { - "rate": 0.12386630784621323, + "rate": 0.1256624037464114, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.19216385106579684, + "rate": 0.19494942580688396, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.11001537245422617, + "rate": 0.10895899050239435, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.33180636332194613, + "rate": 0.32858762765807065, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_supply_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_supply_calibrated.json index afa97909..f9948015 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_supply_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonalTOU_supply_calibrated.json @@ -640,28 +640,28 @@ "energyratestructure": [ [ { - "rate": 0.12263403419973369, + "rate": 0.12263403419973366, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.19225894017408462, + "rate": 0.1922589401740846, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.10902741305097234, + "rate": 0.10902741305097231, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.33245729061632995, + "rate": 0.3324572906163299, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_calibrated.json index 531253d2..776cb4d2 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_calibrated.json @@ -640,14 +640,14 @@ "energyratestructure": [ [ { - "rate": 0.14607171250510662, + "rate": 0.1460717125051067, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.025857519340267764, + "rate": 0.025857519340267778, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply.json index 9e8f8e7d..83052684 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply.json @@ -647,7 +647,7 @@ ], [ { - "rate": 0.10563409147863001, + "rate": 0.10563409147863004, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply_calibrated.json index a3dcdbac..28237b43 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_hp_seasonal_supply_calibrated.json @@ -640,14 +640,14 @@ "energyratestructure": [ [ { - "rate": 0.2400153191276985, + "rate": 0.24001531912769844, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.10590157038814794, + "rate": 0.10590157038814792, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_calibrated.json index 551c7678..a29b7adc 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_calibrated.json @@ -640,84 +640,84 @@ "energyratestructure": [ [ { - "rate": 0.1507979540475916, + "rate": 0.15079795404759158, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.1497199702617073, + "rate": 0.14971997026170727, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15064823407732988, + "rate": 0.15064823407732986, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15228018175318248, + "rate": 0.15228018175318245, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.1551997211732858, + "rate": 0.15519972117328576, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15419659737253236, + "rate": 0.15419659737253233, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15624776096511775, + "rate": 0.15624776096511772, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15864328048930507, + "rate": 0.15864328048930504, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15895769242685465, + "rate": 0.15895769242685462, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15847858852201718, + "rate": 0.15847858852201716, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15379235345282571, + "rate": 0.1537923534528257, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.15722094077181883, + "rate": 0.1572209407718188, "adj": 0.0, "unit": "kWh" } diff --git a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_supply_calibrated.json b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_supply_calibrated.json index a110c118..d5ef0b91 100644 --- a/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_supply_calibrated.json +++ b/rate_design/hp_rates/ny/config/tariffs/electric/cenhud_nonhp_default_supply_calibrated.json @@ -640,84 +640,84 @@ "energyratestructure": [ [ { - "rate": 0.2212411444851879, + "rate": 0.22124114448518797, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.24593165620973484, + "rate": 0.24593165620973492, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.2775470157566682, + "rate": 0.2775470157566683, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.27234784886126634, + "rate": 0.2723478488612664, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.2510423266473427, + "rate": 0.25104232664734283, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.23763511329154033, + "rate": 0.2376351132915404, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.24721485484774894, + "rate": 0.24721485484774902, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.28084350880949754, + "rate": 0.28084350880949766, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.26586548332785026, + "rate": 0.26586548332785037, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.2412634680610974, + "rate": 0.2412634680610975, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.2427900319580452, + "rate": 0.24279003195804527, "adj": 0.0, "unit": "kWh" } ], [ { - "rate": 0.24882991520249081, + "rate": 0.24882991520249093, "adj": 0.0, "unit": "kWh" } From a5c00b61d2fa39dd7e2e94cfd1a796444304111e Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:40:23 +0000 Subject: [PATCH 18/19] Update demand-flex calibration docs with corrected Arcturus model and full results - demand_flex_elasticity_calibration.md: corrected Arcturus with-tech coefficients (interaction term: slope=-0.065 + -0.046 = -0.111), updated results tables with full 7-utility seasonal sweep for both no-tech and with-tech, refreshed invocation examples, added note on enabling_tech defaulting to with-tech - context/README.md: index updated to reflect calibration script move to utils/pre Made-with: Cursor --- context/README.md | 10 +- ... of time-varying rates for electricity.pdf | Bin 0 -> 537908 bytes .../demand_flex_elasticity_calibration.md | 166 +++++++++++++----- .../demand-flex_elasticity_calibration.md | 66 +++---- 4 files changed, 156 insertions(+), 86 deletions(-) create mode 100644 context/docs/Faruqui et al. - 2017 - Arcturus 2.0 A meta-analysis of time-varying rates for electricity.pdf diff --git a/context/README.md b/context/README.md index ccc36b94..3308a7e2 100644 --- a/context/README.md +++ b/context/README.md @@ -56,11 +56,11 @@ BAT framework, residual allocation, and how they connect to the literature. Cost-reflective TOU design: theory and window optimization. -| File | Purpose | -| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| cost_reflective_tou_rate_design.md | Theory and practice of cost-reflective TOU rate design: demand-weighted MC averages, cost-causation ratios, period selection, assumptions, demand flexibility implications, and partial vs. general equilibrium | -| tou_window_optimization.md | TOU window width ($N$) sweep: welfare-loss metric derivation, HP-demand weighting proof, sweep algorithm, CLI/Justfile, NY results | -| demand_flex_elasticity_calibration.md | Per-utility elasticity calibration: Arcturus 2.0 meta-analysis anchor, diagnostic methodology, results table (ε = -0.10 or -0.12 per utility), two savings mechanisms, known limitations | +| File | Purpose | +| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| cost_reflective_tou_rate_design.md | Theory and practice of cost-reflective TOU rate design: demand-weighted MC averages, cost-causation ratios, period selection, assumptions, demand flexibility implications, and partial vs. general equilibrium | +| tou_window_optimization.md | TOU window width ($N$) sweep: welfare-loss metric derivation, HP-demand weighting proof, sweep algorithm, CLI/Justfile, NY results | +| demand_flex_elasticity_calibration.md | Per-utility elasticity calibration: Arcturus 2.0 meta-analysis anchor, diagnostic methodology, results table (ε = -0.10 or -0.12 per utility), two savings mechanisms, known limitations | ### methods/marginal_costs/ diff --git a/context/docs/Faruqui et al. - 2017 - Arcturus 2.0 A meta-analysis of time-varying rates for electricity.pdf b/context/docs/Faruqui et al. - 2017 - Arcturus 2.0 A meta-analysis of time-varying rates for electricity.pdf new file mode 100644 index 0000000000000000000000000000000000000000..07b11ae0d266f385cf2ffaf98810b3d0df0d5cf6 GIT binary patch literal 537908 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¨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 z zvQ+!O$o~u}=Qw9Lxejx@sIE(dh+Z(HKaMIrXNVsIA-UipxeqxCrF{cnF2fMWC}GWm zFFg*}Jn6d8S0teI~ah zGU>Bo*ptF6V2K(TAB^|jTaxZGlSso;W4IYI&vY+~u4OltRybn* zQ=8MR8gQnvpI70&+DqBz{FuAIwHz3&b8_axs2cio22AI*d)t{1s>`HAXJVqW(UR;z z*5?!wQy>*7*)x2~mQF%HXwha)g7dS;e$7uWcx@gzT`(_o~grSQ~j6mP(nX9gI3M=H1&792Gd86fmJmIy- zdG|59_H1ppk)Z`{^OUlCEQJQO4swjZek`W~^F@v!hwK`k=}C%D)yo)3=i#_{G%8V> zWpav*c?WgKF7i-B{TFkKz6u0hPW;~XfU~N3e(%i z-SsD;ScMkS_P1)QXp%z~8P$`<-dAFy*ePN*)N zasgF~*+XN1=Exu`=@5j~8W#H_xfM*omJ5$uo+A0Ta!la?d@%gYM&7TZig4uD@$sIp zG3Ocr-~ijrtDH7+9EbUw6$crjui9-QN{zh}_x7WotmWINb1sj?vDwHO#G&zATwz*U z&-=aZ|G4`Y$o%;=w2@6<5XE?JH^XTJ>8LY{#@oq=p=3{&gc;1N*JM(be`?CfqkNJk z0Ta_44$-lDWmIbUNfF-<(h$0ldnmH-NEF0DhcNk?&319t|_dA{$Rcl9(8s`fqPow+-Fn~tK8(bAjPW2xo6Hn@qlThhcDO3rj=%Wk=b{gAH`N{`6(6VT2;yHD7a&DNbxS9QH&vf6savV(S zwY=EBaASY*VezItVfHhpPvMce!I;CtCYP^ww=e4=3{Ph?-o?SmStNB1% z?}47j!_I_;LFEuT2SOEhqQ_RwJwpC)5{&m!U~3-eCWijab~Ez+?)<|#U<2mm0(*TD^x`p(t@4s2TBF+c&g z)Vuaqddg>HddD4#TBK)V<{eLWm2U0rzNLl&4u;1R!8Avp7qQ5v<`v1|&lYdBA zIbRq8q_6M+=__aHAHX^j9yxbs1x;sQCn5l*!dYU=MIH@M^FLp@0ucW_edXe4Y~V)+ zKKhH(vHy1qS_J-aF9CrwUH#5vIZuZ9bD_$)*8XGWiiqgn)~O_FkJycfkoXMfwlU%7 zv6(R6;|RI*{Pk^vRLLH^K~M(xOUB@Eab})0)7pgFs~F1s*F{Uzyp64qPVVh(4(f70 zhZcuNJl7`IMrPZ}J-^x7J&eD7W#JWZHEU?&CQ)pEh+r{Gobs&vH8lhE%&f~#{bPsI z$M)cJ)f0Cjjfnl8$}4ifj*aYxt6kSR8!FvGcMeEcv{|I2TxgUSED8Fkr*jUl?%}U~ zC!6j4b;rWM;#w~Eqn6F(4aM$jOP>O?LMntwaSTEl2;oI7kZc}k)I^7Kqv{4MaVA3H zHpjzYD_*4DF0L$jh*7LGc?io#aXLexO%cnxcWCTxh`gim$fI~_1KZSj&{3lULGm(1 z2}zpp3-+wi9q_tOzAF~obs8~4e&r`jom%|x<8g6Dq40_@?LZxk;H-7{$_RY(n%_j# z)|%UA78H)P*TyZoS|)--?as7;-rB@#p1RxCtRrvyV@nX)8@M^(`svk4EpxVV#{5*J8GEE zpxu!Qt^>wb+Fy*P+y>~9dPxFjpNV_Wt5(+0&=-D4dpzxL_{slEQyqeOZoc+8tupw! z_qdqPoAvT$0YoIR);Df~Jk^oxAv;+eTE{7(WLzu7x`@vzVcN};%~vhTHZL*eUwO@O zK>n?v{VqOQGTE@hE8@A|E3f6c%S~DmnM85{+xo9x`7MRrIpMQ(uvuWwR#u5xJx$lm zV%zI*Z%_X5<=V9;8UdZhb3U&93R#RSI$1tLVu&|TKJ5o66v2_i-a>qX$9%9f8+d*+ zB7Iy~F*D!e`+MnNwPX_1&G5qE*%*n%H-gf92E+bCzB}1iv8$J!AKMt@e@Nmm=F`@5 zwDdkkiCw?P(?6-UE+qAJA(%5cN+CG?Fbg`DhvZB5)=9bQEy*+T3oenHvNN-7pXreC zN^736QMe~Fz96n7o{cZ{2kY@wPr-X9Rj~!-NNZ8f)-TPAO0QotoLCS$N7jG-apIh) zBNpItodqI>zaZm$X9^Y|FtGFSMkN2W2?Zy@56!2rREI?Fa0x681>eu<5Fjwd4lOAE zLtqpVRc4lP^!4}-t8sVd_kJ(Q=u(KvxG!V%zH0~($IbsB{-;B-?g5sAyvbNC z(IJk8^dJ8jT?_{56;28qeTvfsdf1LKaCZJu`!A|l@Q9c|cr;9m7e>!A+eb9&`=lYUuq88o)*2cusDfe4<21D{GZ zYVm0>Z^QEDP9>1Q`1au8||0TRONdLQR8U5B%2~!hqvA353fM(?jPbzzUsbXf+vcL zz;b49Xw1b+K0F~*b%&ucgANon_EhR1GWo9;$;NoKjpJyhzV3e?w#~2MjDu{>A5K2s zlAVi~!t9cggx}o)#iilXhd+oFFRhV{_O*T%467x6s;h|%`{FTC8cL=gbKcLc8_pm7 zV9q132^_L=AD%XFGj11K2#lUwHiPZNS2cl^e2f9g6&ULB1c zPah?=9cY^rFaHyA6M>cK|95Msa3W;a0=Y3npBC*RpR_oe!6jLLx1*NZ0$IG2U2cYy zxICt2K>*I8e<713JpB-dUy9M?4@UxI-oCK!Kgbk8%HPN0Q|!{SZL!LOy9ous{Au47v{OYe9M; zUNB@D&_!@>6zthb8xWEKc3=J*=+0>DNV0ze_z6u~45`=^{{JB}H=on^SI5R?)AFQg z>07zr(dz5-pux0??WUicDBy=z*YA*rfi41~ms*Vr_zzjnw9)`_*F4yC7TT>(r z=Q{HpN1~=so@)s|ZcVelQx}{Q=m-3_VJEqRx8G&(L%vSw30rq71UcWdd1~UUWEWS# zZ4rTw={g&dTKMC&5Ud0JXVtI<^6?HR`zA>8!CG)Jm_g2IK+K0LYVN}3ef>#Rjmr+V zLt1V&pq7~Eqec$u(O3d`PX%tfF*x3Kvood0dO8C-@f8Nnhj|P+Vap$XDr+lV z!Sf*I(7%tvKcb>S69kK$;qSf=AW=u9-ABl9R?z&15#9z?y!MaL;V&{-Lw{AX^|$*V zh$({PuOH$>Ta-JY(jAjiCXxag=XV>IC$E>jy&OCmd=Y+>T;Pth-xMx2wM)D3E9>Wb zkS9eouX){=LUtwr0~>y?EFWzDA|F|d_i(< zYIG~IaUOA+!=--5JgY+ade0;Q|DgO@q($#$>vj+UA>#bON>#5Pv(#c zCyLX@@1lUnrn!thnm~Q*J;)Yr>BHnYqvY0*7h~a*7qcVE1c*M&D-KnoT_a4hVdSl2 z15HDaaGrNHRiYAd=EdxPn(gE}7`%|L3y&}gM-~e=_#i0S!0wzcrAkiF5FS%)vl!)d zBw zD4rsYIO>(3%miESzJSro86ezsi^$XGoilQ&N3=e9HAC!k1$q`ODvDPe1|Z=t?hX6^ zT%Zd7WYx51Jk>_!WV8RnQ0dk5u6ubfG&&i#1lG!4A@1)_ybRLBn<1v`G8_> zc|`tMXKtXW7QsXP0KoD(H<+%k^HA!Zk2=1CUR!b9mp;X{8$z_AM?S{jnm)-@$9E$Qoa~HM!K%JAgVTh8E7kO{|6X$!z zUNzhDQ}D|UGD`*}4HW5JS5ABdbK3U@!4!&JVd-9~f;^i|8K+m04}8|A^7WEmOl2Il zqN$#@o4d>+J)D1Q)F+gw-6JmoMVaZp%M~HAP$&j;l;NV37~#v9B`(1GQgLEB$cgSF z6QV5xFm7f==-o|FEm0`Z36}eJ$AxNW^6TFe4Up7V2Tl7*!M}NJH%S0rzf^J!D>1Jv zGwq*$7fsOy@dhn`f_AyueE=j_4If71H#(#)Eg2@F5}TH>t~@UUomeYQ4=1FakB4hC zXzWcq2rNSN9?*UrK!HQ`G6gbni>13%&Z- z1Hv_t;>Y}F8*BuY#1#ZuifmvU_Yddt33W=@(YxtO_9s={ME?F>>!X>7hJUq8+fz`F zf>GxgdHsY818)AX7m~J@?Sh491S6kTLxQ zSDGL7#F^cMa=4v;|J$X!ac0Ffuv%u7TOC2Kkp8oGhW*Q(@5z@w3QYrA+ypJEj}y_q zaG|=W66PwWpIH3$mJ1O}e9(IE3ty{IhXjO4|qSF)W-Fw9W}@SL<}l$ODs3v#zy_ljgR^Ly2iJ1 z?F=1Dj>!a>7#Gr@Ms z-$VAc_yXzG#Q|`V^{wMdK95zLl~YWOd=R&VPsY{zz@H0ngNH@Id!*ZVAC12m7h&|) z=!_oXlNCGBEi=uM!jB7~1rUED{=9r|^|ERkC9|P~^p3XRh-Kj5^)+*QCdJ-%j}OSm zCN|Z*L74R)vi%NOVi)RuHNC{p<1(C+fFr+u5td4cC%@UJ+cyE}_!fSFN}A5B_#XG$2qj%1(UmM~6 z%#jB(WDvQq5as<4mYf2L^DIZ(0JL)_Gxcxp!J6APF=dl07>0z~6}e+#j*Ct91MLkb z&!uExa27!~(lbNf|69YGtxcGwogGC$SsfirN40PX7;+W|wS|_b?QNaYMYSce9YA`H z@XGywZs!j}rPt{JwR|i=Mv4O+-^pFWQT}EGF9tN5i{O2GQN%P${8DzU$_DjcT7e&0 zmbYP=nyWc46h!I>l1g_qDfaOnl>+=h*A~Lyc4i9g5m3PwqxlnaiwVmAhSk3S=;?Di zo$`IzAUI=%QTOVdKVy5Yv{jH2{=eVl>^8JG#-ot#rd|Ga6L(D^=$$nrYctimN<32z z4E>N=A6JTfXf=n7{~5|$@jy4@<3&%} zCJu1r#svKikk7p>9>>_;9YjWXTbOA5nr`_z>WmTDv#5jP9yKTG*)5x&7Uh=yYB=uP zvpg%q>frLq-ia6OdW-VDyPv&aIS}K>rtFB}onjK0&Lf1I^*LvK2wVf9NT->0>U7hy z^))HSdZu30!~GANz1fy`;D?_A5ha+Y%O1r}R*fJ<*Z8}8lAPy8EdM&qG<4d z$J@@ymYTiJJub;IS-S^83I1?Iyp?`8VrU5g71zMkR-fF=#1$21AnydcK1#p_H zXB|nzz|X;T;qF5aaM3}esrRJ7OjJAHm2$%vx}I zNO=bQ|4y&>|5Vn0(KOcM*dZO27!=s+Pi6fVnOJ}JHGki_{p$8_fKn^G-JjW*s?L!-Z@s=fT$hEgVfqh^h6_zlzm(R}aAVoNB z*tKU~{4`g+cLH)tDN^-Z|HoQ;IS@-92`^y00ERG!1&L{6_6FXsH97X!-t7Ff2O8q@z6Bmy(NVAJqe zOzK%PyLD(Zn+y!y-!Nc~$~Y^rT8BO&20REpzmDT@8WnUNnSc*W^Jk9sm)buIunr)> zhoAqu+J9h%R`(KI$T)vj9J{y#`VuKkMJ0^3EDW z%9UzWx)eZ|P_x72-RSA|b|Lv2yJy zivt~s6#19RRRxKc-#haIsz;!IDDz)}4v_A+!D*&feM)`OaNEsc{|ySzZ(UsQsaCtR zuyyiV2oHV=9F~{3FoQ=X!}GS9+519XYZg#(a?;S^0gV8_I5K$`D>#iO`dmQ$Hj4MZ zxO?lkD7$W97(_v-0YnLf0YL$2rDKLr2Bk|-x)r6nX6RBFx!9`#kS^e&>AW{r>p;gDdvB)?U50v-e&Le9D{pn@_h2a>${v82C5Y0OWeB z3uN5s6)Pl_M z2y(~cf9c94^};{*tR>HdvGibjIh=ci_04LAWklegzRb z2t!l%n=fI2*D(h>>*KOez|L@+GMfo{1To4RLJ_X_t>5kbs^ZVGi>4G zT5ArAU--k%ZsTHkpfqc>G(yI9m7&&R0;x7(U`D6U5t29#lCn}B;5^Og@eOBw4)zc) zEqsOmuZosVRGYD@!E4qOIJ@Bw`4(f~ev1c=)}!3Cl=z2r4U-asBl2ydU(8ERJ+#4L zlevbbRls1l1T(f6?0Q+p+w3~--&Y-5gv@QJ-^~MmE=&mD`yw~0qdUla{J0LUdF4pM z*y3pO@hRPE=j*BBH|L){KVvkjLvw%BubLxrX_MI4*57v%!VUmaA9#8F(x=jT|SX~6Iof95V`PCuJ>d{jZbIU!r9|{-e=57?r9MJ z$JE`iPUY$!PI+V1tEY$!)6c}~!^$Lyg$dDv`EtD$OTMByFHw=x-gQt#sAQrV(fRdH zLIoCZm6F992eqd($~4QLG|-r*dqm06hnJ|(JyYkS+{2TlhSY>r!7P`5dR?Ni__h7w!ld+RE1U?v&@q;<`S`nRCN?2d<I zqN&)H;qE6dnzGAjKG{}FYCW~-?810ss799iB|y#(5i_>PogmHAxjOl6wyU4hl`{wI z!$Q{r<r#C~H!?c#ETl{lv54CbqK#ga!WHIpX#MaB-tw zFxyvOS`P?*9~@0%RVqH~Sc^aZG{DgF3wfw=+Lzi9M*n>GkpaQyVQqdy`Q>-)%vb+b zL=5{-kA%=P_7&NpWC;mtf=KxPr{8X%O_ zkMZ7kL54%)hMO;4LU>sRqcX4eI)TJ@XwNf7!IzW#^UY zx7j#iY*bHUf+Y{$#C>o(fbmOnpynD=6DrsfdBFbOzb}#|DKWMWY$wZ5%6*?|NxlpyFmyf0nn9^h%L*D0?fv8O zXy`c&i|e^^QF=_}wan)qDaD|Kcq{mC3?YAAeA`6W=a{#^PX)fI3VQQ(MEfkk3>hZ9 zI6MDwA$x9anmmt|ZV!02dN16J<^zXhCGMSOhDn?VqoBOv2Z}|C|6@&gV+U|F@$@-SI>gWmjwipaHDl8r~AP@SK{j*vQQQUWgUGqzN4#n|nwz-_;to@5|zom3|*|3v;9 zhC-F3cxbLQe68)yB9mZGv5|AdGmW|Y*}XYAuQt;XWEA`04kkiZcRbM0om4YyW7JVY zqiW~m`<^jPDtL4!VcBsu>14wmq-r_ZRN_gNdF!h`ik0}!<<45(yQtu` z+OEJz$bsd{EUQa^IbZ2PHb_+BYRCWM!cf%xL#X{UF!H}<$G#Rpv}wG+qa|Bp9CH@NnR|0Dps z*rAuU?C*-j>5#!fQjqT625GS&Hn=fi=FNd!N%f3Avt~)txT@=z0cZwUw z>=4@NlIJK_S@ka&yVK+6ZlK^U)zb5cpIe^{t&A&MPab-VL-%;+jgGqm8{IjY1C^|L z0Ft^mp%p z4OXfhk7C_aPdfkM9Zve|8gmU_56&ngJ~Q1Br$>#X>S}6~+$sQ9BPd@0i;X=4;=8jM zy=yI0K@+L0Og$dQxq-;mm4izU&}0cXf3&>`XnXaqz;;Jx>i^e)bXzL~ls1t{8UB*) z4xT`3{^u7ew2E)t(I_<^)(G8$qmwng+fs~rxQAAYl1;g<1QbK({|E5!$hc9GPvMO z_`%+>0VLKC@`eak7$v+WQic<8>lHQ2l~tGXwwk}k2~Y<>{Q>ywipip%|3PIt`JUlu zJo2;cQDYv(BP-KCL(?zEFQ-Ne9+V+VfzL79F3#gUpBpL_76MRTbAyE-@krBnP>0?T za+Ith=2(bL?H@rh;!0IV11{0pp>(Ef&G984$6I&$0H4r;0HG^SttdU9a>Lb%Lk2Li zw%cjQ{sxM?UV;c2c&!m62=invpNO5TA}RndvuXrK?jC_`*&CWiR!Syeb%=MHfzR)1 zl>x>JH~`}CyGJ%P%X6UHRqQ|<(3ZEjAvCIcCirPsG|1r*v#~w#^K9$WVdR+M%&N2s zD6MMeE@YA+BH66mjn8@*S$QsY)}*goxgDhsO|vGii1;1I)Kvs;gBZb^C&#^uLVoEa z`iZlxUB|IYtq*30*(dRucN+%_(vmWuY%h)=h8oED<0Eu*N!2^+o!OhA#(-ky`g2zI z3dQU2^Ym_oz1wgVlAD_P)yPTP>Y;V`qjhsP6*<}L@7@~!cB%790j&%_q&O7TpPk_*anQmRrH9R12!KoX{N0yN2{TyrQ4M)#=V}|R5=IwK>*s)#= zYF8`{DppSmZqaRAYK_B@vZQJDF!{962#oz1jpu9J_pni+v-Lq|pk;|Wtu=LHXp_Ie zyRE81tVw}cG(XRw8yzOu=?gskY=4BZv~2#W6Rx?BHFy`e)}h$_d#Gc?Uxq&Xb{ziv z+Ot>8+MR)>5b&q1^IUGgVnS?p_W%vY=fBo1v$;@k&o18O++2oC$Gqe5ds!%NZ!QGQ4ufxsOM&2$(mRI(C4_ z{trfTW2pHTTKdVwpb%U4OSVj*0~lf6#)Fd1JO&TI0x;(kO&`E7sP+u|0l%0<8#h

q=k9EiMc#;V1U}V$-$J_2}ekN9&oH>7zB)l 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�w3tLZKBtO#irQH2l5-DQx-xKpa2~pC1rE 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≷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

!_->c5(b7At)`Kk}A0A?(XgokZv}x=?;;Q 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 literal 0 HcmV?d00001 diff --git a/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md b/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md index 8964c3c8..b9083dca 100644 --- a/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md +++ b/context/methods/tou_and_rates/demand_flex_elasticity_calibration.md @@ -12,15 +12,25 @@ Arcturus 2.0 (Faruqui et al., 2017) is a meta-analysis of 62 time-varying pricin ### What they found -Peak demand reduction is strongly predicted by the peak-to-off-peak price ratio -- the higher the ratio, the more customers shift. They fit regression models separately for pilots with and without enabling technology (smart thermostats, in-home displays, etc.). The "no enabling technology" model, which is the one relevant to us, is: +Peak demand reduction is strongly predicted by the peak-to-off-peak price ratio -- the higher the ratio, the more customers shift. They fit regression models separately for pilots with and without enabling technology (smart thermostats, in-home displays, etc.) See Fig 12 in Arcturus 2.0: + +**No enabling technology** (conservative baseline): $$\text{peak\_reduction} = -0.011 + (-0.065) \times \ln(\text{price\_ratio})$$ -So at a 2:1 ratio, customers reduced peak demand by about 5.5%. At 4:1, about 10.6%. The relationship is log-linear -- doubling the ratio doesn't double the response; there are diminishing returns. +**With enabling technology** (smart thermostats, in-home displays, etc.) -- same intercept, slope augmented by the interaction term (-0.046): + +$$\text{peak\_reduction} = -0.011 + (-0.111) \times \ln(\text{price\_ratio})$$ + +At a 2:1 ratio, the no-tech model predicts ~5.6% peak reduction; the with-tech model predicts ~8.8%. At 4:1, ~10.1% vs ~16.5%. The relationship is log-linear -- doubling the ratio doesn't double the response; there are diminishing returns. + +### No-tech vs. with-tech -### Why "no enabling technology" +Arcturus separates results into groups based on whether customers had enabling technology. The no-tech group represents customers responding to price signals alone with no automation. The with-tech group includes customers with smart thermostats, in-home displays, or other automation, producing roughly 2x the demand response at any given price ratio. -Arcturus separates results into groups based on whether customers had enabling technology. The no-tech group represents customers responding to price signals alone with no automation. This is the conservative baseline. With enabling tech, responses are roughly 2x larger -- but we don't assume HP customers have smart controls, so we anchor to the lower bound. +Note: the paper estimates a single regression with an interaction term `ln(ratio) × tech`, where `tech` is a binary dummy. The with-tech slope is the sum of the base slope (-0.065) and the interaction coefficient (-0.046). The intercept is the same for both models. + +Our default calibration uses the **no-tech** model as a conservative baseline. The calibration script also reports **with-tech** recommendations for sensitivity analysis and scenarios that assume HP customers have smart controls. Both sets of seasonal elasticities are written to the periods YAML (`elasticity` and `elasticity_with_tech`); the scenario generation script selects between them based on the `enabling_tech` column in the Google Sheet (empty or TRUE = with-tech; FALSE/no/0 = no-tech). ### How we use it @@ -33,9 +43,27 @@ Our model uses a different functional form -- constant elasticity ($Q_{\text{shi This gives us an empirically grounded elasticity: not a theoretical value, but one that reproduces the aggregate demand response observed in actual pricing pilots at a comparable price ratio. +### Seasonal elasticities + +Summer and winter have very different TOU price ratios (e.g. CenHud: 3.05 summer vs 1.57 winter), so Arcturus predicts different peak reductions for each season. A single annual elasticity is necessarily a compromise: matching summer undershoots winter, matching winter overshoots summer. Seasonal elasticities address this by calibrating each season independently against its own Arcturus target. + +The runtime implementation supports seasonal elasticities natively. In the scenario YAML, elasticity can be specified as a scalar (applied uniformly) or as a `{season: value}` dict: + +```yaml +# Scalar (backward compatible): +elasticity: -0.12 + +# Seasonal: +elasticity: + winter: -0.12 + summer: -0.14 +``` + +The shifting pipeline already iterates per season (`_shift_season` in `utils/cairo.py`), so per-season elasticity resolution adds no structural complexity -- it simply looks up the season-specific value from the dict instead of using the scalar. + ### What Arcturus doesn't tell us -It measures aggregate outcomes across heterogeneous customers -- some shift a lot, some don't shift at all. Our model applies the same epsilon uniformly to every building. The recommended epsilon is therefore an approximation that matches the *average* pilot outcome, not the distribution of individual responses. +It measures aggregate outcomes across heterogeneous customers -- some shift a lot, some don't shift at all. Our model applies the same epsilon uniformly to every building. The recommended epsilon is therefore an approximation that matches the _average_ pilot outcome, not the distribution of individual responses. ## Diagnostic methodology @@ -47,15 +75,15 @@ Each NY utility has different TOU structures derived from their marginal cost pr - **Peak window widths** are either 3 hours (ConEd, NiMo, NYSEG, RGE) or 5 hours (CenHud, OR, PSEG-LI) - **HP customer share** ranges from 1.4% (PSEG-LI) to 4.4% (RGE) of weighted customers -Because HP customers heat with electricity, they consume disproportionately more in winter, when the price ratio is lower. We compute a demand-weighted annual price ratio that reflects this seasonal load distribution, giving us a single Arcturus target per utility. +Because HP customers heat with electricity, they consume disproportionately more in winter, when the price ratio is lower. We compute a demand-weighted annual price ratio that reflects this seasonal load distribution, giving us a single Arcturus target per utility. When using seasonal elasticities, we instead match each season's Arcturus target independently. ### How the diagnostic works -The diagnostic script (`utils/post/diagnose_demand_flex.py`) does the following for each utility: +The calibration script (`utils/pre/calibrate_demand_flex_elasticity.py`) does the following for each utility: 1. Loads the actual hourly electricity consumption for every HP building in the baseline stock (upgrade=00) 2. Loads the TOU derivation data (price ratios, peak hours, base rates per season) -3. At each candidate elasticity (-0.02 through -0.20 in 0.02 steps), applies the same constant-elasticity shifting formula CAIRO uses: $Q_{\text{shifted}} = Q_{\text{orig}} \times (P_{\text{period}} / P_{\text{flat}})^\varepsilon$ +3. At each candidate elasticity (default: -0.04 through -0.50 in 0.02 steps, configurable via `--epsilon-start`/`--epsilon-end`/`--epsilon-step`), applies the same constant-elasticity shifting formula CAIRO uses: $Q_{\text{shifted}} = Q_{\text{orig}} \times (P_{\text{period}} / P_{\text{flat}})^\varepsilon$ 4. Measures the resulting peak reduction percentage 5. Compares to the Arcturus prediction for this utility's price ratio 6. Selects the elasticity whose peak reduction most closely matches Arcturus @@ -76,39 +104,61 @@ We also compute rate arbitrage savings: the dollars an HP customer saves by cons ## Results (March 2026) -### Recommended elasticities +### Seasonal elasticities — no enabling technology + +Each season is matched independently against its own Arcturus no-tech target derived from that season's TOU ratio. These values are written to `config/periods/{utility}.yaml` under the `elasticity` key. -| Utility | Recommended ε | Annual savings/HP bldg | Peak window | Annual weighted ratio | Arcturus target | -| ------- | :-----------: | :--------------------: | :---------: | :-------------------: | :-------------: | -| CenHud | -0.12 | $27.58 | 5 hours | 2.14 | 6.1% | -| ConEd | -0.10 | $25.72 | 3 hours | 3.01 | 8.3% | -| NiMo | -0.10 | $12.47 | 3 hours | 2.17 | 6.1% | -| NYSEG | -0.10 | $12.57 | 3 hours | 2.16 | 6.1% | -| OR | -0.12 | $23.60 | 5 hours | 2.17 | 6.1% | -| PSEG-LI | -0.12 | $46.77 | 5 hours | 2.51 | 7.1% | -| RGE | -0.10 | $13.67 | 3 hours | 2.16 | 6.1% | +| Utility | Summer ε | Summer ratio | Summer Arcturus | Winter ε | Winter ratio | Winter Arcturus | Savings/HP | +| ------- | :------: | :----------: | :-------------: | :------: | :----------: | :-------------: | :--------: | +| CenHud | -0.14 | 3.05 | 8.3% | -0.12 | 1.57 | 4.0% | $19.29 | +| ConEd | -0.12 | 4.33 | 10.6% | -0.10 | 1.98 | 5.5% | $21.05 | +| NiMo | -0.10 | 2.91 | 8.0% | -0.10 | 1.75 | 4.7% | $5.90 | +| NYSEG | -0.10 | 2.93 | 8.1% | -0.10 | 1.75 | 4.7% | $7.40 | +| OR | -0.14 | 3.13 | 8.5% | -0.12 | 1.57 | 4.0% | $14.03 | +| PSEG-LI | -0.14 | 3.80 | 9.8% | -0.12 | 1.67 | 4.4% | $22.73 | +| RGE | -0.10 | 2.87 | 7.9% | -0.10 | 1.75 | 4.7% | $5.65 | + +### Seasonal elasticities — with enabling technology + +Same methodology, but calibrated against the Arcturus with-tech target (slope = -0.111). Written to `config/periods/{utility}.yaml` under the `elasticity_with_tech` key. + +| Utility | Summer ε | Summer Arcturus | Winter ε | Winter Arcturus | Savings/HP | +| ------- | :------: | :-------------: | :------: | :-------------: | :--------: | +| CenHud | -0.22 | 13.5% | -0.18 | 6.1% | $29.38 | +| ConEd | -0.18 | 17.4% | -0.16 | 8.7% | $31.07 | +| NiMo | -0.18 | 13.0% | -0.16 | 7.3% | $9.95 | +| NYSEG | -0.18 | 13.0% | -0.16 | 7.3% | $12.49 | +| OR | -0.24 | 13.8% | -0.20 | 6.1% | $23.25 | +| PSEG-LI | -0.22 | 15.9% | -0.18 | 6.8% | $34.53 | +| RGE | -0.18 | 12.8% | -0.16 | 7.3% | $9.53 | ### Key takeaways -1. **Two natural groups**: Utilities with 3-hour peak windows land at ε = -0.10; utilities with 5-hour peak windows land at ε = -0.12. Wider peak windows shift more kWh per unit of epsilon, so less epsilon is needed to match the same Arcturus target. +1. **Two natural groups**: Utilities with 3-hour peak windows land at ε = -0.10 (no-tech); utilities with 5-hour peak windows land at ε = -0.12. Wider peak windows shift more kWh per unit of epsilon, so less epsilon is needed to match the same Arcturus target. -2. **Savings are modest but real**: $12-$48 per HP building per year from rate arbitrage alone. This is on top of the much larger savings (~$590 for ConEd) from simply switching to the TOU rate structure in the first place. +2. **With-tech elasticities are ~1.5-1.7x larger**: Enabling technology pushes seasonal epsilons from -0.10/−0.14 (no-tech) to roughly -0.16/−0.22 (with-tech), increasing per-HP savings by 50-90%. -3. **Marginal cost savings are negligible**: Delivery MC is nonzero in only 40-113 hours per year. The frozen-residual RR reduction from load shifting is effectively zero at the delivery level. Bill savings come entirely from the rate structure spread. +3. **Savings are modest but real**: No-tech: $6-$23 per HP building per year; with-tech: $10-$35. This is from rate arbitrage alone, on top of the much larger savings (~$590 for ConEd) from simply switching to the TOU rate structure. -4. **Winter matters less than summer for shifting savings**: Winter price ratios (1.6-2.0) are much lower than summer (2.9-4.3), so the rate spread available for arbitrage is smaller. HP customers' heavy winter load contributes little to shifting savings despite being the season where they consume the most. +4. **Marginal cost savings are negligible**: Delivery MC is nonzero in only 40-113 hours per year. The frozen-residual RR reduction from load shifting is effectively zero at the delivery level. Bill savings come entirely from the rate structure spread. -5. **These are conservative estimates**: Arcturus "no enabling technology" represents the low end of observed demand response. With smart thermostats or other enabling technology, larger elasticities would be justified. +5. **Winter matters less than summer for shifting savings**: Winter price ratios (1.6-2.0) are much lower than summer (2.9-4.3), so the rate spread available for arbitrage is smaller. HP customers' heavy winter load contributes little to shifting savings despite being the season where they consume the most. ### CAIRO ground truth comparison -At ε = -0.10 (current setting), CAIRO bill outputs for ConEd show: +The `--compare-batch` flag compares analytical savings predictions against actual CAIRO bill differences. Use `--with-tech` when the CAIRO batch was configured with `elasticity_with_tech`: -- Run 3+4 (default tariff, no flex): mean elec bill = $2,329/year -- Run 11+12 (TOU, no flex): mean = $1,740/year (TOU structure saves $589) -- Run 15+16 (TOU flex, ε=-0.1): mean = $1,729/year (flex adds $10.51 more savings) +```bash +# Compare against no-tech batch: +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity \ + --compare-batch ny_20260325b_r1-16 -Note: the $10.51 CAIRO figure is for the counterfactual all-HP world (runs 15+16, upgrade=02). The diagnostic's $25.72 is per HP building in the realistic baseline stock (upgrade=00). These are different scenarios and are not directly comparable. +# Compare against with-tech batch: +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity \ + --compare-batch ny_20260326_elast_seasonal_tech --with-tech +``` + +Note: CAIRO runs use upgrade=02 (counterfactual all-HP world), while the diagnostic computes savings per HP building in the upgrade=00 baseline stock. These are different scenarios; expect directional agreement rather than exact matches. ## Validation (March 2026) @@ -118,11 +168,11 @@ The shift mechanics were validated against CAIRO's own outputs using `utils/post All 7 NY utilities pass all checks: -| Check | Result | -| ----- | ------ | +| Check | Result | +| ---------------------------------------------- | -------------------------------------------------------------- | | Energy conservation (per building, per season) | PASS -- max \|orig − shifted\| = 0.000 kWh (machine precision) | -| Direction (peak kWh ↓, off-peak kWh ↑) | PASS -- all utilities, both seasons | -| CAIRO tracker match (per-building realized ε) | max \|Δε\| < 0.015, mean \|Δε\| < 0.003 across all utilities | +| Direction (peak kWh ↓, off-peak kWh ↑) | PASS -- all utilities, both seasons | +| CAIRO tracker match (per-building realized ε) | max \|Δε\| < 0.015, mean \|Δε\| < 0.003 across all utilities | The small tracker differences appear only on the **receiver (off-peak) period**, where CAIRO's zero-sum residual differs slightly from our analytical reproduction due to floating-point accumulation order. Donor (peak) period epsilons match CAIRO exactly to machine precision. @@ -130,13 +180,13 @@ The small tracker differences appear only on the **receiver (off-peak) period**, | Utility | Summer ratio | Summer peak red | Winter ratio | Winter peak red | | ------- | :----------: | :-------------: | :----------: | :-------------: | -| CenHud | 3.05 | 6.43% | 1.57 | 3.35% | -| ConEd | 4.33 | 9.83% | 1.98 | 5.44% | -| NiMo | 2.91 | 7.67% | 1.75 | 4.60% | -| NYSEG | 2.93 | 7.68% | 1.75 | 4.60% | -| OR | 3.13 | 6.56% | 1.57 | 3.29% | -| PSEG-LI | 3.80 | 7.40% | 1.67 | 3.69% | -| RGE | 2.87 | 7.54% | 1.75 | 4.58% | +| CenHud | 3.05 | 6.43% | 1.57 | 3.35% | +| ConEd | 4.33 | 9.83% | 1.98 | 5.44% | +| NiMo | 2.91 | 7.67% | 1.75 | 4.60% | +| NYSEG | 2.93 | 7.68% | 1.75 | 4.60% | +| OR | 3.13 | 6.56% | 1.57 | 3.29% | +| PSEG-LI | 3.80 | 7.40% | 1.67 | 3.69% | +| RGE | 2.87 | 7.54% | 1.75 | 4.58% | ### What the diagnostic plots show @@ -152,27 +202,47 @@ Five plots per utility are written to `dev_plots/flex/{utility}/`: - **`building_daily_profile_{bldg_id}.png`**: Per-building view for 5 illustrative buildings at different consumption percentiles. Shows original and shifted load curves overlaid on a representative summer and winter weekday, with TOU rate structure as a step function on the right axis. Suitable as a report visual; per-building hourly data for 100 buildings is written to `{utility}_building_hourly_shifted.parquet` for further use. -### Invoking the validation +### Invoking the calibration and validation ```bash -# One utility with CAIRO tracker cross-check: +# Calibrate all utilities (default sweep -0.04 to -0.50, step -0.02): +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity + +# Calibrate specific utilities with custom range: +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity \ + --utilities cenhud,coned --epsilon-start -0.04 --epsilon-end -0.60 --epsilon-step -0.02 + +# Calibrate and write both no-tech/with-tech to periods YAMLs: +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity --write-periods + +# Compare against CAIRO batch (no-tech): +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity \ + --compare-batch ny_20260325b_r1-16 + +# Compare against CAIRO batch (with-tech): +just -f rate_design/hp_rates/ny/Justfile calibrate-demand-flex-elasticity \ + --compare-batch ny_20260326_elast_seasonal_tech --with-tech + +# Validate shift mechanics (scalar elasticity): just -f rate_design/hp_rates/ny/Justfile validate-demand-flex coned -0.10 ny_20260325b_r1-16 -# All utilities: -just -f rate_design/hp_rates/ny/Justfile validate-demand-flex-all -0.10 ny_20260325b_r1-16 +# Validate with seasonal elasticity: +uv run python -m utils.post.validate_demand_flex_shift \ + --utility coned --elasticity winter=-0.10,summer=-0.08 \ + --output-dir dev_plots/flex/coned -# Elasticity sweep diagnostic: -just -f rate_design/hp_rates/ny/Justfile diagnose-demand-flex +# Validate all utilities: +just -f rate_design/hp_rates/ny/Justfile validate-demand-flex-all -0.10 ny_20260325b_r1-16 ``` ## Known limitations 1. **Uniform vs heterogeneous response**: Arcturus measures aggregate outcomes from diverse customers; our model applies the same epsilon to every HP building. The peak reduction distribution confirms this -- all buildings get the same % reduction, which is unrealistic. 2. **Functional form mismatch**: Arcturus is log-linear; our model is power-law. The comparison is valid at specific price ratios, not across the full curve. -3. **Annual elasticity is a compromise**: A single epsilon can't perfectly match both winter and summer Arcturus targets simultaneously. We use a demand-weighted annual ratio as the calibration anchor. +3. **Seasonal elasticities assume season-specific price response**: Arcturus pilots measured customers responding to a single TOU rate announced at enrollment. Our seasonal elasticities model customers who implicitly respond differently in summer vs winter. This is reasonable -- summer and winter have different peak/off-peak spreads, weather-driven loads, and behavioral incentives -- but it extends beyond what Arcturus directly measured. 4. **Delivery MC is too sparse for meaningful savings**: The frozen-residual channel produces negligible savings because delivery MC is concentrated in ~100 hours/year. Supply MC (NYISO LBMP) would add a broader spread, but supply MCs are zeroed in delivery-only runs. ## References -- Faruqui, A., Sergici, S., & Warner, C. (2017). Arcturus 2.0: A meta-analysis of time-varying rates for electricity. *The Electricity Journal*, 30(10), 64-72. -- Simenone, M. et al. (2023). Bill alignment test paper. *Utilities Policy*. +- Faruqui, A., Sergici, S., & Warner, C. (2017). Arcturus 2.0: A meta-analysis of time-varying rates for electricity. _The Electricity Journal_, 30(10), 64-72. +- Simenone, M. et al. (2023). Bill alignment test paper. _Utilities Policy_. diff --git a/context/plans/demand-flex_elasticity_calibration.md b/context/plans/demand-flex_elasticity_calibration.md index e57ccde3..2ad9c306 100644 --- a/context/plans/demand-flex_elasticity_calibration.md +++ b/context/plans/demand-flex_elasticity_calibration.md @@ -3,13 +3,13 @@ name: Demand-flex elasticity calibration overview: Find the correct elasticity parameter per NY utility by running a fast analytical diagnostic against real TOU structures and MC data, grounded in Arcturus empirical predictions, then validate with targeted CAIRO runs. Results stored at s3://data.sb/switchbox/demand_flex/ with utility partitioning. todos: - id: diagnostic-script - content: "Create `utils/post/diagnose_demand_flex.py`: per-utility analytical diagnostic reading real TOU derivation JSONs and MC data, computing demand shift + MC savings + Arcturus comparison for elasticity sweep" + content: "Create `utils/post/calibrate_demand_flex_elasticity.py`: per-utility analytical calibration reading real TOU derivation JSONs and MC data, computing demand shift + MC savings + Arcturus comparison for elasticity sweep" status: completed - id: shift-validation-script content: "Create `utils/post/validate_demand_flex_shift.py`: reproduce shift analytically, check energy conservation + direction, cross-check vs CAIRO tracker, produce diagnostic plots and data outputs" status: completed - id: justfile-recipes - content: "Add `validate-demand-flex`, `validate-demand-flex-all`, `diagnose-demand-flex` recipes to ny/Justfile" + content: "Add `validate-demand-flex`, `validate-demand-flex-all`, `calibrate-demand-flex-elasticity` recipes to ny/Justfile" status: completed - id: run-diagnostic content: Run the diagnostic and validation for all 7 NY utilities, review results @@ -32,19 +32,20 @@ isProject: false **Phase 1 complete.** All analytical work is done and validated. PR #382 closed the GitHub issue. -| Deliverable | Status | -| ----------- | ------ | -| `utils/post/diagnose_demand_flex.py` -- elasticity sweep diagnostic | Done | -| `utils/post/validate_demand_flex_shift.py` -- shift validation + diagnostic plots | Done | -| `rate_design/hp_rates/ny/Justfile` -- `validate-demand-flex`, `diagnose-demand-flex` recipes | Done | -| `context/methods/tou_and_rates/demand_flex_elasticity_calibration.md` -- methodology writeup | Done | -| Per-utility recommended elasticities (7 utilities) | Done -- see results table | -| Shift validation across all 7 utilities (energy conservation, direction, CAIRO tracker match) | Done -- all pass | -| Diagnostic plots in `dev_plots/flex/{utility}/` | Done | -| Update scenario YAMLs with per-utility elasticities | **Pending** | -| Phase 2 CAIRO sweep at recommended elasticities | Optional / deferred | +| Deliverable | Status | +| -------------------------------------------------------------------------------------------------------- | ------------------------- | +| `utils/post/calibrate_demand_flex_elasticity.py` -- elasticity calibration sweep | Done | +| `utils/post/validate_demand_flex_shift.py` -- shift validation + diagnostic plots | Done | +| `rate_design/hp_rates/ny/Justfile` -- `validate-demand-flex`, `calibrate-demand-flex-elasticity` recipes | Done | +| `context/methods/tou_and_rates/demand_flex_elasticity_calibration.md` -- methodology writeup | Done | +| Per-utility recommended elasticities (7 utilities) | Done -- see results table | +| Shift validation across all 7 utilities (energy conservation, direction, CAIRO tracker match) | Done -- all pass | +| Diagnostic plots in `dev_plots/flex/{utility}/` | Done | +| Update scenario YAMLs with per-utility elasticities | **Pending** | +| Phase 2 CAIRO sweep at recommended elasticities | Optional / deferred | **Recommended elasticities (pending YAML update):** + - ε = -0.10: ConEd, NiMo, NYSEG, RGE (3-hour peak windows) - ε = -0.12: CenHud, OR, PSEG-LI (5-hour peak windows) @@ -64,7 +65,6 @@ Bill savings under demand flex at `epsilon = -0.1` are very small. We need to: From the TOU derivation JSONs (`[ny/config/tou_derivation/](rate_design/hp_rates/ny/config/tou_derivation/)`): - | Utility | Winter Ratio | Winter Peak Hours | Summer Ratio | Summer Peak Hours | | -------- | ------------ | ----------------- | ------------ | ----------------- | | ConEd | 2.0 | 16-18 (3h) | 4.3 | 15-17 (3h) | @@ -72,7 +72,6 @@ From the TOU derivation JSONs (`[ny/config/tou_derivation/](rate_design/hp_rates | NiMo | 1.8 | 17-19 (3h) | 2.9 | 17-19 (3h) | | (5 more) | ... | ... | ... | ... | - A single `epsilon = -0.1` produces very different peak reductions depending on the ratio. Arcturus "no enabling tech" predictions at these ratios: - `peak_reduction = -0.011 + (-0.065) * ln(price_ratio)` @@ -128,11 +127,11 @@ Example for ConEd (from actual files): ```mermaid flowchart TD subgraph phase1 ["Phase 1: Analytical diagnostic (no CAIRO)"] - TOU["TOU derivation JSONs\n(7 utilities x 2 seasons)"] --> diag["diagnose_demand_flex.py"] - MC["MC data from S3\n(bulk + dist per utility)"] --> diag - ARC["Arcturus model\n(no-tech coefficients)"] --> diag - diag --> table["Per-utility table:\nprice ratios, peak reduction %,\nMC spread, savings ceiling,\nrecommended epsilon"] - diag --> csv["CSV at s3://data.sb/\nswitchbox/demand_flex/\ndiagnostic/"] + TOU["TOU derivation JSONs\n(7 utilities x 2 seasons)"] --> cal["calibrate_demand_flex_elasticity.py"] + MC["MC data from S3\n(bulk + dist per utility)"] --> cal + ARC["Arcturus model\n(no-tech coefficients)"] --> cal + cal --> table["Per-utility table:\nprice ratios, peak reduction %,\nMC spread, savings ceiling,\nrecommended epsilon"] + cal --> csv["CSV at s3://data.sb/\nswitchbox/demand_flex/\ndiagnostic/"] end subgraph phase2 ["Phase 2: Targeted CAIRO sweep"] @@ -142,9 +141,7 @@ flowchart TD end ``` - - -## Phase 1: Diagnostic on real load data (`utils/post/diagnose_demand_flex.py`) +## Phase 1: Calibration on real load data (`utils/post/calibrate_demand_flex_elasticity.py`) New script. Uses actual ResStock building loads, customer metadata, and MC data for all 7 NY utilities. Focuses on the **runs 13-14 scenario** (precalc with HP/non-HP subclasses), which reflects the realistic world where only HP buildings (`postprocess_group.has_hp=True`) are on the TOU flex tariff and subject to demand shifting. HP buildings are a small fraction of each utility's customer base -- this is the primary reason bill savings are small. @@ -167,19 +164,22 @@ For each of the 7 NY utilities, for each season, for each elasticity in `[-0.05, **Financial metrics (two distinct savings mechanisms):** -*Rate arbitrage savings (primary, per-HP-customer):* +_Rate arbitrage savings (primary, per-HP-customer):_ + - kWh shifted from peak to offpeak per HP customer (demand-weighted average) - Bill savings from rate structure = kWh_shifted x (peak_rate - offpeak_rate) per season - Annual per-HP-customer bill savings from rate arbitrage - This uses the TOU derivation rates, not calibrated rates (first-order; CAIRO calibration adjusts these) - Comparison baseline: the current default rate structure for each utility (varies by utility) -*RR reduction savings (secondary, system-level):* +_RR reduction savings (secondary, system-level):_ + - HP subclass MC delta ($) = sum_h(MC_h x shift_h) for HP buildings only (demand-weighted) - Per-HP-customer RR savings = HP_MC_delta / HP_weighted_count (under subclass RR) - This is small because HPs are a small share of all customers -*MC data context (NOT Cambium -- all derived from NYISO LBMP and utility PUC filings):* +_MC data context (NOT Cambium -- all derived from NYISO LBMP and utility PUC filings):_ + - NY uses 5 utility-specific MC components: supply energy/NYISO LBMP (most hours nonzero), supply capacity (96 hours/year), supply ancillary, bulk TX (~80 SCR hours/year), dist+sub-TX (~100 PoP hours/year) - **Delivery-only runs (13) zero out supply MCs** (`zero.parquet`). Only dist+sub-tx + bulk_tx are active -- extremely peaky, nonzero in only ~100-180 hours. MC-based RR savings from shifting are small because the spread is so concentrated. - **Delivery+supply runs (14)** include supply energy (NYISO LBMP, nonzero virtually all hours) -- broader MC spread, larger MC savings possible. @@ -209,7 +209,7 @@ For each of the 7 NY utilities, for each season, for each elasticity in `[-0.05, **CLI:** ``` -uv run python -m utils.post.diagnose_demand_flex \ +uv run python -m utils.post.calibrate_demand_flex_elasticity \ --state ny \ --elasticities -0.05,-0.10,-0.15,-0.20 \ [--output-dir /tmp/demand_flex_diagnostic] \ @@ -232,10 +232,11 @@ uv run python -m utils.post.diagnose_demand_flex \ CAIRO outputs at `s3://data.sb/switchbox/cairo/outputs/hp_rates/ny/all_utilities/ny_20260325b_r1-16/` provide actual bill numbers at `epsilon = -0.1` for all 7 utilities. Key finding for ConEd: - Run 3+4 (default tariff, no flex): mean elec bill = $2,329/year -- Run 11+12 (TOU, no flex): mean = $1,740/year (TOU structure saves $589) -- Run 15+16 (TOU flex, epsilon=-0.1): mean = $1,729/year (flex adds only $10.51 more savings) +- Run 11+12 (TOU, no flex): mean = $1,740/year (TOU structure saves $589) +- Run 15+16 (TOU flex, epsilon=-0.1): mean = $1,729/year (flex adds only $10.51 more savings) The demand-flex effect at epsilon=-0.1 is ~0.6% of the bill -- confirming the "small savings" concern. The TOU structure change dominates. The diagnostic should: + 1. Reproduce this $10.51 analytically (validation) 2. Show how it scales with larger elasticity (sweep) 3. Identify the ceiling (is it MC spread, HP share, or both?) @@ -260,13 +261,13 @@ Deferred until Phase 1 results are reviewed. The plan: ## Files to create/modify -- **Create**: `[utils/post/diagnose_demand_flex.py](utils/post/diagnose_demand_flex.py)` -- Phase 1 diagnostic -- **Modify**: `[rate_design/hp_rates/Justfile](rate_design/hp_rates/Justfile)` -- add `diagnose-demand-flex` recipe +- **Create**: `[utils/post/calibrate_demand_flex_elasticity.py](utils/post/calibrate_demand_flex_elasticity.py)` -- Phase 1 calibration +- **Modify**: `[rate_design/hp_rates/Justfile](rate_design/hp_rates/Justfile)` -- add `calibrate-demand-flex-elasticity` recipe - **Update**: Linear ticket RDP-175 with evolved scope ## Relationship to existing RDP-175 work -The existing validation/sensitivity scripts (`[utils/post/validate_demand_flex.py](utils/post/validate_demand_flex.py)`, `[utils/post/sensitivity_demand_flex.py](utils/post/sensitivity_demand_flex.py)`) verify mathematical invariants on synthetic data. This diagnostic is complementary -- it operates on real utility configurations and MC data to answer the calibration question. The validation scripts still need the API fix (PR #379 breakage) but that is independent work. +The former synthetic validation/sensitivity scripts (`validate_demand_flex.py`, `sensitivity_demand_flex.py`) were removed -- their mathematical invariant checks were either tautological or fully covered by the real-data calibration and shift validation scripts. ## Known limitations and caveats @@ -287,4 +288,3 @@ These are explicitly acknowledged; the diagnostic output should note them. - For the sweep, each elasticity re-runs precalc (run 13) which recalibrates via Phase 1.75. The pre-calibrated `_flex.json` is always the same (copy of the derivation tariff). Only the calibrated output changes. So the sweep is self-contained per elasticity value. - How to manage 42 calibrated tariff files? One approach: write to a sweep-specific output directory (not the main `config/tariffs/electric/`), parameterized by elasticity, so sweep artifacts don't pollute the main config. - Should we sweep delivery+supply runs (13+14 -> 15+16) or delivery-only (13 -> 15)? Delivery-only gives the delivery bill savings; adding supply doubles compute time. Recommend delivery-only for the sweep, full 4-run chain only for the final chosen elasticity. - From 3a48075cef06e9f36c1b227a7d7e765ad9f83bf8 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Thu, 26 Mar 2026 20:47:51 +0000 Subject: [PATCH 19/19] Remove unused shift_map assignment in calibrate_demand_flex_elasticity Made-with: Cursor --- utils/pre/calibrate_demand_flex_elasticity.py | 106 ++++++++---------- 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/utils/pre/calibrate_demand_flex_elasticity.py b/utils/pre/calibrate_demand_flex_elasticity.py index 3a5e460c..af036714 100644 --- a/utils/pre/calibrate_demand_flex_elasticity.py +++ b/utils/pre/calibrate_demand_flex_elasticity.py @@ -28,7 +28,6 @@ import logging import math import subprocess -import sys from dataclasses import dataclass, field from pathlib import Path from typing import cast @@ -204,8 +203,7 @@ def arcturus_peak_reduction_with_tech(price_ratio: float) -> float: if price_ratio <= 1.0: return 0.0 return abs( - ARCTURUS_WITH_TECH_INTERCEPT - + ARCTURUS_WITH_TECH_SLOPE * math.log(price_ratio) + ARCTURUS_WITH_TECH_INTERCEPT + ARCTURUS_WITH_TECH_SLOPE * math.log(price_ratio) ) @@ -467,16 +465,14 @@ def diagnose_season( mc_peak_overlap = int((mc_in_peak["mc_kwh"].abs() > 1e-9).sum()) hourly_shift = season_df.copy() - shift_map = targets.set_index(["bldg_id", "energy_period"])["load_shift"] hourly_shift = hourly_shift.merge( targets[["bldg_id", "energy_period", "load_shift", "Q_orig"]], on=["bldg_id", "energy_period"], how="left", ) - period_sums = ( - hourly_shift.groupby(["bldg_id", "energy_period"])["electricity_net"] - .transform("sum") - ) + period_sums = hourly_shift.groupby(["bldg_id", "energy_period"])[ + "electricity_net" + ].transform("sum") hour_share = np.where( period_sums != 0, hourly_shift["electricity_net"] / period_sums, @@ -485,7 +481,9 @@ def diagnose_season( hourly_shift["hourly_shift_kwh"] = hourly_shift["load_shift"] * hour_share weights_series = hourly_shift["bldg_id"].map(hp_weights).fillna(1.0) - hourly_shift["weighted_shift"] = hourly_shift["hourly_shift_kwh"] * weights_series + hourly_shift["weighted_shift"] = ( + hourly_shift["hourly_shift_kwh"] * weights_series + ) system_hourly = ( hourly_shift.groupby("time")["weighted_shift"].sum().reset_index() @@ -602,8 +600,7 @@ def diagnose_utility( ) / total_kwh else: result.annual_weighted_ratio = ( - winter_specs[0].peak_offpeak_ratio - + summer_specs[0].peak_offpeak_ratio + winter_specs[0].peak_offpeak_ratio + summer_specs[0].peak_offpeak_ratio ) / 2 elif ctx.season_specs: result.annual_weighted_ratio = ctx.season_specs[0].peak_offpeak_ratio @@ -678,9 +675,7 @@ def diagnose_utility( season=sn, recommended_elasticity=best_wt_eps, arcturus_target_pct=season_arcturus_wt, - peak_reduction_pct=best_wt_sr.peak_reduction_pct - if best_wt_sr - else 0.0, + peak_reduction_pct=best_wt_sr.peak_reduction_pct if best_wt_sr else 0.0, price_ratio=season_ratio, savings_per_hp=best_wt_sr.rate_arbitrage_savings_per_hp if best_wt_sr @@ -728,9 +723,7 @@ def results_to_dataframe(results: list[UtilityResult]) -> pl.DataFrame: "elasticity": sr.elasticity, "price_ratio": round(sr.price_ratio, 4), "base_rate_kwh": round(sr.base_rate, 6), - "peak_rate_kwh": round( - sr.base_rate * sr.price_ratio, 6 - ), + "peak_rate_kwh": round(sr.base_rate * sr.price_ratio, 6), "n_peak_hours_per_day": sr.n_peak_hours_per_day, "hp_bldg_count": sr.hp_bldg_count, "hp_weighted_share_pct": round(sr.hp_weighted_share, 2), @@ -738,9 +731,7 @@ def results_to_dataframe(results: list[UtilityResult]) -> pl.DataFrame: "hp_peak_kwh_shifted": round(sr.hp_peak_kwh_shifted, 1), "peak_reduction_pct": round(sr.peak_reduction_pct, 3), "kwh_shifted": round(sr.kwh_shifted, 1), - "system_peak_reduction_pct": round( - sr.system_peak_reduction_pct, 4 - ), + "system_peak_reduction_pct": round(sr.system_peak_reduction_pct, 4), "p_flat": round(sr.p_flat, 6), "arcturus_peak_reduction_pct": round( sr.arcturus_peak_reduction_pct, 3 @@ -749,9 +740,7 @@ def results_to_dataframe(results: list[UtilityResult]) -> pl.DataFrame: "rate_arbitrage_savings_per_hp": round( sr.rate_arbitrage_savings_per_hp, 2 ), - "delivery_mc_savings_total": round( - sr.delivery_mc_savings_total, 2 - ), + "delivery_mc_savings_total": round(sr.delivery_mc_savings_total, 2), "delivery_mc_savings_per_hp": round( sr.delivery_mc_savings_per_hp, 2 ), @@ -791,7 +780,9 @@ def _seasonal_rec_field( ) -> float: """Look up a seasonal recommendation field for the output DataFrame.""" recs = ( - ur.seasonal_recommendations_with_tech if with_tech else ur.seasonal_recommendations + ur.seasonal_recommendations_with_tech + if with_tech + else ur.seasonal_recommendations ) for rec in recs: if rec.season == season: @@ -833,9 +824,7 @@ def _read_annual_bills(csv_path: str) -> pl.DataFrame | None: try: return cast( pl.DataFrame, - pl.scan_csv(csv_path) - .filter(pl.col("month") == "Annual") - .collect(), + pl.scan_csv(csv_path).filter(pl.col("month") == "Annual").collect(), ) except Exception as e: log.warning(" Failed to read %s: %s", csv_path, e) @@ -897,32 +886,26 @@ def compare_batch_bills( dirname = parts[-1].rstrip("/") run_dirs[dirname] = f"{batch_prefix}{dirname}" - hp_bldg_ids = ( - hp_meta.filter(pl.col("sb.electric_utility") == utility)["bldg_id"] - .to_list() - ) + hp_bldg_ids = hp_meta.filter(pl.col("sb.electric_utility") == utility)[ + "bldg_id" + ].to_list() pairs: list[PairResult] = [] for label, base_frag, flex_frag, desc in COMPARISON_PAIRS: - base_dir = next( - (v for k, v in run_dirs.items() if base_frag in k), None - ) - flex_dir = next( - (v for k, v in run_dirs.items() if flex_frag in k), None - ) + base_dir = next((v for k, v in run_dirs.items() if base_frag in k), None) + flex_dir = next((v for k, v in run_dirs.items() if flex_frag in k), None) if not base_dir or not flex_dir: log.warning( " %s: %s or %s not found, skipping %s", - utility, base_frag, flex_frag, label, + utility, + base_frag, + flex_frag, + label, ) continue - base_df = _read_annual_bills( - f"{base_dir}/bills/elec_bills_year_run.csv" - ) - flex_df = _read_annual_bills( - f"{flex_dir}/bills/elec_bills_year_run.csv" - ) + base_df = _read_annual_bills(f"{base_dir}/bills/elec_bills_year_run.csv") + flex_df = _read_annual_bills(f"{flex_dir}/bills/elec_bills_year_run.csv") if base_df is None or flex_df is None: continue @@ -940,9 +923,7 @@ def compare_batch_bills( how="inner", ) .with_columns( - (pl.col("bill_baseline") - pl.col("bill_flex")).alias( - "savings" - ), + (pl.col("bill_baseline") - pl.col("bill_flex")).alias("savings"), ) ) @@ -1081,7 +1062,9 @@ def print_summary(results: list[UtilityResult], *, verbose: bool = False) -> Non # ── Seasonal recommendation table ───────────────────────────────────── _print_seasonal_table( - "RECOMMENDED ELASTICITIES — no enabling technology (seasonal)", results, "no_tech" + "RECOMMENDED ELASTICITIES — no enabling technology (seasonal)", + results, + "no_tech", ) _print_seasonal_table( "RECOMMENDED ELASTICITIES — with enabling technology (seasonal)", @@ -1100,10 +1083,7 @@ def print_summary(results: list[UtilityResult], *, verbose: bool = False) -> Non for eps in all_eps: header += f" {eps:>7.2f}" print(header) - print( - f" {'─' * 8} {'─' * 14}" - + "".join(f" {'─' * 7}" for _ in all_eps) - ) + print(f" {'─' * 8} {'─' * 14}" + "".join(f" {'─' * 7}" for _ in all_eps)) for ur in results: if not ur.rr_decrease_by_elasticity: continue @@ -1185,15 +1165,21 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace: p = argparse.ArgumentParser(description=__doc__) p.add_argument("--state", required=True, choices=list(STATE_CONFIGS.keys())) p.add_argument( - "--epsilon-start", type=float, default=-0.04, + "--epsilon-start", + type=float, + default=-0.04, help="Start of elasticity sweep range (default: -0.04)", ) p.add_argument( - "--epsilon-end", type=float, default=-0.50, + "--epsilon-end", + type=float, + default=-0.50, help="End of elasticity sweep range, inclusive (default: -0.50)", ) p.add_argument( - "--epsilon-step", type=float, default=-0.02, + "--epsilon-step", + type=float, + default=-0.02, help="Step size for elasticity sweep (default: -0.02)", ) p.add_argument("--output-dir", type=Path, default=None) @@ -1242,10 +1228,10 @@ def main(argv: list[str] | None = None) -> None: ) cfg = STATE_CONFIGS[args.state] - elasticities = _build_epsilon_range(args.epsilon_start, args.epsilon_end, args.epsilon_step) - utilities = ( - tuple(args.utilities.split(",")) if args.utilities else cfg["utilities"] + elasticities = _build_epsilon_range( + args.epsilon_start, args.epsilon_end, args.epsilon_step ) + utilities = tuple(args.utilities.split(",")) if args.utilities else cfg["utilities"] storage_options = get_aws_storage_options() project_root = Path(__file__).resolve().parents[2] @@ -1276,7 +1262,9 @@ def main(argv: list[str] | None = None) -> None: scale = eia_customer_count / raw_weight_sum if raw_weight_sum > 0 else 1.0 log.info( " EIA customer count: %d, raw weight sum: %.0f, scale factor: %.4f", - eia_customer_count, raw_weight_sum, scale, + eia_customer_count, + raw_weight_sum, + scale, ) hp_weights = {