diff --git a/meshmode/interop/firedrake/connection.py b/meshmode/interop/firedrake/connection.py
index b9cefaec80fbd83b3992c5fac7d67912a3164d96..619773305dfc3e39e3e4071f2ac72f66abf90b7d 100644
--- a/meshmode/interop/firedrake/connection.py
+++ b/meshmode/interop/firedrake/connection.py
@@ -26,6 +26,9 @@ __doc__ = """
 
 import numpy as np
 import numpy.linalg as la
+import six
+
+from modepy import resampling_matrix
 
 from meshmode.interop.firedrake.mesh import import_firedrake_mesh
 from meshmode.interop.firedrake.reference_cell import (
@@ -37,8 +40,6 @@ from meshmode.discretization.poly_element import \
     InterpolatoryQuadratureSimplexGroupFactory
 from meshmode.discretization import Discretization
 
-from modepy import resampling_matrix
-
 
 def _reorder_nodes(orient, nodes, flip_matrix, unflip=False):
     """
@@ -130,19 +131,45 @@ class FromFiredrakeConnection:
                                                        new_nodes=fd_unit_nodes,
                                                        old_nodes=mm_unit_nodes)
 
-        # handle reordering fd->mm
+        # Flipping negative elements corresponds to reordering the nodes.
+        # We handle reordering by storing the permutation explicitly as
+        # a numpy array
+
+        # Get the reordering fd->mm.
+        #
+        # One should note there is something a bit more subtle going on
+        # in the continuous case. All meshmode discretizations use
+        # are discontinuous, so nodes are associated with elements(cells)
+        # not vertices. In a continuous firedrake space, some nodes
+        # are shared between multiple cells. In particular, while the
+        # below "reordering" is indeed a permutation if the firedrake space
+        # is discontinuous, if the firedrake space is continuous then
+        # some firedrake nodes correspond to nodes on multiple meshmode
+        # elements, i.e. those nodes appear multiple times
+        # in the "reordering" array
         flip_mat = get_simplex_element_flip_matrix(ufl_elt.degree(),
                                                    fd_unit_nodes)
         fd_cell_node_list = fdrake_fspace.cell_node_list
         _reorder_nodes(orient, fd_cell_node_list, flip_mat, unflip=False)
         self._reordering_arr_fd2mm = fd_cell_node_list.flatten()
 
-        # handle reordering mm->fd (this only works in the discontinuous
-        #                           case)
-        nnodes = self.to_discr.nnodes
-        mm_cell_node_list = self.to_discr.groups[0].view(np.arange(nnodes))
-        _reorder_nodes(orient, mm_cell_node_list, flip_mat, unflip=True)
-        self._reordering_arr_mm2fd = mm_cell_node_list.flatten()
+        # Now handle the possibility of duplicate nodes
+        unique_fd_nodes, counts = np.unique(self._reordering_arr_fd2mm,
+                                            return_counts=True)
+        # self._duplicate_nodes
+        # maps firedrake nodes associated to more than 1 meshmode node
+        # to all associated meshmode nodes.
+        self._duplicate_nodes = {}
+        if ufl_elt.family() == 'Discontinuous Lagrange':
+            assert np.all(counts == 1), \
+                "This error should never happen, some nodes in a firedrake " \
+                "discontinuous space were duplicated. Contact the developer "
+        else:
+            dup_fd_nodes = set(unique_fd_nodes[counts > 1])
+            for mm_inode, fd_inode in enumerate(self._reordering_arr_fd2mm):
+                if fd_inode in dup_fd_nodes:
+                    self._duplicate_nodes.setdefault(fd_inode, [])
+                    self._duplicate_nodes[fd_inode].append(mm_inode)
 
         # Store things that we need for *from_fspace*
         self._ufl_element = ufl_elt
@@ -228,7 +255,9 @@ class FromFiredrakeConnection:
         np.matmul(out_view, self._resampling_mat_fd2mm.T, out=out_view)
         return out
 
-    def from_meshmode(self, mm_field, out=None):
+    def from_meshmode(self, mm_field, out=None,
+                      assert_fdrake_discontinuous=True,
+                      continuity_tolerance=None):
         """
         transport meshmode field from :attr:`to_discr` into an
         appropriate firedrake function space.
@@ -238,15 +267,26 @@ class FromFiredrakeConnection:
         :param out: If *None* then ignored, otherwise a :mod:`firedrake`
             function of the right function space for the transported data
             to be stored in.
+        :param assert_fdrake_discontinuous: If *True*,
+            disallows conversion to a continuous firedrake function space
+            (i.e. this function checks that ``self.from_fspace()`` is
+             discontinuous and raises a *ValueError* otherwise)
+        :param continuity_tolerance: If converting to a continuous firedrake
+            function space (i.e. if ``self.from_fspace()`` is continuous),
+            assert that at any two meshmode nodes corresponding to the
+            same firedrake node (meshmode is a discontinuous space, so this
+            situation will almost certainly happen), the function being transported
+            has values at most :param:`continuity_tolerance` distance
+            apart. If *None*, no checks are performed.
 
         :return: a :mod:`firedrake` :class:`Function` holding the transported
             data.
         """
-        if self._ufl_element.family() == 'Lagrange':
-            raise ValueError("Cannot convert functions from discontinuous "
-                             " space (meshmode) to continuous firedrake "
-                             " space (reference element family %s)."
-                             % type(self._ufl_element.family()))
+        if self._ufl_element.family() == 'Lagrange' \
+                and assert_fdrake_discontinuous:
+            raise ValueError("Trying to convert to continuous function space "
+                             " with :param:`assert_fdrake_discontinuous` set "
+                             " to *True*")
         # make sure out is a firedrake function in an appropriate
         # function space
         if out is not None:
@@ -273,16 +313,47 @@ class FromFiredrakeConnection:
         if len(out.dat.data.shape) == 1 and len(mm_field.shape) > 1:
             mm_field = mm_field.reshape(mm_field.shape[1])
 
-        # resample from nodes
+        # resample from nodes on reordered view. Have to do this in
+        # a bit of a roundabout way to be careful about duplicated
+        # firedrake nodes.
         by_cell_field_view = self.to_discr.groups[0].view(mm_field)
-        by_cell_out_view = self.to_discr.groups[0].view(out.dat.data.T)
-        np.matmul(by_cell_field_view, self._resampling_mat_mm2fd.T,
-                  out=by_cell_out_view)
-
-        # reorder data
         if len(out.dat.data.shape) == 1:
-            out.dat.data[:] = out.dat.data[self._reordering_arr_mm2fd]
+            reordered_outdata = out.dat.data[self._reordering_arr_fd2mm]
         else:
-            out.dat.data[:] = out.dat.data[self._reordering_arr_mm2fd, :]
+            reordered_outdata = out.dat.data.T[:, self._reordering_arr_fd2mm]
+        by_cell_reordered_view = self.to_discr.groups[0].view(reordered_outdata)
+        np.matmul(by_cell_field_view, self._resampling_mat_mm2fd.T,
+                  out=by_cell_reordered_view)
+        out.dat.data[self._reordering_arr_fd2mm] = reordered_outdata.T
+
+        # Continuity checks
+        if self._ufl_element.family() == 'Lagrange' \
+                and continuity_tolerance is not None:
+            assert isinstance(continuity_tolerance, float)
+            assert continuity_tolerance >= 0
+            # Check each firedrake node which has been duplicated
+            # that all associated values are within the continuity
+            # tolerance
+            for fd_inode, duplicated_mm_nodes in \
+                    six.iteritems(self._duplicate_nodes):
+                mm_inode = duplicated_mm_nodes[0]
+                # Make sure to compare using reordered_outdata not mm_field,
+                # because two meshmode nodes associated to the same firedrake
+                # nodes may have been resampled to distinct nodes on different
+                # elements. reordered_outdata has undone that resampling.
+                for dup_mm_inode in duplicated_mm_nodes[1:]:
+                    if len(reordered_outdata.shape) > 1:
+                        dist = la.norm(reordered_outdata[:, mm_inode]
+                                       - reordered_outdata[:, dup_mm_inode])
+                    else:
+                        dist = la.norm(reordered_outdata[mm_inode]
+                                       - reordered_outdata[dup_mm_inode])
+                    if dist >= continuity_tolerance:
+                        raise ValueError("Meshmode nodes %s and %s represent "
+                                         "the same firedrake node %s, but "
+                                         ":param:`mm_field`'s values are "
+                                         " %s > %s apart)"
+                                         % (mm_inode, dup_mm_inode, fd_inode,
+                                            dist, continuity_tolerance))
 
         return out
diff --git a/meshmode/interop/firedrake/mesh.py b/meshmode/interop/firedrake/mesh.py
index 470cd724dcc732a75d3c50f7c1a24d3dcfa62fb1..d9060b9bfeadf04d5e19a850893797ae5622f33b 100644
--- a/meshmode/interop/firedrake/mesh.py
+++ b/meshmode/interop/firedrake/mesh.py
@@ -267,25 +267,13 @@ def _get_firedrake_orientations(fdrake_mesh, unflipped_group, vertices,
     Return the orientations of the mesh elements:
 
     :param fdrake_mesh: A :mod:`firedrake` instance of :class:`MeshGeometry`
-
     :param unflipped_group: A :class:`SimplexElementGroup` instance with
         (potentially) some negatively oriented elements.
-
     :param vertices: The vertex coordinates as a numpy array of shape
         *(ambient_dim, nvertices)*
-
-    :param normals: _Only_ used if :param:`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 :param:`mesh`*.coordinate.function_space()*'s
-          :attribute:`cell_node_list`)
-
-    :param no_normals_warn: If *True*, raises a warning
-        if :param:`mesh` is a 1-surface embedded in 2-space
-        and :param:`normals` is *None*.
+    :param normals: As described in the kwargs of :func:`import_firedrake_mesh`
+    :param no_normals_warn: As described in the kwargs of
+        :func:`import_firedrake_mesh`
 
     :return: A numpy array, the *i*th element is > 0 if the *ith* element
         is positively oriented, < 0 if negatively oriented.
@@ -337,7 +325,7 @@ def _get_firedrake_orientations(fdrake_mesh, unflipped_group, vertices,
 
 # {{{ Mesh conversion
 
-def import_firedrake_mesh(fdrake_mesh):
+def import_firedrake_mesh(fdrake_mesh, **kwargs):
     """
     Create a :mod:`meshmode` :class:`Mesh` from a :mod:`firedrake`
     :class:`MeshGeometry` with the same cells/elements, vertices, nodes,
@@ -357,7 +345,14 @@ def import_firedrake_mesh(fdrake_mesh):
         1, 2, or 3 and have co-dimension of 0 or 1.
         It must use a simplex as a reference element.
 
-        Finally, its ``coordinates`` attribute must have a function
+        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 the keyword arguments below.
+
+        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
@@ -366,6 +361,19 @@ def import_firedrake_mesh(fdrake_mesh):
         verify this by looking at the ``[0]`` entry of
         ``fdrake_mesh.coordinates.function_space().finat_element.entity_dofs()``.
 
+    :Keyword Arguments:
+        * *normals*: _Only_ used if :param:`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 :param:`mesh`*.coordinate.function_space()*'s
+              :attribute:`cell_node_list`)
+        * *no_normals_warn: If *True* (the default), raises a warning
+          if :param:`fdrake_mesh` is a 1-surface embedded in 2-space
+          and :param:`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)
@@ -449,8 +457,11 @@ def import_firedrake_mesh(fdrake_mesh):
     vertices = np.array([vertices[i] for i in range(len(vertices))]).T
 
     # Use the vertices to compute the orientations and flip the group
-    # FIXME : Allow for passing in normals/no normals warn
-    orient = _get_firedrake_orientations(fdrake_mesh, unflipped_group, vertices)
+    normals = kwargs.get('normals', None)
+    no_normals_warn = kwargs.get('no_normals_warn', True)
+    orient = _get_firedrake_orientations(fdrake_mesh, unflipped_group, vertices,
+                                         normals=normals,
+                                         no_normals_warn=no_normals_warn)
     from meshmode.mesh.processing import flip_simplex_element_group
     group = flip_simplex_element_group(vertices, unflipped_group, orient < 0)
 
diff --git a/test/test_firedrake_interop.py b/test/test_firedrake_interop.py
index 31e4a4ba8484424f859042c5a6a7a6cb2de7959f..7074b58e2fbe35b942a0e8857c8459f636ff9527 100644
--- a/test/test_firedrake_interop.py
+++ b/test/test_firedrake_interop.py
@@ -181,7 +181,7 @@ def test_function_transfer(ctx_factory,
 
 def check_idempotency(fdrake_connection, fdrake_function):
     """
-    Make sure fd->mm->fd and mm->fd->mm are identity for DG spaces
+    Make sure fd->mm->fd and mm->fd->mm are identity
     """
     vdim = None
     if len(fdrake_function.dat.data.shape) > 1:
@@ -191,7 +191,9 @@ def check_idempotency(fdrake_connection, fdrake_function):
     # Test for idempotency fd->mm->fd
     mm_field = fdrake_connection.from_firedrake(fdrake_function)
     fdrake_function_copy = Function(fdrake_fspace)
-    fdrake_connection.from_meshmode(mm_field, fdrake_function_copy)
+    fdrake_connection.from_meshmode(mm_field, fdrake_function_copy,
+                                    assert_fdrake_discontinuous=False,
+                                    continuity_tolerance=1e-8)
 
     np.testing.assert_allclose(fdrake_function_copy.dat.data,
                                fdrake_function.dat.data,
@@ -202,11 +204,12 @@ def check_idempotency(fdrake_connection, fdrake_function):
     np.testing.assert_allclose(mm_field_copy, mm_field, atol=CLOSE_ATOL)
 
 
-def test_scalar_idempotency(ctx_factory, fdrake_mesh, fdrake_degree):
+def test_scalar_idempotency(ctx_factory, fdrake_mesh,
+                            fdrake_family, fdrake_degree):
     """
-    Make sure fd->mm->fd and mm->fd->mm are identity for scalar DG spaces
+    Make sure fd->mm->fd and mm->fd->mm are identity for scalar spaces
     """
-    fdrake_fspace = FunctionSpace(fdrake_mesh, 'DG', fdrake_degree)
+    fdrake_fspace = FunctionSpace(fdrake_mesh, fdrake_family, fdrake_degree)
 
     # Make a function with unique values at each node
     fdrake_unique = Function(fdrake_fspace)
@@ -218,11 +221,12 @@ def test_scalar_idempotency(ctx_factory, fdrake_mesh, fdrake_degree):
     check_idempotency(fdrake_connection, fdrake_unique)
 
 
-def test_vector_idempotency(ctx_factory, fdrake_mesh, fdrake_degree):
+def test_vector_idempotency(ctx_factory, fdrake_mesh,
+                            fdrake_family, fdrake_degree):
     """
-    Make sure fd->mm->fd and mm->fd->mm are identity for vector DG spaces
+    Make sure fd->mm->fd and mm->fd->mm are identity for vector spaces
     """
-    fdrake_vfspace = VectorFunctionSpace(fdrake_mesh, 'DG', fdrake_degree)
+    fdrake_vfspace = VectorFunctionSpace(fdrake_mesh, fdrake_family, fdrake_degree)
 
     # Make a function with unique values at each node
     xx = SpatialCoordinate(fdrake_vfspace.mesh())