Deepesh Thakur / Jun 28 2019
Filament Work-Precision Diagrams Comparison with TRBDF2
Recently TRBDF2 with :GMRES is been the best. This is compare this method with Stabilized methods for Filament Problem
The Model
using Pkg Pkg.update() using OrdinaryDiffEq, Test, Sundials, DiffEqDevTools, LSODA using LinearAlgebra using Plots gr()
GRBackend()
const T = Float64 abstract type AbstractFilamentCache end abstract type AbstractMagneticForce end abstract type AbstractInextensibilityCache end abstract type AbstractSolver end abstract type AbstractSolverCache end
struct FerromagneticContinuous <: AbstractMagneticForce ω :: T F :: Vector{T} end mutable struct FilamentCache{ MagneticForce <: AbstractMagneticForce, InextensibilityCache <: AbstractInextensibilityCache, SolverCache <: AbstractSolverCache } <: AbstractFilamentCache N :: Int μ :: T Cm :: T x :: SubArray{T,1,Vector{T},Tuple{StepRange{Int,Int}},true} y :: SubArray{T,1,Vector{T},Tuple{StepRange{Int,Int}},true} z :: SubArray{T,1,Vector{T},Tuple{StepRange{Int,Int}},true} A :: Matrix{T} P :: InextensibilityCache F :: MagneticForce Sc :: SolverCache end
struct NoHydroProjectionCache <: AbstractInextensibilityCache J :: Matrix{T} P :: Matrix{T} J_JT :: Matrix{T} J_JT_LDLT :: LinearAlgebra.LDLt{T, SymTridiagonal{T}} P0 :: Matrix{T} NoHydroProjectionCache(N::Int) = new( zeros(N, 3*(N+1)), # J zeros(3*(N+1), 3*(N+1)), # P zeros(N,N), # J_JT LinearAlgebra.LDLt{T,SymTridiagonal{T}}(SymTridiagonal(zeros(N), zeros(N-1))), zeros(N, 3*(N+1)) ) end
struct DiffEqSolverCache <: AbstractSolverCache S1 :: Vector{T} S2 :: Vector{T} DiffEqSolverCache(N::Integer) = new(zeros(T,3*(N+1)), zeros(T,3*(N+1))) end
function FilamentCache(N=20; Cm=32, ω=200, Solver=SolverDiffEq) InextensibilityCache = NoHydroProjectionCache SolverCache = DiffEqSolverCache tmp = zeros(3*(N+1)) FilamentCache{FerromagneticContinuous, InextensibilityCache, SolverCache}( N, N+1, Cm, view(tmp,1:3:3*(N+1)), view(tmp,2:3:3*(N+1)), view(tmp,3:3:3*(N+1)), zeros(3*(N+1), 3*(N+1)), # A InextensibilityCache(N), # P FerromagneticContinuous(ω, zeros(3*(N+1))), SolverCache(N) ) end
FilamentCache
0.9s
Julia
function stiffness_matrix!(f::AbstractFilamentCache) N, μ, A = f.N, f.μ, f.A for j in axes(A, 2), i in axes(A, 1) A[i, j] = j == i ? 1 : 0 end for i in 1 : 3 A[i,i] = 1 A[i,3+i] = -2 A[i,6+i] = 1 A[3+i,i] = -2 A[3+i,3+i] = 5 A[3+i,6+i] = -4 A[3+i,9+i] = 1 A[3*(N-1)+i,3*(N-3)+i] = 1 A[3*(N-1)+i,3*(N-2)+i] = -4 A[3*(N-1)+i,3*(N-1)+i] = 5 A[3*(N-1)+i,3*N+i] = -2 A[3*N+i,3*(N-2)+i] = 1 A[3*N+i,3*(N-1)+i] = -2 A[3*N+i,3*N+i] = 1 for j in 2 : N-2 A[3*j+i,3*j+i] = 6 A[3*j+i,3*(j-1)+i] = -4 A[3*j+i,3*(j+1)+i] = -4 A[3*j+i,3*(j-2)+i] = 1 A[3*j+i,3*(j+2)+i] = 1 end end rmul!(A, -μ^4) nothing end
stiffness_matrix! (generic function with 1 method)
function update_separate_coordinates!(f::AbstractFilamentCache, r) N, x, y, z = f.N, f.x, f.y, f.z for i in 1 : length(x) x[i] = r[3*i-2] y[i] = r[3*i-1] z[i] = r[3*i] end nothing end function update_united_coordinates!(f::AbstractFilamentCache, r) N, x, y, z = f.N, f.x, f.y, f.z for i in 1 : length(x) r[3*i-2] = x[i] r[3*i-1] = y[i] r[3*i] = z[i] end nothing end function update_united_coordinates(f::AbstractFilamentCache) r = zeros(T, 3*length(f.x)) update_united_coordinates!(f, r) r end
update_united_coordinates (generic function with 1 method)
function initialize!(initial_conf_type::Symbol, f::AbstractFilamentCache) N, x, y, z = f.N, f.x, f.y, f.z if initial_conf_type == :StraightX x .= range(0, stop=1, length=N+1) y .= 0 z .= 0 else error("Unknown initial configuration requested.") end update_united_coordinates(f) end
initialize! (generic function with 1 method)
function magnetic_force!(::FerromagneticContinuous, f::AbstractFilamentCache, t) # TODO: generalize this for different magnetic fields as well N, μ, Cm, ω, F = f.N, f.μ, f.Cm, f.F.ω, f.F.F F[1] = -μ * Cm * cos(ω*t) F[2] = -μ * Cm * sin(ω*t) F[3*(N+1)-2] = μ * Cm * cos(ω*t) F[3*(N+1)-1] = μ * Cm * sin(ω*t) nothing end
magnetic_force! (generic function with 1 method)
struct SolverDiffEq <: AbstractSolver end function (f::FilamentCache)(dr, r, p, t) f.x, f.y, f.z = r[1:3:end], r[2:3:end], r[3:3:end] jacobian!(f) projection!(f) magnetic_force!(f.F, f, t) A, P, F, S1, S2 = f.A, f.P.P, f.F.F, f.Sc.S1, f.Sc.S2 # implement dr = P * (A*r + F) in an optimized way to avoid temporaries mul!(S1, A, r) S1 .+= F mul!(S2, P, S1) copy!(dr, S2) return dr end
function jacobian!(f::FilamentCache) N, x, y, z, J = f.N, f.x, f.y, f.z, f.P.J for i in 1 : N J[i, 3*i-2] = -2 * (x[i+1]-x[i]) J[i, 3*i-1] = -2 * (y[i+1]-y[i]) J[i, 3*i] = -2 * (z[i+1]-z[i]) J[i, 3*(i+1)-2] = 2 * (x[i+1]-x[i]) J[i, 3*(i+1)-1] = 2 * (y[i+1]-y[i]) J[i, 3*(i+1)] = 2 * (z[i+1]-z[i]) end nothing end
jacobian! (generic function with 1 method)
function projection!(f::FilamentCache) # implement P[:] = I - J'/(J*J')*J in an optimized way to avoid temporaries J, P, J_JT, J_JT_LDLT, P0 = f.P.J, f.P.P, f.P.J_JT, f.P.J_JT_LDLT, f.P.P0 mul!(J_JT, J, J') LDLt_inplace!(J_JT_LDLT, J_JT) ldiv!(P0, J_JT_LDLT, J) mul!(P, P0', J) subtract_from_identity!(P) nothing end
projection! (generic function with 1 method)
function subtract_from_identity!(A) lmul!(-1, A) for i in 1 : size(A,1) A[i,i] += 1 end nothing end
subtract_from_identity! (generic function with 1 method)
function LDLt_inplace!(L::LinearAlgebra.LDLt{T,SymTridiagonal{T}}, A::Matrix{T}) where {T<:Real} n = size(A,1) dv, ev = L.data.dv, L.data.ev for (i,d) in enumerate(diagind(A)) dv[i] = A[d] end for (i,d) in enumerate(diagind(A,-1)) ev[i] = A[d] end for i in 1 : n-1 ev[i] /= dv[i] dv[i+1] -= abs2(ev[i]) * dv[i] end L end
LDLt_inplace! (generic function with 1 method)
Investigating the model
function run(::SolverDiffEq; N=20, Cm=32, ω=200, time_end=1., solver=TRBDF2(autodiff=false), reltol=1e-6, abstol=1e-6) f = FilamentCache(N, Solver=SolverDiffEq, Cm=Cm, ω=ω) r0 = initialize!(:StraightX, f) stiffness_matrix!(f) prob = ODEProblem(ODEFunction(f, jac=(J, u, p, t)->(mul!(J, f.P.P, f.A); nothing)), r0, (0., time_end)) sol = solve(prob, solver, dense=false, reltol=reltol, abstol=abstol) end
run (generic function with 1 method)
This method runs the model with the TRBDF2
method and the default parameters.
sol = run(SolverDiffEq()) plot(sol,vars = (0,25),dpi=200,linewidth=1,legendfontsize=6,legend=:topright)
The model quickly falls into a highly oscillatory mode which then dominates throughout the rest of the solution.
Work-Precision Diagrams
Now let's build the problem and solve it once at high accuracy to get a reference solution:
N=20 f = FilamentCache(N, Solver=SolverDiffEq) r0 = initialize!(:StraightX, f) stiffness_matrix!(f) prob = ODEProblem(f, r0, (0., 0.01)) sol = solve(prob, Vern9(), reltol=1e-14, abstol=1e-14) test_sol = TestSolution(sol);
High Tolerance (Low Accuracy)
Endpoint Error
abstols=1 ./10 .^(4:9) reltols=1 ./10 .^(4:9) setups = [ Dict(:alg=>TRBDF2(autodiff=false,linsolve=LinSolveGMRES())), Dict(:alg=>ROCK2()), Dict(:alg=>ROCK4()), Dict(:alg=>ESERK5()) ]; Names = ["TRBDF2" "ROCK2" "ROCK4" "ESERK5"]
1×4 Array{String,2}:
"TRBDF2" "ROCK2" "ROCK4" "ESERK5"
Speed
wp = WorkPrecisionSet(prob, abstols, reltols, setups; names=Names, appxsol=test_sol, maxiters=Int(1e6), verbose = false, numruns=50) plot(wp,dpi=200,linewidth=1,legend=:topright,legendfontsize=6)
Timeseries Error
wp = WorkPrecisionSet(prob, abstols, reltols, setups; names=Names, appxsol=test_sol, maxiters=Int(1e6), verbose = false, error_estimate=:l2,numruns=50) plot(wp,dpi=200,linewidth=1,legend=:topright,legendfontsize=6)
Dense Error
wp = WorkPrecisionSet(prob, abstols, reltols, setups; names=Names, appxsol=test_sol, maxiters=Int(1e6), verbose = false, dense_errors = true, error_estimate=:L2, numruns=50) plot(wp,dpi=200,linewidth=1,legend=:topright,legendfontsize=6)
Low Tolerance (High Accuracy)
abstols=1 ./10 .^(6:12) reltols=1 ./10 .^(6:12)
7-element Array{Float64,1}:
1.0e-6
1.0e-7
1.0e-8
1.0e-9
1.0e-10
1.0e-11
1.0e-12
Speed
wp = WorkPrecisionSet(prob, abstols, reltols, setups; names=Names, appxsol=test_sol, maxiters=Int(1e6), verbose = false, numruns=50) plot(wp,dpi=200,linewidth=1,legend=:topright,legendfontsize=6)
Timeseries Error
wp = WorkPrecisionSet(prob, abstols, reltols, setups; names=Names, appxsol=test_sol, maxiters=Int(1e6), verbose = false, error_estimate = :l2, numruns=50) plot(wp,dpi=200,linewidth=1,legend=:topright,legendfontsize=6)
Conclusion
As you can see the best performers of the stabilized methods are performing really well her in par with TRBDF2 for this problem.