API Reference

IncompressibleNavierStokes.SetupFunction
Setup(
    x...;
    boundary_conditions = ntuple(d -> (PeriodicBC(), PeriodicBC()), length(x)),
    Re = convert(eltype(x[1]), 1_000),
    bodyforce = nothing,
    issteadybodyforce = true,
    closure_model = nothing,
    ArrayType = Array,
    workgroupsize = 64,
    temperature = nothing,
)

Create setup.

source

Grid

IncompressibleNavierStokes.cosine_gridFunction
cosine_grid(a, b, N)

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

\[x_i = a + \frac{1}{2} \left( 1 - \cos \left( \pi \frac{i}{n} \right) \right) (b - a), \quad i = 0, \dots, N\]

See also stretched_grid.

source
IncompressibleNavierStokes.stretched_gridFunction
stretched_grid(a, b, N, s = 1)

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 $x \in \mathbb{R}^{N + 1}$ such that $x_n = a + \sum_{i = 1}^n s^{i - 1} h$ for $n = 0, \dots , N$. Setting $x_N = b$ then gives $h = (b - a) \frac{1 - s}{1 - s^N}$, resulting in

\[x_n = a + (b - a) \frac{1 - s^n}{1 - s^N}, \quad n = 0, \dots, N.\]

Note that stretched_grid(a, b, N, s)[n] corresponds to $x_{n - 1}$.

See also cosine_grid.

source

Preprocess

IncompressibleNavierStokes.create_initial_conditionsFunction
create_initial_conditions(
    setup,
    initial_velocity,
    t = 0;
    psolver = default_psolver(setup),
    doproject = true,
)

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

source
IncompressibleNavierStokes.random_fieldFunction
random_field(
    setup, t = 0;
    A = 1,
    kp = 10,
    psolver = default_psolver(setup),
    rng = Random.default_rng(),
)

Create random field, as in [14].

  • K: Maximum wavenumber
  • A: Eddy amplitude scaling
  • kp: Peak energy wavenumber
source

Processors

IncompressibleNavierStokes.processorFunction
processor(initialize, finalize = (initialized, state) -> initialized)

Process results from time stepping. Before time stepping, the initialize function is called on an observable of the time stepper state, returning initialized. The observable is updated every time step.

After timestepping, the finalize function is called on initialized and the final state.

See the following example:

function initialize(state)
    s = 0
    println("Let's sum up the time steps")
    on(state) do (; n, t)
        println("The summand is $n, the time is $t")
        s = s + n
    end
    s
end

finalize(i, state) = println("The final sum (at time t=$(state.t)) is $s")
p = processor(initialize, finalize)

When solved for 6 time steps from t=0 to t=2 the displayed output is

Let's sum up the time steps
The summand is 0, the time is 0.0
The summand is 1, the time is 0.4
The summand is 2, the time is 0.8
The summand is 3, the time is 1.2
The summand is 4, the time is 1.6
The summand is 5, the time is 2.0
The final sum (at time t=2.0) is 15
source
IncompressibleNavierStokes.vtk_writerFunction
vtk_writer(;
    setup,
    nupdate = 1,
    dir = "output",
    filename = "solution",
    fields = (:velocity,),
)

Create processor that writes the solution every nupdate time steps to a VTK file. The resulting Paraview data collection file is stored in "$dir/$filename.pvd".

source
IncompressibleNavierStokes.realtimeplotterFunction
realtimeplotter(;
    setup,
    plot = fieldplot,
    nupdate = 1,
    displayfig = true,
    screen = nothing,
    displayupdates = false,
    sleeptime = nothing,
    kwargs...,
)

Processor for plotting the solution in real time.

Keyword arguments:

  • plot: Plot function.
  • nupdate: Show solution every nupdate time step.
  • displayfig: Display the figure at the start.
  • screen: If nothing, use default display. If GLMakie.screen() multiple plots can be displayed in separate windows like in MATLAB (see also GLMakie.closeall()).
  • displayupdates: Display the figure at every update (if using CairoMakie).
  • sleeptime: The sleeptime is slept at every update, to give Makie time to update the plot. Set this to nothing to skip sleeping.

Additional kwargs are passed to the plot function.

source
IncompressibleNavierStokes.fieldplotFunction
fieldplot(
    state;
    setup,
    fieldname = :vorticity,
    type = nothing,
    kwargs...,
)

Plot state field in pressure points. If state is Observable, then the plot is interactive.

Available fieldnames are:

  • :velocity,
  • :vorticity,
  • :streamfunction,
  • :pressure.

Available plot types for 2D are:

  • heatmap (default),
  • image,
  • contour,
  • contourf.

Available plot types for 3D are:

  • contour (default).

The alpha value gets passed to contour in 3D.

source
IncompressibleNavierStokes.energy_spectrum_plotFunction
energy_spectrum_plot(state; setup)

Create energy spectrum plot. The energy at a scalar wavenumber level $\kappa \in \mathbb{N}$ is defined by

\[\hat{e}(\kappa) = \int_{\kappa \leq \| k \|_2 < \kappa + 1} | \hat{e}(k) | \mathrm{d} k,\]

as in San and Staples [15].

source
IncompressibleNavierStokes.animatorFunction
animator(; setup, path, plot = fieldplot, nupdate = 1, kwargs...)

Animate a plot of the solution every update iteration. The animation is saved to path, which should have one of the following extensions:

  • ".mkv"
  • ".mp4"
  • ".webm"
  • ".gif"

The plot is determined by a plotter processor. Additional kwargs are passed to plot.

source

Solvers

IncompressibleNavierStokes.solve_unsteadyFunction
solve_unsteady(;
    setup,
    tlims,
    ustart,
    tempstart = nothing,
    method = RKMethods.RK44(; T = eltype(u₀[1])),
    psolver = default_psolver(setup),
    Δt = nothing,
    cfl = eltype(ustart[1])(0.9),
    n_adapt_Δt = 1,
    docopy = true,
    processors = (;),
)

Solve unsteady problem using method.

If Δt is a real number, it is rounded such that (t_end - t_start) / Δt is an integer. If Δt = nothing, the time step is chosen every n_adapt_Δt iteration with CFL-number cfl .

The processors are called after every time step.

Note that the state observable passed to the processor.initialize function contains vector living on the device, and you may have to move them back to the host using Array(u) in the processor.

Return (; u, t), outputs, where outputs is a named tuple with the outputs of processors with the same field names.

source
IncompressibleNavierStokes.solve_steady_stateFunction
function solve_steady_state(
    setup, V₀, p₀;
    jacobian_type = :newton,
    npicard = 2,
    abstol = 1e-10,
    maxiter = 10,
)

Solve steady state problem of the Navier-Stokes equations. This saddlepoint system arises from linearization of the convective terms.

Each processor is called after every processor.nupdate iteration.

source

Utils

IncompressibleNavierStokes.save_vtkFunction
save_vtk(setup, u, filename = "output/solution"; fieldnames = [:velocity], psolver)

Save velocity and pressure field to a VTK file.

In the case of a 2D setup, the velocity field is saved as a 3D vector with a z-component of zero, as this seems to be preferred by ParaView.

source
IncompressibleNavierStokes.get_limsFunction
get_lims(x, n = 1.5)

Get approximate lower and upper limits of a field x based on the mean and standard deviation ($\mu \pm n \sigma$). If x is constant, a margin of 1e-4 is enforced. This is required for contour plotting functions that require a certain range.

source