diff --git a/python/dune/codegen/__init__.py b/python/dune/codegen/__init__.py
index 243911af33e57531a147b9b4814b14ad65fd408b..e892baf44c4c9fb301536c15c40059a5acc5d602 100644
--- a/python/dune/codegen/__init__.py
+++ b/python/dune/codegen/__init__.py
@@ -6,8 +6,7 @@ os.environ["OMP_NUM_THREADS"] = "1"
 # Trigger imports that involve monkey patching!
 import dune.codegen.loopy.symbolic  # noqa
 
-# Trigger some imports that are needed to have all backend implementations visible
-# to the selection mechanisms
+# Trigger some imports that are needed to have all mixin implementations visible
 import dune.codegen.pdelab  # noqa
 import dune.codegen.sumfact  # noqa
 import dune.codegen.blockstructured  # noqa
diff --git a/python/dune/codegen/blockstructured/__init__.py b/python/dune/codegen/blockstructured/__init__.py
index 138b11463a554ee58ac236d4439120172aabb640..dc9db5e5a7204099e6e8345a45dd5889de66f589 100644
--- a/python/dune/codegen/blockstructured/__init__.py
+++ b/python/dune/codegen/blockstructured/__init__.py
@@ -1,62 +1,7 @@
+import dune.codegen.blockstructured.accumulation
 import dune.codegen.blockstructured.quadrature
 import dune.codegen.blockstructured.argument
 import dune.codegen.blockstructured.geometry
 import dune.codegen.blockstructured.spaces
 import dune.codegen.blockstructured.basis
 import dune.codegen.blockstructured.transformations
-from dune.codegen.options import get_form_option
-from dune.codegen.pdelab.quadrature import pymbolic_quadrature_position
-from dune.codegen.blockstructured.spaces import lfs_inames
-from dune.codegen.blockstructured.basis import (pymbolic_reference_gradient,
-                                                pymbolic_basis)
-from dune.codegen.blockstructured.geometry import (pymbolic_jacobian_inverse,
-                                                   pymbolic_jacobian_determinant,
-                                                   pymbolic_facet_jacobian_determinant,
-                                                   to_global)
-from dune.codegen.blockstructured.tools import sub_element_inames
-from dune.codegen.pdelab import PDELabInterface
-
-
-class BlockStructuredInterface(PDELabInterface):
-    def __init__(self):
-        PDELabInterface.__init__(self)
-
-    def generate_accumulation_instruction(self, expr, visitor):
-        if get_form_option('vectorization_blockstructured'):
-            from dune.codegen.blockstructured.accumulation import generate_accumulation_instruction
-            return generate_accumulation_instruction(expr, visitor)
-        else:
-            return PDELabInterface.generate_accumulation_instruction(self, expr, visitor)
-    #
-    # Local function space related generator functions
-    #
-    # TODO current way to squeeze subelem iname in, not really ideal
-
-    def lfs_inames(self, element, restriction, number=None, context=''):
-        return sub_element_inames() + lfs_inames(element, restriction, number, context)
-
-    #
-    # Test and trial function related generator functions
-    #
-
-    def pymbolic_basis(self, element, restriction, number, context=''):
-        return pymbolic_basis(element, restriction, number, context)
-
-    def pymbolic_reference_gradient(self, element, restriction, number, context=''):
-        return pymbolic_reference_gradient(element, restriction, number, context)
-
-    #
-    # Geometry related generator functions
-    #
-
-    def pymbolic_spatial_coordinate(self):
-        return to_global(pymbolic_quadrature_position())
-
-    def pymbolic_jacobian_determinant(self):
-        return pymbolic_jacobian_determinant()
-
-    def pymbolic_jacobian_inverse(self, i, j, restriction):
-        return pymbolic_jacobian_inverse(i, j, restriction)
-
-    def pymbolic_facet_jacobian_determinant(self):
-        return pymbolic_facet_jacobian_determinant()
diff --git a/python/dune/codegen/blockstructured/accumulation.py b/python/dune/codegen/blockstructured/accumulation.py
index 469e69b4bba506ed50d2800c8bdd98c71338ca03..9af9759d5e3426df12a10db39f94048e48006867 100644
--- a/python/dune/codegen/blockstructured/accumulation.py
+++ b/python/dune/codegen/blockstructured/accumulation.py
@@ -1,17 +1,27 @@
-from dune.codegen.generation import instruction
+from dune.codegen.blockstructured.tools import sub_element_inames
+from dune.codegen.generation import accumulation_mixin, instruction
 from dune.codegen.loopy.target import dtype_floatingpoint
 from dune.codegen.options import get_form_option
-from dune.codegen.pdelab.geometry import world_dimension
-from dune.codegen.pdelab.localoperator import determine_accumulation_space
+from dune.codegen.pdelab.geometry import world_dimension, name_intersection_geometry_wrapper
+from dune.codegen.pdelab.localoperator import determine_accumulation_space, GenericAccumulationMixin
 from dune.codegen.pdelab.argument import name_accumulation_variable
 from dune.codegen.pdelab.localoperator import boundary_predicates
-from dune.codegen.generation.loopy import function_mangler, globalarg
+from dune.codegen.generation.loopy import function_mangler, globalarg, temporary_variable
 import loopy as lp
 import pymbolic.primitives as prim
 
 from loopy.match import Writes
 
 
+@accumulation_mixin("blockstructured")
+class BlockStructuredAccumulationMixin(GenericAccumulationMixin):
+    def generate_accumulation_instruction(self, expr):
+        if get_form_option('vectorization_blockstructured'):
+            return generate_accumulation_instruction_vectorized(expr, self)
+        else:
+            return generate_accumulation_instruction(expr, self)
+
+
 def name_accumulation_alias(container, accumspace):
     name = container + "_" + accumspace.lfs.name + "_alias"
     name_tail = container + "_" + accumspace.lfs.name + "_alias_tail"
@@ -40,24 +50,90 @@ def residual_weight_mangler(knl, func, arg_dtypes):
         return lp.CallMangleInfo(func, (lp.types.NumpyType(dtype_floatingpoint()),), ())
 
 
+def blockstructured_boundary_predicated(measure, subdomain_id):
+    predicates = []
+
+    if subdomain_id not in ['everywhere', 'otherwise']:
+        subelem_inames = sub_element_inames()
+
+        face_id = "face_id"
+        temporary_variable(face_id, shape=())
+        instruction(code="{} = {}.indexInInside();".format(face_id, name_intersection_geometry_wrapper()),
+                    assignees=(face_id,))
+
+        def face_id_equals(id):
+            return prim.Comparison(prim.Variable(face_id), "==", id)
+
+        def iname_equals(iname, i):
+            return prim.Comparison(prim.Variable(iname), "==", i)
+
+        k = get_form_option("number_of_blocks")
+
+        if world_dimension() >= 2:
+            predicates.append(prim.If(face_id_equals(0), iname_equals(subelem_inames[0], 0), True))
+            predicates.append(prim.If(face_id_equals(1), iname_equals(subelem_inames[0], k - 1), True))
+            predicates.append(prim.If(face_id_equals(2), iname_equals(subelem_inames[1], 0), True))
+            predicates.append(prim.If(face_id_equals(3), iname_equals(subelem_inames[1], k - 1), True))
+        if world_dimension() == 3:
+            predicates.append(prim.If(face_id_equals(4), iname_equals(subelem_inames[2], 0), True))
+            predicates.append(prim.If(face_id_equals(5), iname_equals(subelem_inames[2], k - 1), True))
+    return frozenset(predicates)
+
+
 def generate_accumulation_instruction(expr, visitor):
     # Collect the lfs and lfs indices for the accumulate call
     test_lfs = determine_accumulation_space(visitor.test_info, 0)
     # In the jacobian case, also determine the space for the ansatz space
     ansatz_lfs = determine_accumulation_space(visitor.trial_info, 1)
 
+    # Collect the lfs and lfs indices for the accumulate call
+    accumvar = name_accumulation_variable(test_lfs.get_restriction() + ansatz_lfs.get_restriction())
+
+    predicates = boundary_predicates(visitor.measure, visitor.subdomain_id)
+    predicates = predicates.union(blockstructured_boundary_predicated(visitor.measure, visitor.subdomain_id))
+
+    rank = 1 if ansatz_lfs.lfs is None else 2
+
+    from dune.codegen.pdelab.argument import PDELabAccumulationFunction
+    from pymbolic.primitives import Call
+    accexpr = Call(PDELabAccumulationFunction(accumvar, rank),
+                   (test_lfs.get_args() + ansatz_lfs.get_args() + (expr,))
+                   )
+
+    from dune.codegen.generation import instruction
+    quad_inames = visitor.quadrature_inames()
+    lfs_inames = frozenset(visitor.test_info.inames)
+    if visitor.trial_info:
+        lfs_inames = lfs_inames.union(visitor.trial_info.inames)
+
+    instruction(assignees=(),
+                expression=accexpr,
+                forced_iname_deps=lfs_inames.union(frozenset(quad_inames)),
+                forced_iname_deps_is_final=True,
+                predicates=predicates
+                )
+
+
+def generate_accumulation_instruction_vectorized(expr, visitor):
+    # Collect the lfs and lfs indices for the accumulate call
+    test_lfs = determine_accumulation_space(visitor.test_info, 0)
+    # In the jacobian case, also determine the space for the ansatz space
+    ansatz_lfs = determine_accumulation_space(visitor.trial_info, 1)
+
     # Collect the lfs and lfs indices for the accumulate call
     accumvar = name_accumulation_variable(test_lfs.get_restriction() + ansatz_lfs.get_restriction())
     accumvar_alias = name_accumulation_alias(accumvar, test_lfs)
 
     predicates = boundary_predicates(visitor.measure, visitor.subdomain_id)
+    predicates = predicates.union(blockstructured_boundary_predicated(visitor.measure, visitor.subdomain_id))
 
-    quad_inames = visitor.interface.quadrature_inames()
+    quad_inames = visitor.quadrature_inames()
     lfs_inames = visitor.test_info.inames
     if visitor.trial_info:
         lfs_inames = lfs_inames + visitor.trial_info.inames
 
-    assignee = prim.Subscript(prim.Variable(accumvar_alias), tuple(prim.Variable(i) for i in lfs_inames))
+    assignee = prim.Subscript(prim.Variable(accumvar_alias),
+                              tuple(prim.Variable(i) for i in sub_element_inames() + lfs_inames))
 
     expr_with_weight = prim.Product((expr, prim.Call(prim.Variable(accumvar + '.weight'), ())))
 
diff --git a/python/dune/codegen/blockstructured/argument.py b/python/dune/codegen/blockstructured/argument.py
index afe120a8ec142820cf8ea3944538802b721f4469..420773e85bea93ee55cb310255da7fa60d55d9de 100644
--- a/python/dune/codegen/blockstructured/argument.py
+++ b/python/dune/codegen/blockstructured/argument.py
@@ -1,5 +1,4 @@
-from dune.codegen.generation import (backend,
-                                     kernel_cached,
+from dune.codegen.generation import (kernel_cached,
                                      valuearg, instruction, globalarg)
 from dune.codegen.options import get_form_option
 from dune.codegen.pdelab.argument import CoefficientAccess
@@ -27,7 +26,6 @@ def name_alias(container, lfs, element):
 
 # TODO remove the need for element
 @kernel_cached
-@backend(interface="pymbolic_coefficient", name="blockstructured")
 def pymbolic_coefficient(container, lfs, element, index):
     # TODO introduce a proper type for local function spaces!
     if isinstance(lfs, str):
diff --git a/python/dune/codegen/blockstructured/basis.py b/python/dune/codegen/blockstructured/basis.py
index dcaf258b17e0dc4128860d3c58b9801a454f4471..5af8bb48f2bb625a0b336c5cae6d42863f0063c5 100644
--- a/python/dune/codegen/blockstructured/basis.py
+++ b/python/dune/codegen/blockstructured/basis.py
@@ -1,35 +1,142 @@
-from dune.codegen.generation import (backend,
+from loopy import Reduction
+
+from dune.codegen.generation import (basis_mixin,
                                      kernel_cached,
-                                     get_backend,
                                      instruction,
                                      temporary_variable,
                                      globalarg,
                                      class_member,
                                      initializer_list,
                                      include_file,)
-from dune.codegen.tools import get_pymbolic_basename
+from dune.codegen.tools import get_pymbolic_basename, get_pymbolic_indices
 from dune.codegen.loopy.target import type_floatingpoint
-from dune.codegen.pdelab.basis import (declare_cache_temporary,
+from dune.codegen.pdelab.basis import (GenericBasisMixin,
+                                       declare_cache_temporary,
                                        name_localbasis_cache,
                                        type_localbasis,
                                        FEM_name_mangling)
 from dune.codegen.pdelab.driver import (isPk,
                                         isQk,
                                         )
-from dune.codegen.pdelab.geometry import world_dimension
-from dune.codegen.pdelab.quadrature import pymbolic_quadrature_position_in_cell
-from dune.codegen.pdelab.spaces import type_leaf_gfs
+from dune.codegen.pdelab.geometry import world_dimension, component_iname
+from dune.codegen.pdelab.spaces import type_leaf_gfs, name_lfs
 from dune.codegen.pdelab.restriction import restricted_name
 from dune.codegen.blockstructured.spaces import lfs_inames
-from dune.codegen.blockstructured.tools import tensor_index_to_sequential_index
+from dune.codegen.blockstructured.tools import tensor_index_to_sequential_index, sub_element_inames
 
 from ufl import MixedElement
 
 import pymbolic.primitives as prim
 
 
+@basis_mixin("blockstructured")
+class BlockStructuredBasisMixin(GenericBasisMixin):
+    def lfs_inames(self, element, restriction, number, context=""):
+        return lfs_inames(element, restriction, number, context=context)
+
+    def implement_basis(self, element, restriction, number, context=''):
+        assert not isinstance(element, MixedElement)
+        name = "phi_{}".format(FEM_name_mangling(element))
+        name = restricted_name(name, restriction)
+        self.evaluate_basis(element, name, restriction)
+        inames = self.lfs_inames(element, restriction, number, context=context)
+
+        return prim.Subscript(prim.Variable(name), (tensor_index_to_sequential_index(inames, element.degree() + 1), 0))
+
+    @kernel_cached
+    def evaluate_basis(self, element, name, restriction):
+        temporary_variable(name, shape=((element.degree() + 1)**world_dimension(), 1),
+                           decl_method=declare_cache_temporary(element, restriction, 'Function'))
+        cache = name_localbasis_cache(element)
+        qp = self.to_cell(self.quadrature_position_in_micro())
+        localbasis = name_localbasis(element)
+        instruction(inames=self.quadrature_inames(),
+                    code='{} = {}.evaluateFunction({}, {});'.format(name, cache, str(qp), localbasis),
+                    assignees=frozenset({name}),
+                    read_variables=frozenset({get_pymbolic_basename(qp)}),
+                    )
+
+    def implement_reference_gradient(self, element, restriction, number, context=''):
+        assert not isinstance(element, MixedElement)
+        name = "js_{}".format(FEM_name_mangling(element))
+        name = restricted_name(name, restriction)
+        self.evaluate_reference_gradient(element, name, restriction)
+        inames = self.lfs_inames(element, restriction, number, context=context)
+
+        return prim.Subscript(prim.Variable(name), (tensor_index_to_sequential_index(inames, element.degree() + 1), 0))
+
+    @kernel_cached
+    def evaluate_reference_gradient(self, element, name, restriction):
+        temporary_variable(name, shape=((element.degree() + 1)**world_dimension(), 1, world_dimension()),
+                           decl_method=declare_cache_temporary(element, restriction, 'Jacobian'))
+        cache = name_localbasis_cache(element)
+        qp = self.to_cell(self.quadrature_position_in_micro())
+        localbasis = name_localbasis(element)
+        instruction(inames=self.quadrature_inames(),
+                    code='{} = {}.evaluateJacobian({}, {});'.format(name, cache, str(qp), localbasis),
+                    assignees=frozenset({name}),
+                    read_variables=frozenset({get_pymbolic_basename(qp)}),
+                    )
+
+    @kernel_cached
+    def evaluate_coefficient_gradient(self, element, name, container, restriction, index):
+        sub_element = element
+        if isinstance(element, MixedElement):
+            sub_element = element.extract_component(index)[1]
+        from ufl import FiniteElement
+        assert isinstance(sub_element, FiniteElement)
+
+        temporary_variable(name, shape=(element.cell().geometric_dimension(),), managed=True)
+
+        dimindex = component_iname(count=0)
+
+        lfs = name_lfs(element, restriction, index)
+        basis = self.implement_reference_gradient(sub_element, restriction, 0, context='trialgrad')
+        basisindex = get_pymbolic_indices(basis)[:-1]
+        from dune.codegen.tools import maybe_wrap_subscript
+        basis = maybe_wrap_subscript(basis, prim.Variable(dimindex))
+
+        from dune.codegen.blockstructured.argument import pymbolic_coefficient
+        coeff = pymbolic_coefficient(container, lfs, sub_element, basisindex)
+
+        assignee = prim.Subscript(prim.Variable(name), (prim.Variable(dimindex),))
+        reduction_expr = prim.Product((coeff, basis))
+
+        instruction(expression=Reduction("sum", basisindex, reduction_expr, allow_simultaneous=True),
+                    assignee=assignee,
+                    forced_iname_deps=frozenset(self.quadrature_inames() + (dimindex,) + sub_element_inames()),
+                    forced_iname_deps_is_final=True,
+                    )
+
+    @kernel_cached
+    def evaluate_coefficient(self, element, name, container, restriction, index):
+        sub_element = element
+        if isinstance(element, MixedElement):
+            sub_element = element.extract_component(index)[1]
+
+        from ufl import FiniteElement
+        assert isinstance(sub_element, FiniteElement)
+
+        temporary_variable(name, shape=(), managed=True)
+
+        lfs = name_lfs(element, restriction, index)
+        basis = self.implement_basis(sub_element, restriction, 0, context='trial')
+        basisindex = get_pymbolic_indices(basis)[:-1]
+
+        from dune.codegen.blockstructured.argument import pymbolic_coefficient
+        coeff = pymbolic_coefficient(container, lfs, sub_element, basisindex)
+
+        assignee = prim.Variable(name)
+        reduction_expr = prim.Product((coeff, basis))
+
+        instruction(expression=Reduction("sum", basisindex, reduction_expr, allow_simultaneous=True),
+                    assignee=assignee,
+                    forced_iname_deps=frozenset(self.quadrature_inames() + sub_element_inames()),
+                    forced_iname_deps_is_final=True,
+                    )
+
+
 # define FE basis explicitly in localoperator
-@backend(interface="typedef_localbasis", name="blockstructured")
 @class_member(classtag="operator")
 def typedef_localbasis(element, name):
     df = "typename {}::Traits::GridView::ctype".format(type_leaf_gfs(element))
@@ -67,51 +174,3 @@ def name_localbasis(leaf_element):
     globalarg(name)
     define_localbasis(leaf_element, name)
     return name
-
-
-@kernel_cached
-def evaluate_basis(leaf_element, name, restriction):
-    temporary_variable(name, shape=((leaf_element.degree() + 1)**world_dimension(), 1),
-                       decl_method=declare_cache_temporary(leaf_element, restriction, 'Function'))
-    cache = name_localbasis_cache(leaf_element)
-    qp = pymbolic_quadrature_position_in_cell(restriction)
-    localbasis = name_localbasis(leaf_element)
-    instruction(inames=get_backend("quad_inames")(),
-                code='{} = {}.evaluateFunction({}, {});'.format(name, cache, str(qp), localbasis),
-                assignees=frozenset({name}),
-                read_variables=frozenset({get_pymbolic_basename(qp)}),
-                )
-
-
-def pymbolic_basis(leaf_element, restriction, number, context=''):
-    assert not isinstance(leaf_element, MixedElement)
-    name = "phi_{}".format(FEM_name_mangling(leaf_element))
-    name = restricted_name(name, restriction)
-    evaluate_basis(leaf_element, name, restriction)
-    inames = lfs_inames(leaf_element, restriction, number, context=context)
-
-    return prim.Subscript(prim.Variable(name), (tensor_index_to_sequential_index(inames, leaf_element.degree() + 1), 0))
-
-
-@kernel_cached
-def evaluate_reference_gradient(leaf_element, name, restriction):
-    temporary_variable(name, shape=((leaf_element.degree() + 1)**world_dimension(), 1, world_dimension()),
-                       decl_method=declare_cache_temporary(leaf_element, restriction, 'Jacobian'))
-    cache = name_localbasis_cache(leaf_element)
-    qp = pymbolic_quadrature_position_in_cell(restriction)
-    localbasis = name_localbasis(leaf_element)
-    instruction(inames=get_backend("quad_inames")(),
-                code='{} = {}.evaluateJacobian({}, {});'.format(name, cache, str(qp), localbasis),
-                assignees=frozenset({name}),
-                read_variables=frozenset({get_pymbolic_basename(qp)}),
-                )
-
-
-def pymbolic_reference_gradient(leaf_element, restriction, number, context=''):
-    assert not isinstance(leaf_element, MixedElement)
-    name = "js_{}".format(FEM_name_mangling(leaf_element))
-    name = restricted_name(name, restriction)
-    evaluate_reference_gradient(leaf_element, name, restriction)
-    inames = lfs_inames(leaf_element, restriction, number, context=context)
-
-    return prim.Subscript(prim.Variable(name), (tensor_index_to_sequential_index(inames, leaf_element.degree() + 1), 0))
diff --git a/python/dune/codegen/blockstructured/geometry.py b/python/dune/codegen/blockstructured/geometry.py
index 4f1a71d3e3674ea768ef63842ca5aa4def21db61..3ad3a9026d8a5287e54e3950aef0aa199d08cc4e 100644
--- a/python/dune/codegen/blockstructured/geometry.py
+++ b/python/dune/codegen/blockstructured/geometry.py
@@ -1,22 +1,116 @@
-from dune.codegen.pdelab.restriction import restricted_name
-
-from dune.codegen.generation import (get_backend,
+from dune.codegen.generation import (geometry_mixin,
                                      temporary_variable,
                                      instruction,
-                                     get_global_context_value)
+                                     get_global_context_value,
+                                     domain)
 from dune.codegen.tools import get_pymbolic_basename
-from dune.codegen.options import (get_form_option,
-                                  option_switch, get_option)
-from dune.codegen.pdelab.geometry import (world_dimension,
+from dune.codegen.options import get_form_option
+from dune.codegen.pdelab.geometry import (AxiparallelGeometryMixin,
+                                          EquidistantGeometryMixin,
+                                          GenericPDELabGeometryMixin,
+                                          world_dimension,
                                           local_dimension,
-                                          name_facet_jacobian_determinant,
-                                          name_element_corners,
-                                          component_iname)
-from dune.codegen.blockstructured.tools import sub_element_inames
+                                          component_iname,
+                                          enforce_boundary_restriction,
+                                          restricted_name,
+                                          name_cell_geometry
+                                          )
+from dune.codegen.blockstructured.tools import (sub_element_inames,
+                                                name_point_in_macro,
+                                                )
+from dune.codegen.ufl.modified_terminals import Restriction
 import pymbolic.primitives as prim
 from loopy.match import Writes
 
 
+@geometry_mixin("blockstructured_multilinear")
+class BlockStructuredGeometryMixin(GenericPDELabGeometryMixin):
+    def spatial_coordinate(self, o):
+        if self.measure == 'cell':
+            return self.to_global(self.quadrature_position_in_macro())
+        else:
+            assert self.measure == 'exterior_facet' or self.measure == 'interior_facet'
+            micro_qp = self.to_cell(self.quadrature_position_in_micro())
+
+            macro_qp = prim.Variable(name_point_in_macro(micro_qp, self), )
+
+            return self.to_global(macro_qp)
+
+    def to_global(self, local):
+        assert isinstance(local, prim.Expression)
+        name = get_pymbolic_basename(local) + "_global"
+
+        # TODO need to assert somehow that local is of codim 0
+        compute_multilinear_to_global_transformation(name, local, self)
+        return prim.Variable(name)
+
+    def facet_jacobian_determinant(self, o):
+        return prim.Quotient(GenericPDELabGeometryMixin.facet_jacobian_determinant(self, o),
+                             prim.Power(get_form_option("number_of_blocks"), local_dimension()))
+
+    def jacobian_determinant(self, o):
+        name = 'detjac'
+        self.define_jacobian_determinant(name)
+        return prim.Quotient(prim.Variable(name),
+                             prim.Power(get_form_option("number_of_blocks"), local_dimension()))
+
+    def define_jacobian_determinant(self, name):
+        temporary_variable(name, shape=(), managed=True)
+
+        determinant_signed = name_jacobian_determinant_signed(self)
+
+        return instruction(expression=prim.Call(prim.Variable("abs"), (prim.Variable(determinant_signed),)),
+                           assignee=prim.Variable(name),
+                           within_inames=frozenset(sub_element_inames() + self.quadrature_inames()),
+                           depends_on=frozenset({Writes(determinant_signed)})
+                           )
+
+    def jacobian_inverse(self, o):
+        restriction = enforce_boundary_restriction(self)
+
+        assert(len(self.indices) == 2)
+        i, j = self.indices
+        self.indices = None
+
+        name = restricted_name("jit", restriction)
+        self.define_jacobian_inverse_transposed(name, restriction)
+
+        return prim.Product((prim.Subscript(prim.Variable(name), (j, i)),
+                             get_form_option("number_of_blocks")))
+
+    def define_jacobian_inverse_transposed(self, name, restriction):
+        temporary_variable(name, shape=(world_dimension(), world_dimension()), managed=True)
+
+        jacobian = name_jacobian_matrix(self)
+        det_inv = name_jacobian_determinant_inverse(self)
+
+        compute_inverse_transposed(name, det_inv, jacobian, self)
+
+
+@geometry_mixin("blockstructured_axiparallel")
+class AxiparallelBlockStructuredGeometryMixin(AxiparallelGeometryMixin, BlockStructuredGeometryMixin):
+    def to_global(self, local):
+        assert isinstance(local, prim.Expression)
+        name = get_pymbolic_basename(local) + "_global"
+
+        # TODO need to assert somehow that local is of codim 0
+        compute_axiparallel_to_global_transformation(name, local, self)
+        return prim.Variable(name)
+
+    def jacobian_determinant(self, o):
+        return prim.Quotient(AxiparallelGeometryMixin.jacobian_determinant(self, o),
+                             prim.Power(get_form_option("number_of_blocks"), local_dimension()))
+
+    def jacobian_inverse(self, o):
+        return prim.Product((AxiparallelGeometryMixin.jacobian_inverse(self, o),
+                             get_form_option("number_of_blocks")))
+
+
+@geometry_mixin("blockstructured_equidistant")
+class EquidistantBlockStructuredGeometryMixin(EquidistantGeometryMixin, AxiparallelBlockStructuredGeometryMixin):
+    pass
+
+
 # TODO warum muss within_inames_is_final=True gesetzt werden?
 def compute_corner_diff(first, second, additional_terms=tuple()):
     corners = name_element_corners()
@@ -63,8 +157,8 @@ def bilinear_transformation_coefficients():
         raise NotImplementedError()
 
 
-def compute_jacobian(name):
-    pymbolic_pos = get_backend("quad_pos", selector=option_switch("blockstructured"))()
+def compute_jacobian(name, visitor):
+    pymbolic_pos = visitor.quadrature_position_in_macro()
     jac_iname = component_iname(context="jac")
 
     coefficients = bilinear_transformation_coefficients()
@@ -103,23 +197,23 @@ def compute_jacobian(name):
     for i, expr in enumerate(expr_jac):
         instruction(expression=expr,
                     assignee=prim.Subscript(prim.Variable(name), (prim.Variable(jac_iname), i)),
-                    within_inames=frozenset((jac_iname, ) + sub_element_inames() + get_backend(interface="quad_inames")()),
+                    within_inames=frozenset((jac_iname, ) + sub_element_inames() + visitor.quadrature_inames()),
                     depends_on=frozenset({Writes(get_pymbolic_basename(pymbolic_pos))} | {Writes(cd) for cd in coefficients})
                     )
 
 
-def define_jacobian_matrix(name):
+def define_jacobian_matrix(name, visitor):
     temporary_variable(name, shape=(world_dimension(), world_dimension()), managed=True)
-    compute_jacobian(name)
+    compute_jacobian(name, visitor)
 
 
-def name_jacobian_matrix():
+def name_jacobian_matrix(visitor):
     name = "jacobian"
-    define_jacobian_matrix(name)
+    define_jacobian_matrix(name, visitor)
     return name
 
 
-def compute_determinant(name, matrix):
+def compute_determinant(name, matrix, visitor):
     dim = world_dimension()
     matrix_entry = [[prim.Subscript(prim.Variable(matrix), (i, j)) for j in range(dim)] for i in range(dim)]
     if dim == 2:
@@ -137,67 +231,40 @@ def compute_determinant(name, matrix):
         raise NotImplementedError()
     instruction(expression=expr_determinant,
                 assignee=prim.Variable(name),
-                within_inames=frozenset(sub_element_inames() + get_backend(interface="quad_inames")()),
+                within_inames=frozenset(sub_element_inames() + visitor.quadrature_inames()),
                 depends_on=frozenset({Writes(matrix)})
                 )
 
 
-def define_jacobian_determinant(name):
+def define_jacobian_determinant(name, visitor):
     temporary_variable(name, shape=(), managed=True)
-    jacobian = name_jacobian_matrix()
-    compute_determinant(name, jacobian)
+    jacobian = name_jacobian_matrix(visitor)
+    compute_determinant(name, jacobian, visitor)
 
 
-def define_jacobian_determinant_inverse(name):
+def define_jacobian_determinant_inverse(name, visitor):
     temporary_variable(name, shape=(), managed=True)
-    determinant = name_jacobian_determinant()
+    determinant = name_jacobian_determinant_signed(visitor)
     return instruction(expression=prim.Quotient(1., prim.Variable(determinant)),
                        assignee=prim.Variable(name),
-                       within_inames=frozenset(sub_element_inames() + get_backend(interface="quad_inames")()),
-                       depends_on=frozenset({Writes(determinant)})
-                       )
-
-
-def define_jacobian_determinant_abs(name):
-    temporary_variable(name, shape=(), managed=True)
-    determinant = name_jacobian_determinant()
-    return instruction(expression=prim.Call(prim.Variable("abs"), (prim.Variable(determinant),)),
-                       assignee=prim.Variable(name),
-                       within_inames=frozenset(sub_element_inames() + get_backend(interface="quad_inames")()),
+                       within_inames=frozenset(sub_element_inames() + visitor.quadrature_inames()),
                        depends_on=frozenset({Writes(determinant)})
                        )
 
 
-def name_jacobian_determinant():
-    name = "detjac"
-    define_jacobian_determinant(name)
+def name_jacobian_determinant_signed(visitor):
+    name = "detjac_signed"
+    define_jacobian_determinant(name, visitor)
     return name
 
 
-def name_jacobian_determinant_inverse():
+def name_jacobian_determinant_inverse(visitor):
     name = "detjac_inverse"
-    define_jacobian_determinant_inverse(name)
-    return name
-
-
-def name_jacobian_determinant_abs():
-    name = "detjac_abs"
-    define_jacobian_determinant_abs(name)
+    define_jacobian_determinant_inverse(name, visitor)
     return name
 
 
-# scale determinant according to the order of the blockstructuring
-def pymbolic_jacobian_determinant():
-    if get_form_option("constant_transformation_matrix"):
-        from dune.codegen.pdelab.geometry import name_jacobian_determinant
-        n_det = name_jacobian_determinant()
-    else:
-        n_det = name_jacobian_determinant_abs()
-    return prim.Quotient(prim.Variable(n_det),
-                         prim.Power(get_form_option("number_of_blocks"), local_dimension()))
-
-
-def compute_inverse_transposed(name, det_inv, matrix):
+def compute_inverse_transposed(name, det_inv, matrix, visitor):
     dim = world_dimension()
     matrix_entry = [[prim.Subscript(prim.Variable(matrix), (i, j)) for j in range(dim)] for i in range(dim)]
     assignee = [[prim.Subscript(prim.Variable(name), (i, j)) for j in range(dim)] for i in range(dim)]
@@ -244,76 +311,24 @@ def compute_inverse_transposed(name, det_inv, matrix):
         for i in range(dim):
             instruction(expression=exprs[i][j],
                         assignee=assignee[i][j],
-                        within_inames=frozenset(sub_element_inames() + get_backend(interface="quad_inames")()),
+                        within_inames=frozenset(sub_element_inames() + visitor.quadrature_inames()),
                         depends_on=frozenset({Writes(matrix), Writes(det_inv)}))
 
 
-def define_jacobian_inverse_transposed(name):
+def define_jacobian_inverse_transposed(name, visitor):
     temporary_variable(name, shape=(world_dimension(), world_dimension()), managed=True)
-    jacobian = name_jacobian_matrix()
-    det_inv = name_jacobian_determinant_inverse()
-    compute_inverse_transposed(name, det_inv, jacobian)
+    jacobian = name_jacobian_matrix(visitor)
+    det_inv = name_jacobian_determinant_inverse(visitor)
+    compute_inverse_transposed(name, det_inv, jacobian, visitor)
 
 
-def name_jacobian_inverse_transposed(restriction):
+def name_jacobian_inverse_transposed(restriction, visitor):
     name = restricted_name("jit", restriction)
-    define_jacobian_inverse_transposed(name)
-    return name
-
-
-# scale Jacobian according to the order of the blockstructure
-def pymbolic_jacobian_inverse(i, j, restriction):
-    if get_form_option("constant_transformation_matrix"):
-        from dune.codegen.pdelab.geometry import name_constant_jacobian_inverse_transposed
-        name_jit = name_constant_jacobian_inverse_transposed(restriction)
-    else:
-        name_jit = name_jacobian_inverse_transposed(restriction)
-    return prim.Product((get_form_option("number_of_blocks"),
-                         prim.Subscript(prim.Variable(name_jit), (j, i))))
-
-
-# scale determinant according to the order of the blockstructure
-def pymbolic_facet_jacobian_determinant():
-    return prim.Quotient(prim.Variable(name_facet_jacobian_determinant()),
-                         prim.Power(get_form_option("number_of_blocks"), local_dimension()))
-
-
-# translate a point in the micro element into macro coordinates
-def define_point_in_macro(name, point_in_micro):
-    dim = local_dimension()
-    if get_form_option('vectorization_blockstructured'):
-        temporary_variable(name, shape=(dim,), managed=True)
-    else:
-        temporary_variable(name, shape=(dim,), shape_impl=('fv',))
-
-    # point_macro = (point_micro + index_micro) / number_of_blocks
-    # where index_micro = tensor index of the micro element
-    subelem_inames = sub_element_inames()
-    for i in range(dim):
-        if isinstance(point_in_micro, prim.Subscript):
-            expr = prim.Subscript(point_in_micro.aggregate, point_in_micro.index + (i,))
-        else:
-            expr = prim.Subscript(point_in_micro, (i,))
-        expr = prim.Sum((expr, prim.Variable(subelem_inames[i]),))
-        expr = prim.Quotient(expr, get_form_option('number_of_blocks'))
-        # TODO relax within inames
-        instruction(assignee=prim.Subscript(prim.Variable(name), (i,)),
-                    expression=expr,
-                    within_inames=frozenset(subelem_inames + get_backend(interface="quad_inames")()),
-                    tags=frozenset({subelem_inames[i]})
-                    )
-
-
-# TODO add subelem inames if this function gets called
-# TODO change input parameter to string
-def name_point_in_macro(point_in_micro):
-    assert isinstance(point_in_micro, prim.Expression)
-    name = get_pymbolic_basename(point_in_micro) + "_macro"
-    define_point_in_macro(name, point_in_micro)
+    define_jacobian_inverse_transposed(name, visitor)
     return name
 
 
-def apply_default_to_global_transformation(name, local):
+def compute_multilinear_to_global_transformation(name, local, visitor):
     dim = world_dimension()
     temporary_variable(name, shape=(dim,), managed=True)
     corners = name_element_corners()
@@ -344,13 +359,13 @@ def apply_default_to_global_transformation(name, local):
     assignee = prim.Subscript(prim.Variable(name), (dim_pym,))
 
     instruction(assignee=assignee, expression=expr,
-                within_inames=frozenset(sub_element_inames() + get_backend(interface="quad_inames")() + (dim_pym.name,)),
+                within_inames=frozenset(sub_element_inames() + visitor.quadrature_inames() + (dim_pym.name,)),
                 within_inames_is_final=True,
-                depends_on=frozenset({Writes(local.name), Writes(corners)})
+                depends_on=frozenset({Writes(get_pymbolic_basename(local)), Writes(corners)})
                 )
 
 
-def apply_constant_to_global_transformation(name, local):
+def compute_axiparallel_to_global_transformation(name, local, visitor):
     dim = world_dimension()
     temporary_variable(name, shape=(dim,), managed=True)
     corners = name_element_corners()
@@ -367,27 +382,34 @@ def apply_constant_to_global_transformation(name, local):
     assignee = prim.Subscript(prim.Variable(name), (dim_pym,))
 
     instruction(assignee=assignee, expression=expr,
-                within_inames=frozenset(sub_element_inames() + get_backend(interface="quad_inames")() + (dim_pym.name,)),
+                within_inames=frozenset(sub_element_inames() + visitor.quadrature_inames() + (dim_pym.name,)),
                 within_inames_is_final=True,
-                depends_on=frozenset({Writes(local.name), Writes(corners)})
+                depends_on=frozenset({Writes(get_pymbolic_basename(local)), Writes(corners)})
                 )
 
 
-def to_global(local):
-    macro = name_point_in_macro(local)
-    name = macro + "_global"
+def define_element_corners(name):
+    from dune.codegen.pdelab.driver import get_form
+    n_corners = get_form().ufl_cell().num_vertices()
+    temporary_variable(name, shape_impl=('fv', 'fv'), shape=(n_corners, world_dimension()))
 
-    it = get_global_context_value("integral_type")
+    iname = "i_corner"
+    domain(iname, n_corners)
 
+    it = get_global_context_value("integral_type")
     if it == 'cell':
-        if get_form_option("constant_transformation_matrix"):
-            apply_constant_to_global_transformation(name, prim.Variable(macro))
-        else:
-            apply_default_to_global_transformation(name, prim.Variable(macro))
-    elif it == 'exterior_facet' or it == 'interior_facet':
-        from dune.codegen.pdelab.geometry import apply_to_global_transformation
-        apply_to_global_transformation(name, prim.Variable(macro))
+        restriction = Restriction.NONE
+    elif it == 'exterior_facet':
+        restriction = Restriction.POSITIVE
     else:
-        raise NotImplementedError
+        raise NotImplementedError()
 
-    return prim.Variable(name)
+    instruction(code="{0}[{1}] = {2}.corner({3});".format(name, iname, name_cell_geometry(restriction), iname),
+                assignees=frozenset({name}),
+                within_inames=frozenset({iname}), within_inames_is_final=True)
+
+
+def name_element_corners():
+    name = "corners"
+    define_element_corners(name)
+    return name
diff --git a/python/dune/codegen/blockstructured/quadrature.py b/python/dune/codegen/blockstructured/quadrature.py
index 4c78d733866b79817f1d7f43189e157d8967fa76..d3ba4950e8034232ebd41258f571b887caf198a4 100644
--- a/python/dune/codegen/blockstructured/quadrature.py
+++ b/python/dune/codegen/blockstructured/quadrature.py
@@ -1,22 +1,22 @@
-from dune.codegen.generation import (backend)
-from dune.codegen.pdelab.quadrature import (name_quadrature_points,
-                                            quadrature_iname)
-from dune.codegen.blockstructured.geometry import name_point_in_macro
+from dune.codegen.error import CodegenError
+from dune.codegen.generation import quadrature_mixin
+from dune.codegen.pdelab.quadrature import GenericQuadratureMixin
+from dune.codegen.blockstructured.tools import name_point_in_macro
 import pymbolic.primitives as prim
 
 
-@backend(interface="quad_pos", name='blockstructured')
-def pymbolic_quadrature_position():
-    quad_points = name_quadrature_points()
-    quad_iname = quadrature_iname()
+@quadrature_mixin("blockstructured")
+class BlockstructuredQuadratureMixin(GenericQuadratureMixin):
+    def quadrature_position_in_micro(self, index=None):
+        return GenericQuadratureMixin.quadrature_position(self, index)
 
-    qp_in_micro = prim.Subscript(prim.Variable(quad_points), (prim.Variable(quad_iname),))
+    def quadrature_position_in_macro(self, index=None):
+        original = self.quadrature_position_in_micro(index)
+        qp = prim.Variable(name_point_in_macro(original, self), )
+        if index is not None:
+            return prim.Subscript(qp, (index,))
+        else:
+            return qp
 
-    return prim.Variable(name_point_in_macro(qp_in_micro))
-
-
-@backend(interface="qp_in_cell", name="blockstructured")
-def pymbolic_quadrature_position_in_cell(restriction):
-    from dune.codegen.pdelab.geometry import to_cell_coordinates
-    quad_pos = pymbolic_quadrature_position()
-    return to_cell_coordinates(quad_pos, restriction)
+    def quadrature_position(self, index=None):
+        raise CodegenError('Decide if the quadrature point is in the macro or micro element')
diff --git a/python/dune/codegen/blockstructured/spaces.py b/python/dune/codegen/blockstructured/spaces.py
index 688b1984a7cac29922de43d2dae5ad35313cebc5..4602789a77a12bd342adbf4ed8bebc59cb989d0c 100644
--- a/python/dune/codegen/blockstructured/spaces.py
+++ b/python/dune/codegen/blockstructured/spaces.py
@@ -1,5 +1,4 @@
-from dune.codegen.generation import (backend,
-                                     domain)
+from dune.codegen.generation import domain
 from dune.codegen.pdelab.geometry import world_dimension
 from dune.codegen.pdelab.spaces import name_leaf_lfs
 
diff --git a/python/dune/codegen/blockstructured/tools.py b/python/dune/codegen/blockstructured/tools.py
index 3eafcae5914f21538766960d75dbdb2fa2e728ad..802b819f23754aa0862e122f4127a3db18593117 100644
--- a/python/dune/codegen/blockstructured/tools.py
+++ b/python/dune/codegen/blockstructured/tools.py
@@ -1,16 +1,9 @@
-from dune.codegen.ufl.modified_terminals import Restriction
+from dune.codegen.tools import get_pymbolic_basename
 from dune.codegen.generation import (iname,
                                      domain,
-                                     get_global_context_value,
                                      temporary_variable,
                                      instruction)
-from dune.codegen.pdelab.geometry import (local_dimension,
-                                          world_dimension,
-                                          name_localcenter,
-                                          pymbolic_in_cell_coordinates)
-
-from dune.codegen.pdelab.quadrature import quadrature_inames
-from dune.codegen.generation.counter import get_counted_variable
+from dune.codegen.pdelab.geometry import world_dimension
 from dune.codegen.options import get_form_option
 import pymbolic.primitives as prim
 
@@ -20,7 +13,7 @@ import pymbolic.primitives as prim
 @iname
 def sub_element_inames():
     name = "subel"
-    dim = local_dimension()
+    dim = world_dimension()
     dim_names = ["x", "y", "z"] + [str(i) for i in range(4, dim + 1)]
     inames = tuple()
     for i in range(dim):
@@ -29,72 +22,6 @@ def sub_element_inames():
     return inames
 
 
-# define inames for boundary integration
-# In the case of boundary integrationsince we have only d-1 loops,
-# but we need always d inames to compute the macro index.
-# Therefore we must find out which iname is constant.
-# Since loo.py cannot handle if-else blocks very well, this whole
-# computation seems a bit cumbersome.
-def sub_facet_inames():
-    subelem_inames = sub_element_inames()
-
-    center = pymbolic_in_cell_coordinates(prim.Variable(name_localcenter()), Restriction.POSITIVE)
-
-    # check if iname[index] must be constant or not
-    def predicate(index):
-        return prim.Comparison(prim.Subscript(center, index), "==", 0.5)
-
-    def conditional_instruction(index):
-        # instruction for not constant iname
-        # special case for the third iname
-        instruction(assignee=prim.Variable(inames[index]),
-                    expression=prim.Variable(subelem_inames[1 if index == 2 else 0]),
-                    within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())),
-                    predicates=frozenset([predicate(index)])
-                    )
-        # instruction for constant iname
-        instruction(assignee=prim.Variable(inames[index]),
-                    expression=prim.Product(((k - 1), prim.Subscript(center, (index,)))),
-                    within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())),
-                    predicates=frozenset([prim.LogicalNot(predicate(index))])
-                    )
-
-    k = get_form_option("number_of_blocks")
-
-    inames = ("x",)
-    temporary_variable(inames[0])
-    conditional_instruction(0)
-    if world_dimension() < 3:
-        inames = inames + ("y",)
-        temporary_variable(inames[1])
-        conditional_instruction(1)
-    else:
-        inames = inames + ("y",)
-        temporary_variable(inames[1])
-        # one additional condition is needed in 3d for the second iname
-        instruction(assignee=prim.Variable(inames[1]),
-                    expression=prim.Variable(subelem_inames[0]),
-                    within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())),
-                    predicates=frozenset([prim.LogicalAnd((predicate(1), predicate(2)))])
-                    )
-        instruction(assignee=prim.Variable(inames[1]),
-                    expression=prim.Variable(subelem_inames[1]),
-                    within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())),
-                    predicates=frozenset([prim.LogicalAnd((predicate(1), predicate(0)))])
-                    )
-        instruction(assignee=prim.Variable(inames[1]),
-                    expression=prim.Product(((k - 1), prim.Subscript(center, (1,)))),
-                    within_inames=frozenset(subelem_inames).union(frozenset(quadrature_inames())),
-                    predicates=frozenset([prim.LogicalNot(predicate(1))])
-                    )
-
-        inames = inames + ("z",)
-        temporary_variable(inames[2])
-        conditional_instruction(2)
-
-    return inames
-
-
 # compute sequential index for given tensor index, the same as index in base-k to base-10
 def tensor_index_to_sequential_index(indices, k):
     return prim.Sum(tuple(prim.Variable(index) * k ** i for i, index in enumerate(indices)))
@@ -107,13 +34,43 @@ def sequential_index_to_tensor_index(iname, k):
 
 # compute index for higher order FEM for a given Q1 index
 def micro_index_to_macro_index(element, inames):
-    it = get_global_context_value("integral_type")
-    if it == "cell":
-        subelem_inames = sub_element_inames()
-    elif it == "exterior_facet" or it == "interior_facet":
-        subelem_inames = sub_facet_inames()
+    subelem_inames = sub_element_inames()
 
     k = get_form_option("number_of_blocks")
     p = element.degree()
     return prim.Sum(tuple((p * prim.Variable(si) + prim.Variable(bi)) * (p * k + 1) ** i
                           for i, (si, bi) in enumerate(zip(subelem_inames, inames))))
+
+
+# translate a point in the micro element into macro coordinates
+def define_point_in_macro(name, point_in_micro, visitor):
+    # TODO this won't work for 2d mannifolds
+    dim = world_dimension()
+    if get_form_option('vectorization_blockstructured'):
+        temporary_variable(name, shape=(dim,), managed=True)
+    else:
+        temporary_variable(name, shape=(dim,), shape_impl=('fv',))
+
+    # point_macro = (point_micro + index_micro) / number_of_blocks
+    # where index_micro = tensor index of the micro element
+    subelem_inames = sub_element_inames()
+    for i in range(dim):
+        if isinstance(point_in_micro, prim.Subscript):
+            expr = prim.Subscript(point_in_micro.aggregate, point_in_micro.index + (i,))
+        else:
+            expr = prim.Subscript(point_in_micro, (i,))
+        expr = prim.Sum((expr, prim.Variable(subelem_inames[i]),))
+        expr = prim.Quotient(expr, get_form_option('number_of_blocks'))
+        # TODO relax within inames
+        instruction(assignee=prim.Subscript(prim.Variable(name), (i,)),
+                    expression=expr,
+                    within_inames=frozenset(subelem_inames + visitor.quadrature_inames()),
+                    tags=frozenset({subelem_inames[i]})
+                    )
+
+
+def name_point_in_macro(point_in_micro, visitor):
+    assert isinstance(point_in_micro, prim.Expression)
+    name = get_pymbolic_basename(point_in_micro) + "_macro"
+    define_point_in_macro(name, point_in_micro, visitor)
+    return name
diff --git a/python/dune/codegen/generation/__init__.py b/python/dune/codegen/generation/__init__.py
index 3103bac657648779921eb66c7170f5a55059e209..d0cf1d4dc8b6880db13bfdec4458d815ea8208c2 100644
--- a/python/dune/codegen/generation/__init__.py
+++ b/python/dune/codegen/generation/__init__.py
@@ -1,9 +1,3 @@
-from __future__ import absolute_import
-
-from dune.codegen.generation.backend import (backend,
-                                             get_backend,
-                                             )
-
 from dune.codegen.generation.counter import (get_counter,
                                              get_counted_variable,
                                              )
@@ -58,3 +52,10 @@ from dune.codegen.generation.context import (cache_restoring,
                                              global_context,
                                              get_global_context_value,
                                              )
+
+from dune.codegen.generation.mixins import (accumulation_mixin,
+                                            basis_mixin,
+                                            construct_from_mixins,
+                                            geometry_mixin,
+                                            quadrature_mixin,
+                                            )
diff --git a/python/dune/codegen/generation/backend.py b/python/dune/codegen/generation/backend.py
deleted file mode 100644
index a45f7ac8be21f3c358e3473a4bc9a51e4deeb9f2..0000000000000000000000000000000000000000
--- a/python/dune/codegen/generation/backend.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from dune.codegen.generation.cache import _RegisteredFunction
-from dune.codegen.options import option_switch
-from pytools import Record
-
-
-_backend_mapping = {}
-
-
-class FuncProxy(Record):
-    def __init__(self, interface, name, func):
-        Record.__init__(self, interface=interface, name=name, func=func)
-
-    def __call__(self, *args, **kwargs):
-        return self.func(*args, **kwargs)
-
-
-def register_backend(interface, name, func):
-    _backend_mapping.setdefault(interface, {})
-    _backend_mapping[interface][name] = func
-
-
-def backend(interface=None, name='default'):
-    assert interface
-
-    def _dec(func):
-        if not isinstance(func, _RegisteredFunction):
-            # Allow order independence of the generator decorators
-            # and the backend decorator by delaying the registration
-            func = FuncProxy(interface, name, func)
-
-        register_backend(interface, name, func)
-
-        return func
-
-    return _dec
-
-
-def get_backend(interface=None, selector=option_switch("sumfact"), **kwargs):
-    assert interface and selector
-
-    select = selector(**kwargs)
-    assert select in _backend_mapping[interface], "Implementation '{}' for interface '{}' missing!".format(select, interface)
-
-    return _backend_mapping[interface][select]
diff --git a/python/dune/codegen/generation/cache.py b/python/dune/codegen/generation/cache.py
index 223ab1bf6012bab7e19043a6163b936221735fe2..2e555ae99e73b5917d082d6c631d675c70cfc3de 100644
--- a/python/dune/codegen/generation/cache.py
+++ b/python/dune/codegen/generation/cache.py
@@ -8,9 +8,10 @@ from dune.codegen.generation.context import (get_global_context_value,
                                              )
 from dune.codegen.generation.counter import get_counter
 from dune.codegen.options import get_option
+from pytools import ImmutableRecord
 
 # Store a global list of generator functions
-_generators = []
+_generators = {}
 
 
 def _freeze(data):
@@ -85,16 +86,6 @@ class _RegisteredFunction(object):
         # Initialize the memoization cache
         self._memoize_cache = {}
 
-        # Register this generator function
-        _generators.append(self)
-
-        # Allow order independence of the backend and the generator decorators.
-        # If backend was applied first, we resolve the issued FuncProxy object
-        from dune.codegen.generation.backend import FuncProxy, register_backend
-        if isinstance(self.func, FuncProxy):
-            register_backend(self.func.interface, self.func.name, self)
-            self.func = self.func.func
-
     def _get_content(self, key):
         return self._memoize_cache[key].value
 
@@ -180,11 +171,32 @@ def generator_factory(**factory_kwargs):
         # Modify the kwargs according to the factorys kwargs
         for k in factory_kwargs:
             kwargs[k] = factory_kwargs[k]
+        # If there args, this function is used as a decorator, as in this example
+        #
+        # @decorator
+        # def foo():
+        #     pass
+        #
+        # If there are no args, this is used as a decorator factory:
+        #
+        # @decorator(bar=42)
+        # def foo():
+        #     pass
+        #
         if args:
             assert len(args) == 1
-            return _RegisteredFunction(args[0], **kwargs)
+            key = args[0]
+
+            if hasattr(key, "__name__") and key.__name__ == '<lambda>':
+                key = str(kwargs)
+
+            funcobj = _generators.setdefault(key, _RegisteredFunction(args[0], **kwargs))
+            return lambda *a, **ka: funcobj(*a, **ka)
         else:
-            return lambda f: _RegisteredFunction(f, **kwargs)
+            def __dec(f):
+                funcobj = _generators.setdefault(f, _RegisteredFunction(f, **kwargs))
+                return lambda *a, **ka: funcobj(*a, **ka)
+            return __dec
 
     if no_deco:
         return _dec(lambda x: x)
@@ -241,13 +253,13 @@ def retrieve_cache_items(condition=True, make_generable=False):
             return content
 
     # First yield all those items that are not sorted
-    for gen in filter(lambda g: not g.counted, _generators):
+    for gen in filter(lambda g: not g.counted, _generators.values()):
         for item in _filter_cache_items(gen, condition).values():
             yield as_generable(item.value)
 
     # And now the sorted ones
     counted_ones = []
-    for gen in filter(lambda g: g.counted, _generators):
+    for gen in filter(lambda g: g.counted, _generators.values()):
         counted_ones.extend(_filter_cache_items(gen, condition).values())
 
     for item in sorted(counted_ones, key=lambda i: i.count):
@@ -264,12 +276,12 @@ def delete_cache_items(condition=True, keep=False):
     if not keep:
         condition = "not ({})".format(condition)
 
-    for gen in _generators:
+    for gen in _generators.values():
         gen._memoize_cache = _filter_cache_items(gen, condition)
 
 
 def retrieve_cache_functions(condition="True"):
-    return [g.func for g in _generators if eval(condition, _ConditionDict(g.item_tags))]
+    return [g.func for g in _generators.values() if eval(condition, _ConditionDict(g.item_tags))]
 
 
 def inspect_generator(gen):
diff --git a/python/dune/codegen/generation/context.py b/python/dune/codegen/generation/context.py
index cf22ddd4ef4a0dd731c2d3f6cffd805b8b47248c..b90e46a5f6c4acd09b8200ce45b00f14a565beec 100644
--- a/python/dune/codegen/generation/context.py
+++ b/python/dune/codegen/generation/context.py
@@ -37,16 +37,17 @@ class _CacheRestoringContext(object):
     def __enter__(self):
         from dune.codegen.generation.cache import _generators as g
         self.cache = {}
-        for i in g:
-            self.cache[i] = {}
-            for k, v in i._memoize_cache.items():
-                self.cache[i][k] = v
+        for original_func, cache_func in g.items():
+            self.cache[original_func] = {}
+            for k, v in cache_func._memoize_cache.items():
+                self.cache[original_func][k] = v
 
     def __exit__(self, exc_type, exc_value, traceback):
+        from dune.codegen.generation.cache import _generators as g
         for i, c in self.cache.items():
-            i._memoize_cache = {}
+            g[i]._memoize_cache = {}
             for k, v in c.items():
-                i._memoize_cache[k] = v
+                g[i]._memoize_cache[k] = v
 
 
 def cache_restoring():
diff --git a/python/dune/codegen/generation/mixins.py b/python/dune/codegen/generation/mixins.py
new file mode 100644
index 0000000000000000000000000000000000000000..06141dbe873f53d4c4644ee7863bb3fca9fad90b
--- /dev/null
+++ b/python/dune/codegen/generation/mixins.py
@@ -0,0 +1,28 @@
+""" The infrastructure for registered mixin classes """
+
+from functools import partial
+
+
+_mixin_registry = {}
+
+
+def mixin_base(mixintype, name):
+    _mixin_registry.setdefault(mixintype, {})
+
+    def _dec(cls):
+        _mixin_registry[mixintype][name] = cls
+        return cls
+
+    return _dec
+
+
+def construct_from_mixins(base=object, mixins=[], mixintype="geometry", name="GeometryInterface"):
+    mixins = tuple(_mixin_registry[mixintype][m] for m in mixins)
+    return type(name, mixins + (base,), {})
+
+
+# A list of specific mixins that we keep around explicitly
+geometry_mixin = partial(mixin_base, "geometry")
+quadrature_mixin = partial(mixin_base, "quadrature")
+basis_mixin = partial(mixin_base, "basis")
+accumulation_mixin = partial(mixin_base, "accumulation")
diff --git a/python/dune/codegen/loopy/transformations/vectorize_quad.py b/python/dune/codegen/loopy/transformations/vectorize_quad.py
index 2ff6361f445ce44e73c659aa34bf7e4e7f02646b..c3989d8011f74a662ed82d73551a337109c9a776 100644
--- a/python/dune/codegen/loopy/transformations/vectorize_quad.py
+++ b/python/dune/codegen/loopy/transformations/vectorize_quad.py
@@ -11,7 +11,6 @@ from dune.codegen.loopy.transformations.vectorview import (add_vector_view,
                                                            get_vector_view_name,
                                                            )
 from dune.codegen.loopy.symbolic import substitute
-from dune.codegen.sumfact.quadrature import quadrature_inames
 from dune.codegen.tools import get_pymbolic_basename, get_pymbolic_tag, ceildiv
 from dune.codegen.options import get_option
 
@@ -324,8 +323,10 @@ def _vectorize_quadrature_loop(knl, inames, suffix):
 
 def vectorize_quadrature_loop(knl):
     # Loop over the quadrature loops that exist in the kernel.
-    # This is implemented a bit hacky right now...
-    for key, inames in quadrature_inames._memoize_cache.items():
+    # This is implemented very hacky right now...
+    from dune.codegen.generation.cache import _generators as _g
+    gen = list(filter(lambda i: hasattr(i[0], "__name__") and i[0].__name__ == "_quadrature_inames", _g.items()))[0][1]
+    for key, inames in gen._memoize_cache.items():
         element = key[0][0]
         if element is None:
             suffix = ''
diff --git a/python/dune/codegen/options.py b/python/dune/codegen/options.py
index 40f2d1a520ca5506604d8f5ec98709d692b75fc3..bb6bbafaf09dd0b0ae7330b174dfa5805769d09f 100644
--- a/python/dune/codegen/options.py
+++ b/python/dune/codegen/options.py
@@ -98,7 +98,6 @@ class CodegenFormOptionsArray(ImmutableRecord):
     generate_jacobian_apply = CodegenOption(default=False, helpstr="Wether jacobian_allpy_* methods should be generated.")
     generate_residuals = CodegenOption(default=True, helpstr="Whether alpha_* methods should be generated.")
     unroll_dimension_loops = CodegenOption(default=False, helpstr="whether loops over the geometric dimension should be unrolled")
-    precompute_quadrature_info = CodegenOption(default=True, helpstr="compute quadrature points and weights in the constructor of the local operator")
     blockstructured = CodegenOption(default=False, helpstr="Use block structure")
     number_of_blocks = CodegenOption(default=1, helpstr="Number of sub blocks in one direction")
     vectorization_blockstructured = CodegenOption(default=False, helpstr="Vectorize block structuring")
@@ -108,6 +107,10 @@ class CodegenFormOptionsArray(ImmutableRecord):
     control_variable = CodegenOption(default=None, helpstr="Name of control variable in UFL file")
     block_preconditioner_diagonal = CodegenOption(default=False, helpstr="Whether this operator should implement the diagonal part of a block preconditioner")
     block_preconditioner_offdiagonal = CodegenOption(default=False, helpstr="Whether this operator should implement the off-diagonal part of a block preconditioner")
+    geometry_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for geometries. Currently implemented mixins: generic, axiparallel, equidistant, sumfact_multilinear, sumfact_axiparallel, sumfact_equidistant")
+    quadrature_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for quadrature. Currently implemented: generic, sumfact")
+    basis_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for basis function evaluation. Currently implemented: generic, sumfact")
+    accumulation_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for accumulation. Currently implemented: generic, sumfact, control, blockstructured")
     enable_volume = CodegenOption(default=True, helpstr="Whether to assemble volume integrals")
     enable_skeleton = CodegenOption(default=True, helpstr="Whether to assemble skeleton integrals")
     enable_boundary = CodegenOption(default=True, helpstr="Whether to assemble boundary integrals")
@@ -175,7 +178,20 @@ def process_global_options(opt):
 @memoize
 def process_form_options(opt, form):
     if opt.sumfact:
-        opt = opt.copy(unroll_dimension_loops=True)
+        opt = opt.copy(unroll_dimension_loops=True,
+                       quadrature_mixins="sumfact",
+                       basis_mixins="sumfact",
+                       accumulation_mixins="sumfact",
+                       )
+
+    if opt.blockstructured:
+        opt = opt.copy(accumulation_mixins="blockstructured",
+                       quadrature_mixins="blockstructured",
+                       basis_mixins="blockstructured"
+                       )
+
+    if opt.control:
+        opt = opt.copy(accumulation_mixins="control")
 
     if opt.numerical_jacobian:
         opt = opt.copy(generate_jacobians=False, generate_jacobian_apply=False)
diff --git a/python/dune/codegen/pdelab/__init__.py b/python/dune/codegen/pdelab/__init__.py
index affa0725c89539fa88e470ac143f5d76fdb8a18b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/python/dune/codegen/pdelab/__init__.py
+++ b/python/dune/codegen/pdelab/__init__.py
@@ -1,158 +0,0 @@
-""" The pdelab specific parts of the code generation process """
-
-# Trigger some imports that are needed to have all backend implementations visible
-# to the selection mechanisms
-from dune.codegen.generation import (get_backend)
-from dune.codegen.options import option_switch
-from dune.codegen.pdelab.argument import (pymbolic_apply_function,
-                                          pymbolic_apply_function_gradient,
-                                          pymbolic_trialfunction,
-                                          pymbolic_trialfunction_gradient,
-                                          pymbolic_coefficient
-                                          )
-from dune.codegen.pdelab.basis import (pymbolic_basis,
-                                       pymbolic_reference_gradient,
-                                       )
-from dune.codegen.pdelab.function import pymbolic_gridfunction
-from dune.codegen.pdelab.geometry import (component_iname,
-                                          pymbolic_cell_volume,
-                                          pymbolic_facet_area,
-                                          pymbolic_facet_jacobian_determinant,
-                                          pymbolic_jacobian_determinant,
-                                          pymbolic_jacobian_inverse,
-                                          pymbolic_unit_inner_normal,
-                                          pymbolic_unit_outer_normal,
-                                          to_global,
-                                          )
-from dune.codegen.pdelab.index import (name_index,
-                                       )
-from dune.codegen.pdelab.quadrature import (pymbolic_quadrature_weight,
-                                            pymbolic_quadrature_position,
-                                            quadrature_inames,
-                                            )
-from dune.codegen.pdelab.spaces import (lfs_inames,
-                                        )
-from dune.codegen.pdelab.tensors import (pymbolic_list_tensor,
-                                         pymbolic_identity,
-                                         pymbolic_matrix_inverse,
-                                         )
-
-
-class PDELabInterface(object):
-    def __init__(self):
-        # The visitor instance will be registered by its init method
-        self.visitor = None
-
-    # Accumulation interfaces
-    def get_accumulation_info(self, expr, visitor):
-        from dune.codegen.pdelab.localoperator import get_accumulation_info
-        return get_accumulation_info(expr, visitor)
-
-    def list_accumulation_infos(self, expr, visitor):
-        from dune.codegen.pdelab.localoperator import list_accumulation_infos
-        return list_accumulation_infos(expr, visitor)
-
-    def generate_accumulation_instruction(self, expr, visitor):
-        from dune.codegen.pdelab.localoperator import generate_accumulation_instruction
-        return generate_accumulation_instruction(expr, visitor)
-
-    #
-    # TODO: The following ones are actually entirely PDELab independent!
-    # They should be placed elsewhere and be used directly in the visitor.
-    #
-
-    def component_iname(self, context=None, count=None):
-        return component_iname(context=context, count=count)
-
-    def name_index(self, ind):
-        return name_index(ind)
-
-    #
-    # Local function space related generator functions
-    #
-
-    def lfs_inames(self, element, restriction, number=None, context=''):
-        return lfs_inames(element, restriction, number, context)
-
-    def initialize_function_spaces(self, expr, visitor):
-        from dune.codegen.pdelab.spaces import initialize_function_spaces
-        return initialize_function_spaces(expr, visitor)
-
-    #
-    # Test and trial function related generator functions
-    #
-
-    # TODO: should pymbolic_basis also pass the interface?
-    def pymbolic_basis(self, element, restriction, number, context=''):
-        return pymbolic_basis(element, restriction, number, context)
-
-    # TODO: should pymbolic_reference_gradient also pass the interface?
-    def pymbolic_reference_gradient(self, element, restriction, number, context=''):
-        return pymbolic_reference_gradient(element, restriction, number, context)
-
-    def pymbolic_trialfunction_gradient(self, element, restriction, index):
-        return pymbolic_trialfunction_gradient(self.visitor, element, restriction, index)
-
-    def pymbolic_apply_function_gradient(self, element, restriction, index):
-        return pymbolic_apply_function_gradient(self.visitor, element, restriction, index)
-
-    def pymbolic_trialfunction(self, element, restriction, index):
-        return pymbolic_trialfunction(self.visitor, element, restriction, index)
-
-    def pymbolic_apply_function(self, element, restriction, index):
-        return pymbolic_apply_function(self.visitor, element, restriction, index)
-
-    def pymbolic_gridfunction(self, coeff, restriction, grad):
-        return pymbolic_gridfunction(coeff, restriction, grad)
-
-    #
-    # Tensor expression related generator functions
-    #
-
-    def pymbolic_list_tensor(self, o):
-        return pymbolic_list_tensor(o, self.visitor)
-
-    def pymbolic_identity(self, o):
-        return pymbolic_identity(o)
-
-    def pymbolic_matrix_inverse(self, o):
-        return pymbolic_matrix_inverse(o, self.visitor)
-
-    #
-    # Geometry related generator functions
-    #
-
-    def pymbolic_spatial_coordinate(self):
-        return to_global(pymbolic_quadrature_position())
-
-    def pymbolic_facet_jacobian_determinant(self):
-        return pymbolic_facet_jacobian_determinant()
-
-    def pymbolic_jacobian_determinant(self):
-        return pymbolic_jacobian_determinant()
-
-    def pymbolic_jacobian_inverse(self, i, j, restriction):
-        return pymbolic_jacobian_inverse(i, j, restriction)
-
-    def pymbolic_unit_inner_normal(self):
-        return pymbolic_unit_inner_normal()
-
-    def pymbolic_unit_outer_normal(self):
-        return pymbolic_unit_outer_normal()
-
-    def pymbolic_cell_volume(self, restriction):
-        return pymbolic_cell_volume(restriction)
-
-    def pymbolic_facet_area(self):
-        return pymbolic_facet_area()
-
-    #
-    # Quadrature related generator functions
-    #
-
-    def pymbolic_quadrature_weight(self):
-        return pymbolic_quadrature_weight()
-
-    # TODO Should this be part of interface or not?
-    def quadrature_inames(self):
-        return quadrature_inames()
diff --git a/python/dune/codegen/pdelab/adjoint.py b/python/dune/codegen/pdelab/adjoint.py
index 2681b00391d46532a37846ab2c550f98ff5a3d96..d1a986165671817f8de808dd3ea77d3e0536e3f7 100644
--- a/python/dune/codegen/pdelab/adjoint.py
+++ b/python/dune/codegen/pdelab/adjoint.py
@@ -8,7 +8,8 @@ from loopy.types import NumpyType
 
 import pymbolic.primitives as prim
 
-from dune.codegen.generation import (class_member,
+from dune.codegen.generation import (accumulation_mixin,
+                                     class_member,
                                      constructor_parameter,
                                      function_mangler,
                                      get_global_context_value,
@@ -20,10 +21,11 @@ from dune.codegen.generation import (class_member,
 from dune.codegen.options import (get_form_option,
                                   )
 from dune.codegen.loopy.target import dtype_floatingpoint
-from dune.codegen.pdelab import PDELabInterface
 from dune.codegen.pdelab.localoperator import (boundary_predicates,
                                                determine_accumulation_space,
                                                extract_kernel_from_cache,
+                                               GenericAccumulationMixin,
+                                               get_visitor,
                                                )
 
 
@@ -61,7 +63,7 @@ def generate_accumulation_instruction(expr, visitor, accumulation_index, number_
     expr = prim.Sum((assignee, expr))
 
     from dune.codegen.generation import instruction
-    quad_inames = visitor.interface.quadrature_inames()
+    quad_inames = visitor.quadrature_inames()
     instruction(assignee=assignee,
                 expression=expr,
                 forced_iname_deps=frozenset(quad_inames),
@@ -69,44 +71,22 @@ def generate_accumulation_instruction(expr, visitor, accumulation_index, number_
                 )
 
 
-def list_accumulation_infos(expr, visitor):
-    return ["control", ]
-
-
-class ControlInterface(PDELabInterface):
-    """Interface for generating the control localoperator
-
-    In this case we will not accumulate in the residual vector but use
-    a class member representing dJdm instead.
-
-    """
-    def __init__(self, accumulation_index, number_of_controls):
-        """Create ControlInterface
-
-        Arguments:
-        ----------
-        accumulation_index: In which component of the dJdm should be accumulated.
-        number_of_controls: Number of components of dJdm. Needed for creating the member variable.
-        """
+@accumulation_mixin("control")
+class AdjointAccumulationMixin(GenericAccumulationMixin):
+    def set_adjoint_information(self, accumulation_index, number_of_controls):
         self.accumulation_index = accumulation_index
         self.number_of_controls = number_of_controls
 
-    def list_accumulation_infos(self, expr, visitor):
-        return list_accumulation_infos(expr, visitor)
+    def list_accumulation_infos(self, expr):
+        return ["control"]
 
-    def generate_accumulation_instruction(self, expr, visitor):
+    def generate_accumulation_instruction(self, expr):
         return generate_accumulation_instruction(expr,
-                                                 visitor,
+                                                 self,
                                                  self.accumulation_index,
                                                  self.number_of_controls)
 
 
-def get_visitor(measure, subdomain_id, accumulation_index, number_of_controls):
-    interface = ControlInterface(accumulation_index, number_of_controls)
-    from dune.codegen.ufl.visitor import UFL2LoopyVisitor
-    return UFL2LoopyVisitor(interface, measure, subdomain_id)
-
-
 def visit_integral(integral, accumulation_index, number_of_controls):
     integrand = integral.integrand()
     measure = integral.integral_type()
@@ -114,7 +94,8 @@ def visit_integral(integral, accumulation_index, number_of_controls):
 
     # The visitor needs to know about the current index and the number
     # of controls in order to generate the accumulation instruction
-    visitor = get_visitor(measure, subdomain_id, accumulation_index, number_of_controls)
+    visitor = get_visitor(measure, subdomain_id)
+    visitor.set_adjoint_information(accumulation_index, number_of_controls)
 
     # Start the visiting process!
     visitor.accumulate(integrand)
@@ -162,7 +143,6 @@ def generate_kernel(forms):
     return knl
 
 
-# @backend(interface="generate_kernels_per_integral")
 def control_generate_kernels_per_integral(forms):
     """For the control problem forms will have one form for every
     measure. Every form will only contain integrals of one type.
diff --git a/python/dune/codegen/pdelab/argument.py b/python/dune/codegen/pdelab/argument.py
index 6bf800d59cc6ddeb683873793a4e72f9d0b11f0b..c3cc48298c1b2efb70c6c7716b29b221486044e1 100644
--- a/python/dune/codegen/pdelab/argument.py
+++ b/python/dune/codegen/pdelab/argument.py
@@ -11,13 +11,9 @@ from dune.codegen.generation import (domain,
                                      valuearg,
                                      get_global_context_value,
                                      kernel_cached,
-                                     backend
                                      )
 from dune.codegen.loopy.target import dtype_floatingpoint
 from dune.codegen.pdelab.index import name_index
-from dune.codegen.pdelab.basis import (evaluate_coefficient,
-                                       evaluate_coefficient_gradient,
-                                       )
 from dune.codegen.pdelab.spaces import (lfs_iname,
                                         name_lfs_bound,
                                         )
@@ -89,38 +85,6 @@ def accumulation_mangler(target, func, dtypes):
                                   )
 
 
-def pymbolic_trialfunction_gradient(visitor, element, restriction, index):
-    rawname = "gradu_{}".format(index)
-    name = restricted_name(rawname, restriction)
-    container = name_coefficientcontainer(restriction)
-    evaluate_coefficient_gradient(visitor, element, name, container, restriction, index)
-    return Variable(name)
-
-
-def pymbolic_trialfunction(visitor, element, restriction, index):
-    rawname = "u_{}".format(index)
-    name = restricted_name(rawname, restriction)
-    container = name_coefficientcontainer(restriction)
-    evaluate_coefficient(visitor, element, name, container, restriction, index)
-    return Variable(name)
-
-
-def pymbolic_apply_function_gradient(visitor, element, restriction, index):
-    rawname = "gradz_func_{}".format(index)
-    name = restricted_name(rawname, restriction)
-    container = name_applycontainer(restriction)
-    evaluate_coefficient_gradient(visitor, element, name, container, restriction, index)
-    return Variable(name)
-
-
-def pymbolic_apply_function(visitor, element, restriction, index):
-    rawname = "z_func_{}".format(index)
-    name = restricted_name(rawname, restriction)
-    container = name_applycontainer(restriction)
-    evaluate_coefficient(visitor, element, name, container, restriction, index)
-    return Variable(name)
-
-
 def name_coefficientcontainer(restriction):
     name = restricted_name("x", restriction)
     return name
@@ -132,7 +96,6 @@ def name_applycontainer(restriction):
 
 
 @kernel_cached
-@backend(interface="pymbolic_coefficient")
 def pymbolic_coefficient(container, lfs, index):
     # TODO introduce a proper type for local function spaces!
     if isinstance(lfs, str):
diff --git a/python/dune/codegen/pdelab/basis.py b/python/dune/codegen/pdelab/basis.py
index c65e71ebfc9e3a38023033e27dc65d445734edbf..03c069ac518f01efaec8304356fddcf34915668f 100644
--- a/python/dune/codegen/pdelab/basis.py
+++ b/python/dune/codegen/pdelab/basis.py
@@ -1,8 +1,7 @@
 """ Generators for basis evaluations """
 
-from dune.codegen.generation import (backend,
+from dune.codegen.generation import (basis_mixin,
                                      class_member,
-                                     get_backend,
                                      include_file,
                                      instruction,
                                      kernel_cached,
@@ -12,6 +11,9 @@ from dune.codegen.generation import (backend,
 from dune.codegen.options import (option_switch,
                                   get_form_option,
                                   )
+from dune.codegen.pdelab.argument import (name_applycontainer,
+                                          name_coefficientcontainer,
+                                          )
 from dune.codegen.pdelab.spaces import (lfs_iname,
                                         lfs_inames,
                                         name_leaf_lfs,
@@ -19,15 +21,15 @@ from dune.codegen.pdelab.spaces import (lfs_iname,
                                         name_lfs_bound,
                                         type_gfs,
                                         type_leaf_gfs,
+                                        initialize_function_spaces,
                                         )
 from dune.codegen.pdelab.geometry import (component_iname,
                                           world_dimension,
-                                          name_jacobian_inverse_transposed,
-                                          to_cell_coordinates,
                                           name_cell,
                                           )
 from dune.codegen.pdelab.localoperator import (lop_template_ansatz_gfs,
                                                lop_template_test_gfs,
+                                               name_gridfunction_member,
                                                )
 from dune.codegen.tools import (get_pymbolic_basename,
                                 get_pymbolic_indices,
@@ -39,13 +41,241 @@ from dune.codegen.pdelab.driver import (isPk,
                                         isDG)
 
 from pymbolic.primitives import Product, Subscript, Variable
+import pymbolic.primitives as prim
 
 from ufl import MixedElement
 
 from loopy import Reduction
 
 
-@backend(interface="typedef_localbasis")
+@basis_mixin("base")
+class BasisMixinBase(object):
+    def initialize_function_spaces(self, expr):
+        pass
+
+    def lfs_inames(self, element, restriction, number):
+        raise NotImplementedError("Basis Mixins should implement local function space inames")
+
+    def implement_basis(self, element, restriction, number):
+        raise NotImplementedError("Basis Mixins should implement the basis")
+
+    def implement_reference_gradient(self, element, restriction, number, index):
+        raise NotImplementedError("Basis Mixins should implement the basis gradient")
+
+    def implement_trialfunction(self, element, restriction, index):
+        raise NotImplementedError("Basis Mixins should implement trial function evaluation")
+
+    def implement_trialfunction_gradient(self, element, restriction, index):
+        raise NotImplementedError("Basis Mixins should implement trial function gradient evaluation")
+
+    def implement_apply_function(self, element, restriction, index):
+        raise NotImplementedError("Basis Mixins should implement linearization point evaluation")
+
+    def implement_apply_function_gradient(self, element, restriction, index):
+        raise NotImplementedError("Basis Mixins should implement linearization point gradient evaluation")
+
+    def implement_grid_function(self, coeff, restriction, grad):
+        raise NotImplementedError("Basis Mixins should implement grid function evaluation")
+
+
+@basis_mixin("generic")
+class GenericBasisMixin(BasisMixinBase):
+    def initialize_function_spaces(self, expr):
+        return initialize_function_spaces(expr, self)
+
+    def lfs_inames(self, element, restriction, number, context=""):
+        return (lfs_iname(element, restriction, number, context),)
+
+    def implement_basis(self, element, restriction, number, context=''):
+        assert not isinstance(element, MixedElement)
+        name = "phi_{}".format(FEM_name_mangling(element))
+        name = restricted_name(name, restriction)
+        self.evaluate_basis(element, name, restriction)
+        iname, = self.lfs_inames(element, restriction, number, context=context)
+
+        return prim.Subscript(prim.Variable(name), (prim.Variable(iname), 0))
+
+    @kernel_cached
+    def evaluate_basis(self, element, name, restriction):
+        lfs = name_leaf_lfs(element, restriction)
+        temporary_variable(name,
+                           shape=(name_lfs_bound(lfs), 1),
+                           decl_method=declare_cache_temporary(element, restriction, 'Function'),
+                           )
+        cache = name_localbasis_cache(element)
+        qp = self.to_cell(self.quadrature_position())
+
+        instruction(inames=self.quadrature_inames(),
+                    code='{} = {}.evaluateFunction({}, {}.finiteElement().localBasis());'.format(name,
+                                                                                                 cache,
+                                                                                                 str(qp),
+                                                                                                 lfs,
+                                                                                                 ),
+                    assignees=frozenset({name}),
+                    read_variables=frozenset({get_pymbolic_basename(qp)}),
+                    )
+
+    def implement_reference_gradient(self, element, restriction, number, context=''):
+        assert not isinstance(element, MixedElement)
+        name = "js_{}".format(FEM_name_mangling(element))
+        name = restricted_name(name, restriction)
+        self.evaluate_reference_gradient(element, name, restriction)
+        iname, = self.lfs_inames(element, restriction, number, context=context)
+
+        return prim.Subscript(prim.Variable(name), (prim.Variable(iname), 0))
+
+    @kernel_cached
+    def evaluate_reference_gradient(self, element, name, restriction):
+        lfs = name_leaf_lfs(element, restriction)
+        temporary_variable(name,
+                           shape=(name_lfs_bound(lfs), 1, world_dimension()),
+                           decl_method=declare_cache_temporary(element, restriction, 'Jacobian'),
+                           )
+        cache = name_localbasis_cache(element)
+        qp = self.to_cell(self.quadrature_position())
+        instruction(inames=self.quadrature_inames(),
+                    code='{} = {}.evaluateJacobian({}, {}.finiteElement().localBasis());'.format(name,
+                                                                                                 cache,
+                                                                                                 str(qp),
+                                                                                                 lfs,
+                                                                                                 ),
+                    assignees=frozenset({name}),
+                    read_variables=frozenset({get_pymbolic_basename(qp)}),
+                    )
+
+    def implement_trialfunction(self, element, restriction, index):
+        rawname = "u_{}".format(index)
+        name = restricted_name(rawname, restriction)
+        container = name_coefficientcontainer(restriction)
+        self.evaluate_coefficient(element, name, container, restriction, index)
+        return prim.Variable(name)
+
+    def implement_trialfunction_gradient(self, element, restriction, index):
+        rawname = "gradu_{}".format(index)
+        name = restricted_name(rawname, restriction)
+        container = name_coefficientcontainer(restriction)
+        self.evaluate_coefficient_gradient(element, name, container, restriction, index)
+        return prim.Variable(name)
+
+    def implement_apply_function(self, element, restriction, index):
+        rawname = "z_func_{}".format(index)
+        name = restricted_name(rawname, restriction)
+        container = name_applycontainer(restriction)
+        self.evaluate_coefficient(element, name, container, restriction, index)
+        return prim.Variable(name)
+
+    def implement_apply_function_gradient(self, element, restriction, index):
+        rawname = "gradz_func_{}".format(index)
+        name = restricted_name(rawname, restriction)
+        container = name_applycontainer(restriction)
+        self.evaluate_coefficient_gradient(element, name, container, restriction, index)
+        return prim.Variable(name)
+
+    @kernel_cached
+    def evaluate_coefficient(self, element, name, container, restriction, index):
+        sub_element = element
+        if isinstance(element, MixedElement):
+            sub_element = element.extract_component(index)[1]
+
+        from ufl import FiniteElement
+        assert isinstance(sub_element, FiniteElement)
+
+        temporary_variable(name, shape=(), managed=True)
+
+        lfs = name_lfs(element, restriction, index)
+        basis = self.implement_basis(sub_element, restriction, 0, context='trial')
+        basisindex = get_pymbolic_indices(basis)[:-1]
+
+        # TODO get rid ot this!
+        if get_form_option("blockstructured"):
+            from dune.codegen.blockstructured.argument import pymbolic_coefficient
+            coeff = pymbolic_coefficient(container, lfs, sub_element, basisindex)
+        else:
+            from dune.codegen.pdelab.argument import pymbolic_coefficient
+            coeff = pymbolic_coefficient(container, lfs, basisindex[0])
+
+        assignee = prim.Variable(name)
+        reduction_expr = prim.Product((coeff, basis))
+
+        instruction(expression=Reduction("sum", basisindex, reduction_expr, allow_simultaneous=True),
+                    assignee=assignee,
+                    forced_iname_deps=frozenset(self.quadrature_inames()),
+                    forced_iname_deps_is_final=True,
+                    )
+
+    @kernel_cached
+    def evaluate_coefficient_gradient(self, element, name, container, restriction, index):
+        sub_element = element
+        if isinstance(element, MixedElement):
+            sub_element = element.extract_component(index)[1]
+        from ufl import FiniteElement
+        assert isinstance(sub_element, FiniteElement)
+
+        temporary_variable(name, shape=(element.cell().geometric_dimension(),), managed=True)
+
+        dimindex = component_iname(count=0)
+
+        lfs = name_lfs(element, restriction, index)
+        basis = self.implement_reference_gradient(sub_element, restriction, 0, context='trialgrad')
+        basisindex = get_pymbolic_indices(basis)[:-1]
+        from dune.codegen.tools import maybe_wrap_subscript
+        basis = maybe_wrap_subscript(basis, prim.Variable(dimindex))
+
+        # TODO get rid of this
+        if get_form_option("blockstructured"):
+            from dune.codegen.blockstructured.argument import pymbolic_coefficient
+            coeff = pymbolic_coefficient(container, lfs, sub_element, basisindex)
+        else:
+            from dune.codegen.pdelab.argument import pymbolic_coefficient
+            coeff = pymbolic_coefficient(container, lfs, basisindex[0])
+
+        assignee = prim.Subscript(prim.Variable(name), (prim.Variable(dimindex),))
+        reduction_expr = prim.Product((coeff, basis))
+
+        instruction(expression=Reduction("sum", basisindex, reduction_expr, allow_simultaneous=True),
+                    assignee=assignee,
+                    forced_iname_deps=frozenset(self.quadrature_inames()).union(frozenset({dimindex})),
+                    forced_iname_deps_is_final=True,
+                    )
+
+    def implement_gridfunction(self, coeff, restriction, grad):
+        name = "coeff{}{}".format(coeff.count(), "_grad" if grad else "")
+        self.define_grid_function(name, coeff, restriction, grad)
+        return prim.Subscript(prim.Variable(name), (0,))
+
+    def define_grid_function(self, name, coeff, restriction, grad):
+        diffOrder = 1 if grad else 0
+
+        gridfunction = name_gridfunction_member(coeff, restriction, diffOrder)
+        bind_gridfunction_to_element(gridfunction, restriction)
+
+        temporary_variable(name,
+                           shape=(1,) + (world_dimension(),) * diffOrder,
+                           decl_method=declare_grid_function_range(gridfunction),
+                           managed=False,
+                           )
+
+        quadpos = self.to_cell(self.quadrature_position())
+        instruction(code="{} = {}({});".format(name, gridfunction, quadpos),
+                    assignees=frozenset({name}),
+                    within_inames=frozenset(self.quadrature_inames()),
+                    within_inames_is_final=True,
+                    )
+
+
+@preamble
+def bind_gridfunction_to_element(gf, restriction):
+    element = name_cell(restriction)
+    return "{}.bind({});".format(gf, element)
+
+
+def declare_grid_function_range(gridfunction):
+    def _decl(name, kernel, decl_info):
+        return "typename decltype({})::Range {};".format(gridfunction, name)
+
+    return _decl
+
+
 @class_member(classtag="operator")
 def typedef_localbasis(element, name):
     basis_type = "{}::Traits::FiniteElementMap::Traits::FiniteElementType::Traits::LocalBasisType".format(type_leaf_gfs(element))
@@ -61,7 +291,14 @@ def type_localbasis(element):
         name = "DG{}_LocalBasis".format(element._degree)
     else:
         raise NotImplementedError("Element type not known in code generation")
-    get_backend("typedef_localbasis", selector=option_switch("blockstructured"))(element, name)
+
+    # TODO get rid of this
+    if get_form_option("blockstructured"):
+        from dune.codegen.blockstructured.basis import typedef_localbasis as bs_typedef_localbasis
+        bs_typedef_localbasis(element, name)
+    else:
+        typedef_localbasis(element, name)
+
     return name
 
 
@@ -94,62 +331,6 @@ def declare_cache_temporary(element, restriction, which):
     return decl
 
 
-@backend(interface="evaluate_basis")
-@kernel_cached
-def evaluate_basis(leaf_element, name, restriction):
-    lfs = name_leaf_lfs(leaf_element, restriction)
-    temporary_variable(name, shape=(name_lfs_bound(lfs), 1), decl_method=declare_cache_temporary(leaf_element, restriction, 'Function'))
-    cache = name_localbasis_cache(leaf_element)
-    qp = get_backend("qp_in_cell")(restriction)
-    instruction(inames=get_backend("quad_inames")(),
-                code='{} = {}.evaluateFunction({}, {}.finiteElement().localBasis());'.format(name,
-                                                                                             cache,
-                                                                                             str(qp),
-                                                                                             lfs,
-                                                                                             ),
-                assignees=frozenset({name}),
-                read_variables=frozenset({get_pymbolic_basename(qp)}),
-                )
-
-
-def pymbolic_basis(leaf_element, restriction, number, context=''):
-    assert not isinstance(leaf_element, MixedElement)
-    name = "phi_{}".format(FEM_name_mangling(leaf_element))
-    name = restricted_name(name, restriction)
-    evaluate_basis(leaf_element, name, restriction)
-    iname, = lfs_inames(leaf_element, restriction, number, context=context)
-
-    return Subscript(Variable(name), (Variable(iname), 0))
-
-
-@backend(interface="evaluate_grad")
-@kernel_cached
-def evaluate_reference_gradient(leaf_element, name, restriction):
-    lfs = name_leaf_lfs(leaf_element, restriction)
-    temporary_variable(name, shape=(name_lfs_bound(lfs), 1, world_dimension()), decl_method=declare_cache_temporary(leaf_element, restriction, 'Jacobian'))
-    cache = name_localbasis_cache(leaf_element)
-    qp = get_backend("qp_in_cell")(restriction)
-    instruction(inames=get_backend("quad_inames")(),
-                code='{} = {}.evaluateJacobian({}, {}.finiteElement().localBasis());'.format(name,
-                                                                                             cache,
-                                                                                             str(qp),
-                                                                                             lfs,
-                                                                                             ),
-                assignees=frozenset({name}),
-                read_variables=frozenset({get_pymbolic_basename(qp)}),
-                )
-
-
-def pymbolic_reference_gradient(leaf_element, restriction, number, context=''):
-    assert not isinstance(leaf_element, MixedElement)
-    name = "js_{}".format(FEM_name_mangling(leaf_element))
-    name = restricted_name(name, restriction)
-    evaluate_reference_gradient(leaf_element, name, restriction)
-    iname, = lfs_inames(leaf_element, restriction, number, context=context)
-
-    return Subscript(Variable(name), (Variable(iname), 0))
-
-
 def shape_as_pymbolic(shape):
     def _shape_as_pymbolic(s):
         if isinstance(s, str):
@@ -157,71 +338,3 @@ def shape_as_pymbolic(shape):
         else:
             return s
     return tuple(_shape_as_pymbolic(s) for s in shape)
-
-
-@kernel_cached
-def evaluate_coefficient(visitor, element, name, container, restriction, index):
-    sub_element = element
-    if isinstance(element, MixedElement):
-        sub_element = element.extract_component(index)[1]
-
-    from ufl import FiniteElement
-    assert isinstance(sub_element, FiniteElement)
-
-    temporary_variable(name, shape=(), managed=True)
-
-    lfs = name_lfs(element, restriction, index)
-    basis = visitor.interface.pymbolic_basis(sub_element, restriction, 0, context='trial')
-    basisindex = get_pymbolic_indices(basis)[:-1]
-    if get_form_option("blockstructured"):
-        from dune.codegen.blockstructured.argument import pymbolic_coefficient
-        coeff = pymbolic_coefficient(container, lfs, sub_element, basisindex)
-    else:
-        from dune.codegen.pdelab.argument import pymbolic_coefficient
-        coeff = pymbolic_coefficient(container, lfs, basisindex[0])
-
-    assignee = Variable(name)
-
-    reduction_expr = Product((coeff, basis))
-
-    instruction(expression=Reduction("sum", basisindex, reduction_expr, allow_simultaneous=True),
-                assignee=assignee,
-                forced_iname_deps=frozenset(get_backend("quad_inames")()),
-                forced_iname_deps_is_final=True,
-                )
-
-
-@kernel_cached
-def evaluate_coefficient_gradient(visitor, element, name, container, restriction, index):
-    sub_element = element
-    if isinstance(element, MixedElement):
-        sub_element = element.extract_component(index)[1]
-    from ufl import FiniteElement
-    assert isinstance(sub_element, FiniteElement)
-
-    temporary_variable(name, shape=(element.cell().geometric_dimension(),), managed=True)
-
-    dimindex = component_iname(count=0)
-
-    lfs = name_lfs(element, restriction, index)
-    basis = visitor.interface.pymbolic_reference_gradient(sub_element, restriction, 0, context='trialgrad')
-    basisindex = get_pymbolic_indices(basis)[:-1]
-    from dune.codegen.tools import maybe_wrap_subscript
-    basis = maybe_wrap_subscript(basis, Variable(dimindex))
-
-    if get_form_option("blockstructured"):
-        from dune.codegen.blockstructured.argument import pymbolic_coefficient
-        coeff = pymbolic_coefficient(container, lfs, sub_element, basisindex)
-    else:
-        from dune.codegen.pdelab.argument import pymbolic_coefficient
-        coeff = pymbolic_coefficient(container, lfs, basisindex[0])
-
-    assignee = Subscript(Variable(name), (Variable(dimindex),))
-
-    reduction_expr = Product((coeff, basis))
-
-    instruction(expression=Reduction("sum", basisindex, reduction_expr, allow_simultaneous=True),
-                assignee=assignee,
-                forced_iname_deps=frozenset(get_backend("quad_inames")()).union(frozenset({dimindex})),
-                forced_iname_deps_is_final=True,
-                )
diff --git a/python/dune/codegen/pdelab/driver/visitor.py b/python/dune/codegen/pdelab/driver/visitor.py
index 0c45871f5d940a62f85386e7aaccd41d210d1abb..6fab5574d1af6c97841535264677a511d96a727f 100644
--- a/python/dune/codegen/pdelab/driver/visitor.py
+++ b/python/dune/codegen/pdelab/driver/visitor.py
@@ -14,8 +14,7 @@ def set_lop_to_starting_time():
 
 class DriverUFL2PymbolicVisitor(UFL2LoopyVisitor):
     def __init__(self):
-        from dune.codegen.pdelab import PDELabInterface
-        UFL2LoopyVisitor.__init__(self, PDELabInterface(), "exterior_facet", {})
+        UFL2LoopyVisitor.__init__(self, "exterior_facet", {})
 
     def __call__(self, expr):
         self.preambles = []
diff --git a/python/dune/codegen/pdelab/function.py b/python/dune/codegen/pdelab/function.py
deleted file mode 100644
index 74e6b6b6ad08cdf8b6b9c1f0a1c5aeeae5f3f3c9..0000000000000000000000000000000000000000
--- a/python/dune/codegen/pdelab/function.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from dune.codegen.generation import (get_backend,
-                                     instruction,
-                                     kernel_cached,
-                                     preamble,
-                                     temporary_variable,
-                                     )
-from dune.codegen.pdelab.geometry import (name_cell,
-                                          world_dimension,
-                                          )
-from dune.codegen.pdelab.localoperator import name_gridfunction_member
-
-import pymbolic.primitives as prim
-
-
-@preamble
-def bind_gridfunction_to_element(gf, restriction):
-    element = name_cell(restriction)
-    return "{}.bind({});".format(gf, element)
-
-
-def declare_grid_function_range(gridfunction):
-    def _decl(name, kernel, decl_info):
-        return "typename decltype({})::Range {};".format(gridfunction, name)
-
-    return _decl
-
-
-@kernel_cached
-def pymbolic_evaluate_gridfunction(name, coeff, restriction, grad):
-    diffOrder = 1 if grad else 0
-
-    gridfunction = name_gridfunction_member(coeff, restriction, diffOrder)
-    bind_gridfunction_to_element(gridfunction, restriction)
-
-    temporary_variable(name,
-                       shape=(1,) + (world_dimension(),) * diffOrder,
-                       decl_method=declare_grid_function_range(gridfunction),
-                       managed=False,
-                       )
-
-    quadpos = get_backend(interface="qp_in_cell")(restriction)
-    instruction(code="{} = {}({});".format(name, gridfunction, quadpos),
-                assignees=frozenset({name}),
-                within_inames=frozenset(get_backend(interface="quad_inames")()),
-                within_inames_is_final=True,
-                )
-
-
-def pymbolic_gridfunction(coeff, restriction, grad):
-    name = "coeff{}{}".format(coeff.count(), "_grad" if grad else "")
-    pymbolic_evaluate_gridfunction(name, coeff, restriction, grad)
-    return prim.Subscript(prim.Variable(name), (0,))
diff --git a/python/dune/codegen/pdelab/geometry.py b/python/dune/codegen/pdelab/geometry.py
index 7d1e722ad8d92143d44747cbc2622b7eec0431b2..d37a315f2926ffe2f914589925eb77eee9352d5e 100644
--- a/python/dune/codegen/pdelab/geometry.py
+++ b/python/dune/codegen/pdelab/geometry.py
@@ -1,9 +1,8 @@
 from dune.codegen.ufl.modified_terminals import Restriction
 from dune.codegen.pdelab.restriction import restricted_name
-from dune.codegen.generation import (backend,
-                                     class_member,
+from dune.codegen.generation import (class_member,
                                      domain,
-                                     get_backend,
+                                     geometry_mixin,
                                      get_global_context_value,
                                      globalarg,
                                      iname,
@@ -17,11 +16,11 @@ from dune.codegen.options import (get_form_option,
                                   option_switch,
                                   )
 from dune.codegen.loopy.target import dtype_floatingpoint, type_floatingpoint
-from dune.codegen.pdelab.quadrature import quadrature_preamble
+from dune.codegen.pdelab.quadrature import (quadrature_preamble,
+                                            )
 from dune.codegen.tools import get_pymbolic_basename
 from ufl.algorithms import MultiFunction
 from pymbolic.primitives import Variable
-from pymbolic.primitives import Expression as PymbolicExpression
 
 from loopy.match import Writes
 
@@ -29,6 +28,278 @@ import numpy as np
 import pymbolic.primitives as prim
 
 
+@geometry_mixin("base")
+class GeometryMixinBase(object):
+    """
+    This mixin will be in by default by the mixin magic,
+    so it should only define an interface and throw exceptions.
+    """
+    def geo_unhandled(self, o):
+        raise NotImplementedError("Geometry Mixins should handle {}".format(type(o)))
+
+    def jacobian(self, o):
+        raise NotImplementedError("How did you get Jacobian into your form? We only support JacobianInverse right now. Report!")
+
+    spatial_coordinate = geo_unhandled
+    facet_normal = geo_unhandled
+    jacobian_determinant = geo_unhandled
+    jacobian_inverse = geo_unhandled
+    facet_jacobian_determinant = geo_unhandled
+    cell_volume = geo_unhandled
+    facet_area = geo_unhandled
+
+    def to_global(self, local):
+        raise NotImplementedError("Geometry Mixins should implement a to_global mapping")
+
+    def to_cell(self, local):
+        raise NotImplementedError("Geometry Mixins should implement a to_cell mapping")
+
+
+@geometry_mixin("generic")
+class GenericPDELabGeometryMixin(GeometryMixinBase):
+    def spatial_coordinate(self, o):
+        return self.to_global(self.quadrature_position())
+
+    def to_global(self, local):
+        assert isinstance(local, prim.Expression)
+        name = get_pymbolic_basename(local) + "_global"
+
+        temporary_variable(name, shape=(world_dimension(),), shape_impl=("fv",))
+        geo = name_geometry()
+        code = "{} = {}.global({});".format(name,
+                                            geo,
+                                            local,
+                                            )
+
+        quadrature_preamble(self,
+                            code,
+                            assignees=frozenset({name}),
+                            read_variables=frozenset({get_pymbolic_basename(local)}),
+                            depends_on=frozenset({Writes(get_pymbolic_basename(local))})
+                            )
+
+        return prim.Variable(name)
+
+    def to_cell(self, local):
+        assert isinstance(local, prim.Expression)
+
+        restriction = enforce_boundary_restriction(self)
+        if restriction == Restriction.NONE:
+            return local
+
+        basename = get_pymbolic_basename(local)
+        name = "{}_in_{}side".format(basename, "in" if restriction is Restriction.POSITIVE else "out")
+        temporary_variable(name, shape=(world_dimension(),), shape_impl=("fv",))
+        geo = name_in_cell_geometry(restriction)
+        quadrature_preamble(self,
+                            "{} = {}.global({});".format(name,
+                                                         geo,
+                                                         str(local),
+                                                         ),
+                            assignees=frozenset({name}),
+                            read_variables=frozenset({get_pymbolic_basename(local)}),
+                            depends_on=frozenset({Writes(get_pymbolic_basename(local))}),
+                            )
+
+        return prim.Variable(name)
+
+    def facet_jacobian_determinant(self, o):
+        name = "fdetjac"
+        self.define_facet_jacobian_determinant(name)
+        return prim.Variable(name)
+
+    def define_facet_jacobian_determinant(self, name):
+        # NB: This can reuse the same implementation as the cell jacobian
+        #     determinant, because the obtain geometry will be a face geometry
+        return self.define_jacobian_determinant(name)
+
+    def jacobian_determinant(self, o):
+        name = 'detjac'
+        self.define_jacobian_determinant(name)
+        return prim.Variable(name)
+
+    def define_jacobian_determinant(self, name):
+        temporary_variable(name, shape=())
+        geo = name_geometry()
+        pos = self.quadrature_position()
+        code = "{} = {}.integrationElement({});".format(name,
+                                                        geo,
+                                                        str(pos),
+                                                        )
+
+        return quadrature_preamble(self,
+                                   code,
+                                   assignees=frozenset({name}),
+                                   read_variables=frozenset({get_pymbolic_basename(pos)}),
+                                   depends_on=frozenset({Writes(get_pymbolic_basename(pos))}),
+                                   )
+
+    def jacobian_inverse(self, o):
+        restriction = enforce_boundary_restriction(self)
+
+        assert(len(self.indices) == 2)
+        i, j = self.indices
+        self.indices = None
+
+        name = restricted_name("jit", restriction)
+        self.define_jacobian_inverse_transposed(name, restriction)
+
+        return prim.Subscript(prim.Variable(name), (j, i))
+
+    def define_jacobian_inverse_transposed(self, name, restriction):
+        dim = world_dimension()
+        temporary_variable(name, decl_method=define_jacobian_inverse_transposed_temporary(restriction), shape=(dim, dim))
+        geo = name_cell_geometry(restriction)
+        pos = self.to_cell(self.quadrature_position())
+
+        return quadrature_preamble(self,
+                                   "{} = {}.jacobianInverseTransposed({});".format(name,
+                                                                                   geo,
+                                                                                   str(pos),
+                                                                                   ),
+                                   assignees=frozenset({name}),
+                                   read_variables=frozenset({get_pymbolic_basename(pos)}),
+                                   depends_on=frozenset({Writes(get_pymbolic_basename(pos))}),
+                                   )
+
+    def facet_normal(self, o):
+        if self.restriction == Restriction.NEGATIVE:
+            raise NotImplementedError("Inner Normals not implemented!")
+
+        name = "unit_outer_normal"
+        self.define_unit_outer_normal(name)
+        return prim.Variable(name)
+
+    def define_unit_outer_normal(self, name):
+        temporary_variable(name, shape=(world_dimension(),), decl_method=declare_normal)
+
+        ig = name_intersection_geometry_wrapper()
+        qp = self.quadrature_position()
+        return quadrature_preamble(self,
+                                   "{} = {}.unitOuterNormal({});".format(name, ig, qp),
+                                   assignees=frozenset({name}),
+                                   read_variables=frozenset({get_pymbolic_basename(qp)}),
+                                   depends_on=frozenset({Writes(get_pymbolic_basename(qp))}),
+                                   )
+
+    def cell_volume(self, o):
+        restriction = enforce_boundary_restriction(self)
+        name = restricted_name("volume", restriction)
+        self.define_cell_volume(name, restriction)
+        return prim.Variable(name)
+
+    @preamble
+    def define_cell_volume(self, name, restriction):
+        geo = name_cell_geometry(restriction)
+        valuearg(name)
+        return "auto {} = {}.volume();".format(name, geo)
+
+    def facet_area(self, o):
+        name = "area"
+        self.define_facet_area(name)
+        return prim.Variable(name)
+
+    @preamble
+    def define_facet_area(self, name):
+        geo = name_intersection_geometry()
+        valuearg(name)
+        return "auto {} = {}.volume();".format(name, geo)
+
+
+@geometry_mixin("axiparallel")
+class AxiparallelGeometryMixin(GenericPDELabGeometryMixin):
+    def define_unit_outer_normal(self, name):
+        # NB: Strictly speaking, this implementation holds for any non-curved intersection.
+        declare_normal(name, None, None)
+        globalarg(name, shape=(world_dimension(),))
+
+    def jacobian_inverse(self, o):
+        i, j = self.indices
+        assert isinstance(i, int) and isinstance(j, int)
+
+        if i != j:
+            self.indices = None
+            return 0
+
+        jac = GenericPDELabGeometryMixin.jacobian_inverse(self, o)
+
+        return jac
+
+    def facet_area(self, o):
+        # This is not 100% correct, but in practice structured grid implementations are not
+        # embedded into higher dimensional world space.
+        return self.facet_jacobian_determinant(o)
+
+    def cell_volume(self, o):
+        # This is not 100% correct, but in practice structured grid implementations are not
+        # embedded into higher dimensional world space.
+        return self.jacobian_determinant(o)
+
+    def define_facet_jacobian_determinant(self, name):
+        valuearg(name)
+        self._define_facet_jacobian_determinant(name)
+
+    @preamble
+    def _define_facet_jacobian_determinant(self, name):
+        # NB: In the equidistant case, this might be optimized to store *d* values on the operator level
+        #     We don't do that right now for laziness.
+        geo = name_geometry()
+        pos = name_localcenter()
+
+        return "auto {} = {}.integrationElement({});".format(name,
+                                                             geo,
+                                                             pos,
+                                                             )
+
+
+@geometry_mixin("equidistant")
+class EquidistantGeometryMixin(AxiparallelGeometryMixin):
+    def define_jacobian_determinant(self, name):
+        valuearg(name)
+        self._define_jacobian_determinant(name)
+
+    @class_member(classtag="operator")
+    def _define_jacobian_determinant(self, name):
+        from dune.codegen.pdelab.localoperator import lop_template_ansatz_gfs
+        gfst = lop_template_ansatz_gfs()
+        self._define_jacobian_determinant_eval(name)
+        return "typename {}::Traits::GridView::template Codim<0>::Geometry::ctype {};".format(gfst, name)
+
+    @preamble(kernel="operator")
+    def _define_jacobian_determinant_eval(self, name):
+        from dune.codegen.pdelab.localoperator import name_ansatz_gfs_constructor_param, lop_template_range_field
+        gfs = name_ansatz_gfs_constructor_param()
+        rft = lop_template_range_field()
+        return "{} = {}.gridView().template begin<0>()->geometry().integrationElement(Dune::FieldVector<{}, {}>());".format(name, gfs, rft, world_dimension())
+
+    def define_jacobian_inverse_transposed(self, name, restriction):
+        dim = world_dimension()
+        globalarg(name, shape=(dim, dim), managed=False)
+        self._define_jacobian_inverse_transposed(name, restriction)
+
+    @class_member(classtag="operator")
+    def _define_jacobian_inverse_transposed(self, name, restriction):
+        dim = world_dimension()
+        self._define_jacobian_inverse_transposed_eval(name)
+        from dune.codegen.pdelab.localoperator import lop_template_ansatz_gfs
+        gfst = lop_template_ansatz_gfs()
+        return "typename {}::Traits::GridView::template Codim<0>::Geometry::JacobianInverseTransposed {};".format(gfst, name)
+
+    @preamble(kernel="operator")
+    def _define_jacobian_inverse_transposed_eval(self, name):
+        from dune.codegen.pdelab.localoperator import name_ansatz_gfs_constructor_param, lop_template_range_field
+        gfs = name_ansatz_gfs_constructor_param()
+        rft = lop_template_range_field()
+        return "{} = {}.gridView().template begin<0>()->geometry().jacobianInverseTransposed(Dune::FieldVector<{}, {}>());".format(name, gfs, rft, world_dimension())
+
+
+def enforce_boundary_restriction(visitor):
+    if visitor.measure == 'exterior_facet' and visitor.restriction == Restriction.NONE:
+        return Restriction.POSITIVE
+    else:
+        return visitor.restriction
+
+
 @preamble
 def define_reference_element(name):
     geo = name_geometry()
@@ -201,35 +472,6 @@ def name_in_cell_geometry(restriction):
     return name
 
 
-# TODO is it always necessary to add quadrature inames?
-def apply_in_cell_transformation(name, local, restriction):
-    geo = name_in_cell_geometry(restriction)
-    return quadrature_preamble("{} = {}.global({});".format(name,
-                                                            geo,
-                                                            str(local),
-                                                            ),
-                               assignees=frozenset({name}),
-                               read_variables=frozenset({get_pymbolic_basename(local)}),
-                               depends_on=frozenset({Writes(get_pymbolic_basename(local))}),
-                               )
-
-
-def pymbolic_in_cell_coordinates(local, restriction):
-    basename = get_pymbolic_basename(local)
-    name = "{}_in_{}side".format(basename, "in" if restriction is Restriction.POSITIVE else "out")
-    temporary_variable(name, shape=(world_dimension(),), shape_impl=("fv",))
-    apply_in_cell_transformation(name, local, restriction)
-    return Variable(name)
-
-
-def to_cell_coordinates(local, restriction):
-    assert isinstance(local, PymbolicExpression)
-    if restriction == Restriction.NONE:
-        return local
-    else:
-        return pymbolic_in_cell_coordinates(local, restriction)
-
-
 def world_dimension():
     data = get_global_context_value("data")
     form = data.object_by_name[get_form_option("form")]
@@ -250,48 +492,12 @@ def local_dimension():
         return intersection_dimension()
 
 
-def evaluate_unit_outer_normal(name):
-    ig = name_intersection_geometry_wrapper()
-    qp = get_backend("quad_pos")()
-    return quadrature_preamble("{} = {}.unitOuterNormal({});".format(name, ig, qp),
-                               assignees=frozenset({name}),
-                               read_variables=frozenset({get_pymbolic_basename(qp)}),
-                               depends_on=frozenset({Writes(get_pymbolic_basename(qp))}),
-                               )
-
-
 @preamble
 def declare_normal(name, kernel, decl_info):
     ig = name_intersection_geometry_wrapper()
     return "auto {} = {}.centerUnitOuterNormal();".format(name, ig)
 
 
-def pymbolic_unit_outer_normal():
-    name = "unit_outer_normal"
-    if not get_form_option("diagonal_transformation_matrix"):
-        temporary_variable(name, shape=(world_dimension(),), decl_method=declare_normal)
-        evaluate_unit_outer_normal(name)
-    else:
-        declare_normal(name, None, None)
-        globalarg(name, shape=(world_dimension(),))
-    return prim.Variable(name)
-
-
-def evaluate_unit_inner_normal(name):
-    outer = name_unit_outer_normal()
-    return quadrature_preamble("auto {} = -1. * {};".format(name, outer),
-                               assignees=frozenset({name}),
-                               read_variables=frozenset({outer}),
-                               )
-
-
-def pymbolic_unit_inner_normal():
-    name = "inner_normal"
-    temporary_variable(name, shape=(world_dimension(),), decl_method=declare_normal)
-    evaluate_unit_inner_normal(name)
-    return prim.Variable(name)
-
-
 def type_jacobian_inverse_transposed(restriction):
     geo = type_cell_geometry(restriction)
     return "typename {}::JacobianInverseTransposed".format(geo)
@@ -306,213 +512,3 @@ def define_jacobian_inverse_transposed_temporary(restriction):
                                name,
                                )
     return _define_jacobian_inverse_transposed_temporary
-
-
-@preamble(kernel="operator")
-def define_constant_jacobian_inverse_transposed_eval(name):
-    from dune.codegen.pdelab.localoperator import name_ansatz_gfs_constructor_param, lop_template_range_field
-    gfs = name_ansatz_gfs_constructor_param()
-    rft = lop_template_range_field()
-
-    return "{} = {}.gridView().template begin<0>()->geometry().jacobianInverseTransposed(Dune::FieldVector<{}, {}>());".format(name, gfs, rft, world_dimension())
-
-
-@class_member(classtag="operator")
-def define_constant_jacobian_inverse_transposed(name):
-    define_constant_jacobian_inverse_transposed_eval(name)
-    from dune.codegen.pdelab.localoperator import lop_template_ansatz_gfs
-    gfst = lop_template_ansatz_gfs()
-    return "typename {}::Traits::GridView::template Codim<0>::Geometry::JacobianInverseTransposed {};".format(gfst, name)
-
-
-@backend(interface="name_jit", name="constant_transformation_matrix")
-def name_constant_jacobian_inverse_transposed(restriction):
-    name = "jit"
-    dim = world_dimension()
-    globalarg(name, shape=(dim, dim), managed=False)
-    define_constant_jacobian_inverse_transposed(name)
-    return name
-
-
-def define_jacobian_inverse_transposed(name, restriction):
-    dim = world_dimension()
-    temporary_variable(name, decl_method=define_jacobian_inverse_transposed_temporary(restriction), shape=(dim, dim))
-    geo = name_cell_geometry(restriction)
-    pos = get_backend("qp_in_cell", selector=option_switch("blockstructured"))(restriction)
-
-    return quadrature_preamble("{} = {}.jacobianInverseTransposed({});".format(name,
-                                                                               geo,
-                                                                               str(pos),
-                                                                               ),
-                               assignees=frozenset({name}),
-                               read_variables=frozenset({get_pymbolic_basename(pos)}),
-                               depends_on=frozenset({Writes(get_pymbolic_basename(pos))}),
-                               )
-
-
-@backend(interface="name_jit", name="default")
-def name_jacobian_inverse_transposed(restriction):
-    name = restricted_name("jit", restriction)
-    define_jacobian_inverse_transposed(name, restriction)
-    return name
-
-
-def pymbolic_jacobian_inverse(i, j, restriction):
-    # Calculate jacobian_inverse_transposed
-    name = get_backend(interface="name_jit", selector=option_switch("constant_transformation_matrix"))(restriction)
-
-    # Return jacobian inverse -> (j, i) instead of (i, j)
-    return prim.Subscript(prim.Variable(name), (j, i))
-
-
-@preamble(kernel="operator")
-def define_constant_jacobian_determinant_eval(name):
-    from dune.codegen.pdelab.localoperator import name_ansatz_gfs_constructor_param, lop_template_range_field
-    gfs = name_ansatz_gfs_constructor_param()
-    rft = lop_template_range_field()
-
-    return "{} = {}.gridView().template begin<0>()->geometry().integrationElement(Dune::FieldVector<{}, {}>());".format(name, gfs, rft, world_dimension())
-
-
-@class_member(classtag="operator")
-def _define_constant_jacobian_determinant(name):
-    define_constant_jacobian_determinant_eval(name)
-    from dune.codegen.pdelab.localoperator import lop_template_ansatz_gfs
-    gfst = lop_template_ansatz_gfs()
-    return "typename {}::Traits::GridView::template Codim<0>::Geometry::ctype {};".format(gfst, name)
-
-
-@backend(interface="detjac", name="constant_transformation_matrix")
-def define_constant_jacobian_determinant(name):
-    valuearg(name)
-    _define_constant_jacobian_determinant(name)
-
-
-@backend(interface="detjac", name="default")
-def define_jacobian_determinant(name):
-    temporary_variable(name, shape=())
-    geo = name_geometry()
-    pos = get_backend("quad_pos", selector=option_switch("blockstructured"))()
-    code = "{} = {}.integrationElement({});".format(name,
-                                                    geo,
-                                                    str(pos),
-                                                    )
-
-    return quadrature_preamble(code,
-                               assignees=frozenset({name}),
-                               read_variables=frozenset({get_pymbolic_basename(pos)}),
-                               depends_on=frozenset({Writes(get_pymbolic_basename(pos))}),
-                               )
-
-
-@backend(interface="fdetjac", name="constant_transformation_matrix")
-@preamble
-def define_facet_jacobian_determinant(name):
-    # NB: This might be optimized to store *d* values on the operator level
-    #     We don't do that right now for laziness.
-    geo = name_geometry()
-    pos = name_localcenter()
-
-    valuearg(name)
-
-    return "auto {} = {}.integrationElement({});".format(name,
-                                                         geo,
-                                                         pos,
-                                                         )
-
-
-@backend(interface="fdetjac", name="default")
-def define_facet_jacobian_determinant(name):
-    return define_jacobian_determinant(name)
-
-
-def name_jacobian_determinant():
-    name = 'detjac'
-    get_backend(interface="detjac", selector=option_switch("constant_transformation_matrix"))(name)
-    return name
-
-
-def pymbolic_jacobian_determinant():
-    return prim.Variable(name_jacobian_determinant())
-
-
-def name_facet_jacobian_determinant():
-    name = 'fdetjac'
-    get_backend(interface="fdetjac", selector=option_switch("constant_transformation_matrix"))(name)
-    return name
-
-
-def pymbolic_facet_jacobian_determinant():
-    return prim.Variable(name_facet_jacobian_determinant())
-
-
-def apply_to_global_transformation(name, local):
-    temporary_variable(name, shape=(world_dimension(),), shape_impl=("fv",))
-    geo = name_geometry()
-    code = "{} = {}.global({});".format(name,
-                                        geo,
-                                        local,
-                                        )
-    return quadrature_preamble(code,
-                               assignees=frozenset({name}),
-                               read_variables=frozenset({get_pymbolic_basename(local)}),
-                               depends_on=frozenset({Writes(get_pymbolic_basename(local))})
-                               )
-
-
-def to_global(local):
-    assert isinstance(local, prim.Expression)
-    name = get_pymbolic_basename(local) + "_global"
-    apply_to_global_transformation(name, local)
-    return prim.Variable(name)
-
-
-@preamble
-def define_cell_volume(name, restriction):
-    geo = name_cell_geometry(restriction)
-    valuearg(name)
-    return "auto {} = {}.volume();".format(name, geo)
-
-
-def pymbolic_cell_volume(restriction):
-    if get_form_option("constant_transformation_matrix"):
-        return pymbolic_jacobian_determinant()
-    else:
-        name = restricted_name("volume", restriction)
-        define_cell_volume(name, restriction)
-        return prim.Variable(name)
-
-
-@preamble
-def define_facet_area(name):
-    geo = name_intersection_geometry()
-    valuearg(name)
-    return "auto {} = {}.volume();".format(name, geo)
-
-
-def pymbolic_facet_area():
-    if get_form_option("constant_transformation_matrix"):
-        return pymbolic_facet_jacobian_determinant()
-    else:
-        name = "area"
-        define_facet_area(name)
-        return prim.Variable(name)
-
-
-def define_element_corners(name):
-    from dune.codegen.pdelab.driver import get_form
-    n_corners = get_form().ufl_cell().num_vertices()
-    temporary_variable(name, shape_impl=('fv', 'fv'), shape=(n_corners, local_dimension()))
-
-    iname = "i_corner"
-    domain(iname, n_corners)
-    from dune.codegen.generation import instruction
-    instruction(code="{0}[{1}] = {2}.corner({3});".format(name, iname, name_geometry(), iname),
-                assignees=frozenset({name}),
-                within_inames=frozenset({iname}), within_inames_is_final=True)
-
-
-def name_element_corners():
-    name = "corners"
-    define_element_corners(name)
-    return name
diff --git a/python/dune/codegen/pdelab/localoperator.py b/python/dune/codegen/pdelab/localoperator.py
index 12793f96977e46ccb252285454f11344810892bc..70ab9f4793a6163559d070e33e3600f9cd8c5578 100644
--- a/python/dune/codegen/pdelab/localoperator.py
+++ b/python/dune/codegen/pdelab/localoperator.py
@@ -9,17 +9,17 @@ from dune.codegen.options import (get_form_option,
                                   get_option,
                                   option_switch,
                                   set_form_option)
-from dune.codegen.generation import (backend,
+from dune.codegen.generation import (accumulation_mixin,
                                      base_class,
                                      class_basename,
                                      class_member,
+                                     construct_from_mixins,
                                      constructor_parameter,
                                      domain,
                                      dump_accumulate_timer,
                                      end_of_file,
                                      function_mangler,
                                      generator_factory,
-                                     get_backend,
                                      get_global_context_value,
                                      global_context,
                                      iname,
@@ -328,6 +328,30 @@ def boundary_predicates(measure, subdomain_id):
     return frozenset(predicates)
 
 
+@accumulation_mixin("base")
+class AccumulationMixinBase(object):
+    def get_accumulation_info(self, expr):
+        raise NotImplementedError
+
+    def list_accumulation_infos(self, expr):
+        raise NotImplementedError
+
+    def generate_accumulation_instruction(self, expr):
+        raise NotImplementedError
+
+
+@accumulation_mixin("generic")
+class GenericAccumulationMixin(AccumulationMixinBase):
+    def get_accumulation_info(self, expr):
+        return get_accumulation_info(expr, self)
+
+    def list_accumulation_infos(self, expr):
+        return list_accumulation_infos(expr, self)
+
+    def generate_accumulation_instruction(self, expr):
+        return generate_accumulation_instruction(expr, self)
+
+
 class PDELabAccumulationInfo(ImmutableRecord):
     def __init__(self,
                  element=None,
@@ -390,10 +414,10 @@ def get_accumulation_info(expr, visitor):
     if visitor.measure == 'exterior_facet':
         restriction = Restriction.POSITIVE
 
-    inames = visitor.interface.lfs_inames(leaf_element,
-                                          restriction,
-                                          expr.number()
-                                          )
+    inames = visitor.lfs_inames(leaf_element,
+                                restriction,
+                                expr.number()
+                                )
 
     return PDELabAccumulationInfo(element=expr.ufl_element(),
                                   element_index=element_index,
@@ -424,7 +448,7 @@ def generate_accumulation_instruction(expr, visitor):
 
     from dune.codegen.generation import instruction
     from dune.codegen.options import option_switch
-    quad_inames = visitor.interface.quadrature_inames()
+    quad_inames = visitor.quadrature_inames()
     lfs_inames = frozenset(visitor.test_info.inames)
     if visitor.trial_info:
         lfs_inames = lfs_inames.union(visitor.trial_info.inames)
@@ -438,19 +462,24 @@ def generate_accumulation_instruction(expr, visitor):
 
 
 def get_visitor(measure, subdomain_id):
-    # Get a transformer instance for this kernel
-    if get_form_option('sumfact'):
-        from dune.codegen.sumfact import SumFactInterface
-        interface = SumFactInterface()
-    elif get_form_option('blockstructured'):
-        from dune.codegen.blockstructured import BlockStructuredInterface
-        interface = BlockStructuredInterface()
-    else:
-        from dune.codegen.pdelab import PDELabInterface
-        interface = PDELabInterface()
-
+    # Construct the visitor taking into account geometry mixins
     from dune.codegen.ufl.visitor import UFL2LoopyVisitor
-    return UFL2LoopyVisitor(interface, measure, subdomain_id)
+    mixins = get_form_option("geometry_mixins").split(",")
+    VisitorType = construct_from_mixins(base=UFL2LoopyVisitor, mixins=mixins, mixintype="geometry", name="UFLVisitor")
+
+    # Mix quadrature mixins in
+    mixins = get_form_option("quadrature_mixins").split(",")
+    VisitorType = construct_from_mixins(base=VisitorType, mixins=mixins, mixintype="quadrature", name="UFLVisitor")
+
+    # Mix basis mixins in
+    mixins = get_form_option("basis_mixins").split(",")
+    VisitorType = construct_from_mixins(base=VisitorType, mixins=mixins, mixintype="basis", name="UFLVisitor")
+
+    # Mix accumulation mixins in
+    mixins = get_form_option("accumulation_mixins").split(",")
+    VisitorType = construct_from_mixins(base=VisitorType, mixins=mixins, mixintype="accumulation", name="UFLVisitor")
+
+    return VisitorType(measure, subdomain_id)
 
 
 def visit_integral(integral):
@@ -514,9 +543,13 @@ def generate_kernel(integrals):
     return knl
 
 
-@backend(interface="generate_kernels_per_integral")
 def generate_kernels_per_integral(integrals):
-    yield generate_kernel(integrals)
+    if get_form_option("sumfact"):
+        from dune.codegen.sumfact.switch import sumfact_generate_kernels_per_integral
+        for k in sumfact_generate_kernels_per_integral(integrals):
+            yield k
+    else:
+        yield generate_kernel(integrals)
 
 
 def extract_kernel_from_cache(tag, name, signature, wrap_in_cgen=True, add_timings=True):
@@ -759,14 +792,6 @@ def local_operator_default_settings(operator, form):
     for c in sorted(filter(lambda c: c.count() > 2, form.coefficients()), key=lambda c: c.count()):
         name_gridfunction_constructor_argument(c)
 
-    # Set some options!
-    from dune.codegen.pdelab.driver import isQuadrilateral
-    if isQuadrilateral(form.arguments()[0].ufl_element().cell()) and not get_option("grid_unstructured"):
-        from dune.codegen.options import set_form_option
-        # For Yasp Grids the jacobian of the transformation is diagonal and constant on each cell
-        set_form_option('diagonal_transformation_matrix', True)
-        set_form_option('constant_transformation_matrix', True)
-
     # Add right base classes for stationary/instationary operators
     base_class('Dune::PDELab::LocalOperatorDefaultFlags', classtag="operator")
     from dune.codegen.pdelab.driver import is_stationary
@@ -803,7 +828,7 @@ def generate_residual_kernels(form, original_form):
             with global_context(integral_type=measure):
                 from dune.codegen.pdelab.signatures import assembler_routine_name
                 with global_context(kernel=assembler_routine_name()):
-                    kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(form.integrals_by_type(measure))]
+                    kernel = [k for k in generate_kernels_per_integral(form.integrals_by_type(measure))]
 
                 # The integrals might vanish due to unfulfillable boundary conditions.
                 # We only generate the local operator enums/base classes if they did not.
@@ -890,7 +915,7 @@ def generate_jacobian_kernels(form, original_form):
                 with global_context(integral_type=measure):
                     from dune.codegen.pdelab.signatures import assembler_routine_name
                     with global_context(kernel=assembler_routine_name()):
-                        kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(jac_apply_form.integrals_by_type(measure))]
+                        kernel = [k for k in generate_kernels_per_integral(jac_apply_form.integrals_by_type(measure))]
                 operator_kernels[(measure, 'jacobian_apply')] = kernel
 
                 # Generate dummy functions for those kernels, that vanished in the differentiation process
@@ -908,6 +933,8 @@ def generate_jacobian_kernels(form, original_form):
                 if get_form_option("sumfact"):
                     was_sumfact = True
                     if get_form_option("sumfact_regular_jacobians"):
+                        old_geometry_mixins = get_form_option("geometry_mixins")
+                        set_form_option("geometry_mixins", "generic")
                         set_form_option("sumfact", False)
                 for measure in set(i.integral_type() for i in jacform.integrals()):
                     if not measure_is_enabled(measure):
@@ -917,7 +944,7 @@ def generate_jacobian_kernels(form, original_form):
                     with global_context(integral_type=measure):
                         from dune.codegen.pdelab.signatures import assembler_routine_name
                         with global_context(kernel=assembler_routine_name()):
-                            kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(jacform.integrals_by_type(measure))]
+                            kernel = [k for k in generate_kernels_per_integral(jacform.integrals_by_type(measure))]
                     operator_kernels[(measure, 'jacobian')] = kernel
 
                 # Generate dummy functions for those kernels, that vanished in the differentiation process
@@ -932,6 +959,7 @@ def generate_jacobian_kernels(form, original_form):
                 if get_form_option("sumfact_regular_jacobians"):
                     if was_sumfact:
                         set_form_option("sumfact", True)
+                        set_form_option("geometry_mixins", old_geometry_mixins)
 
     return operator_kernels
 
diff --git a/python/dune/codegen/pdelab/quadrature.py b/python/dune/codegen/pdelab/quadrature.py
index 8c3e2b36df63b3120883fd51737fc5ded051b157..ae6a7e2db7212b254e3857bc86813ea420ed0251 100644
--- a/python/dune/codegen/pdelab/quadrature.py
+++ b/python/dune/codegen/pdelab/quadrature.py
@@ -1,22 +1,109 @@
 import numpy
 
-from dune.codegen.generation import (backend,
-                                     class_member,
+from dune.codegen.generation import (class_member,
                                      domain,
-                                     get_backend,
                                      get_global_context_value,
                                      globalarg,
                                      iname,
                                      include_file,
                                      instruction,
                                      preamble,
+                                     quadrature_mixin,
                                      temporary_variable,
                                      valuearg,
                                      )
-from dune.codegen.pdelab.localoperator import lop_template_range_field
+from dune.codegen.pdelab.localoperator import lop_template_range_field, name_ansatz_gfs_constructor_param
 from dune.codegen.options import get_form_option
 
 from pymbolic.primitives import Variable, Subscript
+import pymbolic.primitives as prim
+
+
+@quadrature_mixin("base")
+class QuadratureMixinBase(object):
+    def quad_unhandled(self, o):
+        raise NotImplementedError("Quadrature Mixins should handle {}".format(type(o)))
+
+    quadrature_weight = quad_unhandled
+
+    def quadrature_inames(self):
+        raise NotImplementedError("Quadrature mixins should provide the quadrature inames!")
+
+    def quadrature_position(self, index=None):
+        raise NotImplementedError("Quadrature mixins should provide the quadrature position")
+
+
+@quadrature_mixin("generic")
+class GenericQuadratureMixin(QuadratureMixinBase):
+    def quadrature_weight(self, o):
+        # Create a name that has all the necessary quantities mangled in
+        from dune.codegen.pdelab.geometry import local_dimension
+        dim = local_dimension()
+        order = quadrature_order()
+        name = "qw_dim{}_order{}".format(dim, order)
+
+        shape = name_quadrature_bound()
+        globalarg(name, shape=(shape,))
+        self.define_quadrature_weights(name)
+
+        return prim.Subscript(prim.Variable(name),
+                              tuple(prim.Variable(i) for i in self.quadrature_inames()))
+
+    @class_member(classtag="operator")
+    def define_quadrature_weights(self, name):
+        rf = lop_template_range_field()
+        from dune.codegen.pdelab.geometry import local_dimension
+        dim = local_dimension()
+        self.eval_quadrature_weights(name)
+        return "mutable std::vector<typename Dune::QuadraturePoint<{}, {}>::Field> {};".format(rf, dim, name)
+
+    @preamble(kernel="operator")
+    def eval_quadrature_weights(self, name):
+        gfs = name_ansatz_gfs_constructor_param()
+        quad_order = quadrature_order()
+        include_file("dune/codegen/localbasiscache.hh", filetag='operatorfile')
+        entity = "{}.gridView().template begin<0>()".format(gfs)
+        if self.measure != "cell":
+            entity = "{}.gridView().ibegin(*({}))".format(gfs, entity)
+        return "fillQuadratureWeightsCache({}->geometry(), {}, {});".format(entity, quad_order, name)
+
+    def quadrature_inames(self):
+        return (quadrature_iname(),)
+
+    def quadrature_position(self, index=None):
+        from dune.codegen.pdelab.geometry import local_dimension
+        dim = local_dimension()
+        order = quadrature_order()
+        name = "qp_dim{}_order{}".format(dim, order)
+
+        shape = (name_quadrature_bound(), dim)
+        globalarg(name, shape=shape, managed=False)
+        self.define_quadrature_points(name)
+
+        qp = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in self.quadrature_inames()))
+
+        if index is not None:
+            qp = prim.Subscript(qp, (index,))
+
+        return qp
+
+    @class_member(classtag="operator")
+    def define_quadrature_points(self, name):
+        rf = lop_template_range_field()
+        from dune.codegen.pdelab.geometry import local_dimension
+        dim = local_dimension()
+        self.eval_quadrature_points(name)
+        return "mutable std::vector<typename Dune::QuadraturePoint<{}, {}>::Vector> {};".format(rf, dim, name)
+
+    @preamble(kernel="operator")
+    def eval_quadrature_points(self, name):
+        gfs = name_ansatz_gfs_constructor_param()
+        quad_order = quadrature_order()
+        include_file("dune/codegen/localbasiscache.hh", filetag='operatorfile')
+        entity = "{}.gridView().template begin<0>()".format(gfs)
+        if self.measure != "cell":
+            entity = "{}.gridView().ibegin(*({}))".format(gfs, entity)
+        return "fillQuadraturePointsCache({}->geometry(), {}, {});".format(entity, quad_order, name)
 
 
 @preamble
@@ -56,14 +143,9 @@ def quadrature_iname():
     return "q"
 
 
-@backend(interface="quad_inames")
-def quadrature_inames():
-    return (quadrature_iname(),)
-
-
-def quadrature_preamble(code, **kw):
+def quadrature_preamble(visitor, code, **kw):
     kw['tags'] = kw.get('tags', frozenset({})).union(frozenset({"quad"}))
-    return instruction(inames=get_backend(interface="quad_inames")(), code=code, **kw)
+    return instruction(inames=visitor.quadrature_inames(), code=code, **kw)
 
 
 def name_quadrature_point():
@@ -71,114 +153,6 @@ def name_quadrature_point():
     return "qp"
 
 
-@preamble
-def fill_quadrature_points_cache(name):
-    from dune.codegen.pdelab.geometry import name_geometry
-    geo = name_geometry()
-    quad_order = quadrature_order()
-    include_file("dune/codegen/localbasiscache.hh", filetag='operatorfile')
-    return "fillQuadraturePointsCache({}, {}, {});".format(geo, quad_order, name)
-
-
-@class_member(classtag="operator")
-def typedef_quadrature_points(name):
-    range_field = lop_template_range_field()
-    from dune.codegen.pdelab.geometry import local_dimension
-    dim = local_dimension()
-    return "using {} = typename Dune::QuadraturePoint<{}, {}>::Vector;".format(name, range_field, dim)
-
-
-def type_quadrature_points(name):
-    name = name.upper()
-    typedef_quadrature_points(name)
-    return name
-
-
-@class_member(classtag="operator")
-def define_quadrature_points(name):
-    qp_type = type_quadrature_points(name)
-    return "mutable std::vector<{}> {};".format(qp_type, name)
-
-
-def name_quadrature_points():
-    """Name of vector storing quadrature points as class member"""
-    from dune.codegen.pdelab.geometry import local_dimension
-    dim = local_dimension()
-    order = quadrature_order()
-    name = "qp_dim{}_order{}".format(dim, order)
-    shape = (name_quadrature_bound(), dim)
-    globalarg(name, shape=shape, managed=False)
-    define_quadrature_points(name)
-    fill_quadrature_points_cache(name)
-    return name
-
-
-@backend(interface="quad_pos")
-def pymbolic_quadrature_position():
-    quad_points = name_quadrature_points()
-    quad_iname = quadrature_iname()
-    from pymbolic.primitives import Subscript, Variable
-    return Subscript(Variable(quad_points), (Variable(quad_iname),))
-
-
-@backend(interface="qp_in_cell")
-def pymbolic_quadrature_position_in_cell(restriction):
-    from dune.codegen.pdelab.geometry import to_cell_coordinates
-    quad_pos = get_backend("quad_pos")()
-    return to_cell_coordinates(quad_pos, restriction)
-
-
-@preamble
-def fill_quadrature_weights_cache(name):
-    from dune.codegen.pdelab.geometry import name_geometry
-    geo = name_geometry()
-    quad_order = quadrature_order()
-    include_file("dune/codegen/localbasiscache.hh", filetag='operatorfile')
-    return "fillQuadratureWeightsCache({}, {}, {});".format(geo, quad_order, name)
-
-
-@class_member(classtag="operator")
-def typedef_quadrature_weights(name):
-    range_field = lop_template_range_field()
-    from dune.codegen.pdelab.geometry import local_dimension
-    dim = local_dimension()
-    return "using {} = typename Dune::QuadraturePoint<{}, {}>::Field;".format(name, range_field, dim)
-
-
-def pymbolic_quadrature_weight():
-    vec = name_quadrature_weights()
-    return Subscript(Variable(vec),
-                     tuple(Variable(i) for i in quadrature_inames()))
-
-
-def type_quadrature_weights(name):
-    name = name.upper()
-    typedef_quadrature_weights(name)
-    return name
-
-
-@class_member(classtag="operator")
-def define_quadrature_weights(name):
-    qw_type = type_quadrature_weights(name)
-    return "mutable std::vector<{}> {};".format(qw_type, name)
-
-
-def name_quadrature_weights():
-    """"Name of vector storing quadrature weights as class member"""
-    from dune.codegen.pdelab.geometry import local_dimension
-    dim = local_dimension()
-    order = quadrature_order()
-    name = "qw_dim{}_order{}".format(dim, order)
-    define_quadrature_weights(name)
-    fill_quadrature_weights_cache(name)
-
-    # Quadrature weighs is a globar argument for loopy
-    shape = name_quadrature_bound()
-    globalarg(name, shape=(shape,))
-
-    return name
-
-
 def _estimate_quadrature_order():
     """Estimate quadrature order using polynomial degree estimation from UFL"""
     # According to UFL documentation estimate_total_polynomial_degree
diff --git a/python/dune/codegen/pdelab/tensors.py b/python/dune/codegen/pdelab/tensors.py
index 754e7b24a8f639a3a73a3532ef56c888368dd2af..a924a39ae1bba19a94c47e5542567d8717cedf57 100644
--- a/python/dune/codegen/pdelab/tensors.py
+++ b/python/dune/codegen/pdelab/tensors.py
@@ -14,61 +14,6 @@ import loopy as lp
 import itertools as it
 
 
-def define_list_tensor(name, expr, visitor, stack=()):
-    for i, child in enumerate(expr.ufl_operands):
-        from ufl.classes import ListTensor
-        if isinstance(child, ListTensor):
-            define_list_tensor(name, child, visitor, stack=stack + (i,))
-        else:
-            visexpr = visitor.call(child)
-            from loopy.symbolic import DependencyMapper
-            deps = DependencyMapper(include_subscripts=False, include_lookups=False, include_calls=False)(visexpr)
-            instruction(assignee=prim.Subscript(prim.Variable(name), stack + (i,)),
-                        expression=visitor.call(child),
-                        forced_iname_deps=frozenset(visitor.interface.quadrature_inames()),
-                        depends_on=frozenset({lp.match.Tagged("sumfact_stage1")}),
-                        tags=frozenset({"quad"}),
-                        )
-
-
-@kernel_cached
-def pymbolic_list_tensor(expr, visitor):
-    name = get_counted_variable("listtensor")
-    temporary_variable(name,
-                       shape=expr.ufl_shape,
-                       managed=True,
-                       )
-    define_list_tensor(name, expr, visitor)
-    return prim.Variable(name)
-
-
-@iname
-def identity_iname(name, bound):
-    name = "id_{}_{}".format(name, bound)
-    domain(name, bound)
-    return name
-
-
-def define_identity(name, expr):
-    i = identity_iname("i", expr.ufl_shape[0])
-    j = identity_iname("j", expr.ufl_shape[1])
-    instruction(assignee=prim.Subscript(prim.Variable(name), (prim.Variable(i), prim.Variable(j))),
-                expression=prim.If(prim.Comparison(prim.Variable(i), "==", prim.Variable(j)), 1, 0),
-                forced_iname_deps_is_final=True,
-                )
-
-
-@kernel_cached
-def pymbolic_identity(expr):
-    name = "identity_{}_{}".format(expr.ufl_shape[0], expr.ufl_shape[1])
-    temporary_variable(name,
-                       shape=expr.ufl_shape,
-                       shape_impl=('fm',),
-                       )
-    define_identity(name, expr)
-    return prim.Variable(name)
-
-
 def define_assembled_tensor(name, expr, visitor):
     temporary_variable(name,
                        shape=expr.ufl_shape,
diff --git a/python/dune/codegen/sumfact/__init__.py b/python/dune/codegen/sumfact/__init__.py
index 22a6049614784ced07c1e2a68739a094ec328516..0b5fe7330a7cd5821646aec853e21fc6df35fe2d 100644
--- a/python/dune/codegen/sumfact/__init__.py
+++ b/python/dune/codegen/sumfact/__init__.py
@@ -1,102 +1,3 @@
-from dune.codegen.generation import get_backend
-from dune.codegen.options import option_switch
-from dune.codegen.pdelab.argument import (name_applycontainer,
-                                          name_coefficientcontainer,
-                                          )
-from dune.codegen.sumfact.quadrature import (quadrature_inames,
-                                             quadrature_weight,
-                                             )
-
-from dune.codegen.sumfact.basis import (lfs_inames,
-                                        pymbolic_basis,
-                                        pymbolic_reference_gradient,
-                                        pymbolic_coefficient,
-                                        pymbolic_coefficient_gradient,
-                                        )
-
+import dune.codegen.sumfact.geometry
 import dune.codegen.sumfact.accumulation
 import dune.codegen.sumfact.switch
-
-from dune.codegen.pdelab import PDELabInterface
-
-
-class SumFactInterface(PDELabInterface):
-    def get_accumulation_info(self, expr, visitor):
-        from dune.codegen.sumfact.accumulation import get_accumulation_info
-        return get_accumulation_info(expr, visitor)
-
-    def list_accumulation_infos(self, expr, visitor):
-        from dune.codegen.sumfact.accumulation import list_accumulation_infos
-        return list_accumulation_infos(expr, visitor)
-
-    def generate_accumulation_instruction(self, expr, visitor):
-        from dune.codegen.sumfact.accumulation import generate_accumulation_instruction
-        return generate_accumulation_instruction(expr, visitor)
-
-    def lfs_inames(self, element, restriction, number=None, context=''):
-        return lfs_inames(element, restriction, number, context)
-
-    def pymbolic_basis(self, element, restriction, number):
-        return pymbolic_basis(element, restriction, number)
-
-    def pymbolic_reference_gradient(self, element, restriction, number):
-        ret, indices = pymbolic_reference_gradient(element, restriction, number, self.visitor.indices)
-        self.visitor.indices = indices
-        return ret
-
-    def pymbolic_trialfunction_gradient(self, element, restriction, index):
-        ret = pymbolic_coefficient_gradient(element, restriction, index, name_coefficientcontainer, self.visitor)
-        return ret
-
-    def pymbolic_trialfunction(self, element, restriction, index):
-        ret = pymbolic_coefficient(element, restriction, index, name_coefficientcontainer, self.visitor)
-        return ret
-
-    def pymbolic_apply_function_gradient(self, element, restriction, index):
-        ret = pymbolic_coefficient_gradient(element, restriction, index, name_applycontainer, self.visitor)
-        return ret
-
-    def pymbolic_apply_function(self, element, restriction, index):
-        ret = pymbolic_coefficient(element, restriction, index, name_applycontainer, self.visitor)
-        return ret
-
-    def quadrature_inames(self):
-        # TODO Is broken at the moment. Remove from interface? See also pdelab interface.
-        assert False
-        return quadrature_inames()
-
-    def pymbolic_quadrature_weight(self):
-        return quadrature_weight(self.visitor)
-
-    #
-    # Geometry related generator functions
-    #
-
-    def pymbolic_spatial_coordinate(self):
-        import dune.codegen.sumfact.geometry
-        ret = get_backend(interface="spatial_coordinate", selector=option_switch("diagonal_transformation_matrix"))(self.visitor.do_predicates, self.visitor)
-        return ret
-
-    def pymbolic_unit_outer_normal(self):
-        from dune.codegen.sumfact.geometry import pymbolic_unit_outer_normal
-        return pymbolic_unit_outer_normal(self.visitor)
-
-    def pymbolic_unit_inner_normal(self):
-        from dune.codegen.sumfact.geometry import pymbolic_unit_inner_normal
-        return pymbolic_unit_inner_normal(self.visitor)
-
-    def pymbolic_facet_jacobian_determinant(self):
-        from dune.codegen.sumfact.geometry import pymbolic_facet_jacobian_determinant
-        return pymbolic_facet_jacobian_determinant(self.visitor)
-
-    def pymbolic_facet_area(self):
-        from dune.codegen.sumfact.geometry import pymbolic_facet_area
-        return pymbolic_facet_area(self.visitor)
-
-    def pymbolic_jacobian_inverse(self, i, j, restriction):
-        from dune.codegen.sumfact.geometry import pymbolic_jacobian_inverse
-        return pymbolic_jacobian_inverse(i, j, restriction, self.visitor)
-
-    def pymbolic_jacobian_determinant(self):
-        from dune.codegen.sumfact.geometry import pymbolic_jacobian_determinant
-        return pymbolic_jacobian_determinant(self.visitor)
diff --git a/python/dune/codegen/sumfact/accumulation.py b/python/dune/codegen/sumfact/accumulation.py
index 516da6f08b6c2825f616c925448175b61d454a43..eeb383da8fcec4d7799a15befa6f38698376fdb5 100644
--- a/python/dune/codegen/sumfact/accumulation.py
+++ b/python/dune/codegen/sumfact/accumulation.py
@@ -3,7 +3,7 @@ import itertools
 from dune.codegen.pdelab.argument import (name_accumulation_variable,
                                           PDELabAccumulationFunction,
                                           )
-from dune.codegen.generation import (backend,
+from dune.codegen.generation import (accumulation_mixin,
                                      domain,
                                      dump_accumulate_timer,
                                      generator_factory,
@@ -26,7 +26,7 @@ from dune.codegen.options import (get_form_option,
 from dune.codegen.loopy.flatten import flatten_index
 from dune.codegen.loopy.target import type_floatingpoint
 from dune.codegen.pdelab.driver import FEM_name_mangling
-from dune.codegen.pdelab.localoperator import determine_accumulation_space
+from dune.codegen.pdelab.localoperator import determine_accumulation_space, AccumulationMixinBase
 from dune.codegen.pdelab.restriction import restricted_name
 from dune.codegen.pdelab.signatures import assembler_routine_name
 from dune.codegen.pdelab.geometry import world_dimension
@@ -46,7 +46,6 @@ from dune.codegen.sumfact.symbolic import SumfactKernel, SumfactKernelInterfaceB
 from dune.codegen.ufl.modified_terminals import extract_modified_arguments
 from dune.codegen.tools import get_pymbolic_basename, get_leaf
 from dune.codegen.error import CodegenError
-from dune.codegen.sumfact.quadrature import quadrature_inames
 
 from pytools import ImmutableRecord, product
 
@@ -168,8 +167,8 @@ class AccumulationOutput(SumfactKernelInterfaceBase, ImmutableRecord):
         if self.trial_element is None:
             return ()
         else:
-            from dune.codegen.sumfact.basis import lfs_inames
-            return lfs_inames(get_leaf(self.trial_element, self.trial_element_index), self.restriction)
+            from dune.codegen.sumfact.basis import SumfactBasisMixin
+            return SumfactBasisMixin.lfs_inames(None, get_leaf(self.trial_element, self.trial_element_index), self.restriction)
 
     def realize_input(self, inames, shape, vec_iname, vec_shape, buf, ftags):
         # The result of stage 2 has the correct quadrature permutation but no
@@ -351,6 +350,18 @@ def _dof_offset(element, component):
         return sum(sizes[0:component])
 
 
+@accumulation_mixin("sumfact")
+class SumfactAccumulationMixin(AccumulationMixinBase):
+    def get_accumulation_info(self, expr):
+        return get_accumulation_info(expr, self)
+
+    def list_accumulation_infos(self, expr):
+        return list_accumulation_infos(expr, self)
+
+    def generate_accumulation_instruction(self, expr):
+        return generate_accumulation_instruction(expr, self)
+
+
 class SumfactAccumulationInfo(ImmutableRecord):
     def __init__(self,
                  element=None,
@@ -388,10 +399,10 @@ def get_accumulation_info(expr, visitor):
         from dune.codegen.pdelab.restriction import Restriction
         restriction = Restriction.POSITIVE
 
-    inames = visitor.interface.lfs_inames(leaf_element,
-                                          restriction,
-                                          expr.number()
-                                          )
+    inames = visitor.lfs_inames(leaf_element,
+                                restriction,
+                                expr.number()
+                                )
 
     grad_index = None
     if visitor.reference_grad and expr.number() == 0:
@@ -564,7 +575,7 @@ def generate_accumulation_instruction(expr, visitor):
         assignee = prim.Subscript(lp.TaggedVariable(temp, vsf.tag), pad)
         instruction(assignee=assignee,
                     expression=0,
-                    forced_iname_deps=frozenset(quadrature_inames(trial_leaf_element) + jacobian_inames),
+                    forced_iname_deps=frozenset(visitor.quadrature_inames() + jacobian_inames),
                     forced_iname_deps_is_final=True,
                     tags=frozenset(["quadvec", "gradvec"]),
                     )
@@ -579,9 +590,10 @@ def generate_accumulation_instruction(expr, visitor):
     # with the evaluation of the contribution at all quadrature points
     assignee = prim.Subscript(lp.TaggedVariable(temp, vsf.tag),
                               vsf.quadrature_index(sf, visitor))
+
     contrib_dep = instruction(assignee=assignee,
                               expression=expr,
-                              forced_iname_deps=frozenset(quadrature_inames(trial_leaf_element) + jacobian_inames),
+                              forced_iname_deps=frozenset(visitor.quadrature_inames() + jacobian_inames),
                               forced_iname_deps_is_final=True,
                               tags=frozenset({"quadvec", "sumfact_stage2"}).union(vectag),
                               depends_on=frozenset({deps}).union(frozenset({lp.match.Tagged("sumfact_stage1")})),
diff --git a/python/dune/codegen/sumfact/basis.py b/python/dune/codegen/sumfact/basis.py
index 049e0be9f5adfae7d48ef9adc40a951adcc55924..d8194af77400fc26148263d367fde33f25ed2480 100644
--- a/python/dune/codegen/sumfact/basis.py
+++ b/python/dune/codegen/sumfact/basis.py
@@ -5,7 +5,7 @@ multiplication with the test function is part of the sum factorization kernel.
 """
 import itertools
 
-from dune.codegen.generation import (backend,
+from dune.codegen.generation import (basis_mixin,
                                      domain,
                                      get_counted_variable,
                                      get_counter,
@@ -31,11 +31,11 @@ from dune.codegen.sumfact.permutation import (permute_backward,
                                               sumfact_cost_permutation_strategy,
                                               sumfact_quadrature_permutation_strategy,
                                               )
-from dune.codegen.sumfact.quadrature import quadrature_inames
 from dune.codegen.sumfact.switch import (get_facedir,
                                          get_facemod,
                                          )
 from dune.codegen.pdelab.argument import name_coefficientcontainer
+from dune.codegen.pdelab.basis import GenericBasisMixin
 from dune.codegen.pdelab.geometry import (local_dimension,
                                           world_dimension,
                                           )
@@ -57,6 +57,184 @@ from loopy.match import Writes
 import pymbolic.primitives as prim
 
 
+@basis_mixin("sumfact")
+class SumfactBasisMixin(GenericBasisMixin):
+    def lfs_inames(self, element, restriction, number=1):
+        from ufl import FiniteElement, TensorProductElement
+        assert isinstance(element, (FiniteElement, TensorProductElement))
+        if number == 0:
+            return ()
+        else:
+            dim = world_dimension()
+            return tuple(sumfact_lfs_iname(element, _basis_functions_per_direction(element)[d], d) for d in range(dim))
+
+    def implement_basis(self, element, restriction, number):
+        # If this is a test function we omit it!
+        if number == 0:
+            return 1
+
+        assert not isinstance(element, MixedElement)
+
+        name = "phi_{}".format(FEM_name_mangling(element))
+        name = restricted_name(name, restriction)
+        self.evaluate_basis(element, name, restriction)
+
+        return prim.Variable(name)
+
+    @kernel_cached
+    def evaluate_basis(self, element, name, restriction):
+        temporary_variable(name, shape=())
+        quad_inames = self.quadrature_inames()
+        inames = self.lfs_inames(element, restriction)
+        facedir = get_facedir(restriction)
+
+        # Collect the pairs of lfs/quad inames that are in use
+        # On facets, the normal direction of the facet is excluded
+        #
+        # If facedir is not none, the length of inames and quad_inames is
+        # different. For inames we want to skip the facedir direction, for
+        # quad_inames we need all entries. Thats the reason for the
+        # help_index.
+        basis_per_dir = _basis_functions_per_direction(element)
+        prod = ()
+        help_index = 0
+        for direction in range(len(inames)):
+            if direction != facedir:
+                prod = prod + (BasisTabulationMatrix(basis_size=basis_per_dir[direction],
+                                                     direction=direction).pymbolic((prim.Variable(quad_inames[help_index]), prim.Variable(inames[direction]))),)
+                help_index += 1
+
+        # Add the missing direction on facedirs by evaluating at either 0 or 1
+        if facedir is not None:
+            facemod = get_facemod(restriction)
+            prod = prod + (prim.Call(PolynomialLookup(name_polynomials(element.degree()), False),
+                                     (prim.Variable(inames[facedir]), facemod)),)
+
+        # Issue the product
+        instruction(expression=prim.Product(prod),
+                    assignee=prim.Variable(name),
+                    forced_iname_deps=frozenset(quad_inames + inames),
+                    forced_iname_deps_is_final=True,
+                    )
+
+    def implement_reference_gradient(self, element, restriction, number):
+        # If this is a test function, we omit it.
+        if number == 0:
+            self.indices = None
+            return 1
+
+        assert len(self.indices) == 1
+        index, = self.indices
+
+        # TODO: Change name?
+        name = "js_{}".format(FEM_name_mangling(element))
+        name = restricted_name(name, restriction)
+        name = "{}_{}".format(name, index)
+        self.evaluate_reference_gradient(element, name, restriction, index)
+
+        self.indices = None
+        return prim.Variable(name)
+
+    @kernel_cached
+    def evaluate_reference_gradient(self, element, name, restriction, index):
+        dim = world_dimension()
+        temporary_variable(name, shape=())
+        quad_inames = self.quadrature_inames()
+        inames = self.lfs_inames(element, restriction)
+        facedir = get_facedir(restriction)
+
+        # Map the direction to a quadrature iname
+        quadinamemapping = {}
+        i = 0
+        for d in range(local_dimension()):
+            if d == facedir:
+                i = i + 1
+            quadinamemapping[i] = quad_inames[d]
+            i = i + 1
+
+        prod = []
+        for i in range(dim):
+            if i != facedir:
+                tab = BasisTabulationMatrix(basis_size=_basis_functions_per_direction(element)[i],
+                                            direction=i,
+                                            derivative=index == i)
+                prod.append(tab.pymbolic((prim.Variable(quadinamemapping[i]), prim.Variable(inames[i]))))
+
+        if facedir is not None:
+            facemod = get_facemod(restriction)
+            prod.append(prim.Call(PolynomialLookup(name_polynomials(element.degree()), index == facedir),
+                                  (prim.Variable(inames[facedir]), facemod)),)
+
+        instruction(assignee=prim.Variable(name),
+                    expression=prim.Product(tuple(prod)),
+                    forced_iname_deps=frozenset(quad_inames + inames),
+                    forced_iname_deps_is_final=True,
+                    )
+
+    def implement_trialfunction(self, element, restriction, index):
+        return self.implement_coefficient(element, restriction, index, name_coefficientcontainer)
+
+    def implement_trialfunction_gradient(self, element, restriction, index):
+        derivative = self.indices[0]
+        return self.implement_coefficient(element, restriction, index, name_coefficientcontainer, derivative=derivative)
+
+    def implement_apply_function(self, element, restriction, index):
+        return self.implement_coefficient(element, restriction, index, name_applycontainer)
+
+    def implement_apply_function_gradient(self, element, restriction, index):
+        derivative = self.indices[0]
+        return self.implement_coefficient(element, restriction, index, name_applycontainer, derivative=derivative)
+
+    def implement_coefficient(self, element, restriction, index, coeff_func, derivative=None):
+        sub_element = element
+        if isinstance(element, MixedElement):
+            sub_element = element.extract_component(index)[1]
+        from ufl import FiniteElement
+        assert isinstance(sub_element, (FiniteElement, TensorProductElement))
+
+        # Basis functions per direction
+        basis_size = _basis_functions_per_direction(sub_element)
+
+        # Construct the matrix sequence for this sum factorization
+        matrix_sequence = construct_basis_matrix_sequence(derivative=derivative,
+                                                          facedir=get_facedir(restriction),
+                                                          facemod=get_facemod(restriction),
+                                                          basis_size=basis_size)
+
+        inp = LFSSumfactKernelInput(matrix_sequence=matrix_sequence,
+                                    coeff_func=coeff_func,
+                                    element=element,
+                                    element_index=index,
+                                    restriction=restriction,
+                                    )
+
+        position_priority = derivative
+        if position_priority is None:
+            position_priority = 3
+
+        sf = SumfactKernel(matrix_sequence=matrix_sequence,
+                           interface=inp,
+                           position_priority=position_priority,
+                           )
+
+        from dune.codegen.sumfact.vectorization import attach_vectorization_info
+        vsf = attach_vectorization_info(sf)
+
+        self.indices = None
+
+        # If this sum factorization kernel was not used in the dry run we
+        # just return 0
+        if vsf == 0:
+            return 0
+
+        # Add a sum factorization kernel that implements the evaluation of
+        # the basis functions at quadrature points (stage 1)
+        from dune.codegen.sumfact.realization import realize_sum_factorization_kernel
+        var, _ = realize_sum_factorization_kernel(vsf)
+
+        return prim.Subscript(var, vsf.quadrature_index(sf, self))
+
+
 class LFSSumfactKernelInput(SumfactKernelInterfaceBase, ImmutableRecord):
     def __init__(self,
                  matrix_sequence=None,
@@ -260,99 +438,6 @@ def _basis_functions_per_direction(element):
     return basis_size
 
 
-def pymbolic_coefficient_gradient(element, restriction, index, coeff_func, visitor):
-    sub_element = element
-    grad_index = visitor.indices[0]
-    if isinstance(element, MixedElement):
-        sub_element = element.extract_component(index)[1]
-
-    from ufl import FiniteElement
-    assert isinstance(sub_element, (FiniteElement, TensorProductElement))
-
-    # Number of basis functions per direction
-    basis_size = _basis_functions_per_direction(sub_element)
-
-    # Construct the matrix sequence for this sum factorization
-    matrix_sequence = construct_basis_matrix_sequence(derivative=grad_index,
-                                                      facedir=get_facedir(restriction),
-                                                      facemod=get_facemod(restriction),
-                                                      basis_size=basis_size,
-                                                      )
-
-    inp = LFSSumfactKernelInput(matrix_sequence=matrix_sequence,
-                                coeff_func=coeff_func,
-                                element=element,
-                                element_index=index,
-                                restriction=restriction,
-                                )
-
-    # The sum factorization kernel object gathering all relevant information
-    sf = SumfactKernel(matrix_sequence=matrix_sequence,
-                       position_priority=grad_index,
-                       interface=inp,
-                       )
-
-    from dune.codegen.sumfact.vectorization import attach_vectorization_info
-    vsf = attach_vectorization_info(sf)
-
-    # If this sum factorization kernel was not used in the dry run we
-    # just return 0
-    if vsf == 0:
-        visitor.indices = None
-        return 0
-
-    from dune.codegen.sumfact.realization import realize_sum_factorization_kernel
-    var, insn_dep = realize_sum_factorization_kernel(vsf)
-
-    visitor.indices = None
-    return prim.Subscript(var, vsf.quadrature_index(sf, visitor))
-
-
-def pymbolic_coefficient(element, restriction, index, coeff_func, visitor):
-    sub_element = element
-    if isinstance(element, MixedElement):
-        sub_element = element.extract_component(index)[1]
-    from ufl import FiniteElement
-    assert isinstance(sub_element, (FiniteElement, TensorProductElement))
-
-    # Basis functions per direction
-    basis_size = _basis_functions_per_direction(sub_element)
-
-    # Construct the matrix sequence for this sum factorization
-    matrix_sequence = construct_basis_matrix_sequence(facedir=get_facedir(restriction),
-                                                      facemod=get_facemod(restriction),
-                                                      basis_size=basis_size)
-
-    inp = LFSSumfactKernelInput(matrix_sequence=matrix_sequence,
-                                coeff_func=coeff_func,
-                                element=element,
-                                element_index=index,
-                                restriction=restriction,
-                                )
-
-    sf = SumfactKernel(matrix_sequence=matrix_sequence,
-                       interface=inp,
-                       position_priority=3,
-                       )
-
-    from dune.codegen.sumfact.vectorization import attach_vectorization_info
-    vsf = attach_vectorization_info(sf)
-
-    # If this sum factorization kernel was not used in the dry run we
-    # just return 0
-    if vsf == 0:
-        visitor.indices = None
-        return 0
-
-    # Add a sum factorization kernel that implements the evaluation of
-    # the basis functions at quadrature points (stage 1)
-    from dune.codegen.sumfact.realization import realize_sum_factorization_kernel
-    var, _ = realize_sum_factorization_kernel(vsf)
-
-    visitor.indices = None
-    return prim.Subscript(var, vsf.quadrature_index(sf, visitor))
-
-
 @iname
 def sumfact_lfs_iname(element, bound, dim):
     assert(isinstance(bound, int))
@@ -360,121 +445,3 @@ def sumfact_lfs_iname(element, bound, dim):
     name = "sumfac_lfs_{}_{}".format(FEM_name_mangling(element), dim)
     domain(name, bound)
     return name
-
-
-@backend(interface="lfs_inames", name="sumfact")
-def lfs_inames(element, restriction, number=1, context=''):
-    from ufl import FiniteElement, TensorProductElement
-    assert isinstance(element, (FiniteElement, TensorProductElement))
-    if number == 0:
-        return ()
-    else:
-        dim = world_dimension()
-        return tuple(sumfact_lfs_iname(element, _basis_functions_per_direction(element)[d], d) for d in range(dim))
-
-
-@backend(interface="evaluate_basis", name="sumfact")
-@kernel_cached
-def evaluate_basis(element, name, restriction):
-    temporary_variable(name, shape=())
-    quad_inames = quadrature_inames(element)
-    inames = lfs_inames(element, restriction)
-    facedir = get_facedir(restriction)
-
-    # Collect the pairs of lfs/quad inames that are in use
-    # On facets, the normal direction of the facet is excluded
-    #
-    # If facedir is not none, the length of inames and quad_inames is
-    # different. For inames we want to skip the facedir direction, for
-    # quad_inames we need all entries. Thats the reason for the
-    # help_index.
-    basis_per_dir = _basis_functions_per_direction(element)
-    prod = ()
-    help_index = 0
-    for direction in range(len(inames)):
-        if direction != facedir:
-            prod = prod + (BasisTabulationMatrix(basis_size=basis_per_dir[direction],
-                                                 direction=direction).pymbolic((prim.Variable(quad_inames[help_index]), prim.Variable(inames[direction]))),)
-            help_index += 1
-
-    # Add the missing direction on facedirs by evaluating at either 0 or 1
-    if facedir is not None:
-        facemod = get_facemod(restriction)
-        prod = prod + (prim.Call(PolynomialLookup(name_polynomials(element.degree()), False),
-                                 (prim.Variable(inames[facedir]), facemod)),)
-
-    # Issue the product
-    instruction(expression=prim.Product(prod),
-                assignee=prim.Variable(name),
-                forced_iname_deps=frozenset(quad_inames + inames),
-                forced_iname_deps_is_final=True,
-                )
-
-
-def pymbolic_basis(element, restriction, number):
-    # If this is a test function we omit it!
-    if number == 0:
-        return 1
-
-    assert not isinstance(element, MixedElement)
-
-    name = "phi_{}".format(FEM_name_mangling(element))
-    name = restricted_name(name, restriction)
-    evaluate_basis(element, name, restriction)
-
-    return prim.Variable(name)
-
-
-@backend(interface="evaluate_grad", name="sumfact")
-@kernel_cached
-def evaluate_reference_gradient(element, name, restriction, index):
-    dim = world_dimension()
-    temporary_variable(name, shape=())
-    quad_inames = quadrature_inames(element)
-    inames = lfs_inames(element, restriction)
-    facedir = get_facedir(restriction)
-
-    # Map the direction to a quadrature iname
-    quadinamemapping = {}
-    i = 0
-    for d in range(local_dimension()):
-        if d == facedir:
-            i = i + 1
-        quadinamemapping[i] = quad_inames[d]
-        i = i + 1
-
-    prod = []
-    for i in range(dim):
-        if i != facedir:
-            tab = BasisTabulationMatrix(basis_size=_basis_functions_per_direction(element)[i],
-                                        direction=i,
-                                        derivative=index == i)
-            prod.append(tab.pymbolic((prim.Variable(quadinamemapping[i]), prim.Variable(inames[i]))))
-
-    if facedir is not None:
-        facemod = get_facemod(restriction)
-        prod.append(prim.Call(PolynomialLookup(name_polynomials(element.degree()), index == facedir),
-                              (prim.Variable(inames[facedir]), facemod)),)
-
-    instruction(assignee=prim.Variable(name),
-                expression=prim.Product(tuple(prod)),
-                forced_iname_deps=frozenset(quad_inames + inames),
-                forced_iname_deps_is_final=True,
-                )
-
-
-def pymbolic_reference_gradient(element, restriction, number, indices):
-    # If this is a test function, we omit it.
-    if number == 0:
-        return 1, None
-
-    assert len(indices) == 1
-    index, = indices
-
-    # TODO: Change name?
-    name = "js_{}".format(FEM_name_mangling(element))
-    name = restricted_name(name, restriction)
-    name = "{}_{}".format(name, index)
-    evaluate_reference_gradient(element, name, restriction, index)
-
-    return prim.Variable(name), None
diff --git a/python/dune/codegen/sumfact/geometry.py b/python/dune/codegen/sumfact/geometry.py
index b95b348c43d5820aea9e7e6b2d32491f1fc03023..cef07354adb7dbfbbb85d42e72fc5e7e14d57a08 100644
--- a/python/dune/codegen/sumfact/geometry.py
+++ b/python/dune/codegen/sumfact/geometry.py
@@ -1,8 +1,8 @@
 """ Sum factorized geometry evaluations """
 
-from dune.codegen.generation import (backend,
-                                     class_member,
+from dune.codegen.generation import (class_member,
                                      domain,
+                                     geometry_mixin,
                                      get_counted_variable,
                                      iname,
                                      include_file,
@@ -17,10 +17,14 @@ from dune.codegen.generation import (backend,
                                      )
 from dune.codegen.loopy.flatten import flatten_index
 from dune.codegen.options import get_option
-from dune.codegen.pdelab.geometry import (local_dimension,
+from dune.codegen.pdelab.geometry import (enforce_boundary_restriction,
+                                          local_dimension,
                                           world_dimension,
                                           name_cell_geometry,
                                           name_geometry,
+                                          GenericPDELabGeometryMixin,
+                                          AxiparallelGeometryMixin,
+                                          EquidistantGeometryMixin,
                                           )
 from dune.codegen.pdelab.localoperator import (name_ansatz_gfs_constructor_param,
                                                lop_template_ansatz_gfs,
@@ -34,14 +38,9 @@ from dune.codegen.sumfact.permutation import (permute_backward,
                                               sumfact_cost_permutation_strategy,
                                               sumfact_quadrature_permutation_strategy,
                                               )
-from dune.codegen.sumfact.quadrature import (additional_inames,
-                                             default_quadrature_inames)
-from dune.codegen.sumfact.realization import (name_buffer_storage,
-                                              realize_sum_factorization_kernel,
-                                              )
+from dune.codegen.sumfact.quadrature import additional_inames
 from dune.codegen.sumfact.switch import get_facedir, get_facemod
 from dune.codegen.sumfact.symbolic import SumfactKernelInterfaceBase, SumfactKernel
-from dune.codegen.sumfact.vectorization import attach_vectorization_info
 from dune.codegen.tools import get_pymbolic_basename
 from dune.codegen.options import get_form_option, option_switch
 from dune.codegen.ufl.modified_terminals import Restriction
@@ -52,6 +51,274 @@ from loopy.match import Writes
 
 import pymbolic.primitives as prim
 import numpy as np
+import loopy as lp
+
+
+@geometry_mixin("sumfact_multilinear")
+class SumfactMultiLinearGeometryMixin(GenericPDELabGeometryMixin):
+    def _jacobian_inverse(self):
+        return "jit"
+
+    def jacobian_inverse(self, o):
+        # This differs from the generic one by not falling back to the
+        # transpose of the jacobian inverse.
+        restriction = enforce_boundary_restriction(self)
+
+        assert(len(self.indices) == 2)
+        i, j = self.indices
+        self.indices = None
+
+        name = restricted_name(self._jacobian_inverse(), restriction)
+        self.define_jacobian_inverse(name, restriction)
+
+        return prim.Subscript(prim.Variable(name), (i, j))
+
+    def _jacobian_determinant(self):
+        return "detjac"
+
+    def define_jacobian_determinant(self, o):
+        restriction = Restriction.NONE if self.measure == "cell" else Restriction.POSITIVE
+        self.define_jacobian_inverse(self._jacobian_inverse(), restriction)
+        return prim.Variable(self._jacobian_determinant())
+
+    def define_jacobian_inverse(self, name, restriction):
+        """Return jacobian inverse of the geometry mapping (of a cell)
+
+        At the moment this only works for geometry mappings of cells and not for
+        intersection. We only consider this case as it greatly simplifies code
+        generation for unstructured grids by making the grid edge consistent and
+        avoiding the face-twist problem.
+        """
+        # Calculate the jacobian of the geometry mapping using sum factorization
+        dim = world_dimension()
+        names_jacobian = []
+        for j in range(dim):
+            for i in range(dim):
+                name_jacobian = restricted_name("jacobian_geometry_{}{}".format(i, j), restriction)
+                temporary_variable(name_jacobian)
+                assignee = prim.Variable(name_jacobian)
+                expression = _name_jacobian(i, j, restriction, self)
+                inames = self.quadrature_inames() + additional_inames(self)
+                instruction(assignee=assignee,
+                            expression=expression,
+                            within_inames=frozenset(inames),
+                            depends_on=frozenset({lp.match.Tagged("sumfact_stage1")})
+                            )
+                names_jacobian.append(name_jacobian)
+
+                # The sum factorization kernels from the geometry evaluation of the
+                # jacobians will never appear in the expression for the input of
+                # stage 3. This way the SumfactCollectMapper will never see them
+                # and they will be marked as inactive. Here we explicitly mark the
+                # as used.
+                basis_sf_kernels(expression.aggregate)
+
+        # Calculate the inverse of the jacobian of the geometry mapping and the
+        # determinant by calling a c++ function. Note: The result will be column
+        # major -> fortran style.
+        name_detjac = self._jacobian_determinant()
+        temporary_variable(name_detjac, shape=())
+        ftags = "f,f"
+        temporary_variable(name, shape=(dim, dim), dim_tags=ftags, managed=True)
+        include_file('dune/codegen/sumfact/invertgeometry.hh', filetag='operatorfile')
+        code = "{} = invert_and_return_determinant({}, {});".format(name_detjac,
+                                                                    ", ".join(names_jacobian),
+                                                                    name)
+        silenced_warning("read_no_write({})".format(name_detjac))
+        inames = self.quadrature_inames() + additional_inames(self)
+        return instruction(code=code,
+                           assignees=frozenset([name, name_detjac]),
+                           read_variables=frozenset(names_jacobian),
+                           inames=frozenset(inames),
+                           )
+
+    def facet_jacobian_determinant(self, o):
+        """Absolute value of determinant of jacobian of facet geometry mapping
+
+        Calculate the absolute value of the determinant of the jacobian of the
+        geometry mapping from the reference cell of the intersection to the
+        intersection:
+
+        |\det(\nabla \mu_F)|
+
+        This is done using the surface metric tensor lemma from the lecture notes
+        of Jean-Luc Guermond:
+
+        |\det(\nabla \mu_F)| = \| \tilde{n} \| |\det(\nabla \mu_{T_s})|
+
+        Here \tilde{n} is the outer normal defined by:
+
+        \tilde{n} = (\nabla \mu_{T_s})^{-T} \hat{n}_s
+
+        where \hat{n}_s is the unit outer normal of the corresponding face of the
+        reference cell.
+        """
+        detjac = self.jacobian_determinant(None)
+        norm_of_outer_normal = normalize(self.outer_normal(), world_dimension())
+
+        return prim.Product((detjac, norm_of_outer_normal))
+
+    def outer_normal(self):
+        """ This is the *unnormalized* outer normal """
+        name = "outer_normal"
+        facedir_s = get_facedir(Restriction.POSITIVE)
+        facemod_s = get_facemod(Restriction.POSITIVE)
+
+        temporary_variable(name, shape=(world_dimension(),))
+        for i in range(world_dimension()):
+            assignee = prim.Subscript(prim.Variable(name), i)
+            self.indices = (facedir_s, i)
+            ji = self.jacobian_inverse(None)
+
+            # Note: 2*facemod_s-1 because of
+            # -1 if facemod_s = 0
+            # +1 if facemod_s = 1
+            expression = ji * (2 * facemod_s - 1)
+
+            inames = self.quadrature_inames() + additional_inames(self)
+            instruction(assignee=assignee,
+                        expression=expression,
+                        within_inames=frozenset(inames),
+                        )
+
+        return prim.Variable(name)
+
+    def facet_normal(self, o):
+        index, = self.indices
+        self.indices = None
+
+        outer_normal = self.outer_normal()
+        norm = normalize(outer_normal, world_dimension())
+
+        return prim.Subscript(outer_normal, (index,)) / norm
+
+    def spatial_coordinate(self, o):
+        """Calculate global coordinates of quadrature points for multilinear geometry mappings
+
+        The evalualation is done using sum factorization techniques.
+
+        On facets we use the geometry mapping of the self cell to get a global
+        evaluation of the quadrature points. Avoiding the geometry mapping of the
+        face itself greatly simplifies the generation of code for unstructured
+        grids. Instead of looking at all the possible embeddings of the reference
+        element of the face into the reference element of the cell we can make the
+        grid edge consistent and choose a suitable convention for the order of
+        directions in our sum factorization kernels.
+        """
+        assert len(self.indices) == 1
+        restriction = enforce_boundary_restriction(self)
+
+        # Generate sum factorization kernel and add vectorization info
+        matrix_sequence = construct_basis_matrix_sequence(facedir=get_facedir(restriction),
+                                                          facemod=get_facemod(restriction),
+                                                          basis_size=(2,) * world_dimension())
+        inp = GeoCornersInput(matrix_sequence=matrix_sequence,
+                              direction=self.indices[0],
+                              restriction=restriction,
+                              )
+        sf = SumfactKernel(matrix_sequence=matrix_sequence,
+                           interface=inp,
+                           )
+
+        from dune.codegen.sumfact.vectorization import attach_vectorization_info
+        vsf = attach_vectorization_info(sf)
+
+        # If this sum factorization kernel was not used in the dry run we
+        # just return 0
+        if vsf == 0:
+            self.indices = None
+            return 0
+
+        # Add a sum factorization kernel that implements the evaluation of
+        # the basis functions at quadrature points (stage 1)
+        from dune.codegen.sumfact.realization import realize_sum_factorization_kernel
+        var, _ = realize_sum_factorization_kernel(vsf)
+
+        # The results of this function is already the right component of the
+        # spatial coordinate and we don't need to index this in the visitor
+        self.indices = None
+
+        return prim.Subscript(var, vsf.quadrature_index(sf, self))
+
+
+@geometry_mixin("sumfact_axiparallel")
+class SumfactAxiParallelGeometryMixin(AxiparallelGeometryMixin):
+    def facet_normal(self, o):
+        i, = self.indices
+        self.indices = None
+        assert isinstance(i, int)
+
+        # Use facemod_s and facedir_s
+        if i == get_facedir(Restriction.POSITIVE):
+            if get_facemod(Restriction.POSITIVE):
+                return 1
+            else:
+                return -1
+        else:
+            return 0
+
+
+@geometry_mixin("sumfact_equidistant")
+class SumfactEqudistantGeometryMixin(EquidistantGeometryMixin, SumfactAxiParallelGeometryMixin):
+    def facet_jacobian_determinant(self, o):
+        name = "fdetjac"
+        self.define_facet_jacobian_determinant(name)
+        facedir = get_facedir(Restriction.POSITIVE)
+        globalarg(name, shape=(world_dimension(),))
+        return prim.Subscript(prim.Variable(name), (facedir,))
+
+    @class_member(classtag="operator")
+    def define_facet_jacobian_determinant(self, name):
+        self._define_facet_jacobian_determinant_eval(name)
+        gfst = lop_template_ansatz_gfs()
+        return "std::array<typename {}::Traits::GridView::template Codim<0>::Geometry::ctype, {}> {};".format(gfst, world_dimension(), name)
+
+    @kernel_cached(kernel="operator")
+    def _define_facet_jacobian_determinant_eval(self, name):
+        gfs = name_ansatz_gfs_constructor_param()
+        rft = lop_template_range_field()
+        code = ["{",
+                "  auto e = *({}.gridView().template begin<0>());".format(gfs),
+                "  int dir=0;",
+                "  for(auto is = {0}.gridView().ibegin(e); is != {0}.gridView().iend(e); ++(++is), ++dir)".format(gfs),
+                "    {}[dir] = is->geometry().integrationElement(Dune::FieldVector<{}, {}>());".format(name, rft, world_dimension() - 1),
+                "}",
+                ]
+        instruction(code="\n".join(code),
+                    kernel="operator",
+                    )
+
+    def spatial_coordinate(self, o):
+        index, = self.indices
+
+        # Urgh: *SOMEHOW* construct a face direction. This is not breaking in the unstructured
+        # case, because we do not enter this code path...
+        from dune.codegen.pdelab.restriction import Restriction
+        restriction = Restriction.NONE
+        if self.measure == "interior_facet":
+            restriction = Restriction.POSITIVE
+        from dune.codegen.sumfact.switch import get_facedir
+        face = get_facedir(restriction)
+
+        lowcorner = name_lowerleft_corner()
+        meshwidth = name_meshwidth()
+
+        # If we have to decide which boundary condition to take for this
+        # intersection we always take the boundary condition of the center
+        # of the intersection. We assume that there are no cells with more
+        # than one boundary condition.
+        if self.do_predicates:
+            x = 0.5
+        elif index == face:
+            x = 0
+        else:
+            iindex = index
+            if face is not None and index > face:
+                iindex = iindex - 1
+            x = self.quadrature_position(iindex)
+
+        self.indices = None
+        return prim.Subscript(prim.Variable(lowcorner), (index,)) + x * prim.Subscript(prim.Variable(meshwidth), (index,))
 
 
 @iname
@@ -154,6 +421,7 @@ class GeoCornersInput(SumfactKernelInterfaceBase, ImmutableRecord):
 
         # The array that will be passed to the sum factorization kernel
         # function should contain the coefficients in the cost permuted order!
+        from dune.codegen.sumfact.realization import name_buffer_storage
         name = "input_{}".format(sf.buffer)
         ftags = ",".join(["f"] * (sf.length + 1))
         temporary_variable(name,
@@ -205,56 +473,6 @@ class GeoCornersInput(SumfactKernelInterfaceBase, ImmutableRecord):
         return prim.Subscript(prim.Variable(inp), inames + vec_iname)
 
 
-@backend(interface="spatial_coordinate", name="default")
-def pymbolic_spatial_coordinate_multilinear(do_predicates, visitor):
-    """Calculate global coordinates of quadrature points for multilinear geometry mappings
-
-    The evalualation is done using sum factorization techniques.
-
-    On facets we use the geometry mapping of the self cell to get a global
-    evaluation of the quadrature points. Avoiding the geometry mapping of the
-    face itself greatly simplifies the generation of code for unstructured
-    grids. Instead of looking at all the possible embeddings of the reference
-    element of the face into the reference element of the cell we can make the
-    grid edge consistent and choose a suitable convention for the order of
-    directions in our sum factorization kernels.
-    """
-    assert len(visitor.indices) == 1
-
-    # Adjust restriction for boundary facets
-    restriction = visitor.restriction
-    if get_global_context_value("integral_type") == "exterior_facet":
-        restriction = 1
-
-    # Generate sum factorization kernel and add vectorization info
-    matrix_sequence = construct_basis_matrix_sequence(facedir=get_facedir(restriction),
-                                                      facemod=get_facemod(restriction),
-                                                      basis_size=(2,) * world_dimension())
-    inp = GeoCornersInput(matrix_sequence=matrix_sequence,
-                          direction=visitor.indices[0],
-                          restriction=restriction)
-    sf = SumfactKernel(matrix_sequence=matrix_sequence,
-                       interface=inp,
-                       )
-    vsf = attach_vectorization_info(sf)
-
-    # If this sum factorization kernel was not used in the dry run we
-    # just return 0
-    if vsf == 0:
-        visitor.indices = None
-        return 0
-
-    # Add a sum factorization kernel that implements the evaluation of
-    # the basis functions at quadrature points (stage 1)
-    var, _ = realize_sum_factorization_kernel(vsf)
-
-    # The results of this function is already the right component of the
-    # spatial coordinate and we don't need to index this in the visitor
-    visitor.indices = None
-
-    return prim.Subscript(var, vsf.quadrature_index(sf, visitor))
-
-
 @preamble
 def define_corner(name, low):
     geo = name_geometry()
@@ -298,299 +516,6 @@ def name_meshwidth():
     return name
 
 
-@backend(interface="spatial_coordinate", name="diagonal_transformation_matrix")
-def pymbolic_spatial_coordinate_axiparallel(do_predicates, visitor):
-    assert len(visitor.indices) == 1
-    index, = visitor.indices
-
-    # Urgh: *SOMEHOW* construct a face direction
-    from dune.codegen.pdelab.restriction import Restriction
-    restriction = Restriction.NONE
-    if get_global_context_value("integral_type") == "interior_facet":
-        restriction = Restriction.POSITIVE
-    from dune.codegen.sumfact.switch import get_facedir
-    face = get_facedir(restriction)
-
-    lowcorner = name_lowerleft_corner()
-    meshwidth = name_meshwidth()
-
-    # If we have to decide which boundary condition to take for this
-    # intersection we always take the boundary condition of the center
-    # of the intersection. We assume that there are no cells with more
-    # than one boundary condition.
-    if do_predicates:
-        x = 0.5
-    elif index == face:
-        x = 0
-    else:
-        iindex = index
-        if face is not None and index > face:
-            iindex = iindex - 1
-        from dune.codegen.sumfact.quadrature import pymbolic_indexed_quadrature_position
-        x = pymbolic_indexed_quadrature_position(iindex, visitor)
-
-    visitor.indices = None
-    return prim.Subscript(prim.Variable(lowcorner), (index,)) + x * prim.Subscript(prim.Variable(meshwidth), (index,))
-
-
-def define_outer_normal(name, visitor):
-    """Calculate outer normal (length not necessarily 1)
-
-    This will be used to calculate the unit outer normal and the determinant of
-    the Jacobian of the geometry mapping of the face.
-
-    The outer normal is calculated by multiplying the Jacobian inverse
-    transposed of the geometry mapping of the 'self'-cell with the unit outer
-    normal \hat{n_s} of the face corresponding to the intersetcion in the
-    reference element of the 'self'-cell:
-
-    n = (\nabla \mu_{T_s})^{-T} \hat{n_s}
-
-    Instead of setting up \hat{n_s} we just look at facedir_s and facemod_s.
-    """
-    facedir_s = get_facedir(Restriction.POSITIVE)
-    facemod_s = get_facemod(Restriction.POSITIVE)
-    temporary_variable(name, shape=(world_dimension(),))
-    for i in range(world_dimension()):
-        assignee = prim.Subscript(prim.Variable(name), i)
-        ji = pymbolic_jacobian_inverse(facedir_s, i, Restriction.POSITIVE, visitor)
-
-        # Note: 2*facemod_s-1 because of
-        # -1 if facemod_s = 0
-        # +1 if facemod_s = 1
-        expression = ji * (2 * facemod_s - 1)
-
-        inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-        instruction(assignee=assignee,
-                    expression=expression,
-                    within_inames=frozenset(inames),
-                    )
-
-
-def name_outer_normal(visitor):
-    name = "outer_normal"
-    define_outer_normal(name, visitor)
-    return name
-
-
-def pymbolic_outer_normal(visitor):
-    name = name_outer_normal(visitor)
-    return prim.Variable(name)
-
-
-def define_norm_of_outer_normal(name, visitor):
-    outer_normal = pymbolic_outer_normal(visitor)
-
-    # Calculate the norm of outer_normal
-    temporary_variable(name, shape=())
-    expression = 0
-    for i in range(world_dimension()):
-        expression = prim.Sum((expression, prim.Subscript(outer_normal, i) * prim.Subscript(outer_normal, i)))
-    expression = prim.Call(prim.Variable("sqrt"), (expression,))
-    inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-
-    # Apparently the call of sqrt shadows the dependency on outer normal. Add manually.
-    instruction(assignee=prim.Variable(name),
-                expression=expression,
-                depends_on=frozenset({Writes(get_pymbolic_basename(outer_normal))}),
-                within_inames=frozenset(inames),
-                )
-
-
-def name_norm_of_outer_normal(visitor):
-    name = "norm_of_outer_normal"
-    define_norm_of_outer_normal(name, visitor)
-    return name
-
-
-def pymbolic_norm_of_outer_normal(visitor):
-    name = name_norm_of_outer_normal(visitor)
-    return prim.Variable(name)
-
-
-def pymbolic_unit_outer_normal(visitor):
-    assert (len(visitor.indices) is 1)
-    index = visitor.indices[0]
-    assert isinstance(index, int)
-    if get_form_option("diagonal_transformation_matrix"):
-        # In this case we just return -1, 0 or 1 depending on the current
-        # facemod and facedir. We don't need to (and cannot) index these ints
-        # so we set the indices of the visitor to None.
-        visitor.indices = None
-
-        # Use facemod_s and facedir_s
-        from dune.codegen.sumfact.switch import get_facedir, get_facemod
-        if index == get_facedir(Restriction.POSITIVE):
-            if get_facemod(Restriction.POSITIVE):
-                return 1
-            else:
-                return -1
-        else:
-            return 0
-    else:
-        # In pymbolic_outer_normal we need to create the
-        # jacobian_inverse_transposed which doesn't play nicely with the
-        # visitor having indices. Maybe this should be improved. For now let's
-        # just set them to None and restore them afterwards.
-        visitor.indices = None
-        outer_normal = pymbolic_outer_normal(visitor)
-        norm = pymbolic_norm_of_outer_normal(visitor)
-        visitor.indices = (index,)
-
-        # Create unit_outer_normal by scaling outer_normal to length 1
-        name = "unit_outer_normal"
-        temporary_variable(name, shape=(world_dimension(),))
-        inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-        for i in range(world_dimension()):
-            instruction(assignee=prim.Subscript(prim.Variable(name), i),
-                        expression=prim.Subscript(outer_normal, i) / norm,
-                        within_inames=frozenset(inames),
-                        )
-
-        return prim.Variable(name)
-
-
-def pymbolic_unit_inner_normal(visitor_indices):
-    # Note: The implementation of this was adjusted for unstructured grids but
-    # it was not properly tested. If you need the unit inner normal just remove
-    # the assert(False) statement but make sure to verify that this does the
-    # right thing.
-    assert(False)
-
-    assert (len(visitor.indices) is 1)
-    index = visitor.indices[0]
-    assert isinstance(index, int)
-    if get_form_option("diagonal_transformation_matrix"):
-        # In this case we just return -1, 0 or 1 depending on the current
-        # facemod and facedir. We don't need to (and cannot) index these ints
-        # so we set the indices of the visitor to None.
-        visitor.indices = None
-        if index == get_facedir(Restriction.POSITIVE):
-            if get_facemod(Restriction.POSITIVE):
-                return -1
-            else:
-                return 1
-        else:
-            return 0
-    else:
-        # In pymbolic_outer_normal we need to create the
-        # jacobian_inverse_transposed which doesn't play nicely with the
-        # visitor having indices. Maybe this should be improved. For now let's
-        # just set them to None and restore them afterwards.
-        visitor.indices = None
-        outer_normal = pymbolic_outer_normal(visitor)
-        norm = pymbolic_norm_of_outer_normal(visitor)
-        visitor.indices = (index,)
-
-        # Create unit_outer_normal by scaling outer_normal to length 1
-        name = "unit_outer_normal"
-        temporary_variable(name, shape=(world_dimension(),))
-
-        inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-        for i in range(world_dimension()):
-            instruction(assignee=prim.Subscript(prim.Variable(name), i),
-                        expression=-1 * prim.Subscript(outer_normal, i) / norm,
-                        within_inames=frozenset(inames),
-                        )
-
-        return prim.Variable(name)
-
-
-@kernel_cached(kernel="operator")
-def define_constant_facet_jacobian_determinant_eval(name):
-    gfs = name_ansatz_gfs_constructor_param()
-    rft = lop_template_range_field()
-    code = ["{",
-            "  auto e = *({}.gridView().template begin<0>());".format(gfs),
-            "  int dir=0;",
-            "  for(auto is = {0}.gridView().ibegin(e); is != {0}.gridView().iend(e); ++(++is), ++dir)".format(gfs),
-            "    {}[dir] = is->geometry().integrationElement(Dune::FieldVector<{}, {}>());".format(name, rft, world_dimension() - 1),
-            "}",
-            ]
-    instruction(code="\n".join(code),
-                kernel="operator",
-                )
-
-
-@class_member(classtag="operator")
-def define_constant_facet_jacobian_determinant(name):
-    define_constant_facet_jacobian_determinant_eval(name)
-    gfst = lop_template_ansatz_gfs()
-    return "std::array<typename {}::Traits::GridView::template Codim<0>::Geometry::ctype, {}> {};".format(gfst, world_dimension(), name)
-
-
-def pymbolic_constant_facet_jacobian_determinant():
-    facedir = get_facedir(Restriction.POSITIVE)
-    assert isinstance(facedir, int)
-
-    name = "fdetjac"
-    define_constant_facet_jacobian_determinant(name)
-    globalarg(name, shape=(world_dimension(),))
-    return prim.Subscript(prim.Variable(name), (facedir,))
-
-
-def define_facet_jacobian_determinant(name, visitor):
-    """Absolute value of determinant of jacobian of facet geometry mapping
-
-    Calculate the absolute value of the determinant of the jacobian of the
-    geometry mapping from the reference cell of the intersection to the
-    intersection:
-
-    |\det(\nabla \mu_F)|
-
-    This is done using the surface metric tensor lemma from the lecture notes
-    of Jean-Luc Guermond:
-
-    |\det(\nabla \mu_F)| = \| \tilde{n} \| |\det(\nabla \mu_{T_s})|
-
-    Here \tilde{n} is the outer normal defined by:
-
-    \tilde{n} = (\nabla \mu_{T_s})^{-T} \hat{n}_s
-
-    where \hat{n}_s is the unit outer normal of the corresponding face of the
-    reference cell.
-    """
-    # Calculate norm of outer normal and determinant of jacobian of geometry
-    # mapping of the inside cell -> set restriction to 1 for boundary facets
-    if get_global_context_value("integral_type") == "exterior_facet":
-        assert visitor.restriction is 0
-        visitor.restriction = 1
-    norm_of_outer_normal = pymbolic_norm_of_outer_normal(visitor)
-    detjac = pymbolic_jacobian_determinant(visitor)
-    if get_global_context_value("integral_type") == "exterior_facet":
-        visitor.restriction = 0
-
-    # Multiply detjac and norm_of_outer_normal
-    temporary_variable(name, shape=())
-    inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-    instruction(assignee=prim.Variable(name),
-                expression=detjac * norm_of_outer_normal,
-                within_inames=frozenset(inames),
-                )
-
-
-def name_facet_jacobian_determinant(visitor):
-    name = "fdetjac"
-    define_facet_jacobian_determinant(name, visitor)
-    return name
-
-
-def pymbolic_facet_jacobian_determinant(visitor):
-    if get_form_option("constant_transformation_matrix"):
-        return pymbolic_constant_facet_jacobian_determinant()
-    else:
-        name = name_facet_jacobian_determinant(visitor)
-        return prim.Variable(name)
-
-
-def pymbolic_facet_area(visitor):
-    if get_form_option("constant_transformation_matrix"):
-        return pymbolic_facet_jacobian_determinant(visitor)
-    else:
-        from dune.codegen.pdelab.geometry import pymbolic_facet_area as nonconstant_facet_area
-        return nonconstant_facet_area()
-
-
 def _name_jacobian(i, j, restriction, visitor):
     """Return the (i, j) component of the jacobian of the geometry mapping
 
@@ -615,6 +540,7 @@ def _name_jacobian(i, j, restriction, visitor):
     sf = SumfactKernel(matrix_sequence=matrix_sequence,
                        interface=inp,
                        )
+    from dune.codegen.sumfact.vectorization import attach_vectorization_info
     vsf = attach_vectorization_info(sf)
 
     # If this sum factorization kernel was not used in the dry run we
@@ -625,99 +551,12 @@ def _name_jacobian(i, j, restriction, visitor):
 
     # Add a sum factorization kernel that implements the evaluation of
     # the basis functions at quadrature points (stage 1)
+    from dune.codegen.sumfact.realization import realize_sum_factorization_kernel
     var, _ = realize_sum_factorization_kernel(vsf)
 
     assert(visitor.indices is None)
     return prim.Subscript(var, vsf.quadrature_index(sf, visitor))
 
 
-def define_jacobian_inverse(name, restriction, visitor):
-    """Return jacobian inverse of the geometry mapping (of a cell)
-
-    At the moment this only works for geometry mappings of cells and not for
-    intersection. We only consider this case as it greatly simplifies code
-    generation for unstructured grids by making the grid edge consistent and
-    avoiding the face-twist problem.
-    """
-    # Calculate the jacobian of the geometry mapping using sum factorization
-    dim = world_dimension()
-    names_jacobian = []
-    for j in range(dim):
-        for i in range(dim):
-            name_jacobian = restricted_name("jacobian_geometry_{}{}".format(i, j), restriction)
-            temporary_variable(name_jacobian)
-            assignee = prim.Variable(name_jacobian)
-            expression = _name_jacobian(i, j, restriction, visitor)
-            inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-            instruction(assignee=assignee,
-                        expression=expression,
-                        within_inames=frozenset(inames),
-                        )
-            names_jacobian.append(name_jacobian)
-
-            # The sum factorization kernels from the geometry evaluation of the
-            # jacobians will never appear in the expression for the input of
-            # stage 3. This way the SumfactCollectMapper will never see them
-            # and they will be marked as inactive. Here we explicitly mark the
-            # as used.
-            basis_sf_kernels(expression.aggregate)
-
-    # Calculate the inverse of the jacobian of the geometry mapping and the
-    # determinant by calling a c++ function. Note: The result will be column
-    # major -> fortran style.
-    name_detjac = name_jacobian_determinant(visitor)
-    temporary_variable(name_detjac, shape=())
-    ftags = ",".join(["f"] * 2)
-    temporary_variable(name, shape=(dim, dim), dim_tags=ftags, managed=True)
-    include_file('dune/codegen/sumfact/invertgeometry.hh', filetag='operatorfile')
-    code = "{} = invert_and_return_determinant({}, {});".format(name_detjac,
-                                                                ", ".join(names_jacobian),
-                                                                name)
-    silenced_warning("read_no_write({})".format(name_detjac))
-    inames = default_quadrature_inames(visitor) + additional_inames(visitor)
-    return instruction(code=code,
-                       assignees=frozenset([name, name_detjac]),
-                       read_variables=frozenset(names_jacobian),
-                       inames=frozenset(inames),
-                       )
-
-
-def name_jacobian_inverse(restriction, visitor):
-    name = restricted_name("jacobian_inverse", restriction)
-    define_jacobian_inverse(name, restriction, visitor)
-    return name
-
-
-def pymbolic_jacobian_inverse(i, j, restriction, visitor):
-    # Switch between implementations without using the backend mechanism. In
-    # the case of constant_transformation_matrix we want to use the pdelab
-    # branch, otherwise we want to go into the sumfact implementation instead
-    # of pdelab.
-    if get_form_option("constant_transformation_matrix"):
-        from dune.codegen.pdelab.geometry import name_constant_jacobian_inverse_transposed
-        name = name_constant_jacobian_inverse_transposed(restriction)
-        # Return jacobian inverse -> (j, i) instead of (i, j)
-        return prim.Subscript(prim.Variable(name), (j, i))
-    else:
-        name = name_jacobian_inverse(restriction, visitor)
-        return prim.Subscript(prim.Variable(name), (i, j))
-
-
-def name_jacobian_determinant(visitor):
-    name = "detjac"
-    return name
-
-
-def pymbolic_jacobian_determinant(visitor):
-    if get_form_option("constant_transformation_matrix"):
-        from dune.codegen.pdelab.geometry import pymbolic_jacobian_determinant
-        return pymbolic_jacobian_determinant()
-    else:
-        # The calculation of the jacobian determinant happens in this function
-        if visitor.measure == 'cell':
-            restriction = 0
-        else:
-            restriction = 1
-        name_jacobian_inverse(restriction, visitor)
-
-        return prim.Variable(name_jacobian_determinant(visitor))
+def normalize(expr, dim):
+    return prim.Call(prim.Variable("sqrt"), (prim.Sum(tuple(expr[i] * expr[i] for i in range(dim))),))
diff --git a/python/dune/codegen/sumfact/quadrature.py b/python/dune/codegen/sumfact/quadrature.py
index eedd85da732c60fbe3729451ddbe87ee6319f39c..91e99c4c3c0cd333d6e64353ed06ac0c129a7e05 100644
--- a/python/dune/codegen/sumfact/quadrature.py
+++ b/python/dune/codegen/sumfact/quadrature.py
@@ -1,5 +1,4 @@
-from dune.codegen.generation import (backend,
-                                     domain,
+from dune.codegen.generation import (domain,
                                      function_mangler,
                                      get_global_context_value,
                                      globalarg,
@@ -8,6 +7,7 @@ from dune.codegen.generation import (backend,
                                      kernel_cached,
                                      loopy_class_member,
                                      preamble,
+                                     quadrature_mixin,
                                      temporary_variable,
                                      )
 from dune.codegen.sumfact.switch import get_facedir
@@ -20,6 +20,7 @@ from dune.codegen.pdelab.argument import name_accumulation_variable
 from dune.codegen.pdelab.geometry import (local_dimension,
                                           world_dimension,
                                           )
+from dune.codegen.pdelab.quadrature import GenericQuadratureMixin
 from dune.codegen.options import get_form_option
 from dune.codegen.sumfact.switch import get_facedir
 from dune.codegen.loopy.target import dtype_floatingpoint
@@ -39,6 +40,85 @@ import loopy as lp
 import numpy as np
 
 
+@quadrature_mixin("sumfact")
+class SumfactQuadratureMixin(GenericQuadratureMixin):
+    def quadrature_inames(self):
+        info = self.current_info[1]
+        if info is not None:
+            info = info.element
+
+        return _quadrature_inames(info)
+
+    def quadrature_weight(self, o):
+        # Quadrature points per (local) direction
+        dim = local_dimension()
+        local_qps_per_dir = local_quadrature_points_per_direction()
+        local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir))
+        name = "quad_weights_dim{}_num{}".format(dim, local_qps_per_dir_str)
+
+        # Add a class member
+        loopy_class_member(name,
+                           shape=local_qps_per_dir,
+                           classtag="operator",
+                           dim_tags=",".join(["f"] * dim),
+                           managed=True,
+                           potentially_vectorized=True,
+                           )
+
+        # Precompute it in the constructor
+        inames = constructor_quadrature_inames(dim)
+        assignee = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames))
+        expression = prim.Product(tuple(Subscript(Variable(name_oned_quadrature_weights(local_qps_per_dir[index])),
+                                                  (prim.Variable(iname),)) for index, iname in enumerate(inames)))
+        instruction(assignee=assignee,
+                    expression=expression,
+                    within_inames=frozenset(inames),
+                    kernel="operator",
+                    )
+
+        self.quadrature_inames()
+        ret = prim.Subscript(lp.symbolic.TaggedVariable(name, "operator_precomputed"),
+                             tuple(prim.Variable(i) for i in self.quadrature_inames()))
+
+        # Multiply the accumulation weight
+        baseweight = 1.0
+        if get_form_option("fastdg"):
+            baseweight = prim.Call(BaseWeight(name_accumulation_variable()), ())
+
+        return prim.Product((baseweight, ret))
+
+    def quadrature_position(self, index=None):
+        assert index is not None
+        dim = local_dimension()
+        qps_per_dir = quadrature_points_per_direction()
+        local_qps_per_dir = local_quadrature_points_per_direction()
+        local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir))
+        name = "quad_points_dim{}_num{}_localdir{}".format(dim, local_qps_per_dir_str, index)
+
+        loopy_class_member(name,
+                           shape=local_qps_per_dir,
+                           classtag="operator",
+                           dim_tags=",".join(["f"] * dim),
+                           managed=True,
+                           potentially_vectorized=True,
+                           )
+
+        # Precompute it in the constructor
+        inames = constructor_quadrature_inames(dim)
+        assignee = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames))
+        expression = Subscript(Variable(name_oned_quadrature_points(local_qps_per_dir[index])),
+                               (prim.Variable(inames[index])))
+        instruction(assignee=assignee,
+                    expression=expression,
+                    within_inames=frozenset(inames),
+                    kernel="operator",
+                    )
+
+        return prim.Subscript(lp.symbolic.TaggedVariable(name, "operator_precomputed"),
+                              tuple(prim.Variable(i) for i in self.quadrature_inames())
+                              )
+
+
 class BaseWeight(FunctionIdentifier):
     def __init__(self, accumobj):
         self.accumobj = accumobj
@@ -60,28 +140,8 @@ def base_weight_function_mangler(target, func, dtypes):
         return CallMangleInfo(func.name, (NumpyType(dtype_floatingpoint()),), ())
 
 
-def pymbolic_base_weight():
-    """ This is the base weight that should be multiplied to the quadrature
-    weight. With the fast DG assembler this will handle the weighting of the
-    time discretization scheme.
-    """
-    if get_form_option("fastdg"):
-        return prim.Call(BaseWeight(name_accumulation_variable()), ())
-    else:
-        return 1.0
-
-
-def default_quadrature_inames(visitor):
-    info = visitor.current_info[1]
-    if info is None:
-        element = None
-    else:
-        element = info.element
-    return quadrature_inames(element)
-
-
 @kernel_cached
-def quadrature_inames(element):
+def _quadrature_inames(element):
     if element is None:
         names = tuple("quad_{}".format(d) for d in range(local_dimension()))
     else:
@@ -109,157 +169,6 @@ def constructor_quadrature_inames(dim):
     return tuple(constructor_quad_iname(name, d, local_qps_per_dir[d]) for d in range(local_dimension()))
 
 
-def define_recursive_quadrature_weight(visitor, name, direction):
-    info = visitor.current_info[1]
-    if info is None:
-        element = None
-    else:
-        element = info.element
-    iname = quadrature_inames(element)[direction]
-    temporary_variable(name, shape=(), shape_impl=())
-
-    qps_per_dir = quadrature_points_per_direction()
-    qp_bound = qps_per_dir[direction]
-    instruction(expression=Product((recursive_quadrature_weight(direction + 1),
-                                    Subscript(Variable(name_oned_quadrature_weights(qp_bound)),
-                                              (Variable(iname),)
-                                              ))
-                                   ),
-                assignee=Variable(name),
-                forced_iname_deps=frozenset(quadrature_inames()[direction:]),
-                forced_iname_deps_is_final=True,
-                tags=frozenset({"quad"}),
-                )
-
-
-def recursive_quadrature_weight(visitor, direction=0):
-    if direction == local_dimension():
-        return pymbolic_base_weight()
-    else:
-        name = 'weight_{}'.format(direction)
-        define_recursive_quadrature_weight(visitor, name, direction)
-        return Variable(name)
-
-
-def quadrature_weight(visitor):
-    # Return non-precomputed version
-    if not get_form_option("precompute_quadrature_info"):
-        return recursive_quadrature_weight(visitor)
-
-    # Quadrature points per (local) direction
-    dim = local_dimension()
-    local_qps_per_dir = local_quadrature_points_per_direction()
-    local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir))
-    name = "quad_weights_dim{}_num{}".format(dim, local_qps_per_dir_str)
-
-    # Add a class member
-    loopy_class_member(name,
-                       shape=local_qps_per_dir,
-                       classtag="operator",
-                       dim_tags=",".join(["f"] * dim),
-                       managed=True,
-                       potentially_vectorized=True,
-                       )
-
-    # Precompute it in the constructor
-    inames = constructor_quadrature_inames(dim)
-    assignee = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames))
-    expression = prim.Product(tuple(Subscript(Variable(name_oned_quadrature_weights(local_qps_per_dir[index])),
-                                              (prim.Variable(iname),)) for index, iname in enumerate(inames)))
-    instruction(assignee=assignee,
-                expression=expression,
-                within_inames=frozenset(inames),
-                kernel="operator",
-                )
-
-    info = visitor.current_info[1]
-    if info is None:
-        element = None
-    else:
-        element = info.element
-
-    ret = prim.Subscript(lp.symbolic.TaggedVariable(name, "operator_precomputed"),
-                         tuple(prim.Variable(i) for i in quadrature_inames(element)))
-
-    return prim.Product((pymbolic_base_weight(), ret))
-
-
-def define_quadrature_position(name, local_index):
-    # TODO: This whole function looks quite suspicious:
-    # - qps_per_dir not used
-    # - probably rename index to local_index
-    #
-    # This whole function is probably not used anymore since we use sum
-    # factorization for evaluation of global coordinate, jacobian inverse
-    # transposed and determinant of the jacobian of the geometry mapping.
-    #
-    # Needs to be fixed. Or removed. Added to my to do list ;)
-    assert False
-
-    qps_per_dir = quadrature_points_per_direction()
-    local_qps_per_dir = local_quadrature_points_per_direction()
-    qp_bound = local_qps_per_dir[index]
-    expression = Subscript(Variable(name_oned_quadrature_points(qp_bound)),
-                           (Variable(quadrature_inames()[index]),))
-    instruction(expression=expression,
-                assignee=Subscript(Variable(name), (index,)),
-                forced_iname_deps=frozenset(quadrature_inames(element)),
-                forced_iname_deps_is_final=True,
-                tags=frozenset({"quad"}),
-                )
-
-
-def pymbolic_indexed_quadrature_position(local_index, visitor):
-    """Return the value of the indexed local quadrature points
-
-    Arguments:
-    ----------
-
-    local_index: Local index to the quadrature point. Local means that on the
-        face of a 3D Element the index always will be 0 or 1 but never 2.
-    visitor: UFL tree visitor
-    """
-    # Return the non-precomputed version
-    if not get_form_option("precompute_quadrature_info"):
-        name = 'pos'
-        temporary_variable(name, shape=(local_dimension(),), shape_impl=("fv",))
-        define_quadrature_position(name, local_index)
-        return prim.Subscript(prim.Variable(name), (local_index,))
-
-    assert isinstance(local_index, int)
-    dim = local_dimension()
-    qps_per_dir = quadrature_points_per_direction()
-    local_qps_per_dir = local_quadrature_points_per_direction()
-    local_qps_per_dir_str = '_'.join(map(str, local_qps_per_dir))
-    name = "quad_points_dim{}_num{}_localdir{}".format(dim, local_qps_per_dir_str, local_index)
-
-    loopy_class_member(name,
-                       shape=local_qps_per_dir,
-                       classtag="operator",
-                       dim_tags=",".join(["f"] * dim),
-                       managed=True,
-                       potentially_vectorized=True,
-                       )
-
-    # Precompute it in the constructor
-    inames = constructor_quadrature_inames(dim)
-    assignee = prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in inames))
-    expression = Subscript(Variable(name_oned_quadrature_points(local_qps_per_dir[local_index])),
-                           (prim.Variable(inames[local_index])))
-    instruction(assignee=assignee,
-                expression=expression,
-                within_inames=frozenset(inames),
-                kernel="operator",
-                )
-
-    info = visitor.current_info[1]
-    if info is None:
-        element = None
-    else:
-        element = info.element
-    return prim.Subscript(lp.symbolic.TaggedVariable(name, "operator_precomputed"), tuple(prim.Variable(i) for i in quadrature_inames(element)))
-
-
 def additional_inames(visitor):
     """Return inames for iterating over ansatz space in the case of jacobians
     """
@@ -274,8 +183,7 @@ def additional_inames(visitor):
         restriction = Restriction.NONE
         if get_global_context_value("integral_type") == "interior_facet":
             restriction = Restriction.POSITIVE
-        from dune.codegen.sumfact.basis import lfs_inames
-        lfs_inames = lfs_inames(element, restriction)
+        lfs_inames = visitor.lfs_inames(element, restriction)
         return lfs_inames
     else:
         return ()
diff --git a/python/dune/codegen/sumfact/realization.py b/python/dune/codegen/sumfact/realization.py
index f14599e5e7009997da267bfd16402d1371e6a6ec..8b585d4ce9a91c8565cb9e6b2b9b495b3734b2e6 100644
--- a/python/dune/codegen/sumfact/realization.py
+++ b/python/dune/codegen/sumfact/realization.py
@@ -17,7 +17,6 @@ from dune.codegen.generation import (barrier,
                                      transform,
                                      )
 from dune.codegen.loopy.flatten import flatten_index
-from dune.codegen.pdelab.argument import pymbolic_coefficient
 from dune.codegen.pdelab.basis import shape_as_pymbolic
 from dune.codegen.pdelab.geometry import world_dimension
 from dune.codegen.options import (get_form_option,
diff --git a/python/dune/codegen/sumfact/switch.py b/python/dune/codegen/sumfact/switch.py
index d18ab344b9c1c78d08c85185219a6b2facf95fc7..67cb9df358112e456b4ef9250c3602f1f9aacdec 100644
--- a/python/dune/codegen/sumfact/switch.py
+++ b/python/dune/codegen/sumfact/switch.py
@@ -2,13 +2,11 @@
 
 import csv
 
-from dune.codegen.generation import (backend,
-                                     get_backend,
-                                     get_global_context_value,
+from dune.codegen.generation import (get_global_context_value,
                                      global_context,
                                      )
 from dune.codegen.pdelab.geometry import world_dimension
-from dune.codegen.pdelab.localoperator import generate_kernel
+from dune.codegen.pdelab.localoperator import generate_kernel, generate_kernels_per_integral
 from dune.codegen.pdelab.signatures import (assembly_routine_args,
                                             assembly_routine_signature,
                                             kernel_name,
@@ -17,8 +15,7 @@ from dune.codegen.options import get_form_option, get_option, set_form_option
 from dune.codegen.cgen.clazz import ClassMember
 
 
-@backend(interface="generate_kernels_per_integral", name="sumfact")
-def generate_kernels_per_integral(integrals):
+def sumfact_generate_kernels_per_integral(integrals):
     dim = world_dimension()
     measure = get_global_context_value("integral_type")
 
@@ -29,7 +26,7 @@ def generate_kernels_per_integral(integrals):
         # Maybe skip sum factorization on boundary integrals
         if not get_form_option("sumfact_on_boundary"):
             set_form_option("sumfact", False)
-            for k in get_backend(interface="generate_kernels_per_integral")(integrals):
+            for k in generate_kernels_per_integral(integrals):
                 yield k
             set_form_option("sumfact", True)
             return
@@ -64,7 +61,7 @@ def get_kernel_name(facedir_s=None, facemod_s=None, facedir_n=None, facemod_n=No
 
 def decide_if_kernel_is_necessary(facedir_s, facemod_s, facedir_n, facemod_n):
     # If we are not using YaspGrid, all variants need to be realized
-    if not get_form_option("diagonal_transformation_matrix"):
+    if get_form_option("geometry_mixins") == "sumfact_multilinear":
         # Reduce the variability according to grid info file
         if get_option("grid_info") is not None:
             filename = get_option("grid_info")
diff --git a/python/dune/codegen/sumfact/symbolic.py b/python/dune/codegen/sumfact/symbolic.py
index b8b5b857728ea063edec3812b2132d210ffe93b3..2768ece8993b01565a2eeacb4311fa976174c236 100644
--- a/python/dune/codegen/sumfact/symbolic.py
+++ b/python/dune/codegen/sumfact/symbolic.py
@@ -14,7 +14,6 @@ from dune.codegen.sumfact.permutation import (flop_cost,
                                               sumfact_cost_permutation_strategy,
                                               sumfact_quadrature_permutation_strategy,
                                               )
-from dune.codegen.sumfact.quadrature import quadrature_inames
 from dune.codegen.sumfact.tabulation import BasisTabulationMatrixBase, BasisTabulationMatrixArray
 from dune.codegen.loopy.target import dtype_floatingpoint, type_floatingpoint
 from dune.codegen.loopy.vcl import ExplicitVCLCast, VCLLowerUpperLoad
@@ -713,16 +712,7 @@ class SumfactKernel(SumfactKernelBase, ImmutableRecord, prim.Variable):
         return tuple(mat.quadrature_size for mat in self.matrix_sequence_quadrature_permuted)
 
     def quadrature_index(self, sf, visitor):
-        if visitor.current_info[1] is None:
-            element = None
-            element_index = 0
-        else:
-            element = visitor.current_info[1].element
-            element_index = visitor.current_info[1].element_index
-            if isinstance(element, MixedElement):
-                element = element.extract_component(element_index)[1]
-
-        quad_inames = quadrature_inames(element)
+        quad_inames = visitor.quadrature_inames()
         if len(self.matrix_sequence_quadrature_permuted) == local_dimension():
             return tuple(prim.Variable(i) for i in quad_inames)
 
@@ -1039,16 +1029,7 @@ class VectorizedSumfactKernel(SumfactKernelBase, ImmutableRecord, prim.Variable)
         return 0
 
     def _quadrature_index(self, sf, visitor):
-        if visitor.current_info[1] is None:
-            element = None
-            element_index = 0
-        else:
-            element = visitor.current_info[1].element
-            element_index = visitor.current_info[1].element_index
-            if isinstance(element, MixedElement):
-                element = element.extract_component(element_index)[1]
-
-        quad_inames = quadrature_inames(element)
+        quad_inames = visitor.quadrature_inames()
         index = []
 
         if len(self.matrix_sequence_quadrature_permuted) == local_dimension():
@@ -1077,16 +1058,8 @@ class VectorizedSumfactKernel(SumfactKernelBase, ImmutableRecord, prim.Variable)
         return tuple(index)
 
     def vec_index(self, sf, visitor):
-        if visitor.current_info[1] is None:
-            element = None
-            element_index = 0
-        else:
-            element = visitor.current_info[1].element
-            element_index = visitor.current_info[1].element_index
-            if isinstance(element, MixedElement):
-                element = element.extract_component(element_index)[1]
+        quad_inames = visitor.quadrature_inames()
 
-        quad_inames = quadrature_inames(element)
         sliced = 0
         if len(self.matrix_sequence_quadrature_permuted) == local_dimension():
             for d in range(local_dimension()):
diff --git a/python/dune/codegen/sumfact/vectorization.py b/python/dune/codegen/sumfact/vectorization.py
index e7f36a8f0a741c8e4d262762249c2d005a33ffac..e753652b10b3ac9b1765ee071bce1ccebd15f6b1 100644
--- a/python/dune/codegen/sumfact/vectorization.py
+++ b/python/dune/codegen/sumfact/vectorization.py
@@ -7,12 +7,11 @@ import logging
 from dune.codegen.loopy.target import dtype_floatingpoint
 from dune.codegen.loopy.vcl import get_vcl_type_size
 from dune.codegen.loopy.symbolic import SumfactKernel, VectorizedSumfactKernel
-from dune.codegen.generation import (backend,
-                                     generator_factory,
-                                     get_backend,
+from dune.codegen.generation import (generator_factory,
                                      get_counted_variable,
                                      get_global_context_value,
                                      kernel_cached,
+                                     retrieve_cache_items,
                                      )
 from dune.codegen.pdelab.restriction import (Restriction,
                                              restricted_name,
@@ -151,7 +150,7 @@ class PrimitiveApproximateOpcounter(FlopCounter):
         raise NotImplementedError("The class {} should implement a symbolic flopcounter.".format(type(expr)))
 
 
-@kernel_cached
+@generator_factory(item_tags=("opcounts",), context_tags="kernel")
 def store_operation_count(expr, count):
     return count
 
@@ -185,7 +184,7 @@ def quadrature_penalized_strategy_cost(strat_tuple):
 
     # Determine the number of floating point operations per quadrature point.
     # This flop counting is a very crude approximation, but that is totally sufficient here.
-    ops_per_qp = sum(i.value for i in store_operation_count._memoize_cache.values())
+    ops_per_qp = sum(i for i in retrieve_cache_items("opcounts"))
 
     # Do the actual scaling.
     return float((sf_flops + ops_per_qp * num_qp_new) / (sf_flops + ops_per_qp * num_qp_old)) * cost
diff --git a/python/dune/codegen/ufl/visitor.py b/python/dune/codegen/ufl/visitor.py
index 996024cea6ec966cbb8f5463949c7bb84b2560b2..e55232d26839abb2a8866dc2a57af715156237a7 100644
--- a/python/dune/codegen/ufl/visitor.py
+++ b/python/dune/codegen/ufl/visitor.py
@@ -42,9 +42,7 @@ import numpy as np
 
 
 class UFL2LoopyVisitor(ModifiedTerminalTracker):
-    def __init__(self, interface, measure, subdomain_id):
-        self.interface = interface
-        self.interface.visitor = self
+    def __init__(self, measure, subdomain_id):
         self.measure = measure
         self.subdomain_id = subdomain_id
 
@@ -56,14 +54,14 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
         return self._call(o, do_predicates)
 
     def accumulate(self, o):
-        for info in self.interface.list_accumulation_infos(o, self):
+        for info in self.list_accumulation_infos(o):
             self.current_info = info
             expr = self._call(o, False)
             if expr != 0:
                 if get_form_option("simplify"):
                     from dune.codegen.sympy import simplify_pymbolic_expression
                     expr = simplify_pymbolic_expression(expr)
-                self.interface.generate_accumulation_instruction(expr, self)
+                self.generate_accumulation_instruction(expr)
 
     def _call(self, o, do_predicates):
         # Reset state variables
@@ -94,9 +92,9 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
     #
 
     def argument(self, o):
-        self.interface.initialize_function_spaces(o, self)
+        self.initialize_function_spaces(o)
         # Update the information on where to accumulate this
-        info = self.interface.get_accumulation_info(o, self)
+        info = self.get_accumulation_info(o)
         if o.number() == 0:
             if info != self.current_info[0]:
                 self.indices = None
@@ -131,9 +129,9 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
             raise CodegenUFLError("Gradients should have been transformed to reference gradients!!!")
 
         if self.reference_grad:
-            return self.interface.pymbolic_reference_gradient(leaf_element, restriction, o.number())
+            return self.implement_reference_gradient(leaf_element, restriction, o.number())
         else:
-            return self.interface.pymbolic_basis(leaf_element, restriction, o.number())
+            return self.implement_basis(leaf_element, restriction, o.number())
 
     def coefficient(self, o):
         # Correct the restriction on boundary integrals
@@ -143,7 +141,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
 
         # Do something different for trial function and coefficients from jacobian apply
         if o.count() == 0 or o.count() == 1:
-            self.interface.initialize_function_spaces(o, self)
+            self.initialize_function_spaces(o)
 
             index = None
             if isinstance(o.ufl_element(), MixedElement):
@@ -158,14 +156,14 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
 
             if self.reference_grad:
                 if o.count() == 0:
-                    return self.interface.pymbolic_trialfunction_gradient(o.ufl_element(), restriction, index)
+                    return self.implement_trialfunction_gradient(o.ufl_element(), restriction, index)
                 else:
-                    return self.interface.pymbolic_apply_function_gradient(o.ufl_element(), restriction, index)
+                    return self.implement_apply_function_gradient(o.ufl_element(), restriction, index)
             else:
                 if o.count() == 0:
-                    return self.interface.pymbolic_trialfunction(o.ufl_element(), restriction, index)
+                    return self.implement_trialfunction(o.ufl_element(), restriction, index)
                 else:
-                    return self.interface.pymbolic_apply_function(o.ufl_element(), restriction, index)
+                    return self.implement_apply_function(o.ufl_element(), restriction, index)
 
         # In this case it represents the time variable
         elif o.count() == 2:
@@ -176,7 +174,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
             if self.reference_grad:
                 raise CodegenUFLError("Coefficient gradients should not be transformed to reference element")
 
-            return self.interface.pymbolic_gridfunction(o, restriction, self.grad)
+            return self.implement_gridfunction(o, restriction, self.grad)
 
     def variable(self, o):
         # Right now only scalar varibables are supported
@@ -232,7 +230,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
             if index in self.indexmap:
                 return self.indexmap[index]
             else:
-                return Variable(self.interface.name_index(index))
+                raise CodegenUFLError("Index should have been unrolled!")
 
     def multi_index(self, o):
         return tuple(self._index_or_fixed_index(i) for i in o)
@@ -248,7 +246,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
                 self.indices = None
             return self.call(o.ufl_operands[index])
         else:
-            return self.interface.pymbolic_list_tensor(o)
+            raise CodegenUFLError("Index should have been unrolled!")
 
     def component_tensor(self, o):
         assert len(self.indices) == len(o.ufl_operands[1])
@@ -265,10 +263,13 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
         return ret
 
     def identity(self, o):
-        return self.interface.pymbolic_identity(o)
+        i, j = self.indices
+        assert isinstance(i, int) and isinstance(j, int)
+        return 1 if i == j else 0
 
     def inverse(self, o):
-        return self.interface.pymbolic_matrix_inverse(o)
+        from dune.codegen.pdelab.tensors import pymbolic_matrix_inverse
+        return pymbolic_matrix_inverse(o, self)
 
     #
     # Handlers for arithmetic operators and functions
@@ -421,68 +422,9 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
         return prim.LogicalNot(self.call(o.ufl_operands[0]))
 
     #
-    # Handlers for geometric quantities
+    # NB: Geometry and quadrature related handlers have been moved into configurable mixin classes!
     #
 
-    def spatial_coordinate(self, o):
-        return self.interface.pymbolic_spatial_coordinate()
-
-    def facet_normal(self, o):
-        # The normal must be restricted to be well-defined
-        assert self.restriction is not Restriction.NONE
-
-        # Note: In UFL the jump is defined as: jump(v) = v('+') -
-        # v('-'). The corresponding outer unit normal is
-        # n=FacetNormal(cell)('+'). In order to be consisten with UFL
-        # we need to create the outer unit normal if the restriction
-        # is positive.
-        if self.restriction == Restriction.POSITIVE:
-            return self.interface.pymbolic_unit_outer_normal()
-        if self.restriction == Restriction.NEGATIVE:
-            # It is highly unnatural to have this generator function,
-            # but I do run into subtle trouble with return -1*outer
-            # as the indexing into the normal happens only later.
-            # Not investing more time into this cornercase right now.
-            return self.interface.pymbolic_unit_inner_normal()
-
-    def quadrature_weight(self, o):
-        return self.interface.pymbolic_quadrature_weight()
-
-    def jacobian_determinant(self, o):
-        return self.interface.pymbolic_jacobian_determinant()
-
-    def jacobian_inverse(self, o):
-        restriction = self.restriction
-        if self.measure == 'exterior_facet':
-            restriction = Restriction.POSITIVE
-
-        assert(len(self.indices) == 2)
-        i, j = self.indices
-        self.indices = None
-
-        # Implement diagonal jacobians for unrolled matrices!
-        if get_form_option("diagonal_transformation_matrix"):
-            if isinstance(i, int) and isinstance(j, int) and i != j:
-                return 0
-
-        return self.interface.pymbolic_jacobian_inverse(i, j, restriction)
-
-    def jacobian(self, o):
-        raise NotImplementedError("How did you get Jacobian into your form? We only support JacobianInverse right now. Report!")
-
-    def facet_jacobian_determinant(self, o):
-        return self.interface.pymbolic_facet_jacobian_determinant()
-
-    def cell_volume(self, o):
-        restriction = self.restriction
-        if self.measure == 'exterior_facet':
-            restriction = Restriction.POSITIVE
-
-        return self.interface.pymbolic_cell_volume(restriction)
-
-    def facet_area(self, o):
-        return self.interface.pymbolic_facet_area()
-
     #
     # Equality/Hashability of the visitor
     # In order to pass this visitor into (cached) generator functions, it is beneficial
@@ -491,7 +433,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
     #
 
     def __eq__(self, other):
-        return type(self) == type(other)
+        return isinstance(other, UFL2LoopyVisitor)
 
     def __hash__(self):
         return 0
diff --git a/python/test/dune/codegen/generation/test_backend.py b/python/test/dune/codegen/generation/test_backend.py
deleted file mode 100644
index 76bda979394f0293f5b9d1a9314a80ce58ba8a66..0000000000000000000000000000000000000000
--- a/python/test/dune/codegen/generation/test_backend.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from dune.codegen.generation import (backend,
-                                     get_backend,
-                                     generator_factory,
-                                     )
-
-
-@backend(interface="foo", name="f1")
-@generator_factory()
-def f1():
-    return 1
-
-
-@generator_factory()
-@backend(interface="foo", name="f2")
-def f2():
-    return 2
-
-
-@backend(interface="bar", name="f3")
-@generator_factory()
-def f3():
-    return 3
-
-
-@generator_factory()
-@backend(interface="bar", name="f4")
-def f4():
-    return 4
-
-
-def test_backend():
-    assert get_backend(interface="foo", selector=lambda: "f1")() == 1
-    assert get_backend(interface="foo", selector=lambda: "f2")() == 2
-    assert get_backend(interface="bar", selector=lambda: "f3")() == 3
-    assert get_backend(interface="bar", selector=lambda: "f4")() == 4
diff --git a/test/blockstructured/nonlinear/nonlinear.mini b/test/blockstructured/nonlinear/nonlinear.mini
index 5e9835a0311a98e815e7c54e51a02714b3bef9ee..29d8d531c8a15967f2673ff5a1b9326ab832fae9 100644
--- a/test/blockstructured/nonlinear/nonlinear.mini
+++ b/test/blockstructured/nonlinear/nonlinear.mini
@@ -14,3 +14,4 @@ compare_l2errorsquared = 6e-4
 [formcompiler.r]
 blockstructured = 1
 number_of_blocks = 5
+geometry_mixins = blockstructured_equidistant
diff --git a/test/blockstructured/poisson/3d/poisson.mini b/test/blockstructured/poisson/3d/poisson.mini
index e9e34187a447198666d8de5a11e77e54aaa4ebb1..796c8652795d4e6a9715df7d2ac364d12b1638b9 100644
--- a/test/blockstructured/poisson/3d/poisson.mini
+++ b/test/blockstructured/poisson/3d/poisson.mini
@@ -17,4 +17,5 @@ compare_l2errorsquared = 1e-7
 numerical_jacobian = 1, 0 | expand num
 blockstructured = 1
 number_of_blocks = 3
+geometry_mixins = blockstructured_equidistant
 
diff --git a/test/blockstructured/poisson/poisson.mini b/test/blockstructured/poisson/poisson.mini
index a3a1ff0545f378fe1bed3efefb8911b3645d269f..1d7df30a14d0436c1337734f263a5894d528820f 100644
--- a/test/blockstructured/poisson/poisson.mini
+++ b/test/blockstructured/poisson/poisson.mini
@@ -16,6 +16,7 @@ compare_l2errorsquared = 1e-7
 numerical_jacobian = 1, 0 | expand num
 blockstructured = 1
 number_of_blocks = 3
+geometry_mixins = blockstructured_equidistant
 
 [formcompiler.ufl_variants]
 cell = quadrilateral
diff --git a/test/blockstructured/poisson/poisson_grid.mini b/test/blockstructured/poisson/poisson_grid.mini
index c3c256486cb78e7fd74135abc978a5010f8f08db..e7c979d2e8f4da3595367a81ce60e7e27cc04d59 100644
--- a/test/blockstructured/poisson/poisson_grid.mini
+++ b/test/blockstructured/poisson/poisson_grid.mini
@@ -1,9 +1,12 @@
 __name = blockstructured_poisson_grid_{__exec_suffix}
-__exec_suffix = vec, nonvec | expand vectorize
+__exec_suffix = {vec_suffix}_{dim_suffix}
 
-dim = 2
+dim = 2, 3 | expand dimension
 
-gmshFile = square.msh
+vec_suffix = nonvec, vec | expand vectorized
+dim_suffix = 2d, 3d | expand dimension
+
+gmshFile = square.msh, cube.msh | expand dimension
 
 [wrapper.vtkcompare]
 name = {__name}
@@ -11,16 +14,17 @@ reference = poisson_ref
 extension = vtu
 
 [formcompiler]
-compare_l2errorsquared = 1e-7
+compare_l2errorsquared = 1e-7, 1e-4 | expand dimension
 grid_unstructured = 1
 
 [formcompiler.r]
 matrix_free = 1
 generate_jacobians = 0
 blockstructured = 1
-number_of_blocks = 16
-vectorization_blockstructured = 1, 0 | expand vectorize
+number_of_blocks = 16, 4 | expand dimension
+vectorization_blockstructured = 1, 0 | expand vectorized
+geometry_mixins = blockstructured_multilinear
 
 [formcompiler.ufl_variants]
-cell = quadrilateral
+cell = quadrilateral, hexahedron | expand dimension
 degree = 1
\ No newline at end of file
diff --git a/test/blockstructured/poisson/poisson_matrix_free.mini b/test/blockstructured/poisson/poisson_matrix_free.mini
index 3a76151cdc4d6efe9c069e87c67417063e655c57..514ab95b79ea5febb48da6d4a610c9abbb4d5ba4 100644
--- a/test/blockstructured/poisson/poisson_matrix_free.mini
+++ b/test/blockstructured/poisson/poisson_matrix_free.mini
@@ -15,6 +15,7 @@ compare_l2errorsquared = 1e-7
 matrix_free = 1
 blockstructured = 1
 number_of_blocks = 4
+geometry_mixins = blockstructured_equidistant
 
 [formcompiler.ufl_variants]
 cell = quadrilateral
diff --git a/test/blockstructured/poisson/poisson_neumann.mini b/test/blockstructured/poisson/poisson_neumann.mini
index 1512f88bc1ba25141e121de948da3aed06933b97..26e75dd9cd63ec92179434d4bec5d9e288cc9566 100644
--- a/test/blockstructured/poisson/poisson_neumann.mini
+++ b/test/blockstructured/poisson/poisson_neumann.mini
@@ -16,3 +16,4 @@ compare_l2errorsquared = 1e-8
 numerical_jacobian = 1, 0 | expand num
 blockstructured = 1
 number_of_blocks = 4
+geometry_mixins = blockstructured_equidistant
diff --git a/test/blockstructured/poisson/poisson_tensor.mini b/test/blockstructured/poisson/poisson_tensor.mini
index 2923263610d8260066e2809090b64ce2d10948c9..6f18a924c4deabebaeb8447abd77076b0e2e127f 100644
--- a/test/blockstructured/poisson/poisson_tensor.mini
+++ b/test/blockstructured/poisson/poisson_tensor.mini
@@ -27,6 +27,7 @@ vectorization_blockstructured = 0, 1 | expand vectorized
 generate_jacobians = 0
 blockstructured = 1
 number_of_blocks = 8, 4 | expand dimension
+geometry_mixins = blockstructured_equidistant, blockstructured_multilinear | expand unstructured
 
 [formcompiler.ufl_variants]
 cell = quadrilateral, hexahedron | expand dimension
diff --git a/test/blockstructured/poisson/poisson_unstructured.mini b/test/blockstructured/poisson/poisson_unstructured.mini
index c71c381ada38934e5794e2de566ff895e3db4bc9..73ee2072de494acb4af02e3514a03fd058e5e4ed 100644
--- a/test/blockstructured/poisson/poisson_unstructured.mini
+++ b/test/blockstructured/poisson/poisson_unstructured.mini
@@ -4,7 +4,6 @@ __exec_suffix = {dimname}
 dim = 2, 3 | expand dimension
 dimname = 2d, 3d | expand dimension
 
-
 lowerleft = 0.0 | repeat {dim}
 upperright = 1.0 | repeat {dim}
 elements = 8, 2 | expand dimension | repeat {dim}
@@ -19,9 +18,10 @@ compare_l2errorsquared = 1e-7
 grid_unstructured = 1
 
 [formcompiler.r]
-matrix_free = 1
+matrix_free = 0
 blockstructured = 1
 number_of_blocks = 16, 8 | expand dimension
+geometry_mixins = blockstructured_multilinear
 
 [formcompiler.ufl_variants]
 cell = quadrilateral, hexahedron | expand dimension
diff --git a/test/blockstructured/poisson/poisson_unstructured_vec.mini b/test/blockstructured/poisson/poisson_unstructured_vec.mini
index ddae7714c65011f131ca3a31e85b9197b10901c3..681addc52358037b8daf740c7c4e9aa79a9327e3 100644
--- a/test/blockstructured/poisson/poisson_unstructured_vec.mini
+++ b/test/blockstructured/poisson/poisson_unstructured_vec.mini
@@ -9,7 +9,6 @@ upperright = 1.0 | repeat {dim}
 elements = 8, 2 | expand dimension | repeat {dim}
 elementType = quadrilateral
 
-
 [wrapper.vtkcompare]
 name = {__name}
 reference = poisson_ref
@@ -25,6 +24,7 @@ generate_jacobians = 0
 blockstructured = 1
 number_of_blocks = 16, 8 | expand dimension
 vectorization_blockstructured = 1
+geometry_mixins = blockstructured_multilinear
 
 [formcompiler.ufl_variants]
 cell = quadrilateral, hexahedron | expand dimension
diff --git a/test/blockstructured/poisson/poisson_vec.mini b/test/blockstructured/poisson/poisson_vec.mini
index 9ec5cb244d9e56ca31eb3fe67ecf1c45d6e88f24..7b132230b5ab54469d227389eea0b9ba94c78f4a 100644
--- a/test/blockstructured/poisson/poisson_vec.mini
+++ b/test/blockstructured/poisson/poisson_vec.mini
@@ -21,6 +21,7 @@ generate_jacobians = 0
 blockstructured = 1
 number_of_blocks = 16, 8 | expand dimension
 vectorization_blockstructured = 1
+geometry_mixins = blockstructured_equidistant
 
 [formcompiler.ufl_variants]
 cell = quadrilateral, hexahedron | expand dimension
diff --git a/test/blockstructured/stokes/stokes.mini b/test/blockstructured/stokes/stokes.mini
index 532a4159b6b6019525acdea50da3c18f14e22f9a..251f8a842ab4f6940c4a7015982d00ea3947f2ba 100644
--- a/test/blockstructured/stokes/stokes.mini
+++ b/test/blockstructured/stokes/stokes.mini
@@ -16,3 +16,4 @@ compare_l2errorsquared = 1e-9
 numerical_jacobian = 0, 1 | expand num
 blockstructured = 1
 number_of_blocks = 3
+geometry_mixins = blockstructured_equidistant
diff --git a/test/hyperbolic/linearacoustics.mini b/test/hyperbolic/linearacoustics.mini
index ad1cc95b405ebd4a7624ef8057b99834fb52ccbc..249d3637136504341bccc3f951271c1c68db6e3b 100644
--- a/test/hyperbolic/linearacoustics.mini
+++ b/test/hyperbolic/linearacoustics.mini
@@ -17,7 +17,9 @@ explicit_time_stepping = 1
 operators = mass, r
 
 [formcompiler.mass]
+geometry_mixins = equidistant
 numerical_jacobian = 1
 
 [formcompiler.r]
+geometry_mixins = equidistant
 numerical_jacobian = 1
diff --git a/test/navier-stokes/navierstokes_2d_dg_quadrilateral.mini b/test/navier-stokes/navierstokes_2d_dg_quadrilateral.mini
index 5700107d03d62655e87b50b129aabc5b534134d2..787b3408ce17d074c5c3114ce11cca3e3fddae6f 100644
--- a/test/navier-stokes/navierstokes_2d_dg_quadrilateral.mini
+++ b/test/navier-stokes/navierstokes_2d_dg_quadrilateral.mini
@@ -26,9 +26,11 @@ overlapping = 1
 
 [formcompiler.mass]
 numerical_jacobian = 0, 1 | expand num
+geometry_mixins = equidistant
 
 [formcompiler.r]
 numerical_jacobian = 0, 1 | expand num
+geometry_mixins = equidistant
 
 [instat]
 T = 1e-2
diff --git a/test/navier-stokes/navierstokes_3d_dg_quadrilateral.mini b/test/navier-stokes/navierstokes_3d_dg_quadrilateral.mini
index 8823cb1ba26196a5f0aa01cb78d68ff60625f88b..c0bf23eadb1b5238d94424a424217a1b5ffc616f 100644
--- a/test/navier-stokes/navierstokes_3d_dg_quadrilateral.mini
+++ b/test/navier-stokes/navierstokes_3d_dg_quadrilateral.mini
@@ -21,9 +21,11 @@ operators = mass, r
 
 [formcompiler.mass]
 numerical_jacobian = 0, 1 | expand num
+geometry_mixins = equidistant
 
 [formcompiler.r]
 numerical_jacobian = 0, 1 | expand num
+geometry_mixins = equidistant
 
 [instat]
 T = 5e-2
diff --git a/test/nonlinear/diffusivewave.mini b/test/nonlinear/diffusivewave.mini
index c82f8ed23330982d6caf40f21d71dbb5d96f0588..e02ec95646d5838b95d3be5793adeebc4e060a72 100644
--- a/test/nonlinear/diffusivewave.mini
+++ b/test/nonlinear/diffusivewave.mini
@@ -17,9 +17,11 @@ T = 0.01
 operators = mass, poisson
 
 [formcompiler.mass]
+geometry_mixins = equidistant, sumfact_equidistant | expand sf
 sumfact = 0, 1 | expand sf
 fastdg = 0, 0 | expand sf
 
 [formcompiler.operator]
+geometry_mixins = equidistant, sumfact_equidistant | expand sf
 sumfact = 0, 1 | expand sf
 fastdg = 0, 0 | expand sf
diff --git a/test/poisson/CMakeLists.txt b/test/poisson/CMakeLists.txt
index 0a4f9897176e1aaa2a2864c23bf97135eb67261b..5eeae1a434b4051470540c44d766360d904b4fdd 100644
--- a/test/poisson/CMakeLists.txt
+++ b/test/poisson/CMakeLists.txt
@@ -75,6 +75,12 @@ dune_add_formcompiler_system_test(UFLFILE poisson_customfunction.ufl
                                   INIFILE poisson_customfunction.mini
                                   )
 
+# 13. Poisson Test Case: DG quadrilaterals with axiparallel mixin
+dune_add_formcompiler_system_test(UFLFILE poisson_dg_quadrilateral.ufl
+        BASENAME poisson_dg_axiparallel
+        INIFILE poisson_dg_axiparallel.mini
+        )
+
 # the reference vtk file
 add_executable(poisson_dg_ref reference_main.cc)
 set_target_properties(poisson_dg_ref PROPERTIES EXCLUDE_FROM_ALL 1)
diff --git a/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.mini b/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.mini
index c5ec6e72a870b19eee8d9b8f881c02c770c65da9..7cc6619beb1761050f15e9a71b5f78078838e36f 100644
--- a/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.mini
+++ b/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.mini
@@ -9,3 +9,6 @@ extension = vtu
 
 [formcompiler]
 compare_l2errorsquared = 9e-8
+
+[formcompiler.r]
+geometry_mixins = equidistant
diff --git a/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.mini b/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.mini
index cce6d681b76f031b3a239a04b7d3cb371bd99a85..a4478b53e9f399fab159dead0010286d0c9cc6a3 100644
--- a/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.mini
+++ b/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.mini
@@ -9,3 +9,6 @@ extension = vtu
 
 [formcompiler]
 compare_l2errorsquared = 6e-7
+
+[formcompiler.r]
+geometry_mixins = equidistant
diff --git a/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.mini b/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.mini
index 1d4c4e9660cb9f285b3dc51db9c3c525181dd1ea..23bc16ba4b87ef9381aedbf8e3fb773e02dc5ae0 100644
--- a/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.mini
+++ b/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.mini
@@ -9,3 +9,6 @@ extension = vtu
 
 [formcompiler]
 compare_l2errorsquared = 9e-5
+
+[formcompiler.r]
+geometry_mixins = equidistant
diff --git a/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.mini b/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.mini
index 1442988525b7b3080bcc0efd51e69ccca4e84f63..3c8ebda08525392d88eb1a900572616cd40052e4 100644
--- a/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.mini
+++ b/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.mini
@@ -9,3 +9,6 @@ extension = vtu
 
 [formcompiler]
 compare_l2errorsquared = 1e-4
+
+[formcompiler.r]
+geometry_mixins = equidistant
diff --git a/test/poisson/opcount_poisson_dg_symdiff.mini b/test/poisson/opcount_poisson_dg_symdiff.mini
index d98044df779fa8be1d3c2ee09d4e2023f0d95da9..186c4344f9fed7512cde913433145259a76bf7b5 100644
--- a/test/poisson/opcount_poisson_dg_symdiff.mini
+++ b/test/poisson/opcount_poisson_dg_symdiff.mini
@@ -10,3 +10,4 @@ extension = vtu
 [formcompiler]
 opcounter = 1
 instrumentation_level = 3
+geometry_mixins = equidistant
diff --git a/test/poisson/poisson_dg_axiparallel.mini b/test/poisson/poisson_dg_axiparallel.mini
new file mode 100644
index 0000000000000000000000000000000000000000..1f2490eba8b1f2ce06fc150b5fb71972338c6502
--- /dev/null
+++ b/test/poisson/poisson_dg_axiparallel.mini
@@ -0,0 +1,17 @@
+__name = poisson_dg_axiparallel_{__exec_suffix}
+__exec_suffix = {__exec_symdiff}
+__exec_symdiff = numdiff, symdiff |expand num
+
+extension = 1.0 1.0
+cells = 32 32
+
+[wrapper.vtkcompare]
+name = {__name}
+extension = vtu
+
+[formcompiler]
+compare_l2errorsquared = 7e-7
+
+[formcompiler.r]
+numerical_jacobian = 1, 0 | expand num
+geometry_mixins = axiparallel
diff --git a/test/poisson/poisson_dg_quadrilateral.mini b/test/poisson/poisson_dg_quadrilateral.mini
index dde2e495ab819ed22be6b848518ed71d41cfe30f..3715da8457913ecac94772fd7d045f7d9326ed2b 100644
--- a/test/poisson/poisson_dg_quadrilateral.mini
+++ b/test/poisson/poisson_dg_quadrilateral.mini
@@ -14,3 +14,4 @@ compare_l2errorsquared = 7e-7
 
 [formcompiler.r]
 numerical_jacobian = 1, 0 | expand num
+geometry_mixins = equidistant
diff --git a/test/poisson/poisson_dg_tensor.mini b/test/poisson/poisson_dg_tensor.mini
index d696cebc5196bd711ef4e8e2e1371a977be3a31f..e73c1ebff8b536644c7aefce95451854ea97b9c1 100644
--- a/test/poisson/poisson_dg_tensor.mini
+++ b/test/poisson/poisson_dg_tensor.mini
@@ -15,4 +15,5 @@ extension = vtu
 compare_l2errorsquared = 4e-6
 
 [formcompiler.r]
-numerical_jacobian = 1, 0 | expand num
\ No newline at end of file
+numerical_jacobian = 1, 0 | expand num
+geometry_mixins = equidistant
\ No newline at end of file
diff --git a/test/stokes/stokes_3d_dg_quadrilateral.mini b/test/stokes/stokes_3d_dg_quadrilateral.mini
index 59396277ede15f6a563d04fb448c4d3b5a445b3b..18bd43c75cb65131496e7ecd2beaff69999612c3 100644
--- a/test/stokes/stokes_3d_dg_quadrilateral.mini
+++ b/test/stokes/stokes_3d_dg_quadrilateral.mini
@@ -13,4 +13,5 @@ extension = vtu
 compare_l2errorsquared = 6e-8
 
 [formcompiler.r]
+geometry_mixins = equidistant
 numerical_jacobian = 0, 1 | expand num
diff --git a/test/stokes/stokes_3d_quadrilateral.mini b/test/stokes/stokes_3d_quadrilateral.mini
index 17f3d9f510054ff9014830e258edd7840180248e..d008c6363ccd2381848c09976450d6db2e810001 100644
--- a/test/stokes/stokes_3d_quadrilateral.mini
+++ b/test/stokes/stokes_3d_quadrilateral.mini
@@ -14,4 +14,5 @@ extension = vtu
 compare_l2errorsquared = 1e-10
 
 [formcompiler.r]
+geometry_mixins = equidistant
 numerical_jacobian = 1, 0 | expand num
diff --git a/test/stokes/stokes_dg_quadrilateral.mini b/test/stokes/stokes_dg_quadrilateral.mini
index 78954b12873569589c2874d15858c6121a242eb5..5a1fb4127984eb1b60027247e23efa03e6eacc92 100644
--- a/test/stokes/stokes_dg_quadrilateral.mini
+++ b/test/stokes/stokes_dg_quadrilateral.mini
@@ -13,4 +13,5 @@ extension = vtu
 compare_l2errorsquared = 1e-8
 
 [formcompiler.r]
+geometry_mixins = equidistant
 numerical_jacobian = 0, 1 | expand num
diff --git a/test/stokes/stokes_quadrilateral.mini b/test/stokes/stokes_quadrilateral.mini
index 6ee36e8220463cbb75764a91b0ae1d2970f28eb3..cdaa55d544dd29c6354190291e12704e610ee064 100644
--- a/test/stokes/stokes_quadrilateral.mini
+++ b/test/stokes/stokes_quadrilateral.mini
@@ -14,4 +14,5 @@ extension = vtu
 compare_l2errorsquared = 1e-10
 
 [formcompiler.r]
+geometry_mixins = equidistant
 numerical_jacobian = 1, 0 | expand num
diff --git a/test/sumfact/hyperbolic/linearacoustics.mini b/test/sumfact/hyperbolic/linearacoustics.mini
index 1f7b1fa542172fd28e45321d36ab82dc3cbdc1ef..df4cc0521d7019dde13bb60464206045b3ccc3a3 100644
--- a/test/sumfact/hyperbolic/linearacoustics.mini
+++ b/test/sumfact/hyperbolic/linearacoustics.mini
@@ -19,7 +19,9 @@ operators = mass, r
 [formcompiler.mass]
 numerical_jacobian = 1
 sumfact = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.r]
 numerical_jacobian = 1
 sumfact = 1
+geometry_mixins = sumfact_equidistant
diff --git a/test/sumfact/hyperbolic/lineartransport.mini b/test/sumfact/hyperbolic/lineartransport.mini
index 4ea0034f57922c41dda48dbac96d8ef89ec426b0..07f39ed3a8828bfd9b88f535a2c3dcfb6832cb5e 100644
--- a/test/sumfact/hyperbolic/lineartransport.mini
+++ b/test/sumfact/hyperbolic/lineartransport.mini
@@ -22,7 +22,9 @@ operators = mass, r
 [formcompiler.mass]
 sumfact = 1
 numerical_jacobian = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.r]
 sumfact = 1
 numerical_jacobian = 1
+geometry_mixins = sumfact_equidistant
diff --git a/test/sumfact/hyperbolic/shallowwater.mini b/test/sumfact/hyperbolic/shallowwater.mini
index 5f63f48bd0bd55537d1055ff685d0998ac28ad15..b9c18a34fc8e4759c8ca08e811c8437368a7f899 100644
--- a/test/sumfact/hyperbolic/shallowwater.mini
+++ b/test/sumfact/hyperbolic/shallowwater.mini
@@ -18,6 +18,8 @@ operators = mass, r
 
 [formcompiler.mass]
 sumfact = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.r]
 sumfact = 1
+geometry_mixins = sumfact_equidistant
diff --git a/test/sumfact/mass/mass.mini b/test/sumfact/mass/mass.mini
index 6b0e9db8144fe18f7cf5e89f016d228d74ae9173..6b2f8c1b92674b9d62a7c50eb69b0362665a3897 100644
--- a/test/sumfact/mass/mass.mini
+++ b/test/sumfact/mass/mass.mini
@@ -16,3 +16,4 @@ extension = vtu
 numerical_jacobian = 1, 0 | expand num
 vectorization_quadloop = 1, 0 | expand vec
 sumfact = 1
+geometry_mixins = sumfact_equidistant
diff --git a/test/sumfact/mass/mass_3d.mini b/test/sumfact/mass/mass_3d.mini
index fff87d11bb272dc5d5bd57e8f12569581ddc2485..88795d74e1bc1a84faf73412210ca0a24bc5c517 100644
--- a/test/sumfact/mass/mass_3d.mini
+++ b/test/sumfact/mass/mass_3d.mini
@@ -17,6 +17,7 @@ extension = vtu
 numerical_jacobian = 1, 0 | expand num
 vectorization_quadloop = 1, 0 | expand vec
 sumfact = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 3
diff --git a/test/sumfact/mass/sliced.mini b/test/sumfact/mass/sliced.mini
index 17d331901a999c5d2d90a0453dfc524183f6e132..c4f1c56eac78046fbca4a75ff93136e9b68a6d1b 100644
--- a/test/sumfact/mass/sliced.mini
+++ b/test/sumfact/mass/sliced.mini
@@ -15,6 +15,7 @@ vectorization_strategy = explicit
 vectorization_horizontal = 1
 vectorization_vertical = 4
 sumfact = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 3
diff --git a/test/sumfact/poisson/diagonal.mini b/test/sumfact/poisson/diagonal.mini
index 298fadba9554771a7bdf4810b15ad7e925861cbf..067a8076ab0922e42f34e5fd65c0565b6d00bc0c 100644
--- a/test/sumfact/poisson/diagonal.mini
+++ b/test/sumfact/poisson/diagonal.mini
@@ -18,6 +18,7 @@ vectorization_horizontal = 2
 vectorization_vertical = 2
 quadrature_order = 6, 6, 6
 fastdg = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 3
diff --git a/test/sumfact/poisson/facedir-facemod-variation/poisson_dg_3d_facedir_facemod_variation.mini b/test/sumfact/poisson/facedir-facemod-variation/poisson_dg_3d_facedir_facemod_variation.mini
index 45927298669692f8d3675e1d7896d565b922ae53..6384d72b60f76fa53dea540fff49f32e281dd91f 100644
--- a/test/sumfact/poisson/facedir-facemod-variation/poisson_dg_3d_facedir_facemod_variation.mini
+++ b/test/sumfact/poisson/facedir-facemod-variation/poisson_dg_3d_facedir_facemod_variation.mini
@@ -36,6 +36,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 0
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_dg_3d_facedir_facemod_variation.mini b/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_dg_3d_facedir_facemod_variation.mini
index 664421e4f91a3893c42783c209068e306b110c7e..58090aeb5cedde40572f0371eb839688645c0af9 100644
--- a/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_dg_3d_facedir_facemod_variation.mini
+++ b/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_dg_3d_facedir_facemod_variation.mini
@@ -44,6 +44,7 @@ sumfact = 1
 sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_fastdg_3d_facedir_facemod_variation.mini b/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_fastdg_3d_facedir_facemod_variation.mini
index f814a9aac356cf416a358ff08816d346138d3d9a..5e0b4c29b2d6f89913c9254a7c5b1070855ca8d0 100644
--- a/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_fastdg_3d_facedir_facemod_variation.mini
+++ b/test/sumfact/poisson/facedir-facemod-variation/sumfact_poisson_fastdg_3d_facedir_facemod_variation.mini
@@ -45,6 +45,7 @@ sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
 fastdg = 1
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/opcount_poisson_2d_order2.mini b/test/sumfact/poisson/opcount_poisson_2d_order2.mini
index 538189b2e5c14bd745c748fc43e3161c4013cc77..4d76eac6dc2fbf91edf7fe924a7cc50bc30be5df 100644
--- a/test/sumfact/poisson/opcount_poisson_2d_order2.mini
+++ b/test/sumfact/poisson/opcount_poisson_2d_order2.mini
@@ -18,6 +18,7 @@ instrumentation_level = 4
 
 [formcompiler.r]
 sumfact = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 2
diff --git a/test/sumfact/poisson/opcount_sumfact_poisson_dg_2d_vec.mini b/test/sumfact/poisson/opcount_sumfact_poisson_dg_2d_vec.mini
index b657c1a2c731e0606093ab3c0e00044afaadd3ae..d12c7896ed219317f8ba895ddf61365011d9d2f6 100644
--- a/test/sumfact/poisson/opcount_sumfact_poisson_dg_2d_vec.mini
+++ b/test/sumfact/poisson/opcount_sumfact_poisson_dg_2d_vec.mini
@@ -15,6 +15,7 @@ instrumentation_level = 4
 
 [formcompiler.r]
 sumfact = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 1
diff --git a/test/sumfact/poisson/poisson_2d.mini b/test/sumfact/poisson/poisson_2d.mini
index d9ce1773e575fab9ec9e05057207093c8ea8b747..90cb97db1229c502ada8fa02d190e30394cad4f5 100644
--- a/test/sumfact/poisson/poisson_2d.mini
+++ b/test/sumfact/poisson/poisson_2d.mini
@@ -21,6 +21,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_strategy = explicit, none | expand grad
 quadrature_order = 2, 4
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_2d_unstructured.mini b/test/sumfact/poisson/poisson_2d_unstructured.mini
index 81a8246ea330ca157edcc61e0ceaf0839ffb4465..ce12ec78f77d940a8b8cec3081a5a9708ece0f56 100644
--- a/test/sumfact/poisson/poisson_2d_unstructured.mini
+++ b/test/sumfact/poisson/poisson_2d_unstructured.mini
@@ -30,6 +30,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_3d.mini b/test/sumfact/poisson/poisson_3d.mini
index bf7b0f3bfdc7823e970694ecead48e6ecb4d7f6a..27ef4608c64394b6bf626b423a6f9d1eab1b7de3 100644
--- a/test/sumfact/poisson/poisson_3d.mini
+++ b/test/sumfact/poisson/poisson_3d.mini
@@ -22,6 +22,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = explicit, autotune, none | expand grad
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_3d_unstructured.mini b/test/sumfact/poisson/poisson_3d_unstructured.mini
index 8c1ad02cf98fabd8d69e5df48fcd88e529611e03..7d5710fa68d588a96d4ed23aac0ea72dff6276c6 100644
--- a/test/sumfact/poisson/poisson_3d_unstructured.mini
+++ b/test/sumfact/poisson/poisson_3d_unstructured.mini
@@ -30,6 +30,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_2d.mini b/test/sumfact/poisson/poisson_dg_2d.mini
index d6799eac4600300f86303ea63853365698efdf1f..8903d6597f9d71cecdc027cabaae894b8d3f5820 100644
--- a/test/sumfact/poisson/poisson_dg_2d.mini
+++ b/test/sumfact/poisson/poisson_dg_2d.mini
@@ -21,6 +21,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = explicit, none | expand grad
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_2d_gmsh.mini b/test/sumfact/poisson/poisson_dg_2d_gmsh.mini
index 3437a746a086965c33243c354ca768c944cc9427..8d622b06ff3e6a83b140a6c6db00929fd8fb8c86 100644
--- a/test/sumfact/poisson/poisson_dg_2d_gmsh.mini
+++ b/test/sumfact/poisson/poisson_dg_2d_gmsh.mini
@@ -28,6 +28,7 @@ sumfact = 1
 sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_2d_unstructured.mini b/test/sumfact/poisson/poisson_dg_2d_unstructured.mini
index 0d9423ccbc65a91dfac244006f93331703c913a3..eca089ff89316487fda300c4b45947b5b2d0b83b 100644
--- a/test/sumfact/poisson/poisson_dg_2d_unstructured.mini
+++ b/test/sumfact/poisson/poisson_dg_2d_unstructured.mini
@@ -9,7 +9,6 @@ deg_suffix = deg{formcompiler.ufl_variants.degree}
 {deg_suffix} == deg1 | exclude
 {diff_suffix} == numdiff | exclude
 {quadvec_suffix} == quadvec | exclude
-# {gradvec_suffix} == gradvec | exclude
 
 lowerleft = 0.0 0.0
 upperright = 1.0 1.0
@@ -30,6 +29,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_3d.mini b/test/sumfact/poisson/poisson_dg_3d.mini
index 91556ff0ca2f70d49e9ac89cfaad206040ebda03..3f0485672102ab0d7fffd7ce4f490c0f86a66cff 100644
--- a/test/sumfact/poisson/poisson_dg_3d.mini
+++ b/test/sumfact/poisson/poisson_dg_3d.mini
@@ -22,6 +22,7 @@ numerical_jacobian = 1, 0 | expand num
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = explicit, none | expand grad
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_3d_gmsh.mini b/test/sumfact/poisson/poisson_dg_3d_gmsh.mini
index 3a78bb50786f23711f67165afd987005f267b4e6..fb9b26801ac504ecac69f58d4c0a1f305ce16ccd 100644
--- a/test/sumfact/poisson/poisson_dg_3d_gmsh.mini
+++ b/test/sumfact/poisson/poisson_dg_3d_gmsh.mini
@@ -29,6 +29,7 @@ sumfact = 1
 sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_3d_unstructured.mini b/test/sumfact/poisson/poisson_dg_3d_unstructured.mini
index 7303abfedd817a5df78380c53bc3547ae2b02dc2..ed80ba617c1d91b66d1df9e7b5ecfc508d71fc77 100644
--- a/test/sumfact/poisson/poisson_dg_3d_unstructured.mini
+++ b/test/sumfact/poisson/poisson_dg_3d_unstructured.mini
@@ -9,7 +9,6 @@ deg_suffix = deg{formcompiler.ufl_variants.degree}
 {deg_suffix} == deg1 | exclude
 {diff_suffix} == numdiff | exclude
 {quadvec_suffix} == quadvec | exclude
-# {gradvec_suffix} == gradvec | exclude
 
 lowerleft = 0.0 0.0 0.0
 upperright = 1.0 1.0 1.0
@@ -31,6 +30,7 @@ sumfact = 1
 sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_dg_tensor.mini b/test/sumfact/poisson/poisson_dg_tensor.mini
index f6884f965eac0a47416af97b26aa849a6be688ad..d0b164bd7f25831da4d2dbc755590d6ee6b5aca8 100644
--- a/test/sumfact/poisson/poisson_dg_tensor.mini
+++ b/test/sumfact/poisson/poisson_dg_tensor.mini
@@ -18,6 +18,7 @@ compare_l2errorsquared = 3e-4
 sumfact = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = explicit, none | expand grad
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 2
diff --git a/test/sumfact/poisson/poisson_fastdg_2d.mini b/test/sumfact/poisson/poisson_fastdg_2d.mini
index 53012e325a57387003dc42f2af4268f48cbdaa98..3f33180cf2e0e57ffbe360bd92375ac2c67655ee 100644
--- a/test/sumfact/poisson/poisson_fastdg_2d.mini
+++ b/test/sumfact/poisson/poisson_fastdg_2d.mini
@@ -20,6 +20,7 @@ sumfact = 1
 vectorization_quadloop = 1, 0 | expand quadvec
 vectorization_strategy = explicit, none | expand gradvec
 fastdg = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 2
\ No newline at end of file
diff --git a/test/sumfact/poisson/poisson_fastdg_2d_gmsh.mini b/test/sumfact/poisson/poisson_fastdg_2d_gmsh.mini
index 56b69c773b1ce95fa6387f5313af47a94980f8c0..651c14b14555f35fd52429ad8f56bdbd04acce64 100644
--- a/test/sumfact/poisson/poisson_fastdg_2d_gmsh.mini
+++ b/test/sumfact/poisson/poisson_fastdg_2d_gmsh.mini
@@ -27,6 +27,7 @@ fastdg = 1
 sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/poisson_fastdg_3d.mini b/test/sumfact/poisson/poisson_fastdg_3d.mini
index 46552ce9e960e8a53715016e3de0bd6e770b5425..cc2bd596d77cb6623a1b246f5d1f55ea12d51d76 100644
--- a/test/sumfact/poisson/poisson_fastdg_3d.mini
+++ b/test/sumfact/poisson/poisson_fastdg_3d.mini
@@ -20,6 +20,7 @@ sumfact = 1
 vectorization_quadloop = 1, 0 | expand quadvec
 vectorization_strategy = explicit, none | expand gradvec
 fastdg = 1
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 2
diff --git a/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini b/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini
index 438b8d009c56e42e9f65bd4b708e8f6acd1f6e06..ada6f4aed8cc1769b4793d8f1c6f7ee55ba2cbb3 100644
--- a/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini
+++ b/test/sumfact/poisson/poisson_fastdg_3d_gmsh.mini
@@ -7,7 +7,6 @@ deg_suffix = deg{formcompiler.ufl_variants.degree}
 
 {deg_suffix} == deg1 | exclude
 {quadvec_suffix} == quadvec | exclude
-# {gradvec_suffix} == gradvec | exclude
 
 gmshFile = cube_hexa.msh
 
@@ -27,6 +26,7 @@ fastdg = 1
 sumfact_regular_jacobians = 1
 vectorization_quadloop = 1, 0 | expand quad
 vectorization_strategy = model, none | expand grad
+geometry_mixins = sumfact_multilinear
 
 [formcompiler.ufl_variants]
 degree = 1, 2 | expand deg
diff --git a/test/sumfact/poisson/sliced.mini b/test/sumfact/poisson/sliced.mini
index 55b6fcf7df3b0ee33f06bb97da0e8b555104c161..29a8a6bdb02d2173bd0f327f49af32a22062cb90 100644
--- a/test/sumfact/poisson/sliced.mini
+++ b/test/sumfact/poisson/sliced.mini
@@ -17,6 +17,7 @@ vectorization_strategy = explicit
 vectorization_horizontal = 1
 vectorization_vertical = 4
 quadrature_order = 6
+geometry_mixins = sumfact_equidistant
 
 [formcompiler.ufl_variants]
 degree = 3
diff --git a/test/sumfact/stokes/stokes.mini b/test/sumfact/stokes/stokes.mini
index cfb89ec57d5504fca215ddc08d862e5e2484fc19..91b80c9971051768bed35342e47973380a0b4949 100644
--- a/test/sumfact/stokes/stokes.mini
+++ b/test/sumfact/stokes/stokes.mini
@@ -18,3 +18,4 @@ compare_l2errorsquared = 1e-12
 numerical_jacobian = 1, 0 | expand num
 vectorization_quadloop = 1, 0 | expand quad
 sumfact = 1
+geometry_mixins = sumfact_equidistant
diff --git a/test/sumfact/stokes/stokes_3d_dg.mini b/test/sumfact/stokes/stokes_3d_dg.mini
index 7fb1b22ed1de823afff15f519beb4aa474a3ec11..2f19c49b5309d6e2ea28d966096b169a64d2693f 100644
--- a/test/sumfact/stokes/stokes_3d_dg.mini
+++ b/test/sumfact/stokes/stokes_3d_dg.mini
@@ -17,4 +17,5 @@ compare_l2errorsquared = 1e-10
 [formcompiler.r]
 numerical_jacobian = 0
 sumfact = 1
-fastdg = 1, 0 | expand fastdg
\ No newline at end of file
+fastdg = 1, 0 | expand fastdg
+geometry_mixins = sumfact_equidistant
\ No newline at end of file
diff --git a/test/sumfact/stokes/stokes_dg.mini b/test/sumfact/stokes/stokes_dg.mini
index f34f23422ae888f7ea5d3085b41b87c2ac929346..c58cbc91e7207fba28a45d86de1aa0d892a3dea5 100644
--- a/test/sumfact/stokes/stokes_dg.mini
+++ b/test/sumfact/stokes/stokes_dg.mini
@@ -19,5 +19,6 @@ compare_l2errorsquared = 1e-8
 numerical_jacobian = 0, 1 | expand num
 sumfact = 1
 fastdg = 1, 0 | expand fastdg
+geometry_mixins = sumfact_equidistant
 
 {formcompiler.r.fastdg} == 1 and {formcompiler.r.numerical_jacobian} == 1 | exclude
\ No newline at end of file