SNES
The SNES (Scalable Nonlinear Equations Solvers) module provides methods for solving nonlinear systems of the form F(x) = 0. It builds on KSP for the linear solves within Newton-like methods.
Overview
SNES provides:
- Newton methods: Newton line search, Newton trust region
- Quasi-Newton: L-BFGS, Broyden
- Nonlinear Richardson: With various line search strategies
- FAS multigrid: Full Approximation Scheme for nonlinear problems
- Composite solvers: Combine multiple nonlinear solvers
Creating a SNES Solver
# Basic creation
snes = SNES(petsclib, MPI.COMM_WORLD)
# With options
snes = SNES(petsclib, MPI.COMM_WORLD;
snes_type = "newtonls",
snes_rtol = 1e-8,
snes_max_it = 50
)Setting the Nonlinear Function
Define the residual function F(x). The callback comes first in the argument list, so do block syntax works; v0.4's subject-first order (setfunction!(snes, f!, v)) is gone and has no shim (naming conventions, §8.1):
function residual!(fx, snes, x)
# Compute F(x) and store in fx
fx[1] = x[1]^2 + x[2] - 1
fx[2] = x[1] + x[2]^2 - 1
end
PETSc.set_function!(residual!, snes, f_vec)Setting the Jacobian
Define the Jacobian J = dF/dx. set_snes_jacobian! rather than set_jacobian!, because set_jacobian! belongs to PetscDS and the callback occupies argument 1 here (naming conventions, §4.1):
function jacobian!(J, snes, x)
# Fill Jacobian matrix
J[1, 1] = 2*x[1]
J[1, 2] = 1.0
J[2, 1] = 1.0
J[2, 2] = 2*x[2]
PETSc.assemble!(J)
end
PETSc.set_snes_jacobian!(jacobian!, snes, J, J) # (J, P), P the preconditioner matrixUsing a DM
For PDE problems, associate the SNES with a DM:
PETSc.set_dm!(snes, dm)
# Get the DM from SNES. `dm` is both the accessor and the usual variable name,
# so bind the result to something else (see the naming conventions, §3.2).
d = PETSc.dm(snes)Solving
# Solve with initial guess x
PETSc.solve!(x, snes)
# Get the solution vector. It is a **borrowed** handle: it belongs to `snes`,
# and `destroy!` on it is a no-op (naming conventions, §3.3)
sol = PETSc.solution(snes)
# What PETSc did
PETSc.converged_reason(snes) # positive when it converged
PETSc.iteration_number(snes) # Newton iterations
PETSc.ksp_iterations(snes) # linear iterations, summed over them
PETSc.function_norm(snes) # residual norm at the last iterate
PETSc.ksp(snes) # the linear solver, borrowed
PETSc.destroy!(snes)An iterate the residual cannot be evaluated at (a negative density, say) is reported from inside the residual with PETSc.set_function_domain_error!(snes). A few solvers then cut the step; the others stop and converged_reason says SNES_DIVERGED_FUNCTION_DOMAIN. Throwing instead stops the solve, and solve! rethrows the exception.
Common Solver Options
Nonlinear Solver Types (snes_type)
newtonls- Newton with line search (default)newtontr- Newton with trust regionnrichardson- Nonlinear Richardsonqn- Quasi-Newton (L-BFGS)fas- Full Approximation Scheme multigrid
Convergence Options
snes_rtol- Relative tolerancesnes_atol- Absolute tolerancesnes_stol- Step tolerancesnes_max_it- Maximum iterationssnes_monitor- Print residual each iteration
Line Search Options
snes_linesearch_type-bt(backtracking),basic,l2,cp
Example: Full Setup
petsclib = PETSc.petsclibs[1]
PETSc.initialize(petsclib)
snes = SNES(petsclib, MPI.COMM_WORLD;
snes_monitor = true,
ksp_type = "gmres",
pc_type = "ilu"
)
PETSc.set_function!(residual!, snes, f)
PETSc.set_snes_jacobian!(jacobian!, snes, J, J)
PETSc.set_from_options!(snes)
PETSc.solve!(x, snes)
PETSc.destroy!(snes)Functions
PETSc.LibPETSc.SNES — Method
SNES(petsclib, comm::MPI.Comm; prefix="", options...)Create a PETSc nonlinear solver (SNES) context on the communicator comm.
The options are stored and applied at the start of every solve!, so that a DM and callbacks attached after construction are visible to SNESSetFromOptions. Because they are applied on every solve, they override a setting made in code for the same option.
Arguments
petsclib: The PETSc library instancecomm::MPI.Comm: MPI communicatorprefix::String: Optional prefix for command-line optionsoptions...: Additional PETSc options as keyword arguments
If comm has size 1, the garbage collector will handle cleanup automatically. Otherwise, the user is responsible for calling destroy!.
External Links
- PETSc Manual:
SNES/SNESCreate
- PETSc Manual:
SNES/SNESSetFromOptions
PETSc.converged_reason — Method
converged_reason(snes::AbstractSNES)Why the last solve! stopped, as a LibPETSc.SNESConvergedReason: positive when it converged, negative when it diverged.
External Links
- PETSc Manual:
SNES/SNESGetConvergedReason
PETSc.destroy! — Method
destroy!(snes::AbstractSNES)Destroy a SNES (nonlinear solver) object and release associated resources.
This function is typically called automatically via finalizers when the object is garbage collected, but can be called explicitly to free resources immediately. Does nothing on a borrowed handle, such as the one snes(ts) hands back.
External Links
- PETSc Manual:
SNES/SNESDestroy
PETSc.dm — Method
d = dm(snes::AbstractSNES)The DM attached to snes, narrowed to its flavour.
The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).
External Links
- PETSc Manual:
SNES/SNESGetDM
PETSc.function_norm — Method
function_norm(snes::AbstractSNES)The norm of the residual at the current iterate.
External Links
- PETSc Manual:
SNES/SNESGetFunctionNorm
PETSc.iteration_number — Method
iteration_number(snes::AbstractSNES)The number of nonlinear iterations the last solve! took.
External Links
- PETSc Manual:
SNES/SNESGetIterationNumber
PETSc.ksp — Method
ksp(snes::AbstractSNES)The linear solver snes uses for its Newton steps.
The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).
External Links
- PETSc Manual:
SNES/SNESGetKSP
PETSc.ksp_iterations — Method
ksp_iterations(snes::AbstractSNES)The number of linear iterations the last solve! took, summed over its nonlinear iterations.
External Links
- PETSc Manual:
SNES/SNESGetLinearSolveIterations
PETSc.set_convergence_test! — Function
set_convergence_test!(test!::Function, snes::AbstractSNES)Install a Julia closure as the SNES convergence test (SNESSetConvergenceTest).
test! is called as test!(snes, it, xnorm, gnorm, fnorm) at every iteration (it starts at 0, before the first linear solve) and must return a SNESConvergedReason (e.g. LibPETSc.SNES_CONVERGED_ITERATING to continue, a positive reason to report convergence, or a negative reason to report divergence) — see SNESConvergedReason in LibPETSc. xnorm/gnorm/fnorm are the current iterate/scaled-step/residual 2-norms, computed by PETSc exactly as for SNESConvergedDefault; a custom test that instead needs its own residual (e.g. a normalised force residual computed during FormFunction, not ‖F‖₂) should ignore these and read whatever state it cached during the residual evaluation via its own closure captures.
The closure is kept alive by snes until snes is destroyed or a new test is installed. snes.user_ctx is left alone, so the residual and Jacobian callbacks keep receiving it.
The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is the SNESConvergedReason it decides. An exception it throws comes out of the solve!, step! or setup! that ran it.
External Links
- PETSc Manual:
SNES/SNESSetConvergenceTest
The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).
PETSc.set_dm! — Method
set_dm!(snes::AbstractSNES, dm::AbstractDM)Set dm for snes
External Links
- PETSc Manual:
SNES/SNESSetDM
PETSc.set_function! — Function
set_function!(f!, snes, vec)Set the residual function f! for the nonlinear solver snes.
The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).
The function f! will be called as f!(fx, snes, x) where:
fx: Output vector to store the residual F(x)snes: The SNES contextx: Input vector with current solution
The vec argument is a template vector used for the residual.
The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.
External Links
- PETSc Manual:
SNES/SNESSetFunction
PETSc.set_function_domain_error! — Method
set_function_domain_error!(snes::AbstractSNES)Tell snes, from inside the residual callback, that the iterate it was given lies outside the function's domain (a negative pressure, say). A few solvers then cut the step; the others stop, and converged_reason reports SNES_DIVERGED_FUNCTION_DOMAIN. Returns snes.
Either way solve! returns normally. Throwing from the callback instead stops the solve, and solve! rethrows the exception.
External Links
- PETSc Manual:
SNES/SNESSetFunctionDomainError
PETSc.set_snes_jacobian! — Function
set_snes_jacobian!(
updateJ!::Function,
snes::AbstractSNES,
J::AbstractMat,
P::AbstractMat = J
)Define updateJ! to be the function that updates the Jacobian of the snes.
If J == P then a call to updateJ!(J, snes, x) should set the elements of the PETSc Jacobian (approximation).
If J ≠ P then a call to updateJ!(J, P, snes, x) should set the elements of the PETSc Jacobian (approximation) and preconditioning matrix P.
If a user context is set with set_user_ctx!, then updateJ! may optionally accept that as an additional last argument:
updateJ!(J, snes, x, user_ctx)whenJ == PupdateJ!(J, P, snes, x, user_ctx)whenJ ≠ P
The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.
External Links
- PETSc Manual:
SNES/SNESSetJacobian
The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).
PETSc.set_solution! — Method
set_solution!(snes::AbstractSNES, x::AbstractPetscVec)Set the vector snes solves for, which solution then returns. solve!(x, snes) sets it too. Returns snes.
External Links
- PETSc Manual:
SNES/SNESSetSolution
PETSc.set_type! — Method
set_type!(snes::AbstractSNES, type::Symbol)Set the nonlinear method, for example :newtonls, :newtontr or :fas.
External Links
- PETSc Manual:
SNES/SNESSetType
PETSc.set_user_ctx! — Method
set_user_ctx!(snes::AbstractSNES, ctx)Attach ctx to snes, to be handed back as the last argument of the residual and Jacobian callbacks that have a method accepting it. It is kept with the PETSc object, so a borrowed handle such as snes(ts) sees it too. Returns snes.
PETSc.solution — Method
solution(snes::AbstractSNES)The vector snes solves for, as a PetscVec.
The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).
External Links
- PETSc Manual:
SNES/SNESGetSolution
PETSc.type_name — Method
type_name(snes::AbstractSNES)The name PETSc knows this solver by, as a Symbol (:newtonls, :fas, …), or nothing when no type has been set yet (docs/src/man/naming.md §3.1). v0.4 answered with a String; that is a break with no shim (§16).
External Links
- PETSc Manual:
SNES/SNESGetType
PETSc.user_ctx — Method
user_ctx(snes::AbstractSNES)Whatever was stored with set_user_ctx!, or nothing.