def new_snes_ctx(pc, op, bcs, mat_type, fcp=None): """ Create a new SNES contex for nested preconditioning """ from firedrake.variational_solver import NonlinearVariationalProblem from firedrake.function import Function from firedrake.ufl_expr import action from firedrake.dmhooks import get_appctx from firedrake.solving_utils import _SNESContext dm = pc.getDM() old_appctx = get_appctx(dm).appctx u = Function(op.arguments()[-1].function_space()) F = action(op, u) nprob = NonlinearVariationalProblem(F, u, bcs=bcs, J=op, form_compiler_parameters=fcp) nctx = _SNESContext(nprob, mat_type, mat_type, old_appctx) return nctx
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 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 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. 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 should be specified with `bool` values. For example: .. code-block:: python {'snes_monitor': True} """ 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") 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) # 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 = type(problem.u)(problem.u.function_space()) 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) # 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() dmhooks.set_appctx(dm, self._ctx) self.set_from_options(self.snes)
def _la_solve(A, x, b, **kwargs): r"""Solve a linear algebra problem. :arg A: the assembled bilinear form, a :class:`.Matrix`. :arg x: the :class:`.Function` to write the solution into. :arg b: the :class:`.Function` defining the right hand side values. :kwarg solver_parameters: optional 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 add the near nullspace. :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:: This function no longer accepts :class:`.DirichletBC`\s or :class:`.EquationBC`\s as arguments. Any boundary conditions must be applied when assembling the bilinear form as: .. code-block:: python A = assemble(a, bcs=[bc1]) solve(A, x, b) Example usage: .. code-block:: python _la_solve(A, x, b, solver_parameters=parameters_dict).""" bcs, solver_parameters, nullspace, nullspace_T, near_nullspace, \ options_prefix = _extract_linear_solver_args(A, x, b, **kwargs) if bcs is not None: raise RuntimeError( "It is no longer possible to apply or change boundary conditions after assembling the matrix `A`; pass any necessary boundary conditions to `assemble` when assembling `A`." ) solver = ls.LinearSolver(A, solver_parameters=solver_parameters, nullspace=nullspace, transpose_nullspace=nullspace_T, near_nullspace=near_nullspace, options_prefix=options_prefix) if isinstance(x, firedrake.Vector): x = x.function # linear MG doesn't need RHS, supply zero. lvp = vs.LinearVariationalProblem(a=A.a, L=0, u=x, bcs=A.bcs) mat_type = A.mat_type appctx = solver_parameters.get("appctx", {}) ctx = solving_utils._SNESContext(lvp, mat_type=mat_type, pmat_type=mat_type, appctx=appctx, options_prefix=options_prefix) dm = solver.ksp.dm with dmhooks.add_hooks(dm, solver, appctx=ctx): solver.solve(x, b)
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 initialize(self, pc): from firedrake.assemble import allocate_matrix, create_assembly_callable _, P = pc.getOperators() if pc.getType() != "python": raise ValueError("Expecting PC type python") opc = pc appctx = self.get_appctx(pc) fcp = appctx.get("form_compiler_parameters") V = get_function_space(pc.getDM()) if len(V) == 1: V = FunctionSpace(V.mesh(), V.ufl_element()) else: V = MixedFunctionSpace([V_ for V_ in V]) test = TestFunction(V) trial = TrialFunction(V) if P.type == "python": context = P.getPythonContext() # It only makes sense to preconditioner/invert a diagonal # block in general. That's all we're going to allow. if not context.on_diag: raise ValueError("Only makes sense to invert diagonal block") prefix = pc.getOptionsPrefix() options_prefix = prefix + self._prefix mat_type = PETSc.Options().getString(options_prefix + "mat_type", "aij") (a, bcs) = self.form(pc, test, trial) self.P = allocate_matrix(a, bcs=bcs, form_compiler_parameters=fcp, mat_type=mat_type, options_prefix=options_prefix) self._assemble_P = create_assembly_callable(a, tensor=self.P, bcs=bcs, form_compiler_parameters=fcp, mat_type=mat_type) self._assemble_P() self.P.force_evaluation() # Transfer nullspace over Pmat = self.P.petscmat Pmat.setNullSpace(P.getNullSpace()) tnullsp = P.getTransposeNullSpace() if tnullsp.handle != 0: Pmat.setTransposeNullSpace(tnullsp) # Internally, we just set up a PC object that the user can configure # however from the PETSc command line. Since PC allows the user to specify # a KSP, we can do iterative by -assembled_pc_type ksp. pc = PETSc.PC().create(comm=opc.comm) pc.incrementTabLevel(1, parent=opc) # We set a DM and an appropriate SNESContext on the constructed PC so one # can do e.g. multigrid or patch solves. from firedrake.variational_solver import NonlinearVariationalProblem from firedrake.solving_utils import _SNESContext dm = opc.getDM() octx = get_appctx(dm) oproblem = octx._problem nproblem = NonlinearVariationalProblem(oproblem.F, oproblem.u, bcs, J=a, form_compiler_parameters=fcp) nctx = _SNESContext(nproblem, mat_type, mat_type, octx.appctx) push_appctx(dm, nctx) self._ctx_ref = nctx pc.setDM(dm) pc.setOptionsPrefix(options_prefix) pc.setOperators(Pmat, Pmat) pc.setFromOptions() pc.setUp() self.pc = pc pop_appctx(dm)
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 coarsen(self, fdm, comm): fctx = get_appctx(fdm) test, trial = fctx.J.arguments() fV = test.function_space() fu = fctx._problem.u cele = self.coarsen_element(fV.ufl_element()) cV = firedrake.FunctionSpace(fV.mesh(), cele) cdm = cV.dm cu = firedrake.Function(cV) interpolators = tuple( firedrake.Interpolator(fus, cus) for fus, cus in zip(fu.split(), cu.split())) def inject_state(interpolators): for interpolator in interpolators: interpolator.interpolate() parent = get_parent(fdm) assert parent is not None add_hook(parent, setup=partial(push_parent, cdm, parent), teardown=partial(pop_parent, cdm, parent), call_setup=True) replace_d = { fu: cu, test: firedrake.TestFunction(cV), trial: firedrake.TrialFunction(cV) } cJ = replace(fctx.J, replace_d) cF = replace(fctx.F, replace_d) if fctx.Jp is not None: cJp = replace(fctx.Jp, replace_d) else: cJp = None cbcs = [] for bc in fctx._problem.bcs: # Don't actually need the value, since it's only used for # killing parts of the matrix. This should be generalised # for p-FAS, if anyone ever wants to do that cV_ = cV for index in bc._indices: cV_ = cV_.sub(index) cbcs.append( firedrake.DirichletBC(cV_, firedrake.zero(cV_.shape), bc.sub_domain, method=bc.method)) fcp = fctx._problem.form_compiler_parameters cproblem = firedrake.NonlinearVariationalProblem( cF, cu, cbcs, cJ, Jp=cJp, form_compiler_parameters=fcp, is_linear=fctx._problem.is_linear) cctx = _SNESContext( cproblem, fctx.mat_type, fctx.pmat_type, appctx=fctx.appctx, pre_jacobian_callback=fctx._pre_jacobian_callback, pre_function_callback=fctx._pre_function_callback, post_jacobian_callback=fctx._post_jacobian_callback, post_function_callback=fctx._post_function_callback, options_prefix=fctx.options_prefix, transfer_manager=fctx.transfer_manager) add_hook(parent, setup=partial(push_appctx, cdm, cctx), teardown=partial(pop_appctx, cdm, cctx), call_setup=True) add_hook(parent, setup=partial(inject_state, interpolators), call_setup=True) cdm.setKSPComputeOperators(_SNESContext.compute_operators) cdm.setCreateInterpolation(self.create_interpolation) cdm.setOptionsPrefix(fdm.getOptionsPrefix()) # If we're the coarsest grid of the p-hierarchy, don't # overwrite the coarsen routine; this is so that you can # use geometric multigrid for the p-coarse problem try: self.coarsen_element(cele) cdm.setCoarsen(self.coarsen) except ValueError: pass return cdm
def initialize(self, pc): from firedrake.assemble import allocate_matrix, create_assembly_callable _, P = pc.getOperators() if pc.getType() != "python": raise ValueError("Expecting PC type python") opc = pc appctx = self.get_appctx(pc) fcp = appctx.get("form_compiler_parameters") V = get_function_space(pc.getDM()) if len(V) == 1: V = FunctionSpace(V.mesh(), V.ufl_element()) else: V = MixedFunctionSpace([V_ for V_ in V]) test = TestFunction(V) trial = TrialFunction(V) if P.type == "python": context = P.getPythonContext() # It only makes sense to preconditioner/invert a diagonal # block in general. That's all we're going to allow. if not context.on_diag: raise ValueError("Only makes sense to invert diagonal block") prefix = pc.getOptionsPrefix() options_prefix = prefix + self._prefix mat_type = PETSc.Options().getString(options_prefix + "mat_type", "aij") (a, bcs) = self.form(pc, test, trial) self.P = allocate_matrix(a, bcs=bcs, form_compiler_parameters=fcp, mat_type=mat_type, options_prefix=options_prefix) self._assemble_P = create_assembly_callable(a, tensor=self.P, bcs=bcs, form_compiler_parameters=fcp, mat_type=mat_type) self._assemble_P() self.P.force_evaluation() # Transfer nullspace over Pmat = self.P.petscmat Pmat.setNullSpace(P.getNullSpace()) tnullsp = P.getTransposeNullSpace() if tnullsp.handle != 0: Pmat.setTransposeNullSpace(tnullsp) # Internally, we just set up a PC object that the user can configure # however from the PETSc command line. Since PC allows the user to specify # a KSP, we can do iterative by -assembled_pc_type ksp. pc = PETSc.PC().create(comm=opc.comm) pc.incrementTabLevel(1, parent=opc) # We set a DM and an appropriate SNESContext on the constructed PC so one # can do e.g. multigrid or patch solves. from firedrake.variational_solver import NonlinearVariationalProblem from firedrake.solving_utils import _SNESContext dm = opc.getDM() octx = get_appctx(dm) oproblem = octx._problem nproblem = NonlinearVariationalProblem(oproblem.F, oproblem.u, bcs, J=a, form_compiler_parameters=fcp) nctx = _SNESContext(nproblem, mat_type, mat_type, octx.appctx) push_appctx(dm, nctx) pc.setDM(dm) pc.setOptionsPrefix(options_prefix) pc.setOperators(Pmat, Pmat) pc.setFromOptions() pc.setUp() self.pc = pc pop_appctx(dm)
def _la_solve(A, x, b, **kwargs): r"""Solve a linear algebra problem. :arg A: the assembled bilinear form, a :class:`.Matrix`. :arg x: the :class:`.Function` to write the solution into. :arg b: the :class:`.Function` defining the right hand side values. :kwarg bcs: an optional list of :class:`.DirichletBC`\s and/or :class:`.EquationBC`\s to apply. :kwarg solver_parameters: optional 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 add the near nullspace. :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 passed in as an argument here override the boundary conditions set when the bilinear form was assembled. That is, in the following example: .. code-block:: python A = assemble(a, bcs=[bc1]) solve(A, x, b, bcs=[bc2]) the boundary conditions in `bc2` will be applied to the problem while `bc1` will be ignored. Example usage: .. code-block:: python _la_solve(A, x, b, solver_parameters=parameters_dict).""" bcs, solver_parameters, nullspace, nullspace_T, near_nullspace, \ options_prefix = _extract_linear_solver_args(A, x, b, **kwargs) for bc in _extract_bcs(bcs): if not bc.is_linear: raise RuntimeError("EquationBCs must also be linear when solving linear system.") if bcs is not None: A.bcs = bcs solver = ls.LinearSolver(A, solver_parameters=solver_parameters, nullspace=nullspace, transpose_nullspace=nullspace_T, near_nullspace=near_nullspace, options_prefix=options_prefix) if isinstance(x, firedrake.Vector): x = x.function # linear MG doesn't need RHS, supply zero. lvp = vs.LinearVariationalProblem(a=A.a, L=0, u=x, bcs=bcs) mat_type = A.mat_type appctx = solver_parameters.get("appctx", {}) ctx = solving_utils._SNESContext(lvp, mat_type=mat_type, pmat_type=mat_type, appctx=appctx, options_prefix=options_prefix) dm = solver.ksp.dm with dmhooks.appctx(dm, ctx): solver.solve(x, b)
def _la_solve(A, x, b, **kwargs): r"""Solve a linear algebra problem. :arg A: the assembled bilinear form, a :class:`.Matrix`. :arg x: the :class:`.Function` to write the solution into. :arg b: the :class:`.Function` defining the right hand side values. :kwarg bcs: an optional list of :class:`.DirichletBC`\s to apply. :kwarg solver_parameters: optional 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 add the near nullspace. :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 passed in as an argument here override the boundary conditions set when the bilinear form was assembled. That is, in the following example: .. code-block:: python A = assemble(a, bcs=[bc1]) solve(A, x, b, bcs=[bc2]) the boundary conditions in `bc2` will be applied to the problem while `bc1` will be ignored. Example usage: .. code-block:: python _la_solve(A, x, b, solver_parameters=parameters_dict).""" bcs, solver_parameters, nullspace, nullspace_T, near_nullspace, \ options_prefix = _extract_linear_solver_args(A, x, b, **kwargs) if bcs is not None: A.bcs = bcs solver = ls.LinearSolver(A, solver_parameters=solver_parameters, nullspace=nullspace, transpose_nullspace=nullspace_T, near_nullspace=near_nullspace, options_prefix=options_prefix) if isinstance(x, firedrake.Vector): x = x.function # linear MG doesn't need RHS, supply zero. lvp = vs.LinearVariationalProblem(a=A.a, L=0, u=x, bcs=bcs) mat_type = A.mat_type appctx = solver_parameters.get("appctx", {}) ctx = solving_utils._SNESContext(lvp, mat_type=mat_type, pmat_type=mat_type, appctx=appctx, options_prefix=options_prefix) dm = solver.ksp.dm with dmhooks.appctx(dm, ctx): solver.solve(x, b)
def restrict_or_prolong(self, source, target, mode): if mode == "prolong": coarse = source fine = target else: fine = source coarse = target # Rebuild without any indices V = FunctionSpace(fine.ufl_domain(), fine.function_space().ufl_element()) key = V.dim() firsttime = self.bcs.get(key, None) is None if firsttime: from firedrake.solving_utils import _SNESContext bcs = self.fix_coarse_boundaries(V) a = self.form(V) A = assemble(a, bcs=bcs, mat_type=self.patchparams["mat_type"]) tildeu, rhs = Function(V), Function(V) bform = self.bform(rhs) b = Function(V) problem = LinearVariationalProblem(a=a, L=0, u=tildeu, bcs=bcs) ctx = _SNESContext(problem, mat_type=self.patchparams["mat_type"], pmat_type=self.patchparams["mat_type"], appctx={}, options_prefix="prolongation") solver = LinearSolver(A, solver_parameters=self.patchparams, options_prefix="prolongation") solver._ctx = ctx self.bcs[key] = bcs self.solver[key] = solver self.rhs[key] = tildeu, rhs self.tensors[key] = A, b, bform self.prev_parameters[key] = [ float(param) for param in self.parameters ] else: bcs = self.bcs[key] solver = self.solver[key] A, b, bform = self.tensors[key] tildeu, rhs = self.rhs[key] # Update operator if parameters have changed. if self.rebuild(key): A = solver.A a = self.form(V) bform = self.bform(rhs) self.tensors[key] = A, b, bform A = assemble(a, bcs=bcs, mat_type=self.patchparams["mat_type"], tensor=A) self.prev_parameters[key] = [ float(param) for param in self.parameters ] if mode == "prolong": self.standard_transfer(coarse, rhs, "prolong") b = assemble(bform, bcs=bcs, tensor=b) # # Could do # # solver.solve(tildeu, b) # # but that calls a lot of SNES and KSP overhead. # # We know we just want to apply the PC: with solver.inserted_options(), dmhooks.add_hooks( solver.ksp.dm, solver, appctx=solver._ctx): with b.dat.vec_ro as rhsv: with tildeu.dat.vec_wo as x: solver.ksp.pc.apply(rhsv, x) # fine.assign(rhs - tildeu) fine.dat.data[:] = rhs.dat.data_ro - tildeu.dat.data_ro else: # restrict(rhs, coarse) # return # tildeu.assign(fine) tildeu.dat.data[:] = fine.dat.data_ro bcs.apply(tildeu) with solver.inserted_options(), dmhooks.add_hooks( solver.ksp.dm, solver, appctx=solver._ctx): with tildeu.dat.vec_ro as rhsv: with rhs.dat.vec_wo as x: solver.ksp.pc.apply(rhsv, x) # solver.solve(rhs, fine) b = assemble(bform, tensor=b) # rhs.assign(fine-b) rhs.dat.data[:] = fine.dat.data_ro - b.dat.data_ro self.standard_transfer(rhs, coarse, "restrict")