Here is the proposed API for Field data ingestion into Parcels.
classDiagram
class Fieldset{
+List[Field] fields
}
class Field{
+xr.Dataset|xr.DataArray|ux.Dataset|ux.DataArray data
+parcels.Grid|ux.Grid grid
+Interpolator interpolator
+eval(t, z, y, x, particle=None, applyConversion=True)
}
class Interpolator{
+assert_is_compatible(Field)
+interpolate(Field, rid, t, z, y, x )
}
<<Protocol>> Interpolator
Fieldset ..> Field : depends on
Field ..> Interpolator : depends on
Interpolator <|.. ScalarInterpolator : Realization of
Interpolator <|.. VectorInterpolator : Realization of
Interpolator <|.. etc : Realization of
Here, important things to note are:
-
Interpolators (which would implement the
Interpolatorprotocol) are responsible for the actual interpolation of the data, and performance considerations. There will be interpolation and indexing utilities that can be made available to the interpolators, allowing for code re-use.- Interpolators of the data should handle spatial periodicity and, for the case of rectilinear structured grids, without pre-computing a halo for the FieldSet and Grid (issue).
-
In the
Fieldclass, not all combinations ofdata,grid, andinterpolatorwill logically make sense (e.g., axr.DataArrayon aux.Grid, orux.DataArrayon aparcels.Grid). It's up to theInterpolator.assert_is_compatible(Field)to define what is and is not compatible, and raiseValueError/TypeErroron incompatible data types. The.assert_is_compatible()method also acts as developer documentation, defining clearly for the.interpolate()method what assumptions it is working on. The.assert_is_compatible()method should be lightweight as it will be called onFieldinitialisation. -
The
gridobject, in the case of unstructured grids, will be theGridclass from UXarray. For structuredGrids, it will be an object similar to that ofxgcm.Grid(note that it will be very different from the v3Gridobject hierarchy). -
The
Field.evalmethod takes as input the t,z,y,x spatio-temporal position as required arguments; theparticleis optional and defaults toNoneand theapplyConversionargument is optional and defaults toTrue. Initially, we will calculate the element index for a particle. As a future optimization, we could pass via theparticleobject a "cached" index value that could be used to bypass an index search. This will effectively provide(ti,zi,yi,xi)on a structured grid and(ti,zi,fi)on an unstructured grid (wherefiis the lateral face id); withinevalthese indices will beravel'ed to a single index that can beunravel'ed in theinterpolatemethod. Theravel'ed index is referred to asridin theField.Interpolator.interpolatemethod. In theinterpolatemethod, we envision that a user will benefit from knowing the nearest cell/index from theravel'ed index (which can beunravel'ed) in addition the exact coordinate that we want to interpolate onto. This can permit calculation of interpolation weights using points in the neighborhood of(t,z,y,x).
Below a list of changes in the API that are relevant to users:
-
starttime,endtimeanddtinParticleSet.execute()are nownumpy.timedelta64ornumpy.datetime64objects. This allows for more precise time handling and is consistent with thenumpytime handling. -
pid_originParticleSetis removed. Instead,trajectory_idsis used to provide a list of "trajectory" values (integers) for the particle IDs.