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