FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
parti_domain_control_base.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
9
10#include <kernel/util/dist.hpp>
11#include <kernel/util/dist_file_io.hpp>
12#include <kernel/runtime.hpp>
13#include <kernel/util/simple_arg_parser.hpp>
14#include <kernel/util/property_map.hpp>
15#include <kernel/util/statistics.hpp>
16#include <kernel/geometry/mesh_node.hpp>
17#include <kernel/geometry/partition_set.hpp>
18#include <kernel/geometry/parti_2lvl.hpp>
19#include <kernel/geometry/parti_iterative.hpp>
20#include <kernel/geometry/parti_parmetis.hpp>
21#include <kernel/geometry/parti_zoltan.hpp>
22
23#include <control/domain/domain_control.hpp>
24
25namespace FEAT
26{
27 namespace Control
28 {
29 namespace Domain
30 {
40 template<typename DomainLevel_>
42 public Control::Domain::DomainControl<DomainLevel_>
43 {
44 public:
48 using typename BaseClass::LevelType;
50 using typename BaseClass::LayerType;
52 using typename BaseClass::MeshType;
54 using typename BaseClass::AtlasType;
58 typedef typename LevelType::PartType MeshPartType;
59
62
67 {
68 public:
70 int layer;
78 int desired_level_max, desired_level_min;
79
82
91
96
105
116
117 Ancestor() :
118 layer(0), layer_p(0), num_procs(0), num_parts(0),
119 desired_level_max(0), desired_level_min(0),
122 progeny_comm(),
123 parti_found(false),
124 parti_info(),
125 parti_level(0),
126 parti_apriori(false),
128 {
129 }
130 }; // class Ancestor
131
132 protected:
137
146
149
151 std::deque<std::pair<int,int>> _desired_levels;
152
154 std::deque<std::pair<int,int>> _chosen_levels;
155
162
164 std::deque<Ancestor> _ancestry;
165
166 public:
176 explicit PartiDomainControlBase(const Dist::Comm& comm_, bool support_multi_layered) :
177 BaseClass(comm_),
178 _was_created(false),
179 _adapt_mode(Geometry::AdaptMode::chart),
180 _allow_parti_metis(false),
181 _allow_parti_genetic(false), // this one is exotic
182 _allow_parti_naive(true),
183 _allow_parti_zoltan(false),
184 _support_multi_layered(support_multi_layered),
189 _ancestry()
190 {
191 }
192
195 {
196 }
197
210 virtual bool parse_args(SimpleArgParser& args)
211 {
212 XASSERTM(!_was_created, "This function has to be called before domain control creation!");
213 // Try to parse --parti-type <types...>
214 {
215 auto it = args.query("parti-type");
216 if(it != nullptr)
217 {
218 // disallow all strategies by default
219 this->_reset_parti_types();
220
221 // loop over all allowed strategies
222 for(const auto& t : it->second)
223 {
224 // give derived classes a chance first
225 if(!this->_parse_parti_type(t))
226 {
227 this->_comm.print("ERROR: unknown partitioner type '" + t + "'");
228 return false;
229 }
230 }
231 }
232 }
233
234 // parse --parti-rank-elems <count>
235 args.parse("parti-rank-elems", _required_elems_per_rank);
236
237 // parse --parti-genetic-time <time-init> <time-mutate>
238 args.parse("parti-genetic-time", _genetic_time_init, _genetic_time_mutate);
239
240 // okay
241 return true;
242 }
243
254 virtual bool parse_property_map(const PropertyMap& pmap)
255 {
256 XASSERTM(!_was_created, "This function has to be called before domain control creation!");
257 auto parti_type_p = pmap.query("parti-type");
258 if(parti_type_p.second)
259 {
260 // disallow all strategies by default
261 this->_reset_parti_types();
262
263 std::deque<String> allowed_partitioners = parti_type_p.first.split_by_whitespaces();
264
265 for(const auto& t : allowed_partitioners)
266 {
267 if(!this->_parse_parti_type(t))
268 {
269 this->_comm.print("ERROR: unknown partitioner type '" + t + "'");
270 return false;
271 }
272 }
273 }
274
275 auto parti_rank_elems_p = pmap.query("parti-rank-elems");
276 if(parti_rank_elems_p.second)
277 {
278 if(!parti_rank_elems_p.first.parse(_required_elems_per_rank))
279 {
280 this->_comm.print("ERROR: Failed to parse 'parti-rank-elems'");
281 return false;
282 }
283 }
284
285 auto genetic_time_init_p = pmap.query("parti-genetic-time-init");
286 if(genetic_time_init_p.second)
287 {
288 if(!genetic_time_init_p.first.parse(_genetic_time_init))
289 {
290 this->_comm.print("ERROR: Failed to parse 'parti-genetic-time-init'");
291 return false;
292 }
293 }
294
295 auto genetic_time_mutate_p = pmap.query("parti-genetic-time-mutate");
296 if(genetic_time_mutate_p.second)
297 {
298 if(!genetic_time_mutate_p.first.parse(_genetic_time_mutate))
299 {
300 this->_comm.print("ERROR: Failed to parse 'parti-genetic-time-mutate'");
301 return false;
302 }
303 }
304
305 return true;
306 }
307
315 {
316 XASSERTM(!_was_created, "This function has to be called before domain control creation!");
317 _adapt_mode = adapt_mode;
318 }
319
327 {
328 return _adapt_mode;
329 }
330
337 void set_desired_levels(const String& slvls)
338 {
339 std::deque<String> sl = slvls.split_by_whitespaces();
341 }
342
351 virtual void set_desired_levels(const std::deque<String>& slvls)
352 {
353 XASSERTM(!_was_created, "This function has to be called before domain control creation!");
354
355 const int nranks = this->_comm.size();
356 std::deque<String> sv;
357
358 for(std::size_t i(0); i < slvls.size(); ++i)
359 {
360 int ilvl = -1, nprocs = -1;
361 sv = slvls.at(i).split_by_string(":");
362
363 if((sv.size() < std::size_t(1)) || (sv.size() > std::size_t(2)))
364 throw ParseError("Invalid input format", slvls.at(i), "an int-pair 'level:patches'");
365
366 if(!sv.front().parse(ilvl))
367 throw ParseError("Failed to parse level index", slvls.at(i), "an integer");
368 if((sv.size() > std::size_t(1)) && !sv.back().parse(nprocs))
369 throw ParseError("Failed to parse process count" , slvls.at(i), "an integer");
370
371 // level must be non-negative
372 if(ilvl < 0)
373 throw ParseError("Invalid negative level index", slvls.at(i), "a non-negative level index");
374
375 // first level index?
376 if(i == std::size_t(0))
377 {
378 // if the process count is given, it must match the communicator size
379 if((nprocs >= 0) && (nprocs != this->_comm.size()))
380 throw ParseError("Invalid number of processes for global level: '" + slvls.at(i) +
381 "', expected " + stringify(this->_comm.size()) + " but got " + stringify(nprocs));
382 _desired_levels.push_back(std::make_pair(ilvl, nranks));
383 continue;
384 }
385
386 // make sure the level parameter is non-ascending
387 if(_desired_levels.back().first < ilvl)
388 throw ParseError("Invalid non-descending level index: '" + slvls.at(i) +
389 "', expected <= " + stringify(_desired_levels.back().first) + " but got " + stringify(ilvl));
390
391 // ignore process count for last level
392 if((i + 1) == slvls.size())
393 nprocs = 0;
394 // if the number of processes is identical to the previous one, simply ignore this parameter
395 else if(_desired_levels.back().second == nprocs)
396 continue;
397 // the process count must not be ascending
398 else if(_desired_levels.back().second < nprocs)
399 throw ParseError("Invalid non-descending process count: '" + slvls.at(i) +
400 "', expected < " + stringify(_desired_levels.back().second) + " but got " + stringify(nprocs));
401 // the previous process count must be a multiple of the new one
402 else if(_desired_levels.back().second % nprocs != 0)
403 throw ParseError("Invalid indivisible process count: '" + slvls.at(i) +
404 "', expected a divisor of " + stringify(_desired_levels.back().second) + " but got " + stringify(nprocs));
405
406 // push the level-proc pair
407 _desired_levels.push_back(std::make_pair(ilvl, nprocs));
408 }
409 }
410
421 virtual void set_desired_levels(int lvl_max, int lvl_min = -1)
422 {
423 XASSERTM(!_was_created, "This function has to be called before domain control creation!");
424
425 // single-layered hierarchy
426 int _desired_level_max = lvl_max;
427 int _desired_level_min = (lvl_min >= 0 ? lvl_min : lvl_max + lvl_min + 1);
428
429 XASSERTM(_desired_level_max >= 0, "Invalid level-max");
430 XASSERTM(_desired_level_min >= 0, "Invalid level-min");
431 XASSERTM(_desired_level_max >= _desired_level_min, "Invalid level-min/max combination");
432
433 _desired_levels.emplace_back(std::make_pair(_desired_level_max, this->_comm.size()));
434 _desired_levels.emplace_back(std::make_pair(_desired_level_min, 0));
435 }
436
451 virtual void set_desired_levels(int lvl_max, int lvl_med, int lvl_min)
452 {
453 XASSERTM(!_was_created, "This function has to be called before domain control creation!");
454
455 // double-layered hierarchy
456 int _desired_level_max = lvl_max;
457 int _desired_level_med = (lvl_med >= 0 ? lvl_med : lvl_max + lvl_med + 1);
458 int _desired_level_min = (lvl_min >= 0 ? lvl_min : lvl_med + lvl_min + 1);
459
460 XASSERTM(_desired_level_max >= 0, "Invalid level-max");
461 XASSERTM(_desired_level_med >= 0, "Invalid level-med");
462 XASSERTM(_desired_level_min >= 0, "Invalid level-min");
463
464 // level_max must be strictly greater than level_med
465 XASSERTM(_desired_level_max > _desired_level_med, "Invalid level-max/med combination");
466
467 // level_med must be greater or equal level_min
468 XASSERTM(_desired_level_med >= _desired_level_min, "Invalid level-med/min combination");
469
470 _desired_levels.emplace_back(std::make_pair(_desired_level_max, this->_comm.size()));
471 _desired_levels.emplace_back(std::make_pair(_desired_level_med, 1));
472 _desired_levels.emplace_back(std::make_pair(_desired_level_min, 0));
473 }
474
481 {
482 return _desired_levels.front().first;
483 }
484
491 {
492 return _desired_levels.back().first;
493 }
494
503 {
504 String s;
505 for(std::size_t i(0); (i + 1) < _desired_levels.size(); ++i)
506 {
507 s += stringify(_desired_levels.at(i).first);
508 s += ":";
509 s += stringify(_desired_levels.at(i).second);
510 s += " ";
511 }
512 s += stringify(_desired_levels.back().first);
513 return s;
514 }
515
524 {
525 String s;
526 for(std::size_t i(0); (i + 1) < _chosen_levels.size(); ++i)
527 {
528 s += stringify(_chosen_levels.at(i).first);
529 s += ":";
530 s += stringify(_chosen_levels.at(i).second);
531 s += " ";
532 }
533 s += stringify(_chosen_levels.back().first);
534 return s;
535 }
536
550 {
551 String s;
552
553 for(auto it = this->_ancestry.rbegin(); it != this->_ancestry.rend(); ++it)
554 {
555 if(it != this->_ancestry.rbegin())
556 s += "\n";
557 s += it->parti_info;
558 s += " on level ";
559 s += stringify(it->parti_level);
560 s += " for ";
561 s += stringify(it->num_parts);
562 s += (it->num_parts > 1 ? " patches on " : " patch on ");
563 s += stringify(it->num_procs);
564 s += (it->num_procs > 1 ? " processes" : " process");
565 }
566
567 return s;
568 }
569
575 const std::deque<std::pair<int, int>> get_desired_levels() const
576 {
577 return this->_desired_levels;
578 }
579
585 const std::deque<std::pair<int, int>> get_chosen_levels() const
586 {
587 return this->_chosen_levels;
588 }
589
594 {
595 String s;
596 for(auto it = this->_ancestry.begin(); it != this->_ancestry.end(); ++it)
597 {
598 if(it != this->_ancestry.begin())
599 s += " | ";
600 s += stringify((*it).num_procs);
601 s += ":";
602 s += stringify((*it).num_parts);
603 s += "[";
604 s += stringify((*it).progeny_group).pad_front(2);
605 s += "+";
606 s += stringify((*it).progeny_child);
607 s += ":";
608 s += stringify((*it).progeny_first).pad_front(2);
609 s += "+";
610 s += stringify((*it).progeny_count);
611 s += ".";
612 s += stringify((*it).progeny_comm.rank());
613 s += "]";
614 s += "(";
615 s += stringify((*it).desired_level_max);
616 s += ":";
617 s += stringify((*it).desired_level_min);
618 s += ")";
619 s += ((*it).layer >= 0 ? "*" : " ");
620 s += ((*it).layer_p >= 0 ? ">" : " ");
621 }
622 return s;
623 }
624
628 const std::deque<Ancestor>& get_ancestry() const
629 {
630 return this->_ancestry;
631 }
632
636 std::vector<char> serialize_partitioning() const
637 {
638 BinaryStream bs;
639 typedef std::uint64_t u64;
640
641 // serialization magic number: "F3PaDoCo"
642 const std::uint64_t magic = 0x6F436F4461503346;
643
644 XASSERT(this->_num_global_layers >= this->_ancestry.size());
645
646 if(this->_comm.rank() == 0)
647 {
648 // dump magic
649 bs.write((const char*)&magic, 8u);
650
651 // write finest level
652 const u64 lev = u64(this->_virt_levels.front()->get_level_index());
653 bs.write((const char*)&lev, 8u);
654
655 // serialize ancestry/layers
656 const u64 na = u64(this->_ancestry.size());
657 bs.write((const char*)&na, 8u);
658
659 // write number of ranks per layer in reverse order
660 for(auto it = this->_ancestry.rbegin(); it != this->_ancestry.rend(); ++it)
661 {
662 // write ranks
663 const u64 np = u64(it->num_procs);
664 const u64 pl = u64(it->parti_level);
665 bs.write((const char*)&np, 8u);
666 bs.write((const char*)&pl, 8u);
667 }
668 }
669
670 // loop over all ancestry graphs
671 for(auto it = this->_ancestry.rbegin(); it != this->_ancestry.rend(); ++it)
672 {
673 if(it == this->_ancestry.rbegin())
674 {
675 std::vector<char> buf = it->parti_graph.serialize();
676 bs.write(buf.data(), std::streamsize(buf.size()));
677 continue;
678 }
679
680 // get layer index
681 if(it->layer_p < 0)
682 continue;
683
684 // get parent layer communicator
685 const std::size_t ilp = std::size_t(it->layer_p);
686 XASSERT(ilp < this->_layers.size());
687 const Dist::Comm& comm_p = this->_layers.at(ilp)->comm();
688
689 // serialize graph
690 std::vector<char> buf = it->parti_graph.serialize();
691
692 // choose maximum size
693 u64 buf_size = buf.size();
694
695 // gather individual buffer sizes on rank 0
696 std::vector<u64> all_sizes;
697 if(comm_p.rank() == 0)
698 all_sizes.resize(std::size_t(comm_p.size()));
699 comm_p.gather(&buf_size, 1u, all_sizes.data(), 1u, 0);
700
701 // allreduce maximum size
702 comm_p.allreduce(&buf_size, &buf_size, std::size_t(1), Dist::op_max);
703
704 // adjust buffer size
705 if(buf_size > u64(buf.size()))
706 buf.resize(buf_size);
707
708 // on rank 0, allocate common buffer
709 std::vector<char> com_buf;
710 if(comm_p.rank() == 0)
711 com_buf.resize(std::size_t(buf_size * u64(comm_p.size())));
712
713 // gather all buffers on rank 0
714 comm_p.gather(buf.data(), buf_size, com_buf.data(), buf_size, 0);
715
716 // write each individual buffer
717 if(comm_p.rank() == 0)
718 {
719 char* x = com_buf.data();
720 for(u64 k(0); k < u64(comm_p.size()); ++k)
721 bs.write(&x[k*buf_size], std::streamsize(all_sizes[k]));
722 }
723 }
724
725 return bs.container();
726 }
727
728 protected:
735 virtual void _reset_parti_types()
736 {
739 }
740
749 virtual bool _parse_parti_type(const String& type)
750 {
751 if((type.compare_no_case("metis") == 0) || (type.compare_no_case("parmetis") == 0))
752 return _allow_parti_metis = true;
753 else if(type.compare_no_case("genetic") == 0)
754 return _allow_parti_genetic = true;
755 else if(type.compare_no_case("naive") == 0)
756 return _allow_parti_naive = true;
757 else if(type.compare_no_case("zoltan") == 0)
758 return _allow_parti_zoltan = true;
759 else
760 return false;
761 }
762
773 virtual std::vector<WeightType> _compute_weights(Ancestor& DOXY(ancestor), const MeshNodeType& DOXY(base_mesh_node))
774 {
775 return std::vector<WeightType>();
776 }
777
782 {
783 // for more than 2 desired levels, call _create_ancestry_scattered
784 XASSERTM(this->_desired_levels.size() <= std::size_t(2), "multi-layered control is desired here");
785
786 // allocate and create ancestry
787 this->_ancestry.resize(std::size_t(1));
788 Ancestor& ancestor = this->_ancestry.front();
789
790 // set the layer index to 0
791 ancestor.layer = 0;
792
793 // set the parent layer index to -1
794 ancestor.layer_p = -1;
795
796 // set the total number of processes for this layer
797 ancestor.num_procs = this->_comm.size();
798
799 // set the total number of partitions per progeny group
800 ancestor.num_parts = this->_comm.size();
801
802 // set desired maximum level
803 ancestor.desired_level_max = this->_desired_levels.front().first;
804
805 // set desired minimum level (may be = level_max)
806 ancestor.desired_level_min = this->_desired_levels.back().first;
807
808 // set the progeny group
809 ancestor.progeny_group = 0;
810 ancestor.progeny_child = this->_comm.rank();
811
812 // create the progeny communicator
813 ancestor.progeny_count = this->_comm.size();
814 ancestor.progeny_first = 0;
815 ancestor.progeny_comm = this->_comm.comm_dup();
816 }
817
818 // Note: all following member functions are only required for parallel builds,
819 // so we enclose them in the following #if-block to reduce compile times.
820
821#if defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
822
827 {
828 // create and push global layer
829 this->push_layer(std::make_shared<LayerType>(this->_comm.comm_dup(), 0));
830
831 // loop over all desired layers
832 for(std::size_t ilay(1u); (ilay+1u) < this->_desired_levels.size(); ++ilay)
833 {
834 // get child layer
835 std::shared_ptr<LayerType> layer_c = this->_layers.back();
836
837 // get child layer communicator
838 const Dist::Comm& comm_c = layer_c->comm();
839
840 // get number of processes in child comm
841 const int nprocs_c = comm_c.size();
842
843 // get desired number of processes for parent comm
844 const int nprocs_p = this->_desired_levels.at(ilay).second;
845 XASSERT(nprocs_p < nprocs_c);
846 XASSERT(nprocs_p > 0);
847 XASSERT(nprocs_c % nprocs_p == 0); // same number of children for each parent
848
849 // compute number of siblings = children per parent
850 const int num_sibs = nprocs_c / nprocs_p;
851
852 // get my rank in child comm
853 const int my_rank_c = comm_c.rank();
854
855 // compute my parent's rank in child comm
856 const int parent_rank_c = (my_rank_c / num_sibs) * num_sibs;
857
858 // create our sibling communicator and set the parent rank
859 layer_c->set_parent(comm_c.comm_create_range_incl(num_sibs, parent_rank_c), 0);
860
861 // next, create the actual parent communicator
862 Dist::Comm comm_p = comm_c.comm_create_range_incl(nprocs_p, 0, num_sibs);
863
864 // Are we the parent?
865 if(parent_rank_c == my_rank_c)
866 {
867 // make sure we have a valid communicator
868 XASSERT(!comm_p.is_null());
869
870 // push the parent layer
871 this->push_layer(std::make_shared<LayerType>(std::move(comm_p), int(ilay)));
872 }
873 else
874 {
875 // We are not a parent, so we must have received a null communicator
876 XASSERT(comm_p.is_null());
877
878 // Exit the loop, as we are not part of the party anymore...
879 break;
880 }
881 }
882 }
883
888 {
889 // the layers must have been created already
890 XASSERT(!this->_layers.empty());
891
892 // create a deque with the number of processes for each layer
893 std::deque<int> num_procs;
894 for(auto it = this->_desired_levels.begin(); it != this->_desired_levels.end(); ++it)
895 {
896 if(it->second > 1)
897 num_procs.push_back(it->second);
898 }
899 // manually add the base-layer
900 num_procs.push_back(1);
901
902 // allocate and create ancestry
903 this->_ancestry.resize(num_procs.size()-std::size_t(1));
904
905 // set up the ancestry
906 const int main_rank = this->_comm.rank();
907 const int main_size = this->_comm.size();
908 for(std::size_t i(0); i < this->_ancestry.size(); ++i)
909 {
910 // get our ancestor info
911 Ancestor& ancestor = this->_ancestry.at(i);
912
913 // set the layer index (or -1, if this process is not part of that layer)
914 ancestor.layer = (i < this->_layers.size() ? int(i) : -1);
915
916 // set the parent layer index (or -1, if this process is not in the parent layer)
917 ancestor.layer_p = ((i+1u) < this->_layers.size() ? int(i)+1 : -1);
918
919 // set the total number of processes for this layer
920 ancestor.num_procs = num_procs.at(i);
921
922 // set the total number of partitions per progeny group
923 ancestor.num_parts = num_procs.at(i) / num_procs.at(i+1u);
924
925 // set desired maximum level
926 XASSERT(i < this->_desired_levels.size());
927 ancestor.desired_level_max = this->_desired_levels.at(i).first;
928
929 // set desired minimum level
930 if((i+1u) < this->_desired_levels.size())
931 ancestor.desired_level_min = this->_desired_levels.at(i+1).first;
932 else
933 ancestor.desired_level_min = ancestor.desired_level_max;
934
935 // set the progeny group
936 ancestor.progeny_group = ((main_rank * num_procs.at(i+1u)) / main_size) * ancestor.num_parts;
937 ancestor.progeny_child = ((main_rank * num_procs.at(i)) / main_size) % ancestor.num_parts;
938
939 // create the progeny communicator
940 ancestor.progeny_count = main_size / num_procs.at(i+1u);
941 ancestor.progeny_first = (main_rank / ancestor.progeny_count) * ancestor.progeny_count;
942 ancestor.progeny_comm = this->_comm.comm_create_range_incl(ancestor.progeny_count, ancestor.progeny_first);
943 }
944 }
945
964 const Ancestor& ancestor,
965 const MeshNodeType& base_mesh_node,
966 MeshNodeType& patch_mesh_node,
967 std::vector<int>& neighbor_ranks)
968 {
969 // get the map of the base-mesh halos
970 const std::map<int, std::unique_ptr<MeshPartType>>& base_halo_map = base_mesh_node.get_halo_map();
971
972 // if the base mesh has no halos, then we can jump out of here
973 if(base_halo_map.empty())
974 return;
975
976 // get number of halos
977 const std::size_t num_halos = base_halo_map.size();
978
979 // create a halo splitter
980 Geometry::PatchHaloSplitter<MeshType> halo_splitter(*base_mesh_node.get_mesh(),
981 *base_mesh_node.get_patch(ancestor.progeny_child));
982
983 // add each base-mesh halo to our halo splitter and store the resulting split data size
984 std::vector<int> halo_ranks;
985 std::vector<std::size_t> halo_sizes;
986 for(auto it = base_halo_map.begin(); it != base_halo_map.end(); ++it)
987 {
988 // store halo rank
989 halo_ranks.push_back(it->first);
990
991 // try to split the halo and store the resulting data size
992 halo_sizes.push_back(halo_splitter.add_halo(it->first, *it->second));
993 }
994
995 // This vector will receive the split halo data from all our potential neighbor processes
996 std::vector<Index> halo_recv_data;
997 std::vector<std::vector<Index>> halo_send_data;
998
999 /* ******************************************************************************************************* */
1000 /* ******************************************************************************************************* */
1001 // PHASE I: collect halo data from our siblings
1002 /* ******************************************************************************************************* */
1003 /* ******************************************************************************************************* */
1004 if(ancestor.layer >= 0)
1005 {
1006 XASSERT(this->_layers.size() > std::size_t(ancestor.layer));
1007 const LayerType& layer = *this->_layers.at(std::size_t(ancestor.layer));
1008 const Dist::Comm& sibling_comm = *layer.sibling_comm_ptr();
1009 XASSERT(!sibling_comm.is_null());
1010 const std::size_t num_sibls = std::size_t(sibling_comm.size());
1011
1012 // get the rank of this process in various communicators
1013 const int sibl_rank = sibling_comm.rank();
1014 const int layer_rank = layer.comm().rank();
1015
1016 // gather halo infos of all siblings at sibling rank 0
1017 std::vector<std::size_t> sibl_halo_sizes(sibl_rank == 0 ? num_halos*num_sibls : 0u);
1018 sibling_comm.gather(halo_sizes.data(), halo_sizes.size(), sibl_halo_sizes.data(), halo_sizes.size(), 0);
1019
1020 if(sibl_rank > 0)
1021 {
1022 std::vector<std::vector<Index>> halo_split_data(num_halos);
1023 Dist::RequestVector send_reqs(num_halos);
1024
1025 // serialize all split halos
1026 for(std::size_t i(0); i < num_halos; ++i)
1027 {
1028 // skip empty halos
1029 if(halo_sizes.at(i) == Index(0))
1030 continue;
1031
1032 // serialize split halo data
1033 halo_split_data.at(i) = halo_splitter.serialize_split_halo(halo_ranks[i], layer_rank);
1034 XASSERT(halo_split_data.at(i).size() == halo_sizes.at(i));
1035
1036 // send split data over to our parent process
1037 send_reqs[i] = sibling_comm.isend(halo_split_data.at(i).data(), halo_sizes.at(i), 0);
1038 }
1039
1040 // wait for all pending sends to finish
1041 send_reqs.wait_all();
1042 }
1043 else // if(sibl_rank == 0)
1044 {
1045 halo_send_data.resize(num_halos);
1046
1047 // compute halo send data sizes
1048 for(std::size_t i(0); i < num_halos; ++i)
1049 {
1050 // determine the number of child processes for this halo
1051 // as well as the required send buffer size
1052 Index num_halo_childs = 0u;
1053 std::size_t buffer_size = 0u, offset = 0u;
1054 for(std::size_t j(0); j < num_sibls; ++j)
1055 {
1056 // update required buffer size
1057 buffer_size += sibl_halo_sizes.at(j*num_halos + i);
1058
1059 // this sibling is a child if its split halo is not empty
1060 if(sibl_halo_sizes.at(j*num_halos + i) > std::size_t(0))
1061 ++num_halo_childs;
1062 }
1063
1064 // increase buffer size to store child data offsets
1065 buffer_size += num_halo_childs + Index(1);
1066
1067 // allocate send buffer and get a pointer to its data array
1068 halo_send_data.at(i).resize(buffer_size);
1069 Index* halo_buffer = halo_send_data.at(i).data();
1070
1071 // store child count as first entry of buffer
1072 halo_buffer[0u] = num_halo_childs;
1073
1074 // initialize offset for first child
1075 offset = num_halo_childs + Index(1);
1076 Index coi = 0u; // child offset index
1077
1078 // collect my own split halo
1079 if(sibl_halo_sizes.at(i) > Index(0))
1080 {
1081 std::vector<Index> my_data(halo_splitter.serialize_split_halo(halo_ranks[i], layer_rank));
1082 std::size_t data_size = sibl_halo_sizes.at(i);
1083 halo_buffer[++coi] = Index(offset);
1084 for(std::size_t k(0); k < data_size; ++k)
1085 halo_buffer[offset+k] = my_data[k];
1086 offset += my_data.size();
1087 }
1088
1089 Dist::RequestVector sibl_recv_reqs(num_sibls);
1090
1091 // collect the other siblings
1092 for(std::size_t j(1); j < num_sibls; ++j)
1093 {
1094 // skips all sibling with empty halos
1095 std::size_t data_size = sibl_halo_sizes.at(j*num_halos + i);
1096 if(data_size == std::size_t(0))
1097 continue;
1098 XASSERT(offset+data_size <= buffer_size);
1099
1100 // store offset for this child
1101 halo_buffer[++coi] = Index(offset);
1102
1103 // receive serialized data from this sibling
1104 sibl_recv_reqs[j] = sibling_comm.irecv(&halo_buffer[offset], data_size, int(j));
1105 offset += data_size;
1106 }
1107 XASSERT(offset == buffer_size);
1108
1109 // wait for all pending receives to finish
1110 sibl_recv_reqs.wait_all();
1111 }
1112 }
1113 } // if(ancestor.layer >= 0)
1114
1115 /* ******************************************************************************************************* */
1116 /* ******************************************************************************************************* */
1117 // PHASE II: exchange split halos over parent layer communicator
1118 /* ******************************************************************************************************* */
1119 /* ******************************************************************************************************* */
1120
1121 if(ancestor.layer_p >= 0)
1122 {
1123 XASSERT(!halo_send_data.empty());
1124
1125 // get the parent layer communicator
1126 const Dist::Comm& parent_comm = this->_layers.at(std::size_t(ancestor.layer_p))->comm();
1127
1128 std::vector<std::size_t> halo_recv_sizes(num_halos), halo_send_sizes(num_halos);
1129 Dist::RequestVector halo_recv_reqs(num_halos), halo_send_reqs(num_halos);
1130
1131 // exchange halo send data sizes
1132 for(std::size_t i(0); i < num_halos; ++i)
1133 {
1134 // get halo send size
1135 halo_send_sizes.at(i) = halo_send_data.at(i).size();
1136
1137 // post receive requests
1138 halo_recv_reqs[i] = parent_comm.irecv(&halo_recv_sizes[i], std::size_t(1), halo_ranks[i]);
1139
1140 // post send requests
1141 halo_send_reqs[i] = parent_comm.isend(&halo_send_sizes[i], std::size_t(1), halo_ranks[i]);
1142 }
1143
1144 // wait for sends and receives to finish
1145 halo_recv_reqs.wait_all();
1146 halo_send_reqs.wait_all();
1147
1148 // compute total receive buffer size
1149 std::size_t recv_buf_size = num_halos + std::size_t(1);
1150 for(std::size_t i(0); i < num_halos; ++i)
1151 recv_buf_size += halo_recv_sizes[i];
1152
1153 // allocate receive buffer
1154 halo_recv_data.resize(recv_buf_size);
1155
1156 // set up receive data pointers
1157 halo_recv_data[0] = Index(num_halos) + Index(1);
1158 for(std::size_t i(0); i < num_halos; ++i)
1159 halo_recv_data[i+1u] = Index(halo_recv_data[i] + halo_recv_sizes[i]);
1160
1161 // allocate receive buffers and post receives
1162 for(std::size_t i(0); i < num_halos; ++i)
1163 {
1164 // resize buffer and post receive
1165 halo_recv_reqs[i] = parent_comm.irecv(&halo_recv_data[halo_recv_data[i]], halo_recv_sizes.at(i), halo_ranks[i]);
1166
1167 // post send of actual halo buffer
1168 halo_send_reqs[i] = parent_comm.isend(halo_send_data.at(i).data(), halo_send_sizes.at(i), halo_ranks[i]);
1169 }
1170
1171 // wait for sends and receives to finish
1172 halo_recv_reqs.wait_all();
1173 halo_send_reqs.wait_all();
1174 } // if(ancestor.layer_p >= 0)
1175
1176 /* ******************************************************************************************************* */
1177 /* ******************************************************************************************************* */
1178 // PHASE III: broadcast halo receive data over progeny comm
1179 /* ******************************************************************************************************* */
1180 /* ******************************************************************************************************* */
1181
1182 {
1183 // broadcast receive buffer size
1184 std::size_t recv_data_size = halo_recv_data.size();
1185 ancestor.progeny_comm.bcast(&recv_data_size, std::size_t(1), 0);
1186
1187 // allocate buffer
1188 if(ancestor.progeny_comm.rank() != 0)
1189 {
1190 XASSERT(halo_recv_data.empty()); // should be empty until now
1191 halo_recv_data.resize(recv_data_size);
1192 }
1193 else
1194 {
1195 XASSERT(!halo_recv_data.empty()); // must not be empty
1196 }
1197
1198 // broadcast buffer
1199 ancestor.progeny_comm.bcast(halo_recv_data.data(), recv_data_size, 0);
1200 }
1201
1202 /*{
1203 String s;
1204 for(std::size_t i(0); i < num_halos; ++i)
1205 {
1206 s += stringify(halo_ranks[i]);
1207 s += " >";
1208 for(Index j(halo_recv_data[i]); j < halo_recv_data[i+1]; ++j)
1209 (s += " ") += stringify(halo_recv_data[j]);
1210 s += "\n";
1211 }
1212 this->_comm.allprint(s);
1213 }*/
1214
1215 /* ******************************************************************************************************* */
1216 /* ******************************************************************************************************* */
1217 // PHASE IV: intersect received halo splits
1218 /* ******************************************************************************************************* */
1219 /* ******************************************************************************************************* */
1220
1221 for(std::size_t i(0); i < num_halos; ++i)
1222 {
1223 // get the offset of the first date for this halo
1224 const Index offset = halo_recv_data.at(i);
1225
1226 // get the number of child processes for this halo
1227 const Index num_childs = halo_recv_data.at(offset);
1228
1229 // loop over all child processes
1230 for(Index j(0); j < num_childs; ++j)
1231 {
1232 // compute the offset of this child's data within the large buffer
1233 const Index buffer_offset = offset + halo_recv_data.at(offset+j+1u);
1234
1235 // intersect with our other halo
1236 if(!halo_splitter.intersect_split_halo(halo_ranks[i], halo_recv_data, buffer_offset))
1237 continue; // no intersection
1238
1239 // get the new neighbor's rank
1240 const int neighbor_rank = int(halo_recv_data.at(buffer_offset));
1241
1242 // add the new neighbor to our list
1243 neighbor_ranks.push_back(neighbor_rank);
1244
1245 // create new mesh-part
1246 patch_mesh_node.add_halo(neighbor_rank, halo_splitter.make_unique());
1247 }
1248 }
1249 }
1250
1276 virtual bool _check_parti(Ancestor& ancestor, const MeshNodeType& mesh_node, bool DOXY(is_base_layer))
1277 {
1278 // We need to determine the required partitioning level for a-posteriori partitioning.
1279 // For this, first determine the factor by which the number of elements increases for each refinement:
1280 const Index factor = Index(Geometry::Intern::StandardRefinementTraits<typename BaseClass::ShapeType, BaseClass::ShapeType::dimension>::count);
1281
1282 // compute minimum number of elements for a-posteriori partitioning
1283 const Index min_elems = Index(ancestor.num_parts) * Index(_required_elems_per_rank);
1284
1285 // Okay, get the number of elements on base-mesh level 0
1286 Index num_elems = mesh_node.get_mesh()->get_num_elements();
1287
1288 // compute refinement level on which we have enough elements
1289 int level = 0;
1290 for(; num_elems < min_elems; ++level)
1291 {
1292 num_elems *= factor;
1293 }
1294
1295 // assume that we did not find a partitioning yet
1296 ancestor.parti_found = false;
1297
1298 // finally, the user may have ancestor a greater level for partitioning:
1299 ancestor.parti_level = Math::max(ancestor.parti_level, level);
1300
1301 // No a-priori partitioning; try a-posteriori partitioner later
1302 return false;
1303 }
1304
1317 virtual bool _apply_parti(Ancestor& ancestor, /*const*/ MeshNodeType& base_mesh_node)
1318 {
1319 // compute weights
1320 std::vector<WeightType> weights = this->_compute_weights(ancestor, base_mesh_node);
1321
1322 // First of all, check whether an a-priori partitioning was selected;
1323 // if so, then we do not have to apply any a-posteriori partitioner
1324 if(weights.empty() && ancestor.parti_apriori)
1325 return true;
1326
1327 // ensure that the mesh has enough elements
1328 //XASSERT(ancestor.num_parts <= int(base_mesh_node.get_mesh()->get_num_elements()));
1329 if(base_mesh_node.get_mesh()->get_num_elements() < Index(ancestor.num_parts))
1330 return false;
1331
1332 // try the various a-posteriori partitioners
1333 if(this->_apply_parti_metis(ancestor, base_mesh_node, weights))
1334 return true;
1335 if(this->_apply_parti_zoltan(ancestor, base_mesh_node, weights))
1336 return true;
1337 if(this->_apply_parti_genetic(ancestor, base_mesh_node, weights))
1338 return true;
1339 if(this->_apply_parti_naive(ancestor, base_mesh_node, weights))
1340 return true;
1341
1342 // we should not arrive here...
1343 return false;
1344 }
1345
1361 bool _apply_parti_zoltan(Ancestor& ancestor, const MeshNodeType& base_mesh_node, const std::vector<WeightType>& weights)
1362 {
1363#ifdef FEAT_HAVE_ZOLTAN
1364 // is this even allowed?
1365 if(!this->_allow_parti_zoltan)
1366 return false;
1367
1368 // create partitioner on the corresponding progeny communicator
1369 Geometry::PartiZoltan partitioner(ancestor.progeny_comm);
1370
1371 // get the corresponding adjacency graph for partitioning
1372 constexpr int shape_dim = MeshNodeType::MeshType::ShapeType::dimension;
1373 const auto& faces_at_elem = base_mesh_node.get_mesh()->template get_index_set<shape_dim, 0>();
1374
1375 // call the partitioner
1376 if(!partitioner.execute(faces_at_elem, Index(ancestor.num_parts), weights))
1377 return false;
1378
1379 // create partition graph
1380 ancestor.parti_graph = partitioner.build_elems_at_rank();
1381
1382 // set info string
1383 ancestor.parti_info = String("Applied Zoltan partitioner");
1384
1385 // partitioning found
1386 ancestor.parti_found = true;
1387
1388 // okay
1389 return true;
1390#else // FEAT_HAVE_ZOLTAN
1391 (void)ancestor;
1392 (void)base_mesh_node;
1393 (void)weights;
1394 return false;
1395#endif // FEAT_HAVE_ZOLTAN
1396 }
1397
1413 bool _apply_parti_metis(Ancestor& ancestor, const MeshNodeType& base_mesh_node, const std::vector<WeightType>& weights)
1414 {
1415#ifdef FEAT_HAVE_PARMETIS
1416 // is this even allowed?
1417 if(!this->_allow_parti_metis)
1418 return false;
1419
1420 // create partitioner on the corresponding progeny communicator
1421 Geometry::PartiParMETIS partitioner(ancestor.progeny_comm);
1422
1423 // get the corresponding adjacency graph for partitioning
1424 constexpr int shape_dim = MeshNodeType::MeshType::ShapeType::dimension;
1425 const auto& faces_at_elem = base_mesh_node.get_mesh()->template get_index_set<shape_dim, 0>();
1426
1427 // get vertices-at-element and vertex set
1428 const auto& verts_at_elem = base_mesh_node.get_mesh()->template get_index_set<shape_dim, 0>();
1429 const auto& vertex_set = base_mesh_node.get_mesh()->get_vertex_set();
1430
1431 // call the partitioner
1432 if(!partitioner.execute(faces_at_elem, verts_at_elem, vertex_set, Index(ancestor.num_parts), weights))
1433 return false;
1434
1435 // create partition graph
1436 ancestor.parti_graph = partitioner.build_elems_at_rank();
1437
1438 // set info string
1439 ancestor.parti_info = String("Applied ParMETIS partitioner");
1440
1441 // partitioning found
1442 ancestor.parti_found = true;
1443
1444 // okay
1445 return true;
1446#else
1447 (void)ancestor;
1448 (void)base_mesh_node;
1449 (void)weights;
1450 return false;
1451#endif // FEAT_HAVE_PARMETIS
1452 }
1453
1466 bool _apply_parti_genetic(Ancestor& ancestor, /*const*/ MeshNodeType& base_mesh_node, const std::vector<WeightType>& weights)
1467 {
1468 // is this even allowed?
1469 if(!this->_allow_parti_genetic || !weights.empty())
1470 return false;
1471
1472 // create a genetic partitioner
1474 *base_mesh_node.get_mesh(),
1475 ancestor.progeny_comm,
1476 (Index)ancestor.num_parts,
1477 this->_genetic_time_init,
1478 this->_genetic_time_mutate);
1479
1480 // create elems-at-rank graph
1481 ancestor.parti_graph = partitioner.build_elems_at_rank();
1482
1483 // set info string
1484 ancestor.parti_info = String("Applied genetic partitioner");
1485
1486 // partitioning found
1487 ancestor.parti_found = true;
1488
1489 // okay
1490 return true;
1491 }
1492
1508 bool _apply_parti_naive(Ancestor& ancestor, const MeshNodeType& mesh_node, const std::vector<WeightType>& weights)
1509 {
1510 // is this even allowed?
1511 if(!this->_allow_parti_naive)
1512 return false;
1513
1514 const Index num_parts = Index(ancestor.num_parts);
1515 const Index num_elems = mesh_node.get_mesh()->get_num_elements();
1516 XASSERTM(num_parts <= num_elems, "Base-Mesh does not have enough elements");
1517
1518 // create elems-at-rank graph
1519 ancestor.parti_graph = Adjacency::Graph(num_parts, num_elems, num_elems);
1520 Index* ptr = ancestor.parti_graph.get_domain_ptr();
1521 Index* idx = ancestor.parti_graph.get_image_idx();
1522
1523 // weighted partitioning?
1524 // If num_elems == num_parts, we have to assign one element to each part, independent of weight.
1525 if(!weights.empty() && num_elems > num_parts)
1526 {
1527 // compute total number of non-zero weight elements
1528 WeightType weight_sum(0.0);
1529 for(auto w : weights)
1530 {
1531 weight_sum += w;
1532 }
1533
1534 ptr[0] = Index(0);
1535 WeightType weight_count(0.);
1536 for(Index i(1), last(0); i < num_parts; ++i)
1537 {
1538 // add as many elements as we need to achieve the target weight for this partition
1539 // Set target_weight to at least weight_count. Otherwise we might produce an empty block
1540 // because a previous block already surpassed the new target_weight.
1541 WeightType target_weight = Math::max(weight_count, weight_sum * WeightType(i) / WeightType(num_parts));
1542 for(; (weight_count <= target_weight) && (last < num_elems); ++last)
1543 weight_count += weights[last];
1544
1545 ptr[i] = last;
1546 }
1547 ptr[num_parts] = num_elems;
1548 }
1549 else
1550 {
1551 // unweighted partitioning
1552 ptr[0] = Index(0);
1553 for(Index i(1); i < num_parts; ++i)
1554 ptr[i] = (i*num_elems) / num_parts;
1555 ptr[num_parts] = num_elems;
1556 }
1557
1558 for(Index j(0); j < num_elems; ++j)
1559 idx[j] = j;
1560
1561 // set info string
1562 ancestor.parti_info = String("Applied naive partitioner");
1563
1564 // partitioning found
1565 ancestor.parti_found = true;
1566
1567 // okay
1568 return true;
1569 }
1570#endif // defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
1571 }; // class HierarchDomainControl<...>
1572 } // namespace Domain
1573 } // namespace Control
1574} // namespace FEAT
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
FEAT Kernel base header.
Adjacency Graph implementation.
Definition: graph.hpp:34
Index * get_domain_ptr()
Returns the domain pointer array.
Definition: graph.hpp:359
Index * get_image_idx()
Returns the image node index array.
Definition: graph.hpp:374
Binary Stream class.
std::vector< char > & container()
Returns a reference to the internal vector container.
Domain control base-class template.
bool parti_apriori
specifies whether the chosen partitioning is an a-priori partitioning strategy
int desired_level_max
the desired minimum and maximum refinement levels for this layer
int parti_level
the refinement level on which the patch is to be partitioned
int progeny_first
First process of the progeny group of this process. Numbered w.r.t. the DomainControllers communicato...
String parti_info
a string containing some information about the chosen partitioning
bool parti_found
specifies whether a partitioning was found
int num_procs
the number of processes that participate in this layer
int progeny_count
Size of the progeny group of this process.
Adjacency::Graph parti_graph
this is the actual elements-at-rank partitioning graph
int layer_p
the index of the parent layer or -1, if this process is not part of the parent layer
int layer
the index of the layer that this ancestor object belongs to
int num_parts
the number of partitions for each patch of the parent layer
int progeny_child
Which direct child this process belongs to in its progeny group.
Base-Class for Hierarchical Domain Control implementations.
const std::deque< std::pair< int, int > > get_chosen_levels() const
Returns the deque of chosen refinement levels.
virtual String format_desired_levels() const
Returns the desired levels formatted as a parsable string.
int get_desired_level_min() const
Returns the desired minimum refinement level.
PartiDomainControlBase(const Dist::Comm &comm_, bool support_multi_layered)
Constructor.
virtual std::vector< WeightType > _compute_weights(Ancestor &ancestor, const MeshNodeType &base_mesh_node)
Computes the element partitioning weights.
virtual void _reset_parti_types()
Resets/disables all partitioner types.
void set_adapt_mode(Geometry::AdaptMode adapt_mode)
Sets the adapt-mode for refinement.
String dump_ancestry() const
Debugging function: Returns a string containing encoded ancestry information.
void set_desired_levels(const String &slvls)
Sets the desired levels for the partitioned hierarchy.
const std::deque< std::pair< int, int > > get_desired_levels() const
Returns the deque of desired refinement levels.
std::deque< std::pair< int, int > > _desired_levels
desired level deque
int _required_elems_per_rank
required number of elements per rank for a-posteriori partitioning
std::deque< std::pair< int, int > > _chosen_levels
chosen level deque
virtual void _create_ancestry_scattered()
Creates the layers for a multi-layered domain control in a scattered fashion.
Real WeightType
weight type for partitioner element weights; always Real
double _genetic_time_mutate
time for genetic partitioner mutation
std::deque< Ancestor > _ancestry
the partition ancestry deque
bool _apply_parti_zoltan(Ancestor &ancestor, const MeshNodeType &base_mesh_node, const std::vector< WeightType > &weights)
Applies the Zoltan partitioner onto the base-mesh.
bool _apply_parti_naive(Ancestor &ancestor, const MeshNodeType &mesh_node, const std::vector< WeightType > &weights)
Applies the naive partitioner onto the base-mesh.
bool _apply_parti_metis(Ancestor &ancestor, const MeshNodeType &base_mesh_node, const std::vector< WeightType > &weights)
Applies the METIS partitioner onto the base-mesh.
virtual bool parse_args(SimpleArgParser &args)
Parses the partitioner options from an argument parser.
bool _apply_parti_genetic(Ancestor &ancestor, MeshNodeType &base_mesh_node, const std::vector< WeightType > &weights)
Applies the genetic partitioner onto the base-mesh.
Geometry::RootMeshNode< MeshType > MeshNodeType
our root mesh node type
const std::deque< Ancestor > & get_ancestry() const
Returns a const reference to the ancestry.
std::vector< char > serialize_partitioning() const
Serializes the partitioning information into a binary buffer.
virtual bool _parse_parti_type(const String &type)
Parses a partitioner type.
virtual String format_chosen_levels() const
Returns the chosen levels formatted as a parsable string.
virtual void set_desired_levels(int lvl_max, int lvl_med, int lvl_min)
Sets the desired refinement levels for a double-layered hierarchy.
LevelType::PartType MeshPartType
our mesh-part type
Geometry::AdaptMode get_adapt_mode() const
Gets the adapt-mode for refinement.
virtual void _create_ancestry_single()
Creates the ancestry for a single layer (or a single process)
Geometry::AdaptMode _adapt_mode
the adapt mode for refinement
int get_desired_level_max() const
Returns the desired maximum refinement level.
virtual void set_desired_levels(const std::deque< String > &slvls)
Sets the desired levels for the partitioned hierarchy.
bool _was_created
specifies whether the domain control was already created
virtual String get_chosen_parti_info() const
Returns an informative string about the chosen partitioning.
virtual bool parse_property_map(const PropertyMap &pmap)
Parses the partitioner options from a PropertyMap.
double _genetic_time_init
time for genetic partitioner initialization
virtual bool _check_parti(Ancestor &ancestor, const MeshNodeType &mesh_node, bool is_base_layer)
Checks for an appropriate partitioning strategy.
bool _support_multi_layered
support multi-layered hierarchy?
virtual bool _apply_parti(Ancestor &ancestor, MeshNodeType &base_mesh_node)
Applies an a-posteriori partitioner.
Control::Domain::DomainControl< DomainLevel_ > BaseClass
Our base class.
virtual void set_desired_levels(int lvl_max, int lvl_min=-1)
Sets the desired refinement levels for a single-layered hierarchy.
virtual void _create_multi_layers_scattered()
Creates the layers for a multi-layered domain control in a scattered fashion.
virtual void _split_basemesh_halos(const Ancestor &ancestor, const MeshNodeType &base_mesh_node, MeshNodeType &patch_mesh_node, std::vector< int > &neighbor_ranks)
Splits the base-mesh halos and computes the inter-patch-mesh halos.
Communicator class.
Definition: dist.hpp:1349
void bcast(void *buffer, std::size_t count, const Datatype &datatype, int root) const
Blocking broadcast.
Definition: dist.cpp:541
Comm comm_create_range_incl(int count, int first=0, int stride=1) const
Creates a new sub-communicator from a strided range of ranks.
Definition: dist.cpp:472
void allreduce(const void *sendbuf, void *recvbuf, std::size_t count, const Datatype &datatype, const Operation &op) const
Blocking All-Reduce.
Definition: dist.cpp:655
int size() const
Returns the size of this communicator.
Definition: dist.hpp:1506
Request irecv(void *buffer, std::size_t count, const Datatype &datatype, int source, int tag=0) const
Nonblocking Receive.
Definition: dist.cpp:716
Request isend(const void *buffer, std::size_t count, const Datatype &datatype, int dest, int tag=0) const
Nonblocking Send.
Definition: dist.cpp:704
MPI_Comm comm
our MPI communicator handle
Definition: dist.hpp:1353
void gather(const void *sendbuf, std::size_t sendcount, const Datatype &sendtype, void *recvbuf, std::size_t recvcount, const Datatype &recvtype, int root) const
Blocking gather.
Definition: dist.cpp:553
Comm comm_dup() const
Creates a copy of this communicator.
Definition: dist.cpp:459
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
bool is_null() const
Checks whether this communicator is a null communicator.
Definition: dist.cpp:454
void print(std::ostream &os, const String &msg, int root=0) const
Prints a message line to an output stream.
Definition: dist.cpp:782
Communication Request vector class.
Definition: dist.hpp:640
void wait_all()
Blocks until all active requests are fulfilled.
Definition: dist.cpp:324
MeshType * get_mesh()
Returns the mesh of this node.
Definition: mesh_node.hpp:225
Iterative-Partitioner class template declaration.
ParMETIS mesh/graph partitioner backend.
bool execute(const IndexSet< nf_ > &faces_at_elem, const IndexSet< nv_ > &verts_at_elem, const VertexSet_ &vertices, const Index num_parts, const std::vector< Real > &weights)
Executes the ParMETIS graph partitioner.
Adjacency::Graph build_elems_at_rank() const
Builds and returns the elements-at-rank graph representing the partitioning.
Zoltan hypergraph partitioner backend.
bool execute(const IndexSet< n_ > &faces_at_elem, const Index num_parts, const std::vector< Real > &weights)
Executes the Zoltan partitioner.
Adjacency::Graph build_elems_at_rank() const
Builds and returns the elements-at-rank graph representing the partitioning.
Base-Mesh Patch Halo splitter.
Root mesh node class template.
Definition: mesh_node.hpp:748
const MeshPartType * get_patch(int rank) const
Returns a patch meshpart for a given child.
Definition: mesh_node.hpp:962
const std::map< int, std::unique_ptr< MeshPartType > > & get_halo_map() const
Definition: mesh_node.hpp:896
void add_halo(int rank, std::unique_ptr< MeshPartType > halo_part)
Adds a halo mesh part to this mesh node.
Definition: mesh_node.hpp:874
Class for parser related errors.
Definition: exception.hpp:132
A class organizing a tree of key-value pairs.
std::pair< String, bool > query(String key_path) const
Queries a value by its key path.
Simple argument parser implementation.
const std::pair< int, std::deque< String > > * query(const String &option) const
Query the parameters of an option.
int parse(const String &option, Prms_ &&... prms) const
Parses the parameters of an option.
String class implementation.
Definition: string.hpp:47
std::deque< String > split_by_whitespaces() const
Splits the string by white-spaces.
Definition: string.hpp:519
int compare_no_case(const String &other) const
Compares two strings without regard to case.
Definition: string.hpp:680
String pad_front(size_type len, char c=' ') const
Pads the front of the string up to a desired length.
Definition: string.hpp:393
const Operation op_max(MPI_MAX)
Operation wrapper for MPI_MAX.
Definition: dist.hpp:273
AdaptMode
Adapt mode enumeration.
Definition: mesh_node.hpp:53
T_ max(T_ a, T_ b)
Returns the maximum of two values.
Definition: math.hpp:137
FEAT namespace.
Definition: adjactor.hpp:12
double Real
Real data type.
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:993
std::uint64_t Index
Index data type.