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
|
use_electrolysis_waste_heat: 0.25
|
||||||
electricity_transmission_grid: true
|
electricity_transmission_grid: true
|
||||||
electricity_distribution_grid: true
|
electricity_distribution_grid: true
|
||||||
electricity_distribution_grid_cost_factor: 1.0
|
|
||||||
electricity_grid_connection: true
|
electricity_grid_connection: true
|
||||||
transmission_efficiency:
|
transmission_efficiency:
|
||||||
DC:
|
DC:
|
||||||
@ -871,7 +870,12 @@ clustering:
|
|||||||
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#adjustments
|
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#adjustments
|
||||||
adjustments:
|
adjustments:
|
||||||
electricity: false
|
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
|
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solving
|
||||||
solving:
|
solving:
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
,Unit,Values,Description
|
,Unit,Values,Description
|
||||||
adjustments,,,
|
adjustments,,,
|
||||||
-- electricity,bool or dict,,"Parameter adjustments for capital cost, marginal cost, and maximum capacities of carriers. Applied in :mod:`prepare_network.`"
|
-- electricity,bool or dict,,Parameter adjustments applied in :mod:`prepare_network.`
|
||||||
-- -- {attr},,,"Attribute can be ``e_nom_opt``, ``p_nom_opt``, ``marginal_cost`` or ``capital_cost``"
|
-- -- factor,,,Multiply original value with given factor
|
||||||
-- -- -- {carrier},float,per-unit,"Any carrier of the network to which parameter adjustment factor should be applied."
|
-- -- absolute,,,Set attribute to absolute value
|
||||||
-- sector,bool or dict,,"Parameter adjustments for capital cost, marginal cost, and maximum capacities of carriers. Applied in :mod:`prepare_sector_network.`"
|
-- -- -- {component},,,PyPSA component in :mod:`prepare_network.`
|
||||||
-- -- {attr},,,"Attribute can be ``e_nom_opt``, ``p_nom_opt``, ``marginal_cost`` or ``capital_cost``"
|
-- -- -- -- {carrier},,,Any carrier of the network to which parameter adjustment factor should be applied.
|
||||||
-- -- -- {carrier},float,per-unit,"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
|
.. 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, ...
|
* 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):
|
def aggregate_e_nom(n):
|
||||||
return pd.concat(
|
return pd.concat(
|
||||||
[
|
[
|
||||||
|
@ -64,6 +64,7 @@ import pandas as pd
|
|||||||
import pypsa
|
import pypsa
|
||||||
from _helpers import (
|
from _helpers import (
|
||||||
configure_logging,
|
configure_logging,
|
||||||
|
get,
|
||||||
set_scenario_config,
|
set_scenario_config,
|
||||||
update_config_from_wildcards,
|
update_config_from_wildcards,
|
||||||
)
|
)
|
||||||
@ -75,26 +76,43 @@ idx = pd.IndexSlice
|
|||||||
logger = logging.getLogger(__name__)
|
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:
|
if not adjustments:
|
||||||
return
|
return
|
||||||
|
for modification in adjustments.keys():
|
||||||
for attr, carrier_factor in adjustments.items():
|
modify_attribute(n, adjustments, investment_year, modification)
|
||||||
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}")
|
|
||||||
|
|
||||||
|
|
||||||
def add_co2limit(n, co2limit, Nyears=1.0):
|
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)
|
n.links["p_nom_max"] = n.links.p_nom_max.clip(upper=p_nom_max_set)
|
||||||
|
|
||||||
|
|
||||||
|
# %%
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if "snakemake" not in globals():
|
if "snakemake" not in globals():
|
||||||
from _helpers import mock_snakemake
|
from _helpers import mock_snakemake
|
||||||
|
@ -19,6 +19,7 @@ import pypsa
|
|||||||
import xarray as xr
|
import xarray as xr
|
||||||
from _helpers import (
|
from _helpers import (
|
||||||
configure_logging,
|
configure_logging,
|
||||||
|
get,
|
||||||
set_scenario_config,
|
set_scenario_config,
|
||||||
update_config_from_wildcards,
|
update_config_from_wildcards,
|
||||||
)
|
)
|
||||||
@ -241,38 +242,6 @@ def determine_emission_sectors(options):
|
|||||||
return sectors
|
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(
|
def co2_emissions_year(
|
||||||
countries, input_eurostat, options, emissions_scope, input_co2, 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 pop_layout?
|
||||||
# TODO options?
|
# 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
|
nodes = pop_layout.index
|
||||||
|
|
||||||
n.madd(
|
n.madd(
|
||||||
@ -1339,7 +1302,7 @@ def insert_electricity_distribution_grid(n, costs):
|
|||||||
carrier="electricity distribution grid",
|
carrier="electricity distribution grid",
|
||||||
efficiency=1,
|
efficiency=1,
|
||||||
lifetime=costs.at["electricity distribution grid", "lifetime"],
|
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
|
# 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
|
n, snakemake.input["egs_potentials"], snakemake.input["egs_overlap"], costs
|
||||||
)
|
)
|
||||||
|
|
||||||
maybe_adjust_costs_and_potentials(n, snakemake.params["adjustments"])
|
|
||||||
|
|
||||||
if options["gas_distribution_grid"]:
|
if options["gas_distribution_grid"]:
|
||||||
insert_gas_distribution_costs(n, costs)
|
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:
|
if options.get("cluster_heat_buses", False) and not first_year_myopic:
|
||||||
cluster_heat_buses(n)
|
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)))
|
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
|
||||||
|
|
||||||
sanitize_carriers(n, snakemake.config)
|
sanitize_carriers(n, snakemake.config)
|
||||||
|
Loading…
Reference in New Issue
Block a user