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.0iman 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.15915494309189535ima 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.0477464829275686iman 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.0ima 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.10592646656959151imPotentialFlow.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.07957747154594767imPotentialFlow.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)