2018-01-29 21:28:33 +00:00
|
|
|
# coding: utf-8
|
|
|
|
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
import pandas as pd
|
|
|
|
idx = pd.IndexSlice
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
import scipy as sp
|
|
|
|
import xarray as xr
|
2018-01-30 22:12:36 +00:00
|
|
|
import re
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-02-01 11:42:56 +00:00
|
|
|
from six import iteritems
|
2018-01-29 21:28:33 +00:00
|
|
|
import geopandas as gpd
|
|
|
|
|
|
|
|
import pypsa
|
2018-10-22 21:22:34 +00:00
|
|
|
from add_electricity import load_costs, update_transmission_costs
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
def add_co2limit(n, Nyears=1.):
|
|
|
|
n.add("GlobalConstraint", "CO2Limit",
|
|
|
|
carrier_attribute="co2_emissions", sense="<=",
|
|
|
|
constant=snakemake.config['electricity']['co2limit'] * Nyears)
|
|
|
|
|
|
|
|
def add_emission_prices(n, emission_prices=None, exclude_co2=False):
|
2018-01-30 22:12:36 +00:00
|
|
|
assert False, "Needs to be fixed, adds NAN"
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
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).sum(axis=1)
|
|
|
|
n.generators['marginal_cost'] += n.generators.carrier.map(ep)
|
|
|
|
n.storage_units['marginal_cost'] += n.storage_units.carrier.map(ep)
|
|
|
|
|
2018-01-30 22:12:36 +00:00
|
|
|
def set_line_s_max_pu(n):
|
2018-09-24 16:28:59 +00:00
|
|
|
# set n-1 security margin to 0.5 for 37 clusters and to 0.7 from 200 clusters
|
2018-01-30 22:12:36 +00:00
|
|
|
n_clusters = len(n.buses)
|
2018-09-24 16:28:59 +00:00
|
|
|
s_max_pu = np.clip(0.5 + 0.2 * (n_clusters - 37) / (200 - 45), 0.5, 0.7)
|
2018-01-30 22:12:36 +00:00
|
|
|
n.lines['s_max_pu'] = s_max_pu
|
|
|
|
|
|
|
|
dc_b = n.links.carrier == 'DC'
|
|
|
|
n.links.loc[dc_b, 'p_max_pu'] = s_max_pu
|
|
|
|
n.links.loc[dc_b, 'p_min_pu'] = - s_max_pu
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
def set_line_volume_limit(n, lv):
|
2018-10-22 21:22:34 +00:00
|
|
|
links_dc_b = n.links.carrier == 'DC'
|
|
|
|
|
|
|
|
if np.isinf(lv):
|
|
|
|
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.
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-02-19 09:11:58 +00:00
|
|
|
if lv > 1.0:
|
|
|
|
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['s_nom_min'] = lines_s_nom
|
|
|
|
n.lines['s_nom_extendable'] = True
|
2018-10-22 21:22:34 +00:00
|
|
|
|
|
|
|
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
|
2018-02-19 09:11:58 +00:00
|
|
|
|
|
|
|
n.line_volume_limit = lv * ((lines_s_nom * n.lines['length']).sum() +
|
2018-10-22 21:22:34 +00:00
|
|
|
n.links.loc[links_dc_b].eval('p_nom * length').sum())
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
return n
|
|
|
|
|
2018-01-30 22:12:36 +00:00
|
|
|
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):
|
2018-01-30 22:12:36 +00:00
|
|
|
if not df.empty:
|
|
|
|
pnl[k] = df.resample(offset).mean()
|
|
|
|
|
|
|
|
return m
|
|
|
|
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
# Detect running outside of snakemake and mock snakemake for testing
|
|
|
|
if 'snakemake' not in globals():
|
2018-01-30 22:48:29 +00:00
|
|
|
from vresutils.snakemake import MockSnakemake
|
|
|
|
snakemake = MockSnakemake(
|
|
|
|
wildcards=dict(network='elec', simpl='', clusters='37', lv='2', opts='Co2L-3H'),
|
|
|
|
input=['networks/{network}_s{simpl}_{clusters}.nc'],
|
|
|
|
output=['networks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc']
|
|
|
|
)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-02-10 16:16:20 +00:00
|
|
|
logging.basicConfig(level=snakemake.config['logging_level'])
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
opts = snakemake.wildcards.opts.split('-')
|
|
|
|
|
|
|
|
n = pypsa.Network(snakemake.input[0])
|
|
|
|
Nyears = n.snapshot_weightings.sum()/8760.
|
|
|
|
|
2018-01-30 22:12:36 +00:00
|
|
|
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")
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
if 'Co2L' in opts:
|
|
|
|
add_co2limit(n, Nyears)
|
2018-01-30 22:12:36 +00:00
|
|
|
# add_emission_prices(n, exclude_co2=True)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-01-30 22:12:36 +00:00
|
|
|
# if 'Ep' in opts:
|
|
|
|
# add_emission_prices(n)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-10-22 21:22:34 +00:00
|
|
|
set_line_volume_limit(n, float(snakemake.wildcards.lv))
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
n.export_to_netcdf(snakemake.output[0])
|