pypsa-eur/scripts/prepare_network.py

224 lines
7.5 KiB
Python
Raw Normal View History

# coding: utf-8
"""
2019-08-14 09:08:10 +00:00
Prepare PyPSA network for solving according to :ref:`opts` and :ref:`ll`, such as
- adding an annual **limit** of carbon-dioxide emissions,
2019-11-14 14:50:03 +00:00
- adding an exogenous **price** of carbon-dioxide emissions (or other kinds),
2019-08-14 09:08:10 +00:00
- setting an **N-1 security margin** factor for transmission line capacities,
- specifying a limit on the **cost** of transmission expansion,
- specifying a limit on the **volume** of transmission expansion, and
- reducing the **temporal** resolution by averaging over multiple hours.
2019-08-11 09:40:47 +00:00
Relevant Settings
-----------------
2019-08-11 11:17:36 +00:00
.. code:: yaml
costs:
emission_prices:
USD2013_to_EUR2013:
discountrate:
marginal_cost:
capital_cost:
electricity:
co2limit:
max_hours:
.. seealso::
Documentation of the configuration file ``config.yaml`` at
:ref:`costs_cf`, :ref:`electricity_cf`
2019-08-11 09:40:47 +00:00
Inputs
------
2019-08-12 17:01:53 +00:00
- ``data/costs.csv``: The database of cost assumptions for all included technologies for specific years from various sources; e.g. discount rate, lifetime, investment (CAPEX), fixed operation and maintenance (FOM), variable operation and maintenance (VOM), fuel costs, efficiency, carbon-dioxide intensity.
2019-08-11 20:34:18 +00:00
- ``networks/{network}_s{simpl}_{clusters}.nc``: confer :ref:`cluster`
2019-08-11 09:40:47 +00:00
Outputs
-------
Add logging to logfiles to all snakemake workflow scripts. (#102) * Add logging to logfiles to all snakemake workflow scripts. * Fix missing quotation marks in Snakefile. * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * doc: fix _ec_ filenames in docs * Allow logging message format to be specified in config.yaml. * Add logging for Snakemake rule 'retrieve_databundle '. * Add limited logging to STDERR only for retrieve_*.py scripts. * Import progressbar module only on demand. * Fix logging to file and enable concurrent printing to STDERR for most scripts. * Add new 'logging_format' option to Travis CI test config.yaml. * Add missing parenthesis (bug fix) and cross-os compatible paths. * Fix typos in messages. * Use correct log files for logging (bug fix). * doc: fix line references * config: logging_format in all configs * doc: add doc for logging_format * environment: update to powerplantmatching 0.4.3 * doc: update line references for tutorial.rst * Change logging configuration scheme for config.yaml. * Add helper function for doing basic logging configuration. * Add logpath for prepare_links_p_nom rule. * Outsource basic logging configuration for all scripts to _helper submodule. * Update documentation for changed config.yaml structure. Instead of 'logging_level' and 'logging_format', now 'logging' with subcategories is used. * _helpers: Change configure_logging signature.
2019-11-28 07:22:52 +00:00
- ``networks/{network}_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc``: Complete PyPSA network that will be handed to the ``solve_network`` rule.
2019-08-11 20:34:18 +00:00
2019-08-11 09:40:47 +00:00
Description
-----------
2019-08-11 20:34:18 +00:00
.. tip::
The rule :mod:`prepare_all_networks` runs
2019-08-11 20:34:18 +00:00
for all ``scenario`` s in the configuration file
the rule :mod:`prepare_network`.
2019-08-11 20:34:18 +00:00
"""
Add logging to logfiles to all snakemake workflow scripts. (#102) * Add logging to logfiles to all snakemake workflow scripts. * Fix missing quotation marks in Snakefile. * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * doc: fix _ec_ filenames in docs * Allow logging message format to be specified in config.yaml. * Add logging for Snakemake rule 'retrieve_databundle '. * Add limited logging to STDERR only for retrieve_*.py scripts. * Import progressbar module only on demand. * Fix logging to file and enable concurrent printing to STDERR for most scripts. * Add new 'logging_format' option to Travis CI test config.yaml. * Add missing parenthesis (bug fix) and cross-os compatible paths. * Fix typos in messages. * Use correct log files for logging (bug fix). * doc: fix line references * config: logging_format in all configs * doc: add doc for logging_format * environment: update to powerplantmatching 0.4.3 * doc: update line references for tutorial.rst * Change logging configuration scheme for config.yaml. * Add helper function for doing basic logging configuration. * Add logpath for prepare_links_p_nom rule. * Outsource basic logging configuration for all scripts to _helper submodule. * Update documentation for changed config.yaml structure. Instead of 'logging_level' and 'logging_format', now 'logging' with subcategories is used. * _helpers: Change configure_logging signature.
2019-11-28 07:22:52 +00:00
import logging
logger = logging.getLogger(__name__)
from _helpers import configure_logging
from add_electricity import load_costs, update_transmission_costs
from six import iteritems
import numpy as np
import re
import pypsa
import pandas as pd
idx = pd.IndexSlice
def add_co2limit(n, Nyears=1., factor=None):
2019-11-05 10:21:33 +00:00
if factor is not None:
annual_emissions = factor*snakemake.config['electricity']['co2base']
else:
annual_emissions = snakemake.config['electricity']['co2limit']
n.add("GlobalConstraint", "CO2Limit",
carrier_attribute="co2_emissions", sense="<=",
constant=annual_emissions * Nyears)
def add_emission_prices(n, emission_prices=None, exclude_co2=False):
if emission_prices is None:
emission_prices = snakemake.config['costs']['emission_prices']
if exclude_co2: emission_prices.pop('co2')
ep = (pd.Series(emission_prices).rename(lambda x: x+'_emissions') *
n.carriers.filter(like='_emissions')).sum(axis=1)
n.generators['marginal_cost'] += n.generators.carrier.map(ep)
n.storage_units['marginal_cost'] += n.storage_units.carrier.map(ep)
def set_line_s_max_pu(n):
# set n-1 security margin to 0.5 for 37 clusters and to 0.7 from 200 clusters
n_clusters = len(n.buses)
s_max_pu = np.clip(0.5 + 0.2 * (n_clusters - 37) / (200 - 37), 0.5, 0.7)
n.lines['s_max_pu'] = s_max_pu
def set_line_cost_limit(n, lc, Nyears=1.):
links_dc_b = n.links.carrier == 'DC' if not n.links.empty else pd.Series()
lines_s_nom = n.lines.s_nom.where(
n.lines.type == '',
np.sqrt(3) * n.lines.num_parallel *
n.lines.type.map(n.line_types.i_nom) *
n.lines.bus0.map(n.buses.v_nom)
)
n.lines['capital_cost_lc'] = n.lines['capital_cost']
n.links['capital_cost_lc'] = n.links['capital_cost']
total_line_cost = ((lines_s_nom * n.lines['capital_cost_lc']).sum() +
n.links.loc[links_dc_b].eval('p_nom * capital_cost_lc').sum())
if lc == 'opt':
costs = load_costs(Nyears, snakemake.input.tech_costs,
snakemake.config['costs'], snakemake.config['electricity'])
update_transmission_costs(n, costs, simple_hvdc_costs=False)
else:
# Either line_volume cap or cost
n.lines['capital_cost'] = 0.
n.links.loc[links_dc_b, 'capital_cost'] = 0.
if lc == 'opt' or float(lc) > 1.0:
n.lines['s_nom_min'] = lines_s_nom
n.lines['s_nom_extendable'] = True
n.links.loc[links_dc_b, 'p_nom_min'] = n.links.loc[links_dc_b, 'p_nom']
n.links.loc[links_dc_b, 'p_nom_extendable'] = True
if lc != 'opt':
line_cost = float(lc) * total_line_cost
n.add('GlobalConstraint', 'lc_limit',
type='transmission_expansion_cost_limit',
sense='<=', constant=line_cost, carrier_attribute='AC, DC')
return n
def set_line_volume_limit(n, lv, Nyears=1.):
links_dc_b = n.links.carrier == 'DC' if not n.links.empty else pd.Series()
lines_s_nom = n.lines.s_nom.where(
n.lines.type == '',
np.sqrt(3) * n.lines.num_parallel *
n.lines.type.map(n.line_types.i_nom) *
n.lines.bus0.map(n.buses.v_nom)
)
total_line_volume = ((lines_s_nom * n.lines['length']).sum() +
n.links.loc[links_dc_b].eval('p_nom * length').sum())
if lv == 'opt':
costs = load_costs(Nyears, snakemake.input.tech_costs,
snakemake.config['costs'], snakemake.config['electricity'])
update_transmission_costs(n, costs, simple_hvdc_costs=True)
else:
# Either line_volume cap or cost
n.lines['capital_cost'] = 0.
n.links.loc[links_dc_b, 'capital_cost'] = 0.
if lv == 'opt' or float(lv) > 1.0:
n.lines['s_nom_min'] = lines_s_nom
n.lines['s_nom_extendable'] = True
n.links.loc[links_dc_b, 'p_nom_min'] = n.links.loc[links_dc_b, 'p_nom']
n.links.loc[links_dc_b, 'p_nom_extendable'] = True
if lv != 'opt':
line_volume = float(lv) * total_line_volume
n.add('GlobalConstraint', 'lv_limit',
type='transmission_volume_expansion_limit',
sense='<=', constant=line_volume, carrier_attribute='AC, DC')
return n
def average_every_nhours(n, offset):
logger.info('Resampling the network to {}'.format(offset))
m = n.copy(with_time=False)
snapshot_weightings = n.snapshot_weightings.resample(offset).sum()
m.set_snapshots(snapshot_weightings.index)
m.snapshot_weightings = snapshot_weightings
for c in n.iterate_components():
pnl = getattr(m, c.list_name+"_t")
2018-02-01 11:42:56 +00:00
for k, df in iteritems(c.pnl):
if not df.empty:
pnl[k] = df.resample(offset).mean()
return m
if __name__ == "__main__":
if 'snakemake' not in globals():
Introduce mocksnakemake which acutally parses Snakefile (#107) * rewrite mocksnakemake for parsing real Snakefile * continue add function to scripts * going through all scripts, setting new mocksnakemake * fix plotting scripts * fix build_country_flh * fix build_country_flh II * adjust config files * fix make_summary for tutorial network * create dir also for output * incorporate suggestions * consistent import of mocksnakemake * consistent import of mocksnakemake II * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/plot_network.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/plot_network.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * Update scripts/retrieve_databundle.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * use pathlib for mocksnakemake * rename mocksnakemake into mock_snakemake * revert change in data * Update scripts/_helpers.py Co-Authored-By: euronion <42553970+euronion@users.noreply.github.com> * remove setting logfile in mock_snakemake, use Path in configure_logging * fix fallback path and base_dir fix return type of make_io_accessable * reformulate mock_snakemake * incorporate suggestion, fix typos * mock_snakemake: apply absolute paths again, add assertion error *.py: make hard coded io path accessable for mock_snakemake * retrieve_natura_raster: use snakemake.output for fn_out * include suggestion * Apply suggestions from code review Co-Authored-By: Jonas Hörsch <jonas.hoersch@posteo.de> * linting, add return ad end of file * Update scripts/plot_p_nom_max.py Co-Authored-By: Jonas Hörsch <jonas.hoersch@posteo.de> * Update scripts/plot_p_nom_max.py fixes #112 Co-Authored-By: Jonas Hörsch <jonas.hoersch@posteo.de> * plot_p_nom_max: small correction * config.tutorial.yaml fix snapshots end * use techs instead of technology * revert try out from previous commit, complete replacing * change clusters -> clusts in plot_p_nom_max due to wildcard constraints of clusters * change clusters -> clusts in plot_p_nom_max due to wildcard constraints of clusters II
2019-12-09 20:29:15 +00:00
from _helpers import mock_snakemake
snakemake = mock_snakemake('prepare_network', network='elec', simpl='',
clusters='5', ll='copt', opts='Co2L-24H')
Add logging to logfiles to all snakemake workflow scripts. (#102) * Add logging to logfiles to all snakemake workflow scripts. * Fix missing quotation marks in Snakefile. * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Apply suggestions from code review Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * doc: fix _ec_ filenames in docs * Allow logging message format to be specified in config.yaml. * Add logging for Snakemake rule 'retrieve_databundle '. * Add limited logging to STDERR only for retrieve_*.py scripts. * Import progressbar module only on demand. * Fix logging to file and enable concurrent printing to STDERR for most scripts. * Add new 'logging_format' option to Travis CI test config.yaml. * Add missing parenthesis (bug fix) and cross-os compatible paths. * Fix typos in messages. * Use correct log files for logging (bug fix). * doc: fix line references * config: logging_format in all configs * doc: add doc for logging_format * environment: update to powerplantmatching 0.4.3 * doc: update line references for tutorial.rst * Change logging configuration scheme for config.yaml. * Add helper function for doing basic logging configuration. * Add logpath for prepare_links_p_nom rule. * Outsource basic logging configuration for all scripts to _helper submodule. * Update documentation for changed config.yaml structure. Instead of 'logging_level' and 'logging_format', now 'logging' with subcategories is used. * _helpers: Change configure_logging signature.
2019-11-28 07:22:52 +00:00
configure_logging(snakemake)
opts = snakemake.wildcards.opts.split('-')
n = pypsa.Network(snakemake.input[0])
Nyears = n.snapshot_weightings.sum()/8760.
set_line_s_max_pu(n)
for o in opts:
m = re.match(r'^\d+h$', o, re.IGNORECASE)
if m is not None:
n = average_every_nhours(n, m.group(0))
break
else:
logger.info("No resampling")
for o in opts:
if "Co2L" in o:
m = re.findall("[0-9]*\.?[0-9]+$", o)
if len(m) > 0:
add_co2limit(n, Nyears, float(m[0]))
else:
add_co2limit(n, Nyears)
if 'Ep' in opts:
add_emission_prices(n)
ll_type, factor = snakemake.wildcards.ll[0], snakemake.wildcards.ll[1:]
if ll_type == 'v':
set_line_volume_limit(n, factor, Nyears)
elif ll_type == 'c':
set_line_cost_limit(n, factor, Nyears)
n.export_to_netcdf(snakemake.output[0])