Grids

There are many different types of grids used by ocean models, such as latitude-longitude grids, tiled rectilinear grids, triangular or hexagonal grids, and adaptive unstructured grids. An observational campaign could have stations at arbitrary locations.

neutralocean can handle any dataset of vertical casts: you just have to specify which pairs of casts are adjacent. We think of the horizontal grid as a graph: a collection of nodes and edges. Each node is a vertical cast (which could be ocean or land). Two nodes are joined by an edge when the two vertical casts are adjacent.

Example. Suppose we took 4 casts from the ocean, with three in a triangle and a fourth off to the side (as in the run_4casts.py Test Example):

graph of 4 casts

The nodes (vertical casts) are labelled 0, 1, ... N-1, where N is the number of casts (here N = 4). The order doesn’t really matter, but once it’s chosen we then specify the edges (pairs of adjacent casts) as a 2D array of shape (2, E). Here, E = 4 and edges = np.array([[0, 0, 1, 2], [1, 2, 2, 3]]). Alternatively, edges can be a tuple/list of length 2, with each element a tuple/list of length E: edges = ((0, 0, 1, 2), (1, 2, 2, 3)). What matters is, letting a = edges[0] and b = edges[1], that the nodes a[i] and b[i] are adjacent, for each i = 0, ..., E-1.

For a rectilinear grid (such as a lat-lon grid), most casts are adjacent to four other casts. We can label the casts 0, 1, ..., N-1 starting in the south-west and going first across longitudes and then across latitudes, ending in the north-east. The order doesn’t really matter, but must be specified.

Example. A 4 x 3 grid with N = 12 that is periodic in longitude (x-axis) but not in latitude (y-axis):

4x3 grid as a graph

Note

The order of casts is determined by your data is stored in memory. Suppose Salinity and Temperature are presented as 3D arrays of size (nj,ni,nk), which are the number of grid points in the latitudinal, longitudinal, and vertical dimensions, respectively. The data is actually stored in memory as a long 1D array with the first nk elements being data from the “first” cast, the next nk elements being data from the “second” cast, etc. This orders the casts. Ideally, the vertical dimension is last (varying fastest in memory); if not, you can use the vert_dim argument and neutralocean will internally reorder your data to be so.

The pairs of adjacent casts can be encoded as

edges = (
    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 4, 5, 6, 7, 8, 9, 10, 11),
    (3, 0, 1, 2, 7, 4, 5, 6, 11, 8, 9, 10, 0, 1, 2, 3, 4, 5, 6,  7),
)
#   <--------- x connections ----------->, <--- y connections --->

If the grid also gives geometric information, we should give it to neutralocean. If casts m and n are adjacent, let \(\Delta_{m,n}\) be the distance between cell centers of casts m and n, and let \(\Delta^\perp_{m,n}\) be the distance of the face between the cells of casts m and n. This geometric information is encoded as two E-length lists, dist and distperp respectively, given in the same order as edges. E.g. dist[i] is the distance between casts edges[0][i] and edges[1][i]. This geometry for the 4x3 example can be illustrated as follows (not showing all geometric info):

4x3 grid as a graph

The lists edges, dist, and distperp are passed to neutralocean as a dict named grid (see Potential Density Surface for its docstring).

To ease building these lists, we provide build_grid functions for various grid types. See the Grids API. For the first generic graph example, we’d use neutralocean.grid.graph.build_grid.

For the lat-lon example, we’d use neutralocean.grid.rectilinear.build_grid. See the OCCA example.

A close cousin of rectilinear grids (like lat-lon) is a tiled rectilinear grid, in which there are several (square) rectilinear grids that are placed next to each other, such as the lat-lon-cap used by ECCOv4r4. For these grids, use neutralocean.grid.xgcm.build_grid to build the grid dict. This uses xgcm to handle the tiling, with face connections specified in the xgcm way. See the ECCOv4 example.

For a tripolar grid such as ORCA used by NEMO, we’d use neutralocean.grid.tripolar.build_grid. See the ORCA example.