You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parcels Grid: The logical grid representation (via XGrid or UxGrid) used by Parcels to determine particle position. In structured grids, this corresponds to the grid of F-points (see Structured grids barycentric coordinates #2037). Time is not part of the grid abstraction.
Axis (Axes): The spatial dimensions of the Parcels Grid. For structured grids: X, Y, Z; for unstructured: Z and FACE. These relate closely to the underlying data layout (e.g., in the structured case, only a small correction about left/right grid indexing is required to get to the underlying data representation). Indexing a particle involves locating it along each axis.
Index (Indices): The cell position of a particle along a given axis. Combined indices define the full grid cell a particle occupies.
Barycentric Coordinates: The coordinates defining a particle’s position within a grid cell. In structured grids, this is a single coordinate per axis (e.g., xsi); in unstructured, this can be a mix (e.g., a single coordinate for zeta, and n-coordinates for an n-gon that the particle resides in - 3 for triangular cells).
Encoded Indices (ei): A compact, single-integer encoding of a particle’s full grid indices.
Locating a particle’s cell and barycentric coordinates within a Parcels Grid is essential for interpolation. This functionality should reside in the Grid class, exposing a clear API that the Field class can use for interpolation—ensuring separation of concerns, so Field need not manage grid geometry.
In the structured case, Parcels must support grids with varying axis dimensionality:
3D:
Options: 2D lat + 2D lon + 1D depth (curvilinear), or 1D lat + 1D lon + 1D depth (rectilinear)
Use case: 3D U/V fields
2D:
Options: 2D lat + 2D lon, or 1D lat/lon + 1D depth
Use case: Wave flumes, windage
1D: Single 1D axis (lat/lon/depth)
0D: Constant value everywhere (constant field)
Data may exist on any subset of the grid axes (e.g., U may be on X, Y, Z; depth-averaged U on X, Y). During interpolation, the particle’s full position is used, but only relevant axis indices (e.g., xi, yi) are needed to access the data.
# XGrid classdefunravel_index(self, ei):
""" Converts a single encoded index back into a Z, Y, and X indices. Parameters ---------- ei : int Encoded index to be unraveled. Returns ------- zi : int Vertical index. yi : int Y index. xi : int X index. """defravel_index(self, zi, yi, xi):
... # inverse of unravel_index# UxGrid classdefunravel_index(self, ei):
""" Converts a single encoded index back into a vertical index and face index. Parameters ---------- ei : int Encoded index to be unraveled. Returns ------- zi : int Vertical index. fi : int Face index. """defravel_index(self, zi, fi):
... # inverse of unravel_index# applies to both XGrid and UxGriddefsearch(self, z: float, y: float, x: float, ei=None, search2D: bool=False):
""" Perform a spatial (and optionally vertical) search to locate the grid element that contains a given point (x, y, z). This method delegates to grid-type-specific logic (e.g., structured or unstructured) to determine the appropriate indices and interpolation coordinates for evaluating a field. Parameters ---------- z : float Vertical coordinate of the query point. If `search2D=True`, this may be ignored. y : float Latitude or vertical index, depending on grid type and projection. x : float Longitude or horizontal index, depending on grid type and projection. ei : int, optional A previously computed encoded index (e.g., raveled face or cell index). If provided, the search will first attempt to validate and reuse it before falling back to a global or local search strategy. search2D : bool, default=False If True, perform only a 2D search (x, y), ignoring the vertical component z. Returns ------- bcoords : np.ndarray or tuple Interpolation weights or barycentric coordinates within the containing cell/face. The interpretation of `bcoords` depends on the grid type. ei : int Encoded index of the identified grid cell or face. This value can be cached for future lookups to accelerate repeated searches. Raises ------ FieldOutOfBoundError Raised when the queried point lies outside the bounds of the grid. NotImplementedError Raised if the search method is not implemented for the current grid type. """
Benefits of this approach
Clarifies the return values of .search() and .ravel_index() based on the Grid’s Axes.
For 2D grids, only relevant axes are returned - e.g., no need to return zi, zeta = (0, 0). This simplifies interpolator logic and removes the need for grid dimensionality checks in the interpolator.
Unifies the Grid API, stabilizing BaseGrid behavior across subclasses (XGrid, UxGrid) and enabling future extensions.
Clarifies the link between Axes and barycentric coordinates.
Provides full particle position on the grid via the new API (including the zeta value).
Improves maintainability, as all grid logic stays within the Grid class.
Additional considerations
Caching ei may be unnecessary for 0D or 1D grids, where lookups are already fast.
Unclear role of search2D parameter. Is this legacy or is it still needed?
Sigma layer support: Future users must provide F-points for depth (i.e., a 3D depth array based on X and Y via bathymetry). This would require users to broadcast the sigma ratio with their bathymetry before passing to Parcels (which I think is a good direction for us to go in when we get around to it).
Particles still live in z,y,x space (just this might not be relevant when indexing on some grids). For surface advection we can have z=0
Keen to hear your thoughts (on concept, implementation or terminology) here.
Footnotes
Once finalised, I think we would benefit from documenting this terminology (internally and in documentation for those interested in writing interpolators). ↩
Terminology1:
Locating a particle’s cell and barycentric coordinates within a Parcels Grid is essential for interpolation. This functionality should reside in the Grid class, exposing a clear API that the Field class can use for interpolation—ensuring separation of concerns, so Field need not manage grid geometry.
In the structured case, Parcels must support grids with varying axis dimensionality:
Data may exist on any subset of the grid axes (e.g., U may be on X, Y, Z; depth-averaged U on X, Y). During interpolation, the particle’s full position is used, but only relevant axis indices (e.g., xi, yi) are needed to access the data.
Proposed API
.search(z, y, x)ravel_index(axis_indices)Old API for reference
Old approach
Benefits of this approach
Additional considerations
Keen to hear your thoughts (on concept, implementation or terminology) here.
Footnotes
Once finalised, I think we would benefit from documenting this terminology (internally and in documentation for those interested in writing interpolators). ↩