def __init__(self, v, v_out, bcs=None, solver_parameters=None, constant_jacobian=True): if isinstance(v, expression.Expression) or \ not isinstance(v, (ufl.core.expr.Expr, function.Function)): raise ValueError("Can only project UFL expression or Functions not '%s'" % type(v)) self._same_fspace = (isinstance(v, function.Function) and v.function_space() == v_out.function_space()) self.v = v self.v_out = v_out self.bcs = bcs if not self._same_fspace or self.bcs: V = v_out.function_space() p = ufl_expr.TestFunction(V) q = ufl_expr.TrialFunction(V) a = ufl.inner(p, q)*ufl.dx L = ufl.inner(p, v)*ufl.dx problem = vs.LinearVariationalProblem(a, L, v_out, bcs=self.bcs, constant_jacobian=constant_jacobian) if solver_parameters is None: solver_parameters = {} solver_parameters.setdefault("ksp_type", "cg") self.solver = vs.LinearVariationalSolver(problem, solver_parameters=solver_parameters)
def _solve_varproblem(*args, **kwargs): "Solve variational problem a == L or F == 0" # Extract arguments eq, u, bcs, J, Jp, M, form_compiler_parameters, \ solver_parameters, nullspace, nullspace_T, \ near_nullspace, \ options_prefix = _extract_args(*args, **kwargs) appctx = kwargs.get("appctx", {}) # Solve linear variational problem if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): # Create problem problem = vs.LinearVariationalProblem( eq.lhs, eq.rhs, u, bcs, Jp, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = vs.LinearVariationalSolver( problem, solver_parameters=solver_parameters, nullspace=nullspace, transpose_nullspace=nullspace_T, near_nullspace=near_nullspace, options_prefix=options_prefix, appctx=appctx) solver.solve() # Solve nonlinear variational problem else: if eq.rhs != 0: raise TypeError( "Only '0' support on RHS of nonlinear Equation, not %r" % eq.rhs) # Create problem problem = vs.NonlinearVariationalProblem( eq.lhs, u, bcs, J, Jp, form_compiler_parameters=form_compiler_parameters) # Create solver and call solve solver = vs.NonlinearVariationalSolver( problem, solver_parameters=solver_parameters, nullspace=nullspace, transpose_nullspace=nullspace_T, near_nullspace=near_nullspace, options_prefix=options_prefix, appctx=appctx) solver.solve()
def _solve_varproblem(*args, **kwargs): "Solve variational problem a == L or F == 0" # Extract arguments eq, u, bcs, J, Jp, M, form_compiler_parameters, \ solver_parameters, nullspace, options_prefix, \ nest = _extract_args(*args, **kwargs) # Solve linear variational problem if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): # Create problem problem = vs.LinearVariationalProblem( eq.lhs, eq.rhs, u, bcs, Jp, form_compiler_parameters=form_compiler_parameters, nest=nest) # Create solver and call solve solver = vs.LinearVariationalSolver( problem, solver_parameters=solver_parameters, nullspace=nullspace, options_prefix=options_prefix) with progress(INFO, 'Solving linear variational problem'): solver.solve() # Solve nonlinear variational problem else: if eq.rhs != 0: raise TypeError( "Only '0' support on RHS of nonlinear Equation, not %r" % eq.rhs) # Create problem problem = vs.NonlinearVariationalProblem( eq.lhs, u, bcs, J, Jp, form_compiler_parameters=form_compiler_parameters, nest=nest) # Create solver and call solve solver = vs.NonlinearVariationalSolver( problem, solver_parameters=solver_parameters, nullspace=nullspace, options_prefix=options_prefix) with progress(INFO, 'Solving nonlinear variational problem'): solver.solve()
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 pml(mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=None, solver_parameters=None, inner_region=None, fspace=None, tfspace=None, true_sol_grad=None, pml_type=None, delta=None, quad_const=None, speed=None, pml_min=None, pml_max=None): """ For unlisted arg descriptions, see run_method :arg inner_region: boundary id of non-pml region :arg pml_type: Type of pml function, either 'quadratic' or 'bdy_integral' :arg delta: For :arg:`pml_type` of 'bdy_integral', added to denominator to prevent 1 / 0 at edge of boundary :arg quad_const: For :arg:`pml_type` of 'quadratic', a scaling constant :arg speed: Speed of sound :arg pml_min: A list, *pml_min[i]* is where to begin pml layer in direction *i* :arg pml_max: A list, *pml_max[i]* is where to end pml layer in direction *i* """ # Handle defauls if pml_type is None: pml_type = 'bdy_integral' if delta is None: delta = 1e-3 if quad_const is None: quad_const = 1.0 if speed is None: speed = 340.0 pml_types = ['bdy_integral', 'quadratic'] if pml_type not in pml_types: raise ValueError("PML type of %s is not one of %s" % (pml_type, pml_types)) xx = SpatialCoordinate(mesh) # {{{ create sigma functions for PML sigma = None if pml_type == 'bdy_integral': sigma = [ Constant(speed) / (Constant(delta + extent) - abs(coord)) for extent, coord in zip(pml_max, xx) ] elif pml_type == 'quadratic': sigma = [ Constant(quad_const) * (abs(coord) - Constant(min_))**2 for min_, coord in zip(pml_min, xx) ] r""" Here \kappa is the wave number and c is the speed ..math:: \kappa = \frac{ \omega } { c } """ omega = wave_number * speed # {{{ Set up PML functions gamma = [ Constant(1.0) + conditional( abs(real(coord)) >= real(min_), Constant(1j / omega) * sigma_i, Constant(0.0)) for min_, coord, sigma_i in zip(pml_min, xx, sigma) ] kappa = [None] * len(gamma) gamma_prod = 1.0 for i in range(len(gamma)): gamma_prod *= gamma[i] tensor_i = [Constant(0.0) for _ in range(len(gamma))] tensor_i[i] = 1.0 r""" *i*th entry is .. math:: \frac{\prod_{j\neq i} \gamma_j}{ \gamma_i } """ for j in range(len(gamma)): if j != i: tensor_i[i] *= gamma[j] else: tensor_i[i] /= gamma[j] kappa[i] = tensor_i kappa = as_tensor(kappa) # }}} p = TrialFunction(fspace) q = TestFunction(fspace) k = wave_number # Just easier to look at a = (inner(dot(grad(p), kappa), grad(q)) - Constant(k**2) * gamma_prod * inner(p, q)) * dx n = FacetNormal(mesh) L = inner(dot(true_sol_grad, n), q) * ds(scatterer_bdy_id) bc = DirichletBC(fspace, Constant(0), outer_bdy_id) solution = Function(fspace) #solve(a == L, solution, bcs=[bc], options_prefix=options_prefix) # Create a solver and return the KSP object with the solution so that can get # PETSc information # Create problem problem = vs.LinearVariationalProblem(a, L, solution, [bc], None) # Create solver and call solve solver = vs.LinearVariationalSolver(problem, solver_parameters=solver_parameters, options_prefix=options_prefix) solver.solve() return solver.snes, solution
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 transmission( mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=None, solver_parameters=None, fspace=None, true_sol_grad_expr=None, ): r""" preconditioner_gamma and preconditioner_lambda are used to precondition with the following equation: \Delta u - \kappa^2 \gamma u = 0 (\partial_n - i\kappa\beta) u |_\Sigma = 0 """ # need as tuple so can use integral measure if isinstance(outer_bdy_id, int): outer_bdy_id = [outer_bdy_id] outer_bdy_id = tuple(outer_bdy_id) u = TrialFunction(fspace) v = TestFunction(fspace) a = inner(grad(u), grad(v)) * dx - Constant(wave_number**2) * inner(u, v) * dx \ - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id) n = FacetNormal(mesh) metadata = {'quadrature_degree': 2 * fspace.ufl_element().degree()} L = inner(inner(true_sol_grad_expr, n), v) * ds(scatterer_bdy_id, metadata=metadata) solution = Function(fspace) # {{{ Used for preconditioning if 'gamma' in solver_parameters or 'beta' in solver_parameters: solver_params = dict(solver_parameters) gamma = complex(solver_parameters.pop('gamma', 1.0)) import cmath beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma))) aP = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2 * gamma) * inner(u, v) * dx \ - Constant(1j * wave_number * beta) * inner(u, v) * ds(outer_bdy_id) else: aP = None solver_params = solver_parameters # }}} # prepare to set up pyamg preconditioner if using it using_pyamg = solver_params['pc_type'] == 'pyamg' if using_pyamg: pyamg_tol = solver_parameters.get('pyamg_tol', None) if pyamg_tol is not None: pyamg_tol = float(pyamg_tol) pyamg_maxiter = solver_params.get('pyamg_maxiter', None) if pyamg_maxiter is not None: pyamg_maxiter = int(pyamg_maxiter) del solver_params['pc_type'] # Create a solver and return the KSP object with the solution so that can get # PETSc information # Create problem problem = vs.LinearVariationalProblem(a, L, solution, aP=aP) # Create solver and call solve solver = vs.LinearVariationalSolver(problem, solver_parameters=solver_params, options_prefix=options_prefix) # prepare to set up pyamg preconditioner if using it if using_pyamg: A = assemble(a).M.handle pc = solver.snes.getKSP().pc pc.setType(pc.Type.PYTHON) pc.setPythonContext( AMGTransmissionPreconditioner(wave_number, fspace, A, tol=pyamg_tol, maxiter=pyamg_maxiter, use_plane_waves=True)) # If using pyamg as preconditioner, use it! try: solver.solve() except ConvergenceError: pass return solver.snes, solution
wave_number = 0.1 # one of [0.1, 1.0, 5.0, 10.0] true_sol = get_true_sol_expr(SpatialCoordinate(mesh), wave_number) a = inner(grad(u), grad(v)) * dx \ - Constant(wave_number**2) * inner(u, v) * dx \ - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id) n = FacetNormal(mesh) L = inner(inner(grad(true_sol), n), v) * ds(scatterer_bdy_id) solution = Function(fspace) # Create a solver and return the KSP object with the solution so that can get # PETSc information # Create problem problem = vs.LinearVariationalProblem(a, L, solution) # Create solver pyamg_tol = None pyamg_maxiter = None solver_params = { 'pyamg_tol': pyamg_tol, 'pyamg_maxiter': pyamg_maxiter, 'ksp_monitor': None, } solver = vs.LinearVariationalSolver(problem, solver_parameters=solver_params) # Build matrix and store text file A = assemble(a).M.handle store_mat = True if store_mat: