325325
326326"""
327327 KrylovMethod(;
328+ type = Val(Krylov.GmresSolver),
328329 jacobian_free_jvp = nothing,
329330 forcing_term = ConstantForcing(0),
330- type = Val(Krylov.GmresSolver),
331331 args = (20,),
332332 kwargs = (;),
333333 solve_kwargs = (;),
@@ -347,13 +347,14 @@ where `x_prototype` is `similar` to `x` (and also to `Δx` and `f`).
347347
348348This is primarily a wrapper for a `Krylov.KrylovSolver` from `Krylov.jl`. In
349349`allocate_cache`, the solver is constructed with
350- `solver = type(l, l, args..., Krylov.ktypeof(x_prototype); kwargs...)` (note
351- that `type` must be passed through in a `Val` struct), where
350+ `solver = type(l, l, args..., Krylov.ktypeof(x_prototype); kwargs...)`, where
352351`l = length(x_prototype)` and `Krylov.ktypeof(x_prototype)` is a subtype of
353352`DenseVector` that can be used to store `x_prototype`. By default, the solver
354353is a `Krylov.GmresSolver` with a Krylov subspace size of 20 (the default Krylov
355354subspace size for this solver in `Krylov.jl`). In `run!`, the solver is run with
356355`Krylov.solve!(solver, opj, f; M, ldiv, atol, rtol, verbose, solve_kwargs...)`.
356+ The solver's type can be changed by specifying a different value for `type`,
357+ though this value has to be wrapped in a `Val` to avoid runtime compilation.
357358
358359In the call to `Krylov.solve!`, `opj` is a `LinearOperator` that represents
359360`j(x[n])`, which the solver uses by evaluating `mul!(jΔx, opj, Δx)`. If a
@@ -388,7 +389,7 @@ each iteration of the Krylov method. If a debugger is specified, it is run
388389before the call to `Kyrlov.solve!`.
389390"""
390391Base. @kwdef struct KrylovMethod{
391- T <: Val ,
392+ T <: Val{<:Krylov.KrylovSolver} ,
392393 J <: Union{Nothing, JacobianFreeJVP} ,
393394 F <: ForcingTerm ,
394395 A <: Tuple ,
@@ -412,7 +413,6 @@ solver_type(::KrylovMethod{Val{T}}) where {T} = T
412413function allocate_cache (alg:: KrylovMethod , x_prototype)
413414 (; jacobian_free_jvp, forcing_term, args, kwargs, debugger) = alg
414415 type = solver_type (alg)
415- @assert type isa Type{<: Krylov.KrylovSolver }
416416 l = length (x_prototype)
417417 return (;
418418 jacobian_free_jvp_cache = isnothing (jacobian_free_jvp) ? nothing :
466466"""
467467 NewtonsMethod(;
468468 max_iters = 1,
469- update_j = UpdateEvery(NewNewtonIteration() ),
469+ update_j = UpdateEvery(NewNewtonIteration),
470470 krylov_method = nothing,
471471 convergence_checker = nothing,
472472 verbose = false,
@@ -512,11 +512,23 @@ for its preconditioners, so, since the value computed with `j!` is used as a
512512preconditioner in Krylov methods with a Jacobian-free JVP, using such a Krylov
513513method requires specifying a `j_prototype` that can be passed to `ldiv!`.
514514
515- If `j(x)` changes sufficiently slowly, `update_j` can be changed from
516- `UpdateEvery(NewNewtonIteration())` to some other `UpdateSignalHandler` in order
517- to make the approximation `j(x[n]) ≈ j(x₀)`, where `x₀` is a previous value of
518- `x[n]` (this could even be a value from a previous `run!` of Newton's method).
519- When Newton's method uses this approximation, it is called the "chord method".
515+ If `j(x)` changes sufficiently slowly, `update_j` may be changed from
516+ `UpdateEvery(NewNewtonIteration)` to some other `UpdateSignalHandler` that
517+ gets triggered less frequently, such as `UpdateEvery(NewNewtonSolve)`. This
518+ can be used to make the approximation `j(x[n]) ≈ j(x₀)`, where `x₀` is a
519+ previous value of `x[n]` (possibly even a value from a previous `run!` of
520+ Newton's method). When Newton's method uses such an approximation, it is called
521+ the "chord method".
522+
523+ In addition, `update_j` can be set to an `UpdateSignalHandler` that gets
524+ triggered by signals that originate outside of Newton's method, such as
525+ `UpdateEvery(NewTimeStep)`. It is possible to send any signal for updating `j`
526+ to Newton's method while it is not running by calling
527+ `update!(::NewtonsMethod, cache, ::UpdateSignal, j!)`, where in this case
528+ `j!(j)` is a function that sets `j` in-place without any dependence on `x`
529+ (since `x` is not necessarily defined while Newton's method is not running, this
530+ version of `j!` does not take `x` as an argument). This can be used to make the
531+ approximation `j(x[n]) ≈ j₀`, where `j₀` can have an arbitrary value.
520532
521533If a convergence checker is provided, it gets used to determine whether to stop
522534iterating on iteration `n` based on the value `x[n]` and its error `Δx[n]`;
@@ -534,7 +546,7 @@ Base.@kwdef struct NewtonsMethod{
534546 C <: Union{Nothing, ConvergenceChecker} ,
535547}
536548 max_iters:: Int = 1
537- update_j:: U = UpdateEvery (NewNewtonIteration () )
549+ update_j:: U = UpdateEvery (NewNewtonIteration)
538550 krylov_method:: K = nothing
539551 convergence_checker:: C = nothing
540552 verbose:: Bool = false
@@ -547,7 +559,7 @@ function allocate_cache(alg::NewtonsMethod, x_prototype, j_prototype = nothing)
547559 (isnothing (krylov_method) || isnothing (krylov_method. jacobian_free_jvp))
548560 )
549561 return (;
550- update_j_cache = allocate_cache (update_j),
562+ update_j_cache = allocate_cache (update_j, eltype (x_prototype) ),
551563 krylov_method_cache = isnothing (krylov_method) ? nothing :
552564 allocate_cache (krylov_method, x_prototype),
553565 convergence_checker_cache = isnothing (convergence_checker) ? nothing :
@@ -596,3 +608,9 @@ function run!(alg::NewtonsMethod, cache, x, f!, j! = nothing)
596608 end
597609 end
598610end
611+
612+ function update! (alg:: NewtonsMethod , cache, signal:: UpdateSignal , j!)
613+ (; update_j) = alg
614+ (; update_j_cache, j) = cache
615+ isnothing (j) || run! (update_j, update_j_cache, signal, j!, j)
616+ end
0 commit comments