CAISO Generator Weather Map: CISO Plants and Recent Heat#

Authors: Claire Kim · Brian Nguyen · Created: 2026-07-31 · Tags: #Exploration #Weather #Geospatial

This notebook builds the interactive generator heat map from the EIA-860M generator inventory and recent Open-Meteo weather imputed onto plant coordinates.

Scope is California plants with balancing_authority == "CISO". EIA also carries a small number of CISO-tagged records outside California; California weather proxies are not a clean match for them, so they enter only as a sensitivity check.

Method#

  • Generator source: data/opensource/eia860m_generators.parquet, pulled from EIA-860M.

  • CAISO identifier: EIA uses CISO, not CAISO, in the balancing_authority field.

  • Weather source: Open-Meteo archive API, no API key, recent hourly temperatures aggregated to daily HDD/CDD.

  • Imputation: each plant is assigned to the nearest configured representative Open-Meteo point by plant latitude/longitude.

  • Zone labels: the output keeps both CAISO market-zone labels (NP15, SP15, ZP26) and NOAA/NCEI climate-division proxy labels so the load-zone/weather-zone mismatch remains visible.

import json
from pathlib import Path
import sys

import pandas as pd
import polars as pl
from IPython.display import display

PROJECT_ROOT = Path.cwd()
if PROJECT_ROOT.name == "notebooks":
    PROJECT_ROOT = PROJECT_ROOT.parent

SRC_DIR = PROJECT_ROOT / "src"
if str(SRC_DIR) not in sys.path:
    sys.path.insert(0, str(SRC_DIR))

from chart_theme import html_table
from generator_weather_map import (
    DEFAULT_STATES,
    build_generator_weather_map,
    build_weather_zone_crosswalk,
    ciso_inventory_scope_summary,
    get_location_weights,
    get_locations,
    load_eia_generators,
)

DATA_DIR = PROJECT_ROOT / "data"
OUTPUT_DIR = PROJECT_ROOT / "_output"
PROCESSED_DIR = DATA_DIR / "processed"

pd.set_option("display.max_columns", 120)
pd.set_option("display.width", 180)
print(PROJECT_ROOT)
.

1. Confirm the Generator Scope#

CISO is EIA’s balancing-authority code for CAISO. The table counts what the California-only default keeps and drops.

generators = load_eia_generators(DATA_DIR)
scope_summary = ciso_inventory_scope_summary(generators, states=DEFAULT_STATES)
html_table(pl.from_pandas(scope_summary))
4 rows × 5 columns
scope state generator_rows plants nameplate_capacity_mw
excluded_out_of_scope_state NM 2 2 3,650.20
excluded_out_of_scope_state AZ 31 14 2,904.20
excluded_out_of_scope_state NV 31 10 1,756.40
included CA 2,779 1,661 91,897.10

2. Load the Generator-Weather Data and Build the Map#

The imputed plant tables and the standalone HTML map are produced by the collab_generator_weather doit task (src/chart_caiso_generator_weather_map.py), which queries Open-Meteo only when the local recent-weather cache is missing or stale.

imputed_plants = pd.read_parquet(PROCESSED_DIR / "caiso_generator_weather_imputed.parquet")
daily_panel = pd.read_parquet(PROCESSED_DIR / "caiso_generator_weather_daily_imputed.parquet")

crosswalk = build_weather_zone_crosswalk(
    get_locations("core"),
    location_weights=get_location_weights("core"),
)
fig = build_generator_weather_map(imputed_plants, crosswalk=crosswalk)

print(f"Plant markers: {len(imputed_plants):,}")
print(f"Daily plant-weather rows: {len(daily_panel):,}")
Plant markers: 1,661
Daily plant-weather rows: 36,542

One row per plant marker, with the plant, zone, weather-proxy and recent-heat fields side by side.

preview_cols = [
    "plant_id", "plant_name", "state", "county", "nameplate_capacity_mw",
    "primary_technology_group", "region", "noaa_climate_division_proxy",
    "nearest_weather_location", "distance_to_weather_proxy_km",
    "recent_period_start", "recent_period_end", "recent_temperature_f_max",
    "recent_cdd65_mean", "recent_cdd65_sum", "heat_stress_label",
]
html_table(pl.from_pandas(imputed_plants[preview_cols].head(10)))
10 rows × 16 columns
plant_id plant_name state county nameplate_capacity_mw primary_technology_group region noaa_climate_division_proxy nearest_weather_location distance_to_weather_proxy_km recent_period_start recent_period_end recent_temperature_f_max recent_cdd65_mean recent_cdd65_sum heat_stress_label
6,099 Diablo Canyon CA San Luis Obispo 2,323.00 Nuclear ZP26 San Joaquin Drainage bakersfield 167.67 2026-08-05 2026-08-26 111.50 24.92 548.15 Extreme CDD
260 Dynegy Moss Landing Power Plant Hybrid CA Monterey 1,848.00 Natural gas NP15 Central Coast Drainage san_jose 60.02 2026-08-05 2026-08-26 88.30 4.24 93.32 Warm CDD
350 Ormond Beach CA Ventura 1,612.00 Natural gas SP15 South Coast Drainage los_angeles 85.63 2026-08-05 2026-08-26 101.80 14.67 322.81 Hot CDD
286 Geysers Unit 5-20 CA Sonoma 1,236.00 Geothermal NP15 Sacramento Drainage sacramento 110.71 2026-08-05 2026-08-26 103.50 11.76 258.70 Hot CDD
55,151 La Paloma Generating Plant CA Kern 1,156.00 Natural gas ZP26 San Joaquin Drainage bakersfield 52.71 2026-08-05 2026-08-26 111.50 24.92 548.15 Extreme CDD
315 AES Alamitos LLC CA Los Angeles 1,115.00 Natural gas SP15 South Coast Drainage los_angeles 34.16 2026-08-05 2026-08-26 101.80 14.67 322.81 Hot CDD
6,100 Helms Pumped Storage CA Fresno 1,053.00 Hydro ZP26 San Joaquin Drainage fresno 79.71 2026-08-05 2026-08-26 109.50 23.46 516.17 Extreme CDD
358 Mountainview Generating Station CA San Bernardino 1,036.80 Natural gas SP15 South Coast Drainage los_angeles 92.35 2026-08-05 2026-08-26 101.80 14.67 322.81 Hot CDD
64,210 Bellefield Solar and Energy Storage Farm CA Kern 1,000.00 Battery ZP26 San Joaquin Drainage bakersfield 91.33 2026-08-05 2026-08-26 111.50 24.92 548.15 Extreme CDD
55,518 High Desert Power Plant CA San Bernardino 960.00 Natural gas SP15 South Coast Drainage los_angeles 100.81 2026-08-05 2026-08-26 101.80 14.67 322.81 Hot CDD
capacity_by_region = (
    imputed_plants.groupby(["region", "heat_stress_label"], observed=False)["nameplate_capacity_mw"]
    .sum()
    .reset_index()
    .pivot(index="region", columns="heat_stress_label", values="nameplate_capacity_mw")
    .fillna(0)
)
html_table(pl.from_pandas(capacity_by_region.reset_index()))
3 rows × 4 columns
region Extreme CDD Hot CDD Warm CDD
NP15 0.00 13,024.40 7,040.50
SP15 0.00 36,891.90 0.00
ZP26 34,940.30 0.00 0.00

Central Valley / ZP26 carries the hottest exposure; coastal and northern plants are warm but less extreme. These are imputed proxy-point temperatures, not measured plant conditions.

3. Interactive Map#

Marker size is nameplate MW; marker color is recent mean daily CDD65 at the nearest Open-Meteo proxy. Hover or click a plant for its details.

Execution note: The figure is emitted twice - the Plotly mime type for live sessions, a self-contained CDN embed for static HTML exports. GitHub’s notebook preview runs neither.

fig.update_layout(height=780)
display(
    {
        # nbconvert and the site build drop the Plotly mime type, so the bundle
        # also carries self-contained HTML; each front end picks the richest
        # rendering it supports
        "application/vnd.plotly.v1+json": json.loads(fig.to_json()),
        "text/html": fig.to_html(full_html=False, include_plotlyjs="cdn"),
    },
    raw=True,
)

4. Daily Plant-Weather Panel#

One row per plant per day. A plant-level outage join needs a generator-level panel with plant IDs; the current aggregate outage file supports only a system-level join.

daily_cols = [
    "date", "plant_id", "plant_name", "nameplate_capacity_mw",
    "primary_technology_group", "region", "noaa_climate_division_proxy",
    "nearest_weather_location", "temperature_f_mean", "temperature_f_max",
    "cdd65", "hdd65", "total_cdh65", "total_hdh65",
]
html_table(pl.from_pandas(daily_panel[daily_cols].head(10)))
10 rows × 14 columns
date plant_id plant_name nameplate_capacity_mw primary_technology_group region noaa_climate_division_proxy nearest_weather_location temperature_f_mean temperature_f_max cdd65 hdd65 total_cdh65 total_hdh65
2026-08-05 34 Rollins 12.10 Hydro NP15 Sacramento Drainage sacramento 75.20 98.60 10.20 0.000 223.10 9.000
2026-08-05 72 Venice Hydro 10.10 Hydro SP15 South Coast Drainage los_angeles 74.03 90.80 9.03 0.000 189.70 0.000
2026-08-05 104 J S Eastwood 199.80 Hydro ZP26 San Joaquin Drainage fresno 89.43 105.50 24.43 0.000 513.00 0.000
2026-08-05 180 Volta 2 1.00 Hydro NP15 Sacramento Drainage sacramento 75.20 98.60 10.20 0.000 223.10 9.000
2026-08-05 214 Alta Powerhouse 1.00 Hydro NP15 Sacramento Drainage sacramento 75.20 98.60 10.20 0.000 223.10 9.000
2026-08-05 215 Angels 1.40 Hydro ZP26 San Joaquin Drainage merced 83.32 101.50 18.32 0.000 384.80 0.000
2026-08-05 217 Balch 1 31.00 Hydro ZP26 San Joaquin Drainage fresno 89.43 105.50 24.43 0.000 513.00 0.000
2026-08-05 218 Balch 2 97.20 Hydro ZP26 San Joaquin Drainage fresno 89.43 105.50 24.43 0.000 513.00 0.000
2026-08-05 219 Belden 117.90 Hydro NP15 Sacramento Drainage sacramento 75.20 98.60 10.20 0.000 223.10 9.000
2026-08-05 220 Bucks Creek 66.00 Hydro NP15 Sacramento Drainage sacramento 75.20 98.60 10.20 0.000 223.10 9.000

Geography remains the caveat: plant coordinates, CAISO market hubs, load zones and NOAA weather proxies are related but not identical spatial layers.