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
|
|
|
cops_aggregated[limit] = 7 |
173
|
|
|
|
174
|
|
|
return cops_aggregated |
175
|
|
|
|
176
|
|
|
|
177
|
|
|
def calculate_mixed_cops_by_nuts3(year, name, share_ashp=0.7, share_gshp=0.3, quality_grade=0.4): |
178
|
|
|
""" |
179
|
|
|
Parameters |
180
|
|
|
---------- |
181
|
|
|
year: int |
182
|
|
|
Year of interest |
183
|
|
|
name: string |
184
|
|
|
Name of the analysed set |
185
|
|
|
share_ashp: Float |
186
|
|
|
Share of air sourced heat pumps |
187
|
|
|
share_gshp: Float |
188
|
|
|
Share of ground sourced heat pumps |
189
|
|
|
quality_grade : float |
190
|
|
|
Ambient temperature |
191
|
|
|
|
192
|
|
|
Returns: 2 DataFrames |
193
|
|
|
DataFrames containing mean COP series for each German NUTS3 region |
194
|
|
|
------- |
195
|
|
|
""" |
196
|
|
|
|
197
|
|
|
fn_pattern = "mixed_cops_by_nuts3_{name}_{year}.csv".format(name=name, year=year) |
198
|
|
|
fn_pattern_water = "cops_by_nuts3_{name}_{year}_water.csv".format(name=name, year=year) |
199
|
|
|
fn = os.path.join(cfg.get("paths", "cop_precalc"), fn_pattern) |
200
|
|
|
fn_water = os.path.join(cfg.get("paths", "cop_precalc"), fn_pattern_water) |
201
|
|
|
|
202
|
|
|
if not os.path.isfile(fn): |
203
|
|
|
share_ashp = share_ashp |
204
|
|
|
share_gshp = share_gshp |
205
|
|
|
quality_grade = quality_grade |
206
|
|
|
t_ground = pd.Series(np.ones(8760)*10) |
207
|
|
|
|
208
|
|
|
outfile = os.path.join(cfg.get("paths", "cop_precalc"), "average_temp_by_nuts3_{year}.csv".format(year=year)) |
209
|
|
|
|
210
|
|
|
if not os.path.isfile(outfile): |
211
|
|
|
|
212
|
|
|
# Load NUTS3-geometries |
213
|
|
|
nuts3 = data.database_shapes() |
214
|
|
|
nuts3 = nuts3.to_crs(crs=4326) |
215
|
|
|
|
216
|
|
|
# Get average temperature per NUTS3, coastdat only available until 2014 |
217
|
|
|
coastdat.spatial_average_weather(year, nuts3, 'temp_air', 'deTemp', outfile=outfile) |
218
|
|
|
NUTS3_temp = pd.read_csv(outfile) |
219
|
|
|
NUTS3_temp.drop('Unnamed: 0', axis='columns', inplace=True) |
220
|
|
|
|
221
|
|
|
else: |
222
|
|
|
NUTS3_temp = pd.read_csv(outfile) |
223
|
|
|
NUTS3_temp.drop('Unnamed: 0', axis='columns', inplace=True) |
224
|
|
|
|
225
|
|
|
# Create empty DataFrames |
226
|
|
|
COP_NUTS3 = pd.DataFrame(index=pd.date_range('1/1/'+str(year), periods=8760, freq='H'), |
227
|
|
|
columns=NUTS3_temp.columns) |
228
|
|
|
COP_NUTS3_water = pd.DataFrame(index=pd.date_range('1/1/'+str(year), periods=8760, freq='H'), |
229
|
|
|
columns=NUTS3_temp.columns) |
230
|
|
|
|
231
|
|
|
# Loop through NUTS3-regions and calculate mixed COP for each region |
232
|
|
|
for r in COP_NUTS3.columns: |
233
|
|
|
tmp_cop = calculate_mixed_COPS_per_region(NUTS3_temp[r]-273.15, t_ground, quality_grade=quality_grade, |
234
|
|
|
share_ashp=share_ashp, share_gshp=share_gshp) |
235
|
|
|
tmp_cop.set_index(COP_NUTS3.index, inplace=True) |
236
|
|
|
COP_NUTS3[r] = tmp_cop['COP_mean'] |
237
|
|
|
COP_NUTS3_water[r] = tmp_cop['COPwater_air']* share_ashp + tmp_cop['COPwater_ground'] * share_gshp |
238
|
|
|
|
239
|
|
|
COP_NUTS3.to_csv(fn) |
240
|
|
|
COP_NUTS3_water.to_csv(fn_water) |
241
|
|
|
|
242
|
|
|
else: |
243
|
|
|
COP_NUTS3 = pd.read_csv(fn) |
244
|
|
|
COP_NUTS3_water = pd.read_csv(fn_water) |
245
|
|
|
|
246
|
|
|
return COP_NUTS3, COP_NUTS3_water |
247
|
|
|
|
248
|
|
|
|
249
|
|
|
def aggregate_COP_by_region(regions, COP): |
250
|
|
|
mean_COP = pd.DataFrame(columns=regions.index) |
251
|
|
|
#mean_COP_water = pd.DataFrame(columns=regions.index) |
252
|
|
|
|
253
|
|
|
nuts3_list = demand_disaggregator.get_nutslist_for_regions(regions) |
254
|
|
|
|
255
|
|
|
for zone in regions.index: |
256
|
|
|
idx = nuts3_list.loc[zone]["nuts"] |
257
|
|
|
mean_COP[zone] = COP[idx].mean(axis=1) |
258
|
|
|
#mean_COP_water[zone] = COP_water[idx].mean(axis=1) |
259
|
|
|
|
260
|
|
|
return mean_COP |
261
|
|
|
|
262
|
|
|
|
263
|
|
|
def get_hp_timeseries(heat_profile, cop, E_el): |
264
|
|
|
cop.index = heat_profile.index |
265
|
|
|
|
266
|
|
|
for Q_rated in range(0,10000000,1000): |
267
|
|
|
heat_hp = Q_rated * cop * heat_profile |
268
|
|
|
elc_hp = heat_hp / cop |
269
|
|
|
|
270
|
|
|
if elc_hp.sum() > E_el: |
271
|
|
|
break |
272
|
|
|
|
273
|
|
|
return elc_hp, heat_hp |
|
|
|
|
274
|
|
|
|