def from_vertices_and_simplices(vertices, simplices, order=1, fix_orientation=False): """Imports a mesh from a numpy array of vertices and an array of simplices. :arg vertices: An array of vertex coordinates with shape *(ambient_dim, nvertices)* :arg simplices: An array *(nelements, nvertices)* of (mesh-wide) vertex indices. """ from meshmode.mesh import Mesh from meshmode.mesh.generation import make_group_from_vertices grp = make_group_from_vertices(vertices, simplices, order) if fix_orientation: if grp.dim != vertices.shape[0]: raise ValueError("can only fix orientation of volume meshes") from meshmode.mesh.processing import ( find_volume_mesh_element_group_orientation, flip_simplex_element_group) orient = find_volume_mesh_element_group_orientation(vertices, grp) grp = flip_simplex_element_group(vertices, grp, orient < 0) return Mesh( vertices=vertices, groups=[grp], is_conforming=True)
def from_vertices_and_simplices(vertices, simplices, order=1, fix_orientation=False): """Imports a mesh from a numpy array of vertices and an array of simplices. :arg vertices: An array of vertex coordinates with shape *(ambient_dim, nvertices)* :arg simplices: An array *(nelements, nvertices)* of (mesh-wide) vertex indices. """ from meshmode.mesh import Mesh from meshmode.mesh.generation import make_group_from_vertices grp = make_group_from_vertices(vertices, simplices, order) if fix_orientation: from meshmode.mesh.processing import ( find_volume_mesh_element_group_orientation, flip_simplex_element_group) orient = find_volume_mesh_element_group_orientation(vertices, grp) grp = flip_simplex_element_group(vertices, grp, orient < 0) return Mesh(vertices=vertices, groups=[grp], nodal_adjacency=None, facial_adjacency_groups=None)
def from_vertices_and_simplices(vertices, simplices, order=1, fix_orientation=False): """Imports a mesh from a numpy array of vertices and an array of simplices. :arg vertices: An array of vertex coordinates with shape *(ambient_dim, nvertices)* :arg simplices: An array *(nelements, nvertices)* of (mesh-wide) vertex indices. """ from meshmode.mesh import Mesh from meshmode.mesh.generation import make_group_from_vertices grp = make_group_from_vertices(vertices, simplices, order) if fix_orientation: from meshmode.mesh.processing import ( find_volume_mesh_element_group_orientation, flip_simplex_element_group) orient = find_volume_mesh_element_group_orientation(vertices, grp) grp = flip_simplex_element_group(vertices, grp, orient < 0) return Mesh( vertices=vertices, groups=[grp], nodal_adjacency=None, facial_adjacency_groups=None)
def group(self): if self._group is None: from meshmode.mesh import SimplexElementGroup from meshmode.mesh.processing import flip_simplex_element_group finat_element_a = self.coordinates_a.function_space_a().finat_element_a # IMPORTANT that set :attr:`_group` because # :meth:`orientations` may call :meth:`group` self._group = SimplexElementGroup( finat_element_a.analog().degree, self.vertex_indices(), self.nodes(), dim=self.cell_dimension(), unit_nodes=finat_element_a.unit_nodes()) self._group = flip_simplex_element_group(self.vertices(), self._group, self.orientations() < 0) return self._group
def get_mesh(self): el_type_hist = {} for el_type in self.element_types: el_type_hist[el_type] = el_type_hist.get(el_type, 0) + 1 if not el_type_hist: raise RuntimeError("empty mesh in gmsh input") groups = self.groups = [] ambient_dim = self.points.shape[-1] mesh_bulk_dim = max(el_type.dimensions for el_type in el_type_hist) # {{{ build vertex numbering # map set of face vertex indices to list of tags associated to face face_vertex_indices_to_tags = {} vertex_gmsh_index_to_mine = {} for element, el_vertices in enumerate(self.element_vertices): for gmsh_vertex_nr in el_vertices: if gmsh_vertex_nr not in vertex_gmsh_index_to_mine: vertex_gmsh_index_to_mine[gmsh_vertex_nr] = \ len(vertex_gmsh_index_to_mine) if self.tags: el_tag_indexes = [self.gmsh_tag_index_to_mine[t] for t in self.element_markers[element]] # record tags of boundary dimension el_tags = [self.tags[i][0] for i in el_tag_indexes if self.tags[i][1] == mesh_bulk_dim - 1] el_grp_verts = {vertex_gmsh_index_to_mine[e] for e in el_vertices} face_vertex_indices = frozenset(el_grp_verts) if face_vertex_indices not in face_vertex_indices_to_tags: face_vertex_indices_to_tags[face_vertex_indices] = [] face_vertex_indices_to_tags[face_vertex_indices] += el_tags # }}} # {{{ build vertex array gmsh_vertex_indices, my_vertex_indices = \ list(zip(*vertex_gmsh_index_to_mine.items())) vertices = np.empty( (ambient_dim, len(vertex_gmsh_index_to_mine)), dtype=np.float64) vertices[:, np.array(my_vertex_indices, np.intp)] = \ self.points[np.array(gmsh_vertex_indices, np.intp)].T # }}} from meshmode.mesh import (Mesh, SimplexElementGroup, TensorProductElementGroup) bulk_el_types = set() for group_el_type, ngroup_elements in el_type_hist.items(): if group_el_type.dimensions != mesh_bulk_dim: continue bulk_el_types.add(group_el_type) nodes = np.empty( (ambient_dim, ngroup_elements, group_el_type.node_count()), np.float64) el_vertex_count = group_el_type.vertex_count() vertex_indices = np.empty( (ngroup_elements, el_vertex_count), np.int32) i = 0 for el_vertices, el_nodes, el_type in zip( self.element_vertices, self.element_nodes, self.element_types): if el_type is not group_el_type: continue nodes[:, i] = self.points[el_nodes].T vertex_indices[i] = [ vertex_gmsh_index_to_mine[v_nr] for v_nr in el_vertices ] i += 1 import modepy as mp if isinstance(group_el_type, GmshSimplexElementBase): shape = mp.Simplex(group_el_type.dimensions) elif isinstance(group_el_type, GmshTensorProductElementBase): shape = mp.Hypercube(group_el_type.dimensions) else: raise NotImplementedError( f"gmsh element type: {type(group_el_type).__name__}") space = mp.space_for_shape(shape, group_el_type.order) unit_nodes = mp.equispaced_nodes_for_space(space, shape) if isinstance(group_el_type, GmshSimplexElementBase): group = SimplexElementGroup( group_el_type.order, vertex_indices, nodes, unit_nodes=unit_nodes ) if group.dim == 2: from meshmode.mesh.processing import flip_simplex_element_group group = flip_simplex_element_group(vertices, group, np.ones(ngroup_elements, bool)) elif isinstance(group_el_type, GmshTensorProductElementBase): vertex_shuffle = type(group_el_type)( order=1).get_lexicographic_gmsh_node_indices() group = TensorProductElementGroup( group_el_type.order, vertex_indices[:, vertex_shuffle], nodes, unit_nodes=unit_nodes ) else: # NOTE: already checked above raise AssertionError() groups.append(group) # FIXME: This is heuristic. if len(bulk_el_types) == 1: is_conforming = True else: is_conforming = mesh_bulk_dim < 3 # construct boundary tags for mesh from meshmode.mesh import BTAG_ALL, BTAG_REALLY_ALL boundary_tags = [BTAG_ALL, BTAG_REALLY_ALL] if self.tags: boundary_tags += [tag for tag, dim in self.tags if dim == mesh_bulk_dim-1] # compute facial adjacency for Mesh if there is tag information facial_adjacency_groups = None if is_conforming and self.tags: from meshmode.mesh import _compute_facial_adjacency_from_vertices facial_adjacency_groups = _compute_facial_adjacency_from_vertices( groups, boundary_tags, np.int32, np.int8, face_vertex_indices_to_tags) return Mesh( vertices, groups, is_conforming=is_conforming, facial_adjacency_groups=facial_adjacency_groups, boundary_tags=boundary_tags, **self.mesh_construction_kwargs)
def import_firedrake_mesh(fdrake_mesh, cells_to_use=None, normals=None, no_normals_warn=None): """ Create a :class:`meshmode.mesh.Mesh` from a `firedrake.mesh.MeshGeometry` with the same cells/elements, vertices, nodes, mesh order, and facial adjacency. The vertex and node coordinates will be the same, as well as the cell/element ordering. However, :mod:`firedrake` does not require elements to be positively oriented, so any negative elements are flipped as in :func:`meshmode.mesh.processing.flip_simplex_element_group`. The flipped cells/elements are identified by the returned *firedrake_orient* array :arg fdrake_mesh: `firedrake.mesh.MeshGeometry`. This mesh **must** be in a space of ambient dimension 1, 2, or 3 and have co-dimension of 0 or 1. It must use a simplex as a reference element. In the case of a 2-dimensional mesh embedded in 3-space, the method ``fdrake_mesh.init_cell_orientations`` must have been called. In the case of a 1-dimensional mesh embedded in 2-space, see parameters *normals* and *no_normals_warn*. Finally, the ``coordinates`` attribute must have a function space whose *finat_element* associates a degree of freedom with each vertex. In particular, this means that the vertices of the mesh must have well-defined coordinates. For those unfamiliar with :mod:`firedrake`, you can verify this by looking at .. code-block:: python coords_fspace = fdrake_mesh.coordinates.function_space() vertex_entity_dofs = coords_fspace.finat_element.entity_dofs()[0] for entity, dof_list in vertex_entity_dofs.items(): assert len(dof_list) > 0 :arg cells_to_use: *cells_to_use* is primarily intended for use internally by :func:`~meshmode.interop.firedrake.connection.\ build_connection_from_firedrake`. *cells_to_use* must be either 1. *None*, in which case this argument is ignored, or 2. a numpy array of unique firedrake cell indexes. In case (2.), only cells whose index appears in *cells_to_use* are included in the resultant mesh, and their index in *cells_to_use* becomes the element index in the resultant mesh element group. Any faces or vertices which do not touch a cell in *cells_to_use* are also ignored. Note that in this latter case, some faces that are not boundaries in *fdrake_mesh* may become boundaries in the returned mesh. These "induced" boundaries are marked with :class:`~meshmode.mesh.BTAG_INDUCED_BOUNDARY` instead of :class:`~meshmode.mesh.BTAG_ALL`. :arg normals: **Only** used if *fdrake_mesh* is a 1-surface embedded in 2-space. In this case, - If *None* then all elements are assumed to be positively oriented. - Else, should be a list/array whose *i*\\ th entry is the normal for the *i*\\ th element (*i*\\ th in *mesh.coordinate.function_space()*'s *cell_node_list*) :arg no_normals_warn: If *True* (the default), raises a warning if *fdrake_mesh* is a 1-surface embedded in 2-space and *normals* is *None*. :return: A tuple *(meshmode mesh, firedrake_orient)*. ``firedrake_orient < 0`` is *True* for any negatively oriented firedrake cell (which was flipped by meshmode) and False for any positively oriented firedrake cell (which was not flipped by meshmode). """ # Type validation from firedrake.mesh import MeshGeometry if not isinstance(fdrake_mesh, MeshGeometry): raise TypeError("'fdrake_mesh_topology' must be an instance of " "firedrake.mesh.MeshGeometry, " "not '%s'." % type(fdrake_mesh)) if cells_to_use is not None: if not isinstance(cells_to_use, np.ndarray): raise TypeError("'cells_to_use' must be a np.ndarray or " "*None*") assert len(cells_to_use.shape) == 1 assert np.size(np.unique(cells_to_use)) == np.size(cells_to_use), \ ":arg:`cells_to_use` must have unique entries" assert np.all(np.logical_and(cells_to_use >= 0, cells_to_use < fdrake_mesh.num_cells())) assert fdrake_mesh.ufl_cell().is_simplex(), "Mesh must use simplex cells" gdim = fdrake_mesh.geometric_dimension() tdim = fdrake_mesh.topological_dimension() assert gdim in [1, 2, 3], "Mesh must be in space of ambient dim 1, 2, or 3" assert gdim - tdim in [0, 1], "Mesh co-dimension must be 0 or 1" # firedrake meshes are not guaranteed be fully instantiated until # the .init() method is called. In particular, the coordinates function # may not be accessible if we do not call init(). If the mesh has # already been initialized, nothing will change. For more details # on why we need a second initialization, see # this pull request: # https://github.com/firedrakeproject/firedrake/pull/627 # which details how Firedrake implements a mesh's coordinates # as a function on that very same mesh fdrake_mesh.init() # Get all the nodal information we can from the topology bdy_tags = _get_firedrake_boundary_tags( fdrake_mesh, tag_induced_boundary=cells_to_use is not None) with ProcessLogger(logger, "Retrieving vertex indices and computing " "NodalAdjacency from firedrake mesh"): vertex_indices, nodal_adjacency = \ _get_firedrake_nodal_info(fdrake_mesh, cells_to_use=cells_to_use) # If only using some cells, vertices may need new indices as many # will be removed if cells_to_use is not None: vert_ndx_new2old = np.unique(vertex_indices.flatten()) vert_ndx_old2new = dict(zip(vert_ndx_new2old, np.arange(np.size(vert_ndx_new2old), dtype=vertex_indices.dtype))) vertex_indices = \ np.vectorize(vert_ndx_old2new.__getitem__)(vertex_indices) with ProcessLogger(logger, "Building (possibly) unflipped " "SimplexElementGroup from firedrake unit nodes/nodes"): # Grab the mesh reference element and cell dimension coord_finat_elt = fdrake_mesh.coordinates.function_space().finat_element cell_dim = fdrake_mesh.cell_dimension() # Get finat unit nodes and map them onto the meshmode reference simplex finat_unit_nodes = get_finat_element_unit_nodes(coord_finat_elt) fd_ref_to_mm = get_affine_reference_simplex_mapping(cell_dim, True) finat_unit_nodes = fd_ref_to_mm(finat_unit_nodes) # Now grab the nodes coords = fdrake_mesh.coordinates cell_node_list = coords.function_space().cell_node_list if cells_to_use is not None: cell_node_list = cell_node_list[cells_to_use] nodes = np.real(coords.dat.data[cell_node_list]) # Add extra dim in 1D for shape (nelements, nunit_nodes, dim) if tdim == 1: nodes = np.reshape(nodes, nodes.shape + (1,)) # Transpose nodes to have shape (dim, nelements, nunit_nodes) nodes = np.transpose(nodes, (2, 0, 1)) # make a group (possibly with some elements that need to be flipped) unflipped_group = SimplexElementGroup(coord_finat_elt.degree, vertex_indices, nodes, dim=cell_dim, unit_nodes=finat_unit_nodes) # Next get the vertices (we'll need these for the orientations) with ProcessLogger(logger, "Obtaining vertex coordinates"): coord_finat = fdrake_mesh.coordinates.function_space().finat_element # unit_vertex_indices are the element-local indices of the nodes # which coincide with the vertices, i.e. for element *i*, # vertex 0's coordinates would be nodes[i][unit_vertex_indices[0]]. # This assumes each vertex has some node which coincides with it... # which is normally fine to assume for firedrake meshes. unit_vertex_indices = [] # iterate through the dofs associated to each vertex on the # reference element for _, dofs in sorted(coord_finat.entity_dofs()[0].items()): assert len(dofs) == 1, \ "The function space of the mesh coordinates must have" \ " exactly one degree of freedom associated with " \ " each vertex in order to determine vertex coordinates" dof, = dofs unit_vertex_indices.append(dof) # Now get the vertex coordinates as *(dim, nvertices)*-shaped array if cells_to_use is not None: nvertices = np.size(vert_ndx_new2old) else: nvertices = fdrake_mesh.num_vertices() vertices = np.ndarray((gdim, nvertices), dtype=nodes.dtype) recorded_verts = set() for icell, cell_vertex_indices in enumerate(vertex_indices): for local_vert_id, global_vert_id in enumerate(cell_vertex_indices): if global_vert_id not in recorded_verts: recorded_verts.add(global_vert_id) local_node_nr = unit_vertex_indices[local_vert_id] vertices[:, global_vert_id] = nodes[:, icell, local_node_nr] # Use the vertices to compute the orientations and flip the group with ProcessLogger(logger, "Computing cell orientations"): orient = _get_firedrake_orientations(fdrake_mesh, unflipped_group, vertices, cells_to_use=cells_to_use, normals=normals, no_normals_warn=no_normals_warn) with ProcessLogger(logger, "Flipping group"): from meshmode.mesh.processing import flip_simplex_element_group group = flip_simplex_element_group(vertices, unflipped_group, orient < 0) # Now, any flipped element had its 0 vertex and 1 vertex exchanged. # This changes the local facet nr, so we need to create and then # fix our facial adjacency groups. To do that, we need to figure # out which local facet numbers switched. face_vertex_indices = group.face_vertex_indices() # face indices of the faces not containing vertex 0 and not # containing vertex 1, respectively no_zero_face_ndx, no_one_face_ndx = None, None for iface, face in enumerate(face_vertex_indices): if 0 not in face: no_zero_face_ndx = iface elif 1 not in face: no_one_face_ndx = iface with ProcessLogger(logger, "Building (possibly) unflipped " "FacialAdjacencyGroups"): unflipped_facial_adjacency_groups = \ _get_firedrake_facial_adjacency_groups(fdrake_mesh, cells_to_use=cells_to_use) # applied below to take elements and element_faces # (or neighbors and neighbor_faces) and flip in any faces that need to # be flipped. def flip_local_face_indices(faces, elements): faces = np.copy(faces) neg_elements = np.full(elements.shape, False) # To handle neighbor case, we only need to flip at elements # who have a neighbor, i.e. where neighbors is not a negative # bitmask of bdy tags neg_elements[elements >= 0] = (orient[elements[elements >= 0]] < 0) no_zero = np.logical_and(neg_elements, faces == no_zero_face_ndx) no_one = np.logical_and(neg_elements, faces == no_one_face_ndx) faces[no_zero], faces[no_one] = no_one_face_ndx, no_zero_face_ndx return faces # Create new facial adjacency groups that have been flipped with ProcessLogger(logger, "Flipping FacialAdjacencyGroups"): facial_adjacency_groups = [] for igroup, fagrps in enumerate(unflipped_facial_adjacency_groups): facial_adjacency_groups.append({}) for ineighbor_group, fagrp in fagrps.items(): new_element_faces = flip_local_face_indices(fagrp.element_faces, fagrp.elements) new_neighbor_faces = flip_local_face_indices(fagrp.neighbor_faces, fagrp.neighbors) new_fagrp = FacialAdjacencyGroup(igroup=igroup, ineighbor_group=ineighbor_group, elements=fagrp.elements, element_faces=new_element_faces, neighbors=fagrp.neighbors, neighbor_faces=new_neighbor_faces) facial_adjacency_groups[igroup][ineighbor_group] = new_fagrp return (Mesh(vertices, [group], boundary_tags=bdy_tags, nodal_adjacency=nodal_adjacency, facial_adjacency_groups=facial_adjacency_groups), orient)
def get_mesh(self): el_type_hist = {} for el_type in self.element_types: el_type_hist[el_type] = el_type_hist.get(el_type, 0) + 1 if not el_type_hist: raise RuntimeError("empty mesh in gmsh input") groups = self.groups = [] ambient_dim = self.points.shape[-1] mesh_bulk_dim = max( el_type.dimensions for el_type in six.iterkeys(el_type_hist)) # {{{ build vertex numbering # map set of face vertex indices to list of tags associated to face face_vertex_indices_to_tags = {} vertex_gmsh_index_to_mine = {} for element, (el_vertices, el_type) in enumerate(zip( self.element_vertices, self.element_types)): for gmsh_vertex_nr in el_vertices: if gmsh_vertex_nr not in vertex_gmsh_index_to_mine: vertex_gmsh_index_to_mine[gmsh_vertex_nr] = \ len(vertex_gmsh_index_to_mine) if self.tags: el_tag_indexes = [self.gmsh_tag_index_to_mine[t] for t in self.element_markers[element]] # record tags of boundary dimension el_tags = [self.tags[i][0] for i in el_tag_indexes if self.tags[i][1] == mesh_bulk_dim - 1] el_grp_verts = {vertex_gmsh_index_to_mine[e] for e in el_vertices} face_vertex_indices = frozenset(el_grp_verts) if face_vertex_indices not in face_vertex_indices_to_tags: face_vertex_indices_to_tags[face_vertex_indices] = [] face_vertex_indices_to_tags[face_vertex_indices] += el_tags # }}} # {{{ build vertex array gmsh_vertex_indices, my_vertex_indices = \ list(zip(*six.iteritems(vertex_gmsh_index_to_mine))) vertices = np.empty( (ambient_dim, len(vertex_gmsh_index_to_mine)), dtype=np.float64) vertices[:, np.array(my_vertex_indices, np.intp)] = \ self.points[np.array(gmsh_vertex_indices, np.intp)].T # }}} from meshmode.mesh import (Mesh, SimplexElementGroup, TensorProductElementGroup) bulk_el_types = set() for group_el_type, ngroup_elements in six.iteritems(el_type_hist): if group_el_type.dimensions != mesh_bulk_dim: continue bulk_el_types.add(group_el_type) nodes = np.empty((ambient_dim, ngroup_elements, el_type.node_count()), np.float64) el_vertex_count = group_el_type.vertex_count() vertex_indices = np.empty( (ngroup_elements, el_vertex_count), np.int32) i = 0 for element, (el_vertices, el_nodes, el_type) in enumerate(zip( self.element_vertices, self.element_nodes, self.element_types)): if el_type is not group_el_type: continue nodes[:, i] = self.points[el_nodes].T vertex_indices[i] = [vertex_gmsh_index_to_mine[v_nr] for v_nr in el_vertices] i += 1 unit_nodes = (np.array(group_el_type.lexicographic_node_tuples(), dtype=np.float64).T/group_el_type.order)*2 - 1 if isinstance(group_el_type, GmshSimplexElementBase): group = SimplexElementGroup( group_el_type.order, vertex_indices, nodes, unit_nodes=unit_nodes ) if group.dim == 2: from meshmode.mesh.processing import flip_simplex_element_group group = flip_simplex_element_group(vertices, group, np.ones(ngroup_elements, np.bool)) elif isinstance(group_el_type, GmshTensorProductElementBase): gmsh_vertex_tuples = type(group_el_type)(order=1).gmsh_node_tuples() gmsh_vertex_tuples_loc_dict = dict( (gvt, i) for i, gvt in enumerate(gmsh_vertex_tuples)) from pytools import ( generate_nonnegative_integer_tuples_below as gnitb) vertex_shuffle = np.array([ gmsh_vertex_tuples_loc_dict[vt] for vt in gnitb(2, group_el_type.dimensions)]) group = TensorProductElementGroup( group_el_type.order, vertex_indices[:, vertex_shuffle], nodes, unit_nodes=unit_nodes ) else: raise NotImplementedError("gmsh element type: %s" % type(group_el_type).__name__) groups.append(group) # FIXME: This is heuristic. if len(bulk_el_types) == 1: is_conforming = True else: is_conforming = mesh_bulk_dim < 3 # construct boundary tags for mesh from meshmode.mesh import BTAG_ALL, BTAG_REALLY_ALL boundary_tags = [BTAG_ALL, BTAG_REALLY_ALL] if self.tags: boundary_tags += [tag for tag, dim in self.tags if dim == mesh_bulk_dim-1] boundary_tags = tuple(boundary_tags) # compute facial adjacency for Mesh if there is tag information facial_adjacency_groups = None if is_conforming and self.tags: from meshmode.mesh import _compute_facial_adjacency_from_vertices facial_adjacency_groups = _compute_facial_adjacency_from_vertices( groups, boundary_tags, np.int32, np.int8, face_vertex_indices_to_tags) return Mesh( vertices, groups, is_conforming=is_conforming, facial_adjacency_groups=facial_adjacency_groups, boundary_tags=boundary_tags, **self.mesh_construction_kwargs)
def get_mesh(self): el_type_hist = {} for el_type in self.element_types: el_type_hist[el_type] = el_type_hist.get(el_type, 0) + 1 if not el_type_hist: raise RuntimeError("empty mesh in gmsh input") groups = self.groups = [] ambient_dim = self.points.shape[-1] mesh_bulk_dim = max(el_type.dimensions for el_type in six.iterkeys(el_type_hist)) # {{{ build vertex numbering vertex_index_gmsh_to_mine = {} for el_vertices, el_type in zip(self.element_vertices, self.element_types): for gmsh_vertex_nr in el_vertices: if gmsh_vertex_nr not in vertex_index_gmsh_to_mine: vertex_index_gmsh_to_mine[gmsh_vertex_nr] = \ len(vertex_index_gmsh_to_mine) # }}} # {{{ build vertex array gmsh_vertex_indices, my_vertex_indices = \ list(zip(*six.iteritems(vertex_index_gmsh_to_mine))) vertices = np.empty((ambient_dim, len(vertex_index_gmsh_to_mine)), dtype=np.float64) vertices[:, np.array(my_vertex_indices, np.intp)] = \ self.points[np.array(gmsh_vertex_indices, np.intp)].T # }}} from meshmode.mesh import (Mesh, SimplexElementGroup, TensorProductElementGroup) for group_el_type, ngroup_elements in six.iteritems(el_type_hist): if group_el_type.dimensions != mesh_bulk_dim: continue nodes = np.empty( (ambient_dim, ngroup_elements, el_type.node_count()), np.float64) el_vertex_count = group_el_type.vertex_count() vertex_indices = np.empty((ngroup_elements, el_vertex_count), np.int32) i = 0 for el_vertices, el_nodes, el_type in zip(self.element_vertices, self.element_nodes, self.element_types): if el_type is not group_el_type: continue nodes[:, i] = self.points[el_nodes].T vertex_indices[i] = [ vertex_index_gmsh_to_mine[v_nr] for v_nr in el_vertices ] i += 1 unit_nodes = (np.array(group_el_type.lexicographic_node_tuples(), dtype=np.float64).T / group_el_type.order) * 2 - 1 if isinstance(group_el_type, GmshSimplexElementBase): group = SimplexElementGroup(group_el_type.order, vertex_indices, nodes, unit_nodes=unit_nodes) if group.dim == 2: from meshmode.mesh.processing import flip_simplex_element_group group = flip_simplex_element_group( vertices, group, np.ones(ngroup_elements, np.bool)) elif isinstance(group_el_type, GmshTensorProductElementBase): gmsh_vertex_tuples = type(group_el_type)( order=1).gmsh_node_tuples() gmsh_vertex_tuples_loc_dict = dict( (gvt, i) for i, gvt in enumerate(gmsh_vertex_tuples)) from pytools import (generate_nonnegative_integer_tuples_below as gnitb) vertex_shuffle = np.array([ gmsh_vertex_tuples_loc_dict[vt] for vt in gnitb(2, group_el_type.dimensions) ]) group = TensorProductElementGroup( group_el_type.order, vertex_indices[:, vertex_shuffle], nodes, unit_nodes=unit_nodes) else: raise NotImplementedError("gmsh element type: %s" % type(group_el_type).__name__) # Gmsh seems to produce elements in the opposite orientation # of what we like. Flip them all. groups.append(group) return Mesh(vertices, groups, nodal_adjacency=None, facial_adjacency_groups=None)
def get_mesh(self): el_type_hist = {} for el_type in self.element_types: el_type_hist[el_type] = el_type_hist.get(el_type, 0) + 1 if not el_type_hist: raise RuntimeError("empty mesh in gmsh input") groups = self.groups = [] ambient_dim = self.points.shape[-1] mesh_bulk_dim = max( el_type.dimensions for el_type in six.iterkeys(el_type_hist)) # {{{ build vertex numbering vertex_index_gmsh_to_mine = {} for el_vertices, el_type in zip( self.element_vertices, self.element_types): for gmsh_vertex_nr in el_vertices: if gmsh_vertex_nr not in vertex_index_gmsh_to_mine: vertex_index_gmsh_to_mine[gmsh_vertex_nr] = \ len(vertex_index_gmsh_to_mine) # }}} # {{{ build vertex array gmsh_vertex_indices, my_vertex_indices = \ list(zip(*six.iteritems(vertex_index_gmsh_to_mine))) vertices = np.empty( (ambient_dim, len(vertex_index_gmsh_to_mine)), dtype=np.float64) vertices[:, np.array(my_vertex_indices, np.intp)] = \ self.points[np.array(gmsh_vertex_indices, np.intp)].T # }}} from meshmode.mesh import (Mesh, SimplexElementGroup, TensorProductElementGroup) for group_el_type, ngroup_elements in six.iteritems(el_type_hist): if group_el_type.dimensions != mesh_bulk_dim: continue nodes = np.empty((ambient_dim, ngroup_elements, el_type.node_count()), np.float64) el_vertex_count = group_el_type.vertex_count() vertex_indices = np.empty( (ngroup_elements, el_vertex_count), np.int32) i = 0 for el_vertices, el_nodes, el_type in zip( self.element_vertices, self.element_nodes, self.element_types): if el_type is not group_el_type: continue nodes[:, i] = self.points[el_nodes].T vertex_indices[i] = [vertex_index_gmsh_to_mine[v_nr] for v_nr in el_vertices] i += 1 unit_nodes = (np.array(group_el_type.lexicographic_node_tuples(), dtype=np.float64).T/group_el_type.order)*2 - 1 if isinstance(group_el_type, GmshSimplexElementBase): group = SimplexElementGroup( group_el_type.order, vertex_indices, nodes, unit_nodes=unit_nodes ) if group.dim == 2: from meshmode.mesh.processing import flip_simplex_element_group group = flip_simplex_element_group(vertices, group, np.ones(ngroup_elements, np.bool)) elif isinstance(group_el_type, GmshTensorProductElementBase): group = TensorProductElementGroup( group_el_type.order, vertex_indices, nodes, unit_nodes=unit_nodes ) else: raise NotImplementedError("gmsh element type: %s" % type(group_el_type).__name__) # Gmsh seems to produce elements in the opposite orientation # of what we like. Flip them all. groups.append(group) return Mesh( vertices, groups, nodal_adjacency=None, facial_adjacency_groups=None)