Split simplify_network from cluster_network
This commit is contained in:
parent
2b05c79e94
commit
99299564d6
39
Snakefile
39
Snakefile
@ -4,6 +4,7 @@ localrules: all, prepare_links_p_nom, base_network, add_electricity, add_sectors
|
|||||||
|
|
||||||
wildcard_constraints:
|
wildcard_constraints:
|
||||||
lv="[0-9\.]+",
|
lv="[0-9\.]+",
|
||||||
|
simpl="[a-zA-Z0-9]*",
|
||||||
clusters="[0-9]+",
|
clusters="[0-9]+",
|
||||||
sectors="[+a-zA-Z0-9]+",
|
sectors="[+a-zA-Z0-9]+",
|
||||||
opts="[-+a-zA-Z0-9]+"
|
opts="[-+a-zA-Z0-9]+"
|
||||||
@ -72,16 +73,30 @@ rule add_electricity:
|
|||||||
resources: mem_mb=1000
|
resources: mem_mb=1000
|
||||||
script: "scripts/add_electricity.py"
|
script: "scripts/add_electricity.py"
|
||||||
|
|
||||||
rule cluster_network:
|
rule simplify_network:
|
||||||
input:
|
input:
|
||||||
network='networks/{network}.nc',
|
network='networks/{network}.nc',
|
||||||
regions_onshore="resources/regions_onshore.geojson",
|
regions_onshore="resources/regions_onshore.geojson",
|
||||||
regions_offshore="resources/regions_offshore.geojson"
|
regions_offshore="resources/regions_offshore.geojson"
|
||||||
output:
|
output:
|
||||||
network='networks/{network}_{clusters}.nc',
|
network='networks/{network}_s{simpl}.nc',
|
||||||
regions_onshore="resources/regions_onshore_{network}_{clusters}.geojson",
|
regions_onshore="resources/regions_onshore_{network}_s{simpl}.geojson",
|
||||||
regions_offshore="resources/regions_offshore_{network}_{clusters}.geojson"
|
regions_offshore="resources/regions_offshore_{network}_s{simpl}.geojson"
|
||||||
benchmark: "benchmarks/cluster_network/{network}_{clusters}"
|
benchmark: "benchmarks/simplify_network/{network}_s{simpl}"
|
||||||
|
threads: 1
|
||||||
|
resources: mem_mb=1000
|
||||||
|
script: "scripts/simplify_network.py"
|
||||||
|
|
||||||
|
rule cluster_network:
|
||||||
|
input:
|
||||||
|
network='networks/{network}_s{simpl}.nc',
|
||||||
|
regions_onshore="resources/regions_onshore_{network}_s{simpl}.geojson",
|
||||||
|
regions_offshore="resources/regions_offshore_{network}_s{simpl}.geojson"
|
||||||
|
output:
|
||||||
|
network='networks/{network}_s{simpl}_{clusters}.nc',
|
||||||
|
regions_onshore="resources/regions_onshore_{network}_s{simpl}_{clusters}.geojson",
|
||||||
|
regions_offshore="resources/regions_offshore_{network}_s{simpl}_{clusters}.geojson"
|
||||||
|
benchmark: "benchmarks/cluster_network/{network}_s{simpl}_{clusters}"
|
||||||
threads: 1
|
threads: 1
|
||||||
resources: mem_mb=1000
|
resources: mem_mb=1000
|
||||||
script: "scripts/cluster_network.py"
|
script: "scripts/cluster_network.py"
|
||||||
@ -97,20 +112,20 @@ rule add_sectors:
|
|||||||
script: "scripts/add_sectors.py"
|
script: "scripts/add_sectors.py"
|
||||||
|
|
||||||
rule prepare_network:
|
rule prepare_network:
|
||||||
input: 'networks/elec_{clusters}.nc'
|
input: 'networks/{network}_s{simpl}_{clusters}.nc'
|
||||||
output: 'networks/elec_{clusters}_lv{lv}_{opts}.nc'
|
output: 'networks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc'
|
||||||
threads: 1
|
threads: 1
|
||||||
resources: mem_mb=1000
|
resources: mem_mb=1000
|
||||||
script: "scripts/prepare_network.py"
|
script: "scripts/prepare_network.py"
|
||||||
|
|
||||||
rule solve_network:
|
rule solve_network:
|
||||||
input: "networks/elec_{clusters}_lv{lv}_{opts}.nc"
|
input: "networks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc"
|
||||||
output: "results/networks/{clusters}_lv{lv}_{opts}.nc"
|
output: "results/networks/{network}_s{simpl}_{clusters}_lv{lv}_{opts}.nc"
|
||||||
shadow: "shallow"
|
shadow: "shallow"
|
||||||
log:
|
log:
|
||||||
gurobi="logs/{clusters}_lv{lv}_{opts}_gurobi.log",
|
gurobi="logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_gurobi.log",
|
||||||
python="logs/{clusters}_lv{lv}_{opts}_python.log"
|
python="logs/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_python.log"
|
||||||
benchmark: "benchmarks/solve_network/{clusters}_lv{lv}_{opts}"
|
benchmark: "benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}"
|
||||||
threads: 4
|
threads: 4
|
||||||
resources: mem_mb=lambda w: 100000 * int(w.clusters) // 362
|
resources: mem_mb=lambda w: 100000 * int(w.clusters) // 362
|
||||||
script: "scripts/solve_network.py"
|
script: "scripts/solve_network.py"
|
||||||
|
@ -3,69 +3,30 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
idx = pd.IndexSlice
|
idx = pd.IndexSlice
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import scipy as sp
|
import scipy as sp
|
||||||
|
from scipy.sparse.csgraph import connected_components
|
||||||
import xarray as xr
|
import xarray as xr
|
||||||
import geopandas as gpd
|
import geopandas as gpd
|
||||||
import shapely
|
import shapely
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
from six import iteritems
|
||||||
from six.moves import reduce
|
from six.moves import reduce
|
||||||
|
|
||||||
import pypsa
|
import pypsa
|
||||||
|
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
|
||||||
from pypsa.networkclustering import (busmap_by_stubs, busmap_by_kmeans,
|
from pypsa.networkclustering import (busmap_by_stubs, busmap_by_kmeans,
|
||||||
_make_consense, get_clustering_from_busmap)
|
_make_consense, get_clustering_from_busmap,
|
||||||
|
aggregategenerators, aggregateoneport)
|
||||||
def normed(x):
|
def normed(x):
|
||||||
return (x/x.sum()).fillna(0.)
|
return (x/x.sum()).fillna(0.)
|
||||||
|
|
||||||
def simplify_network_to_380(n):
|
def weighting_for_country(n, x):
|
||||||
## All goes to v_nom == 380
|
|
||||||
|
|
||||||
n.buses['v_nom'] = 380.
|
|
||||||
|
|
||||||
linetype_380, = n.lines.loc[n.lines.v_nom == 380., 'type'].unique()
|
|
||||||
lines_v_nom_b = n.lines.v_nom != 380.
|
|
||||||
n.lines.loc[lines_v_nom_b, 'num_parallel'] *= (n.lines.loc[lines_v_nom_b, 'v_nom'] / 380.)**2
|
|
||||||
n.lines.loc[lines_v_nom_b, 'v_nom'] = 380.
|
|
||||||
n.lines.loc[lines_v_nom_b, 'type'] = linetype_380
|
|
||||||
|
|
||||||
# Replace transformers by lines
|
|
||||||
trafo_map = pd.Series(n.transformers.bus1.values, index=n.transformers.bus0.values)
|
|
||||||
trafo_map = trafo_map[~trafo_map.index.duplicated(keep='first')]
|
|
||||||
several_trafo_b = trafo_map.isin(trafo_map.index)
|
|
||||||
trafo_map.loc[several_trafo_b] = trafo_map.loc[several_trafo_b].map(trafo_map)
|
|
||||||
missing_buses_i = n.buses.index.difference(trafo_map.index)
|
|
||||||
trafo_map = trafo_map.append(pd.Series(missing_buses_i, missing_buses_i))
|
|
||||||
|
|
||||||
for c in n.one_port_components|n.branch_components:
|
|
||||||
df = n.df(c)
|
|
||||||
for col in df.columns:
|
|
||||||
if col.startswith('bus'):
|
|
||||||
df[col] = df[col].map(trafo_map)
|
|
||||||
|
|
||||||
n.mremove("Transformer", n.transformers.index)
|
|
||||||
n.mremove("Bus", n.buses.index.difference(trafo_map))
|
|
||||||
|
|
||||||
return n, trafo_map
|
|
||||||
|
|
||||||
def remove_stubs(n):
|
|
||||||
n.determine_network_topology()
|
|
||||||
|
|
||||||
busmap = busmap_by_stubs(n, ['carrier', 'country'])
|
|
||||||
|
|
||||||
n.buses.loc[busmap.index, ['x','y']] = n.buses.loc[busmap, ['x','y']].values
|
|
||||||
|
|
||||||
clustering = get_clustering_from_busmap(
|
|
||||||
n, busmap,
|
|
||||||
bus_strategies=dict(country=_make_consense("Bus", "country")),
|
|
||||||
line_length_factor=snakemake.config['lines']['length_factor'],
|
|
||||||
aggregate_generators_weighted=True,
|
|
||||||
aggregate_one_ports=["Load", "StorageUnit"]
|
|
||||||
)
|
|
||||||
|
|
||||||
return clustering.network, busmap
|
|
||||||
|
|
||||||
def weighting_for_country(x):
|
|
||||||
conv_carriers = {'OCGT', 'PHS', 'hydro'}
|
conv_carriers = {'OCGT', 'PHS', 'hydro'}
|
||||||
gen = (n
|
gen = (n
|
||||||
.generators.loc[n.generators.carrier.isin(conv_carriers)]
|
.generators.loc[n.generators.carrier.isin(conv_carriers)]
|
||||||
@ -84,8 +45,6 @@ def weighting_for_country(x):
|
|||||||
w= g + l
|
w= g + l
|
||||||
return (w * (100. / w.max())).astype(int)
|
return (w * (100. / w.max())).astype(int)
|
||||||
|
|
||||||
return weighting_for_country
|
|
||||||
|
|
||||||
|
|
||||||
## Plot weighting for Germany
|
## Plot weighting for Germany
|
||||||
|
|
||||||
@ -98,7 +57,7 @@ def plot_weighting(n, country):
|
|||||||
|
|
||||||
# # Determining the number of clusters per country
|
# # Determining the number of clusters per country
|
||||||
|
|
||||||
def distribute_clusters(n_clusters):
|
def distribute_clusters(n, n_clusters):
|
||||||
load = n.loads_t.p_set.mean().groupby(n.loads.bus).sum()
|
load = n.loads_t.p_set.mean().groupby(n.loads.bus).sum()
|
||||||
loadc = load.groupby([n.buses.country, n.buses.sub_network]).sum()
|
loadc = load.groupby([n.buses.country, n.buses.sub_network]).sum()
|
||||||
n_cluster_per_country = n_clusters * normed(loadc)
|
n_cluster_per_country = n_clusters * normed(loadc)
|
||||||
@ -117,44 +76,41 @@ def distribute_clusters(n_clusters):
|
|||||||
|
|
||||||
return n_cluster_per_country.astype(int)
|
return n_cluster_per_country.astype(int)
|
||||||
|
|
||||||
def distribute_clusters_exactly(n_clusters):
|
def distribute_clusters_exactly(n, n_clusters):
|
||||||
for d in [0, 1, -1, 2, -2]:
|
for d in [0, 1, -1, 2, -2]:
|
||||||
n_cluster_per_country = distribute_clusters(n_clusters + d)
|
n_cluster_per_country = distribute_clusters(n, n_clusters + d)
|
||||||
if n_cluster_per_country.sum() == n_clusters:
|
if n_cluster_per_country.sum() == n_clusters:
|
||||||
return n_cluster_per_country
|
return n_cluster_per_country
|
||||||
else:
|
else:
|
||||||
return distribute_clusters(n_clusters)
|
return distribute_clusters(n, n_clusters)
|
||||||
|
|
||||||
def busmap_for_n_clusters(n_clusters):
|
def busmap_for_n_clusters(n, n_clusters):
|
||||||
n_clusters = distribute_clusters_exactly(n_clusters)
|
n.determine_network_topology()
|
||||||
|
|
||||||
|
n_clusters = distribute_clusters_exactly(n, n_clusters)
|
||||||
def busmap_for_country(x):
|
def busmap_for_country(x):
|
||||||
prefix = x.name[0] + x.name[1] + ' '
|
prefix = x.name[0] + x.name[1] + ' '
|
||||||
if len(x) == 1:
|
if len(x) == 1:
|
||||||
return pd.Series(prefix + '0', index=x.index)
|
return pd.Series(prefix + '0', index=x.index)
|
||||||
weight = weighting_for_country(x)
|
weight = weighting_for_country(n, x)
|
||||||
return prefix + busmap_by_kmeans(n, weight, n_clusters[x.name], buses_i=x.index)
|
return prefix + busmap_by_kmeans(n, weight, n_clusters[x.name], buses_i=x.index)
|
||||||
return n.buses.groupby(['country', 'sub_network'], group_keys=False).apply(busmap_for_country)
|
return n.buses.groupby(['country', 'sub_network'], group_keys=False).apply(busmap_for_country)
|
||||||
|
|
||||||
def plot_busmap_for_n_clusters(n_clusters=50):
|
def plot_busmap_for_n_clusters(n, n_clusters=50):
|
||||||
busmap = busmap_for_n_clusters(n_clusters)
|
busmap = busmap_for_n_clusters(n, n_clusters)
|
||||||
cs = busmap.unique()
|
cs = busmap.unique()
|
||||||
cr = sns.color_palette("hls", len(cs))
|
cr = sns.color_palette("hls", len(cs))
|
||||||
n.plot(bus_colors=busmap.map(dict(zip(cs, cr))))
|
n.plot(bus_colors=busmap.map(dict(zip(cs, cr))))
|
||||||
del cs, cr
|
del cs, cr
|
||||||
|
|
||||||
def clustering_for_n_clusters(n_clusters):
|
def clustering_for_n_clusters(n, n_clusters):
|
||||||
clustering = get_clustering_from_busmap(
|
clustering = get_clustering_from_busmap(
|
||||||
n, busmap_for_n_clusters(n_clusters),
|
n, busmap_for_n_clusters(n, n_clusters),
|
||||||
bus_strategies=dict(country=_make_consense("Bus", "country")),
|
bus_strategies=dict(country=_make_consense("Bus", "country")),
|
||||||
aggregate_generators_weighted=True,
|
aggregate_generators_weighted=True,
|
||||||
aggregate_one_ports=["Load", "StorageUnit"]
|
aggregate_one_ports=["Load", "StorageUnit"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# set n-1 security margin to 0.5 for 37 clusters and to 0.7 from 200 clusters
|
|
||||||
# (there was already one of 0.7 in-place)
|
|
||||||
s_max_pu = np.clip(0.5 + 0.2 * (n_clusters - 37) / (200 - 37), 0.5, 0.7)
|
|
||||||
clustering.network.lines['s_max_pu'] = s_max_pu
|
|
||||||
|
|
||||||
return clustering
|
return clustering
|
||||||
|
|
||||||
def save_to_geojson(s, fn):
|
def save_to_geojson(s, fn):
|
||||||
@ -162,40 +118,46 @@ def save_to_geojson(s, fn):
|
|||||||
os.unlink(fn)
|
os.unlink(fn)
|
||||||
s.reset_index().to_file(fn, driver='GeoJSON')
|
s.reset_index().to_file(fn, driver='GeoJSON')
|
||||||
|
|
||||||
def cluster_regions(busmaps):
|
def cluster_regions(busmaps, input=None, output=None):
|
||||||
|
if input is None: input = snakemake.input
|
||||||
|
if output is None: output = snakemake.output
|
||||||
|
|
||||||
busmap = reduce(lambda x, y: x.map(y), busmaps[1:], busmaps[0])
|
busmap = reduce(lambda x, y: x.map(y), busmaps[1:], busmaps[0])
|
||||||
|
|
||||||
for which in ('regions_onshore', 'regions_offshore'):
|
for which in ('regions_onshore', 'regions_offshore'):
|
||||||
regions = gpd.read_file(getattr(snakemake.input, which)).set_index('name')
|
regions = gpd.read_file(getattr(input, which)).set_index('name')
|
||||||
geom_c = regions.geometry.groupby(clustering.busmap).apply(shapely.ops.cascaded_union)
|
geom_c = regions.geometry.groupby(busmap).apply(shapely.ops.cascaded_union)
|
||||||
regions_c = gpd.GeoDataFrame(dict(geometry=geom_c))
|
regions_c = gpd.GeoDataFrame(dict(geometry=geom_c))
|
||||||
save_to_geojson(regions_c, getattr(snakemake.output, which))
|
regions_c.index.name = 'name'
|
||||||
|
save_to_geojson(regions_c, getattr(output, which))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Detect running outside of snakemake and mock snakemake for testing
|
# Detect running outside of snakemake and mock snakemake for testing
|
||||||
if 'snakemake' not in globals():
|
if 'snakemake' not in globals():
|
||||||
from vresutils import Dict
|
from vresutils.snakemake import MockSnakemake, Dict
|
||||||
import yaml
|
snakemake = MockSnakemake(
|
||||||
snakemake = Dict()
|
path='..',
|
||||||
with open('../config.yaml') as f:
|
wildcards=Dict(network='elec', simpl='', clusters='45'),
|
||||||
snakemake.config = yaml.load(f)
|
input=Dict(
|
||||||
snakemake.wildcards = Dict(clusters='37')
|
network='networks/{network}_s{simpl}.nc',
|
||||||
snakemake.input = Dict(network='../networks/elec.nc',
|
regions_onshore='resources/regions_onshore_{network}_s{simpl}.geojson',
|
||||||
regions_onshore='../resources/regions_onshore.geojson',
|
regions_offshore='resources/regions_offshore_{network}_s{simpl}.geojson'
|
||||||
regions_offshore='../resources/regions_offshore.geojson')
|
),
|
||||||
snakemake.output = Dict(network='../networks/elec_{clusters}.nc'.format(**snakemake.wildcards),
|
output=Dict(
|
||||||
regions_onshore='../resources/regions_onshore_{clusters}.geojson'.format(**snakemake.wildcards),
|
network='networks/{network}_s{simpl}_{clusters}.nc',
|
||||||
regions_offshore='../resources/regions_offshore_{clusters}.geojson'.format(**snakemake.wildcards))
|
regions_onshore='resources/regions_onshore_{network}_s{simpl}_{clusters}.geojson',
|
||||||
|
regions_offshore='resources/regions_offshore_{network}_s{simpl}_{clusters}.geojson'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(snakemake.config['logging_level'])
|
||||||
|
|
||||||
n = pypsa.Network(snakemake.input.network)
|
n = pypsa.Network(snakemake.input.network)
|
||||||
|
|
||||||
n, trafo_map = simplify_network_to_380(n)
|
|
||||||
|
|
||||||
n, stub_map = remove_stubs(n)
|
|
||||||
|
|
||||||
n_clusters = int(snakemake.wildcards.clusters)
|
n_clusters = int(snakemake.wildcards.clusters)
|
||||||
clustering = clustering_for_n_clusters(n_clusters)
|
clustering = clustering_for_n_clusters(n, n_clusters)
|
||||||
|
|
||||||
clustering.network.export_to_netcdf(snakemake.output.network)
|
clustering.network.export_to_netcdf(snakemake.output.network)
|
||||||
|
|
||||||
cluster_regions((trafo_map, stub_map, clustering.busmap))
|
cluster_regions((clustering.busmap,))
|
||||||
|
218
scripts/simplify_network.py
Normal file
218
scripts/simplify_network.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
idx = pd.IndexSlice
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import numpy as np
|
||||||
|
import scipy as sp
|
||||||
|
from scipy.sparse.csgraph import connected_components
|
||||||
|
import xarray as xr
|
||||||
|
import geopandas as gpd
|
||||||
|
import shapely
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
from six import iteritems
|
||||||
|
from six.moves import reduce
|
||||||
|
|
||||||
|
import pypsa
|
||||||
|
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
|
||||||
|
from pypsa.networkclustering import (busmap_by_stubs, busmap_by_kmeans,
|
||||||
|
_make_consense, get_clustering_from_busmap,
|
||||||
|
aggregategenerators, aggregateoneport)
|
||||||
|
|
||||||
|
from cluster_network import clustering_for_n_clusters, cluster_regions
|
||||||
|
|
||||||
|
def simplify_network_to_380(n):
|
||||||
|
## All goes to v_nom == 380
|
||||||
|
logger.info("Mapping all network lines onto a single 380kV layer")
|
||||||
|
|
||||||
|
n.buses['v_nom'] = 380.
|
||||||
|
|
||||||
|
linetype_380, = n.lines.loc[n.lines.v_nom == 380., 'type'].unique()
|
||||||
|
lines_v_nom_b = n.lines.v_nom != 380.
|
||||||
|
n.lines.loc[lines_v_nom_b, 'num_parallel'] *= (n.lines.loc[lines_v_nom_b, 'v_nom'] / 380.)**2
|
||||||
|
n.lines.loc[lines_v_nom_b, 'v_nom'] = 380.
|
||||||
|
n.lines.loc[lines_v_nom_b, 'type'] = linetype_380
|
||||||
|
|
||||||
|
# Replace transformers by lines
|
||||||
|
trafo_map = pd.Series(n.transformers.bus1.values, index=n.transformers.bus0.values)
|
||||||
|
trafo_map = trafo_map[~trafo_map.index.duplicated(keep='first')]
|
||||||
|
several_trafo_b = trafo_map.isin(trafo_map.index)
|
||||||
|
trafo_map.loc[several_trafo_b] = trafo_map.loc[several_trafo_b].map(trafo_map)
|
||||||
|
missing_buses_i = n.buses.index.difference(trafo_map.index)
|
||||||
|
trafo_map = trafo_map.append(pd.Series(missing_buses_i, missing_buses_i))
|
||||||
|
|
||||||
|
for c in n.one_port_components|n.branch_components:
|
||||||
|
df = n.df(c)
|
||||||
|
for col in df.columns:
|
||||||
|
if col.startswith('bus'):
|
||||||
|
df[col] = df[col].map(trafo_map)
|
||||||
|
|
||||||
|
n.mremove("Transformer", n.transformers.index)
|
||||||
|
n.mremove("Bus", n.buses.index.difference(trafo_map))
|
||||||
|
|
||||||
|
return n, trafo_map
|
||||||
|
|
||||||
|
def _aggregate_and_move_components(n, busmap, aggregate_one_ports={"Load", "StorageUnit"}):
|
||||||
|
def replace_components(n, c, df, pnl):
|
||||||
|
n.mremove(c, n.df(c).index)
|
||||||
|
|
||||||
|
import_components_from_dataframe(n, df, c)
|
||||||
|
for attr, df in iteritems(pnl):
|
||||||
|
if not df.empty:
|
||||||
|
import_series_from_dataframe(n, df, c, attr)
|
||||||
|
|
||||||
|
generators, generators_pnl = aggregategenerators(n, busmap)
|
||||||
|
replace_components(n, "Generator", generators, generators_pnl)
|
||||||
|
|
||||||
|
for one_port in aggregate_one_ports:
|
||||||
|
df, pnl = aggregateoneport(n, busmap, component=one_port)
|
||||||
|
replace_components(n, one_port, df, pnl)
|
||||||
|
|
||||||
|
buses_to_del = n.buses.index.difference(busmap)
|
||||||
|
n.mremove("Bus", buses_to_del)
|
||||||
|
for c in n.branch_components:
|
||||||
|
df = n.df(c)
|
||||||
|
n.mremove(c, df.index[df.bus0.isin(buses_to_del) | df.bus1.isin(buses_to_del)])
|
||||||
|
|
||||||
|
def simplify_links(n):
|
||||||
|
## Complex multi-node links are folded into end-points
|
||||||
|
logger.info("Simplifying connected link components")
|
||||||
|
|
||||||
|
# Determine connected link components, ignore all links but DC
|
||||||
|
adjacency_matrix = n.adjacency_matrix(branch_components=['Link'],
|
||||||
|
weights=dict(Link=(n.links.carrier == 'DC').astype(float)))
|
||||||
|
|
||||||
|
_, labels = connected_components(adjacency_matrix, directed=False)
|
||||||
|
labels = pd.Series(labels, n.buses.index)
|
||||||
|
|
||||||
|
G = n.graph()
|
||||||
|
|
||||||
|
def split_links(nodes):
|
||||||
|
nodes = frozenset(nodes)
|
||||||
|
|
||||||
|
seen = set()
|
||||||
|
supernodes = {m for m in nodes
|
||||||
|
if len(G.adj[m]) > 2 or (set(G.adj[m]) - nodes)}
|
||||||
|
|
||||||
|
for u in supernodes:
|
||||||
|
for m, ls in iteritems(G.adj[u]):
|
||||||
|
if m not in nodes or m in seen: continue
|
||||||
|
|
||||||
|
buses = [u, m]
|
||||||
|
links = [list(ls)] #[name for name in ls]]
|
||||||
|
|
||||||
|
while m not in (supernodes | seen):
|
||||||
|
seen.add(m)
|
||||||
|
for m2, ls in iteritems(G.adj[m]):
|
||||||
|
if m2 in seen or m2 == u: continue
|
||||||
|
buses.append(m2)
|
||||||
|
links.append(list(ls)) # [name for name in ls])
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# stub
|
||||||
|
break
|
||||||
|
m = m2
|
||||||
|
if m != u:
|
||||||
|
yield pd.Index((u, m)), buses, links
|
||||||
|
seen.add(u)
|
||||||
|
|
||||||
|
busmap = n.buses.index.to_series()
|
||||||
|
|
||||||
|
for lbl in labels.value_counts().loc[lambda s: s > 2].index:
|
||||||
|
|
||||||
|
for b, buses, links in split_links(labels.index[labels == lbl]):
|
||||||
|
if len(buses) <= 2: continue
|
||||||
|
|
||||||
|
logger.debug('nodes = {}'.format(labels.index[labels == lbl]))
|
||||||
|
logger.debug('b = {}\nbuses = {}\nlinks = {}'.format(b, buses, links))
|
||||||
|
|
||||||
|
m = sp.spatial.distance_matrix(n.buses.loc[b, ['x', 'y']],
|
||||||
|
n.buses.loc[buses[1:-1], ['x', 'y']])
|
||||||
|
busmap.loc[buses] = b[np.r_[0, m.argmin(axis=0), 1]]
|
||||||
|
all_links = [i for _, i in sum(links, [])]
|
||||||
|
|
||||||
|
s_max_pu = snakemake.config['links']['s_max_pu']
|
||||||
|
name = n.links.loc[all_links, 'length'].idxmax() + '+{}'.format(len(links) - 1)
|
||||||
|
params = dict(
|
||||||
|
carrier='DC',
|
||||||
|
bus0=b[0], bus1=b[1],
|
||||||
|
length=sum(n.links.loc[[i for _, i in l], 'length'].mean() for l in links),
|
||||||
|
p_nom=min(n.links.loc[[i for _, i in l], 'p_nom'].sum() for l in links),
|
||||||
|
p_max_pu=s_max_pu,
|
||||||
|
p_min_pu=-s_max_pu,
|
||||||
|
underground=False,
|
||||||
|
under_construction=False
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Joining the links {} connecting the buses {} to simple link {}".format(", ".join(all_links), ", ".join(buses), name))
|
||||||
|
|
||||||
|
n.mremove("Link", all_links)
|
||||||
|
|
||||||
|
static_attrs = n.components["Link"]["attrs"].loc[lambda df: df.static]
|
||||||
|
for attr, default in static_attrs.default.iteritems(): params.setdefault(attr, default)
|
||||||
|
n.links.loc[name] = pd.Series(params)
|
||||||
|
|
||||||
|
# n.add("Link", **params)
|
||||||
|
|
||||||
|
logger.debug("Collecting all components using the busmap")
|
||||||
|
|
||||||
|
_aggregate_and_move_components(n, busmap)
|
||||||
|
return n, busmap
|
||||||
|
|
||||||
|
def remove_stubs(n):
|
||||||
|
logger.info("Removing stubs")
|
||||||
|
|
||||||
|
busmap = busmap_by_stubs(n, ['country'])
|
||||||
|
_aggregate_and_move_components(n, busmap)
|
||||||
|
|
||||||
|
return n, busmap
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
path='..',
|
||||||
|
wildcards=Dict(simpl=''),
|
||||||
|
input=Dict(
|
||||||
|
network='networks/elec.nc',
|
||||||
|
regions_onshore='resources/regions_onshore.geojson',
|
||||||
|
regions_offshore='resources/regions_offshore.geojson'
|
||||||
|
),
|
||||||
|
output=Dict(
|
||||||
|
network='networks/elec_s{simpl}.nc',
|
||||||
|
regions_onshore='resources/regions_onshore_s{simpl}.geojson',
|
||||||
|
regions_offshore='resources/regions_offshore_s{simpl}.geojson'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(snakemake.config['logging_level'])
|
||||||
|
|
||||||
|
n = pypsa.Network(snakemake.input.network)
|
||||||
|
|
||||||
|
n, trafo_map = simplify_network_to_380(n)
|
||||||
|
|
||||||
|
n, simplify_links_map = simplify_links(n)
|
||||||
|
|
||||||
|
n, stub_map = remove_stubs(n)
|
||||||
|
|
||||||
|
busmaps = [trafo_map, simplify_links_map, stub_map]
|
||||||
|
|
||||||
|
if snakemake.wildcards.simpl:
|
||||||
|
n_clusters = int(snakemake.wildcards.simpl)
|
||||||
|
clustering = clustering_for_n_clusters(n_clusters)
|
||||||
|
|
||||||
|
n = clustering.network
|
||||||
|
busmaps.append(clustering.busmap)
|
||||||
|
|
||||||
|
n.export_to_netcdf(snakemake.output.network)
|
||||||
|
|
||||||
|
cluster_regions(busmaps, snakemake.input, snakemake.output)
|
Loading…
Reference in New Issue
Block a user