From f042083cad0a2ea7f11bc9c6ca046e415687fe7a Mon Sep 17 00:00:00 2001
From: Thomas Witkowski <thomas.witkowski@gmx.de>
Date: Wed, 15 Aug 2012 09:05:31 +0000
Subject: [PATCH] Better matrix nnz computation when using periodic boundary
 conditions.

---
 AMDiS/src/parallel/MatrixNnzStructure.cc      | 54 +++++++++++------
 AMDiS/src/parallel/MatrixNnzStructure.h       |  8 ++-
 AMDiS/src/parallel/PeriodicMap.cc             | 39 ++++++++++++
 AMDiS/src/parallel/PeriodicMap.h              | 34 +++++++++++
 AMDiS/src/parallel/PetscSolver.h              |  1 -
 AMDiS/src/parallel/PetscSolverGlobalMatrix.cc | 60 +++++--------------
 6 files changed, 129 insertions(+), 67 deletions(-)

diff --git a/AMDiS/src/parallel/MatrixNnzStructure.cc b/AMDiS/src/parallel/MatrixNnzStructure.cc
index c03c8c56..e8484c33 100644
--- a/AMDiS/src/parallel/MatrixNnzStructure.cc
+++ b/AMDiS/src/parallel/MatrixNnzStructure.cc
@@ -37,6 +37,7 @@ namespace AMDiS {
 				  ParallelDofMapping &rowDofMap,
 				  ParallelDofMapping &colDofMap,
 				  PeriodicMap *perMap,
+				  const ElementObjectDatabase& elObjDb,
 				  bool localMatrix)
   {
     FUNCNAME("MatrixNnzStructure::create()");
@@ -147,8 +148,12 @@ namespace AMDiS {
 	      petscRowIdx = rowDofMap.getMatIndex(rowComp, *cursor);
 	  }
 
-	  bool periodicRow = 
-	    (perMap && perMap->isPeriodic(rowFeSpace, rowIt->second.global));
+
+	  // Set of periodic row associations (is empty, if row DOF is not 
+	  // periodic.
+	  std::set<int> perRowAsc;
+	  if (perMap)
+	    perMap->fillAssociations(rowFeSpace, rowIt->second.global, elObjDb, perRowAsc);
 
 
 	  if (localMatrix || rowDofMap[rowFeSpace].isRankDof(*cursor)) {
@@ -186,24 +191,35 @@ namespace AMDiS {
 		     icend = end<nz>(cursor); icursor != icend; ++icursor) {
 		if (colDofMap[colFeSpace].find(col(*icursor), colDofIndex) == false)
 		  continue;
-		
-		int petscColIdx = (colDofMap.isMatIndexFromGlobal() ? 
-				   colDofMap.getMatIndex(colComp, colDofIndex.global) :
-				   colDofMap.getMatIndex(colComp, col(*icursor)));
-	
-		// The row DOF is a rank DOF, if also the column is a rank DOF, 
-		// increment the diagNnz values for this row, 
-		// otherwise the offdiagNnz value.
-		if (petscColIdx >= rankStartColIndex && 
-		    petscColIdx < rankStartColIndex + nRankCols)
-		  dnnz[localPetscRowIdx]++;
-		else
-		  onnz[localPetscRowIdx]++;
-	      }
 
-	      if (periodicRow) {
-		dnnz[localPetscRowIdx] *= 4;
-		onnz[localPetscRowIdx] *= 4;
+		// Set of periodic row associations (is empty, if row DOF is not 
+		// periodic.
+		std::set<int> perColAsc;
+		if (perMap)
+		  perMap->fillAssociations(colFeSpace, colDofIndex.global, elObjDb, perColAsc);
+
+		if (perColAsc.empty()) {
+		  if (colDofMap[colFeSpace].isRankDof(col(*icursor)))
+		    dnnz[localPetscRowIdx]++;
+		  else
+		    onnz[localPetscRowIdx]++;
+		} else {
+		  vector<int> newCols;
+		  perMap->mapDof(colFeSpace, colDofIndex.global, perColAsc, newCols);
+
+		  for (int aa = 0; aa < newCols.size(); aa++) {
+		    int petscColIdx = colDofMap.getMatIndex(colComp, newCols[aa]);
+		    
+		    // The row DOF is a rank DOF, if also the column is a rank DOF, 
+		    // increment the diagNnz values for this row, 
+		    // otherwise the offdiagNnz value.
+		    if (petscColIdx >= rankStartColIndex && 
+			petscColIdx < rankStartColIndex + nRankCols)
+		      dnnz[localPetscRowIdx]++;
+		    else
+		      onnz[localPetscRowIdx]++;
+		  }
+		}
 	      }
 	    }
 	  } else {
diff --git a/AMDiS/src/parallel/MatrixNnzStructure.h b/AMDiS/src/parallel/MatrixNnzStructure.h
index de266459..defb21de 100644
--- a/AMDiS/src/parallel/MatrixNnzStructure.h
+++ b/AMDiS/src/parallel/MatrixNnzStructure.h
@@ -44,16 +44,18 @@ namespace AMDiS {
 		MPI::Intracomm mpiComm,
 		ParallelDofMapping &rowDofMap,
 		ParallelDofMapping &colDofMap,
-		PeriodicMap *perMap = NULL,
+		PeriodicMap *perMap,
+		const ElementObjectDatabase& elObjDb,
 		bool localMatrix = false);
 
     void create(Matrix<DOFMatrix*> *mat,
 		MPI::Intracomm mpiComm,
 		ParallelDofMapping &dofMap,
-		PeriodicMap *perMap = NULL,
+		PeriodicMap *perMap,
+		const ElementObjectDatabase& elObjDb,
 		bool localMatrix = false)
     {
-      create(mat, mpiComm, dofMap, dofMap, perMap, localMatrix);
+      create(mat, mpiComm, dofMap, dofMap, perMap, elObjDb, localMatrix);
     }
 
   protected:
diff --git a/AMDiS/src/parallel/PeriodicMap.cc b/AMDiS/src/parallel/PeriodicMap.cc
index 558a46ce..1fe38a99 100644
--- a/AMDiS/src/parallel/PeriodicMap.cc
+++ b/AMDiS/src/parallel/PeriodicMap.cc
@@ -13,6 +13,7 @@
 #include <map>
 #include <fstream>
 #include "Global.h"
+#include "parallel/ElementObjectDatabase.h"
 #include "parallel/PeriodicMap.h"
 #include "parallel/ParallelTypes.h"
 
@@ -33,6 +34,44 @@ namespace AMDiS {
   }
 
 
+  void PeriodicMap::fillAssociations(const FiniteElemSpace* feSpace,
+				     int globalDofIndex,
+				     const ElementObjectDatabase &elObjDb,
+				     std::set<int>& perAsc)
+  {
+    if (isPeriodic(feSpace, globalDofIndex) == false)
+      return;
+
+    std::set<int>& tmpAsc = getAssociations(feSpace, globalDofIndex);
+    for (std::set<int>::iterator it = tmpAsc.begin(); it != tmpAsc.end(); ++it)
+      if (elObjDb.isValidPeriodicType(*it))
+	perAsc.insert(*it);
+  }
+
+
+  void PeriodicMap::mapDof(const FiniteElemSpace* feSpace,
+			   int globalDofIndex, 
+			   const std::set<int>& perAsc, 
+			   vector<int>& mappedDofs)
+  {
+    mappedDofs.clear();
+    mappedDofs.push_back(globalDofIndex);
+    
+    for (std::set<int>::const_iterator it = perAsc.begin();
+	 it != perAsc.end(); ++it) {
+      int nDofs = static_cast<int>(mappedDofs.size());
+      
+      for (int i = 0; i < nDofs; i++) {
+	TEST_EXIT_DBG(isPeriodic(feSpace, *it, mappedDofs[i]))
+	  ("Wrong periodic DOF associations at boundary %d with DOF %d!\n",
+	   *it, mappedDofs[i]);
+	
+	mappedDofs.push_back(map(feSpace, *it, mappedDofs[i]));
+      }
+    }
+  }
+
+
   void PeriodicMap::serialize(ostream &out, 
 			      vector<const FiniteElemSpace*> feSpaces)
   {
diff --git a/AMDiS/src/parallel/PeriodicMap.h b/AMDiS/src/parallel/PeriodicMap.h
index 8ed5e2e7..0670eeb9 100644
--- a/AMDiS/src/parallel/PeriodicMap.h
+++ b/AMDiS/src/parallel/PeriodicMap.h
@@ -26,6 +26,7 @@
 #include <map>
 #include <set>
 #include <vector>
+#include "AMDiS_fwd.h"
 #include "parallel/ParallelTypes.h"
 #include "Boundary.h"
 #include "Serializer.h"
@@ -126,6 +127,39 @@ namespace AMDiS {
     }
 
 
+    /** \brief
+     * Given a global DOF index in some given finite element space, this
+     * function creates all valid periodic associations of this DOF. If the
+     * DOF is not periodic, this function does not fail but has no effect on
+     * the output data.
+     *
+     * \param[in]  feSpace         feSpace on which the function should work on.
+     * \param[in]  globalDofIndex  global index of a DOF.
+     * \param[in]  elObjDb         Element object database that is used to check
+     *                             if a given periodic index is valid.
+     * \param[out] perAsc          set of periodic associations, is not deleted
+     *                             by this function
+     */
+    void fillAssociations(const FiniteElemSpace* feSpace,
+			  int globalDofIndex,
+			  const ElementObjectDatabase &elObjDb,
+			  std::set<int>& perAsc);
+
+    
+    /** \brief
+     * Maps a given DOF index for all given periodic DOF associations.
+     *
+     * \param[in]   feSpace         feSpace on which the function should work on.
+     * \param[in]   globalDofIndex  global index of a DOF.
+     * \param[in]   perAsc          set of periodic associations.
+     * \param[out]  mappedDofs      set of global DOF indices.
+     */
+    void mapDof(const FiniteElemSpace* feSpace,
+		int globalDofIndex, 
+		const std::set<int>& perAsc, 
+		vector<int>& mappedDofs);
+
+
     /// Returns true, if the DOF (global index) is a periodic DOF.
     inline bool isPeriodic(const FiniteElemSpace *feSpace, int globalDofIndex)
     {
diff --git a/AMDiS/src/parallel/PetscSolver.h b/AMDiS/src/parallel/PetscSolver.h
index 78fd2e55..57f78d28 100644
--- a/AMDiS/src/parallel/PetscSolver.h
+++ b/AMDiS/src/parallel/PetscSolver.h
@@ -113,7 +113,6 @@ namespace AMDiS {
 
     void setKspPrefix(std::string s)
     {
-      MSG("======>>>>>> SET TO %s\n", s.c_str());
       kspPrefix = s;
     }
 
diff --git a/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc b/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc
index c1deae93..70b1c65e 100644
--- a/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc
+++ b/AMDiS/src/parallel/PetscSolverGlobalMatrix.cc
@@ -37,7 +37,8 @@ namespace AMDiS {
 
     if (checkMeshChange(mat))
       nnzInterior.create(mat, mpiCommGlobal, *interiorMap, 
-			 &(meshDistributor->getPeriodicMap()));
+			 &(meshDistributor->getPeriodicMap()),
+			 meshDistributor->getElementObjectDb());
 
     // === Create PETSc vector (solution and a temporary vector). ===
 
@@ -109,7 +110,6 @@ namespace AMDiS {
     KSPSetOperators(kspInterior, matIntInt, matIntInt, SAME_NONZERO_PATTERN); 
     KSPSetTolerances(kspInterior, 0.0, 1e-8, PETSC_DEFAULT, PETSC_DEFAULT);
     KSPSetType(kspInterior, KSPBCGS);
-    MSG("SET PREFIX TOOO: %s\n", kspPrefix.c_str());
     KSPSetOptionsPrefix(kspInterior, kspPrefix.c_str());
     KSPSetFromOptions(kspInterior);
 
@@ -148,12 +148,14 @@ namespace AMDiS {
 
     bool localMatrix = (subdomainLevel == 0);
     if (checkMeshChange(mat, localMatrix)) {
-      nnzInterior.create(mat, mpiCommGlobal, *interiorMap, NULL, localMatrix);
+      nnzInterior.create(mat, mpiCommGlobal, *interiorMap, NULL, 
+			 meshDistributor->getElementObjectDb(),
+			 localMatrix);
 
       if (coarseSpaceMap) {
-	nnzCoarse.create(mat, mpiCommGlobal, *coarseSpaceMap);
-	nnzCoarseInt.create(mat, mpiCommGlobal, *coarseSpaceMap, *interiorMap);
-	nnzIntCoarse.create(mat, mpiCommGlobal, *interiorMap, *coarseSpaceMap);
+	nnzCoarse.create(mat, mpiCommGlobal, *coarseSpaceMap, NULL, meshDistributor->getElementObjectDb());
+	nnzCoarseInt.create(mat, mpiCommGlobal, *coarseSpaceMap, *interiorMap, NULL, meshDistributor->getElementObjectDb());
+	nnzIntCoarse.create(mat, mpiCommGlobal, *interiorMap, *coarseSpaceMap, NULL, meshDistributor->getElementObjectDb());
       }
     }
 
@@ -743,12 +745,9 @@ namespace AMDiS {
 
 	    // Create set of all periodic associations of the column index.
 	    std::set<int> perAsc;
-	    std::set<int>& perColAsc = perMap.getAssociations(colFe, globalColDof);
-	    for (std::set<int>::iterator it = perColAsc.begin(); 
-		 it != perColAsc.end(); ++it)
-	      if (meshDistributor->getElementObjectDb().isValidPeriodicType(*it))
-		perAsc.insert(*it);
-    
+	    perMap.fillAssociations(colFe, globalColDof, 
+				    meshDistributor->getElementObjectDb(), perAsc);
+
 	    // Scale value to the number of periodic associations of the column index.
 	    double scaledValue = 
 	      value(*icursor) * pow(0.5, static_cast<double>(perAsc.size()));
@@ -758,24 +757,7 @@ namespace AMDiS {
 	    // === associations of the column DOF index.                       ===
 
 	    vector<int> newCols;
-
-	    // First, add the original matrix index.
-	    newCols.push_back(globalColDof);
-
-	    // And add all periodic matrix indices.
-	    for (std::set<int>::iterator it = perAsc.begin(); 
-		 it != perAsc.end(); ++it) {
-	      int nCols = static_cast<int>(newCols.size());
-
-	      for (int i = 0; i < nCols; i++) {
- 		TEST_EXIT_DBG(perMap.isPeriodic(colFe, *it, newCols[i]))
- 		  ("Wrong periodic DOF associations at boundary %d with DOF %d!\n",
-		   *it, newCols[i]);
-
-		newCols.push_back(perMap.map(colFe, *it, newCols[i]));
-	      }
-	    }
-
+	    perMap.mapDof(colFe, globalColDof, perAsc, newCols);
 	    for (unsigned int i = 0; i < newCols.size(); i++) {
 	      cols.push_back(interiorMap->getMatIndex(nColMat, newCols[i]));
 	      values.push_back(scaledValue);	      
@@ -809,20 +791,10 @@ namespace AMDiS {
 	  // === indices to the set perAsc.                                    ===
 
 	  std::set<int> perAsc;
-
-	  if (perMap.isPeriodic(colFe, globalColDof)) {
-	    std::set<int>& perColAsc = perMap.getAssociations(colFe, globalColDof);
-	    for (std::set<int>::iterator it = perColAsc.begin(); 
-		 it != perColAsc.end(); ++it)
-	      if (meshDistributor->getElementObjectDb().isValidPeriodicType(*it))
-		perAsc.insert(*it);
-	  }
-
-	  std::set<int>& perRowAsc = perMap.getAssociations(rowFe, globalRowDof);
-	  for (std::set<int>::iterator it = perRowAsc.begin(); 
-	       it != perRowAsc.end(); ++it)
-	    if (meshDistributor->getElementObjectDb().isValidPeriodicType(*it))
-	      perAsc.insert(*it);
+	  perMap.fillAssociations(colFe, globalColDof, 
+				  meshDistributor->getElementObjectDb(), perAsc);
+	  perMap.fillAssociations(rowFe, globalRowDof, 
+				  meshDistributor->getElementObjectDb(), perAsc);
 
 	  // Scale the value with respect to the number of periodic associations.
 	  double scaledValue = 
-- 
GitLab