]> https://gitweb.dealii.org/ - dealii.git/commitdiff
PETScWrappers: Add support for nonlinear solver 15065/head
authorStefano Zampini <stefano.zampini@gmail.com>
Fri, 25 Nov 2022 16:45:47 +0000 (19:45 +0300)
committerStefano Zampini <stefano.zampini@gmail.com>
Fri, 21 Apr 2023 22:33:55 +0000 (01:33 +0300)
17 files changed:
include/deal.II/lac/petsc_snes.h [new file with mode: 0644]
include/deal.II/lac/petsc_snes.templates.h [new file with mode: 0644]
source/lac/CMakeLists.txt
source/lac/petsc_snes.cc [new file with mode: 0644]
tests/petsc/petsc_snes_00.cc [new file with mode: 0644]
tests/petsc/petsc_snes_00.output [new file with mode: 0644]
tests/petsc/petsc_snes_00_in.prm [new file with mode: 0644]
tests/petsc/petsc_snes_01.cc [new file with mode: 0644]
tests/petsc/petsc_snes_01.output [new file with mode: 0644]
tests/petsc/petsc_snes_02.cc [new file with mode: 0644]
tests/petsc/petsc_snes_02.output [new file with mode: 0644]
tests/petsc/petsc_snes_03.cc [new file with mode: 0644]
tests/petsc/petsc_snes_03.mpirun=3.output [new file with mode: 0644]
tests/petsc/petsc_snes_03.output [new file with mode: 0644]
tests/petsc/petsc_snes_04.cc [new file with mode: 0644]
tests/petsc/petsc_snes_04.mpirun=3.output [new file with mode: 0644]
tests/petsc/petsc_snes_04.output [new file with mode: 0644]

diff --git a/include/deal.II/lac/petsc_snes.h b/include/deal.II/lac/petsc_snes.h
new file mode 100644 (file)
index 0000000..ebae3fa
--- /dev/null
@@ -0,0 +1,413 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//---------------------------------------------------------------
+
+#ifndef dealii_petsc_snes_h
+#define dealii_petsc_snes_h
+
+#include <deal.II/base/config.h>
+
+#ifdef DEAL_II_WITH_PETSC
+#  include <deal.II/base/mpi.h>
+#  include <deal.II/base/parameter_handler.h>
+#  include <deal.II/base/smartpointer.h>
+
+#  include <deal.II/lac/petsc_matrix_base.h>
+#  include <deal.II/lac/petsc_precondition.h>
+#  include <deal.II/lac/petsc_vector_base.h>
+
+#  include <petscsnes.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace PETScWrappers
+{
+  /**
+   * Additional parameters that can be passed to the NonlinearSolver class.
+   */
+  class NonlinearSolverData
+  {
+  public:
+    /**
+     * Type that holds real-valued numbers.
+     *
+     * Used to represent norms.
+     */
+    using real_type = PetscReal;
+
+    /**
+     * Initialization parameters for NonlinearSolverData.
+     *
+     * Running parameters:
+     *
+     * @param options_prefix The string indicating the options prefix for command line customization.
+     * @param snes_type The string indicating the PETSc SNES solver type.
+     * @param snes_linesearch_type The string indicating the PETSc linesearch type.
+     * @param absolute_tolerance Absolute error tolerance.
+     * @param relative_tolerance Relative error tolerance.
+     * @param step_tolerance Step tolerance.
+     * @param maximum_non_linear_iterations Maximum number of iterations allowed.
+     * @param max_n_function_evaluations Maximum number of function evaluations allowed.
+     *
+     * @note All parameters values specified here can be overriden by
+     * command line choices.
+     *
+     * @ingroup PETScWrappers
+     */
+    NonlinearSolverData(
+      // Running parameters
+      const std::string &options_prefix                = "",
+      const std::string &snes_type                     = "",
+      const std::string &snes_linesearch_type          = "",
+      const real_type    absolute_tolerance            = 0,
+      const real_type    relative_tolerance            = 0,
+      const real_type    step_tolerance                = 0,
+      const int          maximum_non_linear_iterations = -1,
+      const int          max_n_function_evaluations    = -1)
+      : options_prefix(options_prefix)
+      , snes_type(snes_type)
+      , snes_linesearch_type(snes_linesearch_type)
+      , absolute_tolerance(absolute_tolerance)
+      , relative_tolerance(relative_tolerance)
+      , step_tolerance(step_tolerance)
+      , maximum_non_linear_iterations(maximum_non_linear_iterations)
+      , max_n_function_evaluations(max_n_function_evaluations)
+    {}
+
+    /**
+     * Import parameter values.
+     */
+    void
+    add_parameters(ParameterHandler &prm);
+
+    /**
+     * Options database prefix.
+     */
+    std::string options_prefix;
+
+    /**
+     * PETSc solver type.
+     */
+    std::string snes_type;
+
+    /**
+     * Linesearch type.
+     */
+    std::string snes_linesearch_type;
+
+    /**
+     * Absolute error tolerance for function evaluation.
+     *
+     * @note Non-positive values indicate to use PETSc's default.
+     */
+    real_type absolute_tolerance;
+
+    /**
+     * Relative error tolerance for function evaluation.
+     *
+     * @note Non-positive values indicate to use PETSc's default.
+     */
+    real_type relative_tolerance;
+
+    /**
+     * Step tolerance for solution update.
+     *
+     * @note Non-positive values indicate to use PETSc's default.
+     */
+    real_type step_tolerance;
+
+    /**
+     * Maximum number of nonlinear iterations allowed.
+     *
+     * @note Negative values indicate to use PETSc's default.
+     */
+    int maximum_non_linear_iterations;
+
+    /**
+     * Maximum number of function evaluations allowed.
+     *
+     * @note Negative values indicate to use PETSc's default.
+     */
+    int max_n_function_evaluations;
+  };
+
+  /**
+   * Interface to PETSc SNES solver for nonlinear equations.
+   * The SNES solver is described in the
+   * [PETSc manual](https://petsc.org/release/manual/snes/).
+   *
+   * This class solves the nonlinear system of algebraic
+   * equations $F(x) = 0$.
+   *
+   * The interface to PETSc is realized by means of std::function callbacks
+   * like in the TrilinosWrappers::NOXSolver and SUNDIALS::KINSOL classes.
+   *
+   * NonlinearSolver supports any vector and matrix type having constructors and
+   * methods:
+   *
+   * @code
+   * class VectorType : public Subscriptor
+   *    ...
+   *    explicit VectorType(Vec);
+   *    ...
+   *    Vec & petsc_vector();
+   *    ...
+   * @endcode
+   *
+   * @code
+   * class MatrixType : public Subscriptor
+   *    ...
+   *    explicit MatrixType(Mat);
+   *    ...
+   *    Mat & petsc_matrix();
+   *    ...
+   * @endcode
+   *
+   * In particular, the supported types are the ones that can *wrap*
+   * PETSc's native vector and matrix classes, that are able to modify
+   * them in place, and that can return PETSc native types when requested.
+   *
+   * To use the solvers the user needs to provide the implementation of $F$ via
+   * the NonlinearSolver::residual callback.
+   *
+   * The default linearization procedure of a solver instantiated with
+   * this class consists in using Jacobian-Free-Newton-Krylov; the action of
+   * tangent matrices inside a linear solver process are approximated via
+   * matrix-free finite-differencing of the nonlinear residual equations.
+   * For details, consult the [PETSc
+   * manual](https://petsc.org/release/manual/snes/#sec-nlmatrixfree).
+   *
+   * In alternative, users can also provide the implementation of the
+   * Jacobian. This can be accomplished in two ways:
+   *  - PETSc style using NonlinearSolver::jacobian
+   *  - deal.II style using NonlinearSolver::setup_jacobian and
+   * NonlinearSolver::solve_with_jacobian.
+   *
+   * In case both approaches are implemented, the deal.II style
+   * will be used.
+   *
+   * The second approach is more in style with the deal.II philosophy
+   * and it also allows command line customization, like for example,
+   * @code
+   * ./myApp -snes_type newtontr -ksp_type cg
+   * @endcode
+   * in case the user wants to change the default nonlinear solver to
+   * a trust region solver and iterate on the tangent system with CG,
+   * still using NonlinearSolver::solve_with_jacobian as a preconditioner.
+   *
+   * The first approach has instead the advantage that only the matrix assembly
+   * procedure has to be provided, thus allowing quicker implementations and
+   * faster turnaround for experimenting with linear solver preconditioning
+   * configurations via command line customizations, like for example,
+   * @code
+   * ./myApp -ksp_type cg -pc_type gamg
+   * @endcode
+   *
+   * In case the nonlinear equations are derived from energy minimization
+   * arguments, it may be beneficial to perform linesearch or test trust-region
+   * model reductions using the energy functional. In such cases, users can
+   * set an optional NonlinearSolver::energy callback.
+   *
+   * @ingroup PETScWrappers
+   */
+  template <typename VectorType  = PETScWrappers::VectorBase,
+            typename PMatrixType = PETScWrappers::MatrixBase,
+            typename AMatrixType = PMatrixType>
+  class NonlinearSolver
+  {
+  public:
+    /**
+     * Type that holds real-valued numbers.
+     *
+     * Used to represent norms.
+     */
+    using real_type = PetscReal;
+
+    /**
+     * Constructor.
+     */
+    NonlinearSolver(const NonlinearSolverData &data     = NonlinearSolverData(),
+                    const MPI_Comm &           mpi_comm = PETSC_COMM_WORLD);
+
+    /**
+     * Destructor.
+     */
+    ~NonlinearSolver();
+
+    /**
+     * Conversion operator to gain access to the underlying PETSc type. If you
+     * do this, you cut this class off some information it may need, so this
+     * conversion operator should only be used if you know what you do.
+     */
+    operator SNES() const;
+
+    /**
+     * Return the PETSc SNES object.
+     */
+    SNES
+    petsc_snes();
+
+    /**
+     * Return the underlying MPI communicator.
+     */
+    MPI_Comm
+    get_mpi_communicator() const;
+
+    /**
+     * Reset the solver. It does not change the customization.
+     */
+    void
+    reinit();
+
+    /**
+     * Reset solver. Change customization according to @p data.
+     */
+    void
+    reinit(const NonlinearSolverData &data);
+
+    /**
+     * Set the preconditioning matrix only.
+     *
+     * When used with NonlinearSolver::setup_jacobian and
+     * NonlinearSolver::solve_with_jacobian, PETSc will approximate the
+     * linear system matrix-vector product using an internal matrix-free
+     * representation.
+     *
+     * When used with NonlinearSolver::jacobian PETSc will use the same matrix
+     * for both preconditioning and matrix-vector products.
+     */
+    void
+    set_matrix(PMatrixType &P);
+
+    /**
+     * Set both the linear system matrix and the preconditioning matrix
+     * that PETSc will use.
+     */
+    void
+    set_matrices(AMatrixType &A, PMatrixType &P);
+
+    /**
+     * Solve the nonlinear system of equations $F(x) = 0$.
+     *
+     * This function returns the number of iterations.
+     * The vector @p x must contain the initial guess.
+     * Upon returning, the @p x vector contains the solution.
+     */
+    unsigned int
+    solve(VectorType &x);
+
+    /**
+     * Solve the nonlinear system of equations $F(x) = 0$.
+     *
+     * This function returns the number of iterations.
+     * The vector @p x must contain the initial guess.
+     * Upon returning, the @p x vector contains the solution.
+     *
+     * Here we also set the matrix to precondition the tangent system.
+     */
+    unsigned int
+    solve(VectorType &x, PMatrixType &P);
+
+    /**
+     * Solve the nonlinear system of equations $F(x) = 0$.
+     *
+     * This function returns the number of iterations.
+     * The vector @p x must contain the initial guess.
+     * Upon returning, the @p x vector contains the solution.
+     *
+     * Here we also set the matrices to describe and precondition
+     * the tangent system.
+     */
+    unsigned int
+    solve(VectorType &x, AMatrixType &A, PMatrixType &P);
+
+    /**
+     * Callback for the computation of the nonlinear residual $F(x)$.
+     */
+    std::function<int(const VectorType &x, VectorType &res)> residual;
+
+    /**
+     * Callback for the computation of the Jacobian
+     * $\dfrac{\partial F}{\partial x}$.
+     */
+    std::function<int(const VectorType &x, AMatrixType &A, PMatrixType &P)>
+      jacobian;
+
+    /**
+     * Callback for monitoring the solution process.
+     *
+     * This function is called by NonlinearSolver at the beginning
+     * of each time step. Input arguments are the current step number
+     * and the current value for ||F(x)||.
+     */
+    std::function<int(const VectorType & x,
+                      const unsigned int step_number,
+                      const real_type    f_norm)>
+      monitor;
+
+    /**
+     * Callback to set up the Jacobian system.
+     *
+     * This callback gives full control to users to set up the tangent
+     * operator $\dfrac{\partial F}{\partial x}$.
+     *
+     * Solvers must be provided via NonlinearSolver::solve_with_jacobian.
+     */
+    std::function<int(const VectorType &x)> setup_jacobian;
+
+    /**
+     * Callback for the solution of the tangent system set up with
+     * NonlinearSolver::setup_jacobian.
+     *
+     * This is used as a preconditioner inside the Krylov process.
+     */
+    std::function<int(const VectorType &src, VectorType &dst)>
+      solve_with_jacobian;
+
+    /**
+     * Callback for the computation of the energy function.
+     *
+     * This is usually not needed, since by default SNES assumes that the
+     * objective function to be minimized is $\frac{1}{2} || F(x) ||^2 $.
+     *
+     * However, if the nonlinear equations are derived from energy arguments, it
+     * may be useful to use this callback to perform linesearch or to test for
+     * the reduction in a trust region step.
+     *
+     * The value of the energy function must be returned in @p energy_value.
+     */
+    std::function<int(const VectorType &x, real_type &energy_value)> energy;
+
+  private:
+    /**
+     * The PETSc SNES object.
+     */
+    SNES snes;
+
+    SmartPointer<AMatrixType, NonlinearSolver> A;
+    SmartPointer<PMatrixType, NonlinearSolver> P;
+
+    /**
+     * This flag is used to support versions of PETSc older than 3.13.
+     */
+    bool need_dummy_assemble;
+  };
+
+} // namespace PETScWrappers
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // DEAL_II_WITH_PETSC
+
+#endif
diff --git a/include/deal.II/lac/petsc_snes.templates.h b/include/deal.II/lac/petsc_snes.templates.h
new file mode 100644 (file)
index 0000000..dff02f5
--- /dev/null
@@ -0,0 +1,425 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//---------------------------------------------------------------
+
+#ifndef dealii_petsc_snes_templates_h
+#define dealii_petsc_snes_templates_h
+
+#include <deal.II/base/config.h>
+
+#ifdef DEAL_II_WITH_PETSC
+#  include <deal.II/base/exceptions.h>
+
+#  include <deal.II/lac/petsc_precondition.h>
+#  include <deal.II/lac/petsc_snes.h>
+
+#  include <petscdm.h>
+#  include <petscsnes.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+// Shorthand notation for PETSc error codes.
+#  define AssertPETSc(code)                          \
+    do                                               \
+      {                                              \
+        PetscErrorCode ierr = (code);                \
+        AssertThrow(ierr == 0, ExcPETScError(ierr)); \
+      }                                              \
+    while (0)
+
+// Shorthand notation for User error codes.
+#  define AssertUser(code, name)                                               \
+    do                                                                         \
+      {                                                                        \
+        int ierr = (code);                                                     \
+        AssertThrow(ierr == 0,                                                 \
+                    StandardExceptions::ExcFunctionNonzeroReturn(name, ierr)); \
+      }                                                                        \
+    while (0)
+
+namespace PETScWrappers
+{
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::NonlinearSolver(
+    const NonlinearSolverData &data,
+    const MPI_Comm &           mpi_comm)
+  {
+    AssertPETSc(SNESCreate(mpi_comm, &snes));
+    AssertPETSc(SNESSetApplicationContext(snes, this));
+    reinit(data);
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::operator SNES() const
+  {
+    return snes;
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  SNES
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::petsc_snes()
+  {
+    return snes;
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  MPI_Comm
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::get_mpi_communicator()
+    const
+  {
+    return PetscObjectComm(reinterpret_cast<PetscObject>(snes));
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::~NonlinearSolver()
+  {
+    AssertPETSc(SNESDestroy(&snes));
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  void
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::reinit()
+  {
+    AssertPETSc(SNESReset(snes));
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  void
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::reinit(
+    const NonlinearSolverData &data)
+  {
+    reinit();
+
+    // Solver type
+    if (data.snes_type.size())
+      AssertPETSc(SNESSetType(snes, data.snes_type.c_str()));
+
+    // Linesearch type
+    if (data.snes_linesearch_type.size())
+      {
+        SNESLineSearch linesearch;
+        AssertPETSc(SNESGetLineSearch(snes, &linesearch));
+        AssertPETSc(
+          SNESLineSearchSetType(linesearch, data.snes_linesearch_type.c_str()));
+      }
+
+    // Options prefix
+    if (data.options_prefix.size())
+      AssertPETSc(SNESSetOptionsPrefix(snes, data.options_prefix.c_str()));
+
+    // Solver tolerances
+    PetscReal atol = data.absolute_tolerance > 0.0 ?
+                       data.absolute_tolerance :
+                       static_cast<PetscReal>(PETSC_DEFAULT);
+    PetscReal rtol = data.relative_tolerance > 0.0 ?
+                       data.relative_tolerance :
+                       static_cast<PetscReal>(PETSC_DEFAULT);
+    PetscReal stol = data.step_tolerance > 0.0 ?
+                       data.step_tolerance :
+                       static_cast<PetscReal>(PETSC_DEFAULT);
+
+    // Maximum number of iterations and function evaluations.
+    PetscInt maxit = data.maximum_non_linear_iterations >= 0 ?
+                       data.maximum_non_linear_iterations :
+                       static_cast<PetscInt>(PETSC_DEFAULT);
+    PetscInt maxfe = data.max_n_function_evaluations >= 0 ?
+                       data.max_n_function_evaluations :
+                       static_cast<PetscInt>(PETSC_DEFAULT);
+    AssertPETSc(SNESSetTolerances(snes, atol, rtol, stol, maxit, maxfe));
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  void
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::set_matrix(
+    PMatrixType &P)
+  {
+    this->A = nullptr;
+    this->P = &P;
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  void
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::set_matrices(
+    AMatrixType &A,
+    PMatrixType &P)
+  {
+    this->A = &A;
+    this->P = &P;
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  unsigned int
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::solve(VectorType &x)
+  {
+    const auto snes_function =
+      [](SNES, Vec x, Vec f, void *ctx) -> PetscErrorCode {
+      PetscFunctionBeginUser;
+      auto user = static_cast<NonlinearSolver *>(ctx);
+
+      VectorType xdealii(x);
+      VectorType fdealii(f);
+      AssertUser(user->residual(xdealii, fdealii), "residual");
+      petsc_increment_state_counter(f);
+      PetscFunctionReturn(0);
+    };
+
+    const auto snes_jacobian =
+      [](SNES, Vec x, Mat A, Mat P, void *ctx) -> PetscErrorCode {
+      PetscFunctionBeginUser;
+      auto user = static_cast<NonlinearSolver *>(ctx);
+
+      VectorType  xdealii(x);
+      AMatrixType Adealii(A);
+      PMatrixType Pdealii(P);
+      AssertUser(user->jacobian(xdealii, Adealii, Pdealii), "jacobian");
+      petsc_increment_state_counter(P);
+
+      // Handle the Jacobian-free case
+      // This call allows to resample the linearization point
+      // of the MFFD tangent operator
+      PetscBool flg;
+      AssertPETSc(PetscObjectTypeCompare((PetscObject)A, MATMFFD, &flg));
+      if (flg)
+        {
+          AssertPETSc(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
+          AssertPETSc(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
+        }
+      else
+        petsc_increment_state_counter(A);
+      PetscFunctionReturn(0);
+    };
+
+    const auto snes_jacobian_with_setup =
+      [](SNES, Vec x, Mat A, Mat P, void *ctx) -> PetscErrorCode {
+      PetscFunctionBeginUser;
+      auto user = static_cast<NonlinearSolver *>(ctx);
+
+      VectorType  xdealii(x);
+      AMatrixType Adealii(A);
+      PMatrixType Pdealii(P);
+
+      user->A = &Adealii;
+      user->P = &Pdealii;
+      AssertUser(user->setup_jacobian(xdealii), "setup_jacobian");
+      petsc_increment_state_counter(P);
+
+      // Handle older versions of PETSc for which we cannot pass a MATSHELL
+      // matrix to DMSetMatType. This has been fixed from 3.13 on.
+      if (user->need_dummy_assemble)
+        {
+          AssertPETSc(MatZeroEntries(P));
+          AssertPETSc(MatAssemblyBegin(P, MAT_FINAL_ASSEMBLY));
+          AssertPETSc(MatAssemblyEnd(P, MAT_FINAL_ASSEMBLY));
+        }
+
+      // Handle the Jacobian-free case
+      // This call allows to resample the linearization point
+      // of the MFFD tangent operator
+      PetscBool flg;
+      AssertPETSc(PetscObjectTypeCompare((PetscObject)A, MATMFFD, &flg));
+      if (flg)
+        {
+          AssertPETSc(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY));
+          AssertPETSc(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY));
+        }
+      else
+        petsc_increment_state_counter(A);
+      PetscFunctionReturn(0);
+    };
+
+    const auto snes_monitor =
+      [](SNES snes, PetscInt it, PetscReal f, void *ctx) -> PetscErrorCode {
+      PetscFunctionBeginUser;
+      auto user = static_cast<NonlinearSolver *>(ctx);
+
+      Vec x;
+      AssertPETSc(SNESGetSolution(snes, &x));
+      VectorType xdealii(x);
+      AssertUser(user->monitor(xdealii, it, f), "monitor");
+      PetscFunctionReturn(0);
+    };
+
+    const auto snes_objective =
+      [](SNES, Vec x, PetscReal *f, void *ctx) -> PetscErrorCode {
+      PetscFunctionBeginUser;
+      auto user = static_cast<NonlinearSolver *>(ctx);
+
+      real_type  v;
+      VectorType xdealii(x);
+      AssertUser(user->energy(xdealii, v), "energy");
+      *f = v;
+      PetscFunctionReturn(0);
+    };
+
+    AssertThrow(residual,
+                StandardExceptions::ExcFunctionNotProvided("residual"));
+
+    AssertPETSc(SNESSetSolution(snes, x.petsc_vector()));
+
+    AssertPETSc(SNESSetFunction(snes, nullptr, snes_function, this));
+
+    if (energy)
+      AssertPETSc(SNESSetObjective(snes, snes_objective, this));
+
+    if (setup_jacobian)
+      {
+        AssertPETSc(SNESSetJacobian(snes,
+                                    A ? A->petsc_matrix() : nullptr,
+                                    P ? P->petsc_matrix() : nullptr,
+                                    snes_jacobian_with_setup,
+                                    this));
+        if (!A)
+          set_use_matrix_free(snes, true, false);
+
+
+        // Do not waste memory by creating a dummy AIJ matrix inside PETSc.
+        this->need_dummy_assemble = false;
+        if (!P)
+          {
+#  if DEAL_II_PETSC_VERSION_GTE(3, 13, 0)
+            DM dm;
+            AssertPETSc(SNESGetDM(snes, &dm));
+            AssertPETSc(DMSetMatType(dm, MATSHELL));
+#  else
+            this->need_dummy_assemble = true;
+#  endif
+          }
+      }
+    else
+      {
+        if (jacobian)
+          {
+            AssertPETSc(SNESSetJacobian(snes,
+                                        A ? A->petsc_matrix() :
+                                            (P ? P->petsc_matrix() : nullptr),
+                                        P ? P->petsc_matrix() : nullptr,
+                                        snes_jacobian,
+                                        this));
+          }
+        else
+          // The user did not set any Jacobian callback. PETSc default in this
+          // case is to use FD and thus assemble a dense operator by finite
+          // differencing the residual callbacks. Here instead we decide to
+          // use a full matrix-free approach by default. This choice can always
+          // be overriden from command line.
+          {
+            set_use_matrix_free(snes, false, true);
+          }
+      }
+
+    // In case solve_with_jacobian is provided, create a shell
+    // preconditioner wrapping the user call. The internal Krylov
+    // solver will apply the preconditioner only once. This choice
+    // can be overriden by command line and users can use any other
+    // Krylov method if their solve is not accurate enough.
+    // Using solve_with_jacobian as a preconditioner allows users
+    // to provide approximate solvers and possibly iterate on a matrix-free
+    // approximation of the tangent operator.
+    PreconditionShell precond(
+      PetscObjectComm(reinterpret_cast<PetscObject>(snes)));
+    if (solve_with_jacobian)
+      {
+        precond.vmult = [&](VectorBase &indst, const VectorBase &insrc) -> int {
+          VectorType       dst(static_cast<const Vec &>(indst));
+          const VectorType src(static_cast<const Vec &>(insrc));
+          return solve_with_jacobian(src, dst);
+        };
+
+        // Default Krylov solver (preconditioner only)
+        KSP ksp;
+        AssertPETSc(SNESGetKSP(snes, &ksp));
+        AssertPETSc(KSPSetType(ksp, KSPPREONLY));
+        AssertPETSc(KSPSetPC(ksp, precond.get_pc()));
+      }
+
+    // Attach user monitoring routine
+    if (monitor)
+      AssertPETSc(SNESMonitorSet(snes, snes_monitor, this, nullptr));
+
+    // Allow command line customization.
+    AssertPETSc(SNESSetFromOptions(snes));
+
+    // Having set everything up, now do the actual work
+    // and let PETSc solve the system.
+    // Older versions of PETSc requires the solution vector specified even
+    // if we specified SNESSetSolution upfront
+    AssertPETSc(SNESSolve(snes, nullptr, x.petsc_vector()));
+
+    // Get the number of steps taken.
+    PetscInt nt;
+    AssertPETSc(SNESGetIterationNumber(snes, &nt));
+
+    // Raise an exception if the solver has not converged
+    SNESConvergedReason reason;
+    AssertPETSc(SNESGetConvergedReason(snes, &reason));
+    AssertThrow(reason > 0,
+                ExcMessage("SNES solver did not converge after " +
+                           std::to_string(nt) + " iterations with reason " +
+                           SNESConvergedReasons[reason]));
+
+    // Finally return
+    return nt;
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  unsigned int
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::solve(VectorType & x,
+                                                               PMatrixType &P)
+  {
+    set_matrix(P);
+    return solve(x);
+  }
+
+
+
+  template <typename VectorType, typename PMatrixType, typename AMatrixType>
+  unsigned int
+  NonlinearSolver<VectorType, PMatrixType, AMatrixType>::solve(VectorType & x,
+                                                               AMatrixType &A,
+                                                               PMatrixType &P)
+  {
+    set_matrices(A, P);
+    return solve(x);
+  }
+
+} // namespace PETScWrappers
+
+#  undef AssertPETSc
+#  undef AssertUser
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // DEAL_II_WITH_PETSC
+
+#endif
index 7be2a079a74cb593fdc70802fd9def51610033d2..157dc9d834d4134d57955c80be611a6b9003ca2c 100644 (file)
@@ -108,6 +108,7 @@ if(DEAL_II_WITH_PETSC)
     petsc_parallel_sparse_matrix.cc
     petsc_parallel_vector.cc
     petsc_precondition.cc
+    petsc_snes.cc
     petsc_solver.cc
     petsc_ts.cc
     petsc_sparse_matrix.cc
diff --git a/source/lac/petsc_snes.cc b/source/lac/petsc_snes.cc
new file mode 100644 (file)
index 0000000..065af3a
--- /dev/null
@@ -0,0 +1,74 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2023 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef DOXYGEN
+
+#  include <deal.II/base/config.h>
+
+#  ifdef DEAL_II_WITH_PETSC
+
+#    include <deal.II/lac/petsc_block_sparse_matrix.h>
+#    include <deal.II/lac/petsc_snes.templates.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace PETScWrappers
+{
+  void
+  NonlinearSolverData::add_parameters(ParameterHandler &prm)
+  {
+    prm.enter_subsection("Running parameters");
+    prm.add_parameter(
+      "options prefix",
+      options_prefix,
+      "The string indicating the options prefix for command line customization.");
+    prm.add_parameter("solver type",
+                      snes_type,
+                      "The string indicating the PETSc SNES type.");
+    prm.add_parameter("linesearch type",
+                      snes_linesearch_type,
+                      "The string indicating the PETSc linesearch type.");
+    prm.add_parameter("absolute error tolerance",
+                      absolute_tolerance,
+                      "Absolute error tolerance.");
+    prm.add_parameter("relative error tolerance",
+                      relative_tolerance,
+                      "Relative error tolerance.");
+    prm.add_parameter("step tolerance", step_tolerance, "Step tolerance.");
+    prm.add_parameter("maximum iterations",
+                      maximum_non_linear_iterations,
+                      "Maximum number of iterations allowed.");
+    prm.add_parameter("maximum function evaluations",
+                      max_n_function_evaluations,
+                      "Maximum number of function evaluations allowed.");
+    prm.leave_subsection();
+  }
+
+} // namespace PETScWrappers
+
+template class PETScWrappers::NonlinearSolver<>;
+template class PETScWrappers::NonlinearSolver<PETScWrappers::MPI::Vector>;
+template class PETScWrappers::NonlinearSolver<PETScWrappers::MPI::BlockVector>;
+template class PETScWrappers::NonlinearSolver<PETScWrappers::MPI::Vector,
+                                              PETScWrappers::MPI::SparseMatrix>;
+template class PETScWrappers::NonlinearSolver<
+  PETScWrappers::MPI::BlockVector,
+  PETScWrappers::MPI::BlockSparseMatrix>;
+
+
+DEAL_II_NAMESPACE_CLOSE
+
+#  endif // DEAL_II_WITH_PETSC
+#endif
diff --git a/tests/petsc/petsc_snes_00.cc b/tests/petsc/petsc_snes_00.cc
new file mode 100644 (file)
index 0000000..ddc9c38
--- /dev/null
@@ -0,0 +1,206 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//-----------------------------------------------------------
+
+#include <deal.II/base/parameter_handler.h>
+
+#include <deal.II/lac/petsc_matrix_base.h>
+#include <deal.II/lac/petsc_snes.h>
+#include <deal.II/lac/petsc_vector.h>
+
+#include <cmath>
+
+#include "../tests.h"
+
+/**
+ * Solves the nonlinear system of equations
+ *
+ * (x - y^3 + 1)^3 - y^3 = 0
+ * x + 2y - 3 = 0
+ *
+ * using the PETScWrappers::NonlinearSolver class
+ * that interfaces PETSc SNES solver object.
+ *
+ * Here we test a pure SNES interface and a deal.II approach
+ * when users take control of solving the linear systems.
+ */
+using VectorType = PETScWrappers::MPI::Vector;
+using MatrixType = PETScWrappers::MatrixBase;
+using Solver     = PETScWrappers::NonlinearSolver<VectorType, MatrixType>;
+using real_type  = Solver::real_type;
+
+int
+main(int argc, char **argv)
+{
+  Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);
+
+  std::ofstream out("output");
+
+  PETScWrappers::NonlinearSolverData data;
+  ParameterHandler                   prm;
+
+  data.add_parameters(prm);
+  out << "# Default Parameters" << std::endl;
+  prm.print_parameters(out, ParameterHandler::ShortText);
+
+  std::ifstream ifile(SOURCE_DIR "/petsc_snes_00_in.prm");
+  prm.parse_input(ifile);
+
+  out << "# Testing Parameters" << std::endl;
+  prm.print_parameters(out, ParameterHandler::ShortText);
+
+  {
+    out << "# Test PETSc interface (MFFD)" << std::endl;
+
+    // Create and customize the solver
+    Solver solver(data);
+
+    // Here we only pass the callback for the function evaluation
+    // The tangent system will be approximated via matrix-free finite
+    // differencing.
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      auto x = X[0];
+      auto y = X[1];
+      F(0)   = std::pow(x - std::pow(y, 3) + 1, 3) - std::pow(y, 3);
+      F(1)   = x + 2 * y - 3;
+      F.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    // Test attaching a user-defined monitoring routine
+    solver.monitor = [&](const VectorType & X,
+                         const unsigned int step,
+                         const real_type    fval) -> int {
+      out << "#    " << step << ": " << fval << std::endl;
+      return 0;
+    };
+
+    // Create and initialize solution vector
+    VectorType x(MPI_COMM_SELF, 2, 2);
+    x = 0.0;
+
+    // Solve the nonlinear system (defaults to Newton with cubic-backtracking)
+    auto nit = solver.solve(x);
+
+    out << "#   Solution " << x[0] << ", " << x[1] << std::endl;
+    out << "#   Iterations " << nit << std::endl;
+  }
+
+  {
+    out << "# Test PETSc interface" << std::endl;
+
+    Solver solver(data);
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      auto x = X[0];
+      auto y = X[1];
+      F(0)   = std::pow(x - std::pow(y, 3) + 1, 3) - std::pow(y, 3);
+      F(1)   = x + 2 * y - 3;
+      F.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    // Here we use the Jacobian callback following the PETSc style,
+    // where matrices are used as output arguments.
+    // This test uses A == P, so we do not do anything on A
+    // The intermediate callbacks used to pass from PETSc to deal.II
+    // will handle the case of users requesting Jacobian-free Newton
+    // Krylov (i.e. using -snes_mf_operator)
+    solver.jacobian =
+      [&](const VectorType &X, MatrixType &A, MatrixType &P) -> int {
+      auto x    = X[0];
+      auto y    = X[1];
+      auto f0_x = 3 * std::pow(x - std::pow(y, 3) + 1, 2);
+      auto f0_y = -9 * std::pow(x - std::pow(y, 3) + 1, 2) * std::pow(y, 2) -
+                  3 * std::pow(y, 2);
+      P.set(0, 0, f0_x);
+      P.set(0, 1, f0_y);
+      P.set(1, 0, 1);
+      P.set(1, 1, 2);
+      P.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    VectorType x(MPI_COMM_SELF, 2, 2);
+    x = 0.0;
+
+    // Solve the nonlinear system without specifying matrices
+    // This will use the default matrix type in PETSc for SNES
+    // (MATDENSE), which is ok for our test.
+    // See petsc_snes_01.cc for a case where we pass the matrix
+    auto nit = solver.solve(x);
+
+    out << "#   Solution " << x[0] << ", " << x[1] << std::endl;
+    out << "#   Iterations " << nit << std::endl;
+  }
+
+  {
+    out << "# Test user interface" << std::endl;
+
+    Solver solver(data);
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      auto x = X[0];
+      auto y = X[1];
+      F(0)   = std::pow(x - std::pow(y, 3) + 1, 3) - std::pow(y, 3);
+      F(1)   = x + 2 * y - 3;
+      F.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    // When users want to be in full control of the linear system
+    // solves, they need to use setup_jacobian and solve_with_jacobian
+    // For example, in this case we compute the inverse of the Jacobian
+    // during setup_jacobian and we use it in the solve phase
+    FullMatrix<double> Jinv(2, 2);
+
+    solver.setup_jacobian = [&](const VectorType &X) -> int {
+      auto x    = X[0];
+      auto y    = X[1];
+      auto f0_x = 3 * std::pow(x - std::pow(y, 3) + 1, 2);
+      auto f0_y = -9 * std::pow(x - std::pow(y, 3) + 1, 2) * std::pow(y, 2) -
+                  3 * std::pow(y, 2);
+      FullMatrix<double> J(2, 2);
+      J(0, 0) = f0_x;
+      J(0, 1) = f0_y;
+      J(1, 0) = 1;
+      J(1, 1) = 2;
+      Jinv.invert(J);
+      return 0;
+    };
+
+    // Solve phase. By default, PETSc will use this callback as a preconditioner
+    // within a preconditioner only Krylov solve. Other Krylov
+    // solvers can still be used in a Jacobian-free way and selected at command
+    // line or within user code.
+    solver.solve_with_jacobian = [&](const VectorType &src,
+                                     VectorType &      dst) -> int {
+      dst(0) = Jinv(0, 0) * src(0) + Jinv(0, 1) * src(1);
+      dst(1) = Jinv(1, 0) * src(0) + Jinv(1, 1) * src(1);
+      dst.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    VectorType x(MPI_COMM_SELF, 2, 2);
+    x = 0.0;
+
+    // We don't need to pass matrices, since in this case we don't need them
+    auto nit = solver.solve(x);
+
+    out << "#   Solution " << x[0] << ", " << x[1] << std::endl;
+    out << "#   Iterations " << nit << std::endl;
+  }
+
+  return 0;
+}
diff --git a/tests/petsc/petsc_snes_00.output b/tests/petsc/petsc_snes_00.output
new file mode 100644 (file)
index 0000000..dd9aea4
--- /dev/null
@@ -0,0 +1,40 @@
+# Default Parameters
+subsection Running parameters
+  set absolute error tolerance     = 0
+  set linesearch type              = 
+  set maximum function evaluations = -1
+  set maximum iterations           = -1
+  set options prefix               = 
+  set relative error tolerance     = 0
+  set solver type                  = 
+  set step tolerance               = 0
+end
+# Testing Parameters
+subsection Running parameters
+  set absolute error tolerance     = 1.e-9
+  set linesearch type              = 
+  set maximum function evaluations = 77
+  set maximum iterations           = 23
+  set options prefix               = 
+  set relative error tolerance     = 2.e-9
+  set solver type                  = newtonls
+  set step tolerance               = 3.e-9
+end
+# Test PETSc interface (MFFD)
+#     0: 3.16228
+#     1: 2.84158
+#     2: 2.5438
+#     3: 2.27647
+#     4: 1.36494
+#     5: 0.288774
+#     6: 0.0203366
+#     7: 7.92738e-05
+#     8: 1.22106e-09
+#   Solution 1, 1
+#   Iterations 8
+# Test PETSc interface
+#   Solution 1, 1
+#   Iterations 8
+# Test user interface
+#   Solution 1, 1
+#   Iterations 8
diff --git a/tests/petsc/petsc_snes_00_in.prm b/tests/petsc/petsc_snes_00_in.prm
new file mode 100644 (file)
index 0000000..100e747
--- /dev/null
@@ -0,0 +1,8 @@
+subsection Running parameters
+  set solver type       = newtonls
+  set absolute error tolerance = 1.e-9
+  set relative error tolerance = 2.e-9
+  set step tolerance = 3.e-9
+  set maximum iterations = 23
+  set maximum function evaluations = 77
+end
diff --git a/tests/petsc/petsc_snes_01.cc b/tests/petsc/petsc_snes_01.cc
new file mode 100644 (file)
index 0000000..916d70f
--- /dev/null
@@ -0,0 +1,182 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//-----------------------------------------------------------
+
+#include <deal.II/base/parameter_handler.h>
+
+#include <deal.II/lac/petsc_block_sparse_matrix.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_snes.h>
+
+#include <cmath>
+
+#include "../tests.h"
+
+/**
+ * Solves the nonlinear system of equations
+ *
+ * (x - y^3 + 1)^3 - y^3 = 0
+ * x + 2y - 3 = 0
+ *
+ * using the PETScWrappers::NonlinearSolver class
+ * that interfaces PETSc SNES solver object.
+ *
+ * This code tests the block interface.
+ * See petsc_snes_00.cc for additional information on the callbacks
+ */
+using VectorType = PETScWrappers::MPI::BlockVector;
+using MatrixType = PETScWrappers::MPI::BlockSparseMatrix;
+using Solver     = PETScWrappers::NonlinearSolver<VectorType, MatrixType>;
+
+int
+main(int argc, char **argv)
+{
+  Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);
+
+  std::ofstream out("output");
+
+  {
+    out << "# Test PETSc interface (MFFD)" << std::endl;
+
+    Solver solver;
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      auto x = X.block(0)[0];
+      auto y = X.block(1)[0];
+
+      F.block(0)[0] = std::pow(x - std::pow(y, 3) + 1, 3) - std::pow(y, 3);
+      F.block(1)[0] = x + 2 * y - 3;
+      F.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    VectorType x(2, MPI_COMM_SELF, 1, 1);
+    x = 0.0;
+
+    auto nit = solver.solve(x);
+
+    out << "#   Solution " << x[0] << ", " << x[1] << std::endl;
+    out << "#   Iterations " << nit << std::endl;
+  }
+
+  {
+    out << "# Test PETSc interface" << std::endl;
+
+    Solver solver;
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      auto x = X.block(0)[0];
+      auto y = X.block(1)[0];
+
+      F.block(0)[0] = std::pow(x - std::pow(y, 3) + 1, 3) - std::pow(y, 3);
+      F.block(1)[0] = x + 2 * y - 3;
+      F.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    solver.jacobian =
+      [&](const VectorType &X, MatrixType &A, MatrixType &P) -> int {
+      auto x    = X.block(0)[0];
+      auto y    = X.block(1)[0];
+      auto f0_x = 3 * std::pow(x - std::pow(y, 3) + 1, 2);
+      auto f0_y = -9 * std::pow(x - std::pow(y, 3) + 1, 2) * std::pow(y, 2) -
+                  3 * std::pow(y, 2);
+      P.block(0, 0).set(0, 0, f0_x);
+      P.block(0, 1).set(0, 0, f0_y);
+      P.block(1, 0).set(0, 0, 1);
+      P.block(1, 1).set(0, 0, 2);
+      P.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    VectorType x(2, MPI_COMM_SELF, 1, 1);
+    x = 0.0;
+
+    // We need to create a representative matrix upfront
+    MatrixType J;
+    J.reinit(2, 2);
+    DynamicSparsityPattern csp(1, 1);
+    csp.add(0, 0);
+    IndexSet indices(1);
+    indices.add_range(0, 1);
+    for (unsigned int row = 0; row < 2; ++row)
+      for (unsigned int col = 0; col < 2; ++col)
+        J.block(row, col).reinit(indices, indices, csp, MPI_COMM_SELF);
+    J.collect_sizes();
+
+    // Solve the nonlinear system and specify the matrix instance to
+    // use. The matrix will be returned back to the user for assembly.
+    // Note that this is currently done by wrapping PETSc's Mat objects
+    // within light-weight on-the-fly constructors.
+    // Any additional information stored in non standard members of J
+    // will be lost, unless it can be deducted from the Mat object.
+    auto nit = solver.solve(x, J);
+
+    out << "#   Solution " << x[0] << ", " << x[1] << std::endl;
+    out << "#   Iterations " << nit << std::endl;
+  }
+
+  {
+    out << "# Test user interface" << std::endl;
+
+    Solver solver;
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      auto x = X.block(0)[0];
+      auto y = X.block(1)[0];
+
+      F.block(0)[0] = std::pow(x - std::pow(y, 3) + 1, 3) - std::pow(y, 3);
+      F.block(1)[0] = x + 2 * y - 3;
+      F.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    FullMatrix<double> Jinv(2, 2);
+
+    solver.setup_jacobian = [&](const VectorType &X) -> int {
+      auto x    = X.block(0)[0];
+      auto y    = X.block(1)[0];
+      auto f0_x = 3 * std::pow(x - std::pow(y, 3) + 1, 2);
+      auto f0_y = -9 * std::pow(x - std::pow(y, 3) + 1, 2) * std::pow(y, 2) -
+                  3 * std::pow(y, 2);
+      FullMatrix<double> J(2, 2);
+      J(0, 0) = f0_x;
+      J(0, 1) = f0_y;
+      J(1, 0) = 1;
+      J(1, 1) = 2;
+      Jinv.invert(J);
+      return 0;
+    };
+
+    solver.solve_with_jacobian = [&](const VectorType &src,
+                                     VectorType &      dst) -> int {
+      dst.block(0)[0] =
+        Jinv(0, 0) * src.block(0)[0] + Jinv(0, 1) * src.block(1)[0];
+      dst.block(1)[0] =
+        Jinv(1, 0) * src.block(0)[0] + Jinv(1, 1) * src.block(1)[0];
+      dst.compress(VectorOperation::insert);
+      return 0;
+    };
+
+    VectorType x(2, MPI_COMM_SELF, 1, 1);
+    x = 0.0;
+
+    auto nit = solver.solve(x);
+
+    out << "#   Solution " << x[0] << ", " << x[1] << std::endl;
+    out << "#   Iterations " << nit << std::endl;
+  }
+
+  return 0;
+}
diff --git a/tests/petsc/petsc_snes_01.output b/tests/petsc/petsc_snes_01.output
new file mode 100644 (file)
index 0000000..ac95214
--- /dev/null
@@ -0,0 +1,9 @@
+# Test PETSc interface (MFFD)
+#   Solution 1, 1
+#   Iterations 8
+# Test PETSc interface
+#   Solution 1, 1
+#   Iterations 8
+# Test user interface
+#   Solution 1, 1
+#   Iterations 8
diff --git a/tests/petsc/petsc_snes_02.cc b/tests/petsc/petsc_snes_02.cc
new file mode 100644 (file)
index 0000000..35fc975
--- /dev/null
@@ -0,0 +1,85 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//-----------------------------------------------------------
+
+#include <deal.II/lac/exceptions.h>
+#include <deal.II/lac/petsc_snes.templates.h>
+
+#include "../tests.h"
+
+/**
+ * Tests user defined Vector and Matrix types
+ * and exceptions handling for PETSCWrappers::NonlinearSolver.
+ */
+
+class VectorType : public Subscriptor
+{
+public:
+  explicit VectorType(Vec v)
+    : v(v)
+  {}
+
+  Vec &
+  petsc_vector()
+  {
+    return v;
+  }
+
+private:
+  Vec v;
+};
+
+class MatrixType : public Subscriptor
+{
+public:
+  explicit MatrixType(Mat A)
+    : A(A)
+  {}
+
+  Mat &
+  petsc_matrix()
+  {
+    return A;
+  }
+
+private:
+  Mat A;
+};
+
+using NonlinearSolver = PETScWrappers::NonlinearSolver<VectorType, MatrixType>;
+
+int
+main(int argc, char **argv)
+{
+  initlog();
+
+  Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);
+
+  VectorType v(nullptr);
+  MatrixType A(nullptr);
+
+  NonlinearSolver mysolver;
+
+  try
+    {
+      auto snes = mysolver.petsc_snes();
+      AssertThrow(snes == static_cast<SNES>(mysolver), ExcInternalError());
+      mysolver.solve(v, A);
+    }
+  catch (std::exception &exc)
+    {
+      deallog << exc.what() << std::endl;
+    }
+  return 0;
+}
diff --git a/tests/petsc/petsc_snes_02.output b/tests/petsc/petsc_snes_02.output
new file mode 100644 (file)
index 0000000..7d1d25f
--- /dev/null
@@ -0,0 +1,11 @@
+
+DEAL::
+--------------------------------------------------------
+An error occurred in file <petsc_snes.templates.h> in function
+    unsigned int dealii::PETScWrappers::NonlinearSolver<VectorType, PMatrixType, AMatrixType>::solve(VectorType&) [with VectorType = VectorType; PMatrixType = MatrixType; AMatrixType = MatrixType]
+The violated condition was: 
+    residual
+Additional information: 
+    Please provide an implementation for the function "residual"
+--------------------------------------------------------
+
diff --git a/tests/petsc/petsc_snes_03.cc b/tests/petsc/petsc_snes_03.cc
new file mode 100644 (file)
index 0000000..b7d48a2
--- /dev/null
@@ -0,0 +1,70 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//-----------------------------------------------------------
+
+#include <deal.II/base/parameter_handler.h>
+
+#include <deal.II/lac/petsc_block_sparse_matrix.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_snes.h>
+
+#include <cmath>
+
+#include "../tests.h"
+
+/**
+ * Solves the minimization problem
+ *
+ * min_x ||x||^2
+ *
+ * using the PETScWrappers::NonlinearSolver class
+ * that interfaces PETSc SNES solver object.
+ *
+ * This code tests the objective function (energy) callback.
+ */
+using VectorType = PETScWrappers::MPI::Vector;
+using Solver     = PETScWrappers::NonlinearSolver<VectorType>;
+using real_type  = Solver::real_type;
+
+int
+main(int argc, char **argv)
+{
+  initlog();
+  Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);
+
+  {
+    Solver solver;
+
+    solver.energy = [&](const VectorType &X, real_type &v) -> int {
+      v = X.norm_sqr();
+      return 0;
+    };
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      F.equ(2, X);
+      return 0;
+    };
+
+    auto       commsize = Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
+    auto       commrank = Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
+    VectorType x(MPI_COMM_WORLD, 10, commrank == commsize - 1 ? 10 : 0);
+    x = 1.0;
+
+    auto nit = solver.solve(x);
+
+    deallog << "Iterations " << nit << std::endl;
+    x.print(deallog.get_file_stream());
+  }
+  return 0;
+}
diff --git a/tests/petsc/petsc_snes_03.mpirun=3.output b/tests/petsc/petsc_snes_03.mpirun=3.output
new file mode 100644 (file)
index 0000000..9f16c50
--- /dev/null
@@ -0,0 +1,3 @@
+
+DEAL::Iterations 1
+[Proc2 0-9] -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 
diff --git a/tests/petsc/petsc_snes_03.output b/tests/petsc/petsc_snes_03.output
new file mode 100644 (file)
index 0000000..3b1cde2
--- /dev/null
@@ -0,0 +1,3 @@
+
+DEAL::Iterations 1
+[Proc0 0-9] -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 -8.426e-09 
diff --git a/tests/petsc/petsc_snes_04.cc b/tests/petsc/petsc_snes_04.cc
new file mode 100644 (file)
index 0000000..9f09ff4
--- /dev/null
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------
+//
+//    Copyright (C) 2023 by the deal.II authors
+//
+//    This file is part of the deal.II library.
+//
+//    The deal.II library is free software; you can use it, redistribute
+//    it, and/or modify it under the terms of the GNU Lesser General
+//    Public License as published by the Free Software Foundation; either
+//    version 2.1 of the License, or (at your option) any later version.
+//    The full text of the license can be found in the file LICENSE.md at
+//    the top level directory of deal.II.
+//
+//-----------------------------------------------------------
+
+#include <deal.II/base/parameter_handler.h>
+
+#include <deal.II/lac/petsc_block_sparse_matrix.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_snes.h>
+
+#include <cmath>
+
+#include "../tests.h"
+
+/**
+ * Tests exception raise for non convergence.
+ */
+using VectorType = PETScWrappers::MPI::Vector;
+using Solver     = PETScWrappers::NonlinearSolver<VectorType>;
+using real_type  = Solver::real_type;
+
+int
+main(int argc, char **argv)
+{
+  initlog();
+  Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1);
+
+  {
+    PETScWrappers::NonlinearSolverData data;
+    data.maximum_non_linear_iterations = 0;
+
+    Solver solver(data);
+
+    solver.residual = [&](const VectorType &X, VectorType &F) -> int {
+      F.equ(2, X);
+      return 0;
+    };
+
+    auto       commsize = Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
+    auto       commrank = Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
+    VectorType x(MPI_COMM_WORLD, 10, commrank == commsize - 1 ? 10 : 0);
+    x = 1.0;
+
+    try
+      {
+        auto nit = solver.solve(x);
+      }
+    catch (std::exception &exc)
+      {
+        deallog << exc.what() << std::endl;
+      }
+  }
+  return 0;
+}
diff --git a/tests/petsc/petsc_snes_04.mpirun=3.output b/tests/petsc/petsc_snes_04.mpirun=3.output
new file mode 100644 (file)
index 0000000..f17430f
--- /dev/null
@@ -0,0 +1,12 @@
+
+DEAL::
+--------------------------------------------------------
+An error occurred in file <petsc_snes.templates.h> in function
+    unsigned int dealii::PETScWrappers::NonlinearSolver<VectorType, PMatrixType, AMatrixType>::solve(VectorType&) [with VectorType = dealii::PETScWrappers::MPI::Vector; PMatrixType = dealii::PETScWrappers::MatrixBase; AMatrixType = dealii::PETScWrappers::MatrixBase]
+The violated condition was: 
+    reason > 0
+Additional information: 
+    SNES solver did not converge after 0 iterations with reason
+    DIVERGED_MAX_IT
+--------------------------------------------------------
+
diff --git a/tests/petsc/petsc_snes_04.output b/tests/petsc/petsc_snes_04.output
new file mode 100644 (file)
index 0000000..f17430f
--- /dev/null
@@ -0,0 +1,12 @@
+
+DEAL::
+--------------------------------------------------------
+An error occurred in file <petsc_snes.templates.h> in function
+    unsigned int dealii::PETScWrappers::NonlinearSolver<VectorType, PMatrixType, AMatrixType>::solve(VectorType&) [with VectorType = dealii::PETScWrappers::MPI::Vector; PMatrixType = dealii::PETScWrappers::MatrixBase; AMatrixType = dealii::PETScWrappers::MatrixBase]
+The violated condition was: 
+    reason > 0
+Additional information: 
+    SNES solver did not converge after 0 iterations with reason
+    DIVERGED_MAX_IT
+--------------------------------------------------------
+

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.