def test_resampling_matrix(dims): ncoarse = 5 nfine = 10 coarse_nodes = mp.warp_and_blend_nodes(dims, ncoarse) fine_nodes = mp.warp_and_blend_nodes(dims, nfine) coarse_basis = mp.simplex_onb(dims, ncoarse) fine_basis = mp.simplex_onb(dims, nfine) my_eye = np.dot( mp.resampling_matrix(fine_basis, coarse_nodes, fine_nodes), mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes)) assert la.norm(my_eye - np.eye(len(my_eye))) < 1e-13 my_eye_least_squares = np.dot( mp.resampling_matrix(coarse_basis, coarse_nodes, fine_nodes, least_squares_ok=True), mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes), ) assert la.norm(my_eye_least_squares - np.eye(len(my_eye_least_squares))) < 4e-13
def test_nodal_face_mass_matrix(dim, order=3): from modepy.tools import unit_vertices all_verts = unit_vertices(dim).T basis = mp.simplex_onb(dim, order) np.set_printoptions(linewidth=200) from modepy.matrices import nodal_face_mass_matrix volume_nodes = mp.warp_and_blend_nodes(dim, order) face_nodes = mp.warp_and_blend_nodes(dim-1, order) for iface in range(dim+1): verts = np.hstack([all_verts[:, :iface], all_verts[:, iface+1:]]) fmm = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order, verts) fmm2 = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order+1, verts) assert la.norm(fmm-fmm2, np.inf) < 1e-11 fmm[np.abs(fmm) < 1e-13] = 0 print(fmm) nnz = np.sum(np.abs(fmm) > 0) print(nnz) print(mp.mass_matrix( mp.simplex_onb(dim-1, order), mp.warp_and_blend_nodes(dim-1, order), ))
def test_nodal_face_mass_matrix(dim, order=3): from modepy.tools import unit_vertices all_verts = unit_vertices(dim).T basis = mp.simplex_onb(dim, order) np.set_printoptions(linewidth=200) from modepy.matrices import nodal_face_mass_matrix volume_nodes = mp.warp_and_blend_nodes(dim, order) face_nodes = mp.warp_and_blend_nodes(dim - 1, order) for iface in range(dim + 1): verts = np.hstack([all_verts[:, :iface], all_verts[:, iface + 1:]]) fmm = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order, verts) fmm2 = nodal_face_mass_matrix(basis, volume_nodes, face_nodes, order + 1, verts) assert la.norm(fmm - fmm2, np.inf) < 1e-11 fmm[np.abs(fmm) < 1e-13] = 0 print(fmm) nnz = np.sum(np.abs(fmm) > 0) print(nnz) print( mp.mass_matrix( mp.simplex_onb(dim - 1, order), mp.warp_and_blend_nodes(dim - 1, order), ))
def test_diff_matrix(dims, eltype): n = 5 if eltype == "simplex": nodes = mp.warp_and_blend_nodes(dims, n) basis = mp.simplex_onb(dims, n) grad_basis = mp.grad_simplex_onb(dims, n) elif eltype == "tensor": nodes = mp.legendre_gauss_lobatto_tensor_product_nodes(dims, n) basis = mp.legendre_tensor_product_basis(dims, n) grad_basis = mp.grad_legendre_tensor_product_basis(dims, n) else: raise ValueError(f"unknown element type: {eltype}") diff_mat = mp.differentiation_matrices(basis, grad_basis, nodes) if isinstance(diff_mat, tuple): diff_mat = diff_mat[0] f = np.sin(nodes[0]) df_dx = np.cos(nodes[0]) df_dx_num = np.dot(diff_mat, f) error = la.norm(df_dx - df_dx_num) / la.norm(df_dx) logger.info("error: %.5e", error) assert error < 2.0e-4, error
def plot_element_values(n, nodes, values, resample_n=None, node_tuples=None, show_nodes=False): dims = len(nodes) orig_nodes = nodes orig_values = values if resample_n is not None: import modepy as mp basis = mp.simplex_onb(dims, n) fine_nodes = mp.equidistant_nodes(dims, resample_n) values = np.dot(mp.resampling_matrix(basis, fine_nodes, nodes), values) nodes = fine_nodes n = resample_n from pytools import generate_nonnegative_integer_tuples_summing_to_at_most \ as gnitstam if dims == 1: import matplotlib.pyplot as pt pt.plot(nodes[0], values) if show_nodes: pt.plot(orig_nodes[0], orig_values, "x") pt.show() elif dims == 2: import mayavi.mlab as mlab mlab.triangular_mesh( nodes[0], nodes[1], values, submesh(list(gnitstam(n, 2)))) if show_nodes: mlab.points3d(orig_nodes[0], orig_nodes[1], orig_values, scale_factor=0.05) mlab.show() else: raise RuntimeError("unsupported dimensionality %d" % dims)
def _resample_matrix(self, elgroup_index, ibatch_index): import modepy as mp ibatch = self.groups[elgroup_index].batches[ibatch_index] from_grp = self.from_discr.groups[elgroup_index] return mp.resampling_matrix( mp.simplex_onb(self.from_discr.dim, from_grp.order), ibatch.result_unit_nodes, from_grp.unit_nodes)
def estimate_resid(inner_n): nodes = mp.warp_and_blend_nodes(dims, inner_n) basis = mp.simplex_onb(dims, inner_n) vdm = mp.vandermonde(basis, nodes) f = test_func(nodes[0]) coeffs = la.solve(vdm, f) from modepy.modal_decay import estimate_relative_expansion_residual return estimate_relative_expansion_residual(coeffs.reshape(1, -1), dims, inner_n)
def estimate_resid(inner_n): nodes = mp.warp_and_blend_nodes(dims, inner_n) basis = mp.simplex_onb(dims, inner_n) vdm = mp.vandermonde(basis, nodes) f = test_func(nodes[0]) coeffs = la.solve(vdm, f) from modepy.modal_decay import estimate_relative_expansion_residual return estimate_relative_expansion_residual( coeffs.reshape(1, -1), dims, inner_n)
def test_resampling_matrix(dims, eltype): ncoarse = 5 nfine = 10 if eltype == "simplex": coarse_nodes = mp.warp_and_blend_nodes(dims, ncoarse) fine_nodes = mp.warp_and_blend_nodes(dims, nfine) coarse_basis = mp.simplex_onb(dims, ncoarse) fine_basis = mp.simplex_onb(dims, nfine) elif eltype == "tensor": coarse_nodes = mp.legendre_gauss_lobatto_tensor_product_nodes( dims, ncoarse) fine_nodes = mp.legendre_gauss_lobatto_tensor_product_nodes( dims, nfine) coarse_basis = mp.legendre_tensor_product_basis(dims, ncoarse) fine_basis = mp.legendre_tensor_product_basis(dims, nfine) else: raise ValueError(f"unknown element type: {eltype}") my_eye = np.dot( mp.resampling_matrix(fine_basis, coarse_nodes, fine_nodes), mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes)) assert la.norm(my_eye - np.eye(len(my_eye))) < 3e-13 my_eye_least_squares = np.dot( mp.resampling_matrix(coarse_basis, coarse_nodes, fine_nodes, least_squares_ok=True), mp.resampling_matrix(coarse_basis, fine_nodes, coarse_nodes), ) assert la.norm(my_eye_least_squares - np.eye(len(my_eye_least_squares))) < 4e-13
def test_diff_matrix(dims): n = 5 nodes = mp.warp_and_blend_nodes(dims, n) f = np.sin(nodes[0]) df_dx = np.cos(nodes[0]) diff_mat = mp.differentiation_matrices(mp.simplex_onb(dims, n), mp.grad_simplex_onb(dims, n), nodes) if isinstance(diff_mat, tuple): diff_mat = diff_mat[0] df_dx_num = np.dot(diff_mat, f) print(la.norm(df_dx - df_dx_num)) assert la.norm(df_dx - df_dx_num) < 1e-3
def flip_simplex_element_group(vertices, grp, grp_flip_flags): from modepy.tools import barycentric_to_unit, unit_to_barycentric from meshmode.mesh import SimplexElementGroup if not isinstance(grp, SimplexElementGroup): raise NotImplementedError("flips only supported on " "exclusively SimplexElementGroup-based meshes") # Swap the first two vertices on elements to be flipped. new_vertex_indices = grp.vertex_indices.copy() new_vertex_indices[grp_flip_flags, 0] \ = grp.vertex_indices[grp_flip_flags, 1] new_vertex_indices[grp_flip_flags, 1] \ = grp.vertex_indices[grp_flip_flags, 0] # Generate a resampling matrix that corresponds to the # first two barycentric coordinates being swapped. bary_unit_nodes = unit_to_barycentric(grp.unit_nodes) flipped_bary_unit_nodes = bary_unit_nodes.copy() flipped_bary_unit_nodes[0, :] = bary_unit_nodes[1, :] flipped_bary_unit_nodes[1, :] = bary_unit_nodes[0, :] flipped_unit_nodes = barycentric_to_unit(flipped_bary_unit_nodes) flip_matrix = mp.resampling_matrix( mp.simplex_onb(grp.dim, grp.order), flipped_unit_nodes, grp.unit_nodes) flip_matrix[np.abs(flip_matrix) < 1e-15] = 0 # Flipping twice should be the identity assert la.norm( np.dot(flip_matrix, flip_matrix) - np.eye(len(flip_matrix))) < 1e-13 # Apply the flip matrix to the nodes. new_nodes = grp.nodes.copy() new_nodes[:, grp_flip_flags] = np.einsum( "ij,dej->dei", flip_matrix, grp.nodes[:, grp_flip_flags]) return SimplexElementGroup( grp.order, new_vertex_indices, new_nodes, unit_nodes=grp.unit_nodes)
def test_diff_matrix(dims): n = 5 nodes = mp.warp_and_blend_nodes(dims, n) f = np.sin(nodes[0]) df_dx = np.cos(nodes[0]) diff_mat = mp.differentiation_matrices( mp.simplex_onb(dims, n), mp.grad_simplex_onb(dims, n), nodes) if isinstance(diff_mat, tuple): diff_mat = diff_mat[0] df_dx_num = np.dot(diff_mat, f) print((la.norm(df_dx-df_dx_num))) assert la.norm(df_dx-df_dx_num) < 1e-3
def test_modal_decay(case_name, test_func, dims, n, expected_expn): nodes = mp.warp_and_blend_nodes(dims, n) basis = mp.simplex_onb(dims, n) vdm = mp.vandermonde(basis, nodes) f = test_func(nodes[0]) coeffs = la.solve(vdm, f) if 0: from modepy.tools import plot_element_values plot_element_values(n, nodes, f, resample_n=70, show_nodes=True) from modepy.modal_decay import fit_modal_decay expn, _ = fit_modal_decay(coeffs.reshape(1, -1), dims, n) expn = expn[0] print(f"{case_name}: computed: {expn:g}, expected: {expected_expn:g}") assert abs(expn - expected_expn) < 0.1
def test_diff_matrix_permutation(dims): order = 5 from pytools import \ generate_nonnegative_integer_tuples_summing_to_at_most as gnitstam node_tuples = list(gnitstam(order, dims)) simplex_onb = mp.simplex_onb(dims, order) grad_simplex_onb = mp.grad_simplex_onb(dims, order) nodes = np.array( mp.warp_and_blend_nodes(dims, order, node_tuples=node_tuples)) diff_matrices = mp.differentiation_matrices(simplex_onb, grad_simplex_onb, nodes) for iref_axis in range(dims): perm = mp.diff_matrix_permutation(node_tuples, iref_axis) assert la.norm(diff_matrices[iref_axis] - diff_matrices[0][perm][:, perm]) < 1e-10
def test_modal_decay(case_name, test_func, dims, n, expected_expn): nodes = mp.warp_and_blend_nodes(dims, n) basis = mp.simplex_onb(dims, n) vdm = mp.vandermonde(basis, nodes) f = test_func(nodes[0]) coeffs = la.solve(vdm, f) if 0: from modepy.tools import plot_element_values plot_element_values(n, nodes, f, resample_n=70, show_nodes=True) from modepy.modal_decay import fit_modal_decay expn, _ = fit_modal_decay(coeffs.reshape(1, -1), dims, n) expn = expn[0] print(("%s: computed: %g, expected: %g" % (case_name, expn, expected_expn))) assert abs(expn-expected_expn) < 0.1
def test_modal_face_mass_matrix(dim, order=3): from modepy.tools import unit_vertices all_verts = unit_vertices(dim).T basis = mp.simplex_onb(dim, order) # np.set_printoptions(linewidth=200) from modepy.matrices import modal_face_mass_matrix for iface in range(dim + 1): verts = np.hstack([all_verts[:, :iface], all_verts[:, iface + 1:]]) fmm = modal_face_mass_matrix(basis, order, verts) fmm2 = modal_face_mass_matrix(basis, order + 1, verts) assert la.norm(fmm - fmm2, np.inf) < 1e-11 fmm[np.abs(fmm) < 1e-13] = 0 print(fmm) nnz = np.sum(fmm > 0) print(nnz)
def test_modal_face_mass_matrix(dim, order=3): from modepy.tools import unit_vertices all_verts = unit_vertices(dim).T basis = mp.simplex_onb(dim, order) # np.set_printoptions(linewidth=200) from modepy.matrices import modal_face_mass_matrix for iface in range(dim+1): verts = np.hstack([all_verts[:, :iface], all_verts[:, iface+1:]]) fmm = modal_face_mass_matrix(basis, order, verts) fmm2 = modal_face_mass_matrix(basis, order+1, verts) assert la.norm(fmm-fmm2, np.inf) < 1e-11 fmm[np.abs(fmm) < 1e-13] = 0 print(fmm) nnz = np.sum(fmm > 0) print(nnz)
def make_face_restriction(discr, group_factory, boundary_tag, per_face_groups=False): """Create a mesh, a discretization and a connection to restrict a function on *discr* to its values on the edges of element faces denoted by *boundary_tag*. :arg boundary_tag: The boundary tag for which to create a face restriction. May be :class:`meshmode.discretization.connection.FRESTR_INTERIOR_FACES` to indicate interior faces, or :class:`meshmode.discretization.connection.FRESTR_ALL_FACES` to make a discretization consisting of all (interior and boundary) faces. :arg per_face_groups: If *True*, the resulting discretization is guaranteed to have groups organized as:: (grp0, face0), (grp0, face1), ... (grp0, faceN), (grp1, face0), (grp1, face1), ... (grp1, faceN), ... each with the elements in the same order as the originating group. If *False*, volume and boundary groups correspond with each other one-to-one, and an interpolation batch is created per face. :return: a :class:`meshmode.discretization.connection.DirectDiscretizationConnection` representing the new connection. The new boundary discretization can be obtained from the :attr:`meshmode.discretization.connection.DirectDiscretizationConnection.to_discr` attribute of the return value, and the corresponding new boundary mesh from that. """ if boundary_tag is None: boundary_tag = FACE_RESTR_INTERIOR from warnings import warn warn("passing *None* for boundary_tag is deprecated--pass " "FRESTR_INTERIOR_FACES instead", DeprecationWarning, stacklevel=2) logger.info("building face restriction: start") # {{{ gather boundary vertices bdry_vertex_vol_nrs = _get_face_vertices(discr.mesh, boundary_tag) vol_to_bdry_vertices = np.empty( discr.mesh.vertices.shape[-1], discr.mesh.vertices.dtype) vol_to_bdry_vertices.fill(-1) vol_to_bdry_vertices[bdry_vertex_vol_nrs] = np.arange( len(bdry_vertex_vol_nrs), dtype=np.intp) bdry_vertices = discr.mesh.vertices[:, bdry_vertex_vol_nrs] # }}} from meshmode.mesh import Mesh, SimplexElementGroup bdry_mesh_groups = [] connection_data = {} btag_bit = discr.mesh.boundary_tag_bit(boundary_tag) for igrp, (grp, fagrp_map) in enumerate( zip(discr.groups, discr.mesh.facial_adjacency_groups)): mgrp = grp.mesh_el_group if not isinstance(mgrp, SimplexElementGroup): raise NotImplementedError("can only take boundary of " "SimplexElementGroup-based meshes") # {{{ pull together per-group face lists group_boundary_faces = [] if boundary_tag is FACE_RESTR_INTERIOR: for fagrp in six.itervalues(fagrp_map): if fagrp.ineighbor_group is None: # boundary faces -> not looking for those continue group_boundary_faces.extend( zip(fagrp.elements, fagrp.element_faces)) elif boundary_tag is FACE_RESTR_ALL: group_boundary_faces.extend( (iel, iface) for iface in range(grp.mesh_el_group.nfaces) for iel in range(grp.nelements) ) else: bdry_grp = fagrp_map.get(None) if bdry_grp is not None: nb_el_bits = -bdry_grp.neighbors face_relevant_flags = (nb_el_bits & btag_bit) != 0 group_boundary_faces.extend( zip( bdry_grp.elements[face_relevant_flags], bdry_grp.element_faces[face_relevant_flags])) # }}} grp_face_vertex_indices = mgrp.face_vertex_indices() grp_vertex_unit_coordinates = mgrp.vertex_unit_coordinates() batch_base = 0 # group by face_id for face_id in range(mgrp.nfaces): batch_boundary_el_numbers_in_grp = np.array( [ ibface_el for ibface_el, ibface_face in group_boundary_faces if ibface_face == face_id], dtype=np.intp) # {{{ preallocate arrays for mesh group nbatch_elements = len(batch_boundary_el_numbers_in_grp) if per_face_groups or face_id == 0: if per_face_groups: ngroup_bdry_elements = nbatch_elements else: ngroup_bdry_elements = len(group_boundary_faces) vertex_indices = np.empty( (ngroup_bdry_elements, mgrp.dim+1-1), mgrp.vertex_indices.dtype) bdry_unit_nodes = mp.warp_and_blend_nodes(mgrp.dim-1, mgrp.order) bdry_unit_nodes_01 = (bdry_unit_nodes + 1)*0.5 vol_basis = mp.simplex_onb(mgrp.dim, mgrp.order) nbdry_unit_nodes = bdry_unit_nodes_01.shape[-1] nodes = np.empty( (discr.ambient_dim, ngroup_bdry_elements, nbdry_unit_nodes), dtype=np.float64) # }}} new_el_numbers = batch_base + np.arange(nbatch_elements) if not per_face_groups: batch_base += nbatch_elements # {{{ no per-element axes in these computations # Find boundary vertex indices loc_face_vertices = list(grp_face_vertex_indices[face_id]) # Find unit nodes for boundary element face_vertex_unit_coordinates = \ grp_vertex_unit_coordinates[loc_face_vertices] # Find A, b such that A [e_1 e_2] + b = [r_1 r_2] # (Notation assumes that the volume is 3D and the face is 2D. # Code does not.) b = face_vertex_unit_coordinates[0] A = ( # noqa face_vertex_unit_coordinates[1:] - face_vertex_unit_coordinates[0]).T face_unit_nodes = (np.dot(A, bdry_unit_nodes_01).T + b).T resampling_mat = mp.resampling_matrix( vol_basis, face_unit_nodes, mgrp.unit_nodes) # }}} # {{{ build information for mesh element group # Find vertex_indices glob_face_vertices = mgrp.vertex_indices[ batch_boundary_el_numbers_in_grp][:, loc_face_vertices] vertex_indices[new_el_numbers] = \ vol_to_bdry_vertices[glob_face_vertices] # Find nodes nodes[:, new_el_numbers, :] = np.einsum( "ij,dej->dei", resampling_mat, mgrp.nodes[:, batch_boundary_el_numbers_in_grp, :]) # }}} connection_data[igrp, face_id] = _ConnectionBatchData( group_source_element_indices=batch_boundary_el_numbers_in_grp, group_target_element_indices=new_el_numbers, A=A, b=b, ) is_last_face = face_id + 1 == mgrp.nfaces if per_face_groups or is_last_face: bdry_mesh_group = SimplexElementGroup( mgrp.order, vertex_indices, nodes, unit_nodes=bdry_unit_nodes) bdry_mesh_groups.append(bdry_mesh_group) bdry_mesh = Mesh(bdry_vertices, bdry_mesh_groups) from meshmode.discretization import Discretization bdry_discr = Discretization( discr.cl_context, bdry_mesh, group_factory) with cl.CommandQueue(discr.cl_context) as queue: connection = _build_boundary_connection( queue, discr, bdry_discr, connection_data, per_face_groups) logger.info("building face restriction: done") return connection
import numpy as np import modepy as mp n = 17 # use this total degree dimensions = 2 # Get a basis of orthonormal functions, and their derivatives. basis = mp.simplex_onb(dimensions, n) grad_basis = mp.grad_simplex_onb(dimensions, n) nodes = mp.warp_and_blend_nodes(dimensions, n) x, y = nodes # We want to compute the x derivative of this function: f = np.sin(5*x+y) df_dx = 5*np.cos(5*x+y) # The (generalized) Vandermonde matrix transforms coefficients into # nodal values. So we can find basis coefficients by applying its # inverse: f_coefficients = np.linalg.solve( mp.vandermonde(basis, nodes), f) # Now linearly combine the (x-)derivatives of the basis using # f_coefficients to compute the numerical derivatives. df_dx_num = np.dot( mp.vandermonde(grad_basis, nodes)[0], f_coefficients)
def make_face_restriction(discr, group_factory, boundary_tag, per_face_groups=False): """Create a mesh, a discretization and a connection to restrict a function on *discr* to its values on the edges of element faces denoted by *boundary_tag*. :arg boundary_tag: The boundary tag for which to create a face restriction. May be :class:`meshmode.discretization.connection.FRESTR_INTERIOR_FACES` to indicate interior faces, or :class:`meshmode.discretization.connection.FRESTR_ALL_FACES` to make a discretization consisting of all (interior and boundary) faces. :arg per_face_groups: If *True*, the resulting discretization is guaranteed to have groups organized as:: (grp0, face0), (grp0, face1), ... (grp0, faceN), (grp1, face0), (grp1, face1), ... (grp1, faceN), ... each with the elements in the same order as the originating group. If *False*, volume and boundary groups correspond with each other one-to-one, and an interpolation batch is created per face. :return: a :class:`meshmode.discretization.connection.DirectDiscretizationConnection` representing the new connection. The new boundary discretization can be obtained from the :attr:`meshmode.discretization.connection.DirectDiscretizationConnection.to_discr` attribute of the return value, and the corresponding new boundary mesh from that. """ if boundary_tag is None: boundary_tag = FACE_RESTR_INTERIOR from warnings import warn warn( "passing *None* for boundary_tag is deprecated--pass " "FRESTR_INTERIOR_FACES instead", DeprecationWarning, stacklevel=2) logger.info("building face restriction: start") # {{{ gather boundary vertices bdry_vertex_vol_nrs = _get_face_vertices(discr.mesh, boundary_tag) vol_to_bdry_vertices = np.empty(discr.mesh.vertices.shape[-1], discr.mesh.vertices.dtype) vol_to_bdry_vertices.fill(-1) vol_to_bdry_vertices[bdry_vertex_vol_nrs] = np.arange( len(bdry_vertex_vol_nrs), dtype=np.intp) bdry_vertices = discr.mesh.vertices[:, bdry_vertex_vol_nrs] # }}} from meshmode.mesh import Mesh, SimplexElementGroup bdry_mesh_groups = [] connection_data = {} btag_bit = discr.mesh.boundary_tag_bit(boundary_tag) for igrp, (grp, fagrp_map) in enumerate( zip(discr.groups, discr.mesh.facial_adjacency_groups)): mgrp = grp.mesh_el_group if not isinstance(mgrp, SimplexElementGroup): raise NotImplementedError("can only take boundary of " "SimplexElementGroup-based meshes") # {{{ pull together per-group face lists group_boundary_faces = [] if boundary_tag is FACE_RESTR_INTERIOR: for fagrp in six.itervalues(fagrp_map): if fagrp.ineighbor_group is None: # boundary faces -> not looking for those continue group_boundary_faces.extend( zip(fagrp.elements, fagrp.element_faces)) elif boundary_tag is FACE_RESTR_ALL: group_boundary_faces.extend( (iel, iface) for iface in range(grp.mesh_el_group.nfaces) for iel in range(grp.nelements)) else: bdry_grp = fagrp_map.get(None) if bdry_grp is not None: nb_el_bits = -bdry_grp.neighbors face_relevant_flags = (nb_el_bits & btag_bit) != 0 group_boundary_faces.extend( zip(bdry_grp.elements[face_relevant_flags], bdry_grp.element_faces[face_relevant_flags])) # }}} grp_face_vertex_indices = mgrp.face_vertex_indices() grp_vertex_unit_coordinates = mgrp.vertex_unit_coordinates() batch_base = 0 # group by face_id for face_id in range(mgrp.nfaces): batch_boundary_el_numbers_in_grp = np.array([ ibface_el for ibface_el, ibface_face in group_boundary_faces if ibface_face == face_id ], dtype=np.intp) # {{{ preallocate arrays for mesh group nbatch_elements = len(batch_boundary_el_numbers_in_grp) if per_face_groups or face_id == 0: if per_face_groups: ngroup_bdry_elements = nbatch_elements else: ngroup_bdry_elements = len(group_boundary_faces) vertex_indices = np.empty( (ngroup_bdry_elements, mgrp.dim + 1 - 1), mgrp.vertex_indices.dtype) bdry_unit_nodes = mp.warp_and_blend_nodes( mgrp.dim - 1, mgrp.order) bdry_unit_nodes_01 = (bdry_unit_nodes + 1) * 0.5 vol_basis = mp.simplex_onb(mgrp.dim, mgrp.order) nbdry_unit_nodes = bdry_unit_nodes_01.shape[-1] nodes = np.empty((discr.ambient_dim, ngroup_bdry_elements, nbdry_unit_nodes), dtype=np.float64) # }}} new_el_numbers = batch_base + np.arange(nbatch_elements) if not per_face_groups: batch_base += nbatch_elements # {{{ no per-element axes in these computations # Find boundary vertex indices loc_face_vertices = list(grp_face_vertex_indices[face_id]) # Find unit nodes for boundary element face_vertex_unit_coordinates = \ grp_vertex_unit_coordinates[loc_face_vertices] # Find A, b such that A [e_1 e_2] + b = [r_1 r_2] # (Notation assumes that the volume is 3D and the face is 2D. # Code does not.) b = face_vertex_unit_coordinates[0] A = ( # noqa face_vertex_unit_coordinates[1:] - face_vertex_unit_coordinates[0]).T face_unit_nodes = (np.dot(A, bdry_unit_nodes_01).T + b).T resampling_mat = mp.resampling_matrix(vol_basis, face_unit_nodes, mgrp.unit_nodes) # }}} # {{{ build information for mesh element group # Find vertex_indices glob_face_vertices = mgrp.vertex_indices[ batch_boundary_el_numbers_in_grp][:, loc_face_vertices] vertex_indices[new_el_numbers] = \ vol_to_bdry_vertices[glob_face_vertices] # Find nodes nodes[:, new_el_numbers, :] = np.einsum( "ij,dej->dei", resampling_mat, mgrp.nodes[:, batch_boundary_el_numbers_in_grp, :]) # }}} connection_data[igrp, face_id] = _ConnectionBatchData( group_source_element_indices=batch_boundary_el_numbers_in_grp, group_target_element_indices=new_el_numbers, A=A, b=b, ) is_last_face = face_id + 1 == mgrp.nfaces if per_face_groups or is_last_face: bdry_mesh_group = SimplexElementGroup( mgrp.order, vertex_indices, nodes, unit_nodes=bdry_unit_nodes) bdry_mesh_groups.append(bdry_mesh_group) bdry_mesh = Mesh(bdry_vertices, bdry_mesh_groups) from meshmode.discretization import Discretization bdry_discr = Discretization(discr.cl_context, bdry_mesh, group_factory) with cl.CommandQueue(discr.cl_context) as queue: connection = _build_boundary_connection(queue, discr, bdry_discr, connection_data, per_face_groups) logger.info("building face restriction: done") return connection
import numpy as np import modepy as mp n = 17 # use this total degree dimensions = 2 # Get a basis of orthonormal functions, and their derivatives. basis = mp.simplex_onb(dimensions, n) grad_basis = mp.grad_simplex_onb(dimensions, n) nodes = mp.warp_and_blend_nodes(dimensions, n) x, y = nodes # We want to compute the x derivative of this function: f = np.sin(5 * x + y) df_dx = 5 * np.cos(5 * x + y) # The (generalized) Vandermonde matrix transforms coefficients into # nodal values. So we can find basis coefficients by applying its # inverse: f_coefficients = np.linalg.solve(mp.vandermonde(basis, nodes), f) # Now linearly combine the (x-)derivatives of the basis using # f_coefficients to compute the numerical derivatives. df_dx_num = np.dot(mp.vandermonde(grad_basis, nodes)[0], f_coefficients) assert np.linalg.norm(df_dx - df_dx_num) < 1e-5
def resampling_matrix(self): meg = self.mesh_el_group return mp.resampling_matrix( mp.simplex_onb(self.dim, meg.order), self.unit_nodes, meg.unit_nodes)
def basis(self): return mp.simplex_onb(self.dim, self.order)
def basis(self): if self.dim <= 3: return mp.simplex_onb(self.dim, self.order) else: return mp.simplex_monomial_basis(self.dim, self.order)
def make_boundary_restriction(queue, discr, group_factory): """ :return: a tuple ``(bdry_mesh, bdry_discr, connection)`` """ logger.info("building boundary connection: start") # {{{ build face_map # maps (igrp, el_grp, face_id) to a frozenset of vertex IDs face_map = {} for igrp, mgrp in enumerate(discr.mesh.groups): grp_face_vertex_indices = mgrp.face_vertex_indices() for iel_grp in range(mgrp.nelements): for fid, loc_face_vertices in enumerate(grp_face_vertex_indices): face_vertices = frozenset( mgrp.vertex_indices[iel_grp, fvi] for fvi in loc_face_vertices ) face_map.setdefault(face_vertices, []).append( (igrp, iel_grp, fid)) del face_vertices # }}} boundary_faces = [ face_ids[0] for face_vertices, face_ids in six.iteritems(face_map) if len(face_ids) == 1] from pytools import flatten bdry_vertex_vol_nrs = sorted(set(flatten(six.iterkeys(face_map)))) vol_to_bdry_vertices = np.empty( discr.mesh.vertices.shape[-1], discr.mesh.vertices.dtype) vol_to_bdry_vertices.fill(-1) vol_to_bdry_vertices[bdry_vertex_vol_nrs] = np.arange( len(bdry_vertex_vol_nrs)) bdry_vertices = discr.mesh.vertices[:, bdry_vertex_vol_nrs] from meshmode.mesh import Mesh, SimplexElementGroup bdry_mesh_groups = [] connection_data = {} for igrp, grp in enumerate(discr.groups): mgrp = grp.mesh_el_group group_boundary_faces = [ (ibface_el, ibface_face) for ibface_group, ibface_el, ibface_face in boundary_faces if ibface_group == igrp] if not isinstance(mgrp, SimplexElementGroup): raise NotImplementedError("can only take boundary of " "SimplexElementGroup-based meshes") # {{{ Preallocate arrays for mesh group ngroup_bdry_elements = len(group_boundary_faces) vertex_indices = np.empty( (ngroup_bdry_elements, mgrp.dim+1-1), mgrp.vertex_indices.dtype) bdry_unit_nodes = mp.warp_and_blend_nodes(mgrp.dim-1, mgrp.order) bdry_unit_nodes_01 = (bdry_unit_nodes + 1)*0.5 vol_basis = mp.simplex_onb(mgrp.dim, mgrp.order) nbdry_unit_nodes = bdry_unit_nodes_01.shape[-1] nodes = np.empty( (discr.ambient_dim, ngroup_bdry_elements, nbdry_unit_nodes), dtype=np.float64) # }}} grp_face_vertex_indices = mgrp.face_vertex_indices() grp_vertex_unit_coordinates = mgrp.vertex_unit_coordinates() # batch by face_id batch_base = 0 for face_id in range(len(grp_face_vertex_indices)): batch_boundary_el_numbers_in_grp = np.array( [ ibface_el for ibface_el, ibface_face in group_boundary_faces if ibface_face == face_id], dtype=np.intp) new_el_numbers = np.arange( batch_base, batch_base + len(batch_boundary_el_numbers_in_grp)) # {{{ no per-element axes in these computations # Find boundary vertex indices loc_face_vertices = list(grp_face_vertex_indices[face_id]) # Find unit nodes for boundary element face_vertex_unit_coordinates = \ grp_vertex_unit_coordinates[loc_face_vertices] # Find A, b such that A [e_1 e_2] + b = [r_1 r_2] # (Notation assumes that the volume is 3D and the face is 2D. # Code does not.) b = face_vertex_unit_coordinates[0] A = ( face_vertex_unit_coordinates[1:] - face_vertex_unit_coordinates[0]).T face_unit_nodes = (np.dot(A, bdry_unit_nodes_01).T + b).T resampling_mat = mp.resampling_matrix( vol_basis, face_unit_nodes, mgrp.unit_nodes) # }}} # {{{ build information for mesh element group # Find vertex_indices glob_face_vertices = mgrp.vertex_indices[ batch_boundary_el_numbers_in_grp][:, loc_face_vertices] vertex_indices[new_el_numbers] = \ vol_to_bdry_vertices[glob_face_vertices] # Find nodes nodes[:, new_el_numbers, :] = np.einsum( "ij,dej->dei", resampling_mat, mgrp.nodes[:, batch_boundary_el_numbers_in_grp, :]) # }}} connection_data[igrp, face_id] = _ConnectionBatchData( group_source_element_indices=batch_boundary_el_numbers_in_grp, group_target_element_indices=new_el_numbers, A=A, b=b, ) batch_base += len(batch_boundary_el_numbers_in_grp) bdry_mesh_group = SimplexElementGroup( mgrp.order, vertex_indices, nodes, unit_nodes=bdry_unit_nodes) bdry_mesh_groups.append(bdry_mesh_group) bdry_mesh = Mesh(bdry_vertices, bdry_mesh_groups) from meshmode.discretization import Discretization bdry_discr = Discretization( discr.cl_context, bdry_mesh, group_factory) connection = _build_boundary_connection( queue, discr, bdry_discr, connection_data) logger.info("building boundary connection: done") return bdry_mesh, bdry_discr, connection