geoloop 0.0.1__py3-none-any.whl → 1.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geoloop/axisym/AxisymetricEL.py +751 -0
- geoloop/axisym/__init__.py +3 -0
- geoloop/bin/Flowdatamain.py +89 -0
- geoloop/bin/Lithologymain.py +84 -0
- geoloop/bin/Loadprofilemain.py +100 -0
- geoloop/bin/Plotmain.py +250 -0
- geoloop/bin/Runbatch.py +81 -0
- geoloop/bin/Runmain.py +86 -0
- geoloop/bin/SingleRunSim.py +928 -0
- geoloop/bin/__init__.py +3 -0
- geoloop/cli/__init__.py +0 -0
- geoloop/cli/batch.py +106 -0
- geoloop/cli/main.py +105 -0
- geoloop/configuration.py +946 -0
- geoloop/constants.py +112 -0
- geoloop/geoloopcore/CoaxialPipe.py +503 -0
- geoloop/geoloopcore/CustomPipe.py +727 -0
- geoloop/geoloopcore/__init__.py +3 -0
- geoloop/geoloopcore/b2g.py +739 -0
- geoloop/geoloopcore/b2g_ana.py +535 -0
- geoloop/geoloopcore/boreholedesign.py +683 -0
- geoloop/geoloopcore/getloaddata.py +112 -0
- geoloop/geoloopcore/pyg_ana.py +280 -0
- geoloop/geoloopcore/pygfield_ana.py +519 -0
- geoloop/geoloopcore/simulationparameters.py +130 -0
- geoloop/geoloopcore/soilproperties.py +152 -0
- geoloop/geoloopcore/strat_interpolator.py +194 -0
- geoloop/lithology/__init__.py +3 -0
- geoloop/lithology/plot_lithology.py +277 -0
- geoloop/lithology/process_lithology.py +697 -0
- geoloop/loadflowdata/__init__.py +3 -0
- geoloop/loadflowdata/flow_data.py +161 -0
- geoloop/loadflowdata/loadprofile.py +325 -0
- geoloop/plotting/__init__.py +3 -0
- geoloop/plotting/create_plots.py +1137 -0
- geoloop/plotting/load_data.py +432 -0
- geoloop/utils/RunManager.py +164 -0
- geoloop/utils/__init__.py +0 -0
- geoloop/utils/helpers.py +841 -0
- geoloop-1.0.0b1.dist-info/METADATA +112 -0
- geoloop-1.0.0b1.dist-info/RECORD +46 -0
- geoloop-1.0.0b1.dist-info/entry_points.txt +2 -0
- geoloop-0.0.1.dist-info/licenses/LICENSE → geoloop-1.0.0b1.dist-info/licenses/LICENSE.md +2 -1
- geoloop-0.0.1.dist-info/METADATA +0 -10
- geoloop-0.0.1.dist-info/RECORD +0 -6
- {geoloop-0.0.1.dist-info → geoloop-1.0.0b1.dist-info}/WHEEL +0 -0
- {geoloop-0.0.1.dist-info → geoloop-1.0.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pygfunction as gt
|
|
3
|
+
from scipy.constants import pi
|
|
4
|
+
|
|
5
|
+
from geoloop.geoloopcore.CustomPipe import CustomPipe
|
|
6
|
+
from geoloop.geoloopcore.simulationparameters import SimulationParameters
|
|
7
|
+
from geoloop.geoloopcore.soilproperties import SoilProperties
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class B2G_ana:
|
|
11
|
+
"""
|
|
12
|
+
Class for analytical / semi-analytical borehole-to-ground simulation.
|
|
13
|
+
|
|
14
|
+
This class uses pygfunction's g-function and a load aggregation scheme
|
|
15
|
+
(Claesson-Javed) to compute borehole wall temperatures and then obtains
|
|
16
|
+
fluid/pipe temperatures from `CustomPipe` methods.
|
|
17
|
+
|
|
18
|
+
Attributes
|
|
19
|
+
----------
|
|
20
|
+
custom_pipe : CustomPipe
|
|
21
|
+
Depth-dependent borehole configuration and properties.
|
|
22
|
+
soil_props : SoilProperties
|
|
23
|
+
Depth-dependent soil parameters (conductivity and temperature).
|
|
24
|
+
sim_params : SimulationParameters
|
|
25
|
+
Simulation parameters (time, flow, power, temperature).
|
|
26
|
+
|
|
27
|
+
Notes
|
|
28
|
+
-----
|
|
29
|
+
The simulation can be run for input power or input temperature and calls the appropriate function.
|
|
30
|
+
`runsimulation_power` is called for power simulations
|
|
31
|
+
`runsimulation_temperature` is called for temperature simulations
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
custom_pipe: CustomPipe,
|
|
37
|
+
soil_properties: SoilProperties,
|
|
38
|
+
simulation_parameters: SimulationParameters,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Initialize the analytical BHE model wrapper.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
custom_pipe : CustomPipe
|
|
46
|
+
Pipe/borehole configuration object.
|
|
47
|
+
soil_properties : SoilProperties
|
|
48
|
+
Soil properties provider.
|
|
49
|
+
simulation_parameters : SimulationParameters
|
|
50
|
+
Operational and simulation parameters.
|
|
51
|
+
"""
|
|
52
|
+
self.custom_pipe = custom_pipe
|
|
53
|
+
self.soil_props = soil_properties
|
|
54
|
+
self.sim_params = simulation_parameters
|
|
55
|
+
|
|
56
|
+
def runsimulation(self):
|
|
57
|
+
"""
|
|
58
|
+
Run the simulation of the borehole to ground heat exchanger.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
tuple
|
|
63
|
+
(hours, Q_b, flowrate, qsign, T_fi, T_fo, T_bave,
|
|
64
|
+
z, zseg, T_b, T_ftimes, qbzseg, h_fpipes)
|
|
65
|
+
"""
|
|
66
|
+
if self.sim_params.run_type == SimulationParameters.POWER:
|
|
67
|
+
return self.runsimulation_power()
|
|
68
|
+
else:
|
|
69
|
+
return self.runsimulation_temperature()
|
|
70
|
+
|
|
71
|
+
def runsimulation_temperature(self) -> tuple:
|
|
72
|
+
"""
|
|
73
|
+
Run the simulation of the borehole to ground heat exchanger for an input inlet temperature.
|
|
74
|
+
|
|
75
|
+
The method:
|
|
76
|
+
- computes the segment-wise g-function scaling,
|
|
77
|
+
- uses load-aggregation to obtain borehole wall temperature response,
|
|
78
|
+
- calls CustomPipe methods to compute fluid temperatures for each time step.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
tuple
|
|
83
|
+
(hours, Q_b, flowrate, qsign, T_fi, T_fo, T_bave,
|
|
84
|
+
z, zseg, T_b, T_ftimes, qbzseg, h_fpipes)
|
|
85
|
+
"""
|
|
86
|
+
sim_params = self.sim_params
|
|
87
|
+
custom_pipe = self.custom_pipe
|
|
88
|
+
soil_props = self.soil_props
|
|
89
|
+
nsegments = sim_params.nsegments
|
|
90
|
+
|
|
91
|
+
# temporal arrays and derived geometry
|
|
92
|
+
H = custom_pipe.b.H
|
|
93
|
+
D = custom_pipe.b.D
|
|
94
|
+
Nt = sim_params.Nt
|
|
95
|
+
time = sim_params.time
|
|
96
|
+
m_flow = sim_params.m_flow[0]
|
|
97
|
+
scaleflow = sim_params.m_flow / m_flow
|
|
98
|
+
|
|
99
|
+
dz = H / nsegments
|
|
100
|
+
zmin = D + 0.5 * dz
|
|
101
|
+
zmax = D + H - 0.5 * dz
|
|
102
|
+
zseg = np.linspace(zmin, zmax, nsegments)
|
|
103
|
+
zz = np.linspace(D, D + H, nsegments + 1)
|
|
104
|
+
|
|
105
|
+
k_s = soil_props.get_k_s(zz[0:-1], zz[1:], sim_params.isample)
|
|
106
|
+
z = np.linspace(D, D + H, nsegments + 1)
|
|
107
|
+
|
|
108
|
+
# Build load aggregation + g-function once
|
|
109
|
+
LoadAgg = []
|
|
110
|
+
for k in range(nsegments):
|
|
111
|
+
la = gt.load_aggregation.ClaessonJaved(sim_params.dt, sim_params.tmax)
|
|
112
|
+
LoadAgg.append(la)
|
|
113
|
+
# TODO: JD: the workaround below was not reuired anymore?
|
|
114
|
+
# The field contains only one borehole
|
|
115
|
+
# boreField = [gt.boreholes.Borehole(custom_pipe.b.H, custom_pipe.b.D, custom_pipe.b.r_b, x=0., y=0.)]
|
|
116
|
+
# The field contains only one borehole, but needs one extra at very large distance to be correct, ie. gfunc plateaus at 6.7)
|
|
117
|
+
boreField = [
|
|
118
|
+
gt.boreholes.Borehole(
|
|
119
|
+
custom_pipe.b.H,
|
|
120
|
+
custom_pipe.b.D,
|
|
121
|
+
custom_pipe.b.r_b,
|
|
122
|
+
x=0.0,
|
|
123
|
+
y=0.0,
|
|
124
|
+
tilt=np.radians(0.1),
|
|
125
|
+
),
|
|
126
|
+
gt.boreholes.Borehole(
|
|
127
|
+
custom_pipe.b.H,
|
|
128
|
+
custom_pipe.b.D,
|
|
129
|
+
custom_pipe.b.r_b,
|
|
130
|
+
x=1000.0,
|
|
131
|
+
y=0.0,
|
|
132
|
+
tilt=np.radians(0.1),
|
|
133
|
+
),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
# Get time values needed for g-function evaluation
|
|
137
|
+
time_req = LoadAgg[0].get_times_for_simulation()
|
|
138
|
+
|
|
139
|
+
# Calculate g-function
|
|
140
|
+
# g-Function calculation options
|
|
141
|
+
options = {"nSegments": 8, "disp": False}
|
|
142
|
+
np.seterr(under="ignore")
|
|
143
|
+
|
|
144
|
+
alpha = soil_props.alfa
|
|
145
|
+
gFunc = gt.gfunction.gFunction(
|
|
146
|
+
boreField, alpha, time=time_req, options=options, method="similarities"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Initialize load aggregation scheme
|
|
150
|
+
for k in range(nsegments):
|
|
151
|
+
LoadAgg[k].initialize(gFunc.gFunc / (2 * pi * k_s[k]))
|
|
152
|
+
|
|
153
|
+
# Protect agains zero loads
|
|
154
|
+
Qabsmin = H * 0.1 # assume at least 0.1 W /m to avoid division by zero
|
|
155
|
+
|
|
156
|
+
# Compute deltaT_b by temporal superposition for each segment/time
|
|
157
|
+
deltaT_b = np.zeros(Nt)
|
|
158
|
+
deltaT_bk = np.zeros((Nt, nsegments))
|
|
159
|
+
power = np.zeros(Nt)
|
|
160
|
+
Q_b = sim_params.Q
|
|
161
|
+
|
|
162
|
+
# First pass: compute deltaT_b using aggregated loads
|
|
163
|
+
for i, (t, Q_b_i) in enumerate(zip(time, Q_b)):
|
|
164
|
+
# avoid the Q_B_i to be zero, it is at least the 10% of H in watts
|
|
165
|
+
if abs(Q_b_i) < Qabsmin:
|
|
166
|
+
Q_b_i = Qabsmin * np.sign(Q_b_i)
|
|
167
|
+
|
|
168
|
+
# Increment time step by (1)
|
|
169
|
+
for k in range(nsegments):
|
|
170
|
+
LoadAgg[k].next_time_step(t)
|
|
171
|
+
# Apply current load
|
|
172
|
+
LoadAgg[k].set_current_load(Q_b_i / H)
|
|
173
|
+
|
|
174
|
+
# Evaluate the average borehole wall temeprature
|
|
175
|
+
deltaT_b[i] = 0
|
|
176
|
+
for k in range(nsegments):
|
|
177
|
+
deltaT_bk[i, k] = LoadAgg[k].temporal_superposition()
|
|
178
|
+
|
|
179
|
+
deltaT_b[i] = np.average(deltaT_bk[i, :])
|
|
180
|
+
power[i] = Q_b_i
|
|
181
|
+
|
|
182
|
+
# Initialize output containers
|
|
183
|
+
T_b_top = np.zeros(Nt)
|
|
184
|
+
T_bave = np.zeros(Nt)
|
|
185
|
+
T_fi = np.zeros(Nt)
|
|
186
|
+
T_fo = np.zeros(Nt)
|
|
187
|
+
|
|
188
|
+
Q_b = np.zeros(Nt)
|
|
189
|
+
deltaT_b_ref = np.zeros(Nt)
|
|
190
|
+
flowrate = np.zeros(Nt)
|
|
191
|
+
Rs = np.zeros(Nt)
|
|
192
|
+
qsign = np.zeros(Nt)
|
|
193
|
+
|
|
194
|
+
# Initial guess for Rs (borehole thermal resistance) per time step
|
|
195
|
+
imax = -1
|
|
196
|
+
for i, t in enumerate(time):
|
|
197
|
+
if (i > imax) and (i < Nt):
|
|
198
|
+
p = power[i]
|
|
199
|
+
Rs[i] = deltaT_b[i] / (p / H)
|
|
200
|
+
|
|
201
|
+
minR = 0.01
|
|
202
|
+
Rs = np.where(np.logical_and(Rs < minR, Rs > -0.5), minR, Rs)
|
|
203
|
+
|
|
204
|
+
# iterate to refine Rsz using qbzseg from pipe model (few iterations)
|
|
205
|
+
iter = 0
|
|
206
|
+
niter = 3
|
|
207
|
+
Rsz = np.zeros((len(Rs), nsegments))
|
|
208
|
+
qbzseg = np.zeros((len(Rs), nsegments))
|
|
209
|
+
T_b = np.zeros((len(Rs), nsegments))
|
|
210
|
+
h_fpipes = np.zeros((len(Rs), custom_pipe.nPipes))
|
|
211
|
+
T_ftimes = np.zeros((len(Rs), nsegments + 1, custom_pipe.nPipes))
|
|
212
|
+
|
|
213
|
+
while iter < niter:
|
|
214
|
+
if iter == 0:
|
|
215
|
+
for i, rsi in enumerate(Rs):
|
|
216
|
+
Rsz[i] = rsi * np.ones(nsegments)
|
|
217
|
+
|
|
218
|
+
else:
|
|
219
|
+
# Recompute g-function/loadaggs (keeps same options)
|
|
220
|
+
alpha = soil_props.alfa
|
|
221
|
+
gFunc = gt.gfunction.gFunction(
|
|
222
|
+
boreField,
|
|
223
|
+
alpha,
|
|
224
|
+
time=time_req,
|
|
225
|
+
options=options,
|
|
226
|
+
method="similarities",
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Reset load aggregation
|
|
230
|
+
for k in range(nsegments):
|
|
231
|
+
LoadAgg[k] = gt.load_aggregation.ClaessonJaved(
|
|
232
|
+
sim_params.dt, sim_params.tmax
|
|
233
|
+
)
|
|
234
|
+
# Initialize load aggregation scheme
|
|
235
|
+
LoadAgg[k].initialize(gFunc.gFunc / (2 * pi * k_s[k]))
|
|
236
|
+
|
|
237
|
+
Rsz = np.zeros((len(Rs), nsegments))
|
|
238
|
+
for k in range(nsegments):
|
|
239
|
+
# this only works if time is same as reduced time array
|
|
240
|
+
for i, (t, Q_b_i) in enumerate(zip(time, qbzseg[:, k])):
|
|
241
|
+
# Increment time step by (1)
|
|
242
|
+
LoadAgg[k].next_time_step(t)
|
|
243
|
+
|
|
244
|
+
if abs(Q_b_i * H) < Qabsmin:
|
|
245
|
+
Q_b_i = Qabsmin * np.sign(Q_b_i) / H
|
|
246
|
+
|
|
247
|
+
# Apply current load
|
|
248
|
+
LoadAgg[k].set_current_load(Q_b_i)
|
|
249
|
+
|
|
250
|
+
# Evaluate borehole wall temeprature
|
|
251
|
+
deltaT_bk[i][k] = LoadAgg[k].temporal_superposition()
|
|
252
|
+
Rsz[i, k] = deltaT_bk[i][k] / qbzseg[i, k]
|
|
253
|
+
|
|
254
|
+
# For each time-step compute fluid solution using CustomPipe
|
|
255
|
+
iter += 1
|
|
256
|
+
imax = -1
|
|
257
|
+
for i, t in enumerate(time):
|
|
258
|
+
if (i > imax) and (i < Nt):
|
|
259
|
+
p = power[i]
|
|
260
|
+
custom_pipe.update_scaleflow(scaleflow[i])
|
|
261
|
+
h_f = custom_pipe.h_f
|
|
262
|
+
signpower = np.sign(p)
|
|
263
|
+
T_f_in = sim_params.Tin[i]
|
|
264
|
+
|
|
265
|
+
T_f_out, p, Reff, T_f, Tb, qbz = (
|
|
266
|
+
custom_pipe.get_temperature_depthvar(
|
|
267
|
+
T_f_in,
|
|
268
|
+
signpower,
|
|
269
|
+
Rsz[i],
|
|
270
|
+
soil_props=soil_props,
|
|
271
|
+
nsegments=nsegments,
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
Q_b[i] = p
|
|
276
|
+
T_b_top[i] = Tb[0]
|
|
277
|
+
deltaT_b_ref[i] = deltaT_b[i]
|
|
278
|
+
T_bave[i] = np.average(Tb)
|
|
279
|
+
T_fi[i] = T_f_in
|
|
280
|
+
h_fpipes[i] = h_f
|
|
281
|
+
T_fo[i] = T_f_out
|
|
282
|
+
flowrate[i] = scaleflow[i] * m_flow
|
|
283
|
+
T_ftimes[i] = T_f # stored index time, depth, pipe
|
|
284
|
+
T_b[i] = Tb
|
|
285
|
+
qbzseg[i] = qbz
|
|
286
|
+
qsign[i] = np.sign(max(qbz) * min(qbz))
|
|
287
|
+
imax = i
|
|
288
|
+
|
|
289
|
+
hours = time / 3600.0
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
hours,
|
|
293
|
+
Q_b,
|
|
294
|
+
flowrate,
|
|
295
|
+
qsign,
|
|
296
|
+
T_fi,
|
|
297
|
+
T_fo,
|
|
298
|
+
T_bave,
|
|
299
|
+
z,
|
|
300
|
+
zseg,
|
|
301
|
+
T_b,
|
|
302
|
+
T_ftimes,
|
|
303
|
+
qbzseg,
|
|
304
|
+
h_fpipes,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def runsimulation_power(self) -> tuple:
|
|
308
|
+
"""
|
|
309
|
+
Run the simulation of the borehole to ground heat exchanger for an input heat demand.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
tuple
|
|
314
|
+
(hours, Q_b, flowrate, qsign, T_fi, T_fo, T_bave,
|
|
315
|
+
z, zseg, T_b, T_ftimes, qbzseg, h_fpipes)
|
|
316
|
+
"""
|
|
317
|
+
sim_params = self.sim_params
|
|
318
|
+
custom_pipe = self.custom_pipe
|
|
319
|
+
soil_props = self.soil_props
|
|
320
|
+
nsegments = sim_params.nsegments
|
|
321
|
+
|
|
322
|
+
# geomertry and derived arrays
|
|
323
|
+
H = custom_pipe.b.H
|
|
324
|
+
D = custom_pipe.b.D
|
|
325
|
+
Nt = sim_params.Nt
|
|
326
|
+
time = sim_params.time
|
|
327
|
+
m_flow = sim_params.m_flow[0]
|
|
328
|
+
scaleflow = sim_params.m_flow / m_flow
|
|
329
|
+
|
|
330
|
+
dz = H / nsegments
|
|
331
|
+
zmin = D + 0.5 * dz
|
|
332
|
+
zmax = D + H - 0.5 * dz
|
|
333
|
+
zseg = np.linspace(zmin, zmax, nsegments)
|
|
334
|
+
zz = np.linspace(D, D + H, nsegments + 1)
|
|
335
|
+
|
|
336
|
+
k_s = soil_props.get_k_s(zz[0:-1], zz[1:], sim_params.isample)
|
|
337
|
+
z = np.linspace(D, D + H, nsegments + 1)
|
|
338
|
+
|
|
339
|
+
# prepare load aggregation + gfunc
|
|
340
|
+
# The field contains only one borehole
|
|
341
|
+
old = False
|
|
342
|
+
if old:
|
|
343
|
+
boreField = [
|
|
344
|
+
gt.boreholes.Borehole(
|
|
345
|
+
custom_pipe.b.H, custom_pipe.b.D, custom_pipe.b.r_b, x=0.0, y=0.0
|
|
346
|
+
)
|
|
347
|
+
]
|
|
348
|
+
# The field contains only one borehole, but needs one extra at very large distance to be correct, ie. gfunc plateaus at 6.7)
|
|
349
|
+
else:
|
|
350
|
+
boreField = [
|
|
351
|
+
gt.boreholes.Borehole(
|
|
352
|
+
custom_pipe.b.H,
|
|
353
|
+
custom_pipe.b.D,
|
|
354
|
+
custom_pipe.b.r_b,
|
|
355
|
+
x=0.0,
|
|
356
|
+
y=0.0,
|
|
357
|
+
tilt=np.radians(0.1),
|
|
358
|
+
),
|
|
359
|
+
gt.boreholes.Borehole(
|
|
360
|
+
custom_pipe.b.H,
|
|
361
|
+
custom_pipe.b.D,
|
|
362
|
+
custom_pipe.b.r_b,
|
|
363
|
+
x=1000.0,
|
|
364
|
+
y=0.0,
|
|
365
|
+
tilt=np.radians(0.1),
|
|
366
|
+
),
|
|
367
|
+
]
|
|
368
|
+
|
|
369
|
+
LoadAgg = []
|
|
370
|
+
for k in range(nsegments):
|
|
371
|
+
la = gt.load_aggregation.ClaessonJaved(sim_params.dt, sim_params.tmax)
|
|
372
|
+
LoadAgg.append(la)
|
|
373
|
+
|
|
374
|
+
# Get time values needed for g-function evaluation
|
|
375
|
+
time_req = LoadAgg[0].get_times_for_simulation()
|
|
376
|
+
|
|
377
|
+
# Calculate g-function
|
|
378
|
+
# g-Function calculation options
|
|
379
|
+
options = {"nSegments": 8, "disp": False}
|
|
380
|
+
np.seterr(under="ignore")
|
|
381
|
+
|
|
382
|
+
alpha = soil_props.alfa
|
|
383
|
+
gFunc = gt.gfunction.gFunction(
|
|
384
|
+
boreField, alpha, time=time_req, options=options, method="similarities"
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# initialize load aggregation scheme
|
|
388
|
+
for k in range(nsegments):
|
|
389
|
+
LoadAgg[k].initialize(gFunc.gFunc / (2 * pi * k_s[k]))
|
|
390
|
+
|
|
391
|
+
Qabsmin = H * 0.1 # assume at least 0.1 W /m to avoid division by zero
|
|
392
|
+
|
|
393
|
+
deltaT_b = np.zeros(Nt)
|
|
394
|
+
deltaT_bk = np.zeros((Nt, nsegments))
|
|
395
|
+
power = np.zeros(Nt)
|
|
396
|
+
Q_b = sim_params.Q
|
|
397
|
+
|
|
398
|
+
for i, (t, Q_b_i) in enumerate(zip(time, Q_b)):
|
|
399
|
+
# avoid the Q_B_i to be zero, it is at least the 10% of H in watts
|
|
400
|
+
if abs(Q_b_i) < Qabsmin:
|
|
401
|
+
Q_b_i = Qabsmin * np.sign(Q_b_i)
|
|
402
|
+
|
|
403
|
+
# Increment time step by (1)
|
|
404
|
+
for k in range(nsegments):
|
|
405
|
+
LoadAgg[k].next_time_step(t)
|
|
406
|
+
# Apply current load
|
|
407
|
+
LoadAgg[k].set_current_load(Q_b_i / H)
|
|
408
|
+
|
|
409
|
+
# Evaluate the average borehole wall temeprature
|
|
410
|
+
deltaT_b[i] = 0
|
|
411
|
+
for k in range(nsegments):
|
|
412
|
+
deltaT_bk[i, k] = LoadAgg[k].temporal_superposition()
|
|
413
|
+
|
|
414
|
+
deltaT_b[i] = np.average(deltaT_bk[i, :])
|
|
415
|
+
power[i] = Q_b_i
|
|
416
|
+
|
|
417
|
+
T_b_top = np.zeros(Nt)
|
|
418
|
+
T_bave = np.zeros(Nt)
|
|
419
|
+
T_fi = np.zeros(Nt)
|
|
420
|
+
T_fo = np.zeros(Nt)
|
|
421
|
+
|
|
422
|
+
Q_b = np.zeros(Nt)
|
|
423
|
+
deltaT_b_ref = np.zeros(Nt)
|
|
424
|
+
flowrate = np.zeros(Nt)
|
|
425
|
+
Rs = np.zeros(Nt)
|
|
426
|
+
qsign = np.zeros(Nt)
|
|
427
|
+
|
|
428
|
+
# compute initial Rs
|
|
429
|
+
imax = -1
|
|
430
|
+
for i, t in enumerate(time):
|
|
431
|
+
if (i > imax) and (i < Nt):
|
|
432
|
+
power_i = power[i]
|
|
433
|
+
p = np.maximum(power_i, 100)
|
|
434
|
+
Rs[i] = deltaT_b[i] / (p / H)
|
|
435
|
+
|
|
436
|
+
minR = 0.01
|
|
437
|
+
Rs = np.where(np.logical_and(Rs < minR, Rs > -0.5), minR, Rs)
|
|
438
|
+
|
|
439
|
+
# iterative refinement
|
|
440
|
+
iter = 0
|
|
441
|
+
niter = 3
|
|
442
|
+
Rsz = np.zeros((len(Rs), nsegments))
|
|
443
|
+
qbzseg = np.zeros((len(Rs), nsegments))
|
|
444
|
+
T_b = np.zeros((len(Rs), nsegments))
|
|
445
|
+
h_fpipes = np.zeros((len(Rs), custom_pipe.nPipes))
|
|
446
|
+
T_ftimes = np.zeros((len(Rs), nsegments + 1, custom_pipe.nPipes))
|
|
447
|
+
|
|
448
|
+
while iter < niter:
|
|
449
|
+
if iter == 0:
|
|
450
|
+
for i, rsi in enumerate(Rs):
|
|
451
|
+
Rsz[i] = rsi * np.ones(nsegments)
|
|
452
|
+
|
|
453
|
+
else:
|
|
454
|
+
# reinitilize g-function
|
|
455
|
+
alpha = soil_props.alfa
|
|
456
|
+
gFunc = gt.gfunction.gFunction(
|
|
457
|
+
boreField,
|
|
458
|
+
alpha,
|
|
459
|
+
time=time_req,
|
|
460
|
+
options=options,
|
|
461
|
+
method="similarities",
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
# Reinitialize load aggregation scheme
|
|
465
|
+
for k in range(nsegments):
|
|
466
|
+
LoadAgg[k] = gt.load_aggregation.ClaessonJaved(
|
|
467
|
+
sim_params.dt, sim_params.tmax
|
|
468
|
+
)
|
|
469
|
+
LoadAgg[k].initialize(gFunc.gFunc / (2 * pi * k_s[k]))
|
|
470
|
+
|
|
471
|
+
Rsz = np.zeros((len(Rs), nsegments))
|
|
472
|
+
for k in range(nsegments):
|
|
473
|
+
# this only works if time is same as reduced time array
|
|
474
|
+
for i, (t, Q_b_i) in enumerate(zip(time, qbzseg[:, k])):
|
|
475
|
+
# Increment time step by (1)
|
|
476
|
+
LoadAgg[k].next_time_step(t)
|
|
477
|
+
|
|
478
|
+
if abs(Q_b_i * H) < Qabsmin:
|
|
479
|
+
Q_b_i = Qabsmin * np.sign(Q_b_i) / H
|
|
480
|
+
|
|
481
|
+
# Apply current load
|
|
482
|
+
LoadAgg[k].set_current_load(Q_b_i)
|
|
483
|
+
|
|
484
|
+
# Evaluate borehole wall temeprature
|
|
485
|
+
deltaT_bk[i][k] = LoadAgg[k].temporal_superposition()
|
|
486
|
+
Rsz[i, k] = deltaT_bk[i][k] / qbzseg[i, k]
|
|
487
|
+
iter += 1
|
|
488
|
+
imax = -1
|
|
489
|
+
|
|
490
|
+
# compute fluid temperatures for this iteration
|
|
491
|
+
for i, t in enumerate(time):
|
|
492
|
+
if (i > imax) and (i < Nt):
|
|
493
|
+
# print(il)
|
|
494
|
+
power_i = power[i]
|
|
495
|
+
custom_pipe.update_scaleflow(scaleflow[i])
|
|
496
|
+
h_f = custom_pipe.h_f
|
|
497
|
+
|
|
498
|
+
(T_f_out, T_f_in, Reff, T_f, Tb, qbz) = (
|
|
499
|
+
custom_pipe.get_temperature_depthvar_power(
|
|
500
|
+
power_i, Rsz[i], soil_props, nsegments=nsegments
|
|
501
|
+
)
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
Q_b[i] = power[i]
|
|
505
|
+
T_b_top[i] = Tb[0]
|
|
506
|
+
deltaT_b_ref[i] = deltaT_b[i]
|
|
507
|
+
T_bave[i] = np.average(Tb)
|
|
508
|
+
T_fi[i] = T_f_in
|
|
509
|
+
h_fpipes[i] = h_f
|
|
510
|
+
T_fo[i] = T_f_out
|
|
511
|
+
flowrate[i] = scaleflow[i] * m_flow
|
|
512
|
+
T_ftimes[i] = T_f # stored index time, depth, pipe
|
|
513
|
+
T_b[i] = Tb
|
|
514
|
+
# qbz is calculated at the depth division in segments (zseg)
|
|
515
|
+
qbzseg[i] = qbz
|
|
516
|
+
qsign[i] = np.sign(max(qbz) * min(qbz))
|
|
517
|
+
imax = i
|
|
518
|
+
|
|
519
|
+
hours = time / 3600.0
|
|
520
|
+
|
|
521
|
+
return (
|
|
522
|
+
hours,
|
|
523
|
+
Q_b,
|
|
524
|
+
flowrate,
|
|
525
|
+
qsign,
|
|
526
|
+
T_fi,
|
|
527
|
+
T_fo,
|
|
528
|
+
T_bave,
|
|
529
|
+
z,
|
|
530
|
+
zseg,
|
|
531
|
+
T_b,
|
|
532
|
+
T_ftimes,
|
|
533
|
+
qbzseg,
|
|
534
|
+
h_fpipes,
|
|
535
|
+
)
|