From ba4347ee812e36d3c1cca22a80da6c7ae967bf3a Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Wed, 5 Aug 2026 14:33:30 -0600 Subject: [PATCH 01/45] add adjustText to environment.yml --- environment.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 8df757e15..0a1e1a0a7 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: reeds +name: reeds-at channels: - conda-forge - nodefaults @@ -25,6 +25,7 @@ dependencies: - scipy=1.18 - shapely=2.1 - tqdm=4.68 + - adjustText ## vvv The next packages are optional for default settings and may ## vvv be excluded if you're having trouble building the environment. - fiona=1.10 # for interactive maps From bc2452b4cc7802509df2a9a874660631c631e840 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 6 Aug 2026 17:51:09 -0600 Subject: [PATCH 02/45] Fix map-diff tech aggregation bug --- reeds/reedsplots.py | 55 +++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 5b525a61b..59178f347 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -981,6 +981,31 @@ def plot_diff_maps( dfbase[valcol] *= unitscaler dfcomp[valcol] *= unitscaler + ### Aggregate selected technologies to one value per region for the target year + dfbase_selected = ( + dfbase.loc[(dfbase.i.isin(titles)) & (dfbase.t == year), ['r', valcol]] + .groupby('r', as_index=False)[valcol].sum() + .rename(columns={valcol: f'{valcol}_base'}) + ) + dfcomp_selected = ( + dfcomp.loc[(dfcomp.i.isin(titles)) & (dfcomp.t == year), ['r', valcol]] + .groupby('r', as_index=False)[valcol].sum() + .rename(columns={valcol: f'{valcol}_comp'}) + ) + dfdiff_selected = ( + dfbase_selected + .merge(dfcomp_selected, on='r', how='outer') + .fillna(0) + ) + dfdiff_selected[f'{valcol}_diff'] = ( + dfdiff_selected[f'{valcol}_comp'] - dfdiff_selected[f'{valcol}_base'] + ) + dfdiff_selected[f'{valcol}_pctdiff'] = np.where( + dfdiff_selected[f'{valcol}_base'] != 0, + dfdiff_selected[f'{valcol}_diff'] / dfdiff_selected[f'{valcol}_base'] * 100, + np.nan, + ) + ### Start the plot # plt.close() if (f is None) and (ax is None): @@ -988,32 +1013,18 @@ def plot_diff_maps( else: pass - ###### Calculate the diff - dfdiff = dfbase.merge( - dfcomp, on=['i','r','t'], how='outer', suffixes=('_base','_comp')).fillna(0) - if plot in ['diff','pctdiff','pct_diff','diffpct','diff_pct','pct']: - ### Percent difference - dfdiff['{}_diff'.format(valcol)] = ( - (dfdiff['{}_comp'.format(valcol)] - dfdiff['{}_base'.format(valcol)]) - / dfdiff['{}_base'.format(valcol)] * 100 - ).replace(np.inf,np.nan) - elif plot in ['absdiff', 'abs_diff', 'diffabs', 'diff_abs']: - ### Difference - dfdiff['{}_diff'.format(valcol)] = ( - dfdiff['{}_comp'.format(valcol)] - dfdiff['{}_base'.format(valcol)]) - if zmax is None: zmax = max( - dfdiff.loc[(dfdiff.i.isin(titles))&(dfdiff.t==year),valcol+'_base'].max(), - dfdiff.loc[(dfdiff.i.isin(titles))&(dfdiff.t==year),valcol+'_comp'].max(), + dfdiff_selected[f'{valcol}_base'].max(), + dfdiff_selected[f'{valcol}_comp'].max(), ) ###### Plot the base if plot == 'base': dfplot = dfba.merge( - dfbase.loc[(dfbase.i.isin(titles))&(dfbase.t==year),['r',valcol]], + dfdiff_selected[['r', f'{valcol}_base']], left_index=True, right_on='r', how='left' - ).fillna(0).reset_index(drop=True) + ).fillna(0).reset_index(drop=True).rename(columns={f'{valcol}_base': valcol}) dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) @@ -1021,9 +1032,9 @@ def plot_diff_maps( ###### Plot the comp elif plot == 'comp': dfplot = dfba.merge( - dfcomp.loc[(dfcomp.i.isin(titles))&(dfcomp.t==year),['r',valcol]], + dfdiff_selected[['r', f'{valcol}_comp']], left_index=True, right_on='r', how='left' - ).fillna(0).reset_index(drop=True) + ).fillna(0).reset_index(drop=True).rename(columns={f'{valcol}_comp': valcol}) dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) @@ -1033,7 +1044,7 @@ def plot_diff_maps( legend_kwds['label'] = '{} {} {}\n[% diff]'.format(valcol,i_plot,year) dfplot = dfba.merge( - dfdiff.loc[(dfdiff.i.isin(titles))&(dfdiff.t==year),['r',valcol+'_diff']], + dfdiff_selected[['r', f'{valcol}_pctdiff']], left_index=True, right_on='r', how='left' ).reset_index(drop=True) @@ -1052,7 +1063,7 @@ def plot_diff_maps( legend_kwds['label'] = '{} diff {} [{}]'.format(valcol,i_plot,units) dfplot = dfba.merge( - dfdiff.loc[(dfdiff.i.isin(titles))&(dfdiff.t==year),['r',valcol+'_diff']], + dfdiff_selected[['r', f'{valcol}_diff']], left_index=True, right_on='r', how='left' ).reset_index(drop=True) From 1623eee8b70842a7e7d9992758361c12a81a5650 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 6 Aug 2026 18:33:10 -0600 Subject: [PATCH 03/45] abs cmap rain->tempo in compare_cases.py --- postprocessing/compare_cases.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index 3ab6c8dbe..c6539af8e 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -130,7 +130,7 @@ #%%### Fixed inputs -cmap = cmocean.cm.rain +cmap = cmocean.cm.tempo cmap_diff = plt.cm.RdBu_r ## https://www.whitehouse.gov/wp-content/uploads/2023/11/CircularA-4.pdf discountrate_social = DEFAULT_DISCOUNT_RATE @@ -2595,7 +2595,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): year=lastyear, casebase=casebase, casecomp=casecomp, level=level, plot='base', f=f, ax=ax[0], - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, ) ax[0].annotate( casebase_name, @@ -2606,7 +2606,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): year=lastyear, casebase=casebase, casecomp=casecomp, level=level, plot='comp', f=f, ax=ax[1], - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, ) ax[1].annotate( casecomp_name, From 7f792a6f19a9642b28194225358ac277c0a27411 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 6 Aug 2026 18:37:03 -0600 Subject: [PATCH 04/45] Add region labels in plot_seed_stressperiods --- reeds/reedsplots.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 59178f347..43cc6396f 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -15,6 +15,7 @@ import geopandas as gpd import shapely import cmocean +from adjustText import adjust_text sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import reeds from reeds import plots @@ -5116,12 +5117,18 @@ def plot_seed_stressperiods( ax=ax[row,col], column='val', edgecolor='none', lw=0, cmap=cmap, alpha=alpha, vmin=0, vmax=1, ) + text_artists = [] for i, _row in df.iterrows(): - ax[row,col].annotate( - _row.date, (_row.centroid_x, _row.centroid_y), - ha='center', va='center', color='k', fontsize=fontsize, - path_effects=[pe.withStroke(linewidth=pelinewidth, foreground='w', alpha=pealpha)], + text_artists.append( + ax[row,col].annotate( + _row.date, (_row.centroid_x, _row.centroid_y), + ha='center', va='center', color='k', fontsize=fontsize, + path_effects=[pe.withStroke(linewidth=pelinewidth, foreground='w', alpha=pealpha)], + ) ) + adjust_text(text_artists, ax=ax[row,col], + avoid_self=False, ensure_inside_axes=True, + ) ### Max load @@ -5152,12 +5159,18 @@ def plot_seed_stressperiods( ax=ax[row,col], column='val', edgecolor='none', lw=0, cmap=cmap, alpha=alpha, vmin=0, vmax=1, ) + text_artists = [] for i, _row in df.iterrows(): - ax[row,col].annotate( - _row.date, (_row.centroid_x, _row.centroid_y), - ha='center', va='center', color='k', fontsize=fontsize, - path_effects=[pe.withStroke(linewidth=pelinewidth, foreground='w', alpha=pealpha)], + text_artists.append( + ax[row,col].annotate( + _row.date, (_row.centroid_x, _row.centroid_y), + ha='center', va='center', color='k', fontsize=fontsize, + path_effects=[pe.withStroke(linewidth=pelinewidth, foreground='w', alpha=pealpha)], + ) ) + adjust_text(text_artists, ax=ax[row,col], + avoid_self=False, ensure_inside_axes=True, + ) ### Colorbar row, col = 0, 0 From 8a9957f5e97e345ff98bbc90946832752b51fd18 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 6 Aug 2026 18:42:44 -0600 Subject: [PATCH 05/45] Add region labels in capacity maps --- postprocessing/compare_cases.py | 2 ++ reeds/reedsplots.py | 28 +++++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index c6539af8e..04fa9d475 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -2682,6 +2682,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): ax=ax[coords[case]], column='GW', cmap=cmap, vmin=vmin, vmax=vmax, legend=False, ) + reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fmt = '{:.0f}', fontsize=6) ## Legend if coords[case] == legendcoords: plots.addcolorbarhist( @@ -2753,6 +2754,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): vmax=(absmax if case == basecase else diffmax), legend=False, ) + reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fmt = ('{:.0f}' if case == basecase else '{:+.0f}'), fontsize=6) ## Difference legend if coords[case] == legendcoords: plots.addcolorbarhist( diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 43cc6396f..4b7cda19e 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1029,6 +1029,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) + label_region_value(dfplot, ax=ax, column=valcol, fmt = '{:.1f}', fontsize=6) ###### Plot the comp elif plot == 'comp': @@ -1040,6 +1041,8 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) + label_region_value(dfplot, ax=ax, column=valcol, fmt = '{:.1f}', fontsize=6) + ###### Plot the pct diff elif plot in ['diff','pctdiff','pct_diff','diffpct','diff_pct','pct']: legend_kwds['label'] = '{} {} {}\n[% diff]'.format(valcol,i_plot,year) @@ -1058,6 +1061,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_pctdiff', cmap=cmap, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) + label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', fmt = '{:.1f}', fontsize=6) ###### Plot the absolute diff elif plot in ['absdiff', 'abs_diff', 'diffabs', 'diff_abs']: @@ -1077,6 +1081,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_diff', cmap=plt.cm.RdBu_r, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) + label_region_value(dfplot, ax=ax, column=valcol+'_diff', fmt = '{:.1f}', fontsize=6) ### Finish and return # ax.set_title(title, y=0.95) @@ -6252,15 +6257,24 @@ def get_cf_map(case, tech='wind-ons', timestamp=None, recf=None, crs='EPSG:5070' def label_region_value(df, ax, column, fmt='{:.2f}', color='k', fontsize=8, **kwargs): """kwargs are passed to patheffects.withStroke()""" pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':1}, **kwargs} + text_artists = [] for r, row in df.iterrows(): - ax.annotate( - fmt.format(row[column]), - (row.geometry.centroid.x, row.geometry.centroid.y), - ha='center', va='center', fontsize=fontsize, - color=color, - path_effects=[pe.withStroke(**pe_kwargs)], - zorder=1e9, + value = row.get(column, np.nan) + if not np.isfinite(value): + continue + text_artists.append( + ax.annotate( + fmt.format(value), + (row.geometry.centroid.x, row.geometry.centroid.y), + ha='center', va='center', fontsize=fontsize, + color=color, + path_effects=[pe.withStroke(**pe_kwargs)], + zorder=1e9, + ) ) + adjust_text(text_artists, ax=ax, + avoid_self=False, ensure_inside_axes=True, + ) def map_stressors( From 547fae3a57a8d237cf5cd4f271753b42be93d884 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 09:06:20 -0600 Subject: [PATCH 06/45] Adjust labels in map_neue --- reeds/reedsplots.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 4b7cda19e..105fd0cb3 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -4390,25 +4390,31 @@ def map_neue( ## Labels # decimals = (0 if df.NEUE_ppm.max() >= 10 else 1) decimals = (0 if level in ['st','r'] else 1) + text_artists = [] for r, row in df.sort_values('NEUE_ppm').iterrows(): if highlight_over_threshold: over_threshold = row.NEUE_ppm > neue_threshold else: over_threshold = False - ax[coords[level]].annotate( - f"{row.NEUE_ppm:.{decimals}f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', - c=(over_threshold_textcolor if over_threshold else 'k'), - weight=('bold' if over_threshold else 'normal'), - fontsize={'r':5}.get(level,7), - zorder=1e9, - path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.7)], + text_artists.append( + ax[coords[level]].annotate( + f"{row.NEUE_ppm:.{decimals}f}", + [row.centroid_x, row.centroid_y], + ha='center', va='center', + c=(over_threshold_textcolor if over_threshold else 'k'), + weight=('bold' if over_threshold else 'normal'), + fontsize={'r':5}.get(level,7), + zorder=1e9, + path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.7)], + ) ) if over_threshold and (level == neue_threshold_level): ax[coords[level]].set_title( level, y=0.9, weight='bold', color=over_threshold_textcolor, ) + adjust_text(text_artists, ax=ax[coords[level]], + avoid_self=False, ensure_inside_axes=True, + ) ### Formatting plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=df.NEUE_ppm, From a3182424f9cc63a566557e16a67020cd7431a151 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 09:07:20 -0600 Subject: [PATCH 07/45] Adjust labels in hourly_plots.py --- reeds/input_processing/hourly_plots.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/reeds/input_processing/hourly_plots.py b/reeds/input_processing/hourly_plots.py index d477d72ce..0b927719a 100644 --- a/reeds/input_processing/hourly_plots.py +++ b/reeds/input_processing/hourly_plots.py @@ -12,6 +12,7 @@ from matplotlib import patheffects as pe import cmocean from pathlib import Path +from adjustText import adjust_text sys.path.append(str(Path(__file__).parent.parent.parent)) import reeds from reeds import plots @@ -355,14 +356,20 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) ## Text differences + text_artists = [] for r, row in (dfdiffs[level].assign(val=dfdiffs[level].cf_diff.abs()).sort_values('val')).iterrows(): decimals = 0 if abs(row.cf_diff) >= 1 else 1 - ax[coords[level]].annotate( + text_artists.append( + ax[coords[level]].annotate( f"{row.cf_diff*100:+.{decimals}f}", [row.centroid_x, row.centroid_y], ha='center', va='center', c='k', fontsize={'r':5}.get(level,7), path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.5)], + ) ) + adjust_text(text_artists, ax=ax[coords[level]], + avoid_self=False,ensure_inside_axes=True, + ) ## Colorbar plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=dfdiffs[level].cf_diff*100, nbins=51, @@ -486,14 +493,20 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) ## Text differences + text_artists = [] for r, row in (dfdiffs[level].assign(val=dfdiffs[level][val].abs()).sort_values('val')).iterrows(): decimals = 0 if abs(row[val]) >= 1 else 1 - ax[coords[level]].annotate( - f"{row[val]:+.{decimals}f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', c='k', fontsize={'r':5}.get(level,7), - path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.5)], + text_artists.append( + ax[coords[level]].annotate( + f"{row[val]:+.{decimals}f}", + [row.centroid_x, row.centroid_y], + ha='center', va='center', c='k', fontsize={'r':5}.get(level,7), + path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.5)], + ) ) + adjust_text(text_artists, ax=ax[coords[level]], + avoid_self=False,ensure_inside_axes=True, + ) ## Colorbar plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=dfdiffs[level][val], nbins=51, From 4c7d0ddae1ebe2c4b57d5dbec24234618a983bac Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 09:27:37 -0600 Subject: [PATCH 08/45] Update hourly_plots abs cbar --- reeds/input_processing/hourly_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/input_processing/hourly_plots.py b/reeds/input_processing/hourly_plots.py index 0b927719a..9cc854714 100644 --- a/reeds/input_processing/hourly_plots.py +++ b/reeds/input_processing/hourly_plots.py @@ -231,7 +231,7 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: ### Settings cmaps = { 'cf_actual':plt.cm.turbo, 'cf_rep':plt.cm.turbo, 'cf_diff':plt.cm.RdBu_r, - 'GW_actual':cmocean.cm.rain, 'GW_rep':cmocean.cm.rain, + 'GW_actual':cmocean.cm.tempo, 'GW_rep':cmocean.cm.tempo, 'GW_diff':plt.cm.RdBu_r, 'GW_frac':plt.cm.RdBu_r, 'pct_diff':plt.cm.RdBu_r, } vm = { From 8cf6a8cc046a5276c2f6d58105a1f9dfbf7f65ca Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 13:36:58 -0600 Subject: [PATCH 09/45] Label capacity, net_imports, h2_capacity maps --- reeds/reedsplots.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 105fd0cb3..b2ef2fa26 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1375,6 +1375,7 @@ def map_net_imports( legend=False, vmin=-vmax[year], vmax=vmax[year], ) + label_region_value(df, ax=ax[coords[year]], column='net_import', fmt = '{:+.0f}', fontsize=6) ## Formatting ax[coords[year]].set_title(year, y=0.9) if vlim != 'shared': @@ -2768,6 +2769,7 @@ def map_capacity_techs( 'label': '{} [GW]'.format(tech), } ) + label_region_value(dfplot, ax=ax[coords[tech]], column='GW', fmt = '{:.1f}', fontsize=6) ax[coords[tech]].axis('off') ax[0,0].set_title( '{} ({})'.format(os.path.basename(case), year), @@ -4517,16 +4519,19 @@ def map_h2_capacity( cap_h2turbine.plot( ax=ax[0,0], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Turbines [kT/day]'}}) + label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', fmt = '{:.1f}', fontsize=6) ### Electrolyzers if not cap_h2prod.empty: cap_h2prod.plot( ax=ax[0,1], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Production [kT/day]'}}) + label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', fmt = '{:.1f}', fontsize=6) ### Storage if not cap_h2prod.empty: cap_storage.plot( ax=ax[1,0], column='h2_storage', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Storage [kT]'}}) + label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', fmt = '{:.1f}', fontsize=6) ### Pipelines if not h2_trans_cap.empty: for i,row in h2_trans_cap.iterrows(): From b459be580892346c54317abeb0c64afd011ee4cf Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 13:40:50 -0600 Subject: [PATCH 10/45] rain -> tempo in reedsplots --- reeds/reedsplots.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index b2ef2fa26..20ddf9eaa 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1093,7 +1093,7 @@ def plot_diff_maps( def plot_trans_vsc( case, year=2050, wscale=0.4, alpha=1.0, miles=300, - cmap=cmocean.cm.rain, scale=True, title=True, + cmap=cmocean.cm.tempo, scale=True, title=True, convertermax=None, f=None, ax=None, ): @@ -1188,7 +1188,7 @@ def plot_trans_vsc( def plot_transmission_utilization( case, year=2050, plottype='mean', network='trans', - wscale=0.0004, alpha=1.0, cmap=cmocean.cm.rain, + wscale=0.0004, alpha=1.0, cmap=cmocean.cm.tempo, extent='modeled', f=None, ax=None, thicklevel='transreg', @@ -2715,7 +2715,7 @@ def map_capacity_techs( techs='aggregation', ncols=4, vmax='shared', - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, ): """ techs: list of technologies to plot, or 'aggregation' to plot all aggregated technologies @@ -3456,7 +3456,7 @@ def map_hybrid_pv_wind( case, val='site_cap', year=2050, tech=None, vmax=None, markersize=10.75, #stretch=1.2, - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, f=None, ax=None, figsize=(6,6), dpi=None, ): """ @@ -3464,7 +3464,7 @@ def map_hybrid_pv_wind( cmap: Suggestions: val=site_cap, tech=wind-ons: plt.cm.Blues val=site_cap, tech=upv: plt.cm.Oranges - val=(site_hybridization,site_spurcap), tech=None: cmocean.cm.rain + val=(site_hybridization,site_spurcap), tech=None: cmocean.cm.tempo val=(site_pv_fraction,site_gir), tech=either: plt.cm.turbo or mpl.colors.LinearSegmentedColormap.from_list 'turboclip', [plt.cm.turbo(c) for c in np.linspace(0.1,0.91,101)]) @@ -4343,7 +4343,7 @@ def plot_ra_metrics_bylevel( def map_neue( case, year=2050, iteration='last', samples=None, - vmax=10., cmap=cmocean.cm.rain, label=True, + vmax=10., cmap=cmocean.cm.tempo, label=True, over_vmax_mapcolor=None, over_threshold_textcolor='C3', highlight_over_threshold=True, @@ -4433,7 +4433,7 @@ def map_neue( def map_h2_capacity( case, year=2050, wscale_h2=10, figheight=6, pipescale=0.1, legend_kwds={'shrink':0.6, 'pad':0, 'orientation':'horizontal', 'aspect':12}, - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, ): """ H2 turbines, production (Electrolyzer/SMR), pipelines, and storage @@ -6124,7 +6124,7 @@ def map_outage_days( techs=['gas-cc', 'gas-ct', 'coaloldscr', 'nuclear', 'hyded'], outage_type='forced', aggfunc='max', - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, vmin=0, vmax='auto', fontsize=4, @@ -6325,7 +6325,7 @@ def map_stressors( """ ### Plot setup cmaps = { - 'load': cmocean.cm.rain, + 'load': cmocean.cm.tempo, # 'load': plt.cm.turbo, # 'wind-ons': cmocean.cm.ice_r, 'wind-ons': cmocean.cm.ice, @@ -6899,7 +6899,7 @@ def map_output_byyear( return f, ax, dictplot -def map_prm(case, tmin=2023, cmap=cmocean.cm.rain, scale=3, fontsize=7, vmax=None): +def map_prm(case, tmin=2023, cmap=cmocean.cm.tempo, scale=3, fontsize=7, vmax=None): dfmap = reeds.io.get_dfmap(case) sw = reeds.io.get_switches(case) ### Get final iterations From 17549abf30ee18d74d28860175b566bac0fe11d0 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 17:55:06 -0600 Subject: [PATCH 11/45] Re-attach dropped CRS --- reeds/reedsplots.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 20ddf9eaa..3608fd1e3 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -6256,7 +6256,8 @@ def get_cf_map(case, tech='wind-ons', timestamp=None, recf=None, crs='EPSG:5070' dfsc['i'] = tech + '_' + dfsc['class'].astype(str) sitemap = reeds.io.get_sitemap(offshore=(True if tech == 'wind-ofs' else False)) dfsc['geometry'] = dfsc.index.map(sitemap.geometry) - dfsc = gpd.GeoDataFrame(dfsc).to_crs(crs) + dfsc = gpd.GeoDataFrame(dfsc, geometry='geometry', crs=sitemap.crs) + dfsc = dfsc.to_crs(crs) dfsc['cf'] = dfsc[['i','r']].merge(cf.rename('cf'), on=['i','r'], how='left').cf.values ## Convert to polygons From a45d4337ca177da46f7d8e1a0c597271030a38b9 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 17:56:09 -0600 Subject: [PATCH 12/45] rain -> tempo in plots.py --- reeds/plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reeds/plots.py b/reeds/plots.py index 37b9654e3..e216e8ee2 100644 --- a/reeds/plots.py +++ b/reeds/plots.py @@ -1040,7 +1040,7 @@ def draw_screen_poly(poly, m, def plotusascattermap( dfplot, colorcolumn=None, sizecolumn=None, filterdict=None, sort=True, - basemap=None, cmap=cmocean.cm.rain, mappath=None, + basemap=None, cmap=cmocean.cm.tempo, mappath=None, facecolor='w', edgecolor='w', latlonlabels=None, markersize=None, marker='o', figsize=(10,7.5), dpi=None, zrange=None, colors=None, maptype='scatter', contourlevels=100, @@ -2113,7 +2113,7 @@ def plot_region_bars( def map_years_months( - dfzones, dfdata, cmap=cmocean.cm.rain, aggfunc='mean', + dfzones, dfdata, cmap=cmocean.cm.tempo, aggfunc='mean', colwidth=1.2, rowheight=1.0, simplify=10000, mapbuffer=1.08, vmin=0., vmax=None, outline=0.25, labels=True, **colorbarkwargs, From c31370d98cbb80ab707b7bcd25d77c2b0c28fa13 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 17:56:33 -0600 Subject: [PATCH 13/45] rain -> tempo in single_case_plots.py --- postprocessing/single_case_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postprocessing/single_case_plots.py b/postprocessing/single_case_plots.py index e35683d88..0169e924c 100644 --- a/postprocessing/single_case_plots.py +++ b/postprocessing/single_case_plots.py @@ -29,7 +29,7 @@ ## the position of the annotations crs = 'ESRI:102008' ### General purpose -cmap = cmocean.cm.rain +cmap = cmocean.cm.tempo ### For VRE siting & transmission maps transalpha = 0.25 transcolor = 'k' From 47b0d2628282a9d696470f82124123f84e4bfb59 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 18:04:41 -0600 Subject: [PATCH 14/45] Adjust labels in map_outage_days --- reeds/reedsplots.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 3608fd1e3..080d89b86 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -6182,14 +6182,20 @@ def map_outage_days( dfplot.plot(ax=_ax, column='outage_pct', cmap=cmap, vmin=vmin, vmax=vmax) ## Data values if fontsize: + text_artists = [] for r, row in dfplot.sort_values('outage_pct').iterrows(): - _ax.annotate( - f"{row.outage_pct:.0f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', c='k', - fontsize=fontsize, - path_effects=[pe.withStroke(linewidth=1.4, foreground='w', alpha=0.7)], + text_artists.append( + _ax.annotate( + f"{row.outage_pct:.0f}", + [row.centroid_x, row.centroid_y], + ha='center', va='center', c='k', + fontsize=fontsize, + path_effects=[pe.withStroke(linewidth=1.4, foreground='w', alpha=0.7)], + ) ) + adjust_text(text_artists, ax=_ax, + avoid_self=False, ensure_inside_axes=True, + ) _ax.axis('off') ## Formatting if date == dates[0]: From 2917dd9757b0641cf1432715e7bc1a082acff863 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 18:05:06 -0600 Subject: [PATCH 15/45] rain -> tempo in map_output_byyear --- reeds/reedsplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 080d89b86..700ff2eb5 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -6834,7 +6834,7 @@ def map_output_byyear( oneaxis='columns', yearaxis='rows', mapscale=4, - cmap=cmocean.cm.rain, + cmap=cmocean.cm.tempo, vscale=1, vmin=None, vmax=None, From b7618b478a2461ec7b64aec2380b198c2bd4c450 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 18:06:02 -0600 Subject: [PATCH 16/45] Remove slashes from savenames --- reeds/resource_adequacy/diagnostic_plots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index 3e8f87e19..b47e6fbec 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -987,7 +987,7 @@ def map_pras_failure_rate(sw, dfs, aggfunc='mean', repair=False): plottechs = failsum.loc[failsum != 0].index.get_level_values('i').unique() for tech in plottechs: - savename = f"hourly_failure_rate-year,month-{aggfunc}-{tech.replace('-','')}-{sw['t']}" + savename = f"hourly_failure_rate-year,month-{aggfunc}-{tech.replace('-','').replace('/','')}-{sw['t']}" plt.close() f, ax = plots.map_years_months( dfzones=dfzones, dfdata=failrate[tech], @@ -1008,7 +1008,7 @@ def map_pras_failure_rate(sw, dfs, aggfunc='mean', repair=False): ) repairrate.index = dfs['pras_system']['genrepairrate'].index for tech in plottechs: - savename = f"hourly_repair_rate-year,month-{aggfunc}-{tech.replace('-','')}-{sw['t']}" + savename = f"hourly_repair_rate-year,month-{aggfunc}-{tech.replace('-','').replace('/','')}-{sw['t']}" plt.close() f, ax = plots.map_years_months( dfzones=dfzones, dfdata=repairrate[tech], From 61d2c2a0e000f3532a57d0470416020e415c18ae Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 20 Aug 2026 18:06:26 -0600 Subject: [PATCH 17/45] rain -> tempo in diagnostic_plots.py --- reeds/resource_adequacy/diagnostic_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index b47e6fbec..5f04b93cc 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -426,7 +426,7 @@ def map_dropped_load(sw, dfs, level='r'): ### Background dfba.plot(ax=ax, facecolor='none', edgecolor='k', lw=0.2) ### Data - dfplot.plot(ax=ax, column='val', cmap=cmocean.cm.rain) + dfplot.plot(ax=ax, column='val', cmap=cmocean.cm.tempo) for r, row in dfplot.iterrows(): if row.val > 0: ax.annotate( From e62570e218164792eafbba278330e01217ad6f90 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 12:06:22 -0600 Subject: [PATCH 18/45] Add opt_single_decimal to label_region_value --- reeds/reedsplots.py | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 700ff2eb5..1de42f732 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1029,7 +1029,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) - label_region_value(dfplot, ax=ax, column=valcol, fmt = '{:.1f}', fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol, fontsize=6) ###### Plot the comp elif plot == 'comp': @@ -1041,7 +1041,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) - label_region_value(dfplot, ax=ax, column=valcol, fmt = '{:.1f}', fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol, fontsize=6) ###### Plot the pct diff elif plot in ['diff','pctdiff','pct_diff','diffpct','diff_pct','pct']: @@ -1061,7 +1061,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_pctdiff', cmap=cmap, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) - label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', fmt = '{:.1f}', fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', fontsize=6) ###### Plot the absolute diff elif plot in ['absdiff', 'abs_diff', 'diffabs', 'diff_abs']: @@ -1081,7 +1081,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_diff', cmap=plt.cm.RdBu_r, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) - label_region_value(dfplot, ax=ax, column=valcol+'_diff', fmt = '{:.1f}', fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol+'_diff', fontsize=6) ### Finish and return # ax.set_title(title, y=0.95) @@ -1375,7 +1375,8 @@ def map_net_imports( legend=False, vmin=-vmax[year], vmax=vmax[year], ) - label_region_value(df, ax=ax[coords[year]], column='net_import', fmt = '{:+.0f}', fontsize=6) + label_region_value(df, ax=ax[coords[year]], column='net_import', + opt_single_decimal=False, fmt = '{:+.0f}', fontsize=6) ## Formatting ax[coords[year]].set_title(year, y=0.9) if vlim != 'shared': @@ -2769,7 +2770,7 @@ def map_capacity_techs( 'label': '{} [GW]'.format(tech), } ) - label_region_value(dfplot, ax=ax[coords[tech]], column='GW', fmt = '{:.1f}', fontsize=6) + label_region_value(dfplot, ax=ax[coords[tech]], column='GW', fontsize=6) ax[coords[tech]].axis('off') ax[0,0].set_title( '{} ({})'.format(os.path.basename(case), year), @@ -4519,19 +4520,19 @@ def map_h2_capacity( cap_h2turbine.plot( ax=ax[0,0], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Turbines [kT/day]'}}) - label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', fmt = '{:.1f}', fontsize=6) + label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', fontsize=6) ### Electrolyzers if not cap_h2prod.empty: cap_h2prod.plot( ax=ax[0,1], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Production [kT/day]'}}) - label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', fmt = '{:.1f}', fontsize=6) + label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', fontsize=6) ### Storage if not cap_h2prod.empty: cap_storage.plot( ax=ax[1,0], column='h2_storage', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Storage [kT]'}}) - label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', fmt = '{:.1f}', fontsize=6) + label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', fontsize=6) ### Pipelines if not h2_trans_cap.empty: for i,row in h2_trans_cap.iterrows(): @@ -6272,7 +6273,16 @@ def get_cf_map(case, tech='wind-ons', timestamp=None, recf=None, crs='EPSG:5070' return dfsc -def label_region_value(df, ax, column, fmt='{:.2f}', color='k', fontsize=8, **kwargs): +def label_region_value( + df, + ax, + column, + opt_single_decimal=True, + fmt='{:.0f}', + color='k', + fontsize=8, + **kwargs +): """kwargs are passed to patheffects.withStroke()""" pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':1}, **kwargs} text_artists = [] @@ -6280,6 +6290,9 @@ def label_region_value(df, ax, column, fmt='{:.2f}', color='k', fontsize=8, **kw value = row.get(column, np.nan) if not np.isfinite(value): continue + if opt_single_decimal: + decimals = 0 if abs(value) >= 1 else 1 + fmt = f"{{:.{decimals}f}}" text_artists.append( ax.annotate( fmt.format(value), @@ -6611,7 +6624,8 @@ def map_stressors( ax=ax[1,0], column='load_rank', cmap=cmaps['rank'], vmin=0, vmax=100, ) label_region_value( - df=dflevel, ax=ax[1,0], column='load_rank', fmt='{:.0f}%', + df=dflevel, ax=ax[1,0], column='load_rank', + opt_single_decimal=False, fmt='{:.0f}%', linewidth=2.0, alpha=0.8, ) ax[1,0].set_title('Demand', y=0.9) @@ -6626,7 +6640,8 @@ def map_stressors( ax=ax[1,col], column=f'{tech}_rank', cmap=cmaps['rank'], vmin=0, vmax=100, ) label_region_value( - df=dflevel, ax=ax[1,col], column=f'{tech}_rank', fmt='{:.0f}%', + df=dflevel, ax=ax[1,col], column=f'{tech}_rank', + opt_single_decimal=False, fmt='{:.0f}%', linewidth=2.0, alpha=0.8, ) ax[1,col].set_title(labels.get(tech,tech), y=0.9) @@ -6640,7 +6655,8 @@ def map_stressors( ax=ax[1,3], column='temperature_rank', cmap=cmaps['rank'], vmin=0, vmax=100, ) label_region_value( - df=dflevel, ax=ax[1,3], column='temperature_rank', fmt='{:.0f}%', + df=dflevel, ax=ax[1,3], column='temperature_rank', + opt_single_decimal=False, fmt='{:.0f}%', linewidth=2.0, alpha=0.8, ) ax[1,3].set_title('Temperature', y=0.9) @@ -6658,7 +6674,8 @@ def map_stressors( ) dflevel[f'outage_{tech}'] = outage_region.loc[(y,m,d), tech] label_region_value( - df=dflevel, ax=ax[2,col], column=f'outage_{tech}', fmt='{:.0f}%', + df=dflevel, ax=ax[2,col], column=f'outage_{tech}', + opt_single_decimal=False, fmt='{:.0f}%', linewidth=2.0, alpha=0.8, ) ax[2,col].set_title(label, y=0.9) From 0cc02cf06cdf30b7a99864fb0e214c141f690f77 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 13:48:33 -0600 Subject: [PATCH 19/45] Use opt_single_decimal in compare_cases.py capacity maps --- postprocessing/compare_cases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index 04fa9d475..fc7908518 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -2682,7 +2682,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): ax=ax[coords[case]], column='GW', cmap=cmap, vmin=vmin, vmax=vmax, legend=False, ) - reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fmt = '{:.0f}', fontsize=6) + reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fontsize=6) ## Legend if coords[case] == legendcoords: plots.addcolorbarhist( @@ -2754,7 +2754,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): vmax=(absmax if case == basecase else diffmax), legend=False, ) - reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fmt = ('{:.0f}' if case == basecase else '{:+.0f}'), fontsize=6) + reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fontsize=6) ## Difference legend if coords[case] == legendcoords: plots.addcolorbarhist( From 170ee337b553584e59332e7cb806c4ea1f2d784a Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 13:49:01 -0600 Subject: [PATCH 20/45] Add sub-state regional outlines to plot_diff_maps --- reeds/reedsplots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 1de42f732..29cdeaba5 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1085,6 +1085,7 @@ def plot_diff_maps( ### Finish and return # ax.set_title(title, y=0.95) + dfba.plot(ax=ax, edgecolor='k', facecolor='none', lw=0.1) dfstates.plot(ax=ax, edgecolor='k', facecolor='none', lw=0.25) ax.axis('off') From 31c1cf084cac358b4a37200a052093b64368e7f1 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 13:49:26 -0600 Subject: [PATCH 21/45] Decrease map_capacity_techs label fontsize --- reeds/reedsplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 29cdeaba5..f70849904 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -2771,7 +2771,7 @@ def map_capacity_techs( 'label': '{} [GW]'.format(tech), } ) - label_region_value(dfplot, ax=ax[coords[tech]], column='GW', fontsize=6) + label_region_value(dfplot, ax=ax[coords[tech]], column='GW', fontsize=5) ax[coords[tech]].axis('off') ax[0,0].set_title( '{} ({})'.format(os.path.basename(case), year), From fde159b7546a940c49530654f4a4a1b6f9daa28e Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 14:21:09 -0600 Subject: [PATCH 22/45] Decrease multi-case map capacity tech label fontsize --- postprocessing/compare_cases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index fc7908518..0591a6fae 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -2682,7 +2682,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): ax=ax[coords[case]], column='GW', cmap=cmap, vmin=vmin, vmax=vmax, legend=False, ) - reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fontsize=6) + reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fontsize=5) ## Legend if coords[case] == legendcoords: plots.addcolorbarhist( @@ -2754,7 +2754,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): vmax=(absmax if case == basecase else diffmax), legend=False, ) - reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fontsize=6) + reedsplots.label_region_value(dfplot, ax=ax[coords[case]], column="GW", fontsize=5) ## Difference legend if coords[case] == legendcoords: plots.addcolorbarhist( From 22228afe691ec9d7fd1846580ec0d40347f7d552 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 16:18:42 -0600 Subject: [PATCH 23/45] Update reedsplot.py label format --- reeds/reedsplots.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index f70849904..8f7ff8e0d 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1029,7 +1029,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) - label_region_value(dfplot, ax=ax, column=valcol, fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol, fontsize=5) ###### Plot the comp elif plot == 'comp': @@ -1041,7 +1041,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) - label_region_value(dfplot, ax=ax, column=valcol, fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol, fontsize=5) ###### Plot the pct diff elif plot in ['diff','pctdiff','pct_diff','diffpct','diff_pct','pct']: @@ -1061,7 +1061,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_pctdiff', cmap=cmap, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) - label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', fontsize=5) ###### Plot the absolute diff elif plot in ['absdiff', 'abs_diff', 'diffabs', 'diff_abs']: @@ -1081,7 +1081,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_diff', cmap=plt.cm.RdBu_r, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) - label_region_value(dfplot, ax=ax, column=valcol+'_diff', fontsize=6) + label_region_value(dfplot, ax=ax, column=valcol+'_diff', fontsize=5) ### Finish and return # ax.set_title(title, y=0.95) @@ -1377,7 +1377,7 @@ def map_net_imports( vmin=-vmax[year], vmax=vmax[year], ) label_region_value(df, ax=ax[coords[year]], column='net_import', - opt_single_decimal=False, fmt = '{:+.0f}', fontsize=6) + opt_single_decimal=False, fmt = '{:.0f}', fontsize=5) ## Formatting ax[coords[year]].set_title(year, y=0.9) if vlim != 'shared': @@ -4521,19 +4521,19 @@ def map_h2_capacity( cap_h2turbine.plot( ax=ax[0,0], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Turbines [kT/day]'}}) - label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', fontsize=6) + label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', fontsize=5) ### Electrolyzers if not cap_h2prod.empty: cap_h2prod.plot( ax=ax[0,1], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Production [kT/day]'}}) - label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', fontsize=6) + label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', fontsize=5) ### Storage if not cap_h2prod.empty: cap_storage.plot( ax=ax[1,0], column='h2_storage', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Storage [kT]'}}) - label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', fontsize=6) + label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', fontsize=5) ### Pipelines if not h2_trans_cap.empty: for i,row in h2_trans_cap.iterrows(): @@ -6285,14 +6285,14 @@ def label_region_value( **kwargs ): """kwargs are passed to patheffects.withStroke()""" - pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':1}, **kwargs} + pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':0.7}, **kwargs} text_artists = [] for r, row in df.iterrows(): value = row.get(column, np.nan) if not np.isfinite(value): continue if opt_single_decimal: - decimals = 0 if abs(value) >= 1 else 1 + decimals = 0 if ((abs(value) >= 1) or abs(value) < 0.05) else 1 fmt = f"{{:.{decimals}f}}" text_artists.append( ax.annotate( From 0d3e1ddfa915afe0e3a30b82de2fb9e2b3188472 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 24 Aug 2026 16:19:23 -0600 Subject: [PATCH 24/45] Adjust labels in map_dropped_load --- reeds/resource_adequacy/diagnostic_plots.py | 25 ++++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index 5f04b93cc..bfa332694 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -10,6 +10,7 @@ from glob import glob import traceback import cmocean +from adjustText import adjust_text ### Local imports from pathlib import Path sys.path.append(str(Path(__file__).parent.parent.parent)) @@ -427,17 +428,29 @@ def map_dropped_load(sw, dfs, level='r'): dfba.plot(ax=ax, facecolor='none', edgecolor='k', lw=0.2) ### Data dfplot.plot(ax=ax, column='val', cmap=cmocean.cm.tempo) + text_artists = [] for r, row in dfplot.iterrows(): if row.val > 0: - ax.annotate( - f'{row.val:,.0f} {units[metric,agg][0]}', - (row.centroid_x, row.centroid_y), - color='r', ha='center', va='top', fontsize=6, weight='bold') + text_artists.append( + ax.annotate( + f'{row.val:,.0f} {units[metric,agg][0]}', + (row.centroid_x, row.centroid_y), + color='r', ha='center', va='top', fontsize=6, weight='bold') + ) + adjust_text(text_artists, ax=ax, + avoid_self=False, ensure_inside_axes=True, + ) ### Formatting if level in ['r','rb','ba']: + text_artists = [] for r, row in dfba.iterrows(): - ax.annotate(r, (row.centroid_x, row.centroid_y), - ha='center', va='bottom', fontsize=6, color='C7') + text_artists.append( + ax.annotate(r, (row.centroid_x, row.centroid_y), + ha='center', va='bottom', fontsize=6, color='C7') + ) + adjust_text(text_artists, ax=ax, + avoid_self=False, ensure_inside_axes=True, + ) ax.axis('off') if savefig: plt.savefig(os.path.join(sw['savepath'],savename)) From d43381f633defe1ad75434bc2c6a388d54d1e7b9 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Tue, 25 Aug 2026 11:17:06 -0600 Subject: [PATCH 25/45] Avoid label overlap in maps.py --- docs/source/plotting_scripts/maps.py | 42 +++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/docs/source/plotting_scripts/maps.py b/docs/source/plotting_scripts/maps.py index 7a8f0d1c3..db2d511dd 100644 --- a/docs/source/plotting_scripts/maps.py +++ b/docs/source/plotting_scripts/maps.py @@ -3,6 +3,7 @@ #%% Imports import os import sys +from adjustText import adjust_text import shapely import datetime import numpy as np @@ -206,7 +207,10 @@ } draw_lakes = True draw_counties = False -label_regions = {'hurdlereg': False} +label_regions = { + 'hurdlereg': False + 'r': False, + } for level in dfmap: dfregion = dfmap[level].copy() @@ -230,6 +234,7 @@ dfcounty.plot(ax=ax, facecolor='none', edgecolor='C7', lw=0.02, zorder=1e6) if draw_lakes: greatlakes.plot(ax=ax, edgecolor='#2CA8E7', facecolor='#D3EFFA', lw=0.2, zorder=-1) + text_artists = [] for r, row in dfregion.iterrows(): dfregion.loc[[r]].plot(ax=ax, color=colors[r], alpha=alpha_region, lw=0, zorder=1) if label_regions.get(level, True): @@ -240,15 +245,17 @@ for i, (c, a) in enumerate([('k',1), (colors[r], 0.6)]): if i == 1 and level != 'r': continue - ax.annotate( - (r if level == 'r' else r.replace('_','\n')), - (x, y), - ha='center', va='center', weight='bold', - size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), - color=c, zorder=1e11+i, alpha=a, - path_effects=( - [pe.withStroke(linewidth=1.5, foreground='w', alpha=(1 if i == 0 else 0))] - ), + text_artists.append( + ax.annotate( + (r if level == 'r' else r.replace('_','\n')), + (x, y), + ha='center', va='center', weight='bold', + size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), + color=c, zorder=1e11+i, alpha=a, + path_effects=( + [pe.withStroke(linewidth=1.5, foreground='w', alpha=(1 if i == 0 else 0))] + ), + ) ) if label_zones.get(level, True): for r, row in dfmap['r'].iterrows(): @@ -256,13 +263,16 @@ np.array([row.geometry.centroid.x, row.geometry.centroid.y]) + np.array(offset.get('r', {}).get(r, (0,0))) ) - ax.annotate( - r, - (x, y), - ha='center', va='center', size=6, weight='normal', - color='C7', zorder=1e10, - path_effects=[pe.withStroke(linewidth=0.7, foreground='w', alpha=1)] + text_artists.append( + ax.annotate( + r, + (x, y), + ha='center', va='center', size=6, weight='normal', + color='C7', zorder=1e10, + path_effects=[pe.withStroke(linewidth=0.7, foreground='w', alpha=1)] + ) ) + adjust_text(text_artists, ax=ax, avoid_self=False, ensure_inside_axes=True) ax.axis('off') savename = ( From 4b8a74ea9e8057c1a9d28d16508eeb0c75a184d1 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Tue, 25 Aug 2026 11:27:10 -0600 Subject: [PATCH 26/45] Make cf map NaNs gray and unlabeled --- reeds/input_processing/hourly_plots.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reeds/input_processing/hourly_plots.py b/reeds/input_processing/hourly_plots.py index 9cc854714..74fc630f7 100644 --- a/reeds/input_processing/hourly_plots.py +++ b/reeds/input_processing/hourly_plots.py @@ -352,12 +352,14 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: dfdiffs[level].plot( ax=ax[coords[level]], column='cf_diff', cmap=cmaps['cf_diff'], vmin=vm[tech]['cf_diff'][0], vmax=vm[tech]['cf_diff'][1], - lw=0, legend=False, + lw=0, legend=False, missing_kwds={"color": "lightgrey"}, ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) ## Text differences text_artists = [] for r, row in (dfdiffs[level].assign(val=dfdiffs[level].cf_diff.abs()).sort_values('val')).iterrows(): + if np.isnan(row.cf_diff): + continue decimals = 0 if abs(row.cf_diff) >= 1 else 1 text_artists.append( ax[coords[level]].annotate( From afcd4ae77a258a3279b3bffc28a549149eef763c Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Tue, 25 Aug 2026 13:54:01 -0600 Subject: [PATCH 27/45] Seperate region-colored and black labels for different levels in docs maps --- docs/source/plotting_scripts/maps.py | 33 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/docs/source/plotting_scripts/maps.py b/docs/source/plotting_scripts/maps.py index db2d511dd..bc1a68e6e 100644 --- a/docs/source/plotting_scripts/maps.py +++ b/docs/source/plotting_scripts/maps.py @@ -207,10 +207,7 @@ } draw_lakes = True draw_counties = False -label_regions = { - 'hurdlereg': False - 'r': False, - } +label_regions = {'hurdlereg': False} for level in dfmap: dfregion = dfmap[level].copy() @@ -242,21 +239,37 @@ np.array([row.geometry.centroid.x, row.geometry.centroid.y]) + np.array(offset.get(level, {}).get(r, (0,0))) ) - for i, (c, a) in enumerate([('k',1), (colors[r], 0.6)]): - if i == 1 and level != 'r': - continue + if level == 'r': + # adds region-colored label for r + text_artists.append( + ax.annotate( + r, + (x, y), + ha='center', va='center', weight='bold', + size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), + color=colors[r], zorder=1e11, alpha=1, + path_effects=( + [pe.withStroke(linewidth=1.5, foreground='w', alpha=1)] + ), + ) + ) + else: + # adds black label for other levels text_artists.append( ax.annotate( (r if level == 'r' else r.replace('_','\n')), (x, y), ha='center', va='center', weight='bold', size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), - color=c, zorder=1e11+i, alpha=a, + color='k', zorder=1e11, alpha=1, path_effects=( - [pe.withStroke(linewidth=1.5, foreground='w', alpha=(1 if i == 0 else 0))] + [pe.withStroke(linewidth=1.5, foreground='w', alpha=1)] ), ) ) + adjust_text(text_artists, ax=ax, avoid_self=False, ensure_inside_axes=True) + + text_artists = [] if label_zones.get(level, True): for r, row in dfmap['r'].iterrows(): x, y = ( @@ -272,7 +285,7 @@ path_effects=[pe.withStroke(linewidth=0.7, foreground='w', alpha=1)] ) ) - adjust_text(text_artists, ax=ax, avoid_self=False, ensure_inside_axes=True) + adjust_text(text_artists, ax=ax, avoid_self=False, ensure_inside_axes=True) ax.axis('off') savename = ( From d568a0f63f43242f80353f2e78be738265ef62b6 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 10 Sep 2026 16:45:19 -0600 Subject: [PATCH 28/45] cmap added as global in outage_rates.py and diagnostic_plots.py --- reeds/input_processing/outage_rates.py | 3 ++- reeds/plots.py | 4 ++-- reeds/resource_adequacy/diagnostic_plots.py | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/reeds/input_processing/outage_rates.py b/reeds/input_processing/outage_rates.py index 6d36110a7..d4919ebbf 100644 --- a/reeds/input_processing/outage_rates.py +++ b/reeds/input_processing/outage_rates.py @@ -19,6 +19,7 @@ tz_out = 'Etc/GMT+6' temp_min = -50 temp_max = 60 +cmap = cmocean.cm.tempo ## Only use during_quarters for techs without a monthly scheduled outage rate during_quarters = ['spring', 'fall'] ## Cap the extrapolation of forced outage rates at high/low temperatures to 0.4 because @@ -383,7 +384,7 @@ def plot_outage_forced( plt.close() f, ax = reeds.plots.map_years_months( - dfzones=dfzones, dfdata=dfdata, aggfunc=aggfunc, + dfzones=dfzones, dfdata=dfdata, cmap=cmap, aggfunc=aggfunc, title=f"Monthly {aggfunc}\nforced outage rate,\n{nicelabels.get(pm,pm)} [%]", ) plt.savefig(os.path.join(figpath, f'FOR_monthly-{aggfunc}-{pm}.png')) diff --git a/reeds/plots.py b/reeds/plots.py index e216e8ee2..37b9654e3 100644 --- a/reeds/plots.py +++ b/reeds/plots.py @@ -1040,7 +1040,7 @@ def draw_screen_poly(poly, m, def plotusascattermap( dfplot, colorcolumn=None, sizecolumn=None, filterdict=None, sort=True, - basemap=None, cmap=cmocean.cm.tempo, mappath=None, + basemap=None, cmap=cmocean.cm.rain, mappath=None, facecolor='w', edgecolor='w', latlonlabels=None, markersize=None, marker='o', figsize=(10,7.5), dpi=None, zrange=None, colors=None, maptype='scatter', contourlevels=100, @@ -2113,7 +2113,7 @@ def plot_region_bars( def map_years_months( - dfzones, dfdata, cmap=cmocean.cm.tempo, aggfunc='mean', + dfzones, dfdata, cmap=cmocean.cm.rain, aggfunc='mean', colwidth=1.2, rowheight=1.0, simplify=10000, mapbuffer=1.08, vmin=0., vmax=None, outline=0.25, labels=True, **colorbarkwargs, diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index bfa332694..7f45f4ab4 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -24,7 +24,7 @@ dpi = None interactive = False savefig = True - +cmap = cmocean.cm.tempo #%%### Functions def delete_temporary_files(sw): @@ -427,7 +427,7 @@ def map_dropped_load(sw, dfs, level='r'): ### Background dfba.plot(ax=ax, facecolor='none', edgecolor='k', lw=0.2) ### Data - dfplot.plot(ax=ax, column='val', cmap=cmocean.cm.tempo) + dfplot.plot(ax=ax, column='val', cmap=cmap) text_artists = [] for r, row in dfplot.iterrows(): if row.val > 0: @@ -1003,7 +1003,7 @@ def map_pras_failure_rate(sw, dfs, aggfunc='mean', repair=False): savename = f"hourly_failure_rate-year,month-{aggfunc}-{tech.replace('-','').replace('/','')}-{sw['t']}" plt.close() f, ax = plots.map_years_months( - dfzones=dfzones, dfdata=failrate[tech], + dfzones=dfzones, dfdata=failrate[tech], cmap=cmap, title=f"Monthly {aggfunc}\nhourly failure rate,\n{tech} [%]", ) ## Save it @@ -1024,7 +1024,7 @@ def map_pras_failure_rate(sw, dfs, aggfunc='mean', repair=False): savename = f"hourly_repair_rate-year,month-{aggfunc}-{tech.replace('-','').replace('/','')}-{sw['t']}" plt.close() f, ax = plots.map_years_months( - dfzones=dfzones, dfdata=repairrate[tech], + dfzones=dfzones, dfdata=repairrate[tech], cmap=cmap, title=f"Monthly {aggfunc}\nhourly repair rate,\n{tech} [%]", ) ## Save it @@ -1367,12 +1367,12 @@ def main(sw, debug=False): debug = args.debug # #%%### Inputs for debugging - # reeds_path = reeds.io.reeds_path - # casedir = os.path.join(reeds_path, 'runs', 'v20260715_stressM3_MultiMetricRA') - # t = 2050 - # interactive = True - # iteration = 0 - # debug = True + reeds_path = reeds.io.reeds_path + casedir = os.path.join(reeds_path, 'runs', 'v20260820_USA_defaults') + t = 2035 + interactive = True + iteration = 0 + debug = True #%%### INPUTS ### Switches From 4448bf2a6d6cebd07a7c94ef25e64af70a1a6f97 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 10 Sep 2026 16:53:33 -0600 Subject: [PATCH 29/45] cmap updated with global in single_case_plots.py and diagnostic_plots.py --- postprocessing/single_case_plots.py | 9 +++++---- reeds/reedsplots.py | 20 ++++++++++---------- reeds/resource_adequacy/diagnostic_plots.py | 1 + 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/postprocessing/single_case_plots.py b/postprocessing/single_case_plots.py index 0169e924c..7df41f734 100644 --- a/postprocessing/single_case_plots.py +++ b/postprocessing/single_case_plots.py @@ -267,7 +267,7 @@ plt.close() f,ax = reedsplots.plot_trans_vsc( case=case, year=year, wscale=wscale_straight*1e3, - alpha=1.0, miles=300, + alpha=1.0, miles=300, cmap=cmap, ) savename = f'map_translines_vsc-{year}.png' if write and (f is not None): @@ -288,7 +288,7 @@ for vmax in ['each', 'shared']: try: f,ax = reedsplots.map_capacity_techs( - case, year=year, ncols=ncols, vmax=vmax, + case, year=year, ncols=ncols, vmax=vmax, cmap=cmap, ) savename = f'map_capacity-{year}-{vmax}.png' if write: @@ -646,7 +646,7 @@ try: for y in [y for y in years if y >= 2025]: plt.close() - f, ax, neue, _iteration = reedsplots.map_neue(case=case, year=y) + f, ax, neue, _iteration = reedsplots.map_neue(case=case, year=y, cmap=cmap) savename = f"map_PRAS_neue-{y}i{_iteration}.png" if write: plt.savefig(os.path.join(savepath, savename)) @@ -860,7 +860,7 @@ #%% PRM if iterating if int(sw.GSw_PRM_StressIterateMax) and int(sw.GSw_PRM_UpdateMethod): try: - f, ax, prm_final = reedsplots.map_prm(case) + f, ax, prm_final = reedsplots.map_prm(case, cmap=cmap) savename = 'map_prm.png' if write: plt.savefig(os.path.join(savepath, savename)) @@ -928,6 +928,7 @@ years=[year], vscale=1e-3, vmin=0, + cmap=cmap, title='Sited demand [GW]', ) savename = f'map_loadsite-{year}.png' diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 8f7ff8e0d..cbdc43e2a 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1094,7 +1094,7 @@ def plot_diff_maps( def plot_trans_vsc( case, year=2050, wscale=0.4, alpha=1.0, miles=300, - cmap=cmocean.cm.tempo, scale=True, title=True, + cmap=cmocean.cm.rain, scale=True, title=True, convertermax=None, f=None, ax=None, ): @@ -1189,7 +1189,7 @@ def plot_trans_vsc( def plot_transmission_utilization( case, year=2050, plottype='mean', network='trans', - wscale=0.0004, alpha=1.0, cmap=cmocean.cm.tempo, + wscale=0.0004, alpha=1.0, cmap=cmocean.cm.rain, extent='modeled', f=None, ax=None, thicklevel='transreg', @@ -2717,7 +2717,7 @@ def map_capacity_techs( techs='aggregation', ncols=4, vmax='shared', - cmap=cmocean.cm.tempo, + cmap=cmocean.cm.rain, ): """ techs: list of technologies to plot, or 'aggregation' to plot all aggregated technologies @@ -3458,7 +3458,7 @@ def map_hybrid_pv_wind( case, val='site_cap', year=2050, tech=None, vmax=None, markersize=10.75, #stretch=1.2, - cmap=cmocean.cm.tempo, + cmap=cmocean.cm.rain, f=None, ax=None, figsize=(6,6), dpi=None, ): """ @@ -3466,7 +3466,7 @@ def map_hybrid_pv_wind( cmap: Suggestions: val=site_cap, tech=wind-ons: plt.cm.Blues val=site_cap, tech=upv: plt.cm.Oranges - val=(site_hybridization,site_spurcap), tech=None: cmocean.cm.tempo + val=(site_hybridization,site_spurcap), tech=None: cmocean.cm.rain val=(site_pv_fraction,site_gir), tech=either: plt.cm.turbo or mpl.colors.LinearSegmentedColormap.from_list 'turboclip', [plt.cm.turbo(c) for c in np.linspace(0.1,0.91,101)]) @@ -4345,7 +4345,7 @@ def plot_ra_metrics_bylevel( def map_neue( case, year=2050, iteration='last', samples=None, - vmax=10., cmap=cmocean.cm.tempo, label=True, + vmax=10., cmap=cmocean.cm.rain, label=True, over_vmax_mapcolor=None, over_threshold_textcolor='C3', highlight_over_threshold=True, @@ -4435,7 +4435,7 @@ def map_neue( def map_h2_capacity( case, year=2050, wscale_h2=10, figheight=6, pipescale=0.1, legend_kwds={'shrink':0.6, 'pad':0, 'orientation':'horizontal', 'aspect':12}, - cmap=cmocean.cm.tempo, + cmap=cmocean.cm.rain, ): """ H2 turbines, production (Electrolyzer/SMR), pipelines, and storage @@ -6126,7 +6126,7 @@ def map_outage_days( techs=['gas-cc', 'gas-ct', 'coaloldscr', 'nuclear', 'hyded'], outage_type='forced', aggfunc='max', - cmap=cmocean.cm.tempo, + cmap=cmocean.cm.rain, vmin=0, vmax='auto', fontsize=4, @@ -6852,7 +6852,7 @@ def map_output_byyear( oneaxis='columns', yearaxis='rows', mapscale=4, - cmap=cmocean.cm.tempo, + cmap=cmocean.cm.rain, vscale=1, vmin=None, vmax=None, @@ -6924,7 +6924,7 @@ def map_output_byyear( return f, ax, dictplot -def map_prm(case, tmin=2023, cmap=cmocean.cm.tempo, scale=3, fontsize=7, vmax=None): +def map_prm(case, tmin=2023, cmap=cmocean.cm.rain, scale=3, fontsize=7, vmax=None): dfmap = reeds.io.get_dfmap(case) sw = reeds.io.get_switches(case) ### Get final iterations diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index 7f45f4ab4..55f1ee8ca 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -1056,6 +1056,7 @@ def map_outagerate_new_stressperiods(sw, dfs): dates=dates, outage_type=outage_type, aggfunc=aggfunc, + cmap=cmap, vmax=vmax[outage_type], ) ## Save it From 2ad183c88038b369a83066e6b3f12121cb98b1b8 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Thu, 10 Sep 2026 16:55:33 -0600 Subject: [PATCH 30/45] Use global cmap in compare_cases.py --- postprocessing/compare_cases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index 0591a6fae..0d964b430 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -2595,7 +2595,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): year=lastyear, casebase=casebase, casecomp=casecomp, level=level, plot='base', f=f, ax=ax[0], - cmap=cmocean.cm.tempo, + cmap=cmap, ) ax[0].annotate( casebase_name, @@ -2606,7 +2606,7 @@ def two_bars(dfplot, basecase, colors, ax, col=0, ypad=0.02): year=lastyear, casebase=casebase, casecomp=casecomp, level=level, plot='comp', f=f, ax=ax[1], - cmap=cmocean.cm.tempo, + cmap=cmap, ) ax[1].annotate( casecomp_name, From 9b239401950fcbe70c29846e44bbcd83fe6f0413 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Fri, 11 Sep 2026 17:43:44 -0600 Subject: [PATCH 31/45] adjustText optional for core functionality --- environment.yml | 4 +- reeds/input_processing/hourly_plots.py | 45 ++---- reeds/reedsplots.py | 145 +++++++++----------- reeds/resource_adequacy/diagnostic_plots.py | 36 ++--- 4 files changed, 95 insertions(+), 135 deletions(-) diff --git a/environment.yml b/environment.yml index 0a1e1a0a7..003640058 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: reeds-at +name: reeds channels: - conda-forge - nodefaults @@ -25,9 +25,9 @@ dependencies: - scipy=1.18 - shapely=2.1 - tqdm=4.68 - - adjustText ## vvv The next packages are optional for default settings and may ## vvv be excluded if you're having trouble building the environment. + - adjustText=1.14 # for avoiding map label overlaps - fiona=1.10 # for interactive maps - folium=0.20 # for interactive maps - ipykernel=7.3 # for interactive python in VS Code diff --git a/reeds/input_processing/hourly_plots.py b/reeds/input_processing/hourly_plots.py index 74fc630f7..81dc3b112 100644 --- a/reeds/input_processing/hourly_plots.py +++ b/reeds/input_processing/hourly_plots.py @@ -12,9 +12,9 @@ from matplotlib import patheffects as pe import cmocean from pathlib import Path -from adjustText import adjust_text sys.path.append(str(Path(__file__).parent.parent.parent)) import reeds +from reeds import reedsplots from reeds import plots plots.plotparams() @@ -355,23 +355,14 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: lw=0, legend=False, missing_kwds={"color": "lightgrey"}, ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) - ## Text differences - text_artists = [] - for r, row in (dfdiffs[level].assign(val=dfdiffs[level].cf_diff.abs()).sort_values('val')).iterrows(): - if np.isnan(row.cf_diff): - continue - decimals = 0 if abs(row.cf_diff) >= 1 else 1 - text_artists.append( - ax[coords[level]].annotate( - f"{row.cf_diff*100:+.{decimals}f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', c='k', fontsize={'r':5}.get(level,7), - path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.5)], - ) + dfmap[level]['cf_diff_pct'] = (dfdiffs[level].cf_diff * 100).values + reedsplots.label_region_value( + dfmap[level], + ax[coords[level]], + column='cf_diff_pct', + text_kwargs={'fontsize': {'r':5}.get(level,7)}, + pe_kwargs={'linewidth': 1.5, 'foreground': 'w', 'alpha': 0.5}, ) - adjust_text(text_artists, ax=ax[coords[level]], - avoid_self=False,ensure_inside_axes=True, - ) ## Colorbar plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=dfdiffs[level].cf_diff*100, nbins=51, @@ -494,21 +485,13 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: lw=0, legend=False, ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) - ## Text differences - text_artists = [] - for r, row in (dfdiffs[level].assign(val=dfdiffs[level][val].abs()).sort_values('val')).iterrows(): - decimals = 0 if abs(row[val]) >= 1 else 1 - text_artists.append( - ax[coords[level]].annotate( - f"{row[val]:+.{decimals}f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', c='k', fontsize={'r':5}.get(level,7), - path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.5)], - ) + reedsplots.label_region_value( + dfmap[level], + ax[coords[level]], + column=val, + text_kwargs={'fontsize': {'r':5}.get(level,7)}, + pe_kwargs={'linewidth': 1.5, 'foreground': 'w', 'alpha': 0.5}, ) - adjust_text(text_artists, ax=ax[coords[level]], - avoid_self=False,ensure_inside_axes=True, - ) ## Colorbar plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=dfdiffs[level][val], nbins=51, diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index cbdc43e2a..d03707e46 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -15,7 +15,6 @@ import geopandas as gpd import shapely import cmocean -from adjustText import adjust_text sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import reeds from reeds import plots @@ -1029,7 +1028,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) - label_region_value(dfplot, ax=ax, column=valcol, fontsize=5) + label_region_value(dfplot, ax=ax, column=valcol, text_kwargs={'fontsize':5}) ###### Plot the comp elif plot == 'comp': @@ -1041,7 +1040,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol, cmap=cmap, legend=True, legend_kwds=legend_kwds, vmax=zmax) - label_region_value(dfplot, ax=ax, column=valcol, fontsize=5) + label_region_value(dfplot, ax=ax, column=valcol, text_kwargs={'fontsize':5}) ###### Plot the pct diff elif plot in ['diff','pctdiff','pct_diff','diffpct','diff_pct','pct']: @@ -1061,7 +1060,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_pctdiff', cmap=cmap, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) - label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', fontsize=5) + label_region_value(dfplot, ax=ax, column=valcol+'_pctdiff', text_kwargs={'fontsize':5}) ###### Plot the absolute diff elif plot in ['absdiff', 'abs_diff', 'diffabs', 'diff_abs']: @@ -1081,7 +1080,7 @@ def plot_diff_maps( dfplot.plot(ax=ax, column=valcol+'_diff', cmap=plt.cm.RdBu_r, legend=True, vmin=-zlim, vmax=+zlim, legend_kwds=legend_kwds) - label_region_value(dfplot, ax=ax, column=valcol+'_diff', fontsize=5) + label_region_value(dfplot, ax=ax, column=valcol+'_diff', text_kwargs={'fontsize':5}) ### Finish and return # ax.set_title(title, y=0.95) @@ -1377,7 +1376,7 @@ def map_net_imports( vmin=-vmax[year], vmax=vmax[year], ) label_region_value(df, ax=ax[coords[year]], column='net_import', - opt_single_decimal=False, fmt = '{:.0f}', fontsize=5) + opt_single_decimal=False, fmt = '{:.0f}', text_kwargs={'fontsize':5}) ## Formatting ax[coords[year]].set_title(year, y=0.9) if vlim != 'shared': @@ -2771,7 +2770,7 @@ def map_capacity_techs( 'label': '{} [GW]'.format(tech), } ) - label_region_value(dfplot, ax=ax[coords[tech]], column='GW', fontsize=5) + label_region_value(dfplot, ax=ax[coords[tech]], column='GW', text_kwargs={'fontsize':5}) ax[coords[tech]].axis('off') ax[0,0].set_title( '{} ({})'.format(os.path.basename(case), year), @@ -4394,31 +4393,28 @@ def map_neue( ## Labels # decimals = (0 if df.NEUE_ppm.max() >= 10 else 1) decimals = (0 if level in ['st','r'] else 1) - text_artists = [] + # label_region_value can't handle conditional formating, so keep as-is for r, row in df.sort_values('NEUE_ppm').iterrows(): if highlight_over_threshold: over_threshold = row.NEUE_ppm > neue_threshold else: over_threshold = False - text_artists.append( - ax[coords[level]].annotate( - f"{row.NEUE_ppm:.{decimals}f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', - c=(over_threshold_textcolor if over_threshold else 'k'), - weight=('bold' if over_threshold else 'normal'), - fontsize={'r':5}.get(level,7), - zorder=1e9, - path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.7)], - ) + ax[coords[level]].annotate( + f"{row.NEUE_ppm:.{decimals}f}", + [row.centroid_x, row.centroid_y], + ha='center', va='center', + c=(over_threshold_textcolor if over_threshold else 'k'), + weight=('bold' if over_threshold else 'normal'), + fontsize={'r':5}.get(level,7), + zorder=1e9, + path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.7)], ) + if over_threshold and (level == neue_threshold_level): ax[coords[level]].set_title( level, y=0.9, weight='bold', color=over_threshold_textcolor, ) - adjust_text(text_artists, ax=ax[coords[level]], - avoid_self=False, ensure_inside_axes=True, - ) + ### Formatting plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=df.NEUE_ppm, @@ -4521,19 +4517,19 @@ def map_h2_capacity( cap_h2turbine.plot( ax=ax[0,0], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Turbines [kT/day]'}}) - label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', fontsize=5) + label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', text_kwargs={'fontsize':5}) ### Electrolyzers if not cap_h2prod.empty: cap_h2prod.plot( ax=ax[0,1], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Production [kT/day]'}}) - label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', fontsize=5) + label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', text_kwargs={'fontsize':5}) ### Storage if not cap_h2prod.empty: cap_storage.plot( ax=ax[1,0], column='h2_storage', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Storage [kT]'}}) - label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', fontsize=5) + label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', text_kwargs={'fontsize':5}) ### Pipelines if not h2_trans_cap.empty: for i,row in h2_trans_cap.iterrows(): @@ -5135,18 +5131,13 @@ def plot_seed_stressperiods( ax=ax[row,col], column='val', edgecolor='none', lw=0, cmap=cmap, alpha=alpha, vmin=0, vmax=1, ) - text_artists = [] - for i, _row in df.iterrows(): - text_artists.append( - ax[row,col].annotate( - _row.date, (_row.centroid_x, _row.centroid_y), - ha='center', va='center', color='k', fontsize=fontsize, - path_effects=[pe.withStroke(linewidth=pelinewidth, foreground='w', alpha=pealpha)], - ) - ) - adjust_text(text_artists, ax=ax[row,col], - avoid_self=False, ensure_inside_axes=True, - ) + label_region_value( + df, + ax=ax[row,col], + column='val', + text_kwargs={'fontsize': fontsize}, + pe_kwargs={'linewidth': pelinewidth, 'foreground': 'w', 'alpha': pealpha}, + ) ### Max load @@ -5177,18 +5168,13 @@ def plot_seed_stressperiods( ax=ax[row,col], column='val', edgecolor='none', lw=0, cmap=cmap, alpha=alpha, vmin=0, vmax=1, ) - text_artists = [] - for i, _row in df.iterrows(): - text_artists.append( - ax[row,col].annotate( - _row.date, (_row.centroid_x, _row.centroid_y), - ha='center', va='center', color='k', fontsize=fontsize, - path_effects=[pe.withStroke(linewidth=pelinewidth, foreground='w', alpha=pealpha)], - ) - ) - adjust_text(text_artists, ax=ax[row,col], - avoid_self=False, ensure_inside_axes=True, - ) + label_region_value( + df, + ax=ax[row,col], + column='date', + text_kwargs={'fontsize': fontsize}, + pe_kwargs={'linewidth': pelinewidth, 'foreground': 'w', 'alpha': pealpha}, + ) ### Colorbar row, col = 0, 0 @@ -6184,20 +6170,14 @@ def map_outage_days( dfplot.plot(ax=_ax, column='outage_pct', cmap=cmap, vmin=vmin, vmax=vmax) ## Data values if fontsize: - text_artists = [] - for r, row in dfplot.sort_values('outage_pct').iterrows(): - text_artists.append( - _ax.annotate( - f"{row.outage_pct:.0f}", - [row.centroid_x, row.centroid_y], - ha='center', va='center', c='k', - fontsize=fontsize, - path_effects=[pe.withStroke(linewidth=1.4, foreground='w', alpha=0.7)], - ) - ) - adjust_text(text_artists, ax=_ax, - avoid_self=False, ensure_inside_axes=True, - ) + label_region_value( + df=dfplot, + ax=_ax, + column='outage_pct', + fmt="{:.0f}", + text_kwargs={'fontsize': fontsize}, + pe_kwargs={'linewidth': 1.4, 'foreground': 'w', 'alpha': 0.7}, + ) _ax.axis('off') ## Formatting if date == dates[0]: @@ -6280,30 +6260,35 @@ def label_region_value( column, opt_single_decimal=True, fmt='{:.0f}', - color='k', - fontsize=8, - **kwargs + text_kwargs={'ha':'center', 'va':'center', 'color':'k', 'fontsize':8}, + pe_kwargs={'linewidth':1.5, 'foreground':'w', 'alpha':0.7} ): - """kwargs are passed to patheffects.withStroke()""" - pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':0.7}, **kwargs} + """pe_kwargs are passed to patheffects.withStroke()""" + text_kwargs = {**{'ha':'center', 'va':'center', 'color':'k', 'fontsize':8}, **text_kwargs} + pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':0.7}, **pe_kwargs} text_artists = [] for r, row in df.iterrows(): value = row.get(column, np.nan) - if not np.isfinite(value): - continue - if opt_single_decimal: - decimals = 0 if ((abs(value) >= 1) or abs(value) < 0.05) else 1 - fmt = f"{{:.{decimals}f}}" + if isinstance(value, (int, float, complex)): + if not np.isfinite(value): + continue + if opt_single_decimal: + decimals = 0 if ((abs(value) >= 1) or abs(value) < 0.05) else 1 + fmt = f"{{:.{decimals}f}}" text_artists.append( ax.annotate( - fmt.format(value), + (fmt.format(value) if not isinstance(value, str) else value), (row.geometry.centroid.x, row.geometry.centroid.y), - ha='center', va='center', fontsize=fontsize, - color=color, + **text_kwargs, path_effects=[pe.withStroke(**pe_kwargs)], zorder=1e9, ) ) + try: + from adjustText import adjust_text + except ImportError: + return + adjust_text(text_artists, ax=ax, avoid_self=False, ensure_inside_axes=True, ) @@ -6627,7 +6612,7 @@ def map_stressors( label_region_value( df=dflevel, ax=ax[1,0], column='load_rank', opt_single_decimal=False, fmt='{:.0f}%', - linewidth=2.0, alpha=0.8, + pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[1,0].set_title('Demand', y=0.9) plots.addcolorbarhist( @@ -6643,7 +6628,7 @@ def map_stressors( label_region_value( df=dflevel, ax=ax[1,col], column=f'{tech}_rank', opt_single_decimal=False, fmt='{:.0f}%', - linewidth=2.0, alpha=0.8, + pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[1,col].set_title(labels.get(tech,tech), y=0.9) plots.addcolorbarhist( @@ -6658,7 +6643,7 @@ def map_stressors( label_region_value( df=dflevel, ax=ax[1,3], column='temperature_rank', opt_single_decimal=False, fmt='{:.0f}%', - linewidth=2.0, alpha=0.8, + pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[1,3].set_title('Temperature', y=0.9) plots.addcolorbarhist( @@ -6677,7 +6662,7 @@ def map_stressors( label_region_value( df=dflevel, ax=ax[2,col], column=f'outage_{tech}', opt_single_decimal=False, fmt='{:.0f}%', - linewidth=2.0, alpha=0.8, + pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[2,col].set_title(label, y=0.9) plots.addcolorbarhist( diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index 55f1ee8ca..d0711f87e 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -10,11 +10,11 @@ from glob import glob import traceback import cmocean -from adjustText import adjust_text ### Local imports from pathlib import Path sys.path.append(str(Path(__file__).parent.parent.parent)) import reeds +from reeds import reedsplots from reeds import plots plots.plotparams() @@ -428,28 +428,20 @@ def map_dropped_load(sw, dfs, level='r'): dfba.plot(ax=ax, facecolor='none', edgecolor='k', lw=0.2) ### Data dfplot.plot(ax=ax, column='val', cmap=cmap) - text_artists = [] - for r, row in dfplot.iterrows(): - if row.val > 0: - text_artists.append( - ax.annotate( - f'{row.val:,.0f} {units[metric,agg][0]}', - (row.centroid_x, row.centroid_y), - color='r', ha='center', va='top', fontsize=6, weight='bold') - ) - adjust_text(text_artists, ax=ax, - avoid_self=False, ensure_inside_axes=True, - ) + reedsplots.label_region_value( + dfplot, + ax=ax, + column='val', + fmt=f'{{:,.0f}} {units[metric,agg][0]}', + text_kwargs={'color':'r', 'ha':'center', 'va':'top', 'fontsize':6, 'weight':'bold'} + ) ### Formatting if level in ['r','rb','ba']: - text_artists = [] - for r, row in dfba.iterrows(): - text_artists.append( - ax.annotate(r, (row.centroid_x, row.centroid_y), - ha='center', va='bottom', fontsize=6, color='C7') - ) - adjust_text(text_artists, ax=ax, - avoid_self=False, ensure_inside_axes=True, + reedsplots.label_region_value( + dfba, + ax=ax, + column=dfba.index, + text_kwargs={'ha':'center', 'va':'bottom', 'fontsize':6, 'color':'C7'} ) ax.axis('off') if savefig: @@ -1370,7 +1362,7 @@ def main(sw, debug=False): # #%%### Inputs for debugging reeds_path = reeds.io.reeds_path casedir = os.path.join(reeds_path, 'runs', 'v20260820_USA_defaults') - t = 2035 + t = 2041 interactive = True iteration = 0 debug = True From 5c2b9db7840c80f7bb5055d01c12534e2924c8af Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 14 Sep 2026 10:56:02 -0600 Subject: [PATCH 32/45] Import cmocean in outage_rate.py --- reeds/input_processing/outage_rates.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reeds/input_processing/outage_rates.py b/reeds/input_processing/outage_rates.py index d4919ebbf..ae52ff758 100644 --- a/reeds/input_processing/outage_rates.py +++ b/reeds/input_processing/outage_rates.py @@ -7,6 +7,7 @@ import argparse import h5py from pathlib import Path +import cmocean sys.path.append(str(Path(__file__).parent.parent.parent)) import reeds reeds_path = reeds.io.reeds_path From fd2cfa4b319b2b2899c509160afd596258d9583b Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 14 Sep 2026 11:00:47 -0600 Subject: [PATCH 33/45] Correct adjustText version --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 003640058..bbe8d8b05 100644 --- a/environment.yml +++ b/environment.yml @@ -27,7 +27,7 @@ dependencies: - tqdm=4.68 ## vvv The next packages are optional for default settings and may ## vvv be excluded if you're having trouble building the environment. - - adjustText=1.14 # for avoiding map label overlaps + - adjustText=1.4 # for avoiding map label overlaps - fiona=1.10 # for interactive maps - folium=0.20 # for interactive maps - ipykernel=7.3 # for interactive python in VS Code From 0a0626aa8d228a277a324a76d512812b581abbbb Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 14 Sep 2026 11:13:33 -0600 Subject: [PATCH 34/45] Remove added lines in reedsplots.py --- reeds/reedsplots.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index d03707e46..3ef32448b 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -4409,12 +4409,10 @@ def map_neue( zorder=1e9, path_effects=[pe.withStroke(linewidth=1.5, foreground='w', alpha=0.7)], ) - if over_threshold and (level == neue_threshold_level): ax[coords[level]].set_title( level, y=0.9, weight='bold', color=over_threshold_textcolor, ) - ### Formatting plots.addcolorbarhist( f=f, ax0=ax[coords[level]], data=df.NEUE_ppm, From 07dbb58e31ca5b13c6dd0d3a76a230ebb5aa6cb5 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 14 Sep 2026 14:28:52 -0600 Subject: [PATCH 35/45] Fix load map annotation --- reeds/input_processing/hourly_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/input_processing/hourly_plots.py b/reeds/input_processing/hourly_plots.py index 81dc3b112..957c42fc8 100644 --- a/reeds/input_processing/hourly_plots.py +++ b/reeds/input_processing/hourly_plots.py @@ -486,7 +486,7 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) reedsplots.label_region_value( - dfmap[level], + dfdiffs[level], ax[coords[level]], column=val, text_kwargs={'fontsize': {'r':5}.get(level,7)}, From 23813ca9fadcfe2130f6fbe9809d16bfdfe616d1 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Mon, 14 Sep 2026 15:05:29 -0600 Subject: [PATCH 36/45] Reset diagnostic plots debug inputs --- reeds/resource_adequacy/diagnostic_plots.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reeds/resource_adequacy/diagnostic_plots.py b/reeds/resource_adequacy/diagnostic_plots.py index d0711f87e..a7eb691ac 100644 --- a/reeds/resource_adequacy/diagnostic_plots.py +++ b/reeds/resource_adequacy/diagnostic_plots.py @@ -1360,12 +1360,12 @@ def main(sw, debug=False): debug = args.debug # #%%### Inputs for debugging - reeds_path = reeds.io.reeds_path - casedir = os.path.join(reeds_path, 'runs', 'v20260820_USA_defaults') - t = 2041 - interactive = True - iteration = 0 - debug = True + # reeds_path = reeds.io.reeds_path + # casedir = os.path.join(reeds_path, 'runs', 'v20260715_stressM3_MultiMetricRA') + # t = 2050 + # interactive = True + # iteration = 0 + # debug = True #%%### INPUTS ### Switches From c9c1f3e68f424f7a6e35f5cf94127893eb7a3316 Mon Sep 17 00:00:00 2001 From: Claire Halloran Date: Thu, 8 Oct 2026 15:58:13 -0600 Subject: [PATCH 37/45] Update reeds/reedsplots.py Co-authored-by: Patrick Brown <25125211+patrickbrown4@users.noreply.github.com> --- reeds/reedsplots.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 3ef32448b..93e58349d 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -6258,12 +6258,18 @@ def label_region_value( column, opt_single_decimal=True, fmt='{:.0f}', - text_kwargs={'ha':'center', 'va':'center', 'color':'k', 'fontsize':8}, - pe_kwargs={'linewidth':1.5, 'foreground':'w', 'alpha':0.7} + text_kwargs:None|dict=None, + pe_kwargs:None|dict=None, ): """pe_kwargs are passed to patheffects.withStroke()""" - text_kwargs = {**{'ha':'center', 'va':'center', 'color':'k', 'fontsize':8}, **text_kwargs} - pe_kwargs = {**{'linewidth':1.5, 'foreground':'w', 'alpha':0.7}, **pe_kwargs} + text_kwargs = { + 'ha':'center', 'va':'center', 'color':'k', 'fontsize':8, + **(text_kwargs if isinstance(text_kwargs, dict) else {}) + } + pe_kwargs = { + 'linewidth':1.5, 'foreground':'w', 'alpha':0.7, + **(pe_kwargs if isinstance(pe_kwargs, dict) else {}) + } text_artists = [] for r, row in df.iterrows(): value = row.get(column, np.nan) From 262d79f63257b01a26f1d97edeca8710b3802992 Mon Sep 17 00:00:00 2001 From: Claire Halloran Date: Thu, 8 Oct 2026 15:59:14 -0600 Subject: [PATCH 38/45] Update reeds/reedsplots.py Co-authored-by: Patrick Brown <25125211+patrickbrown4@users.noreply.github.com> --- reeds/reedsplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 93e58349d..7857b119c 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -983,7 +983,7 @@ def plot_diff_maps( ### Aggregate selected technologies to one value per region for the target year dfbase_selected = ( - dfbase.loc[(dfbase.i.isin(titles)) & (dfbase.t == year), ['r', valcol]] + dfbase.loc[(dfbase.i.isin(titles)) & (dfbase.t == year)] .groupby('r', as_index=False)[valcol].sum() .rename(columns={valcol: f'{valcol}_base'}) ) From 7c9f9dfaf66c7102d3e53122064ce2e4e9a695f6 Mon Sep 17 00:00:00 2001 From: Claire Halloran Date: Thu, 8 Oct 2026 15:59:45 -0600 Subject: [PATCH 39/45] Update reeds/reedsplots.py Co-authored-by: Patrick Brown <25125211+patrickbrown4@users.noreply.github.com> --- reeds/reedsplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index 7857b119c..bfecc6aa5 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -988,7 +988,7 @@ def plot_diff_maps( .rename(columns={valcol: f'{valcol}_base'}) ) dfcomp_selected = ( - dfcomp.loc[(dfcomp.i.isin(titles)) & (dfcomp.t == year), ['r', valcol]] + dfcomp.loc[(dfcomp.i.isin(titles)) & (dfcomp.t == year)] .groupby('r', as_index=False)[valcol].sum() .rename(columns={valcol: f'{valcol}_comp'}) ) From eb629bef15df1db38337e8c54b39207b61400138 Mon Sep 17 00:00:00 2001 From: Claire Halloran Date: Thu, 8 Oct 2026 16:01:12 -0600 Subject: [PATCH 40/45] Update reeds/reedsplots.py Co-authored-by: Patrick Brown <25125211+patrickbrown4@users.noreply.github.com> --- reeds/reedsplots.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index bfecc6aa5..cfc71840d 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1375,8 +1375,10 @@ def map_net_imports( legend=False, vmin=-vmax[year], vmax=vmax[year], ) - label_region_value(df, ax=ax[coords[year]], column='net_import', - opt_single_decimal=False, fmt = '{:.0f}', text_kwargs={'fontsize':5}) + label_region_value( + df, ax=ax[coords[year]], column='net_import', + opt_single_decimal=False, text_kwargs={'fontsize':5}, + ) ## Formatting ax[coords[year]].set_title(year, y=0.9) if vlim != 'shared': From f54ebdee6a1c31c9621910eca20e67cdacebfed5 Mon Sep 17 00:00:00 2001 From: Claire Halloran Date: Thu, 8 Oct 2026 16:05:48 -0600 Subject: [PATCH 41/45] Update docs/source/plotting_scripts/maps.py Co-authored-by: Patrick Brown <25125211+patrickbrown4@users.noreply.github.com> --- docs/source/plotting_scripts/maps.py | 40 +++++++++------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/docs/source/plotting_scripts/maps.py b/docs/source/plotting_scripts/maps.py index bc1a68e6e..9de2f0ab4 100644 --- a/docs/source/plotting_scripts/maps.py +++ b/docs/source/plotting_scripts/maps.py @@ -239,34 +239,20 @@ np.array([row.geometry.centroid.x, row.geometry.centroid.y]) + np.array(offset.get(level, {}).get(r, (0,0))) ) - if level == 'r': - # adds region-colored label for r - text_artists.append( - ax.annotate( - r, - (x, y), - ha='center', va='center', weight='bold', - size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), - color=colors[r], zorder=1e11, alpha=1, - path_effects=( - [pe.withStroke(linewidth=1.5, foreground='w', alpha=1)] + ## Shade the r labels with the zone color + for i, (_color, _alpha) in enumerate([('k', 1), (colors[r], 0.6)]): + if i == 1 and level != 'r': + continue + text_artists.append(ax.annotate( + (r if level == 'r' else r.replace('_','\n')), + (x, y), + ha='center', va='center', weight='bold', + size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), + color=_color, zorder=1e11+i, alpha=_alpha, + path_effects=( + [pe.withStroke(linewidth=1.5, foreground='w', alpha=(1 if i == 0 else 0))] ), - ) - ) - else: - # adds black label for other levels - text_artists.append( - ax.annotate( - (r if level == 'r' else r.replace('_','\n')), - (x, y), - ha='center', va='center', weight='bold', - size={'r':7, 'hurdlereg':7, 'st':10}.get(level,11), - color='k', zorder=1e11, alpha=1, - path_effects=( - [pe.withStroke(linewidth=1.5, foreground='w', alpha=1)] - ), - ) - ) + )) adjust_text(text_artists, ax=ax, avoid_self=False, ensure_inside_axes=True) text_artists = [] From 02d01af48d98cee41f7df397798af142789c1e90 Mon Sep 17 00:00:00 2001 From: Claire Halloran Date: Thu, 8 Oct 2026 16:06:50 -0600 Subject: [PATCH 42/45] Update reeds/reedsplots.py Co-authored-by: Patrick Brown <25125211+patrickbrown4@users.noreply.github.com> --- reeds/reedsplots.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index cfc71840d..d39e43fae 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -6174,9 +6174,8 @@ def map_outage_days( df=dfplot, ax=_ax, column='outage_pct', - fmt="{:.0f}", text_kwargs={'fontsize': fontsize}, - pe_kwargs={'linewidth': 1.4, 'foreground': 'w', 'alpha': 0.7}, + pe_kwargs={'linewidth': 1.4}, ) _ax.axis('off') ## Formatting From 50e4b3ffe7941c48b81d9531be9616510a806f79 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Fri, 9 Oct 2026 11:16:11 -0600 Subject: [PATCH 43/45] Add `label_regions` kwarg for some maps --- postprocessing/single_case_plots.py | 9 ++++++++- reeds/reedsplots.py | 15 ++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/postprocessing/single_case_plots.py b/postprocessing/single_case_plots.py index 7df41f734..7c44145cb 100644 --- a/postprocessing/single_case_plots.py +++ b/postprocessing/single_case_plots.py @@ -289,6 +289,9 @@ try: f,ax = reedsplots.map_capacity_techs( case, year=year, ncols=ncols, vmax=vmax, cmap=cmap, + label_regions = ( + True if len(reeds.inputs.parse_regions(case))<=100 else False + ), ) savename = f'map_capacity-{year}-{vmax}.png' if write: @@ -517,7 +520,11 @@ if int(sw.GSw_H2): plt.close() f,ax = reedsplots.map_h2_capacity( - case=case, year=year, cmap=cmap, wscale_h2=wscale_h2) + case=case, year=year, cmap=cmap, wscale_h2=wscale_h2, + label_regions = ( + True if len(reeds.inputs.parse_regions(case))<=100 else False + ), + ) savename = f'map_h2_capacity-{sw.endyear}.png' if write: plt.savefig(os.path.join(savepath, savename)) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index d39e43fae..c70c4c922 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -2719,6 +2719,7 @@ def map_capacity_techs( ncols=4, vmax='shared', cmap=cmocean.cm.rain, + label_regions=True, ): """ techs: list of technologies to plot, or 'aggregation' to plot all aggregated technologies @@ -2772,7 +2773,8 @@ def map_capacity_techs( 'label': '{} [GW]'.format(tech), } ) - label_region_value(dfplot, ax=ax[coords[tech]], column='GW', text_kwargs={'fontsize':5}) + if label_regions: + label_region_value(dfplot, ax=ax[coords[tech]], column='GW', text_kwargs={'fontsize':5}) ax[coords[tech]].axis('off') ax[0,0].set_title( '{} ({})'.format(os.path.basename(case), year), @@ -4431,7 +4433,7 @@ def map_neue( def map_h2_capacity( case, year=2050, wscale_h2=10, figheight=6, pipescale=0.1, legend_kwds={'shrink':0.6, 'pad':0, 'orientation':'horizontal', 'aspect':12}, - cmap=cmocean.cm.rain, + cmap=cmocean.cm.rain, label_regions=True, ): """ H2 turbines, production (Electrolyzer/SMR), pipelines, and storage @@ -4517,19 +4519,22 @@ def map_h2_capacity( cap_h2turbine.plot( ax=ax[0,0], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Turbines [kT/day]'}}) - label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', text_kwargs={'fontsize':5}) + if label_regions: + label_region_value(cap_h2turbine, ax=ax[0,0], column='kTperday', text_kwargs={'fontsize':5}) ### Electrolyzers if not cap_h2prod.empty: cap_h2prod.plot( ax=ax[0,1], column='kTperday', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Production [kT/day]'}}) - label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', text_kwargs={'fontsize':5}) + if label_regions: + label_region_value(cap_h2prod, ax=ax[0,1], column='kTperday', text_kwargs={'fontsize':5}) ### Storage if not cap_h2prod.empty: cap_storage.plot( ax=ax[1,0], column='h2_storage', cmap=cmap, lw=0, vmin=0, legend=True, legend_kwds={**legend_kwds, **{'label':'Storage [kT]'}}) - label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', text_kwargs={'fontsize':5}) + if label_regions: + label_region_value(cap_storage, ax=ax[1,0], column='h2_storage', text_kwargs={'fontsize':5}) ### Pipelines if not h2_trans_cap.empty: for i,row in h2_trans_cap.iterrows(): From bbf18ba05bfca2c2f4be41ca2ce4fcdb251afc27 Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Fri, 9 Oct 2026 11:16:36 -0600 Subject: [PATCH 44/45] Missing values white in hourly_plots.py --- reeds/input_processing/hourly_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/input_processing/hourly_plots.py b/reeds/input_processing/hourly_plots.py index 957c42fc8..5fc00045f 100644 --- a/reeds/input_processing/hourly_plots.py +++ b/reeds/input_processing/hourly_plots.py @@ -352,7 +352,7 @@ def plot_maps(sw, inputs_case, reeds_path, figpath, periodtype='rep', crs='EPSG: dfdiffs[level].plot( ax=ax[coords[level]], column='cf_diff', cmap=cmaps['cf_diff'], vmin=vm[tech]['cf_diff'][0], vmax=vm[tech]['cf_diff'][1], - lw=0, legend=False, missing_kwds={"color": "lightgrey"}, + lw=0, legend=False, missing_kwds={"color": "white"}, ) dfmap[level].plot(ax=ax[coords[level]], facecolor='none', edgecolor='k', lw=0.2) dfmap[level]['cf_diff_pct'] = (dfdiffs[level].cf_diff * 100).values From cda61aa4855531d386aa5da7271f2dafd60cce3c Mon Sep 17 00:00:00 2001 From: clairehalloran Date: Fri, 9 Oct 2026 13:16:11 -0600 Subject: [PATCH 45/45] Remove `opt_single_decimal` arg --- reeds/reedsplots.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index c70c4c922..ae0758946 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -1377,7 +1377,7 @@ def map_net_imports( ) label_region_value( df, ax=ax[coords[year]], column='net_import', - opt_single_decimal=False, text_kwargs={'fontsize':5}, + fmt='{:.0f}', text_kwargs={'fontsize':5}, ) ## Formatting ax[coords[year]].set_title(year, y=0.9) @@ -6262,8 +6262,7 @@ def label_region_value( df, ax, column, - opt_single_decimal=True, - fmt='{:.0f}', + fmt='{auto}', text_kwargs:None|dict=None, pe_kwargs:None|dict=None, ): @@ -6282,9 +6281,9 @@ def label_region_value( if isinstance(value, (int, float, complex)): if not np.isfinite(value): continue - if opt_single_decimal: - decimals = 0 if ((abs(value) >= 1) or abs(value) < 0.05) else 1 - fmt = f"{{:.{decimals}f}}" + if '{auto}' in fmt: + decimals = 0 if ((abs(value) >= 1) or abs(value) < 0.05) else 1 + fmt = fmt.replace('{auto}', f"{{:.{decimals}f}}") text_artists.append( ax.annotate( (fmt.format(value) if not isinstance(value, str) else value), @@ -6621,8 +6620,7 @@ def map_stressors( ) label_region_value( df=dflevel, ax=ax[1,0], column='load_rank', - opt_single_decimal=False, fmt='{:.0f}%', - pe_kwargs={'linewidth':2.0, 'alpha':0.8}, + fmt='{:.0%}', pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[1,0].set_title('Demand', y=0.9) plots.addcolorbarhist( @@ -6637,8 +6635,7 @@ def map_stressors( ) label_region_value( df=dflevel, ax=ax[1,col], column=f'{tech}_rank', - opt_single_decimal=False, fmt='{:.0f}%', - pe_kwargs={'linewidth':2.0, 'alpha':0.8}, + fmt='{:.0%}', pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[1,col].set_title(labels.get(tech,tech), y=0.9) plots.addcolorbarhist( @@ -6652,8 +6649,7 @@ def map_stressors( ) label_region_value( df=dflevel, ax=ax[1,3], column='temperature_rank', - opt_single_decimal=False, fmt='{:.0f}%', - pe_kwargs={'linewidth':2.0, 'alpha':0.8}, + fmt='{:.0%}', pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[1,3].set_title('Temperature', y=0.9) plots.addcolorbarhist( @@ -6671,7 +6667,7 @@ def map_stressors( dflevel[f'outage_{tech}'] = outage_region.loc[(y,m,d), tech] label_region_value( df=dflevel, ax=ax[2,col], column=f'outage_{tech}', - opt_single_decimal=False, fmt='{:.0f}%', + fmt='{:.0%}', pe_kwargs={'linewidth':2.0, 'alpha':0.8}, ) ax[2,col].set_title(label, y=0.9)