Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/source/config-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,13 @@ network is aggregated to. `transmission_network` chooses between the ReEDS zonal
TAMU synthetic nodal network; `topological_boundaries` sets the zone type used after clustering
(county, REeDS zone, state, or balancing area). Use `include` to subset the modeled footprint to
specific zones, states, or balancing authorities (mixed zone types are not supported), and
`aggregate` to pre-aggregate buses into larger regions. `interface_transmission_limits` applies
NARIS2024 inter-regional transfer capacity limits and requires the ReEDS backbone.
`aggregate` to pre-aggregate buses into larger regions. `interface_transmission_limits` switches
on the aggregate inter-regional transfer limits read from
`electricity: transmission_interface_limits` and requires the ReEDS backbone; the limits are
applied in `solve_network` as a per-snapshot cap on the total flow across each interface, and
constrain only the import/export links, so they are inert unless `electricity: imports` or
`electricity: exports` is enabled. See {ref}`spatial` for a worked California example
(`workflow/repo_data/config/config.california.yaml`) at both REeDS-zone and county resolution.

```{eval-rst}
.. literalinclude:: ../../workflow/repo_data/config/config.default.yaml
Expand Down
43 changes: 43 additions & 0 deletions docs/source/config-spatial.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,49 @@ model_topology:

Alternatively, you can use the code reeds_state: 'CA' option to achieve the same result by specifying the entire state.

A complete, maintained California configuration ships with the repository as
`workflow/repo_data/config/config.california.yaml`. It pairs the footprint below with CPUC
SERVM demand, the RESOLVE CAISO interface limits, and enabled imports/exports:

```yaml
scenario:
interconnect: [western]
planning_horizons: [2030, 2035, 2040, 2045] # CPUC SERVM forecast years
clusters: [4] # p8, p9, p10, p11
simpl: [75]

model_topology:
transmission_network: 'reeds'
topological_boundaries: 'reeds_zone'
interface_transmission_limits: true # RESOLVE CAISO interface caps
include:
reeds_state: ['CA']
```

`clusters` is pinned to 4 because the ReEDS zonal backbone cannot be clustered below the
number of zones in the footprint, and California holds exactly four.

To run the same footprint at **county resolution**, switch `topological_boundaries` to
`county` and raise `clusters` to 58 — the number of California counties, and the number of
`p06xxx` nodes in the county NARIS interface table. `simpl: ['county']` selects the
county-FIPS fast path in `cluster_simpl`, so the resource layer is built directly on county
boundaries (a numeric `simpl` of at least 58 also works if you want more resource zones than
transmission nodes):

```yaml
scenario:
clusters: [58] # 58 California counties
simpl: ['county'] # county-FIPS fast path

model_topology:
topological_boundaries: 'county'
```

`add_extra_components` switches from the balancing-area NARIS flowgate file to the county one
(`transmission_capacity_init_AC_county_NARIS2024.csv`) automatically when
`topological_boundaries` is `county` — no other key needs changing. `config.california.yaml`
carries this same block as a commented alternative.

In addition to filtering by `reeds_zone` and `reeds_state`, you can filter by `reeds_ba`, `trans_reg`, and `nerc_reg` shown graphically below.


Expand Down
4 changes: 2 additions & 2 deletions docs/source/configtables/electricity.csv
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ erm:,,,Energy Reserve Margin settings (used when ERM opt is enabled). Ensures su
regional_Co2_limits,--,path,"CSV of per-region CO2 caps in tCO2/yr (``config/policy_constraints/regional_Co2_limits.csv``). Enforced when the ``REM`` keyword is present in the ``{opts}`` wildcard."
technology_capacity_targets,--,path,"CSV of forced minimum/maximum capacity builds by technology and region (``config/policy_constraints/technology_capacity_targets.csv``). Enforced when the ``TCT`` keyword is present in the ``{opts}`` wildcard."
portfolio_standards,--,path,"CSV of RPS/CES clean-energy fractions by region (``config/policy_constraints/portfolio_standards.csv``). Enforced when the ``RPS`` keyword is present in the ``{opts}`` wildcard (covers both RPS and CES targets)."
transmission_interface_limits,--,path,"CSV of MW limits on flows across inter-regional transmission interfaces (``config/policy_constraints/transmission_interface_limits.csv``), paired with ``model_topology: interface_transmission_limits``. Reserved setting — not currently consumed by the workflow."
transmission_interface_limits,--,path,"CSV of MW limits on flows across inter-regional transmission interfaces (``config/policy_constraints/transmission_interface_limits.csv``, columns ``interface, region_1, region_2, flow_12, flow_21``). Applied by ``solve_network`` when ``model_topology: interface_transmission_limits`` is ``true``, as a per-snapshot cap on the **aggregate** flow across each interface rather than a path-by-path limit: ``flow_12`` caps exports out of ``region_1`` into ``region_2``, ``flow_21`` caps imports in the opposite direction. Only the import/export ``Link`` components added by ``add_extra_components`` are constrained, so the caps are inert when ``imports``/``exports`` are disabled."
,,,
co2limit_enable,bool,true or false,"Switch to activate the system-wide CO2 cap below. Optional; defaults to false when unset. Can also be set via the ``Co2L`` keyword in the ``{opts}`` wildcard."
co2limit,:math:`t_{CO_2}/a`,float,"System-wide cap on annual CO2 emissions, added as a global constraint in ``prepare_network``. Only applied when ``co2limit_enable`` is true."
Expand All @@ -33,7 +33,7 @@ gaslimit,MWh thermal,float,"Cap on annual gas-fired primary energy from gas carr
,,,
demand:,,,
-- bus_allocation,--,"One of {``population``, ``breakthrough``}","How zone-level demand is distributed to individual buses. ``population`` (default) weights buses by 2020 Decennial Census county populations (split evenly across each county's substations, then each substation's buses). ``breakthrough`` uses the legacy nominal-demand column (``Pd``) from the 2016-vintage Breakthrough Energy grid model."
-- profile,--,"One of {``efs``, ``eia``, ``eer``, ``servm``}","Datasource for electrical load data. ``EFS`` pulls future state level electrical demand data. ``EIA`` pulls historical balancing level electrical demand data. ``EER`` pulls future state-level profiles from the EER dataset; when selected, ``planning_horizons`` must be one of 2021, 2025, 2030, 2035, 2040, 2045, or 2050 and ``renewable_weather_years`` must contain exactly one year from 2007-2013 or 2016-2023. ``SERVM`` pulls CPUC SERVM hourly load for the six California load regions (California models only); when selected, ``planning_horizons`` must be one of 2026, 2028, 2030, 2032, 2035, 2037, 2040, 2042, or 2045."
-- profile,--,"One of {``efs``, ``eia``, ``eer``, ``servm``}","Datasource for electrical load data. ``EFS`` pulls future state level electrical demand data. ``EIA`` pulls historical balancing level electrical demand data. ``EER`` pulls future state-level profiles from the EER dataset; when selected, ``planning_horizons`` must be one of 2021, 2025, 2030, 2035, 2040, 2045, or 2050 and ``renewable_weather_years`` must contain exactly one year from 2007-2013 or 2016-2023. ``SERVM`` pulls CPUC SERVM hourly load for the six California load regions (California models only); when selected, ``planning_horizons`` must be one of 2026, 2028, 2030, 2032, 2035, 2037, 2040, 2042, or 2045, and each horizon must carry a full 8760-hour snapshot year. Only the ``Net Load`` component is dispatched against; the full component split is written to ``power_zonal_components_s{simpl}.parquet``. See the SERVM section of the demand data page."
-- scenario:,,,
-- -- efs_case,--,"One of {``reference``, ``medium``, ``high``}",(UNDER DEVELOPMENT) Extracts EFS data according to level of adoption
-- -- efs_speed,--,"One of {``slow``, ``moderate``, ``fast``}",(UNDER DEVELOPMENT) Extracts EFS data according to speed of electrification
Expand Down
100 changes: 97 additions & 3 deletions docs/source/data-demand.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,98 @@ the model years 2021, 2025, 2030, 2035, 2040, 2045, and 2050, and each profile i
historical weather year, so `renewable_weather_years` must contain exactly one year from
2007-2013 or 2016-2023.

(servm-demand)=
### CPUC SERVM (California)

`profile: servm` uses the hourly load forecast the California Public Utilities Commission
publishes for its 2026 Integrated Resource Planning cycle, produced with the SERVM
production-cost model. It is a **California-only** dataset — use it with a footprint scoped
to California (`model_topology: include: reeds_state: ['CA']`); the maintained entry point is
`workflow/repo_data/config/config.california.yaml`.

The workflow retrieves one CSV per forecast year from

```
https://files.cpuc.ca.gov/energy/modeling/2026_servm_updates/HourlyLoad_CA_Regions_V2025E_2224_Mon_{year}.csv
```

(~118 MB each, via `retrieve_cpuc_servm_load`). **Nine forecast years are published: 2026,
2028, 2030, 2032, 2035, 2037, 2040, 2042, and 2045.** Unlike EFS, SERVM demand is *not*
interpolated or AEO-scaled between published years, so `scenario: planning_horizons` must be
drawn from that set — any other year raises in `ReadServm`.

#### Regions

SERVM reports six California load regions. Each maps onto the balancing areas PyPSA-USA
carries on its buses (`workflow/repo_data/CPUC/servm_region_map.csv`):

| SERVM region | PyPSA-USA balancing area(s) | Notes |
| --- | --- | --- |
| `PGE` | `CISO-PGAE` | PG&E CAISO footprint |
| `SCE` | `CISO-SCE` | Includes Valley Electric Association's California load, per the CPUC data dictionary |
| `SDGE` | `CISO-SDGE` | San Diego Gas & Electric |
| `IID` | `IID` | Imperial Irrigation District |
| `LADWP` | `LDWP` | Los Angeles Department of Water and Power |
| `NCNC` | `BANC` + `TIDC` | Northern California non-CAISO: Balancing Authority of Northern California and Turlock Irrigation District |

`CISO-VEA` — the Valley Electric Association balancing area — is a Nevada footprint and is
deliberately **excluded** from California-only networks. It carries an empty region in the
mapping file, so its (small) load share is dropped with a log message, while any *unknown*
balancing area introduced by upstream relabeling still hard-fails.

Because the SERVM regions are balancing areas rather than states, the demand is disaggregated
with a purpose-built weights table (`build_servm_load_weights`) instead of the generic
state/BA path: a cluster bus can straddle two SERVM regions (Los Angeles County holds both
`LDWP` and `CISO-SCE` buses), so a bus receives the sum of its share of every region it
overlaps. The underlying per-bus weights are still the `bus_allocation` weights described
below.

#### Components

Each file publishes several load components per region (`Load`, `BTMPV`, `EV`, `DATA_CEN`, ...)
alongside `Net Load`. **Only `Net Load` is dispatched against by the model** — it is what
remains after behind-the-meter PV and other embedded resources. Every published component is
nonetheless carried through on the `subsector` index level and written to the zonal artifact
`resources/<run>/demand/{interconnect}/power_zonal_components_s{simpl}.parquet`, so the
component split stays available for reporting. Components that exist for only some regions
(`EV` and friends are published for PGE/SCE/SDGE only) align to `NaN` there.

#### Weather years

Each forecast-year file stacks 25 weather years (2000-2024) of a full hourly year.
`electricity: demand: scenario: servm_weather_years:` selects which one to use. It takes a
list with **exactly one** entry; multiple entries are reserved for stochastic scenarios
(phase 3) and currently raise `NotImplementedError`, because the demand output path is not
weather-year specific.

**Set `servm_weather_years` equal to the top-level `renewable_weather_years`.** Drawing load
and wind/solar profiles from different weather years decorrelates them and will understate
both the peak-net-load and the flexibility need. A mismatch is permitted but logs a warning.

#### Timezone and calendar caveats

SERVM strips are in **fixed Pacific Standard Time (UTC−8) with no daylight-saving
transition**. This is not stated in the source files; it was verified empirically from the
behind-the-meter PV solar-noon centroid, which sits at hour 12.52 in December and 12.68 in
July — a DST-observing series would move by a full hour between the two. The strips are
rolled forward 8 hours to UTC before being attached.

Two calendar misalignments are accepted, and are immaterial for an hourly
capacity-expansion model, but matter if you compare hour-for-hour against another source:

1. **Monday-start synthetic calendar.** SERVM lays each year's 8760 hours on a synthetic
calendar that starts on a Monday, so weekday-versus-weekend hours do not line up with the
real weekdays of the planning horizon.
2. **Leap weather years.** For a leap *weather* year the SERVM strip contains February 29 and
omits December 31, while PyPSA-USA's snapshots do the opposite (`get_snapshots` drops
February 29 from leap planning horizons). Every hour after February therefore lands one
calendar day earlier than it sat in the source file.

The strip is mapped **positionally** onto the network's own per-period snapshots rather than
onto a synthesised `date_range` — the latter would run a day short of December 31 for the
leap planning horizons (2028, 2032, 2040). As a consequence each planning horizon must carry
exactly 8760 snapshots; a truncated snapshot window cannot be used with `profile: servm`.

## Demand Disaggregation

All of the demand sources above arrive at a coarser resolution than the network: EIA930
Expand Down Expand Up @@ -68,8 +160,9 @@ horizons setting, and the electricity demand setting. If conducting historical s
user must select a planning horizon in the past (2018-2023) and set `profile: eia`.

If conducting forward-looking planning cases the user must set a future planning horizon —
2030, 2040, or 2050 with `profile: efs`, or any of 2021, 2025, 2030, 2035, 2040, 2045, and
2050 with `profile: eer`.
2030, 2040, or 2050 with `profile: efs`; any of 2021, 2025, 2030, 2035, 2040, 2045, and
2050 with `profile: eer`; or any of 2026, 2028, 2030, 2032, 2035, 2037, 2040, 2042, and 2045
with `profile: servm` (California only).

For planning horizons between the EFS data years, PyPSA-USA implements a scaling factor that
interpolates between future years or scales historical demand using forecasts from the Annual
Expand All @@ -81,11 +174,12 @@ scenario:

electricity:
demand:
profile: efs # efs, eia, eer
profile: efs # efs, eia, eer, servm
scenario:
efs_case: reference # reference, medium, high
efs_speed: moderate # slow, moderate, rapid
eer_file: demand_EER2025_100by2050.h5 # used when profile: eer
servm_weather_years: [2019] # used when profile: servm; exactly one year, 2000-2024
aeo: reference
```

Expand Down
31 changes: 31 additions & 0 deletions docs/source/data-transmission.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,37 @@ While representative of the US electricity system, the TAMU network is synthetic
See the [Spatial Configuration](./config-spatial.md) page for information on how to choose between networks.
```

## Interface Transmission Limits

The path-by-path ratings above are complemented by **interface** limits: aggregate MW caps on
the total simultaneous flow across a bundle of paths. PyPSA-USA ships the CPUC RESOLVE
interface table at `config/policy_constraints/transmission_interface_limits.csv`, which rates
the CAISO import/export capability against the rest of WECC:

| Interface | `region_1` (inside) | `region_2` (outside) | `flow_12` (MW) | `flow_21` (MW) |
| --- | --- | --- | --- | --- |
| `CA_NW` | p9, p10, p11 | p2, p5, p6, p7, p8 | 3,592 | 9,269 |
| `CA_SW` | p9, p10, p11 | p12, p13, p25, p27, p28, p30 | 10,901 | 10,463 |
| `CAISO_Imports` | p9, p10, p11 | all of the above | 9,728 | 10,208 |

**Flow orientation:** `flow_12` is the cap on flow *out of* `region_1` (exports), `flow_21` the
cap on flow *into* `region_1` (imports). Enable the table with
`model_topology: interface_transmission_limits: true`; the constraint formulation is described
in [Model Constraints](./model-constraints.md#interface-transmission-limits).

```{warning}
The interface caps are applied to the virtual `imports` / `exports` links created by
`add_extra_components`, so they bind only when `electricity: imports` / `electricity: exports`
are enabled, and they only see flow that crosses the boundary of the modeled footprint.

`p8` (northeastern California) appears in the `region_2` list of every RESOLVE row but is
itself a California zone. In a California-only model it is therefore *inside* the network, and
the internal `p8`-`p9` AC corridor — about 300 MW in the ReEDS/NARIS balancing-area table —
carries no trade links and escapes the `CAISO_Imports` cap. Simultaneous CAISO imports are
understated by roughly that amount. This gap is documented rather than corrected: closing it
would require constraining internal AC lines alongside the trade links.
```

(transmission-data)=
### Data
```{eval-rst}
Expand Down
1 change: 1 addition & 0 deletions docs/source/datatables/demand.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Characteristic,Data Source,Spatial Scale,Temporal Scale
Historical Demand,GridEmissions (EIA930),Balancing Area,Hourly (2018- 2023)
Future Demand,NREL Electrification Futures Study (EFS),States,"Hourly (2030, 2040, 2050)"
Future Demand (California),CPUC SERVM 2026 IRP Hourly Load,Six California load regions (PGE/SCE/SDGE/IID/LADWP/NCNC),"Hourly (2026, 2028, 2030, 2032, 2035, 2037, 2040, 2042, 2045; weather years 2000-2024)"
Loading