Repository: kinnala/scikit-fem

Issue:
Test ElementVector(ElementVector(...))
The following has not been tested but should work:

```python
In [1]: from skfem import *                                                     

In [2]: m = MeshHex().refined()                                                 

In [3]: e = ElementVector(ElementVector(ElementHex1()))                         

In [4]: e                                                                       
Out[4]: <skfem.element.element_vector.ElementVector at 0x7f0e795acdc0>

In [5]: basis = InteriorBasis(m, e)                                             

In [6]: from skfem.helpers import ddot                                          

In [7]: A = asm(BilinearForm(lambda u, v, w: ddot(u, v)), basis)                

In [8]: A                                                                       
Out[8]: 
<243x243 sparse matrix of type '<class 'numpy.float64'>'
	with 3087 stored elements in Compressed Sparse Row format>
```

_Originally posted by @kinnala in https://github.com/kinnala/scikit-fem/issues/619#issuecomment-817656881_

Comments:

Patch:
diff --git a/docs/examples/ex03.py b/docs/examples/ex03.py
index 5d236923..6df35d63 100644
--- a/docs/examples/ex03.py
+++ b/docs/examples/ex03.py
@@ -1,23 +1,25 @@
 """Linear elastic eigenvalue problem."""
 
 from skfem import *
-from skfem.helpers import dot
-from skfem.models.elasticity import linear_elasticity
+from skfem.helpers import dot, ddot, sym_grad
+from skfem.models.elasticity import linear_elasticity, linear_stress
 import numpy as np
 
 m1 = MeshLine(np.linspace(0, 5, 50))
 m2 = MeshLine(np.linspace(0, 1, 10))
-m = m1*m2
+m = m1 * m2
 
 e1 = ElementQuad1()
 
 mapping = MappingIsoparametric(m, e1)
 
-e = ElementVectorH1(e1)
+e = ElementVector(e1)
 
 gb = Basis(m, e, mapping, 2)
 
-K = asm(linear_elasticity(1.0, 1.0), gb)
+lam = 1.
+mu = 1.
+K = asm(linear_elasticity(lam, mu), gb)
 
 @BilinearForm
 def mass(u, v, w):
@@ -25,17 +27,30 @@ def mass(u, v, w):
 
 M = asm(mass, gb)
 
-D = gb.find_dofs({'': m.facets_satisfying(lambda x: x[0]==0.0)})
+D = gb.find_dofs({'left': m.facets_satisfying(lambda x: x[0] == 0.)})
 y = gb.zeros()
 
 I = gb.complement_dofs(D)
 
-L, x = solve(*condense(K, M, I=I), solver=solver_eigen_scipy_sym(k=6, sigma=0.0))
+L, x = solve(*condense(K, M, I=I),
+             solver=solver_eigen_scipy_sym(k=6, sigma=0.0))
 
 y = x[:, 4]
 
+# calculate stress
+sgb = gb.with_element(ElementVector(e))
+C = linear_stress(lam, mu)
+yi = gb.interpolate(y)
+
+@LinearForm
+def proj(v, _):
+    return ddot(C(sym_grad(yi)), v)
+
+sigma = projection(proj, sgb, gb)
+
 if __name__ == "__main__":
-    from skfem.visuals.matplotlib import draw, show
+    from skfem.visuals.matplotlib import plot, draw, show
     M = MeshQuad(np.array(m.p + y[gb.nodal_dofs]), m.t)
-    draw(M)
+    ax = draw(M)
+    plot(M, sigma[sgb.nodal_dofs[0]], ax=ax, colorbar=True)
     show()
diff --git a/docs/listofexamples.rst b/docs/listofexamples.rst
index dfb96f1e..808add85 100644
--- a/docs/listofexamples.rst
+++ b/docs/listofexamples.rst
@@ -39,7 +39,7 @@ This example solves the linear elastic eigenvalue problem
 :math:`\mathrm{div}\,\sigma(u)= \lambda u` with
 the displacement fixed on the left hand side boundary.
 
-.. figure:: https://user-images.githubusercontent.com/973268/87661134-cbec2b00-c768-11ea-81bc-f5455df7cc33.png
+.. figure:: https://user-images.githubusercontent.com/973268/134467300-f7e635ed-39c4-4a36-9e98-aadb6e51961a.png
 
    The fifth eigenmode of Example 3.
 
diff --git a/skfem/utils.py b/skfem/utils.py
index b43d0557..d3d23878 100644
--- a/skfem/utils.py
+++ b/skfem/utils.py
@@ -606,13 +606,23 @@ def projection(fun,
 
     @BilinearForm
     def mass(u, v, w):
-        p = u * v
-        return sum(p) if isinstance(basis_to.elem, ElementVector) else p
-
-    @LinearForm
-    def funv(v, w):
-        p = fun(w.x) * v
-        return sum(p) if isinstance(basis_to.elem, ElementVector) else p
+        from skfem.helpers import dot, ddot
+        p = 0
+        if len(u.value.shape) == 2:
+            p = u * v
+        elif len(u.value.shape) == 3:
+            p = dot(u, v)
+        elif len(u.value.shape) == 4:
+            p = ddot(u, v)
+        return p
+
+    if isinstance(fun, LinearForm):
+        funv = fun
+    else:
+        @LinearForm
+        def funv(v, w):
+            p = fun(w.x) * v
+            return sum(p) if isinstance(basis_to.elem, ElementVector) else p
 
     @BilinearForm
     def deriv(u, v, w):


Test patch:
diff --git a/tests/test_assembly.py b/tests/test_assembly.py
index bdf242ef..eed9a7f6 100644
--- a/tests/test_assembly.py
+++ b/tests/test_assembly.py
@@ -13,14 +13,15 @@ from skfem.element import (ElementQuad1, ElementQuadS2, ElementHex1,
                            ElementTriMorley, ElementVectorH1, ElementQuadP,
                            ElementHex2, ElementTriArgyris, ElementTriP2,
                            ElementTriDG, ElementQuadDG, ElementHexDG,
-                           ElementTetDG, ElementTriHermite)
+                           ElementTetDG, ElementTriHermite, ElementVector)
 from skfem.mesh import (MeshQuad, MeshHex, MeshTet, MeshTri, MeshQuad2,
                         MeshTri2, MeshTet2, MeshHex2, MeshTri1DG, MeshQuad1DG,
                         MeshHex1DG)
 from skfem.assembly import FacetBasis, Basis
 from skfem.utils import projection
 from skfem.models import laplace, unit_load, mass
-from skfem.helpers import grad, dot
+from skfem.helpers import grad, dot, ddot, sym_grad
+from skfem.models import linear_stress
 
 
 class IntegrateOneOverBoundaryQ1(TestCase):
@@ -564,6 +565,30 @@ def test_trilinear_form(m, e):
         assert abs((opt1[i] - opt2).max()) < 1e-10
 
 
+@pytest.mark.parametrize(
+    "m,e",
+    [
+        (MeshTri(), ElementTriP1()),
+        (MeshTri(), ElementTriP2()),
+        (MeshTet(), ElementTetP1()),
+    ]
+)
+def test_matrix_element_projection(m, e):
+
+    E1 = ElementVector(e)
+    E2 = ElementVector(ElementVector(e))
+    basis0 = Basis(m, E1)
+    basis1 = basis0.with_element(E2)
+    C = linear_stress()
+
+    x = basis0.interpolate(np.random.random(basis0.N))
+
+    @LinearForm
+    def proj(v, _):
+        return ddot(C(sym_grad(x)), v)
+
+    y = projection(proj, basis1, basis0)
+
 
 if __name__ == '__main__':
     main()
diff --git a/tests/test_elements.py b/tests/test_elements.py
index aa0024f2..f48ca9a5 100644
--- a/tests/test_elements.py
+++ b/tests/test_elements.py
@@ -361,14 +361,9 @@ def test_dg_element(m, e, edg):
     "e,edg",
     [
         (ElementTriP1(), ElementTriDG),
-        (ElementTriP2(), ElementTriDG),
-        (ElementTetP1(), ElementTetDG),
         (ElementTetP2(), ElementTetDG),
         (ElementTriArgyris(), ElementTriDG),
-        (ElementTriMorley(), ElementTriDG),
-        (ElementTriHermite(), ElementTriDG),
         (ElementQuad1(), ElementQuadDG),
-        (ElementQuad2(), ElementQuadDG),
         (ElementQuadP(4), ElementQuadDG),
         (ElementHex2(), ElementHexDG),
     ]