From 4e37dafc555df6658bc12877e8163b5dc7ab0fd5 Mon Sep 17 00:00:00 2001
From: Matti Pellika <matti.pellikka@tut.fi>
Date: Tue, 20 Mar 2012 17:14:38 +0000
Subject: [PATCH] Cleaner homology solver API

---
 CMakeLists.txt                   |   2 +-
 Geo/Cell.cpp                     |   5 +-
 Geo/CellComplex.cpp              |  54 +++++---
 Geo/CellComplex.h                |  15 ++-
 Geo/GModel.cpp                   |  37 +++---
 Geo/Homology.cpp                 | 207 +++++++++++++++++++++----------
 Geo/Homology.h                   |  47 +++++--
 Plugin/HomologyComputation.cpp   |  16 +--
 demos/homology.geo               |  19 ---
 utils/api_demos/mainHomology.cpp |  13 +-
 10 files changed, 256 insertions(+), 159 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 63c7263b35..1f4de45753 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,7 +93,7 @@ set(GMSH_API
     Geo/discreteFace.h Geo/discreteRegion.h Geo/SPoint2.h Geo/SPoint3.h
     Geo/SVector3.h Geo/STensor3.h Geo/SBoundingBox3d.h Geo/Pair.h Geo/Range.h 
     Geo/SOrientedBoundingBox.h Geo/CellComplex.h Geo/ChainComplex.h Geo/Cell.h
-    Geo/Homology.h Geo/partitionEdge.h Geo/CGNSOptions.h Geo/gmshLevelset.h
+    Geo/Homology.h Geo/Chain.h Geo/partitionEdge.h Geo/CGNSOptions.h Geo/gmshLevelset.h
   Mesh/meshGEdge.h Mesh/meshGFace.h Mesh/meshGFaceOptimize.h Mesh/meshPartition.h
     Mesh/meshGFaceDelaunayInsertion.h Mesh/simple3D.h Mesh/meshPartitionOptions.h 
     Mesh/Voronoi3D.h Mesh/Levy3D.h Mesh/periodical.h
diff --git a/Geo/Cell.cpp b/Geo/Cell.cpp
index cec524dac0..165425e4a3 100644
--- a/Geo/Cell.cpp
+++ b/Geo/Cell.cpp
@@ -351,8 +351,6 @@ void Cell::restoreCell(){
     if(it->second.get() == 0) toRemove.push_back(it->first);
   }
   for(unsigned int i = 0; i < toRemove.size(); i++) _bd.erase(toRemove[i]);
-  _combined = false;
-  _immune = false;
 }
 
 void Cell::addBoundaryCell(int orientation, Cell* cell, bool other)
@@ -475,7 +473,7 @@ CombinedCell::CombinedCell(Cell* c1, Cell* c2, bool orMatch, bool co)
   _num = ++_globalNum;
   _domain = c1->getDomain();
   _combined = true;
-  _immune = false;
+  _immune = (c1->getImmune() || c2->getImmune());
 
   // cells
   c1->getCells(_cells);
@@ -538,6 +536,7 @@ CombinedCell::CombinedCell(std::vector<Cell*>& cells)
   // cells
   for(unsigned int i = 0; i < cells.size(); i++){
     Cell* c = cells.at(i);
+    if(c->getImmune()) _immune = true;
     _cells[c] = 1;
   }
 }
diff --git a/Geo/CellComplex.cpp b/Geo/CellComplex.cpp
index 4df6ce7a65..cf4021e3fa 100644
--- a/Geo/CellComplex.cpp
+++ b/Geo/CellComplex.cpp
@@ -11,12 +11,14 @@
 CellComplex::CellComplex(GModel* model,
 			 std::vector<MElement*>& domainElements,
 			 std::vector<MElement*>& subdomainElements,
+                         std::vector<MElement*>& immuneElements,
                          bool saveOriginalComplex) :
   _model(model), _dim(0), _simplicial(true), _saveorig(saveOriginalComplex)
 {
   _deleteCount = 0;
   _insertCells(subdomainElements, 1);
   _insertCells(domainElements, 0);
+  _immunizeCells(immuneElements);
 
   int num = 0;
   for(int dim = 0; dim < 4; dim++){
@@ -29,6 +31,7 @@ CellComplex::CellComplex(GModel* model,
       cell->saveOriginalBd();
     }
   }
+  _reduced = false;
 }
 
 bool CellComplex::_insertCells(std::vector<MElement*>& elements,
@@ -70,6 +73,18 @@ bool CellComplex::_insertCells(std::vector<MElement*>& elements,
   return true;
 }
 
+bool CellComplex::_immunizeCells(std::vector<MElement*>& elements)
+{
+  for(unsigned int i=0; i < elements.size(); i++){
+    MElement* element = elements.at(i);
+    Cell* cell = new Cell(element, 0);
+    int dim = cell->getDim();
+    citer cit = _cells[dim].find(cell);
+    if(cit != lastCell(dim)) (*cit)->setImmune(true);
+    delete cell;
+  }
+}
+
 CellComplex::~CellComplex()
 {
   for(int i = 0; i < 4; i++){
@@ -169,8 +184,8 @@ int CellComplex::coreduction(Cell* startCell, bool omit,
     s = Q.front();
     Q.pop();
     Qset.erase(s);
-    if(s->getBoundarySize() == 1
-       && inSameDomain(s, s->firstBoundary()->first) ){
+    if(s->getBoundarySize() == 1 &&
+       inSameDomain(s, s->firstBoundary()->first)){
       s->getBoundary(bd_s);
       removeCell(s);
       bd_s.begin()->first->getCoboundary(cbd_c);
@@ -186,6 +201,7 @@ int CellComplex::coreduction(Cell* startCell, bool omit,
       enqueueCells(cbd_c, Q, Qset);
     }
   }
+  _reduced = true;
   return coreductions;
 }
 
@@ -204,7 +220,9 @@ int CellComplex::reduction(int dim, bool omit,
     while(cit != lastCell(dim-1)){
       Cell* cell = *cit;
       if(cell->getCoboundarySize() == 1 &&
-         inSameDomain(cell, cell->firstCoboundary()->first)){
+         inSameDomain(cell, cell->firstCoboundary()->first) &&
+         !cell->getImmune() &&
+         !cell->firstCoboundary()->first->getImmune()){
 	cit++;
 	if(dim == getDim() && omit){
 	  omittedCells.push_back(cell->firstCoboundary()->first);
@@ -219,6 +237,7 @@ int CellComplex::reduction(int dim, bool omit,
       cit++;
     }
   }
+  _reduced = true;
   return count;
 }
 
@@ -236,8 +255,8 @@ int CellComplex::coreduction(int dim, bool omit,
     citer cit = firstCell(dim);
     while(cit != lastCell(dim)){
       Cell* cell = *cit;
-      if( cell->getBoundarySize() == 1
-          && inSameDomain(cell, cell->firstBoundary()->first)){
+      if(cell->getBoundarySize() == 1 &&
+         inSameDomain(cell, cell->firstBoundary()->first)) {
         ++cit;
 	if(dim-1 == 0 && omit){
 	  omittedCells.push_back(cell->firstBoundary()->first);
@@ -252,6 +271,7 @@ int CellComplex::coreduction(int dim, bool omit,
       cit++;
     }
   }
+  _reduced = true;
   return count;
 }
 
@@ -289,7 +309,7 @@ int CellComplex::reduceComplex(bool docombine, bool omit)
   }
 
   Msg::Debug(" %d volumes, %d faces, %d edges and %d vertices",
-            getSize(3), getSize(2), getSize(1), getSize(0));
+             getSize(3), getSize(2), getSize(1), getSize(0));
 
   if(docombine) combine(3);
   reduction(2, false, empty);
@@ -298,8 +318,8 @@ int CellComplex::reduceComplex(bool docombine, bool omit)
   if(docombine) combine(1);
 
   Msg::Debug(" %d volumes, %d faces, %d edges and %d vertices",
-            getSize(3), getSize(2), getSize(1), getSize(0));
-
+             getSize(3), getSize(2), getSize(1), getSize(0));
+  _reduced = true;
   return 0;
 }
 
@@ -313,6 +333,7 @@ void CellComplex::removeSubdomain()
     }
   }
   for(unsigned int i = 0; i < toRemove.size(); i++) removeCell(toRemove[i]);
+  _reduced = true;
 }
 
 int CellComplex::coreduceComplex(bool docombine, bool omit)
@@ -354,7 +375,7 @@ int CellComplex::coreduceComplex(bool docombine, bool omit)
   }
 
   Msg::Debug(" %d volumes, %d faces, %d edges and %d vertices",
-            getSize(3), getSize(2), getSize(1), getSize(0));
+             getSize(3), getSize(2), getSize(1), getSize(0));
 
   if(docombine) cocombine(0);
   coreduction(1, false, empty);
@@ -364,8 +385,8 @@ int CellComplex::coreduceComplex(bool docombine, bool omit)
   coreduction(3, false, empty);
   coherent();
   Msg::Debug(" %d volumes, %d faces, %d edges and %d vertices",
-            getSize(3), getSize(2), getSize(1), getSize(0));
-
+             getSize(3), getSize(2), getSize(1), getSize(0));
+  _reduced = true;
   return 0;
 }
 
@@ -389,7 +410,7 @@ int CellComplex::combine(int dim)
       Cell* s = Q.front();
       Q.pop();
 
-      if(s->getCoboundarySize() == 2){
+      if(s->getCoboundarySize() == 2 && !s->getImmune()){
 	Cell::biter it = s->firstCoboundary();
         int or1 = it->second.get();
         Cell* c1 = it->first;
@@ -399,7 +420,7 @@ int CellComplex::combine(int dim)
         Cell* c2 = it->first;
 
         if(!(*c1 == *c2) && abs(or1) == abs(or2)
-           && inSameDomain(s, c1) && inSameDomain(s, c2)){
+           && inSameDomain(s, c1) && inSameDomain(s, c2)) {
           removeCell(s);
 
           c1->getBoundary(bd_c);
@@ -423,7 +444,7 @@ int CellComplex::combine(int dim)
   Msg::Debug("Cell complex after combining:");
   Msg::Debug(" %d volumes, %d faces, %d edges and %d vertices",
              getSize(3), getSize(2), getSize(1), getSize(0));
-
+  _reduced = true;
   return count;
 }
 
@@ -485,7 +506,7 @@ int CellComplex::cocombine(int dim)
   Msg::Debug("Cell complex after cocombining:");
   Msg::Debug(" %d volumes, %d faces, %d edges and %d vertices",
              getSize(3), getSize(2), getSize(1), getSize(0));
-
+  _reduced = true;
   return count;
 }
 
@@ -577,8 +598,9 @@ bool CellComplex::restoreComplex()
     }
     _newcells.clear();
     Msg::Info("Restored Cell Complex:");
-    Msg::Info(" %d volumes, %d faces, %d edges and %d vertices",
+    Msg::Info("%d volumes, %d faces, %d edges and %d vertices",
                getSize(3), getSize(2), getSize(1), getSize(0));
+    _reduced = false;
     return true;
   }
   else {
diff --git a/Geo/CellComplex.h b/Geo/CellComplex.h
index 71b362050a..3f149d4195 100644
--- a/Geo/CellComplex.h
+++ b/Geo/CellComplex.h
@@ -44,8 +44,12 @@ class CellComplex
 
   int _deleteCount;
 
+  bool _reduced;
+
   // for constructor
-  bool _insertCells(std::vector<MElement*>& elements,  int domain);
+  bool _insertCells(std::vector<MElement*>& elements, int domain);
+
+  bool _immunizeCells(std::vector<MElement*>& elements);
 
   // enqueue cells in queue if they are not there already
   void enqueueCells(std::map<Cell*, short int, Less_Cell>& cells,
@@ -63,6 +67,7 @@ class CellComplex
   CellComplex(GModel* model,
 	      std::vector<MElement*>& domainElements,
 	      std::vector<MElement*>& subdomainElements,
+              std::vector<MElement*>& immuneElements,
               bool saveOriginalComplex=true);
   ~CellComplex();
 
@@ -122,9 +127,11 @@ class CellComplex
   int reduceComplex(bool docombine=true, bool omit=true);
   int coreduceComplex(bool docombine=true, bool omit=true);
 
-  int eulerCharacteristic(){
-    return getSize(0) - getSize(1) + getSize(2) - getSize(3);}
-  void printEuler(){
+  bool isReduced() const { return _reduced; }
+
+  int eulerCharacteristic() {
+    return getSize(0) - getSize(1) + getSize(2) - getSize(3); }
+  void printEuler() {
     printf("Euler characteristic: %d. \n", eulerCharacteristic()); }
 
   // restore the cell complex to its original state before (co)reduction
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index 07df48e583..9d96bc5800 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -2849,27 +2849,26 @@ void GModel::computeHomology()
     Homology* homology = new Homology(this, itp.first->first.first,
                                       itp.first->first.second,
                                       prepareToRestore);
-    CellComplex *cellcomplex = homology->createCellComplex();
-    if(cellcomplex->getSize(0)){
-      for(std::multimap<dpair, std::string>::iterator itt = itp.first;
-          itt != itp.second; itt++){
-        // restore cell complex to non-reduced state if we are reusing it
-        if(itt != itp.first) cellcomplex->restoreComplex();
-        std::string type = itt->second;
-        if(type == "Homology")
-          homology->findHomologyBasis(cellcomplex);
-        else if(type == "Cohomology")
-          homology->findCohomologyBasis(cellcomplex);
-        else
-          Msg::Error("Unknown type of homology computation: %s", type.c_str());
+
+    // do not save 0-, and n-chains, where n is the dimension of the model
+    // (usually not needed for anything, available through the plugin)
+    for(std::multimap<dpair, std::string>::iterator itt = itp.first;
+        itt != itp.second; itt++){
+      std::string type = itt->second;
+      if(type == "Homology") {
+        homology->findHomologyBasis();
+        if(this->getDim() != 1) homology->addChainsToModel(1);
+        if(this->getDim() != 2) homology->addChainsToModel(2);
       }
-      // do not save 0-, and n-chains, where n is the dimension of the model
-      // (usually not needed for anything, available through the plugin)
-      if(this->getDim() != 1) homology->addChainsToModel(1);
-      if(this->getDim() != 2) homology->addChainsToModel(2);
-      _pruneMeshVertexAssociations();
+      else if(type == "Cohomology") {
+        homology->findCohomologyBasis();
+        if(this->getDim() != 1) homology->addCochainsToModel(1);
+        if(this->getDim() != 2) homology->addCochainsToModel(2);
+      }
+      else
+        Msg::Error("Unknown type of homology computation: %s", type.c_str());
     }
-    delete cellcomplex;
+    _pruneMeshVertexAssociations();
     delete homology;
   }
 #else
diff --git a/Geo/Homology.cpp b/Geo/Homology.cpp
index 8b37fc422c..9f9b8ccfd8 100644
--- a/Geo/Homology.cpp
+++ b/Geo/Homology.cpp
@@ -20,7 +20,8 @@ Homology::Homology(GModel* model, std::vector<int> physicalDomain,
 		   bool combine, bool omit, bool smoothen) :
   _model(model), _domain(physicalDomain), _subdomain(physicalSubdomain),
   _saveOrig(saveOrig),
-  _combine(combine), _omit(omit), _smoothen(smoothen)
+  _combine(combine), _omit(omit), _smoothen(smoothen), _cellComplex(NULL),
+  _homologyComputed(false), _cohomologyComputed(false)
 {
   _fileName = "";
 
@@ -61,89 +62,121 @@ Homology::Homology(GModel* model, std::vector<int> physicalDomain,
       }
     }
   }
+  for(unsigned int i = 0; i < _imdomain.size(); i++){
+    for(int j = 0; j < 4; j++){
+      it = groups[j].find(_imdomain.at(i));
+      if(it != groups[j].end()){
+	std::vector<GEntity*> physicalGroup = (*it).second;
+	for(unsigned int k = 0; k < physicalGroup.size(); k++){
+	  _immuneEntities.push_back(physicalGroup.at(k));
+	}
+      }
+    }
+  }
 }
 
-CellComplex* Homology::createCellComplex(std::vector<GEntity*>& domainEntities,
-                                         std::vector<GEntity*>& subdomainEntities)
+void Homology::_createCellComplex()
 {
   Msg::StatusBar(2, true, "Creating cell complex...");
   double t1 = Cpu();
 
-  if(domainEntities.empty()) Msg::Error("Domain is empty");
-  if(subdomainEntities.empty()) Msg::Info("Subdomain is empty");
+  if(_domainEntities.empty()) Msg::Error("Domain is empty");
+  if(_subdomainEntities.empty()) Msg::Info("Subdomain is empty");
 
   std::vector<MElement*> domainElements;
   std::vector<MElement*> subdomainElements;
-  for(unsigned int j=0; j < domainEntities.size(); j++) {
-    for(unsigned int i=0; i < domainEntities.at(j)->getNumMeshElements(); i++){
-      MElement* element = domainEntities.at(j)->getMeshElement(i);
+  std::vector<MElement*> immuneElements;
+  for(unsigned int j=0; j < _domainEntities.size(); j++) {
+    for(unsigned int i=0; i < _domainEntities.at(j)->getNumMeshElements(); i++){
+      MElement* element = _domainEntities.at(j)->getMeshElement(i);
       domainElements.push_back(element);
     }
   }
-  for(unsigned int j=0; j < subdomainEntities.size(); j++) {
-    for(unsigned int i=0; i < subdomainEntities.at(j)->getNumMeshElements();
+  for(unsigned int j=0; j < _subdomainEntities.size(); j++) {
+    for(unsigned int i=0; i < _subdomainEntities.at(j)->getNumMeshElements();
 	i++){
-      MElement* element = subdomainEntities.at(j)->getMeshElement(i);
+      MElement* element = _subdomainEntities.at(j)->getMeshElement(i);
       subdomainElements.push_back(element);
     }
   }
+  for(unsigned int j=0; j < _immuneEntities.size(); j++) {
+    for(unsigned int i=0; i < _immuneEntities.at(j)->getNumMeshElements();
+	i++){
+      MElement* element = _immuneEntities.at(j)->getMeshElement(i);
+      immuneElements.push_back(element);
+    }
+  }
 
-  CellComplex* cellComplex =  new CellComplex(_model,
-					      domainElements,
-					      subdomainElements,
-                                              _saveOrig);
+  if(_cellComplex != NULL) delete _cellComplex;
+  _cellComplex =  new CellComplex(_model,
+                                  domainElements,
+                                  subdomainElements,
+                                  immuneElements,
+                                  _saveOrig);
 
-  if(cellComplex->getSize(0) == 0){
+  if(_cellComplex->getSize(0) == 0) {
     Msg::Error("Cell Complex is empty: check the domain and the mesh");
   }
   double t2 = Cpu();
   Msg::StatusBar(2, true, "Done creating cell complex (%g s)", t2 - t1);
   Msg::Info("%d volumes, %d faces, %d edges and %d vertices",
-            cellComplex->getSize(3), cellComplex->getSize(2),
-	    cellComplex->getSize(1), cellComplex->getSize(0));
-  return cellComplex;
+            _cellComplex->getSize(3), _cellComplex->getSize(2),
+	    _cellComplex->getSize(1), _cellComplex->getSize(0));
 }
 
-CellComplex* Homology::createCellComplex()
+void Homology::_deleteChains()
 {
-  return createCellComplex(_domainEntities, _subdomainEntities);
+  for(int j = 0; j < 4; j ++) {
+    for(unsigned int i = 0; i < _chains[j].size(); i++) {
+      delete _chains[j].at(i);
+      _chains[j].clear();
+    }
+  }
+  _homologyComputed = false;
 }
 
-Homology::~Homology()
+void Homology::_deleteCochains()
 {
-  for(unsigned int i = 0; i < _chains.size(); i++)
-    delete _chains.at(i);
+  for(int j = 0; j < 4; j ++) {
+    for(unsigned int i = 0; i < _cochains[j].size(); i++) {
+      delete _cochains[j].at(i);
+      _cochains[j].clear();
+    }
+  }
+  _cohomologyComputed = false;
 }
 
-void Homology::findHomologyBasis(CellComplex* cellComplex)
+Homology::~Homology()
 {
-  bool ownComplex = false;
-  if(cellComplex==NULL){
-    cellComplex = createCellComplex(_domainEntities,
-				    _subdomainEntities);
-    ownComplex = true;
-  }
+  if(_cellComplex != NULL) delete _cellComplex;
+  _deleteChains();
+  _deleteCochains();
+}
 
+void Homology::findHomologyBasis()
+{
+  if(_cellComplex == NULL) _createCellComplex();
+  if(_cellComplex->isReduced()) _cellComplex->restoreComplex();
   Msg::StatusBar(2, true, "Reducing cell complex...");
 
   double t1 = Cpu();
-  int omitted = cellComplex->reduceComplex(_combine, _omit);
+  int omitted = _cellComplex->reduceComplex(_combine, _omit);
 
   double t2 = Cpu();
   Msg::StatusBar(2, true, "Done reducing cell complex (%g s)", t2 - t1);
   Msg::Info("%d volumes, %d faces, %d edges and %d vertices",
-            cellComplex->getSize(3), cellComplex->getSize(2),
-	    cellComplex->getSize(1), cellComplex->getSize(0));
+            _cellComplex->getSize(3), _cellComplex->getSize(2),
+	    _cellComplex->getSize(1), _cellComplex->getSize(0));
 
   Msg::StatusBar(2, true, "Computing homology space basis ...");
   t1 = Cpu();
-  ChainComplex chainComplex = ChainComplex(cellComplex);
+  ChainComplex chainComplex = ChainComplex(_cellComplex);
   chainComplex.computeHomology();
   t2 = Cpu();
   Msg::StatusBar(2, true, "Done computing homology space basis (%g s)", t2 - t1);
 
-  int dim = cellComplex->getDim();
   std::string domain = _getDomainString(_domain, _subdomain);
+  _deleteChains();
   int HRank[4];
   for(int j = 0; j < 4; j++){
     HRank[j] = 0;
@@ -159,7 +192,7 @@ void Homology::findHomologyBasis(CellComplex* cellComplex)
       chainComplex.getBasisChain(chain, i, j, 3, _smoothen);
       int torsion = chainComplex.getTorsion(j,i);
       if(!chain.empty()) {
-        _createChain(chain, name);
+        _createChain(chain, name, false);
         HRank[j] = HRank[j] + 1;
         if(torsion != 1){
           Msg::Warning("H%d %d has torsion coefficient %d!",
@@ -171,8 +204,6 @@ void Homology::findHomologyBasis(CellComplex* cellComplex)
 
   if(_fileName != "") writeBasisMSH();
 
-  if(ownComplex) delete cellComplex;
-
   Msg::Info("Ranks of domain %shomology spaces:", domain.c_str());
   Msg::Info("H0 = %d", HRank[0]);
   Msg::Info("H1 = %d", HRank[1]);
@@ -182,38 +213,35 @@ void Homology::findHomologyBasis(CellComplex* cellComplex)
 
   Msg::StatusBar(2, false, "H0: %d, H1: %d, H2: %d, H3: %d",
 		 HRank[0], HRank[1], HRank[2], HRank[3]);
+  _homologyComputed = true;
 }
 
-void Homology::findCohomologyBasis(CellComplex* cellComplex)
+void Homology::findCohomologyBasis()
 {
-  bool ownComplex = false;
-  if(cellComplex==NULL){
-    cellComplex = createCellComplex(_domainEntities,
-				    _subdomainEntities);
-    ownComplex = true;
-  }
+  if(_cellComplex == NULL) _createCellComplex();
+  if(_cellComplex->isReduced()) _cellComplex->restoreComplex();
 
   Msg::StatusBar(2, true, "Reducing cell complex...");
 
   double t1 = Cpu();
-  int omitted = cellComplex->coreduceComplex(_combine, _omit);
+  int omitted = _cellComplex->coreduceComplex(_combine, _omit);
   double t2 = Cpu();
 
   Msg::StatusBar(2, true, "Done reducing cell complex (%g s)", t2 - t1);
   Msg::Info("%d volumes, %d faces, %d edges and %d vertices",
-            cellComplex->getSize(3), cellComplex->getSize(2),
-	    cellComplex->getSize(1), cellComplex->getSize(0));
+            _cellComplex->getSize(3), _cellComplex->getSize(2),
+	    _cellComplex->getSize(1), _cellComplex->getSize(0));
 
   Msg::StatusBar(2, true, "Computing cohomology space basis ...");
   t1 = Cpu();
-  ChainComplex chainComplex = ChainComplex(cellComplex);
+  ChainComplex chainComplex = ChainComplex(_cellComplex);
   chainComplex.transposeHMatrices();
   chainComplex.computeHomology(true);
   t2 = Cpu();
   Msg::StatusBar(2, true, "Done computing cohomology space basis (%g s)", t2- t1);
 
-  int dim = cellComplex->getDim();
   std::string domain = _getDomainString(_domain, _subdomain);
+  _deleteCochains();
   int HRank[4];
   for(int i = 0; i < 4; i++) HRank[i] = 0;
   for(int j = 3; j > -1; j--){
@@ -231,7 +259,7 @@ void Homology::findCohomologyBasis(CellComplex* cellComplex)
       chainComplex.getBasisChain(chain, i, j, 3, _smoothen);
       int torsion = chainComplex.getTorsion(j,i);
       if(!chain.empty()) {
-        _createChain(chain, name);
+        _createChain(chain, name, true);
         HRank[j] = HRank[j] + 1;
         if(torsion != 1){
           Msg::Warning("H%d* %d has torsion coefficient %d!", j, i, torsion);
@@ -242,8 +270,6 @@ void Homology::findCohomologyBasis(CellComplex* cellComplex)
 
   if(_fileName != "") writeBasisMSH();
 
-  if(ownComplex) delete cellComplex;
-
   Msg::Info("Ranks of domain %scohomology spaces:", domain.c_str());
   Msg::Info("H0* = %d", HRank[0]);
   Msg::Info("H1* = %d", HRank[1]);
@@ -253,19 +279,71 @@ void Homology::findCohomologyBasis(CellComplex* cellComplex)
 
   Msg::StatusBar(2, false, "H0*: %d, H1*: %d, H2*: %d, H3*: %d",
 		 HRank[0], HRank[1], HRank[2], HRank[3]);
+  _cohomologyComputed = true;
 }
 
 void Homology::addChainsToModel(int dim, bool post)
 {
-  for(unsigned int i = 0; i < _chains.size(); i++) {
-    if(dim != -1 && _chains.at(i)->getDim() == dim)
-      _chains.at(i)->addToModel(this->getModel(), post);
-    else if(dim == -1) _chains.at(i)->addToModel(this->getModel(), post);
-  }
+  if(!_homologyComputed) Msg::Warning("Homology is not computed");
+  if(dim == -1)
+    for(int j = 0; j < 4; j++)
+      for(unsigned int i = 0; i < _chains[j].size(); i++)
+        _chains[j].at(i)->addToModel(this->getModel(), post);
+  else if (dim > -1 && dim < 4)
+    for(unsigned int i = 0; i < _chains[dim].size(); i++)
+      _chains[dim].at(i)->addToModel(this->getModel(), post);
+}
+
+void Homology::addCochainsToModel(int dim, bool post)
+{
+  if(!_cohomologyComputed) Msg::Warning("Cohomology is not computed");
+  if(dim == -1)
+    for(int j = 0; j < 4; j++)
+      for(unsigned int i = 0; i < _cochains[j].size(); i++)
+        _cochains[j].at(i)->addToModel(this->getModel(), post);
+  else if (dim > -1 && dim < 4)
+    for(unsigned int i = 0; i < _cochains[dim].size(); i++)
+      _cochains[dim].at(i)->addToModel(this->getModel(), post);
+}
+
+void Homology::getHomologyBasis(int dim, std::vector<Chain<int> >& hom)
+{
+  if(dim < 0 || dim > 3) return;
+  if(!_homologyComputed) findHomologyBasis();
+
+  hom.resize(_chains[dim].size());
+  for(unsigned int i = 0; i < _chains[dim].size(); i++)
+    hom[i] = *_chains[dim].at(i);
+}
+
+void Homology::getCohomologyBasis(int dim, std::vector<Chain<int> >& coh)
+{
+  if(dim < 0 || dim > 3) return;
+  if(!_cohomologyComputed) findCohomologyBasis();
+
+  coh.resize(_cochains[dim].size());
+  for(unsigned int i = 0; i < _cochains[dim].size(); i++)
+    coh[i] = *_cochains[dim].at(i);
+}
+
+int Homology::betti(int dim)
+{
+  if(dim < 0 || dim > 3) return 0;
+  if(_homologyComputed) return _chains[dim].size();
+  if(_cohomologyComputed) return _cochains[dim].size();
+
+  findHomologyBasis();
+  return _chains[dim].size();
+}
+
+int Homology::eulerCharacteristic()
+{
+  if(_cellComplex == NULL) _createCellComplex();
+  return _cellComplex->eulerCharacteristic();
 }
 
 void Homology::_createChain(std::map<Cell*, int, Less_Cell>& preChain,
-                            std::string name)
+                            std::string name, bool co)
 {
   Chain<int>* chain = new Chain<int>();
   chain->setName(name);
@@ -279,7 +357,8 @@ void Homology::_createChain(std::map<Cell*, int, Less_Cell>& preChain,
     cell->getMeshVertices(v);
     chain->addElemChain(ElemChain(cell->getDim(), v), coeff);
   }
-  _chains.push_back(chain);
+  if(co) _cochains[chain->getDim()].push_back(chain);
+  else _chains[chain->getDim()].push_back(chain);
 }
 
 std::string Homology::_getDomainString(const std::vector<int>& domain,
@@ -331,16 +410,14 @@ void Homology::storeCells(CellComplex* cellComplex, int dim)
   for(CellComplex::citer cit = cellComplex->firstCell(dim);
       cit != cellComplex->lastCell(dim); cit++){
     Cell* cell = *cit;
-
     std::map<Cell*, int, Less_Cell > cells;
     cell->getCells(cells);
     for(Cell::citer it = cells.begin(); it != cells.end(); it++){
       Cell* subCell = it->first;
-
       std::vector<MVertex*> v;
-      cell->getMeshVertices(v);
+      subCell->getMeshVertices(v);
 
-      MElement* e = factory.create(cell->getTypeMSH(), v);
+      MElement* e = factory.create(subCell->getTypeMSH(), v);
       elements.push_back(e);
     }
   }
diff --git a/Geo/Homology.h b/Geo/Homology.h
index 6f396fe20f..16bf789646 100644
--- a/Geo/Homology.h
+++ b/Geo/Homology.h
@@ -30,9 +30,14 @@ class Homology
   // physical group IDs
   std::vector<int> _domain;
   std::vector<int> _subdomain;
+  std::vector<int> _imdomain;
   // corresponding geometrical entities
   std::vector<GEntity*> _domainEntities;
   std::vector<GEntity*> _subdomainEntities;
+  std::vector<GEntity*> _immuneEntities;
+
+  // save original cell complex
+  bool _saveOrig;
 
   // use cell combining
   bool _combine;
@@ -41,14 +46,18 @@ class Homology
   // use chain smoothning
   bool _smoothen;
 
-  // save original cell complex
-  bool _saveOrig;
-
   // file name to store the results
   std::string _fileName;
 
+  // cell complex of the domain
+  CellComplex* _cellComplex;
+
+  bool _homologyComputed;
+  bool _cohomologyComputed;
+
   // resulting chains
-  std::vector<Chain<int>*> _chains;
+  std::vector<Chain<int>*> _chains[4];
+  std::vector<Chain<int>*> _cochains[4];
 
   typedef std::map<Cell*, int, Less_Cell>::iterator citer;
 
@@ -56,35 +65,47 @@ class Homology
   std::string _getDomainString(const std::vector<int>& domain,
                                const std::vector<int>& subdomain);
 
+  // construct the cell complex
+  void _createCellComplex();
+
   // create and store a chain from homology solver result
   void _createChain(std::map<Cell*, int, Less_Cell>& preChain,
-                    std::string name);
+                    std::string name, bool co);
 
+  void _deleteChains();
+  void _deleteCochains();
 
  public:
 
+  // Determine domain and relative subdomain of (co)homology computation
   Homology(GModel* model, std::vector<int> physicalDomain,
 	   std::vector<int> physicalSubdomain,
            bool saveOrig=true,
 	   bool combine=true, bool omit=true, bool smoothen=true);
   ~Homology();
 
-  // create a cell complex from a mesh in geometrical entities of Gmsh
-  CellComplex* createCellComplex(std::vector<GEntity*>& domainEntities,
-				 std::vector<GEntity*>& subdomainEntities);
-  CellComplex* createCellComplex();
-
   GModel* getModel() const { return _model; }
   void setFileName(std::string fileName) { _fileName = fileName; }
 
-  // find the generators/dual generarators  of homology spaces,
-  void findHomologyBasis(CellComplex* cellComplex=NULL);
-  void findCohomologyBasis(CellComplex* cellComplex=NULL);
+  // find the bases of (co)homology spaces
+  void findHomologyBasis();
+  void findCohomologyBasis();
 
   // add chains to Gmsh model
   // dim: only add dim-chains if dim != -1
   // post: create a post-processing view
   void addChainsToModel(int dim=-1, bool post=true);
+  void addCochainsToModel(int dim=-1, bool post=true);
+
+  // get representative chains of a (co)homology space basis
+  void getHomologyBasis(int dim, std::vector<Chain<int> >& hom);
+  void getCohomologyBasis(int dim, std::vector<Chain<int> >& coh);
+
+  // get Betti number
+  int betti(int dim);
+
+  // get Euler characteristic
+  int eulerCharacteristic();
 
   // write the generators to a file
   bool writeBasisMSH(bool binary=false);
diff --git a/Plugin/HomologyComputation.cpp b/Plugin/HomologyComputation.cpp
index 5c38a6e77f..ca7ecbe2e2 100644
--- a/Plugin/HomologyComputation.cpp
+++ b/Plugin/HomologyComputation.cpp
@@ -110,20 +110,16 @@ PView *GMSH_HomologyComputationPlugin::execute(PView *v)
 
   Homology* homology = new Homology(m, domain, subdomain);
   homology->setFileName(fileName);
-  CellComplex* cc = homology->createCellComplex();
-  if(hom != 0){
-    homology->findHomologyBasis(cc);
-  }
-  if(coh != 0){
-    cc->restoreComplex();
-    homology->findCohomologyBasis(cc);
-  }
+
+  if(hom != 0) homology->findHomologyBasis();
+  if(coh != 0) homology->findCohomologyBasis();
+
   for(unsigned int i = 0; i < dimsave.size(); i++) {
     int dim = dimsave.at(i);
-    if(dim > -1 && dim < 4) homology->addChainsToModel(dim);
+    if(dim > -1 && dim < 4 && hom != 0) homology->addChainsToModel(dim);
+    if(dim > -1 && dim < 4 && coh != 0) homology->addCochainsToModel(dim);
   }
 
-  delete cc;
   delete homology;
 
   return 0;
diff --git a/demos/homology.geo b/demos/homology.geo
index de0500b9a4..a8031ed426 100644
--- a/demos/homology.geo
+++ b/demos/homology.geo
@@ -147,22 +147,3 @@ Cohomology {{69}, {70, 71, 72, 73}};
 //  Homology = {{69}, {}}; 
 //  Homology {{69}, {74}}; 
 //  Homology {{74}, {70, 71, 72, 73}}; 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/utils/api_demos/mainHomology.cpp b/utils/api_demos/mainHomology.cpp
index 1697a079f7..b721163118 100644
--- a/utils/api_demos/mainHomology.cpp
+++ b/utils/api_demos/mainHomology.cpp
@@ -32,26 +32,21 @@ int main(int argc, char **argv)
   // initialize
   Homology* homology = new Homology(m, domain, subdomain);
 
-  // construct cell complex of the meshed domain
-  CellComplex* cc = homology->createCellComplex();
-
   // find homology basis elements
-  homology->findHomologyBasis(cc);
-
-  // restore cell complex for a new run
-  cc->restoreComplex();
+  homology->findHomologyBasis();
 
   // find cohomology basis elements
-  homology->findCohomologyBasis(cc);
+  homology->findCohomologyBasis();
 
   // add 1 and 2 dimensional result chains to model
   homology->addChainsToModel(1);
   homology->addChainsToModel(2);
+  homology->addCochainsToModel(1);
+  homology->addCochainsToModel(2);
 
   // write mesh with (co)homology computation result chains
   m->writeMSH("model_hom.msh");
 
-  delete cc;
   delete homology;
   delete m;
   GmshFinalize();
-- 
GitLab