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: 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 _get_firedrake_orientations(fdrake_mesh, unflipped_group, vertices, cells_to_use, normals=None, no_normals_warn=True): r""" Return the orientations of the mesh elements: :arg fdrake_mesh: As described in :func:`import_firedrake_mesh` :arg unflipped_group: A :class:`SimplexElementGroup` instance with (potentially) some negatively oriented elements. :arg vertices: The vertex coordinates as a numpy array of shape *(ambient_dim, nvertices)* (the vertices of *unflipped_group*) :arg normals: As described in :func:`import_firedrake_mesh` :arg no_normals_warn: As described in :func:`import_firedrake_mesh` :arg cells_to_use: If *None*, then ignored. Otherwise, a numpy array of unique firedrake cell indices indicating which cells to use. :return: A numpy array, the *i*\ th element is > 0 if the *i*\ th element is positively oriented, < 0 if negatively oriented. Mesh must have co-dimension 0 or 1. If *cells_to_use* is not *None*, then the *i*\ th entry corresponds to the *cells_to_use[i]*\ th element. """ # compute orientations tdim = fdrake_mesh.topological_dimension() gdim = fdrake_mesh.geometric_dimension() orient = None if gdim == tdim: # If the co-dimension is 0, :mod:`meshmode` has a convenient # function to compute cell orientations from meshmode.mesh.processing import \ find_volume_mesh_element_group_orientation orient = find_volume_mesh_element_group_orientation(vertices, unflipped_group) elif tdim == 1 and gdim == 2: # In this case we have a 1-surface embedded in 2-space. # Firedrake does not provide any convenient way of # letting the user set cell orientations in this case, so we # have to ask the user for cell normals directly. if cells_to_use is None: num_cells = fdrake_mesh.num_cells() else: num_cells = np.size(cells_to_use) orient = np.ones(num_cells) if normals: for i, (normal, vert_indices) in enumerate( zip(np.array(normals), unflipped_group.vertex_indices)): edge = vertices[:, vert_indices[1]] - vertices[:, vert_indices[0]] if np.cross(normal, edge) < 0: orient[i] = -1.0 elif no_normals_warn: warn("Assuming all elements are positively-oriented.") elif tdim == 2 and gdim == 3: # In this case we have a 2-surface embedded in 3-space. # In this case, we assume the user has called # :func:`firedrake.mesh.MeshGeometry.init_cell_orientations`, see # https://www.firedrakeproject.org/variational-problems.html#ensuring-consistent-cell-orientations # noqa : E501 # for a tutorial on how these are usually initialized. # # Unfortunately, *init_cell_orientations* is currently only implemented # in 3D, so we can't use this in the 1/2 case. orient = fdrake_mesh.cell_orientations().dat.data if cells_to_use is not None: orient = orient[cells_to_use] r""" Convert (0 \implies negative, 1 \implies positive) to (-1 \implies negative, 1 \implies positive) """ orient *= 2 orient -= 1 # Make sure the mesh fell into one of the cases # Nb : This should be guaranteed by previous checks, # but is here anyway in case of future development. assert orient is not None return orient
def orientations(self): """ Return the orientations of the mesh elements: an array, the *i*th element is > 0 if the *ith* element is positively oriented, < 0 if negatively oriented :arg normals: _Only_ used if :arg:`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 :arg:`mesh`*.coordinate.function_space()*'s :attribute:`cell_node_list`) :arg no_normals_warn: If *True*, raises a warning if :arg:`mesh` is a 1-surface embedded in 2-space and :arg:`normals` is *None*. """ if self._orient is None: # compute orientations tdim = self.analog().topological_dimension() gdim = self.analog().geometric_dimension() orient = None if gdim == tdim: # We use :mod:`meshmode` to check our orientations from meshmode.mesh.processing import \ find_volume_mesh_element_group_orientation orient = \ find_volume_mesh_element_group_orientation(self.vertices(), self.group()) if tdim == 1 and gdim == 2: # In this case we have a 1-surface embedded in 2-space orient = np.ones(self.nelements()) if self._normals: for i, (normal, vertices) in enumerate(zip( np.array(self._normals), self.vertices())): if np.cross(normal, vertices) < 0: orient[i] = -1.0 elif self._no_normals_warn: warn("Assuming all elements are positively-oriented.") elif tdim == 2 and gdim == 3: # In this case we have a 2-surface embedded in 3-space orient = self.analog().cell_orientations().dat.data r""" Convert (0 \implies negative, 1 \implies positive) to (-1 \implies negative, 1 \implies positive) """ orient *= 2 orient -= np.ones(orient.shape, dtype=orient.dtype) self._orient = orient #Make sure the mesh fell into one of the cases """ NOTE : This should be guaranteed by previous checks, but is here anyway in case of future development. """ assert self._orient is not None return self._orient