diff --git a/python/dune/perftool/loopy/transformer.py b/python/dune/perftool/loopy/transformer.py
index 78e872f6b3410c0323f0b91edeca496c7c7132cc..f3ea83ec0cde54935e734aafb93350e0a4ec837a 100644
--- a/python/dune/perftool/loopy/transformer.py
+++ b/python/dune/perftool/loopy/transformer.py
@@ -237,8 +237,8 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp
         # 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
-            assert isinstance(o, (Expression, VectorExpression))
+            from dune.perftool.ufl.execution import Expression
+            assert isinstance(o, Expression)
 
             # Determine the name of the parameter function
             from dune.perftool.generation import get_global_context_value
diff --git a/python/dune/perftool/pdelab/driver.py b/python/dune/perftool/pdelab/driver.py
index 57ae37c02406953ba799be1322cbc24f1ca42ac5..97f876eb06f35943aa32522cf749cacaff8d388d 100644
--- a/python/dune/perftool/pdelab/driver.py
+++ b/python/dune/perftool/pdelab/driver.py
@@ -423,9 +423,9 @@ def define_intersection_lambda(expression, name):
     if expression is None:
         return "auto {} = [&](const auto& x){{ return 0; }};".format(name)
     if expression.is_global:
-        return "auto {} = [&](const auto& x){{ {} }};".format(name, expression.c_expr)
+        return "auto {} = [&](const auto& x){{ {} }};".format(name, expression.c_expr[0])
     else:
-        return "auto {} = [&](const auto& is, const auto& x){{ {} }};".format(name, expression.c_expr)
+        return "auto {} = [&](const auto& is, const auto& x){{ {} }};".format(name, expression.c_expr[0])
 
 
 @symbol
@@ -741,9 +741,9 @@ def define_boundary_lambda(boundary, name):
     if boundary is None:
         return "auto {} = [&](const auto& x){{ return 0.0; }};".format(name)
     if boundary.is_global:
-        return "auto {} = [&](const auto& x){{ {} }};".format(name, boundary.c_expr)
+        return "auto {} = [&](const auto& x){{ {} }};".format(name, boundary.c_expr[0])
     else:
-        return "auto {} = [&](const auto& e, const auto& x){{ {} }};".format(name, boundary.c_expr)
+        return "auto {} = [&](const auto& e, const auto& x){{ {} }};".format(name, boundary.c_expr[0])
 
 
 @symbol
diff --git a/python/dune/perftool/pdelab/parameter.py b/python/dune/perftool/pdelab/parameter.py
index 79fede9c1eb72e20613b2db92cd6df8b1463050c..624f82dbaa97c4e39bc9ad0223e38613874b4ded 100644
--- a/python/dune/perftool/pdelab/parameter.py
+++ b/python/dune/perftool/pdelab/parameter.py
@@ -69,8 +69,22 @@ def define_set_time_method():
     return result
 
 
+def component_to_tree_path(element, component):
+    subel = element.extract_subelement_component(component)
+
+    def _flatten(x):
+        if isinstance(x, tuple):
+            return '_'.join(_flatten(i) for i in x if i != ())
+        else:
+            return str(x)
+
+    return _flatten(subel)
+
+
 @class_member("parameterclass", access=AccessModifier.PUBLIC)
-def define_parameter_function_class_member(name, expr, t, cell):
+def define_parameter_function_class_member(name, expr, baset, shape, cell):
+    t = construct_nested_fieldvector(baset, shape)
+
     geot = "E" if cell else "I"
     geo = geot.lower()
     result = ["template<typename {}, typename X>".format(geot),
@@ -78,12 +92,32 @@ def define_parameter_function_class_member(name, expr, t, cell):
               "{",
               ]
 
-    if expr.is_global:
-        result.append("  auto x = {}.geometry().global(local);".format(geo))
+    # In the case of a non-scalar parameter function, recurse into leafs
+    if expr.element.value_shape():
+        # Check that this is a VectorElement, as I have no idea how a parameter function
+        # over a non-vector mixed element should be well-defined in PDELab.
+        from ufl import VectorElement
+        assert isinstance(expr.element, VectorElement)
+
+        result.append("  {} result(0.0);".format(t))
+
+        from dune.perftool.ufl.execution import split_expression
+        for i, subexpr in enumerate(split_expression(expr)):
+            child_name = "{}_{}".format(name, component_to_tree_path(expr.element, i))
+            result.append("  result[{}] = {}({}, local);".format(i, child_name, geo))
+            define_parameter_function_class_member(child_name, subexpr, baset, shape[1:], cell)
+
+        result.append("  return result;")
+
     else:
-        result.append("  auto x = local;")
+        # Evaluate a scalar parameter function
+        if expr.is_global:
+            result.append("  auto x = {}.geometry().global(local);".format(geo))
+        else:
+            result.append("  auto x = local;")
+
+        result.append("  " + expr.c_expr[0])
 
-    result.append("  " + expr.c_expr)
     result.append("}")
 
     return result
@@ -175,8 +209,7 @@ def construct_nested_fieldvector(t, shape):
 def cell_parameter_function(name, expr, restriction, cellwise_constant, t='double'):
     shape = expr.ufl_element().value_shape()
     shape_impl = ('fv',) * len(shape)
-    t = construct_nested_fieldvector(t, shape)
-    define_parameter_function_class_member(name, expr, t, True)
+    define_parameter_function_class_member(name, expr, t, shape, True)
     if cellwise_constant:
         from dune.perftool.generation.loopy import default_declaration
         default_declaration(name, shape, shape_impl)
@@ -190,8 +223,7 @@ def cell_parameter_function(name, expr, restriction, cellwise_constant, t='doubl
 def intersection_parameter_function(name, expr, cellwise_constant, t='double'):
     shape = expr.ufl_element().value_shape()
     shape_impl = ('fv',) * len(shape)
-    t = construct_nested_fieldvector(t, shape)
-    define_parameter_function_class_member(name, expr, t, False)
+    define_parameter_function_class_member(name, expr, t, shape, False)
     if cellwise_constant:
         from dune.perftool.generation.loopy import default_declaration
         default_declaration(name, shape, shape_impl)
diff --git a/python/dune/perftool/ufl/execution.py b/python/dune/perftool/ufl/execution.py
index fb0aa6b5d008dd028f3d7903c3b57a43ed728842..8bce0a7bfb7d3eb33b079d74ed67849639091722 100644
--- a/python/dune/perftool/ufl/execution.py
+++ b/python/dune/perftool/ufl/execution.py
@@ -38,7 +38,10 @@ class Coefficient(ufl.Coefficient):
         ufl.Coefficient.__init__(self, element, count)
 
 
-split = ufl.split_functions.split2
+def split(obj):
+    if isinstance(obj, Expression):
+        return split_expression(obj)
+    return ufl.split_functions.split2(obj)
 
 
 def Coefficients(element):
@@ -54,42 +57,60 @@ def TrialFunctions(element):
 
 
 class Expression(Coefficient):
-    def __init__(self, expr, is_global=True, on_intersection=False, cell_type="triangle", cellwise_constant=False):
-        assert isinstance(expr, str)
-        self.c_expr = expr
-        self.is_global = is_global
-        self.on_intersection = on_intersection
-
-        # Avoid ufl complaining about not matching dimension/cells
-        if cellwise_constant:
-            _dg = FiniteElement("DG", cell_type, 0)
-        else:
-            _dg = FiniteElement("DG", cell_type, 1)
+    def __init__(self, cppcode=None, element=None, cell="triangle", degree=None, is_global=True, on_intersection=False, cellwise_constant=False):
+        assert cppcode
 
-        # Initialize a coefficient with a dummy finite element map.
-        Coefficient.__init__(self, _dg)
+        if isinstance(cppcode, str):
+            cppcode = (cppcode,)
 
-    # TODO the subdomain_data code needs a uflid, not idea how to get it here
-    # The standard way through class decorator fails here...
-    def ufl_id(self):
-        return 0
+        def wrap_return(e):
+            if "return" not in e:
+                return "return {};".format(e)
+            else:
+                return e
 
+        cppcode = tuple(wrap_return(e) for e in cppcode)
 
-class VectorExpression(Coefficient):
-    def __init__(self, expr, is_global=True, on_intersection=False, cell_type="triangle", cellwise_constant=False):
-        assert isinstance(expr, str)
-        self.c_expr = expr
+        if cellwise_constant:
+            if element:
+                assert element.degree() == 0
+            elif degree is not None:
+                assert degree == 0
+            else:
+                element = FiniteElement("DG", cell, 0)
+
+        if degree is None:
+            degree = 1
+
+        if element is None:
+            if len(cppcode) == 1:
+                element = FiniteElement("DG", cell, degree)
+            else:
+                element = VectorElement("DG", cell, degree, len(cppcode))
+
+        def element_length(elem):
+            if isinstance(elem, ufl.FiniteElement):
+                return 1
+            else:
+                return elem.value_shape()[0]
+
+        assert element_length(element) == len(cppcode)
+
+        self.c_expr = cppcode
         self.is_global = is_global
         self.on_intersection = on_intersection
-
-        # Avoid ufl complaining about not matching dimension/cells
-        if cellwise_constant:
-            _dgvec = VectorElement("DG", cell_type, 0)
-        else:
-            _dgvec = VectorElement("DG", cell_type, 1)
+        self.element = element
 
         # Initialize a coefficient with a dummy finite element map.
-        Coefficient.__init__(self, _dgvec)
+        Coefficient.__init__(self, element)
+
+    def __mul__(self, other):
+        # Allow the combination of Expressions
+        if isinstance(other, Expression):
+            from ufl import MixedElement
+            return Expression(cppcode=self.c_expr + other.c_expr, element=self.element * other.element)
+        else:
+            return Coefficient.__mul__(self, other)
 
     # TODO the subdomain_data code needs a uflid, not idea how to get it here
     # The standard way through class decorator fails here...
@@ -97,6 +118,19 @@ class VectorExpression(Coefficient):
         return 0
 
 
+def split_expression(expr):
+    assert isinstance(expr, Expression)
+
+    def element_slice(expression, sub):
+        offset = sum(subel.value_size() for subel in expression.element.sub_elements()[:sub])
+        return expression.c_expr[offset:offset + expr.element.sub_elements()[sub].value_size()]
+
+    return tuple(Expression(cppcode=element_slice(expr, i),
+                            element=expr.element.sub_elements()[i])
+                 for i in range(expr.element.num_sub_elements())
+                 )
+
+
 class FiniteElement(ufl.FiniteElement):
     def __init__(self, *args, **kwargs):
         if ('dirichlet_constraints' in kwargs) or ('dirichlet_expression' in kwargs):
diff --git a/test/poisson/dimension-grid-variations/poisson_1d_cg_interval.ufl b/test/poisson/dimension-grid-variations/poisson_1d_cg_interval.ufl
index 864810e8a0dd87330992901f0badaf53c9eedbe3..6bda534f16236d66b351c0ac1c0671a41f03f9de 100644
--- a/test/poisson/dimension-grid-variations/poisson_1d_cg_interval.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_1d_cg_interval.ufl
@@ -1,9 +1,9 @@
-cell_type = "interval"
+cell = "interval"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", cell=cell)
 
-V = FiniteElement("CG", cell_type, 1, dirichlet_expression=g)
+V = FiniteElement("CG", cell, 1, dirichlet_expression=g)
 u = TrialFunction(V)
 v = TestFunction(V)
 
diff --git a/test/poisson/dimension-grid-variations/poisson_1d_dg_interval.ufl b/test/poisson/dimension-grid-variations/poisson_1d_dg_interval.ufl
index f7775baf11b5225addc511a2016ddaac8ad0d9fe..907f19841d51695f3944617ff162b33fce7b5053 100644
--- a/test/poisson/dimension-grid-variations/poisson_1d_dg_interval.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_1d_dg_interval.ufl
@@ -1,14 +1,14 @@
-cell_type = "interval"
+cell = "interval"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", on_intersection=True, cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", on_intersection=True, cell=cell)
 
-V = FiniteElement("DG", cell_type, 1)
+V = FiniteElement("DG", cell, 1)
 
 u = TrialFunction(V)
 v = TestFunction(V)
 
-n = FacetNormal(cell_type)('+')
+n = FacetNormal(cell)('+')
 
 gamma = 1.0
 theta = 1.0
diff --git a/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.ufl b/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.ufl
index 7d43c7eb3ee86e180ad51443d2d4bb370e79e4c2..5a2c570583a2fe200e91a1dbf20b0dde699ec156 100644
--- a/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_2d_cg_quadrilateral.ufl
@@ -1,9 +1,9 @@
-cell_type = "quadrilateral"
+cell = "quadrilateral"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", cell=cell)
 
-V = FiniteElement("CG", cell_type, 1, dirichlet_expression=g)
+V = FiniteElement("CG", cell, 1, dirichlet_expression=g)
 u = TrialFunction(V)
 v = TestFunction(V)
 
diff --git a/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.ufl b/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.ufl
index 24dbccae56b8b93ae305b561e1ac5025d3a53bd7..1ca65e1eaf16d4ff71444bd71626c277b82328c9 100644
--- a/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_2d_dg_quadrilateral.ufl
@@ -1,14 +1,14 @@
-cell_type = "quadrilateral"
+cell = "quadrilateral"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", on_intersection=True, cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", on_intersection=True, cell=cell)
 
-V = FiniteElement("DG", cell_type, 1)
+V = FiniteElement("DG", cell, 1)
 
 u = TrialFunction(V)
 v = TestFunction(V)
 
-n = FacetNormal(cell_type)('+')
+n = FacetNormal(cell)('+')
 
 gamma = 1.0
 theta = 1.0
diff --git a/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.ufl b/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.ufl
index 5c0091d74b1e9349fd6631b18b2ffdfbd42e990d..4cad67e31f354a7eb0c4c6d25b95713f31067612 100644
--- a/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_3d_cg_hexahedron.ufl
@@ -1,9 +1,9 @@
-cell_type = "hexahedron"
+cell = "hexahedron"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", cell=cell)
 
-V = FiniteElement("CG", cell_type, 1, dirichlet_expression=g)
+V = FiniteElement("CG", cell, 1, dirichlet_expression=g)
 u = TrialFunction(V)
 v = TestFunction(V)
 
diff --git a/test/poisson/dimension-grid-variations/poisson_3d_cg_tetrahedron.ufl b/test/poisson/dimension-grid-variations/poisson_3d_cg_tetrahedron.ufl
index 1456671d43ce0de2d3bfbf8d1ca13fda9f8e0733..d8f43c0941267480a07eba857b93d635571af43d 100644
--- a/test/poisson/dimension-grid-variations/poisson_3d_cg_tetrahedron.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_3d_cg_tetrahedron.ufl
@@ -1,7 +1,7 @@
-cell_type = "tetrahedron"
+cell = "tetrahedron"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", cell=cell)
 
 V = FiniteElement("CG", "tetrahedron", 1, dirichlet_expression=g)
 u = TrialFunction(V)
diff --git a/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.ufl b/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.ufl
index 3a410e3011596ab6372028e1e705d26cd5747216..bf5cc370a5651c1a408d2f1b6687d4c3255c0172 100644
--- a/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_3d_dg_hexahedron.ufl
@@ -1,14 +1,14 @@
-cell_type = "hexahedron"
+cell = "hexahedron"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", on_intersection=True, cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", on_intersection=True, cell=cell)
 
-V = FiniteElement("DG", cell_type, 1)
+V = FiniteElement("DG", cell, 1)
 
 u = TrialFunction(V)
 v = TestFunction(V)
 
-n = FacetNormal(cell_type)('+')
+n = FacetNormal(cell)('+')
 
 gamma = 1.0
 theta = 1.0
diff --git a/test/poisson/dimension-grid-variations/poisson_3d_dg_tetrahedron.ufl b/test/poisson/dimension-grid-variations/poisson_3d_dg_tetrahedron.ufl
index a2d8135baa6d8f6556dcff607989af38a9ffa826..4c73c7debf2ac3a5f81941d2e6d12cb5ee4ff4fa 100644
--- a/test/poisson/dimension-grid-variations/poisson_3d_dg_tetrahedron.ufl
+++ b/test/poisson/dimension-grid-variations/poisson_3d_dg_tetrahedron.ufl
@@ -1,14 +1,14 @@
-cell_type = "tetrahedron"
+cell = "tetrahedron"
 
-f = Expression("return -2.0*x.size();", cell_type=cell_type)
-g = Expression("return x.two_norm2();", on_intersection=True, cell_type=cell_type)
+f = Expression("return -2.0*x.size();", cell=cell)
+g = Expression("return x.two_norm2();", on_intersection=True, cell=cell)
 
-V = FiniteElement("DG", cell_type, 1)
+V = FiniteElement("DG", cell, 1)
 
 u = TrialFunction(V)
 v = TestFunction(V)
 
-n = FacetNormal(cell_type)('+')
+n = FacetNormal(cell)('+')
 
 gamma = 1.0
 theta = 1.0
diff --git a/test/stokes/stokes.ufl b/test/stokes/stokes.ufl
index 9252834820f67c72a6b28f08b6b1bd1b164ad04c..e88a011ad8f96852c9a7af2d2cdcde5b835e50da 100644
--- a/test/stokes/stokes.ufl
+++ b/test/stokes/stokes.ufl
@@ -1,8 +1,11 @@
 v_bctype = Expression("if (x[0] < 1. - 1e-8) return 1; else return 0;", on_intersection=True)
-v_dirichlet = Expression("Dune::FieldVector<double, 2> y(0.0); y[0] = 4*x[1]*(1.-x[1]); return y;")
+g_v = Expression(("4*x[1]*(1.-x[1])", "0.0"))
+g_p = Expression("8*x[0]")
+
+g = g_v * g_p
 
 cell = triangle
-P2 = VectorElement("Lagrange", cell, 2, dirichlet_constraints=v_bctype, dirichlet_expression=v_dirichlet)
+P2 = VectorElement("Lagrange", cell, 2, dirichlet_constraints=v_bctype, dirichlet_expression=g_v)
 P1 = FiniteElement("Lagrange", cell, 1)
 TH = P2 * P1
 
diff --git a/test/stokes/stokes_dg.ufl b/test/stokes/stokes_dg.ufl
index 7b15e24ce48c5aac90f274581d3e13f44ae80fb5..b63893f0fe98622e1b24019e930026c615074010 100644
--- a/test/stokes/stokes_dg.ufl
+++ b/test/stokes/stokes_dg.ufl
@@ -1,4 +1,6 @@
-g = VectorExpression("Dune::FieldVector<double, 2> y(0.0); y[0]=4*x[1]*(1.-x[1]); return y;", on_intersection=True)
+g_v = Expression(("4*x[1]*(1.-x[1])", "0.0"), on_intersection=True)
+g_p = Expression("8*(1.-x[0])")
+g = g_v * g_p
 bctype = Expression("if (x[0] < 1. - 1e-8) return 1; else return 0;", on_intersection=True)
 
 cell = triangle
@@ -19,15 +21,15 @@ r = inner(grad(u), grad(v))*dx \
   + inner(avg(grad(u))*n, jump(v))*dS \
   - eps * inner(avg(grad(v))*n, jump(u))*dS \
   - inner(grad(u)*n, v)*ds \
-  + eps * inner(grad(v)*n, u-g)*ds \
+  + eps * inner(grad(v)*n, u-g_v)*ds \
   + sigma * inner(jump(u), jump(v))*dS \
-  + sigma * inner(u-g, v)*ds \
+  + sigma * inner(u-g_v, v)*ds \
   - p*div(v)*dx \
   - avg(p)*inner(jump(v), n)*dS \
   + p*inner(v, n)*ds \
   - q*div(u)*dx \
   - avg(q)*inner(jump(u), n)*dS \
   + q*inner(u, n)*ds \
-  - q*inner(g, n)*ds
+  - q*inner(g_v, n)*ds
 
 forms = [r]