From ac22232d6e7289242016836e428077ea25b2e601 Mon Sep 17 00:00:00 2001
From: Thomas Witkowski <thomas.witkowski@gmx.de>
Date: Mon, 2 Jan 2012 14:35:52 +0000
Subject: [PATCH] Added PETSc global matrix block solver.

---
 AMDiS/CMakeLists.txt                          |   1 +
 AMDiS/src/parallel/PetscProblemStat.cc        |   2 +
 AMDiS/src/parallel/PetscProblemStat.h         |   1 +
 AMDiS/src/parallel/PetscSolverFeti.cc         |  48 ----
 AMDiS/src/parallel/PetscSolverFeti.h          |  33 +--
 .../parallel/PetscSolverGlobalBlockMatrix.cc  | 244 ++++++++++++++++++
 .../parallel/PetscSolverGlobalBlockMatrix.h   |  68 +++++
 AMDiS/src/parallel/PetscSolverGlobalMatrix.cc |  10 -
 8 files changed, 317 insertions(+), 90 deletions(-)
 create mode 100644 AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.cc
 create mode 100644 AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.h

diff --git a/AMDiS/CMakeLists.txt b/AMDiS/CMakeLists.txt
index d4be883b..356e28fa 100644
--- a/AMDiS/CMakeLists.txt
+++ b/AMDiS/CMakeLists.txt
@@ -242,6 +242,7 @@ if(ENABLE_PARALLEL_DOMAIN)
 			${SOURCE_DIR}/parallel/PetscProblemStat.cc
 			${SOURCE_DIR}/parallel/PetscSolverFeti.cc
 			${SOURCE_DIR}/parallel/PetscSolverGlobalMatrix.cc
+			${SOURCE_DIR}/parallel/PetscSolverGlobalBlockMatrix.cc
 			${SOURCE_DIR}/parallel/PetscSolverSchur.cc)
 	elseif(ENABLE_PARALLEL_DOMAIN STREQUAL "PMTL")
 		set(MTL_INCLUDE_DIR "")
diff --git a/AMDiS/src/parallel/PetscProblemStat.cc b/AMDiS/src/parallel/PetscProblemStat.cc
index 6e03c86b..d5e4d9b1 100644
--- a/AMDiS/src/parallel/PetscProblemStat.cc
+++ b/AMDiS/src/parallel/PetscProblemStat.cc
@@ -44,6 +44,8 @@ namespace AMDiS {
 #else
       ERROR_EXIT("PETSc FETI-DP solver is only supported when petsc-dev is used!\n");
 #endif
+    } else if (name == "petsc-block") {
+      petscSolver = new PetscSolverGlobalBlockMatrix();
     } else if (name == "petsc" || name == "") {
       petscSolver = new PetscSolverGlobalMatrix();
     } else {
diff --git a/AMDiS/src/parallel/PetscProblemStat.h b/AMDiS/src/parallel/PetscProblemStat.h
index 9638cc7c..07a837b4 100644
--- a/AMDiS/src/parallel/PetscProblemStat.h
+++ b/AMDiS/src/parallel/PetscProblemStat.h
@@ -29,6 +29,7 @@
 #include "parallel/PetscSolver.h"
 #include "parallel/PetscSolverFeti.h"
 #include "parallel/PetscSolverGlobalMatrix.h"
+#include "parallel/PetscSolverGlobalBlockMatrix.h"
 #include "parallel/PetscSolverSchur.h"
 
 namespace AMDiS {
diff --git a/AMDiS/src/parallel/PetscSolverFeti.cc b/AMDiS/src/parallel/PetscSolverFeti.cc
index 8a014201..02551918 100644
--- a/AMDiS/src/parallel/PetscSolverFeti.cc
+++ b/AMDiS/src/parallel/PetscSolverFeti.cc
@@ -20,14 +20,12 @@ namespace AMDiS {
   using namespace std;
 
 #ifdef HAVE_PETSC_DEV 
-  FetiStatisticsData PetscSolverFeti::fetiStatistics;
 
   // y = mat * x
   int petscMultMatSchurPrimal(Mat mat, Vec x, Vec y)
   {
     // S_PiPi = K_PiPi - K_PiB inv(K_BB) K_BPi
 
-    double first = MPI::Wtime();
     void *ctx;
     MatShellGetContext(mat, &ctx);
     SchurPrimalData* data = static_cast<SchurPrimalData*>(ctx);
@@ -38,9 +36,6 @@ namespace AMDiS {
     MatMult(*(data->mat_primal_primal), x, y);
     VecAXPBY(y, -1.0, 1.0, data->tmp_vec_primal);
 
-    PetscSolverFeti::fetiStatistics.nSchurPrimalApply++;
-    PetscSolverFeti::fetiStatistics.timeSchurPrimalApply += MPI::Wtime() - first;
-
     return 0;
   }
 
@@ -67,8 +62,6 @@ namespace AMDiS {
 
     VecAXPBY(y, 1.0, 1.0, data->tmp_vec_lagrange);
 
-    PetscSolverFeti::fetiStatistics.nFetiApply++;
-
     return 0;
   }
 
@@ -1613,11 +1606,7 @@ namespace AMDiS {
     VecAXPBY(tmp_primal0, 1.0, -1.0, f_primal);
 
     // tmp_primal0 = inv(S_PiPi) (f_Pi - M_PiB * inv(K_BB) * f_B)
-    double first = MPI::Wtime();
     KSPSolve(ksp_schur_primal, tmp_primal0, tmp_primal0);
-    PetscSolverFeti::fetiStatistics.nSchurPrimalSolve++;
-    PetscSolverFeti::fetiStatistics.timeSchurPrimalSolve +=
-      MPI::Wtime() - first;
 
     //
     MatMult(mat_b_primal, tmp_primal0, tmp_b0);
@@ -1629,9 +1618,7 @@ namespace AMDiS {
 
 
     // === Solve with FETI-DP operator. ===
-    first = MPI::Wtime();
     KSPSolve(ksp_feti, vec_rhs, vec_rhs);
-    fetiStatistics.timeFetiApply = MPI::Wtime() -first;
 
     // === Solve for u_primals. ===
 
@@ -1644,11 +1631,7 @@ namespace AMDiS {
     MatMult(mat_primal_b, tmp_b0, tmp_primal1);
 
     VecAXPBY(tmp_primal0, 1.0, 1.0, tmp_primal1);
-    first = MPI::Wtime();
     KSPSolve(ksp_schur_primal, tmp_primal0, tmp_primal0);
-    PetscSolverFeti::fetiStatistics.nSchurPrimalSolve++;
-    PetscSolverFeti::fetiStatistics.timeSchurPrimalSolve +=
-      MPI::Wtime() - first;
 
     
     // === Solve for u_b. ===
@@ -1719,43 +1702,12 @@ namespace AMDiS {
     if (debug) {
       solveFetiMatrix(vec);
     } else {
-    //    resetStatistics();
       solveReducedFetiMatrix(vec);
-    //    printStatistics();
     } 
 
     MeshDistributor::globalMeshDistributor->synchVector(vec);
   }
 
-
-  void PetscSolverFeti::resetStatistics()
-  {
-    fetiStatistics.nFetiApply = 0;
-    fetiStatistics.timeFetiApply = 0.0;
-    fetiStatistics.nSchurPrimalApply = 0;
-    fetiStatistics.timeSchurPrimalApply = 0.0;
-    fetiStatistics.nSchurPrimalSolve = 0;
-    fetiStatistics.timeSchurPrimalSolve = 0.0;
-    fetiStatistics.nLocalSolve = 0;
-    fetiStatistics.timeLocalSolve = 0.0;
-  }
-
-
-  void PetscSolverFeti::printStatistics()
-  {
-    FUNCNAME("PetscSolverFeti::printStatistics()");
-
-    if (MPI::COMM_WORLD.Get_rank() == 0)
-      MSG("FETI-STATISTICS: %d %f %d %f %d %f %d %f\n",
-	  fetiStatistics.nFetiApply,
-	  fetiStatistics.timeFetiApply,
-	  fetiStatistics.nSchurPrimalApply,
-	  fetiStatistics.timeSchurPrimalApply,
-	  fetiStatistics.nSchurPrimalSolve,
-	  fetiStatistics.timeSchurPrimalSolve,
-	  fetiStatistics.nLocalSolve,
-	  fetiStatistics.timeLocalSolve);	  
-  }
 #endif
 
 }
diff --git a/AMDiS/src/parallel/PetscSolverFeti.h b/AMDiS/src/parallel/PetscSolverFeti.h
index 795e381f..336a4545 100644
--- a/AMDiS/src/parallel/PetscSolverFeti.h
+++ b/AMDiS/src/parallel/PetscSolverFeti.h
@@ -125,35 +125,7 @@ namespace AMDiS {
   } FetiPreconditioner;
 
 
-  struct FetiStatisticsData
-  {
-    /// Number of application of the FETI-DP operator.
-    int nFetiApply;
-
-    /// Time for solving the reduced FETI system.
-    double timeFetiApply;
-
-    /// Number of application of the Schur primal operator.
-    int nSchurPrimalApply;
-
-    /// Time for appling the Schur primal operator.
-    double timeSchurPrimalApply;
-
-    /// Number of solution of the Schur primal system.
-    int nSchurPrimalSolve;
-
-    /// Time for solving the Schur primal system.
-    double timeSchurPrimalSolve;
-
-    /// Number of solution of the local subdomain problems.
-    int nLocalSolve;
-
-    /// Time for solving the local subdomain problems.
-    double timeLocalSolve;
-  };
-
-
-  /** \brief
+/** \brief
    * FETI-DP implementation based on PETSc.
    */
   class PetscSolverFeti : public PetscSolver
@@ -365,9 +337,6 @@ namespace AMDiS {
     
     // Number of local nodes that are duals.
     int nLocalDuals;
-
-  public:
-    static FetiStatisticsData fetiStatistics;
   };
 #endif
 
diff --git a/AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.cc b/AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.cc
new file mode 100644
index 00000000..6c1a856c
--- /dev/null
+++ b/AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.cc
@@ -0,0 +1,244 @@
+//
+// Software License for AMDiS
+//
+// Copyright (c) 2010 Dresden University of Technology 
+// All rights reserved.
+// Authors: Simon Vey, Thomas Witkowski et al.
+//
+// This file is part of AMDiS
+//
+// See also license.opensource.txt in the distribution.
+
+
+#include "parallel/PetscSolverGlobalBlockMatrix.h"
+#include "parallel/StdMpi.h"
+#include "parallel/MpiHelper.h"
+
+namespace AMDiS {
+
+  void PetscSolverGlobalBlockMatrix::fillPetscMatrix(Matrix<DOFMatrix*> *mat)
+  {
+    FUNCNAME("PetscSolverGlobalBlockMatrix::fillPetscMatrix()");
+
+    TEST_EXIT_DBG(meshDistributor)("No mesh distributor object defined!\n");
+    TEST_EXIT_DBG(mat)("No DOF matrix defined!\n");
+
+    double wtime = MPI::Wtime();
+    nComponents = mat->getNumRows();
+    int nRankRows = meshDistributor->getNumberRankDofs() * nComponents;
+    int nOverallRows = meshDistributor->getNumberOverallDofs() * nComponents;
+
+    // === Create PETSc vector (solution and a temporary vector). ===
+
+    VecCreateMPI(PETSC_COMM_WORLD, nRankRows, nOverallRows, &petscSolVec);
+    VecCreateMPI(PETSC_COMM_WORLD, nRankRows, nOverallRows, &petscTmpVec);
+
+
+#if (DEBUG != 0)
+    MSG("Fill petsc matrix 1 needed %.5f seconds\n", MPI::Wtime() - wtime);
+#endif
+
+    nestMat = new Mat*[nComponents];
+    for (int i = 0; i < nComponents; i++)
+      nestMat[i] = new Mat[nComponents];
+
+    // === Transfer values from DOF matrices to the PETSc matrix. === 
+
+    for (int i = 0; i < nComponents; i++)
+      for (int j = 0; j < nComponents; j++)
+	if ((*mat)[i][j]) {
+	  MatCreateMPIAIJ(PETSC_COMM_WORLD,
+			  nRankRows, nRankRows,
+			  nOverallRows, nOverallRows,
+			  30, PETSC_NULL, 30, PETSC_NULL,
+			  &(nestMat[i][j]));
+
+	  setDofMatrix(nestMat[i][j], (*mat)[i][j]);
+	} else {
+	  nestMat[i][j] = PETSC_NULL;
+	}
+
+    MatCreateNest(PETSC_COMM_WORLD, 
+		  nComponents, PETSC_NULL, nComponents, PETSC_NULL, 
+		  &(nestMat[0][0]), &petscMatrix);
+
+#if (DEBUG != 0)
+    MSG("Fill petsc matrix 2 needed %.5f seconds\n", MPI::Wtime() - wtime);
+#endif
+
+    MatAssemblyBegin(petscMatrix, MAT_FINAL_ASSEMBLY);
+    MatAssemblyEnd(petscMatrix, MAT_FINAL_ASSEMBLY);
+
+    // === Init PETSc solver. ===
+    KSPCreate(PETSC_COMM_WORLD, &solver);
+    KSPGetPC(solver, &pc);
+    KSPSetOperators(solver, petscMatrix, petscMatrix, SAME_NONZERO_PATTERN); 
+    KSPSetTolerances(solver, 0.0, 1e-8, PETSC_DEFAULT, PETSC_DEFAULT);
+    KSPSetType(solver, KSPBCGS);
+    KSPSetFromOptions(solver);
+    PCSetFromOptions(pc);
+
+    MSG("Fill petsc matrix needed %.5f seconds\n", MPI::Wtime() - wtime);
+  }
+
+
+  void PetscSolverGlobalBlockMatrix::fillPetscRhs(SystemVector *vec)
+  {
+    FUNCNAME("PetscSolverGlobalBlockMatrix::fillPetscRhs()");
+
+    TEST_EXIT_DBG(vec)("NO DOF vector defined!\n");
+
+    nComponents = vec->getSize();
+    int nRankRows = meshDistributor->getNumberRankDofs() * nComponents;
+    int nOverallRows = meshDistributor->getNumberOverallDofs() * nComponents;
+
+    VecCreateMPI(PETSC_COMM_WORLD, nRankRows, nOverallRows, &petscRhsVec);
+
+    // === Transfer values from DOF vector to the PETSc vector. === 
+
+    for (int i = 0; i < nComponents; i++)
+      setDofVector(petscRhsVec, vec->getDOFVector(i), nComponents, i);
+
+    VecAssemblyBegin(petscRhsVec);
+    VecAssemblyEnd(petscRhsVec);
+  }
+
+
+  void PetscSolverGlobalBlockMatrix::solvePetscMatrix(SystemVector &vec, 
+						      AdaptInfo *adaptInfo)
+  {
+    FUNCNAME("PetscSolverGlobalBlockMatrix::solvePetscMatrix()");
+
+    // PETSc.
+    KSPSolve(solver, petscRhsVec, petscSolVec);
+
+    // === Transfere values from PETSc's solution vectors to the DOF vectors. ===
+    int nRankDofs = meshDistributor->getNumberRankDofs();
+    PetscScalar *vecPointer;
+    VecGetArray(petscSolVec, &vecPointer);
+
+    for (int i = 0; i < nComponents; i++) {
+      DOFVector<double> &dofvec = *(vec.getDOFVector(i));
+      for (int j = 0; j < nRankDofs; j++)
+	dofvec[meshDistributor->mapLocalToDofIndex(j)] = 
+	  vecPointer[i * nComponents + j]; 
+    }
+
+    VecRestoreArray(petscSolVec, &vecPointer);
+
+
+    // === Synchronize DOFs at common DOFs, i.e., DOFs that correspond to ===
+    // === more than one partition.                                       ===
+    meshDistributor->synchVector(vec);
+
+
+    // Print iteration counter and residual norm of the solution.
+    printSolutionInfo(adaptInfo);
+
+
+    // === Destroy PETSc's variables. ===
+    VecDestroy(&petscRhsVec);
+  }
+
+
+  void PetscSolverGlobalBlockMatrix::destroyMatrixData()
+  {
+    FUNCNAME("PetscSolverGlobalBlockMatrix::destroyMatrixData()");
+
+    MatDestroy(&petscMatrix);
+    KSPDestroy(&solver);
+    VecDestroy(&petscSolVec);
+    VecDestroy(&petscTmpVec);
+
+    for (int i = 0; i < nComponents; i++) {
+      for (int j = 0; j < nComponents; j++) {
+	if (nestMat[i][j] != PETSC_NULL)
+	  MatDestroy(&(nestMat[i][j]));
+      }
+
+      delete[] nestMat[i];
+    }
+    delete[] nestMat;
+  }
+
+
+  void PetscSolverGlobalBlockMatrix::setDofMatrix(Mat& petscMat, DOFMatrix* mat)
+  {
+    FUNCNAME("PetscSolverGlobalBlockMatrix::setDofMatrix()");
+
+    TEST_EXIT(mat)("No DOFMatrix!\n");
+    TEST_EXIT(petscMat)("No PETSc matrix!\n");
+
+    using mtl::tag::row; using mtl::tag::nz; using mtl::begin; using mtl::end;
+    namespace traits = mtl::traits;
+    typedef DOFMatrix::base_matrix_type Matrix;
+
+    traits::col<Matrix>::type col(mat->getBaseMatrix());
+    traits::const_value<Matrix>::type value(mat->getBaseMatrix());
+
+    typedef traits::range_generator<row, Matrix>::type cursor_type;
+    typedef traits::range_generator<nz, cursor_type>::type icursor_type;
+
+    vector<int> cols;
+    vector<double> values;
+    cols.reserve(300);
+    values.reserve(300);
+    
+    // === Traverse all rows of the dof matrix and insert row wise the values ===
+    // === to the PETSc matrix.                                               ===
+
+    for (cursor_type cursor = begin<row>(mat->getBaseMatrix()), 
+	   cend = end<row>(mat->getBaseMatrix()); cursor != cend; ++cursor) {
+
+      // Global index of the current row DOF.
+      int rowIndex = meshDistributor->mapLocalToGlobal(*cursor);
+
+      cols.clear();
+      values.clear();
+      
+      for (icursor_type icursor = begin<nz>(cursor), icend = end<nz>(cursor); 
+	   icursor != icend; ++icursor) {	
+	// Global index of the current column index.
+	int colIndex = meshDistributor->mapLocalToGlobal(col(*icursor));
+	
+	// Ignore all zero entries, expect it is a diagonal entry.
+	if (value(*icursor) == 0.0 && rowIndex != colIndex)
+	  continue;
+	
+	// Calculate the exact position of the column index in the PETSc matrix.
+	cols.push_back(colIndex);
+	values.push_back(value(*icursor));
+      }
+      
+      MatSetValues(petscMatrix, 1, &rowIndex, cols.size(), 
+		   &(cols[0]), &(values[0]), ADD_VALUES);	
+    }
+  }
+  
+  
+  void PetscSolverGlobalBlockMatrix::setDofVector(Vec& petscVec, 
+						  DOFVector<double>* vec, 
+						  int nComponents,
+						  int row,
+						  bool rankOnly)
+  {
+    FUNCNAME("PetscSolverGlobalBlockMatrix::setDofVector()");
+
+    // Traverse all used DOFs in the dof vector.
+    DOFVector<double>::Iterator dofIt(vec, USED_DOFS);
+    for (dofIt.reset(); !dofIt.end(); ++dofIt) {
+      if (rankOnly && !meshDistributor->getIsRankDof(dofIt.getDOFIndex()))
+	continue;
+
+      // Calculate global row index of the DOF.
+      DegreeOfFreedom globalRowDof = 
+	meshDistributor->mapLocalToGlobal(dofIt.getDOFIndex());
+      // Calculate PETSc index of the row DOF.
+      int index = nComponents * row + globalRowDof;
+      double value = *dofIt;
+
+      VecSetValues(petscVec, 1, &index, &value, ADD_VALUES);
+    }
+  }
+
+}
diff --git a/AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.h b/AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.h
new file mode 100644
index 00000000..98e0255a
--- /dev/null
+++ b/AMDiS/src/parallel/PetscSolverGlobalBlockMatrix.h
@@ -0,0 +1,68 @@
+// ============================================================================
+// ==                                                                        ==
+// == AMDiS - Adaptive multidimensional simulations                          ==
+// ==                                                                        ==
+// ==  http://www.amdis-fem.org                                              ==
+// ==                                                                        ==
+// ============================================================================
+//
+// Software License for AMDiS
+//
+// Copyright (c) 2010 Dresden University of Technology 
+// All rights reserved.
+// Authors: Simon Vey, Thomas Witkowski et al.
+//
+// This file is part of AMDiS
+//
+// See also license.opensource.txt in the distribution.
+
+
+
+/** \file PetscSolverGlobalBlockMatrix.h */
+
+#ifndef AMDIS_PETSC_SOLVER_GLOBAL_BLOCK_MATRIX_H
+#define AMDIS_PETSC_SOLVER_GLOBAL_BLOCK_MATRIX_H
+
+#include "AMDiS_fwd.h"
+#include "parallel/PetscSolver.h"
+
+namespace AMDiS {
+
+  using namespace std;
+
+
+  class PetscSolverGlobalBlockMatrix : public PetscSolver
+  {
+  public:
+    PetscSolverGlobalBlockMatrix()
+      : PetscSolver(),
+	nComponents(0)
+    {}
+
+    void fillPetscMatrix(Matrix<DOFMatrix*> *mat);
+
+    void fillPetscRhs(SystemVector *vec);
+
+    void solvePetscMatrix(SystemVector &vec, AdaptInfo *adaptInfo);    
+
+    void destroyMatrixData();
+
+  protected:
+    /// Takes a DOF matrix and sends the values to the global PETSc matrix.
+    void setDofMatrix(Mat& petscMat, DOFMatrix* mat);
+
+    /// Takes a DOF vector and sends its values to a given PETSc vector.
+    void setDofVector(Vec& petscVec, DOFVector<double>* vec, 
+		      int nComponents, int row, bool rankOnly = false);
+
+  protected:
+    Mat **nestMat;
+
+    int nComponents;
+  };
+
+
+}
+
+#endif
+
diff --git a/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc b/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc
index d0206cf8..21c10576 100644
--- a/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc
+++ b/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc
@@ -16,15 +16,6 @@
 
 namespace AMDiS {
 
-  PetscErrorCode myKSPMonitor(KSP ksp, PetscInt iter, PetscReal rnorm, void *)
-  {    
-    if (iter % 100 == 0 && MPI::COMM_WORLD.Get_rank() == 0)
-      cout << "[0]  Petsc-Iteration " << iter << ": " << rnorm << endl;
-
-    return 0;
-  }
-
-
   void PetscSolverGlobalMatrix::fillPetscMatrix(Matrix<DOFMatrix*> *mat)
   {
     FUNCNAME("PetscSolverGlobalMatrix::fillPetscMatrix()");
@@ -102,7 +93,6 @@ namespace AMDiS {
     KSPSetOperators(solver, petscMatrix, petscMatrix, SAME_NONZERO_PATTERN); 
     KSPSetTolerances(solver, 0.0, 1e-8, PETSC_DEFAULT, PETSC_DEFAULT);
     KSPSetType(solver, KSPBCGS);
-    KSPMonitorSet(solver, myKSPMonitor, PETSC_NULL, 0);
     KSPSetFromOptions(solver);
     PCSetFromOptions(pc);
 
-- 
GitLab