diff --git a/CMakeLists.txt b/CMakeLists.txt
index cc9322f017781ff734fe82ad5489092c76b4bc06..2ac234249ef7768010ee7c2f3cb3ef3077032e20 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,6 +26,7 @@ add_subdirectory(src)
 add_subdirectory(dune)
 add_subdirectory(doc)
 add_subdirectory(cmake/modules)
+add_subdirectory(test)
 
 
 # finalize the dune project, e.g. generating config.h etc.
diff --git a/dune/microstructure/matrix_operations.hh b/dune/microstructure/matrix_operations.hh
index 3cd76beddfeda08b1749c784afbe77ee6a9f40bb..ab0a59332e73849964923ba388c31dadff9c4a0e 100644
--- a/dune/microstructure/matrix_operations.hh
+++ b/dune/microstructure/matrix_operations.hh
@@ -116,7 +116,7 @@ namespace MatrixOperations {
 						{         0,               0,  1.0}};
 			}
 			default:
-				DUNE_THROW(Dune::Exception, " axis not feasible. rotationMatrix is only implemented for 3x3-matrices.");
+				DUNE_THROW(Dune::Exception, " axis not feasible. rotationMatrix is only implemented for 3x3-matrices. Choose between 0: x-axis, 1: y-axis, 2: z-axis");
 		}
 	}
 
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f2e918562b1e2ea68456621c709640e767a8ec12
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(TESTS
+    parametrizedlaminatetest
+    orthotropicrotationtest)
+
+foreach(_test ${TESTS})
+dune_add_test(SOURCES ${_test}.cc)
+add_dune_pythonlibs_flags(${_test})
+target_compile_options(${_test} PRIVATE -g)
+endforeach()
+
+
+# Copy the parsets used for testing into the build dir
+# file(COPY parsets/parametrizedlaminate.parset DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/parsets)
+# file(COPY parsets/orthotropicrotation_1.parset DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/parsets)
+# file(COPY parsets/orthotropicrotation_2.parset DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/parsets)
+
+
+  
\ No newline at end of file
diff --git a/test/orthotropicrotation_1.parset b/test/orthotropicrotation_1.parset
new file mode 100644
index 0000000000000000000000000000000000000000..f337f16b1aff991e24cd50ba300199eb382dad87
--- /dev/null
+++ b/test/orthotropicrotation_1.parset
@@ -0,0 +1,20 @@
+# --- Choose material definition:
+materialFunction = "orthotropicrotation_1"
+
+# --- Choose scale ratio gamma:
+gamma=1.0
+
+#############################################
+#  Solver Type: #1: CG - SOLVER , #2: GMRES - SOLVER, #3: QR - SOLVER (default), #4: UMFPACK - SOLVER
+#############################################
+
+# Note: Running the test with the QR-Solver (3) on gitlab 
+#       fails due to completely wrong results. 
+#       WHY is currently unclear.
+
+Solvertype = 2         # recommended to use iterative solver (e.g GMRES) for finer grid-levels
+#Solver_verbosity = 0  #(default = 2)  degree of information for solver output
+
+#print_conditionNumber = true
+#set_IntegralZero = true            #(default = false)
+#set_oneBasisFunction_Zero = true   #(default = false)
\ No newline at end of file
diff --git a/test/orthotropicrotation_1.py b/test/orthotropicrotation_1.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d15aba6bc3ac5d02c1e560b3dac830f29ebe6c5
--- /dev/null
+++ b/test/orthotropicrotation_1.py
@@ -0,0 +1,53 @@
+import math
+import ctypes
+import os
+import sys
+import numpy as np
+#---------------------------------------------------------------
+
+#--- define indicator function
+def indicatorFunction(x):
+    if  x[2] > 0.25:
+        return 1    #Phase1
+    elif (abs(x[2]) < 0.25):
+        return 2    #Phase2
+    else:
+        return 3    #Phase3
+
+########### Options for material phases: #################################
+#     1. "isotropic"     2. "orthotropic"      3. "transversely_isotropic"   4. "general_anisotropic"
+#########################################################################
+## Notation - Parameter input :
+# isotropic (Lame parameters) : [mu , lambda]
+#         orthotropic         : [E1,E2,E3,G12,G23,G31,nu12,nu13,nu23]   # see https://en.wikipedia.org/wiki/Poisson%27s_ratio with x=1,y=2,z=3
+# transversely_isotropic      : [E1,E2,G12,nu12,nu23]
+# general_anisotropic         : full compliance matrix C
+######################################################################
+
+# --- Number of material phases
+Phases=3
+
+#--- Define different material phases:
+#- PHASE 1
+phase1_type="orthotropic"
+materialParameters_phase1 = [11.2e3,630,1190,700,230,960,0.63,0.49,0.37]  # Walnut parameters (values for compliance matrix) see [Dimwoodie; Timber its nature and behavior p.109]
+
+#- PHASE 2
+phase2_type="orthotropic"
+materialParameters_phase2 = [16.3e3,620,1100,910,190,1180,0.43,0.49,0.38]  # Birch parameters (values for compliance matrix) see [Dimwoodie; Timber its nature and behavior p.109]
+ 
+#- PHASE 3
+phase3_type="orthotropic"
+materialParameters_phase3 = [10.7e3,430,710,620,23,500,0.51,0.38,0.31]   # Norway spruce parameters (values for compliance matrix) see [Dimwoodie; Timber its nature and behavior p.109]
+
+
+#--- define prestrain function for each phase
+# (also works with non-constant values)
+def prestrain_phase1(x):
+    return [[2, 0, 0], [0,2,0], [0,0,2]]
+
+def prestrain_phase2(x):
+    return [[1, 0, 0], [0,1,0], [0,0,1]]
+
+def prestrain_phase3(x):
+    return [[0, 0, 0], [0,0,0], [0,0,0]]
diff --git a/test/orthotropicrotation_2.parset b/test/orthotropicrotation_2.parset
new file mode 100644
index 0000000000000000000000000000000000000000..4eabf4cdf0d377d75a79ac41eeda1bd30d6dd23a
--- /dev/null
+++ b/test/orthotropicrotation_2.parset
@@ -0,0 +1,20 @@
+# --- Choose material definition:
+materialFunction = "orthotropicrotation_2"
+
+# --- Choose scale ratio gamma:
+gamma=1.0
+
+#############################################
+#  Solver Type: #1: CG - SOLVER , #2: GMRES - SOLVER, #3: QR - SOLVER (default), #4: UMFPACK - SOLVER
+#############################################
+
+# Note: Running the test with the QR-Solver (3) on gitlab 
+#       fails due to completely wrong results. 
+#       WHY is currently unclear.
+
+Solvertype = 2         # recommended to use iterative solver (e.g GMRES) for finer grid-levels
+#Solver_verbosity = 0  #(default = 2)  degree of information for solver output
+
+#print_conditionNumber = true
+#set_IntegralZero = true            #(default = false)
+#set_oneBasisFunction_Zero = true   #(default = false)
\ No newline at end of file
diff --git a/test/orthotropicrotation_2.py b/test/orthotropicrotation_2.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a325f52cbe46fd628ce3ea7ec793448655e188c
--- /dev/null
+++ b/test/orthotropicrotation_2.py
@@ -0,0 +1,59 @@
+import math
+import ctypes
+import os
+import sys
+import numpy as np
+#---------------------------------------------------------------
+
+#--- define indicator function
+def indicatorFunction(x):
+    if  x[2] > 0.25:
+        return 1    #Phase1
+    elif (abs(x[2]) < 0.25):
+        return 2    #Phase2
+    else:
+        return 3    #Phase3
+
+########### Options for material phases: #################################
+#     1. "isotropic"     2. "orthotropic"      3. "transversely_isotropic"   4. "general_anisotropic"
+#########################################################################
+## Notation - Parameter input :
+# isotropic (Lame parameters) : [mu , lambda]
+#         orthotropic         : [E1,E2,E3,G12,G23,G31,nu12,nu13,nu23]   # see https://en.wikipedia.org/wiki/Poisson%27s_ratio with x=1,y=2,z=3
+# transversely_isotropic      : [E1,E2,G12,nu12,nu23]
+# general_anisotropic         : full compliance matrix C
+######################################################################
+
+# --- Number of material phases
+Phases=3
+
+# *Rotate material frame of each phase by 180 degrees (pi)
+
+#--- Define different material phases:
+#- PHASE 1
+phase1_type="orthotropic"
+materialParameters_phase1 = [11.2e3,630,1190,700,230,960,0.63,0.49,0.37]  # Walnut parameters (values for compliance matrix) see [Dimwoodie; Timber its nature and behavior p.109]
+phase1_axis = 0
+phase1_angle = np.pi
+
+#- PHASE 2
+phase2_type="orthotropic"
+materialParameters_phase2 = [16.3e3,620,1100,910,190,1180,0.43,0.49,0.38]  # Birch parameters (values for compliance matrix) see [Dimwoodie; Timber its nature and behavior p.109]
+phase2_axis = 1
+phase2_angle = np.pi
+#- PHASE 3
+phase3_type="orthotropic"
+materialParameters_phase3 = [10.7e3,430,710,620,23,500,0.51,0.38,0.31]   # Norway spruce parameters (values for compliance matrix) see [Dimwoodie; Timber its nature and behavior p.109]
+phase3_axis = 2
+phase3_angle = np.pi
+
+#--- define prestrain function for each phase
+# (also works with non-constant values)
+def prestrain_phase1(x):
+    return [[2, 0, 0], [0,2,0], [0,0,2]]
+
+def prestrain_phase2(x):
+    return [[1, 0, 0], [0,1,0], [0,0,1]]
+
+def prestrain_phase3(x):
+    return [[0, 0, 0], [0,0,0], [0,0,0]]
diff --git a/test/orthotropicrotationtest.cc b/test/orthotropicrotationtest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..962652fa4760272289c99f9f7c04cbd598c13946
--- /dev/null
+++ b/test/orthotropicrotationtest.cc
@@ -0,0 +1,283 @@
+#include <config.h>
+#include <array>
+#include <vector>
+#include <fstream>
+
+#include <iostream>
+#include <dune/common/indices.hh>
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.hh>
+#include <dune/common/float_cmp.hh>
+#include <dune/common/math.hh>
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/grid/uggrid.hh>
+#include <dune/grid/yaspgrid.hh>
+#include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh>
+
+#include <dune/istl/matrix.hh>
+#include <dune/istl/bcrsmatrix.hh>
+#include <dune/istl/multitypeblockmatrix.hh>
+#include <dune/istl/multitypeblockvector.hh>
+#include <dune/istl/matrixindexset.hh>
+#include <dune/istl/solvers.hh>
+#include <dune/istl/spqr.hh>
+#include <dune/istl/preconditioners.hh>
+#include <dune/istl/io.hh>
+
+#include <dune/functions/functionspacebases/interpolate.hh>
+#include <dune/functions/backends/istlvectorbackend.hh>
+#include <dune/functions/functionspacebases/powerbasis.hh>
+#include <dune/functions/functionspacebases/compositebasis.hh>
+#include <dune/functions/functionspacebases/lagrangebasis.hh>
+#include <dune/functions/functionspacebases/periodicbasis.hh>
+#include <dune/functions/functionspacebases/subspacebasis.hh>
+#include <dune/functions/functionspacebases/boundarydofs.hh>
+#include <dune/functions/gridfunctions/discreteglobalbasisfunction.hh>
+#include <dune/functions/gridfunctions/gridviewfunction.hh>
+
+#include <dune/common/fvector.hh>
+#include <dune/common/fmatrix.hh>
+
+#include <dune/microstructure/matrix_operations.hh>
+#include <dune/microstructure/CorrectorComputer.hh>
+#include <dune/microstructure/EffectiveQuantitiesComputer.hh>
+#include <dune/microstructure/prestrainedMaterial.hh>
+
+#include <dune/solvers/solvers/umfpacksolver.hh>
+#include <dune/istl/eigenvalue/test/matrixinfo.hh>
+
+#include <dune/fufem/dunepython.hh>
+
+#include <any>
+#include <variant>
+#include <string>
+#include <iomanip>
+#include <filesystem>
+
+using namespace Dune;
+using namespace MatrixOperations;
+
+
+/**
+ * @brief  Compute effective quantities for a trilayer setup with 3 orthotropic phases:
+ *         Once with a canonical orthonormal material frame and once with a material frame 
+ *         that is rotated pi(180 degree) around different axes.
+ *         Aim of the test is to check the correctness of the material frame transformation.   
+ */
+
+
+//////////////////////////////////////////////////
+//   Infrastructure for handling periodicity
+//////////////////////////////////////////////////
+// Check whether two points are equal on R/Z x R/Z x R
+auto equivalent = [](const FieldVector<double,3>& x, const FieldVector<double,3>& y)
+                  {
+                    return ( (FloatCmp::eq(x[0],y[0]) or FloatCmp::eq(x[0]+1,y[0]) or FloatCmp::eq(x[0]-1,y[0]))
+                             and (FloatCmp::eq(x[1],y[1]) or FloatCmp::eq(x[1]+1,y[1]) or FloatCmp::eq(x[1]-1,y[1]))
+                             and (FloatCmp::eq(x[2],y[2]))
+                             );
+                  };
+
+
+int main(int argc, char *argv[])
+{
+  MPIHelper::instance(argc, argv);
+
+  Dune::Timer globalTimer;
+  //--- setup Log-File 
+  std::fstream log;
+
+  std::cout << "Current path is " << std::filesystem::current_path() << '\n';
+  std::filesystem::path file_path = (__FILE__);
+  std::cout<< "File path: " << file_path<<std::endl;
+  std::cout << "dir_path: " << file_path.parent_path() << std::endl;
+  std::string dir_path  = file_path.parent_path();
+
+  ParameterTree parameterSet;
+  if (argc < 2)
+    ParameterTreeParser::readINITree(dir_path + "/orthotropicrotation_1.parset", parameterSet);
+  else
+  {
+    ParameterTreeParser::readINITree(argv[1], parameterSet);
+    ParameterTreeParser::readOptions(argc, argv, parameterSet);
+  }
+
+  //--- Start Python interpreter
+  Python::start();
+  Python::Reference main = Python::import("__main__");
+  Python::run("import math");
+  Python::runStream()
+    << std::endl << "import sys"
+    << std::endl << "sys.path.append('" << dir_path  << "')"
+    << std::endl;
+
+
+  constexpr int dim = 3;
+  constexpr int dimWorld = 3;
+  ///////////////////////////////////
+  // Generate the grid
+  ///////////////////////////////////
+  // --- Corrector Problem Domain (-1/2,1/2)^3:
+  FieldVector<double,dim> lower({-1.0/2.0, -1.0/2.0, -1.0/2.0});
+  FieldVector<double,dim> upper({1.0/2.0, 1.0/2.0, 1.0/2.0});
+
+  int gridLevel = 3;
+
+  std::array<int, dim> nElements = {(int)std::pow(2,gridLevel) ,(int)std::pow(2,gridLevel) ,(int)std::pow(2,gridLevel)};
+  std::cout << "Number of Grid-Elements in each direction: " << nElements << std::endl;
+
+  using CellGridType = YaspGrid<dim, EquidistantOffsetCoordinates<double, dim> >;
+  CellGridType grid_CE(lower,upper,nElements);
+  using GridView = CellGridType::LeafGridView;
+  const GridView gridView_CE = grid_CE.leafGridView();
+
+  //--- Choose a finite element space for Cell Problem
+  using namespace Functions::BasisFactory;
+  Functions::BasisFactory::Experimental::PeriodicIndexSet periodicIndices;
+
+  //--- get PeriodicIndices for periodicBasis (Don't do the following in real life: It has quadratic run-time in the number of vertices.)
+  for (const auto& v1 : vertices(gridView_CE))
+    for (const auto& v2 : vertices(gridView_CE))
+      if (equivalent(v1.geometry().corner(0), v2.geometry().corner(0)))
+      {
+        periodicIndices.unifyIndexPair({gridView_CE.indexSet().index(v1)}, {gridView_CE.indexSet().index(v2)});
+      }
+
+  //--- setup first order periodic Lagrange-Basis
+  auto Basis_CE = makeBasis(
+    gridView_CE,
+    power<dim>(
+      Functions::BasisFactory::Experimental::periodic(lagrange<1>(), periodicIndices),
+      flatLexicographic()
+      ));
+
+  ///////////////////////////////////
+  //  Create prestrained material object
+  ///////////////////////////////////
+  auto material_ = prestrainedMaterial(gridView_CE,parameterSet);
+
+  // --- Get scale ratio
+  double gamma = parameterSet.get<double>("gamma",1.0);
+
+  //------------------------------------------------------------------------------------------------
+  //--- Compute Correctors
+  auto correctorComputer = CorrectorComputer(Basis_CE, material_, gamma, log, parameterSet);
+  correctorComputer.solve();
+
+
+  //--- Compute effective quantities
+  auto effectiveQuantitiesComputer = EffectiveQuantitiesComputer(correctorComputer,material_);
+  effectiveQuantitiesComputer.computeEffectiveQuantities();
+
+  //--- get effective quantities
+  auto Qeff = effectiveQuantitiesComputer.getQeff();
+  auto Beff = effectiveQuantitiesComputer.getBeff();
+//   printmatrix(std::cout, Qeff, "Matrix Qeff", "--");
+//   printvector(std::cout, Beff, "Beff", "--");
+
+
+  /**
+   * @brief Compute "rotated" effective quantitites
+   *
+   */
+//  ParameterTreeParser::readINITree("../test/orthotropicrotation_2.parset", parameterSet);
+ ParameterTreeParser::readINITree(dir_path + "/orthotropicrotation_2.parset", parameterSet);
+ auto material_2 = prestrainedMaterial(gridView_CE,parameterSet);
+
+  //--- Compute Correctors
+  auto correctorComputer_2 = CorrectorComputer(Basis_CE, material_2, gamma, log, parameterSet);
+  correctorComputer_2.solve();
+
+  //--- Compute effective quantities
+  auto effectiveQuantitiesComputer_2 = EffectiveQuantitiesComputer(correctorComputer_2,material_2);
+  effectiveQuantitiesComputer_2.computeEffectiveQuantities();
+
+  //--- get effective quantities
+  auto Qeff_2 = effectiveQuantitiesComputer_2.getQeff();
+  auto Beff_2 = effectiveQuantitiesComputer_2.getBeff();
+
+  /**
+   * @brief  Compare results.
+   *
+   */
+  if( std::abs(Qeff[0][0] - Qeff_2[0][0]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[0][0] is " << Qeff[0][0] << " but with rotated material frame we get : " << Qeff_2[0][0] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[1][0] - Qeff_2[1][0]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[1][0] is " << Qeff[1][0] << " but with rotated material frame we get : " << Qeff_2[1][0] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[2][0] - Qeff_2[2][0]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[2][0] is " << Qeff[2][0] << " but with rotated material frame we get : " << Qeff_2[2][0] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[0][1] - Qeff_2[0][1]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[0][1] is " << Qeff[0][1] << " but with rotated material frame we get : " << Qeff_2[0][1] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[1][1] - Qeff_2[1][1]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[1][1] is " << Qeff[1][1] << " but with rotated material frame we get : " << Qeff_2[1][1] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[2][1] - Qeff_2[2][1]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[2][1] is " << Qeff[2][1] << " but with rotated material frame we get : " << Qeff_2[2][1] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[0][2] - Qeff_2[0][2]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[0][2] is " << Qeff[0][2] << " but with rotated material frame we get : " << Qeff_2[0][2] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[1][2] - Qeff_2[1][2]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[1][2] is " << Qeff[1][2] << " but with rotated material frame we get : " << Qeff_2[1][2] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Qeff[2][2] - Qeff_2[2][2]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[2][2] is " << Qeff[2][2] << " but with rotated material frame we get : " << Qeff_2[2][2] << " !" << std::endl;
+    return 1;
+  }
+
+
+  if( std::abs(Beff[0] - Beff_2[0]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Beff[0] is " << Beff[0] << " but with rotated material frame we get : " << Beff_2[0] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Beff[1] - Beff_2[1]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Beff[1] is " << Beff[1] << " but with rotated material frame we get : " << Beff_2[1] << " !" << std::endl;
+    return 1;
+  }
+  if( std::abs(Beff[2] - Beff_2[2]) > 1e-6)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Beff[2] is " << Beff[2] << " but with rotated material frame we get : " << Beff_2[2] << " !" << std::endl;
+    return 1;
+  }
+
+  std::cout << "Total time elapsed: " << globalTimer.elapsed() << std::endl;
+  std::cout << "Test: orthotropicrotationtest passed." << std::endl;
+}
diff --git a/test/parametrized_laminate.py b/test/parametrized_laminate.py
new file mode 100644
index 0000000000000000000000000000000000000000..9007b436bcc2e51ca4a57929f46cd42014000605
--- /dev/null
+++ b/test/parametrized_laminate.py
@@ -0,0 +1,77 @@
+import math
+import ctypes
+import os
+import sys
+#---------------------------------------------------------------
+
+#Parameters used:
+theta = 0.25;
+mu_1 = 1.0;
+theta_mu = 2.0;
+mu_2 = theta_mu * mu_1;
+rho_1 = 1.0;
+theta_rho = 2.0;
+rho_2 = rho_1*theta_rho;
+
+
+#--- define indicator function
+def indicatorFunction(x):
+    theta=0.25
+    factor=1
+    if (abs(x[0]) < (theta/2) and x[2] < 0 ):
+        return 1    #Phase1
+    elif (abs(x[0]) > (theta/2) and x[2] > 0 ):
+        return 2    #Phase2
+    elif (abs(x[0]) < (theta/2) and x[2] > 0 ):
+        return 3    #Phase3
+    else :
+        return 4    #Phase4
+
+
+########### Options for material phases: #################################
+#     1. "isotropic"     2. "orthotropic"      3. "transversely_isotropic"   4. "general_anisotropic"
+#########################################################################
+## Notation - Parameter input :
+# isotropic (Lame parameters) : [mu , lambda]
+#         orthotropic         : [E1,E2,E3,G12,G23,G31,nu12,nu13,nu23]   # see https://en.wikipedia.org/wiki/Poisson%27s_ratio with x=1,y=2,z=3
+# transversely_isotropic      : [E1,E2,G12,nu12,nu23]
+# general_anisotropic         : full compliance matrix C
+######################################################################
+
+# --- Number of material phases
+Phases=4
+
+#--- Define different material phases:
+
+#- PHASE 1
+phase1_type="isotropic"
+materialParameters_phase1 = [2.0, 0]   
+
+#- PHASE 2
+phase2_type="isotropic"
+materialParameters_phase2 = [1.0, 0]   
+ 
+
+#- PHASE 3
+phase3_type="isotropic"
+materialParameters_phase3 = [2.0, 0]
+
+#- PHASE 4
+phase4_type="isotropic"
+materialParameters_phase4 = [1.0, 0]
+
+
+#--- define prestrain function for each phase
+# (also works with non-constant values)
+def prestrain_phase1(x):
+    return [[2, 0, 0], [0,2,0], [0,0,2]]
+
+def prestrain_phase2(x):
+    return [[1, 0, 0], [0,1,0], [0,0,1]]
+
+def prestrain_phase3(x):
+    return [[0, 0, 0], [0,0,0], [0,0,0]]
+
+def prestrain_phase4(x):
+    return [[0, 0, 0], [0,0,0], [0,0,0]]
+
diff --git a/test/parametrizedlaminate.parset b/test/parametrizedlaminate.parset
new file mode 100644
index 0000000000000000000000000000000000000000..f02b8e746b934135082bba9aa5a3c913269570f9
--- /dev/null
+++ b/test/parametrizedlaminate.parset
@@ -0,0 +1,20 @@
+# --- Choose material definition:
+materialFunction = "parametrized_laminate"
+
+# --- Choose scale ratio gamma:
+gamma=1.0
+
+#############################################
+#  Solver Type: #1: CG - SOLVER , #2: GMRES - SOLVER, #3: QR - SOLVER (default), #4: UMFPACK - SOLVER
+#############################################
+
+# Note: Running the test with the QR-Solver (3) on gitlab 
+#       fails due to completely wrong results. 
+#       WHY is currently unclear.
+
+Solvertype = 2         # recommended to use iterative solver (e.g GMRES) for finer grid-levels
+#Solver_verbosity = 0  #(default = 2)  degree of information for solver output
+
+#print_conditionNumber = true
+#set_IntegralZero = true            #(default = false)
+#set_oneBasisFunction_Zero = true   #(default = false)
\ No newline at end of file
diff --git a/test/parametrizedlaminatetest.cc b/test/parametrizedlaminatetest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..636df2beee9289422265b92d3a4bf00e12b059b8
--- /dev/null
+++ b/test/parametrizedlaminatetest.cc
@@ -0,0 +1,274 @@
+#include <config.h>
+#include <array>
+#include <vector>
+#include <fstream>
+
+#include <iostream>
+#include <dune/common/indices.hh>
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.hh>
+#include <dune/common/float_cmp.hh>
+#include <dune/common/math.hh>
+
+#include <dune/geometry/quadraturerules.hh>
+
+#include <dune/grid/uggrid.hh>
+#include <dune/grid/yaspgrid.hh>
+#include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh>
+
+#include <dune/istl/matrix.hh>
+#include <dune/istl/bcrsmatrix.hh>
+#include <dune/istl/multitypeblockmatrix.hh>
+#include <dune/istl/multitypeblockvector.hh>
+#include <dune/istl/matrixindexset.hh>
+#include <dune/istl/solvers.hh>
+#include <dune/istl/spqr.hh>
+#include <dune/istl/preconditioners.hh>
+#include <dune/istl/io.hh>
+
+#include <dune/functions/functionspacebases/interpolate.hh>
+#include <dune/functions/backends/istlvectorbackend.hh>
+#include <dune/functions/functionspacebases/powerbasis.hh>
+#include <dune/functions/functionspacebases/compositebasis.hh>
+#include <dune/functions/functionspacebases/lagrangebasis.hh>
+#include <dune/functions/functionspacebases/periodicbasis.hh>
+#include <dune/functions/functionspacebases/subspacebasis.hh>
+#include <dune/functions/functionspacebases/boundarydofs.hh>
+#include <dune/functions/gridfunctions/discreteglobalbasisfunction.hh>
+#include <dune/functions/gridfunctions/gridviewfunction.hh>
+
+#include <dune/common/fvector.hh>
+#include <dune/common/fmatrix.hh>
+
+#include <dune/microstructure/matrix_operations.hh>
+#include <dune/microstructure/CorrectorComputer.hh>
+#include <dune/microstructure/EffectiveQuantitiesComputer.hh>
+#include <dune/microstructure/prestrainedMaterial.hh>
+
+#include <dune/solvers/solvers/umfpacksolver.hh>
+#include <dune/istl/eigenvalue/test/matrixinfo.hh>
+
+#include <dune/fufem/dunepython.hh>
+
+#include <any>
+#include <variant>
+#include <string>
+#include <iomanip>
+#include <filesystem>
+
+using namespace Dune;
+using namespace MatrixOperations;
+
+
+/**
+ * @brief In the special case of a parametrized laminate (Lemma 4.5) in
+ *        [Böhnlein,Neukamm,Padilla-Garza,Sander - A homogenized bending theory for prestrained plates]
+ *        some of the effective quantities can be computed analytically.
+ *        This test compares the analytical quantities with numerical results.
+ */
+
+
+//////////////////////////////////////////////////
+//   Infrastructure for handling periodicity
+//////////////////////////////////////////////////
+// Check whether two points are equal on R/Z x R/Z x R
+auto equivalent = [](const FieldVector<double,3>& x, const FieldVector<double,3>& y)
+                  {
+                    return ( (FloatCmp::eq(x[0],y[0]) or FloatCmp::eq(x[0]+1,y[0]) or FloatCmp::eq(x[0]-1,y[0]))
+                             and (FloatCmp::eq(x[1],y[1]) or FloatCmp::eq(x[1]+1,y[1]) or FloatCmp::eq(x[1]-1,y[1]))
+                             and (FloatCmp::eq(x[2],y[2]))
+                             );
+                  };
+
+
+int main(int argc, char *argv[])
+{
+  MPIHelper::instance(argc, argv);
+
+  Dune::Timer globalTimer;
+  //--- setup Log-File 
+  std::fstream log;
+
+  std::cout << "Current path is " << std::filesystem::current_path() << '\n';
+  std::filesystem::path file_path = (__FILE__);
+  std::cout<< "File path: " << file_path<<std::endl;
+  std::cout << "dir_path: " << file_path.parent_path() << std::endl;
+  std::string dir_path  = file_path.parent_path();
+
+  ParameterTree parameterSet;
+  if (argc < 2)
+    ParameterTreeParser::readINITree(dir_path + "/parametrizedlaminate.parset", parameterSet);
+  else
+  {
+    ParameterTreeParser::readINITree(argv[1], parameterSet);
+    ParameterTreeParser::readOptions(argc, argv, parameterSet);
+  }
+
+  // ParameterTree parameterSet;
+  // if (argc < 2)
+  //   ParameterTreeParser::readINITree("parset/parametrizedlaminate.parset", parameterSet);
+  // else
+  // {
+  //   ParameterTreeParser::readINITree(argv[1], parameterSet);
+  //   ParameterTreeParser::readOptions(argc, argv, parameterSet);
+  // }
+
+  // ParameterTree parameterSet;
+  // if (argc < 2)
+  //   ParameterTreeParser::readINITree("/parset/parametrizedlaminate.parset", parameterSet);
+  // else
+  // {
+  //   ParameterTreeParser::readINITree(argv[1], parameterSet);
+  //   ParameterTreeParser::readOptions(argc, argv, parameterSet);
+  // }
+
+  //--- Start Python interpreter
+  Python::start();
+  Python::Reference main = Python::import("__main__");
+  Python::run("import math");
+  Python::runStream()
+    << std::endl << "import sys"
+    << std::endl << "sys.path.append('" << dir_path  << "')"
+    << std::endl;
+
+
+  constexpr int dim = 3;
+  constexpr int dimWorld = 3;
+  ///////////////////////////////////
+  // Generate the grid
+  ///////////////////////////////////
+  // --- Corrector Problem Domain (-1/2,1/2)^3:
+  FieldVector<double,dim> lower({-1.0/2.0, -1.0/2.0, -1.0/2.0});
+  FieldVector<double,dim> upper({1.0/2.0, 1.0/2.0, 1.0/2.0});
+
+  int gridLevel = 3;
+
+  std::array<int, dim> nElements = {(int)std::pow(2,gridLevel) ,(int)std::pow(2,gridLevel) ,(int)std::pow(2,gridLevel)};
+  std::cout << "Number of Grid-Elements in each direction: " << nElements << std::endl;
+
+  using CellGridType = YaspGrid<dim, EquidistantOffsetCoordinates<double, dim> >;
+  CellGridType grid_CE(lower,upper,nElements);
+  using GridView = CellGridType::LeafGridView;
+  const GridView gridView_CE = grid_CE.leafGridView();
+
+  //--- Choose a finite element space for Cell Problem
+  using namespace Functions::BasisFactory;
+  Functions::BasisFactory::Experimental::PeriodicIndexSet periodicIndices;
+
+  //--- get PeriodicIndices for periodicBasis (Don't do the following in real life: It has quadratic run-time in the number of vertices.)
+  for (const auto& v1 : vertices(gridView_CE))
+    for (const auto& v2 : vertices(gridView_CE))
+      if (equivalent(v1.geometry().corner(0), v2.geometry().corner(0)))
+      {
+        periodicIndices.unifyIndexPair({gridView_CE.indexSet().index(v1)}, {gridView_CE.indexSet().index(v2)});
+      }
+
+  //--- setup first order periodic Lagrange-Basis
+  auto Basis_CE = makeBasis(
+    gridView_CE,
+    power<dim>(
+      Functions::BasisFactory::Experimental::periodic(lagrange<1>(), periodicIndices),
+      flatLexicographic()
+      ));
+
+  ///////////////////////////////////
+  //  Create prestrained material object
+  ///////////////////////////////////
+  auto material_ = prestrainedMaterial(gridView_CE,parameterSet);
+
+  // --- Get scale ratio
+  double gamma = parameterSet.get<double>("gamma",1.0);
+
+  //------------------------------------------------------------------------------------------------
+  //--- Compute Correctors
+  auto correctorComputer = CorrectorComputer(Basis_CE, material_, gamma, log, parameterSet);
+  correctorComputer.solve();
+
+
+  //--- Compute effective quantities
+  auto effectiveQuantitiesComputer = EffectiveQuantitiesComputer(correctorComputer,material_);
+  effectiveQuantitiesComputer.computeEffectiveQuantities();
+
+  //--- get effective quantities
+  auto Qeff = effectiveQuantitiesComputer.getQeff();
+  auto Beff = effectiveQuantitiesComputer.getBeff();
+  printmatrix(std::cout, Qeff, "Matrix Qeff", "--");
+  printvector(std::cout, Beff, "Beff", "--");
+
+  /**
+   * @brief Compute analytical effective quantitites
+   *
+   */
+  //Parameters
+  double theta = 0.25;
+  double mu_1 = 1.0;
+  double theta_mu = 2.0;
+  double mu_2 = theta_mu * mu_1;
+  double rho_1 = 1.0;
+  double theta_rho = 2.0;
+  double rho_2 = rho_1*theta_rho;
+
+  //Effective quantities
+  double q_1 = mu_1 * (theta_mu / (6*(theta + (1 - theta)*theta_mu)));
+  double q_2 = (mu_1/6.0) * ((1-theta) + theta*theta_mu);
+  double Beff_1 = (3.0*rho_1/2.0) * (1-(theta*(1+theta_rho)));
+  double Beff_2 = (3.0*rho_1/2.0) * ((1-theta*(1+theta_mu*theta_rho))/(1-theta+theta*theta_mu));
+  double Beff_3 = 0.0;
+  //  std::cout << "q_1: " << q_1 << std::endl;
+  //  std::cout << "q_2: " << q_2 << std::endl;
+  //  std::cout << "Beff_1: " << Beff_1 << std::endl;
+  //  std::cout << "Beff_2: " << Beff_2 << std::endl;
+  //  std::cout << "Beff_3: " << Beff_3 << std::endl;
+
+  /**
+   * @brief  Compare numerical and analytical results.
+   *
+   */
+  if( std::abs(q_1 - Qeff[0][0]) > 1e-2)
+  {
+    std::cout << "std::abs(q_1 - Qeff[0][0]):" << std::abs(q_1 - Qeff[0][0]) << std::endl;
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[0][0] is " << Qeff[0][0] << " but " << q_1 << " was expected!" << std::endl;
+
+    return 1;
+  }
+  if( std::abs(q_2 - Qeff[1][1]) > 1e-2)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[1][1] is " << Qeff[1][1] << " but " << q_2 << " was expected!" << std::endl;
+
+    return 1;
+  }
+  if( std::abs(Beff_1 - Beff[0]) > 1e-2)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Beff[0] is " << Beff[0] << " but " << Beff_1 << " was expected!" << std::endl;
+
+    return 1;
+  }
+  if( std::abs(Beff_2 - Beff[1]) > 1e-2)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Beff[1] is " << Beff[1] << " but " << Beff_2 << " was expected!" << std::endl;
+
+    return 1;
+  }
+  if( std::abs(Beff_3 - Beff[2]) > 1e-2)
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Beff[2] is " << Beff[2] << " but " << Beff_3 << " was expected!" << std::endl;
+    return 1;
+  }
+  if( (Qeff[2][2] < Qeff[0][0]) || (Qeff[2][2] > Qeff[1][1]) )
+  {
+    std::cerr << std::setprecision(9);
+    std::cerr << "Computed Qeff[2][2] is " << Qeff[2][2] << " does not lie between  Qeff[0][0]:"
+              << Qeff[0][0] << " and Qeff[1][1]:" << Qeff[1][1] << " as was expected!" << std::endl;
+    return 1;
+  }
+
+  std::cout << "Total time elapsed: " << globalTimer.elapsed() << std::endl;
+  std::cout << "Test parametrizedlaminatetest passed." << std::endl;
+  return 0;
+}