-
Notifications
You must be signed in to change notification settings - Fork 143
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
[DO NOT MERGE] Feature/fieldset as uxarray obj #1934
base: v4-dev
Are you sure you want to change the base?
Conversation
This is needed to provide (at least) a `_check_complete` call when passing a uxarray dataset to a particleset during initialization
Add draft of an eval method for the UxFieldSet class. Basic interpolation schemes are provided for node registered and face registered fields.
This commit contains a number of "to do" items marked in comments, with more incoming soon. A draft PR will be opened, FOR COMPARISON PURPOSES ONLY, so that we can assess changes required for supporting uxarray
for more information, see https://pre-commit.ci
…cels/Parcels into feature/fieldset-as-uxarray-obj
@@ -14,6 +16,15 @@ | |||
] | |||
|
|||
|
|||
def UxAdvectionEuler(particle, fieldset: UXFieldSet, time): |
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.
We'll need to have advection routines for UXArray that are distinct from XArray, unless we are able to get uniformity in the velocity field naming conventions between structured and unstructured data sets.
g._load_chunk = np.where( | ||
g._load_chunk == g._chunk_loaded_touched, g._chunk_deprecated, g._load_chunk | ||
) | ||
# if pset.fieldset is not None: |
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 didn't seem to be needing to execute for the UXArray data, so I've just commented this out. Not sure what the plans are here, given that I recall us discussing removing chunking and deferred loading.
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 gone in some of the recent updates
@@ -148,7 +149,10 @@ def ArrayClass_init(self, *args, **kwargs): | |||
pid_orig = np.arange(lon.size) | |||
|
|||
if depth is None: | |||
mindepth = self.fieldset.gridset.dimrange("depth")[0] | |||
if type(self.fieldset) == UXFieldSet: | |||
mindepth = 0 # TO DO : get the min depth from the fieldset.uxgrid |
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 suppose we'll need a new implementation for fieldset.gridset.dimrange
. Since the new proposed fieldset is meant to wrap around either an XArray or UXArray dataset, which each have a grid baked into the structure, the gridset
subclass here is irrelevant.
lonlatdepth_dtype = np.float32 # To do : get precision from fieldset | ||
else: | ||
if isinstance(fieldset.U, Field) and (not fieldset.U.allow_time_extrapolation): | ||
_warn_particle_times_outside_fieldset_time_bounds(time, fieldset.U.grid.time_full) |
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 suppose we'll need an updated implementation of _warn_particle_times_outside_fieldset_time_bounds
.. Alternatively, we'll need to replace the fieldset.U.grid.time_full
with a fieldset.time_full
call.
Note that I'm assuming that a UXarray or XArray dataset have a single time dimension for all fields in the dataset. Getting the time extents for the dataset wouldn't depend on the individual fields (e.g. U
or V
); rather it'd be associated with the dimensions of the dataset.
if lonlatdepth_dtype is None: | ||
lonlatdepth_dtype = self.lonlatdepth_dtype_from_field_interp_method(fieldset.U) | ||
if type(fieldset) == UXFieldSet: | ||
lonlatdepth_dtype = np.float32 # To do : get precision from fieldset |
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.
An obvious to-do item here; we need to be able to get the precision from the XArray or UXArray dataset underneath the thin fieldset wrapper.
@@ -191,7 +200,10 @@ def ArrayClass_init(self, *args, **kwargs): | |||
self._repeatkwargs = kwargs | |||
self._repeatkwargs.pop("partition_function", None) | |||
|
|||
ngrids = fieldset.gridset.size | |||
if type(fieldset) == UXFieldSet: | |||
ngrids = 1 |
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.
Here, we explicitly set ngrids to 1. However, with the idea that the fieldset
will store a list of either XArray or UXArray datasets, we will want to set ngrids to the length of the list of datasets.
if type(fieldset) == UXFieldSet: | ||
ngrids = 1 | ||
else: | ||
ngrids = fieldset.gridset.size |
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.
gridset
attribute will be going away with the updated fieldset.
@@ -967,7 +979,10 @@ def execute( | |||
if runtime is not None and endtime is not None: | |||
raise RuntimeError("Only one of (endtime, runtime) can be specified") | |||
|
|||
mintime, maxtime = self.fieldset.gridset.dimrange("time_full") | |||
if type(self.fieldset) == UXFieldSet: | |||
mintime, maxtime = self.fieldset.get_time_range() |
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.
fieldset.get_time_range
is meant to replace fieldset.gridset.dimrange("time_full")
@@ -1037,18 +1052,19 @@ def execute( | |||
|
|||
time_at_startofloop = time | |||
|
|||
next_input = self.fieldset.computeTimeChunk(time, dt) | |||
#next_input = self.fieldset.computeTimeChunk(time, dt) |
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.
Some work is going to be needed to get this implemented. Though I suspect some work is being done in this area currently in the v4-dev. Would be best to discuss together.
|
||
next_time = endtime |
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 was hard-coded, just to get things working for the simple case with one time level in the UXArray dataset.
"""A FieldSet class that holds hydrodynamic data needed to execute particles | ||
in a UXArray.Dataset""" | ||
# Change uxds to ds_list - which is a list of either uxDataset or xarray dataset | ||
def __init__(self, uxds: ux.UxDataset, time_origin: float | np.datetime64 | np.timedelta64 | cftime.datetime = 0): |
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.
The uxds
argument will be changed to ds
(standing for "DataSets"), which will be a List(uxarray.UxDataset | xarray.Dataset)
assert self.uxds.v is not None, "UXFieldSet does not provide v velocity data" | ||
assert self.uxds.uxgrid is not None, "UXFieldSet does not provide a grid" | ||
|
||
def _face_interp(self, field, time, z, y, x, ei): |
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.
For now, hard-coded interpolation schemes are provided for face registered data and node registered data.
We may want to discuss with the Geomar folks if this dichotomy is intuitive for them or if they have a different way of viewing the interpolation problem.
if not self._point_is_in_face(y,x,ei): | ||
# If the point is not in the previously defined face, then | ||
# search for the face again. | ||
# To do : Update the search here to do nearest neighbors search, rather than spatial hash - [email protected] |
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.
For UXArray data, we ought to add a method that does a nearest neighbors search, rather than a hash query when the particle is not found in the ei
face.
I suppose this code here, and the proposed code, ought to placed in a _search_uxindices
method
r = self._face_interp(field, time, z, y, x, fi) | ||
else: | ||
r = self._node_interp(field, time, z, y, x, fi) | ||
|
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.
For 3-D interpolation, we need to add vertical interpolation. Currently, all interpolation uses the fields at the upper most vertical layer
# If the point is not in the previously defined face, then | ||
# search for the face again. | ||
# To do : Update the search here to do nearest neighbors search, rather than spatial hash - [email protected] | ||
print(f"Position : {x}, {y}") |
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.
print statements can probable come out now.. these were needed for debugging..
vim1 = nodes[i - 1] | ||
vi = nodes[i] | ||
vi1 = nodes[(i + 1) % n] | ||
a0 = _triangle_area(vim1, vi, vi1) |
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.
These areas need to be clipped with a minimum value. As we found in uxarray, when a particle lies on a face edge or vertex, these areas can be zero, leading to division by zero.
def UxAdvectionEuler(particle, fieldset: UXFieldSet, time): | ||
"""Advection of particles using Explicit Euler (aka Euler Forward) integration. | ||
on an unstructured grid.""" | ||
vel, ei = fieldset.eval(["u","v"],time,particle.depth,particle.lat,particle.lon, particle.ei[0]) |
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.
Push particle.ei to eval and select which igrid.
Also, push the xarray/uxarray "wrapper" down to the field
class
main
for v3 changes,v4-dev
for v4 changes)