Skip to content

Commit 1fecaa8

Browse files
Merge pull request #923 from OceanParcels/mom5_gridindexing
Implementing gridindexing for MOM5 B-grids
2 parents ca772d6 + f315aa3 commit 1fecaa8

8 files changed

Lines changed: 305 additions & 28 deletions

File tree

parcels/field.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,11 @@ def search_indices_vertical_z(self, z):
570570
z = np.float32(z)
571571
if grid.depth[-1] > grid.depth[0]:
572572
if z < grid.depth[0]:
573-
raise FieldOutOfBoundSurfaceError(0, 0, z, field=self)
573+
# Since MOM5 is indexed at cell bottom, allow z at depth[0] - dz where dz = (depth[1] - depth[0])
574+
if self.gridindexingtype == "mom5" and z > 2*grid.depth[0] - grid.depth[1]:
575+
return (-1, z / grid.depth[0])
576+
else:
577+
raise FieldOutOfBoundSurfaceError(0, 0, z, field=self)
574578
elif z > grid.depth[-1]:
575579
raise FieldOutOfBoundError(0, 0, z, field=self)
576580
depth_indices = grid.depth <= z
@@ -932,7 +936,10 @@ def interpolator3D(self, ti, z, y, x, time):
932936
return (1 - zeta) * f0 + zeta * f1
933937
elif self.interp_method in ['linear', 'bgrid_velocity', 'bgrid_w_velocity']:
934938
if self.interp_method == 'bgrid_velocity':
935-
zeta = 0.
939+
if self.gridindexingtype == 'mom5':
940+
zeta = 1.
941+
else:
942+
zeta = 0.
936943
elif self.interp_method == 'bgrid_w_velocity':
937944
eta = 1.
938945
xsi = 1.
@@ -946,7 +953,11 @@ def interpolator3D(self, ti, z, y, x, time):
946953
xsi*(1-eta) * data[yi, xi+1] + \
947954
xsi*eta * data[yi+1, xi+1] + \
948955
(1-xsi)*eta * data[yi+1, xi]
949-
return (1-zeta) * f0 + zeta * f1
956+
if self.interp_method == 'bgrid_w_velocity' and self.gridindexingtype == 'mom5' and zi == -1:
957+
# Since MOM5 is indexed at cell bottom, allow linear interpolation of W to zero in upper cell)
958+
return zeta * f1
959+
else:
960+
return (1-zeta) * f0 + zeta * f1
950961
elif self.interp_method in ['cgrid_tracer', 'bgrid_tracer']:
951962
return self.data[ti, zi, yi+1, xi+1]
952963
else:

parcels/fieldset.py

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ def check_velocityfields(U, V, W):
234234
if U.grid.xdim == 1 or U.grid.ydim == 1 or V.grid.xdim == 1 or V.grid.ydim == 1:
235235
raise NotImplementedError('C-grid velocities require longitude and latitude dimensions at least length 2')
236236

237-
if U.gridindexingtype not in ['nemo', 'mitgcm']:
238-
raise ValueError("Field.gridindexing has to be one of 'nemo' or 'mitgcm'")
237+
if U.gridindexingtype not in ['nemo', 'mitgcm', 'mom5']:
238+
raise ValueError("Field.gridindexing has to be one of 'nemo', 'mitgcm' or 'mom5")
239239

240240
if U.gridindexingtype == 'mitgcm' and U.grid.gtype in [GridCode.CurvilinearZGrid, GridCode.CurvilinearZGrid]:
241241
raise NotImplementedError('Curvilinear Grids are not implemented for mitgcm-style grid indexing.'
@@ -680,6 +680,72 @@ def from_pop(cls, filenames, variables, dimensions, indices=None, mesh='spherica
680680
raise SyntaxError("'depth_units' has to be 'm' or 'cm'")
681681
return fieldset
682682

683+
@classmethod
684+
def from_mom5(cls, filenames, variables, dimensions, indices=None, mesh='spherical',
685+
allow_time_extrapolation=None, time_periodic=False,
686+
tracer_interp_method='bgrid_tracer', field_chunksize='auto', **kwargs):
687+
"""Initialises FieldSet object from NetCDF files of MOM5 fields.
688+
689+
:param filenames: Dictionary mapping variables to file(s). The
690+
filepath may contain wildcards to indicate multiple files,
691+
or be a list of file.
692+
filenames can be a list [files], a dictionary {var:[files]},
693+
a dictionary {dim:[files]} (if lon, lat, depth and/or data not stored in same files as data),
694+
or a dictionary of dictionaries {var:{dim:[files]}}
695+
time values are in filenames[data]
696+
:param variables: Dictionary mapping variables to variable names in the netCDF file(s).
697+
Note that the built-in Advection kernels assume that U and V are in m/s
698+
:param dimensions: Dictionary mapping data dimensions (lon,
699+
lat, depth, time, data) to dimensions in the netCF file(s).
700+
Note that dimensions can also be a dictionary of dictionaries if
701+
dimension names are different for each variable.
702+
U[k,j+1,i],V[k,j+1,i] ____________________U[k,j+1,i+1],V[k,j+1,i+1]
703+
| |
704+
| W[k-1:k+1,j+1,i+1],T[k,j+1,i+1] |
705+
| |
706+
U[k,j,i],V[k,j,i] ________________________U[k,j,i+1],V[k,j,i+1]
707+
In 2D: U and V nodes are on the cell vertices and interpolated bilinearly as a A-grid.
708+
T node is at the cell centre and interpolated constant per cell as a C-grid.
709+
In 3D: U and V nodes are at the midlle of the cell vertical edges,
710+
They are interpolated bilinearly (independently of z) in the cell.
711+
W nodes are at the centre of the horizontal interfaces, but below the U and V.
712+
They are interpolated linearly (as a function of z) in the cell.
713+
Note that W is normally directed upward in MOM5, but Parcels requires W
714+
in the positive z-direction (downward) so W is multiplied by -1.
715+
T node is at the cell centre, and constant per cell.
716+
:param indices: Optional dictionary of indices for each dimension
717+
to read from file(s), to allow for reading of subset of data.
718+
Default is to read the full extent of each dimension.
719+
Note that negative indices are not allowed.
720+
:param fieldtype: Optional dictionary mapping fields to fieldtypes to be used for UnitConverter.
721+
(either 'U', 'V', 'Kh_zonal', 'Kh_meridional' or None)
722+
:param mesh: String indicating the type of mesh coordinates and
723+
units used during velocity interpolation, see also https://nbviewer.jupyter.org/github/OceanParcels/parcels/blob/master/parcels/examples/tutorial_unitconverters.ipynb:
724+
725+
1. spherical (default): Lat and lon in degree, with a
726+
correction for zonal velocity U near the poles.
727+
2. flat: No conversion, lat/lon are assumed to be in m.
728+
:param allow_time_extrapolation: boolean whether to allow for extrapolation
729+
(i.e. beyond the last available time snapshot)
730+
Default is False if dimensions includes time, else True
731+
:param time_periodic: To loop periodically over the time component of the Field. It is set to either False or the length of the period (either float in seconds or datetime.timedelta object). (Default: False)
732+
This flag overrides the allow_time_interpolation and sets it to False
733+
:param tracer_interp_method: Method for interpolation of tracer fields. It is recommended to use 'bgrid_tracer' (default)
734+
Note that in the case of from_mom5() and from_bgrid(), the velocity fields are default to 'bgrid_velocity'
735+
:param field_chunksize: size of the chunks in dask loading
736+
737+
738+
"""
739+
740+
if 'creation_log' not in kwargs.keys():
741+
kwargs['creation_log'] = 'from_mom5'
742+
fieldset = cls.from_b_grid_dataset(filenames, variables, dimensions, mesh=mesh, indices=indices, time_periodic=time_periodic,
743+
allow_time_extrapolation=allow_time_extrapolation, tracer_interp_method=tracer_interp_method,
744+
field_chunksize=field_chunksize, gridindexingtype='mom5', **kwargs)
745+
if hasattr(fieldset, 'W'):
746+
fieldset.W.set_scaling_factor(-1)
747+
return fieldset
748+
683749
@classmethod
684750
def from_b_grid_dataset(cls, filenames, variables, dimensions, indices=None, mesh='spherical',
685751
allow_time_extrapolation=None, time_periodic=False,

parcels/include/index_search.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ typedef enum
2323

2424
typedef enum
2525
{
26-
NEMO = 0, MITGCM = 1
26+
NEMO = 0, MITGCM = 1, MOM5 = 2
2727
} GridIndexingType;
2828

2929
typedef struct
@@ -56,9 +56,14 @@ typedef enum
5656
RECTILINEAR_Z_GRID=0, RECTILINEAR_S_GRID=1, CURVILINEAR_Z_GRID=2, CURVILINEAR_S_GRID=3
5757
} GridCode;
5858

59-
static inline StatusCode search_indices_vertical_z(type_coord z, int zdim, float *zvals, int *zi, double *zeta)
59+
static inline StatusCode search_indices_vertical_z(type_coord z, int zdim, float *zvals, int *zi, double *zeta, int gridindexingtype)
6060
{
6161
if (zvals[zdim-1] > zvals[0]){
62+
if ((z < zvals[0]) && (gridindexingtype == MOM5) && (z > 2 * zvals[0] - zvals[1])){
63+
*zi = -1;
64+
*zeta = z / zvals[0];
65+
return SUCCESS;
66+
}
6267
if (z < zvals[0]) {return ERROR_THROUGH_SURFACE;}
6368
if (z > zvals[zdim-1]) {return ERROR_OUT_OF_BOUNDS;}
6469
while (*zi < zdim-1 && z > zvals[*zi+1]) ++(*zi);
@@ -162,7 +167,8 @@ static inline void reconnect_bnd_indices(int *xi, int *yi, int xdim, int ydim, i
162167

163168
static inline StatusCode search_indices_rectilinear(type_coord x, type_coord y, type_coord z, CStructuredGrid *grid, GridCode gcode,
164169
int *xi, int *yi, int *zi, double *xsi, double *eta, double *zeta,
165-
int ti, double time, double t0, double t1, int interp_method)
170+
int ti, double time, double t0, double t1, int interp_method,
171+
int gridindexingtype)
166172
{
167173
int xdim = grid->xdim;
168174
int ydim = grid->ydim;
@@ -239,7 +245,7 @@ static inline StatusCode search_indices_rectilinear(type_coord x, type_coord y,
239245
if (zdim > 1){
240246
switch(gcode){
241247
case RECTILINEAR_Z_GRID:
242-
status = search_indices_vertical_z(z, zdim, zvals, zi, zeta);
248+
status = search_indices_vertical_z(z, zdim, zvals, zi, zeta, gridindexingtype);
243249
break;
244250
case RECTILINEAR_S_GRID:
245251
status = search_indices_vertical_s(z, xdim, ydim, zdim, zvals,
@@ -264,7 +270,8 @@ static inline StatusCode search_indices_rectilinear(type_coord x, type_coord y,
264270

265271
static inline StatusCode search_indices_curvilinear(type_coord x, type_coord y, type_coord z, CStructuredGrid *grid, GridCode gcode,
266272
int *xi, int *yi, int *zi, double *xsi, double *eta, double *zeta,
267-
int ti, double time, double t0, double t1, int interp_method)
273+
int ti, double time, double t0, double t1, int interp_method,
274+
int gridindexingtype)
268275
{
269276
int xi_old = *xi;
270277
int yi_old = *yi;
@@ -375,7 +382,7 @@ static inline StatusCode search_indices_curvilinear(type_coord x, type_coord y,
375382
if (zdim > 1){
376383
switch(gcode){
377384
case CURVILINEAR_Z_GRID:
378-
status = search_indices_vertical_z(z, zdim, zvals, zi, zeta);
385+
status = search_indices_vertical_z(z, zdim, zvals, zi, zeta, gridindexingtype);
379386
break;
380387
case CURVILINEAR_S_GRID:
381388
status = search_indices_vertical_s(z, xdim, ydim, zdim, zvals,
@@ -402,18 +409,19 @@ static inline StatusCode search_indices_curvilinear(type_coord x, type_coord y,
402409
* */
403410
static inline StatusCode search_indices(type_coord x, type_coord y, type_coord z, CStructuredGrid *grid,
404411
int *xi, int *yi, int *zi, double *xsi, double *eta, double *zeta,
405-
GridCode gcode, int ti, double time, double t0, double t1, int interp_method)
412+
GridCode gcode, int ti, double time, double t0, double t1, int interp_method,
413+
int gridindexingtype)
406414
{
407415
switch(gcode){
408416
case RECTILINEAR_Z_GRID:
409417
case RECTILINEAR_S_GRID:
410418
return search_indices_rectilinear(x, y, z, grid, gcode, xi, yi, zi, xsi, eta, zeta,
411-
ti, time, t0, t1, interp_method);
419+
ti, time, t0, t1, interp_method, gridindexingtype);
412420
break;
413421
case CURVILINEAR_Z_GRID:
414422
case CURVILINEAR_S_GRID:
415423
return search_indices_curvilinear(x, y, z, grid, gcode, xi, yi, zi, xsi, eta, zeta,
416-
ti, time, t0, t1, interp_method);
424+
ti, time, t0, t1, interp_method, gridindexingtype);
417425
break;
418426
default:
419427
printf("Only RECTILINEAR_Z_GRID, RECTILINEAR_S_GRID, CURVILINEAR_Z_GRID and CURVILINEAR_S_GRID grids are currently implemented\n");

parcels/include/parcels.h

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,19 @@ static inline StatusCode spatial_interpolation_trilinear(double xsi, double eta,
147147
return SUCCESS;
148148
}
149149

150+
/* Trilinear interpolation routine for MOM surface 3D grid */
151+
static inline StatusCode spatial_interpolation_trilinear_surface(double xsi, double eta, double zeta,
152+
float data[2][2][2], float *value)
153+
{
154+
float f1;
155+
f1 = (1-xsi)*(1-eta) * data[0][0][0]
156+
+ xsi *(1-eta) * data[0][0][1]
157+
+ xsi * eta * data[0][1][1]
158+
+ (1-xsi)* eta * data[0][1][0];
159+
*value = zeta * f1;
160+
return SUCCESS;
161+
}
162+
150163
/* Trilinear interpolation routine for 3D grid for tracers with squared inverse distance weighting near land*/
151164
static inline StatusCode spatial_interpolation_trilinear_invdist_land(double xsi, double eta, double zeta, float data[2][2][2], float *value)
152165
{
@@ -475,15 +488,19 @@ static inline StatusCode temporal_interpolation_structured_grid(type_coord x, ty
475488

476489
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid],
477490
&xsi, &eta, &zeta, gcode, ti[igrid],
478-
tsrch, t0, t1, interp_method);
491+
tsrch, t0, t1, interp_method, gridindexingtype);
479492
CHECKSTATUS(status);
480493

481494
if (grid->zdim == 1) {
482495
// last param is a flag, which denotes that we only want the first timestep
483496
// (rather than both)
484497
status = getCell2D(f, xi[igrid], yi[igrid], ti[igrid], data2D, tii == 1); CHECKSTATUS(status);
485498
} else {
486-
status = getCell3D(f, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D, tii == 1); CHECKSTATUS(status);
499+
if ((gridindexingtype == MOM5) && (zi[igrid] == -1)){
500+
status = getCell3D(f, xi[igrid], yi[igrid], 0, ti[igrid], data3D, tii == 1); CHECKSTATUS(status);
501+
} else {
502+
status = getCell3D(f, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D, tii == 1); CHECKSTATUS(status);
503+
}
487504
}
488505

489506
// define a helper macro that will select the appropriate interpolation method
@@ -507,7 +524,7 @@ static inline StatusCode temporal_interpolation_structured_grid(type_coord x, ty
507524
(interp_method == BGRID_VELOCITY) || (interp_method == BGRID_W_VELOCITY)) {
508525
// adjust the normalised coordinate for flux-based interpolation methods
509526
if ((interp_method == CGRID_VELOCITY) || (interp_method == BGRID_W_VELOCITY)) {
510-
if (gridindexingtype == NEMO) {
527+
if ((gridindexingtype == NEMO) || (gridindexingtype == MOM5)) {
511528
// velocity is on the northeast of a tracer cell
512529
xsi = 1;
513530
eta = 1;
@@ -517,10 +534,17 @@ static inline StatusCode temporal_interpolation_structured_grid(type_coord x, ty
517534
eta = 0;
518535
}
519536
} else if (interp_method == BGRID_VELOCITY) {
520-
zeta = 0;
537+
if (gridindexingtype == MOM5) {
538+
zeta = 1;
539+
} else {
540+
zeta = 0;
541+
}
542+
}
543+
if ((gridindexingtype == MOM5) && (zi[igrid] == -1)){
544+
INTERP(spatial_interpolation_bilinear, spatial_interpolation_trilinear_surface);
545+
} else {
546+
INTERP(spatial_interpolation_bilinear, spatial_interpolation_trilinear);
521547
}
522-
523-
INTERP(spatial_interpolation_bilinear, spatial_interpolation_trilinear);
524548
} else if (interp_method == NEAREST) {
525549
INTERP(spatial_interpolation_nearest2D, spatial_interpolation_nearest3D);
526550
} else if ((interp_method == CGRID_TRACER) || (interp_method == BGRID_TRACER)) {
@@ -652,7 +676,7 @@ static inline StatusCode temporal_interpolationUV_c_grid(type_coord x, type_coor
652676
float u0, u1, v0, v1;
653677
double t0 = grid->time[ti[igrid]]; double t1 = grid->time[ti[igrid]+1];
654678
/* Identify grid cell to sample through local linear search */
655-
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zeta, gcode, ti[igrid], time, t0, t1, CGRID_VELOCITY); CHECKSTATUS(status);
679+
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zeta, gcode, ti[igrid], time, t0, t1, CGRID_VELOCITY, gridindexingtype); CHECKSTATUS(status);
656680
if (grid->zdim==1){
657681
float data2D_U[2][2][2], data2D_V[2][2][2];
658682
if (gridindexingtype == NEMO) {
@@ -684,7 +708,7 @@ static inline StatusCode temporal_interpolationUV_c_grid(type_coord x, type_coor
684708
return SUCCESS;
685709
} else {
686710
double t0 = grid->time[ti[igrid]];
687-
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zeta, gcode, ti[igrid], t0, t0, t0+1, CGRID_VELOCITY); CHECKSTATUS(status);
711+
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zeta, gcode, ti[igrid], t0, t0, t0+1, CGRID_VELOCITY, gridindexingtype); CHECKSTATUS(status);
688712
if (grid->zdim==1){
689713
float data2D_U[2][2][2], data2D_V[2][2][2];
690714
if (gridindexingtype == NEMO) {
@@ -873,7 +897,7 @@ static inline StatusCode temporal_interpolationUVW_c_grid(type_coord x, type_coo
873897
float u0, u1, v0, v1, w0, w1;
874898
double t0 = grid->time[ti[igrid]]; double t1 = grid->time[ti[igrid]+1];
875899
/* Identify grid cell to sample through local linear search */
876-
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zet, gcode, ti[igrid], time, t0, t1, CGRID_VELOCITY); CHECKSTATUS(status);
900+
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zet, gcode, ti[igrid], time, t0, t1, CGRID_VELOCITY, gridindexingtype); CHECKSTATUS(status);
877901
status = getCell3D(U, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D_U, 0); CHECKSTATUS(status);
878902
status = getCell3D(V, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D_V, 0); CHECKSTATUS(status);
879903
status = getCell3D(W, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D_W, 0); CHECKSTATUS(status);
@@ -889,7 +913,7 @@ static inline StatusCode temporal_interpolationUVW_c_grid(type_coord x, type_coo
889913
return SUCCESS;
890914
} else {
891915
double t0 = grid->time[ti[igrid]];
892-
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zet, gcode, ti[igrid], t0, t0, t0+1, CGRID_VELOCITY); CHECKSTATUS(status);
916+
status = search_indices(x, y, z, grid, &xi[igrid], &yi[igrid], &zi[igrid], &xsi, &eta, &zet, gcode, ti[igrid], t0, t0, t0+1, CGRID_VELOCITY, gridindexingtype); CHECKSTATUS(status);
893917
status = getCell3D(U, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D_U, 1); CHECKSTATUS(status);
894918
status = getCell3D(V, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D_V, 1); CHECKSTATUS(status);
895919
status = getCell3D(W, xi[igrid], yi[igrid], zi[igrid], ti[igrid], data3D_W, 1); CHECKSTATUS(status);

tests/test_data/access-om2-01_u.nc

33.5 KB
Binary file not shown.

tests/test_data/access-om2-01_v.nc

33.7 KB
Binary file not shown.
33.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)