The phi.field module contains various data structures - such as grids or point clouds - and provides a common interface to access them. This allows the physics to be independent of the underlying data structure to some degree.

Abstract classes

The Field class is the base class that all fields extend. It represents a physical quantity F(x) that defines a value at every point x in n-dimensional space. The values of F(x) may have any number of dimensions, described by the channel dimensions of the Field. Scalar fields have no channel dimensions, vector fields have one, etc.

Important properties:

Important methods

Fields implement many mathematical operators, e.g. +, -, * , /, **. The shift operator @ calls the at() method on the left field.

The class SampledField extends Field to form the basis for all fields that explicitly store their data. The most important sampled fields are CenteredGrid, StaggeredGrid and PointCloud.

Important properties:

Non-sampled fields inherit from AnalyticField. They model F(x) as a function instead of from data.

Built-in Fields

CenteredGrid stores values in a regular grid structure. The grid values are stored in a Tensor whose spatial dimensions match the resolution of the grid. The bounds property stores the physical size of the grid from which the cell size is derived. CenteredGrid.elements is a GridCell matching the grid resolution.

StaggeredGrid stores vector fields in staggered form. The velocity components are not sampled at the cell centers but at the cell faces. This results in the values having different shapes for the different vector components. More on staggered grids.

PointCloud is a set of points or finite elements, each associated with a value.

SoftGeometryMask / HardGeometryMask: 1 inside the geometry, 0 outside.

Noise samples random fluctuations of certain sizes. Currently, it only supports resampling to grids.

AngularVelocity models a vortex-like velocity field around one or multiple points. This is useful for sampling the velocity of rotating objects.

Resampling Fields

Given val: Field and representation: SampledField with different values structures or different sampling points, they can be made compatible using at() or @., keep_extrapolation=False)  # resamples val at the elements of representation
val @ representation  # same as above

These functions return a Field of the same type as representation. If they are already sampled at the same elements, the above operations simply return val. ΦFlow may choose optimized code paths for specific combinations, such as two grids with equal sample point spacing dx.

When resampling staggered grids with keep_extrapolation=True, the sample points of the resampled field may be different from representation. This is because the sample points and value tensor shape of staggered grids depends on the extrapolation type.

Additionally, there are two functions for sampling field values at given locations.


Sampled fields, such as CenteredGrid, StaggeredGrid PointCloud all have an extrapolation member variable of type Extrapolation. The extrapolation determines the values outside the region in which the field is sampled. It takes the place of the boundary condition (e.g. Neumann / Dirichlet) which would be used in a mathematical formulation.

Extrapolations vs Boundary Conditions

While both extrapolation and traditional boundary conditions fill the same role, there are a couple of differences between the two. Boundary conditions determine the field values (or a spatial derivative thereof) at the boundary of a volume, i.e. they cover an n-1 dimensional region. Extrapolations, on the other hand, cover everything outside the sampled volume, i.e. an n-dimensional region.

Numerical methods working directly with traditional boundary conditions have to treat the boundaries separately (e.g. different stencils). With extrapolations, the same computations can typically be achieved by first padding the field and then applying a single operation everywhere. This makes low-order methods more efficient, especially on GPUs or TPUs where fewer kernels need to be launched, reducing the overhead. Also, user code typically is more concise and expressive with extrapolations.

Standard Extrapolations

Standard extrapolation types are listed here.

Custom extrapolations can be implemented by extending the Extrapolation class. Extrapolations also support a limited set of arithmetic operations, e.g. PERIODIC * ZERO = ZERO.

Specifying Extrapolations per Side

Different extrapolation types can be chosen for each side of a domain, e.g. a closed box with an open top. This can be achieved using combine_sides() which allows the extrapolations to be specified by dimension.

The following example uses 0 for the upper face along y and 1 everywhere else.

zero_top = extrapolation.combine_sides(x=extrapolation.ONE, y=(extrapolation.ONE, extrapolation.ZERO))

For a full example, see the pipe demo.