Dimensioning Rules Example

A 7x10 borefield is chosen to verify the dimensioning rules of Per Eskilson (1988) [2]. These g-functions have been computed using the methodology of Cimmino and Bernier (2014) [14]. The uniform borehole wall temperature (UBHWT) boundary condition with 12 segments is used, this was the closest when compared to Eskilson. Specifically, this is boundary condition 3 in Cimmino and Bernier (2014). Cimmino went on to implement the g-function calculation in an open source Python package known as pygfunction, which is published on the Python Package Index (PyPI) [12]. Further improvements to the g-function calculation methodology are detailed in Cimmino (2018) [11]. The g-functions in this example are computed using a computationally improved version of Massimo Cimmino’s methodology, which is discussed in Cook and Spitler (2021) [10]. All of the g-functions in this example were computed using the same dimensionless borehole radius, rb/H, and burial depth, D/H, ratios.

Compute g-Functions with varied B/H Ratios

To ensure that the g-function does scale with the dimensionless parameters, g-functions varying different heights are computed for borefields with uniform spacing of 5 and 8 meters. The boundary condition used is uniform borehole wall temperature (UBHWT). There is an additional two g-functions computed with a B/H=0.0625. The following tables display the inputs used:

The inputs for the B=5m g-functions computed (7x10 borefield)
D (m) H (m) rb (m) B (m) alpha (m2/s) nSegments (-)
0.16667 8 0.004 5 1E-06 12
0.25 12 0.006 5 1E-06 12
0.5 24 0.012 5 1E-06 12
1 48 0.024 5 1E-06 12
2 96 0.048 5 1E-06 12
4 192 0.096 5 1E-06 12
8 384 0.192 5 1E-06 12
The inputs for the B=20m g-functions computed (7x10 borefield)
D (m) H (m) r_b (m) B (m) alpha ((m2/s) nSegments (-)
0.66667 32 0.016 20 1E-06 12
1 48 0.024 20 1E-06 12
2 96 0.048 20 1E-06 12
4 192 0.096 20 1E-06 12
8 384 0.192 20 1E-06 12
Thus, the dimensionless ratios used:
  • D/H = 0.02083

  • rb/H = 0.0005

  • B/H (varies)

  • ln(t/ts) - Eskilsons original 27 points
    ln(t/ts)_values = [-8.5, -7.8, -7.2, -6.5, -5.9, -5.2, -4.5, -3.963, -3.27, -2.864, -2.577, -2.171,
    -1.884, -1.191, -0.497, -0.274, -0.051, 0.196, 0.419, 0.642, 0.873, 1.112, 1.335, 1.679, 2.028, 2.275, 3.003]
    

The additional g-functions computed with a B/H=0.0625 is given in the following table:

Additional g-functions computed with specifically a B/H ratio of 0.0625
D (m) H (m) r_b (m) B (m) alpha ((m2/s) nSegments (-)
2.66667 128 0.064 8 1E-06 12
1.66667 80 0.04 5 1E-06 12

The g-functions from the input tables above are plotted in the Fig. 1. The g-functions computed for the borefield with a uniform spacing, B=5m, are listed in the B/H library legend. This was done because this is like what will be in the library. The g-functions computed using the uniform spacing, B=20m, are listed in the B/H Computed legend. The g-functions which have the same B/H value for the 5m and 20m spacing fields are computed with the same color. There are two additional g-function curves computed for a B/H=0.0625, which will be used as reference for interpolation later on.

../../_images/dimensioning_rules_figure_01.png

Fig. 1 g-Functions plotted with the same rb/H, D/H and borehole layout (7x10), but with varied B/H values

Additionally, mean percentage errors (computed with gFunctionLibrary.statistics.mpe()) are presented in the table below.

Mean percent errors for g-functions with the same B/H, and also computed using a separate B/H
B H0
H1 H2 H3 H4
5
96
48 24 12 8
20 384 192 96 48 32
MPE (%) 4.028e-05
-8.7e-06 5.5e-04 1.381e-02 -4.759e-02

Interpolation

The g-functions computed with B/H ratios of 0.0625 were computed and are displayed in Fig. 1 to have a reference for checking the accuracy of interpolation using the B/H Library g-functions (all containing a B spacing of 5m). The mean percent errors for different methods of interpolation can be seen in the table below.

Mean percentage error of interpolation for a B/H=0.0625 using different interpolation methods
Interpolation Method MPE (%)
linear -0.30
quadratic
-0.05
cubic -0.02
lagrange 1.04

An interpolated g-function (using cubic interpolation) is now plotted on an updated Fig. 2.

../../_images/dimensioning_rules_figure_02.png

Fig. 2 An interpolated g-function curve of B/H=0.0625 is interpolated for using g-functions computed with a uniform spacing of 5m and ranging heights

Source Code

  1# Jack C. Cook
  2# Tuesday, February 2, 2020
  3
  4"""
  5**dimensioning_rules.py**
  6
  7The following goals will be accomplished in this example:
  8    1) proof that g-functions vary based on ln(t/ts), B/H, D/H, and rb/H
  9    2) proof that interpolation over B/H is accurate
 10"""
 11
 12import gFunctionDatabase as gfl
 13import matplotlib.pyplot as plt
 14from scipy.interpolate import lagrange
 15
 16
 17def main():
 18    # Part 1) Prove that the g-functions vary based on ln(t/ts), B/H, D/H, and rb/H visually and mathematically
 19    # Part A) Collect and plot the g-functions for a library file for a 7x10 borefield where B=5
 20    path_to_lib_files: str = 'files/lib/'  # each of the files in here have g-functions like will be stored in lookup
 21    bf_5m_lib = gfl.handle_contents.Borefield(gfl.fileio.js_r(path_to_lib_files + '7x10_B_5_nbh_70.json'))
 22    fig_1, ax_1 = bf_5m_lib.visualize_g_functions()
 23
 24    # Part B) Collect the g-functions for the library file for a 7x10 borefield where B=20
 25    #         Determine the error when computing B/H values
 26    bf_20m_lib = gfl.handle_contents.Borefield(gfl.fileio.js_r(path_to_lib_files + '7x10_B_20_nbh_70.json'))
 27    # If Eskilson was right, and the dimensioning rules are accurate, then the following mean percent errors will be ~0
 28    keys_5m = list(reversed(list(bf_5m_lib.g.keys())))
 29    keys_5m = keys_5m[2:]
 30    print('The heights that will be used in the from the 5m library file:')
 31    print(keys_5m)
 32    print('The heights that will be used in the from the 20m library file:')
 33    keys_20m = list(reversed(list(bf_20m_lib.g.keys())))
 34    print(keys_20m)
 35
 36    mean_percent_errors = []
 37    for i in range(len(keys_5m)):
 38        mean_percent_error = gfl.statistics.mpe(bf_5m_lib.g[keys_5m[i]], bf_20m_lib.g[keys_20m[i]])
 39        mean_percent_errors.append(mean_percent_error)
 40    print('Mean percent errors between g-functions')
 41    print(mean_percent_errors)
 42
 43    # Part C) Plot the g-functions for the B=20 7x10 borefield
 44    lines = []
 45    for i, key in enumerate(keys_20m):
 46        line, = ax_1.plot(bf_20m_lib.log_time, bf_20m_lib.g[key], marker='o', ls='None', color='C' + str(i + 2),
 47                          markersize=3,
 48                          label=str(int(bf_20m_lib.B)) + '/' + str(key))
 49        lines.append(line)
 50
 51    # Part D) Plot a B/H of 0.0625 which is 8/128 and 5/8
 52    path_to_computed = 'files/computed/'
 53    bf_computed_5m = gfl.handle_contents.Borefield(gfl.fileio.js_r(path_to_computed + '7x10_B_5_nbh_70.json'))
 54    bf_computed_8m = gfl.handle_contents.Borefield(gfl.fileio.js_r(path_to_computed + '7x10_B_8_nbh_70.json'))
 55    line_7, = ax_1.plot(bf_computed_5m.log_time, bf_computed_5m.g[80], ls=(0, (3, 5, 1, 5)),
 56                        label=str(int(bf_computed_5m.B)) + '/' + str(80), zorder=2)
 57    line_8, = ax_1.plot(bf_computed_5m.log_time, bf_computed_8m.g[128], ls='dashed',
 58                        label=str(int(bf_computed_5m.B)) + '/' + str(128), zorder=1)
 59    lines.append(line_7)
 60    lines.append(line_8)
 61    second_legend = fig_1.legend(handles=lines,
 62                                 title='B/H'.rjust(8) + '\nComputed', bbox_to_anchor=(1.005, 0.6))
 63    fig_1.gca().add_artist(second_legend)
 64
 65    # Part E) Annotate the plot with the borehole layout and the D/H, rb/H
 66    # add in a sub axes plot with the borehole layout
 67    sub_axes = plt.axes([.0, .47, .5, .5])
 68    x, y = list(zip(*bf_5m_lib.bore_locations))
 69    sub_axes.scatter(x, y)
 70    sub_axes.set_aspect('equal')
 71    sub_axes.axis('off')
 72    # annotate the layout with B spacings
 73    sub_axes.annotate(text='', xy=(0, 0), xytext=(5, 0), arrowprops=dict(arrowstyle='<->'))
 74    sub_axes.annotate(text='', xy=(0, 0), xytext=(0, 5), arrowprops=dict(arrowstyle='<->'))
 75    ax_1.text(-7.8, 53, 'B')
 76    ax_1.text(-8.5, 62, 'B')
 77    # add in text for D/H and rb/H
 78    ax_1.text(-3.5, 124, '$\dfrac{r_b}{H}$=0.0005', fontsize=12)
 79    ax_1.text(-3.5, 111, '$\dfrac{D}{H}$=0.02083', fontsize=12)
 80
 81    fig_1.savefig('dimensioning_rules_figure_01.png')
 82
 83    # Part F) Interpolate for a B/H = 0.0625
 84    B: float = 8.
 85    H: float = 128.
 86    B_over_H: float = B / H
 87    interpolation_kinds = ['linear', 'quadratic', 'cubic', 'lagrange']
 88    g_functions = []
 89    for kind in interpolation_kinds:
 90        g_function, _, _, _ = bf_5m_lib.g_function_interpolation(B_over_H, kind=kind)
 91        bf_5m_lib.interpolation_table = {}  # this needs reset to recompute the table with a new kind
 92        g_functions.append(g_function)
 93    mean_percentage_errors = []
 94    for i in range(len(g_functions)):
 95        MPE = gfl.statistics.mpe(bf_computed_5m.g[80], g_functions[i])
 96        mean_percentage_errors.append(MPE)
 97    print('The errors associated with different interpolation methods:')
 98    for i in range(len(interpolation_kinds)):
 99        print('\t{0}:\t{1:.2f}%'.format(interpolation_kinds[i], mean_percentage_errors[i]))
100    mean_percent_errors_abs = list(map(abs, mean_percentage_errors))
101    idx = mean_percent_errors_abs.index(min(mean_percent_errors_abs))
102    print('{} interpolation is the most accurate'.format(interpolation_kinds[idx]))
103    g_function = g_functions[idx]
104    line_10, = ax_1.plot(bf_5m_lib.log_time, g_function, marker='^', zorder=0, color='C9', markersize=5, ls='None',
105                         label='0.0625')
106
107    third_legend = fig_1.legend(handles=[line_10],
108                                title='B/H'.rjust(10) + '\nInterpolated', bbox_to_anchor=(1.005, 0.2))
109    fig_1.gca().add_artist(third_legend)
110
111    fig_1.savefig('dimensioning_rules_figure_02.png')
112    plt.close(fig_1)
113
114
115if __name__ == '__main__':
116    main()