@@ -6,10 +6,14 @@ Defines methods for injecting energy onto a system of vortices.
66module Forcing
77
88using .. Filaments: AbstractFilament, UnitTangent
9- using .. SyntheticFields: SyntheticFields # for docs only
9+ using .. BiotSavart: BiotSavart, BiotSavartCache
10+ using .. SyntheticFields: SyntheticFields, FourierBandVectorField
1011using LinearAlgebra: ×
12+ using OhMyThreads: Scheduler, SerialScheduler, tforeach
1113
12- export AbstractForcing, NormalFluidForcing
14+ using Adapt: adapt
15+
16+ export AbstractForcing, NormalFluidForcing, FourierBandForcing
1317
1418"""
1519 AbstractForcing
@@ -18,157 +22,28 @@ Abstract type representing a forcing method.
1822"""
1923abstract type AbstractForcing end
2024
25+ init_cache (forcing:: Nothing , args... ) = nothing # called when forcing is disabled
26+
2127"""
22- Forcing.apply!(forcing::AbstractForcing, vs::AbstractVector{<:Vec3}, f::AbstractFilament)
28+ Forcing.apply!(forcing::AbstractForcing, vs::AbstractVector{<:Vec3}, f::AbstractFilament; [scheduler] )
2329
2430Apply forcing to a single filament `f` with self-induced velocities `vs`.
2531
2632At output, the `vs` vector is overwritten with the actual vortex line velocities.
2733
34+ The optional `scheduler` keyword can be used to parallelise computations using one of the
35+ [schedulers defined in OhMyThreads.jl](https://juliafolds2.github.io/OhMyThreads.jl/stable/refs/api/#Schedulers).
36+
2837---
2938
30- Forcing.apply!(forcing::NormalFluidForcing, vs, vn, tangents)
39+ Forcing.apply!(forcing::NormalFluidForcing, vs, vn, tangents; [scheduler] )
3140
3241This variant can be used in the case of a [`NormalFluidForcing`](@ref) if one already has
3342precomputed values of the normal fluid velocity and local unit tangents at filament points.
34-
35- The normal fluid velocities may be computed using [`Forcing.get_velocities!`](@ref).
3643"""
3744function apply! end
3845
39- @doc raw """
40- NormalFluidForcing <: AbstractForcing
41- NormalFluidForcing(vn::Function; α, α′ = 0)
42-
43- Forcing due to mutual friction with a normal fluid.
44-
45- The normal fluid is represented by a function `vn` which should take a position
46- `x⃗::SVector{N, T}` and return a velocity `v⃗::SVector{N, T}` (`N` is the number of
47- dimensions, usually `N = 3`).
48-
49- In particular, the function could be a synthetic velocity field from the
50- [`SyntheticFields`](@ref) module (see below for examples).
51-
52- This type of forcing defines an external velocity ``\b m{v}_{\t ext{f}}`` affecting vortex
53- motion, so that the actual vortex velocity ``\b m{v}_{\t ext{L}}`` is
54-
55- ```math
56- \f rac{\m athrm{d}\b m{s}}{\m athrm{d}t} = \b m{v}_{\t ext{L}} = \b m{v}_{\t ext{s}} + \b m{v}_{\t ext{f}}.
57- ```
58-
59- Here ``\b m{v}_{\t ext{s}}`` is the self-induced velocity obtained by applying Biot–Savart's law.
60-
61- The forcing velocity is of the form:
62-
63- ```math
64- \b m{v}_{\t ext{f}} =
65- α \b m{s}' × (\b m{v}_{\t ext{n}} - \b m{v}_{\t ext{s}})
66- - α' \b m{s}' × \l eft[ \b m{s}' × (\b m{v}_{\t ext{n}} - \b m{v}_{\t ext{s}}) \r ight]
67- ```
68-
69- where ``\b m{s}'`` is the local unit tangent vector, and ``α`` and ``α'`` are non-dimensional
70- coefficients representing the intensity of Magnus and drag forces.
71-
72- # Example
73-
74- Define a mutual friction forcing based on a large-scale normal fluid velocity field
75- (see [`SyntheticFields.FourierBandVectorField`](@ref)):
76-
77- ```jldoctest
78- julia> using VortexPasta.Forcing: NormalFluidForcing
79-
80- julia> using VortexPasta.SyntheticFields: SyntheticFields, FourierBandVectorField
81-
82- julia> using Random: Xoshiro
83-
84- julia> rng = Xoshiro(42); # initialise random number generator (optional, but recommended)
85-
86- julia> Ls = (2π, 2π, 2π); # domain dimensions
87-
88- julia> vn_rms = 1.0; # typical magnitude (rms value) of normal fluid velocity components
89-
90- julia> vn = FourierBandVectorField(undef, Ls; kmin = 0.1, kmax = 1.5) # create field with non-zero Fourier wavevectors kmin ≤ |k⃗| ≤ kmax
91- FourierBandVectorField{Float64, 3} with 9 independent Fourier coefficients in |k⃗| ∈ [1.0, 1.4142]
92-
93- julia> SyntheticFields.init_coefficients!(rng, vn, vn_rms); # randomly set non-zero Fourier coefficients of the velocity field
94-
95- julia> forcing = NormalFluidForcing(vn; α = 0.8, α′ = 0)
96- NormalFluidForcing{Float64} with:
97- ├─ Magnus force coefficient: α = 0.8
98- ├─ Drag force coefficient: α′ = 0.0
99- └─ Normal velocity field: FourierBandVectorField{Float64, 3} with 9 independent Fourier coefficients in |k⃗| ∈ [1.0, 1.4142]
100- ```
101-
102- """
103- struct NormalFluidForcing{
104- T,
105- VelocityField <: Function , # should return an SVector{N, T}
106- } <: AbstractForcing
107- vn :: VelocityField
108- α :: T
109- α′ :: T
110- end
111-
112- function NormalFluidForcing (vn:: F ; α:: T , α′:: Real = 0 ) where {T <: AbstractFloat , F <: Function }
113- NormalFluidForcing (vn, T (α), T (α′))
114- end
115-
116- function Base. show (io:: IO , f:: NormalFluidForcing{T} ) where {T}
117- (; vn, α, α′,) = f
118- indent = get (io, :indent , 0 )
119- nspaces = max (indent, 1 )
120- spaces = " " ^ nspaces
121- print (io, " NormalFluidForcing{$T } with:" )
122- print (io, " \n $(spaces) ├─ Magnus force coefficient: α = " , α)
123- print (io, " \n $(spaces) ├─ Drag force coefficient: α′ = " , α′)
124- print (io, " \n $(spaces) └─ Normal velocity field: " , vn)
125- end
126-
127- """
128- Forcing.get_velocities!(forcing::NormalFluidForcing, vn::AbstractVector{<:Vec3}, f::AbstractFilament)
129-
130- Evaluate normal fluid velocity at filament nodes.
131-
132- Results are written to `vn`, which should have the same length as the filament `f`.
133- """
134- function get_velocities! (forcing:: NormalFluidForcing , vn:: AbstractVector , f:: AbstractFilament )
135- for i ∈ eachindex (vn, f)
136- vn[i] = forcing. vn (f[i])
137- end
138- vn
139- end
140-
141- function apply! (forcing:: NormalFluidForcing , vs:: AbstractVector , f:: AbstractFilament )
142- eachindex (vs) == eachindex (f) || throw (DimensionMismatch (" lengths of filament and velocity vectors don't match" ))
143- @inline get_at_node (i) = (v⃗ₙ = forcing. vn (f[i]), s⃗′ = f[i, UnitTangent ()])
144- _apply! (get_at_node, forcing, vs)
145- end
146-
147- function apply! (forcing:: NormalFluidForcing , vs:: AbstractVector , vn:: AbstractVector , tangents:: AbstractVector )
148- eachindex (vs) == eachindex (vn) == eachindex (tangents) || throw (DimensionMismatch (" lengths of vectors don't match" ))
149- @inline get_at_node (i) = @inbounds (v⃗ₙ = vn[i], s⃗′ = tangents[i],)
150- _apply! (get_at_node, forcing, vs)
151- end
152-
153- # The first argument is a `get_at_node(i)` function which returns a NamedTuple with fields
154- # (v⃗ₛ, v⃗ₙ, s⃗′) with the superfluid velocity, normal fluid velocity and the local unit
155- # tangent at the node `i` of a filament. This is useful if those quantities have been
156- # precomputed.
157- function _apply! (get_at_node:: F , forcing:: NormalFluidForcing , vs:: AbstractVector ) where {F <: Function }
158- (; α, α′,) = forcing
159- V = eltype (vs) # usually Vec3{T} = SVector{3, T}
160- for i ∈ eachindex (vs)
161- (; v⃗ₙ, s⃗′,) = @inline get_at_node (i) # superfluid velocity, normal fluid velocity and unit tangent
162- v⃗ₛ = vs[i]
163- vₙₛ = V (v⃗ₙ) - V (v⃗ₛ) # slip velocity
164- v_perp = s⃗′ × vₙₛ
165- vf = α * v_perp # velocity due to Magnus force
166- if ! iszero (α′)
167- vf = vf - α′ * s⃗′ × v_perp # velocity due to drag force (it's quite common to set α′ = 0)
168- end
169- vs[i] = v⃗ₛ + vf
170- end
171- vs
172- end
46+ include (" normal_fluid.jl" )
47+ include (" fourier_band.jl" )
17348
17449end
0 commit comments