-
-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
automatic differentiation not working in advanced examples? #387
Comments
It shouldn't be needed. The code should be improved to not require it.
That's the right idea.
Yes, you'll need to essentially do this trick at a larger scale for all of the caches in here. You're on the right track. |
Can you point to any documents that discuss this? I have no clue...
I don't see where caches are created. Does something like |
It's using a cache |
So basically all the arrays in the mutable struct BeelerReuterCpu{T} <: Function
t::T # the last timestep time to calculate Δt
diff_coef::T # the diffusion-coefficient (coupling strength)
C::Array{T,2} # intracellular calcium concentration
M::Array{T,2} # sodium current activation gate (m)
H::Array{T,2} # sodium current inactivation gate (h)
J::Array{T,2} # sodium current slow inactivaiton gate (j)
D::Array{T,2} # calcium current activaiton gate (d)
F::Array{T,2} # calcium current inactivation gate (f)
XI::Array{T,2} # inward-rectifying potassium current (iK1)
Δu::Array{T,2} # place-holder for the Laplacian |
Yes, each of those need to be dual caches. Those are all used as temporary storage. |
I created all the caches and pass them as parameters, and now it works. However, I had trouble with the First, I came across the But after that the So my workaround was to add small positive numbers inside like in ERROR: DomainError with -4.7521268120395714e-12:
log will only return a complex result if called with a complex argument. Try log(Complex(x)).
Stacktrace:
[1] throw_complex_domainerror(f::Symbol, x::Float64)
@ Base.Math .\math.jl:33
[2] log(x::Float64)
@ Base.Math .\special\log.jl:285
[3] log
@ ~\.julia\packages\ForwardDiff\QOqCN\src\dual.jl:203 [inlined]
# this is where the log function is called:
[4] calc_iCa(v::ForwardDiff.Dual{ForwardDiff.Tag{SciMLBase.UJacobianWrapper{ODEFunction{true, BeelerReuterCpu{Float64}, LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Float64, NTuple{8, DiffEqBase.DiffCache{Matrix{Float64}, Matrix{ForwardDiff.Dual{nothing, Float64, 12}}}}}, Float64}, Float64, 12}, d::ForwardDiff.Dual{ForwardDiff.Tag{SciMLBase.UJacobianWrapper{ODEFunction{true, BeelerReuterCpu{Float64}, LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Float64, NTuple{8, DiffEqBase.DiffCache{Matrix{Float64}, Matrix{ForwardDiff.Dual{nothing, Float64, 12}}}}}, Float64}, Float64, 12}, f::ForwardDiff.Dual{ForwardDiff.Tag{SciMLBase.UJacobianWrapper{ODEFunction{true, BeelerReuterCpu{Float64}, LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Float64, NTuple{8, DiffEqBase.DiffCache{Matrix{Float64}, Matrix{ForwardDiff.Dual{nothing, Float64, 12}}}}}, Float64}, Float64, 12}, c::ForwardDiff.Dual{ForwardDiff.Tag{SciMLBase.UJacobianWrapper{ODEFunction{true, BeelerReuterCpu{Float64}, LinearAlgebra.UniformScaling{Bool},
Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing}, Float64, NTuple{8, DiffEqBase.DiffCache{Matrix{Float64}, Matrix{ForwardDiff.Dual{nothing, Float64, 12}}}}}, Float64}, Float64, 12}) using DifferentialEquations, Sundials, ForwardDiff, Plots
using DiffEqBase: get_tmp, dualcache
const v0 = -84.624
const v1 = 10.0
const C_K1 = Float64(1.0f0)
const C_x1 = Float64(1.0f0)
const C_Na = Float64(1.0f0)
const C_s = Float64(1.0f0)
const D_Ca = Float64(0.0f0)
const D_Na = Float64(0.0f0)
const g_s = Float64(0.09f0)
const g_Na = Float64(4.0f0)
const g_NaC = Float64(0.005f0)
const ENa = Float64(50.0f0) + D_Na
const γ = Float64(0.5f0)
const C_m = Float64(1.0f0)
mutable struct BeelerReuterCpu{T} <: Function
t::T # the last timestep time to calculate Δt
diff_coef::T # the diffusion-coefficient (coupling strength)
function BeelerReuterCpu(u0::Array{T,2}, diff_coef::T) where {T}
self = new{T}()
ny, nx = size(u0)
self.t = zero(T)
self.diff_coef = diff_coef
return self
end
end
# 5-point stencil
function laplacian(Δu, u)
n1, n2 = size(u)
# internal nodes
for j = 2:n2-1
for i = 2:n1-1
@inbounds Δu[i, j] = u[i+1, j] + u[i-1, j] + u[i, j+1] + u[i, j-1] - 4 * u[i, j]
end
end
# left/right edges
for i = 2:n1-1
@inbounds Δu[i, 1] = u[i+1, 1] + u[i-1, 1] + 2 * u[i, 2] - 4 * u[i, 1]
@inbounds Δu[i, n2] = u[i+1, n2] + u[i-1, n2] + 2 * u[i, n2-1] - 4 * u[i, n2]
end
# top/bottom edges
for j = 2:n2-1
@inbounds Δu[1, j] = u[1, j+1] + u[1, j-1] + 2 * u[2, j] - 4 * u[1, j]
@inbounds Δu[n1, j] = u[n1, j+1] + u[n1, j-1] + 2 * u[n1-1, j] - 4 * u[n1, j]
end
# corners
@inbounds Δu[1, 1] = 2 * (u[2, 1] + u[1, 2]) - 4 * u[1, 1]
@inbounds Δu[n1, 1] = 2 * (u[n1-1, 1] + u[n1, 2]) - 4 * u[n1, 1]
@inbounds Δu[1, n2] = 2 * (u[2, n2] + u[1, n2-1]) - 4 * u[1, n2]
@inbounds Δu[n1, n2] = 2 * (u[n1-1, n2] + u[n1, n2-1]) - 4 * u[n1, n2]
end
@inline function rush_larsen(g, α, β, Δt)
inf = α / (α + β)
τ = 1.0f0 / (α + β)
return clamp(g + (g - inf) * expm1(-Δt / τ), 0.0f0, 1.0f0)
end
function update_M_cpu(g, v, Δt)
# the condition is needed here to prevent NaN when v == 47.0
α = isapprox(v, 47.0f0) ? 10.0f0 : -(v + 47.0f0) / (exp(-0.1f0 * (v + 47.0f0)) - 1.0f0)
β = (40.0f0 * exp(-0.056f0 * (v + 72.0f0)))
return rush_larsen(g, α, β, Δt)
end
function update_H_cpu(g, v, Δt)
α = 0.126f0 * exp(-0.25f0 * (v + 77.0f0))
β = 1.7f0 / (exp(-0.082f0 * (v + 22.5f0)) + 1.0f0)
return rush_larsen(g, α, β, Δt)
end
function update_J_cpu(g, v, Δt)
α = (0.55f0 * exp(-0.25f0 * (v + 78.0f0))) / (exp(-0.2f0 * (v + 78.0f0)) + 1.0f0)
β = 0.3f0 / (exp(-0.1f0 * (v + 32.0f0)) + 1.0f0)
return rush_larsen(g, α, β, Δt)
end
function update_D_cpu(g, v, Δt)
α = γ * (0.095f0 * exp(-0.01f0 * (v - 5.0f0))) / (exp(-0.072f0 * (v - 5.0f0)) + 1.0f0)
β = γ * (0.07f0 * exp(-0.017f0 * (v + 44.0f0))) / (exp(0.05f0 * (v + 44.0f0)) + 1.0f0)
return rush_larsen(g, α, β, Δt)
end
function update_F_cpu(g, v, Δt)
α = γ * (0.012f0 * exp(-0.008f0 * (v + 28.0f0))) / (exp(0.15f0 * (v + 28.0f0)) + 1.0f0)
β = γ * (0.0065f0 * exp(-0.02f0 * (v + 30.0f0))) / (exp(-0.2f0 * (v + 30.0f0)) + 1.0f0)
return rush_larsen(g, α, β, Δt)
end
function update_XI_cpu(g, v, Δt)
α = (0.0005f0 * exp(0.083f0 * (v + 50.0f0))) / (exp(0.057f0 * (v + 50.0f0)) + 1.0f0)
β = (0.0013f0 * exp(-0.06f0 * (v + 20.0f0))) / (exp(-0.04f0 * (v + 20.0f0)) + 1.0f0)
return rush_larsen(g, α, β, Δt)
end
function update_C_cpu(g, d, f, v, Δt)
ECa = D_Ca - 82.3f0 - 13.0278f0 * log(g+1e-11)
kCa = C_s * g_s * d * f
iCa = kCa * (v - ECa)
inf = 1.0f-7 * (0.07f0 - g)
τ = 1.0f0 / 0.07f0
return g + (g - inf) * expm1(-Δt / τ)
end
function update_gates_cpu(u, XI, M, H, J, D, F, C, Δt)
# let Δt = eltype(u).(Δt)
n1, n2 = size(u)
for j = 1:n2
for i = 1:n1
v = u[i, j]
XI[i, j] = update_XI_cpu(XI[i, j], v, Δt)
M[i, j] = update_M_cpu(M[i, j], v, Δt)
H[i, j] = update_H_cpu(H[i, j], v, Δt)
J[i, j] = update_J_cpu(J[i, j], v, Δt)
D[i, j] = update_D_cpu(D[i, j], v, Δt)
F[i, j] = update_F_cpu(F[i, j], v, Δt)
C[i, j] = update_C_cpu(C[i, j], D[i, j], F[i, j], v, Δt)
end
end
# end
end
# iK1 is the inward-rectifying potassium current
function calc_iK1(v)
ea = exp(0.04f0 * (v + 85.0f0))
eb = exp(0.08f0 * (v + 53.0f0))
ec = exp(0.04f0 * (v + 53.0f0))
ed = exp(-0.04f0 * (v + 23.0f0))
return 0.35f0 * (
4.0f0 * (ea - 1.0f0) / (eb + ec) +
0.2f0 * (isapprox(v, -23.0f0) ? 25.0f0 : (v + 23.0f0) / (1.0f0 - ed))
)
end
# ix1 is the time-independent background potassium current
function calc_ix1(v, xi)
ea = exp(0.04f0 * (v + 77.0f0))
eb = exp(0.04f0 * (v + 35.0f0))
return xi * 0.8f0 * (ea - 1.0f0) / eb
end
# iNa is the sodium current (similar to the classic Hodgkin-Huxley model)
function calc_iNa(v, m, h, j)
return C_Na * (g_Na * m^3 * h * j + g_NaC) * (v - ENa)
end
# iCa is the calcium current
function calc_iCa(v, d, f, c)
ECa = D_Ca - 82.3f0 - 13.0278f0 * log(c+1e-11) # ECa is the calcium reversal potential
return C_s * g_s * d * f * (v - ECa)
end
function update_du_cpu(du, u, XI, M, H, J, D, F, C)
n1, n2 = size(u)
for j = 1:n2
for i = 1:n1
v = u[i, j]
# calculating individual currents
iK1 = calc_iK1(v)
ix1 = calc_ix1(v, XI[i, j])
iNa = calc_iNa(v, M[i, j], H[i, j], J[i, j])
iCa = calc_iCa(v, D[i, j], F[i, j], C[i, j])
# # total current
I_sum = iK1 + ix1 + iNa + iCa
# # the reaction part of the reaction-diffusion equation
du[i, j] = -I_sum / C_m
end
end
end
function (f::BeelerReuterCpu)(du, u, p, t)
XI_cache,M_cache,H_cache,J_cache,D_cache,F_cache,C_cache,Δu_cache=p
XI_cache=DiffEqBase.get_tmp(XI_cache, u)
M_cache=DiffEqBase.get_tmp(M_cache, u)
H_cache=DiffEqBase.get_tmp(H_cache, u)
J_cache=DiffEqBase.get_tmp(J_cache, u)
D_cache=DiffEqBase.get_tmp(D_cache, u)
F_cache=DiffEqBase.get_tmp(F_cache, u)
C_cache=DiffEqBase.get_tmp(C_cache, u)
Δu_cache=DiffEqBase.get_tmp(Δu_cache, u)
Δt = t - f.t
if Δt != eltype(u)(0) || t == eltype(u)(0)
update_gates_cpu(u, XI_cache, M_cache, H_cache, J_cache, D_cache, F_cache, C_cache, Δt)
f.t = t
end
laplacian(Δu_cache, u)
# # calculate the reaction portion
update_du_cpu(du, u, XI_cache, M_cache, H_cache, J_cache, D_cache, F_cache, C_cache)
# # ...add the diffusion portion
du .+= f.diff_coef .* Δu_cache
end
function odetest(u0,solver)
T=eltype(u0)
ny,nx = size(u0)
C_cache = DiffEqBase.dualcache(fill(convert(T,0.0001f0), (ny, nx)))
M_cache = DiffEqBase.dualcache(fill(convert(T,0.01f0), (ny, nx)))
H_cache = DiffEqBase.dualcache(fill(convert(T,0.988f0), (ny, nx)))
J_cache = DiffEqBase.dualcache(fill(convert(T,0.975f0), (ny, nx)))
D_cache = DiffEqBase.dualcache(fill(convert(T,0.003f0), (ny, nx)))
F_cache = DiffEqBase.dualcache(fill(convert(T,0.994f0), (ny, nx)))
XI_cache = DiffEqBase.dualcache(fill(convert(T,0.0001f0), (ny, nx)))
Δu_cache = DiffEqBase.dualcache(zeros(T,ny, nx))
p = (C_cache,M_cache,H_cache,J_cache,D_cache,F_cache,XI_cache,Δu_cache)
deriv_cpu = BeelerReuterCpu(u0, 1.0);
prob = ODEProblem(deriv_cpu, u0, (0.0, 50.),p)
@time sol = solve(prob, solver, saveat = 100.0)
deriv_cpu.t = 0.0
heatmap(sol[end])
end
N = 50;
u0 = fill(v0, (N, N));
u0[23:27,23:27] .= v1; # a small square in the middle of the domain
odetest(u0,CVODE_BDF(linear_solver=:GMRES))
odetest(u0,TRBDF2(autodiff = true))
odetest(u0,TRBDF2(linsolve=LinSolveGMRES(),autodiff=false))
odetest(u0,KenCarp4(linsolve=LinSolveGMRES(),autodiff=false)) |
The model might just not be that stable. decrease the tolerance? |
Decreasing |
Also, it seems the |
They can go into the struct. |
I can't get it work to put the cache inside the struct. mutable struct BeelerReuterCpu{T} <: Function
t::T # the last timestep time to calculate Δt
diff_coef::T # the diffusion-coefficient (coupling strength)
C_cache::DiffEqBase.DiffCache
M_cache::DiffEqBase.DiffCache
H_cache::DiffEqBase.DiffCache
J_cache::DiffEqBase.DiffCache
D_cache::DiffEqBase.DiffCache
F_cache::DiffEqBase.DiffCache
XI_cache::DiffEqBase.DiffCache
Δu_cache::DiffEqBase.DiffCache
function BeelerReuterCpu(u0::Array{T,2}, diff_coef::T) where {T}
self = new{T}()
ny, nx = size(u0)
self.t = zero(T)
self.diff_coef = diff_coef
self.C_cache = DiffEqBase.dualcache(fill(convert(T,0.0001), (ny, nx)))
self.M_cache = DiffEqBase.dualcache(fill(convert(T,0.01), (ny, nx)))
self.H_cache = DiffEqBase.dualcache(fill(convert(T,0.988), (ny, nx)))
self.J_cache = DiffEqBase.dualcache(fill(convert(T,0.975), (ny, nx)))
self.D_cache = DiffEqBase.dualcache(fill(convert(T,0.003), (ny, nx)))
self.F_cache = DiffEqBase.dualcache(fill(convert(T,0.994), (ny, nx)))
self.XI_cache = DiffEqBase.dualcache(fill(convert(T,0.0001), (ny, nx)))
self.Δu_cache = DiffEqBase.dualcache(zeros(T,ny, nx))
return self
end
end
and do function (f::BeelerReuterCpu)(du, u, p, t)
# something
f.XI_cache=DiffEqBase.get_tmp(f.XI_cache, u)
#something
end in the function body then the type mismatches since the right hand side becomes So I though may be I need both the original arrays and their caches in the struct like mutable struct BeelerReuterCache{T}
C_cache::DiffEqBase.DiffCache
M_cache::DiffEqBase.DiffCache
H_cache::DiffEqBase.DiffCache
J_cache::DiffEqBase.DiffCache
D_cache::DiffEqBase.DiffCache
F_cache::DiffEqBase.DiffCache
XI_cache::DiffEqBase.DiffCache
Δu_cache::DiffEqBase.DiffCache
function BeelerReuterCache(u0::Array{T,2}) where{T}
self = new{T}()
ny,nx = size(u0)
self.C_cache = DiffEqBase.dualcache(fill(convert(T,0.0001), (ny, nx)))
self.M_cache = DiffEqBase.dualcache(fill(convert(T,0.01), (ny, nx)))
self.H_cache = DiffEqBase.dualcache(fill(convert(T,0.988), (ny, nx)))
self.J_cache = DiffEqBase.dualcache(fill(convert(T,0.975), (ny, nx)))
self.D_cache = DiffEqBase.dualcache(fill(convert(T,0.003), (ny, nx)))
self.F_cache = DiffEqBase.dualcache(fill(convert(T,0.994), (ny, nx)))
self.XI_cache = DiffEqBase.dualcache(fill(convert(T,0.0001), (ny, nx)))
self.Δu_cache = DiffEqBase.dualcache(zeros(T,ny, nx))
return self
end
end
# BeelerReuterCache(zeros(10,10))
mutable struct BeelerReuterCpu{T} <: Function
t::T # the last timestep time to calculate Δt
diff_coef::T # the diffusion-coefficient (coupling strength)
cache::BeelerReuterCache
C::Array{T,2}
M::Array{T,2}
H::Array{T,2}
J::Array{T,2}
D::Array{T,2}
F::Array{T,2}
XI::Array{T,2}
Δu::Array{T,2}
function BeelerReuterCpu(u0::Array{T,2}, diff_coef::T) where {T}
self = new{T}()
ny, nx = size(u0)
self.t = zero(T)
self.diff_coef = diff_coef
self.cache = BeelerReuterCache(u0)
self.C =fill(convert(T,0.0001), (ny, nx))
self.M =fill(convert(T,0.01), (ny, nx))
self.H = fill(convert(T,0.988), (ny, nx))
self.J = fill(convert(T,0.975), (ny, nx))
self.D = fill(convert(T,0.003), (ny, nx))
self.F = fill(convert(T,0.994), (ny, nx))
self.XI = fill(convert(T,0.0001), (ny, nx))
self.Δu = zeros(T,ny, nx)
return self
end
end but that doesn't work either with function (f::BeelerReuterCpu)(du, u, p, t)
# something
f.XI=DiffEqBase.get_tmp(f.cache.XI_cache, u)
#something
end Now the classic error It seems I need something to convert the cache to the original array and backwards too? Anyway, what is the advantage of creating functions in this way like Any help is greatly appreciated. |
that should be a parameterized type. |
I realized that in Now this works mutable struct BeelerReuterCpu{T,chunk_size} <: Function
t::T # the last timestep time to calculate Δt
diff_coef::T # the diffusion-coefficient (coupling strength)
C_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
M_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
H_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
J_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
D_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
F_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
XI_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
Δu_cache::DiffEqBase.DiffCache{Array{T,2},Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
function BeelerReuterCpu(u0::Array{T,2}, diff_coef::T,::Val{chunk_size}) where {T,chunk_size}
self = new{T,chunk_size}()
ny, nx = size(u0)
self.t = zero(T)
self.diff_coef = diff_coef
self.C_cache = DiffEqBase.dualcache(fill(convert(T,0.0001), (ny, nx)),Val{chunk_size})
self.M_cache = DiffEqBase.dualcache(fill(convert(T,0.01), (ny, nx)),Val{chunk_size})
self.H_cache = DiffEqBase.dualcache(fill(convert(T,0.988), (ny, nx)),Val{chunk_size})
self.J_cache = DiffEqBase.dualcache(fill(convert(T,0.975), (ny, nx)),Val{chunk_size})
self.D_cache = DiffEqBase.dualcache(fill(convert(T,0.003), (ny, nx)),Val{chunk_size})
self.F_cache = DiffEqBase.dualcache(fill(convert(T,0.994), (ny, nx)),Val{chunk_size})
self.XI_cache = DiffEqBase.dualcache(fill(convert(T,0.0001), (ny, nx)),Val{chunk_size})
self.Δu_cache = DiffEqBase.dualcache(zeros(T,ny, nx),Val{chunk_size})
return self
end
end
function (f::BeelerReuterCpu)(du, u, p, t)
XI=DiffEqBase.get_tmp(f.XI_cache, u)
M=DiffEqBase.get_tmp(f.M_cache, u)
H=DiffEqBase.get_tmp(f.H_cache, u)
J=DiffEqBase.get_tmp(f.J_cache, u)
D=DiffEqBase.get_tmp(f.D_cache, u)
F=DiffEqBase.get_tmp(f.F_cache, u)
C=DiffEqBase.get_tmp(f.C_cache, u)
Δu=DiffEqBase.get_tmp(f.Δu_cache, du)
Δt = t - f.t
if Δt != eltype(u)(0) || t == eltype(u)(0)
update_gates_cpu(u, XI, M, H, J, D, F, C, Δt)
f.t = t
end
laplacian(Δu, u)
# # calculate the reaction portion
update_du_cpu(du, u, XI, M, H, J, D, F, C)
# # ...add the diffusion portion
du .+= f.diff_coef .* Δu
end Also this immutable version of the function struct with mutable cache struct mutable struct BeelerReuterCpuCache{T,chunk_size}
t::T
C::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
M::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
H::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
J::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
D::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
F::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
XI::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
Δu::DiffEqBase.DiffCache{Array{T,2},
Array{ForwardDiff.Dual{nothing,T,chunk_size},2}}
end
struct BeelerReuterCpu{T,chunk_size}
# t::T # the last timestep time to calculate Δt
diff_coef::T # the diffusion-coefficient (coupling strength)
cache::BeelerReuterCpuCache{T,chunk_size}
end
function BeelerReuterCpu(u0::Array{T,2}, diff_coef::T,
::Val{chunk_size}) where {T,chunk_size}
ny, nx = size(u0)
t = zero(T)
diff_coef = diff_coef
C = DiffEqBase.dualcache(fill(convert(T, 0.0001), (ny, nx)),
Val{chunk_size})
M = DiffEqBase.dualcache(fill(convert(T, 0.01), (ny, nx)), Val{chunk_size})
H = DiffEqBase.dualcache(fill(convert(T, 0.988), (ny, nx)), Val{chunk_size})
J = DiffEqBase.dualcache(fill(convert(T, 0.975), (ny, nx)), Val{chunk_size})
D = DiffEqBase.dualcache(fill(convert(T, 0.003), (ny, nx)), Val{chunk_size})
F = DiffEqBase.dualcache(fill(convert(T, 0.994), (ny, nx)), Val{chunk_size})
XI = DiffEqBase.dualcache(fill(convert(T, 0.0001), (ny, nx)),
Val{chunk_size})
Δu = DiffEqBase.dualcache(zeros(T, ny, nx), Val{chunk_size})
cache = BeelerReuterCpuCache{T,chunk_size}(t, C, M, H, J, D, F, XI, Δu)
return BeelerReuterCpu{T,chunk_size}(diff_coef, cache)
end There's no performance difference. I thought the immutable version might be better... Anyway. Thanks for the help! |
I'm trying to find an example of how to use automatic differentiation in more complex ODE rather than those vanilla examples in the tutorials. Simple ODEs are quite easy to figure out, but I always have the trouble with the
Dual Number
error in complex ODE models. Seems my ODE functions are always not generic enough. And I'd like to learn how to make it work.I was trying to modify the Beeler-Reuter Model (the CPU version) in the advanced tutorial, as the original code doesn't work with
autodiff=true
since it only acceptsFloat
. I finally manage to make it work, but I have many problems that I don't understand. Here is the modified working code:The main changes are :
struct BeelerReuterCpu
parametric.(f::BeelerReuterCpu)(du, u, p, t)
function, when callingupdate_gates_cpu
andlaplacian
functions, the aruguments are converted to the type ofu
, likeeltype(u).(f.Δu)
.I made these changes otherwise I get the
Dual Number
errorMy questions are:
update_gates_cpu
andlaplacian
but notupdate_du_cpu
inside(f::BeelerReuterCpu)(du, u, p, t)
?BeelerReuterCpu
to make the type consistent betweenu
and the fields inBeelerReuterCpu
. I assume that's necessary for theDual Number
to work, but obviously that's not enough. I suppose that's because thestruct
is not created dynamically? Is there a way to do that.DiffEqBase.dualcache
is needed to avoid theDual Number
error when using cache. The cache example given there was very straightforward. But I wonder what exactly is considered as cache. Does it include any intermediary calculation variables?I hope there could be an example of complex ODE with automatic differentiation. All the complex ODE examples in the tutorials and benchmarks only accept
Float
and therefore doesn't work for automatic differentiation.Thanks!
The text was updated successfully, but these errors were encountered: