2018-01-29 21:28:33 +00:00
|
|
|
# coding: utf-8
|
2019-08-08 13:02:28 +00:00
|
|
|
"""
|
2019-08-11 20:34:18 +00:00
|
|
|
Creates networks clustered to ``{cluster}`` number of zones with aggregated buses, generators and transmission corridors.
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-11 09:40:47 +00:00
|
|
|
Relevant Settings
|
|
|
|
-----------------
|
|
|
|
|
2019-08-11 11:17:36 +00:00
|
|
|
.. code:: yaml
|
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
focus_weights:
|
|
|
|
|
2019-08-11 11:17:36 +00:00
|
|
|
renewable: (keys)
|
|
|
|
{technology}:
|
|
|
|
potential:
|
|
|
|
|
|
|
|
solving:
|
|
|
|
solver:
|
|
|
|
name:
|
|
|
|
|
|
|
|
lines:
|
|
|
|
length_factor:
|
|
|
|
|
2019-12-09 20:29:15 +00:00
|
|
|
.. seealso::
|
2019-08-13 08:03:46 +00:00
|
|
|
Documentation of the configuration file ``config.yaml`` at
|
2019-11-07 14:38:25 +00:00
|
|
|
:ref:`toplevel_cf`, :ref:`renewable_cf`, :ref:`solving_cf`, :ref:`lines_cf`
|
2019-08-13 08:03:46 +00:00
|
|
|
|
2019-08-11 09:40:47 +00:00
|
|
|
Inputs
|
|
|
|
------
|
|
|
|
|
2019-08-11 20:34:18 +00:00
|
|
|
- ``resources/regions_onshore_{network}_s{simpl}.geojson``: confer :ref:`simplify`
|
|
|
|
- ``resources/regions_offshore_{network}_s{simpl}.geojson``: confer :ref:`simplify`
|
|
|
|
- ``resources/clustermaps_{network}_s{simpl}.h5``: confer :ref:`simplify`
|
|
|
|
- ``networks/{network}_s{simpl}.nc``: confer :ref:`simplify`
|
|
|
|
|
2019-08-11 09:40:47 +00:00
|
|
|
Outputs
|
2019-08-10 12:25:19 +00:00
|
|
|
-------
|
|
|
|
|
2019-08-11 20:34:18 +00:00
|
|
|
- ``resources/regions_onshore_{network}_s{simpl}_{clusters}.geojson``:
|
|
|
|
|
2019-08-14 13:36:46 +00:00
|
|
|
.. image:: ../img/regions_onshore_elec_s_X.png
|
2019-08-12 17:01:53 +00:00
|
|
|
:scale: 33 %
|
2019-08-11 09:40:47 +00:00
|
|
|
|
2019-08-12 17:01:53 +00:00
|
|
|
- ``resources/regions_offshore_{network}_s{simpl}_{clusters}.geojson``:
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-14 13:36:46 +00:00
|
|
|
.. image:: ../img/regions_offshore_elec_s_X.png
|
2019-08-12 17:01:53 +00:00
|
|
|
:scale: 33 %
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-12 17:01:53 +00:00
|
|
|
- ``resources/clustermaps_{network}_s{simpl}_{clusters}.h5``: Mapping of buses and lines from ``networks/elec_s{simpl}.nc`` to ``networks/elec_s{simpl}_{clusters}.nc``; has keys ['/busmap', '/busmap_s', '/linemap', '/linemap_negative', '/linemap_positive']
|
2019-12-09 20:29:15 +00:00
|
|
|
- ``networks/{network}_s{simpl}_{clusters}.nc``:
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-14 13:36:46 +00:00
|
|
|
.. image:: ../img/elec_s_X.png
|
2019-08-12 17:01:53 +00:00
|
|
|
:scale: 40 %
|
|
|
|
|
|
|
|
Description
|
|
|
|
-----------
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-12 17:01:53 +00:00
|
|
|
.. note::
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-12 17:01:53 +00:00
|
|
|
**Why is clustering used both in** ``simplify_network`` **and** ``cluster_network`` **?**
|
2019-12-09 20:29:15 +00:00
|
|
|
|
2019-08-12 21:48:16 +00:00
|
|
|
Consider for example a network ``networks/elec_s100_50.nc`` in which
|
|
|
|
``simplify_network`` clusters the network to 100 buses and in a second
|
|
|
|
step ``cluster_network``` reduces it down to 50 buses.
|
|
|
|
|
|
|
|
In preliminary tests, it turns out, that the principal effect of
|
|
|
|
changing spatial resolution is actually only partially due to the
|
|
|
|
transmission network. It is more important to differentiate between
|
|
|
|
wind generators with higher capacity factors from those with lower
|
|
|
|
capacity factors, i.e. to have a higher spatial resolution in the
|
|
|
|
renewable generation than in the number of buses.
|
|
|
|
|
|
|
|
The two-step clustering allows to study this effect by looking at
|
|
|
|
networks like ``networks/elec_s100_50m.nc``. Note the additional
|
|
|
|
``m`` in the ``{cluster}`` wildcard. So in the example network
|
|
|
|
there are still up to 100 different wind generators.
|
|
|
|
|
|
|
|
In combination these two features allow you to study the spatial
|
|
|
|
resolution of the transmission network separately from the
|
|
|
|
spatial resolution of renewable generators.
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-12 17:01:53 +00:00
|
|
|
**Is it possible to run the model without the** ``simplify_network`` **rule?**
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-12 21:48:16 +00:00
|
|
|
No, the network clustering methods in the PyPSA module
|
|
|
|
`pypsa.networkclustering <https://github.com/PyPSA/PyPSA/blob/master/pypsa/networkclustering.py>`_
|
|
|
|
do not work reliably with multiple voltage levels and transformers.
|
2019-08-10 12:25:19 +00:00
|
|
|
|
2019-08-11 20:34:18 +00:00
|
|
|
.. tip::
|
2019-08-13 15:52:33 +00:00
|
|
|
The rule :mod:`cluster_all_networks` runs
|
2019-12-09 20:29:15 +00:00
|
|
|
for all ``scenario`` s in the configuration file
|
2019-08-13 15:52:33 +00:00
|
|
|
the rule :mod:`cluster_network`.
|
2019-08-11 20:34:18 +00:00
|
|
|
|
2019-08-08 13:02:28 +00:00
|
|
|
"""
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-01-30 22:11:16 +00:00
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
2019-11-28 07:22:52 +00:00
|
|
|
from _helpers import configure_logging
|
|
|
|
|
|
|
|
import pandas as pd
|
|
|
|
idx = pd.IndexSlice
|
2018-01-30 22:11:16 +00:00
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
import os
|
|
|
|
import numpy as np
|
|
|
|
import geopandas as gpd
|
|
|
|
import shapely
|
2019-09-23 12:32:51 +00:00
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
import seaborn as sns
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
from six.moves import reduce
|
|
|
|
|
2018-02-19 09:03:25 +00:00
|
|
|
import pyomo.environ as po
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
import pypsa
|
2019-09-23 14:44:48 +00:00
|
|
|
from pypsa.networkclustering import (busmap_by_kmeans, busmap_by_spectral_clustering,
|
2019-09-23 12:32:51 +00:00
|
|
|
_make_consense, get_clustering_from_busmap)
|
|
|
|
|
2019-09-23 14:44:48 +00:00
|
|
|
from add_electricity import load_costs
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
def normed(x):
|
|
|
|
return (x/x.sum()).fillna(0.)
|
|
|
|
|
2018-01-30 22:11:16 +00:00
|
|
|
def weighting_for_country(n, x):
|
2019-02-18 17:36:56 +00:00
|
|
|
conv_carriers = {'OCGT','CCGT','PHS', 'hydro'}
|
2018-01-29 21:28:33 +00:00
|
|
|
gen = (n
|
|
|
|
.generators.loc[n.generators.carrier.isin(conv_carriers)]
|
|
|
|
.groupby('bus').p_nom.sum()
|
|
|
|
.reindex(n.buses.index, fill_value=0.) +
|
|
|
|
n
|
|
|
|
.storage_units.loc[n.storage_units.carrier.isin(conv_carriers)]
|
|
|
|
.groupby('bus').p_nom.sum()
|
|
|
|
.reindex(n.buses.index, fill_value=0.))
|
|
|
|
load = n.loads_t.p_set.mean().groupby(n.loads.bus).sum()
|
|
|
|
|
|
|
|
b_i = x.index
|
|
|
|
g = normed(gen.reindex(b_i, fill_value=0))
|
|
|
|
l = normed(load.reindex(b_i, fill_value=0))
|
|
|
|
|
|
|
|
w= g + l
|
2018-08-27 17:14:34 +00:00
|
|
|
return (w * (100. / w.max())).clip(lower=1.).astype(int)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
## Plot weighting for Germany
|
|
|
|
|
2018-08-03 09:49:23 +00:00
|
|
|
def plot_weighting(n, country, country_shape=None):
|
2018-01-29 21:28:33 +00:00
|
|
|
n.plot(bus_sizes=(2*weighting_for_country(n.buses.loc[n.buses.country == country])).reindex(n.buses.index, fill_value=1))
|
2018-08-03 09:49:23 +00:00
|
|
|
if country_shape is not None:
|
|
|
|
plt.xlim(country_shape.bounds[0], country_shape.bounds[2])
|
|
|
|
plt.ylim(country_shape.bounds[1], country_shape.bounds[3])
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
# # Determining the number of clusters per country
|
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
def distribute_clusters(n, n_clusters, focus_weights=None, solver_name=None):
|
2018-09-14 09:22:13 +00:00
|
|
|
if solver_name is None:
|
2019-08-11 11:17:36 +00:00
|
|
|
solver_name = snakemake.config['solving']['solver']['name']
|
2018-09-14 09:22:13 +00:00
|
|
|
|
2018-02-19 09:03:25 +00:00
|
|
|
L = (n.loads_t.p_set.mean()
|
|
|
|
.groupby(n.loads.bus).sum()
|
|
|
|
.groupby([n.buses.country, n.buses.sub_network]).sum()
|
|
|
|
.pipe(normed))
|
|
|
|
|
2019-01-22 10:25:01 +00:00
|
|
|
N = n.buses.groupby(['country', 'sub_network']).size()
|
|
|
|
|
2019-02-13 18:03:57 +00:00
|
|
|
assert n_clusters >= len(N) and n_clusters <= N.sum(), \
|
|
|
|
"Number of clusters must be {} <= n_clusters <= {} for this selection of countries.".format(len(N), N.sum())
|
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
if focus_weights is not None:
|
|
|
|
|
|
|
|
total_focus = sum(list(focus_weights.values()))
|
|
|
|
|
|
|
|
assert total_focus <= 1.0, "The sum of focus weights must be less than or equal to 1."
|
2019-12-09 20:29:15 +00:00
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
for country, weight in focus_weights.items():
|
2019-11-07 15:54:09 +00:00
|
|
|
L[country] = weight / len(L[country])
|
2019-11-07 14:38:25 +00:00
|
|
|
|
|
|
|
remainder = [c not in focus_weights.keys() for c in L.index.get_level_values('country')]
|
|
|
|
L[remainder] = L.loc[remainder].pipe(normed) * (1 - total_focus)
|
|
|
|
|
|
|
|
logger.warning('Using custom focus weights for determining number of clusters.')
|
|
|
|
|
2019-11-09 11:58:04 +00:00
|
|
|
assert np.isclose(L.sum(), 1.0, rtol=1e-3), "Country weights L must sum up to 1.0 when distributing clusters. Is {}.".format(L.sum())
|
2019-11-07 14:38:25 +00:00
|
|
|
|
2018-02-19 09:03:25 +00:00
|
|
|
m = po.ConcreteModel()
|
2019-01-22 10:25:01 +00:00
|
|
|
def n_bounds(model, *n_id):
|
|
|
|
return (1, N[n_id])
|
|
|
|
m.n = po.Var(list(L.index), bounds=n_bounds, domain=po.Integers)
|
2018-02-19 09:03:25 +00:00
|
|
|
m.tot = po.Constraint(expr=(po.summation(m.n) == n_clusters))
|
2019-03-09 16:50:28 +00:00
|
|
|
m.objective = po.Objective(expr=sum((m.n[i] - L.loc[i]*n_clusters)**2 for i in L.index),
|
2018-02-19 09:03:25 +00:00
|
|
|
sense=po.minimize)
|
|
|
|
|
|
|
|
opt = po.SolverFactory(solver_name)
|
2019-02-10 13:43:57 +00:00
|
|
|
if not opt.has_capability('quadratic_objective'):
|
2019-11-08 15:46:29 +00:00
|
|
|
logger.warning(f'The configured solver `{solver_name}` does not support quadratic objectives. Falling back to `ipopt`.')
|
2019-02-10 13:43:57 +00:00
|
|
|
opt = po.SolverFactory('ipopt')
|
|
|
|
|
2018-02-19 09:03:25 +00:00
|
|
|
results = opt.solve(m)
|
|
|
|
assert results['Solver'][0]['Status'].key == 'ok', "Solver returned non-optimally: {}".format(results)
|
|
|
|
|
|
|
|
return pd.Series(m.n.get_values(), index=L.index).astype(int)
|
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
def busmap_for_n_clusters(n, n_clusters, solver_name, focus_weights=None, algorithm="kmeans", **algorithm_kwds):
|
2019-02-06 11:06:32 +00:00
|
|
|
if algorithm == "kmeans":
|
|
|
|
algorithm_kwds.setdefault('n_init', 1000)
|
|
|
|
algorithm_kwds.setdefault('max_iter', 30000)
|
|
|
|
algorithm_kwds.setdefault('tol', 1e-6)
|
2019-01-22 10:24:07 +00:00
|
|
|
|
2018-01-30 22:11:16 +00:00
|
|
|
n.determine_network_topology()
|
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
n_clusters = distribute_clusters(n, n_clusters, focus_weights=focus_weights, solver_name=solver_name)
|
2018-02-19 09:03:25 +00:00
|
|
|
|
2019-02-06 11:06:32 +00:00
|
|
|
def reduce_network(n, buses):
|
|
|
|
nr = pypsa.Network()
|
|
|
|
nr.import_components_from_dataframe(buses, "Bus")
|
|
|
|
nr.import_components_from_dataframe(n.lines.loc[n.lines.bus0.isin(buses.index) & n.lines.bus1.isin(buses.index)], "Line")
|
|
|
|
return nr
|
2018-02-19 09:03:25 +00:00
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
def busmap_for_country(x):
|
|
|
|
prefix = x.name[0] + x.name[1] + ' '
|
2019-01-22 10:24:07 +00:00
|
|
|
logger.debug("Determining busmap for country {}".format(prefix[:-1]))
|
2018-01-29 21:28:33 +00:00
|
|
|
if len(x) == 1:
|
|
|
|
return pd.Series(prefix + '0', index=x.index)
|
2018-01-30 22:11:16 +00:00
|
|
|
weight = weighting_for_country(n, x)
|
2019-02-06 11:06:32 +00:00
|
|
|
|
|
|
|
if algorithm == "kmeans":
|
|
|
|
return prefix + busmap_by_kmeans(n, weight, n_clusters[x.name], buses_i=x.index, **algorithm_kwds)
|
|
|
|
elif algorithm == "spectral":
|
|
|
|
return prefix + busmap_by_spectral_clustering(reduce_network(n, x), n_clusters[x.name], **algorithm_kwds)
|
|
|
|
elif algorithm == "louvain":
|
|
|
|
return prefix + busmap_by_louvain(reduce_network(n, x), n_clusters[x.name], **algorithm_kwds)
|
|
|
|
else:
|
2020-03-16 14:55:01 +00:00
|
|
|
raise ValueError(f"`algorithm` must be one of 'kmeans', 'spectral' or 'louvain'. Is {algorithm}.")
|
2019-02-06 11:06:32 +00:00
|
|
|
|
2019-02-20 20:08:36 +00:00
|
|
|
return (n.buses.groupby(['country', 'sub_network'], group_keys=False, squeeze=True)
|
|
|
|
.apply(busmap_for_country).rename('busmap'))
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-01-30 22:11:16 +00:00
|
|
|
def plot_busmap_for_n_clusters(n, n_clusters=50):
|
|
|
|
busmap = busmap_for_n_clusters(n, n_clusters)
|
2018-01-29 21:28:33 +00:00
|
|
|
cs = busmap.unique()
|
|
|
|
cr = sns.color_palette("hls", len(cs))
|
|
|
|
n.plot(bus_colors=busmap.map(dict(zip(cs, cr))))
|
|
|
|
del cs, cr
|
|
|
|
|
2018-12-31 13:53:11 +00:00
|
|
|
def clustering_for_n_clusters(n, n_clusters, aggregate_carriers=None,
|
2019-02-20 18:06:48 +00:00
|
|
|
line_length_factor=1.25, potential_mode='simple',
|
2019-09-23 14:44:48 +00:00
|
|
|
solver_name="cbc", algorithm="kmeans",
|
2019-11-07 14:38:25 +00:00
|
|
|
extended_link_costs=0, focus_weights=None):
|
2018-12-31 13:53:11 +00:00
|
|
|
|
2019-02-01 17:33:21 +00:00
|
|
|
if potential_mode == 'simple':
|
2018-12-31 13:53:11 +00:00
|
|
|
p_nom_max_strategy = np.sum
|
2019-02-01 17:33:21 +00:00
|
|
|
elif potential_mode == 'conservative':
|
|
|
|
p_nom_max_strategy = np.min
|
2018-12-31 13:53:11 +00:00
|
|
|
else:
|
2019-02-01 17:33:21 +00:00
|
|
|
raise AttributeError("potential_mode should be one of 'simple' or 'conservative', "
|
2018-12-31 13:53:11 +00:00
|
|
|
"but is '{}'".format(potential_mode))
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
clustering = get_clustering_from_busmap(
|
2019-11-07 14:38:25 +00:00
|
|
|
n, busmap_for_n_clusters(n, n_clusters, solver_name, focus_weights, algorithm),
|
2018-01-29 21:28:33 +00:00
|
|
|
bus_strategies=dict(country=_make_consense("Bus", "country")),
|
|
|
|
aggregate_generators_weighted=True,
|
2018-12-31 13:53:11 +00:00
|
|
|
aggregate_generators_carriers=aggregate_carriers,
|
2018-05-18 15:06:55 +00:00
|
|
|
aggregate_one_ports=["Load", "StorageUnit"],
|
2018-12-31 13:53:11 +00:00
|
|
|
line_length_factor=line_length_factor,
|
2019-09-23 14:44:48 +00:00
|
|
|
generator_strategies={'p_nom_max': p_nom_max_strategy},
|
|
|
|
scale_link_capital_costs=False)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2020-03-29 09:39:18 +00:00
|
|
|
if not n.links.empty:
|
|
|
|
nc = clustering.network
|
|
|
|
nc.links['underwater_fraction'] = (n.links.eval('underwater_fraction * length')
|
|
|
|
.div(nc.links.length).dropna())
|
|
|
|
nc.links['capital_cost'] = (nc.links['capital_cost']
|
|
|
|
.add((nc.links.length - n.links.length)
|
|
|
|
.clip(lower=0).mul(extended_link_costs),
|
|
|
|
fill_value=0))
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
return clustering
|
|
|
|
|
|
|
|
def save_to_geojson(s, fn):
|
|
|
|
if os.path.exists(fn):
|
|
|
|
os.unlink(fn)
|
2018-12-19 09:14:41 +00:00
|
|
|
df = s.reset_index()
|
|
|
|
schema = {**gpd.io.file.infer_schema(df), 'geometry': 'Unknown'}
|
|
|
|
df.to_file(fn, driver='GeoJSON', schema=schema)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-01-30 22:11:16 +00:00
|
|
|
def cluster_regions(busmaps, input=None, output=None):
|
|
|
|
if input is None: input = snakemake.input
|
|
|
|
if output is None: output = snakemake.output
|
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
busmap = reduce(lambda x, y: x.map(y), busmaps[1:], busmaps[0])
|
|
|
|
|
|
|
|
for which in ('regions_onshore', 'regions_offshore'):
|
2018-01-30 22:11:16 +00:00
|
|
|
regions = gpd.read_file(getattr(input, which)).set_index('name')
|
|
|
|
geom_c = regions.geometry.groupby(busmap).apply(shapely.ops.cascaded_union)
|
2018-01-29 21:28:33 +00:00
|
|
|
regions_c = gpd.GeoDataFrame(dict(geometry=geom_c))
|
2018-01-30 22:11:16 +00:00
|
|
|
regions_c.index.name = 'name'
|
|
|
|
save_to_geojson(regions_c, getattr(output, which))
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if 'snakemake' not in globals():
|
2019-12-09 20:29:15 +00:00
|
|
|
from _helpers import mock_snakemake
|
|
|
|
snakemake = mock_snakemake('cluster_network', network='elec', simpl='', clusters='5')
|
2019-11-28 07:22:52 +00:00
|
|
|
configure_logging(snakemake)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
|
|
|
n = pypsa.Network(snakemake.input.network)
|
|
|
|
|
2019-11-07 14:38:25 +00:00
|
|
|
focus_weights = snakemake.config.get('focus_weights', None)
|
|
|
|
|
2018-12-31 13:53:11 +00:00
|
|
|
renewable_carriers = pd.Index([tech
|
|
|
|
for tech in n.generators.carrier.unique()
|
|
|
|
if tech.split('-', 2)[0] in snakemake.config['renewable']])
|
|
|
|
|
2018-02-19 09:03:25 +00:00
|
|
|
if snakemake.wildcards.clusters.endswith('m'):
|
|
|
|
n_clusters = int(snakemake.wildcards.clusters[:-1])
|
2019-01-02 16:54:07 +00:00
|
|
|
aggregate_carriers = pd.Index(n.generators.carrier.unique()).difference(renewable_carriers)
|
2018-02-19 09:03:25 +00:00
|
|
|
else:
|
|
|
|
n_clusters = int(snakemake.wildcards.clusters)
|
2019-01-02 16:54:07 +00:00
|
|
|
aggregate_carriers = None # All
|
2018-02-19 09:03:25 +00:00
|
|
|
|
2018-03-13 09:59:44 +00:00
|
|
|
if n_clusters == len(n.buses):
|
|
|
|
# Fast-path if no clustering is necessary
|
2018-03-13 10:50:06 +00:00
|
|
|
busmap = n.buses.index.to_series()
|
|
|
|
linemap = n.lines.index.to_series()
|
|
|
|
clustering = pypsa.networkclustering.Clustering(n, busmap, linemap, linemap, pd.Series(dtype='O'))
|
2018-03-13 09:59:44 +00:00
|
|
|
else:
|
2018-05-18 15:06:55 +00:00
|
|
|
line_length_factor = snakemake.config['lines']['length_factor']
|
2019-10-31 09:37:49 +00:00
|
|
|
hvac_overhead_cost = (load_costs(n.snapshot_weightings.sum()/8760,
|
2019-09-23 14:44:48 +00:00
|
|
|
tech_costs=snakemake.input.tech_costs,
|
|
|
|
config=snakemake.config['costs'],
|
|
|
|
elec_config=snakemake.config['electricity'])
|
2019-10-31 09:37:49 +00:00
|
|
|
.at['HVAC overhead', 'capital_cost'])
|
2018-12-31 13:53:11 +00:00
|
|
|
|
|
|
|
def consense(x):
|
|
|
|
v = x.iat[0]
|
|
|
|
assert ((x == v).all() or x.isnull().all()), (
|
|
|
|
"The `potential` configuration option must agree for all renewable carriers, for now!"
|
|
|
|
)
|
|
|
|
return v
|
|
|
|
potential_mode = consense(pd.Series([snakemake.config['renewable'][tech]['potential']
|
|
|
|
for tech in renewable_carriers]))
|
|
|
|
clustering = clustering_for_n_clusters(n, n_clusters, aggregate_carriers,
|
|
|
|
line_length_factor=line_length_factor,
|
2019-02-20 18:06:48 +00:00
|
|
|
potential_mode=potential_mode,
|
2019-09-23 14:44:48 +00:00
|
|
|
solver_name=snakemake.config['solving']['solver']['name'],
|
2019-11-07 14:38:25 +00:00
|
|
|
extended_link_costs=hvac_overhead_cost,
|
|
|
|
focus_weights=focus_weights)
|
2018-01-29 21:28:33 +00:00
|
|
|
|
2018-03-13 10:50:06 +00:00
|
|
|
clustering.network.export_to_netcdf(snakemake.output.network)
|
2018-09-27 10:18:47 +00:00
|
|
|
with pd.HDFStore(snakemake.output.clustermaps, mode='w') as store:
|
|
|
|
with pd.HDFStore(snakemake.input.clustermaps, mode='r') as clustermaps:
|
2018-07-10 14:31:57 +00:00
|
|
|
for attr in clustermaps.keys():
|
|
|
|
store.put(attr, clustermaps[attr], format="table", index=False)
|
2018-03-13 10:50:06 +00:00
|
|
|
for attr in ('busmap', 'linemap', 'linemap_positive', 'linemap_negative'):
|
2018-03-14 12:19:44 +00:00
|
|
|
store.put(attr, getattr(clustering, attr), format="table", index=False)
|
2018-03-13 10:50:06 +00:00
|
|
|
|
|
|
|
cluster_regions((clustering.busmap,))
|