include("Rotations.jl")
Euler beam element
using StaticArrays, LinearAlgebra, Muscade
Data structure containing the cross section material properties
struct BeamCrossSection
EA :: 𝕣 # axial stiffness
EI₂ :: 𝕣 # bending stiffness about second axis
EI₃ :: 𝕣 # bending stiffness about third axis
GJ :: 𝕣 # torsional stiffness (about longitudinal axis)
μ :: 𝕣 # mass per unit length
ι₁ :: 𝕣 # (mass) moment of inertia for rotation about the element longitudinal axis per unit length
end
BeamCrossSection(;EA=EA,EI₂=EI₂,EI₃=EI₃,GJ=GJ,μ=μ,ι₁=ι₁) = BeamCrossSection(EA,EI₂,EI₃,GJ,μ,ι₁);
Resultant function that computes the internal loads from the strains and curvatures, and external loads on the element.
@espy function resultants(o::BeamCrossSection,ε,κ,xᵧ,rₛₘ,vᵢ)
r₀ = ∂0(rₛₘ) # orientation of the element's local refsys
vᵢ₁ = ∂1(vᵢ) # intrinsic rotation rate of the element's local refsys
vᵢ₂ = ∂2(vᵢ) # intrinsic rotation acceleration of the element's local refsys
xᵧ₀,xᵧ₁,xᵧ₂ = ∂0(xᵧ),∂1(xᵧ),∂2(xᵧ)
xₗ₁ = xᵧ₁ ∘₁ r₀
xₗ₂ = xᵧ₂ ∘₁ r₀
# Compute drag force (example) and added-mass force (example)
# fa = ρ * Ca .* xₗ₂
# fd = .5 * ρ * A .* Cd .* xₗ₁ #.* abs.(xₗ₁)
# Compute translational inertia force
fi = o.μ * xᵧ₂
☼fₑ = fi # external forces at Gauss point.
# Compute roll inertia moment
m₁ₗ = o.ι₁*vᵢ₂[1] #local
mᵧ = ∂0(rₛₘ)[:,1] * m₁ₗ #global
☼mₑ = mᵧ # external couples at Gauss point.
# Compute internal loads
☼fᵢ = o.EA*∂0(ε)
# WARNING: curvatures are defined as rate of rotation along the element, not second derivatives of deflection.
# Hence κ[3]>0 implies +2 direction is inside curve,
# κ[2]>0 implies -3 direction is inside curve.
☼mᵢ = SVector(o.GJ*∂0(κ)[1],o.EI₃*∂0(κ)[2],o.EI₂*∂0(κ)[3])
return fᵢ,mᵢ,fₑ,mₑ
end;
# Static Euler beam element, with two nodes, two Gauss points and 12 degrees of freedom.
const ngp = 4
const ndim = 3
const nXdof = 12
const nUdof = 3
const nXnod = 2;
Shape functions for a beam element with support ζ∈[-1/2,1/2]. Though the shape function matrices are sparse, do not "unroll" them. That would be faster but considerably clutter the code
yₐ(ζ) = 2ζ # differential axial displacement or roll field
yᵤ(ζ) = -4ζ^3 +3ζ # deflection due to differential nodal transverse translation
yᵥ(ζ) = ζ^2 - 1/4 # deflection due to differenttial rotation (bending, not torsion)
κₐ(ζ) = 2 # torsion . κₐ = yₐ′ . Divide by L .
κᵤ(ζ) = -24ζ # curvature. κᵤ = yᵤ′′. Divide by L².
κᵥ(ζ) = 2; # curvature. κᵥ = yᵥ′′. Divide by L .
Data structure describing an EulerBeam3D element as meshed
struct EulerBeam3D{Mat,Uforce} <: AbstractElement
cₘ :: SVector{3,𝕣} # Position of the middle of the element, as meshed
rₘ :: Mat33{𝕣} # Orientation of the element, as meshed, represented by a rotation matrix (from global to local)
ζgp :: SVector{ngp,𝕣} # Location of the Gauss points for the normalized element with length 1
ζnod :: SVector{nXnod,𝕣} # Location of the nodes for the normalized element with length 1
tgₘ :: SVector{ndim,𝕣} # Vector connecting the nodes of the element in the global coordinate system
tgₑ :: SVector{ndim,𝕣} # Vector connecting the nodes of the element in the local coordinate system
yₐ :: SVector{ngp,𝕣} # Value at gp of shape function for differential axial displacement or roll field
yᵤ :: SVector{ngp,𝕣} # Value at gp of shape function for deflection due to differential nodal transverse translation
yᵥ :: SVector{ngp,𝕣} # Value at gp of shape function for deflection due to differenttial rotation (bending, not torsion)
κₐ :: SVector{ngp,𝕣} # Value at gp of shape function for torsion . κₐ = yₐ′ . Divided by L .
κᵤ :: SVector{ngp,𝕣} # Value at gp of shape function for curvature. κᵤ = yᵤ′′. Divided by L².
κᵥ :: SVector{ngp,𝕣} # Value at gp of shape function for curvature. κᵥ = yᵥ′′. Divided by L .
L :: 𝕣 # as meshed length of the element
dL :: SVector{ngp,𝕣} # length associated to each Gauss point
mat :: Mat # used to store material properties (BeamCrossSection, for example)
end;
For performance, residual
will only accept differentiation to first order
Muscade.nosecondorder(::Type{<:EulerBeam3D}) = Val(true)
Define nodes, classes, and field names of dofs
Muscade.doflist( ::Type{EulerBeam3D{Mat,false}}) where{Mat} =
(inod = (1,1,1,1,1,1, 2,2,2,2,2,2),
class= ntuple(i->:X,nXdof),
field= (:t1,:t2,:t3,:r1,:r2,:r3, :t1,:t2,:t3,:r1,:r2,:r3) )
Muscade.doflist( ::Type{EulerBeam3D{Mat,true}}) where{Mat} =
(inod = (1,1,1,1,1,1, 2,2,2,2,2,2, 3,3,3),
class= (ntuple(i->:X,nXdof)...,ntuple(i->:U,nUdof)...),
field= (:t1,:t2,:t3,:r1,:r2,:r3, :t1,:t2,:t3,:r1,:r2,:r3, :t1,:t2,:t3) )
Constructor for the EulerBeam3D element. Arguments: node list, material, and direction of the first bending axis in the global coordinate system.
EulerBeam3D(nod;kwargs...) = EulerBeam3D{false}(nod;kwargs...) # by default, EulerBeam3D does not have Udof.
function EulerBeam3D{Udof}(nod::Vector{Node};mat,orient2::SVector{ndim,𝕣}=SVector(0.,1.,0.)) where {Udof}
c = coord(nod)
# Position of the middle of the element in the global coordinate system (as-meshed)
cₘ = SVector{ndim}((c[1]+c[2])/2)
# Length and tangential vector to the element in the global coordinate system
tgₘ = SVector{ndim}( c[2]-c[1] )
L = norm(tgₘ)
t = tgₘ/L
# Create t, n, b which are the longitudinal and two transverse unit vectors to the element (as-meshed).
# NB: orient2, provided by the user, will define the first bending axis.
orient2/= norm(orient2)
n = orient2 - t*dot(orient2,t)
nn = norm(n)
nn>1e-3 || muscadeerror("Provide a 'orient' input that is not nearly parallel to the element")
n /= nn
b = cross(t,n)
rₘ = SMatrix{ndim,ndim}(t...,n...,b...)
# Tangential vector and node coordinates in the local coordinate system
tgₑ = SVector{ndim}(L,0,0)
# Weight associated to each Gauss point
dL = SVector{ngp}(L/2*(18-sqrt(30))/36,L/2*(18+sqrt(30))/36 ,L/2*(18+sqrt(30))/36,L/2*(18-sqrt(30))/36 )
# Location ζgp of the Gauss points for a unit-length beam element, with nodes at ζnod=±1/2.
ζgp = SVector{ngp }(-1/2*sqrt(3/7+2/7*sqrt(6/5)),-1/2*sqrt(3/7-2/7*sqrt(6/5)), +1/2*sqrt(3/7-2/7*sqrt(6/5)),+1/2*sqrt(3/7+2/7*sqrt(6/5)))
ζnod = SVector{nXnod}(-1/2 ,1/2 )
shapes = (yₐ.(ζgp), yᵤ.(ζgp), yᵥ.(ζgp)*L, κₐ.(ζgp)/L, κᵤ.(ζgp)/L^2, κᵥ.(ζgp)/L)
return EulerBeam3D{typeof(mat),Udof}(cₘ,rₘ,ζgp,ζnod,tgₘ,tgₑ,shapes...,L,dL,mat)
end;
Define now the residual function for the EulerBeam3D element.
@espy function Muscade.residual(o::EulerBeam3D{Mat,Udof}, X,U,A,t,SP,dbg) where{Mat,Udof}
P,ND = constants(X),length(X)
# Compute all quantities at Gauss point, their time derivatives, including intrinsic roll rate and acceleration
gp_,ε_,vₛₘ_,rₛₘ_,vₗ₂_,_ = kinematics(o,motion{P}(X))
gpval,☼ε , rₛₘ = motion⁻¹{P,ND}(gp_,ε_,rₛₘ_ )
vᵢ = intrinsicrotationrates(rₛₘ)
# compute all Jacobians of the above quantities with respect to X₀
X₀ = ∂0(X)
TX₀ = revariate{1}(X₀)
Tgp,Tε,Tvₛₘ,_,_,_ = kinematics(o,TX₀,fast)
gp∂X₀,ε∂X₀,vₛₘ∂X₀ = composeJacobian{P}((Tgp,Tε,Tvₛₘ),X₀)
# Quadrature loop: compute resultants
gp = ntuple(ngp) do igp
☼x,☼κ = gpval[igp].x, gpval[igp].κ
x∂X₀,κ∂X₀ = gp∂X₀[igp].x, gp∂X₀[igp].κ
fᵢ,mᵢ,fₑ,mₑ = ☼resultants(o.mat,ε,κ,x,rₛₘ,vᵢ) # call the "resultant" function to compute loads (local coordinates) from strains/curvatures/etc. using material properties. Note that output is dual of input.
fₑ = Udof ? fₑ-∂0(U) : fₑ
R = (fᵢ ∘₀ ε∂X₀ + mᵢ ∘₁ κ∂X₀ + fₑ ∘₁ x∂X₀ + mₑ ∘₁ vₛₘ∂X₀) * o.dL[igp] # Contribution to the local nodal load of this Gauss point [nXdof] = scalar*[nXdof] + [ndim]⋅[ndim,nXdof] + [ndim]⋅[ndim,nXdof]
@named(R)
end
R = sum(gpᵢ.R for gpᵢ∈gp)
♢κ = motion⁻¹{P,ND}(vₗ₂_).*(2/o.L)
return R,noFB
end;
function kinematics(o::EulerBeam3D,X₀,fast=justinvoke)
cₘ,rₘ,tgₘ,tgₑ,ζnod,ζgp,L = o.cₘ,o.rₘ,o.tgₘ,o.tgₑ,o.ζnod,o.ζgp,o.L # As-meshed element coordinates and describing tangential vector
vₛₘ,rₛₘ,uₗ₂,vₗ₂,cₛₘ = corotated(o,X₀,fast)
ε = √((uₗ₂[1]+L/2)^2+uₗ₂[2]^2+uₗ₂[3]^2)*2/L - 1.
gp = ntuple(ngp) do igp # gp[igp].κ, gp[igp].x
yₐ,yᵤ,yᵥ,κₐ,κᵤ,κᵥ = o.yₐ[igp],o.yᵤ[igp],o.yᵥ[igp],o.κₐ[igp],o.κᵤ[igp],o.κᵥ[igp]
κ = SVector( κₐ*vₗ₂[1], κᵤ*uₗ₂[2]+κᵥ*vₗ₂[3], κᵤ*uₗ₂[3]-κᵥ*vₗ₂[2])
y = SVector(yₐ*uₗ₂[1] , yᵤ*uₗ₂[2]+yᵥ*vₗ₂[3], yᵤ*uₗ₂[3]-yᵥ*vₗ₂[2])
x = rₛₘ∘₁(tgₑ*ζgp[igp]+y)+cₛₘ
(κ=κ,x=x)
end
return gp,ε,vₛₘ,rₛₘ,vₗ₂,uₗ₂
end
vec3(v,ind) = SVector{3}(v[i] for i∈ind);
function corotated(o::EulerBeam3D,X₀,fast=justinvoke)
cₘ,rₘ,tgₘ,tgₑ,ζnod,ζgp,L = o.cₘ,o.rₘ,o.tgₘ,o.tgₑ,o.ζnod,o.ζgp,o.L # As-meshed element coordinates and describing tangential vector
uᵧ₁,vᵧ₁,uᵧ₂,vᵧ₂ = vec3(X₀,1:3), vec3(X₀,4:6), vec3(X₀,7:9), vec3(X₀,10:12)
Δvᵧ,rₛₘ,vₛₘ = fast(SVector(vᵧ₁...,vᵧ₂...)) do v
vᵧ₁,vᵧ₂ = vec3(v,1:3), vec3(v,4:6)
rₛ₁ = fast(Rodrigues,vᵧ₁)
rₛ₂ = fast(Rodrigues,vᵧ₂)
Δvᵧ = 0.5*Rodrigues⁻¹(rₛ₂ ∘₁ rₛ₁')
rₛₘ = fast(Rodrigues,Δvᵧ) ∘₁ rₛ₁ ∘₁ o.rₘ
vₛₘ = Rodrigues⁻¹(rₛₘ)
return Δvᵧ,rₛₘ,vₛₘ
end
cₛ = 0.5*(uᵧ₁+uᵧ₂)
uₗ₂ = rₛₘ' ∘₁ (uᵧ₂+tgₘ*ζnod[2]-cₛ)-tgₑ*ζnod[2] #Local displacement of node 2
vₗ₂ = rₛₘ' ∘₁ Δvᵧ
return vₛₘ,rₛₘ,uₗ₂,vₗ₂,cₛ+cₘ
end;
This page was generated using Literate.jl.