def initialize(self, obj): if complex_mode: raise NotImplementedError("HypreAMS preconditioner not yet implemented in complex mode") Citations().register("Kolev2009") A, P = obj.getOperators() prefix = obj.getOptionsPrefix() V = get_function_space(obj.getDM()) mesh = V.mesh() family = str(V.ufl_element().family()) degree = V.ufl_element().degree() if family != 'Nedelec 1st kind H(curl)' or degree != 1: raise ValueError("Hypre AMS requires lowest order Nedelec elements! (not %s of degree %d)" % (family, degree)) P1 = FunctionSpace(mesh, "Lagrange", 1) G = Interpolator(grad(TestFunction(P1)), V).callable().handle pc = PETSc.PC().create(comm=obj.comm) pc.incrementTabLevel(1, parent=obj) pc.setOptionsPrefix(prefix + "hypre_ams_") pc.setOperators(A, P) pc.setType('hypre') pc.setHYPREType('ams') pc.setHYPREDiscreteGradient(G) zero_beta = PETSc.Options(prefix).getBool("pc_hypre_ams_zero_beta_poisson", default=False) if zero_beta: pc.setHYPRESetBetaPoissonMatrix(None) VectorP1 = VectorFunctionSpace(mesh, "Lagrange", 1) pc.setCoordinates(interpolate(SpatialCoordinate(mesh), VectorP1).dat.data_ro.copy()) pc.setUp() self.pc = pc
def update(self, w): if not self.separate_wind: return if isinstance(w, Function): self.wind.assign(w) else: with self.wind.dat.vec_wo as wind_v: w.copy(wind_v) # Need to update your coarse grid mates, too. # There's probably a better way to do this. dm = self.V.dm wind = self.wind while get_level(get_function_space(dm).mesh())[1] > 0: cdm = dm.coarsen() cctx = get_appctx(cdm) if cctx is None: break cwind = cctx.F._cache['coefficient_mapping'][wind] inject(wind, cwind) dm = cdm wind = cwind
def initialize(self, pc): # Get context from pc _, P = pc.getOperators() dm = pc.getDM() self.prefix = pc.getOptionsPrefix() + self._prefix # Extract function space and mesh to obtain plex and indexing functions V = get_function_space(dm) # Obtain patches from user defined funtion ises = self.get_patches(V) # Create new PC object as ASM type and set index sets for patches asmpc = PETSc.PC().create(comm=pc.comm) asmpc.incrementTabLevel(1, parent=pc) asmpc.setOptionsPrefix(self.prefix + "sub_") asmpc.setOperators(*pc.getOperators()) backend = PETSc.Options().getString(self.prefix + "backend", default="petscasm").lower() # Either use PETSc's ASM PC or use TinyASM (as simple ASM # implementation designed to be fast for small block sizes). if backend == "petscasm": asmpc.setType(asmpc.Type.ASM) # Set default solver parameters asmpc.setASMType(PETSc.PC.ASMType.BASIC) opts = PETSc.Options(asmpc.getOptionsPrefix()) if "sub_pc_type" not in opts: opts["sub_pc_type"] = "lu" if "sub_pc_factor_shift_type" not in opts: opts["sub_pc_factor_shift_type"] = "NONE" lgmap = V.dof_dset.lgmap # Translate to global numbers ises = tuple(lgmap.applyIS(iset) for iset in ises) asmpc.setASMLocalSubdomains(len(ises), ises) elif backend == "tinyasm": if not have_tinyasm: raise ValueError( "To use the TinyASM backend you need to install firedrake with TinyASM (firedrake-update --tinyasm)" ) _, P = asmpc.getOperators() lgmap = V.dof_dset.lgmap P.setLGMap(rmap=lgmap, cmap=lgmap) asmpc.setType("tinyasm") # TinyASM wants local numbers, no need to translate tinyasm.SetASMLocalSubdomains( asmpc, ises, [W.dm.getDefaultSF() for W in V], [W.value_size for W in V], sum(W.value_size * W.dof_dset.total_size for W in V)) asmpc.setUp() else: raise ValueError(f"Unknown backend type f{backend}") asmpc.setFromOptions() self.asmpc = asmpc
def initialize(self, pc): # Make a new DM. # Hook up a (new) coarsen routine on that DM. # Make a new PC, of type MG. # Assign the DM to that PC. odm = pc.getDM() ctx = get_appctx(odm) test, trial = ctx.J.arguments() if test.function_space() != trial.function_space(): raise NotImplementedError("test and trial spaces must be the same") prefix = pc.getOptionsPrefix() options_prefix = prefix + "pmg_" pdm = PETSc.DMShell().create(comm=pc.comm) pdm.setOptionsPrefix(options_prefix) # Get the coarse degree from PETSc options self.coarse_degree = PETSc.Options(options_prefix).getInt("mg_coarse_degree", default=1) # Construct a list with the elements we'll be using V = test.function_space() ele = V.ufl_element() elements = [ele] while True: try: ele_ = self.coarsen_element(ele) assert ele_.value_shape() == ele.value_shape() ele = ele_ except ValueError: break elements.append(ele) sf = odm.getPointSF() section = odm.getDefaultSection() attach_hooks(pdm, level=len(elements)-1, sf=sf, section=section) # Now overwrite some routines on the DM pdm.setRefine(None) pdm.setCoarsen(self.coarsen) pdm.setCreateInterpolation(self.create_interpolation) # We need this for p-FAS pdm.setCreateInjection(self.create_injection) pdm.setSNESJacobian(_SNESContext.form_jacobian) pdm.setSNESFunction(_SNESContext.form_function) pdm.setKSPComputeOperators(_SNESContext.compute_operators) set_function_space(pdm, get_function_space(odm)) parent = get_parent(odm) assert parent is not None add_hook(parent, setup=partial(push_parent, pdm, parent), teardown=partial(pop_parent, pdm, parent), call_setup=True) add_hook(parent, setup=partial(push_appctx, pdm, ctx), teardown=partial(pop_appctx, pdm, ctx), call_setup=True) self.ppc = self.configure_pmg(pc, pdm) self.ppc.setFromOptions() self.ppc.setUp()
def initialize(self, obj): if complex_mode: raise NotImplementedError( "HypreAMS preconditioner not yet implemented in complex mode") Citations().register("Kolev2009") A, P = obj.getOperators() prefix = obj.getOptionsPrefix() V = get_function_space(obj.getDM()) mesh = V.mesh() family = str(V.ufl_element().family()) degree = V.ufl_element().degree() if family != 'Nedelec 1st kind H(curl)' or degree != 1: raise ValueError( "Hypre AMS requires lowest order Nedelec elements! (not %s of degree %d)" % (family, degree)) P1 = FunctionSpace(mesh, "Lagrange", 1) G = Interpolator(grad(TestFunction(P1)), V).callable().handle pc = PETSc.PC().create(comm=obj.comm) pc.incrementTabLevel(1, parent=obj) pc.setOptionsPrefix(prefix + "hypre_ams_") pc.setOperators(A, P) pc.setType('hypre') pc.setHYPREType('ams') pc.setHYPREDiscreteGradient(G) zero_beta = PETSc.Options(prefix).getBool( "pc_hypre_ams_zero_beta_poisson", default=False) if zero_beta: pc.setHYPRESetBetaPoissonMatrix(None) # Build constants basis for the Nedelec space cvecs = [] for i in range(mesh.cell_dimension()): direction = [ 1.0 if i == j else 0.0 for j in range(mesh.cell_dimension()) ] c = project(Constant(direction), V) with c.vector().dat.vec_ro as cvec: cvecs.append(cvec) pc.setHYPRESetEdgeConstantVectors(*cvecs) pc.setUp() self.pc = pc
def initialize(self, pc): # Get context from pc _, P = pc.getOperators() dm = pc.getDM() self.prefix = pc.getOptionsPrefix() + self._prefix # Extract function space and mesh to obtain plex and indexing functions V = get_function_space(dm) # Obtain patches from user defined funtion ises = self.get_patches(V) # Create new PC object as ASM type and set index sets for patches asmpc = PETSc.PC().create(comm=pc.comm) asmpc.incrementTabLevel(1, parent=pc) asmpc.setOptionsPrefix(self.prefix + "_sub") asmpc.setOperators(*pc.getOperators()) asmpc.setType(asmpc.Type.ASM) asmpc.setASMLocalSubdomains(len(ises), ises) asmpc.setFromOptions() self.asmpc = asmpc
def initialize(self, obj): A, P = obj.getOperators() prefix = obj.getOptionsPrefix() V = get_function_space(obj.getDM()) mesh = V.mesh() family = str(V.ufl_element().family()) degree = V.ufl_element().degree() if family != 'Raviart-Thomas' or degree != 1: raise ValueError( "Hypre ADS requires lowest order RT elements! (not %s of degree %d)" % (family, degree)) P1 = FunctionSpace(mesh, "Lagrange", 1) NC1 = FunctionSpace(mesh, "N1curl", 1) # DiscreteGradient G = Interpolator(grad(TestFunction(P1)), NC1).callable().handle # DiscreteCurl C = Interpolator(curl(TestFunction(NC1)), V).callable().handle pc = PETSc.PC().create(comm=obj.comm) pc.incrementTabLevel(1, parent=obj) pc.setOptionsPrefix(prefix + "hypre_ads_") pc.setOperators(A, P) pc.setType('hypre') pc.setHYPREType('ads') pc.setHYPREDiscreteGradient(G) pc.setHYPREDiscreteCurl(C) V = VectorFunctionSpace(mesh, "Lagrange", 1) linear_coordinates = interpolate(SpatialCoordinate(mesh), V).dat.data_ro.copy() pc.setCoordinates(linear_coordinates) pc.setUp() self.pc = pc
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 initialize(self, pc): # Make a new DM. # Hook up a (new) coarsen routine on that DM. # Make a new PC, of type MG. # Assign the DM to that PC. odm = pc.getDM() ctx = get_appctx(odm) test, trial = ctx.J.arguments() if test.function_space() != trial.function_space(): raise NotImplementedError("test and trial spaces must be the same") # Construct a list with the elements we'll be using V = test.function_space() ele = V.ufl_element() elements = [ele] while True: try: ele_ = self.coarsen_element(ele) assert ele_.value_shape() == ele.value_shape() ele = ele_ except ValueError: break elements.append(ele) pdm = PETSc.DMShell().create(comm=pc.comm) sf = odm.getPointSF() section = odm.getDefaultSection() attach_hooks(pdm, level=len(elements) - 1, sf=sf, section=section) # Now overwrite some routines on the DM pdm.setRefine(None) pdm.setCoarsen(self.coarsen) pdm.setCreateInterpolation(self.create_interpolation) pdm.setOptionsPrefix(pc.getOptionsPrefix() + "pmg_") set_function_space(pdm, get_function_space(odm)) parent = get_parent(odm) assert parent is not None add_hook(parent, setup=partial(push_parent, pdm, parent), teardown=partial(pop_parent, pdm, parent), call_setup=True) add_hook(parent, setup=partial(push_appctx, pdm, ctx), teardown=partial(pop_appctx, pdm, ctx), call_setup=True) ppc = PETSc.PC().create(comm=pc.comm) ppc.setOptionsPrefix(pc.getOptionsPrefix() + "pmg_") ppc.setType("mg") ppc.setOperators(*pc.getOperators()) ppc.setDM(pdm) ppc.incrementTabLevel(1, parent=pc) # 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(pc.getOptionsPrefix() + "pmg_") if "mg_coarse_pc_mg_levels" not in opts: opts["mg_coarse_pc_mg_levels"] = odm.getRefineLevel() + 1 ppc.setFromOptions() ppc.setUp() self.ppc = ppc
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)