Forcing
VortexPasta.Forcing
— ModuleForcing
Defines methods for injecting energy onto a system of vortices.
Forcing types
VortexPasta.Forcing.NoForcing
— TypeNoForcing() <: AbstractForcing
Represents the absence of external forcing.
VortexPasta.Forcing.NormalFluidForcing
— TypeNormalFluidForcing <: AbstractForcing
NormalFluidForcing(vn::Function; α, α′ = 0)
Forcing due to mutual friction with a normal fluid.
The normal fluid is represented by a function vn
which should take a position x⃗::SVector{N, T}
and return a velocity v⃗::SVector{N, T}
(N
is the number of dimensions, usually N = 3
).
In particular, the function could be a synthetic velocity field from the SyntheticFields
module (see below for examples).
This type of forcing defines an external velocity $\bm{v}_{\text{f}}$ affecting vortex motion, so that the actual vortex velocity $\bm{v}_{\text{L}}$ is
\[\frac{\mathrm{d}\bm{s}}{\mathrm{d}t} = \bm{v}_{\text{L}} = \bm{v}_{\text{s}} + \bm{v}_{\text{f}}.\]
Here $\bm{v}_{\text{s}}$ is the self-induced velocity obtained by applying Biot–Savart's law.
The forcing velocity is of the form:
\[\bm{v}_{\text{f}} = α \bm{s}' × \bm{v}_{\text{ns}} - α' \bm{s}' × \left( \bm{s}' × \bm{v}_{\text{ns}} \right)\]
where $\bm{s}'$ is the local unit tangent vector, $\bm{v}_{\text{ns}} = \bm{v}_{\text{n}} - \bm{v}_{\text{s}}$ is the local slip velocity, and $α$ and $α'$ are non-dimensional coefficients representing the intensity of Magnus and drag forces.
This "forcing" generally affects all lengthscales, even when $\bm{v}_{\text{n}}$ is a large-scale flow. This may be unwanted for turbulence simulations. In that case, the alternative FourierBandForcing
method can be more relevant.
Example
Define a mutual friction forcing based on a large-scale normal fluid velocity field (see SyntheticFields.FourierBandVectorField
):
julia> using VortexPasta.Forcing: NormalFluidForcing
julia> using VortexPasta.SyntheticFields: SyntheticFields, FourierBandVectorField
julia> using Random: Xoshiro
julia> rng = Xoshiro(42); # initialise random number generator (optional, but recommended)
julia> Ls = (2π, 2π, 2π); # domain dimensions
julia> vn_rms = 1.0; # typical magnitude (rms value) of normal fluid velocity components
julia> vn = FourierBandVectorField(undef, Ls; kmin = 0.1, kmax = 1.5) # create field with non-zero Fourier wavevectors kmin ≤ |k⃗| ≤ kmax
FourierBandVectorField{Float64, 3} with 9 independent Fourier coefficients in |k⃗| ∈ [1.0, 1.4142]
julia> SyntheticFields.init_coefficients!(rng, vn, vn_rms); # randomly set non-zero Fourier coefficients of the velocity field
julia> forcing = NormalFluidForcing(vn; α = 0.8, α′ = 0)
NormalFluidForcing{Float64} with:
├─ Normal velocity field: FourierBandVectorField{Float64, 3} with 9 independent Fourier coefficients in |k⃗| ∈ [1.0, 1.4142]
└─ Friction coefficients: α = 0.8 and α′ = 0.0
VortexPasta.Forcing.FourierBandForcing
— TypeFourierBandForcing <: AbstractForcing
FourierBandForcing(vn::FourierBandVectorField; α, α′ = 0, filtered_vorticity = false)
Forcing due to mutual friction of a normal fluid with a Fourier-filtered superfluid velocity.
This forcing is similar to NormalFluidForcing
, but tries to only affect scales within a given band [kmin, kmax]
in Fourier space. This is achieved by a normal fluid velocity field represented by a FourierBandVectorField
, and by a modified Schwarz's equation in which only a coarse-grained superfluid flow is taken into account in the estimation of the mutual friction term.
Concretely, the vortex line velocity according to this forcing type is:
\[\frac{\mathrm{d}\bm{s}}{\mathrm{d}t} = \bm{v}_{\text{L}} = \bm{v}_{\text{s}} + \bm{v}_{\text{f}}\]
The forcing velocity is of the form:
\[\bm{v}_{\text{f}} = α \bm{s}' × \bm{v}_{\text{ns}}^{>} - α' \bm{s}' × \left( \bm{s}' × \bm{v}_{\text{ns}}^{>} \right)\]
where $\bm{v}_{\text{ns}}^{>} = \bm{v}_{\text{n}} - \bm{v}_{\text{s}}^{>}$ is a filtered slip velocity. In practice, the filtered velocity is active within the same wavenumber range [kmin, kmax]
where vn
is defined. See NormalFluidForcing
for other definitions.
Using a filtered vorticity field
To further ensure that this forcing only affects the chosen range of scales, one can pass filtered_vorticity = true
, which will replace the local unit tangent $\bm{s}'$ with a normalised coarse-grained vorticity. This corresponds to setting $\bm{s}' = \bm{ω}^{>} / |\bm{ω}^{>}|$ where $\bm{ω}^{>}$ is the Fourier-filtered vorticity field.
VortexPasta.Forcing.FourierBandForcingBS
— TypeFourierBandForcingBS <: AbstractForcing
FourierBandForcingBS(; kmin, kmax, α, ε_target)
Forcing based on Biot–Savart energetics within a given range of wavenumbers.
This type of forcing does not rely on an imposed normal fluid velocity. Instead, it starts from the functional derivative of the energy at a given wavevector $\bm{k}$ with respect to the vortex positions (according to the Biot–Savart law), and applies a velocity that ensures energy to increase (if $α > 0$ or $ε_{\text{target}} > 0$) at those wavevectors.
One should pass either α
or ε_target
but never both. They should be positive for energy injection (negative values lead to energy dissipation):
α
(\alpha
) is a non-dimensional coefficient which directly sets the amplitude of the forcing velocity;ε_target
(\varepsilon_target
) has the units of an energy injection rate. In this case, the amplitude $α$ will be adjusted over time in order to keep a roughly constant energy injection rate (which in general will not be equal toε_target
, see remarks below).
Extended help
Forcing definition
This forcing attempts to increase the kinetic energy at selected wavenumbers. Its definition starts from the expression for the kinetic energy at wavenumber $\bm{k}$:
\[E(\bm{k}) = \frac{1}{2} |\bm{v}(\bm{k})|^2 = \frac{1}{2k^2} |\bm{ω}(\bm{k})|^2\]
The idea is to translate the vortex positions by $\bm{s}(ξ) → \bm{s}(ξ) + δ\bm{s}(ξ)$ so that the energy $E(\bm{k})$ increases. To determine such a displacement, one can look at the functional derivative of $E(\bm{k})$ with respect to the positions $\bm{s}$:
\[\begin{align*} \frac{δE(\bm{k})}{δ\bm{s}} &= \frac{1}{2k^2} \bm{ω}(\bm{k}) ⋅ \frac{δ\bm{ω}^*(\bm{k})}{δ\bm{s}} + \text{c.c.}, \\ &= \frac{1}{2} \bm{ψ}(\bm{k}) ⋅ \frac{δ\bm{ω}^*(\bm{k})}{δ\bm{s}} + \text{c.c.}, \end{align*}\]
where $()^*$ denotes a complex conjugate and "c.c." means the complex conjugate of the first term. Here we have used $\bm{ω}(\bm{k}) = k^2 \bm{ψ}(\bm{k})$, which corresponds in physical space to $\bm{ω} = -∇² \bm{ψ}$.
The functional derivative of vorticity in Fourier space is:
\[\begin{align*} \frac{δ\bm{ω}^*(\bm{k})}{δ\bm{s}} &= \frac{Γ}{V} \frac{δ}{δ\bm{s}} ∮ e^{+i \bm{k} ⋅ \bm{s}(ξ)} \bm{s}(ξ)' \, \text{d}ξ \\ &= \frac{Γ}{V} \frac{δ}{δ\bm{s}} ∮ \bm{ℒ}[\bm{s}(ξ), \bm{s}(ξ)'] \, \text{d}ξ \\ &= \frac{Γ}{V} \left[ \frac{∂\bm{ℒ}}{∂\bm{s}} - \frac{\text{d}}{\text{d}ξ} \frac{∂\bm{ℒ}}{∂\bm{s}'} \right] \\ &= \frac{i Γ}{V} e^{+i \bm{k} ⋅ \bm{s}} \left[ \bm{k} ⊗ \bm{s}' - (\bm{k} ⋅ \bm{s}') I \right] = \frac{i Γ}{V} \bm{B}(\bm{k}, \bm{s}, \bm{s}') \end{align*}\]
where $\bm{B}$ is a $3×3$ complex matrix which has dimensions of an inverse length ($L^{-1}$).
The functional derivative of $E(\bm{k})$ can now be written as:
\[\begin{align*} \frac{δE(\bm{k})}{δ\bm{s}} &= \frac{Γ}{2V} i \bm{B} \bm{ψ}(\bm{k}) + \text{c.c.} \\ &= \frac{Γ}{2V} [\bm{s}' × \bm{v}(\bm{k})] e^{i \bm{k} ⋅ \bm{s}} + \text{c.c.} \\ &= ℜ \left\{ \frac{Γ}{V} [\bm{s}' × \bm{v}(\bm{k})] e^{i \bm{k} ⋅ \bm{s}} \right\} \end{align*}\]
Finally, the idea is to advect the filaments with a velocity which is parallel to this result for each forced $\bm{k}$. One can write this velocity as
\[\bm{v}_{\text{f}}(\bm{k}, \bm{s}) = α \, ℜ \left\{ [\bm{s}' × \bm{v}(\bm{k})] e^{i \bm{k} ⋅ \bm{s}} \right\} = α \, \bm{v}_0(\bm{k}, \bm{s})\]
where $α$ is a non-dimensional parameter setting the forcing amplitude.
Estimating the energy injection rate
One can also try to estimate an energy injection rate at wavevector $\bm{k}$ associated to this velocity:
\[\frac{\text{d}E(\bm{k})}{\text{d}t} = ∮ \frac{δE(\bm{k})}{δ\bm{s}} ⋅ \frac{\text{d}\bm{s}}{\text{d}t} \, \mathrm{d}ξ = α \frac{Γ}{V} ∮ |\bm{v}_0|^2 \, \mathrm{d}ξ\]
In general, this estimate may be quite inaccurate since the forcing can also affect the energy at wavevectors other than $\bm{k}$. But still, this estimate is the one used when $ε_{\text{target}}$ is given, and may allow to obtain a roughly constant energy injection rate (even if it's generally different than the "target" one).
Abstract types
VortexPasta.Forcing.AbstractForcing
— TypeAbstractForcing
Abstract type representing a forcing method.
Internals
VortexPasta.Forcing.apply!
— FunctionForcing.apply!(forcing::AbstractForcing, vs::AbstractVector{<:Vec3}, f::AbstractFilament; [scheduler])
Apply forcing to a single filament f
with self-induced velocities vs
.
At output, the vs
vector is overwritten with the actual vortex line velocities.
The optional scheduler
keyword can be used to parallelise computations using one of the schedulers defined in OhMyThreads.jl.
Forcing.apply!(forcing::NormalFluidForcing, vs, vn, tangents; [scheduler])
This variant can be used in the case of a NormalFluidForcing
if one already has precomputed values of the normal fluid velocity and local unit tangents at filament points.