Plate Motions

The motion of a plate is specified through two data types:

  • RigidBodyMotion is the type that should be used to represent the plate's velocity. For example, in advect!(plate₊, plate₋, platevel, Δt), platevel is of type RigidBodyMotion. It contains the most current values (ċ, c̈, α̇) (the plate's centroid velocity and acceleration, and angular velocity, respectively), as well as a Kinematics type.

  • Kinematics is an abstract type representing a function that takes in a time and returns (ċ, c̈, α̇)

Motion

By default, RigidBodyMotion assumes a constant translational and angular velocity. For example,

julia> motion = Plates.RigidBodyMotion(1.0im, π/2)
Rigid Body Motion:
  ċ = 0.0 + 1.0im
  c̈ = 0.0 + 0.0im
  α̇ = 1.57
  α̈ = 0.0
  Constant (ċ = 0.0 + 1.0im, α̇ = 1.5707963267948966)

Here, Constant is a subtype of Kinematics that returns the same (ċ, c̈, α̇) triple at all times

julia> motion.kin.([0.0, 1.0, 2.0])
3-element Vector{Tuple{ComplexF64, ComplexF64, Float64, ComplexF64}}:
 (0.0 + 1.0im, 0.0 + 0.0im, 1.5707963267948966, 0.0 + 0.0im)
 (0.0 + 1.0im, 0.0 + 0.0im, 1.5707963267948966, 0.0 + 0.0im)
 (0.0 + 1.0im, 0.0 + 0.0im, 1.5707963267948966, 0.0 + 0.0im)

Calling Plates.RigidBodyMotion(1.0im, π/2) is equivalent doing

kin = Plates.RigidBodyMotions.Constant(1.0im, π/2)
motion = Plates.RigidBodyMotion(1.0im, 0.0im, π/2, 0.0, kin)

# output

Rigid Body Motion:
  ċ = 0.0 + 1.0im
  c̈ = 0.0 + 0.0im
  α̇ = 1.57
  α̈ = 0.0
  Constant (ċ = 0.0 + 1.0im, α̇ = 1.5707963267948966)

The next section describes how to construct more interesting kinematics.

Kinematics

The Kinematics type is just an abstract type for functions that take in time and return the (ċ, c̈, α̇) triple. Let's create a MyMotion type that describes a horizontally translating plate that also sinusoidally pitches about its centroid.

import PotentialFlow.Plates.RigidBodyMotions: Kinematics

struct MyMotion <: Kinematics
    U₀::ComplexF64
    ω::Float64
end

(m::MyMotion)(t) = (m.U₀, 0.0im, sin(m.ω*t))

sinusoid = MyMotion(1.0, π/4)

# output

MyMotion(1.0 + 0.0im, 0.7853981633974483)

We can then evaluate sinusoid at different times

julia> sinusoid.([0.0, 1.0, 2.0])
3-element Vector{Tuple{ComplexF64, ComplexF64, Float64}}:
 (1.0 + 0.0im, 0.0 + 0.0im, 0.0)
 (1.0 + 0.0im, 0.0 + 0.0im, 0.7071067811865475)
 (1.0 + 0.0im, 0.0 + 0.0im, 1.0)

Profiles

To make defining complex kinematics a little eaiser, the library also provides a Profile type, an abstract type for real-valued functions of time. Before going into how to define new profiles, we'll first show an example of why we might want to represent functions as a type. We start off with a predefined profile, a smooth ramp:

using Plots
using PotentialFlow.Plates.RigidBodyMotions

ramp = RigidBodyMotions.EldredgeRamp(6)

T = range(-1, 4, length=200)
plot(T, ramp.(T), xlabel = "t", ylabel="Smoothed Ramp",
     legend = :none, linewidth = 2)

Ramp

Now suppose we want to scale the ramp and shift it

shifted_ramp = -(ramp >> 2)

plot(T, shifted_ramp.(T), xlabel = "t", ylabel="Smoothed Ramp",
     legend = :none, linewidth = 2, size=(600,300))

Shifted ramp

then take its derivative

ddt_ramp = d_dt(shifted_ramp)

plot(T, ddt_ramp.(T), xlabel = "t", ylabel="Smoothed Ramp",
     legend = :none, linewidth = 2, size = (600, 200))

ddt ramp

We see that wrapping these functions in a type allows us to operate on them as if they values, making it easier to compose multiple motions together:

ps_ramp = RigidBodyMotions.ColoniusRamp(5)
composed_ramp = ramp - (ps_ramp >> 2)

plot(T, composed_ramp.(T), xlabel = "t", ylabel="Smoothed Ramp",
     legend = :none, linewidth = 2, size = (600, 300))

Composed ramp

Defining a profile

Defining a profile is done in two steps:

  1. Create a subtype of RigidBodyMotions.Profile that contains the relavant parameters, e.g.
  2. Add a method on the type (see Function like objects)

For example,

using PotentialFlow.Plates.RigidBodyMotions

struct Sinusoid <: RigidBodyMotions.Profile
    ω::Float64
end

(s::Sinusoid)(t) = sin(s.ω*t)

which can then be used as follows:

T = range(-6, 6, length = 200)

s = Sinusoid(2.0)
c = d_dt(2s >> 0.5)

using Plots
plot(T, [s.(T) c.(T)], xlabel = "t", color = ["#00BFFF" "#D4CA3A"],
     legend = :none, linewidth = 2)

Custom profile

Function Documentation

PotentialFlow.RigidBodyMotions.PitchHeaveType
PitchHeave <: Kinematics

Kinematics describing an oscillatory pitching and heaving (i.e. plunging) motion

Constructors

Fields

  • U₀: Freestream velocity

  • a: Axis of pitch rotation, relative to the plate centroid

  • K: Reduced frequency $K = \frac{\Omega c}{2U_0}$

  • ϕ: Phase lag of pitch to heave (in radians)

  • α₀: Mean angle of attack

  • Δα: Amplitude of pitching

  • A: Amplitude of translational heaving

  • Y

  • Ÿ

  • α

  • α̇

  • α̈

source
PotentialFlow.RigidBodyMotions.PitchupType
Pitchup <: Kinematics

Kinematics describing a pitchup motion (horizontal translation with rotation)

Constructors

Fields

  • U₀: Freestream velocity

  • a: Axis of rotation, relative to the plate centroid

  • K: Non-dimensional pitch rate $K = \dot{\alpha}_0\frac{c}{2U_0}$

  • α₀: Initial angle of attack

  • t₀: Nominal start of pitch up

  • Δα: Total pitching angle

  • α

  • α̇

  • α̈

source
PotentialFlow.RigidBodyMotions.RigidBodyMotionType
RigidBodyMotion

A type to store the plate's current kinematics

Fields

  • : current centroid velocity
  • : current centroid acceleration
  • α̇: current angular velocity
  • α̈: current angular acceleration
  • kin: a Kinematics structure

The first four fields are meant as a cache of the current kinematics while the kin field can be used to find the plate kinematics at any time.

source
Base.:*Method
s::Number * p::Profile

Returns a scaled profile with (s*p)(t) = s*p(t)

Example

julia> s = RigidBodyMotions.Sinusoid(π)
Sinusoid (ω = 3.14)

julia> 2s
2 × (Sinusoid (ω = 3.14))

julia> (2s).([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 0.0
 2.0
 1.4142135623730951
source
Base.:+Method
p₁::Profile + p₂::Profile

Add the profiles so that (p₁ + p₂)(t) = p₁(t) + p₂(t).

Examples

julia> ramp₁ = RigidBodyMotions.EldredgeRamp(5)
logcosh ramp (aₛ = 5.0)

julia> ramp₂ = RigidBodyMotions.ColoniusRamp(5)
power series ramp (n = 5.0)

julia> ramp₁ + ramp₂
AddedProfiles:
  logcosh ramp (aₛ = 5.0)
  power series ramp (n = 5.0)


julia> ramp₁ + (ramp₂ + ramp₁) == ramp₁ + ramp₂ + ramp₁
true
source
Base.:-Method
-(p₁::Profile, p₂::Profile)
julia> s = RigidBodyMotions.Sinusoid(π)
Sinusoid (ω = 3.14)

julia> 2s
2 × (Sinusoid (ω = 3.14))

julia> (2s).([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 0.0
 2.0
 1.4142135623730951

julia> s = RigidBodyMotions.Sinusoid(π);

julia> s.([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 0.0
 1.0
 0.7071067811865476

julia> (-s).([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 -0.0
 -1.0
 -0.7071067811865476

julia> (s - s).([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 0.0
 0.0
 0.0
source
Base.:>>Method
p::Profile >> Δt::Number

Shift the profile in time so that (p >> Δt)(t) = p(t - Δt)

Example

julia> s = RigidBodyMotions.Sinusoid(π);

julia> s >> 0.5
Sinusoid (ω = 3.14) >> 0.5

julia> (s >> 0.5).([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 -1.0
  0.0
  0.7071067811865475

julia> (s << 0.5).([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
  1.0
  1.2246467991473532e-16
 -0.7071067811865475
source
PotentialFlow.RigidBodyMotions.d_dtMethod
d_dt(p::Profile)

Take the time derivative of p and return it as a new profile.

Example

julia> s = RigidBodyMotions.Sinusoid(π)
Sinusoid (ω = 3.14)

julia> s.([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
 0.0
 1.0
 0.7071067811865476

julia> c = RigidBodyMotions.d_dt(s)
d/dt (Sinusoid (ω = 3.14))

julia> c.([0.0, 0.5, 0.75])
3-element Array{Float64,1}:
  3.141592653589793
  1.9236706937217898e-16
 -2.221441469079183
source

Index