Ejemplo n.º 1
0
def main(write_output=True):
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)

    from meshmode.mesh.generation import generate_warped_rect_mesh
    mesh = generate_warped_rect_mesh(dim=2, order=4, n=6)

    discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=4)

    sym_op = sym.normal(sym.BTAG_ALL, mesh.dim)
    #sym_op = sym.nodes(mesh.dim, where=sym.BTAG_ALL)
    print(sym.pretty(sym_op))
    op = bind(discr, sym_op)
    print()
    print(op.eval_code)

    vec = op(queue)

    vis = shortcuts.make_visualizer(discr, 4)
    vis.write_vtk_file("geo.vtu", [
        ])

    bvis = shortcuts.make_boundary_visualizer(discr, 4)
    bvis.write_vtk_file("bgeo.vtu", [
        ("normals", vec)
        ])
Ejemplo n.º 2
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.project("vol", self.absorb_tag)(e))
        absorb_h = sym.cse(sym.project("vol", self.absorb_tag)(h))

        bc = flat_obj_array(
                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
Ejemplo n.º 3
0
def main(write_output=True):
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    from meshmode.mesh import BTAG_ALL
    from meshmode.mesh.generation import generate_warped_rect_mesh
    mesh = generate_warped_rect_mesh(dim=2, order=4, nelements_side=6)

    discr = DiscretizationCollection(actx, mesh, order=4)

    sym_op = sym.normal(BTAG_ALL, mesh.dim)
    # sym_op = sym.nodes(mesh.dim, dd=BTAG_ALL)
    print(sym.pretty(sym_op))
    op = bind(discr, sym_op)
    print()
    print(op.eval_code)

    vec = op(actx)

    vis = shortcuts.make_visualizer(discr)
    vis.write_vtk_file("geo.vtu", [])

    bvis = shortcuts.make_boundary_visualizer(discr)
    bvis.write_vtk_file("bgeo.vtu", [("normals", vec)])
Ejemplo n.º 4
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
Ejemplo n.º 5
0
 def normal(self, dd):
     surface_discr = self.discr_from_dd(dd)
     actx = surface_discr._setup_actx
     return freeze(
             bind(self,
                 sym.normal(dd, surface_discr.ambient_dim, surface_discr.dim),
                 local_only=True)
             (array_context=actx))
Ejemplo n.º 6
0
    def flux(self, w):
        """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 flat_obj_array(
                    # flux e,
                    1/2*(
                        -self.space_cross_h(normal, h.ext-h.int)
                        # 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.ext-e.int)
                        # 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 flat_obj_array(
                    # flux e,
                    (
                        -1/(Z_int+Z_ext)*self.space_cross_h(normal,
                            Z_ext*(h.ext-h.int)
                            - self.flux_type*self.space_cross_e(normal, e.ext-e.int))
                        ),
                    # flux h
                    (
                        1/(Y_int + Y_ext)*self.space_cross_e(normal,
                            Y_ext*(e.ext-e.int)
                            + self.flux_type*self.space_cross_h(normal, h.ext-h.int))
                        ),
                    )
        else:
            raise ValueError("maxwell: invalid flux_type (%s)"
                    % self.flux_type)
Ejemplo n.º 7
0
def v_dot_n_tpair(velocity, dd=None):
    if dd is None:
        dd = sym.DOFDesc(sym.FACE_RESTR_INTERIOR)

    ambient_dim = len(velocity)
    normal = sym.normal(dd.with_qtag(None), ambient_dim, dim=ambient_dim - 2)

    return sym.int_tpair(velocity.dot(normal),
                         qtag=dd.quadrature_tag,
                         from_dd=dd.with_qtag(None))
Ejemplo n.º 8
0
def normal(dcoll, dd):
    """Get unit normal to specified surface discretization, *dd*.

    :arg dd: a :class:`~grudge.dof_desc.DOFDesc` as the surface discretization.
    :returns: an object array of :class:`~meshmode.dof_array.DOFArray`.
    """
    surface_discr = dcoll.discr_from_dd(dd)
    actx = surface_discr._setup_actx
    return freeze(
        bind(dcoll,
             sym.normal(dd, surface_discr.ambient_dim, surface_discr.dim),
             local_only=True)(array_context=actx))
Ejemplo n.º 9
0
def get_strong_wave_op_with_discr_direct(cl_ctx, dims=2, order=4):
    from meshmode.mesh.generation import generate_regular_rect_mesh
    mesh = generate_regular_rect_mesh(a=(-0.5, ) * dims,
                                      b=(0.5, ) * dims,
                                      n=(16, ) * dims)

    logger.debug("%d elements", mesh.nelements)

    discr = DGDiscretizationWithBoundaries(cl_ctx, mesh, order=order)

    source_center = np.array([0.1, 0.22, 0.33])[:dims]
    source_width = 0.05
    source_omega = 3

    sym_x = sym.nodes(mesh.dim)
    sym_source_center_dist = sym_x - source_center
    sym_t = sym.ScalarVariable("t")

    from meshmode.mesh import BTAG_ALL

    c = -0.1
    sign = -1

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

    source_f = (
        sym.sin(source_omega * sym_t) *
        sym.exp(-np.dot(sym_source_center_dist, sym_source_center_dist) /
                source_width**2))

    rad_normal = sym.normal(BTAG_ALL, dims)

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

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

    sym_operator = (
        -sym.join_fields(-c * np.dot(sym.nabla(dims), v) - source_f, -c *
                         (sym.nabla(dims) * u)) + sym.InverseMassOperator()(
                             sym.FaceMassOperator()
                             (dg_flux(c, sym.int_tpair(w)) +
                              dg_flux(c, sym.bv_tpair(BTAG_ALL, w, rad_bc)))))

    return (sym_operator, discr)
Ejemplo n.º 10
0
def v_dot_n_tpair(velocity, dd=None):
    from grudge.dof_desc import DOFDesc
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR

    if dd is None:
        dd = DOFDesc(FACE_RESTR_INTERIOR)

    ambient_dim = len(velocity)
    normal = sym.normal(dd.with_discr_tag(None),
                        ambient_dim,
                        dim=ambient_dim - 2)

    return sym.int_tpair(velocity.dot(normal),
                         qtag=dd.discretization_tag,
                         from_dd=dd.with_discr_tag(None))
Ejemplo n.º 11
0
    def flux(self, w):
        u = w[0]
        v = w[1:]
        normal = sym.normal(w.dd, self.ambient_dim)

        flux_weak = flat_obj_array(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 * flat_obj_array(
                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)
Ejemplo n.º 12
0
def dg_flux(c, tpair):
    u = tpair[0]
    v = tpair[1:]

    dims = len(v)

    normal = sym.normal(tpair.dd, dims)
    flux_weak = flat_obj_array(np.dot(v.avg, normal), u.avg * normal)

    flux_weak -= (1 if c > 0 else -1) * flat_obj_array(
        0.5 * (u.int - u.ext), 0.5 * (normal * np.dot(normal, v.int - v.ext)))

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

    return sym.project(tpair.dd, "all_faces")(c * flux_strong)
Ejemplo n.º 13
0
def dg_flux(c, tpair):
    u = tpair[0]
    v = tpair[1:]

    dims = len(v)

    normal = sym.normal(tpair.dd, dims)

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

    flux_weak -= (1 if c > 0 else -1) * sym.join_fields(
        0.5 * (u.int - u.ext), 0.5 * (normal * np.dot(normal, v.int - v.ext)))

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

    return sym.interp(tpair.dd, "all_faces")(c * flux_strong)
Ejemplo n.º 14
0
def test_empty_boundary(actx_factory):
    # https://github.com/inducer/grudge/issues/54

    from meshmode.mesh import BTAG_NONE

    actx = actx_factory()

    dim = 2
    mesh = mgen.generate_regular_rect_mesh(a=(-0.5, ) * dim,
                                           b=(0.5, ) * dim,
                                           nelements_per_axis=(8, ) * dim,
                                           order=4)
    discr = DiscretizationCollection(actx, mesh, order=4)
    normal = bind(discr, sym.normal(BTAG_NONE, dim, dim=dim - 1))(actx)
    from meshmode.dof_array import DOFArray
    for component in normal:
        assert isinstance(component, DOFArray)
        assert len(component) == len(discr.discr_from_dd(BTAG_NONE).groups)
Ejemplo n.º 15
0
    def weak_flux(self, u):
        normal = sym.normal(u. dd, self.ambient_dim)

        v_dot_normal = sym.cse(self.v.dot(normal), "v_dot_normal")
        norm_v = sym.sqrt((self.v**2).sum())

        if self.flux_type == "central":
            return u.avg*v_dot_normal
        elif self.flux_type == "lf":
            return u.avg*v_dot_normal + 0.5*norm_v*(u.int - u.ext)
        elif self.flux_type == "upwind":
            return (
                    v_dot_normal * sym.If(
                        sym.Comparison(v_dot_normal, ">", 0),
                        u.int,  # outflow
                        u.ext,  # inflow
                        ))
        else:
            raise ValueError("invalid flux type")
Ejemplo n.º 16
0
def advection_weak_flux(flux_type, u, velocity):
    normal = sym.normal(u.dd, len(velocity))
    v_dot_n = sym.cse(velocity.dot(normal), "v_dot_normal")

    flux_type = flux_type.lower()
    if flux_type == "central":
        return u.avg * v_dot_n
    elif flux_type == "lf":
        norm_v = sym.sqrt((velocity**2).sum())
        return u.avg * v_dot_n + 0.5 * norm_v * (u.int - u.ext)
    elif flux_type == "upwind":
        u_upwind = sym.If(
                sym.Comparison(v_dot_n, ">", 0),
                u.int,      # outflow
                u.ext       # inflow
                )
        return u_upwind * v_dot_n
    else:
        raise ValueError("flux `{}` is not implemented".format(flux_type))
Ejemplo n.º 17
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 * flat_obj_array(
                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 * flat_obj_array(
                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)
Ejemplo n.º 18
0
    def flux(self, w):
        u = w[0]
        v = w[1:]
        normal = sym.normal(w.dd, self.ambient_dim)

        flux_weak = flat_obj_array(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 * flat_obj_array(
                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 = flat_obj_array(np.dot(v.int, normal),
                                     u.int * normal) - flux_weak

        return self.c * flux_strong
Ejemplo n.º 19
0
def test_2d_gauss_theorem(actx_factory):
    """Verify Gauss's theorem explicitly on a mesh"""

    pytest.importorskip("meshpy")

    from meshpy.geometry import make_circle, GeometryBuilder
    from meshpy.triangle import MeshInfo, build

    geob = GeometryBuilder()
    geob.add_geometry(*make_circle(1))
    mesh_info = MeshInfo()
    geob.set(mesh_info)

    mesh_info = build(mesh_info)

    from meshmode.mesh.io import from_meshpy
    mesh = from_meshpy(mesh_info, order=1)

    actx = actx_factory()

    discr = DGDiscretizationWithBoundaries(actx, mesh, order=2)

    def f(x):
        return flat_obj_array(
                sym.sin(3*x[0])+sym.cos(3*x[1]),
                sym.sin(2*x[0])+sym.cos(x[1]))

    gauss_err = bind(discr,
            sym.integral((
                sym.nabla(2) * f(sym.nodes(2))
                ).sum())
            -  # noqa: W504
            sym.integral(
                sym.project("vol", sym.BTAG_ALL)(f(sym.nodes(2)))
                .dot(sym.normal(sym.BTAG_ALL, 2)),
                dd=sym.BTAG_ALL)
            )(actx)

    assert abs(gauss_err) < 1e-13
Ejemplo n.º 20
0
def main(ctx_factory, dim=2, order=4, product_tag=None, visualize=False):
    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue)

    # {{{ parameters

    # sphere radius
    radius = 1.0
    # sphere resolution
    resolution = 64 if dim == 2 else 1

    # cfl
    dt_factor = 2.0
    # final time
    final_time = np.pi

    # velocity field
    sym_x = sym.nodes(dim)
    c = make_obj_array([-sym_x[1], sym_x[0], 0.0])[:dim]
    # flux
    flux_type = "lf"

    # }}}

    # {{{ discretization

    if dim == 2:
        from meshmode.mesh.generation import make_curve_mesh, ellipse
        mesh = make_curve_mesh(lambda t: radius * ellipse(1.0, t),
                               np.linspace(0.0, 1.0, resolution + 1), order)
    elif dim == 3:
        from meshmode.mesh.generation import generate_icosphere
        mesh = generate_icosphere(radius,
                                  order=4 * order,
                                  uniform_refinement_rounds=resolution)
    else:
        raise ValueError("unsupported dimension")

    discr_tag_to_group_factory = {}
    if product_tag == "none":
        product_tag = None
    else:
        product_tag = dof_desc.DISCR_TAG_QUAD

    from meshmode.discretization.poly_element import \
            PolynomialWarpAndBlendGroupFactory, \
            QuadratureSimplexGroupFactory

    discr_tag_to_group_factory[dof_desc.DISCR_TAG_BASE] = \
        PolynomialWarpAndBlendGroupFactory(order)

    if product_tag:
        discr_tag_to_group_factory[product_tag] = \
            QuadratureSimplexGroupFactory(order=4*order)

    from grudge import DiscretizationCollection
    discr = DiscretizationCollection(
        actx, mesh, discr_tag_to_group_factory=discr_tag_to_group_factory)

    volume_discr = discr.discr_from_dd(dof_desc.DD_VOLUME)
    logger.info("ndofs:     %d", volume_discr.ndofs)
    logger.info("nelements: %d", volume_discr.mesh.nelements)

    # }}}

    # {{{ symbolic operators

    def f_initial_condition(x):
        return x[0]

    from grudge.models.advection import SurfaceAdvectionOperator
    op = SurfaceAdvectionOperator(c, flux_type=flux_type, quad_tag=product_tag)

    bound_op = bind(discr, op.sym_operator())
    u0 = bind(discr, f_initial_condition(sym_x))(actx, t=0)

    def rhs(t, u):
        return bound_op(actx, t=t, u=u)

    # check velocity is tangential
    sym_normal = sym.surface_normal(dim, dim=dim - 1,
                                    dd=dof_desc.DD_VOLUME).as_vector()
    error = bind(discr, sym.norm(2, c.dot(sym_normal)))(actx)
    logger.info("u_dot_n:   %.5e", error)

    # }}}

    # {{{ time stepping

    # compute time step
    h_min = bind(discr, sym.h_max_from_volume(discr.ambient_dim,
                                              dim=discr.dim))(actx)
    dt = dt_factor * h_min / order**2
    nsteps = int(final_time // dt) + 1
    dt = final_time / nsteps + 1.0e-15

    logger.info("dt:        %.5e", dt)
    logger.info("nsteps:    %d", nsteps)

    from grudge.shortcuts import set_up_rk4
    dt_stepper = set_up_rk4("u", dt, u0, rhs)
    plot = Plotter(actx, discr, order, visualize=visualize)

    norm = bind(discr, sym.norm(2, sym.var("u")))
    norm_u = norm(actx, u=u0)

    step = 0

    event = dt_stepper.StateComputed(0.0, 0, 0, u0)
    plot(event, "fld-surface-%04d" % 0)

    if visualize and dim == 3:
        from grudge.shortcuts import make_visualizer
        vis = make_visualizer(discr)
        vis.write_vtk_file("fld-surface-velocity.vtu",
                           [("u", bind(discr, c)(actx)),
                            ("n", bind(discr, sym_normal)(actx))],
                           overwrite=True)

        df = dof_desc.DOFDesc(FACE_RESTR_INTERIOR)
        face_discr = discr.connection_from_dds(dof_desc.DD_VOLUME, df).to_discr

        face_normal = bind(
            discr, sym.normal(df, face_discr.ambient_dim,
                              dim=face_discr.dim))(actx)

        from meshmode.discretization.visualization import make_visualizer
        vis = make_visualizer(actx, face_discr)
        vis.write_vtk_file("fld-surface-face-normals.vtu",
                           [("n", face_normal)],
                           overwrite=True)

    for event in dt_stepper.run(t_end=final_time, max_steps=nsteps + 1):
        if not isinstance(event, dt_stepper.StateComputed):
            continue

        step += 1
        if step % 10 == 0:
            norm_u = norm(actx, u=event.state_component)
            plot(event, "fld-surface-%04d" % step)

        logger.info("[%04d] t = %.5f |u| = %.5e", step, event.t, norm_u)

    plot(event, "fld-surface-%04d" % step)
Ejemplo n.º 21
0
def test_face_normal_surface(actx_factory, mesh_name):
    """Check that face normals are orthogonal to the surface normal"""
    actx = actx_factory()

    # {{{ geometry

    if mesh_name == "2-1-ellipse":
        from mesh_data import EllipseMeshBuilder
        builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0)
    elif mesh_name == "spheroid":
        from mesh_data import SpheroidMeshBuilder
        builder = SpheroidMeshBuilder()
    else:
        raise ValueError("unknown mesh name: %s" % mesh_name)

    mesh = builder.get_mesh(builder.resolutions[0], builder.mesh_order)
    discr = DiscretizationCollection(actx, mesh, order=builder.order)

    volume_discr = discr.discr_from_dd(dof_desc.DD_VOLUME)
    logger.info("ndofs:    %d", volume_discr.ndofs)
    logger.info("nelements: %d", volume_discr.mesh.nelements)

    # }}}

    # {{{ symbolic
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR

    dv = dof_desc.DD_VOLUME
    df = dof_desc.as_dofdesc(FACE_RESTR_INTERIOR)

    ambient_dim = mesh.ambient_dim
    dim = mesh.dim

    sym_surf_normal = sym.project(dv,
                                  df)(sym.surface_normal(ambient_dim,
                                                         dim=dim,
                                                         dd=dv).as_vector())
    sym_surf_normal = sym_surf_normal / sym.sqrt(sum(sym_surf_normal**2))

    sym_face_normal_i = sym.normal(df, ambient_dim, dim=dim - 1)
    sym_face_normal_e = sym.OppositeInteriorFaceSwap(df)(sym_face_normal_i)

    if mesh.ambient_dim == 3:
        # NOTE: there's only one face tangent in 3d
        sym_face_tangent = (
            sym.pseudoscalar(ambient_dim, dim - 1, dd=df) /
            sym.area_element(ambient_dim, dim - 1, dd=df)).as_vector()

    # }}}

    # {{{ checks

    def _eval_error(x):
        return bind(discr, sym.norm(np.inf, sym.var("x", dd=df), dd=df))(actx,
                                                                         x=x)

    rtol = 1.0e-14

    surf_normal = bind(discr, sym_surf_normal)(actx)

    face_normal_i = bind(discr, sym_face_normal_i)(actx)
    face_normal_e = bind(discr, sym_face_normal_e)(actx)

    # check interpolated surface normal is orthogonal to face normal
    error = _eval_error(surf_normal.dot(face_normal_i))
    logger.info("error[n_dot_i]:    %.5e", error)
    assert error < rtol

    # check angle between two neighboring elements
    error = _eval_error(face_normal_i.dot(face_normal_e) + 1.0)
    logger.info("error[i_dot_e]:    %.5e", error)
    assert error > rtol

    # check orthogonality with face tangent
    if ambient_dim == 3:
        face_tangent = bind(discr, sym_face_tangent)(actx)

        error = _eval_error(face_tangent.dot(face_normal_i))
        logger.info("error[t_dot_i]:  %.5e", error)
        assert error < 5 * rtol
Ejemplo n.º 22
0
    def flux(self, u):
        normal = sym.normal(u.dd, self.ambient_dim)
        v_dot_normal = sym.cse(self.v.dot(normal), "v_dot_normal")

        return u.int * v_dot_normal - self.weak_flux(u)
Ejemplo n.º 23
0
def test_surface_divergence_theorem(actx_factory, mesh_name, visualize=False):
    r"""Check the surface divergence theorem.

        .. math::

            \int_Sigma \phi \nabla_i f_i =
            \int_\Sigma \nabla_i \phi f_i +
            \int_\Sigma \kappa \phi f_i n_i +
            \int_{\partial \Sigma} \phi f_i m_i

        where :math:`n_i` is the surface normal and :class:`m_i` is the
        face normal (which should be orthogonal to both the surface normal
        and the face tangent).
    """
    actx = actx_factory()

    # {{{ cases

    if mesh_name == "2-1-ellipse":
        from mesh_data import EllipseMeshBuilder
        builder = EllipseMeshBuilder(radius=3.1, aspect_ratio=2.0)
    elif mesh_name == "spheroid":
        from mesh_data import SpheroidMeshBuilder
        builder = SpheroidMeshBuilder()
    elif mesh_name == "circle":
        from mesh_data import EllipseMeshBuilder
        builder = EllipseMeshBuilder(radius=1.0, aspect_ratio=1.0)
    elif mesh_name == "starfish":
        from mesh_data import StarfishMeshBuilder
        builder = StarfishMeshBuilder()
    elif mesh_name == "sphere":
        from mesh_data import SphereMeshBuilder
        builder = SphereMeshBuilder(radius=1.0, mesh_order=16)
    else:
        raise ValueError("unknown mesh name: %s" % mesh_name)

    # }}}

    # {{{ convergene

    def f(x):
        return flat_obj_array(
            sym.sin(3 * x[1]) + sym.cos(3 * x[0]) + 1.0,
            sym.sin(2 * x[0]) + sym.cos(x[1]),
            3.0 * sym.cos(x[0] / 2) + sym.cos(x[1]),
        )[:ambient_dim]

    from pytools.convergence import EOCRecorder
    eoc_global = EOCRecorder()
    eoc_local = EOCRecorder()

    theta = np.pi / 3.33
    ambient_dim = builder.ambient_dim
    if ambient_dim == 2:
        mesh_rotation = np.array([
            [np.cos(theta), -np.sin(theta)],
            [np.sin(theta), np.cos(theta)],
        ])
    else:
        mesh_rotation = np.array([
            [1.0, 0.0, 0.0],
            [0.0, np.cos(theta), -np.sin(theta)],
            [0.0, np.sin(theta), np.cos(theta)],
        ])

    mesh_offset = np.array([0.33, -0.21, 0.0])[:ambient_dim]

    for i, resolution in enumerate(builder.resolutions):
        from meshmode.mesh.processing import affine_map
        from meshmode.discretization.connection import FACE_RESTR_ALL

        mesh = builder.get_mesh(resolution, builder.mesh_order)
        mesh = affine_map(mesh, A=mesh_rotation, b=mesh_offset)

        from meshmode.discretization.poly_element import \
                QuadratureSimplexGroupFactory
        discr = DiscretizationCollection(actx,
                                         mesh,
                                         order=builder.order,
                                         discr_tag_to_group_factory={
                                             "product":
                                             QuadratureSimplexGroupFactory(
                                                 2 * builder.order)
                                         })

        volume = discr.discr_from_dd(dof_desc.DD_VOLUME)
        logger.info("ndofs:     %d", volume.ndofs)
        logger.info("nelements: %d", volume.mesh.nelements)

        dd = dof_desc.DD_VOLUME
        dq = dd.with_discr_tag("product")
        df = dof_desc.as_dofdesc(FACE_RESTR_ALL)
        ambient_dim = discr.ambient_dim
        dim = discr.dim

        # variables
        sym_f = f(sym.nodes(ambient_dim, dd=dd))
        sym_f_quad = f(sym.nodes(ambient_dim, dd=dq))
        sym_kappa = sym.summed_curvature(ambient_dim, dim=dim, dd=dq)
        sym_normal = sym.surface_normal(ambient_dim, dim=dim,
                                        dd=dq).as_vector()

        sym_face_normal = sym.normal(df, ambient_dim, dim=dim - 1)
        sym_face_f = sym.project(dd, df)(sym_f)

        # operators
        sym_stiff = sum(
            sym.StiffnessOperator(d)(f) for d, f in enumerate(sym_f))
        sym_stiff_t = sum(
            sym.StiffnessTOperator(d)(f) for d, f in enumerate(sym_f))
        sym_k = sym.MassOperator(dq,
                                 dd)(sym_kappa * sym_f_quad.dot(sym_normal))
        sym_flux = sym.FaceMassOperator()(sym_face_f.dot(sym_face_normal))

        # sum everything up
        sym_op_global = sym.NodalSum(dd)(sym_stiff - (sym_stiff_t + sym_k))
        sym_op_local = sym.ElementwiseSumOperator(dd)(sym_stiff -
                                                      (sym_stiff_t + sym_k +
                                                       sym_flux))

        # evaluate
        op_global = bind(discr, sym_op_global)(actx)
        op_local = bind(discr, sym_op_local)(actx)

        err_global = abs(op_global)
        err_local = bind(discr, sym.norm(np.inf, sym.var("x")))(actx,
                                                                x=op_local)
        logger.info("errors: global %.5e local %.5e", err_global, err_local)

        # compute max element size
        h_max = bind(
            discr,
            sym.h_max_from_volume(discr.ambient_dim, dim=discr.dim,
                                  dd=dd))(actx)
        eoc_global.add_data_point(h_max, err_global)
        eoc_local.add_data_point(h_max, err_local)

        if visualize:
            from grudge.shortcuts import make_visualizer
            vis = make_visualizer(discr, vis_order=builder.order)

            filename = f"surface_divergence_theorem_{mesh_name}_{i:04d}.vtu"
            vis.write_vtk_file(filename, [("r", actx.np.log10(op_local))],
                               overwrite=True)

    # }}}

    order = min(builder.order, builder.mesh_order) - 0.5
    logger.info("\n%s", str(eoc_global))
    logger.info("\n%s", str(eoc_local))

    assert eoc_global.max_error() < 1.0e-12 \
            or eoc_global.order_estimate() > order - 0.5

    assert eoc_local.max_error() < 1.0e-12 \
            or eoc_local.order_estimate() > order - 0.5