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 matrix

Using 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 region
  • nrichardson - Nonlinear Richardson
  • qn - Quasi-Newton (L-BFGS)
  • fas - Full Approximation Scheme multigrid

Convergence Options

  • snes_rtol - Relative tolerance
  • snes_atol - Absolute tolerance
  • snes_stol - Step tolerance
  • snes_max_it - Maximum iterations
  • snes_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 instance
  • comm::MPI.Comm: MPI communicator
  • prefix::String: Optional prefix for command-line options
  • options...: 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

source
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

source
PETSc.dm — Method
d = dm(snes::AbstractSNES)

The DM attached to snes, narrowed to its flavour.

Borrowed handle

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

source
PETSc.ksp — Method
ksp(snes::AbstractSNES)

The linear solver snes uses for its Newton steps.

Borrowed handle

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

source
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.

Callback

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

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).

source
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 context
  • x: Input vector with current solution

The vec argument is a template vector used for the residual.

Callback

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

source
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

source
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) when J == P
  • updateJ!(J, P, snes, x, user_ctx) when J ≠ P
Callback

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

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).

source
PETSc.set_type! — Method
set_type!(snes::AbstractSNES, type::Symbol)

Set the nonlinear method, for example :newtonls, :newtontr or :fas.

External Links

source
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.

source
PETSc.solution — Method
solution(snes::AbstractSNES)

The vector snes solves for, as a PetscVec.

Borrowed handle

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

source
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

source