Ejemplo n.º 1
0
    def __init__(self,
                 problem,
                 *,
                 solver_parameters=None,
                 options_prefix=None,
                 nullspace=None,
                 transpose_nullspace=None,
                 near_nullspace=None,
                 appctx=None,
                 pre_jacobian_callback=None,
                 post_jacobian_callback=None,
                 pre_function_callback=None,
                 post_function_callback=None):
        r"""
        :arg problem: A :class:`NonlinearVariationalProblem` to solve.
        :kwarg nullspace: an optional :class:`.VectorSpaceBasis` (or
               :class:`.MixedVectorSpaceBasis`) spanning the null
               space of the operator.
        :kwarg transpose_nullspace: as for the nullspace, but used to
               make the right hand side consistent.
        :kwarg near_nullspace: as for the nullspace, but used to
               specify the near nullspace (for multigrid solvers).
        :kwarg solver_parameters: Solver parameters to pass to PETSc.
               This should be a dict mapping PETSc options to values.
        :kwarg appctx: A dictionary containing application context that
               is passed to the preconditioner if matrix-free.
        :kwarg options_prefix: an optional prefix used to distinguish
               PETSc options.  If not provided a unique prefix will be
               created.  Use this option if you want to pass options
               to the solver from the command line in addition to
               through the ``solver_parameters`` dict.
        :kwarg pre_jacobian_callback: A user-defined function that will
               be called immediately before Jacobian assembly. This can
               be used, for example, to update a coefficient function
               that has a complicated dependence on the unknown solution.
        :kwarg post_jacobian_callback: As above, but called after the
               Jacobian has been assembled.
        :kwarg pre_function_callback: As above, but called immediately
               before residual assembly.
        :kwarg post_function_callback: As above, but called immediately
               after residual assembly.

        Example usage of the ``solver_parameters`` option: to set the
        nonlinear solver type to just use a linear solver, use

        .. code-block:: python3

            {'snes_type': 'ksponly'}

        PETSc flag options (where the presence of the option means something) should
        be specified with ``None``.
        For example:

        .. code-block:: python3

            {'snes_monitor': None}

        To use the ``pre_jacobian_callback`` or ``pre_function_callback``
        functionality, the user-defined function must accept the current
        solution as a petsc4py Vec. Example usage is given below:

        .. code-block:: python3

            def update_diffusivity(current_solution):
                with cursol.dat.vec_wo as v:
                    current_solution.copy(v)
                solve(trial*test*dx == dot(grad(cursol), grad(test))*dx, diffusivity)

            solver = NonlinearVariationalSolver(problem,
                                                pre_jacobian_callback=update_diffusivity)

        """
        assert isinstance(problem, NonlinearVariationalProblem)

        solver_parameters = solving_utils.set_defaults(
            solver_parameters,
            problem.J.arguments(),
            ksp_defaults=self.DEFAULT_KSP_PARAMETERS,
            snes_defaults=self.DEFAULT_SNES_PARAMETERS)
        super().__init__(solver_parameters, options_prefix)
        # Now the correct parameters live in self.parameters (via the
        # OptionsManager mixin)
        mat_type = self.parameters.get("mat_type")
        pmat_type = self.parameters.get("pmat_type")
        ctx = solving_utils._SNESContext(
            problem,
            mat_type=mat_type,
            pmat_type=pmat_type,
            appctx=appctx,
            pre_jacobian_callback=pre_jacobian_callback,
            pre_function_callback=pre_function_callback,
            post_jacobian_callback=post_jacobian_callback,
            post_function_callback=post_function_callback,
            options_prefix=self.options_prefix)

        self.snes = PETSc.SNES().create(comm=problem.dm.comm)

        self._problem = problem

        self._ctx = ctx
        self._work = problem.u.dof_dset.layout_vec.duplicate()
        self.snes.setDM(problem.dm)

        ctx.set_function(self.snes)
        ctx.set_jacobian(self.snes)
        ctx.set_nullspace(nullspace,
                          problem.J.arguments()[0].function_space()._ises,
                          transpose=False,
                          near=False)
        ctx.set_nullspace(transpose_nullspace,
                          problem.J.arguments()[1].function_space()._ises,
                          transpose=True,
                          near=False)
        ctx.set_nullspace(near_nullspace,
                          problem.J.arguments()[0].function_space()._ises,
                          transpose=False,
                          near=True)
        ctx._nullspace = nullspace
        ctx._nullspace_T = transpose_nullspace
        ctx._near_nullspace = near_nullspace

        # Set from options now, so that people who want to noodle with
        # the snes object directly (mostly Patrick), can.  We need the
        # DM with an app context in place so that if the DM is active
        # on a subKSP the context is available.
        dm = self.snes.getDM()
        with dmhooks.add_hooks(dm, self, appctx=self._ctx, save=False):
            self.set_from_options(self.snes)

        # Used for custom grid transfer.
        self._transfer_operators = ()
        self._setup = False
Ejemplo n.º 2
0
    def __init__(self, A, *, P=None, solver_parameters=None,
                 nullspace=None, transpose_nullspace=None,
                 near_nullspace=None, options_prefix=None):
        """A linear solver for assembled systems (Ax = b).

        :arg A: a :class:`~.MatrixBase` (the operator).
        :arg P: an optional :class:`~.MatrixBase` to construct any
             preconditioner from; if none is supplied ``A`` is
             used to construct the preconditioner.
        :kwarg parameters: (optional) dict of solver parameters.
        :kwarg nullspace: an optional :class:`~.VectorSpaceBasis` (or
            :class:`~.MixedVectorSpaceBasis` spanning the null space
            of the operator.
        :kwarg transpose_nullspace: as for the nullspace, but used to
               make the right hand side consistent.
        :kwarg near_nullspace: as for the nullspace, but used to set
               the near nullpace.
        :kwarg options_prefix: an optional prefix used to distinguish
               PETSc options.  If not provided a unique prefix will be
               created.  Use this option if you want to pass options
               to the solver from the command line in addition to
               through the ``solver_parameters`` dict.

        .. note::

          Any boundary conditions for this solve *must* have been
          applied when assembling the operator.
        """
        if not isinstance(A, matrix.MatrixBase):
            raise TypeError("Provided operator is a '%s', not a MatrixBase" % type(A).__name__)
        if P is not None and not isinstance(P, matrix.MatrixBase):
            raise TypeError("Provided preconditioner is a '%s', not a MatrixBase" % type(P).__name__)

        solver_parameters = solving_utils.set_defaults(solver_parameters,
                                                       A.a.arguments(),
                                                       ksp_defaults=self.DEFAULT_KSP_PARAMETERS)
        self.A = A
        self.comm = A.comm
        self.P = P if P is not None else A

        # Set up parameters mixin
        super().__init__(solver_parameters, options_prefix)

        self.A.petscmat.setOptionsPrefix(self.options_prefix)
        self.P.petscmat.setOptionsPrefix(self.options_prefix)

        # If preconditioning matrix is matrix-free, then default to no
        # preconditioning.
        if isinstance(self.P, matrix.ImplicitMatrix):
            self.set_default_parameter("pc_type", "none")

        self.ksp = PETSc.KSP().create(comm=self.comm)

        W = self.test_space
        # DM provides fieldsplits (but not operators)
        self.ksp.setDM(W.dm)
        self.ksp.setDMActive(False)

        if nullspace is not None:
            nullspace._apply(self.A)
            if P is not None:
                nullspace._apply(self.P)

        if transpose_nullspace is not None:
            transpose_nullspace._apply(self.A, transpose=True)
            if P is not None:
                transpose_nullspace._apply(self.P, transpose=True)

        if near_nullspace is not None:
            near_nullspace._apply(self.A, near=True)
            if P is not None:
                near_nullspace._apply(self.P, near=True)

        self.nullspace = nullspace
        self.transpose_nullspace = transpose_nullspace
        self.near_nullspace = near_nullspace
        # Operator setting must come after null space has been
        # applied
        self.ksp.setOperators(A=self.A.petscmat, P=self.P.petscmat)
        # Set from options now (we're not allowed to change parameters
        # anyway).
        self.set_from_options(self.ksp)