Problem setup
A problem is defined by a call to Setup, which precomputes all grid quantities:
setup = Setup(;
x = (range(0.0, 2.0, 129), range(0.0, 1.0, 65)),
boundary_conditions = (;
u = (
(DirichletBC((1.0, 0.0)), PressureBC()), # x-left, x-right
(DirichletBC(), DirichletBC()), # y-bottom, y-top
),
),
)Grid
The tuple x contains one vector of volume boundary coordinates per dimension (so a 2D grid with N × M volumes needs vectors of length N + 1 and M + 1). Uniform grids are created with range; non-uniform grids can be created with stretched_grid, cosine_grid, or tanh_grid, or any custom monotone vector. The grid can be visualized with plotgrid.
Note that some functionality requires a uniform grid in one or more directions: the spectral pressure solvers and the spectral quantities require uniformity in the periodic directions.
Boundary conditions
The available boundary condition types are PeriodicBC, DirichletBC, SymmetricBC, and PressureBC. They are given per field, dimension, and side: boundary_conditions.u[α][s] is the boundary condition for the velocity in direction α on the low (s = 1) or high (s = 2) side. Each boundary has exactly one type of boundary condition, and for periodic boundary conditions the opposite boundary must also be periodic. For simulations with a temperature equation, add a temp field with boundary conditions for the temperature.
Boundary conditions are enforced by filling the ghost volumes of the field arrays, see Spatial and temporal discretization.
Initial conditions
Fields are allocated with scalarfield and vectorfield, and initialized with velocityfield and temperaturefield from a given analytical expression. For periodic boxes, random_field creates a random velocity field with a prescribed energy spectrum that is exactly divergence free on the staggered grid.
API
Setup and grid
IncompressibleNavierStokes.Dimension Type
struct Dimension{N}Represent an N-dimensional space. Returns N when called.
julia> d = Dimension(3)
Dimension{3}()
julia> d()
3Fields
sourceIncompressibleNavierStokes.Setup Method
Setup(; x, boundary_conditions, backend, workgroupsize)Create useful quantities for Cartesian box mesh ``x[1] \times \dots \times x[d]with boundary conditionsboundary_conditions. Return a named tuple ([α]` denotes a tuple index) with the following fields:
N[α]: Number of finite volumes in directionα, including ghost volumesNu[α][β]: Number ofu[α]velocity DOFs in directionβNp[α]: Number of pressure DOFs in directionαIu[α]: Cartesian index range ofu[α]velocity DOFsIp: Cartesian index range of pressure DOFsxlims[α]: Tuple containing the limits of the physical domain (not grid) in the directionαx[α]: α-coordinates of all volume boundaries, including the left point of the first ghost volumexu[α][β]: β-coordinates ofu[α]velocity pointsxp[α]: α-coordinates of pressure pointsΔ[α]: All volume widths in directionαΔu[α]: Distance between pressure points in directionαA_stag[α]: Interpolation weights from volume centers to volume faces in the directionα
Note that the memory footprint of the redundant 1D-arrays above is negligible compared to the memory footprint of the 2D/3D-fields used in the code.
sourceIncompressibleNavierStokes.cosine_grid Method
cosine_grid(a, b, N) -> AnyCreate a nonuniform grid of N + 1 points from a to b using a cosine profile, i.e.
See also stretched_grid.
IncompressibleNavierStokes.stretched_grid Function
stretched_grid(a, b, N) -> Any
stretched_grid(a, b, N, s) -> AnyCreate a nonuniform grid of N + 1 points from a to b with a stretch factor of s. If s = 1, return a uniform spacing from a to b. Otherwise, return a vector
Note that stretched_grid(a, b, N, s)[n] corresponds to
See also cosine_grid.
IncompressibleNavierStokes.tanh_grid Function
tanh_grid(a, b, N) -> Any
tanh_grid(a, b, N, γ) -> AnyCreate a nonuniform grid of N + 1 points from a to b, as proposed by Trias et al. [16].
Boundary conditions
IncompressibleNavierStokes.AbstractBC Type
abstract type AbstractBCBoundary condition for one side of the domain.
Fields
sourceIncompressibleNavierStokes.DirichletBC Type
struct DirichletBC{U} <: IncompressibleNavierStokes.AbstractBCDirichlet boundary conditions for the velocity. The value u is one of:
nothing(default): no-slip boundary conditions, where all velocity components are zero;a tuple of
dconstants(u1_BC, ..., ud_BC), one per velocity component, wheredis the dimension;a function
(dim, x..., t) -> u_BCreturning the boundary value of velocity componentdimat the pointx...and timet.
Fields
u: Boundary condition
IncompressibleNavierStokes.PeriodicBC Type
struct PeriodicBC <: IncompressibleNavierStokes.AbstractBCPeriodic boundary conditions. Must be periodic on both sides.
Fields
sourceIncompressibleNavierStokes.PressureBC Type
struct PressureBC <: IncompressibleNavierStokes.AbstractBCPressure boundary conditions. The pressure is prescribed on the boundary (usually an "outlet"). The velocity has zero Neumann conditions.
Note: Currently, the pressure is prescribed with the constant value of zero on the entire boundary.
Fields
sourceIncompressibleNavierStokes.SymmetricBC Type
struct SymmetricBC <: IncompressibleNavierStokes.AbstractBCSymmetric boundary conditions. The parallel velocity and pressure is the same at each side of the boundary. The normal velocity is zero.
Fields
sourceIncompressibleNavierStokes.apply_bc_p! Method
apply_bc_p!(p, t, setup; kwargs...) -> AnyApply pressure boundary conditions (in-place version).
sourceIncompressibleNavierStokes.apply_bc_p Method
apply_bc_p(p, t, setup; kwargs...) -> AnyApply pressure boundary conditions (differentiable version).
sourceIncompressibleNavierStokes.apply_bc_temp! Method
apply_bc_temp!(temp, t, setup; kwargs...) -> AnyApply temperature boundary conditions (in-place version).
sourceIncompressibleNavierStokes.apply_bc_temp Method
apply_bc_temp(temp, t, setup; kwargs...) -> AnyApply temperature boundary conditions (differentiable version).
sourceIncompressibleNavierStokes.apply_bc_u! Method
apply_bc_u!(u, t, setup; kwargs...) -> AnyApply velocity boundary conditions (in-place version).
sourceIncompressibleNavierStokes.apply_bc_u Method
apply_bc_u(u, t, setup; kwargs...) -> AnyApply velocity boundary conditions (differentiable version).
sourceIncompressibleNavierStokes.boundary Method
boundary(β, N, I, isright) -> AnyGet boundary indices of boundary layer normal to β. The CartesianIndices given by I should contain those of the inner DOFs, typically Ip or Iu[α]. The boundary layer is then just outside those.
IncompressibleNavierStokes.offset_p Function
offset_p(bc, isright)Number of non-DOF pressure components at boundary. If isright, it is at the end/right/rear/top boundary, otherwise beginning.
IncompressibleNavierStokes.offset_u Function
offset_u(bc, isright, isnormal)Number of non-DOF velocity components at boundary. If isnormal, then the velocity is normal to the boundary, else parallel. If isright, it is at the end/right/rear/top boundary, otherwise beginning.
Field initializers
IncompressibleNavierStokes.orlandi_profile Method
orlandi_profile(k; kpeak) -> AnyInitial energy spectrum profile kpeak, as in Orlandi [17].
IncompressibleNavierStokes.random_field Function
random_field(setup; ...) -> Any
random_field(
setup,
t;
profile,
totalenergy,
rng,
kwargs...
) -> AnyCreate a random divergence-free velocity field with a prescribed energy spectrum profile. The energy in the wavenumber shell κ ≤ |k| < κ + 1 is totalenergy * profile(κ; kwargs...) / p, where p normalizes the profile such that the mean kinetic energy density ⟨u_i u_i⟩ / 2 is exactly totalenergy. By default, the Orlandi form orlandi_profile is used (pass e.g. kpeak = 5 to move the peak).
The field is constructed in spectral space from white noise, projected onto the divergence-free space of the staggered grid (using the modified wavenumbers of the staggered divergence operator), and rescaled shell-wise. The resulting field is thus exactly divergence-free on the staggered grid and has exactly the prescribed spectrum.
sourceIncompressibleNavierStokes.scalarfield Method
scalarfield(setup) -> AnyCreate empty scalar field.
sourceIncompressibleNavierStokes.symmetric_tensorfield Method
symmetric_tensorfield(
setup
) -> Union{NamedTuple{(:xx, :xy, :xz, :yy, :yz, :zz), <:NTuple{6, Any}}, NamedTuple{(:xx, :xy, :yy), <:Tuple{Any, Any, Any}}}Symmetric tensor field, stored as a named tuple σ.ij.
IncompressibleNavierStokes.temperaturefield Function
temperaturefield(setup, tempfunc) -> Any
temperaturefield(setup, tempfunc, t) -> AnyCreate temperature field from function with boundary conditions at time t.
IncompressibleNavierStokes.tensorfield Method
tensorfield(
setup
) -> Union{NamedTuple{(:xx, :yx, :zx, :xy, :yy, :zy, :xz, :yz, :zz), <:NTuple{9, Any}}, NamedTuple{(:xx, :yx, :xy, :yy), <:NTuple{4, Any}}}Non-symmetric tensor field, stored as a named tuple σ.ij.
IncompressibleNavierStokes.vectorfield Method
vectorfield(setup) -> AnyCreate empty vector field.
sourceIncompressibleNavierStokes.velocityfield Function
velocityfield(setup, ufunc; ...) -> Any
velocityfield(setup, ufunc, t; psolver, doproject) -> AnyCreate divergence free velocity field u with boundary conditions at time t. The initial conditions of u[α] are specified by the function ufunc(α, x...).