move building of daily heat profile to its own script
Previously this was handled inside prepare_sector_network.py.
This commit is contained in:
parent
fbff32dcfc
commit
6c20ce83d7
@ -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",
|
||||||
|
69
scripts/build_hourly_heat_demand.py
Normal file
69
scripts/build_hourly_heat_demand.py
Normal 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)
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user