11 #if defined(HAVE_KBIPACK)
13 Homology::Homology(
GModel *model,
const std::vector<int> &physicalDomain,
14 const std::vector<int> &physicalSubdomain,
15 const std::vector<int> &physicalImdomain,
bool saveOrig,
16 int combine,
bool omit,
bool smoothen,
int heuristic)
17 : _model(model), _domain(physicalDomain), _subdomain(physicalSubdomain),
18 _imdomain(physicalImdomain), _saveOrig(saveOrig), _combine(combine),
19 _omit(omit), _smoothen(smoothen), _heuristic(heuristic),
27 std::vector<GEntity *> entities;
29 for(
auto it = entities.begin(); it != entities.end(); it++) {
30 if((*it)->dim() ==
dim) _domainEntities.push_back(*it);
41 for(std::size_t i = 0; i < 4; i++) {
42 _homologyComputed[i] =
false;
43 _cohomologyComputed[i] =
false;
47 if(abs(_heuristic) > 1) _heuristic = 0;
50 std::vector<int> vecN0(
int n)
54 for(
int i = 0; i < n; i++) v.push_back(i);
59 std::vector<GEntity *> &entities)
62 std::map<int, std::vector<GEntity *> > groups[4];
63 _model->getPhysicalGroups(groups);
65 for(std::size_t i = 0; i < physicalGroups.size(); i++) {
66 for(
int j = 0; j < 4; j++) {
67 auto it = groups[j].find(physicalGroups.at(i));
68 if(it != groups[j].end()) {
69 std::vector<GEntity *> physicalGroup = (*it).second;
70 for(std::size_t k = 0; k < physicalGroup.size(); k++) {
71 entities.push_back(physicalGroup.at(k));
78 void Homology::_getElements(
const std::vector<GEntity *> &entities,
79 std::vector<MElement *> &elements)
82 for(std::size_t j = 0; j < entities.size(); j++) {
83 for(std::size_t i = 0; i < entities.at(j)->getNumMeshElements(); i++) {
90 void Homology::_createCellComplex()
95 if(_domainEntities.empty())
Msg::Error(
"Domain is empty");
96 if(_subdomainEntities.empty())
Msg::Info(
"Subdomain is empty");
98 std::vector<MElement *> domainElements;
99 std::vector<MElement *> subdomainElements;
100 std::vector<MElement *> nondomainElements;
101 std::vector<MElement *> nonsubdomainElements;
102 std::vector<MElement *> immuneElements;
104 _getElements(_domainEntities, domainElements);
105 _getElements(_subdomainEntities, subdomainElements);
106 _getElements(_nondomainEntities, nondomainElements);
107 _getElements(_nonsubdomainEntities, nonsubdomainElements);
108 _getElements(_immuneEntities, immuneElements);
110 if(_cellComplex !=
nullptr)
delete _cellComplex;
111 _cellComplex =
new CellComplex(_model, domainElements, subdomainElements,
112 nondomainElements, nonsubdomainElements,
113 immuneElements, _saveOrig);
115 if(_cellComplex->getSize(0) == 0) {
116 Msg::Error(
"Cell Complex is empty: check the domain and the mesh");
119 Msg::StatusBar(
true,
"Done creating cell complex (Wall %gs, CPU %gs)",
121 Msg::Info(
"%d volumes, %d faces, %d edges, and %d vertices",
122 _cellComplex->getSize(3), _cellComplex->getSize(2),
123 _cellComplex->getSize(1), _cellComplex->getSize(0));
126 void Homology::_deleteChains(std::vector<int> dim)
128 for(std::size_t j = 0; j < dim.size(); j++) {
130 if(d < 0 || d > 3)
continue;
131 for(std::size_t i = 0; i < _chains[d].size(); i++) {
132 delete _chains[d].at(i);
135 _homologyComputed[d] =
false;
139 void Homology::_deleteCochains(std::vector<int> dim)
141 for(std::size_t j = 0; j < dim.size(); j++) {
143 if(d < 0 || d > 3)
continue;
144 for(std::size_t i = 0; i < _cochains[d].size(); i++) {
145 delete _cochains[d].at(i);
147 _cochains[d].clear();
148 _cohomologyComputed[d] =
false;
152 Homology::~Homology()
154 if(_cellComplex !=
nullptr)
delete _cellComplex;
159 void Homology::findHomologyBasis(std::vector<int> dim)
162 std::string domain = _getDomainString(_domain, _subdomain);
164 Msg::Info(
"To compute domain (%s) homology spaces", domain.c_str());
171 if(_cellComplex ==
nullptr) _createCellComplex();
172 if(_cellComplex->isReduced()) _cellComplex->restoreComplex();
177 double size1 = _cellComplex->getSize(-1);
178 _cellComplex->reduceComplex(_combine, _omit);
180 if(_combine > 1 && !_smoothen) {
181 for(
int i = 1; i <= 3; i++) {
182 if(!std::binary_search(dim.begin(), dim.end(), i)) {
183 _cellComplex->cocombine(i - 1);
189 double size2 = _cellComplex->getSize(-1);
190 Msg::StatusBar(
true,
"Done reducing cell complex (Wall %gs, CPU %gs, %g%%)",
191 w2 -
w1, t2 - t1, (1. - size2 / size1) * 100.);
192 Msg::Info(
"%d volumes, %d faces, %d edges, and %d vertices",
193 _cellComplex->getSize(3), _cellComplex->getSize(2),
194 _cellComplex->getSize(1), _cellComplex->getSize(0));
199 ChainComplex chainComplex = ChainComplex(_cellComplex);
200 chainComplex.computeHomology();
204 "Done computing homology space bases (Wall %gs, CPU %gs)",
208 for(
int j = 0; j < 4; j++) {
211 for(
int i = 1; i <= chainComplex.getBasisSize(j, 3); i++) {
212 std::string generator = convertInt(i);
214 std::string name =
"H_" +
dimension + domain + generator;
215 std::map<Cell *, int, CellPtrLessThan> chain;
216 chainComplex.getBasisChain(chain, i, j, 3, _smoothen);
217 int torsion = chainComplex.getTorsion(j, i);
219 _createChain(chain, name,
false);
220 _betti[j] = _betti[j] + 1;
222 Msg::Warning(
"H_%d %d has torsion coefficient %d!", j, i, torsion);
228 if(_fileName !=
"") writeBasisMSH();
230 Msg::Info(
"Ranks of domain (%s) homology spaces:", domain.c_str());
237 Msg::Info(
"Done computing (%s) homology spaces (Wall %gs, CPU %gs)",
238 domain.c_str(), w3 - w0, t3 - t0);
239 Msg::StatusBar(
false,
"H_0: %d, H_1: %d, H_2: %d, H_3: %d", _betti[0],
240 _betti[1], _betti[2], _betti[3]);
242 for(std::size_t i = 0; i < dim.size(); i++) {
244 if(d >= 0 && d < 4) _homologyComputed[d] =
true;
248 void Homology::findCohomologyBasis(std::vector<int> dim)
251 std::string domain = _getDomainString(_domain, _subdomain);
253 Msg::Info(
"To compute domain (%s) cohomology spaces", domain.c_str());
260 if(_cellComplex ==
nullptr) _createCellComplex();
261 if(_cellComplex->isReduced()) _cellComplex->restoreComplex();
266 double size1 = _cellComplex->getSize(-1);
268 _cellComplex->coreduceComplex(_combine, _omit, _heuristic);
270 std::sort(dim.begin(), dim.end());
272 for(
int i = 2; i >= 0; i--) {
273 if(!std::binary_search(dim.begin(), dim.end(), i)) {
274 _cellComplex->combine(i + 1);
280 double size2 = _cellComplex->getSize(-1);
282 Msg::StatusBar(
true,
"Done reducing cell complex (Wall %gs, CPU %gs, %g %%)",
283 w2 -
w1, t2 - t1, (1. - size2 / size1) * 100.);
284 Msg::Info(
"%d volumes, %d faces, %d edges, and %d vertices",
285 _cellComplex->getSize(3), _cellComplex->getSize(2),
286 _cellComplex->getSize(1), _cellComplex->getSize(0));
291 ChainComplex chainComplex = ChainComplex(_cellComplex);
292 chainComplex.computeHomology(
true);
296 "Done computing cohomology space bases (Wall %gs, CPU %gs)",
299 _deleteCochains(dim);
300 for(
int i = 0; i < 4; i++) _betti[i] = 0;
301 for(
int j = 3; j > -1; j--) {
304 for(
int i = 1; i <= chainComplex.getBasisSize(j, 3); i++) {
305 std::string generator = convertInt(i);
307 std::string name =
"H^" +
dimension + domain + generator;
308 std::map<Cell *, int, CellPtrLessThan> chain;
309 chainComplex.getBasisChain(chain, i, j, 3,
false);
310 int torsion = chainComplex.getTorsion(j, i);
312 _createChain(chain, name,
true);
313 _betti[j] = _betti[j] + 1;
315 Msg::Warning(
"H^%d %d has torsion coefficient %d!", j, i, torsion);
321 if(_fileName !=
"") writeBasisMSH();
323 Msg::Info(
"Ranks of domain (%s) cohomology spaces:", domain.c_str());
330 Msg::Info(
"Done computing (%s) cohomology spaces (Wall %gs, CPU %gs)",
331 domain.c_str(), w3 - w0, t3 - t0);
332 Msg::StatusBar(
false,
"H^0: %d, H^1: %d, H^2: %d, H^3: %d", _betti[0],
333 _betti[1], _betti[2], _betti[3]);
335 for(std::size_t i = 0; i < dim.size(); i++) {
337 if(d >= 0 && d < 4) _cohomologyComputed[d] =
true;
341 bool Homology::isBettiComputed()
const
343 bool computed =
true;
344 for(
int i = 0; i < 4; i++) {
345 if(_betti[i] == -1) computed =
false;
350 bool Homology::isHomologyComputed(std::vector<int> dim)
const
352 bool computed =
true;
353 for(std::size_t i = 0; i < dim.size(); i++) {
355 if(d < 0 || d > 3)
continue;
356 computed = computed && _homologyComputed[d];
361 bool Homology::isCohomologyComputed(std::vector<int> dim)
const
363 bool computed =
true;
364 for(std::size_t i = 0; i < dim.size(); i++) {
366 if(d < 0 || d > 3)
continue;
367 computed = computed && _cohomologyComputed[d];
372 void Homology::findCompatibleBasisPair(
int master, std::vector<int> dim)
374 if(!this->isHomologyComputed(dim)) this->findHomologyBasis(dim);
375 if(!this->isCohomologyComputed(dim)) this->findCohomologyBasis(dim);
376 for(std::size_t idim = 0; idim < dim.size(); idim++) {
377 int d = dim.at(idim);
378 if(d < 1 || d > 2)
continue;
379 int n = this->betti(d);
381 if((
int)_chains[d].size() != n || (
int)_cochains[d].size() != n) {
382 Msg::Warning(
"Cannot produce compatible %d-(co)homology bases.", d);
383 Msg::Debug(
"%d basis %d-chains and %d basis %d-cochains.",
384 (
int)_chains[d].size(), d, (
int)_cochains[d].size(), d);
388 for(
int i = 0; i < n; i++) {
389 for(
int j = 0; j < n; j++) {
391 m(i, j) = incidence(*_cochains[d].at(i), *_chains[d].at(j));
393 m(i, j) = incidence(*_chains[d].at(i), *_cochains[d].at(j));
397 int det = m.determinant();
398 if(abs(det) != 1 || !m.invertInPlace()) {
399 Msg::Warning(
"Cannot produce compatible %d-(co)homology bases.", d);
401 for(
int i = 0; i < n; i++)
402 for(
int j = 0; j < n; j++)
Msg::Debug(
"(%d, %d) = %d", i, j, m(i, j));
406 std::vector<Chain<int> *> newBasis(n);
409 for(
int i = 0; i < n; i++) {
410 newBasis.at(i) =
new Chain<int>();
411 for(
int j = 0; j < n; j++) {
412 *newBasis.at(i) += (int)m(i, j) * (*_cochains[d].at(j));
415 for(
int i = 0; i < n; i++) {
416 newBasis.at(i)->setName(_cochains[d].at(i)->getName());
417 delete _cochains[d].at(i);
418 _cochains[d].at(i) = newBasis.at(i);
422 for(
int i = 0; i < n; i++) {
423 newBasis.at(i) =
new Chain<int>();
424 for(
int j = 0; j < n; j++) {
425 *newBasis.at(i) += (int)m(i, j) * (*_chains[d].at(j));
428 for(
int i = 0; i < n; i++) {
429 newBasis.at(i)->setName(_chains[d].at(i)->getName());
430 delete _chains[d].at(i);
431 _chains[d].at(i) = newBasis.at(i);
437 std::vector<int> Homology::_addToModel(
int dim,
bool co,
bool post,
438 int physicalNumRequest)
const
440 std::vector<int> physicals;
441 if(dim < 0 || dim > 3)
return physicals;
444 for(std::size_t i = 0; i < _chains[dim].size(); i++) {
445 if(physicalNumRequest != -1)
446 pgnum = physicalNumRequest + i;
450 _chains[dim].at(i)->addToModel(this->getModel(), post, pgnum));
454 for(std::size_t i = 0; i < _cochains[dim].size(); i++) {
455 if(physicalNumRequest != -1)
456 pgnum = physicalNumRequest + i;
460 _cochains[dim].at(i)->addToModel(this->getModel(), post, pgnum));
463 if(!physicals.empty()) {
464 std::vector<int> empty;
465 std::string span = _getDomainString(physicals, empty);
466 std::string domain = _getDomainString(_domain, _subdomain);
468 Msg::Info(
"Span H_%d(%s) = %s", dim, domain.c_str(), span.c_str());
470 Msg::Info(
"Span H^%d(%s) = %s", dim, domain.c_str(), span.c_str());
475 std::vector<int> Homology::addChainsToModel(
int dim,
bool post,
476 int physicalNumRequest)
const
478 std::vector<int> physicals;
479 if(dim > -1 && !_homologyComputed[dim])
482 for(
int j = 0; j < 4; j++) {
483 std::vector<int> p = _addToModel(j,
false, post, physicalNumRequest);
484 physicals.insert(physicals.end(), p.begin(), p.end());
487 else if(dim > -1 && dim < 4) {
488 physicals = _addToModel(dim,
false, post, physicalNumRequest);
493 std::vector<int> Homology::addCochainsToModel(
int dim,
bool post,
494 int physicalNumRequest)
const
496 std::vector<int> physicals;
497 if(dim > -1 && !_cohomologyComputed[dim])
500 for(
int j = 0; j < 4; j++) {
501 std::vector<int> p = _addToModel(j,
true, post, physicalNumRequest);
502 physicals.insert(physicals.end(), p.begin(), p.end());
505 else if(dim > -1 && dim < 4) {
506 physicals = _addToModel(dim,
true, post, physicalNumRequest);
511 void Homology::getHomologyBasis(
int dim, std::vector<Chain<int> > &hom)
513 if(dim < 0 || dim > 3)
return;
514 if(!_homologyComputed[dim]) findHomologyBasis();
516 hom.resize(_chains[dim].size());
517 for(std::size_t i = 0; i < _chains[dim].size(); i++)
518 hom[i] = *_chains[dim].at(i);
521 void Homology::getCohomologyBasis(
int dim, std::vector<Chain<int> > &coh)
523 if(dim < 0 || dim > 3)
return;
524 if(!_cohomologyComputed[dim]) findCohomologyBasis();
526 coh.resize(_cochains[dim].size());
527 for(std::size_t i = 0; i < _cochains[dim].size(); i++)
528 coh[i] = *_cochains[dim].at(i);
531 void Homology::findBettiNumbers()
533 if(!isBettiComputed()) {
534 if(_cellComplex ==
nullptr) _createCellComplex();
535 if(_cellComplex->isReduced()) _cellComplex->restoreComplex();
539 double size1 = _cellComplex->getSize(-1);
541 _cellComplex->bettiReduceComplex();
544 double size2 = _cellComplex->getSize(-1);
547 "Done reducing cell complex (Wall %gs, CPU %gs, %g %%)",
548 w2 -
w1, t2 - t1, (1. - size2 / size1) * 100.);
549 Msg::Info(
"%d volumes, %d faces, %d edges, and %d vertices",
550 _cellComplex->getSize(3), _cellComplex->getSize(2),
551 _cellComplex->getSize(1), _cellComplex->getSize(0));
556 ChainComplex chainComplex = ChainComplex(_cellComplex);
557 chainComplex.computeHomology();
559 for(
int i = 0; i < 4; i++) _betti[i] = chainComplex.getBasisSize(i, 3);
567 std::string domain = _getDomainString(_domain, _subdomain);
568 Msg::Info(
"Domain %s Betti numbers:", domain.c_str());
574 Msg::StatusBar(
false,
"b0: %d, b1: %d, b2: %d, b3: %d", _betti[0], _betti[1],
575 _betti[2], _betti[3]);
578 int Homology::betti(
int dim)
580 if(dim < 0 || dim > 3)
return 0;
581 if(_betti[dim] != -1)
return _betti[dim];
587 int Homology::eulerCharacteristic()
589 if(_cellComplex ==
nullptr) _createCellComplex();
590 return _cellComplex->eulerCharacteristic();
593 void Homology::_createChain(std::map<Cell *, int, CellPtrLessThan> &preChain,
594 const std::string &name,
bool co)
596 Chain<int> *chain =
new Chain<int>();
597 chain->setName(name);
599 for(
auto cit = preChain.begin(); cit != preChain.end(); cit++) {
600 Cell *cell = cit->first;
601 int coeff = cit->second;
602 if(coeff == 0)
continue;
604 std::vector<MVertex *> v;
606 chain->addElemChain(ElemChain(cell->
getDim(), v), coeff);
609 _cochains[chain->getDim()].push_back(chain);
611 _chains[chain->getDim()].push_back(chain);
614 std::string Homology::_getDomainString(
const std::vector<int> &domain,
615 const std::vector<int> &subdomain)
const
617 std::string domainString =
"{";
621 for(std::size_t i = 0; i < domain.size(); i++) {
622 std::string temp = convertInt(domain.at(i));
623 domainString += temp;
624 if(domain.size() - 1 > i) { domainString +=
","; }
629 if(!subdomain.empty()) {
630 domainString +=
",{";
631 for(std::size_t i = 0; i < subdomain.size(); i++) {
632 std::string temp = convertInt(subdomain.at(i));
633 domainString += temp;
634 if(subdomain.size() - 1 > i) { domainString +=
","; }
642 bool Homology::writeBasisMSH(
bool binary)
644 if(_fileName.empty())
return false;
645 if(!_model->writeMSH(_fileName, 2.0, binary))
return false;
646 Msg::Info(
"Wrote homology computation results to %s", _fileName.c_str());
650 void Homology::storeCells(
CellComplex *cellComplex,
int dim)
652 std::vector<MElement *> elements;
658 std::map<Cell *, int, CellPtrLessThan> cells;
660 for(
auto it = cells.begin(); it != cells.end(); it++) {
661 Cell *subCell = it->first;
662 std::vector<MVertex *> v;
666 elements.push_back(e);
671 for(
int i = 0; i < 4; i++) max[i] = _model->getMaxElementaryNumber(i);
672 int entityNum = *std::max_element(max, max + 4) + 1;
673 for(
int i = 0; i < 4; i++) max[i] = _model->getMaxPhysicalNumber(i);
674 int physicalNum = *std::max_element(max, max + 4) + 1;
676 std::map<int, std::vector<MElement *> > entityMap;
677 entityMap[entityNum] = elements;
678 std::map<int, std::map<int, std::string> > physicalMap;
679 std::map<int, std::string> physicalInfo;
680 physicalInfo[physicalNum] =
"Cell Complex";
681 physicalMap[entityNum] = physicalInfo;
683 _model->storeChain(dim, entityMap, physicalMap);
684 _model->setPhysicalName(
"Cell Complex", dim, physicalNum);