def facial_adjacency_groups(self): """ Return a :mod:`meshmode` list of :class:`FacialAdjacencyGroups` as used in the construction of a :mod:`meshmode` :class:`Mesh` """ # {{{ Compute facial adjacency groups if not already done if self._facial_adjacency_groups is None: from meshmode.mesh import _compute_facial_adjacency_from_vertices self._facial_adjacency_groups = _compute_facial_adjacency_from_vertices( [self.group()], self.bdy_tags(), np.int32, np.int8, face_vertex_indices_to_tags=self.face_vertex_indices_to_tags()) # }}} return self._facial_adjacency_groups
def generate_box_mesh(axis_coords, order=1, coord_dtype=np.float64, group_cls=None, boundary_tag_to_face=None, mesh_type=None): r"""Create a semi-structured mesh. :param axis_coords: a tuple with a number of entries corresponding to the number of dimensions, with each entry a numpy array specifying the coordinates to be used along that axis. :param group_cls: One of :class:`meshmode.mesh.SimplexElementGroup` or :class:`meshmode.mesh.TensorProductElementGroup`. :param boundary_tag_to_face: an optional dictionary for tagging boundaries. The keys correspond to custom boundary tags, with the values giving a list of the faces on which they should be applied in terms of coordinate directions (``+x``, ``-x``, ``+y``, ``-y``, ``+z``, ``-z``, ``+w``, ``-w``). For example:: boundary_tag_to_face={"bdry_1": ["+x", "+y"], "bdry_2": ["-x"]} :param mesh_type: In two dimensions with non-tensor-product elements, *mesh_type* may be set to ``"X"`` to generate this type of mesh:: _______ |\ /| | \ / | | X | | / \ | |/ \| ^^^^^^^ instead of the default:: _______ |\ | | \ | | \ | | \ | | \| ^^^^^^^ Specifying a value other than *None* for all other mesh dimensionalities and element types is an error. .. versionchanged:: 2017.1 *group_factory* parameter added. .. versionchanged:: 2020.1 *boundary_tag_to_face* parameter added. .. versionchanged:: 2020.3 *group_factory* deprecated and renamed to *group_cls*. """ if boundary_tag_to_face is None: boundary_tag_to_face = {} for iaxis, axc in enumerate(axis_coords): if len(axc) < 2: raise ValueError("need at least two points along axis %d" % (iaxis + 1)) dim = len(axis_coords) shape = tuple(len(axc) for axc in axis_coords) from pytools import product nvertices = product(shape) vertex_indices = np.arange(nvertices).reshape(*shape) vertices = np.empty((dim, ) + shape, dtype=coord_dtype) for idim in range(dim): vshape = (shape[idim], ) + (1, ) * (dim - 1 - idim) vertices[idim] = axis_coords[idim].reshape(*vshape) vertices = vertices.reshape(dim, -1) from meshmode.mesh import SimplexElementGroup, TensorProductElementGroup if group_cls is None: group_cls = SimplexElementGroup if issubclass(group_cls, SimplexElementGroup): is_tp = False elif issubclass(group_cls, TensorProductElementGroup): is_tp = True else: raise ValueError(f"unsupported value for 'group_cls': {group_cls}") el_vertices = [] if dim == 1: if mesh_type is not None: raise ValueError(f"unsupported mesh type: '{mesh_type}'") for i in range(shape[0] - 1): # a--b a = vertex_indices[i] b = vertex_indices[i + 1] el_vertices.append(( a, b, )) elif dim == 2: if mesh_type == "X" and not is_tp: shape_m1 = tuple(si - 1 for si in shape) nmidpoints = product(shape_m1) midpoint_indices = ( nvertices + np.arange(nmidpoints).reshape(*shape_m1, order="F")) midpoints = np.empty((dim, ) + shape_m1, dtype=coord_dtype) for idim in range(dim): vshape = (shape_m1[idim], ) + (1, ) * idim left_axis_coords = axis_coords[idim][:-1] right_axis_coords = axis_coords[idim][1:] midpoints[idim] = ( 0.5 * (left_axis_coords + right_axis_coords)).reshape(*vshape) midpoints = midpoints.reshape(dim, -1) vertices = np.concatenate((vertices, midpoints), axis=1) elif mesh_type is None: pass else: raise ValueError(f"unsupported mesh type: '{mesh_type}'") for i in range(shape[0] - 1): for j in range(shape[1] - 1): # c--d # | | # a--b a = vertex_indices[i, j] b = vertex_indices[i + 1, j] c = vertex_indices[i, j + 1] d = vertex_indices[i + 1, j + 1] if is_tp: el_vertices.append((a, b, c, d)) elif mesh_type == "X": m = midpoint_indices[i, j] el_vertices.append((a, b, m)) el_vertices.append((b, d, m)) el_vertices.append((d, c, m)) el_vertices.append((c, a, m)) else: el_vertices.append((a, b, c)) el_vertices.append((d, c, b)) elif dim == 3: if mesh_type is not None: raise ValueError("unsupported mesh_type") for i in range(shape[0] - 1): for j in range(shape[1] - 1): for k in range(shape[2] - 1): a000 = vertex_indices[i, j, k] a001 = vertex_indices[i, j, k + 1] a010 = vertex_indices[i, j + 1, k] a011 = vertex_indices[i, j + 1, k + 1] a100 = vertex_indices[i + 1, j, k] a101 = vertex_indices[i + 1, j, k + 1] a110 = vertex_indices[i + 1, j + 1, k] a111 = vertex_indices[i + 1, j + 1, k + 1] if is_tp: el_vertices.append( (a000, a100, a010, a110, a001, a101, a011, a111)) else: el_vertices.append((a000, a100, a010, a001)) el_vertices.append((a101, a100, a001, a010)) el_vertices.append((a101, a011, a010, a001)) el_vertices.append((a100, a010, a101, a110)) el_vertices.append((a011, a010, a110, a101)) el_vertices.append((a011, a111, a101, a110)) else: raise NotImplementedError("box meshes of dimension %d" % dim) el_vertices = np.array(el_vertices, dtype=np.int32) grp = make_group_from_vertices(vertices.reshape(dim, -1), el_vertices, order, group_cls=group_cls) # {{{ compute facial adjacency for mesh if there is tag information facial_adjacency_groups = None face_vertex_indices_to_tags = {} boundary_tags = list(boundary_tag_to_face.keys()) axes = ["x", "y", "z", "w"] if boundary_tags: vert_index_to_tuple = { vertex_indices[itup]: itup for itup in np.ndindex(shape) } for tag_idx, tag in enumerate(boundary_tags): # Need to map the correct face vertices to the boundary tags for face in boundary_tag_to_face[tag]: if len(face) != 2: raise ValueError("face identifier '%s' does not " "consist of exactly two characters" % face) side, axis = face try: axis = axes.index(axis) except ValueError: raise ValueError("unrecognized axis in face identifier '%s'" % face) if axis >= dim: raise ValueError( "axis in face identifier '%s' does not exist in %dD" % (face, dim)) if side == "-": vert_crit = 0 elif side == "+": vert_crit = shape[axis] - 1 else: raise ValueError( "first character of face identifier '%s' is not" "'+' or '-'" % face) for ielem in range(0, grp.nelements): for ref_fvi in grp.face_vertex_indices(): fvi = grp.vertex_indices[ielem, ref_fvi] try: fvi_tuples = [vert_index_to_tuple[i] for i in fvi] except KeyError: # Happens for interior faces of "X" meshes because # midpoints aren't in vert_index_to_tuple. We don't # care about them. continue if all(fvi_tuple[axis] == vert_crit for fvi_tuple in fvi_tuples): key = frozenset(fvi) face_vertex_indices_to_tags.setdefault(key, []).append(tag) if boundary_tags: from meshmode.mesh import (_compute_facial_adjacency_from_vertices, BTAG_ALL, BTAG_REALLY_ALL) boundary_tags.extend([BTAG_ALL, BTAG_REALLY_ALL]) facial_adjacency_groups = _compute_facial_adjacency_from_vertices( [grp], boundary_tags, np.int32, np.int8, face_vertex_indices_to_tags) else: facial_adjacency_groups = None # }}} from meshmode.mesh import Mesh return Mesh(vertices, [grp], facial_adjacency_groups=facial_adjacency_groups, is_conforming=True, boundary_tags=boundary_tags)
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 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 generate_box_mesh(axis_coords, order=1, coord_dtype=np.float64, group_factory=None, boundary_tag_to_face=None): """Create a semi-structured mesh. :param axis_coords: a tuple with a number of entries corresponding to the number of dimensions, with each entry a numpy array specifying the coordinates to be used along that axis. :param group_factory: One of :class:`meshmode.mesh.SimplexElementGroup` or :class:`meshmode.mesh.TensorProductElementGroup`. :param boundary_tag_to_face: an optional dictionary for tagging boundaries. The keys correspond to custom boundary tags, with the values giving a list of the faces on which they should be applied in terms of coordinate directions (``+x``, ``-x``, ``+y``, ``-y``, ``+z``, ``-z``, ``+w``, ``-w``). For example:: boundary_tag_to_face={"bdry_1": ["+x", "+y"], "bdry_2": ["-x"]} .. versionchanged:: 2017.1 *group_factory* parameter added. .. versionchanged:: 2020.1 *boundary_tag_to_face* parameter added. """ if boundary_tag_to_face is None: boundary_tag_to_face = {} for iaxis, axc in enumerate(axis_coords): if len(axc) < 2: raise ValueError("need at least two points along axis %d" % (iaxis + 1)) dim = len(axis_coords) shape = tuple(len(axc) for axc in axis_coords) from pytools import product nvertices = product(shape) vertex_indices = np.arange(nvertices).reshape(*shape, order="F") vertices = np.empty((dim, ) + shape, dtype=coord_dtype) for idim in range(dim): vshape = (shape[idim], ) + (1, ) * idim vertices[idim] = axis_coords[idim].reshape(*vshape) vertices = vertices.reshape(dim, -1) from meshmode.mesh import SimplexElementGroup, TensorProductElementGroup if group_factory is None: group_factory = SimplexElementGroup if issubclass(group_factory, SimplexElementGroup): is_tp = False elif issubclass(group_factory, TensorProductElementGroup): is_tp = True else: raise ValueError("unsupported value for 'group_factory': %s" % group_factory) el_vertices = [] if dim == 1: for i in range(shape[0] - 1): # a--b a = vertex_indices[i] b = vertex_indices[i + 1] el_vertices.append(( a, b, )) elif dim == 2: for i in range(shape[0] - 1): for j in range(shape[1] - 1): # c--d # | | # a--b a = vertex_indices[i, j] b = vertex_indices[i + 1, j] c = vertex_indices[i, j + 1] d = vertex_indices[i + 1, j + 1] if is_tp: el_vertices.append((a, b, c, d)) else: el_vertices.append((a, b, c)) el_vertices.append((d, c, b)) elif dim == 3: for i in range(shape[0] - 1): for j in range(shape[1] - 1): for k in range(shape[2] - 1): a000 = vertex_indices[i, j, k] a001 = vertex_indices[i, j, k + 1] a010 = vertex_indices[i, j + 1, k] a011 = vertex_indices[i, j + 1, k + 1] a100 = vertex_indices[i + 1, j, k] a101 = vertex_indices[i + 1, j, k + 1] a110 = vertex_indices[i + 1, j + 1, k] a111 = vertex_indices[i + 1, j + 1, k + 1] if is_tp: el_vertices.append( (a000, a001, a010, a011, a100, a101, a110, a111)) else: el_vertices.append((a000, a100, a010, a001)) el_vertices.append((a101, a100, a001, a010)) el_vertices.append((a101, a011, a010, a001)) el_vertices.append((a100, a010, a101, a110)) el_vertices.append((a011, a010, a110, a101)) el_vertices.append((a011, a111, a101, a110)) else: raise NotImplementedError("box meshes of dimension %d" % dim) el_vertices = np.array(el_vertices, dtype=np.int32) grp = make_group_from_vertices(vertices.reshape(dim, -1), el_vertices, order, group_factory=group_factory) # {{{ compute facial adjacency for mesh if there is tag information facial_adjacency_groups = None face_vertex_indices_to_tags = {} boundary_tags = list(boundary_tag_to_face.keys()) axes = ["x", "y", "z", "w"] if boundary_tags: vert_index_to_tuple = { vertex_indices[itup]: itup for itup in np.ndindex(shape) } for tag_idx, tag in enumerate(boundary_tags): # Need to map the correct face vertices to the boundary tags for face in boundary_tag_to_face[tag]: if len(face) != 2: raise ValueError("face identifier '%s' does not " "consist of exactly two characters" % face) side, axis = face try: axis = axes.index(axis) except ValueError: raise ValueError("unrecognized axis in face identifier '%s'" % face) if axis >= dim: raise ValueError( "axis in face identifier '%s' does not exist in %dD" % (face, dim)) if side == "-": vert_crit = 0 elif side == "+": vert_crit = shape[axis] - 1 else: raise ValueError( "first character of face identifier '%s' is not" "'+' or '-'" % face) for ielem in range(0, grp.nelements): for ref_fvi in grp.face_vertex_indices(): fvi = grp.vertex_indices[ielem, ref_fvi] fvi_tuples = [vert_index_to_tuple[i] for i in fvi] if all(fvi_tuple[axis] == vert_crit for fvi_tuple in fvi_tuples): key = frozenset(fvi) face_vertex_indices_to_tags.setdefault(key, []).append(tag) if boundary_tags: from meshmode.mesh import (_compute_facial_adjacency_from_vertices, BTAG_ALL, BTAG_REALLY_ALL) boundary_tags.extend([BTAG_ALL, BTAG_REALLY_ALL]) facial_adjacency_groups = _compute_facial_adjacency_from_vertices( [grp], boundary_tags, np.int32, np.int8, face_vertex_indices_to_tags) else: facial_adjacency_groups = None # }}} from meshmode.mesh import Mesh return Mesh(vertices, [grp], facial_adjacency_groups=facial_adjacency_groups, is_conforming=True, boundary_tags=boundary_tags)