Computing Velocities
Sources and Targets
Velocity computations in vortex models essentially boils down to pairwise interactions between sources and targets. We may be interested in how a system of vortex elements induces velocity on at point, at multiple points, on other vortex elements, or on itself.
The three key functions for computing velocities are
induce_velocity(target, source, t)
induce_velocity!(velocity, target, source, t)
self_induce_velocity!(velocity, source, t)
The !
suffix in the last two function signatures indicate that the velocity
argument will be overwritten by the results of the computation. In most cases, the induced velocities will be indpendent of the time t
, but it is included in the function signatures for flexibility.
Sources of velocity can be any one of:
a single vortex element, e.g.
julia> src = Vortex.Point(im, 1.0); julia> induce_velocity(0.0 + 0.0im, src, 0.0) 0.15915494309189535 - 0.0im
an array of homogenous vortex types, e.g.
julia> srcs = Vortex.Point.([im, 1.0], 1.0); julia> induce_velocity(0.0 + 0.0im, srcs, 0.0) 0.15915494309189535 - 0.15915494309189535im
a tuple of different vortex types, e.g.
julia> srcs₂ = Vortex.Point.([2im, 2.0], -2.0); julia> sys = (srcs, srcs₂); julia> induce_velocity(0.0 + 0.0im, sys, 0.0) 0.0 + 0.0im
In the examples above, the target was just complex number 0.0 + 0.0im
. However we can also have
an array of complex numbers, e.g.
julia> targets = ComplexF64.(1:3); julia> induce_velocity(targets, src, 0.0) 3-element Vector{ComplexF64}: 0.07957747154594767 + 0.07957747154594767im 0.03183098861837907 + 0.06366197723675814im 0.01591549430918953 + 0.0477464829275686im
an array of vortex elements, e.g.
julia> targets₂ = Vortex.Point.(im*(1.0:3), 1.0); julia> induce_velocity(targets₂, src, 0.0) 3-element Vector{ComplexF64}: 0.0 + 0.0im -0.15915494309189535 + 0.0im -0.07957747154594767 + 0.0im
a tuple with any of the above, e.g.
julia> targets₃ = Vortex.Point.(-3.0:-1, -1.0); julia> sys = (targets, (targets₂, targets₃)); julia> induce_velocity(sys, src, 0.0) (ComplexF64[0.07957747154594767 + 0.07957747154594767im, 0.03183098861837907 + 0.06366197723675814im, 0.01591549430918953 + 0.0477464829275686im], (ComplexF64[0.0 + 0.0im, -0.15915494309189535 + 0.0im, -0.07957747154594767 + 0.0im], ComplexF64[0.01591549430918953 - 0.0477464829275686im, 0.03183098861837907 - 0.06366197723675814im, 0.07957747154594767 - 0.07957747154594767im]))
Since the structure of these targets can get complicated, e.g. nested tuples), the library also provides a set of functions for creating and resizing the velocity
variable for in-place computations. For example:
julia> vels = allocate_velocity(sys)
(ComplexF64[0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im], (ComplexF64[0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im], ComplexF64[0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im]))
julia> induce_velocity!(vels, sys, src, 0.0)
(ComplexF64[0.07957747154594767 + 0.07957747154594767im, 0.03183098861837907 + 0.06366197723675814im, 0.01591549430918953 + 0.0477464829275686im], (ComplexF64[0.0 + 0.0im, -0.15915494309189535 + 0.0im, -0.07957747154594767 + 0.0im], ComplexF64[0.01591549430918953 - 0.0477464829275686im, 0.03183098861837907 - 0.06366197723675814im, 0.07957747154594767 - 0.07957747154594767im]))
The remaining sections of this page list the documentation for all the relevant methods for computing velocities. More detailed examples that show these methods working together can be found in the getting started guide and the Jupyter notebooks.
Methods
PotentialFlow.Motions.allocate_velocity
— Functionallocate_velocity(srcs)
Allocate arrays of ComplexF64
to match the structure of srcs
Example
julia> points = Vortex.Point.(rand(ComplexF64, 2), rand(2));
julia> blobs = Vortex.Blob.(rand(ComplexF64, 3), rand(3), rand(3));
julia> allocate_velocity(points)
2-element Array{Complex{Float64},1}:
0.0 + 0.0im
0.0 + 0.0im
julia> allocate_velocity((points, blobs))
(Complex{Float64}[0.0+0.0im, 0.0+0.0im], Complex{Float64}[0.0+0.0im, 0.0+0.0im, 0.0+0.0im])
PotentialFlow.Motions.reset_velocity!
— Functionreset_velocity!(vels[, srcs])
Set all velocities in vels
to zero
If srcs
is provided, then the arrays in vels
are resized their source counterpart, if necessary.
Example
julia> ẋs = (rand(ComplexF64, 1), rand(ComplexF64, 1))
(Complex{Float64}[0.236033+0.346517im], Complex{Float64}[0.312707+0.00790928im])
julia> points = Vortex.Point.(rand(ComplexF64, 2), rand(2));
julia> blobs = Vortex.Blob.(rand(ComplexF64, 3), rand(3), rand(3));
julia> reset_velocity!(ẋs, (points, blobs));
julia> ẋs
(Complex{Float64}[0.0+0.0im, 0.0+0.0im], Complex{Float64}[0.0+0.0im, 0.0+0.0im, 0.0+0.0im])
PotentialFlow.Motions.induce_velocity
— Functioninduce_velocity(target, element, time)
Compute the velocity induced by element
on target
target
can be:
- a
ComplexF64
- a subtype of
Vortex.PointSource
- an array or tuple of vortex elements
while the element
can be:
- any subtype of
Vortex.Element
- an array or tuple of vortex elements
Example
julia> z = rand(ComplexF64)
0.23603334566204692 + 0.34651701419196046im
julia> point = Vortex.Point(z, rand());
julia> srcs = Vortex.Point.(rand(ComplexF64, 10), rand(10));
julia> induce_velocity(z, srcs[1], 0.0)
0.08722212007570912 + 0.14002850279102955im
julia> induce_velocity(point, srcs[1], 0.0)
0.08722212007570912 + 0.14002850279102955im
julia> induce_velocity(z, srcs, 0.0)
-0.4453372874427177 - 0.10592646656959151im
julia> induce_velocity(point, srcs, 0.0)
-0.4453372874427177 - 0.10592646656959151im
PotentialFlow.Motions.induce_velocity!
— Functioninduce_velocity!(vels, target, element, time)
Compute the velocity induced by element
on target
and store the result in vels
vels
should be the output of a call to allocate_velocity
, target
can be an array or tuple of vortex elements, while the element
can be:
- any subtype of
Vortex.Element
- an array or tuple of vortex elements
Example
julia> cluster₁ = Vortex.Point.(rand(ComplexF64, 5), rand(5));
julia> cluster₂ = Vortex.Point.(rand(ComplexF64, 5), rand(5));
julia> targets = (cluster₁, cluster₂);
julia> sources = Vortex.Blob.(rand(ComplexF64), rand(10), 0.1);
julia> ẋs = allocate_velocity(targets);
julia> induce_velocity!(ẋs, targets, sources, 0.0)
(Complex{Float64}[-1.28772-1.82158im, 1.9386-1.64147im, -1.56438+1.57158im, -0.626254+0.375842im, -0.806568-0.213201im], Complex{Float64}[-0.583672-2.26031im, -0.329778-1.43388im, 0.426927+1.55352im, -0.93755+0.241361im, -1.08949-0.35598im])
PotentialFlow.Motions.self_induce_velocity!
— Functionself_induce_velocity!(vels, elements, time)
Compute the self induced velocity of one or more vortex elements
This involves a recursive call to self_induce_velocity!
and pairwise calls to mutually_induce_velocity!
.
Example
julia> points = Vortex.Point.([-1, 1], 1.0)
2-element Array{PotentialFlow.Points.Point{Float64},1}:
Vortex.Point(-1.0 + 0.0im, 1.0)
Vortex.Point(1.0 + 0.0im, 1.0)
julia> vels = allocate_velocity(points)
2-element Array{Complex{Float64},1}:
0.0 + 0.0im
0.0 + 0.0im
julia> self_induce_velocity!(vels, points, 0.0) # should be ±0.25im/π
2-element Array{Complex{Float64},1}:
0.0 - 0.07957747154594767im
0.0 + 0.07957747154594767im
PotentialFlow.Motions.mutually_induce_velocity!
— Functionmutually_induce_velocity!(vs₁, vs₂, e₁, e₂, t)
Compute the mutually induced velocities between e₁
and e₂
at time t
and store the results in vs₁
and vs₂
The default implementation simply calls induce_velocity!
twice. This method is meant to be overwritten to take advantage of symmetries in certain pairwise vortex interations. For example, the velocity kernel for a point vortex is antisymmetric, so in computing the mutually induced velocities of two arrays of point vortices, we can half the number of calls to the velocity kernel.
PotentialFlow.Motions.advect!
— Functionadvect!(srcs₊, srcs₋, vels, Δt)
Moves the elements in srcs₋
by their corresponding velocity in vels
over the interval Δt
and store the results in src₊
.
srcs₋
and srcs₊
can be either a array of vortex elements or a tuple.
Example
julia> points₋ = [Vortex.Point(x + 0im, 1.0) for x in 1:5];
julia> points₊ = Vector{Vortex.Point}(undef, 5);
julia> vels = [ y*im for y in 1.0:5 ];
julia> advect!(points₊, points₋, vels, 1e-2);
julia> points₊
5-element Array{PotentialFlow.Points.Point{Float64},1}:
Vortex.Point(1.0 + 0.01im, 1.0)
Vortex.Point(2.0 + 0.02im, 1.0)
Vortex.Point(3.0 + 0.03im, 1.0)
Vortex.Point(4.0 + 0.04im, 1.0)
Vortex.Point(5.0 + 0.05im, 1.0)
PotentialFlow.Motions.advect
— Functionadvect(src::Element, velocity::ComplexF64, Δt)
Return a new element that represents src
advected by velocity
over Δt
.
If this method is implemented by any type T
where kind(T)
is a Singleton
, then an array of type AbstractArray{T}
can be passed in the first two arguments of advect!
. Note that this method is usually only defined for singleton elements
Example
julia> point = Vortex.Point(1.0 + 0.0, 1.0);
julia> advect(point, 1.0im, 1e-2)
Vortex.Point(1.0 + 0.01im, 1.0)