add option to vary parameter (#1244)
* 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 b7154f046954bd6de34c2910f3f9f52b44d5f233. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Fabian Neumann <fabian.neumann@outlook.de>
This commit is contained in:
parent
e8e0833e3a
commit
1b97a16bfa
@ -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:
|
||||
@ -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:
|
||||
|
@ -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.
|
||||
|
|
@ -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, ...
|
||||
|
||||
|
@ -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(
|
||||
[
|
||||
|
@ -64,6 +64,7 @@ import pandas as pd
|
||||
import pypsa
|
||||
from _helpers import (
|
||||
configure_logging,
|
||||
get,
|
||||
set_scenario_config,
|
||||
update_config_from_wildcards,
|
||||
)
|
||||
@ -75,26 +76,43 @@ idx = pd.IndexSlice
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def maybe_adjust_costs_and_potentials(n, adjustments):
|
||||
def modify_attribute(n, adjustments, investment_year, modification="factor"):
|
||||
if not adjustments[modification]:
|
||||
return
|
||||
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
|
||||
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 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
|
||||
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 modification in adjustments.keys():
|
||||
modify_attribute(n, adjustments, investment_year, modification)
|
||||
|
||||
|
||||
def add_co2limit(n, co2limit, Nyears=1.0):
|
||||
@ -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
|
||||
|
@ -19,6 +19,7 @@ import pypsa
|
||||
import xarray as xr
|
||||
from _helpers import (
|
||||
configure_logging,
|
||||
get,
|
||||
set_scenario_config,
|
||||
update_config_from_wildcards,
|
||||
)
|
||||
@ -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
|
||||
):
|
||||
@ -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(
|
||||
@ -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
|
||||
@ -4794,8 +4757,6 @@ if __name__ == "__main__":
|
||||
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)
|
||||
|
||||
@ -4821,6 +4782,10 @@ if __name__ == "__main__":
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user