Skip to content

Add regional interconnection costs#138

Draft
Max-Vanatta wants to merge 15 commits into
mainfrom
int_supply_curve_regional
Draft

Add regional interconnection costs#138
Max-Vanatta wants to merge 15 commits into
mainfrom
int_supply_curve_regional

Conversation

@Max-Vanatta

@Max-Vanatta Max-Vanatta commented Jul 8, 2026

Copy link
Copy Markdown

Summary

Replaces the flat intra-zone point-of-interconnection (POI) / network-reinforcement adder (GSw_TransIntraCost) with an optional increasing-cost supply curve. Successive amounts of new capacity built in a zone pay a higher marginal reinforcement cost, applied to all technologies through the existing INV_POI / eq_POI_cap mechanism.

While this is referred to as the POI in the code currently, this is purely the scaffolding that was used to implement the inclusion. POI in reV is separate from the reinforcement costs (POI is not the network costs). In this implementation, the POI from reV is still included but the reinforcement assumptions previously in reV are stripped out and replaced by the binned costs.

The bin structure reuses the VRE resource-supply-curve pattern (rtscbin as the indexer rather than rscbin): each bin has an incremental capacity width and a per-bin cost. The flat cost is the degenerate one-bin case, so numpoibins = 1 (the default) reproduces current behavior exactly — the binned method is fully opt-in.

Method documentation: inputs/transmission/poi_supply_curve.md.

Technical details

Implementation notes

  • Input processing (reeds/input_processing/): make_poi_supply_curve.py (new) builds the zonal cost bins from raw_interconnection_TSC_data.csv via optimal capacity-weighted least-squares segmentation. transmission.py::write_poi_supply_curve writes poi_supply_curve.csv (flat GSw_TransIntraCost when numpoibins=1, otherwise the binned curve with an unlimited GSw_POIUpperCost backstop bin). raw_interconnection_TSC_data.csv is the single source for every spatial resolution (like the hashed transmission cost files) — no per-zone-set fallback; a run whose regions it does not cover fails loudly (matching the transmission cost/distance validation).
  • GAMS: INV_POI(r,t)INV_POI(r,rtscbin,t); new set rtscbin, params cost_poi_bin/cap_poi_bin/poi_bin_feas, and eq_POI_binlim (incremental bin-width cap) in b_inputs.gms/c_model.gms. Objective (d_objective.gms), var-fixing (5_varfix.gms), and reporting (report.gms, report_params.csv) updated to carry the rtscbin index. The single zonal curve applies to every technology (the free existing capacity poi_cap_init and the non-generator POI terms — spur, converter, LCC — attach here too).
  • Double-counting avoided: when the curve is active (numpoibins != 1 and GSw_TransIntraCost > 0), reeds/io.py drops the reinforcement cost already embedded in the VRE resource supply curves (all regions, since coverage is enforced), so it is not counted twice. Retained unchanged when numpoibins = 1.

Additional changes

  • reeds/reedsplots.py + postprocessing/single_case_plots.py: plotting helper for the POI supply curve.
  • inputs/transmission/README.md: provenance for the raw input file; dollaryear.csv entry added (raw_interconnection_TSC_data.csv, USD2024). The per-zone-set poi_supply_curve_{GSw_ZoneSet}.csv placeholder files and their runfiles.csv copy entry were removed — the curve is built at run time from the single raw source.

Switches added/removed/changed

Switch Change Description
numpoibins added Number of POI cost bins. 1 (default) = legacy flat GSw_TransIntraCost; >1 = binned curve; 0 = defualt (one bin per raw segment).
GSw_POIUpperCost added [USD2004/kW] cost of the unlimited backstop bin above the finite binned capacities; only active when numpoibins != 1.
GSw_TransIntraCost unchanged Now also serves as the numpoibins = 1 flat-cost value.

Known incompatibilities

  • numpoibins != 1 (binned or defualt curve) requires GSw_TransIntraCost > 0; the curve is gated on that switch, so the combination numpoibins != 1 with GSw_TransIntraCost = 0 is rejected with a ValueError in runreeds.py rather than silently charging no reinforcement.
  • numpoibins = 1 (the default) reproduces the legacy flat-cost behavior exactly.

Relevant sources or documentation

  • Interconnection cost data from the TSC model with still to be PR-ed interconnection optimization. TODO: Add in the exact links and docs (noted in inputs/transmission/README.md).

Validation, testing, and comparison report(s)

  • Pending. This is a model + data change, so full-US reference and full-US decarb comparison reports are required and will be added before merge.
  • Test-case configs are included for exercising the feature at ERCOT resolution: cases_transmission_test.csv (ERCOT_default = flat legacy, ERCOT_0 = near-zero flat, ERCOT_regional = binned curve).

Checklist for author

Details to double-check

  • Charge code provided to reviewers
  • Included comparison reports for appropriate test cases (pending)
  • Documentation updated (inputs/transmission/README.md, inputs/transmission/poi_supply_curve.md, docs/source/model_documentation.md)
  • If input data added/modified:
    • Dollar year recorded (dollaryear.csv, USD2024) and converted to 2004$ for GAMS
    • Units are specified
    • Preprocessing steps documented/committed to ReEDS_Input_Processing (TSC-side; to confirm)
    • New large data files handled with .h5 instead of .csv (only raw_interconnection_TSC_data.csv is added, which is small; the per-zone-set curve files were removed in favor of the single raw source)
  • Code formatting standardized
  • Reusable functions used where possible instead of copy/pasted code

General information to guide review

  • Zero impact on results of default case (numpoibins=1 default)
  • No large data file(s) added/modified (only the small raw_interconnection_TSC_data.csv)
  • No substantive impact on runtime for full-US reference case
  • No change to package requirements
  • No change to process flow (runreeds.py gains one compatibility check: numpoibins != 1 requires GSw_TransIntraCost > 0)

Did you use LLM tools (chatbot or copilot) in the preparation of this PR? If so, describe how

Yes. Claude Code (Anthropic) was used across several sessions as a pair-programming and drafting assistant, with the author directing the design and reviewing/testing all changes performed by the LLM. Specifically, this was involved in:

  • Feature implementation: planning and scaffolding the conversion of the flat INV_POI adder into a binned supply curve — GAMS sets/params/equations (b_inputs.gms, c_model.gms, d_objective.gms), make_poi_supply_curve.py, the transmission.py writer, and the objective/report/var-fix wiring.
  • Hardening & hygiene: the USD2024→2004$ dollar-year conversion via get_inflatable()/dollaryear.csv; replacing most silent-default sw.get() switch lookups with direct indexing (keeping .get() only where switches are re-read from pre-feature runs); relocating make_poi_supply_curve.py into reeds/input_processing/; renaming the mislabeled capacity_MWcapacity_GW column; consolidating and restyling the documentation; and diff-hygiene cleanup.
  • Review response: a multi-agent code review of the branch drove several fixes — gating the double-count reinforcement drop on numpoibins != 1 (so defualt mode doesn't double-count), a runreeds.py guard rejecting numpoibins != 1 with GSw_TransIntraCost = 0, consolidating the curves to the single raw_interconnection_TSC_data.csv source with fail-loud region-coverage validation (removing the per-zone-set placeholder files), and the sw.get() backward-compat for postprocessing on older runs.

All LLM-assisted changes were reviewed, directed, and tested by the author.

Note: parts of this PR — including the summary of LLM usage above — were themselves drafted by Claude Code (from the branch diff and prior session history) and reviewed/revised by the author.

Max-Vanatta and others added 15 commits May 31, 2026 21:15
…ariant)

Derived from int_supply_cruve_sub by stripping the wind (wind-ons)
reinforcement limit, leaving the zonal / technology-agnostic increasing-cost
POI reinforcement supply curve and its regional-curve fixes intact.

Removed:
- GAMS: INV_WPOI variable, eq_WPOI_cap / eq_WPOI_binlim / eq_WPOI_link
  (c_model.gms), cap_wpoi_bin / wpoi_sc_dat load + subset clip (b_inputs.gms),
  INV_WPOI var-fixing (5_varfix.gms), wind_poi_capacity_bin report
  (report.gms, report_params.csv)
- Python: transmission.py::write_wind_poi_supply_curve + its call;
  runreeds.py GSw_WindReinf/numpoibins compatibility guard
- Switches/data: GSw_WindReinf (cases.csv), ERCOT_Wind test config
  (cases_transmission_test.csv), wind-ons_nodal_supply_curve.csv +
  its dollaryear.csv / README entries
- Docs: wind sections in poi_supply_curve.md

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… doc/test fixes

Behavioral:
- io.py: gate the double-count reinforcement drop on numpoibins != 1 (was > 1),
  so the native curve (numpoibins=0) no longer double-counts VRE reinforcement
- runreeds.py: raise when numpoibins != 1 with GSw_TransIntraCost == 0 (the
  curve is gated on GSw_TransIntraCost, so that combo silently charged nothing)

Quick/safe:
- cases_transmission_test.csv: run the ERCOT_default/ERCOT_0 baselines (ignore=0),
  fix ERCOT_defualt->ERCOT_default typo, add trailing newline
- model_documentation.md: reinforcement drop covers wind/solar/CSP/geothermal
- reedsplots.py: label POI curve y-axis [2004$/kW] (values are deflated)
- PR_description.md: typos, test-case list, document the new compatibility guard

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Per review direction:
- transmission.py: raw_interconnection_TSC_data.csv is now the single source of
  the POI reinforcement curves for every spatial resolution (like the hashed
  transmission cost files). Removed the per-zone-set fallback; if the raw file is
  missing or does not cover every model region when numpoibins != 1, raise
  (mirroring the transmission cost/distance / ITL validation)
- Deleted the committed placeholder poi_supply_curve_{zoneset}.csv files
  (~30.6k lines) and the runfiles.csv copy entry; write_poi_supply_curve writes
  inputs_case/poi_supply_curve.csv itself
- dollaryear.csv: drop the now-unused poi_supply_curve.csv entry (raw file stays
  USD2024)
- io.py: read numpoibins/GSw_TransIntraCost via sw.get() with legacy defaults so
  postprocessing (input plots, reeds_to_rev) doesn't AttributeError on
  pre-feature runs
- Docs (README, poi_supply_curve.md, model_documentation.md,
  make_poi_supply_curve.py, PR_description.md) updated for the raw-only source

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Remove reedsplots.plot_poi_supply_curve and its single_case_plots.py driver
block; update PR text to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@patrickbrown4 patrickbrown4 self-requested a review July 8, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant