* add_electricity: stores and links instead of storageunits * move code for attaching in add_extra_stores.py * Update Snakefile Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Update Snakefile Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * Update Snakefile Co-Authored-By: Fabian Neumann <fabian.neumann@outlook.de> * rename to add_extra_components.py * incorporate suggestions * Snakefile: update script name * extra_components: import _add_missing_carriers_from_costs * Add '_ec" infix in Snakefile * Snakefile add infix at missing spot * remove unintendetely inserted data.bundle, correct import in add_extra_components * environment: fix pyyaml version * set powerplantmatching as conda-dependency * environment fix minimal ppm version * environment reinsert pyyaml * environment: carry over changes to environment.docs.yaml * extra_components: add carriers * update docstring * Update scripts/add_extra_components.py * Update scripts/add_extra_components.py * tutorial: fix hyperlink [skip travis] * update release notes * update documentation * add_electricity: merge removal of suptech
167 lines
6.5 KiB
Python
167 lines
6.5 KiB
Python
# coding: utf-8
|
|
"""
|
|
Adds extra extendable components to the clustered and simplified network.
|
|
|
|
Relevant Settings
|
|
-----------------
|
|
|
|
.. code:: yaml
|
|
|
|
costs:
|
|
year:
|
|
USD2013_to_EUR2013:
|
|
dicountrate:
|
|
emission_prices:
|
|
|
|
electricity:
|
|
max_hours:
|
|
marginal_cost:
|
|
capital_cost:
|
|
extendable_carriers:
|
|
StorageUnit:
|
|
Store:
|
|
|
|
.. seealso::
|
|
Documentation of the configuration file ``config.yaml`` at :ref:`costs_cf`,
|
|
:ref:`electricity_cf`
|
|
|
|
Inputs
|
|
------
|
|
|
|
- ``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.
|
|
|
|
Outputs
|
|
-------
|
|
|
|
- ``networks/{network}_s{simpl}_{clusters}_ec.nc``:
|
|
|
|
|
|
Description
|
|
-----------
|
|
|
|
The rule :mod:`add_extra_components` attaches additional extendable components to the clustered and simplified network. These can be configured in the ``config.yaml`` at ``electricity: extendable_carriers: ``. It processes ``networks/{network}_s{simpl}_{clusters}.nc`` to build ``networks/{network}_s{simpl}_{clusters}_ec.nc``, which in contrast to the former (depending on the configuration) contain with **zero** initial capacity
|
|
|
|
- ``StorageUnits`` of carrier 'H2' and/or 'battery'. If this option is chosen, every bus is given an extendable ``StorageUnit`` of the corresponding carrier. The energy and power capacities are linked through a parameter that specifies the energy capacity as maximum hours at full dispatch power and is configured in ``electricity: max_hours:``. This linkage leads to one investment variable per storage unit. The default ``max_hours`` lead to long-term hydrogen and short-term battery storage units.
|
|
|
|
- ``Stores`` of carrier 'H2' and/or 'battery' in combination with ``Links``. If this option is chosen, the script adds extra buses with corresponding carrier where energy ``Stores`` are attached and which are connected to the corresponding power buses via two links, one each for charging and discharging. This leads to three investment variables for the energy capacity, charging and discharging capacity of the storage unit.
|
|
"""
|
|
|
|
import logging
|
|
import pandas as pd
|
|
import pypsa
|
|
from add_electricity import (load_costs, normed, add_nice_carrier_names,
|
|
_add_missing_carriers_from_costs)
|
|
|
|
idx = pd.IndexSlice
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def attach_storageunits(n, costs):
|
|
elec_opts = snakemake.config['electricity']
|
|
carriers = elec_opts['extendable_carriers']['StorageUnit']
|
|
max_hours = elec_opts['max_hours']
|
|
|
|
_add_missing_carriers_from_costs(n, costs, carriers)
|
|
|
|
buses_i = n.buses.index
|
|
|
|
for carrier in carriers:
|
|
n.madd("StorageUnit", buses_i, ' ' + carrier,
|
|
bus=buses_i,
|
|
carrier=carrier,
|
|
p_nom_extendable=True,
|
|
capital_cost=costs.at[carrier, 'capital_cost'],
|
|
marginal_cost=costs.at[carrier, 'marginal_cost'],
|
|
efficiency_store=costs.at[carrier, 'efficiency'],
|
|
efficiency_dispatch=costs.at[carrier, 'efficiency'],
|
|
max_hours=max_hours[carrier],
|
|
cyclic_state_of_charge=True)
|
|
|
|
def attach_stores(n, costs):
|
|
elec_opts = snakemake.config['electricity']
|
|
carriers = elec_opts['extendable_carriers']['Store']
|
|
|
|
_add_missing_carriers_from_costs(n, costs, carriers)
|
|
|
|
buses_i = n.buses.index
|
|
bus_sub_dict = {k: n.buses[k].values for k in ['x', 'y', 'country']}
|
|
|
|
if 'H2' in carriers:
|
|
h2_buses_i = n.madd("Bus", buses_i + " H2", carrier="H2", **bus_sub_dict)
|
|
|
|
n.madd("Store", h2_buses_i,
|
|
bus=h2_buses_i,
|
|
carrier='H2',
|
|
e_nom_extendable=True,
|
|
e_cyclic=True,
|
|
capital_cost=costs.at["hydrogen storage", "capital_cost"])
|
|
|
|
n.madd("Link", h2_buses_i + " Electrolysis",
|
|
bus0=buses_i,
|
|
bus1=h2_buses_i,
|
|
carrier='H2 electrolysis',
|
|
p_nom_extendable=True,
|
|
efficiency=costs.at["electrolysis", "efficiency"],
|
|
capital_cost=costs.at["electrolysis", "capital_cost"])
|
|
|
|
n.madd("Link", h2_buses_i + " Fuel Cell",
|
|
bus0=h2_buses_i,
|
|
bus1=buses_i,
|
|
carrier='H2 fuel cell',
|
|
p_nom_extendable=True,
|
|
efficiency=costs.at["fuel cell", "efficiency"],
|
|
#NB: fixed cost is per MWel
|
|
capital_cost=costs.at["fuel cell", "capital_cost"] * costs.at["fuel cell", "efficiency"])
|
|
|
|
if 'battery' in carriers:
|
|
b_buses_i = n.madd("Bus", buses_i + " battery", carrier="battery", **bus_sub_dict)
|
|
|
|
n.madd("Store", b_buses_i,
|
|
bus=b_buses_i,
|
|
carrier='battery',
|
|
e_cyclic=True,
|
|
e_nom_extendable=True,
|
|
capital_cost=costs.at['battery storage', 'capital_cost'])
|
|
|
|
n.madd("Link", b_buses_i + " charger",
|
|
bus0=buses_i,
|
|
bus1=b_buses_i,
|
|
carrier='battery charger',
|
|
efficiency=costs.at['battery inverter', 'efficiency']**0.5,
|
|
capital_cost=costs.at['battery inverter', 'capital_cost'],
|
|
p_nom_extendable=True)
|
|
|
|
n.madd("Link", b_buses_i + " discharger",
|
|
bus0=b_buses_i,
|
|
bus1=buses_i,
|
|
carrier='battery discharger',
|
|
efficiency=costs.at['battery inverter','efficiency']**0.5,
|
|
capital_cost=costs.at['battery inverter', 'capital_cost'],
|
|
p_nom_extendable=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Detect running outside of snakemake and mock snakemake for testing
|
|
if 'snakemake' not in globals():
|
|
from vresutils.snakemake import MockSnakemake, Dict
|
|
|
|
snakemake = MockSnakemake(output=['networks/elec_s_5_ec.nc'])
|
|
snakemake.input = snakemake.expand(
|
|
Dict(network='networks/elec_s_5.nc',
|
|
tech_costs='data/costs.csv'))
|
|
|
|
logging.basicConfig(level=snakemake.config['logging_level'])
|
|
|
|
n = pypsa.Network(snakemake.input.network)
|
|
Nyears = n.snapshot_weightings.sum()/8760.
|
|
costs = load_costs(Nyears, tech_costs=snakemake.input.tech_costs,
|
|
config=snakemake.config['costs'],
|
|
elec_config=snakemake.config['electricity'])
|
|
|
|
attach_storageunits(n, costs)
|
|
attach_stores(n, costs)
|
|
|
|
add_nice_carrier_names(n, config=snakemake.config)
|
|
|
|
n.export_to_netcdf(snakemake.output[0])
|