Merge pull request #243 from martacki/simplify_to_substations
Simplify to substations
This commit is contained in:
commit
f7d76659c5
@ -19,6 +19,10 @@ scenario:
|
|||||||
|
|
||||||
countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK']
|
countries: ['AL', 'AT', 'BA', 'BE', 'BG', 'CH', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'ME', 'MK', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'SE', 'SI', 'SK']
|
||||||
|
|
||||||
|
clustering:
|
||||||
|
simplify:
|
||||||
|
to_substations: false # network is simplified to nodes with positive or negative power injection (i.e. substations or offwind connections)
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
start: "2013-01-01"
|
start: "2013-01-01"
|
||||||
end: "2014-01-01"
|
end: "2014-01-01"
|
||||||
|
@ -19,6 +19,10 @@ scenario:
|
|||||||
|
|
||||||
countries: ['DE']
|
countries: ['DE']
|
||||||
|
|
||||||
|
clustering:
|
||||||
|
simplify:
|
||||||
|
to_substations: false # network is simplified to nodes with positive or negative power injection (i.e. substations or offwind connections)
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
start: "2013-03-01"
|
start: "2013-03-01"
|
||||||
end: "2013-04-01"
|
end: "2013-04-01"
|
||||||
|
3
doc/configtables/clustering.csv
Normal file
3
doc/configtables/clustering.csv
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
,Unit,Values,Description
|
||||||
|
simplify,,,
|
||||||
|
-- to_substations,bool,"{'true','false'}","Aggregates all nodes without power injection (positive or negative, i.e. demand or generation) to electrically closest ones"
|
|
@ -19,6 +19,7 @@ Upcoming Release
|
|||||||
* Fix: Value for ``co2base`` in ``config.yaml`` adjusted to 1.487e9 t CO2-eq (from 3.1e9 t CO2-eq). The new value represents emissions related to the electricity sector for EU+UK. The old value was ~2x too high and used when the emissions wildcard in ``{opts}`` was used.
|
* Fix: Value for ``co2base`` in ``config.yaml`` adjusted to 1.487e9 t CO2-eq (from 3.1e9 t CO2-eq). The new value represents emissions related to the electricity sector for EU+UK. The old value was ~2x too high and used when the emissions wildcard in ``{opts}`` was used.
|
||||||
* Add option to include marginal costs of links representing fuel cells, electrolysis, and battery inverters
|
* Add option to include marginal costs of links representing fuel cells, electrolysis, and battery inverters
|
||||||
[`#232 <https://github.com/PyPSA/pypsa-eur/pull/232>`_].
|
[`#232 <https://github.com/PyPSA/pypsa-eur/pull/232>`_].
|
||||||
|
* Add option to pre-aggregate nodes without power injections (positive or negative, i.e. generation or demand) to electrically closest nodes or neighbors in ``simplify_network``. Defaults to ``False``. This affects nodes that are no substations or have no offshore connection.
|
||||||
* Fix: Add escape in :mod:`base_network` if all TYNDP links are already contained in the network [`#246 <https://github.com/PyPSA/pypsa-eur/pull/246>`_].
|
* Fix: Add escape in :mod:`base_network` if all TYNDP links are already contained in the network [`#246 <https://github.com/PyPSA/pypsa-eur/pull/246>`_].
|
||||||
* Bugfix in :mod:`solve_operations_network`: optimised capacities are now fixed for all extendable links, not only HVDC links [`#244 <https://github.com/PyPSA/pypsa-eur/pull/244>`_].
|
* Bugfix in :mod:`solve_operations_network`: optimised capacities are now fixed for all extendable links, not only HVDC links [`#244 <https://github.com/PyPSA/pypsa-eur/pull/244>`_].
|
||||||
* The ``focus_weights`` are now also considered when pre-clustering in the :mod:`simplify_network` rule [`#241 <https://github.com/PyPSA/pypsa-eur/pull/241>`_].
|
* The ``focus_weights`` are now also considered when pre-clustering in the :mod:`simplify_network` rule [`#241 <https://github.com/PyPSA/pypsa-eur/pull/241>`_].
|
||||||
|
@ -53,41 +53,41 @@ Likewise, the example's temporal scope can be restricted (e.g. to a single month
|
|||||||
|
|
||||||
.. literalinclude:: ../config.tutorial.yaml
|
.. literalinclude:: ../config.tutorial.yaml
|
||||||
:language: yaml
|
:language: yaml
|
||||||
:lines: 22-25
|
:lines: 24-27
|
||||||
|
|
||||||
It is also possible to allow less or more carbon-dioxide emissions. Here, we limit the emissions of Germany 100 Megatonnes per year.
|
It is also possible to allow less or more carbon-dioxide emissions. Here, we limit the emissions of Germany 100 Megatonnes per year.
|
||||||
|
|
||||||
.. literalinclude:: ../config.tutorial.yaml
|
.. literalinclude:: ../config.tutorial.yaml
|
||||||
:language: yaml
|
:language: yaml
|
||||||
:lines: 36,38
|
:lines: 38,40
|
||||||
|
|
||||||
PyPSA-Eur also includes a database of existing conventional powerplants.
|
PyPSA-Eur also includes a database of existing conventional powerplants.
|
||||||
We can select which types of powerplants we like to be included with fixed capacities:
|
We can select which types of powerplants we like to be included with fixed capacities:
|
||||||
|
|
||||||
.. literalinclude:: ../config.tutorial.yaml
|
.. literalinclude:: ../config.tutorial.yaml
|
||||||
:language: yaml
|
:language: yaml
|
||||||
:lines: 36,52
|
:lines: 38,54
|
||||||
|
|
||||||
To accurately model the temporal and spatial availability of renewables such as wind and solar energy, we rely on historical weather data.
|
To accurately model the temporal and spatial availability of renewables such as wind and solar energy, we rely on historical weather data.
|
||||||
It is advisable to adapt the required range of coordinates to the selection of countries.
|
It is advisable to adapt the required range of coordinates to the selection of countries.
|
||||||
|
|
||||||
.. literalinclude:: ../config.tutorial.yaml
|
.. literalinclude:: ../config.tutorial.yaml
|
||||||
:language: yaml
|
:language: yaml
|
||||||
:lines: 54-62
|
:lines: 56-63
|
||||||
|
|
||||||
We can also decide which weather data source should be used to calculate potentials and capacity factor time-series for each carrier.
|
We can also decide which weather data source should be used to calculate potentials and capacity factor time-series for each carrier.
|
||||||
For example, we may want to use the ERA-5 dataset for solar and not the default SARAH-2 dataset.
|
For example, we may want to use the ERA-5 dataset for solar and not the default SARAH-2 dataset.
|
||||||
|
|
||||||
.. literalinclude:: ../config.tutorial.yaml
|
.. literalinclude:: ../config.tutorial.yaml
|
||||||
:language: yaml
|
:language: yaml
|
||||||
:lines: 64,107-108
|
:lines: 65,108-109
|
||||||
|
|
||||||
Finally, it is possible to pick a solver. For instance, this tutorial uses the open-source solvers CBC and Ipopt and does not rely
|
Finally, it is possible to pick a solver. For instance, this tutorial uses the open-source solvers CBC and Ipopt and does not rely
|
||||||
on the commercial solvers Gurobi or CPLEX (for which free academic licenses are available).
|
on the commercial solvers Gurobi or CPLEX (for which free academic licenses are available).
|
||||||
|
|
||||||
.. literalinclude:: ../config.tutorial.yaml
|
.. literalinclude:: ../config.tutorial.yaml
|
||||||
:language: yaml
|
:language: yaml
|
||||||
:lines: 170,180-181
|
:lines: 171,181-182
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ from functools import reduce
|
|||||||
|
|
||||||
import pypsa
|
import pypsa
|
||||||
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
|
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
|
||||||
from pypsa.networkclustering import busmap_by_stubs, aggregategenerators, aggregateoneport
|
from pypsa.networkclustering import busmap_by_stubs, aggregategenerators, aggregateoneport, get_clustering_from_busmap, _make_consense
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -312,7 +312,6 @@ def simplify_links(n):
|
|||||||
_aggregate_and_move_components(n, busmap, connection_costs_to_bus)
|
_aggregate_and_move_components(n, busmap, connection_costs_to_bus)
|
||||||
return n, busmap
|
return n, busmap
|
||||||
|
|
||||||
|
|
||||||
def remove_stubs(n):
|
def remove_stubs(n):
|
||||||
logger.info("Removing stubs")
|
logger.info("Removing stubs")
|
||||||
|
|
||||||
@ -324,6 +323,42 @@ def remove_stubs(n):
|
|||||||
|
|
||||||
return n, busmap
|
return n, busmap
|
||||||
|
|
||||||
|
def aggregate_to_substations(n, buses_i=None):
|
||||||
|
# can be used to aggregate a selection of buses to electrically closest neighbors
|
||||||
|
# if no buses are given, nodes that are no substations or without offshore connection are aggregated
|
||||||
|
|
||||||
|
if buses_i is None:
|
||||||
|
logger.info("Aggregating buses that are no substations or have no valid offshore connection")
|
||||||
|
buses_i = list(set(n.buses.index)-set(n.generators.bus)-set(n.loads.bus))
|
||||||
|
|
||||||
|
weight = pd.concat({'Line': n.lines.length/n.lines.s_nom.clip(1e-3),
|
||||||
|
'Link': n.links.length/n.links.p_nom.clip(1e-3)})
|
||||||
|
|
||||||
|
adj = n.adjacency_matrix(branch_components=['Line', 'Link'], weights=weight)
|
||||||
|
|
||||||
|
bus_indexer = n.buses.index.get_indexer(buses_i)
|
||||||
|
dist = pd.DataFrame(dijkstra(adj, directed=False, indices=bus_indexer), buses_i, n.buses.index)
|
||||||
|
|
||||||
|
dist[buses_i] = np.inf # bus in buses_i should not be assigned to different bus in buses_i
|
||||||
|
|
||||||
|
for c in n.buses.country.unique():
|
||||||
|
incountry_b = n.buses.country == c
|
||||||
|
dist.loc[incountry_b, ~incountry_b] = np.inf
|
||||||
|
|
||||||
|
busmap = n.buses.index.to_series()
|
||||||
|
busmap.loc[buses_i] = dist.idxmin(1)
|
||||||
|
|
||||||
|
clustering = get_clustering_from_busmap(n, busmap,
|
||||||
|
bus_strategies=dict(country=_make_consense("Bus", "country")),
|
||||||
|
aggregate_generators_weighted=True,
|
||||||
|
aggregate_generators_carriers=None,
|
||||||
|
aggregate_one_ports=["Load", "StorageUnit"],
|
||||||
|
line_length_factor=1.0,
|
||||||
|
generator_strategies={'p_nom_max': 'sum'},
|
||||||
|
scale_link_capital_costs=False)
|
||||||
|
|
||||||
|
return clustering.network, busmap
|
||||||
|
|
||||||
|
|
||||||
def cluster(n, n_clusters):
|
def cluster(n, n_clusters):
|
||||||
logger.info(f"Clustering to {n_clusters} buses")
|
logger.info(f"Clustering to {n_clusters} buses")
|
||||||
@ -365,6 +400,10 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
busmaps = [trafo_map, simplify_links_map, stub_map]
|
busmaps = [trafo_map, simplify_links_map, stub_map]
|
||||||
|
|
||||||
|
if snakemake.config.get('clustering', {}).get('simplify', {}).get('to_substations', False):
|
||||||
|
n, substation_map = aggregate_to_substations(n)
|
||||||
|
busmaps.append(substation_map)
|
||||||
|
|
||||||
if snakemake.wildcards.simpl:
|
if snakemake.wildcards.simpl:
|
||||||
n, cluster_map = cluster(n, int(snakemake.wildcards.simpl))
|
n, cluster_map = cluster(n, int(snakemake.wildcards.simpl))
|
||||||
busmaps.append(cluster_map)
|
busmaps.append(cluster_map)
|
||||||
|
@ -18,6 +18,10 @@ scenario:
|
|||||||
|
|
||||||
countries: ['DE']
|
countries: ['DE']
|
||||||
|
|
||||||
|
clustering:
|
||||||
|
simplify:
|
||||||
|
to_substations: false # network is simplified to nodes with positive or negative power injection (i.e. substations or offwind connections)
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
start: "2013-03-01"
|
start: "2013-03-01"
|
||||||
end: "2013-03-08"
|
end: "2013-03-08"
|
||||||
|
Loading…
Reference in New Issue
Block a user