Skip to content

Commit c1294f3

Browse files
alxsmithcursoragent
andcommitted
Document zone vs utility load and canonical PJM slugs
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7513138 commit c1294f3

1 file changed

Lines changed: 47 additions & 1 deletion

File tree

context/code/data/pjm_hourly_loads.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,56 @@ Output schema:
130130
Utility aggregation maps each utility to its zone(s) via
131131
`data/pjm/zone_mapping/csv/pjm_utility_zone_mapping.csv` and sums zone loads by
132132
timestamp. Each MD utility maps to exactly one zone (bge→BC, pepco→PEP, dpl→DPL,
133-
potomac-edison→AP), so a utility series equals its zone series. The
133+
poted→AP), so a utility series equals its zone series. The
134134
`capacity_weight` column is for capacity-cost allocation and is **not** applied
135135
to load.
136136

137+
## Zone vs utility load (shared zones, scale-invariance, multi-state zones)
138+
139+
PJM zones and retail utilities are not 1:1, and the aggregation deliberately
140+
treats them as distinct things. Three consequences are worth spelling out, since
141+
they look surprising at first glance.
142+
143+
**Utilities that share a zone get identical load profiles.** A co-op or
144+
municipal that sits inside a host IOU's transmission zone (e.g. `smeco` inside
145+
PEPCO; `choptank`, `easton_muni`, `berlin_muni` inside DPL; `poted`,
146+
`somerset_rec`, `hagerstown_muni` inside APS) is mapped to that zone and receives
147+
the **full zone load series**`capacity_weight` is for capacity-cost
148+
allocation and is never applied to split load. So every utility in a zone gets
149+
the same hourly shape. Each utility is also computed independently, so
150+
adding or dropping a co-op/municipal in a zone does **not** change any other
151+
utility's load — dropping the phantom slugs that were never assigned in ResStock
152+
(`an-electric`, `thurmont`, `williamsport`) left `poted`/`dpl` etc. unchanged.
153+
154+
**Why identical (and full-zone) profiles are acceptable: the MC allocation is
155+
scale-invariant.** The sub-TX/DX marginal-cost methods that consume these loads
156+
(`generate_utility_tx_dx_mc.py`: peak-of-peak weighting, exceedance weighting)
157+
allocate a fixed `$/kW-yr` cost across hours using only the **shape** of the load
158+
profile — they normalize by the profile's own peak/exceedance, so the absolute
159+
magnitude divides out. Two utilities with the same shape but different absolute
160+
MW get the same hourly MC allocation; a utility assigned its whole zone's MW
161+
rather than just its retail slice gets the same allocation it would from a scaled
162+
copy of that shape. Magnitude is not over- or under-attributed because magnitude
163+
does not enter the allocation.
164+
165+
**Multi-state zones are a shape proxy, not a magnitude error.** Several PJM zones
166+
extend well beyond Maryland — APS (Allegheny Power Systems) covers parts of WV,
167+
PA, and VA; the PEPCO and DPL zones include DC and DE. So `poted`'s load shape is
168+
the whole APS zone's shape, which mixes non-MD load. This is an acknowledged
169+
approximation: we use the zone shape as a **proxy** for the MD retail territory's
170+
shape, accepting that the wider footprint can smear the timing of the local peak.
171+
Because the allocation is scale-invariant, the concern is purely "is the _shape_
172+
representative?", not "are we attributing too many MW?". Customer filtering to the
173+
MD retail territory happens upstream in ResStock utility assignment, not here.
174+
175+
**Consistent with the NY (NYISO) treatment.** NYISO load aggregation
176+
(`data/nyiso/hourly_demand/aggregate_nyiso_utility_loads.py`) follows the same
177+
rule: it sums full zone loads for each utility and uses `capacity_weight` only
178+
for capacity allocation, not to split load. NY utilities that cover the same
179+
zone(s) likewise end up with identical load profiles. The MD approach (shared
180+
APS/PEPCO/DPL profiles, full-zone load) is the PJM analogue of that established
181+
NY precedent.
182+
137183
## Running it
138184

139185
From the repo root (scripts run as modules because they import `data.pjm`):

0 commit comments

Comments
 (0)