-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: master
Are you sure you want to change the base?
Conversation
@Myoldmopar can you take a look here? IMO this is headed in the right direction. We may want to see about some more helper methods for creating the lists of borehole params, or maybe we just leave that to the user? |
pygfunction/api.py
Outdated
for borehole_config_items in borehole_config_list: | ||
_borehole = Borehole(*borehole_config_items) | ||
borefield.append(_borehole) | ||
|
||
self.gfunc = gFunction( | ||
borefield, | ||
alpha, | ||
time=time, | ||
boundary_condition=boundary_condition, | ||
options=options, | ||
method=solver_method, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Myoldmopar responding to this comment (https://github.com/mitchute/pygfunction/pull/4/files#r1834563947) the gFunction class needs a list of Borehole class instances. All this is doing is setting up the init arguments which that class needs. Those parameters are here: https://github.com/mitchute/pygfunction/pull/4/files#diff-3451cc474d5698222168f66a293339a6bd296ee2c56f56409866bbae99917a79R25
pygfunction does have some methods for building out these parameter lists for some GHE configurations, so maybe we can offload some of that there. For custom GHE configurations, I think we'll likely end up doing a lot of this ourselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pygfunction/api.py
Outdated
from typing import Dict, Optional, Any, List | ||
|
||
|
||
class PYG(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about renaming the class. It kinda depends on what we expect this class to ultimately contain/do. Is it always going to be representative of a single borehole field? I'm open to suggestion, but this feels a bit vague.
pygfunction/api.py
Outdated
|
||
class PYG(object): | ||
|
||
def __init__(self, borehole_config_list: List[List[float]], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be a bit nicer to pass in a list of structures instead of a list of list of floats. With the list of floats, our tools aren't able to help inform us of what each float should be. We could do some simple dataclasses that capture the different float members, and even add some helper functions to generate them for common cases. I just really want this API to be clean and self-documenting as much as possible.
pygfunction/api.py
Outdated
options = {} | ||
|
||
for borehole_config_items in borehole_config_list: | ||
_borehole = Borehole(*borehole_config_items) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And in the little structures, we could have a to_list method that smashes the values down to be consumed by PyGFunction. That keeps that implementation detail away from API consumers.
tests/test_api.py
Outdated
|
||
# -- Assert | ||
assert isinstance(pyg_1.to_list(), list) | ||
assert isinstance(pyg_2.to_list(), list) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And before we get too much farther, we should probably grab a third-party source to generate some g-functions and do validation, or at least verification, on the g-function.
@vtnate @Myoldmopar take a look. I still need to add some validation cases. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks quite good. Very usable, in my opinion.
pygfunction/api.py
Outdated
self.tilt_angle_list: List[float] = [] # TODO: Is this degrees? And I assume from vertical? | ||
self.orientation_angle_list: List[float] = [] # TODO: Same question | ||
self.x_coords: List[float] = [] # TODO: What are the units here, I'm assuming meters | ||
self.y_coords: List[float] = [] # TODO: Same question |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, modified this class just a bit. Now the constructor simply instantiates the lists. This will allow for us to create any variety of setup methods later on that can flexibly build out borehole fields, and the constructor won't need to change.
tests/test_api.py
Outdated
def run_test(xy): | ||
# compute g-functions | ||
bh_field_params = BoreholeFieldParameters() | ||
bh_field_params.initialize_borehole_field_generic(xy, self.height, self.depth, self.bh_radius) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the real change, just moving the arguments out of the constructor to free us up to make changes later without breaking constructor calls.
tests/test_api.py
Outdated
bh_field_params = BoreholeFieldParameters() | ||
bh_field_params.initialize_borehole_field_generic( | ||
xy_coords, self.height, self.depth, self.bh_radius, tilt_angle=20, orientation_angle=20 | ||
) | ||
g = GFunctionGenerator(bh_field_params, self.alpha, self.time, solver_method="detailed") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Myoldmopar should we just mash these into a single class? Surprisingly, I don't see a borehole field class in pygfunction. Maybe we just make this all a single basic class BoreholeField
that does all this? It might be a bit more self-contained, and possibly a cleaner implementation.
bh_field = BoreholeField()
bh_field.init_generic_field(args...)
bh_field.generate_g_functions(args...)
pygfunction/api.py
Outdated
self.gfunc = gFunction( | ||
self.as_config(), | ||
alpha, | ||
time=time, | ||
boundary_condition=boundary_condition, | ||
options=options, | ||
method=solver_method, | ||
) | ||
|
||
return self.gfunc.gFunc | ||
|
||
def to_list(self): | ||
"""returns a _list_ of g-function values""" | ||
return self.gfunc.gFunc.tolist() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Myoldmopar OK, I have combined the classes. Maybe one final (for now) question, is regarding the return types for each of these.
get_g_functions
returns a numpy array; to_list
converts that array to a list. Do these seem OK for now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it were my personal preference alone, I would say we just have get_g_functions return the plain list of floats and leave numpy out of it. It's easy enough for an API client to create a numpy array of it if they want it. It may be a slight inconvenience to a few people, but it's going to be completely insignificant in runtime and keeps up operating on plain python as much as possible. And then we don't need this to_list function anymore.
If we don't want to go that far, then I might still be inclined to remove this as_list function and just return a numpy array from get_g_functions. The client can still do whatever they like with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense, hence my apprehension. I'll update that.
@@ -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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- The
gFunction
class now acceptsBorefield
objects. That did not require changes in thegFunction
class (outside ofisinstance
checks) since the new class behaves like a list of boreholes for all operations done in pygfunction. - The
Borefield.evaluate_g_function
class method returns an array of g-function values. This was to align with the currentgFunction.evaluate_g_function
method. - I moved borefield creation functions (e.g.
boreholes.rectangle_field
) to theBorefield
class. They needed to be re-implemented to generate arrays of geometrical parameters instead of lists of boreholes. The functions in theboreholes
module now have a deprecation warning. - 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.
This is an example showing how to pass purely data (floats) to PyGFunction and have it return a gfunction in list form.
There are lots of commented lines in this first draft, while we explore various techniques for achieving a simpler use of PyGFunction in GHED.