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

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_velocityFunction
allocate_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])
source
PotentialFlow.Motions.reset_velocity!Function
reset_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])
source
PotentialFlow.Motions.induce_velocityFunction
induce_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
source
PotentialFlow.Motions.induce_velocity!Function
induce_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])
source
PotentialFlow.Motions.self_induce_velocity!Function
self_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
source
PotentialFlow.Motions.mutually_induce_velocity!Function
mutually_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.

source
PotentialFlow.Motions.advect!Function
advect!(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)
source
PotentialFlow.Motions.advectFunction
advect(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)
source

Index