Exemplo n.º 1
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.join_fields(F1, F2, F3, F4)
Exemplo n.º 2
0
    def __init__(self, domain_n_exprs, ne,
            interfaces, use_l2_weighting=None):
        """
        :attr interfaces: a tuple of tuples
            ``(outer_domain, inner_domain, interface_id)``,
            where *outer_domain* and *inner_domain* are indices into
            *domain_k_names*,
            and *interface_id* is a symbolic name for the discretization of the
            interface. 'outer' designates the side of the interface to which
            the normal points.
        :attr domain_n_exprs: a tuple of variable names of the Helmholtz
            parameter *k*, to be used inside each part of the source geometry.
            May also be a tuple of strings, which will be transformed into
            variable references of the corresponding names.
        :attr beta: A symbolic expression for the wave number in the :math:`z`
            direction. May be a string, which will be interpreted as a variable
            name.
        """

        self.interfaces = interfaces

        ne = sym.var(ne)
        self.ne = sym.cse(ne, "ne")

        self.domain_n_exprs = [
                sym.var(n_expr)
                for idom, n_expr in enumerate(domain_n_exprs)]
        del domain_n_exprs

        import pymbolic.primitives as p

        def upper_half_square_root(x):
            return p.If(
                    p.Comparison(
                        (x**0.5).a.imag,
                        "<", 0),
                    1j*(-x)**0.5,
                    x**0.5)

        self.domain_K_exprs = [
                sym.cse(
                    upper_half_square_root(n_expr**2-ne**2),
                    "K%d" % i)
                for i, n_expr in enumerate(self.domain_n_exprs)]

        from sumpy.kernel import HelmholtzKernel
        self.kernel = HelmholtzKernel(2, allow_evanescent=True)
Exemplo n.º 3
0
 def __init__(self, omega, mus, epss):
     from sumpy.kernel import HelmholtzKernel
     self.kernel = HelmholtzKernel(3)
     self.omega = omega
     self.mus = mus
     self.epss = epss
     self.ks = [
             sym.cse(omega*(eps*mu)**0.5, "k%d" % i)
             for i, (eps, mu) in enumerate(zip(epss, mus))]
Exemplo n.º 4
0
    def __init__(self, domain_n_exprs, ne, interfaces, use_l2_weighting=None):
        """
        :attr interfaces: a tuple of tuples
            ``(outer_domain, inner_domain, interface_id)``,
            where *outer_domain* and *inner_domain* are indices into
            *domain_k_names*,
            and *interface_id* is a symbolic name for the discretization of the
            interface. 'outer' designates the side of the interface to which
            the normal points.
        :attr domain_n_exprs: a tuple of variable names of the Helmholtz
            parameter *k*, to be used inside each part of the source geometry.
            May also be a tuple of strings, which will be transformed into
            variable references of the corresponding names.
        :attr beta: A symbolic expression for the wave number in the :math:`z`
            direction. May be a string, which will be interpreted as a variable
            name.
        """

        self.interfaces = interfaces

        ne = sym.var(ne)
        self.ne = sym.cse(ne, "ne")

        self.domain_n_exprs = [
            sym.var(n_expr) for idom, n_expr in enumerate(domain_n_exprs)
        ]
        del domain_n_exprs

        import pymbolic.primitives as p

        def upper_half_square_root(x):
            return p.If(p.Comparison((x**0.5).a.imag, "<", 0), 1j * (-x)**0.5,
                        x**0.5)

        self.domain_K_exprs = [
            sym.cse(upper_half_square_root(n_expr**2 - ne**2), "K%d" % i)
            for i, n_expr in enumerate(self.domain_n_exprs)
        ]

        from sumpy.kernel import HelmholtzKernel
        self.kernel = HelmholtzKernel(2, allow_evanescent=True)
Exemplo n.º 5
0
    def representation(self, i, sol):
        u = self.split_unknown(sol)
        Jxyz = sym.cse(sym.tangential_to_xyz(u.jt), "Jxyz")
        Mxyz = sym.cse(sym.tangential_to_xyz(u.mt), "Mxyz")

        # omega = self.omega
        mu = self.mus[i]
        eps = self.epss[i]
        k = self.ks[i]

        S = partial(sym.S, self.kernel, qbx_forced_limit=None, k=k)

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

        grad = partial(sym.grad, 3)

        E0 = 1j*k*eps*S(Jxyz) + mu*curl_S(Mxyz) - grad(S(u.rho_e))
        H0 = -1j*k*mu*S(Mxyz) + eps*curl_S(Jxyz) + grad(S(u.rho_m))

        return sym.flat_obj_array(E0, H0)
Exemplo n.º 6
0
 def get_weight(self, dofdesc=None):
     """
     :returns: a symbolic expression for the weights (quadrature weights
         and area elements) on *dofdesc* if :attr:`use_l2_weighting` is
         *True* and ``1`` otherwise.
     """
     if self.use_l2_weighting:
         return sym.cse(
                 sym.area_element(self.dim, dofdesc=dofdesc)
                 * sym.QWeight(dofdesc=dofdesc))
     else:
         return 1
Exemplo n.º 7
0
    def representation(self, i, sol):
        u = self.split_unknown(sol)
        Jxyz = sym.cse(sym.tangential_to_xyz(u.jt), "Jxyz")
        Mxyz = sym.cse(sym.tangential_to_xyz(u.mt), "Mxyz")

        # omega = self.omega
        mu = self.mus[i]
        eps = self.epss[i]
        k = self.ks[i]

        S = partial(sym.S, self.kernel, qbx_forced_limit=None, k=k)

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

        grad = partial(sym.grad, 3)

        E0 = 1j*k*eps*S(Jxyz) + mu*curl_S(Mxyz) - grad(S(u.rho_e))
        H0 = -1j*k*mu*S(Mxyz) + eps*curl_S(Jxyz) + grad(S(u.rho_m))

        return sym.join_fields(E0, H0)
Exemplo n.º 8
0
    def scattered_volume_field(self, Jt, rho, qbx_forced_limit=None):
        """
        This will return an object array of six entries, the first three of which
        represent the electric, and the second three of which represent the
        magnetic field. This satisfies the time-domain Maxwell's equations
        as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`.
        """
        Jxyz = sym.cse(sym.tangential_to_xyz(Jt), "Jxyz")

        A = sym.S(self.kernel, Jxyz, k=self.k, qbx_forced_limit=qbx_forced_limit)
        phi = sym.S(self.kernel, rho, k=self.k, qbx_forced_limit=qbx_forced_limit)

        E_scat = 1j*self.k*A - sym.grad(3, phi)
        H_scat = sym.curl(A)

        return sym.flat_obj_array(E_scat, H_scat)
Exemplo n.º 9
0
    def scattered_volume_field(self, Jt, rho, qbx_forced_limit=None):
        """
        This will return an object array of six entries, the first three of which
        represent the electric, and the second three of which represent the
        magnetic field. This satisfies the time-domain Maxwell's equations
        as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`.
        """
        Jxyz = sym.cse(sym.tangential_to_xyz(Jt), "Jxyz")

        A = sym.S(self.kernel, Jxyz, k=self.k, qbx_forced_limit=qbx_forced_limit)
        phi = sym.S(self.kernel, rho, k=self.k, qbx_forced_limit=qbx_forced_limit)

        E_scat = 1j*self.k*A - sym.grad(3, phi)
        H_scat = sym.curl(A)

        return sym.join_fields(E_scat, H_scat)
Exemplo n.º 10
0
    def _structured_unknown(self, unknown, with_l2_weights):
        """
        :arg with_l2_weights: If True, return the 'bare' unknowns
            that do not have the :math:`L^2` weights divided out.
            Note: Those unknowns should *not* be interpreted as
            point values of a density.
        :returns: an array of unknowns, with the following index axes:
            ``[pot_kind, field_kind, i_interface]``, where
            ``pot_kind`` is 0 for the single-layer part and 1 for the double-layer
            part,
            ``field_kind`` is 0 for the E-field and 1 for the H-field part,
            ``i_interface`` is the number of the enclosed domain, starting from 0.
        """
        result = np.zeros((2, 2, len(self.interfaces)), dtype=np.object)

        i_unknown = 0
        for pot_kind in self.pot_kinds:
            for field_kind in self.field_kinds:
                for i_interface in range(len(self.interfaces)):

                    if self.is_field_present(field_kind):
                        dens = unknown[i_unknown]
                        i_unknown += 1
                    else:
                        dens = 0

                    _, _, interface_id = self.interfaces[i_interface]

                    if not with_l2_weights:
                        dens = sym.cse(
                            dens / self.get_sqrt_weight(interface_id),
                            "dens_{pot}_{field}_{intf}".format(
                                pot={
                                    0: "S",
                                    1: "D"
                                }[pot_kind],
                                field={
                                    self.field_kind_e: "E",
                                    self.field_kind_h: "H"
                                }[field_kind],
                                intf=i_interface))

                    result[pot_kind, field_kind, i_interface] = dens

        assert i_unknown == len(unknown)
        return result
Exemplo n.º 11
0
    def _structured_unknown(self, unknown, with_l2_weights):
        """
        :arg with_l2_weights: If True, return the 'bare' unknowns
            that do not have the :math:`L^2` weights divided out.
            Note: Those unknowns should *not* be interpreted as
            point values of a density.
        :returns: an array of unknowns, with the following index axes:
            ``[side, field_kind, i_interface]``, where
            ``side`` is o for the outside part and i for the interior part,
            ``field_kind`` is 0 for the E-field and 1 for the H-field part,
            ``i_interface`` is the number of the enclosed domain, starting from 0.
        """
        result = np.zeros((2, 2, len(self.interfaces)), dtype=np.object)

        i_unknown = 0
        for side in self.sides:
            for field_kind in self.field_kinds:
                for i_interface in range(len(self.interfaces)):

                    if self.is_field_present(field_kind):
                        dens = unknown[i_unknown]
                        i_unknown += 1
                    else:
                        dens = 0

                    _, _, interface_id = self.interfaces[i_interface]

                    if not with_l2_weights:
                        dens = sym.cse(
                                dens/self.get_sqrt_weight(interface_id),
                                "dens_{side}_{field}_{dom}".format(
                                    side={
                                        self.side_out: "o",
                                        self.side_in: "i"}
                                    [side],
                                    field={
                                        self.field_kind_e: "E",
                                        self.field_kind_h: "H"
                                        }
                                    [field_kind],
                                    dom=i_interface))

                    result[side, field_kind, i_interface] = dens

        assert i_unknown == len(unknown)
        return result
Exemplo n.º 12
0
def get_sym_maxwell_plane_wave(amplitude_vec,
                               v,
                               omega,
                               epsilon=1,
                               mu=1,
                               dofdesc=None):
    r"""Return a symbolic expression that, when bound to a
    :class:`pytential.source.PointPotentialSource` will yield
    a field satisfying Maxwell's equations.

    :arg amplitude_vec: should be orthogonal to *v*. If it is not,
        it will be orthogonalized.
    :arg v: a three-vector representing the phase velocity of the wave
        (may be an object array of variables or a vector of concrete numbers)
        While *v* may mathematically be complex-valued, this function
        is for now only tested for real values.
    :arg omega: Accepts the "Helmholtz k" to be compatible with other parts
        of this module.

    Uses the sign convention :math:`\exp(-1 \omega t)` for the time dependency.

    This will return an object of six entries, the first three of which
    represent the electric, and the second three of which represent the
    magnetic field. This satisfies the time-domain Maxwell's equations
    as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`.
    """

    # See section 7.1 of Jackson, third ed. for derivation.

    # NOTE: for complex, need to ensure real(n).dot(imag(n)) = 0  (7.15)

    x = sym.nodes(3, dofdesc=dofdesc).as_vector()

    v_mag_squared = sym.cse(np.dot(v, v), "v_mag_squared")
    n = v / sym.sqrt(v_mag_squared)

    amplitude_vec = amplitude_vec - np.dot(amplitude_vec, n) * n

    c_inv = np.sqrt(mu * epsilon)

    e = amplitude_vec * sym.exp(1j * np.dot(n * omega, x))

    return sym.flat_obj_array(e, c_inv * sym.cross(n, e))
Exemplo n.º 13
0
    def operator(self, u, **kwargs):
        """
        :param u: symbolic variable for the density.
        :param kwargs: additional keyword arguments passed on to the layer
            potential constructor.
        """
        sqrt_w = self.get_sqrt_weight()
        inv_sqrt_w_u = sym.cse(u/sqrt_w)

        if self.is_unique_only_up_to_constant():
            # The exterior Dirichlet operator in this representation
            # has a nullspace. The mean of the density must be matched
            # to the desired solution separately. As is, this operator
            # returns a mean that is not well-specified.
            #
            # See Hackbusch, https://books.google.com/books?id=Ssnf7SZB0ZMC
            # Theorem 8.2.18b

            ones_contribution = (
                    sym.Ones() * sym.mean(self.dim, self.dim - 1, inv_sqrt_w_u))
        else:
            ones_contribution = 0

        def S(density):  # noqa
            return sym.S(self.kernel, density,
                    kernel_arguments=self.kernel_arguments,
                    qbx_forced_limit=+1, **kwargs)

        def D(density):  # noqa
            return sym.D(self.kernel, density,
                    kernel_arguments=self.kernel_arguments,
                    qbx_forced_limit="avg", **kwargs)

        return (
                -0.5 * self.loc_sign * u + sqrt_w * (
                    self.alpha * S(inv_sqrt_w_u)
                    - D(inv_sqrt_w_u)
                    + ones_contribution
                    )
                )
Exemplo n.º 14
0
    def _operator(self, sigma, normal, mu, qbx_forced_limit):
        slp_qbx_forced_limit = qbx_forced_limit
        if slp_qbx_forced_limit == "avg":
            slp_qbx_forced_limit = +1

        # NOTE: we set a dofdesc here to force the evaluation of this integral
        # on the source instead of the target when using automatic tagging
        # see :meth:`pytential.symbolic.mappers.LocationTagger._default_dofdesc`
        dd = sym.DOFDescriptor(None, discr_stage=sym.QBX_SOURCE_STAGE1)

        int_sigma = sym.integral(self.ambient_dim, self.dim, sigma, dofdesc=dd)
        meanless_sigma = sym.cse(sigma - sym.mean(self.ambient_dim, self.dim, sigma))

        op_k = self.stresslet.apply(sigma, normal, mu,
                qbx_forced_limit=qbx_forced_limit)
        op_s = (
                self.alpha / (2.0 * np.pi) * int_sigma
                - self.stokeslet.apply(meanless_sigma, mu,
                    qbx_forced_limit=slp_qbx_forced_limit)
                )

        return op_k + self.eta * op_s
Exemplo n.º 15
0
def get_sym_maxwell_plane_wave(amplitude_vec, v, omega, epsilon=1, mu=1, where=None):
    r"""Return a symbolic expression that, when bound to a
    :class:`pytential.source.PointPotentialSource` will yield
    a field satisfying Maxwell's equations.

    :arg amplitude_vec: should be orthogonal to *v*. If it is not,
        it will be orthogonalized.
    :arg v: a three-vector representing the phase velocity of the wave
        (may be an object array of variables or a vector of concrete numbers)
        While *v* may mathematically be complex-valued, this function
        is for now only tested for real values.
    :arg omega: Accepts the "Helmholtz k" to be compatible with other parts
        of this module.

    Uses the sign convention :math:`\exp(-1 \omega t)` for the time dependency.

    This will return an object of six entries, the first three of which
    represent the electric, and the second three of which represent the
    magnetic field. This satisfies the time-domain Maxwell's equations
    as verified by :func:`sumpy.point_calculus.frequency_domain_maxwell`.
    """

    # See section 7.1 of Jackson, third ed. for derivation.

    # NOTE: for complex, need to ensure real(n).dot(imag(n)) = 0  (7.15)

    x = sym.nodes(3, where).as_vector()

    v_mag_squared = sym.cse(np.dot(v, v), "v_mag_squared")
    n = v/sym.sqrt(v_mag_squared)

    amplitude_vec = amplitude_vec - np.dot(amplitude_vec, n)*n

    c_inv = np.sqrt(mu*epsilon)

    e = amplitude_vec * sym.exp(1j*np.dot(n*omega, x))

    return sym.join_fields(e, c_inv * sym.cross(n, e))
Exemplo n.º 16
0
    def representation(self, domain_idx, unknown):
        """"Return a tuple of vectors [Ex, Ey, Ez] and [Hx, Hy, Hz]
        representing the solution to Maxwell's equation on domain
        *domain_idx*.
        """
        result = np.zeros(4, dtype=object)

        unk_idx = self.unknown_index
        k_v = self.k_vacuum

        for i in range(len(self.interfaces)):
            dom0i, dom1i, where = self.interfaces[i]

            if domain_idx == dom0i:
                domi = dom0i
            elif domain_idx == dom1i:
                domi = dom1i
            else:
                # Interface does not border the requested domain
                continue

            beta = self.ne * k_v
            k = sym.cse(k_v * self.domain_n_exprs[domi], "k%d" % domi)

            jt = unknown[unk_idx["jz", i]]
            jz = unknown[unk_idx["jt", i]]
            mt = unknown[unk_idx["mz", i]]
            mz = unknown[unk_idx["mt", i]]

            def diff_axis(iaxis, operand):
                v = np.array(2, dtype=np.float64)
                v[iaxis] = 1
                d = sym.Derivative()
                return d.resolve(
                    (sym.MultiVector(v).scalar_product(d.dnabla(2))) *
                    d(operand))

            from functools import partial
            dx = partial(diff_axis, 1)
            dy = partial(diff_axis, 1)

            tangent = self.tangent(where)
            tau1 = tangent[0]
            tau2 = tangent[1]

            S = self.S  # noqa
            D = self.D  # noqa
            T = self.T  # noqa

            # Ex
            result[0] += (-1 / (1j * k_v) * dx(T(where, domi, jt)) +
                          beta / k_v * dx(S(domi, jz)) + k**2 /
                          (1j * k_v) * S(domi, jt * tau1) - dy(S(domi, mz)) +
                          1j * beta * S(domi, mt * tau2))

            # Ey
            result[1] += (-1 / (1j * k_v) * dy(T(where, domi, jt)) +
                          beta / k_v * dy(S(domi, jz)) + k**2 /
                          (1j * k_v) * S(domi, jt * tau2) + dx(S(domi, mz)) -
                          1j * beta * S(domi, mt * tau1))

            # Ez
            result[2] += (-beta / k_v * T(where, domi, jt) + (k**2 - beta**2) /
                          (1j * k_v) * S(domi, jz) + D(domi, mt))

            # Hx
            result[3] += (1 / (1j * k_v) * dx(T(where, domi, mt)) -
                          beta / k_v * dx(S(domi, mz)) - k**2 /
                          (1j * k_v) * S(domi, mt * tau1) -
                          k**2 / k_v**2 * dy(S(domi, jz)) +
                          1j * beta * k**2 / k_v**2 * S(domi, jt * tau2))

            # Hy
            result[4] += (1 / (1j * k_v) * dy(T(where, domi, mt)) -
                          beta / k_v * dy(S(domi, mz)) - k**2 /
                          (1j * k_v) * S(domi, mt * tau2) +
                          k**2 / k_v**2 * dx(S(domi, jz)) -
                          1j * beta * k**2 / k_v**2 * S(domi, jt * tau1))

            # Hz
            result[5] += (beta / k_v * T(where, domi, mt) - (k**2 - beta**2) /
                          (1j * k_v) * S(domi, mz) +
                          k**2 / k_v**2 * D(domi, jt))

        return result
Exemplo n.º 17
0
    def operator(self, unknown):
        result = np.zeros(4 * len(self.interfaces), dtype=object)

        unk_idx = self.unknown_index

        for i in range(len(self.interfaces)):
            idx_jt = unk_idx["jz", i]
            idx_jz = unk_idx["jt", i]
            idx_mt = unk_idx["mz", i]
            idx_mz = unk_idx["mt", i]

            phi1 = unknown[idx_jt]
            phi2 = unknown[idx_jz]
            phi3 = unknown[idx_mt]
            phi4 = unknown[idx_mz]

            ne = self.ne

            dom0i, dom1i, where = self.interfaces[i]

            tangent = self.tangent(where)
            normal = sym.cse(sym.normal(2, 1, where), "normal")

            S = self.S  # noqa
            D = self.D  # noqa
            T = self.T  # noqa

            def Tt(where, dom, density):  # noqa
                return sym.tangential_derivative(2, T(where, dom,
                                                      density)).xproject(0)

            def Sn(dom, density):  # noqa
                return sym.normal_derivative(
                    2, S(dom, density, qbx_forced_limit="avg"))

            def St(dom, density):  # noqa
                return sym.tangential_derivative(2, S(dom,
                                                      density)).xproject(0)

            n0 = self.domain_n_exprs[dom0i]
            n1 = self.domain_n_exprs[dom1i]

            a11 = sym.cse(n0**2 * D(dom0i, phi1) - n1**2 * D(dom1i, phi1),
                          "a11")
            a22 = sym.cse(-n0**2 * Sn(dom0i, phi2) + n1**2 * Sn(dom1i, phi2),
                          "a22")
            a33 = sym.cse(D(dom0i, phi3) - D(dom1i, phi3), "a33")
            a44 = sym.cse(-Sn(dom0i, phi4) + Sn(dom1i, phi4), "a44")

            a21 = sym.cse(
                -1j * ne *
                (n0**2 * tangent.scalar_product(S(dom0i, normal * phi1)) -
                 n1**2 * tangent.scalar_product(S(dom1i, normal * phi1))),
                "a21")

            a43 = sym.cse(
                -1j * ne * (tangent.scalar_product(S(dom0i, normal * phi3)) -
                            tangent.scalar_product(S(dom1i, normal * phi3))),
                "a43")

            a13 = +1 * sym.cse(
                ne * (T(where, dom0i, phi3) - T(where, dom1i, phi3)), "a13")
            a31 = -1 * sym.cse(
                ne * (T(where, dom0i, phi1) - T(where, dom1i, phi1)), "a31")

            a24 = +1 * sym.cse(ne * (St(dom0i, phi4) - St(dom1i, phi4)), "a24")
            a42 = -1 * sym.cse(ne * (St(dom0i, phi2) - St(dom1i, phi2)), "a42")

            a14 = sym.cse(
                1j * ((n0**2 - ne**2) * S(dom0i, phi4) -
                      (n1**2 - ne**2) * S(dom1i, phi4)), "a14")
            a32 = -sym.cse(
                1j * ((n0**2 - ne**2) * S(dom0i, phi2) -
                      (n1**2 - ne**2) * S(dom1i, phi2)), "a32")

            def a23_expr(phi):
                return (
                    1j * (Tt(where, dom0i, phi) - Tt(where, dom1i, phi)) - 1j *
                    (n0**2 * tangent.scalar_product(S(dom0i, tangent * phi)) -
                     n1**2 * tangent.scalar_product(S(dom1i, tangent * phi))))

            a23 = +1 * sym.cse(a23_expr(phi3), "a23")
            a41 = -1 * sym.cse(a23_expr(phi1), "a41")

            d1 = (n0**2 + n1**2) / 2 * phi1
            d2 = (n0**2 + n1**2) / 2 * phi2
            d3 = phi3
            d4 = phi4

            result[idx_jt] += d1 + a11 + 000 + a13 + a14
            result[idx_jz] += d2 + a21 + a22 + a23 + a24
            result[idx_mt] += d3 + a31 + a32 + a33 + 0
            result[idx_mz] += d4 + a41 + a42 + a43 + a44

            # TODO: L2 weighting
            # TODO: Add representation contributions to other boundaries
            # abutting the domain
            return result
Exemplo n.º 18
0
        def nxcurlS(qbx_forced_limit):

            return sym.n_cross(sym.curl(sym.S(
                knl,
                sym.cse(sym.tangential_to_xyz(density_sym), "jxyz"),
                qbx_forced_limit=qbx_forced_limit)))
Exemplo n.º 19
0
 def tangent(self, where):
     return sym.cse(
         (sym.pseudoscalar(2, 1, where) / sym.area_element(2, 1, where)),
         "tangent")
Exemplo n.º 20
0
def main(mesh_name="torus", visualize=False):
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    if mesh_name == "torus":
        rout = 10
        rin = 1

        from meshmode.mesh.generation import generate_torus
        base_mesh = generate_torus(
                rout, rin, 40, 4,
                mesh_order)

        from meshmode.mesh.processing import affine_map, merge_disjoint_meshes
        # nx = 1
        # ny = 1
        nz = 1
        dz = 0
        meshes = [
                affine_map(
                    base_mesh,
                    A=np.diag([1, 1, 1]),
                    b=np.array([0, 0, iz*dz]))
                for iz in range(nz)]

        mesh = merge_disjoint_meshes(meshes, single_group=True)

        if visualize:
            from meshmode.mesh.visualization import draw_curve
            draw_curve(mesh)
            import matplotlib.pyplot as plt
            plt.show()
    else:
        raise ValueError(f"unknown mesh name: {mesh_name}")

    pre_density_discr = Discretization(
            actx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (
            QBXLayerPotentialSource, QBXTargetAssociationFailedException)
    qbx = QBXLayerPotentialSource(
            pre_density_discr, fine_order=bdry_ovsmp_quad_order, qbx_order=qbx_order,
            fmm_order=fmm_order,
            )

    from sumpy.visualization import FieldPlotter
    fplot = FieldPlotter(np.zeros(3), extent=20, npoints=50)
    targets = actx.from_numpy(fplot.points)

    from pytential import GeometryCollection
    places = GeometryCollection({
        "qbx": qbx,
        "qbx_target_assoc": qbx.copy(target_association_tolerance=0.2),
        "targets": PointsTarget(targets)
        }, auto_where="qbx")
    density_discr = places.get_discretization("qbx")

    # {{{ describe bvp

    from sumpy.kernel import LaplaceKernel
    kernel = LaplaceKernel(3)

    sigma_sym = sym.var("sigma")
    #sqrt_w = sym.sqrt_jac_q_weight(3)
    sqrt_w = 1
    inv_sqrt_w_sigma = sym.cse(sigma_sym/sqrt_w)

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    bdry_op_sym = (loc_sign*0.5*sigma_sym
            + sqrt_w*(
                sym.S(kernel, inv_sqrt_w_sigma, qbx_forced_limit=+1)
                + sym.D(kernel, inv_sqrt_w_sigma, qbx_forced_limit="avg")
                ))

    # }}}

    bound_op = bind(places, bdry_op_sym)

    # {{{ fix rhs and solve

    from meshmode.dof_array import thaw, flatten, unflatten
    nodes = thaw(actx, density_discr.nodes())
    source = np.array([rout, 0, 0])

    def u_incoming_func(x):
        from pytools.obj_array import obj_array_vectorize
        x = obj_array_vectorize(actx.to_numpy, flatten(x))
        x = np.array(list(x))
        #        return 1/cl.clmath.sqrt( (x[0] - source[0])**2
        #                                +(x[1] - source[1])**2
        #                                +(x[2] - source[2])**2 )
        return 1.0/la.norm(x - source[:, None], axis=0)

    bc = unflatten(actx,
            density_discr,
            actx.from_numpy(u_incoming_func(nodes)))

    bvp_rhs = bind(places, sqrt_w*sym.var("bc"))(actx, bc=bc)

    from pytential.solve import gmres
    gmres_result = gmres(
            bound_op.scipy_op(actx, "sigma", dtype=np.float64),
            bvp_rhs, tol=1e-14, progress=True,
            stall_iterations=0,
            hard_failure=True)

    sigma = bind(places, sym.var("sigma")/sqrt_w)(
            actx, sigma=gmres_result.solution)

    # }}}

    from meshmode.discretization.visualization import make_visualizer
    bdry_vis = make_visualizer(actx, density_discr, 20)
    bdry_vis.write_vtk_file("laplace.vtu", [
        ("sigma", sigma),
        ])

    # {{{ postprocess/visualize

    repr_kwargs = dict(
            source="qbx_target_assoc",
            target="targets",
            qbx_forced_limit=None)
    representation_sym = (
            sym.S(kernel, inv_sqrt_w_sigma, **repr_kwargs)
            + sym.D(kernel, inv_sqrt_w_sigma, **repr_kwargs))

    try:
        fld_in_vol = actx.to_numpy(
                bind(places, representation_sym)(actx, sigma=sigma))
    except QBXTargetAssociationFailedException as e:
        fplot.write_vtk_file("laplace-dirichlet-3d-failed-targets.vts", [
            ("failed", e.failed_target_flags.get(queue)),
            ])
        raise

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file("laplace-dirichlet-3d-potential.vts", [
        ("potential", fld_in_vol),
        ])
Exemplo n.º 21
0
        def nxcurlS(qbx_forced_limit):

            return sym.n_cross(sym.curl(sym.S(
                knl,
                sym.cse(sym.tangential_to_xyz(density_sym), "jxyz"),
                qbx_forced_limit=qbx_forced_limit)))
Exemplo n.º 22
0
    def operator(self, unknown):
        result = np.zeros(4*len(self.interfaces), dtype=object)

        unk_idx = self.unknown_index

        for i in range(len(self.interfaces)):
            idx_jt = unk_idx["jz", i]
            idx_jz = unk_idx["jt", i]
            idx_mt = unk_idx["mz", i]
            idx_mz = unk_idx["mt", i]

            phi1 = unknown[idx_jt]
            phi2 = unknown[idx_jz]
            phi3 = unknown[idx_mt]
            phi4 = unknown[idx_mz]

            ne = self.ne

            dom0i, dom1i, where = self.interfaces[i]

            tangent = self.tangent(where)
            normal = sym.cse(
                    sym.normal(2, 1, where),
                    "normal")

            S = self.S  # noqa
            D = self.D  # noqa
            T = self.T  # noqa

            def Tt(where, dom, density):  # noqa
                return sym.tangential_derivative(
                        2, T(where, dom, density)).xproject(0)

            def Sn(dom, density):  # noqa
                return sym.normal_derivative(
                        2,
                        S(dom, density,
                            qbx_forced_limit="avg"))

            def St(dom, density):  # noqa
                return sym.tangential_derivative(2, S(dom, density)).xproject(0)

            n0 = self.domain_n_exprs[dom0i]
            n1 = self.domain_n_exprs[dom1i]

            a11 = sym.cse(n0**2 * D(dom0i, phi1) - n1**2 * D(dom1i, phi1), "a11")
            a22 = sym.cse(-n0**2 * Sn(dom0i, phi2) + n1**2 * Sn(dom1i, phi2), "a22")
            a33 = sym.cse(D(dom0i, phi3)-D(dom1i, phi3), "a33")
            a44 = sym.cse(-Sn(dom0i, phi4) + Sn(dom1i, phi4), "a44")

            a21 = sym.cse(-1j * ne * (
                    n0**2 * tangent.scalar_product(
                        S(dom0i, normal * phi1))
                    - n1**2 * tangent.scalar_product(
                        S(dom1i, normal * phi1))), "a21")

            a43 = sym.cse(-1j * ne * (
                    tangent.scalar_product(
                        S(dom0i, normal * phi3))
                    - tangent.scalar_product(
                        S(dom1i, normal * phi3))), "a43")

            a13 = +1*sym.cse(
                    ne*(T(where, dom0i, phi3) - T(where, dom1i, phi3)), "a13")
            a31 = -1*sym.cse(
                    ne*(T(where, dom0i, phi1) - T(where, dom1i, phi1)), "a31")

            a24 = +1*sym.cse(ne*(St(dom0i, phi4) - St(dom1i, phi4)), "a24")
            a42 = -1*sym.cse(ne*(St(dom0i, phi2) - St(dom1i, phi2)), "a42")

            a14 = sym.cse(1j*(
                    (n0**2 - ne**2) * S(dom0i, phi4)
                    - (n1**2 - ne**2) * S(dom1i, phi4)
                    ), "a14")
            a32 = -sym.cse(1j*(
                    (n0**2 - ne**2) * S(dom0i, phi2)
                    - (n1**2 - ne**2) * S(dom1i, phi2)
                    ), "a32")

            def a23_expr(phi):
                return (
                        1j * (Tt(where, dom0i, phi) - Tt(where, dom1i, phi))
                        - 1j * (
                            n0**2 * tangent.scalar_product(
                                S(dom0i, tangent * phi))
                            - n1**2 * tangent.scalar_product(
                                S(dom1i, tangent * phi))))

            a23 = +1*sym.cse(a23_expr(phi3), "a23")
            a41 = -1*sym.cse(a23_expr(phi1), "a41")

            d1 = (n0**2 + n1**2)/2 * phi1
            d2 = (n0**2 + n1**2)/2 * phi2
            d3 = phi3
            d4 = phi4

            result[idx_jt] += d1 + a11 + 000 + a13 + a14
            result[idx_jz] += d2 + a21 + a22 + a23 + a24
            result[idx_mt] += d3 + a31 + a32 + a33 + 0
            result[idx_mz] += d4 + a41 + a42 + a43 + a44

            # TODO: L2 weighting
            # TODO: Add representation contributions to other boundaries
            # abutting the domain
            return result
Exemplo n.º 23
0
def timing_run(nx, ny, visualize=False):
    import logging
    logging.basicConfig(level=logging.WARNING)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    mesh = make_mesh(nx=nx, ny=ny, visualize=visualize)

    density_discr = Discretization(
        actx, mesh,
        InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (QBXLayerPotentialSource,
                               QBXTargetAssociationFailedException)
    qbx = QBXLayerPotentialSource(density_discr,
                                  fine_order=bdry_ovsmp_quad_order,
                                  qbx_order=qbx_order,
                                  fmm_order=fmm_order)

    places = {"qbx": qbx}
    if visualize:
        from sumpy.visualization import FieldPlotter
        fplot = FieldPlotter(np.zeros(2), extent=5, npoints=1500)
        targets = PointsTarget(actx.from_numpy(fplot.points))

        places.update({
            "plot-targets":
            targets,
            "qbx-indicator":
            qbx.copy(target_association_tolerance=0.05,
                     fmm_level_to_order=lambda lev: 7,
                     qbx_order=2),
            "qbx-target-assoc":
            qbx.copy(target_association_tolerance=0.1)
        })

    from pytential import GeometryCollection
    places = GeometryCollection(places, auto_where="qbx")
    density_discr = places.get_discretization("qbx")

    # {{{ describe bvp

    from sumpy.kernel import HelmholtzKernel
    kernel = HelmholtzKernel(2)

    sigma_sym = sym.var("sigma")
    sqrt_w = sym.sqrt_jac_q_weight(2)
    inv_sqrt_w_sigma = sym.cse(sigma_sym / sqrt_w)

    # Brakhage-Werner parameter
    alpha = 1j

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    k_sym = sym.var("k")
    S_sym = sym.S(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit=+1)
    D_sym = sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit="avg")
    bdry_op_sym = -loc_sign * 0.5 * sigma_sym + sqrt_w * (alpha * S_sym +
                                                          D_sym)

    # }}}

    bound_op = bind(places, bdry_op_sym)

    # {{{ fix rhs and solve

    mode_nr = 3

    from meshmode.dof_array import thaw
    nodes = thaw(actx, density_discr.nodes())
    angle = actx.np.arctan2(nodes[1], nodes[0])

    sigma = actx.np.cos(mode_nr * angle)

    # }}}

    # {{{ postprocess/visualize

    repr_kwargs = dict(k=sym.var("k"), qbx_forced_limit=+1)

    sym_op = sym.S(kernel, sym.var("sigma"), **repr_kwargs)
    bound_op = bind(places, sym_op)

    print("FMM WARM-UP RUN 1: %5d elements" % mesh.nelements)
    bound_op(actx, sigma=sigma, k=k)
    queue.finish()

    print("FMM WARM-UP RUN 2: %5d elements" % mesh.nelements)
    bound_op(actx, sigma=sigma, k=k)
    queue.finish()

    from time import time
    t_start = time()
    bound_op(actx, sigma=sigma, k=k)
    actx.queue.finish()
    elapsed = time() - t_start

    print("FMM TIMING RUN:    %5d elements -> %g s" %
          (mesh.nelements, elapsed))

    if visualize:
        ones_density = density_discr.zeros(queue)
        ones_density.fill(1)
        indicator = bind(places,
                         sym_op,
                         auto_where=("qbx-indicator", "plot-targets"))(
                             queue, sigma=ones_density).get()

        try:
            fld_in_vol = bind(places,
                              sym_op,
                              auto_where=("qbx-target-assoc",
                                          "plot-targets"))(queue,
                                                           sigma=sigma,
                                                           k=k).get()
        except QBXTargetAssociationFailedException as e:
            fplot.write_vtk_file("scaling-study-failed-targets.vts", [
                ("failed", e.failed_target_flags.get(queue)),
            ])
            raise

        fplot.write_vtk_file("scaling-study-potential.vts", [
            ("potential", fld_in_vol),
            ("indicator", indicator),
        ])

    return (mesh.nelements, elapsed)
Exemplo n.º 24
0
    def __init__(self,
                 mode,
                 k_vacuum,
                 domain_k_exprs,
                 beta,
                 interfaces,
                 use_l2_weighting=None):
        """
        :attr mode: one of 'te', 'tm', 'tem'
        :attr k_vacuum: A symbolic expression for the wave number in vacuum.
            May be a string, which will be interpreted as a variable name.
        :attr interfaces: a tuple of tuples
            ``(outer_domain, inner_domain, interface_id)``,
            where *outer_domain* and *inner_domain* are indices into
            *domain_k_names*,
            and *interface_id* is a symbolic name for the discretization of the
            interface. 'outer' designates the side of the interface to which
            the normal points.
        :attr domain_k_exprs: a tuple of variable names of the Helmholtz
            parameter *k*, to be used inside each part of the source geometry.
            May also be a tuple of strings, which will be transformed into
            variable references of the corresponding names.
        :attr beta: A symbolic expression for the wave number in the :math:`z`
            direction. May be a string, which will be interpreted as a variable
            name.
        """

        if use_l2_weighting is None:
            use_l2_weighting = False

        super(Dielectric2DBoundaryOperatorBase,
              self).__init__(use_l2_weighting=use_l2_weighting)

        if mode == "te":
            self.ez_enabled = False
            self.hz_enabled = True
        elif mode == "tm":
            self.ez_enabled = True
            self.hz_enabled = False
        elif mode == "tem":
            self.ez_enabled = True
            self.hz_enabled = True
        else:
            raise ValueError("invalid mode '%s'" % mode)

        self.interfaces = interfaces

        fk_e = self.field_kind_e
        fk_h = self.field_kind_h

        dir_none = self.dir_none
        dir_normal = self.dir_normal
        dir_tangential = self.dir_tangential

        if isinstance(beta, str):
            beta = sym.var(beta)
        beta = sym.cse(beta, "beta")

        if isinstance(k_vacuum, str):
            k_vacuum = sym.var(k_vacuum)
        k_vacuum = sym.cse(k_vacuum, "k_vac")

        self.domain_k_exprs = [
            sym.var(k_expr) if isinstance(k_expr, str) else sym.cse(
                k_expr, "k%d" % idom)
            for idom, k_expr in enumerate(domain_k_exprs)
        ]
        del domain_k_exprs

        # Note the case of k/K!
        # "K" is the 2D Helmholtz parameter.
        # "k" is the 3D Helmholtz parameter.

        self.domain_K_exprs = [
            sym.cse((k_expr**2 - beta**2)**0.5, "K%d" % i)
            for i, k_expr in enumerate(self.domain_k_exprs)
        ]

        from sumpy.kernel import HelmholtzKernel
        self.kernel = HelmholtzKernel(2, allow_evanescent=True)

        # {{{ build bc list

        # list of tuples, where each tuple consists of BCTermDescriptor instances

        all_bcs = []
        for i_interface, (outer_domain, inner_domain,
                          _) in (enumerate(self.interfaces)):
            k_outer = self.domain_k_exprs[outer_domain]
            k_inner = self.domain_k_exprs[inner_domain]

            all_bcs += [
                (  # [E] = 0
                    self.BCTermDescriptor(i_interface=i_interface,
                                          direction=dir_none,
                                          field_kind=fk_e,
                                          coeff_outer=1,
                                          coeff_inner=-1), ),
                (  # [H] = 0
                    self.BCTermDescriptor(i_interface=i_interface,
                                          direction=dir_none,
                                          field_kind=fk_h,
                                          coeff_outer=1,
                                          coeff_inner=-1), ),
                (
                    self.BCTermDescriptor(
                        i_interface=i_interface,
                        direction=dir_tangential,
                        field_kind=fk_e,
                        coeff_outer=beta / (k_outer**2 - beta**2),
                        coeff_inner=-beta / (k_inner**2 - beta**2)),
                    self.BCTermDescriptor(
                        i_interface=i_interface,
                        direction=dir_normal,
                        field_kind=fk_h,
                        coeff_outer=sym.cse(-k_vacuum /
                                            (k_outer**2 - beta**2)),
                        coeff_inner=sym.cse(k_vacuum /
                                            (k_inner**2 - beta**2))),
                ),
                (self.BCTermDescriptor(
                    i_interface=i_interface,
                    direction=dir_tangential,
                    field_kind=fk_h,
                    coeff_outer=beta / (k_outer**2 - beta**2),
                    coeff_inner=-beta / (k_inner**2 - beta**2)),
                 self.BCTermDescriptor(
                     i_interface=i_interface,
                     direction=dir_normal,
                     field_kind=fk_e,
                     coeff_outer=sym.cse(
                         (k_outer**2 / k_vacuum) / (k_outer**2 - beta**2)),
                     coeff_inner=sym.cse(-(k_inner**2 / k_vacuum) /
                                         (k_inner**2 - beta**2)))),
            ]

            del k_outer
            del k_inner

        self.bcs = []
        for bc in all_bcs:
            any_significant_e = any(
                term.field_kind == fk_e
                and term.direction in [dir_normal, dir_none] for term in bc)
            any_significant_h = any(
                term.field_kind == fk_h
                and term.direction in [dir_normal, dir_none] for term in bc)
            is_necessary = ((self.ez_enabled and any_significant_e)
                            or (self.hz_enabled and any_significant_h))

            # Only keep tangential modes for TEM. Otherwise,
            # no jump in H already implies jump condition on
            # tangential derivative.
            is_tem = self.ez_enabled and self.hz_enabled
            terms = tuple(term for term in bc
                          if term.direction != dir_tangential or is_tem)

            if is_necessary:
                self.bcs.append(terms)

        assert (len(all_bcs) * (int(self.ez_enabled) + int(self.hz_enabled)) //
                2 == len(self.bcs))
Exemplo n.º 25
0
 def tangent(self, where):
     return sym.cse(
             (sym.pseudoscalar(2, 1, where)
             / sym.area_element(2, 1, where)),
             "tangent")
Exemplo n.º 26
0
    def representation(self, domain_idx, unknown):
        """"Return a tuple of vectors [Ex, Ey, Ez] and [Hx, Hy, Hz]
        representing the solution to Maxwell's equation on domain
        *domain_idx*.
        """
        result = np.zeros(4, dtype=object)

        unk_idx = self.unknown_index
        k_v = self.k_vacuum

        for i in range(len(self.interfaces)):
            dom0i, dom1i, where = self.interfaces[i]

            if domain_idx == dom0i:
                domi = dom0i
            elif domain_idx == dom1i:
                domi = dom1i
            else:
                # Interface does not border the requested domain
                continue

            beta = self.ne*k_v
            k = sym.cse(k_v * self.domain_n_exprs[domi],
                    "k%d" % domi)

            jt = unknown[unk_idx["jz", i]]
            jz = unknown[unk_idx["jt", i]]
            mt = unknown[unk_idx["mz", i]]
            mz = unknown[unk_idx["mt", i]]

            def diff_axis(iaxis, operand):
                v = np.array(2, dtype=np.float64)
                v[iaxis] = 1
                d = sym.Derivative()
                return d.resolve(
                        (sym.MultiVector(v).scalar_product(d.dnabla(2)))
                        * d(operand))

            from functools import partial
            dx = partial(diff_axis, 1)
            dy = partial(diff_axis, 1)

            tangent = self.tangent(where)
            tau1 = tangent[0]
            tau2 = tangent[1]

            S = self.S  # noqa
            D = self.D  # noqa
            T = self.T  # noqa

            # Ex
            result[0] += (
                    -1/(1j*k_v) * dx(T(where, domi, jt))
                    + beta/k_v * dx(S(domi, jz))
                    + k**2/(1j*k_v) * S(domi, jt * tau1)
                    - dy(S(domi, mz))
                    + 1j*beta * S(domi, mt*tau2)
                    )

            # Ey
            result[1] += (
                    - 1/(1j*k_v) * dy(T(where, domi, jt))
                    + beta/k_v * dy(S(domi, jz))
                    + k**2/(1j*k_v) * S(domi, jt * tau2)
                    + dx(S(domi, mz))
                    - 1j*beta*S(domi, mt * tau1)
                    )

            # Ez
            result[2] += (
                    - beta/k_v * T(where, domi, jt)
                    + (k**2 - beta**2)/(1j*k_v) * S(domi, jz)
                    + D(domi, mt)
                    )

            # Hx
            result[3] += (
                    1/(1j*k_v) * dx(T(where, domi, mt))
                    - beta/k_v * dx(S(domi, mz))
                    - k**2/(1j*k_v) * S(domi, mt*tau1)
                    - k**2/k_v**2 * dy(S(domi, jz))
                    + 1j * beta * k**2/k_v**2 * S(domi, jt*tau2)
                    )

            # Hy
            result[4] += (
                    1/(1j*k_v) * dy(T(where, domi, mt))
                    - beta/k_v * dy(S(domi, mz))
                    - k**2/(1j*k_v) * S(domi, mt * tau2)
                    + k**2/k_v**2 * dx(S(domi, jz))
                    - 1j*beta * k**2/k_v**2 * S(domi, jt*tau1)
                    )

            # Hz
            result[5] += (
                    beta/k_v * T(where, domi, mt)
                    - (k**2 - beta**2)/(1j*k_v) * S(domi, mz)
                    + k**2/k_v**2 * D(domi, jt)
                    )

        return result
Exemplo n.º 27
0
    def operator(self, u, **kwargs):
        """
        :param u: symbolic variable for the density.
        :param kwargs: additional keyword arguments passed on to the layer
            potential constructor.
        """
        sqrt_w = self.get_sqrt_weight()
        inv_sqrt_w_u = sym.cse(u/sqrt_w)
        laplace_s_inv_sqrt_w_u = sym.cse(
                sym.S(self.laplace_kernel, inv_sqrt_w_u, qbx_forced_limit=+1)
                )

        kwargs["kernel_arguments"] = self.kernel_arguments

        # NOTE: the improved operator here is based on right-precondioning
        # by a single layer and then using some Calderon identities to simplify
        # the result. The integral equation we start with for Neumann is
        #       I + S' + alpha D' = g
        # where D' is hypersingular

        if self.use_improved_operator:
            def Sp(density):
                return sym.Sp(self.laplace_kernel,
                        density,
                        qbx_forced_limit="avg")

            # NOTE: using the Calderon identity
            #   D' S = -u/4 + S'^2
            Dp0S0u = -0.25 * u + Sp(Sp(inv_sqrt_w_u))

            from sumpy.kernel import HelmholtzKernel, LaplaceKernel
            if isinstance(self.kernel, HelmholtzKernel):
                DpS0u = (
                        sym.Dp(
                            self.kernel - self.laplace_kernel,
                            laplace_s_inv_sqrt_w_u,
                            qbx_forced_limit=+1, **kwargs)
                        + Dp0S0u)
            elif isinstance(self.kernel, LaplaceKernel):
                DpS0u = Dp0S0u
            else:
                raise ValueError(f"no improved operator for '{self.kernel}' known")
        else:
            DpS0u = sym.Dp(self.kernel, laplace_s_inv_sqrt_w_u, **kwargs)

        if self.is_unique_only_up_to_constant():
            # The interior Neumann operator in this representation
            # has a nullspace. The mean of the density must be matched
            # to the desired solution separately. As is, this operator
            # returns a mean that is not well-specified.

            ones_contribution = (
                    sym.Ones() * sym.mean(self.dim, self.dim - 1, inv_sqrt_w_u))
        else:
            ones_contribution = 0

        kwargs["qbx_forced_limit"] = "avg"
        return (
                -0.5 * self.loc_sign * u
                + sqrt_w * (
                    sym.Sp(self.kernel, inv_sqrt_w_u, **kwargs)
                    - self.alpha * DpS0u
                    + ones_contribution
                    )
                )
Exemplo n.º 28
0
    def center_info(self):
        """Return a :class:`CenterInfo`. |cached|

        """
        self_discr = self.lpot_source.density_discr

        ncenters = 0
        for el_group in self_discr.groups:
            kept_indices = self.kept_center_indices(el_group)
            # two: one for positive side, one for negative side
            ncenters += 2 * len(kept_indices) * el_group.nelements

        from pytential import sym, bind
        from pytools.obj_array import make_obj_array
        with cl.CommandQueue(self.cl_context) as queue:
            radii_sym = sym.cse(2*sym.area_element(), "radii")
            all_radii, all_pos_centers, all_neg_centers = bind(self_discr,
                    make_obj_array([
                        radii_sym,
                        sym.Nodes() + radii_sym*sym.normal(),
                        sym.Nodes() - radii_sym*sym.normal()
                        ]))(queue)

            # The centers are returned from the above as multivectors.
            all_pos_centers = all_pos_centers.as_vector(np.object)
            all_neg_centers = all_neg_centers.as_vector(np.object)

            # -1 for inside, +1 for outside
            sides = cl.array.empty(
                    self.cl_context, ncenters, np.int8)
            radii = cl.array.empty(
                    self.cl_context, ncenters, self.coord_dtype)
            centers = make_obj_array([
                cl.array.empty(self.cl_context, ncenters,
                    self.coord_dtype)
                for i in range(self_discr.ambient_dim)])

            ibase = 0
            for el_group in self_discr.groups:
                kept_center_indices = self.kept_center_indices(el_group)
                group_len = len(kept_indices) * el_group.nelements

                for side, all_centers in [
                        (+1, all_pos_centers),
                        (-1, all_neg_centers),
                        ]:

                    sides[ibase:ibase + group_len].fill(side, queue=queue)

                    radii_view = radii[ibase:ibase + group_len] \
                            .reshape(el_group.nelements, len(kept_indices))
                    centers_view = make_obj_array([
                        centers_i[ibase:ibase + group_len]
                        .reshape((el_group.nelements, len(kept_indices)))
                        for centers_i in centers
                        ])
                    all_centers_view = make_obj_array([
                        el_group.view(pos_centers_i)
                        for pos_centers_i in all_centers
                        ])
                    self.code_getter.pick_expansion_centers(queue,
                            centers=centers_view,
                            all_centers=all_centers_view,
                            radii=radii_view,
                            all_radii=el_group.view(all_radii),
                            kept_center_indices=kept_center_indices)

                    ibase += group_len

            assert ibase == ncenters

        return CenterInfo(
                sides=sides,
                radii=radii,
                centers=centers).with_queue(None)
Exemplo n.º 29
0
def main(mesh_name="ellipse", visualize=False):
    import logging
    logging.basicConfig(level=logging.INFO)  # INFO for more progress info

    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    from meshmode.mesh.generation import ellipse, make_curve_mesh
    from functools import partial

    if mesh_name == "ellipse":
        mesh = make_curve_mesh(partial(ellipse, 1),
                               np.linspace(0, 1, nelements + 1), mesh_order)
    elif mesh_name == "ellipse_array":
        base_mesh = make_curve_mesh(partial(ellipse, 1),
                                    np.linspace(0, 1, nelements + 1),
                                    mesh_order)

        from meshmode.mesh.processing import affine_map, merge_disjoint_meshes
        nx = 2
        ny = 2
        dx = 2 / nx
        meshes = [
            affine_map(base_mesh,
                       A=np.diag([dx * 0.25, dx * 0.25]),
                       b=np.array([dx * (ix - nx / 2), dx * (iy - ny / 2)]))
            for ix in range(nx) for iy in range(ny)
        ]

        mesh = merge_disjoint_meshes(meshes, single_group=True)

        if visualize:
            from meshmode.mesh.visualization import draw_curve
            draw_curve(mesh)
            import matplotlib.pyplot as plt
            plt.show()
    else:
        raise ValueError(f"unknown mesh name: {mesh_name}")

    pre_density_discr = Discretization(
        actx, mesh,
        InterpolatoryQuadratureSimplexGroupFactory(bdry_quad_order))

    from pytential.qbx import (QBXLayerPotentialSource,
                               QBXTargetAssociationFailedException)
    qbx = QBXLayerPotentialSource(pre_density_discr,
                                  fine_order=bdry_ovsmp_quad_order,
                                  qbx_order=qbx_order,
                                  fmm_order=fmm_order)

    from sumpy.visualization import FieldPlotter
    fplot = FieldPlotter(np.zeros(2), extent=5, npoints=500)
    targets = actx.from_numpy(fplot.points)

    from pytential import GeometryCollection
    places = GeometryCollection(
        {
            "qbx":
            qbx,
            "qbx_high_target_assoc_tol":
            qbx.copy(target_association_tolerance=0.05),
            "targets":
            PointsTarget(targets)
        },
        auto_where="qbx")
    density_discr = places.get_discretization("qbx")

    # {{{ describe bvp

    from sumpy.kernel import LaplaceKernel, HelmholtzKernel
    kernel = HelmholtzKernel(2)

    sigma_sym = sym.var("sigma")
    sqrt_w = sym.sqrt_jac_q_weight(2)
    inv_sqrt_w_sigma = sym.cse(sigma_sym / sqrt_w)

    # Brakhage-Werner parameter
    alpha = 1j

    # -1 for interior Dirichlet
    # +1 for exterior Dirichlet
    loc_sign = +1

    k_sym = sym.var("k")
    bdry_op_sym = (
        -loc_sign * 0.5 * sigma_sym + sqrt_w *
        (alpha * sym.S(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit=+1)
         - sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, qbx_forced_limit="avg")))

    # }}}

    bound_op = bind(places, bdry_op_sym)

    # {{{ fix rhs and solve

    from meshmode.dof_array import thaw
    nodes = thaw(actx, density_discr.nodes())
    k_vec = np.array([2, 1])
    k_vec = k * k_vec / la.norm(k_vec, 2)

    def u_incoming_func(x):
        return actx.np.exp(1j * (x[0] * k_vec[0] + x[1] * k_vec[1]))

    bc = -u_incoming_func(nodes)

    bvp_rhs = bind(places, sqrt_w * sym.var("bc"))(actx, bc=bc)

    from pytential.solve import gmres
    gmres_result = gmres(bound_op.scipy_op(actx,
                                           sigma_sym.name,
                                           dtype=np.complex128,
                                           k=k),
                         bvp_rhs,
                         tol=1e-8,
                         progress=True,
                         stall_iterations=0,
                         hard_failure=True)

    # }}}

    # {{{ postprocess/visualize

    repr_kwargs = dict(source="qbx_high_target_assoc_tol",
                       target="targets",
                       qbx_forced_limit=None)
    representation_sym = (
        alpha * sym.S(kernel, inv_sqrt_w_sigma, k=k_sym, **repr_kwargs) -
        sym.D(kernel, inv_sqrt_w_sigma, k=k_sym, **repr_kwargs))

    u_incoming = u_incoming_func(targets)
    ones_density = density_discr.zeros(actx)
    for elem in ones_density:
        elem.fill(1)

    indicator = actx.to_numpy(
        bind(places, sym.D(LaplaceKernel(2), sigma_sym,
                           **repr_kwargs))(actx, sigma=ones_density))

    try:
        fld_in_vol = actx.to_numpy(
            bind(places, representation_sym)(actx,
                                             sigma=gmres_result.solution,
                                             k=k))
    except QBXTargetAssociationFailedException as e:
        fplot.write_vtk_file("helmholtz-dirichlet-failed-targets.vts",
                             [("failed", e.failed_target_flags.get(queue))])
        raise

    #fplot.show_scalar_in_mayavi(fld_in_vol.real, max_val=5)
    fplot.write_vtk_file("helmholtz-dirichlet-potential.vts", [
        ("potential", fld_in_vol),
        ("indicator", indicator),
        ("u_incoming", actx.to_numpy(u_incoming)),
    ])
Exemplo n.º 30
0
    def __init__(self, mode, k_vacuum, domain_k_exprs, beta,
            interfaces, use_l2_weighting=None):
        """
        :attr mode: one of 'te', 'tm', 'tem'
        :attr k_vacuum: A symbolic expression for the wave number in vacuum.
            May be a string, which will be interpreted as a variable name.
        :attr interfaces: a tuple of tuples
            ``(outer_domain, inner_domain, interface_id)``,
            where *outer_domain* and *inner_domain* are indices into
            *domain_k_names*,
            and *interface_id* is a symbolic name for the discretization of the
            interface. 'outer' designates the side of the interface to which
            the normal points.
        :attr domain_k_exprs: a tuple of variable names of the Helmholtz
            parameter *k*, to be used inside each part of the source geometry.
            May also be a tuple of strings, which will be transformed into
            variable references of the corresponding names.
        :attr beta: A symbolic expression for the wave number in the :math:`z`
            direction. May be a string, which will be interpreted as a variable
            name.
        """

        if use_l2_weighting is None:
            use_l2_weighting = False

        super(Dielectric2DBoundaryOperatorBase, self).__init__(
                use_l2_weighting=use_l2_weighting)

        if mode == "te":
            self.ez_enabled = False
            self.hz_enabled = True
        elif mode == "tm":
            self.ez_enabled = True
            self.hz_enabled = False
        elif mode == "tem":
            self.ez_enabled = True
            self.hz_enabled = True
        else:
            raise ValueError("invalid mode '%s'" % mode)

        self.interfaces = interfaces

        fk_e = self.field_kind_e
        fk_h = self.field_kind_h

        dir_none = self.dir_none
        dir_normal = self.dir_normal
        dir_tangential = self.dir_tangential

        if isinstance(beta, str):
            beta = sym.var(beta)
        beta = sym.cse(beta, "beta")

        if isinstance(k_vacuum, str):
            k_vacuum = sym.var(k_vacuum)
        k_vacuum = sym.cse(k_vacuum, "k_vac")

        self.domain_k_exprs = [
                sym.var(k_expr)
                if isinstance(k_expr, str)
                else sym.cse(k_expr, "k%d" % idom)
                for idom, k_expr in enumerate(domain_k_exprs)]
        del domain_k_exprs

        # Note the case of k/K!
        # "K" is the 2D Helmholtz parameter.
        # "k" is the 3D Helmholtz parameter.

        self.domain_K_exprs = [
                sym.cse((k_expr**2-beta**2)**0.5, "K%d" % i)
                for i, k_expr in enumerate(self.domain_k_exprs)]

        from sumpy.kernel import HelmholtzKernel
        self.kernel = HelmholtzKernel(2, allow_evanescent=True)

        # {{{ build bc list

        # list of tuples, where each tuple consists of BCTermDescriptor instances

        all_bcs = []
        for i_interface, (outer_domain, inner_domain, _) in (
                enumerate(self.interfaces)):
            k_outer = self.domain_k_exprs[outer_domain]
            k_inner = self.domain_k_exprs[inner_domain]

            all_bcs += [
                    (  # [E] = 0
                        self.BCTermDescriptor(
                            i_interface=i_interface,
                            direction=dir_none,
                            field_kind=fk_e,
                            coeff_outer=1,
                            coeff_inner=-1),
                        ),
                    (  # [H] = 0
                        self.BCTermDescriptor(
                            i_interface=i_interface,
                            direction=dir_none,
                            field_kind=fk_h,
                            coeff_outer=1,
                            coeff_inner=-1),
                        ),
                    (
                        self.BCTermDescriptor(
                            i_interface=i_interface,
                            direction=dir_tangential,
                            field_kind=fk_e,
                            coeff_outer=beta/(k_outer**2-beta**2),
                            coeff_inner=-beta/(k_inner**2-beta**2)),
                        self.BCTermDescriptor(
                            i_interface=i_interface,
                            direction=dir_normal,
                            field_kind=fk_h,
                            coeff_outer=sym.cse(-k_vacuum/(k_outer**2-beta**2)),
                            coeff_inner=sym.cse(k_vacuum/(k_inner**2-beta**2))),
                        ),
                    (
                        self.BCTermDescriptor(
                            i_interface=i_interface,
                            direction=dir_tangential,
                            field_kind=fk_h,
                            coeff_outer=beta/(k_outer**2-beta**2),
                            coeff_inner=-beta/(k_inner**2-beta**2)),
                        self.BCTermDescriptor(
                            i_interface=i_interface,
                            direction=dir_normal,
                            field_kind=fk_e,
                            coeff_outer=sym.cse(
                                (k_outer**2/k_vacuum)/(k_outer**2-beta**2)),
                            coeff_inner=sym.cse(
                                -(k_inner**2/k_vacuum)
                                / (k_inner**2-beta**2)))
                        ),
                    ]

            del k_outer
            del k_inner

        self.bcs = []
        for bc in all_bcs:
            any_significant_e = any(
                    term.field_kind == fk_e
                    and term.direction in [dir_normal, dir_none]
                    for term in bc)
            any_significant_h = any(
                    term.field_kind == fk_h
                    and term.direction in [dir_normal, dir_none]
                    for term in bc)
            is_necessary = (
                    (self.ez_enabled and any_significant_e)
                    or (self.hz_enabled and any_significant_h))

            # Only keep tangential modes for TEM. Otherwise,
            # no jump in H already implies jump condition on
            # tangential derivative.
            is_tem = self.ez_enabled and self.hz_enabled
            terms = tuple(
                    term
                    for term in bc
                    if term.direction != dir_tangential
                    or is_tem)

            if is_necessary:
                self.bcs.append(terms)

        assert (len(all_bcs)
                * (int(self.ez_enabled) + int(self.hz_enabled)) // 2
                == len(self.bcs))