|
1
|
|
|
import os |
|
2
|
|
|
import oemof.thermal.compression_heatpumps_and_chillers as cmpr_hp_chiller |
|
3
|
|
|
import pandas as pd |
|
4
|
|
|
import numpy as np |
|
5
|
|
|
from disaggregator import data |
|
6
|
|
|
from reegis import coastdat, demand_disaggregator |
|
7
|
|
|
from reegis import config as cfg |
|
8
|
|
|
|
|
9
|
|
|
|
|
10
|
|
|
def flow_temperature_dependent_on_ambient(t_max, t_min, t_amb): |
|
11
|
|
|
""" |
|
12
|
|
|
Parameters |
|
13
|
|
|
---------- |
|
14
|
|
|
t_max : int |
|
15
|
|
|
Maximum flow temperature of heating system |
|
16
|
|
|
t_min : int |
|
17
|
|
|
Minimum flow temperature of heating system |
|
18
|
|
|
t_amb : pd.Series |
|
19
|
|
|
Ambient temperature |
|
20
|
|
|
|
|
21
|
|
|
Returns: list |
|
22
|
|
|
List containing flow temperature depending on heatload/ambient temperature |
|
23
|
|
|
------- |
|
24
|
|
|
""" |
|
25
|
|
|
|
|
26
|
|
|
delta_t_flow = t_max - t_min |
|
27
|
|
|
delta_t_amb = 30 |
|
28
|
|
|
T_flow = [t_max - (delta_t_flow/delta_t_amb) * i for i in range(31)] |
|
29
|
|
|
T_round = np.round(t_amb) |
|
30
|
|
|
|
|
31
|
|
|
t_ind = np.zeros(shape=(len(t_amb))) |
|
32
|
|
|
tvl = np.zeros(shape=(len(t_amb))) # Vorlauftemperatur |
|
33
|
|
|
for i in range(len(t_amb)): |
|
34
|
|
|
if T_round[i] < -14: |
|
35
|
|
|
t_ind[i] = 1 |
|
36
|
|
|
elif T_round[i] >= -14 and T_round[i] < 0: |
|
37
|
|
|
t_ind[i] = abs(15 + T_round[i]) # nimm erste 15 Stellen bei -10 bis 0°C |
|
38
|
|
|
elif T_round[i] >= 0: |
|
39
|
|
|
t_ind[i] = min(31, 16 + T_round[i]) |
|
40
|
|
|
tvl[i] = int(max(t_min, T_flow[int(t_ind[i] - 1)])) |
|
41
|
|
|
|
|
42
|
|
|
tvl = list(tvl) |
|
43
|
|
|
|
|
44
|
|
|
return tvl |
|
45
|
|
|
|
|
46
|
|
|
|
|
47
|
|
|
def calculate_COP(t_flow, t_low, quality_grade): |
|
48
|
|
|
""" |
|
49
|
|
|
Parameters |
|
50
|
|
|
---------- |
|
51
|
|
|
t_flow : int |
|
52
|
|
|
Maximum flow temperature of heating system |
|
53
|
|
|
t_low : Series |
|
54
|
|
|
Minimum flow temperature of heating system |
|
55
|
|
|
quality_grade : float |
|
56
|
|
|
Ambient temperature |
|
57
|
|
|
|
|
58
|
|
|
Returns: list |
|
59
|
|
|
List containing flow temperature depending on heatload/ambient temperature |
|
60
|
|
|
------- |
|
61
|
|
|
""" |
|
62
|
|
|
|
|
63
|
|
|
cops = cmpr_hp_chiller.calc_cops( |
|
64
|
|
|
temp_high=list([t_flow]), |
|
65
|
|
|
temp_low=t_low, |
|
66
|
|
|
quality_grade=quality_grade, |
|
67
|
|
|
mode='heat_pump', |
|
68
|
|
|
temp_threshold_icing=2, |
|
69
|
|
|
factor_icing=0.8) |
|
70
|
|
|
|
|
71
|
|
|
return cops |
|
72
|
|
|
|
|
73
|
|
|
|
|
74
|
|
|
def calculate_dynamic_COP(t_flow, t_low, quality_grade): |
|
75
|
|
|
r""" |
|
76
|
|
|
This function calculates COPs of a heatpump. Both, the flow temperature and the heat source must be given |
|
77
|
|
|
as a timeseries and can very for each timestep. |
|
78
|
|
|
|
|
79
|
|
|
Parameters |
|
80
|
|
|
---------- |
|
81
|
|
|
t_flow : Series |
|
82
|
|
|
Flow temperature series |
|
83
|
|
|
t_low : Series |
|
84
|
|
|
Heat source of heatpump |
|
85
|
|
|
quality_grade : float |
|
86
|
|
|
Carnot efficiency factor of heat pump |
|
87
|
|
|
|
|
88
|
|
|
Returns: Series |
|
89
|
|
|
Series containing COPs |
|
90
|
|
|
------- |
|
91
|
|
|
""" |
|
92
|
|
|
cops = [] |
|
93
|
|
|
for i in range(0, len(t_flow)): |
|
94
|
|
|
tmp_high = list([t_flow[i]]) |
|
95
|
|
|
tmp_low = list([t_low[i]]) |
|
96
|
|
|
|
|
97
|
|
|
cop = cmpr_hp_chiller.calc_cops( |
|
98
|
|
|
temp_high=tmp_high, |
|
99
|
|
|
temp_low=tmp_low, |
|
100
|
|
|
quality_grade=quality_grade, |
|
101
|
|
|
mode='heat_pump', |
|
102
|
|
|
temp_threshold_icing=2, |
|
103
|
|
|
factor_icing=0.8) |
|
104
|
|
|
|
|
105
|
|
|
cops.append(cop[0]) |
|
106
|
|
|
|
|
107
|
|
|
cops = pd.Series(cops) |
|
108
|
|
|
|
|
109
|
|
|
return cops |
|
110
|
|
|
|
|
111
|
|
|
|
|
112
|
|
|
def calculate_mixed_COPS_per_region(t_amb, t_ground, quality_grade, share_ashp=0.7, share_gshp=0.3): |
|
113
|
|
|
""" |
|
114
|
|
|
Parameters |
|
115
|
|
|
---------- |
|
116
|
|
|
t_amb : Series |
|
117
|
|
|
Ambient temperature |
|
118
|
|
|
t_ground : Series |
|
119
|
|
|
Ground temperature for GSHP |
|
120
|
|
|
quality_grade : float |
|
121
|
|
|
Ambient temperature |
|
122
|
|
|
share_ashp: Float |
|
123
|
|
|
Share of air sourced heat pumps |
|
124
|
|
|
share_gshp: Float |
|
125
|
|
|
Share of ground sourced heat pumps |
|
126
|
|
|
|
|
127
|
|
|
Returns: DataFrame |
|
128
|
|
|
DataFrame containing some COP series |
|
129
|
|
|
------- |
|
130
|
|
|
""" |
|
131
|
|
|
|
|
132
|
|
|
t_flow = dict(high = 75, medium=55, low = 40) |
|
133
|
|
|
|
|
134
|
|
|
# Calculate flow temperature for different temperature levels |
|
135
|
|
|
tvl75 = flow_temperature_dependent_on_ambient(t_flow['high'], 50, t_amb) |
|
136
|
|
|
tvl50 = flow_temperature_dependent_on_ambient(t_flow['medium'], 30 , t_amb) |
|
137
|
|
|
tvl35 = flow_temperature_dependent_on_ambient(t_flow['low'], 30 , t_amb) |
|
138
|
|
|
|
|
139
|
|
|
# Calculate COPs for air sourced heat pump |
|
140
|
|
|
cop75_ASHP = calculate_dynamic_COP(tvl75, t_amb, quality_grade) |
|
141
|
|
|
cop50_ASHP = calculate_dynamic_COP(tvl50, t_amb, quality_grade) |
|
142
|
|
|
cop35_ASHP = calculate_dynamic_COP(tvl35, t_amb, quality_grade) |
|
143
|
|
|
copwater_ASHP = calculate_dynamic_COP(np.ones(shape=8760)*50, t_amb, quality_grade) |
|
144
|
|
|
|
|
145
|
|
|
# Calculate COPs for ground sourced heat pump |
|
146
|
|
|
cop75_GSHP = calculate_dynamic_COP(tvl75, t_ground, quality_grade) |
|
147
|
|
|
cop50_GSHP = calculate_dynamic_COP(tvl50, t_ground, quality_grade) |
|
148
|
|
|
cop35_GSHP = calculate_dynamic_COP(tvl35, t_ground, quality_grade) |
|
149
|
|
|
copwater_GSHP = calculate_dynamic_COP(np.ones(shape=8760)*50, t_ground, quality_grade) |
|
150
|
|
|
|
|
151
|
|
|
cops_aggregated = pd.DataFrame(columns=['COP35_air', 'COP50_air', 'COP75_air', 'COP35_ground', 'COP50_ground', |
|
152
|
|
|
'COP75_ground', 'COPwater_air', 'COPwater_ground','COP_mean' ]) |
|
153
|
|
|
|
|
154
|
|
|
# Write COP-Series to DataFrame |
|
155
|
|
|
cops_aggregated['COP35_air'] = cop35_ASHP |
|
156
|
|
|
cops_aggregated['COP50_air'] = cop50_ASHP |
|
157
|
|
|
cops_aggregated['COP75_air'] = cop75_ASHP |
|
158
|
|
|
cops_aggregated['COP35_ground'] = cop35_GSHP |
|
159
|
|
|
cops_aggregated['COP50_ground'] = cop50_GSHP |
|
160
|
|
|
cops_aggregated['COP75_ground'] = cop75_GSHP |
|
161
|
|
|
cops_aggregated['COPwater_air'] = copwater_ASHP |
|
162
|
|
|
cops_aggregated['COPwater_ground'] = copwater_GSHP |
|
163
|
|
|
|
|
164
|
|
|
# Calculate mean COP and add it to DataFrame |
|
165
|
|
|
cop_air_mean = (cop35_ASHP + cop50_ASHP + cop75_ASHP) / 3 |
|
166
|
|
|
cop_ground_mean = (cop35_GSHP + cop50_GSHP + cop75_GSHP) / 3 |
|
167
|
|
|
cop_mean = share_ashp * cop_air_mean + share_gshp * cop_ground_mean |
|
168
|
|
|
cops_aggregated['COP_mean'] = cop_mean |
|
169
|
|
|
|
|
170
|
|
|
# Limit COP to 7 |
|
171
|
|
|
limit = cops_aggregated >= 7 |
|
172
|
|
|
artifact = cops_aggregated < 0 |
|
173
|
|
|
cops_aggregated[limit] = 7 |
|
174
|
|
|
cops_aggregated[artifact] = 7 |
|
175
|
|
|
|
|
176
|
|
|
return cops_aggregated |
|
177
|
|
|
|
|
178
|
|
|
|
|
179
|
|
|
def calculate_mixed_cops_by_nuts3(year, name, share_ashp=0.7, share_gshp=0.3, quality_grade=0.4): |
|
180
|
|
|
""" |
|
181
|
|
|
Parameters |
|
182
|
|
|
---------- |
|
183
|
|
|
year: int |
|
184
|
|
|
Year of interest |
|
185
|
|
|
name: string |
|
186
|
|
|
Name of the analysed set |
|
187
|
|
|
share_ashp: Float |
|
188
|
|
|
Share of air sourced heat pumps |
|
189
|
|
|
share_gshp: Float |
|
190
|
|
|
Share of ground sourced heat pumps |
|
191
|
|
|
quality_grade : float |
|
192
|
|
|
Ambient temperature |
|
193
|
|
|
|
|
194
|
|
|
Returns: 2 DataFrames |
|
195
|
|
|
DataFrames containing mean COP series for each German NUTS3 region |
|
196
|
|
|
------- |
|
197
|
|
|
""" |
|
198
|
|
|
|
|
199
|
|
|
fn_pattern = "mixed_cops_by_nuts3_{name}_{year}.csv".format(name=name, year=year) |
|
200
|
|
|
fn_pattern_water = "cops_by_nuts3_{name}_{year}_water.csv".format(name=name, year=year) |
|
201
|
|
|
fn = os.path.join(cfg.get("paths", "cop_precalc"), fn_pattern) |
|
202
|
|
|
fn_water = os.path.join(cfg.get("paths", "cop_precalc"), fn_pattern_water) |
|
203
|
|
|
|
|
204
|
|
|
if not os.path.isfile(fn): |
|
205
|
|
|
share_ashp = share_ashp |
|
206
|
|
|
share_gshp = share_gshp |
|
207
|
|
|
quality_grade = quality_grade |
|
208
|
|
|
t_ground = pd.Series(np.ones(8760)*10) |
|
209
|
|
|
|
|
210
|
|
|
outfile = os.path.join(cfg.get("paths", "cop_precalc"), "average_temp_by_nuts3_{year}.csv".format(year=year)) |
|
211
|
|
|
|
|
212
|
|
|
if not os.path.isfile(outfile): |
|
213
|
|
|
|
|
214
|
|
|
# Load NUTS3-geometries |
|
215
|
|
|
nuts3 = data.database_shapes() |
|
216
|
|
|
nuts3 = nuts3.to_crs(crs=4326) |
|
217
|
|
|
|
|
218
|
|
|
# Get average temperature per NUTS3, coastdat only available until 2014 |
|
219
|
|
|
coastdat.spatial_average_weather(year, nuts3, 'temp_air', 'deTemp', outfile=outfile) |
|
220
|
|
|
NUTS3_temp = pd.read_csv(outfile) |
|
221
|
|
|
NUTS3_temp.drop('Unnamed: 0', axis='columns', inplace=True) |
|
222
|
|
|
|
|
223
|
|
|
else: |
|
224
|
|
|
NUTS3_temp = pd.read_csv(outfile) |
|
225
|
|
|
NUTS3_temp.drop('Unnamed: 0', axis='columns', inplace=True) |
|
226
|
|
|
|
|
227
|
|
|
# Create empty DataFrames |
|
228
|
|
|
COP_NUTS3 = pd.DataFrame(index=pd.date_range('1/1/'+str(year), periods=8760, freq='H'), |
|
229
|
|
|
columns=NUTS3_temp.columns) |
|
230
|
|
|
COP_NUTS3_water = pd.DataFrame(index=pd.date_range('1/1/'+str(year), periods=8760, freq='H'), |
|
231
|
|
|
columns=NUTS3_temp.columns) |
|
232
|
|
|
|
|
233
|
|
|
# Loop through NUTS3-regions and calculate mixed COP for each region |
|
234
|
|
|
for r in COP_NUTS3.columns: |
|
235
|
|
|
tmp_cop = calculate_mixed_COPS_per_region(NUTS3_temp[r]-273.15, t_ground, quality_grade=quality_grade, |
|
236
|
|
|
share_ashp=share_ashp, share_gshp=share_gshp) |
|
237
|
|
|
tmp_cop.set_index(COP_NUTS3.index, inplace=True) |
|
238
|
|
|
COP_NUTS3[r] = tmp_cop['COP_mean'] |
|
239
|
|
|
COP_NUTS3_water[r] = tmp_cop['COPwater_air']* share_ashp + tmp_cop['COPwater_ground'] * share_gshp |
|
240
|
|
|
|
|
241
|
|
|
COP_NUTS3.to_csv(fn) |
|
242
|
|
|
COP_NUTS3_water.to_csv(fn_water) |
|
243
|
|
|
|
|
244
|
|
|
else: |
|
245
|
|
|
COP_NUTS3 = pd.read_csv(fn) |
|
246
|
|
|
COP_NUTS3_water = pd.read_csv(fn_water) |
|
247
|
|
|
|
|
248
|
|
|
return COP_NUTS3, COP_NUTS3_water |
|
249
|
|
|
|
|
250
|
|
|
|
|
251
|
|
|
def aggregate_COP_by_region(regions, COP): |
|
252
|
|
|
mean_COP = pd.DataFrame(columns=regions.index) |
|
253
|
|
|
#mean_COP_water = pd.DataFrame(columns=regions.index) |
|
254
|
|
|
|
|
255
|
|
|
nuts3_list = demand_disaggregator.get_nutslist_for_regions(regions) |
|
256
|
|
|
|
|
257
|
|
|
for zone in regions.index: |
|
258
|
|
|
idx = nuts3_list.loc[zone]["nuts"] |
|
259
|
|
|
mean_COP[zone] = COP[idx].mean(axis=1) |
|
260
|
|
|
#mean_COP_water[zone] = COP_water[idx].mean(axis=1) |
|
261
|
|
|
|
|
262
|
|
|
return mean_COP |
|
263
|
|
|
|
|
264
|
|
|
|
|
265
|
|
|
def get_hp_timeseries(heat_profile, cop, E_el): |
|
266
|
|
|
cop.index = heat_profile.index |
|
267
|
|
|
|
|
268
|
|
|
for Q_rated in range(0,10000000,1000): |
|
269
|
|
|
heat_hp = Q_rated * cop * heat_profile |
|
270
|
|
|
elc_hp = heat_hp / cop |
|
271
|
|
|
|
|
272
|
|
|
if elc_hp.sum() > E_el: |
|
273
|
|
|
break |
|
274
|
|
|
|
|
275
|
|
|
return elc_hp, heat_hp |
|
|
|
|
|
|
276
|
|
|
|