-
Notifications
You must be signed in to change notification settings - Fork 131
Use Jacobian-free Newton-Krylov for SDIRK time integrators #2505
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
Use Jacobian-free Newton-Krylov for SDIRK time integrators #2505
Conversation
Review checklistThis checklist is meant to assist creators of PRs (to let them know what reviewers will typically look for) and reviewers (to guide them in a structured review process). Items do not need to be checked explicitly for a PR to be eligible for merging. Purpose and scope
Code quality
Documentation
Testing
Performance
Verification
Created with ❤️ by the Trixi.jl community. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2505 +/- ##
==========================================
+ Coverage 93.36% 96.76% +3.41%
==========================================
Files 513 515 +2
Lines 42395 42430 +35
==========================================
+ Hits 39578 41056 +1478
+ Misses 2817 1374 -1443
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Nice idea! CC @vchuravy as inspiration for Ariadne.jl. Do you have some comparisons with other methods (either implicit or explicit)? |
I think for now these methods are very inefficient, as most runtime and allocations are actually spent in the integrators: My main motivation was to provide elixirs that actually use the |
|
Is this bad performance related to the performance issues of IMEX methods we observed in DispersiveShallowWater.jl, @ranocha? |
|
I think if you run the simulation for a subsequent time, i.e., after all datatstructures/caches are initialized, these numbers should vastly improve. At least that is what I noted for other stuff from the SciML ecosystem like NonlinearSolve etc. |
|
Did you experience this specifically with split ODE solvers, too? I mainly have bad experience with their current state. |
|
Hm so the scenario for DIRK methods from the PR #2508 looks kinda similar, see the comment by Marco: |
Hey, it is a good time to talk about this. It was one of my goals this summer to get the PDE space up and revived (for the Thunderbolt.jl project we have going, but of course there's a lot of knock-on effects to that as well) and so there has been a lot of motion here:
And with that, we then just got a bunch of new benchmarks up on this (live today!):
What's going on here needs a little bit of an explanation. Exponential IntegratorsFirst of all, The results are all very discouraging for exponential integrators by a few orders of magnitude in their own territory. At first I thought that might be pessimistic? Is ExponentialUtilities.jl really slow? But other benchmarks against things like ExpoKit show that it's fine, actually pretty great. I think it's just fundamental to exponential integrators. If you think about it, Rosenbrock methods are like exponential integrators where you only keep the first N terms of the expansion Now even if you do a Krylov subspace method for the exponential, it is competing against the performance of doing a Krylov subspace method for linear solving. But exponentials have all sorts of quirks that makes them harder to solve than a linear system, kind of obvious because one is a linear system and the other isn't, and these details are things like the fact that high condition numbers mean the need to split the exponential calculation into chunks ("adaptive timestepping", since the exponential is solving a linear ODE), doing multiple different Krylov subspaces, and then multiplying the solutions together. And then there's the fact that for structured So I have 0 hope in the exponential integrators 😅. I think all of the literature out there touting them is academic without good enough software to back it up, and I think this is the definitive software attempt to say "I have beaten all of the implementations out there that I could find, and so this looks like the supreme being of exponential integrators, and it's still losing by 2 orders of magnitude". Nail in the coffin. ARKODE... wins?So ARKODE actually does pretty well, and there's a funny story to this.
If you go to any of the stiff ODE benchmarks, you'll notice ARKODE fails: https://docs.sciml.ai/SciMLBenchmarksOutput/stable/StiffODE/Orego/#Omissions-and-Tweaking So basically Sundials has setup the defaults for ARKODE in such a way where "most" stiff ODEs fail with it, but these "semi-stiff" ODEs you get from PDE semi-discretizations (which can be better to do with implicit solvers, but are much less stiff than things like biological stiff ODEs with conditions numbers of around 10^10) just absolutely fail to even solve with ARKODE a good chunk of the time. And when it does work like in POLLU it doesn't do so well:
So the time stepper seems to be setup to be extremely aggressive, in a way that's not a good default as a stiff ODE solver, but actually works quite well in this context of PDEs. Should we make Current State and Next StepsSo with all of that said:
|
|
Oh and not only did we have do the SciMLOperators.jl v1 but also the DifferentiationInterface.jl change 😅. So I'm going to have to scramble and see if any of the allocations reported in NumericalMathematics/DispersiveShallowWater.jl#207 are related to that... I'd say we should be ready for more large-scale banging away at this in October. If anyone had any time to donate, I would say the best help would be to help with getting each of the steppers setup with JET and AllocCheck tests like https://github.com/SciML/OrdinaryDiffEq.jl/blob/master/lib/OrdinaryDiffEqTsit5/test/allocation_tests.jl . The introduction of DifferentiationInterface was kind of a nightmare to all of this and we could've handled it much more elegantly if we compiler-enforced 0 allocations for every |
That's great to hear 🥳 Thanks a lot for improving the state of the art in this field!
There may be one more idea to try: For something like the KdV example with spectral methods (like in https://docs.sciml.ai/SciMLBenchmarksOutput/stable/SimpleHandwrittenPDE/kdv_spectral_wpd/), the exponential could be computed much more efficiently using an FFT. The same also holds for other methods (fully explicit or implicit in the linear stiff part only). Right now, the stiff third-derivative discretization uses a dense matrix, so there is room for improvement for many classes of methods. Is there an option to tell OrdinaryDiffEq.jl and SciMLOperators.jl something like "Hey, this |
That's really interesting! Do you have an idea how to change the current KenCarp Julia implementations to make them always at least as good as ARKODE? |
| sol = solve(ode, | ||
| # Use (diagonally) implicit Runge-Kutta method with Jacobian-free (!) Newton-Krylov (GMRES) solver | ||
| # See https://docs.sciml.ai/DiffEqDocs/stable/tutorials/advanced_ode_example/#Using-Jacobian-Free-Newton-Krylov | ||
| KenCarp47(autodiff = AutoFiniteDiff(), linsolve = KrylovJL_GMRES()); | ||
| ode_default_options()..., callback = callbacks) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please check how the ARKODE methods perform compared to the KenCarp methods implemented in OrdinaryDiffEq.jl?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm the summary callback seems to be not compatible with the more abstract DifferentialEquations options:
ERROR: type DEOptions has no field adaptive
Stacktrace:
[1] getproperty
@ ./Base.jl:37 [inlined]
[2] initialize_summary_callback(cb::DiscreteCallback{…}, u::Vector{…}, t::Float64, integrator::Sundials.ARKODEIntegrator{…}; reset_threads::Bool)
@ Trixi ~/git/Trixi.jl/src/callbacks_step/summary.jl:197
[3] initialize_summary_callback
@ ~/git/Trixi.jl/src/callbacks_step/summary.jl:148 [inlined]
[4] initialize
@ ~/git/Trixi.jl/src/callbacks_step/summary.jl:20 [inlined]
[5] initialize!
@ ~/.julia/packages/DiffEqBase/AfBQT/src/callbacks.jl:18 [inlined]
[6] initialize!
@ ~/.julia/packages/DiffEqBase/AfBQT/src/callbacks.jl:7 [inlined]
[7] initialize_callbacks!
@ ~/.julia/packages/Sundials/ixqpi/src/common_interface/solve.jl:1574 [inlined]
[8] initialize_callbacks!(integrator::Sundials.ARKODEIntegrator{…})
@ Sundials ~/.julia/packages/Sundials/ixqpi/src/common_interface/solve.jl:1569
[9] __init(prob::ODEProblem{…}, alg::ARKODE{…}, timeseries::Vector{…}, ts::Vector{…}, ks::Vector{…}; verbose::Bool, callback::CallbackSet{…}, abstol::Float64, reltol::Float64, saveat::Vector{…}, tstops::Vector{…}, d_discontinuities::Vector{…}, maxiters::Int64, dt::Nothing, dtmin::Float64, dtmax::Float64, timeseries_errors::Bool, dense_errors::Bool, save_everystep::Bool, save_idxs::Nothing, dense::Bool, save_on::Bool, save_start::Bool, save_end::Bool, save_timeseries::Nothing, progress::Bool, progress_steps::Int64, progress_name::String, progress_message::typeof(DiffEqBase.ODE_DEFAULT_PROG_MESSAGE), progress_id::Symbol, advance_to_tstop::Bool, stop_at_next_tstop::Bool, userdata::Nothing, alias_u0::Bool, kwargs::@Kwargs{})
@ Sundials ~/.julia/packages/Sundials/ixqpi/src/common_interface/solve.jl:951
[10] __solve(prob::ODEProblem{…}, alg::ARKODE{…}, timeseries::Vector{…}, ts::Vector{…}, ks::Vector{…}, recompile::Type{…}; calculate_error::Bool, kwargs::@Kwargs{…})
@ Sundials ~/.julia/packages/Sundials/ixqpi/src/common_interface/solve.jl:15
[11] __solve (repeats 5 times)
@ ~/.julia/packages/Sundials/ixqpi/src/common_interface/solve.jl:3 [inlined]
[12] solve_call(_prob::ODEProblem{…}, args::ARKODE{…}; merge_callbacks::Bool, kwargshandle::Nothing, kwargs::@Kwargs{…})
@ DiffEqBase ~/.julia/packages/DiffEqBase/AfBQT/src/solve.jl:657
[13] solve_call
@ ~/.julia/packages/DiffEqBase/AfBQT/src/solve.jl:614 [inlined]
[14] #solve_up#45
@ ~/.julia/packages/DiffEqBase/AfBQT/src/solve.jl:1211 [inlined]
[15] solve_up
@ ~/.julia/packages/DiffEqBase/AfBQT/src/solve.jl:1188 [inlined]
[16] #solve#43
@ ~/.julia/packages/DiffEqBase/AfBQT/src/solve.jl:1083 [inlined]
[17] top-level scope
@ ~/git/Trixi.jl/examples/tree_1d_dgsem/elixir_diffusion_ldg_newton_krylov.jl:64
Some type information was truncated. Use `show(err)` to see complete types.for
sol = solve(ode,
ARKODE();
ode_default_options()..., callback = callbacks)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without callbacks the simulation runs, but usefulness is questionable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but usefulness is questionable.
What do you mean by that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error seems to say you're doing something on the non public API, likely something like integrator.opts.adaptive. Sundials cannot be mutated to being non adaptive between steps as it's stepper cannot be non adaptive in general.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by that?
Callbacks are somewhat essential for meaningful simulations, right? So what I mean is that unless we rewrite the callbacks to be Sundials-ready, the simulations with sundials are relatively useless.
|
I added tolerances for the GMRES solver which greatly reduces runtime without significantly impacting the outcome. From my side this is ready to go 👍 |
examples/p4est_2d_dgsem/elixir_navierstokes_viscous_shock_newton_krylov.jl
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! How did you choose the ODE solver tolerances?
Such that the error is space-dominated, i.e., stricter tolerances do not lead to (significantly) smaller errors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks


I played a bit with https://docs.sciml.ai/DiffEqDocs/stable/tutorials/advanced_ode_example/#Using-Jacobian-Free-Newton-Krylov