# Python Post-processing Noteboook
from fileinput import filename

from utils import *

Load data#

This loads results data from the results folder

RESULTS_FOLDER = 'simulations_run_20250411_122833'

# RESULTS_FOLDER = os.path.join('..', 'output', RESULTS_FOLDER)
RESULTS_FOLDER = os.path.join(RESULTS_FOLDER)

SCENARIOS_RENAME = {
    'BAU': 'BAU',
    'baseline': 'baseline',
    'Retrade': 'SAPP Interco Plan',
    'DemandTransmissionIFCEnergySecurity': 'IFC Energy Security',
    'DemandTransmissionIFC': 'IFC Baseline',
}

RESULTS_FOLDER, GRAPHS_FOLDER, dict_specs, epm_input, epm_results, mapping_gen_fuel = process_simulation_results(
    RESULTS_FOLDER, SCENARIOS_RENAME=SCENARIOS_RENAME, folder='')
pInterconUtilizationExtExp not in epm_results.keys().
AdditiononalCapacity_trans not in epm_results.keys().
pInterconUtilizationExtImp not in epm_results.keys().
pYearlyTrade not in epm_results.keys().
Interchange not in epm_results.keys().
pInterchangeExtExp not in epm_results.keys().
interchanges not in epm_results.keys().
pCurtailedVRET not in epm_results.keys().
InterconUtilization not in epm_results.keys().
pCurtailedStoHY not in epm_results.keys().
InterchangeExtImp not in epm_results.keys().
pNPVByYear not in epm_results.keys().
pHourlyFlow not in epm_results.keys().
pFuelDispatch not in epm_results.keys().
annual_line_capa not in epm_results.keys().
pPlantFuelDispatch not in epm_results.keys().
pFuelDispatch not found in epm_dict
pPlantFuelDispatch not found in epm_dict

Create geographical zone data#

Update geojson_to_epm.csv in postprocessing/static/ to define zones.

Required Columns:

  • Geojson: Zone name (must match countries.geojson).

  • EPM: Corresponding zone name in the EPM model.

Optional (for country subdivisions):

  • region: North, South, East, or West.

  • country: Country name (must match countries.geojson).

  • division: 'NS' (North-South) or 'EW' (East-West).

Example:

Geojson

EPM

region

country

division

South Africa

South_Africa

Namibia

Namibia

Democratic Republic of the Congo - North

DRC

north

Democratic Republic of the Congo

NS

Democratic Republic of the Congo - South

DRC_South

south

Democratic Republic of the Congo

NS

Democratic Republic of the Congo is split into North/South; other zones remain the same.

zone_map, geojson_to_epm = get_json_data(epm_results=epm_results, dict_specs=dict_specs)

epm_to_geojson = {v: k for k, v in geojson_to_epm.items()}  # Reverse dictionary

zone_map, centers = create_zonemap(zone_map, map_geojson_to_epm=geojson_to_epm)

Plots#

Transmission maps#

# Plotting exchanges with arrows

tmp = epm_results['pInterchange'].copy()
df_congested = epm_results['pCongested'].copy().rename(columns={'value': 'congestion'})
tmp = tmp.merge(df_congested, on=['scenario', 'year', 'zone', 'z2'], how='left')
tmp = tmp.fillna(0)
tmp_rev = tmp.copy().rename(columns={'zone': 'z2', 'z2': 'zone'})
tmp_rev['value'] = - tmp_rev['value']
df_combined = pd.concat([tmp, tmp_rev], ignore_index=True)
df_combined = df_combined.groupby(['scenario', 'year', 'zone', 'z2'])[['value', 'congestion']].sum().reset_index()
df_net = df_combined[df_combined['value'] > 0]
df_net = df_net.rename(columns={'zone': 'zone_from', 'z2': 'zone_to'})

scenario = 'baseline'
# scenario = 'SAPP Interco Plan'
# scenario = 'IFC Energy Security'

year = 2035

filename = None

make_interconnection_map(zone_map, df_net, centers, filename=filename, year=year, scenario=scenario,
                         label_yoffset=0.01, label_xoffset=-0.05, label_fontsize=10, show_labels=False, plot_colored_countries=False,
                         min_display_value=100, column='value', plot_lines=False, format_y = lambda y, _: '{:.0f}'.format(y), offset=-1.5,
                         min_line_width=0.7, max_line_width=1.5, arrow_linewidth=0.1, mutation_scale=20, color_col='congestion')
/Users/celia/Documents/WorldBank/Energy_planning/EPM/epm/postprocessing/utils.py:3716: UserWarning: Setting the 'color' property will override the edgecolor or facecolor properties.
  arrow = FancyArrowPatch((start_x, start_y), (end_x, end_y),
../_images/aeb7479d5039f9c7ddd0f9d5a7a1cf78bf39fc231b7656406f4f3fd717b5e759.png
# Plotting transmission lines capacity

capa_transmission = epm_results['pAnnualTransmissionCapacity'].copy()
utilization_transmission = epm_results['pInterconUtilization'].copy()
utilization_transmission['value'] = utilization_transmission['value'] * 100  # percentage
utilization_transmission = keep_max_direction(utilization_transmission)
transmission_data = capa_transmission.rename(columns={'value': 'capacity'}).merge(utilization_transmission.rename
                                                                                  (columns={'value': 'utilization'}),  on=['scenario', 'zone', 'z2', 'year'])
transmission_data = transmission_data.rename(columns={'zone': 'zone_from', 'z2': 'zone_to'})

filename = None

scenario = 'baseline'
year = 2035

make_interconnection_map(zone_map, transmission_data, centers, filename=filename, year=year, scenario=scenario,
                         label_yoffset=0.01, label_xoffset=-0.05, label_fontsize=10, show_labels=False,
                         min_display_value=100, column='capacity')
../_images/b28b7071a330bcc50763a2f163357b5c944636a11064e80813e904858a1b0654.png
# Plotting transmission lines total utilization

filename = None
scenario = 'baseline'

year = 2035

make_interconnection_map(zone_map, transmission_data, centers, year=year, scenario=scenario,
                         column='utilization',
                         min_capacity=0.01, label_yoffset=0.01, label_xoffset=-0.05,
                         label_fontsize=10, show_labels=False, min_display_value=50,
                         format_y=lambda y, _: '{:.0f} %'.format(y), filename=filename,
                         title='Transmission utilization (%)', show_arrows=True, arrow_offset_ratio=0.4,
                         arrow_size=25, plot_colored_countries=False)
../_images/71c0baa8b6a31e65a00070720c872a2a3c4d17006c385b546e5bedd08d40e7e5.png

Capacity#

pCapacityByFuel = epm_results['pCapacityByFuel'].copy()
# pCapacityByFuel = pCapacityByFuel.loc[pCapacityByFuel.zone.isin(list(geojson_to_epm.values()))]
filename = None
year = 2035
scenario = 'baseline'

make_capacity_mix_map(zone_map, pCapacityByFuel, dict_specs['colors'], centers, year=year, region='SAPP', scenario=scenario,
                      filename=filename, map_epm_to_geojson=geojson_to_epm, figsize=(12,8), bbox_to_anchor=(0.7, 0.5), loc='center left',  pie_sizing=True,  min_size=2, max_size=2, percent_cap=10)
/Users/celia/Documents/WorldBank/Energy_planning/EPM/epm/postprocessing/utils.py:3480: UserWarning: Geometry is in a geographic CRS. Results from 'area' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.

  region_sizes['area'] = region_sizes.geometry.area
../_images/3831964b3e5b1e07f8f0148d758a1343241afb812364fa4c1d81f5716dc7334b.png
# Capacities as bar plots

df = epm_results['pCapacityByFuel'].copy()
# df = df.loc[df.scenario.isin(['BAU', 'SAPP Interco Plan'])]
df = df.loc[df.scenario.isin(['baseline', 'SAPP Interco Plan'])]
df['value'] = df['value'] / 1e3
filename = None
make_stacked_bar_subplots(df, filename, dict_specs['colors'], column_stacked='fuel',
                          column_xaxis='year', 
                          column_value='value', column_multiple_bars='scenario',
                          select_xaxis=[2025,2030,2035],
                          format_y=lambda y, _: '{:.0f} GW'.format(y), rotation=45, cap=2,
                          format_label="{:.0f}", figsize=(8,4))
../_images/a2e4d8615a07ca51706c3148f24dda1d45a28ab00f86605e4b33c54732fb3be1.png
# Bar plot of differences in capacities across scenarios per country

df = epm_results['pCapacityByFuel'].copy()
df = df.loc[df.scenario.isin(['baseline', 'SAPP Interco Plan'])]
# df = df.loc[(df.year == 2035)]
# df = df.loc[(df.scenario == 'Retrade')]
scenario_reference = 'baseline'

if scenario_reference in df['scenario'].unique() and len(df['scenario'].unique()) > 1:
    df_diff = df.pivot_table(index=['zone', 'year', 'fuel'], columns='scenario', values='value', fill_value=0)
    df_diff = (df_diff.T - df_diff[scenario_reference]).T
    df_diff = df_diff.drop(scenario_reference, axis=1)
    df_diff = df_diff.stack().reset_index()
    df_diff.rename(columns={0: 'value'}, inplace=True)

    filename = None # Only for display in the notebook
    make_stacked_bar_subplots(df_diff, filename, dict_colors=dict_specs['colors'], column_stacked='fuel',
                              column_xaxis='zone', column_value='value', column_multiple_bars='year',
                              format_y=lambda y, _: '{:.0f} MW'.format(y), annotate=False, rotation=45,
                              cols_per_row=5, figsize=(15,4), hspace=0.7, show_total=False)
../_images/001a416aa0ee34a3c089b21b2f6497277fb83a3e41593d4ed75d0768b1d44f26.png
# Bar plot of capacities per country

df = epm_results['pCapacityByFuel'].copy()
df = df.loc[df.scenario.isin(['baseline', 'SAPP Interco Plan'])]
# df = df.loc[(df.year == 2035)]
df = df.loc[(df.scenario == 'SAPP Interco Plan')]
df = df.loc[(df.zone != 'South_Africa')]  # removing South Africa which is one order of magnitude above other countries
# scenario_reference = 'BAU'

filename = None # Only for display in the notebook
make_stacked_bar_subplots(df, filename, dict_colors=dict_specs['colors'], column_stacked='fuel',
                          column_xaxis='zone', column_value='value', column_multiple_bars='year',
                          format_y=lambda y, _: '{:.0f} MW'.format(y), annotate=False, rotation=45,
                          cols_per_row=5, figsize=(18,4), hspace=0.7, show_total=False)
../_images/1d1ad0e44c6bd81d4ca9da92ae6eba9d1d10ecbe4cfe1b0ab663c60241818db5.png

Energy#

pEnergyByFuel = epm_results['pEnergyByFuel'].copy()

filename = None
year = 2025
scenario = 'SAPP Interco Plan'

make_capacity_mix_map(zone_map, pEnergyByFuel, dict_specs['colors'], centers, year=year, region='SAPP', scenario=scenario,
                      filename=filename, map_epm_to_geojson=geojson_to_epm, figsize=(12,8), bbox_to_anchor=(0.7, 0.5), loc='center left',  pie_sizing=True,  min_size=2, max_size=2, percent_cap=10)
/Users/celia/Documents/WorldBank/Energy_planning/EPM/epm/postprocessing/utils.py:3480: UserWarning: Geometry is in a geographic CRS. Results from 'area' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.

  region_sizes['area'] = region_sizes.geometry.area
../_images/eadcf6f7528870b10183ec4f314c2466939403ba0a8c8c6923176d00f9b8db55.png

System costs#

df = epm_results['pSummary'].copy()
df = df.loc[df.scenario.isin(['baseline', 'SAPP Interco Plan'])]  # choosing which scenarios to include in the plot
df = df.set_index(['scenario', 'attribute']).squeeze().unstack(['attribute'])
columns_reserves = ['Sys Spinning Reserve violation: $m', 'Sys Planning Reserve violation: $m',
                    'Zonal Spinning Reserve violation: $m', 'Zonal Planning Reserve violation: $m']
columns_reserves = [c for c in columns_reserves if c in df.columns]
df['Unmet reserve cost: $m'] = sum(df[c] for c in columns_reserves)
df = df.stack().reset_index().rename(columns={0: 'value'})

# choosing which attributes to include in the plot
df = df.loc[df.attribute.isin(["Annualized capex: $m", "Additional transmission costs: $m",
                               "Fixed O&M: $m", "Variable O&M: $m", "Fuel cost: $m", "Unmet demand cost: $m",
                               "Unmet reserve cost: $m", "Spinning reserve costs: $m", "Excess Generation Costs: $m"])]

scenario_ref = 'baseline'  # scenario used for comparison
if scenario_ref in df['scenario'].unique() and len(df['scenario'].unique()) > 1:
    df_diff = df.pivot_table(index=['attribute'], columns='scenario', values='value', fill_value=0)
    df_diff = (df_diff.T - df_diff[scenario_ref]).T
    df_diff = df_diff.drop(scenario_ref, axis=1)
    df_diff = df_diff.stack().reset_index()
    df_diff.rename(columns={0: 'value'}, inplace=True)

    filename = None # Only for display in the notebook
    make_stacked_bar_subplots(df_diff, filename, dict_colors=dict_specs['colors'], column_stacked='attribute',
                              column_xaxis=None, column_value='value', column_multiple_bars='scenario',
                              format_y=lambda y, _: '{:.0f} $m'.format(y), annotate=False, rotation=45,
                              cols_per_row=4, figsize=(5,4), hspace=0.7, show_total=False)
../_images/9f89365d0c797d426772b021d71afcd5adbfd834c580d179909999ca7d2ffb74.png
# Variation in costs across scenarios for each country

df = epm_results['pCostSummary'].copy()
df = df.loc[df.scenario.isin(['baseline', 'SAPP Interco Plan'])]  # choosing which scenarios to include in the plot

# choosing which attributes to include in the plot
costs_comparison = ["Annualized capex: $m", "Fixed O&M: $m", "Variable O&M: $m", "Transmission additions: $m",
                    "Spinning Reserve costs: $m", "Unmet demand costs: $m", "Excess generation: $m",
                    "VRE curtailment: $m"]
df = df.loc[df.attribute.isin(costs_comparison)]
df = df.loc[(df.year == 2035)]

scenario_reference = 'baseline'  # scenario used for comparison
if scenario_reference in df['scenario'].unique() and len(df['scenario'].unique()) > 1:
    df_diff = df.pivot_table(index=['zone', 'year', 'attribute'], columns='scenario', values='value', fill_value=0)
    df_diff = (df_diff.T - df_diff[scenario_reference]).T
    df_diff = df_diff.drop(scenario_reference, axis=1)
    df_diff = df_diff.stack().reset_index()
    df_diff.rename(columns={0: 'value'}, inplace=True)

    filename = None
    make_stacked_bar_subplots(df_diff, filename, dict_colors=dict_specs['colors'], column_stacked='attribute',
                              column_xaxis='zone', column_value='value', column_multiple_bars='scenario',
                              format_y=lambda y, _: '{:.0f} $m'.format(y), annotate=False, rotation=45,
                              cols_per_row=4, figsize=(16,4), hspace=0.7, show_total=True)
../_images/ed1bd561729a9ac8af0a5ae9feddce2c02465d4eaf0da1c3d17bfaef8a24fea1.png

Dispatch#

pDispatch = epm_results['pDispatch'].copy()
pPlantDispatch = epm_results['pPlantDispatch'].copy()

dfs_to_plot_area = {
    'pPlantDispatch': filter_dataframe(pPlantDispatch, {'attribute': ['Generation']}),
    'pDispatch': filter_dataframe(pDispatch, {'attribute': ['Unmet demand', 'Exports', 'Imports', 'Storage Charge']})
}

dfs_to_plot_line = {
    'pDispatch': filter_dataframe(pDispatch, {'attribute': ['Demand']})
}

seasons = pPlantDispatch.season.unique()
days = pPlantDispatch.day.unique()

select_time = {'season': ['Q1', 'Q2', 'Q3', 'Q4'], 'day': days}
# select_time = {'season': ['Q4'], 'day': days}
year = 2035
scenario = 'SAPP Interco Plan'
zone = 'Angola'

filename = None

make_complete_fuel_dispatch_plot(dfs_area=dfs_to_plot_area, dfs_line=dfs_to_plot_line, dict_colors=dict_specs['colors'],
                                 zone=zone, year=year, scenario=scenario, select_time=select_time, filename=filename,  figsize=(16,5),
                                 reorder_dispatch=['Hydro', 'Solar', 'Wind', 'Nuclear', 'Coal', 'Oil', 'Gas', 'Imports', 'Battery Storage'], stacked=True, bottom=None)
../_images/295788d65e7f63babc5c92ae1a24f99bb2c4403f03d0fd2eea10d003efa77e2a.png