Comparing regions¶
Generate residential fleets for two regions, overlay their aggregate load shapes, and read off where — and why — they differ.
ev-flow on PyPI, pev_synth in Python
You pip install ev-flow and then import pev_synth — same
project, two names (the scikit-learn / sklearn convention). The
command-line tool is ev-flow; the importable Python package is
pev_synth.
This tutorial needs a built cache
The wheel ships only the Python code, not the cached fleet data. Build
the local cache once with ev-flow bootstrap before running anything
below — see the Quickstart's
Bootstrap the data cache
step. You need a cache for each region you compare; bootstrap can build
them, or use python -m pev_synth.cache_regen one --region <name>
--profile-type residential per region in a dev checkout. Until a region's
cache exists, generate_profiles(...) raises FileNotFoundError for it.
The plotting helper also needs matplotlib (pip install "ev-flow[plotting]").
1. Generate two regional fleets¶
The eight registered regions are always listed by
list_regions, even before any cache is built:
import pev_synth as ps
ps.list_regions()
# ['bay_area', 'boston', 'chicago', 'dallas_fort_worth',
# 'la_basin', 'new_york_metro', 'seattle', 'us_national']
Draw a residential fleet for each of two contrasting regions. We use the same
n and seed for both so the comparison isolates the region, not the fleet
size or the random draw.
bay = ps.generate_profiles('residential', n=500, region='bay_area', seed=7)
nyc = ps.generate_profiles('residential', n=500, region='new_york_metro', seed=7)
bay.region.display_name, nyc.region.display_name
# ('Bay Area, CA', 'New York Metro')
bay.region.tz, nyc.region.tz
# ('America/Los_Angeles', 'America/New_York')
Compare like with like
Both fleets share profile_type='residential', the same n, and the same
seed. Keeping those fixed means any difference you see in the curves comes
from the regional model (travel patterns, sales mix, climate), not from
sampling noise or scale.
2. Overlay the plugged-in curves¶
Pull each fleet's wide plug-status matrix over the same representative week.
Read both on each region's own local wall-clock (tz=...region.tz) so the
daily shapes line up by local time of day rather than by UTC — New York is three
hours ahead of the Bay Area, and overlaying raw UTC would shift the curves apart
for that reason alone.
window = ('2001-06-04', '2001-06-11')
bay_plug = bay.plug_status(*window, freq='15min', tz=bay.region.tz)
nyc_plug = nyc.plug_status(*window, freq='15min', tz=nyc.region.tz)
The pev_synth.plotting.plot_aggregate_load helper sums each wide matrix to a
plugged-in count and draws it. Pass a shared ax to overlay both on one figure,
and give each call a distinct label and color:
import matplotlib.pyplot as plt
from pev_synth import plotting
fig, ax = plt.subplots(figsize=(10, 4))
plotting.plot_aggregate_load(bay_plug, ax=ax, label='Bay Area', color='#0072B2')
plotting.plot_aggregate_load(nyc_plug, ax=ax, label='New York Metro', color='#D55E00')
ax.set_title('Residential plugged-in count — Bay Area vs New York Metro (local time)')
plt.savefig('region_comparison_plugged_in.png', dpi=150, bbox_inches='tight')
To compare the shape rather than the level (both fleets are 500 EVs here, but in general they need not be), normalise each curve to a plugged-in fraction before plotting:
bay_frac = bay_plug.mean(axis=1).to_frame('frac') # mean over EVs == fraction plugged in
nyc_frac = nyc_plug.mean(axis=1).to_frame('frac')
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(bay_frac.index, bay_frac['frac'], color='#0072B2', label='Bay Area')
ax.plot(nyc_frac.index, nyc_frac['frac'], color='#D55E00', label='New York Metro')
ax.set_ylabel('fraction of fleet plugged in')
ax.set_xlabel('time (each region, local)')
ax.legend(loc='best')
3. Overlay the charging-load curves (kW)¶
The plugged-in count shows connection; the kW load shows power drawn.
Fleet.aggregate_load returns the charge-as-
soon-as-plugged-in kW baseline as a pandas.Series:
bay_kw = bay.aggregate_load(*window, freq='1h', tz=bay.region.tz)
nyc_kw = nyc.aggregate_load(*window, freq='1h', tz=nyc.region.tz)
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(bay_kw.index, bay_kw.to_numpy(), color='#0072B2', label='Bay Area')
ax.plot(nyc_kw.index, nyc_kw.to_numpy(), color='#D55E00', label='New York Metro')
ax.set_ylabel('aggregate charging power (kW)')
ax.set_xlabel('time (each region, local)')
ax.set_title('Residential charging load — Bay Area vs New York Metro')
ax.legend(loc='best')
plt.savefig('region_comparison_load_kw.png', dpi=150, bbox_inches='tight')
What you should see, and why¶
Both regions share the residential signature — an evening-led plug-in build-up, an overnight plateau, and a morning unplug. The differences between them trace back to how the regions are defined in ev-flow's region registry, so keep the interpretation grounded in what the model actually varies:
- Sales mix differs. Bay Area uses a California CVRP-derived sales mix;
New York Metro uses the NYSERDA Drive Clean mix. Different battery-size and
powertrain mixes shift per-session energy and the achievable
max_charge_kw, which moves the height of the kW curve more than its timing. - Climate differs. New York Metro carries a winter energy-uplift multiplier
(cold-weather consumption is higher per mile), whereas the Bay Area's is
pinned at 1.0. In a summer week like the one above this barely shows; the
gap widens if you query a winter week (e.g.
('2001-01-08', '2001-01-15')) because higher per-mile energy means more kWh to deliver per session. - Housing differs (interpret with care). New York Metro is flagged in the region notes as multi-unit-dwelling (MUD) dominant, while the Bay Area is more single-family. This is the regional context most relevant to residential access to home charging in the real world.
Don't over-read the housing difference
ev-flow's v3.x residential pipeline grounds travel and plug-in behaviour in NHTS micro-data and the regional sales mix; it does not model curbside, garage-orphan, or shared-MUD charging infrastructure as a distinct mechanism, and it does not model public or workplace charging as a substitute for absent home charging. The MUD note is regional context for interpreting the result, not a claim that ev-flow simulates MUD charging access. The dominant driver of the shape you see is still the at-home overnight travel/plug-in pattern shared by both regions. Read region-to- region differences as directional, and confirm against the Methodology and region notes before drawing strong conclusions.
The honest summary: expect the two regions to look broadly similar in shape
(residential overnight charging dominates everywhere) and to differ mostly in
level and in seasonal sensitivity, driven by the sales mix and the
winter multiplier. If you need a starker contrast, compare a mild-climate region
against a cold one (e.g. seattle vs chicago) on a winter week.
Next steps¶
- Residential aggregate load — the single-region version of these curves, with more on what each one represents.
- Save and reload a fleet — persist each regional fleet so a later comparison reloads instantly.
- The API Reference and region registry document each region's states, timezone, sales-mix source and climate multiplier.