Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential API for PyGFunction #4

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5294981
example api showing how to pass in config and return gfunction
vtnate Nov 1, 2024
21ccaa8
test demonstrating use of api
vtnate Nov 1, 2024
f6241ab
fix typing and allow options dict
vtnate Nov 6, 2024
97a7068
add todo in api test
vtnate Nov 6, 2024
dce7030
some cleanups
mitchute Nov 8, 2024
a4ec330
fix type hinting that I broke
mitchute Nov 8, 2024
2c3cf40
incorporate suggestions
mitchute Nov 11, 2024
1c9d52a
fix typing
mitchute Nov 11, 2024
a9a18df
more typing
mitchute Nov 11, 2024
3e0869a
cleanup tests
mitchute Nov 12, 2024
b2f5988
Updated API file a bit
Myoldmopar Nov 12, 2024
cbf728c
add docstrings and other documenting comments
mitchute Nov 12, 2024
ce9d520
combine classes, update docs
mitchute Nov 12, 2024
a67ad63
return list
mitchute Nov 12, 2024
a4fb763
Initial implementation of Borefield class
MassimoCimmino Nov 24, 2024
f52c143
Class method to evaluate g-function
MassimoCimmino Nov 24, 2024
5f86520
Change init to use arrays instead of boreholes
MassimoCimmino Nov 27, 2024
7c1bfc8
Fix broadcasting of tilt and orientation
MassimoCimmino Nov 27, 2024
8320711
Fix borefield.from_file to call class init
MassimoCimmino Nov 27, 2024
5f31f6b
Docstrings
MassimoCimmino Nov 27, 2024
4707959
Remove api module
MassimoCimmino Nov 27, 2024
fc78296
Test g-functions
MassimoCimmino Nov 28, 2024
f2d4341
Test borefield.from_boreholes
MassimoCimmino Nov 28, 2024
4806d16
Implement bore field creation methods
MassimoCimmino Nov 28, 2024
d5374ce
Deprecation warnings for old bore field functions
MassimoCimmino Nov 28, 2024
1a5f0c1
Fix Borefield.from_file
MassimoCimmino Nov 28, 2024
59e0d99
Update examples
MassimoCimmino Nov 28, 2024
6027e42
Boreholes can have negative tilt
MassimoCimmino Nov 28, 2024
f999eac
Save field to file
MassimoCimmino Nov 28, 2024
2010311
Fix dense and box fields
MassimoCimmino Nov 28, 2024
4364c06
Update tests
MassimoCimmino Nov 28, 2024
0645d57
Remove blank space
MassimoCimmino Nov 28, 2024
8f5543c
Import Self from typing_extensions
MassimoCimmino Nov 29, 2024
c512fc7
Update CHANGELOG.md
MassimoCimmino Nov 29, 2024
5972850
Update requirements
MassimoCimmino Nov 29, 2024
e3c92d7
Review changes
MassimoCimmino Nov 30, 2024
34d5543
Re-order methods
MassimoCimmino Nov 30, 2024
033c9ac
Implement Borefield.visualize_field
MassimoCimmino Dec 1, 2024
1084a09
Typing for Borefield.visualize_field
MassimoCimmino Dec 1, 2024
a37a8ca
Final review
MassimoCimmino Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

* [Issue 215](https://github.com/MassimoCimmino/pygfunction/issues/215) - Implemented variable fluid mass flow rate g-functions. Bore fields with series-connected boreholes and reversible flow direction can now be simulated.
* [Issue 282](https://github.com/MassimoCimmino/pygfunction/issues/282) - Enabled the use of negative mass flow rates in `Pipe` and `Network` classes to model reversed flow direction.
* [Pull Request 308](https://github.com/MassimoCimmino/pygfunction/pull/308) - Introduced a new `borefield` module. The new `Borefield` class replaces lists of `Borehole` objects as the preferred way to configure bore fields. The `Borefield.evaluate_g_function` method evaluates g-functions using the 'UHTR' and 'UBWT' boundary conditions. Deprecated bore field creation functions in the `boreholes` module (e.g. `boreholes.rectangle_field()`). These functions are replaced by methods of the new `Borefield` class. They will be removed in `v3.0.0`.

## Version 2.2.3 (2024-07-01)

Expand Down
1 change: 1 addition & 0 deletions doc/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ numpydoc >= 1.2.0
recommonmark >= 0.6.0
sphinx >= 4.4.0
secondarycoolantprops >= 1.1.0
typing_extensions >= 4.0.1
3 changes: 2 additions & 1 deletion doc/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
Setting up pygfunction
**********************

*pygfunction* uses Python 3.7, along with the following packages:
*pygfunction* uses Python 3.8, along with the following packages:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MassimoCimmino thanks, this looks great! One minor point - python 3.8 is past end of life, so perhaps in a separate PR this could be bumped up to a more current version.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I tend to update Python versions and dependencies on major and minor releases. This is one place where I had forgotten to update on the last version.

A little summary of my implementation if you want to discuss any of the changes:

  1. The gFunction class now accepts Borefield objects. That did not require changes in the gFunction class (outside of isinstance checks) since the new class behaves like a list of boreholes for all operations done in pygfunction.
  2. The Borefield.evaluate_g_function class method returns an array of g-function values. This was to align with the current gFunction.evaluate_g_function method.
  3. I moved borefield creation functions (e.g. boreholes.rectangle_field) to the Borefield class. They needed to be re-implemented to generate arrays of geometrical parameters instead of lists of boreholes. The functions in the boreholes module now have a deprecation warning.
  4. Examples are update to use the new interface for bore field creation.

Something to consider is if the boreholes.visualize_borefield should also be moved to the Borefield class. I remember we had a discussion about the dependency for matplotlib. An option is to include the method and import matplotlib at runtime within the method instead of the top of the module.

The PR is much larger than I expected. I will review it and make sure it is compatible with the planned changes for issue MassimoCimmino#210.

- matplotlib (>= 3.5.1),
- numpy (>= 1.21.5)
- scipy (>= 1.7.3)
- SecondaryCoolantProps (>= 1.1)
- typing_extensions (>= 4.0.1)

*pygfunction*'s- documentation is built using:
- sphinx (>= 4.4.0)
Expand Down
21 changes: 8 additions & 13 deletions examples/bore_field_thermal_resistance.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import AutoMinorLocator
from scipy import pi

import pygfunction as gt

Expand Down Expand Up @@ -61,15 +60,11 @@ def main():
# Borehole field
# -------------------------------------------------------------------------

boreField = []
bore_connectivity = []
for i in range(nBoreholes):
x = i*B
borehole = gt.boreholes.Borehole(H, D, r_b, x, 0.)
boreField.append(borehole)
# Boreholes are connected in series: The index of the upstream
# borehole is that of the previous borehole
bore_connectivity.append(i - 1)
x = np.arange(nBoreholes) * B
borefield = gt.borefield.Borefield(H, D, r_b, x, 0.)
# Boreholes are connected in series: The index of the upstream
# borehole is that of the previous borehole
bore_connectivity = [i - 1 for i in range(nBoreholes)]

# -------------------------------------------------------------------------
# Evaluate the effective bore field thermal resistance
Expand All @@ -91,16 +86,16 @@ def main():
# Fluid to inner pipe wall thermal resistance (Single U-tube)
h_f = gt.pipes.convective_heat_transfer_coefficient_circular_pipe(
m_flow_pipe, r_in, mu_f, rho_f, k_f, cp_f, epsilon)
R_f = 1.0/(h_f*2*pi*r_in)
R_f = 1.0 / (h_f * 2 * np.pi * r_in)

# Single U-tube, same for all boreholes in the bore field
UTubes = []
for borehole in boreField:
for borehole in borefield:
SingleUTube = gt.pipes.SingleUTube(
pos_pipes, r_in, r_out, borehole, k_s, k_g, R_f + R_p)
UTubes.append(SingleUTube)
network = gt.networks.Network(
boreField[:nBoreholes],
borefield[:nBoreholes],
UTubes[:nBoreholes],
bore_connectivity=bore_connectivity[:nBoreholes])

Expand Down
9 changes: 5 additions & 4 deletions examples/comparison_gfunction_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,23 @@ def main():
# Field of 6x4 (n=24) boreholes
N_1 = 6
N_2 = 4
field = gt.boreholes.rectangle_field(N_1, N_2, B, B, H, D, r_b)
borefield = gt.borefield.Borefield.rectangle_field(
N_1, N_2, B, B, H, D, r_b)

# -------------------------------------------------------------------------
# Evaluate g-functions
# -------------------------------------------------------------------------
t0 = perf_counter()
gfunc_detailed = gt.gfunction.gFunction(
field, alpha, time=time, options=options, method='detailed')
borefield, alpha, time=time, options=options, method='detailed')
t1 = perf_counter()
t_detailed = t1 - t0
gfunc_similarities = gt.gfunction.gFunction(
field, alpha, time=time, options=options, method='similarities')
borefield, alpha, time=time, options=options, method='similarities')
t2 = perf_counter()
t_similarities = t2 - t1
gfunc_equivalent = gt.gfunction.gFunction(
field, alpha, time=time, options=options, method='equivalent')
borefield, alpha, time=time, options=options, method='equivalent')
t3 = perf_counter()
t_equivalent = t3 - t2

Expand Down
20 changes: 10 additions & 10 deletions examples/comparison_load_aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import matplotlib.pyplot as plt
import numpy as np
from scipy.constants import pi
from scipy.interpolate import interp1d
from scipy.signal import fftconvolve

Expand Down Expand Up @@ -64,12 +63,12 @@ def main():
# -------------------------------------------------------------------------

# The field contains only one borehole
boreField = [gt.boreholes.Borehole(H, D, r_b, x=0., y=0.)]
borehole = gt.boreholes.Borehole(H, D, r_b, x=0., y=0.)
# Evaluate the g-function on a geometrically expanding time grid
time_gFunc = gt.utilities.time_geometric(dt, tmax, 50)
# Calculate g-function
gFunc = gt.gfunction.gFunction(
boreField, alpha, time=time_gFunc, options=options)
borehole, alpha, time=time_gFunc, options=options)

# -------------------------------------------------------------------------
# Simulation
Expand All @@ -88,7 +87,7 @@ def main():
bounds_error=False,
fill_value=(0., gFunc.gFunc[-1]))(time_req)
# Initialize load aggregation scheme
LoadAgg.initialize(gFunc_int/(2*pi*k_s))
LoadAgg.initialize(gFunc_int / (2 * np.pi * k_s))

tic = perf_counter()
for i in range(Nt):
Expand Down Expand Up @@ -116,7 +115,8 @@ def main():
g = interp1d(time_gFunc, gFunc.gFunc)(time)

# Convolution in Fourier domain
T_b_exact = T_g - fftconvolve(dQ, g/(2.0*pi*k_s*H), mode='full')[0:Nt]
T_b_exact = T_g - fftconvolve(
dQ, g / (2.0 * np.pi * k_s * H), mode='full')[0:Nt]

# -------------------------------------------------------------------------
# plot results
Expand Down Expand Up @@ -186,14 +186,14 @@ def synthetic_load(x):

func = (168.0-C)/168.0
for i in [1, 2, 3]:
func += 1.0/(i*pi)*(np.cos(C*pi*i/84.0) - 1.0) \
*(np.sin(pi*i/84.0*(x - B)))
func = func*A*np.sin(pi/12.0*(x - B)) \
*np.sin(pi/4380.0*(x - B))
func += 1.0/(i*np.pi)*(np.cos(C*np.pi*i/84.0) - 1.0) \
*(np.sin(np.pi*i/84.0*(x - B)))
func = func*A*np.sin(np.pi/12.0*(x - B)) \
*np.sin(np.pi/4380.0*(x - B))

y = func + (-1.0)**np.floor(D/8760.0*(x - B))*abs(func) \
+ E*(-1.0)**np.floor(D/8760.0*(x - B)) \
/np.sign(np.cos(D*pi/4380.0*(x - F)) + G)
/np.sign(np.cos(D*np.pi/4380.0*(x - F)) + G)
return -y


Expand Down
19 changes: 8 additions & 11 deletions examples/custom_bore_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
""" Example of definition of a bore field using custom borehole positions.

"""
import numpy as np

import pygfunction as gt


Expand All @@ -20,32 +22,27 @@ def main():
# Position 1 has a borehole that is directly on top of another bore
# Position 2 has a borehole with radius inside of another bore
# The duplicates will be removed with the remove_duplicates function
pos = [(0.0, 0.0),
(0.0, 0.0), # Duplicate (for example purposes)
(0.03, 0.0), # Duplicate (for example purposes)
(5.0, 0.),
(3.5, 4.0),
(1.0, 7.0),
(5.5, 5.5)]
x = np.array([0., 0., 0.03, 5., 3.5, 1., 5.5])
y = np.array([0., 0., 0.00, 0., 4.0, 7., 5.5])

# -------------------------------------------------------------------------
# Borehole field
# -------------------------------------------------------------------------

# Build list of boreholes
field = [gt.boreholes.Borehole(H, D, r_b, x, y) for (x, y) in pos]
borefield = gt.borefield.Borefield(H, D, r_b, x, y)

# -------------------------------------------------------------------------
# Find and remove duplicates from borehole field
# -------------------------------------------------------------------------

field = gt.boreholes.remove_duplicates(field, disp=True)
borefield = gt.borefield.Borefield.from_boreholes(
gt.boreholes.remove_duplicates(borefield, disp=True))

# -------------------------------------------------------------------------
# Draw bore field
# -------------------------------------------------------------------------

gt.boreholes.visualize_field(field)
borefield.visualize_field()

return

Expand Down
4 changes: 2 additions & 2 deletions examples/custom_bore_field_from_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ def main():
# -------------------------------------------------------------------------

# Build list of boreholes
field = gt.boreholes.field_from_file(filename)
borefield = gt.borefield.Borefield.from_file(filename)

# -------------------------------------------------------------------------
# Draw bore field
# -------------------------------------------------------------------------

gt.boreholes.visualize_field(field)
borefield.visualize_field()

return

Expand Down
15 changes: 8 additions & 7 deletions examples/discretize_boreholes.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ def main():
# Field of 6x4 (n=24) boreholes
N_1 = 6
N_2 = 4
boreField = gt.boreholes.rectangle_field(N_1, N_2, B, B, H, D, r_b)
gt.boreholes.visualize_field(boreField)
nBoreholes = len(boreField)
borefield = gt.borefield.Borefield.rectangle_field(
N_1, N_2, B, B, H, D, r_b)
gt.boreholes.visualize_field(borefield)
nBoreholes = len(borefield)

# -------------------------------------------------------------------------
# Initialize pipe model
Expand All @@ -107,14 +108,14 @@ def main():

# Single U-tube, same for all boreholes in the bore field
UTubes = []
for borehole in boreField:
for borehole in borefield:
SingleUTube = gt.pipes.SingleUTube(
pos_pipes, r_in, r_out, borehole, k_s, k_g, R_f + R_p)
UTubes.append(SingleUTube)
m_flow_network = m_flow_borehole*nBoreholes

# Network of boreholes connected in parallel
network = gt.networks.Network(boreField, UTubes)
network = gt.networks.Network(borefield, UTubes)

# -------------------------------------------------------------------------
# Evaluate the g-functions for the borefield
Expand All @@ -128,7 +129,7 @@ def main():

# Calculate the g-function for uniform borehole wall temperature
gfunc_UBWT_uniform = gt.gfunction.gFunction(
boreField, alpha, time=time, boundary_condition='UBWT',
borefield, alpha, time=time, boundary_condition='UBWT',
options=options_uniform)

# Compute g-function for the MIFT case with equal number of segments per
Expand All @@ -139,7 +140,7 @@ def main():

# Calculate the g-function for uniform borehole wall temperature
gfunc_UBWT_unequal = gt.gfunction.gFunction(
boreField, alpha, time=time, boundary_condition='UBWT',
borefield, alpha, time=time, boundary_condition='UBWT',
options=options_unequal)

# Compute the rmse between the reference cases and the discretized
Expand Down
16 changes: 8 additions & 8 deletions examples/equal_inlet_temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import AutoMinorLocator
from scipy import pi

import pygfunction as gt

Expand Down Expand Up @@ -74,8 +73,9 @@ def main():
# Field of 6x4 (n=24) boreholes
N_1 = 6
N_2 = 4
boreField = gt.boreholes.rectangle_field(N_1, N_2, B, B, H, D, r_b)
nBoreholes = len(boreField)
borefield = gt.borefield.Borefield.rectangle_field(
N_1, N_2, B, B, H, D, r_b)
nBoreholes = len(borefield)

# -------------------------------------------------------------------------
# Initialize pipe model
Expand All @@ -88,29 +88,29 @@ def main():
m_flow_pipe = m_flow_borehole
h_f = gt.pipes.convective_heat_transfer_coefficient_circular_pipe(
m_flow_pipe, r_in, mu_f, rho_f, k_f, cp_f, epsilon)
R_f = 1.0/(h_f*2*pi*r_in)
R_f = 1.0 / (h_f * 2 * np.pi * r_in)

# Single U-tube, same for all boreholes in the bore field
UTubes = []
for borehole in boreField:
for borehole in borefield:
SingleUTube = gt.pipes.SingleUTube(
pos_pipes, r_in, r_out, borehole, k_s, k_g, R_f + R_p)
UTubes.append(SingleUTube)
m_flow_network = m_flow_borehole * nBoreholes
network = gt.networks.Network(boreField, UTubes)
network = gt.networks.Network(borefield, UTubes)

# -------------------------------------------------------------------------
# Evaluate the g-functions for the borefield
# -------------------------------------------------------------------------

# Calculate the g-function for uniform heat extraction rate
gfunc_uniform_Q = gt.gfunction.gFunction(
boreField, alpha, time=time, boundary_condition='UHTR',
borefield, alpha, time=time, boundary_condition='UHTR',
options=options)

# Calculate the g-function for uniform borehole wall temperature
gfunc_uniform_T = gt.gfunction.gFunction(
boreField, alpha, time=time, boundary_condition='UBWT',
borefield, alpha, time=time, boundary_condition='UBWT',
options=options)

# Calculate the g-function for equal inlet fluid temperature
Expand Down
22 changes: 9 additions & 13 deletions examples/fluid_temperature.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"""
import matplotlib.pyplot as plt
import numpy as np
from scipy.constants import pi

import pygfunction as gt

Expand Down Expand Up @@ -85,16 +84,13 @@ def main():

# The field contains only one borehole
borehole = gt.boreholes.Borehole(H, D, r_b, x=0., y=0.)
boreField = [borehole]
# Get time values needed for g-function evaluation
time_req = LoadAgg.get_times_for_simulation()
# Calculate g-function
gFunc = gt.gfunction.gFunction(
boreField, alpha, time=time_req, options=options)
# gt.gfunction.uniform_temperature(boreField, time_req, alpha,
# nSegments=nSegments)
borehole, alpha, time=time_req, options=options)
# Initialize load aggregation scheme
LoadAgg.initialize(gFunc.gFunc/(2*pi*k_s))
LoadAgg.initialize(gFunc.gFunc / (2 * np.pi * k_s))

# -------------------------------------------------------------------------
# Initialize pipe models
Expand All @@ -107,11 +103,11 @@ def main():
# U-tube in series)
h_f = gt.pipes.convective_heat_transfer_coefficient_circular_pipe(
m_flow, rp_in, visc_f, den_f, k_f, cp_f, epsilon)
R_f_ser = 1.0/(h_f*2*pi*rp_in)
R_f_ser = 1.0 / (h_f * 2 * np.pi * rp_in)
# Fluid to inner pipe wall thermal resistance (Double U-tube in parallel)
h_f = gt.pipes.convective_heat_transfer_coefficient_circular_pipe(
m_flow/2, rp_in, visc_f, den_f, k_f, cp_f, epsilon)
R_f_par = 1.0/(h_f*2*pi*rp_in)
R_f_par = 1.0 / (h_f * 2 * np.pi * rp_in)

# Single U-tube
SingleUTube = gt.pipes.SingleUTube(pos_single, rp_in, rp_out,
Expand Down Expand Up @@ -284,13 +280,13 @@ def synthetic_load(x):

func = (168.0-C)/168.0
for i in [1, 2, 3]:
func += 1.0/(i*pi)*(np.cos(C*pi*i/84.0)-1.0) \
*(np.sin(pi*i/84.0*(x-B)))
func = func*A*np.sin(pi/12.0*(x-B)) \
*np.sin(pi/4380.0*(x-B))
func += 1.0/(i*np.pi)*(np.cos(C*np.pi*i/84.0)-1.0) \
*(np.sin(np.pi*i/84.0*(x-B)))
func = func*A*np.sin(np.pi/12.0*(x-B)) \
*np.sin(np.pi/4380.0*(x-B))

y = func + (-1.0)**np.floor(D/8760.0*(x-B))*abs(func) \
+ E*(-1.0)**np.floor(D/8760.0*(x-B))/np.sign(np.cos(D*pi/4380.0*(x-F))+G)
+ E*(-1.0)**np.floor(D/8760.0*(x-B))/np.sign(np.cos(D*np.pi/4380.0*(x-F))+G)
return -y


Expand Down
Loading
Loading