コード例 #1
0
ファイル: __init__.py プロジェクト: inducer/pytential
    def operator(self, unk):
        u = self.split_unknown(unk)

        Jxyz = cse(tangential_to_xyz(u.jt), "Jxyz")
        Mxyz = cse(tangential_to_xyz(u.mt), "Mxyz")

        omega = self.omega
        mu0, mu1 = self.mus
        eps0, eps1 = self.epss
        k0, k1 = self.ks

        S = partial(sym.S, self.kernel, qbx_forced_limit="avg")

        def curl_S(dens, k):
            return sym.curl(sym.S(self.kernel, dens, qbx_forced_limit="avg", k=k))

        grad = partial(sym.grad, 3)

        E0 = sym.cse(1j*omega*mu0*eps0*S(Jxyz, k=k0)
            + mu0*curl_S(Mxyz, k=k0) - grad(S(u.rho_e, k=k0)), "E0")
        H0 = sym.cse(-1j*omega*mu0*eps0*S(Mxyz, k=k0)
            + eps0*curl_S(Jxyz, k=k0) + grad(S(u.rho_m, k=k0)), "H0")
        E1 = sym.cse(1j*omega*mu1*eps1*S(Jxyz, k=k1)
            + mu1*curl_S(Mxyz, k=k1) - grad(S(u.rho_e, k=k1)), "E1")
        H1 = sym.cse(-1j*omega*mu1*eps1*S(Mxyz, k=k1)
            + eps1*curl_S(Jxyz, k=k1) + grad(S(u.rho_m, k=k1)), "H1")

        F1 = (xyz_to_tangential(sym.n_cross(H1-H0) + 0.5*(eps0+eps1)*Jxyz))
        F2 = (sym.n_dot(eps1*E1-eps0*E0) + 0.5*(eps1+eps0)*u.rho_e)
        F3 = (xyz_to_tangential(sym.n_cross(E1-E0) + 0.5*(mu0+mu1)*Mxyz))

        # sign flip included
        F4 = -sym.n_dot(mu1*H1-mu0*H0) + 0.5*(mu1+mu0)*u.rho_m  # noqa pylint:disable=invalid-unary-operand-type

        return sym.join_fields(F1, F2, F3, F4)
コード例 #2
0
    def operator(self, unk):
        u = self.split_unknown(unk)

        Jxyz = cse(tangential_to_xyz(u.jt), "Jxyz")
        Mxyz = cse(tangential_to_xyz(u.mt), "Mxyz")

        omega = self.omega
        mu0, mu1 = self.mus
        eps0, eps1 = self.epss
        k0, k1 = self.ks

        S = partial(sym.S, self.kernel, qbx_forced_limit="avg")

        def curl_S(dens, k):
            return sym.curl(sym.S(self.kernel, dens, qbx_forced_limit="avg", k=k))

        grad = partial(sym.grad, 3)

        E0 = sym.cse(1j*omega*mu0*eps0*S(Jxyz, k=k0)
            + mu0*curl_S(Mxyz, k=k0) - grad(S(u.rho_e, k=k0)), "E0")
        H0 = sym.cse(-1j*omega*mu0*eps0*S(Mxyz, k=k0)
            + eps0*curl_S(Jxyz, k=k0) + grad(S(u.rho_m, k=k0)), "H0")
        E1 = sym.cse(1j*omega*mu1*eps1*S(Jxyz, k=k1)
            + mu1*curl_S(Mxyz, k=k1) - grad(S(u.rho_e, k=k1)), "E1")
        H1 = sym.cse(-1j*omega*mu1*eps1*S(Mxyz, k=k1)
            + eps1*curl_S(Jxyz, k=k1) + grad(S(u.rho_m, k=k1)), "H1")

        F1 = (xyz_to_tangential(sym.n_cross(H1-H0) + 0.5*(eps0+eps1)*Jxyz))
        F2 = (sym.n_dot(eps1*E1-eps0*E0) + 0.5*(eps1+eps0)*u.rho_e)
        F3 = (xyz_to_tangential(sym.n_cross(E1-E0) + 0.5*(mu0+mu1)*Mxyz))

        # sign flip included
        F4 = -sym.n_dot(mu1*H1-mu0*H0) + 0.5*(mu1+mu0)*u.rho_m  # noqa pylint:disable=invalid-unary-operand-type

        return sym.flat_obj_array(F1, F2, F3, F4)
コード例 #3
0
    def rho_rhs(self, Jt, Einc_xyz):
        Jxyz = cse(tangential_to_xyz(Jt), "Jxyz")

        return (sym.n_dot(Einc_xyz)
                + 1j*self.k*sym.n_dot(sym.S(
                    self.kernel, Jxyz, k=self.k,
                    # continuous--qbx_forced_limit doesn't really matter
                    qbx_forced_limit="avg")))
コード例 #4
0
ファイル: __init__.py プロジェクト: choward1491/pytential
    def rhs(self, Einc_xyz, Hinc_xyz):
        mu1 = self.mus[1]
        eps1 = self.epss[1]

        return sym.flat_obj_array(xyz_to_tangential(sym.n_cross(Hinc_xyz)),
                                  sym.n_dot(eps1 * Einc_xyz),
                                  xyz_to_tangential(sym.n_cross(Einc_xyz)),
                                  sym.n_dot(-mu1 * Hinc_xyz))
コード例 #5
0
ファイル: __init__.py プロジェクト: inducer/pytential
    def rho_rhs(self, Jt, Einc_xyz):
        Jxyz = cse(tangential_to_xyz(Jt), "Jxyz")

        return (sym.n_dot(Einc_xyz)
                + 1j*self.k*sym.n_dot(sym.S(
                    self.kernel, Jxyz, k=self.k,
                    # continuous--qbx_forced_limit doesn't really matter
                    qbx_forced_limit="avg")))
コード例 #6
0
    def rhs(self, Einc_xyz, Hinc_xyz):
        mu1 = self.mus[1]
        eps1 = self.epss[1]

        return sym.join_fields(xyz_to_tangential(sym.n_cross(Hinc_xyz)),
                               sym.n_dot(eps1 * Einc_xyz),
                               xyz_to_tangential(sym.n_cross(Einc_xyz)),
                               sym.n_dot(-mu1 * Hinc_xyz))
コード例 #7
0
ファイル: __init__.py プロジェクト: inducer/pytential
    def rhs(self, Einc_xyz, Hinc_xyz):
        mu1 = self.mus[1]
        eps1 = self.epss[1]

        return sym.join_fields(
            xyz_to_tangential(sym.n_cross(Hinc_xyz)),
            sym.n_dot(eps1*Einc_xyz),
            xyz_to_tangential(sym.n_cross(Einc_xyz)),
            sym.n_dot(-mu1*Hinc_xyz))
コード例 #8
0
def nonlocal_integral_eq(
    mesh,
    scatterer_bdy_id,
    outer_bdy_id,
    wave_number,
    options_prefix=None,
    solver_parameters=None,
    fspace=None,
    vfspace=None,
    true_sol_grad_expr=None,
    actx=None,
    dgfspace=None,
    dgvfspace=None,
    meshmode_src_connection=None,
    qbx_kwargs=None,
):
    r"""
        see run_method for descriptions of unlisted args

        args:

        gamma and beta are used to precondition
        with the following equation:

        \Delta u - \kappa^2 \gamma u = 0
        (\partial_n - i\kappa\beta) u |_\Sigma = 0
    """
    # make sure we get outer bdy id as tuple in case it consists of multiple ids
    if isinstance(outer_bdy_id, int):
        outer_bdy_id = [outer_bdy_id]
    outer_bdy_id = tuple(outer_bdy_id)
    # away from the excluded region, but firedrake and meshmode point
    # into
    pyt_inner_normal_sign = -1

    ambient_dim = mesh.geometric_dimension()

    # {{{ Build src and tgt

    # build connection meshmode near src boundary -> src boundary inside meshmode
    from meshmode.discretization.poly_element import \
        InterpolatoryQuadratureSimplexGroupFactory
    from meshmode.discretization.connection import make_face_restriction
    factory = InterpolatoryQuadratureSimplexGroupFactory(
        dgfspace.finat_element.degree)
    src_bdy_connection = make_face_restriction(actx,
                                               meshmode_src_connection.discr,
                                               factory, scatterer_bdy_id)
    # source is a qbx layer potential
    from pytential.qbx import QBXLayerPotentialSource
    disable_refinement = (fspace.mesh().geometric_dimension() == 3)
    qbx = QBXLayerPotentialSource(src_bdy_connection.to_discr,
                                  **qbx_kwargs,
                                  _disable_refinement=disable_refinement)

    # get target indices and point-set
    target_indices, target = get_target_points_and_indices(
        fspace, outer_bdy_id)

    # }}}

    # build the operations
    from pytential import bind, sym
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * sym.grad(
        ambient_dim,
        sym.D(HelmholtzKernel(ambient_dim),
              sym.var("u"),
              k=sym.var("k"),
              qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
    """
    op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D(
        HelmholtzKernel(ambient_dim),
        sym.var("u"),
        k=sym.var("k"),
        qbx_forced_limit=None))

    # bind the operations
    pyt_grad_op = bind((qbx, target), grad_op)
    pyt_op = bind((qbx, target), op)

    # }}}

    class MatrixFreeB(object):
        def __init__(self, A, pyt_grad_op, pyt_op, actx, kappa):
            """
            :arg kappa: The wave number
            """

            self.actx = actx
            self.k = kappa
            self.pyt_op = pyt_op
            self.pyt_grad_op = pyt_grad_op
            self.A = A
            self.meshmode_src_connection = meshmode_src_connection

            # {{{ Create some functions needed for multing
            self.x_fntn = Function(fspace)

            # CG
            self.potential_int = Function(fspace)
            self.potential_int.dat.data[:] = 0.0
            self.grad_potential_int = Function(vfspace)
            self.grad_potential_int.dat.data[:] = 0.0
            self.pyt_result = Function(fspace)

            self.n = FacetNormal(mesh)
            self.v = TestFunction(fspace)

            # some meshmode ones
            self.x_mm_fntn = self.meshmode_src_connection.discr.empty(
                self.actx, dtype='c')

            # }}}

        def mult(self, mat, x, y):
            # Copy function data into the fivredrake function
            self.x_fntn.dat.data[:] = x[:]
            # Transfer the function to meshmode
            self.meshmode_src_connection.from_firedrake(project(
                self.x_fntn, dgfspace),
                                                        out=self.x_mm_fntn)
            # Restrict to boundary
            x_mm_fntn_on_bdy = src_bdy_connection(self.x_mm_fntn)

            # Apply the operation
            potential_int_mm = self.pyt_op(self.actx,
                                           u=x_mm_fntn_on_bdy,
                                           k=self.k)
            grad_potential_int_mm = self.pyt_grad_op(self.actx,
                                                     u=x_mm_fntn_on_bdy,
                                                     k=self.k)
            # Store in firedrake
            self.potential_int.dat.data[target_indices] = potential_int_mm.get(
            )
            for dim in range(grad_potential_int_mm.shape[0]):
                self.grad_potential_int.dat.data[
                    target_indices, dim] = grad_potential_int_mm[dim].get()

            # Integrate the potential
            r"""
            Compute the inner products using firedrake. Note this
            will be subtracted later, hence appears off by a sign.

            .. math::

                \langle
                    n(x) \cdot \nabla(
                        \int_\Gamma(
                            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                        )d\gamma(y)
                    ), v
                \rangle_\Sigma
                - \langle
                    i \kappa \cdot
                    \int_\Gamma(
                        u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                    )d\gamma(y), v
                \rangle_\Sigma
            """
            self.pyt_result = assemble(
                inner(inner(self.grad_potential_int, self.n), self.v) *
                ds(outer_bdy_id) -
                inner(self.potential_int, self.v) * ds(outer_bdy_id))

            # y <- Ax - evaluated potential
            self.A.mult(x, y)
            with self.pyt_result.dat.vec_ro as ep:
                y.axpy(-1, ep)

    # {{{ Compute normal helmholtz operator
    u = TrialFunction(fspace)
    v = TestFunction(fspace)
    r"""
    .. math::

        \langle
            \nabla u, \nabla v
        \rangle
        - \kappa^2 \cdot \langle
            u, v
        \rangle
        - i \kappa \langle
            u, v
        \rangle_\Sigma
    """
    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)

    # get the concrete matrix from a general bilinear form
    A = assemble(a).M.handle
    # }}}

    # {{{ Setup Python matrix
    B = PETSc.Mat().create()

    # build matrix context
    Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, actx, wave_number)

    # set up B as same size as A
    B.setSizes(*A.getSizes())

    B.setType(B.Type.PYTHON)
    B.setPythonContext(Bctx)
    B.setUp()
    # }}}

    # {{{ Create rhs

    # Remember f is \partial_n(true_sol)|_\Gamma
    # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y)

    sigma = sym.make_sym_vector("sigma", ambient_dim)
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * \
        sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim),
                                    sym.n_dot(sigma),
                                    k=sym.var("k"), qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            f(y) H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
        )
    """
    op = 1j * sym.var("k") * pyt_inner_normal_sign * \
        sym.S(HelmholtzKernel(ambient_dim),
              sym.n_dot(sigma),
              k=sym.var("k"),
              qbx_forced_limit=None)

    rhs_grad_op = bind((qbx, target), grad_op)
    rhs_op = bind((qbx, target), op)

    # Transfer to meshmode
    metadata = {'quadrature_degree': 2 * fspace.ufl_element().degree()}
    dg_true_sol_grad = project(true_sol_grad_expr,
                               dgvfspace,
                               form_compiler_parameters=metadata)
    true_sol_grad_mm = meshmode_src_connection.from_firedrake(dg_true_sol_grad,
                                                              actx=actx)
    true_sol_grad_mm = src_bdy_connection(true_sol_grad_mm)
    # Apply the operations
    f_grad_convoluted_mm = rhs_grad_op(actx,
                                       sigma=true_sol_grad_mm,
                                       k=wave_number)
    f_convoluted_mm = rhs_op(actx, sigma=true_sol_grad_mm, k=wave_number)
    # Transfer function back to firedrake
    f_grad_convoluted = Function(vfspace)
    f_convoluted = Function(fspace)
    f_grad_convoluted.dat.data[:] = 0.0
    f_convoluted.dat.data[:] = 0.0

    for dim in range(f_grad_convoluted_mm.shape[0]):
        f_grad_convoluted.dat.data[target_indices,
                                   dim] = f_grad_convoluted_mm[dim].get()
    f_convoluted.dat.data[target_indices] = f_convoluted_mm.get()
    r"""
        \langle
            f, v
        \rangle_\Gamma
        + \langle
            i \kappa \cdot \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y), v
        \rangle_\Sigma
        - \langle
            n(x) \cdot \nabla(
                \int_\Gamma(
                    f(y) H_0^{(1)}(\kappa |x - y|)
                )d\gamma(y)
            ), v
        \rangle_\Sigma
    """
    rhs_form = inner(inner(true_sol_grad_expr, FacetNormal(mesh)),
                     v) * ds(scatterer_bdy_id, metadata=metadata) \
        + inner(f_convoluted, v) * ds(outer_bdy_id) \
        - inner(inner(f_grad_convoluted, FacetNormal(mesh)),
                v) * ds(outer_bdy_id)

    rhs = assemble(rhs_form)

    # {{{ set up a solver:
    solution = Function(fspace, name="Computed Solution")

    #       {{{ Used for preconditioning
    if 'gamma' in solver_parameters or 'beta' in solver_parameters:
        gamma = complex(solver_parameters.pop('gamma', 1.0))

        import cmath
        beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma)))

        p = 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)
        P = assemble(p).M.handle

    else:
        P = A
    #       }}}

    # Set up options to contain solver parameters:
    ksp = PETSc.KSP().create()
    if solver_parameters['pc_type'] == 'pyamg':
        del solver_parameters['pc_type']  # We are using the AMG preconditioner

        pyamg_tol = solver_parameters.get('pyamg_tol', None)
        if pyamg_tol is not None:
            pyamg_tol = float(pyamg_tol)
        pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None)
        if pyamg_maxiter is not None:
            pyamg_maxiter = int(pyamg_maxiter)
        ksp.setOperators(B)
        ksp.setUp()
        pc = ksp.pc
        pc.setType(pc.Type.PYTHON)
        pc.setPythonContext(
            AMGTransmissionPreconditioner(wave_number,
                                          fspace,
                                          A,
                                          tol=pyamg_tol,
                                          maxiter=pyamg_maxiter,
                                          use_plane_waves=True))
    # Otherwise use regular preconditioner
    else:
        ksp.setOperators(B, P)

    options_manager = OptionsManager(solver_parameters, options_prefix)
    options_manager.set_from_options(ksp)

    import petsc4py.PETSc
    petsc4py.PETSc.Sys.popErrorHandler()
    with rhs.dat.vec_ro as b:
        with solution.dat.vec as x:
            ksp.solve(b, x)
    # }}}

    return ksp, solution
コード例 #9
0
ファイル: basic_setup.py プロジェクト: benSepanski/fd2mm
i.e. a shift of the fundamental solution
"""
expr = fd.ln(fd.sqrt((xx[0] + 2)**2 + (xx[1] + 2)**2))
f = fd.Function(V).interpolate(expr)
gradf = fd.Function(Vdim).interpolate(fd.grad(expr))

# Let's create an operator which plugs in f, \partial_n f
# to Green's formula

sigma = sym.make_sym_vector("sigma", 2)
op = -(sym.D(LaplaceKernel(2),
          sym.var("u"),
          qbx_forced_limit=None)
    - sym.S(LaplaceKernel(2),
            sym.n_dot(sigma),
            qbx_forced_limit=None))

from meshmode.mesh import BTAG_ALL
outer_bdy_id = BTAG_ALL

# Think of this like :mod:`pytential`'s :function:`bind`
pyt_op = fd2mm.fd_bind(cl_ctx,
                       fspace_analog, op, source=(V, outer_bdy_id),
                       target=V, with_refinement=with_refinement,
                       qbx_kwargs=qbx_kwargs
                       )

# Compute the operation and store in g
g = fd.Function(V)
pyt_op(queue, u=f, sigma=gradf, result_function=g)
コード例 #10
0
def nonlocal_integral_eq(
    mesh,
    scatterer_bdy_id,
    outer_bdy_id,
    wave_number,
    options_prefix=None,
    solver_parameters=None,
    fspace=None,
    vfspace=None,
    true_sol_grad=None,
    queue=None,
    fspace_analog=None,
    qbx_kwargs=None,
):
    r"""
        see run_method for descriptions of unlisted args

        args:

        :arg queue: A command queue for the computing context

        gamma and beta are used to precondition
        with the following equation:

        \Delta u - \kappa^2 \gamma u = 0
        (\partial_n - i\kappa\beta) u |_\Sigma = 0
    """
    with_refinement = True
    # away from the excluded region, but firedrake and meshmode point
    # into
    pyt_inner_normal_sign = -1

    ambient_dim = mesh.geometric_dimension()

    # {{{ Create operator
    from pytential import sym
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * sym.grad(
        ambient_dim,
        sym.D(HelmholtzKernel(ambient_dim),
              sym.var("u"),
              k=sym.var("k"),
              qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
    """
    op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D(
        HelmholtzKernel(ambient_dim),
        sym.var("u"),
        k=sym.var("k"),
        qbx_forced_limit=None))

    pyt_grad_op = fd2mm.fd_bind(
        queue.context,
        fspace_analog,
        grad_op,
        source=(fspace, scatterer_bdy_id),
        target=(vfspace, outer_bdy_id),
        with_refinement=with_refinement,
        qbx_kwargs=qbx_kwargs,
    )

    pyt_op = fd2mm.fd_bind(
        queue.context,
        fspace_analog,
        op,
        source=(fspace, scatterer_bdy_id),
        target=(fspace, outer_bdy_id),
        with_refinement=with_refinement,
        qbx_kwargs=qbx_kwargs,
    )

    # }}}

    class MatrixFreeB(object):
        def __init__(self, A, pyt_grad_op, pyt_op, queue, kappa):
            """
            :arg kappa: The wave number
            """

            self.queue = queue
            self.k = kappa
            self.pyt_op = pyt_op
            self.pyt_grad_op = pyt_grad_op
            self.A = A

            # {{{ Create some functions needed for multing
            self.x_fntn = Function(fspace)

            self.potential_int = Function(fspace)
            self.potential_int.dat.data[:] = 0.0
            self.grad_potential_int = Function(vfspace)
            self.grad_potential_int.dat.data[:] = 0.0
            self.pyt_result = Function(fspace)

            self.n = FacetNormal(mesh)
            self.v = TestFunction(fspace)
            # }}}

        def mult(self, mat, x, y):
            # Perform pytential operation
            self.x_fntn.dat.data[:] = x[:]

            self.pyt_op(self.queue,
                        self.potential_int,
                        u=self.x_fntn,
                        k=self.k)
            self.pyt_grad_op(self.queue,
                             self.grad_potential_int,
                             u=self.x_fntn,
                             k=self.k)

            # Integrate the potential
            r"""
            Compute the inner products using firedrake. Note this
            will be subtracted later, hence appears off by a sign.

            .. math::

                \langle
                    n(x) \cdot \nabla(
                        \int_\Gamma(
                            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                        )d\gamma(y)
                    ), v
                \rangle_\Sigma
                - \langle
                    i \kappa \cdot
                    \int_\Gamma(
                        u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                    )d\gamma(y), v
                \rangle_\Sigma
            """
            self.pyt_result = assemble(
                inner(inner(self.grad_potential_int, self.n), self.v) *
                ds(outer_bdy_id) -
                inner(self.potential_int, self.v) * ds(outer_bdy_id))

            # y <- Ax - evaluated potential
            self.A.mult(x, y)
            with self.pyt_result.dat.vec_ro as ep:
                y.axpy(-1, ep)

    # {{{ Compute normal helmholtz operator
    u = TrialFunction(fspace)
    v = TestFunction(fspace)
    r"""
    .. math::

        \langle
            \nabla u, \nabla v
        \rangle
        - \kappa^2 \cdot \langle
            u, v
        \rangle
        - i \kappa \langle
            u, v
        \rangle_\Sigma
    """
    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)

    # get the concrete matrix from a general bilinear form
    A = assemble(a).M.handle
    # }}}

    # {{{ Setup Python matrix
    B = PETSc.Mat().create()

    # build matrix context
    Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, queue, wave_number)

    # set up B as same size as A
    B.setSizes(*A.getSizes())

    B.setType(B.Type.PYTHON)
    B.setPythonContext(Bctx)
    B.setUp()
    # }}}

    # {{{ Create rhs

    # Remember f is \partial_n(true_sol)|_\Gamma
    # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y)
    from pytential import sym

    sigma = sym.make_sym_vector("sigma", ambient_dim)
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * \
        sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim),
                                    sym.n_dot(sigma),
                                    k=sym.var("k"), qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            f(y) H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
        )
    """
    op = 1j * sym.var("k") * pyt_inner_normal_sign * \
        sym.S(HelmholtzKernel(ambient_dim),
              sym.n_dot(sigma),
              k=sym.var("k"),
              qbx_forced_limit=None)

    rhs_grad_op = fd2mm.fd_bind(
        queue.context,
        fspace_analog,
        grad_op,
        source=(vfspace, scatterer_bdy_id),
        target=(vfspace, outer_bdy_id),
        with_refinement=with_refinement,
        qbx_kwargs=qbx_kwargs,
    )
    rhs_op = fd2mm.fd_bind(
        queue.context,
        fspace_analog,
        op,
        source=(vfspace, scatterer_bdy_id),
        target=(fspace, outer_bdy_id),
        with_refinement=with_refinement,
        qbx_kwargs=qbx_kwargs,
    )

    f_grad_convoluted = Function(vfspace)
    f_convoluted = Function(fspace)
    rhs_grad_op(queue, f_grad_convoluted, sigma=true_sol_grad, k=wave_number)
    rhs_op(queue, f_convoluted, sigma=true_sol_grad, k=wave_number)
    r"""
        \langle
            f, v
        \rangle_\Gamma
        + \langle
            i \kappa \cdot \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y), v
        \rangle_\Sigma
        - \langle
            n(x) \cdot \nabla(
                \int_\Gamma(
                    f(y) H_0^{(1)}(\kappa |x - y|)
                )d\gamma(y)
            ), v
        \rangle_\Sigma
    """
    rhs_form = inner(inner(true_sol_grad, FacetNormal(mesh)),
                     v) * ds(scatterer_bdy_id) \
        + inner(f_convoluted, v) * ds(outer_bdy_id) \
        - inner(inner(f_grad_convoluted, FacetNormal(mesh)),
                v) * ds(outer_bdy_id)

    rhs = assemble(rhs_form)

    # {{{ set up a solver:
    solution = Function(fspace, name="Computed Solution")

    #       {{{ Used for preconditioning
    if 'gamma' in solver_parameters or 'beta' in solver_parameters:
        gamma = complex(solver_parameters.pop('gamma', 1.0))

        import cmath
        beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma)))

        p = 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)
        P = assemble(p).M.handle

    else:
        P = A
    #       }}}

    # Set up options to contain solver parameters:
    ksp = PETSc.KSP().create()
    if solver_parameters['pc_type'] == 'pyamg':
        del solver_parameters['pc_type']  # We are using the AMG preconditioner

        pyamg_tol = solver_parameters.get('pyamg_tol', None)
        if pyamg_tol is not None:
            pyamg_tol = float(pyamg_tol)
        pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None)
        if pyamg_maxiter is not None:
            pyamg_maxiter = int(pyamg_maxiter)
        ksp.setOperators(B)
        ksp.setUp()
        pc = ksp.pc
        pc.setType(pc.Type.PYTHON)
        pc.setPythonContext(
            AMGTransmissionPreconditioner(wave_number,
                                          fspace,
                                          A,
                                          tol=pyamg_tol,
                                          maxiter=pyamg_maxiter,
                                          use_plane_waves=True))
    # Otherwise use regular preconditioner
    else:
        ksp.setOperators(B, P)

    options_manager = OptionsManager(solver_parameters, options_prefix)
    options_manager.set_from_options(ksp)

    with rhs.dat.vec_ro as b:
        with solution.dat.vec as x:
            ksp.solve(b, x)
    # }}}

    return ksp, solution
コード例 #11
0
def test_greens_formula(degree, family, ambient_dim):

    fine_order = 4 * degree
    # Parameter to tune accuracy of pytential
    fmm_order = 5
    # This should be (order of convergence = qbx_order + 1)
    qbx_order = degree
    with_refinement = True

    qbx_kwargs = {
        'fine_order': fine_order,
        'fmm_order': fmm_order,
        'qbx_order': qbx_order
    }

    if ambient_dim == 2:
        mesh = mesh2d
        r"""
        ..math:

            \ln(\sqrt{(x+1)^2 + (y+1)^2})

        i.e. a shift of the fundamental solution
        """
        x, y = fd.SpatialCoordinate(mesh)
        expr = fd.ln(fd.sqrt((x + 2)**2 + (y + 2)**2))
    elif ambient_dim == 3:
        mesh = mesh3d
        x, y, z = fd.SpatialCoordinate(mesh)
        r"""
        ..math:

            \f{1}{4\pi \sqrt{(x-2)^2 + (y-2)^2 + (z-2)^2)}}

        i.e. a shift of the fundamental solution
        """
        expr = fd.Constant(1 / 4 / fd.pi) * 1 / fd.sqrt((x - 2)**2 +
                                                        (y - 2)**2 +
                                                        (z - 2)**2)
    else:
        raise ValueError("Ambient dimension must be 2 or 3, not %s" %
                         ambient_dim)

    # Let's compute some layer potentials!
    V = fd.FunctionSpace(mesh, family, degree)
    Vdim = fd.VectorFunctionSpace(mesh, family, degree)

    mesh_analog = fd2mm.MeshAnalog(mesh)
    fspace_analog = fd2mm.FunctionSpaceAnalog(cl_ctx, mesh_analog, V)

    true_sol = fd.Function(V).interpolate(expr)
    grad_true_sol = fd.Function(Vdim).interpolate(fd.grad(expr))

    # Let's create an operator which plugs in f, \partial_n f
    # to Green's formula

    sigma = sym.make_sym_vector("sigma", ambient_dim)
    op = -(sym.D(
        LaplaceKernel(ambient_dim), sym.var("u"), qbx_forced_limit=None) -
           sym.S(LaplaceKernel(ambient_dim),
                 sym.n_dot(sigma),
                 qbx_forced_limit=None))

    from meshmode.mesh import BTAG_ALL
    outer_bdy_id = BTAG_ALL

    # Think of this like :mod:`pytential`'s :function:`bind`
    pyt_op = fd2mm.fd_bind(cl_ctx,
                           fspace_analog,
                           op,
                           source=(V, outer_bdy_id),
                           target=V,
                           qbx_kwargs=qbx_kwargs,
                           with_refinement=with_refinement)

    # Compute the operation and store in result
    result = fd.Function(V)

    pyt_op(queue, u=true_sol, sigma=grad_true_sol, result_function=result)

    # Compare with f
    fnorm = fd.sqrt(fd.assemble(fd.inner(true_sol, true_sol) * fd.dx))
    l2_err = fd.sqrt(
        fd.assemble(fd.inner(true_sol - result, true_sol - result) * fd.dx))
    rel_l2_err = l2_err / fnorm

    # TODO: Make this more strict
    assert rel_l2_err < 0.09