From d7f6bb25c579a14436d01da96b64af1fce320a48 Mon Sep 17 00:00:00 2001
From: Matti Pellika <matti.pellikka@tut.fi>
Date: Wed, 22 Apr 2009 07:07:53 +0000
Subject: [PATCH] Added Chain class in order to visualize the homology
 generator chains. Modified utils/misc/celldriver.cpp example to demonstrate
 this.

---
 Geo/CellComplex.cpp       | 38 ++++++++++------
 Geo/CellComplex.h         | 23 +++++-----
 Geo/ChainComplex.cpp      | 92 +++++++++++++++++++++++++++++++++++++--
 Geo/ChainComplex.h        | 40 ++++++++++++++++-
 utils/misc/celldriver.cpp | 56 ++++++++++++++++++++----
 5 files changed, 212 insertions(+), 37 deletions(-)

diff --git a/Geo/CellComplex.cpp b/Geo/CellComplex.cpp
index 5d17436d71..b7da2fc688 100644
--- a/Geo/CellComplex.cpp
+++ b/Geo/CellComplex.cpp
@@ -15,6 +15,15 @@ CellComplex::CellComplex( std::vector<GEntity*> domain, std::vector<GEntity*> su
   // subdomain need to be inserted first!
   insertCells(true);
   insertCells(false);
+
+  int tag = 1;
+  for(int i = 0; i < 4; i++){
+    for(citer cit = firstCell(i); cit != lastCell(i); cit++){
+      Cell* cell = *cit;
+      cell->setTag(tag);
+      tag++;
+    }
+  }
   
 }
 void CellComplex::insertCells(bool subdomain){  
@@ -493,13 +502,18 @@ int CellComplex::writeComplexMSH(const std::string &name){
   
     
   FILE *fp = fopen(name.c_str(), "w");
+  
   if(!fp){
     Msg::Error("Unable to open file '%s'", name.c_str());
     printf("Unable to open file.");
     return 0;
   }
   
-  fprintf(fp, "$NOD\n");
+  
+  
+  fprintf(fp, "$MeshFormat\n2.0 0 8\n$EndMeshFormat\n");
+  
+  fprintf(fp, "$Nodes\n");
   
   std::set<MVertex*, Less_MVertex> domainVertices;
   getDomainVertices(domainVertices, true);
@@ -513,38 +527,34 @@ int CellComplex::writeComplexMSH(const std::string &name){
   }
   
       
-  fprintf(fp, "$ENDNOD\n");
-  fprintf(fp, "$ELM\n");
+  fprintf(fp, "$EndNodes\n");
+  fprintf(fp, "$Elements\n");
 
   fprintf(fp, "%d\n", _cells[0].size() + _cells[1].size() + _cells[2].size() + _cells[3].size());
   
-  int index = 1;
-  
   for(citer cit = firstCell(0); cit != lastCell(0); cit++) {
     Cell* vertex = *cit;
-    fprintf(fp, "%d %d %d %d %d %d\n", index, 15, 0, 1, 1, vertex->getVertex(0));
-    index++;
+    fprintf(fp, "%d %d %d %d %d %d %d\n", vertex->getTag(), 15, 3, 0, 0, 0, vertex->getVertex(0));
   }
   
   
   for(citer cit = firstCell(1); cit != lastCell(1); cit++) {
     Cell* edge = *cit;
-    fprintf(fp, "%d %d %d %d %d %d %d\n", index, 1, 0, 1, 2, edge->getVertex(0), edge->getVertex(1));
-    index++;
+    fprintf(fp, "%d %d %d %d %d %d %d %d\n", edge->getTag(), 1, 3, 0, 0, 0, edge->getVertex(0), edge->getVertex(1));
   }
   
   for(citer cit = firstCell(2); cit != lastCell(2); cit++) {
     Cell* face = *cit;
-    fprintf(fp, "%d %d %d %d %d %d %d %d\n", index, 2, 0, 1, 3, face->getVertex(0), face->getVertex(1), face->getVertex(2));
-    index++;
+    fprintf(fp, "%d %d %d %d %d %d %d %d %d\n", face->getTag(), 2, 3, 0, 0, 0, face->getVertex(0), face->getVertex(1), face->getVertex(2));
   }
   for(citer cit = firstCell(3); cit != lastCell(3); cit++) {
     Cell* volume = *cit;
-    fprintf(fp, "%d %d %d %d %d %d %d %d %d\n", index, 4, 0, 1, 4, volume->getVertex(0), volume->getVertex(1), volume->getVertex(2), volume->getVertex(3));
-    index++;
+    fprintf(fp, "%d %d %d %d %d %d %d %d %d %d\n", volume->getTag(), 4, 3, 0, 0, 0, volume->getVertex(0), volume->getVertex(1), volume->getVertex(2), volume->getVertex(3));
   }
     
-  fprintf(fp, "$ENDELM\n");
+  fprintf(fp, "$EndElements\n");
+  
+  fclose(fp);
   
   return 1;
 }
diff --git a/Geo/CellComplex.h b/Geo/CellComplex.h
index 9cf34a2a59..ee70c278c8 100644
--- a/Geo/CellComplex.h
+++ b/Geo/CellComplex.h
@@ -35,17 +35,20 @@ class Cell
 
    int _bdSize;
    int _cbdSize;
-   
+      
    // whether this cell belongs to a subdomain
    // used in relative homology computation
    bool _inSubdomain;
    
+   int _tag;
+   
   public:
    Cell(){}
    virtual ~Cell(){}
    
    virtual int getDim() const { return _dim; };
-   //virtual int getTag() const { return _tag; };
+   virtual int getTag() const { return _tag; };
+   virtual void setTag(int tag) { _tag = tag; };
    
    // get the number of vertices this cell has
    virtual int getNumVertices() const = 0; //{return _vertices.size();}
@@ -68,7 +71,7 @@ class Cell
    virtual void setBdSize(int size) { _bdSize = size; }
    virtual int getCbdSize() { return _cbdSize; }
    virtual void setCbdSize(int size) { _cbdSize = size; }
-   
+      
    virtual bool inSubdomain() const { return _inSubdomain; }
    
    // print cell vertices
@@ -112,7 +115,7 @@ class Simplex : public Cell
 { 
    
  public:
-   Simplex(){
+   Simplex() : Cell() {
      _cbdSize = 1000; // big number
      _bdSize = 1000;
    }
@@ -344,11 +347,7 @@ class CellComplex
      
    // remove cell from the queue set
    virtual void removeCellQset(Cell*& cell, std::set<Cell*, Less_Cell>& Qset);
-   
-   // get all the finite element mesh vertices in the domain of this cell complex
-   virtual void getDomainVertices(std::set<MVertex*, Less_MVertex>& domainVertices, 
-                                  bool subdomain);
-   
+      
    // insert cells into this cell complex
    virtual void insertCells(bool subdomain);
    
@@ -369,6 +368,9 @@ class CellComplex
    
    virtual std::set<Cell*, Less_Cell> getCells(int dim){ return _cells[dim]; }
    
+   // get all the finite element mesh vertices in the domain of this cell complex
+   virtual void getDomainVertices(std::set<MVertex*, Less_MVertex>& domainVertices, bool subdomain);
+   
    // iterators to the first and last cells of certain dimension
    virtual citer firstCell(int dim) {return _cells[dim].begin(); }
    virtual citer lastCell(int dim) {return _cells[dim].end(); }
@@ -377,7 +379,6 @@ class CellComplex
    virtual std::set<Cell*, Less_Cell>::iterator findCell(int dim, std::vector<int>& vertices);
    virtual std::set<Cell*, Less_Cell>::iterator findCell(int dim, int vertex, int dummy=0);
    
-   
    // kappa for two cells of this cell complex
    // implementation will vary depending on cell type
    virtual int kappa(Cell* sigma, Cell* tau) const { return sigma->kappa(tau); }
@@ -424,7 +425,7 @@ class CellComplex
    // print the vertices of cells of certain dimension
    virtual void printComplex(int dim, bool subcomplex);
    
-   // write this cell complex in legacy .msh format
+   // write this cell complex in .msh format
    virtual int writeComplexMSH(const std::string &name); 
    
    
diff --git a/Geo/ChainComplex.cpp b/Geo/ChainComplex.cpp
index 5bdcd8f517..7bb70d2e6a 100644
--- a/Geo/ChainComplex.cpp
+++ b/Geo/ChainComplex.cpp
@@ -12,6 +12,11 @@
 ChainComplex::ChainComplex(CellComplex* cellComplex){
   
   _HMatrix[0] = NULL;
+  _kerH[0] = NULL;
+  _codH[0] = NULL;
+  _JMatrix[0] = NULL;
+  _QMatrix[0] = NULL;
+  _Hbasis[0] = NULL;
   
   for(int dim = 1; dim < 4; dim++){
     unsigned int cols = cellComplex->getSize(dim);
@@ -256,15 +261,17 @@ void ChainComplex::computeHomology(){
       if(getCodHMatrix(i+1) == NULL){
         _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1,
                                      gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
-      }
+      }  
       else if(getJMatrix(i) == NULL || getQMatrix(i) == NULL){
         _Hbasis[i] = NULL;
-      }
+      } 
       else{
         _Hbasis[i] = copy_gmp_matrix(getKerHMatrix(i), 1, 1, 
                                      gmp_matrix_rows(getKerHMatrix(i)), gmp_matrix_cols(getKerHMatrix(i)));
+        
         gmp_matrix_right_mult(_Hbasis[i], getQMatrix(i));
-      }  
+      } 
+      
     } 
     
     
@@ -303,5 +310,84 @@ void ChainComplex::matrixTest(){
   return; 
 }
 
+std::vector<int> ChainComplex::getCoeffVector(int dim, int chainNumber){
+  
+  std::vector<int> coeffVector;
+  
+  if(dim < 0 || dim > 3) return coeffVector;
+  if(_Hbasis[dim] == NULL || gmp_matrix_cols(_Hbasis[dim]) < chainNumber) return coeffVector;
+  
+  int rows = gmp_matrix_rows(_Hbasis[dim]);
+  
+  int elemi;
+  long int elemli;
+  mpz_t elem;
+  mpz_init(elem);
+  
+  for(int i = 1; i <= rows; i++){
+    gmp_matrix_get_elem(elem, i, chainNumber, _Hbasis[dim]);
+    elemli = mpz_get_si(elem);
+    elemi = elemli;
+    coeffVector.push_back(elemi);
+    //printf("coeff: %d \n", coeffVector.at(i-1));
+  }
+  
+  mpz_clear(elem);
+  return coeffVector;
+  
+}
 
 #endif
+
+Chain::Chain(std::set<Cell*, Less_Cell> cells, std::vector<int> coeffs, CellComplex* cellComplex, std::string name){
+  
+  int i = 0;
+  for(std::set<Cell*, Less_Cell>::iterator cit = cells.begin(); cit != cells.end(); cit++){
+    Cell* cell = *cit;
+    if(!cell->inSubdomain() && coeffs.size() > i){
+      if(coeffs.at(i) != 0) _cells.push_back( std::make_pair(cell, coeffs.at(i)) );
+      i++;
+    }
+  }
+  
+  _name = name;
+  _cellComplex = cellComplex;
+  
+}
+
+int Chain::writeChainMSH(const std::string &name){
+
+  //_cellComplex->writeComplexMSH(name);
+  
+  FILE *fp = fopen(name.c_str(), "a");
+  if(!fp){
+    Msg::Error("Unable to open file '%s'", name.c_str());
+        printf("Unable to open file.");
+        return 0;
+  }
+
+  fprintf(fp, "\n$ElementData\n");
+  
+  fprintf(fp, "1 \n");
+  fprintf(fp, "\"%s\" \n", getName().c_str());
+  fprintf(fp, "1 \n");
+  fprintf(fp, "0.0 \n");
+  fprintf(fp, "4 \n");
+  fprintf(fp, "0 \n");
+  fprintf(fp, "1 \n");
+  fprintf(fp, "%d \n", getSize());
+  fprintf(fp, "0 \n");
+  
+  for(int i = 0; i < _cells.size(); i++){
+    
+    fprintf(fp, "%d %d \n", getCell(i)->getTag(), getCoeff(i));
+    
+  }
+  
+  fprintf(fp, "$EndElementData\n");
+  
+  fclose(fp);
+  
+  return 1;
+  
+}
diff --git a/Geo/ChainComplex.h b/Geo/ChainComplex.h
index 89b505a900..4e37193107 100644
--- a/Geo/ChainComplex.h
+++ b/Geo/ChainComplex.h
@@ -49,6 +49,8 @@ class ChainComplex{
    // bases for homology groups
    gmp_matrix* _Hbasis[4];
    
+   
+   
   public:
    
    ChainComplex(CellComplex* cellComplex);
@@ -85,6 +87,9 @@ class ChainComplex{
    // Compute bases for the homology groups of this chain complex 
    virtual void computeHomology();
    
+   virtual std::vector<int> getCoeffVector(int dim, int chainNumber);
+   virtual int getBasisSize(int dim) {  if(dim > -1 || dim < 4) return gmp_matrix_cols(_Hbasis[dim]); else return 0; } 
+   
    virtual int printMatrix(gmp_matrix* matrix){ 
      printf("%d rows and %d columns\n", gmp_matrix_rows(matrix), gmp_matrix_cols(matrix)); 
      return gmp_matrix_printf(matrix); } 
@@ -93,7 +98,40 @@ class ChainComplex{
    virtual void matrixTest();
 };
 
-
 #endif
 
+// A class representing a chain.
+// Used to visualize generators of the homology groups.
+class Chain{
+   
+  private:
+   // cells and their coefficients in this chain
+   std::vector< std::pair <Cell*, int> > _cells;
+   // name of the chain (optional)
+   std::string _name;
+   // cell complex this chain belongs to
+   CellComplex* _cellComplex;
+   
+  public:
+   Chain(){}
+   Chain(std::set<Cell*, Less_Cell> cells, std::vector<int> coeffs, CellComplex* cellComplex, std::string name="Chain");
+   ~Chain(){}
+   
+   // get i:th cell of this chain
+   virtual Cell* getCell(int i) { return _cells.at(i).first; }
+   // get coeffcient of i:th cell of this chain
+   virtual int getCoeff(int i) { return _cells.at(i).second; }
+   
+   // number of cells in this chain 
+   virtual int getSize() { return _cells.size(); }
+   
+   // get/set chain name
+   virtual std::string getName() { return _name; }
+   virtual void setName(std::string name) { _name=name; }
+
+   // append this chain to a 2.0 .msh file as $ElementData
+   virtual int writeChainMSH(const std::string &name);
+   
+};
+
 #endif
diff --git a/utils/misc/celldriver.cpp b/utils/misc/celldriver.cpp
index 278eff7a47..a515b2c819 100755
--- a/utils/misc/celldriver.cpp
+++ b/utils/misc/celldriver.cpp
@@ -5,15 +5,26 @@
 //
 // Then compile this driver with "g++ celldriver.cpp -lGmsh -llapack -lblas -lgmp"
 //
-// This program creates .msh files reduced_complex.msh and coreduced_complex.msh
-// of an two port transformer model to represent its relative homology groups.
+//
+// This program creates .msh file chains.msh which represents the generators of 
+// an two port transformer model's homology groups.
 
 
 #include <stdio.h>
+#include <sstream>
 #include <gmsh/Gmsh.h>
 #include <gmsh/GModel.h>
 #include <gmsh/MElement.h>
 #include <gmsh/CellComplex.h>
+#include <gmsh/ChainComplex.h>
+
+template <class TTypeA, class TTypeB>
+bool convert(const TTypeA& input, TTypeB& output ){
+  std::stringstream stream;
+  stream << input;
+  stream >> output;
+  return stream.good();
+}
 
 
 int main(int argc, char **argv)
@@ -46,16 +57,45 @@ int main(int argc, char **argv)
   s= *(++sub);
   subdomain.push_back(s);
   
-  CellComplex complex = CellComplex(domain, subdomain);
+  // create a cell complex
+  CellComplex* complex = new CellComplex(domain, subdomain);
 
   printf("Cell complex of this model has: %d volumes, %d faces, %d edges and %d vertices\n",
-         complex.getSize(3), complex.getSize(2), complex.getSize(1), complex.getSize(0));  
+         complex->getSize(3), complex->getSize(2), complex->getSize(1), complex->getSize(0));  
+  
+  // reduce the complex in order to ease homology computation
+  complex->reduceComplex();
+  
+  // create a chain complex of a cell complex (construct boundary operator matrices)
+  ChainComplex* chains = new ChainComplex(complex);
+  // compute the homology using the boundary operator matrices
+  chains->computeHomology();
+  
+  // write the reduced cell complex to a .msh file
+  complex->writeComplexMSH("chains.msh");
+  
+  // Append the homology generators to the .msh file as post-processing views. 
+  // To visualize the result, open chains.msh with Gmsh GUI and deselect all mesh
+  // entities from Tools->Options->Mesh->Visibility.
+  for(int j = 0; j < 4; j++){
+    for(int i = 1; i <= chains->getBasisSize(j); i++){
+    
+      std::string generator;
+      std::string dimension;
+      convert(i, generator);
+      convert(j, dimension);
+      std::string name = dimension + "D Generator " + generator;
+      
+      Chain* chain = new Chain(complex->getCells(j), chains->getCoeffVector(j,i), complex, name);
+      chain->writeChainMSH("chains.msh");
+      delete chain;
+      
+    }
+  }
   
-  complex.reduceComplex();
-  complex.writeComplexMSH("reduced_complex.msh");
-  complex.coreduceComplex();
-  complex.writeComplexMSH("coreduced_complex.msh");
     
+  delete chains;
+  delete complex;  
   delete m;
   GmshFinalize();
   
-- 
GitLab