Skip to content

The vortex filament model

The standard vortex filament model (VFM) describes the motion of thin vortex lines in three-dimensional space.

Vortex lines are assumed to be very thin with respect to the scales of interest, such that they can be effectively described as spatial curves.

The Biot–Savart law

In the VFM, each vortex line induces a velocity field around it given by the Biot–Savart law:

v(x)=Γ4πC(sx)×ds|sx|3

where Γ is the vortex circulation (equal to κ for quantum vortices), and s is a point along the vortex line. Here C denotes the whole set of vortex lines in the system.

Mathematically, the above equation derives from the vorticity field:

ω(x)×v(x)=ΓCδ(sx)ds

where δ is Dirac delta function. That is, the vorticity field is singular and localised at the locations of quantum vortices.

The Biot–Savart law describes in particular the motion induced by vortex filaments on themselves and on surrounding vortex lines. The VFM thus describes the collective motion of a set of mutually-interacting vortex filaments which obey the Biot–Savart law. Note that the Biot–Savart integral is singular when evaluated at a vortex location sC, and the integral must be desingularised by taking into account the finite thickness of the vortex core. The VFM also accounts for vortex reconnections, which occur when two vortex segments are sufficiently close to each other and which change the topology of the vortex system.

Desingularisation

In the VFM, one usually wants to evaluate the Biot–Savart law at locations x=s0 on the vortex. It is clear that the Biot–Savart integral, as written above, is singular when evaluated at a point s0 on the curve.

The divergence of the Biot–Savart integral is of course unphysical, and is related to the fact that the actual thickness of vortex lines not really infinitesimal but finite. The standard way of accounting for the radius a of the vortex core is to split the integral into local and non-local parts:

v(s0)=Γ4πC0(ss0)×ds|ss0|3+Γ4πCC0(ss0)×ds|ss0|3=vlocal(s0)+vnon-local(s0)

Here C0 denotes a portion of the set of curves C which is in the neighbourhood of the point of interest s0.

To illustrate this, the figure below shows a trefoil knot curve, or rather its projection on the XY plane. Note that here we represent a discretised version of the curve, where the number of degrees of freedom is finite and controlled by the positions of the markers.

Code for this figure
julia
using CairoMakie
CairoMakie.activate!(pt_per_unit = 1.0)
Makie.set_theme!()

using VortexPasta.Filaments
using VortexPasta.Filaments: Vec3
using VortexPasta.PredefinedCurves: define_curve, TrefoilKnot

trefoil = define_curve(TrefoilKnot())
N = 32
i = (N ÷ 2) + 3
refinement = 8
colours = Makie.wong_colors()
f = Filaments.init(trefoil, ClosedFilament, N, CubicSplineMethod())

fig = Figure(; Text = (fontsize = 24,), size = (600, 500))
ax = Axis(fig[1, 1]; aspect = DataAspect(), xlabel = L"x", ylabel = L"y")
hidexdecorations!(ax; label = false, ticklabels = false, ticks = false)
hideydecorations!(ax; label = false, ticklabels = false, ticks = false)

# Plot complete filament in grey
let color = (:grey, 0.6)
    plot!(ax, f; refinement, color, linewidth = 1.5, arrows3d = (;))
    text!(
        ax, f(i ÷ 2 + 1, 0.6);
        text = L"\bar{\mathcal{C}}_{\!i}", align = (:right, :bottom), color,
        fontsize = 28,
    )
end

# Plot point of interest and local segment around it
let color = colours[2], arrowcol = colours[3]
    # Plot nodes (i - 1):(i + 1)
    scatter!(ax, getindex.(Ref(f), (i - 1):(i + 1)); color)
    text!(
        ax, f[i - 1] + Vec3(0.08, 0.0, 0.0);
        text = L"\mathbf{s}_{i - 1}", align = (:left, :center), color,
    )
    text!(
        ax, f[i] + Vec3(0.08, 0.0, 0.0);
        text = L"\mathbf{s}_i", align = (:left, :center), color,
    )
    text!(
        ax, f[i + 1] + Vec3(-0.08, 0.08, 0.0);
        text = L"\mathbf{s}_{i + 1}", align = (:right, :center), color,
    )

    # Plot local segments
    let ζs = range(0, 1; length = refinement + 1)
        lines!(ax, f.(i - 1, ζs); color, linewidth = 3)
        lines!(ax, f.(i, ζs); color, linewidth = 3)
        text!(
            f(i - 1, 0.5);
            text = L"ℓ^{-}", align = (:right, :bottom), color,
        )
        text!(
            f(i, 0.6) + Vec3(-0.08, 0, 0);
            text = L"ℓ^{+}", align = (:right, :center), color,
        )
        text!(
            ax, f(i - 1, 0.7) + Vec3(0.2, 0.0, 0.0);
            text = L"\mathcal{C}_{\!i}", align = (:left, :top), color,
            fontsize = 28,
        )
    end

    # Plot tangent and curvature vectors (assuming this is a 2D plot...)
    s⃗ = f[i]
= f[i, UnitTangent()]
    ρ⃗ = f[i, CurvatureVector()]
    lengthscale = 1.2
    arrows2d!(ax, [s⃗[1]], [s⃗[2]], [t̂[1]], [t̂[2]]; color = arrowcol, shaftwidth = 1.5, tipwidth = 10, lengthscale)
    arrows2d!(ax, [s⃗[1]], [s⃗[2]], [ρ⃗[1]], [ρ⃗[2]]; color = arrowcol, shaftwidth = 1.5, tipwidth = 10, lengthscale)
    text!(
        ax, s⃗ + lengthscale * t̂;
        text = L"\mathbf{s}′", align = (:left, :center), color = arrowcol,
        offset = (2, -4),
    )
    text!(
        ax, s⃗ + lengthscale * ρ⃗;
        text = L"\mathbf{s}″", align = (:right, :center), color = arrowcol,
        offset = (4, 6),
    )
end

save("trefoil_local.png", fig)

Here, to evaluate the velocity induced by the trefoil vortex on its point si, we split the curve into a local part Ci (orange) and a non-local part C¯i=CCi (grey). The non-local part is far from the singularity, so there is no need to modify the Biot–Savart integral as written above. As for the local part, we can approximate it using a Taylor expansion of the Biot–Savart integral about si and truncating the integral at a small distance ϵa from the singularity. See for instance Arms and Hama (1965).

More explicitly, from a Taylor expansion of s(ξ) close to si=s(ξi), one can show that the Biot–Savart integrand is approximately

[s(ξ)si]×s(ξ)|s(ξ)si|3si×si2|ξξi|,

where ξ is the curve arc length and derivatives (primes) are with respect to ξ. Note that si and si are respectively the unit tangent and curvature vectors at si (see figure).

Now, if one integrates e.g. from s(ξi+ϵ) to si+1=s(ξi+1), one gets:

vi+Γ4πsi×si2ξi+ϵξi+1dξξξi=Γ4πsi×si2ln(+ϵ),

where +=ξi+1ξi is the length of the curve segment sisi+1 (see figure). Doing something similar for the segment si1s(ξiϵ) and adding both contributions, we obtain the local velocity:

vlocal(si)=Γ4πsi×siln(+ϵ)=Γ4πsi×si[ln(2+a)Δ],

where we have taken the cut-off distance to be ϵ=eΔa2 (Saffman, 1993; §11.2). Here Δ is a coefficient which depends on the form of the vorticity profile within the vortex core (see the vortex ring tutorial).

In the code, the local velocity is sometimes referred to as the LIA (local induction approximation) term, as it has been historically used as a fast (and incomplete) approximation to the full Biot–Savart integral.

Streamfunction and energy

Everything that has been discussed until now applies to the velocity derived from the Biot–Savart law. Sometimes we may also be interested in the streamfunction vector ψ. In particular, the streamfunction values on vortex lines can be used to estimate the total energy of the vortex filament system.

Streamfunction from vortex lines

In three dimensions, the streamfunction is a vector field which is directly related to the velocity and vorticity by

v=×ψω=×v=²ψ

That is, if one knows the vorticity field ω, then the streamfunction is the solution of a Poisson equation with the vorticity as source term. The solution can be written in terms of the Green's function G(r)=1/(4πr) associated to the 3D Poisson equation:

ψ(x)=(Gω)(x)=G(xy)ω(y)d3y=14πω(y)|xy|d3y

Using the fact that the vorticity has the form ω(x)=ΓCδ(sx)ds leads to an expression relating the streamfunction to the geometry of vortex lines:

ψ(x)=ΓCG(xs)ds=Γ4πCds|xs|

Note that the usual Biot–Savart law for the velocity can be recovered by taking the curl of the above expression:

v(x)=×ψ(x)=ΓCG(xs)×ds=Γ4πC(xs)×ds|xs|3

where we have used G(r)=r/(4πr3).

Connection with kinetic energy

The kinetic energy of the system is given by

E=ρ2v(x)v(x)d3x=ρ2ψ(x)ω(x)d3x

where the second equality is obtained using integration by parts and assuming we're in a periodic domain (or, alternatively, that the velocity goes to zero far from the vortices), and ρ is the fluid density. As before, using ω(x)=ΓCδ(sx)ds leads to

E=ρΓ2Cψ(s)ds

That is, the energy is simply the line integral of the tangential component of the streamfunction along vortex filaments.

Note that, if one uses the expression for ψ(x) from the previous section, one can directly write the total energy from the configuration of the vortex lines:

E=ρΓ28πCCdsds1|ss1|

where the prime over the integral is to account for the desingularisation of the streamfunction integral (see section below).

Desingularisation of the streamfunction integral

As for the velocity, the Biot–Savart integral for the streamfunction is singular when evaluated on a vortex line. We can use the same approach to desingularise it, by splitting the integral onto a local and a non-local part.

Using the same notation as for the velocity, a leading-order Taylor expansion of the integrand leads to:

ψi+Γ2πsi2ξi+ϵξi+1dξξξi=Γ2πsi2ln(+ϵ)

and then to:

ψlocal(si)=Γsi2πln(+ϵ)

Note that, here, the cut-off distance ϵ is not necessarily the same as for the velocity. In fact, if one considers a system of non-colliding vortex rings, one can show that, for the system to be Hamiltonian, one needs to take ϵ=eΔ1a2 (as verified in the example below). This finally leads to:

ψlocal(si)=Γsi2π[ln(2+a)(Δ1)]

Example: vortex ring

For a circular vortex ring of radius R, one can analytically compute the non-local Biot–Savart integrals. Under the approximation that the local segments are much smaller than the vortex radius (+R), one obtains:

vnon-local(si)=Γ4πsi×siln(8R)ψnon-local(si)=Γ2πsi[ln(8R)2]

where we have defined =2+. In the expression for the velocity, si×si=z^/R, where z^ is the direction orthogonal to the plane where the ring lives. As a reminder, si is the local unit tangent vector, while the second derivative si corresponds to the curvature vector (oriented towards the "interior" of the curve) and whose magnitude is 1/R.

Adding the local integrals obtained by desingularisation leads to:

v(si)=Γz^4πR[ln(8Ra)Δ]ψ(si)=Γsi2π[ln(8Ra)(Δ+1)]

The first expression obtained is the well-known self-induced velocity of a thin vortex ring.

From the second expression, we can easily obtain the energy associated to the vortex ring:

E=ρΓ2R2[ln(8Ra)(Δ+1)]

We can finally check that, with the desingularisation procedure described in the previous sections, the vortex ring obeys Hamilton's equation v=E/p (Roberts and Donnelly, 1970). Here p is the vortex impulse (Saffman, 1993):

p=ρΓ2Cs×ds

For a vortex ring, p=ρΓπR2z^, that is, the impulse is directly related to the area enclosed by the vortex.

Noting that

ER=ρΓ22[ln(8Ra)Δ]andpR=2ρΓπRz^,

we finally obtain that

Ep=Γ4πR[ln(8Ra)Δ]=vring

Energy in open domains

In open (non-periodic and unbounded) domains, assuming that the velocity field goes to zero sufficiently far from the vortices, a commonly used alternative expression for the energy is (Saffman, 1993):

E=ρΓCv(s×ds)

which only requires knowing the velocity v on the vortex filaments. Besides the above assumptions, this assumes that the fluid is incompressible everywhere.

However, this definition does not satisfy Hamilton's equation, meaning that this "energy" may present fluctuations in time in cases where the previous definition will be conserved.

This can be easily shown for the case of a vortex ring, in which case applying this definition to the expression for the vortex ring velocity leads to

E=ρΓ2R2[ln(8Ra)Δ]

which overestimates the actual vortex ring energy by a ρΓ2R/2 term (and thus doesn't satisfy Hamilton's equation).

These differences might be explained by the fact that this second definition doesn't take into account the internal structure of the vortices, unlike the first definition which does this via the local contribution to the streamfunction.

References

A classical reference for the VFM is Schwarz (1985), while a more modern review is given by Hänninen and Baggaley (2014). Details on the streamfunction vector, its desingularisation and the choice of cut-off to ensure energy conservation can be found in Polanco (2025).