The problem is, it does not work. Test:
using BenchmarkTools, ForwardDiff
DUAL = true
# Read the initial conditions from a .mat file
state_vec, kite_pos, kite_vel, wind_vel, tether_length, settings = get_initial_conditions("test/data/input_basic_test.mat")
if DUAL
state_vec = ForwardDiff.Dual[Dual(state_vec[1]), Dual(state_vec[2]), Dual(state_vec[3])]
end
simulate_tether(state_vec, kite_pos, kite_vel, wind_vel, tether_length, settings; prn=true, dual=DUAL)
Simulation function:
function simulate_tether(state_vec, kite_pos, kite_vel, wind_vel, tether_length, settings; prn=false, dual=false)
Ns = size(wind_vel, 2)
if dual
buffers= [zeros(Dual, 3, Ns), zeros(Dual, 3, Ns), zeros(Dual, 3, Ns), zeros(Dual, 3, Ns), zeros(Dual, 3, Ns)]
else
buffers= [zeros(3, Ns), zeros(3, Ns), zeros(3, Ns), zeros(3, Ns), zeros(3, Ns)]
end
# Pack parameters in param named tuple - false sets res! for in-place solution
param = (kite_pos=kite_pos, kite_vel=kite_vel, wind_vel=wind_vel,
tether_length=tether_length, settings=settings, buffers=buffers,
returnFlag=false)
# Define the nonlinear problem
prob = NonlinearProblem(res!, state_vec, param)
# Solve the problem with TrustRegion method
if dual
sol = solve(prob, TrustRegion(); show_trace = Val(false))
else
sol = solve(prob, TrustRegion(autodiff=AutoFiniteDiff()); show_trace = Val(false))
end
iterations = sol.stats.nsteps # Field name may vary; verify with `propertynames(sol)`
state_vec = sol.u
if prn
println("Iterations: ", iterations)
end
# Set the returnFlag to true so that res! returns outputs
param = (; param..., returnFlag=true)
res = zeros(3)
res, force_kite, tether_pos, p0 = res!(res, state_vec, param)
force_gnd = state_vec[3]
state_vec, tether_pos, force_gnd, force_kite, p0
end
Callback function:
function res!(res, state_vec, param)
function cross1(a::Dual, b)
cross(a, b)
end
function cross1(a, b::Dual)
cross(a, b)
end
function cross1(a, b)
cross(MVec3(a), MVec3(b))
end
kite_pos, kite_vel, wind_vel, tether_length, settings, buffers, returnFlag = param
base_type = typeof(state_vec[1])
g = abs(settings.g_earth[3])
Ns = size(wind_vel, 2)
Ls = tether_length / (Ns + 1)
mj = settings.rho_tether * Ls
drag_coeff = -0.5 * settings.rho * Ls * settings.d_tether * settings.cd_tether
A = Ο/4 * (settings.d_tether/1000)^2
E = settings.c_spring / A
# Preallocate arrays
FT = buffers[1]
Fd = buffers[2]
pj = buffers[3]
vj = buffers[4]
aj = buffers[5]
# Unpack state variables
ΞΈ, Ο, Tn = state_vec[1], state_vec[2], state_vec[3]
# Precompute common values
sinΞΈ = sin(ΞΈ)
cosΞΈ = cos(ΞΈ)
sinΟ = sin(Ο)
cosΟ = cos(Ο)
norm_p = norm(kite_pos)
p_unit = kite_pos ./ norm_p
v_parallel = dot(kite_vel, p_unit)
# First element calculations
FT[1, Ns] = Tn * sinΞΈ * cosΟ
FT[2, Ns] = Tn * sinΟ
FT[3, Ns] = Tn * cosΞΈ * cosΟ
pj[1, Ns] = Ls * sinΞΈ * cosΟ
pj[2, Ns] = Ls * sinΟ
pj[3, Ns] = Ls * cosΞΈ * cosΟ
# Velocity and acceleration calculations
Ο = cross1(kite_pos / norm_p^2, kite_vel) # 3 alloc
a = cross1(Ο, @view(pj[:, Ns])) # 3 alloc
b = cross1(Ο, cross1(Ο, @view(pj[:, Ns])))
vj[:, Ns] .= v_parallel * p_unit + a
aj[:, Ns] .= b
# Drag calculation for first element
v_a_p1 = vj[1, Ns] - wind_vel[1, Ns]
v_a_p2 = vj[2, Ns] - wind_vel[2, Ns]
v_a_p3 = vj[3, Ns] - wind_vel[3, Ns]
if all(x -> abs(x) < 1e-3, (v_a_p1, v_a_p2, v_a_p3))
Fd[:, Ns] .= zero(base_type)
else
dir1, dir2, dir3 = pj[1, Ns]/Ls, pj[2, Ns]/Ls, pj[3, Ns]/Ls
v_dot_dir = v_a_p1*dir1 + v_a_p2*dir2 + v_a_p3*dir3
v_a_p_t1 = v_dot_dir * dir1
v_a_p_t2 = v_dot_dir * dir2
v_a_p_t3 = v_dot_dir * dir3
v_a_p_n1 = v_a_p1 - v_a_p_t1
v_a_p_n2 = v_a_p2 - v_a_p_t2
v_a_p_n3 = v_a_p3 - v_a_p_t3
norm_v_a_p_n = sqrt(v_a_p_n1^2 + v_a_p_n2^2 + v_a_p_n3^2)
coeff = drag_coeff * norm_v_a_p_n
Fd[1, Ns] = coeff * v_a_p_n1
Fd[2, Ns] = coeff * v_a_p_n2
Fd[3, Ns] = coeff * v_a_p_n3
end
# Process other segments
@inbounds for ii in Ns:-1:2
# Tension force calculations
if ii == Ns
mj_total = 1.5mj
g_term = mj_total * g
else
mj_total = mj
g_term = mj * g
end
for k in 1:3
FT[k, ii-1] = mj_total * aj[k, ii] + FT[k, ii] - Fd[k, ii]
end
FT[3, ii-1] += g_term
# Position calculations
ft_norm = sqrt(FT[1, ii-1]^2 + FT[2, ii-1]^2 + FT[3, ii-1]^2)
l_i_1 = (ft_norm/(E*A) + 1) * Ls
ft_dir = FT[1, ii-1]/ft_norm, FT[2, ii-1]/ft_norm, FT[3, ii-1]/ft_norm
pj[1, ii-1] = pj[1, ii] + l_i_1 * ft_dir[1]
pj[2, ii-1] = pj[2, ii] + l_i_1 * ft_dir[2]
pj[3, ii-1] = pj[3, ii] + l_i_1 * ft_dir[3]
# Velocity and acceleration
a = cross1(Ο, @view(pj[:, ii-1])) # 28 allocations
b = cross1(Ο, cross1(Ο, @view(pj[:, ii-1]))) # 28 allocations
vj[:, ii-1] .= v_parallel * p_unit + a
aj[:, ii-1] .= b
# Drag calculations
v_a_p1 = vj[1, ii] - wind_vel[1, ii]
v_a_p2 = vj[2, ii] - wind_vel[2, ii]
v_a_p3 = vj[3, ii] - wind_vel[3, ii]
if all(x -> abs(x) < 1e-3, (v_a_p1, v_a_p2, v_a_p3))
Fd[:, ii-1] .= zero(base_type)
else
dx = pj[1, ii-1] - pj[1, ii]
dy = pj[2, ii-1] - pj[2, ii]
dz = pj[3, ii-1] - pj[3, ii]
segment_norm = sqrt(dx^2 + dy^2 + dz^2)
dir1 = dx/segment_norm
dir2 = dy/segment_norm
dir3 = dz/segment_norm
v_dot_dir = v_a_p1*dir1 + v_a_p2*dir2 + v_a_p3*dir3
v_a_p_t1 = v_dot_dir * dir1
v_a_p_t2 = v_dot_dir * dir2
v_a_p_t3 = v_dot_dir * dir3
v_a_p_n1 = v_a_p1 - v_a_p_t1
v_a_p_n2 = v_a_p2 - v_a_p_t2
v_a_p_n3 = v_a_p3 - v_a_p_t3
norm_v_a_p_n = sqrt(v_a_p_n1^2 + v_a_p_n2^2 + v_a_p_n3^2)
coeff = drag_coeff * norm_v_a_p_n
Fd[1, ii-1] = coeff * v_a_p_n1
Fd[2, ii-1] = coeff * v_a_p_n2
Fd[3, ii-1] = coeff * v_a_p_n3
end
end
# Final ground connection calculations
T0_1 = 1.5mj*aj[1,1] + FT[1,1] - Fd[1,1]
T0_2 = 1.5mj*aj[2,1] + FT[2,1] - Fd[2,1]
T0_3 = 1.5mj*aj[3,1] + FT[3,1] - Fd[3,1] + 1.5mj*g
T0_norm = sqrt(T0_1^2 + T0_2^2 + T0_3^2)
l_i_1 = (T0_norm/(E*A) + 1) * Ls
T0_dir1 = T0_1/T0_norm
T0_dir2 = T0_2/T0_norm
T0_dir3 = T0_3/T0_norm
p0 = [pj[1,1] + l_i_1*T0_dir1,
pj[2,1] + l_i_1*T0_dir2,
pj[3,1] + l_i_1*T0_dir3]
res .= kite_pos - p0
if returnFlag
return res, MVector(T0_1, T0_2, T0_3), pj, p0
else
nothing
end
end
Error message:
julia> include("examples_quasistatic/benchmark_qsm.jl")
ERROR: LoadError: MethodError: no method matching Float64(::ForwardDiff.Dual{Nothing, Float64, 0})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.
Stack trace:
Stacktrace:
[1] convert(::Type{Float64}, x::ForwardDiff.Dual{Nothing, Float64, 0})
@ Base ./number.jl:7
[2] macro expansion
@ ~/.julia/packages/StaticArraysCore/7xxEJ/src/StaticArraysCore.jl:88 [inlined]
[3] convert_ntuple
@ ~/.julia/packages/StaticArraysCore/7xxEJ/src/StaticArraysCore.jl:84 [inlined]
[4] MVector{3, Float64}(x::Tuple{ForwardDiff.Dual{β¦}, ForwardDiff.Dual{β¦}, ForwardDiff.Dual{β¦}})
@ StaticArraysCore ~/.julia/packages/StaticArraysCore/7xxEJ/src/StaticArraysCore.jl:192
[5] convert
@ ~/.julia/packages/StaticArrays/LSPcF/src/convert.jl:212 [inlined]
[6] StaticArray
@ ~/.julia/packages/StaticArrays/LSPcF/src/convert.jl:182 [inlined]
[7] (::var"#cross1#22")(a::MVector{3, Float64}, b::SubArray{ForwardDiff.Dual, 1, Matrix{β¦}, Tuple{β¦}, true})
@ Main ~/repos/Tethers.jl/src/Tether_quasistatic.jl:129
[8] res!(res::Vector{β¦}, state_vec::Vector{β¦}, param::@NamedTuple{β¦})
@ Main ~/repos/Tethers.jl/src/Tether_quasistatic.jl:171
[9] (::NonlinearFunction{β¦})(::Vector{β¦}, ::Vararg{β¦})
@ SciMLBase ~/.julia/packages/SciMLBase/sYmAV/src/scimlfunctions.jl:2469
[10] evaluate_f(prob::NonlinearProblem{β¦}, u::Vector{β¦})
@ NonlinearSolve ~/.julia/packages/NonlinearSolve/sBl1H/src/internal/helpers.jl:6
[11] __init(::NonlinearProblem{β¦}, ::GeneralizedFirstOrderAlgorithm{β¦}; stats::SciMLBase.NLStats, alias_u0::Bool, maxiters::Int64, abstol::Nothing, reltol::Nothing, maxtime::Nothing, termination_condition::Nothing, internalnorm::Function, linsolve_kwargs::@NamedTuple{}, kwargs::@Kwargs{β¦})
@ NonlinearSolve ~/.julia/packages/NonlinearSolve/sBl1H/src/core/generalized_first_order.jl:166
[12] __solve(::NonlinearProblem{β¦}, ::GeneralizedFirstOrderAlgorithm{β¦}; stats::SciMLBase.NLStats, kwargs::@Kwargs{β¦})
@ NonlinearSolve ~/.julia/packages/NonlinearSolve/sBl1H/src/core/generic.jl:3
[13] solve_call(_prob::NonlinearProblem{β¦}, args::GeneralizedFirstOrderAlgorithm{β¦}; merge_callbacks::Bool, kwargshandle::Nothing, kwargs::@Kwargs{β¦})
@ DiffEqBase ~/.julia/packages/DiffEqBase/PbBEl/src/solve.jl:635
[14] solve_up(prob::NonlinearProblem{β¦}, sensealg::Nothing, u0::Vector{β¦}, p::@NamedTuple{β¦}, args::GeneralizedFirstOrderAlgorithm{β¦}; kwargs::@Kwargs{β¦})
@ DiffEqBase ~/.julia/packages/DiffEqBase/PbBEl/src/solve.jl:1112
[15] solve(prob::NonlinearProblem{β¦}, args::GeneralizedFirstOrderAlgorithm{β¦}; sensealg::Nothing, u0::Nothing, p::Nothing, wrap::Val{β¦}, kwargs::@Kwargs{β¦})
@ DiffEqBase ~/.julia/packages/DiffEqBase/PbBEl/src/solve.jl:1100
[16] simulate_tether(state_vec::Vector{β¦}, kite_pos::MVector{β¦}, kite_vel::MVector{β¦}, wind_vel::Matrix{β¦}, tether_length::Float64, settings::Settings; prn::Bool, dual::Bool)
@ Main ~/repos/Tethers.jl/src/Tether_quasistatic.jl:67
[17] top-level scope
@ ~/repos/Tethers.jl/examples_quasistatic/benchmark_qsm.jl:12
[18] include(fname::String)
@ Main ./sysimg.jl:38
[19] top-level scope
@ REPL[7]:1
in expression starting at /home/ufechner/repos/Tethers.jl/examples_quasistatic/benchmark_qsm.jl:12
Some type information was truncated. Use `show(err)` to see complete types.
The stack trace does not really help. It does not tell me where in the callback function an error is happening.