Skip to content

Problem setup

A problem is defined by a call to Setup, which precomputes all grid quantities:

julia
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
julia
struct Dimension{N}

Represent an N-dimensional space. Returns N when called.

julia
julia> d = Dimension(3)
Dimension{3}()

julia> d()
3

Fields

source
IncompressibleNavierStokes.Setup Method
julia
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 volumes

  • Nu[α][β]: Number of u[α] velocity DOFs in direction β

  • Np[α]: Number of pressure DOFs in direction α

  • Iu[α]: Cartesian index range of u[α] velocity DOFs

  • Ip: Cartesian index range of pressure DOFs

  • xlims[α]: 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 volume

  • xu[α][β]: β-coordinates of u[α] velocity points

  • xp[α]: α-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.

source
IncompressibleNavierStokes.cosine_grid Method
julia
cosine_grid(a, b, N) -> Any

Create a nonuniform grid of N + 1 points from a to b using a cosine profile, i.e.

xi=a+12(1cos(πin))(ba),i=0,,N

See also stretched_grid.

source
IncompressibleNavierStokes.stretched_grid Function
julia
stretched_grid(a, b, N) -> Any
stretched_grid(a, b, N, s) -> Any

Create 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 xRN+1 such that xn=a+i=1nsi1h for n=0,,N. Setting xN=b then gives h=(ba)1s1sN, resulting in

xn=a+(ba)1sn1sN,n=0,,N.

Note that stretched_grid(a, b, N, s)[n] corresponds to xn1.

See also cosine_grid.

source
IncompressibleNavierStokes.tanh_grid Function
julia
tanh_grid(a, b, N) -> Any
tanh_grid(a, b, N, γ) -> Any

Create a nonuniform grid of N + 1 points from a to b, as proposed by Trias et al. [16].

source

Boundary conditions

IncompressibleNavierStokes.AbstractBC Type
julia
abstract type AbstractBC

Boundary condition for one side of the domain.

Fields

source
IncompressibleNavierStokes.DirichletBC Type
julia
struct DirichletBC{U} <: IncompressibleNavierStokes.AbstractBC

Dirichlet 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 d constants (u1_BC, ..., ud_BC), one per velocity component, where d is the dimension;

  • a function (dim, x..., t) -> u_BC returning the boundary value of velocity component dim at the point x... and time t.

Fields

  • u: Boundary condition
source
IncompressibleNavierStokes.PeriodicBC Type
julia
struct PeriodicBC <: IncompressibleNavierStokes.AbstractBC

Periodic boundary conditions. Must be periodic on both sides.

Fields

source
IncompressibleNavierStokes.PressureBC Type
julia
struct PressureBC <: IncompressibleNavierStokes.AbstractBC

Pressure 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

source
IncompressibleNavierStokes.SymmetricBC Type
julia
struct SymmetricBC <: IncompressibleNavierStokes.AbstractBC

Symmetric boundary conditions. The parallel velocity and pressure is the same at each side of the boundary. The normal velocity is zero.

Fields

source
IncompressibleNavierStokes.apply_bc_p! Method
julia
apply_bc_p!(p, t, setup; kwargs...) -> Any

Apply pressure boundary conditions (in-place version).

source
IncompressibleNavierStokes.apply_bc_p Method
julia
apply_bc_p(p, t, setup; kwargs...) -> Any

Apply pressure boundary conditions (differentiable version).

source
IncompressibleNavierStokes.apply_bc_temp! Method
julia
apply_bc_temp!(temp, t, setup; kwargs...) -> Any

Apply temperature boundary conditions (in-place version).

source
IncompressibleNavierStokes.apply_bc_temp Method
julia
apply_bc_temp(temp, t, setup; kwargs...) -> Any

Apply temperature boundary conditions (differentiable version).

source
IncompressibleNavierStokes.apply_bc_u! Method
julia
apply_bc_u!(u, t, setup; kwargs...) -> Any

Apply velocity boundary conditions (in-place version).

source
IncompressibleNavierStokes.apply_bc_u Method
julia
apply_bc_u(u, t, setup; kwargs...) -> Any

Apply velocity boundary conditions (differentiable version).

source
IncompressibleNavierStokes.boundary Method
julia
boundary(β, N, I, isright) -> Any

Get 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.

source
IncompressibleNavierStokes.offset_p Function
julia
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.

source
IncompressibleNavierStokes.offset_u Function
julia
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.

source

Field initializers

IncompressibleNavierStokes.orlandi_profile Method
julia
orlandi_profile(k; kpeak) -> Any

Initial energy spectrum profile E(k)k4e2(k/kp)2 with peak wavenumber kpeak, as in Orlandi [17].

source
IncompressibleNavierStokes.random_field Function
julia
random_field(setup; ...) -> Any
random_field(
    setup,
    t;
    profile,
    totalenergy,
    rng,
    kwargs...
) -> Any

Create 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.

source
IncompressibleNavierStokes.scalarfield Method
julia
scalarfield(setup) -> Any

Create empty scalar field.

source
IncompressibleNavierStokes.symmetric_tensorfield Method
julia
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.

source
IncompressibleNavierStokes.temperaturefield Function
julia
temperaturefield(setup, tempfunc) -> Any
temperaturefield(setup, tempfunc, t) -> Any

Create temperature field from function with boundary conditions at time t.

source
IncompressibleNavierStokes.tensorfield Method
julia
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.

source
IncompressibleNavierStokes.vectorfield Method
julia
vectorfield(setup) -> Any

Create empty vector field.

source
IncompressibleNavierStokes.velocityfield Function
julia
velocityfield(setup, ufunc; ...) -> Any
velocityfield(setup, ufunc, t; psolver, doproject) -> Any

Create divergence free velocity field u with boundary conditions at time t. The initial conditions of u[α] are specified by the function ufunc(α, x...).

source