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.
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:
.shape: Shape
contains batch and spatial dimensions from.spatial_rank: int = len(shape.spatial)
is the dimensionality of physical spaceImportant methods
sample_at(Tensor) -> Tensor
computes the field values at the given pointssample_in(Geometry) -> Tensor
computes the field values in the given volumesat(SampledField) -> SampledField
returns a field with the same sample points as the specified representation.unstack(dim) -> tuple[Field]
slices the field along a dimensionFields 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:
.values: Tensor
data that is used in sampling.geometry: Geometry
sample points as finite volumes.points: Tensor
center points of elements
.extrapolation: Extrapolation
determines how values outside the region covered by values
are determined.Non-sampled fields inherit from AnalyticField
.
They model F(x)
as a function instead of from data.
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.geometry
is a UniformGrid
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.
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.
Given val: Field
and representation: SampledField
with different values structures or different sampling points,
they can be made compatible using at()
or @
.
val.at(representation, keep_boundary=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_boundary=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.
sample
samples the field values at the location of a single geometry or geometry batch.reduce_sample
differs from sample
in that the geometry here describes
staggered locations at which the individual channel components of the field are stored.
For centered grids, sample
and reduce_sample
are equal.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.
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 extrapolation types are listed here.
PERIODIC
copies the values from the opposite side.BOUNDARY
copies the closest value from the grid. For the boundary condition ∂u/∂x = 0, this is accurate to second order.ConstantExtrapolation
, such as ZERO
or ONE
fill the outside with a constant value.
For a boundary condition u=c, the first padded value is exact and values padded further out are accurate to first order.Custom extrapolations can be implemented by extending the
Extrapolation
class.
Extrapolations also support a limited set of arithmetic operations, e.g. PERIODIC * ZERO = ZERO
.
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.