Merge remote-tracking branch 'origin/master' into graphics-to-doc-img
This commit is contained in:
commit
9318dd6b2b
Binary file not shown.
Before Width: | Height: | Size: 227 KiB After Width: | Height: | Size: 208 KiB |
@ -135,7 +135,7 @@ as part of the `Stromnetze Research Initiative
|
|||||||
Workflow
|
Workflow
|
||||||
========
|
========
|
||||||
|
|
||||||
.. image:: ../doc/img/workflow.png
|
.. image:: img/workflow.png
|
||||||
:class: full-width
|
:class: full-width
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
|
@ -21,8 +21,7 @@ Having downloaded the necessary data,
|
|||||||
With these and the externally extracted ENTSO-E online map topology
|
With these and the externally extracted ENTSO-E online map topology
|
||||||
(``data/entsoegridkit``), it can build a base PyPSA network with the following rules:
|
(``data/entsoegridkit``), it can build a base PyPSA network with the following rules:
|
||||||
|
|
||||||
- :mod:`base_network` builds and stores the base network with all buses, HVAC lines and HVDC links, while
|
- :mod:`base_network` builds and stores the base network with all buses, HVAC lines and HVDC links, and determines `Voronoi cells <https://en.wikipedia.org/wiki/Voronoi_diagram>`__ for all substations.
|
||||||
- :mod:`build_bus_regions` determines `Voronoi cells <https://en.wikipedia.org/wiki/Voronoi_diagram>`__ for all substations.
|
|
||||||
|
|
||||||
Then the process continues by calculating conventional power plant capacities, potentials, and per-unit availability time series for variable renewable energy carriers and hydro power plants with the following rules:
|
Then the process continues by calculating conventional power plant capacities, potentials, and per-unit availability time series for variable renewable energy carriers and hydro power plants with the following rules:
|
||||||
|
|
||||||
@ -35,13 +34,6 @@ Then the process continues by calculating conventional power plant capacities, p
|
|||||||
The central rule :mod:`add_electricity` then ties all the different data inputs
|
The central rule :mod:`add_electricity` then ties all the different data inputs
|
||||||
together into a detailed PyPSA network stored in ``networks/elec.nc``.
|
together into a detailed PyPSA network stored in ``networks/elec.nc``.
|
||||||
|
|
||||||
.. _busregions:
|
|
||||||
|
|
||||||
Rule ``build_bus_regions``
|
|
||||||
=============================
|
|
||||||
|
|
||||||
.. automodule:: build_bus_regions
|
|
||||||
|
|
||||||
.. _cutout:
|
.. _cutout:
|
||||||
|
|
||||||
Rule ``build_cutout``
|
Rule ``build_cutout``
|
||||||
|
@ -186,7 +186,7 @@ Upcoming Release
|
|||||||
|
|
||||||
* Data on existing renewable capacities is now consistently taken from powerplantmatching (instead of being retrieved separately); the dataset has also been updated to include 2023 values.
|
* Data on existing renewable capacities is now consistently taken from powerplantmatching (instead of being retrieved separately); the dataset has also been updated to include 2023 values.
|
||||||
|
|
||||||
* Added shapes to .nc file for different stages of the network object in `base_network`, `build_bus_regions`, and `cluster_network`.
|
* Added shapes to .nc file for different stages of the network object in `base_network`, `simplify_network`, and `cluster_network`; the `build_bus_regions` rule is now integrated into the `base_network` rule.
|
||||||
|
|
||||||
* Fix p_nom_min of renewables generators for myopic approach and add check of existing capacities in `add_land_use_constraint_m`.
|
* Fix p_nom_min of renewables generators for myopic approach and add check of existing capacities in `add_land_use_constraint_m`.
|
||||||
|
|
||||||
|
124
doc/tutorial.rst
124
doc/tutorial.rst
@ -133,32 +133,31 @@ This triggers a workflow of multiple preceding jobs that depend on each rule's i
|
|||||||
graph[bgcolor=white, margin=0];
|
graph[bgcolor=white, margin=0];
|
||||||
node[shape=box, style=rounded, fontname=sans, fontsize=10, penwidth=2];
|
node[shape=box, style=rounded, fontname=sans, fontsize=10, penwidth=2];
|
||||||
edge[penwidth=2, color=grey];
|
edge[penwidth=2, color=grey];
|
||||||
0[label = "solve_network", color = "0.39 0.6 0.85", style="rounded"];
|
0[label = "solve_network", color = "0.24 0.6 0.85", style="rounded"];
|
||||||
1[label = "prepare_network\nll: copt\nopts: Co2L-24H", color = "0.29 0.6 0.85", style="rounded"];
|
1[label = "prepare_network\nll: vopt\nopts: Co2L-3H", color = "0.10 0.6 0.85", style="rounded"];
|
||||||
2[label = "add_extra_components", color = "0.28 0.6 0.85", style="rounded"];
|
2[label = "add_extra_components", color = "0.33 0.6 0.85", style="rounded"];
|
||||||
3[label = "cluster_network\nclusters: 6", color = "0.19 0.6 0.85", style="rounded"];
|
3[label = "cluster_network\nclusters: 128", color = "0.59 0.6 0.85", style="rounded"];
|
||||||
4[label = "simplify_network\nsimpl: ", color = "0.01 0.6 0.85", style="rounded"];
|
4[label = "simplify_network\nsimpl: ", color = "0.18 0.6 0.85", style="rounded"];
|
||||||
5[label = "add_electricity", color = "0.49 0.6 0.85", style="rounded"];
|
5[label = "add_electricity", color = "0.48 0.6 0.85", style="rounded"];
|
||||||
6[label = "build_renewable_profiles\ntechnology: solar", color = "0.21 0.6 0.85", style="rounded"];
|
6[label = "build_renewable_profiles\ntechnology: solar", color = "0.29 0.6 0.85", style="rounded"];
|
||||||
7[label = "base_network", color = "0.27 0.6 0.85", style="rounded"];
|
7[label = "base_network", color = "0.30 0.6 0.85", style="rounded"];
|
||||||
8[label = "build_shapes", color = "0.26 0.6 0.85", style="rounded"];
|
8[label = "build_shapes", color = "0.61 0.6 0.85", style="rounded"];
|
||||||
9[label = "retrieve_databundle", color = "0.59 0.6 0.85", style="rounded"];
|
9[label = "retrieve_databundle", color = "0.06 0.6 0.85", style="rounded"];
|
||||||
10[label = "retrieve_natura_raster", color = "0.47 0.6 0.85", style="rounded"];
|
10[label = "retrieve_natura_raster", color = "0.03 0.6 0.85", style="rounded"];
|
||||||
11[label = "build_bus_regions", color = "0.13 0.6 0.85", style="rounded"];
|
11[label = "retrieve_cutout\ncutout: europe-2013-sarah", color = "0.50 0.6 0.85", style="rounded"];
|
||||||
12[label = "retrieve_cutout\ncutout: be-03-2013-era5", color = "0.36 0.6 0.85", style="rounded,dashed"];
|
12[label = "build_renewable_profiles\ntechnology: onwind", color = "0.29 0.6 0.85", style="rounded"];
|
||||||
13[label = "build_renewable_profiles\ntechnology: onwind", color = "0.21 0.6 0.85", style="rounded"];
|
13[label = "retrieve_cutout\ncutout: europe-2013-era5", color = "0.50 0.6 0.85", style="rounded"];
|
||||||
14[label = "build_renewable_profiles\ntechnology: offwind-ac", color = "0.21 0.6 0.85", style="rounded"];
|
14[label = "build_renewable_profiles\ntechnology: offwind-ac", color = "0.29 0.6 0.85", style="rounded"];
|
||||||
15[label = "build_ship_raster", color = "0.00 0.6 0.85", style="rounded"];
|
15[label = "build_ship_raster", color = "0.16 0.6 0.85", style="rounded"];
|
||||||
16[label = "retrieve_ship_raster", color = "0.51 0.6 0.85", style="rounded,dashed"];
|
16[label = "retrieve_ship_raster", color = "0.53 0.6 0.85", style="rounded"];
|
||||||
17[label = "build_renewable_profiles\ntechnology: offwind-dc", color = "0.21 0.6 0.85", style="rounded"];
|
17[label = "build_renewable_profiles\ntechnology: offwind-dc", color = "0.29 0.6 0.85", style="rounded"];
|
||||||
18[label = "build_line_rating", color = "0.05 0.6 0.85", style="rounded"];
|
18[label = "build_hydro_profile", color = "0.47 0.6 0.85", style="rounded"];
|
||||||
19[label = "retrieve_cost_data\nyear: 2030", color = "0.15 0.6 0.85", style="rounded"];
|
19[label = "retrieve_cost_data\nyear: 2030", color = "0.21 0.6 0.85", style="rounded"];
|
||||||
20[label = "build_powerplants", color = "0.54 0.6 0.85", style="rounded"];
|
20[label = "build_powerplants", color = "0.56 0.6 0.85", style="rounded"];
|
||||||
21[label = "build_electricity_demand", color = "0.52 0.6 0.85", style="rounded"];
|
21[label = "build_electricity_demand", color = "0.54 0.6 0.85", style="rounded"];
|
||||||
22[label = "retrieve_electricity_demand", color = "0.22 0.6 0.85", style="rounded"];
|
22[label = "retrieve_electricity_demand", color = "0.34 0.6 0.85", style="rounded"];
|
||||||
23[label = "copy_config", color = "0.44 0.6 0.85", style="rounded"];
|
23[label = "retrieve_synthetic_electricity_demand", color = "0.65 0.6 0.85", style="rounded"];
|
||||||
1 -> 0
|
1 -> 0
|
||||||
23 -> 0
|
|
||||||
2 -> 1
|
2 -> 1
|
||||||
19 -> 1
|
19 -> 1
|
||||||
3 -> 2
|
3 -> 2
|
||||||
@ -167,15 +166,14 @@ This triggers a workflow of multiple preceding jobs that depend on each rule's i
|
|||||||
19 -> 3
|
19 -> 3
|
||||||
5 -> 4
|
5 -> 4
|
||||||
19 -> 4
|
19 -> 4
|
||||||
11 -> 4
|
7 -> 4
|
||||||
6 -> 5
|
6 -> 5
|
||||||
13 -> 5
|
12 -> 5
|
||||||
14 -> 5
|
14 -> 5
|
||||||
17 -> 5
|
17 -> 5
|
||||||
7 -> 5
|
|
||||||
18 -> 5
|
18 -> 5
|
||||||
|
7 -> 5
|
||||||
19 -> 5
|
19 -> 5
|
||||||
11 -> 5
|
|
||||||
20 -> 5
|
20 -> 5
|
||||||
9 -> 5
|
9 -> 5
|
||||||
21 -> 5
|
21 -> 5
|
||||||
@ -185,37 +183,32 @@ This triggers a workflow of multiple preceding jobs that depend on each rule's i
|
|||||||
10 -> 6
|
10 -> 6
|
||||||
8 -> 6
|
8 -> 6
|
||||||
11 -> 6
|
11 -> 6
|
||||||
12 -> 6
|
|
||||||
8 -> 7
|
8 -> 7
|
||||||
9 -> 8
|
9 -> 8
|
||||||
8 -> 11
|
7 -> 12
|
||||||
7 -> 11
|
9 -> 12
|
||||||
7 -> 13
|
10 -> 12
|
||||||
9 -> 13
|
8 -> 12
|
||||||
10 -> 13
|
13 -> 12
|
||||||
8 -> 13
|
|
||||||
11 -> 13
|
|
||||||
12 -> 13
|
|
||||||
7 -> 14
|
7 -> 14
|
||||||
9 -> 14
|
9 -> 14
|
||||||
10 -> 14
|
10 -> 14
|
||||||
15 -> 14
|
15 -> 14
|
||||||
8 -> 14
|
8 -> 14
|
||||||
11 -> 14
|
13 -> 14
|
||||||
12 -> 14
|
|
||||||
16 -> 15
|
16 -> 15
|
||||||
12 -> 15
|
13 -> 15
|
||||||
7 -> 17
|
7 -> 17
|
||||||
9 -> 17
|
9 -> 17
|
||||||
10 -> 17
|
10 -> 17
|
||||||
15 -> 17
|
15 -> 17
|
||||||
8 -> 17
|
8 -> 17
|
||||||
11 -> 17
|
13 -> 17
|
||||||
12 -> 17
|
8 -> 18
|
||||||
7 -> 18
|
13 -> 18
|
||||||
12 -> 18
|
|
||||||
7 -> 20
|
7 -> 20
|
||||||
22 -> 21
|
22 -> 21
|
||||||
|
23 -> 21
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
|
||||||
@ -226,28 +219,25 @@ In the terminal, this will show up as a list of jobs to be run:
|
|||||||
|
|
||||||
Building DAG of jobs...
|
Building DAG of jobs...
|
||||||
Job stats:
|
Job stats:
|
||||||
job count
|
job count
|
||||||
--------------------------- -------
|
------------------------------------- -------
|
||||||
add_electricity 1
|
add_electricity 1
|
||||||
add_extra_components 1
|
add_extra_components 1
|
||||||
base_network 1
|
build_line_rating 1
|
||||||
build_bus_regions 1
|
build_renewable_profiles 4
|
||||||
build_electricity_demand 1
|
build_ship_raster 1
|
||||||
build_line_rating 1
|
cluster_network 1
|
||||||
build_powerplants 1
|
prepare_network 1
|
||||||
build_renewable_profiles 4
|
retrieve_cost_data 1
|
||||||
build_shapes 1
|
retrieve_cutout 1
|
||||||
build_ship_raster 1
|
retrieve_databundle 1
|
||||||
cluster_network 1
|
retrieve_electricity_demand 1
|
||||||
copy_config 1
|
retrieve_natura_raster 1
|
||||||
prepare_network 1
|
retrieve_ship_raster 1
|
||||||
retrieve_cost_data 1
|
retrieve_synthetic_electricity_demand 1
|
||||||
retrieve_databundle 1
|
simplify_network 1
|
||||||
retrieve_electricity_demand 1
|
solve_network 1
|
||||||
retrieve_natura_raster 1
|
total 19
|
||||||
simplify_network 1
|
|
||||||
solve_network 1
|
|
||||||
total 22
|
|
||||||
|
|
||||||
|
|
||||||
``snakemake`` then runs these jobs in the correct order.
|
``snakemake`` then runs these jobs in the correct order.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -86,7 +86,9 @@ rule base_network:
|
|||||||
offshore_shapes=resources("offshore_shapes.geojson"),
|
offshore_shapes=resources("offshore_shapes.geojson"),
|
||||||
europe_shape=resources("europe_shape.geojson"),
|
europe_shape=resources("europe_shape.geojson"),
|
||||||
output:
|
output:
|
||||||
resources("networks/base.nc"),
|
base_network=resources("networks/base.nc"),
|
||||||
|
regions_onshore=resources("regions_onshore.geojson"),
|
||||||
|
regions_offshore=resources("regions_offshore.geojson"),
|
||||||
log:
|
log:
|
||||||
logs("base_network.log"),
|
logs("base_network.log"),
|
||||||
benchmark:
|
benchmark:
|
||||||
@ -127,27 +129,6 @@ rule build_shapes:
|
|||||||
"../scripts/build_shapes.py"
|
"../scripts/build_shapes.py"
|
||||||
|
|
||||||
|
|
||||||
rule build_bus_regions:
|
|
||||||
params:
|
|
||||||
countries=config_provider("countries"),
|
|
||||||
input:
|
|
||||||
country_shapes=resources("country_shapes.geojson"),
|
|
||||||
offshore_shapes=resources("offshore_shapes.geojson"),
|
|
||||||
base_network=resources("networks/base.nc"),
|
|
||||||
output:
|
|
||||||
regions_onshore=resources("regions_onshore.geojson"),
|
|
||||||
regions_offshore=resources("regions_offshore.geojson"),
|
|
||||||
log:
|
|
||||||
logs("build_bus_regions.log"),
|
|
||||||
threads: 1
|
|
||||||
resources:
|
|
||||||
mem_mb=1000,
|
|
||||||
conda:
|
|
||||||
"../envs/environment.yaml"
|
|
||||||
script:
|
|
||||||
"../scripts/build_bus_regions.py"
|
|
||||||
|
|
||||||
|
|
||||||
if config["enable"].get("build_cutout", False):
|
if config["enable"].get("build_cutout", False):
|
||||||
|
|
||||||
rule build_cutout:
|
rule build_cutout:
|
||||||
|
@ -5,10 +5,7 @@
|
|||||||
|
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
"""
|
"""
|
||||||
Creates the network topology from a `ENTSO-E map extract.
|
Creates the network topology from an `ENTSO-E map extract <https://github.com/PyPSA/GridKit/tree/master/entsoe>`_ (March 2022) as a PyPSA network.
|
||||||
|
|
||||||
<https://github.com/PyPSA/GridKit/tree/master/entsoe>`_ (March 2022) as a PyPSA
|
|
||||||
network.
|
|
||||||
|
|
||||||
Relevant Settings
|
Relevant Settings
|
||||||
-----------------
|
-----------------
|
||||||
@ -59,8 +56,19 @@ Outputs
|
|||||||
.. image:: img/base.png
|
.. image:: img/base.png
|
||||||
:scale: 33 %
|
:scale: 33 %
|
||||||
|
|
||||||
|
- ``resources/regions_onshore.geojson``:
|
||||||
|
|
||||||
|
.. image:: img/regions_onshore.png
|
||||||
|
:scale: 33 %
|
||||||
|
|
||||||
|
- ``resources/regions_offshore.geojson``:
|
||||||
|
|
||||||
|
.. image:: img/regions_offshore.png
|
||||||
|
:scale: 33 %
|
||||||
|
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
Creates the network topology from an ENTSO-E map extract, and create Voronoi shapes for each bus representing both onshore and offshore regions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@ -75,11 +83,11 @@ import shapely
|
|||||||
import shapely.prepared
|
import shapely.prepared
|
||||||
import shapely.wkt
|
import shapely.wkt
|
||||||
import yaml
|
import yaml
|
||||||
from _helpers import configure_logging, get_snapshots, set_scenario_config
|
from _helpers import REGION_COLS, configure_logging, get_snapshots, set_scenario_config
|
||||||
from packaging.version import Version, parse
|
from packaging.version import Version, parse
|
||||||
from scipy import spatial
|
from scipy import spatial
|
||||||
from scipy.sparse import csgraph
|
from scipy.sparse import csgraph
|
||||||
from shapely.geometry import LineString, Point
|
from shapely.geometry import LineString, Point, Polygon
|
||||||
|
|
||||||
PD_GE_2_2 = parse(pd.__version__) >= Version("2.2")
|
PD_GE_2_2 = parse(pd.__version__) >= Version("2.2")
|
||||||
|
|
||||||
@ -779,9 +787,147 @@ def base_network(
|
|||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def voronoi_partition_pts(points, outline):
|
||||||
|
"""
|
||||||
|
Compute the polygons of a voronoi partition of `points` within the polygon
|
||||||
|
`outline`. Taken from
|
||||||
|
https://github.com/FRESNA/vresutils/blob/master/vresutils/graph.py.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
points : Nx2 - ndarray[dtype=float]
|
||||||
|
outline : Polygon
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
polygons : N - ndarray[dtype=Polygon|MultiPolygon]
|
||||||
|
"""
|
||||||
|
points = np.asarray(points)
|
||||||
|
|
||||||
|
if len(points) == 1:
|
||||||
|
polygons = [outline]
|
||||||
|
else:
|
||||||
|
xmin, ymin = np.amin(points, axis=0)
|
||||||
|
xmax, ymax = np.amax(points, axis=0)
|
||||||
|
xspan = xmax - xmin
|
||||||
|
yspan = ymax - ymin
|
||||||
|
|
||||||
|
# to avoid any network positions outside all Voronoi cells, append
|
||||||
|
# the corners of a rectangle framing these points
|
||||||
|
vor = spatial.Voronoi(
|
||||||
|
np.vstack(
|
||||||
|
(
|
||||||
|
points,
|
||||||
|
[
|
||||||
|
[xmin - 3.0 * xspan, ymin - 3.0 * yspan],
|
||||||
|
[xmin - 3.0 * xspan, ymax + 3.0 * yspan],
|
||||||
|
[xmax + 3.0 * xspan, ymin - 3.0 * yspan],
|
||||||
|
[xmax + 3.0 * xspan, ymax + 3.0 * yspan],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
polygons = []
|
||||||
|
for i in range(len(points)):
|
||||||
|
poly = Polygon(vor.vertices[vor.regions[vor.point_region[i]]])
|
||||||
|
|
||||||
|
if not poly.is_valid:
|
||||||
|
poly = poly.buffer(0)
|
||||||
|
|
||||||
|
with np.errstate(invalid="ignore"):
|
||||||
|
poly = poly.intersection(outline)
|
||||||
|
|
||||||
|
polygons.append(poly)
|
||||||
|
|
||||||
|
return polygons
|
||||||
|
|
||||||
|
|
||||||
|
def build_bus_shapes(n, country_shapes, offshore_shapes, countries):
|
||||||
|
country_shapes = gpd.read_file(country_shapes).set_index("name")["geometry"]
|
||||||
|
offshore_shapes = gpd.read_file(offshore_shapes)
|
||||||
|
offshore_shapes = offshore_shapes.reindex(columns=REGION_COLS).set_index("name")[
|
||||||
|
"geometry"
|
||||||
|
]
|
||||||
|
|
||||||
|
onshore_regions = []
|
||||||
|
offshore_regions = []
|
||||||
|
|
||||||
|
for country in countries:
|
||||||
|
c_b = n.buses.country == country
|
||||||
|
|
||||||
|
onshore_shape = country_shapes[country]
|
||||||
|
onshore_locs = (
|
||||||
|
n.buses.loc[c_b & n.buses.onshore_bus]
|
||||||
|
.sort_values(
|
||||||
|
by="substation_lv", ascending=False
|
||||||
|
) # preference for substations
|
||||||
|
.drop_duplicates(subset=["x", "y"], keep="first")[["x", "y"]]
|
||||||
|
)
|
||||||
|
onshore_regions.append(
|
||||||
|
gpd.GeoDataFrame(
|
||||||
|
{
|
||||||
|
"name": onshore_locs.index,
|
||||||
|
"x": onshore_locs["x"],
|
||||||
|
"y": onshore_locs["y"],
|
||||||
|
"geometry": voronoi_partition_pts(
|
||||||
|
onshore_locs.values, onshore_shape
|
||||||
|
),
|
||||||
|
"country": country,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if country not in offshore_shapes.index:
|
||||||
|
continue
|
||||||
|
offshore_shape = offshore_shapes[country]
|
||||||
|
offshore_locs = n.buses.loc[c_b & n.buses.substation_off, ["x", "y"]]
|
||||||
|
offshore_regions_c = gpd.GeoDataFrame(
|
||||||
|
{
|
||||||
|
"name": offshore_locs.index,
|
||||||
|
"x": offshore_locs["x"],
|
||||||
|
"y": offshore_locs["y"],
|
||||||
|
"geometry": voronoi_partition_pts(offshore_locs.values, offshore_shape),
|
||||||
|
"country": country,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
offshore_regions_c = offshore_regions_c.loc[offshore_regions_c.area > 1e-2]
|
||||||
|
offshore_regions.append(offshore_regions_c)
|
||||||
|
|
||||||
|
shapes = pd.concat(onshore_regions, ignore_index=True)
|
||||||
|
|
||||||
|
return onshore_regions, offshore_regions, shapes
|
||||||
|
|
||||||
|
|
||||||
|
def append_bus_shapes(n, shapes, type):
|
||||||
|
"""
|
||||||
|
Append shapes to the network. If shapes with the same component and type
|
||||||
|
already exist, they will be removed.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n (pypsa.Network): The network to which the shapes will be appended.
|
||||||
|
shapes (geopandas.GeoDataFrame): The shapes to be appended.
|
||||||
|
**kwargs: Additional keyword arguments used in `n.madd`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
remove = n.shapes.query("component == 'Bus' and type == @type").index
|
||||||
|
n.mremove("Shape", remove)
|
||||||
|
|
||||||
|
offset = n.shapes.index.astype(int).max() + 1 if not n.shapes.empty else 0
|
||||||
|
shapes = shapes.rename(lambda x: int(x) + offset)
|
||||||
|
n.madd(
|
||||||
|
"Shape",
|
||||||
|
shapes.index,
|
||||||
|
geometry=shapes.geometry,
|
||||||
|
idx=shapes.name,
|
||||||
|
component="Bus",
|
||||||
|
type=type,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if "snakemake" not in globals():
|
if "snakemake" not in globals():
|
||||||
|
|
||||||
from _helpers import mock_snakemake
|
from _helpers import mock_snakemake
|
||||||
|
|
||||||
snakemake = mock_snakemake("base_network")
|
snakemake = mock_snakemake("base_network")
|
||||||
@ -803,5 +949,22 @@ if __name__ == "__main__":
|
|||||||
snakemake.config,
|
snakemake.config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
onshore_regions, offshore_regions, shapes = build_bus_shapes(
|
||||||
|
n,
|
||||||
|
snakemake.input.country_shapes,
|
||||||
|
snakemake.input.offshore_shapes,
|
||||||
|
snakemake.params.countries,
|
||||||
|
)
|
||||||
|
|
||||||
|
shapes.to_file(snakemake.output.regions_onshore)
|
||||||
|
append_bus_shapes(n, shapes, "onshore")
|
||||||
|
|
||||||
|
if offshore_regions:
|
||||||
|
shapes = pd.concat(offshore_regions, ignore_index=True)
|
||||||
|
shapes.to_file(snakemake.output.regions_offshore)
|
||||||
|
append_bus_shapes(n, shapes, "offshore")
|
||||||
|
else:
|
||||||
|
offshore_shapes.to_frame().to_file(snakemake.output.regions_offshore)
|
||||||
|
|
||||||
n.meta = snakemake.config
|
n.meta = snakemake.config
|
||||||
n.export_to_netcdf(snakemake.output[0])
|
n.export_to_netcdf(snakemake.output.base_network)
|
||||||
|
@ -1,218 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# SPDX-FileCopyrightText: : 2017-2024 The PyPSA-Eur Authors
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
"""
|
|
||||||
Creates Voronoi shapes for each bus representing both onshore and offshore
|
|
||||||
regions.
|
|
||||||
|
|
||||||
Relevant Settings
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. code:: yaml
|
|
||||||
|
|
||||||
countries:
|
|
||||||
|
|
||||||
.. seealso::
|
|
||||||
Documentation of the configuration file ``config/config.yaml`` at
|
|
||||||
:ref:`toplevel_cf`
|
|
||||||
|
|
||||||
Inputs
|
|
||||||
------
|
|
||||||
|
|
||||||
- ``resources/country_shapes.geojson``: confer :ref:`shapes`
|
|
||||||
- ``resources/offshore_shapes.geojson``: confer :ref:`shapes`
|
|
||||||
- ``networks/base.nc``: confer :ref:`base`
|
|
||||||
|
|
||||||
Outputs
|
|
||||||
-------
|
|
||||||
|
|
||||||
- ``resources/regions_onshore.geojson``:
|
|
||||||
|
|
||||||
.. image:: img/regions_onshore.png
|
|
||||||
:scale: 33 %
|
|
||||||
|
|
||||||
- ``resources/regions_offshore.geojson``:
|
|
||||||
|
|
||||||
.. image:: img/regions_offshore.png
|
|
||||||
:scale: 33 %
|
|
||||||
|
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import geopandas as gpd
|
|
||||||
import numpy as np
|
|
||||||
import pandas as pd
|
|
||||||
import pypsa
|
|
||||||
from _helpers import REGION_COLS, configure_logging, set_scenario_config
|
|
||||||
from scipy.spatial import Voronoi
|
|
||||||
from shapely.geometry import Polygon
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def voronoi_partition_pts(points, outline):
|
|
||||||
"""
|
|
||||||
Compute the polygons of a voronoi partition of `points` within the polygon
|
|
||||||
`outline`. Taken from
|
|
||||||
https://github.com/FRESNA/vresutils/blob/master/vresutils/graph.py.
|
|
||||||
|
|
||||||
Attributes
|
|
||||||
----------
|
|
||||||
points : Nx2 - ndarray[dtype=float]
|
|
||||||
outline : Polygon
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
polygons : N - ndarray[dtype=Polygon|MultiPolygon]
|
|
||||||
"""
|
|
||||||
points = np.asarray(points)
|
|
||||||
|
|
||||||
if len(points) == 1:
|
|
||||||
polygons = [outline]
|
|
||||||
else:
|
|
||||||
xmin, ymin = np.amin(points, axis=0)
|
|
||||||
xmax, ymax = np.amax(points, axis=0)
|
|
||||||
xspan = xmax - xmin
|
|
||||||
yspan = ymax - ymin
|
|
||||||
|
|
||||||
# to avoid any network positions outside all Voronoi cells, append
|
|
||||||
# the corners of a rectangle framing these points
|
|
||||||
vor = Voronoi(
|
|
||||||
np.vstack(
|
|
||||||
(
|
|
||||||
points,
|
|
||||||
[
|
|
||||||
[xmin - 3.0 * xspan, ymin - 3.0 * yspan],
|
|
||||||
[xmin - 3.0 * xspan, ymax + 3.0 * yspan],
|
|
||||||
[xmax + 3.0 * xspan, ymin - 3.0 * yspan],
|
|
||||||
[xmax + 3.0 * xspan, ymax + 3.0 * yspan],
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
polygons = []
|
|
||||||
for i in range(len(points)):
|
|
||||||
poly = Polygon(vor.vertices[vor.regions[vor.point_region[i]]])
|
|
||||||
|
|
||||||
if not poly.is_valid:
|
|
||||||
poly = poly.buffer(0)
|
|
||||||
|
|
||||||
with np.errstate(invalid="ignore"):
|
|
||||||
poly = poly.intersection(outline)
|
|
||||||
|
|
||||||
polygons.append(poly)
|
|
||||||
|
|
||||||
return polygons
|
|
||||||
|
|
||||||
|
|
||||||
def append_bus_shapes(n, shapes, type):
|
|
||||||
"""
|
|
||||||
Append shapes to the network. If shapes with the same component and type
|
|
||||||
already exist, they will be removed.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
n (pypsa.Network): The network to which the shapes will be appended.
|
|
||||||
shapes (geopandas.GeoDataFrame): The shapes to be appended.
|
|
||||||
**kwargs: Additional keyword arguments used in `n.madd`.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
remove = n.shapes.query("component == 'Bus' and type == @type").index
|
|
||||||
n.mremove("Shape", remove)
|
|
||||||
|
|
||||||
offset = n.shapes.index.astype(int).max() + 1 if not n.shapes.empty else 0
|
|
||||||
shapes = shapes.rename(lambda x: int(x) + offset)
|
|
||||||
n.madd(
|
|
||||||
"Shape",
|
|
||||||
shapes.index,
|
|
||||||
geometry=shapes.geometry,
|
|
||||||
idx=shapes.name,
|
|
||||||
component="Bus",
|
|
||||||
type=type,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if "snakemake" not in globals():
|
|
||||||
from _helpers import mock_snakemake
|
|
||||||
|
|
||||||
snakemake = mock_snakemake("build_bus_regions")
|
|
||||||
configure_logging(snakemake)
|
|
||||||
set_scenario_config(snakemake)
|
|
||||||
|
|
||||||
countries = snakemake.params.countries
|
|
||||||
|
|
||||||
base_network = snakemake.input.base_network
|
|
||||||
n = pypsa.Network(base_network)
|
|
||||||
|
|
||||||
country_shapes = gpd.read_file(snakemake.input.country_shapes).set_index("name")[
|
|
||||||
"geometry"
|
|
||||||
]
|
|
||||||
offshore_shapes = gpd.read_file(snakemake.input.offshore_shapes)
|
|
||||||
offshore_shapes = offshore_shapes.reindex(columns=REGION_COLS).set_index("name")[
|
|
||||||
"geometry"
|
|
||||||
]
|
|
||||||
|
|
||||||
onshore_regions = []
|
|
||||||
offshore_regions = []
|
|
||||||
|
|
||||||
for country in countries:
|
|
||||||
c_b = n.buses.country == country
|
|
||||||
|
|
||||||
onshore_shape = country_shapes[country]
|
|
||||||
onshore_locs = (
|
|
||||||
n.buses.loc[c_b & n.buses.onshore_bus]
|
|
||||||
.sort_values(
|
|
||||||
by="substation_lv", ascending=False
|
|
||||||
) # preference for substations
|
|
||||||
.drop_duplicates(subset=["x", "y"], keep="first")[["x", "y"]]
|
|
||||||
)
|
|
||||||
onshore_regions.append(
|
|
||||||
gpd.GeoDataFrame(
|
|
||||||
{
|
|
||||||
"name": onshore_locs.index,
|
|
||||||
"x": onshore_locs["x"],
|
|
||||||
"y": onshore_locs["y"],
|
|
||||||
"geometry": voronoi_partition_pts(
|
|
||||||
onshore_locs.values, onshore_shape
|
|
||||||
),
|
|
||||||
"country": country,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if country not in offshore_shapes.index:
|
|
||||||
continue
|
|
||||||
offshore_shape = offshore_shapes[country]
|
|
||||||
offshore_locs = n.buses.loc[c_b & n.buses.substation_off, ["x", "y"]]
|
|
||||||
offshore_regions_c = gpd.GeoDataFrame(
|
|
||||||
{
|
|
||||||
"name": offshore_locs.index,
|
|
||||||
"x": offshore_locs["x"],
|
|
||||||
"y": offshore_locs["y"],
|
|
||||||
"geometry": voronoi_partition_pts(offshore_locs.values, offshore_shape),
|
|
||||||
"country": country,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
offshore_regions_c = offshore_regions_c.loc[offshore_regions_c.area > 1e-2]
|
|
||||||
offshore_regions.append(offshore_regions_c)
|
|
||||||
|
|
||||||
shapes = pd.concat(onshore_regions, ignore_index=True)
|
|
||||||
shapes.to_file(snakemake.output.regions_onshore)
|
|
||||||
append_bus_shapes(n, shapes, "onshore")
|
|
||||||
|
|
||||||
if offshore_regions:
|
|
||||||
shapes = pd.concat(offshore_regions, ignore_index=True)
|
|
||||||
shapes.to_file(snakemake.output.regions_offshore)
|
|
||||||
append_bus_shapes(n, shapes, "offshore")
|
|
||||||
|
|
||||||
else:
|
|
||||||
offshore_shapes.to_frame().to_file(snakemake.output.regions_offshore)
|
|
||||||
|
|
||||||
# save network with shapes
|
|
||||||
n.export_to_netcdf(base_network)
|
|
@ -135,7 +135,7 @@ import pypsa
|
|||||||
import seaborn as sns
|
import seaborn as sns
|
||||||
from _helpers import configure_logging, set_scenario_config, update_p_nom_max
|
from _helpers import configure_logging, set_scenario_config, update_p_nom_max
|
||||||
from add_electricity import load_costs
|
from add_electricity import load_costs
|
||||||
from build_bus_regions import append_bus_shapes
|
from base_network import append_bus_shapes
|
||||||
from packaging.version import Version, parse
|
from packaging.version import Version, parse
|
||||||
from pypsa.clustering.spatial import (
|
from pypsa.clustering.spatial import (
|
||||||
busmap_by_greedy_modularity,
|
busmap_by_greedy_modularity,
|
||||||
|
@ -95,7 +95,7 @@ import pypsa
|
|||||||
import scipy as sp
|
import scipy as sp
|
||||||
from _helpers import configure_logging, set_scenario_config, update_p_nom_max
|
from _helpers import configure_logging, set_scenario_config, update_p_nom_max
|
||||||
from add_electricity import load_costs
|
from add_electricity import load_costs
|
||||||
from build_bus_regions import append_bus_shapes
|
from base_network import append_bus_shapes
|
||||||
from cluster_network import cluster_regions, clustering_for_n_clusters
|
from cluster_network import cluster_regions, clustering_for_n_clusters
|
||||||
from pypsa.clustering.spatial import (
|
from pypsa.clustering.spatial import (
|
||||||
aggregateoneport,
|
aggregateoneport,
|
||||||
|
Loading…
Reference in New Issue
Block a user