trixi-framework / trixi-framework/Trixi.jl

Cannot use implicit time integration with automatic differentiation

Open
#2,369 5 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Julia
Stars
731
Forks
166
Avg merge
2d 18h
Merged PRs (30d)
25

Description

Automatic differentiation is mentioned as one of the key features in the README.md, so I would expect this to work.
```julia
julia> using Trixi, OrdinaryDiffEqSDIRK

julia> trixi_include(default_example(), sol = nothing, stepsize_callback = nothing);
[ Info: You just called `trixi_include`. Julia may now compile the code, please be patient.

julia> sol = solve(ode, ImplicitEuler(); ode_default_options()..., callback=callbacks)

████████╗██████╗ ██╗██╗ ██╗██╗
╚══██╔══╝██╔══██╗██║╚██╗██╔╝██║
██║ ██████╔╝██║ ╚███╔╝ ██║
██║ ██╔══██╗██║ ██╔██╗ ██║
██║ ██║ ██║██║██╔╝ ██╗██║
╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═╝

[...]

────────────────────────────────────────────────────────────────────────────────────────────────────
Simulation running 'LinearScalarAdvectionEquation2D' with DGSEM(polydeg=3)
────────────────────────────────────────────────────────────────────────────────────────────────────
#timesteps: 0 run time: 3.87500000e-06 s
Δt: 0.00000000e+00 └── GC time: 0.00000000e+00 s (0.000%)
sim. time: 0.00000000e+00 (0.000%) time/DOF/rhs!: NaN s
PID: Inf s
#DOFs per field: 4096 alloc'd memory: 361.143 MiB
#elements: 256

Variable: scalar
L2 error: 3.94120825e-06
Linf error: 1.23670425e-05
∑∂S/∂U ⋅ Uₜ : -2.19124035e-16
────────────────────────────────────────────────────────────────────────────────────────────────────

ERROR: First call to automatic differentiation for the Jacobian
failed. This means that the user `f` function is not compatible
with automatic differentiation. Methods to fix this include:

1. Turn off automatic differentiation (e.g. Rosenbrock23() becomes
Rosenbrock23(autodiff = AutoFiniteDiff())). More details can befound at
https://docs.sciml.ai/DiffEqDocs/stable/features/performance_overloads/
2. Improving the compatibility of `f` with ForwardDiff.jl automatic
differentiation (using tools like PreallocationTools.jl). More details
can be found at https://docs.sciml.ai/DiffEqDocs/stable/basics/faq/#Autodifferentiation-and-Dual-Numbers
3. Defining analytical Jacobians. More details can be
found at https://docs.sciml.ai/DiffEqDocs/stable/types/ode_types/#SciMLBase.ODEFunction

Note: turning off automatic differentiation tends to have a very minimal
performance impact (for this use case, because it's forward mode for a
square Jacobian. This is different from optimization gradient scenarios).
However, one should be careful as some methods are more sensitive to
accurate gradients than others. Specifically, Rodas methods like `Rodas4`
and `Rodas5P` require accurate Jacobians in order to have good convergence,
while many other methods like BDF (`QNDF`, `FBDF`), SDIRK (`KenCarp4`),
and Rosenbrock-W (`Rosenbrock23`) do not. Thus if using an algorithm which
is sensitive to autodiff and solving at a low tolerance, please change the
algorithm as well.

MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{DiffEqBase.OrdinaryDiffEqTag, Float64}, Float64, 12})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
(::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat
@ Base rounding.jl:265
(::Type{T})(::T) where T<:Number
@ Core boot.jl:900
Float64(::Irrational{:mad_constant})
@ StatsBase irrationals.jl:251
...

Stacktrace:
[1] jacobian!(J::Matrix{…}, f::Function, x::Vector{…}, fx::Vector{…}, integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, jac_config::Tuple{…})
@ OrdinaryDiffEqDifferentiation ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_wrappers.jl:223
[2] calc_J!
@ ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:222 [inlined]
[3] calc_W!(W::Matrix{…}, integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, nlsolver::OrdinaryDiffEqNonlinearSolve.NLSolver{…}, cache::OrdinaryDiffEqSDIRK.ImplicitEulerCache{…}, dtgamma::Float64, repeat_step::Bool, newJW::Nothing)
@ OrdinaryDiffEqDifferentiation ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:627
[4] update_W!
@ ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:719 [inlined]
[5] update_W!
@ ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:718 [inlined]
[6] nlsolve!(nlsolver::OrdinaryDiffEqNonlinearSolve.NLSolver{…}, integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, cache::OrdinaryDiffEqSDIRK.ImplicitEulerCache{…}, repeat_step::Bool)
@ OrdinaryDiffEqNonlinearSolve ~/.julia/packages/OrdinaryDiffEqNonlinearSolve/e4hoO/src/nlsolve.jl:27
[7] perform_step!(integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, cache::OrdinaryDiffEqSDIRK.ImplicitEulerCache{…}, repeat_step::Bool)
@ OrdinaryDiffEqSDIRK ~/.julia/packages/OrdinaryDiffEqSDIRK/YAA9M/src/sdirk_perform_step.jl:94
[8] perform_step!
@ ~/.julia/packages/OrdinaryDiffEqSDIRK/YAA9M/src/sdirk_perform_step.jl:79 [inlined]
[9] solve!(integrator::OrdinaryDiffEqCore.ODEIntegrator{…})
@ OrdinaryDiffEqCore ~/.julia/packages/OrdinaryDiffEqCore/bMOsj/src/solve.jl:620
[10] __solve(::ODEProblem{…}, ::ImplicitEuler{…}; kwargs::@Kwargs{…})
@ OrdinaryDiffEqCore ~/.julia/packages/OrdinaryDiffEqCore/bMOsj/src/solve.jl:7
[11] __solve
@ ~/.julia/packages/OrdinaryDiffEqCore/bMOsj/src/solve.jl:1 [inlined]
[12] #solve_call#35
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:635 [inlined]
[13] solve_call
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:592 [inlined]
[14] #solve_up#44
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:1142 [inlined]
[15] solve_up
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:1120 [inlined]
[16] #solve#42
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:1057 [inlined]
[17] top-level scope
@ REPL[7]:1

caused by: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{DiffEqBase.OrdinaryDiffEqTag, Float64}, Float64, 12})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
(::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat
@ Base rounding.jl:265
(::Type{T})(::T) where T<:Number
@ Core boot.jl:900
Float64(::Irrational{:mad_constant})
@ StatsBase irrationals.jl:251
...

Stacktrace:
[1] convert(::Type{Float64}, x::ForwardDiff.Dual{ForwardDiff.Tag{DiffEqBase.OrdinaryDiffEqTag, Float64}, Float64, 12})
@ Base ./number.jl:7
[2] setindex!
@ ./array.jl:994 [inlined]
[3] macro expansion
@ ~/git/Trixi.jl/src/solvers/dgsem_tree/dg_2d.jl:557 [inlined]
[4] macro expansion
@ ~/.julia/packages/Polyester/eqrC9/src/closure.jl:525 [inlined]
[5] macro expansion
@ ~/git/Trixi.jl/src/auxiliary/auxiliary.jl:213 [inlined]
[6] prolong2interfaces!(cache::@NamedTuple{…}, u::Array{…}, mesh::TreeMesh{…}, equations::LinearScalarAdvectionEquation2D{…}, surface_integral::SurfaceIntegralWeakForm{…}, dg::DGSEM{…})
@ Trixi ~/git/Trixi.jl/src/solvers/dgsem_tree/dg_2d.jl:550
[7] macro expansion
@ ~/git/Trixi.jl/src/solvers/dgsem_tree/dg_2d.jl:130 [inlined]
[8] macro expansion
@ ~/git/TrixiBase.jl/src/trixi_timeit.jl:64 [inlined]
[9] rhs!(du::Array{…}, u::Array{…}, t::Float64, mesh::TreeMesh{…}, equations::LinearScalarAdvectionEquation2D{…}, boundary_conditions::Trixi.BoundaryConditionPeriodic, source_terms::Nothing, dg::DGSEM{…}, cache::@NamedTuple{…})
@ Trixi ~/git/Trixi.jl/src/solvers/dgsem_tree/dg_2d.jl:129
[10] macro expansion
@ ~/git/TrixiBase.jl/src/trixi_timeit.jl:64 [inlined]
[11] rhs!(du_ode::Vector{…}, u_ode::Vector{…}, semi::SemidiscretizationHyperbolic{…}, t::Float64)
@ Trixi ~/git/Trixi.jl/src/semidiscretization/semidiscretization_hyperbolic.jl:421
[12] ODEFunction
@ ~/.julia/packages/SciMLBase/c6Noy/src/scimlfunctions.jl:2470 [inlined]
[13] UJacobianWrapper
@ ~/.julia/packages/SciMLBase/c6Noy/src/function_wrappers.jl:32 [inlined]
[14] chunk_mode_jacobian!(result::Matrix{…}, f!::SciMLBase.UJacobianWrapper{…}, y::Vector{…}, x::Vector{…}, cfg::ForwardDiff.JacobianConfig{…})
@ ForwardDiff ~/.julia/packages/ForwardDiff/UBbGT/src/jacobian.jl:187
[15] jacobian!
@ ~/.julia/packages/ForwardDiff/UBbGT/src/jacobian.jl:84 [inlined]
[16] jacobian!(::SciMLBase.UJacobianWrapper{…}, ::Vector{…}, ::Matrix{…}, ::DifferentiationInterfaceForwardDiffExt.ForwardDiffTwoArgJacobianPrep{…}, ::ADTypes.AutoForwardDiff{…}, ::Vector{…})
@ DifferentiationInterfaceForwardDiffExt ~/.julia/packages/DifferentiationInterface/Yk2Kt/ext/DifferentiationInterfaceForwardDiffExt/twoarg.jl:489
[17] jacobian!(J::Matrix{…}, f::Function, x::Vector{…}, fx::Vector{…}, integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, jac_config::Tuple{…})
@ OrdinaryDiffEqDifferentiation ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_wrappers.jl:221
[18] calc_J!
@ ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:222 [inlined]
[19] calc_W!(W::Matrix{…}, integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, nlsolver::OrdinaryDiffEqNonlinearSolve.NLSolver{…}, cache::OrdinaryDiffEqSDIRK.ImplicitEulerCache{…}, dtgamma::Float64, repeat_step::Bool, newJW::Nothing)
@ OrdinaryDiffEqDifferentiation ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:627
[20] update_W!
@ ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:719 [inlined]
[21] update_W!
@ ~/.julia/packages/OrdinaryDiffEqDifferentiation/M5647/src/derivative_utils.jl:718 [inlined]
[22] nlsolve!(nlsolver::OrdinaryDiffEqNonlinearSolve.NLSolver{…}, integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, cache::OrdinaryDiffEqSDIRK.ImplicitEulerCache{…}, repeat_step::Bool)
@ OrdinaryDiffEqNonlinearSolve ~/.julia/packages/OrdinaryDiffEqNonlinearSolve/e4hoO/src/nlsolve.jl:27
[23] perform_step!(integrator::OrdinaryDiffEqCore.ODEIntegrator{…}, cache::OrdinaryDiffEqSDIRK.ImplicitEulerCache{…}, repeat_step::Bool)
@ OrdinaryDiffEqSDIRK ~/.julia/packages/OrdinaryDiffEqSDIRK/YAA9M/src/sdirk_perform_step.jl:94
[24] perform_step!
@ ~/.julia/packages/OrdinaryDiffEqSDIRK/YAA9M/src/sdirk_perform_step.jl:79 [inlined]
[25] solve!(integrator::OrdinaryDiffEqCore.ODEIntegrator{…})
@ OrdinaryDiffEqCore ~/.julia/packages/OrdinaryDiffEqCore/bMOsj/src/solve.jl:620
[26] __solve(::ODEProblem{…}, ::ImplicitEuler{…}; kwargs::@Kwargs{…})
@ OrdinaryDiffEqCore ~/.julia/packages/OrdinaryDiffEqCore/bMOsj/src/solve.jl:7
[27] __solve
@ ~/.julia/packages/OrdinaryDiffEqCore/bMOsj/src/solve.jl:1 [inlined]
[28] #solve_call#35
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:635 [inlined]
[29] solve_call
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:592 [inlined]
[30] #solve_up#44
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:1142 [inlined]
[31] solve_up
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:1120 [inlined]
[32] #solve#42
@ ~/.julia/packages/DiffEqBase/zYZst/src/solve.jl:1057 [inlined]
[33] top-level scope
@ REPL[7]:1
Some type information was truncated. Use `show(err)` to see complete types.
```

Contributor guide

Open the contributing guide

Research direction

Reproduce the example with `ImplicitEuler()` and automatic differentiation, then inspect `src/solvers/dgsem_tree/dg_2d.jl` around line 550 and `src/semidiscretization/semidiscretization_hyperbolic.jl` around line 421, where the stack trace reaches the failing Float64 conversion. Done means the reported implicit solve completes with automatic differentiation enabled and the relevant regression coverage passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.