Skip to content
Merged
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
75 changes: 70 additions & 5 deletions docs/source/config-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,68 @@ PyPSA-USA supports two sources for renewable capacity-factor time series, select
- `atlite` — runtime computation of capacity factors from ERA5 cutouts, weighted by Copernicus / CORINE land-use exclusions (the legacy default; see [Renewable Capacity Factors](renewable_cfs) under Model Data).
- `godeeep` — pre-computed regional climate-model capacity factors from the [GODEEEP](https://www.pnnl.gov/projects/godeeep) (Grid Operations, Decarbonization, Environmental and Energy Equity Platform) dataset, weighted at runtime by NREL reV land-access exclusions.

The `godeeep` path consumes two file families published on Zenodo, downloaded automatically by `scripts/zenodo_downloader.py` on first run:
The `godeeep` path consumes two file families:

- **Compressed GODEEEP capacity factors** — per-cell hourly capacity factors on the GODEEEP Lambert Conformal grid, uint8-quantized and zlib-compressed (~12× smaller than the raw aggregated files). One Zenodo record per `(tech, scenario)`:
- solar: [historical](https://doi.org/10.5281/zenodo.20127513), [rcp45hotter](https://doi.org/10.5281/zenodo.20127523), [rcp45cooler](https://doi.org/10.5281/zenodo.20127562), [rcp85hotter](https://doi.org/10.5281/zenodo.20127589), [rcp85cooler](https://doi.org/10.5281/zenodo.20127633)
- wind (125 m): [historical](https://doi.org/10.5281/zenodo.20127520), [rcp45hotter](https://doi.org/10.5281/zenodo.20127545), [rcp45cooler](https://doi.org/10.5281/zenodo.20127572), [rcp85hotter](https://doi.org/10.5281/zenodo.20127604), [rcp85cooler](https://doi.org/10.5281/zenodo.20127645)
- **Compressed GODEEEP capacity factors** — per-cell hourly capacity factors on the GODEEEP Lambert Conformal grid, uint8-quantized and zlib-compressed (~12× smaller than the raw aggregated files; ~350 MB per solar year, ~800 MB per wind year). One file per `(tech, hub height, scenario, year)`, retrieved by `rule retrieve_godeeep_cf` from the sources declared in `godeeep_cf_registry` (see below).
- **NREL land-access artifacts** ([10.5281/zenodo.20127899](https://doi.org/10.5281/zenodo.20127899)) — `avail_{tech}_{access}[_cec|_boem].nc` per-cell availability rasters and `caps_{tech}_{access}[_cec|_boem].nc` per-bus rollups (`weight`, `p_nom_max`, `potential`, `average_distance`, and `underwater_fraction` for offshore).

#### How capacity-factor files are retrieved

Capacity-factor files are no longer downloaded from inside `build_renewable_profiles`. A dedicated
rule, `retrieve_godeeep_cf`, places each file, driven by the `godeeep_cf_registry` block of
`config.common.yaml`. The registry is an *ordered* list of sources — a local mirror and Zenodo —
and each source declares exactly which `(dataset key, year)` pairs it holds. The first source
declaring the requested pair wins.

There are **no fallback paths**: no default hub height, no nearest-year substitution, no swap to an
unscreened variant. A request for a dataset or year that no source declares fails at snakemake
parse time with an error naming the dataset key, the requested year and the years available from
each source. Local hits are symlinked into the run's `data/` directory (set `copy_local: true` to
copy instead) and verified against the mirror's `SHA256SUMS` manifest before use.

#### Which data is available

Dataset keys are `{tech}{wind_height}_{scenario}_compressed`. On Sherlock all of the following is
reachable out of the box, because the shipped config declares the group's Oak mirror
(`/oak/stanford/groups/iazevedo/GoDEEEP_Capacity_Factors_compressed`, group `oak_iazevedo`) as the
first source. Off Sherlock, edit `godeeep_cf_registry` to remove or repoint the `local` source —
only the Zenodo subset is then reachable.

| Dataset key | `godeeep_wind_height` | Oak mirror | Zenodo |
|---|---|---|---|
| `solar_historical_compressed` | — | 1980–2022 | 2012 only ([20127513](https://doi.org/10.5281/zenodo.20127513)) |
| `wind_100m_historical_compressed` | `"_100m"` | 1980–2022 | not published |
| `wind_125m_historical_compressed` | `"_125m"` | 1980–2022 | 2012 only ([20127520](https://doi.org/10.5281/zenodo.20127520)) |
| `solar_rcp45hotter_compressed` | — | — | 2030 / 2040 / 2050 ([20127523](https://doi.org/10.5281/zenodo.20127523)) |
| `solar_rcp45cooler_compressed` | — | — | 2030 / 2040 / 2050 ([20127562](https://doi.org/10.5281/zenodo.20127562)) |
| `solar_rcp85hotter_compressed` | — | — | 2030 / 2040 / 2050 ([20127589](https://doi.org/10.5281/zenodo.20127589)) |
| `solar_rcp85cooler_compressed` | — | — | 2030 / 2040 / 2050 ([20127633](https://doi.org/10.5281/zenodo.20127633)) |
| `wind_125m_rcp45hotter_compressed` | `"_125m"` | — | **not published** (record 20127545 returns HTTP 404) |
| `wind_125m_rcp45cooler_compressed` | `"_125m"` | — | 2030 / 2040 / 2050 ([20127572](https://doi.org/10.5281/zenodo.20127572)) |
| `wind_125m_rcp85hotter_compressed` | `"_125m"` | — | 2030 / 2040 / 2050 ([20127604](https://doi.org/10.5281/zenodo.20127604)) |
| `wind_125m_rcp85cooler_compressed` | `"_125m"` | — | 2030 / 2040 / 2050 ([20127645](https://doi.org/10.5281/zenodo.20127645)) |

Notable gaps:

- **Full historical coverage (1980–2022) requires the Oak mirror.** From Zenodo the historical
scenario is a single year, 2012.
- **100 m hub-height wind exists only on the Oak mirror.** Zenodo publishes 125 m only.
- **`rcp45hotter` is solar-only.** The corresponding 125 m wind record was never registered, so the
registry declares it with an empty year list; requesting it fails with `available years: (none)`
rather than silently downloading nothing.

The Oak mirror also holds an 80 m wind archive (1980–2022, sha256-verified alongside the rest), but
it is not yet wired into `godeeep_cf_registry` and the workflow accepts only `"_100m"` and
`"_125m"` for `godeeep_wind_height`. (`"_80m"` was the silent default behind
[#803](https://github.com/PyPSA/pypsa-usa/issues/803) and is now rejected.)

```{eval-rst}
.. literalinclude:: ../../workflow/repo_data/config/config.common.yaml
:language: yaml
:start-after: # docs : GODEEEP_CF_REGISTRY
:end-before: # docs :
```

#### Configuring a godeeep run

A complete godeeep configuration requires four config blocks beyond the standard `electricity:` / `clustering:` settings:
Expand All @@ -309,7 +364,10 @@ A complete godeeep configuration requires four config blocks beyond the standard
dataset: godeeep # set to atlite for the ERA5 + CORINE workflow
```

2. **Scenario and year selection**. The GODEEEP dataset has one historical record (2012) and four future climate scenarios (`rcp45hotter`, `rcp45cooler`, `rcp85hotter`, `rcp85cooler`) at planning horizons 2030 / 2040 / 2050.
2. **Scenario and year selection**. The GODEEEP dataset has a historical scenario and four future
climate scenarios (`rcp45hotter`, `rcp45cooler`, `rcp85hotter`, `rcp85cooler`) at planning
horizons 2030 / 2040 / 2050. Historical years run 1980–2022 via the Oak mirror; from Zenodo only
2012 is available. Any `(scenario, year)` outside the table above fails at parse time.

```yaml
renewable_scenarios: ["rcp45cooler"] # one of: historical | rcp45hotter | rcp45cooler | rcp85hotter | rcp85cooler
Expand All @@ -318,6 +376,13 @@ A complete godeeep configuration requires four config blocks beyond the standard

Future-scenario years come from the `planning_horizons` wildcard (under `scenario:`); the historical year comes from `renewable_weather_years`.

Wind runs must also set the hub height, which has **no default** — leaving it unset fails the
run:

```yaml
godeeep_wind_height: "_125m" # "_100m" (Oak mirror only) | "_125m" (Oak mirror + Zenodo)
```

3. **Snapshots** — the temporal slice within the chosen year. For godeeep this controls how much of the 8760-hour GODEEEP CF is sampled:

```yaml
Expand Down
6 changes: 3 additions & 3 deletions docs/source/data-generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ exclusion assumptions.

#### GODEEEP (default)

The default capacity-factor source (`renewable.dataset: godeeep`) is the [GODEEEP](https://www.pnnl.gov/projects/godeeep) dataset — regional-climate-model capacity factors developed at Pacific Northwest National Laboratory under the Grid Operations, Decarbonization, Environmental and Energy Equity Platform. Designed for multi-year climate-change scenario studies, GODEEEP provides hourly solar PV and 125 m hub-height wind capacity factors on a 12 km Lambert Conformal grid for:
The default capacity-factor source (`renewable.dataset: godeeep`) is the [GODEEEP](https://www.pnnl.gov/projects/godeeep) dataset — regional-climate-model capacity factors developed at Pacific Northwest National Laboratory under the Grid Operations, Decarbonization, Environmental and Energy Equity Platform. Designed for multi-year climate-change scenario studies, GODEEEP provides hourly solar PV and 100 m / 125 m hub-height wind capacity factors on a 12 km Lambert Conformal grid for:

- **One historical year** (2012) calibrated against observed weather.
- **A historical period** (1980–2022) calibrated against observed weather.
- **Four future climate scenarios** — `rcp45hotter`, `rcp45cooler`, `rcp85hotter`, `rcp85cooler` — under the RCP4.5 and RCP8.5 emissions pathways, downscaled with two GCM ensemble members per pathway.
- **Three planning horizons** (2030, 2040, 2050) per future scenario, drawn from contiguous 20-year (wind) or 40-year (solar) windows.

The raw GODEEEP files are large (~4.4 GB per `(tech, scenario, year)` triple). PyPSA-USA consumes a uint8-quantized + zlib-compressed variant (~350 MB for solar, ~800 MB for wind) published as 10 Zenodo records keyed by `(tech, scenario)`. The compressed files are pulled automatically at runtime by `scripts/zenodo_downloader.py`.
The raw GODEEEP files are large (~4.4 GB per `(tech, scenario, year)` triple). PyPSA-USA consumes a uint8-quantized + zlib-compressed variant (~350 MB for solar, ~800 MB for wind). Each compressed file is placed by `rule retrieve_godeeep_cf` from the sources declared in the `godeeep_cf_registry` config block — a local mirror (on Sherlock, the full 1980–2022 historical archive at both hub heights) and Zenodo records keyed by `(tech, scenario)`. Only part of the matrix is on Zenodo; see [`renewable: godeeep`](godeeep_cf) for exactly which `(dataset, year)` pairs each source holds.

GODEEEP capacity factors are re-aggregated to PyPSA-USA bus polygons using a runtime weighting step:

Expand Down
1 change: 1 addition & 0 deletions docs/source/model-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ on first use:
| `retrieve_eer_demand_data` | EER forecasted demand profiles (when `demand: profile: eer`) |
| `retrieve_gridemissions_data` | Historical grid emissions data |
| `retrieve_nrel_exclusion_artifact` | NREL reV-based land-access exclusion layers |
| `retrieve_godeeep_cf` | One compressed GODEEEP capacity-factor file, from the local mirror or Zenodo record declared in `godeeep_cf_registry` (godeeep runs) |
| `retrieve_res_eulp` / `retrieve_com_eulp` | NREL End-Use Load Profiles (sector runs) |
| `retrieve_sector_databundle` | Sector-coupling data bundle (sector runs) |
| `retrieve_seismic_risk_mask` | Seismic-risk exclusion mask (EGS runs) |
Expand Down
77 changes: 76 additions & 1 deletion workflow/repo_data/config/config.common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,87 @@ renewable:
renewable_land_access: # REQUIRED for godeeep; one of: reference | limited | open
apply_cec_basescreen: false # overlay CEC Wind/Solar BaseScreen on onwind+solar (CA-only)
apply_boem_osw: false # overlay BOEM offshore-wind planning areas on offwind + offwind_floating
godeeep_wind_height: "_100m" # raw GODEEEP wind-CF hub height; "_100m" | "_125m"
godeeep_wind_height: "_125m" # raw GODEEEP wind-CF hub height; "_100m" | "_125m" — NO DEFAULT is applied if unset, the run fails loudly.
# "_125m" is the only height published on Zenodo; "_100m" REQUIRES the local (Oak) source below.
# "_80m" is not a GODEEEP product — there is no data at that height.
nrel_caps_reassign: # out-of-footprint caps recovery for footprint-scoped runs (caps are rolled up nationally)
enable: false # when true, fold unmapped caps entries onto the nearest in-footprint bus (needs x/y in the caps file)
max_km: 100 # only reassign entries within this distance (km); farther entries stay dropped (prevents cross-seam teleporting)


# ====================================================================
# GODEEEP CF REGISTRY — where compressed capacity-factor files come from
# ====================================================================
# Declares every (dataset key, year) the workflow is allowed to retrieve.
# `godeeep_cf_registry.py` walks `sources` IN ORDER and the FIRST source
# declaring the requested (dataset key, year) wins. There are NO fallback
# paths: a missing dataset/year raises an error naming the dataset key, the
# requested year and the available years per source — it never substitutes a
# different year, hub height or screening variant (issue #803).
#
# Dataset keys are "{tech}{wind_height}_{scenario}_compressed", e.g.
# `solar_historical_compressed`, `wind_125m_rcp85cooler_compressed`.
# `years:` takes an inclusive range string ("1980-2022") or an explicit list.
# docs : GODEEEP_CF_REGISTRY
godeeep_cf_registry:
copy_local: false # false = the retrieve rule symlinks local hits (no duplication); true = copy into the run's data/
sources:
# ---- 1. Local Oak mirror: full 1980-2022 historical archive, both hub heights.
- kind: local
root: /oak/stanford/groups/iazevedo/GoDEEEP_Capacity_Factors_compressed
layout: "{scenario}/{tech_dir}/{filename}" # placeholders: scenario, tech_dir (solar|wind_100m|wind_125m), technology, wind_height, year, filename, dataset_key
datasets:
solar_historical_compressed:
years: "1980-2022"
wind_100m_historical_compressed:
years: "1980-2022"
wind_125m_historical_compressed:
years: "1980-2022"
# ---- 2. Zenodo: the published subset. Historical is a single year (2012);
# future scenarios hold 2030/2040/2050 per (tech, scenario) record.
# Every year list below was read off the live Zenodo API file listing
# (GET https://zenodo.org/api/records/<id>), not assumed.
- kind: zenodo
datasets:
solar_historical_compressed:
record: 20127513
years: [2012]
wind_125m_historical_compressed:
record: 20127520
years: [2012]
solar_rcp45hotter_compressed:
record: 20127523
years: [2030, 2040, 2050]
solar_rcp45cooler_compressed:
record: 20127562
years: [2030, 2040, 2050]
solar_rcp85hotter_compressed:
record: 20127589
years: [2030, 2040, 2050]
solar_rcp85cooler_compressed:
record: 20127633
years: [2030, 2040, 2050]
wind_125m_rcp45cooler_compressed:
record: 20127572
years: [2030, 2040, 2050]
wind_125m_rcp85hotter_compressed:
record: 20127604
years: [2030, 2040, 2050]
wind_125m_rcp85cooler_compressed:
record: 20127645
years: [2030, 2040, 2050]
# wind_125m_rcp45hotter is NOT published. Record 20127545 (hardcoded in the
# old zenodo_downloader.py) returns HTTP 404 "The persistent identifier is
# not registered", and no such record appears in a Zenodo search of the
# series — the other 9 records of the series do. Declaring it with an empty
# year list keeps the failure honest: requesting it raises "available years:
# (none)" instead of the legacy silent `None` download path. Use the local
# source or re-publish the record to make this scenario runnable.
wind_125m_rcp45hotter_compressed:
record: 20127545
years: []


# ====================================================================
# ATLITE — ERA5 cutout definitions
# ====================================================================
Expand Down
19 changes: 14 additions & 5 deletions workflow/repo_data/config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ run:
# Sets which precomputed/atlite-derived CF time series the renewable
# attach pipeline consumes. `godeeep` requires `renewable_land_access`
# below; `atlite` requires a populated cutout (see config.common.yaml).
#
# `godeeep` also retrieves its CF files through the `godeeep_cf_registry`
# block in config.common.yaml: every (dataset, year) the run may fetch is
# declared there, sources are tried in order, and an undeclared
# dataset/year fails at parse time naming the dataset key, the requested
# year and the years each source offers. Nothing is substituted silently.
# docs : RENEWABLE_DATASET
renewable:
dataset: godeeep # atlite | godeeep — see docs/source/config-configuration.md#renewable-godeeep
Expand Down Expand Up @@ -95,11 +101,12 @@ enable:
# ====================================================================
# For Atlite (any year) and GODEEEP-historical, configure renewable_weather_years.
# For GODEEEP future scenarios, the year is taken from planning_horizons above.
# Under `godeeep` the first entry must be a year declared for the requested
# dataset in `godeeep_cf_registry` (config.common.yaml) — the Oak mirror covers
# 1980-2022 historical, Zenodo publishes only 2012. An undeclared year is a
# parse-time error, never a silent swap to a neighbouring year.
# docs : RENEWABLE_WEATHER_YEARS
renewable_weather_years: [2019] # weather years to use (single year unless multi-horizon godeeep)
# renewable_weather_years_by_horizon: # OPTIONAL: map planning-horizon → weather year (multi-horizon atlite)
# 2030: 2019
# 2040: 2019

# docs : SNAPSHOTS
snapshots:
Expand All @@ -108,10 +115,12 @@ snapshots:
inclusive: "left" # pandas.date_range inclusive arg; left | right | both | neither

# GODEEEP future-scenario controls. Used only when renewable.dataset = godeeep
# and renewable_scenarios != "historical".
# and renewable_scenarios != "historical". Future-scenario CF years come from
# scenario.planning_horizons above; the (scenario, hub height, year) triple must
# be declared in `godeeep_cf_registry` (config.common.yaml). Note rcp45hotter is
# published for solar only — its wind_125m Zenodo record is unregistered.
# docs : RENEWABLE_SCENARIOS
renewable_scenarios: ["rcp85cooler"] # climate scenario; historical | rcp45cooler | rcp45hotter | rcp85cooler | rcp85hotter
# renewable_scenario_years: [2030] # OPTIONAL: explicit list of GODEEEP scenario years; defaults to planning_horizons

# For multi-horizon godeeep, the YEAR is taken from each planning_horizon
# automatically — these knobs only set the month/day slice within that year.
Expand Down
Loading
Loading