diff --git a/meshmode/mesh/processing.py b/meshmode/mesh/processing.py
index 679d98b34184cc2b596b39640527491796f75c60..170b0a006fb8ea8ac612a7759f9e2fb0abe195f5 100644
--- a/meshmode/mesh/processing.py
+++ b/meshmode/mesh/processing.py
@@ -33,110 +33,161 @@ __doc__ = """
 """
 
 
-def find_volume_mesh_element_orientations(mesh):
-    result = np.empty(mesh.nelements, dtype=np.float64)
+def find_volume_mesh_element_group_orientation(mesh, grp):
+    """Return a positive floating point number for each positively
+    oriented element, and a negative floating point number for
+    each negatively oriented element.
+    """
 
     from meshmode.mesh import SimplexElementGroup
 
-    for grp in mesh.groups:
-        if not isinstance(grp, SimplexElementGroup):
-            raise NotImplementedError(
-                    "finding element orientations "
-                    "only supported on "
-                    "exclusively SimplexElementGroup-based meshes")
+    if not isinstance(grp, SimplexElementGroup):
+        raise NotImplementedError(
+                "finding element orientations "
+                "only supported on "
+                "exclusively SimplexElementGroup-based meshes")
+
+    # (ambient_dim, nelements, nvertices)
+    vertices = mesh.vertices[:, grp.vertex_indices]
+
+    # (ambient_dim, nelements, nspan_vectors)
+    spanning_vectors = (
+            vertices[:, :, 1:] - vertices[:, :, 0][:, :, np.newaxis])
 
-        # (ambient_dim, nelements, nvertices)
-        vertices = mesh.vertices[:, grp.vertex_indices]
+    ambient_dim = spanning_vectors.shape[0]
+    nspan_vectors = spanning_vectors.shape[-1]
 
-        # (ambient_dim, nelements, nspan_vectors)
-        spanning_vectors = (
-                vertices[:, :, 1:] - vertices[:, :, 0][:, :, np.newaxis])
+    spanning_object_array = np.empty(
+            (nspan_vectors, ambient_dim),
+            dtype=np.object)
 
-        ambient_dim = spanning_vectors.shape[0]
-        nspan_vectors = spanning_vectors.shape[-1]
+    for ispan in xrange(nspan_vectors):
+        for idim in xrange(ambient_dim):
+            spanning_object_array[ispan, idim] = \
+                    spanning_vectors[idim, :, ispan]
 
-        spanning_object_array = np.empty(
-                (nspan_vectors, ambient_dim),
-                dtype=np.object)
+    from pymbolic.geometric_algebra import MultiVector
 
-        for ispan in xrange(nspan_vectors):
-            for idim in xrange(ambient_dim):
-                spanning_object_array[ispan, idim] = \
-                        spanning_vectors[idim, :, ispan]
+    mvs = [MultiVector(vec) for vec in spanning_object_array]
 
-        from pymbolic.geometric_algebra import MultiVector
+    from operator import xor
+    outer_prod = -reduce(xor, mvs)
 
-        mvs = [MultiVector(vec) for vec in spanning_object_array]
+    return (outer_prod.I | outer_prod).as_scalar()
 
-        from operator import xor
-        outer_prod = reduce(xor, mvs)
 
-        signed_area_element = (outer_prod.I | outer_prod).as_scalar()
+def find_volume_mesh_element_orientations(mesh, tolerate_unimplemented_checks=False):
+    """Return a positive floating point number for each positively
+    oriented element, and a negative floating point number for
+    each negatively oriented element.
 
-        result[grp.element_nr_base:grp.element_nr_base + grp.nelements] \
-                = signed_area_element
+    :arg tolerate_unimplemented_checks: If *True*, elements for which no
+        check is available will return NaN.
+    """
+
+    result = np.empty(mesh.nelements, dtype=np.float64)
+
+    for grp in mesh.groups:
+        result_grp_view = result[
+                grp.element_nr_base:grp.element_nr_base + grp.nelements]
+
+        if tolerate_unimplemented_checks:
+            try:
+                signed_area_elements = \
+                        find_volume_mesh_element_group_orientation(mesh, grp)
+            except NotImplementedError:
+                result_grp_view[:] = float("nan")
+            else:
+                assert not np.isnan(signed_area_elements).any()
+                result_grp_view[:] = signed_area_elements
+        else:
+            signed_area_elements = \
+                    find_volume_mesh_element_group_orientation(mesh, grp)
+            assert not np.isnan(signed_area_elements).any()
+            result_grp_view[:] = signed_area_elements
 
     return result
 
 
+def test_volume_mesh_element_orientations(mesh):
+    area_elements = find_volume_mesh_element_orientations(
+            mesh, tolerate_unimplemented_checks=True)
+
+    valid = ~np.isnan(area_elements)
+
+    return (area_elements[valid] > 0).all()
+
+
 # {{{ flips
 
-def perform_flips(mesh, flip_flags):
+def flip_simplex_element_group(vertices, grp, grp_flip_flags):
     from modepy.tools import barycentric_to_unit, unit_to_barycentric
 
-    flip_flags = flip_flags.astype(np.bool)
+    from meshmode.mesh import SimplexElementGroup
 
-    from meshmode.mesh import Mesh, SimplexElementGroup
+    if not isinstance(grp, SimplexElementGroup):
+        raise NotImplementedError("flips only supported on "
+                "exclusively SimplexElementGroup-based meshes")
 
-    new_groups = []
-    for grp in mesh.groups:
-        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.
 
-        grp_flip_flags = flip_flags[
-                grp.element_nr_base:grp.element_nr_base+grp.nelements]
+    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]
 
-        # Swap the first two vertices on elements to be flipped.
+    # Generate a resampling matrix that corresponds to the
+    # first two barycentric coordinates being swapped.
 
-        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]
+    bary_unit_nodes = unit_to_barycentric(grp.unit_nodes)
 
-        # Generate a resampling matrix that corresponds to the
-        # first two barycentric coordinates being swapped.
+    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)
 
-        bary_unit_nodes = unit_to_barycentric(grp.unit_nodes)
+    flip_matrix = mp.resampling_matrix(
+            mp.simplex_onb(grp.dim, grp.order),
+            flipped_unit_nodes, 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[np.abs(flip_matrix) < 1e-15] = 0
 
-        flip_matrix = mp.resampling_matrix(
-                mp.simplex_onb(grp.dim, grp.order),
-                flipped_unit_nodes, grp.unit_nodes)
+    # Flipping twice should be the identity
+    assert la.norm(
+            np.dot(flip_matrix, flip_matrix)
+            - np.eye(len(flip_matrix))) < 1e-13
 
-        flip_matrix[np.abs(flip_matrix) < 1e-15] = 0
+    # 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])
 
-        # Flipping twice should be the identity
-        assert la.norm(
-                np.dot(flip_matrix, flip_matrix)
-                - np.eye(len(flip_matrix))) < 1e-13
+    return SimplexElementGroup(
+            grp.order, new_vertex_indices, new_nodes,
+            unit_nodes=grp.unit_nodes)
 
-        # 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])
 
-        new_groups.append(SimplexElementGroup(
-            grp.order, new_vertex_indices, new_nodes,
-            unit_nodes=grp.unit_nodes))
+def perform_flips(mesh, flip_flags, skip_tests=False):
+    flip_flags = flip_flags.astype(np.bool)
+
+    from meshmode.mesh import Mesh
+
+    new_groups = []
+    for grp in mesh.groups:
+        grp_flip_flags = flip_flags[
+                grp.element_nr_base:grp.element_nr_base+grp.nelements]
+
+        if grp_flip_flags.any():
+            new_grp = flip_simplex_element_group(
+                    mesh.vertices, grp, grp_flip_flags)
+        else:
+            new_grp = grp.copy()
+
+        new_groups.append(new_grp)
 
-    return Mesh(mesh.vertices, new_groups)
+    return Mesh(mesh.vertices, new_groups, skip_tests=skip_tests)
 
 # }}}