Skip to content

Add API for MIFT Boundary Condition #315

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

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

mitchute
Copy link
Contributor

@mitchute mitchute commented Apr 9, 2025

PR adds an API for computing g-functions using static inputs based on the MIFT boundary condition. As a reminder, PR #308 added a parallel API for the UHTR and UBWT boundary conditions. Since the input requirements are so different from the two schemes (MIFT vs UHTR and UBWT), rather than trying to unify the API, IMO it looks more straightforward for users to pick which one they want based on the BC and level of inputs they are interested in providing. The RR is still somewhat "drafty," and somethings like the use of Enum and the right sets of input arguments to expose need to be pinned down. The code is functional for the current test cases, but we likely should be testing and exposing more, as needed to get to the appropriate basic functionality level. We could add lots of fanciness on top after that.

Feel free to review/discuss and we can go from there.

@MassimoCimmino
Copy link
Owner

Reading through the code, it seems to me the API has two objectives:

  1. To follow-up on Create API for computing g-function values from static inputs #308 and implement an API that uses the Network object to evaluate g-functions.
  2. To facilitate the creation of Network (and Pipe) objects using static inputs and automate the evaluation of the required resistances.

For consistensy, my opinion is that (1.) should be addressed by implementing a evaluate_g_function method in the Network class, analogous to the method now implemented in the Borefield class.

My proposal for (2.) is to create a new class GroundHeatExchanger (or something similar) that encapsulates the cross-sectional geometry of the boreholes, evaluates thermal resistances, and includes methods to create Pipe or Network instances. Long-term, this could conveniently deal with #304 and also solve an issue of Network initialization where the multipole solution is evaluated nBoreholes times even when the geometry is the same. I have code from another project that could be adapted for implementation into pygfunction.

@mitchute
Copy link
Contributor Author

@MassimoCimmino thanks for your input. We can update this to adapt to your suggestions.

Copy link
Contributor Author

@mitchute mitchute left a comment

Choose a reason for hiding this comment

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

@MassimoCimmino thanks for your feedback. These updates are a first pass at addressing your suggestions.

Comment on lines +4 to +7
class GHEType(Enum):
BOREHOLE = auto()
BOREFIElD = auto()
NETWORK = auto()
Copy link
Contributor Author

@mitchute mitchute May 14, 2025

Choose a reason for hiding this comment

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

Needed this to break circular import issues.

Edit: I just noticed the lower case l in BOREFIElD. Need to update that.

Comment on lines +260 to +265
# needed to break circular imports
from .solvers import (
Detailed,
Equivalent,
Similarities
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's some lingering circular dependencies among the files. Needed to import these here to break the circular import problems.

Comment on lines -1239 to +1244
if isinstance(boreholes_or_network, Borehole):
if boreholes_or_network.ghe_type == GHEType.BOREHOLE:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Related to circular import issues

Comment on lines -1243 to +1248
if isinstance(boreholes_or_network, Network):
elif boreholes_or_network.ghe_type == GHEType.NETWORK:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seemed to me like these should be chained if-else statements, not a series of if blocks, since they didn't return early.

Comment on lines -1536 to -1540
def equal_inlet_temperature(
boreholes, UTubes, m_flow_borehole, cp_f, time, alpha,
kind='linear', nSegments=8, segment_ratios=segment_ratios,
use_similarities=True, disTol=0.01, tol=1.0e-6, dtype=np.double,
disp=False, **kwargs):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are all tagged for deprecation. I removed them so I didn't have to continue to deal with circular import problems.

Comment on lines +12 to +16
class GroundHeatExchanger:

def __init__(self,
H: npt.ArrayLike,
D: npt.ArrayLike,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New GHE class. Constructor and evaluate_g_function arguments need a more thorough review.

Comment on lines -995 to +1006
self._stored_coefficients = [() for i in range(nMethods)]
self._stored_coefficients = [() for _ in range(nMethods)]
self._stored_m_flow_cp = [np.empty(self.nInlets)*np.nan
for i in range(nMethods)]
self._stored_nSegments = [np.nan for i in range(nMethods)]
self._stored_segment_ratios = [np.nan for i in range(nMethods)]
for _ in range(nMethods)]
self._stored_nSegments = [np.nan for _ in range(nMethods)]
self._stored_segment_ratios = [np.nan for _ in range(nMethods)]
Copy link
Contributor Author

@mitchute mitchute May 14, 2025

Choose a reason for hiding this comment

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

unused variables

Comment on lines 3623 to 3626
def compute_R_fp(
pipe_type: PipeType, m_flow_borehole: float, r_in: Union[float, tuple, list],
r_out: Union[float, tuple, list], k_p: float, epsilon: float, fluid: Fluid) -> float:
if pipe_type in [PipeType.SINGLEUTUBE, PipeType.DOUBLEUTUBESERIES]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some worker functions

k_p: float,
fluid_name: str,
fluid_concentration_pct: float,
m_flow_ghe,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would prefer to not have to pass this in to the constructor, but that's going to require some more work.

Comment on lines +343 to +380
@pytest.mark.parametrize("field, boundary_condition, method, opts, pipe_type, m_flow_network, expected", [
# 'equivalent' solver - unequal segments - UBWT - single u-tube
('single_borehole', 'UBWT', 'equivalent', 'unequal_segments', 'single_Utube', 0.05, np.array([5.59717446, 6.36257605, 6.60517223])),
('single_borehole_short', 'UBWT', 'equivalent', 'unequal_segments', 'single_Utube', 0.05, np.array([4.15784411, 4.98477603, 5.27975732])),
('ten_boreholes_rectangular', 'UBWT', 'equivalent', 'unequal_segments', 'single_Utube', 0.25, np.array([10.89935004, 17.09864925, 19.0795435])),
# 'equivalent' solver - unequal segments - UHTR - single u-tube
('single_borehole', 'UHTR', 'equivalent', 'unequal_segments', 'single_Utube', 0.05, np.array([5.61855789, 6.41336758, 6.66933682])),
('single_borehole_short', 'UHTR', 'equivalent', 'unequal_segments', 'single_Utube', 0.05, np.array([4.18276733, 5.03671562, 5.34369772])),
('ten_boreholes_rectangular', 'UHTR', 'equivalent', 'unequal_segments', 'single_Utube', 0.25, np.array([11.27831804, 18.48075762, 21.00669237])),
# 'equivalent' solver - unequal segments - MIFT - single u-tube
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'single_Utube', 0.05, np.array([5.76597302, 6.51058473, 6.73746895])),
('single_borehole_short', 'MIFT', 'equivalent', 'unequal_segments', 'single_Utube', 0.05, np.array([4.17105954, 5.00930075, 5.30832133])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'single_Utube', 0.25, np.array([12.66229998, 18.57852681, 20.33535907])),
# 'equivalent' solver - unequal segments - MIFT - double u-tube parallel
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_parallel', 0.05, np.array([6.47497545, 7.18728277, 7.39167598])),
('single_borehole_short', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_parallel', 0.05, np.array([4.17080765, 5.00341368, 5.2989709])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_parallel', 0.25, np.array([15.96448954, 21.43320976, 22.90761598])),
# 'equivalent' solver - unequal segments - MIFT - double u-tube series
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series', 0.05, np.array([5.69118368, 6.44386342, 6.67721347])),
('single_borehole_short', 'MIFT','equivalent', 'unequal_segments', 'double_Utube_series', 0.05, np.array([4.16750616, 5.00249502, 5.30038701])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series', 0.25, np.array([11.94256058, 17.97858109, 19.83460231])),
# 'equivalent' solver - unequal segments - MIFT - double u-tube series asymmetrical
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series_asymmetrical', 0.05, np.array([5.69174709, 6.4441862 , 6.67709693])),
('single_borehole_short', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series_asymmetrical', 0.05, np.array([4.16851817, 5.00453267, 5.30282913])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series_asymmetrical', 0.25, np.array([11.96927941, 18.00481705, 19.856554])),
# 'equivalent' solver - unequal segments - MIFT - double u-tube series asymmetrical
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series_asymmetrical', 0.05, np.array([5.69174709, 6.4441862, 6.67709693])),
('single_borehole_short', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series_asymmetrical', 0.05, np.array([4.16851817, 5.00453267, 5.30282913])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'double_Utube_series_asymmetrical', 0.25, np.array([11.96927941, 18.00481705, 19.856554])),
# 'equivalent' solver - unequal segments - MIFT - coaxial annular inlet
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'coaxial_annular_in', 0.05, np.array([6.10236427, 6.77069069, 6.95941276])),
('single_borehole_short', 'MIFT', 'equivalent', 'unequal_segments', 'coaxial_annular_in', 0.05, np.array([4.06874781, 4.89701125, 5.19157017])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'coaxial_annular_in', 0.25, np.array([16.03433989, 21.18241954, 22.49479982])),
# 'equivalent' solver - unequal segments - MIFT - coaxial annular outlet
('single_borehole', 'MIFT', 'equivalent', 'unequal_segments', 'coaxial_annular_out', 0.05, np.array([6.10236427, 6.77069069, 6.95941276])),
('single_borehole_short', 'MIFT', 'equivalent', 'unequal_segments', 'coaxial_annular_out', 0.05, np.array([4.06874781, 4.89701125, 5.19157017])),
('ten_boreholes_rectangular', 'MIFT', 'equivalent', 'unequal_segments', 'coaxial_annular_out', 0.25, np.array([16.03433989, 21.18241954, 22.49510883])),
])
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some tests to verify things are nominally functional. Expected values pulled from existing tests from above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants