@@ -129,6 +129,53 @@ def get_axis_dim(self, axis: _XGRID_AXES) -> int:
129129
130130 return get_cell_count_along_dim (self .xgcm_grid .axes [axis ])
131131
132+ def localize (self , position : dict [_XGRID_AXES , tuple [int , float ]], dims : list [str ]) -> dict [str , tuple [int , float ]]:
133+ """
134+ Uses the grid context (i.e., the staggering of the grid) to convert a position relative
135+ to the F-points in the grid to a position relative to the staggered grid the array
136+ of interest is defined on.
137+
138+ Uses dimensions of the DataArray to determine the staggered grid.
139+
140+ WARNING: This API is unstable and subject to change in future versions.
141+
142+ Parameters
143+ ----------
144+ position : dict
145+ A mapping of the axis to a tuple of (index, barycentric coordinate) for the
146+ F-points in the grid.
147+ dims : list[str]
148+ A list of dimension names that the DataArray is defined on. This is used to determine
149+ the staggering of the grid and which axis each dimension corresponds to.
150+
151+ Returns
152+ -------
153+ dict[str, tuple[int, float]]
154+ A mapping of the dimension names to a tuple of (index, barycentric coordinate) for
155+ the staggered grid the DataArray is defined on.
156+
157+ Example
158+ -------
159+ >>> position = {'X': (5, 0.51), 'Y': (
160+ 10, 0.25), 'Z': (3, 0.75)}
161+ >>> dims = ['time', 'depth', 'YC', 'XC']
162+ >>> grid.localize(position, dims)
163+ {'depth': (3, 0.75), 'YC': (9, 0.75), 'XC': (5, 0.01)}
164+ """
165+ axis_to_var = {get_axis_from_dim_name (self .xgcm_grid .axes , dim ): dim for dim in dims }
166+ var_positions = {
167+ axis : get_xgcm_position_from_dim_name (self .xgcm_grid .axes , dim ) for axis , dim in axis_to_var .items ()
168+ }
169+ return {
170+ axis_to_var [axis ]: _convert_center_pos_to_fpoint (
171+ index = index ,
172+ bcoord = bcoord ,
173+ xgcm_position = var_positions [axis ],
174+ f_points_xgcm_position = self ._fpoint_info [axis ],
175+ )
176+ for axis , (index , bcoord ) in position .items ()
177+ }
178+
132179 @property
133180 def _z4d (self ) -> Literal [0 , 1 ]:
134181 """
@@ -185,6 +232,20 @@ def search(self, z, y, x, ei=None):
185232
186233 raise NotImplementedError ("Searching in >2D lon/lat arrays is not implemented yet." )
187234
235+ @cached_property
236+ def _fpoint_info (self ):
237+ """Returns a mapping of the spatial axes in the Grid to their XGCM positions."""
238+ xgcm_axes = self .xgcm_grid .axes
239+ f_point_positions = ["left" , "right" , "inner" , "outer" ]
240+ axis_position_mapping = {}
241+ for axis in self .axes :
242+ coords = xgcm_axes [axis ].coords
243+ edge_positions = [pos for pos in coords .keys () if pos in f_point_positions ]
244+ assert len (edge_positions ) == 1 , f"Axis { axis } has multiple edge positions: { edge_positions } "
245+ axis_position_mapping [axis ] = edge_positions [0 ]
246+
247+ return axis_position_mapping
248+
188249
189250def get_axis_from_dim_name (axes : _XGCM_AXES , dim : str ) -> _XGCM_AXIS_DIRECTION | None :
190251 """For a given dimension name in a grid, returns the direction axis it is on."""
@@ -337,3 +398,28 @@ def _search_1d_array(
337398 i = np .argmin (arr <= x ) - 1
338399 bcoord = (x - arr [i ]) / (arr [i + 1 ] - arr [i ])
339400 return i , bcoord
401+
402+
403+ def _convert_center_pos_to_fpoint (
404+ * , index : int , bcoord : float , xgcm_position : _XGCM_AXIS_POSITION , f_points_xgcm_position : _XGCM_AXIS_POSITION
405+ ) -> tuple [int , float ]:
406+ """Converts a physical position relative to the cell edges defined in the grid to be relative to the center point.
407+
408+ This is used to "localize" a position to be relative to the staggered grid at which the field is defined, so that
409+ it can be easily interpolated.
410+
411+ This also handles different model input cell edges and centers are staggered in different directions (e.g., with NEMO and MITgcm).
412+ """
413+ if xgcm_position != "center" : # Data is already defined on the F points
414+ return index , bcoord
415+
416+ bcoord = bcoord - 0.5
417+ if bcoord < 0 :
418+ bcoord += 1.0
419+ index -= 1
420+
421+ # Correct relative to the f-point position
422+ if f_points_xgcm_position in ["inner" , "right" ]:
423+ index += 1
424+
425+ return index , bcoord
0 commit comments