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: "#f9d002"
solar PV: "#f9d002" solar PV: "#f9d002"
solar thermal: '#ffbf2b' 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' solar rooftop: '#ffea80'
# gas # gas
OCGT: '#e0986c' OCGT: '#e0986c'
@ -804,9 +809,15 @@ plotting:
gas boiler: '#db6a25' gas boiler: '#db6a25'
gas boilers: '#db6a25' gas boilers: '#db6a25'
gas boiler marginal: '#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' gas: '#e05b09'
fossil gas: '#e05b09' fossil gas: '#e05b09'
natural gas: '#e05b09' natural gas: '#e05b09'
biogas to gas: '#e36311'
CCGT: '#a85522' CCGT: '#a85522'
CCGT marginal: '#a85522' CCGT marginal: '#a85522'
allam: '#B98F76' allam: '#B98F76'
@ -819,6 +830,11 @@ plotting:
# oil # oil
oil: '#c9c9c9' oil: '#c9c9c9'
oil boiler: '#adadad' 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' agriculture machinery oil: '#949494'
shipping oil: "#808080" shipping oil: "#808080"
land transport oil: '#afafaf' land transport oil: '#afafaf'
@ -844,13 +860,20 @@ plotting:
solid biomass for industry CC: '#47411c' solid biomass for industry CC: '#47411c'
solid biomass for industry co2 from atmosphere: '#736412' solid biomass for industry co2 from atmosphere: '#736412'
solid biomass for industry co2 to stored: '#47411c' solid biomass for industry co2 to stored: '#47411c'
urban central solid biomass CHP: '#9d9042'
urban central solid biomass CHP CC: '#6c5d28'
biomass boiler: '#8A9A5B' 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' biomass to liquid: '#32CD32'
BioSNG: '#123456' BioSNG: '#123456'
# power transmission # power transmission
lines: '#6c9459' lines: '#6c9459'
transmission lines: '#6c9459' transmission lines: '#6c9459'
electricity distribution grid: '#97ad8c' electricity distribution grid: '#97ad8c'
low voltage: '#97ad8c'
# electricity demand # electricity demand
Electric load: '#110d63' Electric load: '#110d63'
electric demand: '#110d63' electric demand: '#110d63'
@ -861,24 +884,48 @@ plotting:
# battery + EVs # battery + EVs
battery: '#ace37f' battery: '#ace37f'
battery storage: '#ace37f' battery storage: '#ace37f'
battery charger: '#88a75b'
battery discharger: '#5d4e29'
home battery: '#80c944' home battery: '#80c944'
home battery storage: '#80c944' home battery storage: '#80c944'
home battery charger: '#5e8032'
home battery discharger: '#3c5221'
BEV charger: '#baf238' BEV charger: '#baf238'
V2G: '#e5ffa8' V2G: '#e5ffa8'
land transport EV: '#baf238' land transport EV: '#baf238'
Li ion: '#baf238' Li ion: '#baf238'
# hot water storage # hot water storage
water tanks: '#e69487' 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 storage: '#e69487'
hot water charging: '#e69487' hot water charging: '#e8998b'
hot water discharging: '#e69487' 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 demand
Heat load: '#cc1f1f' Heat load: '#cc1f1f'
heat: '#cc1f1f' heat: '#cc1f1f'
heat demand: '#cc1f1f' heat demand: '#cc1f1f'
rural heat: '#ff5c5c' rural heat: '#ff5c5c'
residential rural heat: '#ff7c7c'
services rural heat: '#ff9c9c'
central heat: '#cc1f1f' central heat: '#cc1f1f'
urban central heat: '#d15959'
decentral heat: '#750606' decentral heat: '#750606'
residential urban decentral heat: '#a33c3c'
services urban decentral heat: '#cc1f1f'
low-temperature heat for industry: '#8f2727' low-temperature heat for industry: '#8f2727'
process heat: '#ff0000' process heat: '#ff0000'
agriculture heat: '#d9a5a5' agriculture heat: '#d9a5a5'
@ -886,14 +933,26 @@ plotting:
heat pumps: '#2fb537' heat pumps: '#2fb537'
heat pump: '#2fb537' heat pump: '#2fb537'
air heat pump: '#36eb41' 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' ground heat pump: '#2fb537'
residential rural ground heat pump: '#48f74f'
services rural ground heat pump: '#5af95d'
Ambient: '#98eb9d' Ambient: '#98eb9d'
CHP: '#8a5751' CHP: '#8a5751'
urban central gas CHP: '#8d5e56'
CHP CC: '#634643' CHP CC: '#634643'
urban central gas CHP CC: '#6e4e4c'
CHP heat: '#8a5751' CHP heat: '#8a5751'
CHP electric: '#8a5751' CHP electric: '#8a5751'
district heating: '#e8beac' district heating: '#e8beac'
resistive heater: '#d8f9b8' 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' retrofitting: '#8487e8'
building retrofitting: '#8487e8' building retrofitting: '#8487e8'
# hydrogen # hydrogen
@ -905,13 +964,16 @@ plotting:
SMR CC: '#4f1745' SMR CC: '#4f1745'
H2 liquefaction: '#d647bd' H2 liquefaction: '#d647bd'
hydrogen storage: '#bf13a0' hydrogen storage: '#bf13a0'
H2 Store: '#bf13a0'
H2 storage: '#bf13a0' H2 storage: '#bf13a0'
land transport fuel cell: '#6b3161' land transport fuel cell: '#6b3161'
H2 pipeline: '#f081dc' H2 pipeline: '#f081dc'
H2 pipeline retrofitted: '#ba99b5' H2 pipeline retrofitted: '#ba99b5'
H2 Fuel Cell: '#c251ae' H2 Fuel Cell: '#c251ae'
H2 fuel cell: '#c251ae'
H2 turbine: '#991f83' H2 turbine: '#991f83'
H2 Electrolysis: '#ff29d9' H2 Electrolysis: '#ff29d9'
H2 electrolysis: '#ff29d9'
# ammonia # ammonia
NH3: '#46caf0' NH3: '#46caf0'
ammonia: '#46caf0' ammonia: '#46caf0'
@ -960,9 +1022,11 @@ plotting:
waste: '#e3d37d' waste: '#e3d37d'
other: '#000000' other: '#000000'
geothermal: '#ba91b1' geothermal: '#ba91b1'
AC: "#70af1d"
AC-AC: "#70af1d" AC-AC: "#70af1d"
AC line: "#70af1d" AC line: "#70af1d"
links: "#8a1caf" links: "#8a1caf"
HVDC links: "#8a1caf" HVDC links: "#8a1caf"
DC: "#8a1caf"
DC-DC: "#8a1caf" DC-DC: "#8a1caf"
DC link: "#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: 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``. * 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 * Fix docs readthedocs built
@ -24,12 +26,17 @@ Upcoming Release
hydrogen fuel cell. Add switches for both re-electrification options under hydrogen fuel cell. Add switches for both re-electrification options under
``sector: hydrogen_turbine:`` and ``sector: hydrogen_fuel_cell:``. ``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. * Remove ``vresutils`` dependency.
* Add option to include a piecewise linear approximation of transmission losses, * Add option to include a piecewise linear approximation of transmission losses,
e.g. by setting ``solving: options: transmission_losses: 2`` for an e.g. by setting ``solving: options: transmission_losses: 2`` for an
approximation with two tangents. approximation with two tangents.
PyPSA-Eur 0.8.0 (18th March 2023) PyPSA-Eur 0.8.0 (18th March 2023)
================================= =================================

View File

@ -59,6 +59,9 @@ rule build_powerplants:
rule base_network: rule base_network:
params:
countries=config["countries"],
snapshots=config["snapshots"],
input: input:
eg_buses="data/entsoegridkit/buses.csv", eg_buses="data/entsoegridkit/buses.csv",
eg_lines="data/entsoegridkit/lines.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"]) datafiles.extend(["natura/Natura2000_end2015.shp", "GEBCO_2014_2D.nc"])
rule retrieve_databundle: rule retrieve_databundle:
params:
tutorial=config["tutorial"],
output: output:
expand("data/bundle/{file}", file=datafiles), expand("data/bundle/{file}", file=datafiles),
log: log:

View File

@ -283,7 +283,7 @@ def get_aggregation_strategies(aggregation_strategies):
# when custom values are specified in the config. # when custom values are specified in the config.
import numpy as np 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 = dict(country=_make_consense("Bus", "country"))
bus_strategies.update(aggregation_strategies.get("buses", {})) bus_strategies.update(aggregation_strategies.get("buses", {}))

View File

@ -121,18 +121,68 @@ def calculate_annuity(n, r):
return 1 / n return 1 / n
def _add_missing_carriers_from_costs(n, costs, carriers): def add_missing_carriers(n, carriers):
missing_carriers = pd.Index(carriers).difference(n.carriers.index) """
if missing_carriers.empty: Function to add missing carriers to the network without raising errors.
return """
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] n.carriers["nice_name"] = n.carriers.nice_name.where(
emissions = costs.loc[suptechs, emissions_cols].fillna(0.0) n.carriers.nice_name != "", nice_names
emissions.index = missing_carriers )
n.import_components_from_dataframe(emissions, "Carrier") 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): 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( 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(n, carriers)
_add_missing_carriers_from_costs(n, costs, technologies)
for tech in technologies: for car in carriers:
if tech == "hydro": if car == "hydro":
continue 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: if ds.indexes["bus"].empty:
continue continue
suptech = tech.split("-", 2)[0] supcar = car.split("-", 2)[0]
if suptech == "offwind": if supcar == "offwind":
underwater_fraction = ds["underwater_fraction"].to_pandas() underwater_fraction = ds["underwater_fraction"].to_pandas()
connection_cost = ( connection_cost = (
line_length_factor line_length_factor
* ds["average_distance"].to_pandas() * ds["average_distance"].to_pandas()
* ( * (
underwater_fraction underwater_fraction
* costs.at[tech + "-connection-submarine", "capital_cost"] * costs.at[car + "-connection-submarine", "capital_cost"]
+ (1.0 - underwater_fraction) + (1.0 - underwater_fraction)
* costs.at[tech + "-connection-underground", "capital_cost"] * costs.at[car + "-connection-underground", "capital_cost"]
) )
) )
capital_cost = ( capital_cost = (
costs.at["offwind", "capital_cost"] costs.at["offwind", "capital_cost"]
+ costs.at[tech + "-station", "capital_cost"] + costs.at[car + "-station", "capital_cost"]
+ connection_cost + connection_cost
) )
logger.info( logger.info(
"Added connection cost of {:0.0f}-{:0.0f} Eur/MW/a to {}".format( "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: else:
capital_cost = costs.at[tech, "capital_cost"] capital_cost = costs.at[car, "capital_cost"]
n.madd( n.madd(
"Generator", "Generator",
ds.indexes["bus"], ds.indexes["bus"],
" " + tech, " " + car,
bus=ds.indexes["bus"], bus=ds.indexes["bus"],
carrier=tech, carrier=car,
p_nom_extendable=tech in extendable_carriers["Generator"], p_nom_extendable=car in extendable_carriers["Generator"],
p_nom_max=ds["p_nom_max"].to_pandas(), p_nom_max=ds["p_nom_max"].to_pandas(),
weight=ds["weight"].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, capital_cost=capital_cost,
efficiency=costs.at[suptech, "efficiency"], efficiency=costs.at[supcar, "efficiency"],
p_max_pu=ds["profile"].transpose("time", "bus").to_pandas(), p_max_pu=ds["profile"].transpose("time", "bus").to_pandas(),
) )
@ -371,8 +420,9 @@ def attach_conventional_generators(
conventional_params, conventional_params,
conventional_inputs, conventional_inputs,
): ):
carriers = set(conventional_carriers) | set(extendable_carriers["Generator"]) carriers = list(set(conventional_carriers) | set(extendable_carriers["Generator"]))
_add_missing_carriers_from_costs(n, costs, carriers) add_missing_carriers(n, carriers)
add_co2_emissions(n, costs, carriers)
ppl = ( ppl = (
ppl.query("carrier in @carriers") 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): 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 = (
ppl.query('carrier == "hydro"') ppl.query('carrier == "hydro"')
@ -560,7 +611,8 @@ def attach_extendable_generators(n, costs, ppl, carriers):
logger.warning( logger.warning(
"The function `attach_extendable_generators` is deprecated in v0.5.0." "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: for tech in carriers:
if tech.startswith("OCGT"): if tech.startswith("OCGT"):
@ -642,7 +694,7 @@ def attach_OPSD_renewables(n, tech_map):
buses = n.buses.loc[gens.bus.unique()] buses = n.buses.loc[gens.bus.unique()]
gens_per_bus = gens.groupby("bus").p_nom.count() 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.groupby(["bus"]).Capacity.sum()
caps = caps / gens_per_bus.reindex(caps.index, fill_value=1) 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 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 __name__ == "__main__":
if "snakemake" not in globals(): if "snakemake" not in globals():
from _helpers import mock_snakemake from _helpers import mock_snakemake
@ -829,7 +866,8 @@ if __name__ == "__main__":
max_line_rating, max_line_rating,
) )
add_nice_carrier_names(n, snakemake.config) sanitize_carriers(n, snakemake.config)
n.meta = snakemake.config n.meta = snakemake.config
n.export_to_netcdf(snakemake.output[0]) n.export_to_netcdf(snakemake.output[0])

View File

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

View File

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

View File

@ -714,6 +714,7 @@ def base_network(
n.name = "PyPSA-Eur" n.name = "PyPSA-Eur"
n.set_snapshots(pd.date_range(freq="h", **config["snapshots"])) 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(buses, "Bus")
n.import_components_from_dataframe(lines, "Line") 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)}") logging.warning(f"No powerplants known in: {', '.join(countries_wo_ppl)}")
substations = n.buses.query("substation_lv") substations = n.buses.query("substation_lv")
ppl = ppl.dropna(subset=["lat", "lon"])
ppl = map_country_bus(ppl, substations) ppl = map_country_bus(ppl, substations)
bus_null_b = ppl["bus"].isnull() bus_null_b = ppl["bus"].isnull()

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ from _helpers import (
override_component_attrs, override_component_attrs,
update_config_with_sector_opts, 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 build_energy_totals import build_co2_totals, build_eea_co2, build_eurostat_co2
from networkx.algorithms import complement from networkx.algorithms import complement
from networkx.algorithms.connectivity.edge_augmentation import k_edge_augmentation 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))) n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
sanitize_carriers(n, snakemake.config)
n.export_to_netcdf(snakemake.output[0]) n.export_to_netcdf(snakemake.output[0])

View File

@ -53,7 +53,7 @@ if __name__ == "__main__":
snakemake snakemake
) # TODO Make logging compatible with progressbar (see PR #102) ) # 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" url = "https://zenodo.org/record/3517921/files/pypsa-eur-tutorial-data-bundle.tar.xz"
else: else:
url = "https://zenodo.org/record/3517935/files/pypsa-eur-data-bundle.tar.xz" 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 _helpers import configure_logging, get_aggregation_strategies, update_p_nom_max
from add_electricity import load_costs from add_electricity import load_costs
from cluster_network import cluster_regions, clustering_for_n_clusters from cluster_network import cluster_regions, clustering_for_n_clusters
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe from pypsa.clustering.spatial import (
from pypsa.networkclustering import (
aggregategenerators, aggregategenerators,
aggregateoneport, aggregateoneport,
busmap_by_stubs, busmap_by_stubs,
get_clustering_from_busmap, get_clustering_from_busmap,
) )
from pypsa.io import import_components_from_dataframe, import_series_from_dataframe
from scipy.sparse.csgraph import connected_components, dijkstra from scipy.sparse.csgraph import connected_components, dijkstra
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

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