From 5871149ae3941d656cd44d743425a7da1a3f1cb5 Mon Sep 17 00:00:00 2001
From: Dominic Kempf <dominic.kempf@iwr.uni-heidelberg.de>
Date: Wed, 5 Dec 2018 09:56:44 +0100
Subject: [PATCH] Generic PDELab stuff is working again

---
 .../dune/codegen/blockstructured/__init__.py  |  34 --
 .../codegen/blockstructured/accumulation.py   |   2 +-
 python/dune/codegen/blockstructured/basis.py  |   1 -
 .../codegen/blockstructured/quadrature.py     |  11 +-
 python/dune/codegen/blockstructured/tools.py  |   4 +-
 python/dune/codegen/generation/__init__.py    |   4 +-
 python/dune/codegen/generation/mixins.py      |  15 +-
 python/dune/codegen/options.py                |   4 +-
 python/dune/codegen/pdelab/__init__.py        |  46 +--
 python/dune/codegen/pdelab/argument.py        |  35 --
 python/dune/codegen/pdelab/basis.py           | 310 +++++++++++-------
 python/dune/codegen/pdelab/geometry.py        | 138 ++++----
 python/dune/codegen/pdelab/localoperator.py   |  14 +-
 python/dune/codegen/pdelab/quadrature.py      | 206 +++++-------
 python/dune/codegen/sumfact/__init__.py       |   5 -
 python/dune/codegen/sumfact/quadrature.py     |  12 +
 python/dune/codegen/ufl/visitor.py            |  23 +-
 test/poisson/poisson_dg_quadrilateral.mini    |   1 +
 18 files changed, 404 insertions(+), 461 deletions(-)

diff --git a/python/dune/codegen/blockstructured/__init__.py b/python/dune/codegen/blockstructured/__init__.py
index 138b1146..fe3eb1f7 100644
--- a/python/dune/codegen/blockstructured/__init__.py
+++ b/python/dune/codegen/blockstructured/__init__.py
@@ -5,7 +5,6 @@ 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)
@@ -27,36 +26,3 @@ class BlockStructuredInterface(PDELabInterface):
             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 469e69b4..0f54fbe9 100644
--- a/python/dune/codegen/blockstructured/accumulation.py
+++ b/python/dune/codegen/blockstructured/accumulation.py
@@ -52,7 +52,7 @@ def generate_accumulation_instruction(expr, visitor):
 
     predicates = boundary_predicates(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
diff --git a/python/dune/codegen/blockstructured/basis.py b/python/dune/codegen/blockstructured/basis.py
index dcaf258b..3477a182 100644
--- a/python/dune/codegen/blockstructured/basis.py
+++ b/python/dune/codegen/blockstructured/basis.py
@@ -17,7 +17,6 @@ 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.restriction import restricted_name
 from dune.codegen.blockstructured.spaces import lfs_inames
diff --git a/python/dune/codegen/blockstructured/quadrature.py b/python/dune/codegen/blockstructured/quadrature.py
index 4c78d733..64282c68 100644
--- a/python/dune/codegen/blockstructured/quadrature.py
+++ b/python/dune/codegen/blockstructured/quadrature.py
@@ -1,10 +1,17 @@
-from dune.codegen.generation import (backend)
-from dune.codegen.pdelab.quadrature import (name_quadrature_points,
+from dune.codegen.generation import (backend,
+                                     quadrature_mixin,
+                                     )
+from dune.codegen.pdelab.quadrature import (GenericQuadratureMixin,
                                             quadrature_iname)
 from dune.codegen.blockstructured.geometry import name_point_in_macro
 import pymbolic.primitives as prim
 
 
+@quadrature_mixin("blockstructured")
+class BlockstructuredQuadratureMixin(GenericQuadratureMixin):
+    pass
+
+
 @backend(interface="quad_pos", name='blockstructured')
 def pymbolic_quadrature_position():
     quad_points = name_quadrature_points()
diff --git a/python/dune/codegen/blockstructured/tools.py b/python/dune/codegen/blockstructured/tools.py
index 3eafcae5..e3150804 100644
--- a/python/dune/codegen/blockstructured/tools.py
+++ b/python/dune/codegen/blockstructured/tools.py
@@ -6,10 +6,8 @@ from dune.codegen.generation import (iname,
                                      instruction)
 from dune.codegen.pdelab.geometry import (local_dimension,
                                           world_dimension,
-                                          name_localcenter,
-                                          pymbolic_in_cell_coordinates)
+                                          name_localcenter,)
 
-from dune.codegen.pdelab.quadrature import quadrature_inames
 from dune.codegen.generation.counter import get_counted_variable
 from dune.codegen.options import get_form_option
 import pymbolic.primitives as prim
diff --git a/python/dune/codegen/generation/__init__.py b/python/dune/codegen/generation/__init__.py
index cf729760..ac8de9f4 100644
--- a/python/dune/codegen/generation/__init__.py
+++ b/python/dune/codegen/generation/__init__.py
@@ -59,6 +59,8 @@ from dune.codegen.generation.context import (cache_restoring,
                                              get_global_context_value,
                                              )
 
-from dune.codegen.generation.mixins import (construct_from_mixins,
+from dune.codegen.generation.mixins import (basis_mixin,
+                                            construct_from_mixins,
                                             geometry_mixin,
+                                            quadrature_mixin,
                                             )
diff --git a/python/dune/codegen/generation/mixins.py b/python/dune/codegen/generation/mixins.py
index a8a7e72b..ce267972 100644
--- a/python/dune/codegen/generation/mixins.py
+++ b/python/dune/codegen/generation/mixins.py
@@ -1,9 +1,12 @@
 """ The infrastructure for registered mixin classes """
 
+from functools import partial
+
+
 _mixin_registry = {}
 
 
-def mixin_base(name, mixintype):
+def mixin_base(mixintype, name):
     _mixin_registry.setdefault(mixintype, {})
 
     def _dec(cls):
@@ -13,10 +16,12 @@ def mixin_base(name, mixintype):
     return _dec
 
 
-def geometry_mixin(name):
-    return mixin_base(name, "geometry")
-
-
 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")
\ No newline at end of file
diff --git a/python/dune/codegen/options.py b/python/dune/codegen/options.py
index fcb5d9ed..8526339b 100644
--- a/python/dune/codegen/options.py
+++ b/python/dune/codegen/options.py
@@ -55,7 +55,9 @@ class CodegenGlobalOptionsArray(ImmutableRecord):
     target_name = CodegenOption(default=None, helpstr="The target name from CMake")
     operator_to_build = CodegenOption(default=None, helpstr="The operators from the list that is about to be build now. CMake sets this one!!!")
     debug_interpolate_input = CodegenOption(default=False, helpstr="Should the input for printresidual and printmatix be interpolated (instead of random input).")
-    geometry = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for geometries. Currently implemented mixins: generic, axiparallel, equidistant")
+    geometry_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for geometries. Currently implemented mixins: generic, axiparallel, equidistant")
+    quadrature_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for quadrature. Currently implemented: generic")
+    basis_mixins = CodegenOption(default="generic", helpstr="A comma separated list of mixin identifiers to use for basis function evaluation. Currently implemented: generic")
 
     # Arguments that are mainly to be set by logic depending on other options
     max_vector_width = CodegenOption(default=256, helpstr=None)
diff --git a/python/dune/codegen/pdelab/__init__.py b/python/dune/codegen/pdelab/__init__.py
index 42915eac..604cacf0 100644
--- a/python/dune/codegen/pdelab/__init__.py
+++ b/python/dune/codegen/pdelab/__init__.py
@@ -4,21 +4,9 @@
 # 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.argument import pymbolic_coefficient
 from dune.codegen.pdelab.function import pymbolic_gridfunction
 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,
@@ -70,27 +58,6 @@ class PDELabInterface(object):
     #
     # 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)
 
@@ -106,14 +73,3 @@ class PDELabInterface(object):
 
     def pymbolic_matrix_inverse(self, o):
         return pymbolic_matrix_inverse(o, self.visitor)
-
-    #
-    # 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/argument.py b/python/dune/codegen/pdelab/argument.py
index 8972c32c..f9c6c56b 100644
--- a/python/dune/codegen/pdelab/argument.py
+++ b/python/dune/codegen/pdelab/argument.py
@@ -15,9 +15,6 @@ from dune.codegen.generation import (domain,
                                      )
 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 +86,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
diff --git a/python/dune/codegen/pdelab/basis.py b/python/dune/codegen/pdelab/basis.py
index 6b6afb3b..c88ab605 100644
--- a/python/dune/codegen/pdelab/basis.py
+++ b/python/dune/codegen/pdelab/basis.py
@@ -1,6 +1,7 @@
 """ Generators for basis evaluations """
 
 from dune.codegen.generation import (backend,
+                                     basis_mixin,
                                      class_member,
                                      get_backend,
                                      include_file,
@@ -12,6 +13,7 @@ from dune.codegen.generation import (backend,
 from dune.codegen.options import (option_switch,
                                   get_form_option,
                                   )
+from dune.codegen.pdelab.argument import name_coefficientcontainer
 from dune.codegen.pdelab.spaces import (lfs_iname,
                                         lfs_inames,
                                         name_leaf_lfs,
@@ -22,7 +24,6 @@ from dune.codegen.pdelab.spaces import (lfs_iname,
                                         )
 from dune.codegen.pdelab.geometry import (component_iname,
                                           world_dimension,
-                                          to_cell_coordinates,
                                           name_cell,
                                           )
 from dune.codegen.pdelab.localoperator import (lop_template_ansatz_gfs,
@@ -38,12 +39,195 @@ 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
 
 
+@basis_mixin("base")
+class BasisMixinBase(object):
+    def lfs_inames(self, *args):
+        raise NotImplementedError("Basis Mixins should implement local function space inames")
+
+    def implement_basis(self, *args):
+        raise NotImplementedError("Basis Mixins should implement the basis")
+
+    def implement_reference_gradient(self, *args):
+        raise NotImplementedError("Basis Mixins should implement the basis gradient")
+
+    def implement_trialfunction(self, *args):
+        raise NotImplementedError("Basis Mixins should implement trial function evaluation")
+
+    def implement_trialfunction_gradient(self, *args):
+        raise NotImplementedError("Basis Mixins should implement trial function gradient evaluation")
+
+    def implement_apply_function(self, *args):
+        raise NotImplementedError("Basis Mixins should implement linearization point evaluation")
+
+    def implement_apply_function_gradient(self, *args):
+        raise NotImplementedError("Basis Mixins should implement linearization point gradient evaluation")
+
+
+@basis_mixin("generic")
+class GenericBasisMixin(BasisMixinBase):
+    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 rif 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,
+                    )
+
+
 @backend(interface="typedef_localbasis")
 @class_member(classtag="operator")
 def typedef_localbasis(element, name):
@@ -93,62 +277,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):
@@ -156,71 +284,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/geometry.py b/python/dune/codegen/pdelab/geometry.py
index ba59e267..0efd5a27 100644
--- a/python/dune/codegen/pdelab/geometry.py
+++ b/python/dune/codegen/pdelab/geometry.py
@@ -18,13 +18,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 (pymbolic_quadrature_position,
-                                            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
 
@@ -38,25 +36,74 @@ 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 unhandled(self, o):
+    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 = unhandled
-    facet_normal = unhandled
-    jacobian_determinant = unhandled
-    jacobian_inverse = unhandled
-    facet_jacobian_determinant = unhandled
-    cell_volume = unhandled
-    facet_area = unhandled
+    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 GenericPDELabGeometry(object):
+class GenericPDELabGeometryMixin(GeometryMixinBase):
     def spatial_coordinate(self, o):
-        return to_global(pymbolic_quadrature_position())
+        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"
@@ -76,13 +123,14 @@ class GenericPDELabGeometry(object):
     def define_jacobian_determinant(self, name):
         temporary_variable(name, shape=())
         geo = name_geometry()
-        pos = get_backend("quad_pos", selector=option_switch("blockstructured"))()
+        pos = self.quadrature_position()
         code = "{} = {}.integrationElement({});".format(name,
                                                         geo,
                                                         str(pos),
                                                         )
 
-        return quadrature_preamble(code,
+        return quadrature_preamble(self,
+                                   code,
                                    assignees=frozenset({name}),
                                    read_variables=frozenset({get_pymbolic_basename(pos)}),
                                    depends_on=frozenset({Writes(get_pymbolic_basename(pos))}),
@@ -104,9 +152,10 @@ class GenericPDELabGeometry(object):
         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)
+        pos = self.to_cell(self.quadrature_position())
 
-        return quadrature_preamble("{} = {}.jacobianInverseTransposed({});".format(name,
+        return quadrature_preamble(self,
+                                   "{} = {}.jacobianInverseTransposed({});".format(name,
                                                                                    geo,
                                                                                    str(pos),
                                                                                    ),
@@ -127,8 +176,9 @@ class GenericPDELabGeometry(object):
         temporary_variable(name, shape=(world_dimension(),), decl_method=declare_normal)
 
         ig = name_intersection_geometry_wrapper()
-        qp = get_backend("quad_pos")()
-        return quadrature_preamble("{} = {}.unitOuterNormal({});".format(name, ig, qp),
+        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))}),
@@ -412,35 +462,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")]
@@ -481,24 +502,3 @@ def define_jacobian_inverse_transposed_temporary(restriction):
                                name,
                                )
     return _define_jacobian_inverse_transposed_temporary
-
-
-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)
diff --git a/python/dune/codegen/pdelab/localoperator.py b/python/dune/codegen/pdelab/localoperator.py
index 44446de5..252b7f8b 100644
--- a/python/dune/codegen/pdelab/localoperator.py
+++ b/python/dune/codegen/pdelab/localoperator.py
@@ -425,7 +425,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)
@@ -452,8 +452,16 @@ def get_visitor(measure, subdomain_id):
 
     # Construct the visitor taking into account geometry mixins
     from dune.codegen.ufl.visitor import UFL2LoopyVisitor
-    mixins = get_option("geometry").split(",")
-    VisitorType = construct_from_mixins(base=UFL2LoopyVisitor, mixins=mixins, name="UFLVisitor")
+    mixins = get_option("geometry_mixins").split(",")
+    VisitorType = construct_from_mixins(base=UFL2LoopyVisitor, mixins=mixins, mixintype="geometry", name="UFLVisitor")
+
+    # Mix quadrature mixins in
+    mixins = get_option("quadrature_mixins").split(",")
+    VisitorType = construct_from_mixins(base=VisitorType, mixins=mixins, mixintype="quadrature", name="UFLVisitor")
+
+    # Mix basis mixins in
+    mixins = get_option("basis_mixins").split(",")
+    VisitorType = construct_from_mixins(base=VisitorType, mixins=mixins, mixintype="basis", name="UFLVisitor")
 
     return VisitorType(interface, measure, subdomain_id)
 
diff --git a/python/dune/codegen/pdelab/quadrature.py b/python/dune/codegen/pdelab/quadrature.py
index 8c3e2b36..8066423b 100644
--- a/python/dune/codegen/pdelab/quadrature.py
+++ b/python/dune/codegen/pdelab/quadrature.py
@@ -10,13 +10,100 @@ from dune.codegen.generation import (backend,
                                      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):
+        raise NotImplementedError("Quadrature mixins should provide the quadrature position")
+
+    def quadrature_position_in_cell(self, restriction):
+        raise NotImplementedError("Quadrature mixins hsould provide the embedding of the quadrature rule into neighboring cells")
+
+
+@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):
+        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)
+
+        return prim.Subscript(prim.Variable(name), tuple(prim.Variable(i) for i in self.quadrature_inames()))
+
+    @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/sumfact/__init__.py b/python/dune/codegen/sumfact/__init__.py
index f887ceda..5dc7f3b1 100644
--- a/python/dune/codegen/sumfact/__init__.py
+++ b/python/dune/codegen/sumfact/__init__.py
@@ -60,11 +60,6 @@ class SumFactInterface(PDELabInterface):
         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)
 
diff --git a/python/dune/codegen/sumfact/quadrature.py b/python/dune/codegen/sumfact/quadrature.py
index eedd85da..d5181a28 100644
--- a/python/dune/codegen/sumfact/quadrature.py
+++ b/python/dune/codegen/sumfact/quadrature.py
@@ -8,6 +8,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 +21,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 +41,16 @@ import loopy as lp
 import numpy as np
 
 
+@quadrature_mixin("sumfact")
+class SumfactQuadratureMixin(GenericQuadratureMixin):
+    def quadrature_inames(self):
+        element = self.trial_info
+        if self.trial_info is not None:
+            element = element.element
+
+        return quadrature_inames(element)
+
+
 class BaseWeight(FunctionIdentifier):
     def __init__(self, accumobj):
         self.accumobj = accumobj
diff --git a/python/dune/codegen/ufl/visitor.py b/python/dune/codegen/ufl/visitor.py
index 9c54018e..10afd7bf 100644
--- a/python/dune/codegen/ufl/visitor.py
+++ b/python/dune/codegen/ufl/visitor.py
@@ -131,9 +131,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
@@ -158,14 +158,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:
@@ -421,14 +421,7 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
         return prim.LogicalNot(self.call(o.ufl_operands[0]))
 
     #
-    # Handlers for quadrature related stuff
-    #
-
-    def quadrature_weight(self, o):
-        return self.interface.pymbolic_quadrature_weight()
-
-    #
-    # NB: Geometry related handlers have been moved into configurable mixin classes!
+    # NB: Geometry and quadrature related handlers have been moved into configurable mixin classes!
     #
 
     #
@@ -439,7 +432,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/test/poisson/poisson_dg_quadrilateral.mini b/test/poisson/poisson_dg_quadrilateral.mini
index dde2e495..162e2479 100644
--- a/test/poisson/poisson_dg_quadrilateral.mini
+++ b/test/poisson/poisson_dg_quadrilateral.mini
@@ -11,6 +11,7 @@ extension = vtu
 
 [formcompiler]
 compare_l2errorsquared = 7e-7
+geometry = equidistant
 
 [formcompiler.r]
 numerical_jacobian = 1, 0 | expand num
-- 
GitLab