Skip to content

Commit

Permalink
add option to vary parameter (#1244)
Browse files Browse the repository at this point in the history
* add option to vary parameter

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* remove logger.info

* adjust maybe_adjust_costs_and_potentials

* update configtables

* revert removed cost_factor

* reset build_energy_totals to master

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add release notes

* Revert "revert removed cost_factor"

This reverts commit b7154f0.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Fabian Neumann <[email protected]>
  • Loading branch information
3 people authored Sep 12, 2024
1 parent e8e0833 commit 1b97a16
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 69 deletions.
8 changes: 6 additions & 2 deletions config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,6 @@ sector:
use_electrolysis_waste_heat: 0.25
electricity_transmission_grid: true
electricity_distribution_grid: true
electricity_distribution_grid_cost_factor: 1.0
electricity_grid_connection: true
transmission_efficiency:
DC:
Expand Down Expand Up @@ -871,7 +870,12 @@ clustering:
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#adjustments
adjustments:
electricity: false
sector: false
sector:
factor:
Link:
electricity distribution grid:
capital_cost: 1.0
absolute: false

# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solving
solving:
Expand Down
22 changes: 14 additions & 8 deletions doc/configtables/adjustments.csv
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
,Unit,Values,Description
adjustments,,,
-- electricity,bool or dict,,"Parameter adjustments for capital cost, marginal cost, and maximum capacities of carriers. Applied in :mod:`prepare_network.`"
-- -- {attr},,,"Attribute can be ``e_nom_opt``, ``p_nom_opt``, ``marginal_cost`` or ``capital_cost``"
-- -- -- {carrier},float,per-unit,"Any carrier of the network to which parameter adjustment factor should be applied."
-- sector,bool or dict,,"Parameter adjustments for capital cost, marginal cost, and maximum capacities of carriers. Applied in :mod:`prepare_sector_network.`"
-- -- {attr},,,"Attribute can be ``e_nom_opt``, ``p_nom_opt``, ``marginal_cost`` or ``capital_cost``"
-- -- -- {carrier},float,per-unit,"Any carrier of the network to which parameter adjustment factor should be applied."
,Unit,Values,Description
adjustments,,,
-- electricity,bool or dict,,Parameter adjustments applied in :mod:`prepare_network.`
-- -- factor,,,Multiply original value with given factor
-- -- absolute,,,Set attribute to absolute value
-- -- -- {component},,,PyPSA component in :mod:`prepare_network.`
-- -- -- -- {carrier},,,Any carrier of the network to which parameter adjustment factor should be applied.
-- -- -- -- -- {attr},float,per-unit,Attribute to which parameter adjustment factor should be applied.
-- sector,bool or dict,,Parameter adjustments applied in :mod:`prepare_sector_network.`
-- -- factor,,,Multiply original value with given factor
-- -- absolute,,,Set attribute to absolute value
-- -- -- {component},,,PyPSA component in :mod:`prepare_network.`
-- -- -- -- {carrier},,,Any carrier of the network to which parameter adjustment factor should be applied.
-- -- -- -- -- {attr},Float or dict,per-unit,Attribute to which parameter adjustment factor should be applied. Can be also a dictionary with planning horizons as keys.
1 change: 1 addition & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Release Notes

.. Upcoming Release
* Add function ``modify_attribute`` which allows to adjust every attribute of every PyPSA component either by a multiplication with a factor or setting an absolute value. These adjustments can also depend on the planning horizons and are set in the config under ``adjustments``. The function ``maybe_adjust_costs_and_potentials`` is removed.

* Add technology options for methanol, like electricity production from methanol, biomass to methanol, methanol to kerosene, ...

Expand Down
32 changes: 32 additions & 0 deletions scripts/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,38 @@ def aggregate_p(n):
)


def get(item, investment_year=None):
"""
Check whether item depends on investment year.
"""
if not isinstance(item, dict):
return item
elif investment_year in item.keys():
return item[investment_year]
else:
logger.warning(
f"Investment key {investment_year} not found in dictionary {item}."
)
keys = sorted(item.keys())
if investment_year < keys[0]:
logger.warning(f"Lower than minimum key. Taking minimum key {keys[0]}")
return item[keys[0]]
elif investment_year > keys[-1]:
logger.warning(f"Higher than maximum key. Taking maximum key {keys[0]}")
return item[keys[-1]]
else:
logger.warning(
"Interpolate linearly between the next lower and next higher year."
)
lower_key = max(k for k in keys if k < investment_year)
higher_key = min(k for k in keys if k > investment_year)
lower = item[lower_key]
higher = item[higher_key]
return lower + (higher - lower) * (investment_year - lower_key) / (
higher_key - lower_key
)


def aggregate_e_nom(n):
return pd.concat(
[
Expand Down
55 changes: 37 additions & 18 deletions scripts/prepare_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import pypsa
from _helpers import (
configure_logging,
get,
set_scenario_config,
update_config_from_wildcards,
)
Expand All @@ -75,26 +76,43 @@
logger = logging.getLogger(__name__)


def maybe_adjust_costs_and_potentials(n, adjustments):
if not adjustments:
def modify_attribute(n, adjustments, investment_year, modification="factor"):
if not adjustments[modification]:
return

for attr, carrier_factor in adjustments.items():
for carrier, factor in carrier_factor.items():
# beware if factor is 0 and p_nom_max is np.inf, 0*np.inf is nan
if carrier == "AC": # lines do not have carrier
n.lines[attr] *= factor
change_dict = adjustments[modification]
for c in change_dict.keys():
if c not in n.components.keys():
logger.warning(f"{c} needs to be a PyPSA Component")
continue
for carrier in change_dict[c].keys():
ind_i = n.df(c)[n.df(c).carrier == carrier].index
if ind_i.empty:
continue
comps = {
"p_nom_max": {"Generator", "Link", "StorageUnit"},
"e_nom_max": {"Store"},
"capital_cost": {"Generator", "Link", "StorageUnit", "Store"},
"marginal_cost": {"Generator", "Link", "StorageUnit", "Store"},
}
for c in n.iterate_components(comps[attr]):
sel = c.df.index[c.df.carrier == carrier]
c.df.loc[sel, attr] *= factor
logger.info(f"changing {attr} for {carrier} by factor {factor}")
for parameter in change_dict[c][carrier].keys():
if parameter not in n.df(c).columns:
logger.warning(f"Attribute {parameter} needs to be in {c} columns.")
continue
if investment_year:
factor = get(change_dict[c][carrier][parameter], investment_year)
else:
factor = change_dict[c][carrier][parameter]
if modification == "factor":
logger.info(f"Modify {parameter} of {carrier} by factor {factor} ")
n.df(c).loc[ind_i, parameter] *= factor
elif modification == "absolute":
logger.info(f"Set {parameter} of {carrier} to {factor} ")
n.df(c).loc[ind_i, parameter] = factor
else:
logger.warning(
f"{modification} needs to be either 'absolute' or 'factor'."
)


def maybe_adjust_costs_and_potentials(n, adjustments, investment_year=None):
if not adjustments:
return
for modification in adjustments.keys():
modify_attribute(n, adjustments, investment_year, modification)


def add_co2limit(n, co2limit, Nyears=1.0):
Expand Down Expand Up @@ -300,6 +318,7 @@ def set_line_nom_max(
n.links["p_nom_max"] = n.links.p_nom_max.clip(upper=p_nom_max_set)


# %%
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
Expand Down
47 changes: 6 additions & 41 deletions scripts/prepare_sector_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import xarray as xr
from _helpers import (
configure_logging,
get,
set_scenario_config,
update_config_from_wildcards,
)
Expand Down Expand Up @@ -241,38 +242,6 @@ def determine_emission_sectors(options):
return sectors


def get(item, investment_year=None):
"""
Check whether item depends on investment year.
"""
if not isinstance(item, dict):
return item
elif investment_year in item.keys():
return item[investment_year]
else:
logger.warning(
f"Investment key {investment_year} not found in dictionary {item}."
)
keys = sorted(item.keys())
if investment_year < keys[0]:
logger.warning(f"Lower than minimum key. Taking minimum key {keys[0]}")
return item[keys[0]]
elif investment_year > keys[-1]:
logger.warning(f"Higher than maximum key. Taking maximum key {keys[0]}")
return item[keys[-1]]
else:
logger.warning(
"Interpolate linearly between the next lower and next higher year."
)
lower_key = max(k for k in keys if k < investment_year)
higher_key = min(k for k in keys if k > investment_year)
lower = item[lower_key]
higher = item[higher_key]
return lower + (higher - lower) * (investment_year - lower_key) / (
higher_key - lower_key
)


def co2_emissions_year(
countries, input_eurostat, options, emissions_scope, input_co2, year
):
Expand Down Expand Up @@ -1313,12 +1282,6 @@ def insert_electricity_distribution_grid(n, costs):
# TODO pop_layout?
# TODO options?

cost_factor = options["electricity_distribution_grid_cost_factor"]

logger.info(
f"Inserting electricity distribution grid with investment cost factor of {cost_factor:.2f}"
)

nodes = pop_layout.index

n.madd(
Expand All @@ -1339,7 +1302,7 @@ def insert_electricity_distribution_grid(n, costs):
carrier="electricity distribution grid",
efficiency=1,
lifetime=costs.at["electricity distribution grid", "lifetime"],
capital_cost=costs.at["electricity distribution grid", "fixed"] * cost_factor,
capital_cost=costs.at["electricity distribution grid", "fixed"],
)

# deduct distribution losses from electricity demand as these are included in total load
Expand Down Expand Up @@ -4794,8 +4757,6 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
n, snakemake.input["egs_potentials"], snakemake.input["egs_overlap"], costs
)

maybe_adjust_costs_and_potentials(n, snakemake.params["adjustments"])

if options["gas_distribution_grid"]:
insert_gas_distribution_costs(n, costs)

Expand All @@ -4821,6 +4782,10 @@ def add_enhanced_geothermal(n, egs_potentials, egs_overlap, costs):
if options.get("cluster_heat_buses", False) and not first_year_myopic:
cluster_heat_buses(n)

maybe_adjust_costs_and_potentials(
n, snakemake.params["adjustments"], investment_year
)

n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))

sanitize_carriers(n, snakemake.config)
Expand Down

0 comments on commit 1b97a16

Please sign in to comment.