API Reference
IncompressibleNavierStokes.IncompressibleNavierStokes
— ModuleIncompressibleNavierStokes
Energy-conserving solvers for the incompressible Navier-Stokes equations.
IncompressibleNavierStokes.Setup
— FunctionSetup(
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.
Grid
IncompressibleNavierStokes.Dimension
— TypeDimension(N)
Represent an N
-dimensional space. Returns N
when called.
julia> d = Dimension(3)
Dimension{3}()
julia> d()
3
IncompressibleNavierStokes.Grid
— FunctionGrid(x, boundary_conditions)
Create nonuniform Cartesian box mesh x[1]
× ... × x[d]
with boundary conditions boundary_conditions
.
IncompressibleNavierStokes.max_size
— Functionmax_size(grid)
Get size of the largest grid element.
IncompressibleNavierStokes.cosine_grid
— Functioncosine_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
.
IncompressibleNavierStokes.stretched_grid
— Functionstretched_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
.
IncompressibleNavierStokes.tanh_grid
— Functiontanh_grid(a, b, N, γ = typeof(a)(1))
Create a nonuniform grid of N + 1
points from a
to b
, as proposed by Trias et al. [13].
Preprocess
IncompressibleNavierStokes.create_initial_conditions
— Functioncreate_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...)
.
IncompressibleNavierStokes.random_field
— Functionrandom_field(
setup, t = 0;
A = 1,
kp = 10,
psolver = default_psolver(setup),
rng = Random.default_rng(),
)
Create random field, as in [14].
K
: Maximum wavenumberA
: Eddy amplitude scalingkp
: Peak energy wavenumber
Processors
IncompressibleNavierStokes.processor
— Functionprocessor(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
IncompressibleNavierStokes.timelogger
— Functiontimelogger(; showmax = true, nupdate = 1)
Create processor that logs time step information.
IncompressibleNavierStokes.observefield
— Functionobservefield(
state;
setup,
fieldname,
logtol = eps(eltype(setup.grid.x[1])),
psolver = nothing,
)
Observe field fieldname
at pressure points.
IncompressibleNavierStokes.vtk_writer
— Functionvtk_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"
.
IncompressibleNavierStokes.fieldsaver
— Functionfieldsaver(; setup, nupdate = 1)
Create processor that stores the solution and time every nupdate
time step.
IncompressibleNavierStokes.realtimeplotter
— Functionrealtimeplotter(;
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 everynupdate
time step.displayfig
: Display the figure at the start.screen
: Ifnothing
, use default display. IfGLMakie.screen()
multiple plots can be displayed in separate windows like in MATLAB (see alsoGLMakie.closeall()
).displayupdates
: Display the figure at every update (if using CairoMakie).sleeptime
: Thesleeptime
is slept at every update, to give Makie time to update the plot. Set this tonothing
to skip sleeping.
Additional kwargs
are passed to the plot
function.
IncompressibleNavierStokes.fieldplot
— Functionfieldplot(
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 type
s for 2D are:
heatmap
(default),image
,contour
,contourf
.
Available plot type
s for 3D are:
contour
(default).
The alpha
value gets passed to contour
in 3D.
IncompressibleNavierStokes.energy_history_plot
— Functionenergy_history_plot(state; setup)
Create energy history plot.
IncompressibleNavierStokes.energy_spectrum_plot
— Functionenergy_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].
IncompressibleNavierStokes.animator
— Functionanimator(; 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
.
Solvers
IncompressibleNavierStokes.get_cfl_timestep!
— Functionget_cfl_timestep!(buf, u, setup)
Get proposed maximum time step for convection and diffusion terms.
IncompressibleNavierStokes.solve_unsteady
— Functionsolve_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.
IncompressibleNavierStokes.solve_steady_state
— Functionfunction 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.
Utils
IncompressibleNavierStokes.save_vtk
— Functionsave_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.
IncompressibleNavierStokes.plotgrid
— Functionplotgrid(x...)
Plot nonuniform Cartesian grid.
IncompressibleNavierStokes.get_lims
— Functionget_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.
IncompressibleNavierStokes.plotmat
— Functionplotmat(A)
Plot matrix.