move building of daily heat profile to its own script

Previously this was handled inside prepare_sector_network.py.
This commit is contained in:
Tom Brown 2024-01-15 16:47:19 +01:00 committed by Fabian Neumann
parent fbff32dcfc
commit 6c20ce83d7
4 changed files with 102 additions and 32 deletions

View File

@ -123,7 +123,7 @@ rule cluster_gas_network:
"../scripts/cluster_gas_network.py" "../scripts/cluster_gas_network.py"
rule build_heat_demands: rule build_daily_heat_demand:
params: params:
snapshots={k: config["snapshots"][k] for k in ["start", "end", "inclusive"]}, snapshots={k: config["snapshots"][k] for k in ["start", "end", "inclusive"]},
input: input:
@ -131,18 +131,39 @@ rule build_heat_demands:
regions_onshore=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson", regions_onshore=RESOURCES + "regions_onshore_elec_s{simpl}_{clusters}.geojson",
cutout="cutouts/" + CDIR + config["atlite"]["default_cutout"] + ".nc", cutout="cutouts/" + CDIR + config["atlite"]["default_cutout"] + ".nc",
output: output:
heat_demand=RESOURCES + "heat_demand_{scope}_elec_s{simpl}_{clusters}.nc", heat_demand=RESOURCES + "daily_heat_demand_{scope}_elec_s{simpl}_{clusters}.nc",
resources: resources:
mem_mb=20000, mem_mb=20000,
threads: 8 threads: 8
log: log:
LOGS + "build_heat_demands_{scope}_{simpl}_{clusters}.loc", LOGS + "build_daily_heat_demand_{scope}_{simpl}_{clusters}.loc",
benchmark: benchmark:
BENCHMARKS + "build_heat_demands/{scope}_s{simpl}_{clusters}" BENCHMARKS + "build_daily_heat_demand/{scope}_s{simpl}_{clusters}"
conda: conda:
"../envs/environment.yaml" "../envs/environment.yaml"
script: script:
"../scripts/build_heat_demand.py" "../scripts/build_daily_heat_demand.py"
rule build_hourly_heat_demand:
params:
snapshots=config["snapshots"],
input:
heat_profile="data/heat_load_profile_BDEW.csv",
heat_demand=RESOURCES + "daily_heat_demand_{scope}_elec_s{simpl}_{clusters}.nc",
output:
heat_demand=RESOURCES + "hourly_heat_demand_{scope}_elec_s{simpl}_{clusters}.nc",
resources:
mem_mb=2000,
threads: 8
log:
LOGS + "build_hourly_heat_demand_{scope}_{simpl}_{clusters}.loc",
benchmark:
BENCHMARKS + "build_hourly_heat_demand/{scope}_s{simpl}_{clusters}"
conda:
"../envs/environment.yaml"
script:
"../scripts/build_hourly_heat_demand.py"
rule build_temperature_profiles: rule build_temperature_profiles:
@ -727,7 +748,6 @@ rule prepare_sector_network:
if config["foresight"] == "overnight" if config["foresight"] == "overnight"
else RESOURCES else RESOURCES
+ "biomass_potentials_s{simpl}_{clusters}_{planning_horizons}.csv", + "biomass_potentials_s{simpl}_{clusters}_{planning_horizons}.csv",
heat_profile="data/heat_load_profile_BDEW.csv",
costs="data/costs_{}.csv".format(config["costs"]["year"]) costs="data/costs_{}.csv".format(config["costs"]["year"])
if config["foresight"] == "overnight" if config["foresight"] == "overnight"
else "data/costs_{planning_horizons}.csv", else "data/costs_{planning_horizons}.csv",
@ -740,9 +760,7 @@ rule prepare_sector_network:
simplified_pop_layout=RESOURCES + "pop_layout_elec_s{simpl}.csv", simplified_pop_layout=RESOURCES + "pop_layout_elec_s{simpl}.csv",
industrial_demand=RESOURCES industrial_demand=RESOURCES
+ "industrial_energy_demand_elec_s{simpl}_{clusters}_{planning_horizons}.csv", + "industrial_energy_demand_elec_s{simpl}_{clusters}_{planning_horizons}.csv",
heat_demand_urban=RESOURCES + "heat_demand_urban_elec_s{simpl}_{clusters}.nc", hourly_heat_demand_total=RESOURCES + "hourly_heat_demand_total_elec_s{simpl}_{clusters}.nc",
heat_demand_rural=RESOURCES + "heat_demand_rural_elec_s{simpl}_{clusters}.nc",
heat_demand_total=RESOURCES + "heat_demand_total_elec_s{simpl}_{clusters}.nc",
temp_soil_total=RESOURCES + "temp_soil_total_elec_s{simpl}_{clusters}.nc", temp_soil_total=RESOURCES + "temp_soil_total_elec_s{simpl}_{clusters}.nc",
temp_soil_rural=RESOURCES + "temp_soil_rural_elec_s{simpl}_{clusters}.nc", temp_soil_rural=RESOURCES + "temp_soil_rural_elec_s{simpl}_{clusters}.nc",
temp_soil_urban=RESOURCES + "temp_soil_urban_elec_s{simpl}_{clusters}.nc", temp_soil_urban=RESOURCES + "temp_soil_urban_elec_s{simpl}_{clusters}.nc",

View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2023 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
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
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake(
"build_heat_demands",
simpl="",
clusters=48,
)
snapshots = pd.date_range(freq="h", **snakemake.params.snapshots)
daily_space_heat_demand = (
xr.open_dataarray(snakemake.input.heat_demand)
.to_pandas()
.reindex(index=snapshots, method="ffill")
)
intraday_profiles = pd.read_csv(snakemake.input.heat_profile, index_col=0)
sectors = ["residential", "services"]
uses = ["water", "space"]
heat_demand = {}
for sector, use in product(sectors, uses):
weekday = list(intraday_profiles[f"{sector} {use} weekday"])
weekend = list(intraday_profiles[f"{sector} {use} weekend"])
weekly_profile = weekday * 5 + weekend * 2
intraday_year_profile = generate_periodic_profiles(
daily_space_heat_demand.index.tz_localize("UTC"),
nodes=daily_space_heat_demand.columns,
weekly_profile=weekly_profile,
)
if use == "space":
heat_demand[f"{sector} {use}"] = daily_space_heat_demand * intraday_year_profile
else:
heat_demand[f"{sector} {use}"] = intraday_year_profile
heat_demand = pd.concat(heat_demand,
axis=1,
names = ["sector use", "node"])
heat_demand.index.name="snapshots"
print(heat_demand)
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)

View File

@ -18,7 +18,7 @@ import numpy as np
import pandas as pd import pandas as pd
import pypsa import pypsa
import xarray as xr import xarray as xr
from _helpers import generate_periodic_profiles, update_config_with_sector_opts from _helpers import update_config_with_sector_opts
from add_electricity import calculate_annuity, sanitize_carriers 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
@ -1639,14 +1639,8 @@ def add_land_transport(n, costs):
def build_heat_demand(n): def build_heat_demand(n):
# copy forward the daily average heat demand into each hour, so it can be multiplied by the intraday profile
daily_space_heat_demand = (
xr.open_dataarray(snakemake.input.heat_demand_total)
.to_pandas()
.reindex(index=n.snapshots, method="ffill")
)
intraday_profiles = pd.read_csv(snakemake.input.heat_profile, index_col=0) heat_demand_shape = xr.open_dataset(snakemake.input.hourly_heat_demand_total).to_dataframe().unstack(level=1)
sectors = ["residential", "services"] sectors = ["residential", "services"]
uses = ["water", "space"] uses = ["water", "space"]
@ -1654,25 +1648,14 @@ def build_heat_demand(n):
heat_demand = {} heat_demand = {}
electric_heat_supply = {} electric_heat_supply = {}
for sector, use in product(sectors, uses): for sector, use in product(sectors, uses):
weekday = list(intraday_profiles[f"{sector} {use} weekday"])
weekend = list(intraday_profiles[f"{sector} {use} weekend"])
weekly_profile = weekday * 5 + weekend * 2
intraday_year_profile = generate_periodic_profiles(
daily_space_heat_demand.index.tz_localize("UTC"),
nodes=daily_space_heat_demand.columns,
weekly_profile=weekly_profile,
)
if use == "space": name = f"{sector} {use}"
heat_demand_shape = daily_space_heat_demand * intraday_year_profile
else:
heat_demand_shape = intraday_year_profile
heat_demand[f"{sector} {use}"] = ( heat_demand[name] = (
heat_demand_shape / heat_demand_shape.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[f"{sector} {use}"] = (
heat_demand_shape / heat_demand_shape.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
heat_demand = pd.concat(heat_demand, axis=1) heat_demand = pd.concat(heat_demand, axis=1)