def flux_num(self, q, fluxes, bdry_tag_state_flux):
        n = self.len_q
        d = len(fluxes)
        fvph = FluxVectorPlaceholder(n*(d+1)+1)
        speed_ph = fvph[0]
        state_ph = fvph[1:n+1]
        fluxes_ph = [fvph[i*n+1:(i+1)*n+1] for i in range(1,d+1)]
        normal = make_normal(d)

        flux_strong = 0.5*sum(n_i*(f_i.ext-f_i.int) for n_i, f_i in zip(normal, fluxes_ph))

        if self.flux_type == "central":
            pass
        elif self.flux_type == "lf":
            penalty = flux_max(speed_ph.int,speed_ph.ext)*(state_ph.ext-state_ph.int)
            flux_strong = 0.5 * penalty + flux_strong
        else:
            raise ValueError("Invalid flux type '%s'" % self.flux_type)

        flux_op = get_flux_operator(flux_strong)
        int_operand = join_fields(self.wave_speed(q), q, *fluxes)

        return (flux_op(int_operand)
                +sum(flux_op(BoundaryPair(int_operand, join_fields(0, bdry_state, *bdry_fluxes), tag))
                     for tag, bdry_state, bdry_fluxes in bdry_tag_state_flux))
Example #2
0
    def add_div_bcs(self, tgt, bc_getter, dirichlet_tags, neumann_tags,
                    stab_term, adjust_flux, flux_v, flux_arg_int,
                    grad_flux_arg_count):
        from pytools.obj_array import make_obj_array, join_fields
        n_times = tgt.normal_times_flux

        def unwrap_cse(expr):
            from pymbolic.primitives import CommonSubexpression
            if isinstance(expr, CommonSubexpression):
                return expr.child
            else:
                return expr

        for tag in dirichlet_tags:
            dir_bc_w = join_fields([0] * grad_flux_arg_count, [
                bc_getter(tag, unwrap_cse(vol_expr))
                for vol_expr in flux_arg_int[grad_flux_arg_count:]
            ])
            tgt.add_boundary_flux(adjust_flux(n_times(flux_v.int - stab_term)),
                                  flux_arg_int, dir_bc_w, tag)

        loc_bc_vec = make_obj_array([0] * len(flux_arg_int))

        for tag in neumann_tags:
            neu_bc_w = join_fields(
                NeumannBCGenerator(tag, bc_getter(tag, None))(tgt.operand),
                [0] * len(flux_arg_int))

            tgt.add_boundary_flux(adjust_flux(n_times(flux_v.ext)), loc_bc_vec,
                                  neu_bc_w, tag)
Example #3
0
def make_lax_friedrichs_flux(wave_speed, state, fluxes, bdry_tags_states_and_fluxes,
        strong):
    from pytools.obj_array import join_fields
    from hedge.flux import make_normal, FluxVectorPlaceholder, flux_max

    n = len(state)
    d = len(fluxes)
    normal = make_normal(d)
    fvph = FluxVectorPlaceholder(len(state)*(1+d)+1)

    wave_speed_ph = fvph[0]
    state_ph = fvph[1:1+n]
    fluxes_ph = [fvph[1+i*n:1+(i+1)*n] for i in range(1, d+1)]

    penalty = flux_max(wave_speed_ph.int,wave_speed_ph.ext)*(state_ph.ext-state_ph.int)

    if not strong:
        num_flux = 0.5*(sum(n_i*(f_i.int+f_i.ext) for n_i, f_i in zip(normal, fluxes_ph))
                - penalty)
    else:
        num_flux = 0.5*(sum(n_i*(f_i.int-f_i.ext) for n_i, f_i in zip(normal, fluxes_ph))
                + penalty)

    from hedge.optemplate import get_flux_operator
    flux_op = get_flux_operator(num_flux)
    int_operand = join_fields(wave_speed, state, *fluxes)

    from hedge.optemplate import BoundaryPair
    return (flux_op(int_operand)
            + sum(
                flux_op(BoundaryPair(int_operand,
                    join_fields(0, bdry_state, *bdry_fluxes), tag))
                for tag, bdry_state, bdry_fluxes in bdry_tags_states_and_fluxes))
Example #4
0
    def add_div_bcs(self, tgt, bc_getter, dirichlet_tags, neumann_tags,
            stab_term, adjust_flux, flux_v, flux_arg_int,
            grad_flux_arg_count):
        from pytools.obj_array import make_obj_array, join_fields
        n_times = tgt.normal_times_flux

        def unwrap_cse(expr):
            from pymbolic.primitives import CommonSubexpression
            if isinstance(expr, CommonSubexpression):
                return expr.child
            else:
                return expr

        for tag in dirichlet_tags:
            dir_bc_w = join_fields(
                    [0]*grad_flux_arg_count,
                    [bc_getter(tag, unwrap_cse(vol_expr)) for vol_expr in
                        flux_arg_int[grad_flux_arg_count:]])
            tgt.add_boundary_flux(
                    adjust_flux(n_times(flux_v.int-stab_term)),
                    flux_arg_int, dir_bc_w, tag)

        loc_bc_vec = make_obj_array([0]*len(flux_arg_int))

        for tag in neumann_tags:
            neu_bc_w = join_fields(
                    NeumannBCGenerator(tag, bc_getter(tag, None))(tgt.operand),
                    [0]*len(flux_arg_int))

            tgt.add_boundary_flux(
                    adjust_flux(n_times(flux_v.ext)),
                    loc_bc_vec, neu_bc_w, tag)
    def op_template(self):
        dim = self.dimensions
        q = make_vector_field('q', self.len_q)
        f2 = make_vector_field('f2', self.len_f2)
        w = join_fields(q, f2)
        dim_subset = (True,) * dim + (False,) * (3-dim)

        def pad_vec(v, subset):
            result = numpy.zeros((3,), dtype=object)
            result[numpy.array(subset, dtype=bool)] = v
            return result

        sigma = pad_vec(make_vector_field('sigma', dim),dim_subset)
        alpha = pad_vec(make_vector_field('alpha', dim),dim_subset)
        kappa = pad_vec(make_vector_field('kappa', dim),dim_subset)

        fluxes = self.flux(w, kappa)

        # Boundary conditions
        q_bc_stressfree = BoundarizeOperator(self.boundaryconditions_tag['stressfree'])(q)
        q_bc_stressfree_null = BoundarizeOperator(self.boundaryconditions_tag['stressfree'])(0)
        q_bc_fixed = BoundarizeOperator(self.boundaryconditions_tag['fixed'])(q)
        q_bc_fixed_null = BoundarizeOperator(self.boundaryconditions_tag['fixed'])(0)

        all_tags_and_bcs = [
                (self.boundaryconditions_tag['stressfree'], q_bc_stressfree, q_bc_stressfree_null),
                (self.boundaryconditions_tag['fixed'], q_bc_fixed, q_bc_fixed_null)
                           ]

        bdry_tag_state_flux = [(tag, bc, self.bdry_flux(bc, bc_null, tag))
                               for tag, bc, bc_null in all_tags_and_bcs]

        # Entire operator
        nabla = make_nabla(dim)
        res_q = (numpy.dot(nabla, fluxes) + InverseMassOperator()
                 * (self.flux_num(q, fluxes, bdry_tag_state_flux)))
        res_q = self.add_sources(res_q)

        F2 = self.F2(w)
        P = self.P(q)
        v = self.v(q)
        if dim == 1:
            F = [P[0],v[0]]
        elif dim == 2:
            F = [P[0],P[2],v[0],v[1],
                 P[2],P[1],v[1],v[0]]
        elif dim == 3:
            F = [P[0],P[5],P[4],v[0],v[2],v[1],
                 P[5],P[1],P[3],v[1],v[2],v[0],
                 P[4],P[3],P[2],v[2],v[1],v[0]]
        else:
            raise ValueError("Invalid dimension")

        res_f2 = numpy.zeros(self.len_f2, dtype=object)
        for i in range(dim):
            for j in range(dim*2):
                res_f2[i*dim*2+j] = (-1)*F2[i*dim*2+j]*alpha[i]-sigma[i]/kappa[i]*(F2[i*dim*2+j]+F[i*dim*2+j])

        return join_fields(res_q, res_f2)
Example #6
0
    def sym_operator(self):
        d = self.ambient_dim

        w = sym.make_sym_array("w", d + 1)
        u = w[0]
        v = w[1:]

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

        # dirichlet BCs -------------------------------------------------------
        dir_u = sym.cse(sym.interp("vol", self.dirichlet_tag)(u))
        dir_v = sym.cse(sym.interp("vol", 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 = sym.Field("dir_bc_u")
            dir_bc = join_fields(2 * dir_g - dir_u, dir_v)
        else:
            dir_bc = join_fields(-dir_u, dir_v)

        dir_bc = sym.cse(dir_bc, "dir_bc")

        # neumann BCs ---------------------------------------------------------
        neu_u = sym.cse(sym.interp("vol", self.neumann_tag)(u))
        neu_v = sym.cse(sym.interp("vol", self.neumann_tag)(v))
        neu_bc = sym.cse(join_fields(neu_u, -neu_v), "neu_bc")

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

        rad_u = sym.cse(sym.interp("vol", self.radiation_tag)(u))
        rad_v = sym.cse(sym.interp("vol", self.radiation_tag)(v))

        rad_bc = sym.cse(
            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)),
            "rad_bc")

        # entire operator -----------------------------------------------------
        def flux(pair):
            return sym.interp(pair.dd, "all_faces")(self.flux(pair))

        result = sym.InverseMassOperator()(
            join_fields(-self.c *
                        np.dot(sym.stiffness_t(self.ambient_dim), v), -self.c *
                        (sym.stiffness_t(self.ambient_dim) * u)) -
            sym.FaceMassOperator()
            (flux(sym.int_tpair(w)) +
             flux(sym.bv_tpair(self.dirichlet_tag, w, dir_bc)) +
             flux(sym.bv_tpair(self.neumann_tag, w, neu_bc)) +
             flux(sym.bv_tpair(self.radiation_tag, w, rad_bc))))

        result[0] += self.source_f

        return result
Example #7
0
    def flux(self, w):
        """The template for the numerical flux for variable coefficients.

        :param flux_type: can be in [0,1] for anything between central and upwind,
          or "lf" for Lax-Friedrichs.

        As per Hesthaven and Warburton page 433.
        """

        normal = sym.normal(w.dd, self.dimensions)

        if self.fixed_material:
            e, h = self.split_eh(w)
            epsilon = self.epsilon
            mu = self.mu

        Z_int = (mu/epsilon)**0.5  # noqa: N806
        Y_int = 1/Z_int  # noqa: N806
        Z_ext = (mu/epsilon)**0.5  # noqa: N806
        Y_ext = 1/Z_ext  # noqa: N806

        if self.flux_type == "lf":
            # if self.fixed_material:
            #     max_c = (self.epsilon*self.mu)**(-0.5)

            return join_fields(
                    # flux e,
                    1/2*(
                        -self.space_cross_h(normal, h.int-h.ext)
                        # multiplication by epsilon undoes material divisor below
                        #-max_c*(epsilon*e.int - epsilon*e.ext)
                    ),
                    # flux h
                    1/2*(
                        self.space_cross_e(normal, e.int-e.ext)
                        # multiplication by mu undoes material divisor below
                        #-max_c*(mu*h.int - mu*h.ext)
                    ))
        elif isinstance(self.flux_type, (int, float)):
            # see doc/maxima/maxwell.mac
            return join_fields(
                    # flux e,
                    (
                        -1/(Z_int+Z_ext)*self.space_cross_h(normal,
                            Z_ext*(h.int-h.ext)
                            - self.flux_type*self.space_cross_e(normal, e.int-e.ext))
                        ),
                    # flux h
                    (
                        1/(Y_int + Y_ext)*self.space_cross_e(normal,
                            Y_ext*(e.int-e.ext)
                            + self.flux_type*self.space_cross_h(normal, h.int-h.ext))
                        ),
                    )
        else:
            raise ValueError("maxwell: invalid flux_type (%s)"
                    % self.flux_type)
Example #8
0
    def __call__(self, x, y, three_mult=None):
        """Compute the subsetted cross product on the indexables *x* and *y*.

        :param three_mult: a function of three arguments *sign, xj, yk*
          used in place of the product *sign*xj*yk*. Defaults to just this
          product if not given.
        """
        from pytools.obj_array import join_fields
        if three_mult is None:
            return join_fields(*[f(x, y) for f in self.functions])
        else:
            return join_fields(
                    *[sum(three_mult(lc, x[j], y[k]) for lc, j, k in lcjk)
                    for lcjk in self.component_lcjk])
Example #9
0
 def __call__(self, x, y, three_mult=None):
     """Compute the subsetted cross product on the indexables *x* and *y*.
     :param three_mult: a function of three arguments *sign, xj, yk*
       used in place of the product *sign*xj*yk*. Defaults to just this
       product if not given.
     """
     from pytools.obj_array import join_fields
     if three_mult is None:
         return join_fields(*[f(x, y) for f in self.functions])
     else:
         return join_fields(*[
             sum(three_mult(lc, x[j], y[k]) for lc, j, k in lcjk)
             for lcjk in self.component_lcjk
         ])
Example #10
0
    def flux(self, w):
        u = w[0]
        v = w[1:]
        normal = sym.normal(w.dd, self.ambient_dim)

        flux_weak = join_fields(np.dot(v.avg, normal), u.avg * normal)

        if self.flux_type == "central":
            return -self.c * flux_weak
        elif self.flux_type == "upwind":
            return -self.c * (flux_weak + self.sign * join_fields(
                0.5 * (u.int - u.ext), 0.5 *
                (normal * np.dot(normal, v.int - v.ext))))
        else:
            raise ValueError("invalid flux type '%s'" % self.flux_type)
Example #11
0
 def noslip_state(self, state):
     from hedge.optemplate import make_normal
     state0 = join_fields(make_sym_vector("bc_q_noslip", 2),
                          [0] * self.dimensions)
     normal = make_normal(self.noslip_tag, self.dimensions)
     bc = self.make_bc_info("bc_q_noslip", self.noslip_tag, state, state0)
     return self.inflow_state_inner(normal, bc, "noslip")
Example #12
0
    def absorbing_bc(self, w):
        """Construct part of the flux operator template for 1st order
        absorbing boundary conditions.
        """

        absorb_normal = sym.normal(self.absorb_tag, self.dimensions)

        e, h = self.split_eh(w)

        if self.fixed_material:
            epsilon = self.epsilon
            mu = self.mu

        absorb_Z = (mu/epsilon)**0.5  # noqa: N806
        absorb_Y = 1/absorb_Z  # noqa: N806

        absorb_e = sym.cse(sym.interp("vol", self.absorb_tag)(e))
        absorb_h = sym.cse(sym.interp("vol", 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
Example #13
0
def get_spherical_coord(x_vec):
    """
    :param x_vec: is an array whose leading dimension iterates over
        the X, Y, Z axes, and whose further dimensions may iterate over
        a number of points.

    :returns: object array of [r, phi, theta].
        phi is the angle in (x,y) in :math:`(-\\pi,\\pi)`.
    """

    if len(x_vec) != 3:
        raise ValueError("only 3-d arrays are supported")

    x = x_vec[0]
    y = x_vec[1]
    z = x_vec[2]

    r = numpy.sqrt(x**2 + y**2 + z**2)

    from warnings import warn
    if (numpy.any(r < numpy.power(10.0, -10.0))):
        warn('spherical coordinate transformation ill-defined at r=0')

    phi = numpy.arctan2(y, x)
    theta = numpy.arccos(z / r)

    from pytools.obj_array import join_fields
    return join_fields(r, phi, theta)
Example #14
0
    def integral_equation(self, r_tilde, q_tilde, hvf_coefficients):
        fix = 0

        if self.invertible:
            s_ones = cse(S(0, Ones()), "s_ones")

            def inv_rank_one_coeff(u):
                return cse(Mean(cse(S(0, cse(S(0, u))))))

            r_coeff = inv_rank_one_coeff(r_tilde)
            q_coeff = inv_rank_one_coeff(q_tilde)

            from pytools.obj_array import join_fields
            factors = self.cluster_points()

            fix = join_fields(
                factors[0] * s_ones * (r_coeff),
                factors[1] * Ones() * (q_coeff),
            )

        return DebyeOperatorBase.integral_equation(self,
                                                   r_tilde,
                                                   q_tilde,
                                                   hvf_coefficients,
                                                   fix=fix)
Example #15
0
def get_spherical_coord(x_vec):
    """
    :param x_vec: is an array whose leading dimension iterates over
        the X, Y, Z axes, and whose further dimensions may iterate over
        a number of points.

    :returns: object array of [r, phi, theta].
        phi is the angle in (x,y) in :math:`(-\\pi,\\pi)`.
    """

    if len(x_vec) != 3:
        raise ValueError("only 3-d arrays are supported")

    x = x_vec[0]
    y = x_vec[1]
    z = x_vec[2]

    r = numpy.sqrt(x**2+y**2+z**2)

    from warnings import warn
    if(numpy.any(r<numpy.power(10.0,-10.0))):
        warn('spherical coordinate transformation ill-defined at r=0')

    phi = numpy.arctan2(y,x)
    theta = numpy.arccos(z/r)

    from pytools.obj_array import join_fields
    return join_fields(r,phi,theta)
Example #16
0
    def set_up_stepper(self,
                       discr,
                       field_var_name,
                       sym_rhs,
                       num_fields,
                       function_registry=base_function_registry,
                       exec_mapper_factory=ExecutionMapper):
        dt_method = LSRK4MethodBuilder(component_id=field_var_name)
        dt_code = dt_method.generate()
        self.field_var_name = field_var_name
        self.state_name = f"input_{field_var_name}"

        # Transcribe the phase.
        output_vars, results, yielded_states = transcribe_phase(
            dt_code, field_var_name, num_fields, "primary", sym_rhs)

        # Build the bound operator for the time integrator.
        output_t = results[0]
        output_dt = results[1]
        output_states = results[2]
        output_residuals = results[3]

        assert len(output_states) == num_fields
        assert len(output_states) == len(output_residuals)

        from pytools.obj_array import join_fields
        flattened_results = join_fields(output_t, output_dt, *output_states)

        self.bound_op = bind(discr,
                             flattened_results,
                             function_registry=function_registry,
                             exec_mapper_factory=exec_mapper_factory)
Example #17
0
    def pmc_bc(self, w):
        "Construct part of the flux operator template for PMC boundary conditions"
        e, h = self.split_eh(w)

        pmc_e = sym.cse(sym.interp("vol", self.pmc_tag)(e))
        pmc_h = sym.cse(sym.interp("vol", self.pmc_tag)(h))

        return join_fields(pmc_e, -pmc_h)
Example #18
0
    def map_numpy_array(self, expr):
        if len(expr.shape) != 1:
            raise ValueError("only 1D numpy arrays are supported")

        from pytools.obj_array import join_fields
        return join_fields(*[
            self.rec(expr[i])
            for i in range(len(expr))])
Example #19
0
def wave_operator(discr, c, w):
    u = w[0]
    v = w[1:]

    dir_u = discr.interp("vol", BTAG_ALL, u)
    dir_v = discr.interp("vol", BTAG_ALL, v)
    dir_bval = join_fields(dir_u, dir_v)
    dir_bc = join_fields(-dir_u, dir_v)

    return (
        -join_fields(-c * discr.div(v), -c * discr.grad(u)) +  # noqa: W504
        discr.inverse_mass(
            discr.face_mass(
                wave_flux(discr, c=c, w_tpair=interior_trace_pair(discr, w)) +
                wave_flux(
                    discr, c=c, w_tpair=TracePair(BTAG_ALL, dir_bval, dir_bc)))
        ))
Example #20
0
 def noslip_state(self, state):
     from hedge.optemplate import make_normal
     state0 = join_fields(
         make_sym_vector("bc_q_noslip", 2),
         [0]*self.dimensions)
     normal = make_normal(self.noslip_tag, self.dimensions)
     bc = self.make_bc_info("bc_q_noslip", self.noslip_tag, state, state0)
     return self.inflow_state_inner(normal, bc, "noslip")
def main():
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)

    target_order = 10

    from functools import partial
    nelements = 30
    qbx_order = 4

    from sumpy.kernel import LaplaceKernel
    from meshmode.mesh.generation import (  # noqa
            ellipse, cloverleaf, starfish, drop, n_gon, qbx_peanut,
            make_curve_mesh)
    mesh = make_curve_mesh(partial(ellipse, 1),
            np.linspace(0, 1, nelements+1),
            target_order)

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            InterpolatoryQuadratureSimplexGroupFactory

    density_discr = Discretization(cl_ctx, mesh,
            InterpolatoryQuadratureSimplexGroupFactory(target_order))

    from pytential.qbx import QBXLayerPotentialSource
    qbx = QBXLayerPotentialSource(density_discr, 4*target_order,
            qbx_order, fmm_order=False)

    from pytools.obj_array import join_fields
    sig_sym = sym.var("sig")
    knl = LaplaceKernel(2)
    op = join_fields(
            sym.tangential_derivative(mesh.ambient_dim,
                sym.D(knl, sig_sym, qbx_forced_limit=+1)).as_scalar(),
            sym.tangential_derivative(mesh.ambient_dim,
                sym.D(knl, sig_sym, qbx_forced_limit=-1)).as_scalar(),
            )

    nodes = density_discr.nodes().with_queue(queue)
    angle = cl.clmath.atan2(nodes[1], nodes[0])
    n = 10
    sig = cl.clmath.sin(n*angle)
    dt_sig = n*cl.clmath.cos(n*angle)

    res = bind(qbx, op)(queue, sig=sig)

    extval = res[0].get()
    intval = res[1].get()
    pv = 0.5*(extval + intval)

    dt_sig_h = dt_sig.get()

    import matplotlib.pyplot as pt
    pt.plot(extval, label="+num")
    pt.plot(pv + dt_sig_h*0.5, label="+ex")
    pt.legend(loc="best")
    pt.show()
Example #22
0
def test_stepper_mem_ops(ctx_factory, use_fusion):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)

    dims = 2

    sym_operator, discr = get_strong_wave_op_with_discr_direct(cl_ctx,
                                                               dims=dims,
                                                               order=3)

    t_start = 0
    dt = 0.04
    t_end = 0.2

    from pytools.obj_array import join_fields
    ic = join_fields(discr.zeros(queue),
                     [discr.zeros(queue) for i in range(discr.dim)])

    if not use_fusion:
        bound_op = bind(discr,
                        sym_operator,
                        exec_mapper_factory=ExecutionMapperWithMemOpCounting)

        stepper = RK4TimeStepper(
            queue,
            discr,
            "w",
            bound_op,
            1 + discr.dim,
            get_strong_wave_component,
            exec_mapper_factory=ExecutionMapperWithMemOpCounting)

    else:
        stepper = FusedRK4TimeStepper(
            queue,
            discr,
            "w",
            sym_operator,
            1 + discr.dim,
            get_strong_wave_component,
            exec_mapper_factory=ExecutionMapperWithMemOpCounting)

    step = 0

    nsteps = int(np.ceil((t_end + 1e-9) / dt))
    for (_, _, profile_data) in stepper.run(ic,
                                            t_start,
                                            dt,
                                            t_end,
                                            return_profile_data=True):
        step += 1
        logger.info("step %d/%d", step, nsteps)

    logger.info("using fusion? %s", use_fusion)
    logger.info("bytes read: %d", profile_data["bytes_read"])
    logger.info("bytes written: %d", profile_data["bytes_written"])
    logger.info("bytes total: %d",
                profile_data["bytes_read"] + profile_data["bytes_written"])
Example #23
0
 def _bound_op(self, queue, t, *args, profile_data=None):
     from pytools.obj_array import join_fields
     context = {"t": t, self.field_var_name: join_fields(*args)}
     result = self.grudge_bound_op(queue,
                                   profile_data=profile_data,
                                   **context)
     if profile_data is not None:
         result = result[0]
     return result
Example #24
0
    def conservative_to_primitive(self, q, use_cses=True):
        if use_cses:
            from hedge.optemplate.primitives import make_common_subexpression as cse
        else:

            def cse(x, name):
                return x

        return join_fields(self.rho(q), self.p(q), self.u(q))
Example #25
0
    def conservative_to_primitive(self, q, use_cses=True):
        if use_cses:
            from hedge.optemplate.primitives import make_common_subexpression as cse
        else:
            def cse(x, name): return x

        return join_fields(
               self.rho(q),
               self.p(q),
               self.u(q))
Example #26
0
    def normal(self, where):
        bdry_discr = self.get_discr(where)

        with cl.CommandQueue(self.cl_context) as queue:
            ((a, ),
             (b, )) = with_queue(queue,
                                 parametrization_derivative(queue, bdry_discr))

            nrm = 1 / (a**2 + b**2)**0.5
            return without_queue(join_fields(b * nrm, -a * nrm))
    def flux(self, w, k):
        F2 = self.F2(w) #get F'' from state vector w
        q = self.q(w)
        P = self.P(q)
        v = self.v(q)
        v_null = Field('state_null')

        dim = self.dimensions

        # One entry for each flux direction
        if dim == 1:
            raise NotImplementedError
            #return [cse(join_fields(
            #           v_null,
            #            (P[0]+F2[0])/k[0],  # flux rho_v
            #            (v[0]+F2[1])/k[0]   # flux F
            #            ), "x_flux")]
        elif dim == 2:
            return [cse(join_fields(
                        v_null, (P[0]+F2[0])/k[0],(P[3]+F2[1])/k[0],      # flux rho_v
                        (v[0]+F2[2])/k[0],v_null,v_null,(v[1]+F2[3])/k[0] # flux F
                        ), "x_flux"),
                    cse(join_fields(
                        v_null, (P[2]+F2[4])/k[1],(P[1]+F2[5])/k[1],      # flux rho_v
                        v_null,(v[1]+F2[6])/k[1],(v[0]+F2[7])/k[1],v_null # flux F
                        ), "y_flux")]
        elif dim == 3:
            raise NotImplementedError
            #return [cse(join_fields(
            #            v_null, (P[0]+F2[0])/k[0],(P[8]+F2[1])/k[0],(P[7]+F2[2])/k[0],                                     # flux rho_v
            #            (v[0]+F2[3])/k[0],v_null,v_null,v_null,v_null,v_null,v_null,(v[2]+F2[4])/k[0],(v[1]+F2[5])/k[0]    # flux F
            #            ), "x_flux"),
            #        cse(join_fields(
            #            v_null, (P[5]+F2[6])/k[1],(P[1]+F2[7])/k[1],(P[6]+F2[8])/k[1],                                     # flux rho_v
            #            v_null,(v[1]+F2[9])/k[1],v_null,v_null,v_null,(v[0]+F2[10])/k[1],(v[2]+F2[11])/k[1],v_null,v_null  # flux F
            #            ), "y_flux"),
            #        cse(join_fields(
            #            v_null, (P[4]+F2[12])/k[2],(P[3]+F2[13])/k[2],(P[2]+F2[14])/k[2],                                  # flux rho_v
            #            v_null,v_null,(v[2]+F2[15])/k[2],(v[1]+F2[16])/k[2],(v[0]+F2[17])/k[2],v_null,v_null,v_null,v_null # flux F
            #            ), "z_flux")]
        else:
            raise ValueError("Invalid dimension")
Example #28
0
    def flux(self, w):
        c = w[0]
        u = w[1]
        v = w[2:]
        normal = sym.normal(w.dd, self.ambient_dim)

        if self.flux_type == "central":
            return -0.5 * join_fields(
                np.dot(v.int * c.int + v.ext * c.ext, normal),
                (u.int * c.int + u.ext * c.ext) * normal)

        elif self.flux_type == "upwind":
            return -0.5 * join_fields(
                np.dot(normal, c.ext * v.ext + c.int * v.int) + c.ext * u.ext -
                c.int * u.int,
                normal * (np.dot(normal, c.ext * v.ext - c.int * v.int) +
                          c.ext * u.ext + c.int * u.int))

        else:
            raise ValueError("invalid flux type '%s'" % self.flux_type)
Example #29
0
    def __call__(self, fields):
        from hedge.tools import join_fields

        #get conserved fields
        rho=self.op.rho(fields)
        e=self.op.e(fields)
        rho_velocity=self.op.rho_u(fields)

        #get primitive fields
        #to do

        #reset field values to cell average
        rhoLim=self.get_average(rho)
        eLim=self.get_average(e)
        temp=join_fields([self.get_average(rho_vel)
                for rho_vel in rho_velocity])

        #should do for primitive fields too

        return join_fields(rhoLim, eLim, temp)
Example #30
0
    def __call__(self, fields):
        from grudge.tools import join_fields

        #get conserved fields
        rho=self.op.rho(fields)
        e=self.op.e(fields)
        rho_velocity=self.op.rho_u(fields)

        #get primitive fields
        #to do

        #reset field values to cell average
        rhoLim=self.get_average(rho)
        eLim=self.get_average(e)
        temp=join_fields([self.get_average(rho_vel)
                for rho_vel in rho_velocity])

        #should do for primitive fields too

        return join_fields(rhoLim, eLim, temp)
Example #31
0
def make_field_vector(name, components):
    """Return an object array of *components* subscripted
    :class:`Variable` instances.

    :param components: The number of components in the vector.
    """
    if isinstance(components, int):
        components = list(range(components))

    from pytools.obj_array import join_fields
    vfld = Field(name)
    return join_fields(*[vfld.index(i) for i in components])
Example #32
0
    def flux(self, w):
        u = w[0]
        v = w[1:]
        normal = sym.normal(w.dd, self.ambient_dim)

        flux_weak = join_fields(np.dot(v.avg, normal), u.avg * normal)

        if self.flux_type == "central":
            pass
        elif self.flux_type == "upwind":
            # see doc/notes/grudge-notes.tm
            flux_weak -= self.sign * join_fields(
                0.5 * (u.int - u.ext), 0.5 *
                (normal * np.dot(normal, v.int - v.ext)))
        else:
            raise ValueError("invalid flux type '%s'" % self.flux_type)

        flux_strong = join_fields(np.dot(v.int, normal),
                                  u.int * normal) - flux_weak

        return self.c * flux_strong
Example #33
0
def make_sym_vector(name, components):
    """Return an object array of *components* subscripted
    :class:`Field` instances.

    :param components: The number of components in the vector.
    """
    if isinstance(components, int):
        components = range(components)

    from pytools.obj_array import join_fields
    vfld = Variable(name)
    return join_fields(*[vfld[i] for i in components])
Example #34
0
    def wall_state(self, state):
        from grudge.symbolic import RestrictToBoundary
        bc = RestrictToBoundary(self.wall_tag)(state)
        wall_rho = self.rho(bc)
        wall_e = self.e(bc)  # <3 eve
        wall_rho_u = self.rho_u(bc)

        from grudge.symbolic import make_normal
        normal = make_normal(self.wall_tag, self.dimensions)

        return join_fields(
            wall_rho, wall_e,
            wall_rho_u - 2 * numpy.dot(wall_rho_u, normal) * normal)
Example #35
0
def bump(discr, queue, t=0):
    source_center = np.array([0.0, 0.05])
    source_width = 0.05
    source_omega = 3

    nodes = discr.volume_discr.nodes().with_queue(queue)
    center_dist = join_fields([
        nodes[0] - source_center[0],
        nodes[1] - source_center[1],
    ])

    return (np.cos(source_omega * t) *
            clmath.exp(-np.dot(center_dist, center_dist) / source_width**2))
Example #36
0
    def wall_state(self, state):
        from hedge.optemplate import BoundarizeOperator
        bc = BoundarizeOperator(self.wall_tag)(state)
        wall_rho = self.rho(bc)
        wall_e = self.e(bc)  # <3 eve
        wall_rho_u = self.rho_u(bc)

        from hedge.optemplate import make_normal
        normal = make_normal(self.wall_tag, self.dimensions)

        return join_fields(
            wall_rho, wall_e,
            wall_rho_u - 2 * numpy.dot(wall_rho_u, normal) * normal)
Example #37
0
    def inflow_state_inner(self, normal, bc, name):
        # see grudge/doc/maxima/euler.mac
        return join_fields(
            # bc rho
            cse(bc.rho0
            + numpy.dot(normal, bc.dumvec)*bc.rho0/(2*bc.c0) + bc.dpm/(2*bc.c0*bc.c0), "bc_rho_"+name),

            # bc p
            cse(bc.p0
            + bc.c0*bc.rho0*numpy.dot(normal, bc.dumvec)/2 + bc.dpm/2, "bc_p_"+name),

            # bc u
            cse(bc.u0
            + normal*numpy.dot(normal, bc.dumvec)/2 + bc.dpm*normal/(2*bc.c0*bc.rho0), "bc_u_"+name))
Example #38
0
    def inflow_state_inner(self, normal, bc, name):
        # see hedge/doc/maxima/euler.mac
        return join_fields(
            # bc rho
            cse(bc.rho0
            + numpy.dot(normal, bc.dumvec)*bc.rho0/(2*bc.c0) + bc.dpm/(2*bc.c0*bc.c0), "bc_rho_"+name),

            # bc p
            cse(bc.p0
            + bc.c0*bc.rho0*numpy.dot(normal, bc.dumvec)/2 + bc.dpm/2, "bc_p_"+name),

            # bc u
            cse(bc.u0
            + normal*numpy.dot(normal, bc.dumvec)/2 + bc.dpm*normal/(2*bc.c0*bc.rho0), "bc_u_"+name))
Example #39
0
    def wall_state(self, state):
        from hedge.optemplate import BoundarizeOperator
        bc = BoundarizeOperator(self.wall_tag)(state)
        wall_rho = self.rho(bc)
        wall_e = self.e(bc) # <3 eve
        wall_rho_u = self.rho_u(bc)

        from hedge.optemplate import make_normal
        normal = make_normal(self.wall_tag, self.dimensions)

        return join_fields(
                wall_rho,
                wall_e,
                wall_rho_u - 2*numpy.dot(wall_rho_u, normal) * normal)
Example #40
0
    def primitive_to_conservative(self, prims, use_cses=True):
        if not use_cses:
            from hedge.optemplate.primitives import make_common_subexpression as cse
        else:

            def cse(x, name):
                return x

        rho = prims[0]
        p = prims[1]
        u = prims[2:]
        e = self.equation_of_state.p_to_e(p, rho, u)

        return join_fields(rho, cse(e, "e"), cse(rho * u, "rho_u"))
Example #41
0
    def __init__(self,
                 queue,
                 discr,
                 field_var_name,
                 grudge_bound_op,
                 num_fields,
                 component_getter,
                 exec_mapper_factory=ExecutionMapper):
        """Arguments:

            field_var_name: The name of the simulation variable

            grudge_bound_op: The BoundExpression for the right-hand side

            num_fields: The number of components in the simulation variable

            component_getter: A function, which, given an object array
               representing the simulation variable, splits the array into
               its components

        """
        super().__init__(queue, component_getter)

        # Construct sym_rhs to have the effect of replacing the RHS calls in the
        # dagrt code with calls of the grudge operator.
        from grudge.symbolic.primitives import FunctionSymbol, Variable
        call = sym.cse(
            FunctionSymbol("grudge_op")(
                *((Variable("t", dd=sym.DD_SCALAR), ) + tuple(
                    Variable(field_var_name, dd=sym.DD_VOLUME)[i]
                    for i in range(num_fields)))))

        from pytools.obj_array import join_fields
        sym_rhs = join_fields(*(call[i] for i in range(num_fields)))

        self.queue = queue
        self.grudge_bound_op = grudge_bound_op

        from grudge.function_registry import register_external_function

        freg = register_external_function(base_function_registry,
                                          "grudge_op",
                                          implementation=self._bound_op,
                                          dd=sym.DD_VOLUME)

        self.set_up_stepper(discr, field_var_name, sym_rhs, num_fields, freg,
                            exec_mapper_factory)

        self.component_getter = component_getter
Example #42
0
    def primitive_to_conservative(self, prims, use_cses=True):
        if not use_cses:
            from hedge.optemplate.primitives import make_common_subexpression as cse
        else:
            def cse(x, name): return x

        rho = prims[0]
        p = prims[1]
        u = prims[2:]
        e = self.equation_of_state.p_to_e(p, rho, u)

        return join_fields(
               rho,
               cse(e, "e"),
               cse(rho * u, "rho_u"))
Example #43
0
def wave_flux(discr, c, w_tpair):
    u = w_tpair[0]
    v = w_tpair[1:]

    normal = with_queue(u.int.queue, discr.normal(w_tpair.where))

    flux_weak = join_fields(np.dot(v.avg, normal), normal[0] * u.avg,
                            normal[1] * u.avg)

    # upwind
    v_jump = np.dot(normal, v.int - v.ext)
    flux_weak -= join_fields(
        0.5 * (u.int - u.ext),
        0.5 * normal[0] * v_jump,
        0.5 * normal[1] * v_jump,
    )

    flux_strong = join_fields(
        np.dot(v.int, normal),
        u.int * normal[0],
        u.int * normal[1],
    ) - flux_weak

    return discr.interp(w_tpair.where, "all_faces", c * flux_strong)
Example #44
0
def make_lax_friedrichs_flux(wave_speed, state, fluxes,
                             bdry_tags_states_and_fluxes, strong):
    from pytools.obj_array import join_fields
    from hedge.flux import make_normal, FluxVectorPlaceholder, flux_max

    n = len(state)
    d = len(fluxes)
    normal = make_normal(d)
    fvph = FluxVectorPlaceholder(len(state) * (1 + d) + 1)

    wave_speed_ph = fvph[0]
    state_ph = fvph[1:1 + n]
    fluxes_ph = [fvph[1 + i * n:1 + (i + 1) * n] for i in range(1, d + 1)]

    penalty = flux_max(wave_speed_ph.int,
                       wave_speed_ph.ext) * (state_ph.ext - state_ph.int)

    if not strong:
        num_flux = 0.5 * (sum(n_i * (f_i.int + f_i.ext)
                              for n_i, f_i in zip(normal, fluxes_ph)) -
                          penalty)
    else:
        num_flux = 0.5 * (sum(n_i * (f_i.int - f_i.ext)
                              for n_i, f_i in zip(normal, fluxes_ph)) +
                          penalty)

    from hedge.optemplate import get_flux_operator
    flux_op = get_flux_operator(num_flux)
    int_operand = join_fields(wave_speed, state, *fluxes)

    from hedge.optemplate import BoundaryPair
    return (flux_op(int_operand) + sum(
        flux_op(
            BoundaryPair(int_operand, join_fields(0, bdry_state, *bdry_fluxes),
                         tag))
        for tag, bdry_state, bdry_fluxes in bdry_tags_states_and_fluxes))
Example #45
0
    def volume_field_base(self, r, q, j):
        k = self.k

        grad_phi = grad_S(k, r, 3)
        grad_psi = grad_S(k, q, 3)

        m = cse(self.m(j), "m")

        A = cse(S(k, j), "A")
        Q = cse(S(k, m), "Q")

        E = 1j * k * A - grad_phi - curl_S_volume(k, m)
        H = curl_S_volume(k, j) + 1j * k * Q - grad_psi

        from pytools.obj_array import join_fields
        return join_fields(E, H)
Example #46
0
    def volume_field_base(self, r, q, j):
        k = self.k

        grad_phi = grad_S(k, r, 3)
        grad_psi = grad_S(k, q, 3)

        m = cse(self.m(j), "m")

        A = cse(S(k, j), "A")
        Q = cse(S(k, m), "Q")

        E = 1j*k*A - grad_phi - curl_S_volume(k, m)
        H = curl_S_volume(k, j) + 1j*k*Q - grad_psi

        from pytools.obj_array import join_fields
        return join_fields(E, H)
Example #47
0
def make_sym_vector(name, components, var_factory=None):
    """Return an object array of *components* subscripted
    :class:`Variable` (or subclass) instances.

    :arg components: The number of components in the vector.
    :arg var_factory: The :class:`Variable` subclass to use for instantiating
        the scalar variables.
    """
    if var_factory is None:
        var_factory = Variable

    if isinstance(components, int):
        components = list(range(components))

    from pytools.obj_array import join_fields
    vfld = var_factory(name)
    return join_fields(*[vfld.index(i) for i in components])
Example #48
0
    def integral_equation(self, *args, **kwargs):
        nxnxE, ndotH = self.boundary_field(*args)
        nxnxE = cse(nxnxE, "nxnxE")

        fix = kwargs.pop("fix", 0)
        if kwargs:
            raise TypeError("invalid keyword argument(s)")

        from pytools.obj_array import make_obj_array
        eh_op = make_obj_array([
            2*_debye_S0_surf_div(nxnxE),
            -ndotH,
            ]) + fix

        k = self.k
        j = cse(self.j(*args), "j")
        m = cse(self.m(j), "m")
        A = cse(S(k, j), "A")

        E_minus_grad_phi = 1j*k*A - curl_S_volume(k, m)

        from hellskitchen.fmm import DifferenceKernel
        from pytools.obj_array import join_fields
        return join_fields(
                eh_op,
                # FIXME: These are inefficient. They compute a full volume field,
                # but only actually use the line part of it.

                [
                    # Grad phi integrated on a loop does not contribute.
                    LineIntegral(E_minus_grad_phi, a_cycle_name)
                    for a_cycle_name in self.a_cycle_names
                    ],
                [
                    LineIntegral(
                        # (E(k) - E(0))/(1jk)
                        # Grad phi integrated on a loop does not contribute.
                        (1j*k*A - curl_S_volume(DifferenceKernel(k), m))
                            /(1j*k),
                        b_cycle_name)
                    for b_cycle_name in self.b_cycle_names
                    ]
                )
Example #49
0
    def flux(self, q):
        from pytools import delta

        return [ # one entry for each flux direction
                cse(join_fields(
                    # flux rho
                    self.rho_u(q)[i],

                    # flux E
                    cse(self.e(q)+self.cse_p(q))*self.cse_u(q)[i],

                    # flux rho_u
                    make_obj_array([
                        self.rho_u(q)[i]*self.cse_u(q)[j] 
                        + delta(i,j) * self.cse_p(q)
                        for j in range(self.dimensions)
                        ])
                    ), "%s_flux" % AXES[i])
                for i in range(self.dimensions)]
Example #50
0
    def outflow_state(self, state):
        from hedge.optemplate import make_normal
        normal = make_normal(self.outflow_tag, self.dimensions)
        bc = self.make_bc_info("bc_q_out", self.outflow_tag, state)

        # see hedge/doc/maxima/euler.mac
        return join_fields(
            # bc rho
            cse(bc.rho0
            + bc.drhom + numpy.dot(normal, bc.dumvec)*bc.rho0/(2*bc.c0)
            - bc.dpm/(2*bc.c0*bc.c0), "bc_rho_outflow"),

            # bc p
            cse(bc.p0
            + bc.c0*bc.rho0*numpy.dot(normal, bc.dumvec)/2 + bc.dpm/2, "bc_p_outflow"),

            # bc u
            cse(bc.u0
            + bc.dumvec - normal*numpy.dot(normal, bc.dumvec)/2
            + bc.dpm*normal/(2*bc.c0*bc.rho0), "bc_u_outflow"))
    def bdry_flux(self, q_bdry, q_null, tag):
        if tag == self.boundaryconditions_tag['stressfree']:
            signP = -1
            signv = 1
        elif tag == self.boundaryconditions_tag['fixed']:
            signP = 1
            signv = -1
        else:
            raise ValueError("Invalid boundary conditions")

        dim = self.dimensions

        P = self.P(q_bdry)
        v = self.v(q_bdry)
        v_null = q_null

        # One entry for each flux direction
        if dim == 1:
            return [cse(join_fields(
                        v_null,
                        signP*P[0], # flux rho_v
                        signv*v[0]  # flux F
                        ), "x_bflux")]
        elif dim == 2:
            return [cse(join_fields(
                        v_null, signP*P[0],signP*P[2],       # flux rho_v
                        signv*v[0],v_null,v_null,signv*v[1]  # flux F
                        ), "x_bflux"),
                    cse(join_fields(
                        v_null, signP*P[2],signP*P[1],       # flux rho_v
                        v_null,signv*v[1],signv*v[0],v_null  # flux F
                        ), "y_bflux")]
        elif dim == 3:
            return [cse(join_fields(
                        v_null, signP*P[0],signP*P[5],signP*P[4],                                  # flux rho_v
                        signv*v[0],v_null,v_null,v_null,v_null,v_null,signv*v[2],v_null,signv*v[1] # flux F
                        ), "x_bflux"),
                    cse(join_fields(
                        v_null, signP*P[5],signP*P[1],signP*P[3],                                  # flux rho_v
                        v_null,signv*v[1],v_null,v_null,signv*v[2],v_null,v_null,signv*v[0],v_null # flux F
                        ), "y_bflux"),
                    cse(join_fields(
                        v_null, signP*P[4],signP*P[3],signP*P[2],                                  # flux rho_v
                        v_null,v_null,signv*v[2],signv*v[1],v_null,signv*v[0],v_null,v_null,v_null # flux F
                        ), "z_bflux")]
        else:
            raise ValueError("Invalid dimension")
Example #52
0
    def make_second_order_part(self):
        state = self.state()
        faceq_state = self.faceq_state()
        volq_state = self.volq_state()

        volq_tau_mat = self.tau(to_vol_quad, state)
        faceq_tau_mat = self.tau(to_int_face_quad, state)

        return join_fields(
                0, 
                self.div(
                    numpy.sum(volq_tau_mat*self.cse_u(volq_state), axis=1)
                    + self.heat_conduction_grad(to_vol_quad)
                    ,
                    numpy.sum(faceq_tau_mat*self.cse_u(faceq_state), axis=1)
                    + self.heat_conduction_grad(to_int_face_quad)
                    ,
                    ),
                [
                    self.div(volq_tau_mat[i], faceq_tau_mat[i])
                    for i in range(self.dimensions)]
                )
Example #53
0
    def integral_equation(self, r_tilde, q_tilde, hvf_coefficients):
        fix = 0

        if self.invertible:
            s_ones = cse(S(0, Ones()), "s_ones")

            def inv_rank_one_coeff(u):
                return cse(Mean(cse(S(0, cse(S(0, u))))))

            r_coeff = inv_rank_one_coeff(r_tilde)
            q_coeff = inv_rank_one_coeff(q_tilde)

            from pytools.obj_array import join_fields
            factors = self.cluster_points()

            fix = join_fields(
                    factors[0]*s_ones*(r_coeff),
                    factors[1]*Ones()*(q_coeff),
                    )

        return DebyeOperatorBase.integral_equation(
                self, r_tilde, q_tilde, hvf_coefficients, fix=fix)
    def flux(self, q):
        P = self.P(q)
        v = self.v(q)
        v_null = Field('state_null')
        dim = self.dimensions

        # One entry for each flux direction
        if dim == 1:
            return [cse(join_fields(
                        v_null,
                        P[0],  # flux rho_v
                        v[0]   # flux F
                        ), "x_flux")]

        elif dim == 2:
            return [cse(join_fields(
                        v_null, P[0],P[2],# flux rho_v
                        v[0],v_null,v[1]  # flux F
                        ), "x_flux"),
                    cse(join_fields(
                        v_null, P[2],P[1],# flux rho_v
                        v_null,v[1],v[0]  # flux F
                        ), "y_flux")]
        elif dim == 3:
            return [cse(join_fields(
                        v_null, P[0],P[5],P[4],             # flux rho_v
                        v[0],v_null,v_null,v_null,v[2],v[1] # flux F
                        ), "x_flux"),
                    cse(join_fields(
                        v_null, P[5],P[1],P[3],             # flux rho_v
                        v_null,v[1],v_null,v[2],v_null,v[0] # flux F
                        ), "y_flux"),
                    cse(join_fields(
                        v_null, P[4],P[3],P[2],             # flux rho_v
                        v_null,v_null,v[2],v[1],v[0],v_null # flux F
                        ), "z_flux")]
        else:
            raise ValueError("Invalid dimension")
Example #55
0
    def op_template(self, sensor_scaling=None, viscosity_only=False):
        u = self.cse_u
        rho = self.cse_rho
        rho_u = self.rho_u
        p = self.p
        e = self.e

        # {{{ artificial diffusion
        def make_artificial_diffusion():
            if self.artificial_viscosity_mode not in ["diffusion"]:
                return 0

            dq = self.grad_of_state()

            return make_obj_array([
                self.div(
                    to_vol_quad(self.sensor())*to_vol_quad(dq[i]),
                    to_int_face_quad(self.sensor())*to_int_face_quad(dq[i])) 
                for i in range(dq.shape[0])])
        # }}}

        # {{{ state setup

        volq_flux = self.flux(self.volq_state())
        faceq_flux = self.flux(self.faceq_state())

        from hedge.optemplate.primitives import CFunction
        sqrt = CFunction("sqrt")

        speed = self.characteristic_velocity_optemplate(self.state())

        has_viscosity = not is_zero(self.get_mu(self.state(), to_quad_op=None))

        # }}}

        # {{{ operator assembly -----------------------------------------------
        from hedge.flux.tools import make_lax_friedrichs_flux
        from hedge.optemplate.operators import InverseMassOperator

        from hedge.optemplate.tools import make_stiffness_t

        primitive_bcs_as_quad_conservative = dict(
                (tag, self.primitive_to_conservative(to_bdry_quad(bc)))
                for tag, bc in 
                self.get_primitive_boundary_conditions().iteritems())

        def get_bc_tuple(tag):
            state = self.state()
            bc = make_obj_array([
                self.get_boundary_condition_for(tag, s_i) for s_i in state])
            return tag, bc, self.flux(bc)

        first_order_part = InverseMassOperator()(
                numpy.dot(make_stiffness_t(self.dimensions), volq_flux)
                - make_lax_friedrichs_flux(
                    wave_speed=cse(to_int_face_quad(speed), "emax_c"),

                    state=self.faceq_state(), fluxes=faceq_flux,
                    bdry_tags_states_and_fluxes=[
                        get_bc_tuple(tag) for tag in self.get_boundary_tags()],
                    strong=False))

        if viscosity_only:
            first_order_part = 0*first_order_part

        result = join_fields(
                first_order_part 
                + self.make_second_order_part()
                + make_artificial_diffusion()
                + self.make_extra_terms(),
                 speed)

        if self.source is not None:
            result = result + join_fields(
                    make_sym_vector("source_vect", len(self.state())),
                    # extra field for speed
                    0)

        return result