Exemple #1
0
    def op_template(self):
        from hedge.optemplate import (Field, BoundaryPair, get_flux_operator,
                                      make_stiffness_t, InverseMassOperator,
                                      BoundarizeOperator,
                                      QuadratureGridUpsampler,
                                      QuadratureInteriorFacesGridUpsampler)

        u = Field("u")

        to_quad = QuadratureGridUpsampler("quad")
        to_int_face_quad = QuadratureInteriorFacesGridUpsampler("quad")

        # boundary conditions -------------------------------------------------
        bc_in = Field("bc_in")
        bc_out = BoundarizeOperator(self.outflow_tag) * u

        stiff_t = make_stiffness_t(self.dimensions)
        m_inv = InverseMassOperator()

        flux_op = get_flux_operator(self.flux())

        return m_inv(
            numpy.dot(self.v, stiff_t * u) -
            (flux_op(u) + flux_op(BoundaryPair(u, bc_in, self.inflow_tag)) +
             flux_op(BoundaryPair(u, bc_out, self.outflow_tag))))
Exemple #2
0
        def make_optemplate():
            from hedge.optemplate.operators import QuadratureGridUpsampler
            from hedge.optemplate import Field, make_stiffness_t

            u = Field("u")
            qu = QuadratureGridUpsampler("quad")(u)

            return make_stiffness_t(2)[0](Field("intercept")(qu))
Exemple #3
0
    def op_template(self):
        from hedge.mesh import TAG_ALL
        from hedge.optemplate import Field, BoundaryPair, \
                make_nabla, InverseMassOperator, get_flux_operator

        u = Field("u")
        bc = Field("bc")

        nabla = make_nabla(self.dimensions)
        flux_op = get_flux_operator(self.flux())

        return nabla * u - InverseMassOperator()(
            flux_op(u) + flux_op(BoundaryPair(u, bc, TAG_ALL)))
Exemple #4
0
    def op_template(self):
        from hedge.optemplate import Field, BoundaryPair, \
                get_flux_operator, make_nabla, InverseMassOperator

        u = Field("u")
        bc_in = Field("bc_in")

        nabla = make_nabla(self.dimensions)
        m_inv = InverseMassOperator()

        flux_op = get_flux_operator(self.flux())

        return (-numpy.dot(self.v, nabla * u) + m_inv(
            flux_op(u) + flux_op(BoundaryPair(u, bc_in, self.inflow_tag))))
Exemple #5
0
        def make_diffusion(arg):
            if with_sensor or (
                    self.diffusion_coeff is not None and self.diffusion_coeff != 0):
                if self.diffusion_coeff is None:
                    diffusion_coeff = 0
                else:
                    diffusion_coeff = self.diffusion_coeff

                if with_sensor:
                    diffusion_coeff += Field("sensor")

                from hedge.second_order import SecondDerivativeTarget

                # strong_form here allows the reuse the value of grad u.
                grad_tgt = SecondDerivativeTarget(
                        self.dimensions, strong_form=True,
                        operand=arg)

                self.diffusion_scheme.grad(grad_tgt, bc_getter=None,
                        dirichlet_tags=[], neumann_tags=[])

                div_tgt = SecondDerivativeTarget(
                        self.dimensions, strong_form=False,
                        operand=diffusion_coeff*grad_tgt.minv_all)

                self.diffusion_scheme.div(div_tgt,
                        bc_getter=None,
                        dirichlet_tags=[], neumann_tags=[])

                return div_tgt.minv_all
            else:
                return 0
Exemple #6
0
    def bind_characteristic_velocity(self, discr):
        from hedge.optemplate import Field
        compiled = discr.compile(
            self.characteristic_velocity_optemplate(Field("u")))

        def do(u):
            return compiled(u=u)

        return do
Exemple #7
0
    def bind(self, discr):
        from hedge.optemplate import Field
        bound_op = discr.compile(self(Field("f")))

        def apply_op(field):
            from hedge.tools import with_object_array_or_scalar
            return with_object_array_or_scalar(lambda f: bound_op(f=f), field)

        return apply_op
Exemple #8
0
    def op_template(self, apply_minv, u=None, dir_bc=None, neu_bc=None):
        from hedge.optemplate import Field
        if u is None:
            u = Field("u")

        result = PoissonOperator.op_template(self, apply_minv, u, dir_bc,
                                             neu_bc)

        if apply_minv:
            return result + self.k**2 * u
        else:
            from hedge.optemplate import MassOperator
            return result + self.k**2 * MassOperator()(u)
Exemple #9
0
    def absorbing_bc(self, w=None):
        """Construct part of the flux operator template for 1st order
        absorbing boundary conditions.
        """

        from hedge.optemplate import normal
        absorb_normal = normal(self.absorb_tag, self.dimensions)

        from hedge.optemplate import BoundarizeOperator, Field
        from hedge.tools import join_fields

        e, h = self.split_eh(self.field_placeholder(w))

        if self.fixed_material:
            epsilon = self.epsilon
            mu = self.mu
        else:
            epsilon = cse(
                BoundarizeOperator(self.absorb_tag)(Field("epsilon")))
            mu = cse(BoundarizeOperator(self.absorb_tag)(Field("mu")))

        absorb_Z = (mu / epsilon)**0.5
        absorb_Y = 1 / absorb_Z

        absorb_e = BoundarizeOperator(self.absorb_tag)(e)
        absorb_h = BoundarizeOperator(self.absorb_tag)(h)

        bc = join_fields(
            absorb_e + 1 / 2 *
            (self.space_cross_h(absorb_normal,
                                self.space_cross_e(absorb_normal, absorb_e)) -
             absorb_Z * self.space_cross_h(absorb_normal, absorb_h)),
            absorb_h + 1 / 2 *
            (self.space_cross_e(absorb_normal,
                                self.space_cross_h(absorb_normal, absorb_h)) +
             absorb_Y * self.space_cross_e(absorb_normal, absorb_e)))

        return bc
def test_quadrature_tri_mass_mat_monomial():
    """Check that quadrature integration on triangles is exact as designed."""

    from hedge.mesh.generator import make_square_mesh

    mesh = make_square_mesh(a=-1, b=1, max_area=4 * 1 / 8 + 0.001)
    order = 4
    discr = discr_class(mesh,
                        order=order,
                        debug=discr_class.noninteractive_debug_flags(),
                        quad_min_degrees={"quad": 3 * order})

    m, n = 2, 1
    f = Monomial((m, n))
    f_vec = discr.interpolate_volume_function(lambda x, el: f(x))

    from hedge.discretization import ones_on_volume
    ones = ones_on_volume(discr)

    if False:
        from hedge.visualization import SiloVisualizer
        vis = SiloVisualizer(discr)
        visf = vis.make_file("test")
        vis.add_data(visf, [("f", f_vec * f_vec)])
        visf.close()

    from hedge.optemplate import (MassOperator, Field, QuadratureGridUpsampler)
    f_fld = Field("f")
    mass_op = discr.compile(MassOperator()(f_fld * f_fld))
    from hedge.optemplate.primitives import make_common_subexpression as cse
    f_upsamp = cse(QuadratureGridUpsampler("quad")(f_fld))
    quad_mass_op = discr.compile(MassOperator()(f_upsamp * f_upsamp))

    num_integral_1 = numpy.dot(ones, mass_op(f=f_vec))
    num_integral_2 = numpy.dot(ones, quad_mass_op(f=f_vec))
    true_integral = 4 / ((2 * m + 1) * (2 * n + 1))
    err_1 = abs(num_integral_1 - true_integral)
    err_2 = abs(num_integral_2 - true_integral)
    print num_integral_1, num_integral_2, true_integral
    print err_1, err_2
    assert err_1 > 1e-8
    assert err_2 < 1e-14
Exemple #11
0
    def op_template(self):
        from hedge.optemplate import \
                make_sym_vector, \
                BoundaryPair, \
                get_flux_operator, \
                make_nabla, \
                InverseMassOperator, \
                BoundarizeOperator

        d = self.dimensions

        w = make_sym_vector("w", d+1)
        u = w[0]
        v = w[1:]

        # boundary conditions -------------------------------------------------
        from hedge.tools import join_fields

        # dirichlet BCs -------------------------------------------------------
        from hedge.optemplate import normal, Field

        dir_u = BoundarizeOperator(self.dirichlet_tag) * u
        dir_v = BoundarizeOperator(self.dirichlet_tag) * v
        if self.dirichlet_bc_f:
            # FIXME
            from warnings import warn
            warn("Inhomogeneous Dirichlet conditions on the wave equation "
                    "are still having issues.")

            dir_g = Field("dir_bc_u")
            dir_bc = join_fields(2*dir_g - dir_u, dir_v)
        else:
            dir_bc = join_fields(-dir_u, dir_v)

        # neumann BCs ---------------------------------------------------------
        neu_u = BoundarizeOperator(self.neumann_tag) * u
        neu_v = BoundarizeOperator(self.neumann_tag) * v
        neu_bc = join_fields(neu_u, -neu_v)

        # radiation BCs -------------------------------------------------------
        rad_normal = normal(self.radiation_tag, d)

        rad_u = BoundarizeOperator(self.radiation_tag) * u
        rad_v = BoundarizeOperator(self.radiation_tag) * v

        rad_bc = join_fields(
                0.5*(rad_u - self.sign*np.dot(rad_normal, rad_v)),
                0.5*rad_normal*(np.dot(rad_normal, rad_v) - self.sign*rad_u)
                )

        # entire operator -----------------------------------------------------
        nabla = make_nabla(d)
        flux_op = get_flux_operator(self.flux())

        from hedge.tools import join_fields
        result = (
                - join_fields(
                    -self.c*np.dot(nabla, v),
                    -self.c*(nabla*u)
                    )
                +
                InverseMassOperator() * (
                    flux_op(w)
                    + flux_op(BoundaryPair(w, dir_bc, self.dirichlet_tag))
                    + flux_op(BoundaryPair(w, neu_bc, self.neumann_tag))
                    + flux_op(BoundaryPair(w, rad_bc, self.radiation_tag))
                    ))

        result[0] += self.source_f

        return result
def test_interior_fluxes_tet():
    """Check tetrahedron surface integrals computed using interior fluxes
    against their known values.
    """

    import meshpy.tet as tet
    from math import pi, sin, cos

    mesh_info = tet.MeshInfo()

    # construct a two-box extrusion of this base
    base = [(-pi, -pi, 0), (pi, -pi, 0), (pi, pi, 0), (-pi, pi, 0)]

    # first, the nodes
    mesh_info.set_points(base + [(x, y, z + pi)
                                 for x, y, z in base] + [(x, y, z + pi + 1)
                                                         for x, y, z in base])

    # next, the facets

    # vertex indices for a box missing the -z face
    box_without_minus_z = [
        [4, 5, 6, 7],
        [0, 4, 5, 1],
        [1, 5, 6, 2],
        [2, 6, 7, 3],
        [3, 7, 4, 0],
    ]

    def add_to_all_vertex_indices(facets, increment):
        return [[pt + increment for pt in facet] for facet in facets]

    mesh_info.set_facets(
        [[0, 1, 2, 3]]  # base
        + box_without_minus_z  # first box
        + add_to_all_vertex_indices(box_without_minus_z, 4)  # second box
    )

    # set the volume properties -- this is where the tet size constraints are
    mesh_info.regions.resize(2)
    mesh_info.regions[0] = [
        0,
        0,
        pi / 2,  # point in volume -> first box
        0,  # region tag (user-defined number)
        0.5,  # max tet volume in region
    ]
    mesh_info.regions[1] = [
        0,
        0,
        pi + 0.5,  # point in volume -> second box
        1,  # region tag (user-defined number,  arbitrary)
        0.1,  # max tet volume in region
    ]

    generated_mesh = tet.build(mesh_info,
                               attributes=True,
                               volume_constraints=True)

    from hedge.mesh import make_conformal_mesh
    mesh = make_conformal_mesh(generated_mesh.points, generated_mesh.elements)

    from hedge.discretization.local import TetrahedronDiscretization
    from hedge.discretization import ones_on_volume
    discr = discr_class(mesh,
                        TetrahedronDiscretization(4),
                        debug=discr_class.noninteractive_debug_flags())

    def f_u(x, el):
        if generated_mesh.element_attributes[el.id] == 1:
            return cos(x[0] - x[1] + x[2])
        else:
            return 0

    def f_l(x, el):
        if generated_mesh.element_attributes[el.id] == 0:
            return sin(x[0] - x[1] + x[2])
        else:
            return 0

    u_l = discr.interpolate_volume_function(f_l)
    u_u = discr.interpolate_volume_function(f_u)
    u = u_l + u_u

    # visualize the produced field
    #from hedge.visualization import SiloVisualizer
    #vis = SiloVisualizer(discr)
    #visf = vis.make_file("sandwich")
    #vis.add_data(visf,
    #[("u_l", u_l), ("u_u", u_u)],
    #expressions=[("u", "u_l+u_u")])

    # make sure the surface integral of the difference
    # between top and bottom is zero
    from hedge.flux import make_normal, FluxScalarPlaceholder
    from hedge.optemplate import Field, get_flux_operator

    fluxu = FluxScalarPlaceholder()
    res = discr.compile(
        get_flux_operator(
            (fluxu.int - fluxu.ext) * make_normal(discr.dimensions)[1]) *
        Field("u"))(u=u)

    ones = ones_on_volume(discr)
    assert abs(numpy.dot(res, ones)) < 5e-14
def test_2d_gauss_theorem():
    """Verify Gauss's theorem explicitly on a mesh"""

    from hedge.mesh.generator import make_disk_mesh
    from math import sin, cos
    from numpy import dot

    mesh = make_disk_mesh()
    order = 2

    discr = discr_class(mesh,
                        order=order,
                        debug=discr_class.noninteractive_debug_flags())
    ref_discr = discr_class(mesh, order=order)

    from hedge.flux import make_normal, FluxScalarPlaceholder

    normal = make_normal(discr.dimensions)
    flux_f_ph = FluxScalarPlaceholder(0)
    one_sided_x = flux_f_ph.int * normal[0]
    one_sided_y = flux_f_ph.int * normal[1]

    def f1(x, el):
        return sin(3 * x[0]) + cos(3 * x[1])

    def f2(x, el):
        return sin(2 * x[0]) + cos(x[1])

    from hedge.discretization import ones_on_volume
    ones = ones_on_volume(discr)
    f1_v = discr.interpolate_volume_function(f1)
    f2_v = discr.interpolate_volume_function(f2)

    from hedge.optemplate import BoundaryPair, Field, make_nabla, \
            get_flux_operator
    nabla = make_nabla(discr.dimensions)

    divergence = nabla[0].apply(discr, f1_v) + nabla[1].apply(discr, f2_v)
    int_div = discr.integral(divergence)

    flux_optp = (
        get_flux_operator(one_sided_x)(BoundaryPair(Field("f1"),
                                                    Field("fz"))) +
        get_flux_operator(one_sided_y)(BoundaryPair(Field("f2"), Field("fz"))))

    from hedge.mesh import TAG_ALL
    bdry_val = discr.compile(flux_optp)(f1=f1_v,
                                        f2=f2_v,
                                        fz=discr.boundary_zeros(TAG_ALL))
    ref_bdry_val = ref_discr.compile(flux_optp)(
        f1=f1_v, f2=f2_v, fz=discr.boundary_zeros(TAG_ALL))

    boundary_int = dot(bdry_val, ones)

    if False:
        from hedge.visualization import SiloVisualizer
        vis = SiloVisualizer(discr)
        visf = vis.make_file("test")

        from hedge.tools import make_obj_array
        from hedge.mesh import TAG_ALL
        vis.add_data(visf, [
            ("bdry", bdry_val),
            ("ref_bdry", ref_bdry_val),
            ("div", divergence),
            ("f", make_obj_array([f1_v, f2_v])),
            ("n",
             discr.volumize_boundary_field(discr.boundary_normals(TAG_ALL),
                                           TAG_ALL)),
        ],
                     expressions=[("bdiff", "bdry-ref_bdry")])

        #print abs(boundary_int-int_div)

    assert abs(boundary_int - int_div) < 5e-15
Exemple #14
0
    def op_template(self, with_sensor):
        from hedge.optemplate import (Field, make_stiffness_t, make_nabla,
                                      InverseMassOperator,
                                      ElementwiseMaxOperator,
                                      get_flux_operator)

        from hedge.optemplate.operators import (
            QuadratureGridUpsampler, QuadratureInteriorFacesGridUpsampler)

        to_quad = QuadratureGridUpsampler("quad")
        to_if_quad = QuadratureInteriorFacesGridUpsampler("quad")

        u = Field("u")
        u0 = Field("u0")

        # boundary conditions -------------------------------------------------
        minv_st = make_stiffness_t(self.dimensions)
        nabla = make_nabla(self.dimensions)
        m_inv = InverseMassOperator()

        def flux(u):
            return u**2 / 2
            #return u0*u

        emax_u = self.characteristic_velocity_optemplate(u)
        from hedge.flux.tools import make_lax_friedrichs_flux
        from pytools.obj_array import make_obj_array
        num_flux = make_lax_friedrichs_flux(
            #u0,
            to_if_quad(emax_u),
            make_obj_array([to_if_quad(u)]),
            [make_obj_array([flux(to_if_quad(u))])],
            [],
            strong=False)[0]

        from hedge.second_order import SecondDerivativeTarget

        if self.viscosity is not None or with_sensor:
            viscosity_coeff = 0
            if with_sensor:
                viscosity_coeff += Field("sensor")

            if isinstance(self.viscosity, float):
                viscosity_coeff += self.viscosity
            elif self.viscosity is None:
                pass
            else:
                raise TypeError("unsupported type of viscosity coefficient")

            # strong_form here allows IPDG to reuse the value of grad u.
            grad_tgt = SecondDerivativeTarget(self.dimensions,
                                              strong_form=True,
                                              operand=u)

            self.viscosity_scheme.grad(grad_tgt,
                                       bc_getter=None,
                                       dirichlet_tags=[],
                                       neumann_tags=[])

            div_tgt = SecondDerivativeTarget(self.dimensions,
                                             strong_form=False,
                                             operand=viscosity_coeff *
                                             grad_tgt.minv_all)

            self.viscosity_scheme.div(div_tgt,
                                      bc_getter=None,
                                      dirichlet_tags=[],
                                      neumann_tags=[])

            viscosity_bit = div_tgt.minv_all
        else:
            viscosity_bit = 0

        return m_inv((minv_st[0](flux(to_quad(u)))) - num_flux) \
                + viscosity_bit
Exemple #15
0
    def op_template(self, apply_minv, u=None, dir_bc=None, neu_bc=None):
        """
        :param apply_minv: :class:`bool` specifying whether to compute a complete
          divergence operator. If False, the final application of the inverse
          mass operator is skipped. This is used in :meth:`op` in order to
          reduce the scheme :math:`M^{-1} S u = f` to :math:`S u = M f`, so
          that the mass operator only needs to be applied once, when preparing
          the right hand side in :meth:`prepare_rhs`.

          :class:`hedge.models.diffusion.DiffusionOperator` needs this.
        """

        from hedge.optemplate import Field, make_sym_vector
        from hedge.second_order import SecondDerivativeTarget

        if u is None:
            u = Field("u")
        if dir_bc is None:
            dir_bc = Field("dir_bc")
        if neu_bc is None:
            neu_bc = Field("neu_bc")

        # strong_form here allows IPDG to reuse the value of grad u.
        grad_tgt = SecondDerivativeTarget(self.dimensions,
                                          strong_form=True,
                                          operand=u)

        def grad_bc_getter(tag, expr):
            assert tag == self.dirichlet_tag
            return dir_bc

        self.scheme.grad(grad_tgt,
                         bc_getter=grad_bc_getter,
                         dirichlet_tags=[self.dirichlet_tag],
                         neumann_tags=[self.neumann_tag])

        def apply_diff_tensor(v):
            if isinstance(self.diffusion_tensor, np.ndarray):
                sym_diff_tensor = self.diffusion_tensor
            else:
                sym_diff_tensor = (make_sym_vector("diffusion",
                                                   self.dimensions**2).reshape(
                                                       self.dimensions,
                                                       self.dimensions))

            return np.dot(sym_diff_tensor, v)

        div_tgt = SecondDerivativeTarget(self.dimensions,
                                         strong_form=False,
                                         operand=apply_diff_tensor(
                                             grad_tgt.minv_all))

        def div_bc_getter(tag, expr):
            if tag == self.dirichlet_tag:
                return dir_bc
            elif tag == self.neumann_tag:
                return neu_bc
            else:
                assert False, "divergence bc getter " \
                        "asked for '%s' BC for '%s'" % (tag, expr)

        self.scheme.div(div_tgt,
                        div_bc_getter,
                        dirichlet_tags=[self.dirichlet_tag],
                        neumann_tags=[self.neumann_tag])

        if apply_minv:
            return div_tgt.minv_all
        else:
            return div_tgt.all
Exemple #16
0
    def op_template(self, with_sensor=False):
        # {{{ operator preliminaries ------------------------------------------
        from hedge.optemplate import (Field, BoundaryPair, get_flux_operator,
                                      make_stiffness_t, InverseMassOperator,
                                      make_sym_vector, ElementwiseMaxOperator,
                                      BoundarizeOperator)

        from hedge.optemplate.primitives import make_common_subexpression as cse

        from hedge.optemplate.operators import (
            QuadratureGridUpsampler, QuadratureInteriorFacesGridUpsampler)

        to_quad = QuadratureGridUpsampler("quad")
        to_if_quad = QuadratureInteriorFacesGridUpsampler("quad")

        from hedge.tools import join_fields, \
                                ptwise_dot

        u = Field("u")
        v = make_sym_vector("v", self.dimensions)
        c = ElementwiseMaxOperator()(ptwise_dot(1, 1, v, v))

        quad_u = cse(to_quad(u))
        quad_v = cse(to_quad(v))

        w = join_fields(u, v, c)
        quad_face_w = to_if_quad(w)
        # }}}

        # {{{ boundary conditions ---------------------------------------------

        from hedge.mesh import TAG_ALL
        bc_c = to_quad(BoundarizeOperator(TAG_ALL)(c))
        bc_u = to_quad(Field("bc_u"))
        bc_v = to_quad(BoundarizeOperator(TAG_ALL)(v))

        if self.bc_u_f is "None":
            bc_w = join_fields(0, bc_v, bc_c)
        else:
            bc_w = join_fields(bc_u, bc_v, bc_c)

        minv_st = make_stiffness_t(self.dimensions)
        m_inv = InverseMassOperator()

        flux_op = get_flux_operator(self.flux())
        # }}}

        # {{{ diffusion -------------------------------------------------------
        if with_sensor or (self.diffusion_coeff is not None
                           and self.diffusion_coeff != 0):
            if self.diffusion_coeff is None:
                diffusion_coeff = 0
            else:
                diffusion_coeff = self.diffusion_coeff

            if with_sensor:
                diffusion_coeff += Field("sensor")

            from hedge.second_order import SecondDerivativeTarget

            # strong_form here allows IPDG to reuse the value of grad u.
            grad_tgt = SecondDerivativeTarget(self.dimensions,
                                              strong_form=True,
                                              operand=u)

            self.diffusion_scheme.grad(grad_tgt,
                                       bc_getter=None,
                                       dirichlet_tags=[],
                                       neumann_tags=[])

            div_tgt = SecondDerivativeTarget(self.dimensions,
                                             strong_form=False,
                                             operand=diffusion_coeff *
                                             grad_tgt.minv_all)

            self.diffusion_scheme.div(div_tgt,
                                      bc_getter=None,
                                      dirichlet_tags=[],
                                      neumann_tags=[])

            diffusion_part = div_tgt.minv_all
        else:
            diffusion_part = 0

        # }}}

        to_quad = QuadratureGridUpsampler("quad")
        quad_u = cse(to_quad(u))
        quad_v = cse(to_quad(v))

        return m_inv(numpy.dot(minv_st, cse(quad_v*quad_u))
                - (flux_op(quad_face_w)
                    + flux_op(BoundaryPair(quad_face_w, bc_w, TAG_ALL)))) \
                            + diffusion_part
Exemple #17
0
    def op_template(self):
        from hedge.tools import join_fields
        from hedge.optemplate import Field, make_vector_field, BoundaryPair, \
                BoundarizeOperator, make_normal, get_flux_operator, \
                make_nabla, InverseMassOperator

        w = make_vector_field("w", self.component_count)
        e, h, phi = self.split_ehphi(w)
        rho = Field("rho")

        # local part ----------------------------------------------------------
        nabla = make_nabla(self.maxwell_op.dimensions)

        # in conservation form: u_t + A u_x = 0
        # we're describing the A u_x part, the sign gets reversed
        # below.
        max_local_op = join_fields(self.maxwell_op.local_derivatives(w), 0)

        c = self.maxwell_op.c
        chi = self.chi

        hyp_local_operator = max_local_op + join_fields(
            c * chi * (nabla * phi),
            0 * h,
            c * chi * numpy.dot(nabla, e) -
            c * chi * rho / self.maxwell_op.epsilon

            # sign gets reversed below, so this is actually
            # the decay it advertises to be.
            + self.phi_decay * phi)

        # BCs -----------------------------------------------------------------
        pec_tag = self.maxwell_op.pec_tag

        pec_e = BoundarizeOperator(pec_tag)(e)
        pec_h = BoundarizeOperator(pec_tag)(h)
        pec_phi = BoundarizeOperator(pec_tag)(phi)
        pec_n = make_normal(pec_tag, self.maxwell_op.dimensions)

        bc = "prev"
        print "HYP CLEAN BC", bc
        if bc == "char":
            # see hedge/doc/maxima/eclean.mac for derivation
            pec_bc = join_fields(
                -pec_e + 3 / 2 * pec_n * numpy.dot(pec_n, pec_e) +
                1 / 2 * pec_phi * pec_n, pec_h,
                1 / 2 * (pec_phi + numpy.dot(pec_n, pec_e)))
        elif bc == "invent":
            # see hedge/doc/maxima/eclean.mac for derivation
            pec_bc = join_fields(-pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e),
                                 pec_h, pec_phi)
        elif bc == "munz":
            # Munz et al
            pec_bc = join_fields(-pec_e, pec_h,
                                 pec_phi - numpy.dot(pec_n, pec_e))
        elif bc == "prev":
            # previous condition
            pec_bc = join_fields(-pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e),
                                 pec_h, -pec_phi)

        # assemble operator ---------------------------------------------------
        flux_op = get_flux_operator(self.flux())
        return -hyp_local_operator + InverseMassOperator()(
            flux_op(w) + flux_op(BoundaryPair(w, pec_bc, pec_tag)))
def test_interior_fluxes_tri():
    """Check triangle surface integrals computed using interior fluxes
    against their known values.
    """

    from math import pi, sin, cos

    def round_trip_connect(start, end):
        for i in range(start, end):
            yield i, i + 1
        yield end, start

    a = -pi
    b = pi
    points = [(a, 0), (b, 0), (a, -1), (b, -1), (a, 1), (b, 1)]

    import meshpy.triangle as triangle

    mesh_info = triangle.MeshInfo()
    mesh_info.set_points(points)
    mesh_info.set_facets([(0, 1), (1, 3), (3, 2), (2, 0), (0, 4), (4, 5),
                          (1, 5)])

    mesh_info.regions.resize(2)
    mesh_info.regions[0] = [
        0,
        -0.5,  # coordinate
        1,  # lower element tag
        0.1,  # max area
    ]
    mesh_info.regions[1] = [
        0,
        0.5,  # coordinate
        2,  # upper element tag
        0.01,  # max area
    ]

    generated_mesh = triangle.build(mesh_info,
                                    attributes=True,
                                    volume_constraints=True)

    #triangle.write_gnuplot_mesh("mesh.dat", generated_mesh)

    def element_tagger(el):
        if generated_mesh.element_attributes[el.id] == 1:
            return ["upper"]
        else:
            return ["lower"]

    from hedge.mesh import make_conformal_mesh
    mesh = make_conformal_mesh(generated_mesh.points, generated_mesh.elements)

    from hedge.discretization.local import TriangleDiscretization
    from hedge.discretization import ones_on_volume
    discr = discr_class(mesh,
                        TriangleDiscretization(4),
                        debug=discr_class.noninteractive_debug_flags())

    def f_u(x, el):
        if generated_mesh.element_attributes[el.id] == 1:
            return cos(x[0] - x[1])
        else:
            return 0

    def f_l(x, el):
        if generated_mesh.element_attributes[el.id] == 0:
            return sin(x[0] - x[1])
        else:
            return 0

    # u_l = discr.interpolate_volume_function(f_l)
    u_u = discr.interpolate_volume_function(f_u)
    u = u_u + u_u

    #discr.visualize_vtk("dual.vtk", [("u", u)])

    from hedge.flux import make_normal, FluxScalarPlaceholder
    from hedge.optemplate import Field, get_flux_operator
    fluxu = FluxScalarPlaceholder()
    res = discr.compile(
        get_flux_operator(
            (fluxu.int - fluxu.ext) * make_normal(discr.dimensions)[1]) *
        Field("u"))(u=u)

    ones = ones_on_volume(discr)
    err = abs(numpy.dot(res, ones))
    #print err
    assert err < 5e-14