def configure_pmg(self, snes, pdm): odm = snes.getDM() psnes = PETSc.SNES().create(comm=snes.comm) psnes.setOptionsPrefix(snes.getOptionsPrefix() + "pfas_") psnes.setType("fas") psnes.setDM(pdm) psnes.incrementTabLevel(1, parent=snes) (f, residual) = snes.getFunction() assert residual is not None (fun, args, kargs) = residual psnes.setFunction(fun, f.duplicate(), args=args, kargs=kargs) pdm.setGlobalVector(f.duplicate()) self.dummy = f.duplicate() psnes.setSolution(f.duplicate()) # PETSc unfortunately requires us to make an ugly hack. # We would like to use GMG for the coarse solve, at least # sometimes. But PETSc will use this p-DM's getRefineLevels() # instead of the getRefineLevels() of the MeshHierarchy to # decide how many levels it should use for PCMG applied to # the p-MG's coarse problem. So we need to set an option # for the user, if they haven't already; I don't know any # other way to get PETSc to know this at the right time. opts = PETSc.Options(snes.getOptionsPrefix() + "pfas_") if "fas_coarse_pc_mg_levels" not in opts: opts["fas_coarse_pc_mg_levels"] = odm.getRefineLevel() + 1 if "fas_coarse_snes_fas_levels" not in opts: opts["fas_coarse_snes_fas_levels"] = odm.getRefineLevel() + 1 return psnes
def __init__(self, problem, **kwargs): 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 pre_function_callback: As above, but called immediately before residual assembly Example usage of the ``solver_parameters`` option: to set the nonlinear solver type to just use a linear solver, use .. code-block:: python {'snes_type': 'ksponly'} PETSc flag options (where the presence of the option means something) should be specified with ``None``. For example: .. code-block:: python {'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:: python 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) parameters = kwargs.get("solver_parameters") if "parameters" in kwargs: raise TypeError("Use solver_parameters, not parameters") nullspace = kwargs.get("nullspace") nullspace_T = kwargs.get("transpose_nullspace") near_nullspace = kwargs.get("near_nullspace") options_prefix = kwargs.get("options_prefix") pre_j_callback = kwargs.get("pre_jacobian_callback") pre_f_callback = kwargs.get("pre_function_callback") super(NonlinearVariationalSolver, self).__init__(parameters, options_prefix) # Allow anything, interpret "matfree" as matrix_free. mat_type = self.parameters.get("mat_type") pmat_type = self.parameters.get("pmat_type") matfree = mat_type == "matfree" pmatfree = pmat_type == "matfree" appctx = kwargs.get("appctx") ctx = solving_utils._SNESContext(problem, mat_type=mat_type, pmat_type=pmat_type, appctx=appctx, pre_jacobian_callback=pre_j_callback, pre_function_callback=pre_f_callback, options_prefix=self.options_prefix) # No preconditioner by default for matrix-free if (problem.Jp is not None and pmatfree) or matfree: self.set_default_parameter("pc_type", "none") elif ctx.is_mixed: # Mixed problem, use jacobi pc if user has not supplied # one. self.set_default_parameter("pc_type", "jacobi") 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(nullspace_T, 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 = nullspace_T 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.appctx(dm, self._ctx): self.set_from_options(self.snes) # Used for custom grid transfer. self._transfer_operators = () self._setup = False
def __init__(self, problem, **kwargs): """ :arg problem: A :class:`NonlinearVariationalProblem` to solve. :kwarg nullspace: an optional :class:`.VectorSpaceBasis` (or :class:`.MixedVectorSpaceBasis`) spanning the null space of the operator. :kwarg solver_parameters: Solver parameters to pass to PETSc. This should be a dict mapping PETSc options to values. For example, to set the nonlinear solver type to just use a linear solver: :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. .. code-block:: python {'snes_type': 'ksponly'} PETSc flag options should be specified with `bool` values. For example: .. code-block:: python {'snes_monitor': True} """ parameters, nullspace, options_prefix = solving_utils._extract_kwargs( **kwargs) # Do this first so __del__ doesn't barf horribly if we get an # error in __init__ if options_prefix is not None: self._opt_prefix = options_prefix self._auto_prefix = False else: self._opt_prefix = 'firedrake_snes_%d_' % NonlinearVariationalSolver._id self._auto_prefix = True NonlinearVariationalSolver._id += 1 assert isinstance(problem, NonlinearVariationalProblem) ctx = solving_utils._SNESContext(problem) self.snes = PETSc.SNES().create() self.snes.setOptionsPrefix(self._opt_prefix) # Mixed problem, use jacobi pc if user has not supplied one. if ctx.is_mixed: parameters.setdefault('pc_type', 'jacobi') # Allow command-line arguments to override dict parameters opts = PETSc.Options() for k, v in opts.getAll().iteritems(): if k.startswith(self._opt_prefix): parameters[k[len(self._opt_prefix):]] = v self._problem = problem self._ctx = ctx 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) self.parameters = parameters
def __init__(self, problem, **kwargs): """ Solve a :class:`NonlinearVariationalProblem` on a hierarchy of meshes. :arg problem: A :class:`NonlinearVariationalProblem` or iterable thereof (if specifying the problem on each level by hand). :kwarg nullspace: an optional :class:`.VectorSpaceBasis` (or :class:`MixedVectorSpaceBasis`) spanning the null space of the operator. :kwarg solver_parameters: Solver parameters to pass to PETSc. This should be a dict mapping PETSc options to values. PETSc flag options should be specified with `bool` values (:data:`True` for on, :data:`False` for off). :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 :data:`solver_parameters` dict. .. note:: This solver is set up for use with geometric multigrid, that is you can use :data:`"snes_type": "fas"` or :data:`"pc_type": "mg"` transparently. """ # Do this first so __del__ doesn't barf horribly if we get an # error in __init__ parameters, nullspace, options_prefix \ = firedrake.solving_utils._extract_kwargs(**kwargs) if options_prefix is not None: self._opt_prefix = options_prefix self._auto_prefix = False else: self._opt_prefix = "firedrake_nlvsh_%d_" % NLVSHierarchy._id self._auto_prefix = True NLVSHierarchy._id += 1 if isinstance(problem, firedrake.NonlinearVariationalProblem): # We just got a single problem so coarsen up the hierarchy problems = [] while True: if problem: problems.append(problem) else: break problem = coarsen_problem(problem) problems.reverse() else: # User has provided list of problems problems = problem ctx = firedrake.solving_utils._SNESContext(problems) if nullspace is not None: raise NotImplementedError( "Coarsening nullspaces no yet implemented") snes = PETSc.SNES().create() snes.setDM(problems[-1].dm) self.problems = problems self.snes = snes self.ctx = ctx self.ctx.set_function(self.snes) self.ctx.set_jacobian(self.snes) self.snes.setOptionsPrefix(self._opt_prefix) # Allow command-line arguments to override dict parameters opts = PETSc.Options() for k, v in opts.getAll().iteritems(): if k.startswith(self._opt_prefix): parameters[k[len(self._opt_prefix):]] = v self.parameters = parameters