diff --git a/dune/perftool/matrixfree.hh b/dune/perftool/matrixfree.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b5bf65ff1faacc89b4a1fd46b17bfec121572ed7
--- /dev/null
+++ b/dune/perftool/matrixfree.hh
@@ -0,0 +1,30 @@
+#ifndef DUNE_PERFTOOL_MATRIXFREE_HH
+#define DUNE_PERFTOOL_MATRIXFREE_HH
+
+#include <iostream>
+
+#include <dune/pdelab/backend/istl/seqistlsolverbackend.hh>
+
+namespace Dune{
+  namespace PDELab{
+
+    template <typename GO, typename V>
+    void solveMatrixFree(GO& go, V& x ){
+      typedef Dune::PDELab::OnTheFlyOperator<V,V,GO> ISTLOnTheFlyOperator;
+      ISTLOnTheFlyOperator opb(go);
+      Dune::Richardson<V,V> richardson(1.0);
+      Dune::BiCGSTABSolver<V> solverb(opb,richardson,1E-10,5000,2);
+      Dune::InverseOperatorResult stat;
+      // evaluate residual w.r.t initial guess
+      using TrialGridFunctionSpace = typename GO::Traits::TrialGridFunctionSpace;
+      using W = Dune::PDELab::Backend::Vector<TrialGridFunctionSpace,typename V::ElementType>;
+      W r(go.testGridFunctionSpace(),0.0);
+      go.residual(x,r);
+      // solve the jacobian system
+      V z(go.trialGridFunctionSpace(),0.0);
+      solverb.apply(z,r,stat);
+      x -= z;
+    }
+  }
+}
+#endif
diff --git a/python/dune/perftool/loopy/functions.py b/python/dune/perftool/loopy/functions.py
index 58bd1d1f71b537c753cb6c56c2d2e2a7580352b4..2338e33f19bbb022f5ef30654b36b94bfb0913e1 100644
--- a/python/dune/perftool/loopy/functions.py
+++ b/python/dune/perftool/loopy/functions.py
@@ -23,16 +23,15 @@ def lfs_child_mangler(target, func, dtypes):
 
 
 class CoefficientAccess(FunctionIdentifier):
-    def __init__(self, restriction):
-        self.restriction = restriction
+    def __init__(self, container):
+        self.container = container
 
     def __getinitargs__(self):
-        return (self.restriction,)
+        return (self.container,)
 
     @property
     def name(self):
-        from dune.perftool.pdelab.argument import name_coefficientcontainer
-        return name_coefficientcontainer(self.restriction)
+        return self.container
 
 
 def coefficient_mangler(target, func, dtypes):
diff --git a/python/dune/perftool/loopy/transformer.py b/python/dune/perftool/loopy/transformer.py
index 54938c547e06ccf743fdcac29594cd1eed7396da..630ed6e871963f0f2483282865586550ed955946 100644
--- a/python/dune/perftool/loopy/transformer.py
+++ b/python/dune/perftool/loopy/transformer.py
@@ -50,12 +50,13 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp
         # Initialize the local function spaces that we might need for this term
         # We therefore gather a list of modified trial functions too.
         from dune.perftool.ufl.modified_terminals import extract_modified_arguments
-        test_ma = extract_modified_arguments(o)
-        trial_ma = extract_modified_arguments(o, trialfunction=True, testfunction=False)
+        test_ma = extract_modified_arguments(o,)
+        trial_ma = extract_modified_arguments(o, coeffcount=0)
+        apply_ma = extract_modified_arguments(o, coeffcount=1)
 
         # All test and trial functions on boundary integrals are technically restricted
         import itertools
-        for ma in itertools.chain(test_ma, trial_ma):
+        for ma in itertools.chain(test_ma, trial_ma, apply_ma):
             if self.measure == 'exterior_facet':
                 ma.restriction = Restriction.NEGATIVE
 
@@ -209,21 +210,29 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp
             return Subscript(Variable(name_testfunction(leaf_element, restriction)), (Variable(iname),))
 
     def coefficient(self, o):
-        # If this is a trialfunction, we do something entirely different
-        if o.count() == 0:
+        # Do something different for trial function and coefficients from jacobian apply
+        if o.count() == 0 or o.count() == 1:
             # Correct the restriction on boundary integrals
             restriction = self.restriction
             if self.measure == 'exterior_facet':
                 restriction = Restriction.NEGATIVE
 
             if self.grad:
-                from dune.perftool.pdelab.argument import name_trialfunction_gradient
-                return Variable(name_trialfunction_gradient(o.ufl_element(), restriction, self.component))
+                if o.count() == 0:
+                    from dune.perftool.pdelab.argument import name_trialfunction_gradient
+                    return Variable(name_trialfunction_gradient(o.ufl_element(), restriction, self.component))
+                else:
+                    from dune.perftool.pdelab.argument import name_apply_function_gradient
+                    return Variable(name_apply_function_gradient(o.ufl_element(), restriction, self.component))
             else:
-                from dune.perftool.pdelab.argument import name_trialfunction
-                return Variable(name_trialfunction(o.ufl_element(), restriction, self.component))
-
-        # so this is a parameter function
+                if o.count() == 0:
+                    from dune.perftool.pdelab.argument import name_trialfunction
+                    return Variable(name_trialfunction(o.ufl_element(), restriction, self.component))
+                else:
+                    from dune.perftool.pdelab.argument import name_apply_function
+                    return Variable(name_apply_function(o.ufl_element(), restriction, self.component))
+
+        # Check if this is a parameter function
         else:
             # We expect all coefficients to be of type Expression or VectorExpression!
             from dune.perftool.ufl.execution import Expression, VectorExpression
diff --git a/python/dune/perftool/options.py b/python/dune/perftool/options.py
index ff563cfb2bd1a50af135571fc43a248009b26018..bbea79cfe4e129bc19fc3e96563dd2c34cc2ff43 100644
--- a/python/dune/perftool/options.py
+++ b/python/dune/perftool/options.py
@@ -13,6 +13,7 @@ def get_form_compiler_arguments():
     parser.add_argument("--operator-file", type=str, help="The filename for the generated local operator header")
     parser.add_argument('--version', action='version', version='%(prog)s 0.1')
     parser.add_argument("--numerical-jacobian", action="store_true", help="use numerical jacobians (only makes sense, if uflpdelab for some reason fails to generate analytic jacobians)")
+    parser.add_argument("--matrix-free", action="store_true", help="use iterative solver with matrix free jacobian application")
     parser.add_argument(
         "--exact-solution-expression",
         type=str,
diff --git a/python/dune/perftool/pdelab/argument.py b/python/dune/perftool/pdelab/argument.py
index 6ff8444d27df79f4822629de12d15310422448e6..dc3a508b0da97591af582b75119cfaca0134e161 100644
--- a/python/dune/perftool/pdelab/argument.py
+++ b/python/dune/perftool/pdelab/argument.py
@@ -5,8 +5,8 @@ from dune.perftool.ufl.modified_terminals import ModifiedArgumentDescriptor
 from dune.perftool.pdelab import (name_index,
                                   restricted_name,
                                   )
-from dune.perftool.pdelab.basis import (evaluate_trialfunction,
-                                        evaluate_trialfunction_gradient,
+from dune.perftool.pdelab.basis import (evaluate_coefficient,
+                                        evaluate_coefficient_gradient,
                                         lfs_iname,
                                         name_basis,
                                         name_basis_gradient,
@@ -35,7 +35,8 @@ def name_testfunction(element, restriction):
 def name_trialfunction_gradient(element, restriction, component):
     rawname = "gradu" + "_".join(str(c) for c in component)
     name = restricted_name(rawname, restriction)
-    evaluate_trialfunction_gradient(element, name, restriction, component)
+    container = name_coefficientcontainer(restriction)
+    evaluate_coefficient_gradient(element, name, container, restriction, component)
     return name
 
 
@@ -43,7 +44,26 @@ def name_trialfunction_gradient(element, restriction, component):
 def name_trialfunction(element, restriction, component):
     rawname = "u" + "_".join(str(c) for c in component)
     name = restricted_name(rawname, restriction)
-    evaluate_trialfunction(element, name, restriction, component)
+    container = name_coefficientcontainer(restriction)
+    evaluate_coefficient(element, name, container, restriction, component)
+    return name
+
+
+@symbol
+def name_apply_function_gradient(element, restriction, component):
+    rawname = "gradz_func" + "_".join(str(c) for c in component)
+    name = restricted_name(rawname, restriction)
+    container = name_applycontainer(restriction)
+    evaluate_coefficient_gradient(element, name, container, restriction, component)
+    return name
+
+
+@symbol
+def name_apply_function(element, restriction, component):
+    rawname = "z_func" + "_".join(str(c) for c in component)
+    name = restricted_name(rawname, restriction)
+    container = name_applycontainer(restriction)
+    evaluate_coefficient(element, name, container, restriction, component)
     return name
 
 
@@ -73,8 +93,14 @@ def name_coefficientcontainer(restriction):
     return name
 
 
+@symbol
+def name_applycontainer(restriction):
+    name = restricted_name("z", restriction)
+    return name
+
+
 @pymbolic_expr
-def pymbolic_coefficient(lfs, index, restriction):
+def pymbolic_coefficient(container, lfs, index):
     # TODO introduce a proper type for local function spaces!
     if isinstance(lfs, str):
         valuearg(lfs, dtype=loopy.types.NumpyType("str"))
@@ -85,7 +111,7 @@ def pymbolic_coefficient(lfs, index, restriction):
         lfs = Variable(lfs)
 
     from dune.perftool.loopy.functions import CoefficientAccess
-    return Call(CoefficientAccess(restriction), (lfs, Variable(index),))
+    return Call(CoefficientAccess(container), (lfs, Variable(index),))
 
 
 @symbol
@@ -101,7 +127,8 @@ def name_argumentspace(ma):
         if ma.argexpr.number() == 1:
             return name_trialfunctionspace(ma.restriction)
     if isinstance(ma.argexpr, Coefficient):
-        assert ma.argexpr.count() == 0
+        # Index 0 is reserved for trialfunction, index 1 is reserved for jacobian apply function
+        assert ma.argexpr.count() < 2
         return name_trialfunctionspace(ma.restriction)
     # We should never encounter an argument other than 0 or 1
     assert False
@@ -121,7 +148,6 @@ def name_jacobian(restriction1, restriction2):
     # Restrictions may only differ if NONE
     if (restriction1 == Restriction.NONE) or (restriction2 == Restriction.NONE):
         assert restriction1 == restriction2
-
     return restricted_name(restricted_name("jac", restriction1), restriction2)
 
 
@@ -143,7 +169,7 @@ def type_residual():
 def name_accumulation_variable(restrictions=(Restriction.NONE,)):
     from dune.perftool.generation import get_global_context_value
     ft = get_global_context_value("form_type")
-    if ft == 'residual':
+    if ft == 'residual' or ft == 'jacobian_apply':
         return name_residual(*restrictions)
     if ft == 'jacobian':
         return name_jacobian(*restrictions)
@@ -153,7 +179,7 @@ def name_accumulation_variable(restrictions=(Restriction.NONE,)):
 def type_accumulation_variable():
     from dune.perftool.generation import get_global_context_value
     ft = get_global_context_value("form_type")
-    if ft == 'residual':
+    if ft == 'residual' or ft == 'jacobian_apply':
         return type_residual()
     if ft == 'jacobian':
         return type_jacobian()
diff --git a/python/dune/perftool/pdelab/basis.py b/python/dune/perftool/pdelab/basis.py
index cacdb4cd07b1c8dccb01880b2b406d33d4e74b67..d6adc144be19f513a6f27830a9fa5483d9ce84e7 100644
--- a/python/dune/perftool/pdelab/basis.py
+++ b/python/dune/perftool/pdelab/basis.py
@@ -323,7 +323,7 @@ def shape_as_pymbolic(shape):
 
 
 @cached
-def evaluate_trialfunction(element, name, restriction, component):
+def evaluate_coefficient(element, name, container, restriction, component):
     from ufl.functionview import select_subelement
     sub_element = select_subelement(element, component)
 
@@ -348,7 +348,7 @@ def evaluate_trialfunction(element, name, restriction, component):
         lfs = lfs_child(lfs, idims, shape=shape_as_pymbolic(shape))
 
     from dune.perftool.pdelab.argument import pymbolic_coefficient
-    coeff = pymbolic_coefficient(lfs, index, restriction)
+    coeff = pymbolic_coefficient(container, lfs, index)
     assignee = Subscript(Variable(name), tuple(Variable(i) for i in idims))
 
     reduction_expr = Product((coeff, Subscript(Variable(basis), Variable(index))))
@@ -360,7 +360,7 @@ def evaluate_trialfunction(element, name, restriction, component):
 
 
 @cached
-def evaluate_trialfunction_gradient(element, name, restriction, component):
+def evaluate_coefficient_gradient(element, name, container, restriction, component):
     # First we determine the rank of the tensor we are talking about
     from ufl.functionview import select_subelement
     sub_element = select_subelement(element, component)
@@ -387,10 +387,11 @@ def evaluate_trialfunction_gradient(element, name, restriction, component):
         lfs = lfs_child(lfs, idims[:-1], shape=shape_as_pymbolic(shape[:-1]))
 
     from dune.perftool.pdelab.argument import pymbolic_coefficient
-    coeff = pymbolic_coefficient(lfs, index, restriction)
+    coeff = pymbolic_coefficient(container, lfs, index)
     assignee = Subscript(Variable(name), tuple(Variable(i) for i in idims))
 
     reduction_expr = Product((coeff, Subscript(Variable(basis), (Variable(index), Variable(idims[-1])))))
+
     instruction(expression=Reduction("sum", index, reduction_expr, allow_simultaneous=True),
                 assignee=assignee,
                 forced_iname_deps=frozenset({quadrature_iname()}).union(frozenset(idims)),
diff --git a/python/dune/perftool/pdelab/driver.py b/python/dune/perftool/pdelab/driver.py
index 9940aba70a83b17e606b3912bda72aa55ef4dd9a..ff87bd114c7ee54107f25ece4e5d0d83531e3c87 100644
--- a/python/dune/perftool/pdelab/driver.py
+++ b/python/dune/perftool/pdelab/driver.py
@@ -887,6 +887,7 @@ def define_stationarynonlinearproblemsolver(name):
     ls = name_linearsolver()
     return "{}  {}({}, {}, {});".format(snptype, name, go, x, ls)
 
+
 @symbol
 def name_stationarylinearproblemsolver():
     define_stationarylinearproblemsolver("slp")
@@ -903,11 +904,20 @@ def name_stationarynonlinearproblemsolver():
 def dune_solve():
     # Test if form is linear in ansatzfunction
     if is_linear(_form):
-        slp = name_stationarylinearproblemsolver()
-        return "{}.apply();".format(slp)
+        if get_option("matrix_free"):
+            go = name_gridoperator()
+            x = name_vector()
+            include_file("dune/perftool/matrixfree.hh", filetag="driver")
+            return "solveMatrixFree({},{});".format(go, x)
+        else:
+            slp = name_stationarylinearproblemsolver()
+            return "{}.apply();".format(slp)
     else:
-        snp = name_stationarynonlinearproblemsolver()
-        return "{}.apply();".format(snp)
+        if get_option("matrix_free"):
+            raise NotImplementedError("Nonlinear and matrix free is not yet implemented")
+        else:
+            snp = name_stationarynonlinearproblemsolver()
+            return "{}.apply();".format(snp)
 
 
 @preamble
@@ -1058,7 +1068,7 @@ def vtkoutput():
 
     if get_option("exact_solution_expression"):
         from ufl import MixedElement, VectorElement, TensorElement
-        if isinstance(_form.coefficients()[0].ufl_element(),  (MixedElement, VectorElement, TensorElement)):
+        if isinstance(_form.coefficients()[0].ufl_element(), (MixedElement, VectorElement, TensorElement)):
             raise NotImplementedError("Comparing to exact solution is olny implemented for scalar elements.")
 
         if get_option("compare_dofs"):
@@ -1066,6 +1076,5 @@ def vtkoutput():
         if get_option("compare_l2errorsquared"):
             compare_L2_squared()
 
-
     return ["Dune::PDELab::addSolutionToVTKWriter({}, {}, {}, Dune::PDELab::vtk::defaultNameScheme(), {});".format(vtkwriter, gfs, vec, predicate),
             "{}.write({}, Dune::VTK::ascii);".format(vtkwriter, vtkfile)]
diff --git a/python/dune/perftool/pdelab/localoperator.py b/python/dune/perftool/pdelab/localoperator.py
index bf4f93e0ede645c726d630841c1b76f6f3b20696..b539e14fdce6942cf288968e47cc2effc7928a65 100644
--- a/python/dune/perftool/pdelab/localoperator.py
+++ b/python/dune/perftool/pdelab/localoperator.py
@@ -36,7 +36,8 @@ def lop_template_gfs(ma):
         if ma.argexpr.number() == 1:
             return lop_template_ansatz_gfs()
     if isinstance(ma.argexpr, Coefficient):
-        assert ma.argexpr.count() == 0
+        # Index 0 is reserved for trialfunction, index 1 is reserved for jacobian apply function
+        assert ma.argexpr.count() < 2
         return lop_template_ansatz_gfs()
     assert False
 
@@ -150,6 +151,17 @@ def assembly_routine_signature():
             from dune.perftool.pdelab.signatures import jacobian_skeleton_signature
             return jacobian_skeleton_signature()
 
+    if form_type == 'jacobian_apply':
+        if integral_type == 'cell':
+            from dune.perftool.pdelab.signatures import jacobian_apply_volume_signature
+            return jacobian_apply_volume_signature()
+        if integral_type == 'exterior_facet':
+            from dune.perftool.pdelab.signatures import jacobian_apply_boundary_signature
+            return jacobian_apply_boundary_signature()
+        if integral_type == 'interior_facet':
+            from dune.perftool.pdelab.signatures import jacobian_apply_skeleton_signature
+            return jacobian_apply_skeleton_signature()
+
     assert False
 
 
@@ -319,16 +331,30 @@ def generate_localoperator_kernels(formdata, data):
 
                     _, loptype = class_type_from_cache("operator")
                     which = ufl_measure_to_pdelab_measure(measure)
+
+                    # Numerical jacobian methods
                     base_class("Dune::PDELab::NumericalJacobian{}<{}>".format(which, loptype), classtag="operator")
 
                     # Add the initializer list for that base class
                     ini = name_initree_member()
                     ini_constructor = name_initree_constructor_param()
+
+                    # Numerical jacobian methods
                     initializer_list("Dune::PDELab::NumericalJacobian{}<{}>".format(which, loptype),
                                      ["{}.get<double>(\"numerical_epsilon.{}\", 1e-9)".format(ini_constructor, ini, which.lower())],
                                      classtag="operator",
                                      )
 
+                    if get_option("matrix_free"):
+                        # Numeical jacobian apply base class
+                        base_class("Dune::PDELab::NumericalJacobianApply{}<{}>".format(which, loptype), classtag="operator")
+
+                        # Numerical jacobian apply initializer list
+                        initializer_list("Dune::PDELab::NumericalJacobianApply{}<{}>".format(which, loptype),
+                                         ["{}.get<double>(\"numerical_epsilon.{}\", 1e-9)".format(ini_constructor, ini, which.lower())],
+                                         classtag="operator",
+                                         )
+
                 operator_kernels[(measure, 'residual')] = kernel
 
     # Generate the necessary jacobian methods
@@ -351,7 +377,30 @@ def generate_localoperator_kernels(formdata, data):
             for it in alpha_measures - jacobian_measures:
                 operator_kernels[(it, 'jacobian')] = None
 
-        # TODO: JacobianApply for matrix-free computations.
+        # Jacobian apply methods for matrix-free computations
+        if get_option("matrix_free"):
+            # The apply vector has reserved index 1 so we directly use Coefficient class from ufl
+            from ufl import Coefficient
+            apply_coefficient = Coefficient(form.coefficients()[0].ufl_element(), 1)
+
+            # Create application of jacobian on vector
+            from ufl import action
+            jac_apply_form = action(jacform, apply_coefficient)
+
+            # Create kernel for jacobian application
+            with global_context(form_type="jacobian_apply"):
+                for measure in set(i.integral_type() for i in jac_apply_form.integrals()):
+                    with global_context(integral_type=measure):
+                        kernel = generate_kernel(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
+                    # We *could* solve this problem by using lambda_* terms but we do not really want that, so
+                    # we use empty jacobian assembly methods instead
+                    alpha_measures = set(i.integral_type() for i in form.integrals())
+                    jacobian_apply_measures = set(i.integral_type() for i in jac_apply_form.integrals())
+                    for it in alpha_measures - jacobian_apply_measures:
+                        operator_kernels[(it, 'jacobian_apply')] = None
 
     # Return the set of generated kernels
     return operator_kernels
diff --git a/python/dune/perftool/pdelab/signatures.py b/python/dune/perftool/pdelab/signatures.py
index b0e02593ba8975387c243ea999edec99fe183310..e778b5952ac299f852c4bb071c8e933a00a74207 100644
--- a/python/dune/perftool/pdelab/signatures.py
+++ b/python/dune/perftool/pdelab/signatures.py
@@ -9,6 +9,7 @@ from dune.perftool.pdelab.argument import (name_accumulation_variable,
                                            type_accumulation_variable,
                                            name_coefficientcontainer,
                                            type_coefficientcontainer,
+                                           name_applycontainer,
                                            name_testfunctionspace,
                                            type_testfunctionspace,
                                            name_trialfunctionspace,
@@ -236,3 +237,111 @@ def jacobian_skeleton_signature():
                                                                                                                                                                                     av_nn,
                                                                                                                                                                                     )
             ]
+
+
+@symbol
+def jacobian_apply_volume_signature():
+    geot = type_geometry_wrapper()
+    geo = name_geometry_wrapper()
+    lfsut = type_trialfunctionspace()
+    lfsu = name_trialfunctionspace(Restriction.NONE)
+    lfsvt = type_testfunctionspace()
+    lfsv = name_testfunctionspace(Restriction.NONE)
+    cct = type_coefficientcontainer()
+    ac = name_applycontainer(Restriction.NONE)
+    avt = type_accumulation_variable()
+    av = name_accumulation_variable((Restriction.NONE,))
+    return ['template<typename {}, typename {}, typename {}, typename {}, typename {}>'.format(geot,
+                                                                                               lfsut,
+                                                                                               cct,
+                                                                                               lfsvt,
+                                                                                               avt,
+                                                                                               ),
+            'void jacobian_apply_volume(const {}& {}, const {}& {}, const {}& {}, const {}& {}, {}& {}) const'.format(
+                geot,
+                geo,
+                lfsut,
+                lfsu,
+                cct,
+                ac,
+                lfsvt,
+                lfsv,
+                avt,
+                av,)
+            ]
+
+
+@symbol
+def jacobian_apply_boundary_signature():
+    geot = type_geometry_wrapper()
+    geo = name_geometry_wrapper()
+    lfsut = type_trialfunctionspace()
+    lfsu = name_trialfunctionspace(Restriction.NEGATIVE)
+    lfsvt = type_testfunctionspace()
+    lfsv = name_testfunctionspace(Restriction.NEGATIVE)
+    cct = type_coefficientcontainer()
+    ac = name_applycontainer(Restriction.NEGATIVE)
+    avt = type_accumulation_variable()
+    av = name_accumulation_variable((Restriction.NEGATIVE,))
+    return ['template<typename {}, typename {}, typename {}, typename {}, typename {}>'.format(geot,
+                                                                                               lfsut,
+                                                                                               cct,
+                                                                                               lfsvt,
+                                                                                               avt,
+                                                                                               ),
+            'void jacobian_apply_boundary(const {}& {}, const {}& {}, const {}& {}, const {}& {}, {}& {}) const'.format(
+                geot,
+                geo,
+                lfsut,
+                lfsu,
+                cct,
+                ac,
+                lfsvt,
+                lfsv,
+                avt,
+                av,)
+            ]
+
+
+@symbol
+def jacobian_apply_skeleton_signature():
+    geot = type_geometry_wrapper()
+    geo = name_geometry_wrapper()
+    lfsut = type_trialfunctionspace()
+    lfsu_s = name_trialfunctionspace(Restriction.NEGATIVE)
+    lfsu_n = name_trialfunctionspace(Restriction.POSITIVE)
+    lfsvt = type_testfunctionspace()
+    lfsv_s = name_testfunctionspace(Restriction.NEGATIVE)
+    lfsv_n = name_testfunctionspace(Restriction.POSITIVE)
+    cct = type_coefficientcontainer()
+    ac_s = name_applycontainer(Restriction.NEGATIVE)
+    ac_n = name_applycontainer(Restriction.POSITIVE)
+    avt = type_accumulation_variable()
+    av_s = name_accumulation_variable((Restriction.NEGATIVE,))
+    av_n = name_accumulation_variable((Restriction.POSITIVE,))
+    return ['template<typename {}, typename {}, typename {}, typename {}, typename {}>'.format(geot,
+                                                                                               lfsut,
+                                                                                               cct,
+                                                                                               lfsvt,
+                                                                                               avt,
+                                                                                               ),
+            'void jacobian_apply_skeleton(const {}& {}, const {}& {}, const {}& {}, const {}& {}, const {}& {}, const {}& {}, const {}& {}, {}& {}, {}& {}) const'.format(
+                geot,
+                geo,
+                lfsut,
+                lfsu_s,
+                cct,
+                ac_s,
+                lfsvt,
+                lfsv_s,
+                lfsut,
+                lfsu_n,
+                cct,
+                ac_n,
+                lfsvt,
+                lfsv_n,
+                avt,
+                av_s,
+                avt,
+                av_n,)
+            ]
diff --git a/python/dune/perftool/ufl/execution.py b/python/dune/perftool/ufl/execution.py
index 8018e8593cbc3cffb304a6aea1fa62f8423846ff..5fe6975b233a73a2629186489742f5e897b6d7f2 100644
--- a/python/dune/perftool/ufl/execution.py
+++ b/python/dune/perftool/ufl/execution.py
@@ -31,8 +31,10 @@ class Coefficient(ufl.Coefficient):
     def __init__(self, element, count=None):
         if count and count is 0:
             raise ValueError("The coefficient of index 0 is reserved for the trial function in uflpdelab")
-        if not count and ufl.Coefficient._globalcount is 0:
-            count = 1
+        if count and count is 1:
+            raise ValueError("The coefficient of index 1 is reserved for the jacobian apply vector in uflpdelab")
+        if not count and ufl.Coefficient._globalcount < 2:
+            count = 2
         ufl.Coefficient.__init__(self, element, count)
 
 
diff --git a/python/dune/perftool/ufl/modified_terminals.py b/python/dune/perftool/ufl/modified_terminals.py
index 27543551b6b218c299e4b82502a4e00c2cf1af47..82bb8eee94b2c0ec9e3a46835faae0e0dc610e70 100644
--- a/python/dune/perftool/ufl/modified_terminals.py
+++ b/python/dune/perftool/ufl/modified_terminals.py
@@ -103,10 +103,9 @@ class ModifiedArgumentDescriptor(MultiFunction):
 class _ModifiedArgumentExtractor(MultiFunction):
     """ A multifunction that extracts and returns the set of modified arguments """
 
-    def __call__(self, o, argnumber=None, testfunction=True, trialfunction=False):
+    def __call__(self, o, argnumber=None, coeffcount=None):
         self.argnumber = argnumber
-        self.trialfunction = trialfunction
-        self.testfunction = testfunction
+        self.coeffcount = coeffcount
         self.modified_arguments = set()
         ret = self.call(o)
         if ret:
@@ -132,14 +131,12 @@ class _ModifiedArgumentExtractor(MultiFunction):
     function_view = pass_on
 
     def argument(self, o):
-        if self.testfunction:
-            if self.argnumber is None or o.number() == self.argnumber:
-                return o
+        if self.argnumber is None or o.number() == self.argnumber:
+            return o
 
     def coefficient(self, o):
-        if self.trialfunction:
-            if o.count() == 0:
-                return o
+        if o.count() == self.coeffcount:
+            return o
 
     call = MultiFunction.__call__
 
diff --git a/test/nonlinear/CMakeLists.txt b/test/nonlinear/CMakeLists.txt
index a78cccd05c560d4ed69edf860d2cbbc25f3928b4..cd481d317a73263b0df08383db3e1ab6f6f2dc1d 100644
--- a/test/nonlinear/CMakeLists.txt
+++ b/test/nonlinear/CMakeLists.txt
@@ -3,7 +3,6 @@ add_generated_executable(UFLFILE nonlinear.ufl
                          FORM_COMPILER_ARGS
                          )
 
-
 dune_add_system_test(TARGET nonlinear_symdiff
                      INIFILE nonlinear_symdiff.mini
                      SCRIPT dune_vtkcompare.py
diff --git a/test/poisson/CMakeLists.txt b/test/poisson/CMakeLists.txt
index 88f33d3a7d5a8727eabec2abdf3d286bff882680..1a7aa445b5fc8cc706650cea3830dbce78b43559 100644
--- a/test/poisson/CMakeLists.txt
+++ b/test/poisson/CMakeLists.txt
@@ -88,6 +88,50 @@ dune_add_system_test(TARGET poisson_dg_neumann_symdiff
                      INIFILE poisson_dg_neumann_symdiff.mini
                      )
 
+# 9. Poisson Test Case: matrix free, numeric differentiation
+add_generated_executable(UFLFILE poisson.ufl
+                         TARGET poisson_numdiff_matrix_free
+                         FORM_COMPILER_ARGS --numerical-jacobian --matrix-free
+                         )
+
+dune_add_system_test(TARGET poisson_numdiff_matrix_free
+                     INIFILE poisson_numdiff_matrix_free.mini
+                     SCRIPT dune_vtkcompare.py
+                     )
+
+# 10. Poisson Test Case: matrix free, symbolic differentiation
+add_generated_executable(UFLFILE poisson.ufl
+                         TARGET poisson_symdiff_matrix_free
+                         FORM_COMPILER_ARGS --matrix-free
+                         )
+
+dune_add_system_test(TARGET poisson_symdiff_matrix_free
+                     INIFILE poisson_symdiff_matrix_free.mini
+                     SCRIPT dune_vtkcompare.py
+                     )
+
+# # 11. Poisson Test Case: dg, matrix free, numeric differentiation
+# add_generated_executable(UFLFILE poisson_dg.ufl
+#                          TARGET poisson_dg_numdiff_matrix_free
+#                          FORM_COMPILER_ARGS --numerical-jacobian --matrix-free
+#                          )
+
+# dune_add_system_test(TARGET poisson_dg_numdiff_matrix_free
+#                      INIFILE poisson_dg_numdiff_matrix_free.mini
+#                      SCRIPT dune_vtkcompare.py
+#                      )
+
+# # 12. Poisson Test Case: dg, matrix free, symbolic differentiation
+# add_generated_executable(UFLFILE poisson_dg.ufl
+#                          TARGET poisson_dg_symdiff_matrix_free
+#                          FORM_COMPILER_ARGS --matrix-free
+#                          )
+
+# dune_add_system_test(TARGET poisson_dg_symdiff_matrix_free
+#                      INIFILE poisson_dg_symdiff_matrix_free.mini
+#                      SCRIPT dune_vtkcompare.py
+#                      )
+
 # Add the reference code with the PDELab localoperator that produced
 # the reference vtk file
 add_executable(poisson_dg_ref reference_main.cc)
diff --git a/test/poisson/dimension-grid-variations/CMakeLists.txt b/test/poisson/dimension-grid-variations/CMakeLists.txt
index 13b52b6dc48b7b6b17bdd5a5a4b723f91de2954b..9f2229d084feb7e05baa3111ce4b6d4879269a5f 100644
--- a/test/poisson/dimension-grid-variations/CMakeLists.txt
+++ b/test/poisson/dimension-grid-variations/CMakeLists.txt
@@ -13,7 +13,7 @@ dune_add_system_test(TARGET poisson_1d_cg_interval
 
 add_generated_executable(UFLFILE poisson_1d_dg_interval.ufl
                          TARGET poisson_1d_dg_interval
-                         FORM_COMPILER_ARGS --exact-solution-expression g --compare-dofs 1e-2 --compare-l2errorsquared 1e-7
+                         FORM_COMPILER_ARGS --exact-solution-expression g --compare-dofs 1e-2 --compare-l2errorsquared 1e-5
                          )
 
 dune_add_system_test(TARGET poisson_1d_dg_interval
diff --git a/test/poisson/poisson_dg_numdiff_matrix_free.mini b/test/poisson/poisson_dg_numdiff_matrix_free.mini
new file mode 100644
index 0000000000000000000000000000000000000000..b45d4a8654e11107f8bbf111db0f996461085e2a
--- /dev/null
+++ b/test/poisson/poisson_dg_numdiff_matrix_free.mini
@@ -0,0 +1,11 @@
+__name = poisson_dg_numdiff_matrix_free
+
+lowerleft = 0.0 0.0
+upperright = 1.0 1.0
+elements = 32 32
+elementType = simplical
+
+[wrapper.vtkcompare]
+name = {__name}
+reference = poisson_dg_ref
+extension = vtu
diff --git a/test/poisson/poisson_dg_symdiff_matrix_free.mini b/test/poisson/poisson_dg_symdiff_matrix_free.mini
new file mode 100644
index 0000000000000000000000000000000000000000..c3fcaf7fcf1576850a8d1a99f4a1d8b07d0ddcd8
--- /dev/null
+++ b/test/poisson/poisson_dg_symdiff_matrix_free.mini
@@ -0,0 +1,11 @@
+__name = poisson_dg_symdiff_matrix_free
+
+lowerleft = 0.0 0.0
+upperright = 1.0 1.0
+elements = 32 32
+elementType = simplical
+
+[wrapper.vtkcompare]
+name = {__name}
+reference = poisson_dg_ref
+extension = vtu
diff --git a/test/poisson/poisson_numdiff_matrix_free.mini b/test/poisson/poisson_numdiff_matrix_free.mini
new file mode 100644
index 0000000000000000000000000000000000000000..4662d83881c20c13ffe49055338e1fc775020ab3
--- /dev/null
+++ b/test/poisson/poisson_numdiff_matrix_free.mini
@@ -0,0 +1,11 @@
+__name = poisson_numdiff_matrix_free
+
+lowerleft = 0.0 0.0
+upperright = 1.0 1.0
+elements = 32 32
+elementType = simplical
+
+[wrapper.vtkcompare]
+name = {__name}
+reference = poisson_ref
+extension = vtu
diff --git a/test/poisson/poisson_symdiff_matrix_free.mini b/test/poisson/poisson_symdiff_matrix_free.mini
new file mode 100644
index 0000000000000000000000000000000000000000..8d288ef3358fdda09706b80874e444d0ff5c8b2c
--- /dev/null
+++ b/test/poisson/poisson_symdiff_matrix_free.mini
@@ -0,0 +1,11 @@
+__name = poisson_symdiff_matrix_free
+
+lowerleft = 0.0 0.0
+upperright = 1.0 1.0
+elements = 32 32
+elementType = simplical
+
+[wrapper.vtkcompare]
+name = {__name}
+reference = poisson_ref
+extension = vtu