Staggered grids are a key component of the marker-and-cell (MAC) method [Harlow and Welch 1965]. They sample the velocity components not at the cell centers but in staggered form at the corresponding face centers. Their main advantage is that the divergence of a cell can be computed exactly.

Φ_{Flow} only stores valid velocity values in memory.
This may require non-uniform tensors for the values since the numbers of horizontal and vertical faces are generally not equal.
Depending on the boundary conditions, the outer-most values may also be redundant and, thus, not stored.

Φ_{Flow} represents staggered grids as instances of `StaggeredGrid`

.
They have the same properties as `CenteredGrid`

but the `values`

field may reference a
non-uniform tensor
to reflect the varying number of x, y and z sample points.

In [1]:

```
# !pip install --quiet phiflow
from phi.flow import *
grid = StaggeredGrid(0, extrapolation.BOUNDARY, x=10, y=10)
grid.values
```

Out[1]:

(xˢ=(x=11, y=10) int64, yˢ=(x=10, y=11) int64, vectorᶜ=x,y) non-uniform

`extrapolation.ZERO`

, it would be one less (see above image).

The `StaggeredGrid`

constructor supports two modes:

**Direct construction**`StaggeredGrid(values: Tensor, extrapolation, bounds)`

. All required fields are passed as arguments and stored as-is. The`values`

tensor must have the correct shape considering the extrapolation.**Construction by resampling**`StaggeredGrid(values: Any, extrapolation, bounds, resolution, **resolution)`

. When specifying the resolution as a`Shape`

or via keyword arguments, non-Tensor values can be passed for`values`

, such as geometries, other fields, constants or functions (see the documentation).

Examples:

In [2]:

```
domain = dict(x=10, y=10, bounds=Box(x=1, y=1), extrapolation=extrapolation.ZERO)
grid = StaggeredGrid((1, -1), **domain) # from constant vector
grid = StaggeredGrid(Noise(), **domain) # sample analytic field
grid = StaggeredGrid(grid, **domain) # resample existing field
grid = StaggeredGrid(lambda x: math.exp(-x), **domain) # function value(location)
grid = resample(Sphere(x=0, y=0, radius=1), StaggeredGrid(0, **domain)) # no anti-aliasing
grid = resample(Sphere(x=0, y=0, radius=1), StaggeredGrid(0, **domain), soft=True) # with anti-aliasing
```

`StaggeredGrid`

from NumPy arrays (or TensorFlow/PyTorch/Jax tensors), the tensors first need to be converted to Φ_{Flow} tensors using `tensor()`

or `wrap()`

.

In [3]:

```
vx = tensor(np.zeros([33, 32]), spatial('x,y'))
vy = tensor(np.zeros([32, 33]), spatial('x,y'))
StaggeredGrid(math.stack([vx, vy], channel('vector')), extrapolation.BOUNDARY)
vx = tensor(np.zeros([32, 32]), spatial('x,y'))
vy = tensor(np.zeros([32, 32]), spatial('x,y'))
StaggeredGrid(math.stack([vx, vy], channel('vector')), extrapolation.PERIODIC)
vx = tensor(np.zeros([31, 32]), spatial('x,y'))
vy = tensor(np.zeros([32, 31]), spatial('x,y'))
StaggeredGrid(math.stack([vx, vy], channel('vector')), 0)
```

Out[3]:

StaggeredGrid[(xˢ=32, yˢ=32, vectorᶜ=2), size=(x=32, y=32) int64, extrapolation=0]

Staggered grids can also be created from other fields using `field.at()`

or `@`

by passing an existing `StaggeredGrid`

.

Some field functions also return `StaggeredGrids`

:

`spatial_gradient()`

with`type=StaggeredGrid`

`stagger()`

For non-periodic staggered grids, the `values`

tensor is non-uniform
to reflect the different number of sample points for each component.

In [4]:

```
grid.values
```

Out[4]:

(xˢ=(x=9, y=10) int64, yˢ=(x=10, y=9) int64, vectorᶜ=x,y) non-uniform

Functions to get a uniform tensor:

`uniform_values()`

interpolates the staggered values to the cell centers and returns a`CenteredGrid`

`at_centers()`

interpolates the staggered values to the cell centers and returns a`CenteredGrid`

`staggered_tensor()`

pads the internal tensor to an invariant shape with n+1 entries along all dimensions.

In [5]:

```
grid.uniform_values()
```

Out[5]:

(xˢ=11, yˢ=11, vectorᶜ=x,y) 0.601 ± 0.480 (0e+00...1e+00)

Like tensors, grids can be sliced using the standard syntax.
When selecting a vector component, such as `x`

or `y`

, the result is represented as a `CenteredGrid`

with shifted locations.

In [6]:

```
grid.vector['x'] # select component
```

Out[6]:

CenteredGrid[(xˢ=9, yˢ=10), size=(x=0.900, y=1.000), extrapolation=0]

`values`

directly.

In [7]:

```
grid.values.x[3:4] # spatial slice
```

Out[7]:

(xˢ=1, yˢ=(x=10, y=9) int64, vectorᶜ=x,y) non-uniform

In [8]:

```
grid.values.x[0] # spatial slice
```

Out[8]:

(yˢ=(x=10, y=9) int64, vectorᶜ=x,y) non-uniform

Slicing along batch dimensions has no special effect, this just slices the `values`

.

In [9]:

```
grid.batch[0] # batch slice
```

Out[9]:

StaggeredGrid[(xˢ=10, yˢ=10, vectorᶜ=x,y), size=(x=1, y=1) int64, extrapolation=0]

Fields can also be sliced using `unstack()`

.
This returns a `tuple`

of all slices along a dimension.