CoMMA 1.3.2
A geometric agglomerator for unstructured meshes
Loading...
Searching...
No Matches
Agglomerator.h
Go to the documentation of this file.
1#ifndef COMMA_PROJECT_AGGLOMERATOR_H
2#define COMMA_PROJECT_AGGLOMERATOR_H
3
4/*
5 * CoMMA
6 *
7 * Copyright © 2024 ONERA
8 *
9 * Authors: Nicolas Lantos, Alberto Remigi, and Riccardo Milani
10 * Contributors: Karim Anemiche
11 *
12 * This Source Code Form is subject to the terms of the Mozilla Public
13 * License, v. 2.0. If a copy of the MPL was not distributed with this
14 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
15 */
16
17#include <cmath>
18#include <deque>
19#include <functional>
20#include <iostream>
21#include <iterator>
22#include <limits>
23#include <memory>
24#include <numeric>
25#include <optional>
26#include <set>
27#include <stdexcept>
28#include <vector>
29
30#include "CoMMA/ARComputer.h"
31#include "CoMMA/CoMMADefs.h"
33#include "CoMMA/Dual_Graph.h"
34#include "CoMMA/Neighbourhood.h"
35#include "CoMMA/Util.h"
36
37namespace comma {
38
39// How to pass parameters from base class
40// https://stackoverflow.com/questions/9692675/c-constructor-where-parameters-are-used-by-base-class-constructor
41// https://stackoverflow.com/questions/120876/what-are-the-rules-for-calling-the-base-class-constructor
42// Error handling from
43// https://weseetips.wordpress.com/tag/exception-from-constructor-initializer-list/
44
52template<
53 typename CoMMAIndexType,
54 typename CoMMAWeightType,
55 typename CoMMAIntType>
57public:
68 graph,
69 std::shared_ptr<
71 cc_graph,
73 seeds_pool,
74 CoMMAIntType dimension
75 ) :
76 _dimension(dimension),
77 _fc_graph(graph),
78 _cc_graph(cc_graph),
79 _seeds_pool(seeds_pool) {
80 if ((_dimension != 2) && (_dimension != 3)) {
81 throw std::range_error("dimension can only be 2 or 3");
82 }
83 if (_dimension == 2) {
85 } else {
87 }
88 _l_nb_of_cells.push_back(graph->_number_of_cells);
89 }
90
92 virtual ~Agglomerator() = default;
93
97 inline std::vector<CoMMAIndexType> get_fc_2_cc() const {
98 return _cc_graph->_fc_2_cc;
99 }
100
113 const CoMMAIntType goal_card,
114 const CoMMAIntType min_card,
115 const CoMMAIntType max_card,
116 const std::vector<CoMMAWeightType> &priority_weights,
117 bool correction_steps
118 ) = 0;
119
120protected:
123 CoMMAIntType _dimension;
126 CoMMAIntType _min_neighbourhood = 3;
129 CoMMAIntType _min_card = 0;
133 CoMMAIntType _max_card = 0;
137 CoMMAIntType _goal_card = 0;
141 CoMMAIntType _threshold_card = 0;
143 std::vector<CoMMAIndexType> _l_nb_of_cells;
147 std::shared_ptr<Dual_Graph<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>>
151 std::shared_ptr<
157 std::shared_ptr<Seeds_Pool<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>>
159};
160
169template<
170 typename CoMMAIndexType,
171 typename CoMMAWeightType,
172 typename CoMMAIntType>
174 public Agglomerator<CoMMAIndexType, CoMMAWeightType, CoMMAIntType> {
175public:
177 using AnisotropicLine = std::deque<CoMMAIndexType>;
178
180 using AnisotropicLinePtr = std::shared_ptr<AnisotropicLine>;
181
183 using CoMMAPairType = std::pair<CoMMAIndexType, CoMMAWeightType>;
186 std::set<CoMMAPairType, CustomPairGreaterFunctor<CoMMAPairType>>;
187
218 graph,
219 std::shared_ptr<
221 cc_graph,
223 seeds_pool,
224 CoMMAIntType dimension,
225 const CoMMAWeightType threshold_anisotropy,
226 const std::vector<CoMMAIndexType> &agglomerationLines_Idx,
227 const std::vector<CoMMAIndexType> &agglomerationLines,
228 const std::vector<CoMMAWeightType> &priority_weights,
229 const bool build_lines,
230 const bool odd_line_length,
231 const std::optional<CoMMAIndexType> max_cells_in_line,
233 const bool force_line_direction = true
234 ) :
235 Agglomerator<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>(
236 graph, cc_graph, seeds_pool, dimension
237 ),
240 _odd_line_length(odd_line_length),
241 _max_cells_in_line(max_cells_in_line),
242 _cell_coupling(cell_coupling),
243 _force_line_direction(force_line_direction) {
244 // for every defined level (1 by default), contains the number of cells
245 // e.g. _l_nb_of_cells[0]= number of cells on finest level
246 // _l_nb_of_cells[1]= number of cells on the first coarse level
247 this->_l_nb_of_cells.push_back(graph->_number_of_cells);
248
249 this->_nb_lines = std::vector<CoMMAIndexType>(2);
250 this->_v_lines = std::vector<std::vector<AnisotropicLinePtr>>(2);
251
252 if (build_lines) {
253 const CoMMAWeightType thr =
254 (threshold_anisotropy > 1 || threshold_anisotropy < 0)
255 ? threshold_anisotropy
256 : static_cast<CoMMAWeightType>(1. / threshold_anisotropy);
257 // if the finest agglomeration line is not computed, hence compute it
258 // (REMEMBER! We compute the agglomeration lines only on the (original)
259 // finest level
260 this->_should_agglomerate =
261 this->build_anisotropic_lines(priority_weights, thr);
262 } else {
263 // case in which we have already agglomerated one level and hence we have
264 // already agglomeration lines available; no need to recreate them.
265 this->_nb_lines[0] = 0;
266 for (auto idx_ptr = agglomerationLines_Idx.cbegin() + 1;
267 idx_ptr != agglomerationLines_Idx.cend();
268 ++idx_ptr) {
269 const auto ln_sz = *idx_ptr - (*(idx_ptr - 1));
270 if (ln_sz > 1) {
271 this->_v_lines[0].push_back(std::make_shared<AnisotropicLine>(
272 agglomerationLines.cbegin() + (*(idx_ptr - 1)),
273 agglomerationLines.cbegin() + (*idx_ptr)
274 ));
275 this->_nb_lines[0]++;
276 }
277 }
278 this->_should_agglomerate = this->_nb_lines[0] > 0;
279 if (!this->_should_agglomerate)
280 std::cout
281 << "CoMMA - No anisotropic line was built (e.g., only one-cell "
282 "lines). Skipping anisotropic agglomeration"
283 << std::endl;
284 }
285 }
286
288 ~Agglomerator_Anisotropic() override = default;
289
307 const CoMMAIntType goal_card,
308 const CoMMAIntType min_card,
309 const CoMMAIntType max_card,
310 const std::vector<CoMMAWeightType> &priority_weights,
311 bool correction_steps
312 ) override {
313 // Unused parameters
314 CoMMAUnused(goal_card);
315 CoMMAUnused(min_card);
316 CoMMAUnused(max_card);
317 CoMMAUnused(correction_steps);
318
319 if (this->_should_agglomerate) {
320 // Necessary for the create_cc but maybe we need in
321 // some way to change that.
322 constexpr bool is_anisotropic = true;
323 constexpr CoMMAIntType compactness = 1;
324
325 // Setting up lambda function that will perform the loop. This is needed
326 // since we want to loop forwards or backwards according to some runtime
327 // value See, e.g., https://stackoverflow.com/a/56133699/12152457
328 auto loop_line = [&](auto begin, auto end) {
329 AnisotropicLinePtr line_lvl_p_one = std::make_shared<AnisotropicLine>();
330 typename decltype(this->_max_cells_in_line)::value_type n_cells{0};
331 for (auto line_it = begin; line_it != end; line_it += 2) {
332 if (this->_max_cells_in_line.has_value()
333 && n_cells + 2 > this->_max_cells_in_line.value()) {
334 // Max length reached, break
335 break;
336 }
337 // we agglomerate cells along the agglomeration line, hence we have to
338 // go through the faces and agglomerate two faces together
339 // Here we have to consider a special case when we have an odd number
340 // of cells: THIS IS FUNDAMENTAL FOR THE CONVERGENCE OF THE MULTIGRID
341 // ALGORITHM
342 std::unordered_set<CoMMAIndexType> s_fc = {
343 *line_it, *std::next(line_it)
344 };
345 n_cells += 2;
346 // We update the neighbours. At this stage, we do not check if it is
347 // or will be agglomerated since there will be a cleaning step after
348 // the anisotropic agglomeration
349 this->_aniso_neighbours.insert(
350 this->_aniso_neighbours.end(),
351 this->_fc_graph->neighbours_cbegin(*line_it),
352 this->_fc_graph->neighbours_cend(*line_it)
353 );
354 this->_aniso_neighbours.insert(
355 this->_aniso_neighbours.end(),
356 this->_fc_graph->neighbours_cbegin(*std::next(line_it)),
357 this->_fc_graph->neighbours_cend(*std::next(line_it))
358 );
359 if (
360 std::distance(line_it, end) == 3
361 || (
362 this->_max_cells_in_line.has_value()
363 && n_cells + 1 == this->_max_cells_in_line.value())) {
364 if (this->_odd_line_length) {
365 // If only three cells left, agglomerate them
366 s_fc.insert(*std::next(line_it, 2));
367 n_cells++;
368 this->_aniso_neighbours.insert(
369 this->_aniso_neighbours.end(),
370 this->_fc_graph->neighbours_cbegin(*std::next(line_it, 2)),
371 this->_fc_graph->neighbours_cend(*std::next(line_it, 2))
372 );
373 }
374 line_it++; // Ensure to break the loop after current iteration
375 }
376 // We create the coarse cell
377 const CoMMAIndexType i_cc =
378 this->_cc_graph->create_cc(s_fc, compactness, is_anisotropic);
379 line_lvl_p_one->push_back(i_cc);
380 }
381
382 this->_v_lines[1].push_back(line_lvl_p_one);
383 }; // End lambda def
384
385 // Process of every agglomeration lines:
386 for (auto line_ptr = this->_v_lines[0].begin();
387 line_ptr != this->_v_lines[0].end();
388 line_ptr++) {
389 // We iterate on the anisotropic lines of a particular level (the level
390 // 1, where they were copied from level 0). We create a pointer to an
391 // empty deque for the line + 1, and hence for the next level of
392 // agglomeration
393 auto line = *line_ptr;
394 if (line->size() <= 1) {
395 // the agglomeration_line is empty and hence the iterator points again
396 // to the empty deque, updating what is pointed by it and hence
397 // __v_lines[1] (each time we iterate on the line, a new deque
398 // line_lvl_p_one is defined)
399 continue;
400 }
401 // We start agglomerating from the head or the tail of the line
402 // according to which of the two has more boundary faces, or priority or
403 // ID
404 const auto l_fr = line->front(), l_bk = line->back();
405 const auto bnd_fr = this->_fc_graph->get_n_boundary_faces(l_fr),
406 bnd_bk = this->_fc_graph->get_n_boundary_faces(l_bk);
407 const auto w_fr = priority_weights[l_fr], w_bk = priority_weights[l_bk];
408 const bool forward_line =
409 bnd_fr > bnd_bk // Greater boundaries...
410 || (bnd_fr == bnd_bk &&
411 (w_fr > w_bk // ...or greater priority...
412 || (w_fr == w_bk && l_fr < l_bk) // ..or smaller index
413 ));
414
415 if (forward_line)
416 loop_line(line->cbegin(), line->cend());
417 else
418 loop_line(line->crbegin(), line->crend());
419
420 } // End loop on lines
421
422 this->update_seeds_pool();
423
424 } // End if should agglomerate
425 else
426 // If it didn't agglomerate, initialize for sure
427 this->_seeds_pool->initialize();
428 }
429
434 if (!this->_aniso_neighbours.empty()) {
435 // Example of erase taken from
436 // https://en.cppreference.com/w/cpp/container/deque/erase
437 for (auto it = this->_aniso_neighbours.begin();
438 it != this->_aniso_neighbours.end();) {
439 if (this->_cc_graph->_is_fc_agglomerated[*it])
440 it = this->_aniso_neighbours.erase(it);
441 else
442 ++it;
443 }
444 if (!this->_aniso_neighbours.empty()) {
445 // The master queue is the one of the first agglomerated cell:
446 // It is important to set it in the case the user asked for
447 // neighbourhood priority
448 this->_seeds_pool->set_top_queue(
449 this->_fc_graph->get_n_boundary_faces(this->_aniso_neighbours.front())
450 );
451 this->_seeds_pool->update(this->_aniso_neighbours);
452 }
453 }
454 // Even if we have updated it, the seeds pool might need initialization, for
455 // instance, if it was set up with boundary priority
456 if (this->_seeds_pool->need_initialization(
457 this->_cc_graph->_is_fc_agglomerated
458 ))
459 this->_seeds_pool->initialize();
460 }
461
470 CoMMAIntType level,
471 std::vector<CoMMAIndexType> &aniso_lines_idx,
472 std::vector<CoMMAIndexType> &aniso_lines
473 ) const {
474 // Reset lines
475 aniso_lines_idx.clear();
476 aniso_lines.clear();
477 if (this->_should_agglomerate && !this->_v_lines[level].empty()) {
478 // If at the level of agglomeration "level" the vector containing the
479 // number of lines is empty, hence it means no line has been found at the
480 // current level.
481 // Variable cumulating the number of fine cells in the agglomeration lines
482 // of the current level
483 CoMMAIndexType idx = 0;
484 aniso_lines_idx.reserve(this->_v_lines[level].size() + 1);
485 aniso_lines_idx.push_back(idx);
486 aniso_lines.reserve(
487 this->_cc_graph->_cc_counter > 0 ? this->_cc_graph->_cc_counter
488 : this->_fc_graph->_number_of_cells
489 );
490 // We cycle over the line (in _v_lines)
491 for (const auto &line_ptr : this->_v_lines[level]) {
492 const CoMMAIndexType line_size = line_ptr->size();
493 // This vector store the end of a line and the start of a new
494 // anisotropic line WARNING! We are storing the anisotropic lines in a
495 // vector so we need a way to point to the end of a line and the
496 // starting of a new one.
497 aniso_lines_idx.push_back(line_size + idx);
498 // Here we store the index of the cell.
499 aniso_lines.insert(
500 aniso_lines.end(), line_ptr->cbegin(), line_ptr->cend()
501 );
502 idx += line_size;
503 }
504 aniso_lines_idx.shrink_to_fit();
505 aniso_lines.shrink_to_fit();
506 }
507 }
508
510 std::vector<CoMMAIndexType> _nb_lines;
511
518 std::vector<std::vector<AnisotropicLinePtr>> _v_lines;
519
524
525protected:
538 const std::vector<CoMMAWeightType> &priority_weights,
539 const CoMMAWeightType threshold_anisotropy
540 ) {
541 std::deque<CoMMAIndexType> aniso_seeds_pool;
542 // It is the max_weight, hence the maximum area among the faces composing
543 // the cell. Used to recognized the face
544 std::vector<CoMMAWeightType> max_weights(
545 this->_fc_graph->_number_of_cells, 0.0
546 );
547 std::vector<CoMMAWeightType> alt_weights{};
548 std::vector<bool> to_treat(this->_fc_graph->_number_of_cells, false);
549 // Computation of the anisotropic cell, alias of the cells for which the
550 // ratio between the face with maximum area and the face with minimum area
551 // is more than a given threshold.
552 this->_fc_graph->tag_anisotropic_cells(
553 alt_weights,
554 max_weights,
555 to_treat,
556 aniso_seeds_pool,
557 threshold_anisotropy,
558 priority_weights,
559 this->_cell_coupling,
560 0
561 );
562 std::vector<CoMMAWeightType> &ref_weights =
564 ? this->_fc_graph->_m_CRS_Values
565 : alt_weights;
566 if (aniso_seeds_pool.empty()) {
567 std::cout << "CoMMA - No anisotropic cell found. Skipping anisotropic "
568 "agglomeration"
569 << std::endl;
570 return false;
571 }
572 // Size might not be the dimension
573 const auto pts_dim = this->_fc_graph->_centers[0].size();
574 // size of the line
575 this->_nb_lines[0] = 0;
576 // we cycle on all the anisotropic cells identified before
577 for (auto &i_fc : aniso_seeds_pool) {
578 // seed from where we start building the line
579 if (!to_treat[i_fc]) {
580 // If the cell has been already treated, continue to the next
581 // anisotropic cell
582 continue;
583 }
584 // we save the primal seed for the opposite direction check that will
585 // happen later
586 const auto primal_seed = i_fc;
587 std::optional<std::vector<CoMMAWeightType>> primal_dir = std::nullopt;
588 // seed to be considered to add or not a new cell to the line
589 CoMMAIndexType seed = primal_seed;
590 // Create the new line
591 AnisotropicLinePtr cur_line = std::make_shared<AnisotropicLine>();
592 // we add the first seed
593 cur_line->push_back(seed);
594 to_treat[seed] = false;
595 // Flag to determine end of line
596 bool end = false;
597 // Flag to determine if we arrived at the end of an extreme of a line
598 bool opposite_direction_check = false;
599 // Info about growth direction
600 std::vector<CoMMAWeightType> prev_dir(pts_dim);
601 bool empty_line = true;
602 // Start the check from the seed
603 // while the line is not ended
604 while (!end) {
605 // for the seed (that is updated each time end!= true) we fill the
606 // neighbours and the weights
607 // Candidates to continue the line
608 CoMMASetOfPairType candidates;
609 // If the line is long enough, we use the direction. Otherwise, we use
610 // the weight. Putting a high-level if to reduce the branching inside
611 // the loop over the neighbours.
612 if (!this->_force_line_direction || empty_line) {
614 seed, to_treat, ref_weights, max_weights, candidates
615 );
616 } else {
617 // If not an empty line, we check the direction, see
618 // !dot_deviate below
620 seed, to_treat, ref_weights, max_weights, prev_dir, true, candidates
621 );
622 }
623 if (!candidates.empty()) {
624 // Even if we have more than one candidate, we choose just one
625 // otherwise we risk to add 2 (problematic with primal seed)
626 // It is what is done in Mavriplis
627 // https://scicomp.stackexchange.com/questions/41830/anisotropic-lines-identification-algorithm
631 // update the seed to the actual candidate
632 const auto old_seed = seed;
633 seed = candidates.begin()->first;
634 if (!opposite_direction_check) {
635 cur_line->push_back(seed);
636 } else {
637 cur_line->push_front(seed);
638 }
639 to_treat[seed] = false;
640 empty_line = false;
641 // Always new_seed - old_seed to be sure to keep the same direction
642 this->_fc_graph->get_center_direction(seed, old_seed, prev_dir);
643 if (!primal_dir.has_value()) primal_dir = prev_dir;
644 }
645 // 0 candidate, we are at the end of the line or at the end of one
646 // direction
647 else /*if (candidates.size() == 0)*/ {
648 // Before giving up, let's try another thing: Doing the same things as
649 // above with the only difference that we allow to move through any
650 // faces and not only the maximum one but still checking for direction
651 if (this->_force_line_direction && !empty_line) {
652 // If not an empty line, we check the direction, see is_parallel
653 // below
655 seed,
656 to_treat,
657 ref_weights,
658 max_weights,
659 prev_dir,
660 false,
661 candidates
662 );
663 if (!candidates.empty()) {
664 // We found one! Keep going!
665 const auto old_seed = seed;
666 seed = candidates.begin()->first;
667 if (!opposite_direction_check) {
668 cur_line->push_back(seed);
669 } else {
670 cur_line->push_front(seed);
671 }
672 to_treat[seed] = false;
673 empty_line = false;
674 // Always new_seed - old_seed to be sure to keep the same
675 // direction
676 this->_fc_graph->get_center_direction(seed, old_seed, prev_dir);
677 if (!primal_dir.has_value()) primal_dir = prev_dir;
678 } else {
679 // If we have already looked at the other side of the line or if
680 // the primal seed is where we are already at, there is nothing
681 // that we can do
682 if (!opposite_direction_check && seed != primal_seed) {
683 seed = primal_seed;
684 // The check seed != primal_seed should ensure that
685 // primal_dir != null.
686 // Changing direction with -
687 for (auto xyz = decltype(prev_dir.size()){0};
688 xyz < prev_dir.size();
689 xyz++) {
690 prev_dir[xyz] = -primal_dir.value()[xyz];
691 }
692 opposite_direction_check = true;
693 } else {
694 end = true;
695 }
696 }
697 } // End last step check on neighbours
698 else {
699 // If we have already looked at the other side of the line or if
700 // the primal seed is where we are already at, there is nothing
701 // that we can do
702 if (!opposite_direction_check && seed != primal_seed) {
703 seed = primal_seed;
704 // The check seed != primal_seed should ensure that
705 // primal_dir != null
706 // Changing direction with -
707 for (auto xyz = decltype(prev_dir.size()){0};
708 xyz < prev_dir.size();
709 xyz++) {
710 prev_dir[xyz] = -primal_dir.value()[xyz];
711 }
712 opposite_direction_check = true;
713 } else {
714 end = true;
715 }
716 }
717 } // End of no candidates case
718 } // End of a line
719 // we push the deque to the list if are bigger than 1
720 if (cur_line->size() > 1) {
721 this->_v_lines[0].push_back(cur_line);
722 this->_nb_lines[0] += 1;
723 }
724 } // End of loop over anisotropic cells
725
726 if (this->_nb_lines[0] == 0) {
727 std::cout << "CoMMA - No anisotropic line was built (e.g., only isolated "
728 "anisotropic cells). Skipping anisotropic agglomeration"
729 << std::endl;
730 return false;
731 }
732 return true;
733 }
734
741 inline bool is_high_coupling(
742 const CoMMAWeightType weight, const CoMMAWeightType ref_weight
743 ) {
744 return weight > 0.90 * ref_weight;
745 }
746
759 const CoMMAIndexType seed,
760 const std::vector<bool> &to_treat,
761 const std::vector<CoMMAWeightType> &weights,
762 const std::vector<CoMMAWeightType> &max_weights,
763 CoMMASetOfPairType &candidates
764 ) {
765 const auto offset = this->_fc_graph->_m_CRS_Row_Ptr[seed];
766 auto n_it = std::next(this->_fc_graph->_m_CRS_Col_Ind.cbegin(), offset);
767 auto w_it = std::next(weights.cbegin(), offset);
768 for (; n_it != this->_fc_graph->neighbours_cend(seed); ++n_it, ++w_it) {
769 if (to_treat[*n_it] && this->is_high_coupling(*w_it, max_weights[seed])) {
770 candidates.emplace(*n_it, *w_it);
771 }
772 } // end for loop
773 }
774
791 const CoMMAIndexType seed,
792 const std::vector<bool> &to_treat,
793 const std::vector<CoMMAWeightType> &weights,
794 const std::vector<CoMMAWeightType> &max_weights,
795 const std::vector<CoMMAWeightType> &prev_dir,
796 const bool check_weight,
797 CoMMASetOfPairType &candidates
798 ) {
799 const auto offset = this->_fc_graph->_m_CRS_Row_Ptr[seed];
800 auto n_it = std::next(this->_fc_graph->_m_CRS_Col_Ind.cbegin(), offset);
801 auto w_it = std::next(weights.cbegin(), offset);
802 const auto pts_dim = this->_fc_graph->_centers[0].size();
803 for (; n_it != this->_fc_graph->neighbours_cend(seed); ++n_it, ++w_it) {
804 if (to_treat[*n_it]
805 && (!check_weight || this->is_high_coupling(*w_it, max_weights[seed]))) {
806 std::vector<CoMMAWeightType> cur_dir(pts_dim);
807 // Always new_seed - old_seed to be sure to keep the same direction
808 this->_fc_graph->get_center_direction(*n_it, seed, cur_dir);
809 const auto dot = dot_product<CoMMAWeightType>(prev_dir, cur_dir);
810 // If not > 0 we might change direction 180 degrees
811 if (dot > 0 && !dot_deviate<CoMMAWeightType>(dot))
812 candidates.emplace(*n_it, dot);
813 }
814 } // end for loop
815 }
816
820 std::deque<CoMMAIndexType> _aniso_neighbours;
821
824
828 std::optional<CoMMAIndexType> _max_cells_in_line;
829
832
837};
838
847template<
848 typename CoMMAIndexType,
849 typename CoMMAWeightType,
850 typename CoMMAIntType>
852 public Agglomerator<CoMMAIndexType, CoMMAWeightType, CoMMAIntType> {
853public:
862 CoMMAIndexType,
863 CoMMAWeightType,
864 CoMMAIntType>;
868 CoMMAIntType _fc_iter;
869
871 std::shared_ptr<NeighbourhoodCreatorBaseType> _neigh_crtor;
872
874 std::shared_ptr<ARComputer<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>>
876
895 graph,
896 std::shared_ptr<
898 cc_graph,
900 seeds_pool,
901 CoMMAIntType dimension,
902 CoMMAAspectRatioT aspect_ratio,
903 CoMMANeighbourhoodT neighbourhood_type,
904 CoMMAIntType fc_iter
905 ) :
906 Agglomerator<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>(
907 graph, cc_graph, seeds_pool, dimension
908 ),
909 _fc_iter(fc_iter) {
910 if (neighbourhood_type == CoMMANeighbourhoodT::EXTENDED)
911 _neigh_crtor = std::make_shared<NeighbourhoodCreatorExtType>();
912 else
913 _neigh_crtor = std::make_shared<NeighbourhoodCreatorPFType>();
914
915 switch (aspect_ratio) {
917 if (dimension == 2) {
918 _ar_computer = std::make_shared<
920 graph
921 );
922 } else {
923 _ar_computer = std::make_shared<
925 graph
926 );
927 }
928 break;
929 }
931 _ar_computer = std::make_shared<
933 graph
934 );
935 break;
936 }
937 case DIAMETER: {
938 _ar_computer = std::make_shared<
940 break;
941 }
942 case ONE_OVER_MEASURE: {
943 _ar_computer = std::make_shared<
945 break;
946 }
948 _ar_computer = std::make_shared<
950 graph
951 );
952 break;
953 }
955 if (dimension == 2) {
956 _ar_computer = std::make_shared<ARExternalWeightOverRadius<
957 CoMMAIndexType,
958 CoMMAWeightType,
959 CoMMAIntType,
960 2>>(graph);
961 } else {
962 _ar_computer = std::make_shared<ARExternalWeightOverRadius<
963 CoMMAIndexType,
964 CoMMAWeightType,
965 CoMMAIntType,
966 3>>(graph);
967 }
968 break;
969 }
970 case EXTERNAL_WEIGHTS: {
971 _ar_computer = std::make_shared<
973 graph
974 );
975 break;
976 }
978 if (dimension == 2) {
979 _ar_computer = std::make_shared<ARMaxBaryDistanceOverRadius<
980 CoMMAIndexType,
981 CoMMAWeightType,
982 CoMMAIntType,
983 2>>(graph);
984 } else {
985 _ar_computer = std::make_shared<ARMaxBaryDistanceOverRadius<
986 CoMMAIndexType,
987 CoMMAWeightType,
988 CoMMAIntType,
989 3>>(graph);
990 }
991 break;
992 }
994 _ar_computer = std::make_shared<ARMaxOverMinBaryDistance<
995 CoMMAIndexType,
996 CoMMAWeightType,
997 CoMMAIntType>>(graph, 1e-12);
998 break;
999 }
1001 _ar_computer = std::make_shared<ARExternalWeightOverRadius<
1002 CoMMAIndexType,
1003 CoMMAWeightType,
1004 CoMMAIntType,
1005 1>>(graph);
1006 break;
1007 }
1008 default:
1009 throw std::invalid_argument("CoMMA - Error: Unknown aspect-ratio type");
1010 } /* Switch */
1011 }
1012
1014 ~Agglomerator_Isotropic() override = default;
1015
1026 CoMMAIntType goal_card = 0,
1027 CoMMAIntType min_card = 0,
1028 CoMMAIntType max_card = 0
1029 ) {
1030 // Note[RM]: I tried make the following const static but ended up
1031 // with some SEGFAULT with Intel possibly linked to the following
1032 // https://stackoverflow.com/a/36406774
1033 std::unordered_map<CoMMAIntType, CoMMAIntType> d_default_min_card = {
1034 {2, 3}, {3, 6}
1035 };
1036 std::unordered_map<CoMMAIntType, CoMMAIntType> d_default_max_card = {
1037 {2, 5}, {3, 10}
1038 };
1039 std::unordered_map<CoMMAIntType, CoMMAIntType> d_default_goal_card = {
1040 {2, 4}, {3, 8}
1041 };
1042 std::unordered_map<CoMMAIntType, CoMMAIntType> d_default_threshold_card = {
1043 {2, 2}, {3, 3}
1044 };
1045 // Definition of _min_card
1046 if (min_card == 0) {
1047 this->_min_card = d_default_min_card.at(this->_dimension);
1048 } else {
1049 this->_min_card = min_card;
1050 }
1051
1052 // Definition of _max_card
1053 if (max_card == 0) {
1054 this->_max_card = d_default_max_card.at(this->_dimension);
1055 } else {
1056 this->_max_card = max_card;
1057 }
1058 // Definition of _goal_card
1059 if (goal_card == 0) {
1060 this->_goal_card = d_default_goal_card.at(this->_dimension);
1061 } else {
1062 this->_goal_card = goal_card;
1063 }
1064
1065 // Definition of _threshold_card
1066 this->_threshold_card = d_default_threshold_card.at(this->_dimension);
1067 }
1068
1093 const CoMMAIntType goal_card,
1094 const CoMMAIntType min_card,
1095 const CoMMAIntType max_card,
1096 const std::vector<CoMMAWeightType> &priority_weights,
1097 bool correction_steps
1098 ) override {
1099 set_agglomeration_parameter(goal_card, min_card, max_card);
1100 constexpr bool is_anistropic = false;
1101 // We define a while for which we control the number of agglomerated cells
1102 const CoMMAIndexType nb_of_fc = this->_l_nb_of_cells[0];
1103 while (this->_cc_graph->get_number_of_fc_agglomerated() < nb_of_fc) {
1104 // 1) Choose a new seed
1105 const auto seed =
1106 this->_seeds_pool->choose_new_seed(this->_cc_graph->_is_fc_agglomerated
1107 );
1108 assert(seed.has_value());
1109 // 2) Choose the set of Coarse Cells with the specification of the
1110 // algorithm in the children class
1111 CoMMAIntType compactness = 0;
1112 const std::unordered_set<CoMMAIndexType> set_current_cc =
1114 seed.value(), priority_weights, compactness
1115 );
1116 // 3) Creation of cc
1117 // We check that threshold cardinality is reached
1118 // const bool creation_delayed =
1119 // (static_cast<CoMMAIntType>(s_current_cc.size()) <=
1120 // this->_threshold_card);
1121 // @TODO: We prefer to have coarse cells created right now, it might be
1122 // interesting to explore if delaying was allowed
1123 constexpr bool is_creation_delayed = false;
1124 this->_cc_graph->create_cc(
1125 set_current_cc, compactness, is_anistropic, is_creation_delayed
1126 );
1127 }
1128 // When we exit from this process all the cells are agglomerated, apart the
1129 // delayed ones
1130 // We proceed in creating the delayed ones
1131 this->_cc_graph->cc_create_all_delayed_cc();
1132 // Correct if necessary
1133 if (correction_steps) {
1134 this->_cc_graph->correct(this->_max_card);
1135 }
1136 this->_l_nb_of_cells.push_back(this->_cc_graph->_cc_counter);
1137 }
1138
1148 virtual std::unordered_set<CoMMAIndexType>
1150 const CoMMAIndexType seed,
1151 const std::vector<CoMMAWeightType> &priority_weights,
1152 CoMMAIntType &compactness
1153 ) = 0;
1154};
1155
1164template<
1165 typename CoMMAIndexType,
1166 typename CoMMAWeightType,
1167 typename CoMMAIntType>
1169 public Agglomerator_Isotropic<CoMMAIndexType, CoMMAWeightType, CoMMAIntType> {
1170public:
1188 graph,
1189 std::shared_ptr<
1191 cc_graph,
1193 seeds_pool,
1194 CoMMAIntType dimension,
1195 CoMMAAspectRatioT aspect_ratio,
1196 CoMMANeighbourhoodT neighbourhood_type,
1197 CoMMAIntType fc_iter
1198 ) :
1199 Agglomerator_Isotropic<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>(
1200 graph,
1201 cc_graph,
1202 seeds_pool,
1203 dimension,
1204 aspect_ratio,
1205 neighbourhood_type,
1206 fc_iter
1207 ) {
1208 // no particular constructor
1209 }
1210
1212 ~Agglomerator_Biconnected() override = default;
1213
1224 std::unordered_set<CoMMAIndexType> choose_optimal_cc_and_update_seeds_pool(
1225 const CoMMAIndexType seed,
1226 const std::vector<CoMMAWeightType> &priority_weights,
1227 CoMMAIntType &compactness
1228 ) override {
1229 bool is_order_primary = false;
1230 // The goal of this function is to choose from a pool of neighbour the
1231 // better one to build a compact coarse cell
1232 assert(this->_goal_card > 1); // _goal_card has been initialized
1233 // OUTPUT: Definition of the current cc, IT WILL BE GIVEN AS AN OUTPUT
1234 std::unordered_set<CoMMAIndexType> s_current_cc = {seed};
1235 // Dictionary of the neighbourhood of the seed, the key is the global index
1236 // of the cell and the value the order of distance from the seed (1 order
1237 // direct neighbourhood, 2 order etc.)
1238 std::unordered_map<CoMMAIndexType, CoMMAIntType> d_n_of_seed;
1239 // Number of fine cells constituting the current coarse cell in
1240 // construction.
1241 CoMMAIntType size_current_cc = 1; // CC contains only one cell: the seed
1242 // set to 3 as default we set to this value the maximum order to which we
1243 // search to compose the coarse cell
1244 CoMMAIntType max_order_of_neighbourhood =
1245 std::max(this->_min_neighbourhood, this->_max_card / this->_dimension);
1246
1247 // We fill the d_n_of_seeds considering the initial seed passed
1248 this->_fc_graph->compute_neighbourhood_of_cc(
1249 s_current_cc,
1250 max_order_of_neighbourhood, // in and out
1251 d_n_of_seed, // output, the function fills the dictionary
1252 this->_max_card,
1253 // anisotropic cells agglomerated (not to take into account in the
1254 // process)
1255 this->_cc_graph->_is_fc_agglomerated
1256 );
1257
1258 // If no neighbour is found for seed: this case happened only when isotropic
1259 // cell is surrounded by anisotropic cells.
1260 if (d_n_of_seed.empty()) {
1261 // d_n_of_seed is empty, i.e: the cc is not complete and no neighbour are
1262 // available!
1263 compactness = 0;
1264 } else if (static_cast<CoMMAIntType>(
1265 d_n_of_seed.size() + s_current_cc.size()
1266 )
1267 < this->_goal_card) {
1268 // Not enough available neighbour, the dictionary of the available cells
1269 // size summed with the current cell size is not enough to reach the goal
1270 // cardinality: creation of a (too small) coarse cell.
1271 // We add ALL the cells of the dictionary to the set of current coarse
1272 // cell.
1273 for (auto &i_k_v : d_n_of_seed) {
1274 s_current_cc.insert(i_k_v.first);
1275 }
1276 compactness =
1277 this->_fc_graph->compute_min_fc_compactness_inside_a_cc(s_current_cc);
1278 } else {
1279 // If we passed the two previous checks, the minimum size is the goal
1280 // cardinality required
1281 // TODO: CHECK THAT, if the goal is 2, the minimum size would be 3?
1282 // ARGUABLE! Let's think to 3
1283 // CC in construction
1284 decltype(s_current_cc) tmp_cc = {seed};
1285 // volume of cc is at first the volume of the seed.
1287 seed, this->_fc_graph
1288 );
1289 // This dictionary is used to store the eligible cc: i.e. its size is
1290 // inside the permitted range. This is useful to track back our step if
1291 // needed. [size of the current, [cell set, d_n_of seed]]
1292 std::unordered_map<
1293 CoMMAIntType,
1294 std::pair<
1295 std::unordered_set<CoMMAIndexType>,
1296 std::unordered_map<CoMMAIndexType, CoMMAIntType>>>
1297 dict_cc_in_creation;
1298 CoMMAIntType min_external_faces =
1299 std::numeric_limits<CoMMAIntType>::max();
1300 CoMMAIntType max_compact = 0;
1301 CoMMAIntType arg_min_card = this->_min_card;
1302 // Here we define the exact dimension of the coarse cell as the min
1303 // between the max cardinality given as an input (remember the constructor
1304 // choice in case of -1) and the dictionary of the boundary cells, it
1305 // means the total number of neighbourhood cells until the order we have
1306 // given (as default 3, so until the third order)
1307 const CoMMAIntType max_ind = std::min(
1308 this->_max_card, static_cast<CoMMAIntType>(d_n_of_seed.size() + 1)
1309 );
1310 // This formula does not work
1311 CoMMAIntType number_of_external_faces_current_cc =
1312 this->_fc_graph->get_nb_of_neighbours(seed)
1313 + this->_fc_graph->get_n_boundary_faces(seed) - 1;
1314 // d_keys_to_set from Util.h, it takes the keys of the unordered map and
1315 // create an unordered set. The unordered set is representing hence all
1316 // the neighbours of seed until a given order.
1317 const std::unordered_set<CoMMAIndexType> s_neighbours_of_seed =
1318 d_keys_to_set<CoMMAIndexType, CoMMAIntType>(d_n_of_seed);
1319 // Build the class neighbourhood
1320 auto neighbourhood = this->_neigh_crtor->create(
1321 s_neighbours_of_seed, priority_weights, this->_dimension
1322 );
1323 // Compactness is used when choosing the best cell. We compute it
1324 // iteratively as the agglomeration advances.
1325 std::unordered_map<CoMMAIndexType, CoMMAIntType> cur_compact_by_fc{};
1326 cur_compact_by_fc.reserve(max_ind);
1327 cur_compact_by_fc[seed] = 0;
1328 constexpr auto second_less = [](const auto &left, const auto &right) {
1329 return left.second < right.second;
1330 };
1331 CoMMAIndexType next_cell = seed; // Dummy initialization
1332 // Choice of the fine cells to agglomerate we enter in a while, we store
1333 // anyways all the possible coarse cells (not only the max dimension one)
1334 while (size_current_cc < max_ind) {
1335 // Update the neighbourhood and generate the candidates
1336 neighbourhood->update_it(
1337 next_cell,
1338 this->_fc_graph->neighbours_cbegin(next_cell),
1339 this->_fc_graph->neighbours_cend(next_cell)
1340 );
1341 next_cell = 0; // Dummy initialization
1343 min_ar_cc_feats{};
1344 CoMMAIntType max_faces_in_common = 0;
1345 // We compute the best fine cell to add, based on the aspect
1346 // ratio and is given back in next_cell. It takes account also
1347 // the fine cells that has been added until now.
1348 // min(max_ind - size_current_cc, this->_fc_iter)
1350 std::min(max_ind - size_current_cc, this->_fc_iter),
1351 neighbourhood,
1352 d_n_of_seed,
1353 is_order_primary,
1354 cur_cc_feats,
1355 tmp_cc,
1356 next_cell,
1357 // output
1358 max_faces_in_common,
1359 min_ar_cc_feats
1360 );
1361
1362 // This formula does not work
1363 number_of_external_faces_current_cc +=
1364 this->_fc_graph->get_nb_of_neighbours(next_cell)
1365 + this->_fc_graph->get_n_boundary_faces(next_cell) - 1
1366 - 2 * max_faces_in_common;
1367 // we increase the cc
1368 size_current_cc++;
1369 tmp_cc.insert(next_cell);
1370
1371 // Update compactness
1372 CoMMAIntType argmin_compact{0};
1373 for (auto neigh = this->_fc_graph->neighbours_cbegin(next_cell);
1374 neigh != this->_fc_graph->neighbours_cend(next_cell);
1375 ++neigh) {
1376 if (tmp_cc.find(*neigh) != tmp_cc.end()) {
1377 ++argmin_compact;
1378 ++cur_compact_by_fc[*neigh];
1379 }
1380 }
1381 cur_compact_by_fc[next_cell] = argmin_compact;
1382 const CoMMAIntType cur_compact =
1383 min_element(
1384 cur_compact_by_fc.cbegin(), cur_compact_by_fc.cend(), second_less
1385 )
1386 ->second;
1387 // Equivalent to:
1388 // this->_fc_graph->compute_min_fc_compactness_inside_a_cc(tmp_cc);
1389
1390 // if the constructed cc is eligible, i.e. its size is inside the
1391 // permitted range we store it inside dict_cc_in_creation This choice is
1392 // based on the idea that the smallest cc (w.r.t. card) is may be not
1393 // the one that minimized the number of external faces. If this if is
1394 // satisfied it means we have an eligible cell
1395 if ((this->_min_card <= size_current_cc)
1396 || size_current_cc == max_ind) {
1397 if (cur_compact > max_compact) {
1398 max_compact = cur_compact;
1399 min_external_faces = number_of_external_faces_current_cc;
1400 arg_min_card = size_current_cc;
1401
1402 } else if (cur_compact == max_compact) {
1403 if (
1404 (number_of_external_faces_current_cc < min_external_faces) ||
1405 (number_of_external_faces_current_cc == min_external_faces &&
1406 arg_min_card != this->_goal_card)) {
1407 min_external_faces = number_of_external_faces_current_cc;
1408 arg_min_card = size_current_cc;
1409 }
1410 }
1411
1412 // We update the dictionary of eligible coarse cells
1413 std::unordered_map<CoMMAIndexType, CoMMAIntType> new_dict;
1414 new_dict[next_cell] = d_n_of_seed[next_cell];
1415 std::pair<
1416 std::unordered_set<CoMMAIndexType>,
1417 std::unordered_map<CoMMAIndexType, CoMMAIntType>>
1418 tmp_pair = std::make_pair(tmp_cc, new_dict);
1419 dict_cc_in_creation[size_current_cc] = tmp_pair;
1420 }
1421
1422 // Update of cc features after new fc has been added
1423 cur_cc_feats = std::move(min_ar_cc_feats);
1424
1425 // Remove added fc from the available neighbours
1426 d_n_of_seed.erase(next_cell);
1427 }
1428
1429 // Selecting best CC to return
1430 s_current_cc = std::move(dict_cc_in_creation[arg_min_card].first);
1431
1432 // If we do not chose the biggest cc, we put the useless fc back to the
1433 // pool
1434 for (auto i_s = arg_min_card + 1; i_s < max_ind + 1; i_s++) {
1435 // for all size of Cell from arg_min_card+1 to min(max_card,
1436 // len(d_n_of_seed) + 1) + 1
1437 // d_n_of_seed.
1438 for (auto iKV : dict_cc_in_creation[i_s].second) {
1439 d_n_of_seed[iKV.first] = iKV.second;
1440 }
1441 }
1442
1443 // Updating Seeds Pool with the neighbours of the final CC. Strategy:
1444 // - Compute the direct neighbours of the CC (not yet agglomerated)
1445 // - Insert in the queue starting with those of lowest neighbourhood order
1446 // wrt
1447 // to the original seed
1448 // - If more than one cell with the same order, use priority weights
1449 const auto cc_neighs = this->_fc_graph->get_neighbourhood_of_cc(
1450 s_current_cc, this->_cc_graph->_is_fc_agglomerated
1451 );
1452 // Basically, like d_n_of_seed but with key and value swapped
1453 std::map<CoMMAIntType, std::unordered_set<CoMMAIndexType>>
1454 neighs_by_order{};
1455 // For those that were outside max neighbourhood order
1456 std::unordered_set<CoMMAIndexType> neighs_not_found{};
1457 for (const auto &neigh : cc_neighs) {
1458 if (d_n_of_seed.find(neigh) != d_n_of_seed.end())
1459 neighs_by_order[d_n_of_seed.at(neigh)].insert(neigh);
1460 else
1461 neighs_not_found.insert(neigh);
1462 }
1463 for (const auto &[o, neighs] : neighs_by_order)
1464 if (!neighs.empty())
1465 this->_seeds_pool->order_new_seeds_and_update(neighs);
1466 if (!neighs_not_found.empty())
1467 this->_seeds_pool->order_new_seeds_and_update(neighs_not_found);
1468
1469 assert(arg_min_card == static_cast<CoMMAIntType>(s_current_cc.size()));
1470
1471 compactness = max_compact;
1472 } // end else
1473 return s_current_cc;
1474 }
1475
1498 const CoMMAIntType fc_iter,
1499 const std::shared_ptr<
1501 neighbourhood,
1502 const std::unordered_map<CoMMAIndexType, CoMMAIntType> &d_n_of_seed,
1503 const bool &is_order_primary,
1505 const std::unordered_set<CoMMAIndexType> &s_of_fc_for_current_cc,
1506 CoMMAIndexType &argmin_ar,
1507 CoMMAIntType &max_faces_in_common,
1509 ) const {
1510 CoMMAUnused(fc_iter);
1511 // this function defines the best fine cells to add to create the coarse
1512 // cell for the current coarse cell considered
1513 CoMMAWeightType min_ar = std::numeric_limits<CoMMAWeightType>::max();
1514 const auto neighbours = neighbourhood->get_candidates();
1515 CoMMAIndexType arg_max_faces_in_common = neighbours[0];
1516
1517 for (const auto &i_fc : neighbours) {
1518 // we test every possible new cell to chose the one that locally maximizes
1519 // the number of shared faces and/or minimizes the Aspect Ratio Compute
1520 // features of the CC obtained by adding i_fc
1521 CoMMAIntType number_faces_in_common = 0;
1522 CoMMAWeightType new_ar = std::numeric_limits<CoMMAWeightType>::min();
1524 new_min_ar_cc_feats{};
1525 this->_ar_computer->compute_and_update_features(
1526 i_fc,
1527 cc_feats,
1528 s_of_fc_for_current_cc,
1529 // out
1530 number_faces_in_common,
1531 new_ar,
1532 new_min_ar_cc_feats
1533 );
1534
1535 // Neighbourhood order of i_fc wrt to original seed of CC
1536 // [i_fc] is not const the method at returns the reference to the value of
1537 // the key i_fc.
1538 const CoMMAIntType &order = d_n_of_seed.at(i_fc);
1539
1540 // TODO This version seems good but refactorisation to do: perhaps it is
1541 // not needed to update every new possible coarse cell aspect ratio?
1542 // TODO also need to remove the list of min_ar, argmin_ar, etc.
1543 if (number_faces_in_common >= max_faces_in_common
1544 or is_order_primary) { // if is_order_primary is True the order of
1545 // neighbourhood is primary
1546 if (number_faces_in_common == max_faces_in_common or is_order_primary) {
1547 // If the number of faces in common is the same, let's see whether
1548 // it's worth to update or not
1549
1550 if (order <= d_n_of_seed.at(arg_max_faces_in_common)) {
1551 // [arg_max_faces_in_common] is not const.
1552 if (order == d_n_of_seed.at(arg_max_faces_in_common)) {
1553 if (new_ar < min_ar and number_faces_in_common > 0) {
1554 // The second condition asserts the connectivity of the coarse
1555 // element.
1556 min_ar = new_ar;
1557 argmin_ar = i_fc;
1558 min_ar_cc_feats = std::move(new_min_ar_cc_feats);
1559
1560 arg_max_faces_in_common = i_fc;
1561 // The number of face in common is the same no need to touch it
1562 }
1563 } else {
1564 // Case :number_faces_in_common == max_faces_in_common and order <
1565 // dict_neighbours_of_seed[arg_max_faces_in_common]:
1566 arg_max_faces_in_common = i_fc;
1567 min_ar = new_ar;
1568 argmin_ar = i_fc;
1569 min_ar_cc_feats = std::move(new_min_ar_cc_feats);
1570 // The number of face in common is the same no need to touch it
1571 }
1572 }
1573 } else {
1574 // Case number_faces_in_common > max_faces_in_common:
1575 // -> Just update and see what comes next
1576 max_faces_in_common = number_faces_in_common;
1577 arg_max_faces_in_common = i_fc;
1578 min_ar = new_ar;
1579 argmin_ar = i_fc;
1580 min_ar_cc_feats = std::move(new_min_ar_cc_feats);
1581 }
1582 }
1583 }
1584 }
1585};
1586
1594template<
1595 typename CoMMAIndexType,
1596 typename CoMMAWeightType,
1597 typename CoMMAIntType>
1600 CoMMAIndexType,
1601 CoMMAWeightType,
1602 CoMMAIntType> {
1603public:
1621 graph,
1622 std::shared_ptr<
1624 cc_graph,
1626 seeds_pool,
1627 CoMMAIntType dimension,
1628 CoMMAAspectRatioT aspect_ratio,
1629 CoMMANeighbourhoodT neighbourhood_type,
1630 CoMMAIntType fc_iter
1631 ) :
1632 Agglomerator_Biconnected<CoMMAIndexType, CoMMAWeightType, CoMMAIntType>(
1633 graph,
1634 cc_graph,
1635 seeds_pool,
1636 dimension,
1637 aspect_ratio,
1638 neighbourhood_type,
1639 fc_iter
1640 ) {
1641 // no particular constructor
1642 }
1643
1645 ~Agglomerator_Iterative() override = default;
1646
1670 const CoMMAIntType fc_iter,
1671 const std::shared_ptr<
1673 neighbourhood,
1674 const std::unordered_map<CoMMAIndexType, CoMMAIntType> &d_n_of_seed,
1675 const bool &is_order_primary,
1677 const std::unordered_set<CoMMAIndexType> &s_of_fc_for_current_cc,
1678 CoMMAIndexType &argmin_ar,
1679 CoMMAIntType &max_faces_in_common,
1681 ) const override {
1682 CoMMAIndexType outer_argmax_faces{0};
1683 CoMMAIntType ref_max_faces = max_faces_in_common;
1684 CoMMAWeightType outer_ar = std::numeric_limits<CoMMAWeightType>::max();
1685 for (const auto &i_fc : neighbourhood->get_candidates()) {
1686 auto cur_neighbourhood = this->_neigh_crtor->clone(neighbourhood);
1687 CoMMAWeightType inner_ar{-1.};
1688 CoMMAIntType inner_max_faces_in_common{0};
1690 inner_min_ar_cc_feats{};
1691 this->_ar_computer->compute_and_update_features(
1692 i_fc,
1693 cc_feats,
1694 s_of_fc_for_current_cc,
1695 inner_max_faces_in_common,
1696 inner_ar,
1697 inner_min_ar_cc_feats
1698 );
1699 cur_neighbourhood->update_it(
1700 i_fc,
1701 this->_fc_graph->neighbours_cbegin(i_fc),
1702 this->_fc_graph->neighbours_cend(i_fc)
1703 );
1704 std::unordered_set<CoMMAIndexType> cur_fc{
1705 s_of_fc_for_current_cc.begin(), s_of_fc_for_current_cc.end()
1706 };
1707 cur_fc.insert(i_fc);
1708 // Store value of mother cell
1709 const CoMMAIntType ref_inner_faces = inner_max_faces_in_common;
1710
1711 if (fc_iter > 1) {
1712 CoMMAIndexType cur_argmin{0};
1713 CoMMAIntType cur_max_faces_in_common{0};
1715 cur_min_ar_cc_feats{};
1716 CoMMAWeightType cur_min_ar{0.};
1718 fc_iter - 1,
1719 cur_neighbourhood,
1720 d_n_of_seed,
1721 is_order_primary,
1722 inner_min_ar_cc_feats,
1723 cur_fc,
1724 // output
1725 cur_argmin,
1726 cur_max_faces_in_common,
1727 cur_min_ar_cc_feats
1728 );
1729 // We just keep the min AR and the max faces in common
1730 if (cur_max_faces_in_common > inner_max_faces_in_common) {
1731 inner_max_faces_in_common = cur_max_faces_in_common;
1732 } else if (cur_max_faces_in_common == inner_max_faces_in_common
1733 && cur_min_ar < inner_ar) {
1734 inner_ar = cur_min_ar;
1735 }
1736 }
1737
1738 const CoMMAIntType &order = d_n_of_seed.at(i_fc);
1739
1740 // TODO This version seems good but refactorisation to do: perhaps it is
1741 // not needed to update every new possible coarse cell aspect ratio?
1742 // TODO also need to remove the list of min_ar, argmin_ar, etc.
1743 if (inner_max_faces_in_common >= ref_max_faces
1744 or is_order_primary) { // if is_order_primary is True the order of
1745 // neighbourhood is primary
1746 if (inner_max_faces_in_common == ref_max_faces or is_order_primary) {
1747 // If the number of faces in common is the same, let's see whether
1748 // it's worth to update or not
1749
1750 if (order <= d_n_of_seed.at(outer_argmax_faces)) {
1751 // [outer_argmax_faces] is not const.
1752 if (order == d_n_of_seed.at(outer_argmax_faces)) {
1753 if (inner_ar < outer_ar and inner_max_faces_in_common > 0) {
1754 // The second condition asserts the connectivity of the coarse
1755 // element.
1756 argmin_ar = i_fc;
1757 // Outer AR is the min AR of the children, but since diameter
1758 // and volume are used in the next step, we keep those of the
1759 // mother cell...
1760 outer_ar = inner_ar;
1761 min_ar_cc_feats = std::move(inner_min_ar_cc_feats);
1762 // ... same for faces in common
1763 max_faces_in_common = ref_inner_faces;
1764
1765 outer_argmax_faces = i_fc;
1766 }
1767 } else {
1768 // Case :inner_max_faces_in_common == ref_max_faces and order <
1769 // dict_neighbours_of_seed[outer_argmax_faces]:
1770 outer_argmax_faces = i_fc;
1771 argmin_ar = i_fc;
1772 // Outer AR is the min AR of the children, but since diameter and
1773 // volume are used in the next step, we keep those of the mother
1774 // cell...
1775 outer_ar = inner_ar;
1776 min_ar_cc_feats = std::move(inner_min_ar_cc_feats);
1777 // ... same for faces in common
1778 max_faces_in_common = ref_inner_faces;
1779 }
1780 }
1781 } else {
1782 // Case inner_max_faces_in_common > ref_max_faces:
1783 // -> Just update and see what comes next
1784 ref_max_faces = inner_max_faces_in_common;
1785 outer_argmax_faces = i_fc;
1786 argmin_ar = i_fc;
1787 // Outer AR is the min AR of the children, but since diameter and
1788 // volume are used in the next step, we keep those of the mother
1789 // cell...
1790 outer_ar = inner_ar;
1791 min_ar_cc_feats = std::move(inner_min_ar_cc_feats);
1792 // ... same for faces in common
1793 max_faces_in_common = ref_inner_faces;
1794 }
1795 }
1796 } // for i_fc
1797 }
1798};
1799
1800} // end namespace comma
1801
1802#endif // COMMA_PROJECT_AGGLOMERATOR_H
#define CoMMAUnused(var)
Convenient function to avoid unused warnings.
Definition: Util.h:38
ARComputer. Here, AR is the ratio of the diameter over the smallest edge.
Definition: ARComputer.h:466
ARComputer. Here, AR is the ratio of the diameter over the estimated one (typically,...
Definition: ARComputer.h:361
ARComputer. Here, AR is the approximated diameter.
Definition: ARComputer.h:606
ARComputer. Here, AR is the ratio of the external weights over the measure. With dim equal to 2,...
Definition: ARComputer.h:529
ARComputer. Here, AR is the total external weights (that is, from a geometric point of view,...
Definition: ARComputer.h:659
ARComputer. Here, AR is the ratio of the maximum over minimum distance of the cell centers from the b...
Definition: ARComputer.h:764
ARComputer. Here, AR is the ratio of the maximum over minimum distance of the cell centers from the b...
Definition: ARComputer.h:832
ARComputer. Here, AR is one over the internal weights (looking for the minimum leads to the maximizat...
Definition: ARComputer.h:710
ARComputer. Here, AR is the reciprocal of the measure, hence the optimal solution should be the one w...
Definition: ARComputer.h:416
Agglomerator_Anisotropic class is a child class of the Agglomerator class that specializes the implem...
Definition: Agglomerator.h:174
CoMMACellCouplingT _cell_coupling
Definition: Agglomerator.h:831
bool build_anisotropic_lines(const std::vector< CoMMAWeightType > &priority_weights, const CoMMAWeightType threshold_anisotropy)
Build the anisotropic lines at the first level (only called at the first level of agglomeration)....
Definition: Agglomerator.h:537
void export_anisotropic_lines(CoMMAIntType level, std::vector< CoMMAIndexType > &aniso_lines_idx, std::vector< CoMMAIndexType > &aniso_lines) const
Function that prepares the anisotropic lines for output.
Definition: Agglomerator.h:469
std::pair< CoMMAIndexType, CoMMAWeightType > CoMMAPairType
Type of pair.
Definition: Agglomerator.h:183
bool _odd_line_length
Whether anisotropic lines with odd length are allowed.
Definition: Agglomerator.h:823
void find_cell_candidates_for_line_max_weight(const CoMMAIndexType seed, const std::vector< bool > &to_treat, const std::vector< CoMMAWeightType > &weights, const std::vector< CoMMAWeightType > &max_weights, CoMMASetOfPairType &candidates)
Find cells which are good candidates to be added to the anisotropic line. In order to identify the ca...
Definition: Agglomerator.h:758
void update_seeds_pool()
Update the seeds pool with the neighbours of the anisotropic cells agglomerated so far.
Definition: Agglomerator.h:433
Agglomerator_Anisotropic(std::shared_ptr< Dual_Graph< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > graph, std::shared_ptr< Coarse_Cell_Container< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > cc_graph, std::shared_ptr< Seeds_Pool< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > seeds_pool, CoMMAIntType dimension, const CoMMAWeightType threshold_anisotropy, const std::vector< CoMMAIndexType > &agglomerationLines_Idx, const std::vector< CoMMAIndexType > &agglomerationLines, const std::vector< CoMMAWeightType > &priority_weights, const bool build_lines, const bool odd_line_length, const std::optional< CoMMAIndexType > max_cells_in_line, CoMMACellCouplingT cell_coupling=CoMMACellCouplingT::MAX_WEIGHT, const bool force_line_direction=true)
Constructor.
Definition: Agglomerator.h:216
bool _force_line_direction
Definition: Agglomerator.h:836
void agglomerate_one_level(const CoMMAIntType goal_card, const CoMMAIntType min_card, const CoMMAIntType max_card, const std::vector< CoMMAWeightType > &priority_weights, bool correction_steps) override
Specialization of the pure virtual function to the class Agglomerator_Anisotropic....
Definition: Agglomerator.h:306
bool _should_agglomerate
Whether agglomeration is possible: for instance, if anisotropy requested but no anisotropic cells fou...
Definition: Agglomerator.h:523
std::optional< CoMMAIndexType > _max_cells_in_line
Maximum number of cells in an anisotropic line; when this value is reached, all reaming cells are dis...
Definition: Agglomerator.h:828
std::deque< CoMMAIndexType > AnisotropicLine
Container for an anisotropic line.
Definition: Agglomerator.h:177
std::shared_ptr< AnisotropicLine > AnisotropicLinePtr
(Shared) Pointer to an anisotropic line
Definition: Agglomerator.h:180
std::set< CoMMAPairType, CustomPairGreaterFunctor< CoMMAPairType > > CoMMASetOfPairType
Type of set of pairs.
Definition: Agglomerator.h:186
~Agglomerator_Anisotropic() override=default
Destructor.
std::vector< std::vector< AnisotropicLinePtr > > _v_lines
_v_lines : Agglomeration lines structure:
Definition: Agglomerator.h:518
std::deque< CoMMAIndexType > _aniso_neighbours
Neighbours of the anisotropic cells agglomerated. They are used to update the seeds pool.
Definition: Agglomerator.h:820
void find_cell_candidates_for_line_direction(const CoMMAIndexType seed, const std::vector< bool > &to_treat, const std::vector< CoMMAWeightType > &weights, const std::vector< CoMMAWeightType > &max_weights, const std::vector< CoMMAWeightType > &prev_dir, const bool check_weight, CoMMASetOfPairType &candidates)
Find cells which are good candidates to be added to the anisotropic line. In order to identify the ca...
Definition: Agglomerator.h:790
std::vector< CoMMAIndexType > _nb_lines
Vector of number of Anisotropic agglomeration lines per level.
Definition: Agglomerator.h:510
bool is_high_coupling(const CoMMAWeightType weight, const CoMMAWeightType ref_weight)
Tell whether a weight should be considered as high coupling between two cells.
Definition: Agglomerator.h:741
Child class of Agglomerator_Isotropic where is implemented a specific biconnected algorithm for the a...
Definition: Agglomerator.h:1169
~Agglomerator_Biconnected() override=default
Destructor.
std::unordered_set< CoMMAIndexType > choose_optimal_cc_and_update_seeds_pool(const CoMMAIndexType seed, const std::vector< CoMMAWeightType > &priority_weights, CoMMAIntType &compactness) override
Specialization of the pure virtual function in the parent class, to be used in couple with the agglom...
Definition: Agglomerator.h:1224
virtual void compute_best_fc_to_add(const CoMMAIntType fc_iter, const std::shared_ptr< Neighbourhood< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > neighbourhood, const std::unordered_map< CoMMAIndexType, CoMMAIntType > &d_n_of_seed, const bool &is_order_primary, const CellFeatures< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > &cc_feats, const std::unordered_set< CoMMAIndexType > &s_of_fc_for_current_cc, CoMMAIndexType &argmin_ar, CoMMAIntType &max_faces_in_common, CellFeatures< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > &min_ar_cc_feats) const
Computes the best fine cell to add to the coarse cell. The choice depends on: the number of shared fa...
Definition: Agglomerator.h:1497
Agglomerator_Biconnected(std::shared_ptr< Dual_Graph< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > graph, std::shared_ptr< Coarse_Cell_Container< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > cc_graph, std::shared_ptr< Seeds_Pool< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > seeds_pool, CoMMAIntType dimension, CoMMAAspectRatioT aspect_ratio, CoMMANeighbourhoodT neighbourhood_type, CoMMAIntType fc_iter)
Constructor of the class. No specific implementation, it instantiates the base class Agglomerator_Iso...
Definition: Agglomerator.h:1186
Agglomerator_Isotropic class is a child class of the Agglomerator class that specializes the implemen...
Definition: Agglomerator.h:852
std::shared_ptr< ARComputer< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > _ar_computer
Object computing the aspect-ratio of a coarse cell.
Definition: Agglomerator.h:875
virtual std::unordered_set< CoMMAIndexType > choose_optimal_cc_and_update_seeds_pool(const CoMMAIndexType seed, const std::vector< CoMMAWeightType > &priority_weights, CoMMAIntType &compactness)=0
Pure virtual function that must be implemented in child classes to define the optimal coarse cell.
std::shared_ptr< NeighbourhoodCreatorBaseType > _neigh_crtor
Creator responsible for neighborhood objects.
Definition: Agglomerator.h:871
CoMMAIntType _fc_iter
Number of iterations allowed for the algorithm choosing which fine cell to add next.
Definition: Agglomerator.h:868
void set_agglomeration_parameter(CoMMAIntType goal_card=0, CoMMAIntType min_card=0, CoMMAIntType max_card=0)
The task of the function is to set the parameters of determine the cardinality limits with respect to...
Definition: Agglomerator.h:1025
Agglomerator_Isotropic(std::shared_ptr< Dual_Graph< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > graph, std::shared_ptr< Coarse_Cell_Container< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > cc_graph, std::shared_ptr< Seeds_Pool< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > seeds_pool, CoMMAIntType dimension, CoMMAAspectRatioT aspect_ratio, CoMMANeighbourhoodT neighbourhood_type, CoMMAIntType fc_iter)
Constructor. The constructor takes as arguments the same arguments of the father and in this way acti...
Definition: Agglomerator.h:893
void agglomerate_one_level(const CoMMAIntType goal_card, const CoMMAIntType min_card, const CoMMAIntType max_card, const std::vector< CoMMAWeightType > &priority_weights, bool correction_steps) override
Specialization of the pure virtual function to the class Agglomerator_Isotropic. We add the override ...
Definition: Agglomerator.h:1092
~Agglomerator_Isotropic() override=default
Destructor.
Child class of Agglomerator_Isotropic which implements a specialized iterative algorithm for the sear...
Definition: Agglomerator.h:1602
Agglomerator_Iterative(std::shared_ptr< Dual_Graph< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > graph, std::shared_ptr< Coarse_Cell_Container< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > cc_graph, std::shared_ptr< Seeds_Pool< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > seeds_pool, CoMMAIntType dimension, CoMMAAspectRatioT aspect_ratio, CoMMANeighbourhoodT neighbourhood_type, CoMMAIntType fc_iter)
Constructor of the class. No specific implementation, it instantiates the base class Agglomerator_Bic...
Definition: Agglomerator.h:1619
~Agglomerator_Iterative() override=default
Destructor.
void compute_best_fc_to_add(const CoMMAIntType fc_iter, const std::shared_ptr< Neighbourhood< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > neighbourhood, const std::unordered_map< CoMMAIndexType, CoMMAIntType > &d_n_of_seed, const bool &is_order_primary, const CellFeatures< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > &cc_feats, const std::unordered_set< CoMMAIndexType > &s_of_fc_for_current_cc, CoMMAIndexType &argmin_ar, CoMMAIntType &max_faces_in_common, CellFeatures< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > &min_ar_cc_feats) const override
Specialization of the parent function. This is an iterative version. Computes the best fine cell to a...
Definition: Agglomerator.h:1669
A class responsible to do the interface between the different kinds of agglomerator.
Definition: Agglomerator.h:56
std::vector< CoMMAIndexType > _l_nb_of_cells
List of number of cells per coarse cell created.
Definition: Agglomerator.h:143
virtual ~Agglomerator()=default
The destructor of the class.
Agglomerator(std::shared_ptr< Dual_Graph< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > graph, std::shared_ptr< Coarse_Cell_Container< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > cc_graph, std::shared_ptr< Seeds_Pool< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > seeds_pool, CoMMAIntType dimension)
The constructor of the interface.
Definition: Agglomerator.h:66
CoMMAIntType _min_card
Minimum cardinality (default = 0, meaning, equal to the dimension)
Definition: Agglomerator.h:129
std::shared_ptr< Coarse_Cell_Container< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > _cc_graph
pointer to Coarse_Cell_Container element
Definition: Agglomerator.h:153
std::shared_ptr< Dual_Graph< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > _fc_graph
Dual_Graph object determining Fine cells graph and hence the connectivity.
Definition: Agglomerator.h:148
CoMMAIntType _threshold_card
Threshold cardinality (default = 0, meaning, equal to the dimension)
Definition: Agglomerator.h:141
CoMMAIntType _min_neighbourhood
Minimum number of neighbourhood we extend to search the neighbourhood in the greedy algorithm....
Definition: Agglomerator.h:126
CoMMAIntType _dimension
Dimensionality of the problem (_dimension = 2 -> 2D, _dimension = 3 -> 3D)
Definition: Agglomerator.h:123
CoMMAIntType _max_card
Maximum cardinality (default = 0, meaning, 5 or 10 for, resp., 2- and 3D.
Definition: Agglomerator.h:133
std::shared_ptr< Seeds_Pool< CoMMAIndexType, CoMMAWeightType, CoMMAIntType > > _seeds_pool
Seeds_Pool object giving the order in which the fine cells should be considered when agglomerating.
Definition: Agglomerator.h:158
CoMMAIntType _goal_card
Goal cardinality (default = 0, meaning, 4 or 8 for, resp., 2- and 3D.
Definition: Agglomerator.h:137
std::vector< CoMMAIndexType > get_fc_2_cc() const
Accessor to retrieve the fine cells to coarse cells from the coarse cell graphs class.
Definition: Agglomerator.h:97
virtual void agglomerate_one_level(const CoMMAIntType goal_card, const CoMMAIntType min_card, const CoMMAIntType max_card, const std::vector< CoMMAWeightType > &priority_weights, bool correction_steps)=0
Pure virtual function which implementation is specified in the related child classes and that defines...
Convenient class containing salient features of a cell. According to to the chosen AR computation (se...
Definition: ARComputer.h:36
Class implementing a custom container where the coarse cells are stored.
Definition: Coarse_Cell_Container.h:41
A class implementing the CRS global graph representation of the global mesh.
Definition: Dual_Graph.h:548
Pure abstract class for a creator of Neighbourhood objects. It can create from scratch or by copy.
Definition: Neighbourhood.h:427
Creator of Neighbourhood_Extended objects. It can create from scratch or by copy.
Definition: Neighbourhood.h:475
Class representing the neighbourhood of a given cell in the graph. Mind that no information about the...
Definition: Neighbourhood.h:42
Creator of Neighbourhood_Extended objects. It can create from scratch or by copy.
Definition: Neighbourhood.h:543
Class representing the pool of all the seeds for creating a coarse cell.
Definition: Seeds_Pool.h:200
Definition: Agglomerator.h:37
CoMMAAspectRatioT
Type of aspect-ratio. Notation:
Definition: CoMMADefs.h:73
@ ONE_OVER_MEASURE
Definition: CoMMADefs.h:85
@ ONE_OVER_INTERNAL_WEIGHTS
Definition: CoMMADefs.h:87
@ ALGEBRAIC_PERIMETER_OVER_MEASURE
Definition: CoMMADefs.h:99
@ EXTERNAL_WEIGHTS
Definition: CoMMADefs.h:91
@ MAX_BARY_DIST_OVER_RADIUS
Definition: CoMMADefs.h:93
@ DIAMETER_OVER_RADIUS
Definition: CoMMADefs.h:75
@ DIAMETER_OVER_MIN_EDGE
Definition: CoMMADefs.h:79
@ MAX_OVER_MIN_BARY_DIST
Definition: CoMMADefs.h:95
@ DIAMETER
Definition: CoMMADefs.h:81
@ PERIMETER_OVER_RADIUS
Definition: CoMMADefs.h:89
CoMMACellCouplingT
Type of coupling between cells in an anisotropic line.
Definition: CoMMADefs.h:103
@ MAX_WEIGHT
Definition: CoMMADefs.h:105
CoMMANeighbourhoodT
Type of neighbourhood (of a coarse cell) considered when agglomerating.
Definition: CoMMADefs.h:37
@ EXTENDED
Extended, all neighbours of the coarse cell.
Definition: CoMMADefs.h:38