Merge branch 'master' into fneum/line-rating

This commit is contained in:
Fabian Neumann 2023-07-03 19:27:05 +02:00 committed by GitHub
commit f0b44a91ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 197 additions and 75 deletions

View File

@ -796,6 +796,11 @@ plotting:
solar: "#f9d002"
solar PV: "#f9d002"
solar thermal: '#ffbf2b'
residential rural solar thermal: '#f1c069'
services rural solar thermal: '#eabf61'
residential urban decentral solar thermal: '#e5bc5a'
services urban decentral solar thermal: '#dfb953'
urban central solar thermal: '#d7b24c'
solar rooftop: '#ffea80'
# gas
OCGT: '#e0986c'
@ -804,9 +809,15 @@ plotting:
gas boiler: '#db6a25'
gas boilers: '#db6a25'
gas boiler marginal: '#db6a25'
residential rural gas boiler: '#d4722e'
residential urban decentral gas boiler: '#cb7a36'
services rural gas boiler: '#c4813f'
services urban decentral gas boiler: '#ba8947'
urban central gas boiler: '#b0904f'
gas: '#e05b09'
fossil gas: '#e05b09'
natural gas: '#e05b09'
biogas to gas: '#e36311'
CCGT: '#a85522'
CCGT marginal: '#a85522'
allam: '#B98F76'
@ -819,6 +830,11 @@ plotting:
# oil
oil: '#c9c9c9'
oil boiler: '#adadad'
residential rural oil boiler: '#a9a9a9'
services rural oil boiler: '#a5a5a5'
residential urban decentral oil boiler: '#a1a1a1'
urban central oil boiler: '#9d9d9d'
services urban decentral oil boiler: '#999999'
agriculture machinery oil: '#949494'
shipping oil: "#808080"
land transport oil: '#afafaf'
@ -844,13 +860,20 @@ plotting:
solid biomass for industry CC: '#47411c'
solid biomass for industry co2 from atmosphere: '#736412'
solid biomass for industry co2 to stored: '#47411c'
urban central solid biomass CHP: '#9d9042'
urban central solid biomass CHP CC: '#6c5d28'
biomass boiler: '#8A9A5B'
residential rural biomass boiler: '#a1a066'
residential urban decentral biomass boiler: '#b0b87b'
services rural biomass boiler: '#c6cf98'
services urban decentral biomass boiler: '#dde5b5'
biomass to liquid: '#32CD32'
BioSNG: '#123456'
# power transmission
lines: '#6c9459'
transmission lines: '#6c9459'
electricity distribution grid: '#97ad8c'
low voltage: '#97ad8c'
# electricity demand
Electric load: '#110d63'
electric demand: '#110d63'
@ -861,24 +884,48 @@ plotting:
# battery + EVs
battery: '#ace37f'
battery storage: '#ace37f'
battery charger: '#88a75b'
battery discharger: '#5d4e29'
home battery: '#80c944'
home battery storage: '#80c944'
home battery charger: '#5e8032'
home battery discharger: '#3c5221'
BEV charger: '#baf238'
V2G: '#e5ffa8'
land transport EV: '#baf238'
Li ion: '#baf238'
# hot water storage
water tanks: '#e69487'
residential rural water tanks: '#f7b7a3'
services rural water tanks: '#f3afa3'
residential urban decentral water tanks: '#f2b2a3'
services urban decentral water tanks: '#f1b4a4'
urban central water tanks: '#e9977d'
hot water storage: '#e69487'
hot water charging: '#e69487'
hot water discharging: '#e69487'
hot water charging: '#e8998b'
urban central water tanks charger: '#b57a67'
residential rural water tanks charger: '#b4887c'
residential urban decentral water tanks charger: '#b39995'
services rural water tanks charger: '#b3abb0'
services urban decentral water tanks charger: '#b3becc'
hot water discharging: '#e99c8e'
urban central water tanks discharger: '#b9816e'
residential rural water tanks discharger: '#ba9685'
residential urban decentral water tanks discharger: '#baac9e'
services rural water tanks discharger: '#bbc2b8'
services urban decentral water tanks discharger: '#bdd8d3'
# heat demand
Heat load: '#cc1f1f'
heat: '#cc1f1f'
heat demand: '#cc1f1f'
rural heat: '#ff5c5c'
residential rural heat: '#ff7c7c'
services rural heat: '#ff9c9c'
central heat: '#cc1f1f'
urban central heat: '#d15959'
decentral heat: '#750606'
residential urban decentral heat: '#a33c3c'
services urban decentral heat: '#cc1f1f'
low-temperature heat for industry: '#8f2727'
process heat: '#ff0000'
agriculture heat: '#d9a5a5'
@ -886,14 +933,26 @@ plotting:
heat pumps: '#2fb537'
heat pump: '#2fb537'
air heat pump: '#36eb41'
residential urban decentral air heat pump: '#48f74f'
services urban decentral air heat pump: '#5af95d'
urban central air heat pump: '#6cfb6b'
ground heat pump: '#2fb537'
residential rural ground heat pump: '#48f74f'
services rural ground heat pump: '#5af95d'
Ambient: '#98eb9d'
CHP: '#8a5751'
urban central gas CHP: '#8d5e56'
CHP CC: '#634643'
urban central gas CHP CC: '#6e4e4c'
CHP heat: '#8a5751'
CHP electric: '#8a5751'
district heating: '#e8beac'
resistive heater: '#d8f9b8'
residential rural resistive heater: '#bef5b5'
residential urban decentral resistive heater: '#b2f1a9'
services rural resistive heater: '#a5ed9d'
services urban decentral resistive heater: '#98e991'
urban central resistive heater: '#8cdf85'
retrofitting: '#8487e8'
building retrofitting: '#8487e8'
# hydrogen
@ -905,13 +964,16 @@ plotting:
SMR CC: '#4f1745'
H2 liquefaction: '#d647bd'
hydrogen storage: '#bf13a0'
H2 Store: '#bf13a0'
H2 storage: '#bf13a0'
land transport fuel cell: '#6b3161'
H2 pipeline: '#f081dc'
H2 pipeline retrofitted: '#ba99b5'
H2 Fuel Cell: '#c251ae'
H2 fuel cell: '#c251ae'
H2 turbine: '#991f83'
H2 Electrolysis: '#ff29d9'
H2 electrolysis: '#ff29d9'
# ammonia
NH3: '#46caf0'
ammonia: '#46caf0'
@ -960,9 +1022,11 @@ plotting:
waste: '#e3d37d'
other: '#000000'
geothermal: '#ba91b1'
AC: "#70af1d"
AC-AC: "#70af1d"
AC line: "#70af1d"
links: "#8a1caf"
HVDC links: "#8a1caf"
DC: "#8a1caf"
DC-DC: "#8a1caf"
DC link: "#8a1caf"

View File

@ -16,6 +16,8 @@ Upcoming Release
* Bugfix: Correct typo in the CPLEX solver configuration in ``config.default.yaml``.
* Bugfix: Error in ``add_electricity`` where carriers were added multiple times to the network, resulting in a non-unique carriers error.
* Renamed script file from PyPSA-EUR ``build_load_data`` to ``build_electricity_demand`` and ``retrieve_load_data`` to ``retrieve_electricity_demand``.
* Fix docs readthedocs built
@ -24,12 +26,17 @@ Upcoming Release
hydrogen fuel cell. Add switches for both re-electrification options under
``sector: hydrogen_turbine:`` and ``sector: hydrogen_fuel_cell:``.
* A new function named ``sanitize_carrier`` ensures that all unique carrier names are present in the network's carriers attribute, and adds nice names and colors for each carrier according to the provided configuration dictionary.
* Additional tech_color are added to include previously unlisted carriers.
* Remove ``vresutils`` dependency.
* Add option to include a piecewise linear approximation of transmission losses,
e.g. by setting ``solving: options: transmission_losses: 2`` for an
approximation with two tangents.
PyPSA-Eur 0.8.0 (18th March 2023)
=================================

View File

@ -59,6 +59,9 @@ rule build_powerplants:
rule base_network:
params:
countries=config["countries"],
snapshots=config["snapshots"],
input:
eg_buses="data/entsoegridkit/buses.csv",
eg_lines="data/entsoegridkit/lines.csv",

View File

@ -19,8 +19,6 @@ if config["enable"].get("retrieve_databundle", True):
datafiles.extend(["natura/Natura2000_end2015.shp", "GEBCO_2014_2D.nc"])
rule retrieve_databundle:
params:
tutorial=config["tutorial"],
output:
expand("data/bundle/{file}", file=datafiles),
log:

View File

@ -283,7 +283,7 @@ def get_aggregation_strategies(aggregation_strategies):
# when custom values are specified in the config.
import numpy as np
from pypsa.networkclustering import _make_consense
from pypsa.clustering.spatial import _make_consense
bus_strategies = dict(country=_make_consense("Bus", "country"))
bus_strategies.update(aggregation_strategies.get("buses", {}))

View File

@ -121,18 +121,68 @@ def calculate_annuity(n, r):
return 1 / n
def _add_missing_carriers_from_costs(n, costs, carriers):
missing_carriers = pd.Index(carriers).difference(n.carriers.index)
if missing_carriers.empty:
return
def add_missing_carriers(n, carriers):
"""
Function to add missing carriers to the network without raising errors.
"""
missing_carriers = set(carriers) - set(n.carriers.index)
if len(missing_carriers) > 0:
n.madd("Carrier", missing_carriers)
emissions_cols = (
costs.columns.to_series().loc[lambda s: s.str.endswith("_emissions")].values
def sanitize_carriers(n, config):
"""
Sanitize the carrier information in a PyPSA Network object.
The function ensures that all unique carrier names are present in the network's
carriers attribute, and adds nice names and colors for each carrier according
to the provided configuration dictionary.
Parameters
----------
n : pypsa.Network
A PyPSA Network object that represents an electrical power system.
config : dict
A dictionary containing configuration information, specifically the
"plotting" key with "nice_names" and "tech_colors" keys for carriers.
Returns
-------
None
The function modifies the 'n' PyPSA Network object in-place, updating the
carriers attribute with nice names and colors.
Warnings
--------
Raises a warning if any carrier's "tech_colors" are not defined in the config dictionary.
"""
for c in n.iterate_components():
if "carrier" in c.df:
add_missing_carriers(n, c.df)
carrier_i = n.carriers.index
nice_names = (
pd.Series(config["plotting"]["nice_names"])
.reindex(carrier_i)
.fillna(carrier_i.to_series().str.title())
)
suptechs = missing_carriers.str.split("-").str[0]
emissions = costs.loc[suptechs, emissions_cols].fillna(0.0)
emissions.index = missing_carriers
n.import_components_from_dataframe(emissions, "Carrier")
n.carriers["nice_name"] = n.carriers.nice_name.where(
n.carriers.nice_name != "", nice_names
)
colors = pd.Series(config["plotting"]["tech_colors"]).reindex(carrier_i)
if colors.isna().any():
missing_i = list(colors.index[colors.isna()])
logger.warning(f"tech_colors for carriers {missing_i} not defined in config.")
n.carriers["color"] = n.carriers.color.where(n.carriers.color != "", colors)
def add_co2_emissions(n, costs, carriers):
"""
Add CO2 emissions to the network's carriers attribute.
"""
suptechs = n.carriers.loc[carriers].index.str.split("-").str[0]
n.carriers.loc[carriers, "co2_emissions"] = costs.co2_emissions[suptechs].values
def load_costs(tech_costs, config, max_hours, Nyears=1.0):
@ -307,57 +357,56 @@ def update_transmission_costs(n, costs, length_factor=1.0):
def attach_wind_and_solar(
n, costs, input_profiles, technologies, extendable_carriers, line_length_factor=1
n, costs, input_profiles, carriers, extendable_carriers, line_length_factor=1
):
# TODO: rename tech -> carrier, technologies -> carriers
_add_missing_carriers_from_costs(n, costs, technologies)
add_missing_carriers(n, carriers)
for tech in technologies:
if tech == "hydro":
for car in carriers:
if car == "hydro":
continue
with xr.open_dataset(getattr(input_profiles, "profile_" + tech)) as ds:
with xr.open_dataset(getattr(input_profiles, "profile_" + car)) as ds:
if ds.indexes["bus"].empty:
continue
suptech = tech.split("-", 2)[0]
if suptech == "offwind":
supcar = car.split("-", 2)[0]
if supcar == "offwind":
underwater_fraction = ds["underwater_fraction"].to_pandas()
connection_cost = (
line_length_factor
* ds["average_distance"].to_pandas()
* (
underwater_fraction
* costs.at[tech + "-connection-submarine", "capital_cost"]
* costs.at[car + "-connection-submarine", "capital_cost"]
+ (1.0 - underwater_fraction)
* costs.at[tech + "-connection-underground", "capital_cost"]
* costs.at[car + "-connection-underground", "capital_cost"]
)
)
capital_cost = (
costs.at["offwind", "capital_cost"]
+ costs.at[tech + "-station", "capital_cost"]
+ costs.at[car + "-station", "capital_cost"]
+ connection_cost
)
logger.info(
"Added connection cost of {:0.0f}-{:0.0f} Eur/MW/a to {}".format(
connection_cost.min(), connection_cost.max(), tech
connection_cost.min(), connection_cost.max(), car
)
)
else:
capital_cost = costs.at[tech, "capital_cost"]
capital_cost = costs.at[car, "capital_cost"]
n.madd(
"Generator",
ds.indexes["bus"],
" " + tech,
" " + car,
bus=ds.indexes["bus"],
carrier=tech,
p_nom_extendable=tech in extendable_carriers["Generator"],
carrier=car,
p_nom_extendable=car in extendable_carriers["Generator"],
p_nom_max=ds["p_nom_max"].to_pandas(),
weight=ds["weight"].to_pandas(),
marginal_cost=costs.at[suptech, "marginal_cost"],
marginal_cost=costs.at[supcar, "marginal_cost"],
capital_cost=capital_cost,
efficiency=costs.at[suptech, "efficiency"],
efficiency=costs.at[supcar, "efficiency"],
p_max_pu=ds["profile"].transpose("time", "bus").to_pandas(),
)
@ -371,8 +420,9 @@ def attach_conventional_generators(
conventional_params,
conventional_inputs,
):
carriers = set(conventional_carriers) | set(extendable_carriers["Generator"])
_add_missing_carriers_from_costs(n, costs, carriers)
carriers = list(set(conventional_carriers) | set(extendable_carriers["Generator"]))
add_missing_carriers(n, carriers)
add_co2_emissions(n, costs, carriers)
ppl = (
ppl.query("carrier in @carriers")
@ -428,7 +478,8 @@ def attach_conventional_generators(
def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities, carriers, **params):
_add_missing_carriers_from_costs(n, costs, carriers)
add_missing_carriers(n, carriers)
add_co2_emissions(n, costs, carriers)
ppl = (
ppl.query('carrier == "hydro"')
@ -560,7 +611,8 @@ def attach_extendable_generators(n, costs, ppl, carriers):
logger.warning(
"The function `attach_extendable_generators` is deprecated in v0.5.0."
)
_add_missing_carriers_from_costs(n, costs, carriers)
add_missing_carriers(n, carriers)
add_co2_emissions(n, costs, carriers)
for tech in carriers:
if tech.startswith("OCGT"):
@ -642,7 +694,7 @@ def attach_OPSD_renewables(n, tech_map):
buses = n.buses.loc[gens.bus.unique()]
gens_per_bus = gens.groupby("bus").p_nom.count()
caps = map_country_bus(df.query("Fueltype == @fueltype"), buses)
caps = map_country_bus(df.query("Fueltype == @fueltype and lat == lat"), buses)
caps = caps.groupby(["bus"]).Capacity.sum()
caps = caps / gens_per_bus.reindex(caps.index, fill_value=1)
@ -715,21 +767,6 @@ def attach_line_rating(
n.lines_t.s_max_pu *= s_max_pu
def add_nice_carrier_names(n, config):
carrier_i = n.carriers.index
nice_names = (
pd.Series(config["plotting"]["nice_names"])
.reindex(carrier_i)
.fillna(carrier_i.to_series().str.title())
)
n.carriers["nice_name"] = nice_names
colors = pd.Series(config["plotting"]["tech_colors"]).reindex(carrier_i)
if colors.isna().any():
missing_i = list(colors.index[colors.isna()])
logger.warning(f"tech_colors for carriers {missing_i} not defined in config.")
n.carriers["color"] = colors
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
@ -829,7 +866,8 @@ if __name__ == "__main__":
max_line_rating,
)
add_nice_carrier_names(n, snakemake.config)
sanitize_carriers(n, snakemake.config)
n.meta = snakemake.config
n.export_to_netcdf(snakemake.output[0])

View File

@ -22,6 +22,7 @@ import numpy as np
import pypsa
import xarray as xr
from _helpers import override_component_attrs, update_config_with_sector_opts
from add_electricity import sanitize_carriers
from prepare_sector_network import cluster_heat_buses, define_spatial, prepare_costs
cc = coco.CountryConverter()
@ -665,4 +666,6 @@ if __name__ == "__main__":
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
sanitize_carriers(n, snakemake.config)
n.export_to_netcdf(snakemake.output[0])

View File

@ -56,11 +56,7 @@ import numpy as np
import pandas as pd
import pypsa
from _helpers import configure_logging
from add_electricity import (
_add_missing_carriers_from_costs,
add_nice_carrier_names,
load_costs,
)
from add_electricity import load_costs, sanitize_carriers
idx = pd.IndexSlice
@ -70,7 +66,7 @@ logger = logging.getLogger(__name__)
def attach_storageunits(n, costs, extendable_carriers, max_hours):
carriers = extendable_carriers["StorageUnit"]
_add_missing_carriers_from_costs(n, costs, carriers)
n.madd("Carrier", carriers)
buses_i = n.buses.index
@ -101,7 +97,7 @@ def attach_storageunits(n, costs, extendable_carriers, max_hours):
def attach_stores(n, costs, extendable_carriers):
carriers = extendable_carriers["Store"]
_add_missing_carriers_from_costs(n, costs, carriers)
n.madd("Carrier", carriers)
buses_i = n.buses.index
bus_sub_dict = {k: n.buses[k].values for k in ["x", "y", "country"]}
@ -161,6 +157,8 @@ def attach_stores(n, costs, extendable_carriers):
marginal_cost=costs.at["battery", "marginal_cost"],
)
n.madd("Carrier", ["battery charger", "battery discharger"])
n.madd(
"Link",
b_buses_i + " charger",
@ -211,6 +209,8 @@ def attach_hydrogen_pipelines(n, costs, extendable_carriers):
h2_links.index = h2_links.apply(lambda c: f"H2 pipeline {c.bus0}-{c.bus1}", axis=1)
# add pipelines
n.add("Carrier", "H2 pipeline")
n.madd(
"Link",
h2_links.index,
@ -245,7 +245,7 @@ if __name__ == "__main__":
attach_stores(n, costs, extendable_carriers)
attach_hydrogen_pipelines(n, costs, extendable_carriers)
add_nice_carrier_names(n, snakemake.config)
sanitize_carriers(n, snakemake.config)
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
n.export_to_netcdf(snakemake.output[0])

View File

@ -714,6 +714,7 @@ def base_network(
n.name = "PyPSA-Eur"
n.set_snapshots(pd.date_range(freq="h", **config["snapshots"]))
n.madd("Carrier", ["AC", "DC"])
n.import_components_from_dataframe(buses, "Bus")
n.import_components_from_dataframe(lines, "Line")

View File

@ -149,6 +149,7 @@ if __name__ == "__main__":
logging.warning(f"No powerplants known in: {', '.join(countries_wo_ppl)}")
substations = n.buses.query("substation_lv")
ppl = ppl.dropna(subset=["lat", "lon"])
ppl = map_country_bus(ppl, substations)
bus_null_b = ppl["bus"].isnull()

View File

@ -188,7 +188,7 @@ import geopandas as gpd
import numpy as np
import xarray as xr
from _helpers import configure_logging
from dask.distributed import Client, LocalCluster
from dask.distributed import Client
from pypsa.geo import haversine
from shapely.geometry import LineString
@ -204,6 +204,7 @@ if __name__ == "__main__":
nprocesses = int(snakemake.threads)
noprogress = snakemake.config["run"].get("disable_progressbar", True)
noprogress = noprogress or not snakemake.config["atlite"]["show_progress"]
params = snakemake.params.renewable[snakemake.wildcards.technology]
resource = params["resource"] # pv panel params / wind turbine params
correction_factor = params.get("correction_factor", 1.0)
@ -216,8 +217,10 @@ if __name__ == "__main__":
if correction_factor != 1.0:
logger.info(f"correction_factor is set as {correction_factor}")
cluster = LocalCluster(n_workers=nprocesses, threads_per_worker=1)
client = Client(cluster, asynchronous=True)
if nprocesses > 1:
client = Client(n_workers=nprocesses, threads_per_worker=1)
else:
client = None
cutout = atlite.Cutout(snakemake.input.cutout)
regions = gpd.read_file(snakemake.input.regions)
@ -289,7 +292,8 @@ if __name__ == "__main__":
potential = capacity_per_sqkm * availability.sum("bus") * area
func = getattr(cutout, resource.pop("method"))
resource["dask_kwargs"] = {"scheduler": client}
if client is not None:
resource["dask_kwargs"] = {"scheduler": client}
capacity_factor = correction_factor * func(capacity_factor=True, **resource)
layout = capacity_factor * area * capacity_per_sqkm
profile, capacities = func(

View File

@ -89,7 +89,7 @@ Description
**Is it possible to run the model without the** ``simplify_network`` **rule?**
No, the network clustering methods in the PyPSA module
`pypsa.networkclustering <https://github.com/PyPSA/PyPSA/blob/master/pypsa/networkclustering.py>`_
`pypsa.clustering.spatial <https://github.com/PyPSA/PyPSA/blob/master/pypsa/clustering/spatial.py>`_
do not work reliably with multiple voltage levels and transformers.
.. tip::
@ -134,7 +134,7 @@ import pyomo.environ as po
import pypsa
import seaborn as sns
from _helpers import configure_logging, get_aggregation_strategies, update_p_nom_max
from pypsa.networkclustering import (
from pypsa.clustering.spatial import (
busmap_by_greedy_modularity,
busmap_by_hac,
busmap_by_kmeans,
@ -484,7 +484,7 @@ if __name__ == "__main__":
# Fast-path if no clustering is necessary
busmap = n.buses.index.to_series()
linemap = n.lines.index.to_series()
clustering = pypsa.networkclustering.Clustering(
clustering = pypsa.clustering.spatial.Clustering(
n, busmap, linemap, linemap, pd.Series(dtype="O")
)
else:

View File

@ -298,6 +298,8 @@ if __name__ == "__main__":
break
for o in opts:
if "+" not in o:
continue
oo = o.split("+")
suptechs = map(lambda c: c.split("-", 2)[0], n.carriers.index)
if oo[0].startswith(tuple(suptechs)):

View File

@ -22,7 +22,7 @@ from _helpers import (
override_component_attrs,
update_config_with_sector_opts,
)
from add_electricity import calculate_annuity
from add_electricity import calculate_annuity, sanitize_carriers
from build_energy_totals import build_co2_totals, build_eea_co2, build_eurostat_co2
from networkx.algorithms import complement
from networkx.algorithms.connectivity.edge_augmentation import k_edge_augmentation
@ -3425,4 +3425,6 @@ if __name__ == "__main__":
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
sanitize_carriers(n, snakemake.config)
n.export_to_netcdf(snakemake.output[0])

View File

@ -53,7 +53,7 @@ if __name__ == "__main__":
snakemake
) # TODO Make logging compatible with progressbar (see PR #102)
if snakemake.params.tutorial:
if snakemake.config["tutorial"]:
url = "https://zenodo.org/record/3517921/files/pypsa-eur-tutorial-data-bundle.tar.xz"
else:
url = "https://zenodo.org/record/3517935/files/pypsa-eur-data-bundle.tar.xz"

View File

@ -95,13 +95,13 @@ import scipy as sp
from _helpers import configure_logging, get_aggregation_strategies, update_p_nom_max
from add_electricity import load_costs
from cluster_network import cluster_regions, clustering_for_n_clusters
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
from pypsa.networkclustering import (
from pypsa.clustering.spatial import (
aggregategenerators,
aggregateoneport,
busmap_by_stubs,
get_clustering_from_busmap,
)
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
from scipy.sparse.csgraph import connected_components, dijkstra
logger = logging.getLogger(__name__)

View File

@ -235,8 +235,7 @@ def add_CCL_constraints(n, config):
p_nom = n.model["Generator-p_nom"]
gens = n.generators.query("p_nom_extendable").rename_axis(index="Generator-ext")
grouper = [gens.bus.map(n.buses.country), gens.carrier]
grouper = xr.DataArray(pd.MultiIndex.from_arrays(grouper), dims=["Generator-ext"])
grouper = pd.concat([gens.bus.map(n.buses.country), gens.carrier])
lhs = p_nom.groupby(grouper).sum().rename(bus="country")
minimum = xr.DataArray(agg_p_nom_minmax["min"].dropna()).rename(dim_0="group")