-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathgfunction.py
769 lines (684 loc) · 28.4 KB
/
gfunction.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
from __future__ import absolute_import, division, print_function
import time as tim
import numpy as np
from scipy.constants import pi
from scipy.interpolate import interp1d as interp1d
from .boreholes import Borehole
from .heat_transfer import thermal_response_factors
from .networks import network_thermal_resistance
def uniform_heat_extraction(boreholes, time, alpha, use_similarities=True,
disTol=0.01, tol=1.0e-6, processes=None,
disp=False):
"""
Evaluate the g-function with uniform heat extraction along boreholes.
This function superimposes the finite line source (FLS) solution to
estimate the g-function of a geothermal bore field.
Parameters
----------
boreholes : list of Borehole objects
List of boreholes included in the bore field.
time : float or array
Values of time (in seconds) for which the g-function is evaluated.
alpha : float
Soil thermal diffusivity (in m2/s).
use_similarities : bool, optional
True if similarities are used to limit the number of FLS evaluations.
Default is True.
disTol : float, optional
Relative tolerance on radial distance. Two distances
(d1, d2) between two pairs of boreholes are considered equal if the
difference between the two distances (abs(d1-d2)) is below tolerance.
Default is 0.01.
tol : float, optional
Relative tolerance on length and depth. Two lengths H1, H2
(or depths D1, D2) are considered equal if abs(H1 - H2)/H2 < tol.
Default is 1.0e-6.
processes : int, optional
Number of processors to use in calculations. If the value is set to
None, a number of processors equal to cpu_count() is used.
Default is None.
disp : bool, optional
Set to true to print progression messages.
Default is False.
Returns
-------
gFunction : float or array
Values of the g-function
Examples
--------
>>> b1 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=0., y=0.)
>>> b2 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=5., y=0.)
>>> alpha = 1.0e-6
>>> time = np.array([1.0*10**i for i in range(4, 12)])
>>> gt.gfunction.uniform_heat_extraction([b1, b2], time, alpha)
array([ 0.75978163, 1.84860837, 2.98861057, 4.33496051, 6.29199383,
8.13636888, 9.08401497, 9.20736188])
"""
if disp:
print(60*'-')
print('Calculating g-function for uniform heat extraction rate')
print(60*'-')
# Initialize chrono
tic = tim.time()
# Number of boreholes
nBoreholes = len(boreholes)
# Number of time values
nt = len(np.atleast_1d(time))
# Initialize heat extraction rates
Q = np.ones(nBoreholes)
# Initialize g-function
gFunction = np.zeros(nt)
# Borehole lengths
H = np.array([b.H for b in boreholes])
# Calculate borehole to borehole thermal response factors
h_ij = thermal_response_factors(
boreholes, time, alpha, use_similarities=use_similarities,
splitRealAndImage=False, disTol=disTol, tol=tol, processes=processes,
disp=disp)
toc1 = tim.time()
# Evaluate g-function at all times
if disp:
print('Building and solving system of equations ...')
for i in range(nt):
Tb = h_ij[:,:,i].dot(Q)
# The g-function is the average of all borehole wall temperatures
gFunction[i] = np.dot(Tb, H) / sum(H)
toc2 = tim.time()
if disp:
print('{} sec'.format(toc2 - toc1))
print('Total time for g-function evaluation: {} sec'.format(
toc2 - tic))
print(60*'-')
# Return float if time is a scalar
if np.isscalar(time):
gFunction = np.asscalar(gFunction)
return gFunction
def uniform_temperature(boreholes, time, alpha, nSegments=12, method='linear',
use_similarities=True, disTol=0.01, tol=1.0e-6,
processes=None, disp=False):
"""
Evaluate the g-function with uniform borehole wall temperature.
This function superimposes the finite line source (FLS) solution to
estimate the g-function of a geothermal bore field. Each borehole is
modeled as a series of finite line source segments, as proposed in
[#CimminoBernier2014]_.
Parameters
----------
boreholes : list of Borehole objects
List of boreholes included in the bore field.
time : float or array
Values of time (in seconds) for which the g-function is evaluated.
alpha : float
Soil thermal diffusivity (in m2/s).
nSegments : int, optional
Number of line segments used per borehole.
Default is 12.
method : string, optional
Interpolation method used for segment-to-segment thermal response
factors. See documentation for scipy.interpolate.interp1d.
Default is linear.
use_similarities : bool, optional
True if similarities are used to limit the number of FLS evaluations.
Default is True.
disTol : float, optional
Relative tolerance on radial distance. Two distances
(d1, d2) between two pairs of boreholes are considered equal if the
difference between the two distances (abs(d1-d2)) is below tolerance.
Default is 0.01.
tol : float, optional
Relative tolerance on length and depth. Two lengths H1, H2
(or depths D1, D2) are considered equal if abs(H1 - H2)/H2 < tol.
Default is 1.0e-6.
processes : int, optional
Number of processors to use in calculations. If the value is set to
None, a number of processors equal to cpu_count() is used.
Default is None.
disp : bool, optional
Set to true to print progression messages.
Default is False.
Returns
-------
gFunction : float or array
Values of the g-function
Examples
--------
>>> b1 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=0., y=0.)
>>> b2 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=5., y=0.)
>>> alpha = 1.0e-6
>>> time = np.array([1.0*10**i for i in range(4, 12)])
>>> gt.gfunction.uniform_temperature([b1, b2], time, alpha)
array([ 0.75978079, 1.84859851, 2.98852756, 4.33406497, 6.27830732,
8.05746656, 8.93697282, 9.04925079])
References
----------
.. [#CimminoBernier2014] Cimmino, M., & Bernier, M. (2014). A
semi-analytical method to generate g-functions for geothermal bore
fields. International Journal of Heat and Mass Transfer, 70, 641-650.
"""
if disp:
print(60*'-')
print('Calculating g-function for uniform borehole wall temperature')
print(60*'-')
# Initialize chrono
tic = tim.time()
# Number of boreholes
nBoreholes = len(boreholes)
# Total number of line sources
nSources = nSegments*nBoreholes
# Number of time values
nt = len(np.atleast_1d(time))
# Initialize g-function
gFunction = np.zeros(nt)
# Initialize segment heat extraction rates
Q = np.zeros((nSources, nt))
# Split boreholes into segments
boreSegments = _borehole_segments(boreholes, nSegments)
# Vector of time values
t = np.atleast_1d(time).flatten()
# Calculate segment to segment thermal response factors
h_ij = thermal_response_factors(
boreSegments, t, alpha, use_similarities=use_similarities,
splitRealAndImage=True, disTol=disTol, tol=tol, processes=processes,
disp=disp)
toc1 = tim.time()
if disp:
print('Building and solving system of equations ...')
# -------------------------------------------------------------------------
# Build a system of equation [A]*[X] = [B] for the evaluation of the
# g-function. [A] is a coefficient matrix, [X] = [Qb,Tb] is a state
# space vector of the borehole heat extraction rates and borehole wall
# temperature (equal for all segments), [B] is a coefficient vector.
# -------------------------------------------------------------------------
# Segment lengths
Hb = np.array([b.H for b in boreSegments])
# Vector of time steps
dt = np.hstack((t[0], t[1:] - t[:-1]))
if not np.isscalar(time) and len(time) > 1:
# Spline object for thermal response factors
h_dt = interp1d(np.hstack((0., t)),
np.dstack((np.zeros((nSources,nSources)), h_ij)),
kind=method, axis=2)
# Thermal response factors evaluated at t=dt
h_dt = h_dt(dt)
else:
h_dt = h_ij
# Thermal response factor increments
dh_ij = np.concatenate((h_ij[:,:,0:1], h_ij[:,:,1:]-h_ij[:,:,:-1]), axis=2)
# Energy conservation: sum([Qb*Hb]) = sum([Hb])
A_eq2 = np.hstack((Hb, 0.))
B_eq2 = np.atleast_1d(np.sum(Hb))
# Build and solve the system of equations at all times
for p in range(nt):
# Current thermal response factor matrix
h_ij_dt = h_dt[:,:,p]
# Reconstructed load history
Q_reconstructed = load_history_reconstruction(t[0:p+1], Q[:,0:p+1])
# Borehole wall temperature for zero heat extraction at current step
Tb_0 = _temporal_superposition(dh_ij, Q_reconstructed)
# Spatial superposition: [Tb] = [Tb0] + [h_ij_dt]*[Qb]
A_eq1 = np.hstack((h_ij_dt, -np.ones((nSources, 1))))
B_eq1 = -Tb_0
# Assemble equations
A = np.vstack((A_eq1, A_eq2))
B = np.hstack((B_eq1, B_eq2))
# Solve the system of equations
X = np.linalg.solve(A, B)
# Store calculated heat extraction rates
Q[:,p] = X[0:nSources]
# The borehole wall temperatures are equal for all segments
Tb = X[-1]
gFunction[p] = Tb
toc2 = tim.time()
if disp:
print('{} sec'.format(toc2 - toc1))
print('Total time for g-function evaluation: {} sec'.format(
toc2 - tic))
print(60*'-')
# Return float if time is a scalar
if np.isscalar(time):
gFunction = np.asscalar(gFunction)
return gFunction
def equal_inlet_temperature(boreholes, UTubes, m_flow, cp, time, alpha,
method='linear', nSegments=12,
use_similarities=True, disTol=0.01, tol=1.0e-6,
processes=None, disp=False):
"""
Evaluate the g-function with equal inlet fluid temperatures.
This function superimposes the finite line source (FLS) solution to
estimate the g-function of a geothermal bore field. Each borehole is
modeled as a series of finite line source segments, as proposed in
[#Cimmino2015]_.
Parameters
----------
boreholes : list of Borehole objects
List of boreholes included in the bore field.
UTubes : list of pipe objects
Model for pipes inside each borehole.
m_flow : float or array
Fluid mass flow rate per borehole (in kg/s).
cp : float
Fluid specific isobaric heat capacity (in J/kg.K).
time : float or array
Values of time (in seconds) for which the g-function is evaluated.
alpha : float
Soil thermal diffusivity (in m2/s).
nSegments : int, optional
Number of line segments used per borehole.
Default is 12.
method : string, optional
Interpolation method used for segment-to-segment thermal response
factors. See documentation for scipy.interpolate.interp1d.
Default is 'linear'.
use_similarities : bool, optional
True if similarities are used to limit the number of FLS evaluations.
Default is True.
disTol : float, optional
Relative tolerance on radial distance. Two distances
(d1, d2) between two pairs of boreholes are considered equal if the
difference between the two distances (abs(d1-d2)) is below tolerance.
Default is 0.01.
tol : float, optional
Relative tolerance on length and depth. Two lengths H1, H2
(or depths D1, D2) are considered equal if abs(H1 - H2)/H2 < tol.
Default is 1.0e-6.
processes : int, optional
Number of processors to use in calculations. If the value is set to
None, a number of processors equal to cpu_count() is used.
Default is None.
disp : bool, optional
Set to true to print progression messages.
Default is False.
Returns
-------
gFunction : float or array
Values of the g-function
Examples
--------
>>> b1 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=0., y=0.)
>>> b2 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=5., y=0.)
>>> alpha = 1.0e-6
>>> time = np.array([1.0*10**i for i in range(4, 12)])
>>> gt.gfunction.uniform_temperature([b1, b2], time, alpha)
array([ 0.75978079, 1.84859851, 2.98852756, 4.33406497, 6.27830732,
8.05746656, 8.93697282, 9.04925079])
References
----------
.. [#Cimmino2015] Cimmino, M. (2015). The effects of borehole thermal
resistances and fluid flow rate on the g-functions of geothermal bore
fields. International Journal of Heat and Mass Transfer, 91, 1119-1127.
"""
if disp:
print(60*'-')
print('Calculating g-function for equal inlet fluid temperature')
print(60*'-')
# Initialize chrono
tic = tim.time()
# Number of boreholes
nBoreholes = len(boreholes)
# Total number of line sources
nSources = nSegments*nBoreholes
# Number of time values
nt = len(np.atleast_1d(time))
# Initialize g-function
gFunction = np.zeros(nt)
# Initialize segment heat extraction rates
Q = np.zeros((nSources, nt))
# If m_flow is supplied as float, apply m_flow to all boreholes
if np.isscalar(m_flow):
m_flow = np.tile(m_flow, nBoreholes)
# Split boreholes into segments
boreSegments = _borehole_segments(boreholes, nSegments)
# Vector of time values
t = np.atleast_1d(time).flatten()
# Calculate segment to segment thermal response factors
h_ij = thermal_response_factors(
boreSegments, t, alpha, use_similarities=use_similarities,
splitRealAndImage=True, disTol=disTol, tol=tol, processes=processes,
disp=disp)
toc1 = tim.time()
if disp:
print('Building and solving system of equations ...')
# -------------------------------------------------------------------------
# Build a system of equation [A]*[X] = [B] for the evaluation of the
# g-function. [A] is a coefficient matrix, [X] = [Qb,Tb,Tf_in] is a state
# space vector of the borehole heat extraction rates, borehole wall
# temperatures and inlet fluid temperature (equal for all boreholes),
# [B] is a coefficient vector.
# -------------------------------------------------------------------------
# Segment lengths
Hb = np.array([b.H for b in boreSegments])
# Vector of time steps
dt = np.hstack((t[0], t[1:] - t[:-1]))
if not np.isscalar(time) and len(time) > 1:
# Spline object for thermal response factors
h_dt = interp1d(np.hstack((0., t)),
np.dstack((np.zeros((nSources,nSources)), h_ij)),
kind=method, axis=2)
# Thermal response factors evaluated at t=dt
h_dt = h_dt(dt)
else:
h_dt = h_ij
# Thermal response factor increments
dh_ij = np.concatenate((h_ij[:,:,0:1], h_ij[:,:,1:]-h_ij[:,:,:-1]), axis=2)
# Energy balance on borehole segments:
# [Q_{b,i}] = [a_in]*[T_{f,in}] + [a_{b,i}]*[T_{b,i}]
A_eq2 = np.hstack((-np.eye(nSources), np.zeros((nSources, nSources + 1))))
B_eq2 = np.zeros(nSources)
for i in range(nBoreholes):
# Coefficients for current borehole
a_in, a_b = UTubes[i].coefficients_borehole_heat_extraction_rate(
m_flow[i], cp, nSegments)
# Matrix coefficients ranges
# Rows
j1 = i*nSegments
j2 = (i+1) * nSegments
# Columns
n1 = j1 + nSources
n2 = j2 + nSources
# Segment length
Hi = boreholes[i].H / nSegments
A_eq2[j1:j2, -1:] = a_in / (-2.0*pi*UTubes[i].k_s*Hi)
A_eq2[j1:j2, n1:n2] = a_b / (-2.0*pi*UTubes[i].k_s*Hi)
# Energy conservation: sum([Qb*Hb]) = sum([Hb])
A_eq3 = np.hstack((Hb, np.zeros(nSources + 1)))
B_eq3 = np.atleast_1d(np.sum(Hb))
# Build and solve the system of equations at all times
for p in range(nt):
# Current thermal response factor matrix
h_ij_dt = h_dt[:,:,p]
# Reconstructed load history
Q_reconstructed = load_history_reconstruction(t[0:p+1], Q[:,0:p+1])
# Borehole wall temperature for zero heat extraction at current step
Tb_0 = _temporal_superposition(dh_ij, Q_reconstructed)
# Spatial superposition: [Tb] = [Tb0] + [h_ij_dt]*[Qb]
A_eq1 = np.hstack((h_ij_dt,
-np.eye(nSources),
np.zeros((nSources, 1))))
B_eq1 = -Tb_0
# Assemble equations
A = np.vstack((A_eq1, A_eq2, A_eq3))
B = np.hstack((B_eq1, B_eq2, B_eq3))
# Solve the system of equations
X = np.linalg.solve(A, B)
# Store calculated heat extraction rates
Q[:,p] = X[0:nSources]
# The gFunction is equal to the average borehole wall temperature
Tb = X[nSources:2*nSources]
gFunction[p] = Tb.dot(Hb) / np.sum(Hb)
toc2 = tim.time()
if disp:
print('{} sec'.format(toc2 - toc1))
print('Total time for g-function evaluation: {} sec'.format(
toc2 - tic))
print(60*'-')
# Return float if time is a scalar
if np.isscalar(time):
gFunction = np.asscalar(gFunction)
return gFunction
def mixed_inlet_temperature(network, m_flow, cp,
time, alpha, method='linear', nSegments=12,
use_similarities=True, disTol=0.01, tol=1.0e-6,
processes=None, disp=False):
"""
Evaluate the g-function with mixed inlet fluid temperatures.
This function superimposes the finite line source (FLS) solution to
estimate the g-function of a geothermal bore field. Each borehole is
modeled as a series of finite line source segments, as proposed in
[#Cimmino2018]_. The piping configurations between boreholes can be any
combination of series and parallel connections.
Parameters
----------
network : Network objects
List of boreholes included in the bore field.
m_flow : float or array
Total mass flow rate into the network or inlet mass flow rates
into each circuit of the network (in kg/s). If a float is supplied,
the total mass flow rate is split equally into all circuits.
cp : float or array
Fluid specific isobaric heat capacity (in J/kg.degC).
Must be the same for all circuits (a single float can be supplied).
time : float or array
Values of time (in seconds) for which the g-function is evaluated.
alpha : float
Soil thermal diffusivity (in m2/s).
nSegments : int, optional
Number of line segments used per borehole.
Default is 12.
method : string, optional
Interpolation method used for segment-to-segment thermal response
factors. See documentation for scipy.interpolate.interp1d.
Default is 'linear'.
use_similarities : bool, optional
True if similarities are used to limit the number of FLS evaluations.
Default is True.
disTol : float, optional
Relative tolerance on radial distance. Two distances
(d1, d2) between two pairs of boreholes are considered equal if the
difference between the two distances (abs(d1-d2)) is below tolerance.
Default is 0.01.
tol : float, optional
Relative tolerance on length and depth. Two lengths H1, H2
(or depths D1, D2) are considered equal if abs(H1 - H2)/H2 < tol.
Default is 1.0e-6.
processes : int, optional
Number of processors to use in calculations. If the value is set to
None, a number of processors equal to cpu_count() is used.
Default is None.
disp : bool, optional
Set to true to print progression messages.
Default is False.
Returns
-------
gFunction : float or array
Values of the g-function
Examples
--------
>>> b1 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=0., y=0.)
>>> b2 = gt.boreholes.Borehole(H=150., D=4., r_b=0.075, x=5., y=0.)
>>> Utube1 = gt.pipes.SingleUTube(pos=[(-0.05, 0), (0, -0.05)],
r_in=0.015, r_out=0.02,
borehole=b1,k_s=2, k_g=1, R_fp=0.1)
>>> Utube2 = gt.pipes.SingleUTube(pos=[(-0.05, 0), (0, -0.05)],
r_in=0.015, r_out=0.02,
borehole=b1,k_s=2, k_g=1, R_fp=0.1)
>>> bore_connectivity = [-1, 0]
>>> network = gt.networks.Network([b1, b2], [Utube1, Utube2], bore_connectivity)
>>> time = np.array([1.0*10**i for i in range(4, 12)])
>>> m_flow = 0.25
>>> cp = 4000.
>>> alpha = 1.0e-6
>>> gt.gfunction.mixed_inlet_temperature(network, m_flow, cp, time, alpha)
array([0.63782415, 1.63304116, 2.72191316, 4.04091713, 5.98240458,
7.77216202, 8.66195828, 8.77567215])
References
----------
.. [#Cimmino2018] Cimmino, M. (2018). g-Functions for bore fields with
mixed parallel and series connections considering the axial fluid
temperature variations. Proceedings of the IGSHPA Sweden Research Track
2018. Stockholm, Sweden. pp. 262-270.
"""
if disp:
print(60*'-')
print('Calculating g-function for mixed inlet fluid temperatures')
print(60*'-')
# Initialize chrono
tic = tim.time()
# Number of boreholes
nBoreholes = network.nBoreholes
# Total number of line sources
nSources = nSegments*nBoreholes
# Number of time values
nt = len(np.atleast_1d(time))
# Initialize g-function
gFunction = np.zeros(nt)
# Initialize segment heat extraction rates
Q = np.zeros((nSources, nt))
# Ground thermal conductivity
k_s = network.p[0].k_s
# Split boreholes into segments
boreSegments = _borehole_segments(network.b, nSegments)
# Vector of time values
t = np.atleast_1d(time).flatten()
# Calculate segment to segment thermal response factors
h_ij = thermal_response_factors(
boreSegments, t, alpha, use_similarities=use_similarities,
splitRealAndImage=True, disTol=disTol, tol=tol, processes=processes,
disp=disp)
toc1 = tim.time()
if disp:
print('Building and solving system of equations ...')
# -------------------------------------------------------------------------
# Build a system of equation [A]*[X] = [B] for the evaluation of the
# g-function. [A] is a coefficient matrix, [X] = [Qb,Tb,Tf_in] is a state
# space vector of the borehole heat extraction rates, borehole wall
# temperatures and inlet fluid temperature (into the bore field),
# [B] is a coefficient vector.
# -------------------------------------------------------------------------
# Segment lengths
Hb = np.array([[b.H for b in boreSegments]])
# Vector of time steps
dt = np.hstack((t[0], t[1:] - t[:-1]))
if not np.isscalar(time) and len(time) > 1:
# Spline object for thermal response factors
h_dt = interp1d(np.hstack((0., t)),
np.dstack((np.zeros((nSources,nSources)), h_ij)),
kind=method, axis=2)
# Thermal response factors evaluated at t=dt
h_dt = h_dt(dt)
else:
h_dt = h_ij
# Thermal response factor increments
dh_ij = np.concatenate((h_ij[:,:,0:1], h_ij[:,:,1:]-h_ij[:,:,:-1]), axis=2)
# Energy balance on borehole segments:
# [Q_{b,i}] = [a_in]*[T_{f,in}] + [a_{b,i}]*[T_{b,i}]
a_in, a_b = network.coefficients_borehole_heat_extraction_rate(
m_flow, cp, nSegments)
A_eq2 = np.hstack((np.eye(nSources), a_b/(2.0*pi*k_s*Hb.T), a_in/(2.0*pi*k_s*Hb.T)))
B_eq2 = np.zeros(nSources)
# Energy conservation: sum([Qb*Hb]) = sum([Hb])
A_eq3 = np.hstack((Hb, np.zeros((1, nSources + 1))))
B_eq3 = np.atleast_1d(np.sum(Hb))
# Build and solve the system of equations at all times
for p in range(nt):
# Current thermal response factor matrix
h_ij_dt = h_dt[:,:,p]
# Reconstructed load history
Q_reconstructed = load_history_reconstruction(t[0:p+1], Q[:,0:p+1])
# Borehole wall temperature for zero heat extraction at current step
Tb_0 = _temporal_superposition(dh_ij, Q_reconstructed)
# Spatial superposition: [Tb] = [Tb0] + [h_ij_dt]*[Qb]
A_eq1 = np.hstack((h_ij_dt,
-np.eye(nSources),
np.zeros((nSources, 1))))
B_eq1 = -Tb_0
# Assemble equations
B = np.hstack((B_eq1, B_eq2, B_eq3))
A = np.vstack((A_eq1, A_eq2, A_eq3))
# Solve the system of equations
X = np.linalg.solve(A, B)
# Store calculated heat extraction rates
Q[:,p] = X[0:nSources]
# The gFunction is equal to the average borehole wall temperature
Tf_in = X[-1]
Tf_out = Tf_in - 2*pi*k_s*np.sum(Hb)/(m_flow*cp)
Tf = 0.5*(Tf_in + Tf_out)
Rfield = network_thermal_resistance(network, m_flow, cp)
Tb_eff = Tf - 2*pi*k_s*Rfield
gFunction[p] = Tb_eff
toc2 = tim.time()
if disp:
print('{} sec'.format(toc2 - toc1))
print('Total time for g-function evaluation: {} sec'.format(
toc2 - tic))
print(60*'-')
# Return float if time is a scalar
if np.isscalar(time):
gFunction = np.asscalar(gFunction)
return gFunction
def load_history_reconstruction(time, Q):
"""
Reconstructs the load history.
This function calculates an equivalent load history for an inverted order
of time step sizes.
Parameters
----------
time : array
Values of time (in seconds) in the load history.
Q : array
Heat extraction rates (in Watts) of all segments at all times.
Returns
-------
Q_reconstructed : array
Reconstructed load history.
"""
# Number of heat sources
nSources = Q.shape[0]
# Time step sizes
dt = np.hstack((time[0], time[1:]-time[:-1]))
# Time vector
t = np.hstack((0., time, time[-1] + time[0]))
# Inverted time step sizes
dt_reconstructed = dt[::-1]
# Reconstructed time vector
t_reconstructed = np.hstack((0., np.cumsum(dt_reconstructed)))
# Accumulated heat extracted
f = np.hstack((np.zeros((nSources, 1)), np.cumsum(Q*dt, axis=1)))
f = np.hstack((f, f[:,-1:]))
# Create interpolation object for accumulated heat extracted
sf = interp1d(t, f, kind='linear', axis=1)
# Reconstructed load history
Q_reconstructed = (sf(t_reconstructed[1:]) - sf(t_reconstructed[:-1])) \
/ dt_reconstructed
return Q_reconstructed
def _borehole_segments(boreholes, nSegments):
"""
Split boreholes into segments.
This function goes through the list of boreholes and builds a new list,
with each borehole split into nSegments.
Parameters
----------
boreholes : list of Borehole objects
List of boreholes included in the bore field.
nSegments : int
Number of line segments used per borehole.
Returns
-------
boreSegments : list
List of borehole segments.
"""
boreSegments = []
for b in boreholes:
for i in range(nSegments):
# Divide borehole into segments of equal length
H = b.H / nSegments
# Buried depth of the i-th segment
D = b.D + i * b.H / nSegments
# Add to list of segments
boreSegments.append(Borehole(H, D, b.r_b, b.x, b.y))
return boreSegments
def _temporal_superposition(dh_ij, Q):
"""
Temporal superposition for inequal time steps.
Parameters
----------
dh_ij : array
Values of the segment-to-segment thermal response factor increments at
the given time step.
Q : array
Heat extraction rates of all segments at all times.
Returns
-------
Tb_0 : array
Current values of borehole wall temperatures assuming no heat
extraction during current time step.
"""
# Number of heat sources
nSources = Q.shape[0]
# Number of time steps
nt = Q.shape[1]
# Borehole wall temperature
Tb_0 = np.zeros(nSources)
# Spatial and temporal superpositions
for it in range(nt):
Tb_0 += dh_ij[:,:,it].dot(Q[:,nt-it-1])
return Tb_0