Estimating model parameters

We estimate the mass and damping matrices of a coupled linear oscillator (floater moving in the surge, sway and yaw) based on a decay tests In the following, we define the necessary element and residual function describing the dynamic behaviour of the floater.

using Muscade,StaticArrays,Interpolations,GLMakie

fold(x::SVector{6}) = SMatrix{3,3}( x[1],x[2],x[3],
                                    x[2],x[4],x[5],
                                    x[3],x[5],x[6])

const floatermotion  = (:surge,:sway,:yaw)
const idx    = (:11,:12,:16,:22,:26,:66)

struct FloaterOnCalmWater <: AbstractElement
    K   :: SMatrix{3,3,𝕣}
    C   :: SMatrix{3,3,𝕣}
    M   :: SMatrix{3,3,𝕣}
end
FloaterOnCalmWater(nod::Vector{Node};K,C,M  )  = FloaterOnCalmWater(K,C,M)

Muscade.doflist(::Type{<:FloaterOnCalmWater}) = (inod = (ntuple(i-> 1,3)...,ntuple(i-> 1,3)...,ntuple(i-> 1,6)...,                  ntuple(i-> 1,6)...           ),
                                                 class= (ntuple(i->:X,3)...,ntuple(i->:U,3)...,ntuple(i->:A,6)...,                  ntuple(i->:A,6)...          ),
                                                 field= (floatermotion...  ,floatermotion...  ,ntuple(i->Symbol(:M,idx[i]),6)...,   ntuple(i->Symbol(:C,idx[i]),6)...))

@espy function Muscade.residual(o::FloaterOnCalmWater,   X,U,A,t,SP,dbg)
    x,x′,x″    = ∂0(X),∂1(X),∂2(X)
    ☼u         = ∂0(U)
    a          = exp10.(A)
    ☼r₂        = (o.M.*fold(a[@SVector [i for i∈1:6 ]]))∘x″
    ☼r₁        = (o.C.*fold(a[@SVector [i for i∈7:12]]))∘x′
    ☼r₀        = o.K∘x
    return r₀+r₁+r₂-u,  noFB
end

This is a tailor-made cost element where the cost is made dependent on the iteration number. In practice, this is used to first solve an XU problem (costs on A are prohibitive) before solving the actual XUA problem.

struct SingleDecayAcost{Field,Tcost,Tcostargs} <: AbstractElement
    cost     :: Tcost
    costargs :: Tcostargs
    fac      :: 𝕣1
end

SingleDecayAcost(nod::Vector{Node};field::Symbol,fac,cost::Function ,costargs=()) = SingleDecayAcost{field,typeof(cost),typeof(costargs)}(cost,costargs,fac)
Muscade.doflist(::Type{<:SingleDecayAcost{Field,Tcost,Tcostargs}}) where{Field,Tcost,Tcostargs} = (inod=(1,),class=(:A,),field=(Field,))
@espy function Muscade.lagrangian(o::SingleDecayAcost,Λ,X,U,A,t,SP,dbg)
    iter  = min(length(o.fac),default{:iter}(SP,length(o.fac)))
    ☼cost = o.cost(    A[1]  ,o.costargs...)
    return cost*o.fac[iter],noFB
end

Define stiffness, damping and mass matrix for the true system

K         = fold(SVector{6}([1.0,    0.0,     0.0,     1.0,    0.0,     1.0]))
C         = fold(SVector{6}([0.25,   -0.2,     0.1,     0.15,   -0.15,     0.03]))
M         = fold(SVector{6}([1.0,    0.1,     0.2,     0.5,     0.1,     0.1]));

Solve direct problem

model     = Model(:MooredFloater)
n1        = addnode!(model,𝕣[0,0,0])
e1        = addelement!(model,FloaterOnCalmWater,[n1]; K,C,M)
initialstate    = initialize!(model;time=0.)
initialstate    = setdof!(initialstate,[2.0];   field=:surge,   nodID=[n1], order=0)
initialstate    = setdof!(initialstate,[1.0];    field=:sway,    nodID=[n1], order=0)
initialstate    = setdof!(initialstate,[-5.0];  field=:yaw,     nodID=[n1], order=0)
T               = 0.1 *(1:250)
state           = solve(SweepX{2};  initialstate,time= T,verbose=false);
surge   = [s.X[1][1] for s∈state]
sway    = [s.X[1][2] for s∈state]
yaw     = [s.X[1][3] for s∈state];

Create fake measurements

surgeMeas = surge   + .05 * randn(length(T))
swayMeas = sway     + .05 * randn(length(T))
yawMeas = yaw       + .1 * randn(length(T));

Create intial guesses for M and C

Cguess         = fold(SVector{6}([0.1,   -0.1,     0.1,     0.1,   -0.1,     0.1]))
Mguess         = fold(SVector{6}([1.0,    1.0,     1.0,     1.0,    1.0,     1.0]));

Create XUA model

modelXUA     = Model(:MooredFloater)
n1        = addnode!(modelXUA,𝕣[0,0,0])
e1        = addelement!(modelXUA,FloaterOnCalmWater,[n1]; K,C=Cguess,M=Mguess);

Assign costs to unknown forces

Quu       = @SVector [0.05 ^-2 for i=1:3 ]
e2        = [addelement!(modelXUA,SingleDofCost     ,[n1]; class=:U,field=f           ,    cost=(u,t)-> 0.5*Quu[i]*u^2)  for (i,f)∈enumerate(floatermotion)];

Assign costs to variations of model parameters (wrt guess).

fac       = [256,128,64,32,16,8,4,2,1]
QCaa      = @SVector [.1 ^-2 for i=1:6 ]
e3        = [addelement!(modelXUA,SingleDecayAcost  ,[n1];          field=f,fac,           cost=(a  )-> 0.5*QCaa[i]/length(T)*a^2) for (i,f)∈enumerate((:C11,:C12,:C16,:C22,:C26,:C66))]
QMaa      = @SVector [.1 ^-2 for i=1:6 ]
e4        = [addelement!(modelXUA,SingleDecayAcost  ,[n1];          field=f,fac,           cost=(a  )-> 0.5*QMaa[i]/length(T)*a^2) for (i,f)∈enumerate((:M11,:M12,:M16,:M22,:M26,:M66))];

Assign costs to measurement errors

surgeInt    = linear_interpolation(T, surgeMeas)
swayInt     = linear_interpolation(T, swayMeas)
yawInt      = linear_interpolation(T, yawMeas)
@once devSurge(surge,t)     = 1e-1 ^-2 * (surge-surgeInt(t))^2
@once devSway(sway,t)       = 1e-1 ^-2 * (sway-swayInt(t))^2
@once devYaw(yaw,t)         = 1e-1 ^-2 * (yaw-yawInt(t))^2
e5             = addelement!(modelXUA,SingleDofCost,[n1];class=:X,field=:surge,    cost=devSurge)
e6             = addelement!(modelXUA,SingleDofCost,[n1];class=:X,field=:sway,     cost=devSway)
e7             = addelement!(modelXUA,SingleDofCost,[n1];class=:X,field=:yaw,      cost=devYaw);


#Solve inverse problem
initialstateXUA    = initialize!(modelXUA;time=0.)
stateXUA         = solve(DirectXUA{2,0,1};initialstate=initialstateXUA,time=T,
                        maxiter=100,saveiter=true,fastresidual=true,
                        maxΔx=1e-5,maxΔλ=Inf,maxΔu=1e-5,maxΔa=1e-5);



Muscade: DirectXUA{2, 0, 1} solver


    Preparing assembler

    Iteration   1
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=6.4e+02 ≤     Inf
        maxₜ(|ΔX|)=2.2e+00 ≤ 1.0e-05
        maxₜ(|ΔU|)=1.6e+00 ≤ 1.0e-05
             |ΔA| =0.0e+00 ≤ 1.0e-05

    Iteration   2
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=4.7e+02 ≤     Inf
        maxₜ(|ΔX|)=1.2e+00 ≤ 1.0e-05
        maxₜ(|ΔU|)=5.5e-01 ≤ 1.0e-05
             |ΔA| =2.2e-01 ≤ 1.0e-05

    Iteration   3
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=2.0e+00 ≤ 1.0e-05
        maxₜ(|ΔU|)=1.4e+00 ≤ 1.0e-05
             |ΔA| =3.0e-01 ≤ 1.0e-05

    Iteration   4
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.5e+02 ≤     Inf
        maxₜ(|ΔX|)=1.2e+00 ≤ 1.0e-05
        maxₜ(|ΔU|)=3.1e-01 ≤ 1.0e-05
             |ΔA| =5.7e-01 ≤ 1.0e-05

    Iteration   5
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=1.9e+02 ≤     Inf
        maxₜ(|ΔX|)=7.5e-01 ≤ 1.0e-05
        maxₜ(|ΔU|)=6.5e-01 ≤ 1.0e-05
             |ΔA| =6.2e-01 ≤ 1.0e-05

    Iteration   6
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.7e+02 ≤     Inf
        maxₜ(|ΔX|)=8.0e-01 ≤ 1.0e-05
        maxₜ(|ΔU|)=6.4e-01 ≤ 1.0e-05
             |ΔA| =3.5e-01 ≤ 1.0e-05

    Iteration   7
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=1.9e+02 ≤     Inf
        maxₜ(|ΔX|)=5.7e-01 ≤ 1.0e-05
        maxₜ(|ΔU|)=3.6e-01 ≤ 1.0e-05
             |ΔA| =2.9e-01 ≤ 1.0e-05

    Iteration   8
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=2.7e-01 ≤ 1.0e-05
        maxₜ(|ΔU|)=2.8e-01 ≤ 1.0e-05
             |ΔA| =2.8e-01 ≤ 1.0e-05

    Iteration   9
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.2e+02 ≤     Inf
        maxₜ(|ΔX|)=1.1e-01 ≤ 1.0e-05
        maxₜ(|ΔU|)=4.0e-02 ≤ 1.0e-05
             |ΔA| =1.9e-01 ≤ 1.0e-05

    Iteration  10
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=1.1e-02 ≤ 1.0e-05
        maxₜ(|ΔU|)=1.6e-02 ≤ 1.0e-05
             |ΔA| =2.8e-02 ≤ 1.0e-05

    Iteration  11
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=1.8e-03 ≤ 1.0e-05
        maxₜ(|ΔU|)=8.3e-04 ≤ 1.0e-05
             |ΔA| =2.4e-03 ≤ 1.0e-05

    Iteration  12
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=1.6e-04 ≤ 1.0e-05
        maxₜ(|ΔU|)=1.0e-04 ≤ 1.0e-05
             |ΔA| =4.9e-04 ≤ 1.0e-05

    Iteration  13
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=2.8e-05 ≤ 1.0e-05
        maxₜ(|ΔU|)=1.0e-05 ≤ 1.0e-05
             |ΔA| =6.7e-05 ≤ 1.0e-05

    Iteration  14
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=4.1e-06 ≤ 1.0e-05
        maxₜ(|ΔU|)=2.3e-06 ≤ 1.0e-05
             |ΔA| =1.4e-05 ≤ 1.0e-05

    Iteration  15
        Assembling, solving, decrementing.
        maxₜ(|ΔΛ|)=2.3e+02 ≤     Inf
        maxₜ(|ΔX|)=8.4e-07 ≤ 1.0e-05
        maxₜ(|ΔU|)=5.0e-07 ≤ 1.0e-05
             |ΔA| =2.7e-06 ≤ 1.0e-05

    Converged in  15 iterations.
    nel=19, nvar=2262, nstep=250
    DirectXUA{2, 0, 1} time:   17 [s]
Muscade done.

Fetch and display estimated model parameters

lastIter = findlastassigned(stateXUA); niter = lastIter;
Mest      = Mguess .* fold(exp10.(SVector{6}(stateXUA[niter][1].A[1:6 ])))
Cest      = Cguess .* fold(exp10.(SVector{6}(stateXUA[niter][1].A[7:12])));

Fetch response and loads

surgeRec   = [s.X[1][1] for s∈stateXUA[niter]]
swayRec    = [s.X[1][2] for s∈stateXUA[niter]]
yawRec     = [s.X[1][3] for s∈stateXUA[niter]]
surgeExtF   = [s.U[1][1] for s∈stateXUA[niter]]
swayExtF    = [s.U[1][2] for s∈stateXUA[niter]]
yawExtF     = [s.U[1][3] for s∈stateXUA[niter]]
req = @request r₂,r₁,r₀
loads = getresult(stateXUA[niter],req,[e1])
inertiaLoads = [loads[i][:r₂] for i∈1:length(T)]
dampingLoads = [loads[i][:r₁] for i∈1:length(T)]
stiffnessLoads = [loads[i][:r₀] for i∈1:length(T)];

Create a figure with the results

fig      = Figure(size = (2000,1000));

Display response

ax=Axis(fig[1,1], ylabel="Surge", yminorgridvisible = true,xminorgridvisible = true)
scatter!(fig[1,1],T,surgeMeas, color=RGBf(.8, .8, .8),        label=L"\text{Measurements}")
lines!(fig[1,1],T,surge,       color=:black, linestyle=:dash, label=L"\text{Exact direct solution } (M,C,K)")
lines!(fig[1,1],T,surgeRec,    color=:black,                  label=L"\text{Inverse solution } (\hat{M},\hat{C},K)")
ylims!(ax,minimum(surgeMeas),maximum(surgeMeas))
ax.title="Displacements [m,deg]"
axislegend()

ax=Axis(fig[2,1], ylabel="Sway", yminorgridvisible = true,xminorgridvisible = true)
scatter!(fig[2,1],T,swayMeas,   color=RGBf(.8, .8, .8))
lines!(fig[2,1],T,sway,         color=:black, linestyle=:dash)
ylims!(ax,-1,2)
lines!(fig[2,1],T,swayRec,      color=:black)
ylims!(ax,minimum(swayMeas),maximum(swayMeas))

ax=Axis(fig[3,1], ylabel="Yaw", yminorgridvisible = true,xminorgridvisible = true,xlabel="Time [s]")
scatter!(fig[3,1],T,yawMeas,    color=RGBf(.8, .8, .8))
lines!(fig[3,1],T,yaw,          color=:black, linestyle=:dash)
lines!(fig[3,1],T,yawRec,       color=:black)
ylims!(ax,minimum(yawMeas),maximum(yawMeas))

Display loads

ax=Axis(fig[1,2], yminorgridvisible = true,xminorgridvisible = true)
lines!(fig[1,2],T,-[inertiaLoads[i][1] for i∈1:length(T)],  color=:red,     label=L"\text{Inertia } (\hat{M})")
lines!(fig[1,2],T,-[dampingLoads[i][1] for i∈1:length(T)],  color=:blue,    label=L"\text{Damping } (\hat{C})")
lines!(fig[1,2],T,-[stiffnessLoads[i][1] for i∈1:length(T)],color=:green,   label=L"\text{Stiffness } (K)")
lines!(fig[1,2],T,surgeExtF,                                color=:black,   label=L"\text{Unknown}")
ax.title="Loads [N, Nm]"
ylims!(ax,-2,2)
axislegend()

ax=Axis(fig[2,2], yminorgridvisible = true,xminorgridvisible = true)
lines!(fig[2,2],T,-[inertiaLoads[i][2] for i∈1:length(T)],      color=:red  )
lines!(fig[2,2],T,-[dampingLoads[i][2] for i∈1:length(T)],      color=:blue )
lines!(fig[2,2],T,-[stiffnessLoads[i][2] for i∈1:length(T)],    color=:green)
lines!(fig[2,2],T,swayExtF,                                     color=:black)
ylims!(ax,-2,3)

ax=Axis(fig[3,2], yminorgridvisible = true,xminorgridvisible = true,xlabel="Time [s]")
lines!(fig[3,2],T,-[inertiaLoads[i][3] for i∈1:length(T)],      color=:red  )
lines!(fig[3,2],T,-[dampingLoads[i][3] for i∈1:length(T)],      color=:blue)
lines!(fig[3,2],T,-[stiffnessLoads[i][3] for i∈1:length(T)],    color=:green)
lines!(fig[3,2],T,yawExtF,                                      color=:black)
ylims!(ax,-4,4)

ax=Axis(fig[1,3], limits=(nothing,nothing,0,2),
    xticks = (1:6, [L"\hat{M}_{11}/M_{11}", L"\hat{M}_{12}/M_{12}", L"\hat{M}_{16}/M_{16}",L"\hat{C}_{11}/C_{11}", L"\hat{C}_{12}/C_{12}", L"\hat{C}_{16}/C_{16}"]),
    yminorgridvisible = true,xminorgridvisible = true)
barplot!(ax,[1,2,3,4,5,6], [Mest[1,1]/M[1,1],Mest[1,2]/M[1,2],Mest[1,3]/M[1,3], Cest[1,1]/C[1,1],Cest[1,2]/C[1,2],Cest[1,3]/C[1,3]],
    bar_labels = :y,color=:white,strokewidth=1,strokecolor=[:red,:red,:red,:blue,:blue,:blue])
ax.title="Estimated model parameters compared to exact solution (iteration " * string(niter) * "/" * string(lastIter) * ")"

ax=Axis(fig[2,3], limits=(nothing,nothing,0,2),
    xticks = (1:6, [L"\hat{M}_{21}/M_{21}", L"\hat{M}_{22}/M_{22}", L"\hat{M}_{26}/M_{26}",L"\hat{C}_{21}/C_{21}", L"\hat{C}_{22}/C_{22}", L"\hat{C}_{26}/C_{26}"]),
    yminorgridvisible = true,xminorgridvisible = true)
barplot!(ax,[1,2,3,4,5,6], [Mest[2,1]/M[2,1],Mest[2,2]/M[2,2],Mest[2,3]/M[2,3], Cest[2,1]/C[2,1],Cest[2,2]/C[2,2],Cest[2,3]/C[2,3]],
    bar_labels = :y,color=:white,strokewidth=1,strokecolor=[:red,:red,:red,:blue,:blue,:blue])

ax=Axis(fig[3,3], limits=(nothing,nothing,0,2),
    xticks = (1:6, [L"\hat{M}_{61}/M_{61}", L"\hat{M}_{62}/M_{62}", L"\hat{M}_{66}/M_{66}",L"\hat{C}_{61}/C_{61}", L"\hat{C}_{62}/C_{62}", L"\hat{C}_{66}/C_{66}"]),
    yminorgridvisible = true,xminorgridvisible = true)
barplot!(ax,[1,2,3,4,5,6], [Mest[3,1]/M[3,1],Mest[3,2]/M[3,2],Mest[3,3]/M[3,3], Cest[3,1]/C[3,1],Cest[3,2]/C[3,2],Cest[3,3]/C[3,3]],
    bar_labels = :y,color=:white,strokewidth=1,strokecolor=[:red,:red,:red,:blue,:blue,:blue])

currentDir = @__DIR__
if occursin("build", currentDir)
    save(normpath(joinpath(currentDir,"..","src","assets","decay.png")),fig)
elseif occursin("examples", currentDir)
    save(normpath(joinpath(currentDir,"decay.png")),fig)
end

Result


This page was generated using Literate.jl.