diff --git a/AMDiS/src/AdaptInstationary.cc b/AMDiS/src/AdaptInstationary.cc
index de4bf478266e879555842163176775c4d94a709c..996bf0c8064be013d87f039445658283350428e1 100644
--- a/AMDiS/src/AdaptInstationary.cc
+++ b/AMDiS/src/AdaptInstationary.cc
@@ -17,6 +17,11 @@
 #include "ProblemTimeInterface.h"
 #include "Serializer.h"
 
+#if HAVE_PARALLEL_DOMAIN_AMDIS
+#include "parallel/MeshDistributor.h"
+#include <petsc.h>
+#endif
+
 namespace AMDiS {
 
   AdaptInstationary::AdaptInstationary(std::string name,
@@ -31,8 +36,8 @@ namespace AMDiS {
   {
     FUNCNAME("AdaptInstationary::AdaptInstationary()");
 
-//     MSG("You make use of the obsolete constructor AdaptInstationary::AdaptInstationary(...)!\n");
-//     MSG("Please use the constructor that uses references instead of pointers!\n");
+    MSG("You make use of the obsolete constructor AdaptInstationary::AdaptInstationary(...)!\n");
+    MSG("Please use the constructor that uses references instead of pointers!\n");
 
     initConstructor(problemStat, info, initialInfo, initialTimestampSet);
  }
@@ -262,6 +267,8 @@ namespace AMDiS {
   {
     FUNCNAME("AdaptInstationary::oneTimestep()");
 
+    MSG("ONE TIMESTEP!\n");
+
     adaptInfo->setTimestepIteration(0);
 
     switch (strategy) {
@@ -294,6 +301,10 @@ namespace AMDiS {
 
     TEST_EXIT(adaptInfo->getTimestep() > 0)("timestep <= 0!\n");
 
+#if HAVE_PARALLEL_DOMAIN_AMDIS
+    MeshDistributor::globalMeshDistributor->initParallelization(); 
+#endif
+
     if (adaptInfo->getTimestepNumber() == 0) {
       adaptInfo->setTime(adaptInfo->getStartTime());
       initialAdaptInfo->setStartTime(adaptInfo->getStartTime());
@@ -315,10 +326,7 @@ namespace AMDiS {
 
       if (breakWhenStable && (adaptInfo->getSolverIterations() == 0)) 
 	break;
-      /*     
-      if (adaptInfo->forceBreak) 
-	  break;
-      */
+
       // Check if there is a runtime limitation. If there is a runtime limitation
       // and there is no more time for a next adaption loop, than return the error
       // code for rescheduling the problem and break the adaption loop.
@@ -328,6 +336,11 @@ namespace AMDiS {
       }
     }
 
+#ifdef HAVE_PARALLEL_DOMAIN_AMDIS
+    MeshDistributor::globalMeshDistributor->exitParallelization();
+    PetscFinalize();
+#endif
+
     return errorCode;
   }
 
diff --git a/AMDiS/src/AdaptStationary.cc b/AMDiS/src/AdaptStationary.cc
index 75c94d089272dd6ef9bd99dd0675ead73d40bfa1..a95c7184bc04e302e04a1bb4ad12e20cb67e7bd2 100644
--- a/AMDiS/src/AdaptStationary.cc
+++ b/AMDiS/src/AdaptStationary.cc
@@ -16,6 +16,11 @@
 #include "ProblemIterationInterface.h"
 #include <math.h>
 
+#if HAVE_PARALLEL_DOMAIN_AMDIS
+#include "parallel/MeshDistributor.h"
+#include <petsc.h>
+#endif
+
 namespace AMDiS {
 
   AdaptStationary::AdaptStationary(std::string name,
@@ -25,8 +30,8 @@ namespace AMDiS {
   {
     FUNCNAME("AdaptStationary::AdaptStationary()");
 
-//     MSG("You make use of the obsolete constructor AdaptStationary::AdaptStationary(...)!\n");
-//     MSG("Please use the constructor that uses references instead of pointers!\n");
+    MSG("You make use of the obsolete constructor AdaptStationary::AdaptStationary(...)!\n");
+    MSG("Please use the constructor that uses references instead of pointers!\n");
 
     initialize();
   }
@@ -45,6 +50,10 @@ namespace AMDiS {
   {
     FUNCNAME("AdaptStationary::adapt()");
 
+#if HAVE_PARALLEL_DOMAIN_AMDIS
+    MeshDistributor::globalMeshDistributor->initParallelization(); 
+#endif
+
     // initial iteration
     if (adaptInfo->getSpaceIteration() == -1) {
       problemIteration->beginIteration(adaptInfo);
@@ -68,6 +77,11 @@ namespace AMDiS {
       adaptInfo->incSpaceIteration();
     }
 
+#ifdef HAVE_PARALLEL_DOMAIN_AMDIS
+    MeshDistributor::globalMeshDistributor->exitParallelization();
+    PetscFinalize();
+#endif
+
     return 0;
   }
 
diff --git a/AMDiS/src/parallel/MeshDistributor.cc b/AMDiS/src/parallel/MeshDistributor.cc
index b5ed15196c31f02e49dc58fbd4eeddb87c87dc9a..e0310499e918a142c15c9736604217f3036c3c19 100644
--- a/AMDiS/src/parallel/MeshDistributor.cc
+++ b/AMDiS/src/parallel/MeshDistributor.cc
@@ -143,6 +143,7 @@ namespace AMDiS {
 
     TEST_EXIT(feSpace)("No FE space has been defined for the mesh distributor!\n");
     TEST_EXIT(mesh)("No mesh has been defined for the mesh distributor!\n");
+    TEST_EXIT(!initialized)("MeshDistributor is already initialized!\n");
 
 #ifdef HAVE_ZOLTAN
     int a = 0;
diff --git a/AMDiS/src/parallel/PetscSolverFeti.cc b/AMDiS/src/parallel/PetscSolverFeti.cc
index 232037765d86319710bdb75c82fdf527f225f483..9b076f923310f109f033c725ea66829871787567 100644
--- a/AMDiS/src/parallel/PetscSolverFeti.cc
+++ b/AMDiS/src/parallel/PetscSolverFeti.cc
@@ -47,7 +47,7 @@ namespace AMDiS {
 
     void *ctx;
     MatShellGetContext(mat, &ctx);
-    PetscFetiData* data = static_cast<PetscFetiData*>(ctx);
+    FetiData* data = static_cast<FetiData*>(ctx);
 
     // y = L inv(K_BB) trans(L) x
     MatMultTranspose(*(data->mat_lagrange), x, data->tmp_vec_b);
@@ -71,61 +71,139 @@ namespace AMDiS {
 
 
   // y = PC * x
-  PetscErrorCode petscApplyFetiPrecon(PC pc, Vec x, Vec y)
+  PetscErrorCode petscApplyFetiDirichletPrecon(PC pc, Vec x, Vec y)
   {
+    // Get data for the preconditioner
     void *ctx;
     PCShellGetContext(pc, &ctx);
-    PetscFetiPreconData* data = static_cast<PetscFetiPreconData*>(ctx);
+    FetiDirichletPreconData* data = static_cast<FetiDirichletPreconData*>(ctx);
 
+    // Multiply with scaled Lagrange constraint matrix.
     MatMultTranspose(*(data->mat_lagrange_scaled), x, data->tmp_vec_b);
 
-    int sizeB;
-    int sizeBound;
-    VecGetLocalSize(data->tmp_vec_b, &sizeB);
-    VecGetLocalSize(data->tmp_vec_bound0, &sizeBound);
 
-    PetscScalar *local_b;
-    VecGetArray(data->tmp_vec_b, &local_b);
+    // === Restriction of the B nodes to the boundary nodes. ===
 
-    PetscScalar *local_bound;
-    VecGetArray(data->tmp_vec_bound0, &local_bound);
+    int nLocalB;
+    int nLocalDuals;
+    VecGetLocalSize(data->tmp_vec_b, &nLocalB);
+    VecGetLocalSize(data->tmp_vec_duals0, &nLocalDuals);
 
-    for (int i = sizeB - sizeBound, j = 0; i < sizeB; i++, j++)
-      local_bound[j] = local_b[i];
+    PetscScalar *local_b, *local_duals;
+    VecGetArray(data->tmp_vec_b, &local_b);
+    VecGetArray(data->tmp_vec_duals0, &local_duals);
+
+    for (int i = nLocalB - nLocalDuals, j = 0; i < nLocalB; i++, j++)
+      local_duals[j] = local_b[i];
 
     VecRestoreArray(data->tmp_vec_b, &local_b);
-    VecRestoreArray(data->tmp_vec_bound0, &local_bound);
+    VecRestoreArray(data->tmp_vec_duals0, &local_duals);
 
 
+    // === K_DD - K_DI inv(K_II) K_ID ===
 
-    MatMult(*(data->mat_bound_bound), data->tmp_vec_bound0, data->tmp_vec_bound1);
+    MatMult(*(data->mat_duals_duals), data->tmp_vec_duals0, data->tmp_vec_duals1);
 
-    MatMult(*(data->mat_interior_bound), data->tmp_vec_bound0, data->tmp_vec_interior);
+    MatMult(*(data->mat_interior_duals), data->tmp_vec_duals0, data->tmp_vec_interior);
     KSPSolve(*(data->ksp_interior), data->tmp_vec_interior, data->tmp_vec_interior);
-    MatMult(*(data->mat_bound_interior), data->tmp_vec_interior, data->tmp_vec_bound0);
+    MatMult(*(data->mat_duals_interior), data->tmp_vec_interior, data->tmp_vec_duals0);
+
+    VecAXPBY(data->tmp_vec_duals0, 1.0, -1.0, data->tmp_vec_duals1);
+
+
+    // === Prolongation from local dual nodes to B nodes.
+
+    VecGetArray(data->tmp_vec_b, &local_b);
+    VecGetArray(data->tmp_vec_duals0, &local_duals);
+
+    for (int i = nLocalB - nLocalDuals, j = 0; i < nLocalB; i++, j++)
+      local_b[i] = local_duals[j];
+
+    VecRestoreArray(data->tmp_vec_b, &local_b);
+    VecRestoreArray(data->tmp_vec_duals0, &local_duals);
+
+
+    // Multiply with scaled Lagrange constraint matrix.
+    MatMult(*(data->mat_lagrange_scaled), data->tmp_vec_b, y);
+
+    return 0;
+  }
+
+
+  // y = PC * x
+  PetscErrorCode petscApplyFetiLumpedPrecon(PC pc, Vec x, Vec y)
+  {
+    // Get data for the preconditioner
+    void *ctx;
+    PCShellGetContext(pc, &ctx);
+    FetiLumpedPreconData* data = static_cast<FetiLumpedPreconData*>(ctx);
+
+    // Multiply with scaled Lagrange constraint matrix.
+    MatMultTranspose(*(data->mat_lagrange_scaled), x, data->tmp_vec_b);
 
-    VecAXPBY(data->tmp_vec_bound0, 1.0, -1.0, data->tmp_vec_bound1);
 
+    // === Restriction of the B nodes to the boundary nodes. ===
 
+    int nLocalB;
+    int nLocalDuals;
+    VecGetLocalSize(data->tmp_vec_b, &nLocalB);
+    VecGetLocalSize(data->tmp_vec_duals0, &nLocalDuals);
 
+    PetscScalar *local_b, *local_duals;
     VecGetArray(data->tmp_vec_b, &local_b);
-    VecGetArray(data->tmp_vec_bound0, &local_bound);
+    VecGetArray(data->tmp_vec_duals0, &local_duals);
 
-    for (int i = sizeB - sizeBound, j = 0; i < sizeB; i++, j++)
-      local_b[i] = local_bound[j];
+    for (int i = nLocalB - nLocalDuals, j = 0; i < nLocalB; i++, j++)
+      local_duals[j] = local_b[i];
 
     VecRestoreArray(data->tmp_vec_b, &local_b);
-    VecRestoreArray(data->tmp_vec_bound0, &local_bound);
+    VecRestoreArray(data->tmp_vec_duals0, &local_duals);
+
+
+    // === K_DD ===
+
+    MatMult(*(data->mat_duals_duals), data->tmp_vec_duals0, data->tmp_vec_duals1);
+
 
+    // === Prolongation from local dual nodes to B nodes.
 
+    VecGetArray(data->tmp_vec_b, &local_b);
+    VecGetArray(data->tmp_vec_duals1, &local_duals);
+
+    for (int i = nLocalB - nLocalDuals, j = 0; i < nLocalB; i++, j++)
+      local_b[i] = local_duals[j];
 
+    VecRestoreArray(data->tmp_vec_b, &local_b);
+    VecRestoreArray(data->tmp_vec_duals0, &local_duals);
 
+
+    // Multiply with scaled Lagrange constraint matrix.
     MatMult(*(data->mat_lagrange_scaled), data->tmp_vec_b, y);
 
     return 0;
   }
 
 
+  PetscSolverFeti::PetscSolverFeti()
+    : PetscSolver(),
+      nComponents(-1)
+  {
+    FUNCNAME("PetscSolverFeti::PetscSolverFeti()");
+
+    string preconditionerName = "";
+    Parameters::get("parallel->solver->precon", preconditionerName);
+    if (preconditionerName == "" || preconditionerName == "none") {
+      fetiPreconditioner = FETI_NONE;
+    } else if (preconditionerName == "dirichlet") {
+      fetiPreconditioner = FETI_DIRICHLET;
+    } else if (preconditionerName == "lumped") {
+      fetiPreconditioner = FETI_LUMPED;
+    } else {
+      ERROR_EXIT("Preconditioner \"%s\" not available!\n", preconditionerName.c_str());
+    }
+  }
+
+
   void PetscSolverFeti::updateDofData()
   {
     FUNCNAME("PetscSolverFeti::updateDofData()");
@@ -438,7 +516,7 @@ namespace AMDiS {
       it->second = nLocalInterior + rStartB;
       nLocalInterior++;
     }
-    nLocalBound = duals.size();
+    nLocalDuals = duals.size();
 
     TEST_EXIT_DBG(nLocalInterior + primals.size() + duals.size() == 
 		  static_cast<unsigned int>(admin->getUsedDofs()))
@@ -561,23 +639,23 @@ namespace AMDiS {
 
     // === Create FETI-DP solver object. ===
 
-    petscFetiData.mat_primal_primal = &mat_primal_primal;
-    petscFetiData.mat_primal_b = &mat_primal_b;
-    petscFetiData.mat_b_primal = &mat_b_primal;
-    petscFetiData.mat_lagrange = &mat_lagrange;
-    petscFetiData.ksp_b = &ksp_b;
-    petscFetiData.ksp_schur_primal = &ksp_schur_primal;
+    fetiData.mat_primal_primal = &mat_primal_primal;
+    fetiData.mat_primal_b = &mat_primal_b;
+    fetiData.mat_b_primal = &mat_b_primal;
+    fetiData.mat_lagrange = &mat_lagrange;
+    fetiData.ksp_b = &ksp_b;
+    fetiData.ksp_schur_primal = &ksp_schur_primal;
 
 
-    VecDuplicate(f_b, &(petscFetiData.tmp_vec_b));
-    VecDuplicate(f_primal, &(petscFetiData.tmp_vec_primal));
-    MatGetVecs(mat_lagrange, PETSC_NULL, &(petscFetiData.tmp_vec_lagrange));
+    VecDuplicate(f_b, &(fetiData.tmp_vec_b));
+    VecDuplicate(f_primal, &(fetiData.tmp_vec_primal));
+    MatGetVecs(mat_lagrange, PETSC_NULL, &(fetiData.tmp_vec_lagrange));
 
 
     MatCreateShell(PETSC_COMM_WORLD,
 		   nRankLagrange * nComponents, nRankLagrange * nComponents,
 		   nOverallLagrange * nComponents, nOverallLagrange * nComponents,
-		   &petscFetiData, &mat_feti);
+		   &fetiData, &mat_feti);
     MatShellSetOperation(mat_feti, MATOP_MULT, (void(*)(void))petscMultMatFeti);
 
 
@@ -587,36 +665,54 @@ namespace AMDiS {
     KSPSetFromOptions(ksp_feti);
 
 
-    // === Create FETI-DP Dirichlet preconditioner object. ===
+    // === Create FETI-DP preconditioner object. ===
 
-    KSPCreate(PETSC_COMM_SELF, &ksp_interior);
-    KSPSetOperators(ksp_interior, mat_interior_interior, mat_interior_interior, SAME_NONZERO_PATTERN);
-    KSPSetOptionsPrefix(ksp_interior, "solver_interior_");
-    KSPSetFromOptions(ksp_interior);
+    if (fetiPreconditioner != FETI_NONE) {
+      MatDuplicate(mat_lagrange, MAT_COPY_VALUES, &mat_lagrange_scaled);
+      MatScale(mat_lagrange_scaled, 0.5);
+    }
 
-    
-    MatDuplicate(mat_lagrange, MAT_COPY_VALUES, &mat_lagrange_scaled);
-    MatScale(mat_lagrange_scaled, 0.5);
-
-    petscFetiPreconData.mat_lagrange_scaled = &mat_lagrange_scaled;
-    petscFetiPreconData.mat_interior_interior = &mat_interior_interior;
-    petscFetiPreconData.mat_bound_bound = &mat_bound_bound;
-    petscFetiPreconData.mat_interior_bound = &mat_interior_bound;
-    petscFetiPreconData.mat_bound_interior = &mat_bound_interior;
-    petscFetiPreconData.ksp_interior = &ksp_interior;
-    petscFetiPreconData.nLocalInterior = nLocalInterior;
-    petscFetiPreconData.nLocalBound = nLocalBound;
-
-    VecDuplicate(f_b, &(petscFetiPreconData.tmp_vec_b));
-
-    MatGetVecs(mat_bound_bound, PETSC_NULL, &(petscFetiPreconData.tmp_vec_bound0));
-    MatGetVecs(mat_bound_bound, PETSC_NULL, &(petscFetiPreconData.tmp_vec_bound1));
-    MatGetVecs(mat_interior_interior, PETSC_NULL, &(petscFetiPreconData.tmp_vec_interior));
-
-    KSPGetPC(ksp_feti, &precon_feti);
-    PCSetType(precon_feti, PCSHELL);
-    PCShellSetContext(precon_feti, static_cast<void*>(&petscFetiPreconData));
-    PCShellSetApply(precon_feti, petscApplyFetiPrecon);
+    switch (fetiPreconditioner) {
+    case FETI_DIRICHLET:           
+      KSPCreate(PETSC_COMM_SELF, &ksp_interior);
+      KSPSetOperators(ksp_interior, mat_interior_interior, mat_interior_interior, SAME_NONZERO_PATTERN);
+      KSPSetOptionsPrefix(ksp_interior, "solver_interior_");
+      KSPSetFromOptions(ksp_interior);
+            
+      fetiDirichletPreconData.mat_lagrange_scaled = &mat_lagrange_scaled;
+      fetiDirichletPreconData.mat_interior_interior = &mat_interior_interior;
+      fetiDirichletPreconData.mat_duals_duals = &mat_duals_duals;
+      fetiDirichletPreconData.mat_interior_duals = &mat_interior_duals;
+      fetiDirichletPreconData.mat_duals_interior = &mat_duals_interior;
+      fetiDirichletPreconData.ksp_interior = &ksp_interior;
+      
+      VecDuplicate(f_b, &(fetiDirichletPreconData.tmp_vec_b));      
+      MatGetVecs(mat_duals_duals, PETSC_NULL, &(fetiDirichletPreconData.tmp_vec_duals0));
+      MatGetVecs(mat_duals_duals, PETSC_NULL, &(fetiDirichletPreconData.tmp_vec_duals1));
+      MatGetVecs(mat_interior_interior, PETSC_NULL, &(fetiDirichletPreconData.tmp_vec_interior));
+      
+      KSPGetPC(ksp_feti, &precon_feti);
+      PCSetType(precon_feti, PCSHELL);
+      PCShellSetContext(precon_feti, static_cast<void*>(&fetiDirichletPreconData));
+      PCShellSetApply(precon_feti, petscApplyFetiDirichletPrecon);
+      
+      break;
+
+    case FETI_LUMPED:
+      fetiLumpedPreconData.mat_lagrange_scaled = &mat_lagrange_scaled;
+      fetiLumpedPreconData.mat_duals_duals = &mat_duals_duals;
+
+      VecDuplicate(f_b, &(fetiLumpedPreconData.tmp_vec_b));
+      MatGetVecs(mat_duals_duals, PETSC_NULL, &(fetiLumpedPreconData.tmp_vec_duals0));
+      MatGetVecs(mat_duals_duals, PETSC_NULL, &(fetiLumpedPreconData.tmp_vec_duals1));
+
+      KSPGetPC(ksp_feti, &precon_feti);
+      PCSetType(precon_feti, PCSHELL);
+      PCShellSetContext(precon_feti, static_cast<void*>(&fetiLumpedPreconData));
+      PCShellSetApply(precon_feti, petscApplyFetiLumpedPrecon);
+      
+      break;
+    }
   }
   
 
@@ -626,37 +722,50 @@ namespace AMDiS {
 
     // === Destroy FETI-DP solver object. ===
 
-    petscFetiData.mat_primal_primal = PETSC_NULL;
-    petscFetiData.mat_primal_b = PETSC_NULL;
-    petscFetiData.mat_b_primal = PETSC_NULL;
-    petscFetiData.mat_lagrange = PETSC_NULL;
-    petscFetiData.ksp_b = PETSC_NULL;
-    petscFetiData.ksp_schur_primal = PETSC_NULL;
+    fetiData.mat_primal_primal = PETSC_NULL;
+    fetiData.mat_primal_b = PETSC_NULL;
+    fetiData.mat_b_primal = PETSC_NULL;
+    fetiData.mat_lagrange = PETSC_NULL;
+    fetiData.ksp_b = PETSC_NULL;
+    fetiData.ksp_schur_primal = PETSC_NULL;
 
-    VecDestroy(&petscFetiData.tmp_vec_b);
-    VecDestroy(&petscFetiData.tmp_vec_primal);
-    VecDestroy(&petscFetiData.tmp_vec_lagrange);
+    VecDestroy(&fetiData.tmp_vec_b);
+    VecDestroy(&fetiData.tmp_vec_primal);
+    VecDestroy(&fetiData.tmp_vec_lagrange);
 
     MatDestroy(&mat_feti);
     KSPDestroy(&ksp_feti);
 
 
-    // === Destroy FETI-DP Dirichlet preconditioner object. ===
-
-    KSPDestroy(&ksp_interior);
-
-    petscFetiPreconData.mat_lagrange_scaled = NULL;
-    petscFetiPreconData.mat_interior_interior = NULL;
-    petscFetiPreconData.mat_bound_bound = NULL;
-    petscFetiPreconData.mat_interior_bound = NULL;
-    petscFetiPreconData.mat_bound_interior = NULL;
-    petscFetiPreconData.ksp_interior = NULL;
-
-    VecDestroy(&petscFetiPreconData.tmp_vec_b);
-    VecDestroy(&petscFetiPreconData.tmp_vec_bound0);
-    VecDestroy(&petscFetiPreconData.tmp_vec_bound1);
-    VecDestroy(&petscFetiPreconData.tmp_vec_interior);
-    MatDestroy(&mat_lagrange_scaled);
+    // === Destroy FETI-DP preconditioner object. ===
+
+    switch (fetiPreconditioner) {
+    case FETI_DIRICHLET:           
+      KSPDestroy(&ksp_interior);
+
+      fetiDirichletPreconData.mat_lagrange_scaled = NULL;
+      fetiDirichletPreconData.mat_interior_interior = NULL;
+      fetiDirichletPreconData.mat_duals_duals = NULL;
+      fetiDirichletPreconData.mat_interior_duals = NULL;
+      fetiDirichletPreconData.mat_duals_interior = NULL;
+      fetiDirichletPreconData.ksp_interior = NULL;
+      
+      VecDestroy(&fetiDirichletPreconData.tmp_vec_b);
+      VecDestroy(&fetiDirichletPreconData.tmp_vec_duals0);
+      VecDestroy(&fetiDirichletPreconData.tmp_vec_duals1);
+      VecDestroy(&fetiDirichletPreconData.tmp_vec_interior);
+      MatDestroy(&mat_lagrange_scaled);
+      break;
+
+    case FETI_LUMPED:
+      fetiLumpedPreconData.mat_lagrange_scaled = NULL;
+      fetiLumpedPreconData.mat_duals_duals = NULL;
+
+      VecDestroy(&fetiLumpedPreconData.tmp_vec_b);
+      VecDestroy(&fetiLumpedPreconData.tmp_vec_duals0);
+      VecDestroy(&fetiLumpedPreconData.tmp_vec_duals1);
+      break;
+    }
   }
 
 
@@ -753,6 +862,10 @@ namespace AMDiS {
   {
     FUNCNAME("PetscSolverFeti::fillPetscMatrix()");   
 
+    PetscLogStage stageFetiCreate;
+    PetscLogStageRegister("Stage 0 CREATE FETI-DP", &stageFetiCreate);
+    PetscLogStagePush(stageFetiCreate);
+
     nComponents = vec->getSize();
 
     // === Create all sets and indices. ===
@@ -767,7 +880,7 @@ namespace AMDiS {
     int nRowsRankPrimal = nRankPrimals * nComponents;
     int nRowsOverallPrimal = nOverallPrimals * nComponents;
     int nRowsInterior = nLocalInterior * nComponents;
-    int nRowsBound = nLocalBound * nComponents;
+    int nRowsDual = nLocalDuals * nComponents;
 
     MatCreateMPIAIJ(PETSC_COMM_WORLD,
 		    nRowsRankB, nRowsRankB, nRowsOverallB, nRowsOverallB,
@@ -789,23 +902,26 @@ namespace AMDiS {
 		    100, PETSC_NULL, 100, PETSC_NULL, &mat_primal_b);
 
 
-    // === Create matrices for Dirichlet FETI-DP preconditioner. ===
-
-    MatCreateSeqAIJ(PETSC_COMM_SELF,
-		    nRowsInterior, nRowsInterior, 100, PETSC_NULL,
-		    &mat_interior_interior);
-
-    MatCreateSeqAIJ(PETSC_COMM_SELF,
-		    nRowsBound, nRowsBound, 100, PETSC_NULL,
-		    &mat_bound_bound);
-
-    MatCreateSeqAIJ(PETSC_COMM_SELF,
-		    nRowsInterior, nRowsBound, 100, PETSC_NULL,
-		    &mat_interior_bound);
-
-    MatCreateSeqAIJ(PETSC_COMM_SELF,
-		    nRowsBound, nRowsInterior, 100, PETSC_NULL,
-		    &mat_bound_interior);
+    // === Create matrices for FETI-DP preconditioner. ===
+
+    if (fetiPreconditioner != FETI_NONE)
+      MatCreateSeqAIJ(PETSC_COMM_SELF,
+		      nRowsDual, nRowsDual, 100, PETSC_NULL,
+		      &mat_duals_duals);
+
+    if (fetiPreconditioner == FETI_DIRICHLET) {
+      MatCreateSeqAIJ(PETSC_COMM_SELF,
+		      nRowsInterior, nRowsInterior, 100, PETSC_NULL,
+		      &mat_interior_interior);
+      
+      MatCreateSeqAIJ(PETSC_COMM_SELF,
+		      nRowsInterior, nRowsDual, 100, PETSC_NULL,
+		      &mat_interior_duals);
+      
+      MatCreateSeqAIJ(PETSC_COMM_SELF,
+		      nRowsDual, nRowsInterior, 100, PETSC_NULL,
+		      &mat_duals_interior);
+    }
 
     
     // === Prepare traverse of sequentially created matrices. ===
@@ -967,33 +1083,51 @@ namespace AMDiS {
 	  // === For preconditioner ===
 
 	  if (!rowPrimal) {
-	    int rowIndex = globalIndexB[*cursor] - rStartB;
-
-	    if (rowIndex < nLocalInterior) {
-	      int rowIndex2 = 
-		(globalIndexB[*cursor] - rStartB) * nComponents + i;
-
-	      MatSetValues(mat_interior_interior, 1, &rowIndex2, colsLocal.size(),
-			   &(colsLocal[0]), &(valuesLocal[0]), INSERT_VALUES);
-
-	      if (colsLocalOther.size()) 
-		MatSetValues(mat_interior_bound, 1, &rowIndex2, colsLocalOther.size(),
-			     &(colsLocalOther[0]), &(valuesLocalOther[0]), INSERT_VALUES);
-	    } else {
-	      int rowIndex2 = 
-		(globalIndexB[*cursor] - rStartB - nLocalInterior) * nComponents + i;
-
-	      MatSetValues(mat_bound_bound, 1, &rowIndex2, colsLocal.size(),
-			   &(colsLocal[0]), &(valuesLocal[0]), INSERT_VALUES);
-
-	      if (colsLocalOther.size()) 
-		MatSetValues(mat_bound_interior, 1, &rowIndex2, colsLocalOther.size(),
-			     &(colsLocalOther[0]), &(valuesLocalOther[0]), INSERT_VALUES);
+	    switch (fetiPreconditioner) {
+	    case FETI_DIRICHLET:
+	      {
+		int rowIndex = globalIndexB[*cursor] - rStartB;
+		
+		if (rowIndex < nLocalInterior) {
+		  int rowIndex2 = 
+		    (globalIndexB[*cursor] - rStartB) * nComponents + i;
+		  
+		  MatSetValues(mat_interior_interior, 1, &rowIndex2, colsLocal.size(),
+			       &(colsLocal[0]), &(valuesLocal[0]), INSERT_VALUES);
+		  
+		  if (colsLocalOther.size()) 
+		    MatSetValues(mat_interior_duals, 1, &rowIndex2, colsLocalOther.size(),
+				 &(colsLocalOther[0]), &(valuesLocalOther[0]), INSERT_VALUES);
+		} else {
+		  int rowIndex2 = 
+		    (globalIndexB[*cursor] - rStartB - nLocalInterior) * nComponents + i;
+		  
+		  MatSetValues(mat_duals_duals, 1, &rowIndex2, colsLocal.size(),
+			       &(colsLocal[0]), &(valuesLocal[0]), INSERT_VALUES);
+		  
+		  if (colsLocalOther.size()) 
+		    MatSetValues(mat_duals_interior, 1, &rowIndex2, colsLocalOther.size(),
+				 &(colsLocalOther[0]), &(valuesLocalOther[0]), INSERT_VALUES);
+		  
+		}
+	      }
+	      break;
 
-	    }
+	    case FETI_LUMPED:
+	      {
+		int rowIndex = globalIndexB[*cursor] - rStartB;
+		
+		if (rowIndex >= nLocalInterior) {
+		  int rowIndex2 = 
+		    (globalIndexB[*cursor] - rStartB - nLocalInterior) * nComponents + i;
+		  
+		  MatSetValues(mat_duals_duals, 1, &rowIndex2, colsLocal.size(),
+			       &(colsLocal[0]), &(valuesLocal[0]), INSERT_VALUES);		
+		}
+	      }		
+	      break;
+	    }	  
 	  }
-
-
 	} 
       }
     }
@@ -1012,19 +1146,25 @@ namespace AMDiS {
 
     MatAssemblyBegin(mat_primal_b, MAT_FINAL_ASSEMBLY);
     MatAssemblyEnd(mat_primal_b, MAT_FINAL_ASSEMBLY);
-	  
 
-    MatAssemblyBegin(mat_interior_interior, MAT_FINAL_ASSEMBLY);
-    MatAssemblyEnd(mat_interior_interior, MAT_FINAL_ASSEMBLY);
 
-    MatAssemblyBegin(mat_bound_bound, MAT_FINAL_ASSEMBLY);
-    MatAssemblyEnd(mat_bound_bound, MAT_FINAL_ASSEMBLY);
+    // === Start global assembly procedure for preconditioner matrices. ===
 
-    MatAssemblyBegin(mat_interior_bound, MAT_FINAL_ASSEMBLY);
-    MatAssemblyEnd(mat_interior_bound, MAT_FINAL_ASSEMBLY);
+    if (fetiPreconditioner != FETI_NONE) {
+      MatAssemblyBegin(mat_duals_duals, MAT_FINAL_ASSEMBLY);
+      MatAssemblyEnd(mat_duals_duals, MAT_FINAL_ASSEMBLY); 
+    }
 
-    MatAssemblyBegin(mat_bound_interior, MAT_FINAL_ASSEMBLY);
-    MatAssemblyEnd(mat_bound_interior, MAT_FINAL_ASSEMBLY);
+    if (fetiPreconditioner == FETI_DIRICHLET) {
+      MatAssemblyBegin(mat_interior_interior, MAT_FINAL_ASSEMBLY);
+      MatAssemblyEnd(mat_interior_interior, MAT_FINAL_ASSEMBLY);
+      
+      MatAssemblyBegin(mat_interior_duals, MAT_FINAL_ASSEMBLY);
+      MatAssemblyEnd(mat_interior_duals, MAT_FINAL_ASSEMBLY);
+      
+      MatAssemblyBegin(mat_duals_interior, MAT_FINAL_ASSEMBLY);
+      MatAssemblyEnd(mat_duals_interior, MAT_FINAL_ASSEMBLY);
+    }
 
 
     // === Create and fill PETSc's right hand side vectors. ===
@@ -1080,6 +1220,8 @@ namespace AMDiS {
     // === Create PETSc solver for the FETI-DP operator. ===
 
     createFetiKsp();
+
+    PetscLogStagePop();
   }
 
 
@@ -1225,6 +1367,10 @@ namespace AMDiS {
 
     // === Create solver for the non primal (thus local) variables. ===
 
+    PetscLogStage stagePreFeti;
+    PetscLogStageRegister("Stage 0 PRE FETI-DP", &stagePreFeti);
+    PetscLogStagePush(stagePreFeti);
+
     KSPCreate(PETSC_COMM_WORLD, &ksp_b);
     KSPSetOperators(ksp_b, mat_b_b, mat_b_b, SAME_NONZERO_PATTERN);
     KSPSetOptionsPrefix(ksp_b, "solver_b_");
@@ -1269,9 +1415,17 @@ namespace AMDiS {
 
     // === Solve with FETI-DP operator. ===
 
-    MSG("START FETI SOLVE!\n");
+    PetscLogStagePop();
+    PetscLogStage stageFeti0, stageFeti1, stageFeti2;
+    PetscLogStageRegister("Stage 0 FETI-DP", &stageFeti0);
+    PetscLogStageRegister("Stage 1 FETI-DP", &stageFeti1);
+    PetscLogStageRegister("Stage 2 FETI-DP", &stageFeti2);
+    PetscLogStagePush(stageFeti0);
+    
     KSPSolve(ksp_feti, vec_rhs, vec_rhs);
 
+    PetscLogStagePop();
+    PetscLogStagePush(stageFeti1);
    
     // === Solve for u_primals. ===
 
@@ -1301,6 +1455,9 @@ namespace AMDiS {
 
     KSPSolve(ksp_b, tmp_b0, tmp_b0);
 
+    PetscLogStagePop();
+    PetscLogStagePush(stageFeti2);
+
 
     // === And recover AMDiS solution vectors. ===
     
@@ -1335,10 +1492,16 @@ namespace AMDiS {
     
     // === Destroy preconditioner data structures. ===
 
-    MatDestroy(&mat_interior_interior);
-    MatDestroy(&mat_bound_bound);
-    MatDestroy(&mat_interior_bound);
-    MatDestroy(&mat_bound_interior);
+    if (fetiPreconditioner != FETI_NONE)
+      MatDestroy(&mat_duals_duals);
+
+    if (fetiPreconditioner == FETI_DIRICHLET) {
+      MatDestroy(&mat_interior_interior);
+      MatDestroy(&mat_interior_duals);
+      MatDestroy(&mat_duals_interior);
+    }
+
+    PetscLogStagePop();
   }
 
 
diff --git a/AMDiS/src/parallel/PetscSolverFeti.h b/AMDiS/src/parallel/PetscSolverFeti.h
index 0a29a9414cfd63a37c65dcfa6ae514d44753b051..6a03db6c9496dcdb78df7218a83170c2181388fb 100644
--- a/AMDiS/src/parallel/PetscSolverFeti.h
+++ b/AMDiS/src/parallel/PetscSolverFeti.h
@@ -60,7 +60,7 @@ namespace AMDiS {
    * the system matrix reduced to the Lagrange multipliers.
    * \ref petscMultMatFeti
    */
-  struct PetscFetiData {
+  struct FetiData {
     /// Pointers to the matrix containing the primal variables.
     Mat *mat_primal_primal;
    
@@ -87,11 +87,11 @@ namespace AMDiS {
   };
 
 
-  struct PetscFetiPreconData {
+  struct FetiDirichletPreconData {
     /// Matrix of scaled Lagrange variables.
     Mat *mat_lagrange_scaled;
 
-    Mat *mat_interior_interior, *mat_bound_bound, *mat_interior_bound, *mat_bound_interior;
+    Mat *mat_interior_interior, *mat_duals_duals, *mat_interior_duals, *mat_duals_interior;
 
     /// Pointer to the solver for \ref PetscSolverFeti::mat_bb.
     KSP *ksp_interior;
@@ -99,27 +99,42 @@ namespace AMDiS {
     /// Temporal vector on the B variables.
     Vec tmp_vec_b;
 
-    Vec tmp_vec_bound0, tmp_vec_bound1;
-    
-    Vec tmp_vec_interior;
+    /// Temporal vector on the dual variables.
+    Vec tmp_vec_duals0, tmp_vec_duals1;
     
+    /// Temporal vector on the interior variables.
+    Vec tmp_vec_interior;    
+  };
 
-    int nLocalInterior;
 
-    int nLocalBound;
+  struct FetiLumpedPreconData {
+    /// Matrix of scaled Lagrange variables.
+    Mat *mat_lagrange_scaled;
+
+    Mat *mat_duals_duals;
+
+    /// Temporal vector on the B variables.
+    Vec tmp_vec_b;
+
+    /// Temporal vector on the dual variables.
+    Vec tmp_vec_duals0, tmp_vec_duals1;
   };
 
 
+  typedef enum {
+    FETI_NONE = 0,
+    FETI_DIRICHLET = 1,
+    FETI_LUMPED = 2
+  } FetiPreconditioner;
+
+
   /** \brief
    * FETI-DP implementation based on PETSc.
    */
   class PetscSolverFeti : public PetscSolver
   {
   public:
-    PetscSolverFeti() 
-      : PetscSolver(),
-	nComponents(-1)
-    {}
+    PetscSolverFeti();
 
     /// Assemble the sequentially created matrices and vectors to the
     /// global matrices and vectors required by the FETI-DP method.
@@ -288,22 +303,29 @@ namespace AMDiS {
     Mat mat_feti;
 
     /// Data for MatMult operation in matrix \ref mat_feti
-    PetscFetiData petscFetiData;
+    FetiData fetiData;
 
+    /// Defines which preconditioner should be used to solve the reduced
+    /// FETI-DP system.
+    FetiPreconditioner fetiPreconditioner;
+
+    /// Preconditioner object for the reduced FETI-DP system.
+    PC precon_feti;
 
     Mat mat_lagrange_scaled;
    
-    PC precon_feti;
+    FetiDirichletPreconData fetiDirichletPreconData;
 
-    PetscFetiPreconData petscFetiPreconData;
+    FetiLumpedPreconData fetiLumpedPreconData;
 
-    Mat mat_interior_interior, mat_bound_bound, mat_interior_bound, mat_bound_interior;
+    Mat mat_interior_interior, mat_duals_duals, mat_interior_duals, mat_duals_interior;
 
     KSP ksp_interior;
 
     int nLocalInterior;
     
-    int nLocalBound;
+    // Number of local nodes that are duals.
+    int nLocalDuals;
   };
 #endif