def test_modal_mass_matrix_for_face(dims, shape_cls, order=3): vol_shape = shape_cls(dims) vol_space = mp.space_for_shape(vol_shape, order) vol_basis = mp.basis_for_space(vol_space, vol_shape) from modepy.matrices import modal_mass_matrix_for_face for face in mp.faces_for_shape(vol_shape): face_space = mp.space_for_shape(face, order) face_basis = mp.basis_for_space(face_space, face) face_quad = mp.quadrature_for_space(mp.space_for_shape(face, 2*order), face) face_quad2 = mp.quadrature_for_space( mp.space_for_shape(face, 2*order+2), face) fmm = modal_mass_matrix_for_face( face, face_quad, face_basis.functions, vol_basis.functions) fmm2 = modal_mass_matrix_for_face( face, face_quad2, face_basis.functions, vol_basis.functions) error = la.norm(fmm - fmm2, np.inf) / la.norm(fmm2, np.inf) logger.info("fmm error: %.5e", error) assert error < 1e-11, f"error {error:.5e} on face {face.face_index}" fmm[np.abs(fmm) < 1e-13] = 0 nnz = np.sum(fmm > 0) logger.info("fmm: nnz %d\n%s", nnz, fmm)
def test_deprecated_nodal_face_mass_matrix(dims, order=3): # FIXME DEPRECATED remove along with nodal_face_mass_matrix (>=2022) vol_shape = mp.Simplex(dims) vol_space = mp.space_for_shape(vol_shape, order) vertices = mp.unit_vertices_for_shape(vol_shape) volume_nodes = mp.edge_clustered_nodes_for_space(vol_space, vol_shape) volume_basis = mp.basis_for_space(vol_space, vol_shape) from modepy.matrices import nodal_face_mass_matrix for face in mp.faces_for_shape(vol_shape): face_space = mp.space_for_shape(face, order) face_nodes = mp.edge_clustered_nodes_for_space(face_space, face) face_vertices = vertices[:, face.volume_vertex_indices] fmm = nodal_face_mass_matrix( volume_basis.functions, volume_nodes, face_nodes, order, face_vertices) fmm2 = nodal_face_mass_matrix( volume_basis.functions, volume_nodes, face_nodes, order+1, face_vertices) error = la.norm(fmm - fmm2, np.inf) / la.norm(fmm2, np.inf) logger.info("fmm error: %.5e", error) assert error < 5e-11, f"error {error:.5e} on face {face.face_index}" fmm[np.abs(fmm) < 1e-13] = 0 nnz = np.sum(fmm > 0) logger.info("fmm: nnz %d\n%s", nnz, fmm) logger.info("mass matrix:\n%s", mp.mass_matrix( mp.basis_for_space(face_space, face).functions, mp.edge_clustered_nodes_for_space(face_space, face)))
def test_deprecated_modal_face_mass_matrix(dims, order=3): # FIXME DEPRECATED remove along with modal_face_mass_matrix (>=2022) shape = mp.Simplex(dims) space = mp.space_for_shape(shape, order) vertices = mp.unit_vertices_for_shape(shape) basis = mp.basis_for_space(space, shape) from modepy.matrices import modal_face_mass_matrix for face in mp.faces_for_shape(shape): face_vertices = vertices[:, face.volume_vertex_indices] fmm = modal_face_mass_matrix( basis.functions, order, face_vertices) fmm2 = modal_face_mass_matrix( basis.functions, order+1, face_vertices) error = la.norm(fmm - fmm2, np.inf) / la.norm(fmm2, np.inf) logger.info("fmm error: %.5e", error) assert error < 1e-11, f"error {error:.5e} on face {face.face_index}" fmm[np.abs(fmm) < 1e-13] = 0 nnz = np.sum(fmm > 0) logger.info("fmm: nnz %d\n%s", nnz, fmm)
def test_normals(shape): vol_vertices = mp.unit_vertices_for_shape(shape) vol_centroid = np.mean(vol_vertices, axis=1) for face in mp.faces_for_shape(shape): face_vertices = vol_vertices[:, face.volume_vertex_indices] face_centroid = np.mean(face_vertices, axis=1) normal = mp.face_normal(face) assert normal @ (face_centroid-vol_centroid) > 0 for i in range(len(face_vertices)-1): assert abs( (face_vertices[:, i+1] - face_vertices[:, 0]) @ normal) < 1e-13 assert abs(la.norm(normal, 2) - 1) < 1e-13
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) import modepy as mp shape = mp.Simplex(volgrp.dim) unit_vertices = mp.unit_vertices_for_shape(shape).T for face in mp.faces_for_shape(shape): face_vertices = unit_vertices[np.array( face.volume_vertex_indices)].T matrix[:, face.face_index, :] = mp.nodal_face_mass_matrix( volgrp.basis_obj().functions, volgrp.unit_nodes, afgrp.unit_nodes, volgrp.order, face_vertices) actx = self._setup_actx return actx.freeze(actx.from_numpy(matrix))
def test_nodal_mass_matrix_for_face(dims, shape_cls, order=3): vol_shape = shape_cls(dims) vol_space = mp.space_for_shape(vol_shape, order) volume_nodes = mp.edge_clustered_nodes_for_space(vol_space, vol_shape) volume_basis = mp.basis_for_space(vol_space, vol_shape) from modepy.matrices import (nodal_mass_matrix_for_face, nodal_quad_mass_matrix_for_face) for face in mp.faces_for_shape(vol_shape): face_space = mp.space_for_shape(face, order) face_basis = mp.basis_for_space(face_space, face) face_nodes = mp.edge_clustered_nodes_for_space(face_space, face) face_quad = mp.quadrature_for_space(mp.space_for_shape(face, 2*order), face) face_quad2 = mp.quadrature_for_space( mp.space_for_shape(face, 2*order+2), face) fmm = nodal_mass_matrix_for_face( face, face_quad, face_basis.functions, volume_basis.functions, volume_nodes, face_nodes) fmm2 = nodal_quad_mass_matrix_for_face( face, face_quad2, volume_basis.functions, volume_nodes) for f_face in face_basis.functions: fval_nodal = f_face(face_nodes) fval_quad = f_face(face_quad2.nodes) assert ( la.norm(fmm@fval_nodal - fmm2@fval_quad, np.inf) / la.norm(fval_quad)) < 3e-15 fmm[np.abs(fmm) < 1e-13] = 0 nnz = np.sum(fmm > 0) logger.info("fmm: nnz %d\n%s", nnz, fmm) logger.info("mass matrix:\n%s", mp.mass_matrix(face_basis.functions, face_nodes))
def 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) import modepy as mp from meshmode.discretization import ElementGroupWithBasis from meshmode.discretization.poly_element import \ QuadratureSimplexElementGroup n = volgrp.order m = afgrp.order vol_basis = volgrp.basis_obj() faces = mp.faces_for_shape(volgrp.shape) for iface, face in enumerate(faces): # If the face group is defined on a higher-order # quadrature grid, use the underlying quadrature rule if isinstance(afgrp, QuadratureSimplexElementGroup): face_quadrature = afgrp.quadrature_rule() if face_quadrature.exact_to < m: raise ValueError( "The face quadrature rule is only exact for polynomials " f"of total degree {face_quadrature.exact_to}. Please " "ensure a quadrature rule is used that is at least " f"exact for degree {m}.") else: # NOTE: This handles the general case where # volume and surface quadrature rules may have different # integration orders face_quadrature = mp.quadrature_for_space( mp.space_for_shape(face, 2 * max(n, m)), face) # If the group has a nodal basis and is unisolvent, # we use the basis on the face to compute the face mass matrix if (isinstance(afgrp, ElementGroupWithBasis) and afgrp.space.space_dim == afgrp.nunit_dofs): face_basis = afgrp.basis_obj() # Sanity check for face quadrature accuracy. Not integrating # degree N + M polynomials here is asking for a bad time. if face_quadrature.exact_to < m + n: raise ValueError( "The face quadrature rule is only exact for polynomials " f"of total degree {face_quadrature.exact_to}. Please " "ensure a quadrature rule is used that is at least " f"exact for degree {n+m}.") matrix[:, iface, :] = mp.nodal_mass_matrix_for_face( face, face_quadrature, face_basis.functions, vol_basis.functions, volgrp.unit_nodes, afgrp.unit_nodes, ) else: # Otherwise, we use a routine that is purely quadrature-based # (no need for explicit face basis functions) matrix[:, iface, :] = mp.nodal_quad_mass_matrix_for_face( face, face_quadrature, vol_basis.functions, volgrp.unit_nodes, ) return matrix
def _modepy_faces(self): return mp.faces_for_shape(self._modepy_shape)