Create summary CSV files for all scenario runs including costs, capacities, capacity factors, curtailment, energy balances, prices and other metrics.
calculate_co2_balance(n, label, co2_balance, withdrawal_stores=['CO2 capture'])
calc the co2 balance [DOES NOT INCLUDE EMISSION GENERATING LINKSs] Args: n (pypsa.Network): the network object withdrawal_stores (list, optional): names of stores. Defaults to ["CO2 capture"]. label (str): the label for the column co2_balance (pd.DataFrame): the df to update
Returns: |
|
---|
Source code in workflow/scripts/make_summary.py
def calculate_co2_balance(
n: pypsa.Network, label: str, co2_balance: pd.DataFrame, withdrawal_stores=["CO2 capture"]
) -> pd.DataFrame:
"""calc the co2 balance [DOES NOT INCLUDE EMISSION GENERATING LINKSs]
Args:
n (pypsa.Network): the network object
withdrawal_stores (list, optional): names of stores. Defaults to ["CO2 capture"].
label (str): the label for the column
co2_balance (pd.DataFrame): the df to update
Returns:
tuple[float,float,float]: balance,
"""
# year *(assumes one planning year intended),
year = int(np.round(n.snapshots.year.values.mean(), 0))
# emissions from generators (from fneumann course)
emissions = (
n.generators_t.p
/ n.generators.efficiency
* n.generators.carrier.map(n.carriers.co2_emissions)
) # t/h
emissions_carrier = (
(n.snapshot_weightings.generators @ emissions).groupby(n.generators.carrier).sum()
)
# format and drop 0 values
emissions_carrier = emissions_carrier.where(emissions_carrier > 0).dropna()
emissions_carrier.rename(year, inplace=True)
emissions_carrier = emissions_carrier.to_frame()
# CO2 withdrawal
stores = n.stores_t.e.T.groupby(n.stores.carrier).sum()
co2_stores = stores.index.intersection(withdrawal_stores)
co2_withdrawal = stores.iloc[:, -1].loc[co2_stores] * -1
co2_withdrawal.rename(year, inplace=True)
co2_withdrawal = co2_withdrawal.to_frame()
year_balance = pd.concat([emissions_carrier, co2_withdrawal])
# combine with previous
co2_balance = co2_balance.reindex(year_balance.index.union(co2_balance.index))
co2_balance.loc[year_balance.index, label] = year_balance[year]
return co2_balance
calculate_peak_dispatch(n, label, supply)
Calculate the MAX dispatch of each component at the buses aggregated by carrier.
Parameters: |
|
---|
Returns: |
|
---|
Source code in workflow/scripts/make_summary.py
def calculate_peak_dispatch(n: pypsa.Network, label: str, supply: pd.DataFrame) -> pd.DataFrame:
"""Calculate the MAX dispatch of each component at the buses aggregated by
carrier.
Args:
n (pypsa.Network): the network object
label (str): the labe representing the pathway
supply (pd.DataFrame): supply energy balance (empty df)
Returns:
pd.DataFrame: updated supply DF
"""
sup_ = n.statistics.supply(
groupby=pypsa.statistics.get_carrier_and_bus_carrier, aggregate_time="max"
)
supply_reordered = sup_.reorder_levels([2, 0, 1])
supply_reordered.sort_index(inplace=True)
supply[label] = supply_reordered
return supply
calculate_supply_energy(n, label, supply_energy)
Calculate the total energy supply/consuption of each component at the buses aggregated by carrier.
Parameters: |
|
---|
Returns: |
|
---|
Source code in workflow/scripts/make_summary.py
def calculate_supply_energy(
n: pypsa.Network, label: str, supply_energy: pd.DataFrame
) -> pd.DataFrame:
"""Calculate the total energy supply/consuption of each component at the buses
aggregated by carrier.
Args:
n (pypsa.Network): the network object
label (str): the labe representing the pathway
supply_energy (pd.DataFrame): supply energy balance (empty df)
Returns:
pd.DataFrame: updated supply energy balance
"""
eb = n.statistics.energy_balance(groupby=pypsa.statistics.get_carrier_and_bus_carrier)
# fragile
eb_reordered = eb.reorder_levels([2, 0, 1])
eb_reordered.sort_index(inplace=True)
eb_reordered.rename(index={"AC": "transmission losses"}, level=2, inplace=True)
supply_energy[label] = eb_reordered
return supply_energy
calculate_weighted_prices(n, label, weighted_prices)
Demand-weighed prices for stores and loads. For stores if withdrawal is zero, use supply instead. Args: n (pypsa.Network): the network object label (str): the label representing the pathway (not needed, refactor) weighted_prices (pd.DataFrame): the dataframe to write to (not needed, refactor)
Returns: |
|
---|
Source code in workflow/scripts/make_summary.py
def calculate_weighted_prices(
n: pypsa.Network, label: str, weighted_prices: pd.DataFrame
) -> pd.DataFrame:
"""Demand-weighed prices for stores and loads.
For stores if withdrawal is zero, use supply instead.
Args:
n (pypsa.Network): the network object
label (str): the label representing the pathway (not needed, refactor)
weighted_prices (pd.DataFrame): the dataframe to write to (not needed, refactor)
Returns:
pd.DataFrame: updated weighted_prices
"""
entries = pd.Index(["electricity", "heat", "H2", "CO2 capture", "gas", "biomass"])
weighted_prices = weighted_prices.reindex(entries)
# loads
loads = (
n.statistics.revenue(comps="Load", groupby=pypsa.statistics.get_bus_carrier)
/ n.statistics.withdrawal(comps="Load", groupby=pypsa.statistics.get_bus_carrier)
* -1
)
loads.rename(index={"AC": "electricity"}, inplace=True)
# stores
w = n.statistics.withdrawal(comps="Store")
# biomass stores have no withdrawal for some reason
w[w == 0] = n.statistics.supply(comps="Store")[w == 0]
weighted_prices[label] = pd.concat([loads, n.statistics.revenue(comps="Store") / w])
return weighted_prices