-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: heat pump cop profiles #393
Changes from 8 commits
7b4be56
89dcd08
7eb1f97
c0923bc
5c41b25
9b823cf
e8f8c11
2a3d47a
6e4bd05
170aa27
4dc3989
30fc430
c7ecbd7
2314dad
8c10c6d
4ba0957
70f8712
40358da
e01958d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -189,41 +189,36 @@ parameters: | |
nuts-year: 2013 | ||
heat: | ||
space_heat: | ||
carnot-performance: 0.36 # [Nouvel_2015] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor cleanup here to remove some unused configs and update refs (still more to do) |
||
gas-eff: 0.97 # [DEA_2016], but 70-80% according to [Qu_2014] | ||
oil-eff: 0.9 # [DEA_2016], but 0.63 according to [martin_2014] | ||
gas-eff: 0.97 # [@DEA:2020], but 70-80% according to [Qu_2014] | ||
oil-eff: 0.9 # [@DEA:2020], but 0.63 according to [martin_2014] | ||
solid-fossil-eff: 0.8 # Assume same as biofuel | ||
biofuel-eff: 0.8 # [mermoud_2015] [Chandrasekaran_2013] [DEA_2016] | ||
biofuel-eff: 0.8 # [@DEA:2020] [mermoud_2015] [Chandrasekaran_2013] [DEA_2016] | ||
solar-thermal-eff: 1 # Eurostat energy balances method | ||
electricity-eff: 1 # must be 1 for the time being (we assume 1 -> 1 electricity -> heat conversion) | ||
space-heat-temp: 36 # degrees C [Nouvel_2015] | ||
hp-cop: 3.5 | ||
water_heat: | ||
gas-eff: 0.97 # [DEA_2016], but 70-80% according to [Qu_2014] | ||
oil-eff: 0.9 # [DEA_2016], but 0.63 according to [martin_2014] | ||
hot_water: | ||
gas-eff: 0.97 # [@DEA:2020], but 70-80% according to [Qu_2014] | ||
oil-eff: 0.9 # [@DEA:2020], but 0.63 according to [martin_2014] | ||
solid-fossil-eff: 0.8 # Assume same as biofuel | ||
biofuel-eff: 0.8 # [mermoud_2015] [Chandrasekaran_2013] [DEA_2016] | ||
solar-thermal-eff: 1 # Eurostat energy balances method | ||
electricity-eff: 1 # must be 1 for the time being (we assume 1 -> 1 electricity -> heat conversion) | ||
water-heat-temp: 52 # degrees C [Nouvel_2015] | ||
hp-cop: 3.5 | ||
cooking: | ||
gas-eff: 0.28 # [Karunanithy_2016] | ||
oil-eff: 0.28 # [Karunanithy_2016] assuming oil == gas efficiency | ||
solid-fossil-eff: 0.15 # [Ramanathan_1994] scaled down 60%, based on values calculated by [Karunanithy_2016] | ||
biofuel-eff: 0.1 # [Ramanathan_1994] scaled down 60%, based on values calculated by [Karunanithy_2016] | ||
electricity-eff: 0.5 # [Karunanithy_2016] based on 2/3 40% efficient direct electric, 1/3 70% efficient induction | ||
heat-pump: | ||
sink-temperature: | ||
sink-temperature: # All values are assumed. | ||
underfloor: 35 | ||
radiator-large: 50 | ||
radiator-conventional: 65 | ||
hot-water: 60 | ||
space-heat-sink-ratio: # All values are assumed. | ||
space-heat-sink-shares: # All values are assumed. | ||
underfloor: 0.1 | ||
radiator-large: 0.15 | ||
radiator-conventional: 0.75 | ||
heat-pump-ratio: # see https://stats.ehpa.org, 2018 market data assuming current ashp = air-to-air AND air-to-water | ||
heat-pump-shares: # see https://stats.ehpa.org, 2018 market data assuming current ashp = air-to-air AND air-to-water | ||
ashp: 0.9 | ||
gshp: 0.1 | ||
correction-factor: 0.85 # [@Ruhnau:2019] | ||
brynpickering marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -458,9 +458,6 @@ properties: | |
description: Parameters of space heating technologies. | ||
additionalProperties: false | ||
properties: | ||
carnot-performance: | ||
type: number | ||
description: Carnot performance. | ||
gas-eff: | ||
type: number | ||
description: Gas boiler efficiency. | ||
|
@@ -479,13 +476,7 @@ properties: | |
electricity-eff: | ||
type: number | ||
description: Electric heater efficiency. | ||
space-heat-temp: | ||
type: number | ||
description: Space heating temperature. | ||
hp-cop: | ||
type: number | ||
description: Heat pump coefficient of performance. | ||
water_heat: | ||
hot_water: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. water_heat -> hot_water applied everywhere for consistency |
||
type: object | ||
description: Parameters of water heating technologies. | ||
additionalProperties: false | ||
|
@@ -508,12 +499,6 @@ properties: | |
electricity-eff: | ||
type: number | ||
description: Electric heater efficiency. | ||
water-heat-temp: | ||
type: number | ||
description: Water heating temperature. | ||
hp-cop: | ||
type: number | ||
description: Heat pump coefficient of performance. | ||
cooking: | ||
type: object | ||
description: Parameters of cooking technologies. | ||
|
@@ -557,9 +542,9 @@ properties: | |
hot-water: | ||
type: integer | ||
description: Hot water temperature (not space heating). | ||
space-heat-sink-ratio: | ||
space-heat-sink-shares: | ||
type: object | ||
description: Ratio of building stock assumed to have different space heating methods | ||
description: Share of building stock assumed to have different space heating methods | ||
additionalProperties: false | ||
properties: | ||
underfloor: | ||
|
@@ -571,9 +556,11 @@ properties: | |
radiator-conventional: | ||
type: number | ||
description: Conventional radiators (usually installed alongside gas boilers). | ||
heat-pump-ratio: | ||
heat-pump-shares: | ||
type: object | ||
description: Assumed ratio of air- vs ground-source heat pumps. Must sum to 1. | ||
description: >- | ||
Market share of air- vs ground-source heat pumps, which will be used to create a generic "heat pump" technology. | ||
Must sum to 1. | ||
additionalProperties: false | ||
properties: | ||
ashp: | ||
|
@@ -590,7 +577,7 @@ properties: | |
type: number | ||
minimum: 0 | ||
maximum: 1 | ||
description: Correction factor to go from manufacturer data to 'real' systems | ||
description: Correction factor to go from manufacturer data on heat pump performance to 'real' system performance. | ||
scope: | ||
type: object | ||
description: Spatial and temporal scope bounding the models. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
from functools import partial | ||
from multiprocessing import Pool | ||
|
||
import pandas as pd | ||
import xarray as xr | ||
|
||
|
||
def group_gridcells( | ||
gridded_data: xr.Dataset, | ||
grid_weight: xr.DataArray, | ||
annual_demand: xr.DataArray, | ||
threads: int, | ||
) -> pd.DataFrame: | ||
"""Group gridded heat data into resolution-specific units, taking a weighted average of grid values. | ||
|
||
Also take a weighted average of hot water and space heat demand (using per-unit annual demands) to return a single "heat" timeseries. | ||
|
||
Args: | ||
gridded_data (xr.Dataset): Gridded timeseries space heat and hot water data. | ||
grid_weight (xr.DataArray): Weighted mapping from grid (a.k.a. "site") to units. | ||
annual_demand (xr.DataArray): Per-unit annual space heating and hot water demands. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find it hard to understand why this input is necessary. Can you explain? Is this a bias-correction? Or is it because the time series have no meaningful scale / are normed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's doing two sets of weighted averages: weighted averaging "sites" into "IDs" (using population) and weighted averaging "space heat" and "hot water" demand into a general profile for "heat" (using annual demands). |
||
threads (int): Number of threads over which to undertake multiprocessing. | ||
|
||
Returns: | ||
pd.DataFrame: Index: timeseries, Columns: unit IDs | ||
""" | ||
|
||
partial_func = partial( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe rename function to "apply_weights" or similar? |
||
_site_weighted_ave, gridded_data=gridded_data, grid_weight=grid_weight | ||
) | ||
# This is a slow operation, so we parallelise it. | ||
with Pool(threads) as pool: | ||
per_id_averages = pool.map(partial_func, grid_weight.id.values) | ||
weighted_average_ds = xr.concat(per_id_averages, dim="id") | ||
|
||
weighted_average_da = ( | ||
weighted_average_ds.to_array("end_use") | ||
.groupby("time.year") | ||
.apply(_end_use_weighted_ave, annual_demand=annual_demand) | ||
) | ||
return weighted_average_da.astype("float32").to_series().unstack("id") | ||
|
||
|
||
def prepare_annual_demand(annual_demand: pd.Series) -> xr.DataArray: | ||
"""Restructure annual demand MultiIndex series into a multi-dimensional array. | ||
|
||
Result sums over all building categories and only contains hot water and space heating demands (not cooking). | ||
""" | ||
return ( | ||
annual_demand.rename_axis(columns="id") | ||
.stack() | ||
.to_xarray() | ||
.sel(end_use=["space_heat", "hot_water"]) | ||
.sum("cat_name") | ||
) | ||
|
||
|
||
def _site_weighted_ave( | ||
id: str, gridded_data: xr.Dataset, grid_weight: xr.DataArray | ||
) -> xr.Dataset: | ||
"""Multi-processing helper function to get the weighted average of all gridcells for a given spatial unit (id).""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is nothing "multi-processing" related in this function, right? Better remove from comment in that case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More that it only exists because it is needs to be its own function so that it can be called by the multiprocessor. |
||
id_grid_weight = grid_weight.sel(id=id).dropna("site") | ||
return ( | ||
gridded_data.reindex_like(id_grid_weight) | ||
# we normalise the weights in case they aren't already | ||
* (id_grid_weight / id_grid_weight.sum("site")) | ||
).sum(["site"]) | ||
|
||
|
||
def _end_use_weighted_ave( | ||
one_year_profile: xr.DataArray, annual_demand: xr.DataArray | ||
) -> xr.DataArray: | ||
"""Take a weighted average of all heat energy end uses using annual demands per spatial unit as the weights.""" | ||
year = one_year_profile.time.dt.year[0] | ||
normalised_demand = annual_demand / annual_demand.sum("end_use") | ||
demand = one_year_profile * normalised_demand.sel(year=year).drop("year") | ||
return demand.sum("end_use") | ||
|
||
|
||
if __name__ == "__main__": | ||
gridded_data = xr.open_dataset(snakemake.input.gridded_timeseries_data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First use of this approach in the repository. The line will make history. :D |
||
grid_weights = xr.open_dataarray(snakemake.input.grid_weights) | ||
annual_demand = pd.read_csv(snakemake.input.annual_demand, index_col=[0, 1, 2]) | ||
annual_demand_ds = prepare_annual_demand(annual_demand) | ||
resolution_specific_data = group_gridcells( | ||
gridded_data, grid_weights, annual_demand_ds, snakemake.threads | ||
) | ||
resolution_specific_data.to_csv(snakemake.output[0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems you've implemented most parts of #39, too. Is that correct? If yes, would add that too.