Ejemplo n.º 1
0
def wave_flux(dcoll, c, w_tpair):
    dd = w_tpair.dd
    dd_quad = dd.with_discr_tag(DISCR_TAG_QUAD)

    u = w_tpair[0]
    v = w_tpair[1:]

    normal = thaw(u.int.array_context, op.normal(dcoll, dd))

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

    # upwind
    flux_weak += flat_obj_array(
        0.5 * (u.ext - u.int),
        0.5 * normal * np.dot(normal, v.ext - v.int),
    )

    # FIXME this flux is only correct for continuous c
    dd_allfaces_quad = dd_quad.with_dtag("all_faces")
    c_quad = op.project(dcoll, "vol", dd_quad, c)
    flux_quad = op.project(dcoll, dd, dd_quad, flux_weak)

    return op.project(dcoll, dd_quad, dd_allfaces_quad, c_quad * flux_quad)
Ejemplo n.º 2
0
Archivo: em.py Proyecto: sll2/grudge
    def absorbing_bc(self, w):
        """Construct part of the flux operator template for 1st order
        absorbing boundary conditions.
        """

        actx = get_container_context_recursively(w)
        absorb_normal = thaw(self.dcoll.normal(dd=self.absorb_tag), actx)

        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 = op.project(self.dcoll, "vol", self.absorb_tag, e)
        absorb_h = op.project(self.dcoll, "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 wave_operator(dcoll, c, w):
    u = w[0]
    v = w[1:]

    dir_u = op.project(dcoll, "vol", BTAG_ALL, u)
    dir_v = op.project(dcoll, "vol", BTAG_ALL, v)
    dir_bval = flat_obj_array(dir_u, dir_v)
    dir_bc = flat_obj_array(-dir_u, dir_v)

    dd_quad = DOFDesc("vol", DISCR_TAG_QUAD)
    c_quad = op.project(dcoll, "vol", dd_quad, c)
    w_quad = op.project(dcoll, "vol", dd_quad, w)
    u_quad = w_quad[0]
    v_quad = w_quad[1:]

    dd_allfaces_quad = DOFDesc("all_faces", DISCR_TAG_QUAD)

    return (op.inverse_mass(
        dcoll,
        flat_obj_array(-op.weak_local_div(dcoll, dd_quad, c_quad * v_quad),
                       -op.weak_local_grad(dcoll, dd_quad, c_quad * u_quad))
        +  # noqa: W504
        op.face_mass(
            dcoll, dd_allfaces_quad,
            wave_flux(dcoll, c=c, w_tpair=op.interior_trace_pair(dcoll, w)) +
            wave_flux(dcoll,
                      c=c,
                      w_tpair=TracePair(
                          BTAG_ALL, interior=dir_bval, exterior=dir_bc)))))
Ejemplo n.º 4
0
 def _interp_to_surf_quad(utpair):
     local_dd = utpair.dd
     local_dd_quad = local_dd.with_discr_tag(quadrature_tag)
     return TracePair(local_dd_quad,
                      interior=op.project(discr, local_dd, local_dd_quad,
                                          utpair.int),
                      exterior=op.project(discr, local_dd, local_dd_quad,
                                          utpair.ext))
Ejemplo n.º 5
0
Archivo: em.py Proyecto: sll2/grudge
    def pmc_bc(self, w):
        """Construct part of the flux operator template for PMC boundary conditions
        """
        e, h = self.split_eh(w)

        pmc_e = op.project(self.dcoll, "vol", self.pmc_tag, e)
        pmc_h = op.project(self.dcoll, "vol", self.pmc_tag, h)

        return flat_obj_array(pmc_e, -pmc_h)
Ejemplo n.º 6
0
    def __init__(self,
                 dcoll: DiscretizationCollection,
                 remote_rank,
                 vol_field,
                 tag=None):
        self.tag = self.base_tag
        if tag is not None:
            self.tag += tag

        self.dcoll = dcoll
        self.array_context = vol_field.array_context
        self.remote_btag = BTAG_PARTITION(remote_rank)
        self.bdry_discr = dcoll.discr_from_dd(self.remote_btag)

        from grudge.op import project

        self.local_dof_array = project(dcoll, "vol", self.remote_btag,
                                       vol_field)

        local_data = self.array_context.to_numpy(flatten(self.local_dof_array))
        comm = self.dcoll.mpi_communicator

        self.send_req = comm.Isend(local_data, remote_rank, tag=self.tag)
        self.remote_data_host = np.empty_like(local_data)
        self.recv_req = comm.Irecv(self.remote_data_host, remote_rank,
                                   self.tag)
Ejemplo n.º 7
0
    def flux(self, u_tpair):
        from grudge.dof_desc import DD_VOLUME

        surf_v = op.project(self.dcoll, DD_VOLUME,
                            u_tpair.dd.with_discr_tag(None), self.v)
        return surface_advection_weak_flux(self.dcoll, self.flux_type, u_tpair,
                                           surf_v)
Ejemplo n.º 8
0
def to_quad_int_tpairs(dcoll, u, quad_tag):
    from grudge.dof_desc import DISCR_TAG_QUAD
    from grudge.trace_pair import TracePair

    if issubclass(quad_tag, DISCR_TAG_QUAD):
        return [
            TracePair(tpair.dd.with_discr_tag(quad_tag),
                      interior=op.project(dcoll, tpair.dd,
                                          tpair.dd.with_discr_tag(quad_tag),
                                          tpair.int),
                      exterior=op.project(dcoll, tpair.dd,
                                          tpair.dd.with_discr_tag(quad_tag),
                                          tpair.ext))
            for tpair in op.interior_trace_pairs(dcoll, u)
        ]
    else:
        return op.interior_trace_pairs(dcoll, u)
Ejemplo n.º 9
0
def mv_normal(
    actx: ArrayContext,
    dcoll: DiscretizationCollection,
    dd,
) -> MultiVector:
    """Exterior unit normal as a :class:`~pymbolic.geometric_algebra.MultiVector`.
    This supports both volume discretizations
    (where ambient == topological dimension) and surface discretizations
    (where ambient == topological dimension + 1). In the latter case, extra
    processing ensures that the returned normal is in the local tangent space
    of the element at the point where the normal is being evaluated.

    :arg dd: a :class:`~grudge.dof_desc.DOFDesc` as the surface discretization.
    :returns: a :class:`~pymbolic.geometric_algebra.MultiVector`
        containing the unit normals.
    """
    import grudge.dof_desc as dof_desc

    dd = dof_desc.as_dofdesc(dd)

    dim = dcoll.discr_from_dd(dd).dim
    ambient_dim = dcoll.ambient_dim

    if dim == ambient_dim:
        raise ValueError(
            "may only request normals on domains whose topological "
            f"dimension ({dim}) differs from "
            f"their ambient dimension ({ambient_dim})")

    if dim == ambient_dim - 1:
        return rel_mv_normal(actx, dcoll, dd=dd)

    # NOTE: In the case of (d - 2)-dimensional curves, we don't really have
    # enough information on the face to decide what an "exterior face normal"
    # is (e.g the "normal" to a 1D curve in 3D space is actually a
    # "normal plane")
    #
    # The trick done here is that we take the surface normal, move it to the
    # face and then take a cross product with the face tangent to get the
    # correct exterior face normal vector.
    assert dim == ambient_dim - 2

    from grudge.op import project
    import grudge.dof_desc as dof_desc

    volm_normal = MultiVector(
        project(
            dcoll, dof_desc.DD_VOLUME, dd,
            rel_mv_normal(actx, dcoll,
                          dd=dof_desc.DD_VOLUME).as_vector(dtype=object)))
    pder = pseudoscalar(actx, dcoll, dd=dd)

    mv = -(volm_normal ^ pder) << volm_normal.I.inv()

    return mv / actx.np.sqrt(mv.norm_squared())
Ejemplo n.º 10
0
def wave_operator(dcoll, c, w):
    u = w[0]
    v = w[1:]

    dir_u = op.project(dcoll, "vol", BTAG_ALL, u)
    dir_v = op.project(dcoll, "vol", BTAG_ALL, v)
    dir_bval = flat_obj_array(dir_u, dir_v)
    dir_bc = flat_obj_array(-dir_u, dir_v)

    return (op.inverse_mass(
        dcoll,
        flat_obj_array(-c * op.weak_local_div(dcoll, v),
                       -c * op.weak_local_grad(dcoll, u)) +  # noqa: W504
        op.face_mass(
            dcoll,
            wave_flux(dcoll, c=c, w_tpair=op.interior_trace_pair(dcoll, w)) +
            wave_flux(dcoll,
                      c=c,
                      w_tpair=TracePair(
                          BTAG_ALL, interior=dir_bval, exterior=dir_bc)))))
Ejemplo n.º 11
0
def wave_operator(dcoll, c, w):
    u = w[0]
    v = w[1:]

    dir_u = op.project(dcoll, "vol", BTAG_ALL, u)
    dir_v = op.project(dcoll, "vol", BTAG_ALL, v)
    dir_bval = flat_obj_array(dir_u, dir_v)
    dir_bc = flat_obj_array(-dir_u, dir_v)

    dd_quad = DOFDesc("vol", DISCR_TAG_QUAD)
    c_quad = op.project(dcoll, "vol", dd_quad, c)
    w_quad = op.project(dcoll, "vol", dd_quad, w)
    u_quad = w_quad[0]
    v_quad = w_quad[1:]

    dd_allfaces_quad = DOFDesc("all_faces", DISCR_TAG_QUAD)

    return (
        op.inverse_mass(
            dcoll,
            flat_obj_array(
                -op.weak_local_div(dcoll, dd_quad, c_quad*v_quad),
                -op.weak_local_grad(dcoll, dd_quad, c_quad*u_quad) \
                # pylint: disable=invalid-unary-operand-type
            ) + op.face_mass(
                dcoll,
                dd_allfaces_quad,
                wave_flux(
                    dcoll, c=c,
                    w_tpair=op.bdry_trace_pair(dcoll,
                                               BTAG_ALL,
                                               interior=dir_bval,
                                               exterior=dir_bc)
                ) + sum(
                    wave_flux(dcoll, c=c, w_tpair=tpair)
                    for tpair in op.interior_trace_pairs(dcoll, w)
                )
            )
        )
    )
Ejemplo n.º 12
0
def simple_mpi_communication_entrypoint():
    cl_ctx = cl.create_some_context()
    queue = cl.CommandQueue(cl_ctx)
    actx = PyOpenCLArrayContext(queue, force_device_scalars=True)

    from meshmode.distributed import MPIMeshDistributor, get_partition_by_pymetis
    from meshmode.mesh import BTAG_ALL

    from mpi4py import MPI
    comm = MPI.COMM_WORLD
    num_parts = comm.Get_size()

    mesh_dist = MPIMeshDistributor(comm)

    if mesh_dist.is_mananger_rank():
        from meshmode.mesh.generation import generate_regular_rect_mesh
        mesh = generate_regular_rect_mesh(a=(-1,)*2,
                                          b=(1,)*2,
                                          nelements_per_axis=(2,)*2)

        part_per_element = get_partition_by_pymetis(mesh, num_parts)

        local_mesh = mesh_dist.send_mesh_parts(mesh, part_per_element, num_parts)
    else:
        local_mesh = mesh_dist.receive_mesh_part()

    dcoll = DiscretizationCollection(actx, local_mesh, order=5,
            mpi_communicator=comm)

    x = thaw(dcoll.nodes(), actx)
    myfunc = actx.np.sin(np.dot(x, [2, 3]))

    from grudge.dof_desc import as_dofdesc

    dd_int = as_dofdesc("int_faces")
    dd_vol = as_dofdesc("vol")
    dd_af = as_dofdesc("all_faces")

    all_faces_func = op.project(dcoll, dd_vol, dd_af, myfunc)
    int_faces_func = op.project(dcoll, dd_vol, dd_int, myfunc)
    bdry_faces_func = op.project(dcoll, BTAG_ALL, dd_af,
                                 op.project(dcoll, dd_vol, BTAG_ALL, myfunc))

    hopefully_zero = (
        op.project(
            dcoll, "int_faces", "all_faces",
            dcoll.opposite_face_connection()(int_faces_func)
        )
        + sum(op.project(dcoll, tpair.dd, "all_faces", tpair.int)
              for tpair in op.cross_rank_trace_pairs(dcoll, myfunc))
    ) - (all_faces_func - bdry_faces_func)

    error = actx.to_numpy(flat_norm(hopefully_zero, ord=np.inf))

    print(__file__)
    with np.printoptions(threshold=100000000, suppress=True):
        logger.debug(hopefully_zero)
    logger.info("error: %.5e", error)

    assert error < 1e-14
Ejemplo n.º 13
0
 def get_flux(u_tpair):
     dd = u_tpair.dd
     dd_allfaces = dd.with_dtag("all_faces")
     normal = thaw(dcoll.normal(dd), actx)
     u_avg = u_tpair.avg
     if vectorize:
         if nested:
             flux = make_obj_array(
                 [u_avg_i * normal for u_avg_i in u_avg])
         else:
             flux = np.outer(u_avg, normal)
     else:
         flux = u_avg * normal
     return op.project(dcoll, dd, dd_allfaces, flux)
Ejemplo n.º 14
0
    def operator(self, t, w):
        dcoll = self.dcoll
        u = w[0]
        v = w[1:]
        actx = u.array_context

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

        # dirichlet BCs -------------------------------------------------------
        dir_u = op.project(dcoll, "vol", self.dirichlet_tag, u)
        dir_v = op.project(dcoll, "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 = self.dirichlet_bc_f
            dir_bc = flat_obj_array(2 * dir_g - dir_u, dir_v)
        else:
            dir_bc = flat_obj_array(-dir_u, dir_v)

        # neumann BCs ---------------------------------------------------------
        neu_u = op.project(dcoll, "vol", self.neumann_tag, u)
        neu_v = op.project(dcoll, "vol", self.neumann_tag, v)
        neu_bc = flat_obj_array(neu_u, -neu_v)

        # radiation BCs -------------------------------------------------------
        rad_normal = thaw(dcoll.normal(dd=self.radiation_tag), actx)

        rad_u = op.project(dcoll, "vol", self.radiation_tag, u)
        rad_v = op.project(dcoll, "vol", self.radiation_tag, v)

        rad_bc = flat_obj_array(
            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 -----------------------------------------------------
        def flux(tpair):
            return op.project(dcoll, tpair.dd, "all_faces", self.flux(tpair))

        result = (op.inverse_mass(
            dcoll,
            flat_obj_array(-self.c * op.weak_local_div(dcoll, v),
                           -self.c * op.weak_local_grad(dcoll, u)) -
            op.face_mass(
                dcoll,
                sum(
                    flux(tpair)
                    for tpair in op.interior_trace_pairs(dcoll, w)) +
                flux(op.bv_trace_pair(dcoll, self.dirichlet_tag, w, dir_bc)) +
                flux(op.bv_trace_pair(dcoll, self.neumann_tag, w, neu_bc)) +
                flux(op.bv_trace_pair(dcoll, self.radiation_tag, w, rad_bc)))))

        result[0] = result[0] + self.source_f(actx, dcoll, t)

        return result
Ejemplo n.º 15
0
def wave_flux(dcoll, c, w_tpair):
    u = w_tpair.u
    v = w_tpair.v

    normal = thaw(dcoll.normal(w_tpair.dd), u.int.array_context)

    flux_weak = WaveState(u=v.avg @ normal, v=u.avg * normal)

    # upwind
    v_jump = v.diff @ normal
    flux_weak += WaveState(
        u=0.5 * u.diff,
        v=0.5 * v_jump * normal,
    )

    return op.project(dcoll, w_tpair.dd, "all_faces", c * flux_weak)
Ejemplo n.º 16
0
def v_dot_n_tpair(actx, dcoll, velocity, trace_dd):
    from grudge.dof_desc import DTAG_BOUNDARY
    from grudge.trace_pair import TracePair
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR

    normal = thaw(dcoll.normal(trace_dd.with_discr_tag(None)), actx)
    v_dot_n = velocity.dot(normal)
    i = op.project(dcoll, trace_dd.with_discr_tag(None), trace_dd, v_dot_n)

    if trace_dd.domain_tag is FACE_RESTR_INTERIOR:
        e = dcoll.opposite_face_connection()(i)
    elif isinstance(trace_dd.domain_tag, DTAG_BOUNDARY):
        e = dcoll.distributed_boundary_swap_connection(trace_dd)(i)
    else:
        raise ValueError("Unrecognized domain tag: %s" % trace_dd.domain_tag)

    return TracePair(trace_dd, interior=i, exterior=e)
Ejemplo n.º 17
0
def wave_flux(dcoll, c, w_tpair):
    u = w_tpair[0]
    v = w_tpair[1:]

    normal = thaw(u.int.array_context, op.normal(dcoll, w_tpair.dd))

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

    # upwind
    flux_weak += flat_obj_array(
        0.5 * (u.ext - u.int),
        0.5 * normal * np.dot(normal, v.ext - v.int),
    )

    return op.project(dcoll, w_tpair.dd, "all_faces", c * flux_weak)
Ejemplo n.º 18
0
    def _normal():
        dim = dcoll.discr_from_dd(dd).dim
        ambient_dim = dcoll.ambient_dim

        if dim == ambient_dim:
            raise ValueError(
                "may only request normals on domains whose topological "
                f"dimension ({dim}) differs from "
                f"their ambient dimension ({ambient_dim})")

        if dim == ambient_dim - 1:
            result = rel_mv_normal(actx, dcoll, dd=dd)
        else:
            # NOTE: In the case of (d - 2)-dimensional curves, we don't really have
            # enough information on the face to decide what an "exterior face normal"
            # is (e.g the "normal" to a 1D curve in 3D space is actually a
            # "normal plane")
            #
            # The trick done here is that we take the surface normal, move it to the
            # face and then take a cross product with the face tangent to get the
            # correct exterior face normal vector.
            assert dim == ambient_dim - 2

            from grudge.op import project

            volm_normal = MultiVector(
                project(
                    dcoll, dof_desc.DD_VOLUME, dd,
                    rel_mv_normal(
                        actx, dcoll,
                        dd=dof_desc.DD_VOLUME).as_vector(dtype=object)))
            pder = pseudoscalar(actx, dcoll, dd=dd)

            mv = -(volm_normal ^ pder) << volm_normal.I.inv()

            result = mv / actx.np.sqrt(mv.norm_squared())

        if _use_geoderiv_connection:
            result = dcoll._base_to_geoderiv_connection(dd)(result)

        return freeze(result, actx)
Ejemplo n.º 19
0
def _interior_trace_pair(dcoll: DiscretizationCollection, vec) -> TracePair:
    r"""Return a :class:`TracePair` for the interior faces of
    *dcoll* with a discretization tag specified by *discr_tag*.
    This does not include interior faces on different MPI ranks.

    :arg vec: a :class:`~meshmode.dof_array.DOFArray` or object array of
        :class:`~meshmode.dof_array.DOFArray`\ s.
    :returns: a :class:`TracePair` object.
    """
    from grudge.op import project

    i = project(dcoll, "vol", "int_faces", vec)

    def get_opposite_face(el):
        if isinstance(el, Number):
            return el
        else:
            return dcoll.opposite_face_connection()(el)

    e = obj_array_vectorize(get_opposite_face, i)

    return TracePair("int_faces", interior=i, exterior=e)
Ejemplo n.º 20
0
def bv_trace_pair(dcoll: DiscretizationCollection, dd, interior,
                  exterior) -> TracePair:
    """Returns a trace pair defined on the exterior boundary. The interior
    argument is assumed to be defined on the volume discretization, and will
    therefore be restricted to the boundary *dd* prior to creating a
    :class:`TracePair`.

    :arg dd: a :class:`~grudge.dof_desc.DOFDesc`, or a value convertible to one,
        which describes the boundary discretization.
    :arg interior: a :class:`~meshmode.dof_array.DOFArray` that contains data
        defined in the volume, which will be restricted to the boundary denoted
        by *dd*. The result will be used as the interior value
        for the flux.
    :arg exterior: a :class:`~meshmode.dof_array.DOFArray` that contains data
        that already lives on the boundary representing the exterior value to
        be used for the flux.
    :returns: a :class:`TracePair` on the boundary.
    """
    from grudge.op import project

    interior = project(dcoll, "vol", dd, interior)
    return bdry_trace_pair(dcoll, dd, interior, exterior)
Ejemplo n.º 21
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
    from meshmode.mesh import BTAG_ALL

    mesh = from_meshpy(mesh_info, order=1)

    actx = actx_factory()

    dcoll = DiscretizationCollection(actx, mesh, order=2)
    volm_disc = dcoll.discr_from_dd(dof_desc.DD_VOLUME)
    x_volm = thaw(volm_disc.nodes(), actx)

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

    f_volm = f(x_volm)
    int_1 = op.integral(dcoll, "vol", op.local_div(dcoll, f_volm))

    prj_f = op.project(dcoll, "vol", BTAG_ALL, f_volm)
    normal = thaw(dcoll.normal(BTAG_ALL), actx)
    int_2 = op.integral(dcoll, BTAG_ALL, prj_f.dot(normal))

    assert abs(int_1 - int_2) < 1e-13
Ejemplo n.º 22
0
def wave_operator(dcoll, c, w):
    u = w.u
    v = w.v

    dir_w = op.project(dcoll, "vol", BTAG_ALL, w)
    dir_u = dir_w.u
    dir_v = dir_w.v
    dir_bval = WaveState(u=dir_u, v=dir_v)
    dir_bc = WaveState(u=-dir_u, v=dir_v)

    return (op.inverse_mass(
        dcoll,
        WaveState(u=-c * op.weak_local_div(dcoll, v),
                  v=-c * op.weak_local_grad(dcoll, u)) +
        op.face_mass(
            dcoll,
            wave_flux(dcoll,
                      c=c,
                      w_tpair=op.bdry_trace_pair(
                          dcoll, BTAG_ALL, interior=dir_bval, exterior=dir_bc))
            + sum(
                wave_flux(dcoll, c=c, w_tpair=tpair)
                for tpair in op.interior_trace_pairs(dcoll, w)))))
Ejemplo n.º 23
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)
    dcoll = DiscretizationCollection(actx, mesh, order=builder.order)

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

    # }}}

    # {{{ Compute surface and face normals
    from meshmode.discretization.connection import FACE_RESTR_INTERIOR
    from grudge.geometry import normal

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

    ambient_dim = mesh.ambient_dim

    surf_normal = op.project(dcoll, dv, df, normal(actx, dcoll, dd=dv))
    surf_normal = surf_normal / actx.np.sqrt(sum(surf_normal**2))

    face_normal_i = thaw(dcoll.normal(df), actx)
    face_normal_e = dcoll.opposite_face_connection()(face_normal_i)

    if mesh.ambient_dim == 3:
        from grudge.geometry import pseudoscalar, area_element
        # NOTE: there's only one face tangent in 3d
        face_tangent = (pseudoscalar(actx, dcoll, dd=df) /
                        area_element(actx, dcoll, dd=df)).as_vector(
                            dtype=object)

    # }}}

    # {{{ checks

    def _eval_error(x):
        return op.norm(dcoll, x, np.inf, dd=df)

    rtol = 1.0e-14

    # 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:
        error = _eval_error(face_tangent.dot(face_normal_i))
        logger.info("error[t_dot_i]:  %.5e", error)
        assert error < 5 * rtol
Ejemplo n.º 24
0
dt = 0.001
t = 0
t_final = 0.5

# timestepper loop
while t < t_final:
    # extract the left boundary trace pair
    lbnd_tpair = op.bv_trace_pair(dcoll,
                                  dd=left_bndry,
                                  interior=uh,
                                  exterior=left_boundary_condition(x_bndry, t))
    # extract the right boundary trace pair
    rbnd_tpair = op.bv_trace_pair(dcoll,
                                  dd=right_bndry,
                                  interior=uh,
                                  exterior=op.project(dcoll, "vol",
                                                      right_bndry, uh))
    # extract the trace pairs on the interior faces
    interior_tpair = op.interior_trace_pair(dcoll,
                                            uh)
    Su = op.weak_local_grad(dcoll, uh)

    lift = op.face_mass(dcoll,
                        # left boundary weak-flux terms
                        op.project(dcoll,
                                   left_bndry, "all_faces",
                                   flux(dcoll, lbnd_tpair))
                        # right boundary weak-flux terms
                        + op.project(dcoll,
                                     right_bndry, "all_faces",
                                     flux(dcoll, rbnd_tpair))
                        # interior weak-flux terms
Ejemplo n.º 25
0
 def to_quad(arg):
     return op.project(dcoll, DD_VOLUME, quad_dd, arg)
Ejemplo n.º 26
0
Archivo: em.py Proyecto: sll2/grudge
 def flux(pair):
     return op.project(dcoll, pair.dd, "all_faces", self.flux(pair))
Ejemplo n.º 27
0
    def flux(self, u_tpair):
        from grudge.dof_desc import DD_VOLUME

        surf_v = op.project(self.dcoll, DD_VOLUME, u_tpair.dd, self.v)
        return advection_weak_flux(self.dcoll, self.flux_type, u_tpair, surf_v)
Ejemplo n.º 28
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)

    # }}}

    # {{{ convergence

    def f(x):
        return flat_obj_array(
            actx.np.sin(3 * x[1]) + actx.np.cos(3 * x[0]) + 1.0,
            actx.np.sin(2 * x[0]) + actx.np.cos(x[1]),
            3.0 * actx.np.cos(x[0] / 2) + actx.np.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

        qtag = dof_desc.DISCR_TAG_QUAD
        dcoll = DiscretizationCollection(actx,
                                         mesh,
                                         order=builder.order,
                                         discr_tag_to_group_factory={
                                             qtag:
                                             QuadratureSimplexGroupFactory(
                                                 2 * builder.order)
                                         })

        volume = dcoll.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(qtag)
        df = dof_desc.as_dofdesc(FACE_RESTR_ALL)
        ambient_dim = dcoll.ambient_dim

        # variables
        f_num = f(thaw(dcoll.nodes(dd=dd), actx))
        f_quad_num = f(thaw(dcoll.nodes(dd=dq), actx))

        from grudge.geometry import normal, summed_curvature

        kappa = summed_curvature(actx, dcoll, dd=dq)
        normal = normal(actx, dcoll, dd=dq)
        face_normal = thaw(dcoll.normal(df), actx)
        face_f = op.project(dcoll, dd, df, f_num)

        # operators
        stiff = op.mass(
            dcoll,
            sum(
                op.local_d_dx(dcoll, i, f_num_i)
                for i, f_num_i in enumerate(f_num)))
        stiff_t = sum(
            op.weak_local_d_dx(dcoll, i, f_num_i)
            for i, f_num_i in enumerate(f_num))
        kterm = op.mass(dcoll, dq, kappa * f_quad_num.dot(normal))
        flux = op.face_mass(dcoll, face_f.dot(face_normal))

        # sum everything up
        op_global = op.nodal_sum(dcoll, dd, stiff - (stiff_t + kterm))
        op_local = op.elementwise_sum(dcoll, dd,
                                      stiff - (stiff_t + kterm + flux))

        err_global = abs(op_global)
        err_local = op.norm(dcoll, op_local, np.inf)
        logger.info("errors: global %.5e local %.5e", err_global, err_local)

        # compute max element size
        from grudge.dt_utils import h_max_from_volume

        h_max = h_max_from_volume(dcoll)

        eoc_global.add_data_point(h_max, actx.to_numpy(err_global))
        eoc_local.add_data_point(h_max, err_local)

        if visualize:
            from grudge.shortcuts import make_visualizer
            vis = make_visualizer(dcoll)

            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
Ejemplo n.º 29
0
 def project(self, src, tgt, vec):
     return op.project(self, src, tgt, vec)
Ejemplo n.º 30
0
 def flux(tpair):
     return op.project(dcoll, tpair.dd, face_dd, self.flux(tpair))