Ejemplo n.º 1
0
class DGDiscretization:
    def __init__(self, actx, mesh, order):
        self.order = order

        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import \
                PolynomialWarpAndBlendGroupFactory
        self.group_factory = PolynomialWarpAndBlendGroupFactory(order=order)
        self.volume_discr = Discretization(actx, mesh, self.group_factory)

        assert self.volume_discr.dim == 2

    @property
    def _setup_actx(self):
        return self.volume_discr._setup_actx

    @property
    def array_context(self):
        return self.volume_discr.array_context

    @property
    def dim(self):
        return self.volume_discr.dim

    # {{{ discretizations/connections

    @memoize_method
    def boundary_connection(self, boundary_tag):
        from meshmode.discretization.connection import make_face_restriction
        return make_face_restriction(self.volume_discr._setup_actx,
                                     self.volume_discr,
                                     self.group_factory,
                                     boundary_tag=boundary_tag)

    @memoize_method
    def interior_faces_connection(self):
        from meshmode.discretization.connection import (make_face_restriction,
                                                        FACE_RESTR_INTERIOR)
        return make_face_restriction(self.volume_discr._setup_actx,
                                     self.volume_discr,
                                     self.group_factory,
                                     FACE_RESTR_INTERIOR,
                                     per_face_groups=False)

    @memoize_method
    def opposite_face_connection(self):
        from meshmode.discretization.connection import \
                make_opposite_face_connection

        return make_opposite_face_connection(self._setup_actx,
                                             self.interior_faces_connection())

    @memoize_method
    def all_faces_connection(self):
        from meshmode.discretization.connection import (make_face_restriction,
                                                        FACE_RESTR_ALL)
        return make_face_restriction(self.volume_discr._setup_actx,
                                     self.volume_discr,
                                     self.group_factory,
                                     FACE_RESTR_ALL,
                                     per_face_groups=False)

    @memoize_method
    def get_to_all_face_embedding(self, where):
        from meshmode.discretization.connection import \
                make_face_to_all_faces_embedding

        faces_conn = self.get_connection("vol", where)
        return make_face_to_all_faces_embedding(self._setup_actx, faces_conn,
                                                self.get_discr("all_faces"))

    def get_connection(self, src, tgt):
        src_tgt = (src, tgt)

        if src_tgt == ("vol", "int_faces"):
            return self.interior_faces_connection()
        elif src_tgt == ("vol", "all_faces"):
            return self.all_faces_connection()
        elif src_tgt == ("vol", BTAG_ALL):
            return self.boundary_connection(tgt)
        elif src_tgt == ("int_faces", "all_faces"):
            return self.get_to_all_face_embedding(src)
        elif src_tgt == (BTAG_ALL, "all_faces"):
            return self.get_to_all_face_embedding(src)
        else:
            raise ValueError(f"locations '{src}'->'{tgt}' not understood")

    def interp(self, src, tgt, vec):
        if (isinstance(vec, np.ndarray) and vec.dtype.char == "O"
                and not isinstance(vec, DOFArray)):
            return obj_array_vectorize(lambda el: self.interp(src, tgt, el),
                                       vec)

        return self.get_connection(src, tgt)(vec)

    def get_discr(self, where):
        if where == "vol":
            return self.volume_discr
        elif where == "all_faces":
            return self.all_faces_connection().to_discr
        elif where == "int_faces":
            return self.interior_faces_connection().to_discr
        elif where == BTAG_ALL:
            return self.boundary_connection(where).to_discr
        else:
            raise ValueError(f"location '{where}' not understood")

    # }}}

    @memoize_method
    def parametrization_derivative(self):
        return freeze(
            parametrization_derivative(self._setup_actx, self.volume_discr))

    @memoize_method
    def vol_jacobian(self):
        [a, b], [c, d] = thaw(self._setup_actx,
                              self.parametrization_derivative())
        return freeze(a * d - b * c)

    @memoize_method
    def inverse_parametrization_derivative(self):
        [a, b], [c, d] = thaw(self._setup_actx,
                              self.parametrization_derivative())

        result = np.zeros((2, 2), dtype=object)
        det = a * d - b * c
        result[0, 0] = d / det
        result[0, 1] = -b / det
        result[1, 0] = -c / det
        result[1, 1] = a / det

        return freeze(result)

    def zeros(self, actx):
        return self.volume_discr.zeros(actx)

    def grad(self, vec):
        ipder = self.inverse_parametrization_derivative()

        dref = [
            self.volume_discr.num_reference_derivative((idim, ), vec)
            for idim in range(self.volume_discr.dim)
        ]

        return make_obj_array([
            sum(dref_i * ipder_i
                for dref_i, ipder_i in zip(dref, ipder[iambient]))
            for iambient in range(self.volume_discr.ambient_dim)
        ])

    def div(self, vecs):
        return sum(self.grad(vec_i)[i] for i, vec_i in enumerate(vecs))

    @memoize_method
    def normal(self, where):
        bdry_discr = self.get_discr(where)

        ((a, ), (b, )) = parametrization_derivative(self._setup_actx,
                                                    bdry_discr)

        nrm = 1 / (a**2 + b**2)**0.5
        return freeze(flat_obj_array(b * nrm, -a * nrm))

    @memoize_method
    def face_jacobian(self, where):
        bdry_discr = self.get_discr(where)

        ((a, ), (b, )) = parametrization_derivative(self._setup_actx,
                                                    bdry_discr)

        return freeze((a**2 + b**2)**0.5)

    @memoize_method
    def get_inverse_mass_matrix(self, grp, dtype):
        import modepy as mp
        matrix = mp.inverse_mass_matrix(grp.basis(), grp.unit_nodes)

        actx = self._setup_actx
        return actx.freeze(actx.from_numpy(matrix))

    def inverse_mass(self, vec):
        if (isinstance(vec, np.ndarray) and vec.dtype.char == "O"
                and not isinstance(vec, DOFArray)):
            return obj_array_vectorize(lambda el: self.inverse_mass(el), vec)

        @memoize_in(self, "elwise_linear_knl")
        def knl():
            return make_loopy_program(
                """{[iel,idof,j]:
                    0<=iel<nelements and
                    0<=idof<ndiscr_nodes_out and
                    0<=j<ndiscr_nodes_in}""",
                "result[iel,idof] = sum(j, mat[idof, j] * vec[iel, j])",
                name="diff")

        discr = self.volume_discr

        result = discr.empty_like(vec)

        for grp in discr.groups:
            matrix = self.get_inverse_mass_matrix(grp, vec.entry_dtype)

            vec.array_context.call_loopy(knl(),
                                         mat=matrix,
                                         result=result[grp.index],
                                         vec=vec[grp.index])

        return result / self.vol_jacobian()

    @memoize_method
    def get_local_face_mass_matrix(self, afgrp, volgrp, dtype):
        nfaces = volgrp.mesh_el_group.nfaces
        assert afgrp.nelements == nfaces * volgrp.nelements

        matrix = np.empty((volgrp.nunit_dofs, nfaces, afgrp.nunit_dofs),
                          dtype=dtype)

        from modepy.tools import UNIT_VERTICES
        import modepy as mp
        for iface, fvi in enumerate(
                volgrp.mesh_el_group.face_vertex_indices()):
            face_vertices = UNIT_VERTICES[volgrp.dim][np.array(fvi)].T
            matrix[:, iface, :] = mp.nodal_face_mass_matrix(
                volgrp.basis(), volgrp.unit_nodes, afgrp.unit_nodes,
                volgrp.order, face_vertices)

        actx = self._setup_actx
        return actx.freeze(actx.from_numpy(matrix))

    def face_mass(self, vec):
        if (isinstance(vec, np.ndarray) and vec.dtype.char == "O"
                and not isinstance(vec, DOFArray)):
            return obj_array_vectorize(lambda el: self.face_mass(el), vec)

        @memoize_in(self, "face_mass_knl")
        def knl():
            return make_loopy_program(
                """{[iel,idof,f,j]:
                    0<=iel<nelements and
                    0<=f<nfaces and
                    0<=idof<nvol_nodes and
                    0<=j<nface_nodes}""", "result[iel,idof] = "
                "sum(f, sum(j, mat[idof, f, j] * vec[f, iel, j]))",
                name="face_mass")

        all_faces_conn = self.get_connection("vol", "all_faces")
        all_faces_discr = all_faces_conn.to_discr
        vol_discr = all_faces_conn.from_discr

        result = vol_discr.empty_like(vec)

        fj = self.face_jacobian("all_faces")
        vec = vec * fj

        assert len(all_faces_discr.groups) == len(vol_discr.groups)

        for afgrp, volgrp in zip(all_faces_discr.groups, vol_discr.groups):
            nfaces = volgrp.mesh_el_group.nfaces

            matrix = self.get_local_face_mass_matrix(afgrp, volgrp,
                                                     vec.entry_dtype)

            vec.array_context.call_loopy(knl(),
                                         mat=matrix,
                                         result=result[volgrp.index],
                                         vec=vec[afgrp.index].reshape(
                                             nfaces, volgrp.nelements,
                                             afgrp.nunit_dofs))

        return result
Ejemplo n.º 2
0
class DGDiscretization:
    def __init__(self, cl_ctx, mesh, order):
        self.order = order

        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import \
                PolynomialWarpAndBlendGroupFactory
        self.group_factory = PolynomialWarpAndBlendGroupFactory(order=order)
        self.volume_discr = Discretization(cl_ctx, mesh, self.group_factory)

        assert self.volume_discr.dim == 2

    @property
    def cl_context(self):
        return self.volume_discr.cl_context

    @property
    def dim(self):
        return self.volume_discr.dim

    # {{{ discretizations/connections

    @memoize_method
    def boundary_connection(self, boundary_tag):
        from meshmode.discretization.connection import make_face_restriction
        return make_face_restriction(self.volume_discr,
                                     self.group_factory,
                                     boundary_tag=boundary_tag)

    @memoize_method
    def interior_faces_connection(self):
        from meshmode.discretization.connection import (make_face_restriction,
                                                        FACE_RESTR_INTERIOR)
        return make_face_restriction(self.volume_discr,
                                     self.group_factory,
                                     FACE_RESTR_INTERIOR,
                                     per_face_groups=False)

    @memoize_method
    def opposite_face_connection(self):
        from meshmode.discretization.connection import \
                make_opposite_face_connection

        return make_opposite_face_connection(self.interior_faces_connection())

    @memoize_method
    def all_faces_connection(self):
        from meshmode.discretization.connection import (make_face_restriction,
                                                        FACE_RESTR_ALL)
        return make_face_restriction(self.volume_discr,
                                     self.group_factory,
                                     FACE_RESTR_ALL,
                                     per_face_groups=False)

    @memoize_method
    def get_to_all_face_embedding(self, where):
        from meshmode.discretization.connection import \
                make_face_to_all_faces_embedding

        faces_conn = self.get_connection("vol", where)
        return make_face_to_all_faces_embedding(faces_conn,
                                                self.get_discr("all_faces"))

    def get_connection(self, src, tgt):
        src_tgt = (src, tgt)

        if src_tgt == ("vol", "int_faces"):
            return self.interior_faces_connection()
        elif src_tgt == ("vol", "all_faces"):
            return self.all_faces_connection()
        elif src_tgt == ("vol", BTAG_ALL):
            return self.boundary_connection(tgt)
        elif src_tgt == ("int_faces", "all_faces"):
            return self.get_to_all_face_embedding(src)
        elif src_tgt == (BTAG_ALL, "all_faces"):
            return self.get_to_all_face_embedding(src)
        else:
            raise ValueError(f"locations '{src}'->'{tgt}' not understood")

    def interp(self, src, tgt, vec):
        if is_obj_array(vec):
            return with_object_array_or_scalar(
                lambda el: self.interp(src, tgt, el), vec)

        return self.get_connection(src, tgt)(vec.queue, vec)

    def get_discr(self, where):
        if where == "vol":
            return self.volume_discr
        elif where == "all_faces":
            return self.all_faces_connection().to_discr
        elif where == "int_faces":
            return self.interior_faces_connection().to_discr
        elif where == BTAG_ALL:
            return self.boundary_connection(where).to_discr
        else:
            raise ValueError(f"location '{where}' not understood")

    # }}}

    @memoize_method
    def parametrization_derivative(self):
        with cl.CommandQueue(self.cl_context) as queue:
            return without_queue(
                parametrization_derivative(queue, self.volume_discr))

    @memoize_method
    def vol_jacobian(self):
        with cl.CommandQueue(self.cl_context) as queue:
            [a, b], [c, d] = with_queue(queue,
                                        self.parametrization_derivative())
            return (a * d - b * c).with_queue(None)

    @memoize_method
    def inverse_parametrization_derivative(self):
        with cl.CommandQueue(self.cl_context) as queue:
            [a, b], [c, d] = with_queue(queue,
                                        self.parametrization_derivative())

            result = np.zeros((2, 2), dtype=object)
            det = a * d - b * c
            result[0, 0] = d / det
            result[0, 1] = -b / det
            result[1, 0] = -c / det
            result[1, 1] = a / det

            return without_queue(result)

    def zeros(self, queue):
        return self.volume_discr.zeros(queue)

    def grad(self, vec):
        ipder = self.inverse_parametrization_derivative()

        queue = vec.queue
        dref = [
            self.volume_discr.num_reference_derivative(queue, (idim, ),
                                                       vec).with_queue(queue)
            for idim in range(self.volume_discr.dim)
        ]

        return make_obj_array([
            sum(dref_i * ipder_i
                for dref_i, ipder_i in zip(dref, ipder[iambient]))
            for iambient in range(self.volume_discr.ambient_dim)
        ])

    def div(self, vecs):
        return sum(self.grad(vec_i)[i] for i, vec_i in enumerate(vecs))

    @memoize_method
    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))

    @memoize_method
    def face_jacobian(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))

            return ((a**2 + b**2)**0.5).with_queue(None)

    @memoize_method
    def get_inverse_mass_matrix(self, grp, dtype):
        import modepy as mp
        matrix = mp.inverse_mass_matrix(grp.basis(), grp.unit_nodes)

        with cl.CommandQueue(self.cl_context) as queue:
            return (cla.to_device(queue, matrix).with_queue(None))

    def inverse_mass(self, vec):
        if is_obj_array(vec):
            return with_object_array_or_scalar(
                lambda el: self.inverse_mass(el), vec)

        @memoize_in(self, "elwise_linear_knl")
        def knl():
            knl = lp.make_kernel("""{[k,i,j]:
                    0<=k<nelements and
                    0<=i<ndiscr_nodes_out and
                    0<=j<ndiscr_nodes_in}""",
                                 "result[k,i] = sum(j, mat[i, j] * vec[k, j])",
                                 default_offset=lp.auto,
                                 name="diff")

            knl = lp.split_iname(knl, "i", 16, inner_tag="l.0")
            return lp.tag_inames(knl, dict(k="g.0"))

        discr = self.volume_discr

        result = discr.empty(queue=vec.queue, dtype=vec.dtype)

        for grp in discr.groups:
            matrix = self.get_inverse_mass_matrix(grp, vec.dtype)

            knl()(vec.queue,
                  mat=matrix,
                  result=grp.view(result),
                  vec=grp.view(vec))

        return result / self.vol_jacobian()

    @memoize_method
    def get_local_face_mass_matrix(self, afgrp, volgrp, dtype):
        nfaces = volgrp.mesh_el_group.nfaces
        assert afgrp.nelements == nfaces * volgrp.nelements

        matrix = np.empty((volgrp.nunit_nodes, nfaces, afgrp.nunit_nodes),
                          dtype=dtype)

        from modepy.tools import UNIT_VERTICES
        import modepy as mp
        for iface, fvi in enumerate(
                volgrp.mesh_el_group.face_vertex_indices()):
            face_vertices = UNIT_VERTICES[volgrp.dim][np.array(fvi)].T
            matrix[:, iface, :] = mp.nodal_face_mass_matrix(
                volgrp.basis(), volgrp.unit_nodes, afgrp.unit_nodes,
                volgrp.order, face_vertices)

        with cl.CommandQueue(self.cl_context) as queue:
            return (cla.to_device(queue, matrix).with_queue(None))

    def face_mass(self, vec):
        if is_obj_array(vec):
            return with_object_array_or_scalar(lambda el: self.face_mass(el),
                                               vec)

        @memoize_in(self, "face_mass_knl")
        def knl():
            knl = lp.make_kernel(
                """{[k,i,f,j]:
                    0<=k<nelements and
                    0<=f<nfaces and
                    0<=i<nvol_nodes and
                    0<=j<nface_nodes}""",
                "result[k,i] = sum(f, sum(j, mat[i, f, j] * vec[f, k, j]))",
                default_offset=lp.auto,
                name="face_mass")

            knl = lp.split_iname(knl, "i", 16, inner_tag="l.0")
            return lp.tag_inames(knl, dict(k="g.0"))

        all_faces_conn = self.get_connection("vol", "all_faces")
        all_faces_discr = all_faces_conn.to_discr
        vol_discr = all_faces_conn.from_discr

        result = vol_discr.empty(queue=vec.queue, dtype=vec.dtype)

        fj = self.face_jacobian("all_faces")
        vec = vec * fj

        assert len(all_faces_discr.groups) == len(vol_discr.groups)

        for afgrp, volgrp in zip(all_faces_discr.groups, vol_discr.groups):
            nfaces = volgrp.mesh_el_group.nfaces

            matrix = self.get_local_face_mass_matrix(afgrp, volgrp, vec.dtype)

            input_view = afgrp.view(vec).reshape(nfaces, volgrp.nelements,
                                                 afgrp.nunit_nodes)
            knl()(vec.queue,
                  mat=matrix,
                  result=volgrp.view(result),
                  vec=input_view)

        return result