add release notes, minor code improvements

This commit is contained in:
Fabian Neumann 2024-01-22 09:18:26 +01:00
parent d98ad95332
commit 2183e742b2
10 changed files with 75 additions and 43 deletions

View File

@ -28,6 +28,24 @@ Upcoming Release
* Cluster residential and services heat buses by default. Can be disabled with ``cluster_heat_buses: false``. * Cluster residential and services heat buses by default. Can be disabled with ``cluster_heat_buses: false``.
* Bugfix: Do not reduce district heat share when building population-weighted
energy statistics. Previously the district heating share was being multiplied
by the population weighting, reducing the DH share with multiple nodes.
* Move building of daily heat profile to its own rule
:mod:`build_hourly_heat_demand` from :mod:`prepare_sector_network`.
* In :mod:`build_energy_totals`, district heating shares are now reported in a
separate file.
* Move calculation of district heating share to its own rule
:mod:`build_district_heat_share`.
* Move building of distribution of existing heating to own rule
:mod:`build_existing_heating_distribution`. This makes the distribution of
existing heating to urban/rural, residential/services and spatially more
transparent.
PyPSA-Eur 0.9.0 (5th January 2024) PyPSA-Eur 0.9.0 (5th January 2024)
================================== ==================================

View File

@ -20,6 +20,12 @@ Rule ``add_existing_baseyear``
.. automodule:: add_existing_baseyear .. automodule:: add_existing_baseyear
Rule ``build_existing_heating_distribution``
==============================================================================
.. automodule:: build_existing_heating_distribution
Rule ``build_ammonia_production`` Rule ``build_ammonia_production``
============================================================================== ==============================================================================
@ -60,10 +66,20 @@ Rule ``build_gas_network``
.. automodule:: build_gas_network .. automodule:: build_gas_network
Rule ``build_heat_demand`` Rule ``build_daily_heat_demand``
============================================================================== ==============================================================================
.. automodule:: build_heat_demand .. automodule:: build_daily_heat_demand
Rule ``build_hourly_heat_demand``
==============================================================================
.. automodule:: build_hourly_heat_demand
Rule ``build_district_heat_share``
==============================================================================
.. automodule:: build_district_heat_share
Rule ``build_industrial_distribution_key`` Rule ``build_industrial_distribution_key``
============================================================================== ==============================================================================

View File

@ -28,9 +28,10 @@ rule solve_sector_network:
+ "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}_memory.log", + "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}_memory.log",
python=RESULTS python=RESULTS
+ "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}_python.log", + "logs/elec_s{simpl}_{clusters}_l{ll}_{opts}_{sector_opts}_{planning_horizons}_python.log",
threads: config["solving"]["solver_options"][config["solving"]["solver"]["options"]].get( threads:
"threads", 4 config["solving"]["solver_options"][
) config["solving"]["solver"]["options"]
].get("threads", 4)
resources: resources:
mem_mb=config["solving"]["mem"], mem_mb=config["solving"]["mem"],
walltime=config["solving"].get("walltime", "12:00:00"), walltime=config["solving"].get("walltime", "12:00:00"),

View File

@ -451,7 +451,7 @@ def add_heating_capacities_installed_before_baseyear(
efficiency=efficiency, efficiency=efficiency,
capital_cost=costs.at[costs_name, "efficiency"] capital_cost=costs.at[costs_name, "efficiency"]
* costs.at[costs_name, "fixed"], * costs.at[costs_name, "fixed"],
p_nom=existing_heating[(name, f"{heat_pump_type} heat pump")][nodes] * ratio / costs.at[costs_name, "efficiency"], p_nom=existing_heating.loc[nodes, (name, f"{heat_pump_type} heat pump")] * ratio / costs.at[costs_name, "efficiency"],
build_year=int(grouping_year), build_year=int(grouping_year),
lifetime=costs.at[costs_name, "lifetime"], lifetime=costs.at[costs_name, "lifetime"],
) )
@ -470,7 +470,7 @@ def add_heating_capacities_installed_before_baseyear(
* costs.at[f"{name_type} resistive heater", "fixed"] * costs.at[f"{name_type} resistive heater", "fixed"]
), ),
p_nom=( p_nom=(
existing_heating[(name, "resistive heater")][nodes] existing_heating.loc[nodes, (name, "resistive heater")]
* ratio * ratio
/ costs.at[f"{name_type} resistive heater", "efficiency"] / costs.at[f"{name_type} resistive heater", "efficiency"]
), ),
@ -493,7 +493,7 @@ def add_heating_capacities_installed_before_baseyear(
* costs.at[f"{name_type} gas boiler", "fixed"] * costs.at[f"{name_type} gas boiler", "fixed"]
), ),
p_nom=( p_nom=(
existing_heating[(name, "gas boiler")][nodes] existing_heating.loc[nodes, (name, "gas boiler")]
* ratio * ratio
/ costs.at[f"{name_type} gas boiler", "efficiency"] / costs.at[f"{name_type} gas boiler", "efficiency"]
), ),
@ -514,7 +514,7 @@ def add_heating_capacities_installed_before_baseyear(
capital_cost=costs.at["decentral oil boiler", "efficiency"] capital_cost=costs.at["decentral oil boiler", "efficiency"]
* costs.at["decentral oil boiler", "fixed"], * costs.at["decentral oil boiler", "fixed"],
p_nom= ( p_nom= (
existing_heating[(name, "oil boiler")][nodes] existing_heating.loc[nodes, (name, "oil boiler")]
* ratio * ratio
/ costs.at["decentral oil boiler", "efficiency"]), / costs.at["decentral oil boiler", "efficiency"]),
build_year=int(grouping_year), build_year=int(grouping_year),

View File

@ -18,7 +18,8 @@ if __name__ == "__main__":
from _helpers import mock_snakemake from _helpers import mock_snakemake
snakemake = mock_snakemake( snakemake = mock_snakemake(
"build_heat_demands", "build_daily_heat_demands",
scope="total",
simpl="", simpl="",
clusters=48, clusters=48,
) )

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2023 The PyPSA-Eur Authors # SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
""" """
@ -21,9 +21,10 @@ if __name__ == "__main__":
from _helpers import mock_snakemake from _helpers import mock_snakemake
snakemake = mock_snakemake( snakemake = mock_snakemake(
"build_heat_demands", "build_district_heat_share",
simpl="", simpl="",
clusters=48, clusters=48,
planning_horizons="2050",
) )
investment_year = int(snakemake.wildcards.planning_horizons[-4:]) investment_year = int(snakemake.wildcards.planning_horizons[-4:])
@ -68,10 +69,10 @@ if __name__ == "__main__":
f"resulting in new average share of {dist_fraction_node.mean():.2%}" f"resulting in new average share of {dist_fraction_node.mean():.2%}"
) )
df = pd.DataFrame(dtype=float) df = pd.DataFrame({
"original district heat share": district_heat_share,
df["original district heat share"] = district_heat_share "district fraction of node": dist_fraction_node,
df["district fraction of node"] = dist_fraction_node "urban fraction": urban_fraction
df["urban fraction"] = urban_fraction }, dtype=float)
df.to_csv(snakemake.output.district_heat_share) df.to_csv(snakemake.output.district_heat_share)

View File

@ -583,10 +583,10 @@ def build_district_heat_share(countries, idees):
# Missing district heating share # Missing district heating share
dh_share = pd.read_csv( dh_share = pd.read_csv(
snakemake.input.district_heat_share, index_col=0, usecols=[0, 1] snakemake.input.district_heat_share, index_col=0, usecols=[0, 1]
) ).div(100).squeeze()
# make conservative assumption and take minimum from both data sets # make conservative assumption and take minimum from both data sets
district_heat_share = pd.concat( district_heat_share = pd.concat(
[district_heat_share, dh_share.reindex(index=district_heat_share.index) / 100], axis=1 [district_heat_share, dh_share.reindex_like(district_heat_share)], axis=1
).min(axis=1) ).min(axis=1)
district_heat_share.name = "district heat share" district_heat_share.name = "district heat share"

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2023 The PyPSA-Eur Authors # SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
""" """
@ -7,8 +7,6 @@ Builds table of existing heat generation capacities for initial planning
horizon. horizon.
""" """
import pandas as pd import pandas as pd
import sys
from pypsa.descriptors import Dict
import numpy as np import numpy as np
import country_converter as coco import country_converter as coco
@ -17,19 +15,13 @@ cc = coco.CountryConverter()
def build_existing_heating(): def build_existing_heating():
# retrieve existing heating capacities # retrieve existing heating capacities
techs = [
"gas boiler",
"oil boiler",
"resistive heater",
"air heat pump",
"ground heat pump",
]
existing_heating = pd.read_csv(snakemake.input.existing_heating, existing_heating = pd.read_csv(snakemake.input.existing_heating,
index_col=0, index_col=0,
header=0) header=0)
# data for Albania, Montenegro and Macedonia not included in database existing_heating.loc["Albania"] = np.nan # data for Albania, Montenegro and Macedonia not included in database
existing_heating.loc["Albania"] = np.nan
existing_heating.loc["Montenegro"] = np.nan existing_heating.loc["Montenegro"] = np.nan
existing_heating.loc["Macedonia"] = np.nan existing_heating.loc["Macedonia"] = np.nan
@ -104,5 +96,14 @@ def build_existing_heating():
if __name__ == "__main__": if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"build_existing_heating_distribution",
simpl="",
clusters=48,
planning_horizons=2050,
)
build_existing_heating() build_existing_heating()

View File

@ -6,19 +6,19 @@
Build hourly heat demand time series from daily ones. Build hourly heat demand time series from daily ones.
""" """
import pandas as pd
import xarray as xr
from _helpers import generate_periodic_profiles, update_config_with_sector_opts
from itertools import product from itertools import product
import pandas as pd
import xarray as xr
from _helpers import generate_periodic_profiles
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( snakemake = mock_snakemake(
"build_heat_demands", "build_hourly_heat_demands",
scope="total",
simpl="", simpl="",
clusters=48, clusters=48,
) )
@ -58,12 +58,6 @@ if __name__ == "__main__":
heat_demand.index.name="snapshots" heat_demand.index.name="snapshots"
print(heat_demand) ds = heat_demand.stack().to_xarray()
print(heat_demand.stack())
ds = heat_demand.stack().to_xarray()#xr.Dataset.from_dataframe(heat_demand)
print(ds)
ds.to_netcdf(snakemake.output.heat_demand) ds.to_netcdf(snakemake.output.heat_demand)

View File

@ -1654,7 +1654,7 @@ def build_heat_demand(n):
heat_demand[name] = ( heat_demand[name] = (
heat_demand_shape[name] / heat_demand_shape[name].sum() heat_demand_shape[name] / heat_demand_shape[name].sum()
).multiply(pop_weighted_energy_totals[f"total {sector} {use}"]) * 1e6 ).multiply(pop_weighted_energy_totals[f"total {sector} {use}"]) * 1e6
electric_heat_supply[f"{sector} {use}"] = ( electric_heat_supply[name] = (
heat_demand_shape[name] / heat_demand_shape[name].sum() heat_demand_shape[name] / heat_demand_shape[name].sum()
).multiply(pop_weighted_energy_totals[f"electricity {sector} {use}"]) * 1e6 ).multiply(pop_weighted_energy_totals[f"electricity {sector} {use}"]) * 1e6