FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
voxel_domain_control.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#pragma once
6
8
9#include <kernel/util/dist.hpp>
10#include <kernel/util/dist_file_io.hpp>
11#include <kernel/util/stop_watch.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/mesh_file_reader.hpp>
18#include <kernel/geometry/partition_set.hpp>
19#include <kernel/geometry/parti_2lvl.hpp>
20#include <kernel/geometry/parti_iterative.hpp>
21#include <kernel/geometry/parti_parmetis.hpp>
22#include <kernel/geometry/parti_zoltan.hpp>
23#include <kernel/geometry/common_factories.hpp>
24#include <kernel/geometry/patch_mesh_factory.hpp>
25#include <kernel/geometry/patch_meshpart_factory.hpp>
26#include <kernel/geometry/patch_meshpart_splitter.hpp>
27#include <kernel/geometry/cgal.hpp>
28#include <kernel/geometry/voxel_map.hpp>
29
30#include <control/domain/parti_domain_control_base.hpp>
31
32namespace FEAT
33{
34 namespace Control
35 {
36 namespace Domain
37 {
48 template<typename DomainLevel_>
50 public DomainLevel_
51 {
52 public:
54 typedef DomainLevel_ BaseClass;
56 using typename BaseClass::MeshType;
58 using typename BaseClass::MeshNodeType;
59
61 std::shared_ptr<DomainLevel_> slag_level;
62
65
68
69 explicit VoxelDomainLevelWrapper(int _lvl_idx, std::unique_ptr<MeshNodeType> node,
70 Adjacency::Coloring&& coloring, Adjacency::Coloring&& layering) :
71 BaseClass(_lvl_idx, std::move(node)),
72 slag_level(),
73 element_coloring(std::forward<Adjacency::Coloring>(coloring)),
74 element_layering(std::forward<Adjacency::Coloring>(layering))
75 {
76 }
77
78 explicit VoxelDomainLevelWrapper(int _lvl_idx, std::unique_ptr<MeshNodeType> node,
79 std::unique_ptr<MeshNodeType> slag_node, Adjacency::Coloring&& coloring, Adjacency::Coloring&& layering) :
80 BaseClass(_lvl_idx, std::move(node)),
81 slag_level(),
82 element_coloring(std::forward<Adjacency::Coloring>(coloring)),
83 element_layering(std::forward<Adjacency::Coloring>(layering))
84 {
85 if(slag_node)
86 slag_level = std::make_shared<DomainLevel_>(_lvl_idx, std::move(slag_node));
87 }
88 }; // class VoxelDomainLevelWrapper<...>
89
95 template<typename DomainLevel_>
97 public Control::Domain::PartiDomainControlBase< DomainLevel_ >
98 {
99 public:
103 using typename BaseClass::LevelType;
105 using typename BaseClass::LayerType;
107 using typename BaseClass::MeshType;
109 using typename BaseClass::AtlasType;
111 using typename BaseClass::MeshNodeType;
113 using typename BaseClass::MeshPartType;
115 using typename BaseClass::WeightType;
117 using typename BaseClass::Ancestor;
118
120 static constexpr int shape_dim = MeshType::shape_dim;
121
123 typedef typename MeshType::CoordType CoordType;
124
125 protected:
128
132 std::array<Index, shape_dim> _base_slices;
133
135 std::unique_ptr<Geometry::VoxelMap> _voxel_map;
136
138 std::unique_ptr<MeshNodeType> _base_mesh_node;
139
141 StopWatch _watch_create_base_mesh, _watch_create_voxel_map, _watch_create_hierarchy;
142
145
147 bool _unstructered_mesh = false;
148
149 public:
159 explicit VoxelDomainControl(const Dist::Comm& comm_, bool support_multi_layered) :
160 BaseClass(comm_, support_multi_layered),
161 _create_stage(0),
162 _keep_voxel_map(false),
163 _unstructered_mesh(false)
164 {
165 }
166
169 {
170 }
171
185 {
186 this->_keep_voxel_map = true;
187 }
188
189 Tiny::Vector<CoordType, shape_dim> get_bounding_box_min() const
190 {
191 return _bbox_min;
192 }
193
194 Tiny::Vector<CoordType, shape_dim> get_bounding_box_max() const
195 {
196 return _bbox_max;
197 }
198
222 {
223 XASSERTM(shape_dim == 2, "invalid dimension: this function can only be called for 2D domains!");
224 XASSERTM(_create_stage == 0, "base mesh already created!");
225
226 XASSERT(num_x > Index(0));
227 XASSERT(num_y > Index(0));
228 XASSERT(x_min < x_max);
229 XASSERT(y_min < y_max);
230
232
233 _base_slices[0] = num_x;
234 _base_slices[1] = num_y;
235 _bbox_min[0] = x_min;
236 _bbox_max[0] = x_max;
237 _bbox_min[1] = y_min;
238 _bbox_max[1] = y_max;
239
240 // create a structured mesh from factory
243
244 // get the node's vertex set
245 auto& vtx = _base_mesh_node->get_mesh()->get_vertex_set();
246 Index n = vtx.get_num_vertices();
247 for(Index i(0); i < n; ++i)
248 {
249 vtx[i][0] = x_min + (x_max - x_min) * vtx[i][0];
250 vtx[i][1] = y_min + (y_max - y_min) * vtx[i][1];
251 }
252
254
255 // update creation stage
256 this->_create_stage = 1;
257
258 // return the base-mesh node
259 return *_base_mesh_node.get();
260 }
261
288 CoordType y_min, CoordType y_max, CoordType z_min, CoordType z_max)
289 {
290 XASSERTM(shape_dim == 3, "invalid dimension: this function can only be called for 3D domains!");
291 XASSERTM(_create_stage == 0, "base mesh already created!");
292
293 XASSERT(num_x > Index(0));
294 XASSERT(num_y > Index(0));
295 XASSERT(num_z > Index(0));
296 XASSERT(x_min < x_max);
297 XASSERT(y_min < y_max);
298 XASSERT(z_min < z_max);
299
301
302 _base_slices[0] = num_x;
303 _base_slices[1] = num_y;
304 _base_slices[2] = num_z;
305 _bbox_min[0] = x_min;
306 _bbox_max[0] = x_max;
307 _bbox_min[1] = y_min;
308 _bbox_max[1] = y_max;
309 _bbox_min[2] = z_min;
310 _bbox_max[2] = z_max;
311
312 // create a structured mesh from factory
314 Geometry::StructUnitCubeFactory<MeshType>::make_unique_from(num_x, num_y, num_z), &this->_atlas);
315
316 // get the node's vertex set
317 auto& vtx = _base_mesh_node->get_mesh()->get_vertex_set();
318 Index n = vtx.get_num_vertices();
319 for(Index i(0); i < n; ++i)
320 {
321 vtx[i][0] = x_min + (x_max - x_min) * vtx[i][0];
322 vtx[i][1] = y_min + (y_max - y_min) * vtx[i][1];
323 vtx[i][2] = z_min + (z_max - z_min) * vtx[i][2];
324 }
325
327
328 // update creation stage
329 this->_create_stage = 1;
330
331 // return the base-mesh node
332 return *_base_mesh_node.get();
333 }
334
335 MeshNodeType& set_base_mesh(std::unique_ptr<MeshNodeType>&& mesh_node)
336 {
337 XASSERTM(_create_stage == 0, "base mesh already created!");
338
340 _base_mesh_node = std::move(mesh_node);
341
342 for(int k = 0; k < shape_dim; ++k)
343 {
345 }
346
347 for(int k = 0; k < shape_dim; ++k)
348 {
350 _bbox_max[k] = Math::Limits<CoordType>::lowest();
351 }
352
353 auto& vtx = _base_mesh_node->get_mesh()->get_vertex_set();
354 Index n = vtx.get_num_vertices();
355 for(Index i(0); i < n; ++i)
356 {
357 for(int k = 0; k < shape_dim; ++k)
358 {
359 _bbox_min[k] = Math::min(_bbox_min[k], vtx[i][k]);
360 _bbox_max[k] = Math::max(_bbox_max[k], vtx[i][k]);
361 }
362 }
363
365
366 // update creation stage
367 this->_create_stage = 1;
368
369 // update unstructered mesh flag
370 this->_unstructered_mesh = true;
371
372 // return the base-mesh node
373 return *_base_mesh_node.get();
374 }
375
386 {
387 this->_voxel_map->set_out_of_bounds_value(value);
388 }
389
410 {
411 XASSERTM(this->_create_stage >= 1, "invalid creation stage; create the base-mesh first");
412 XASSERTM(this->_create_stage <= 1, "invalid creation stage: voxel map already set");
413
414 this->_watch_create_voxel_map.start();
415#ifdef FEAT_COMPILER_CLANG
416 this->_voxel_map.reset(new Geometry::VoxelMap());
417#else
418 this->_voxel_map = std::make_unique<Geometry::VoxelMap>();
419#endif
420 this->_precreate_voxel_map(resolution);
421 this->_voxel_map->compute_map(this->_comm, masker, true);
422 this->_watch_create_voxel_map.stop();
423
424 // update creation stage
425 this->_create_stage = 2;
426 }
427
449 template<typename Lambda_>
450 void create_voxel_map_from_lambda(Lambda_&& lambda, Real resolution)
451 {
452 XASSERTM(this->_create_stage >= 1, "invalid creation stage; create the base-mesh first");
453 XASSERTM(this->_create_stage <= 1, "invalid creation stage: voxel map already set");
454
455 this->_watch_create_voxel_map.start();
456#ifdef FEAT_COMPILER_CLANG
457 this->_voxel_map.reset(new Geometry::VoxelMap());
458#else
459 this->_voxel_map = std::make_unique<Geometry::VoxelMap>();
460#endif
461 this->_precreate_voxel_map(resolution);
462 if constexpr(shape_dim == 2)
463 this->_voxel_map->compute_map_from_lambda_2d(this->_comm, std::forward<Lambda_>(lambda), true);
464 else
465 this->_voxel_map->compute_map_from_lambda_3d(this->_comm, std::forward<Lambda_>(lambda), true);
466 this->_watch_create_voxel_map.stop();
467
468 // update creation stage
469 this->_create_stage = 2;
470 }
471
493 {
494 XASSERTM(this->_create_stage >= 1, "invalid creation stage; create the base-mesh first");
495 XASSERTM(this->_create_stage <= 1, "invalid creation stage: voxel map already set");
496
497 this->_watch_create_voxel_map.start();
498#ifdef FEAT_COMPILER_CLANG
499 this->_voxel_map.reset(new Geometry::VoxelMap());
500#else
501 this->_voxel_map = std::make_unique<Geometry::VoxelMap>();
502#endif
503 this->_precreate_voxel_map(resolution);
504 this->_voxel_map->compute_map_from_chart(this->_comm, chart, invert, true);
505 this->_watch_create_voxel_map.stop();
506
507 // update creation stage
508 this->_create_stage = 2;
509 }
510
532 void create_voxel_map_from_off(const String& filename, bool invert, Real resolution)
533 {
534 std::stringstream sstr;
535 DistFileIO::read_common(sstr, filename, this->_comm);
536 this->create_voxel_map_from_off(sstr, invert, resolution);
537 }
538
562 void create_voxel_map_from_off(std::istream& is, bool invert, Real resolution)
563 {
564 XASSERTM(this->_create_stage >= 1, "invalid creation stage; create the base-mesh first");
565 XASSERTM(this->_create_stage <= 1, "invalid creation stage: voxel map already set");
566
567 // this only makes sense in 3D...
568 XASSERTM(shape_dim == 3, "CGAL OFF voxel map creation is only available in 3D");
569
570 this->_watch_create_voxel_map.start();
571
572#ifdef FEAT_COMPILER_CLANG
573 this->_voxel_map.reset(new Geometry::VoxelMap());
574#else
575 this->_voxel_map = std::make_unique<Geometry::VoxelMap>();
576#endif
577 this->_precreate_voxel_map(resolution);
578 this->_voxel_map->compute_map_from_off_3d(this->_comm, is, invert, true);
579
580 this->_watch_create_voxel_map.stop();
581
582 // update creation stage
583 this->_create_stage = 2;
584 }
585
596 {
597 XASSERTM(this->_create_stage >= 1, "invalid creation stage; create the base-mesh first");
598 XASSERTM(this->_create_stage <= 1, "invalid creation stage: voxel map already set");
599
600 this->_watch_create_voxel_map.start();
601
602#ifdef FEAT_COMPILER_CLANG
603 this->_voxel_map.reset(new Geometry::VoxelMap());
604#else
605 this->_voxel_map = std::make_unique<Geometry::VoxelMap>();
606#endif
607 Geometry::VoxelMap::ReadResult result = this->_voxel_map->read(this->_comm, filename);
608
609 this->_watch_create_voxel_map.stop();
610
611 // update creation stage
612 this->_create_stage = 2;
613
614 return result;
615 }
616
627 {
628 XASSERTM(this->_voxel_map, "voxel map not created yet");
629 return this->_voxel_map->write(filename);
630 }
631
639 {
640 XASSERTM(this->_create_stage >= 2, "invalid creation stage; create the base-mesh and voxel map first");
641 XASSERTM(this->_create_stage <= 2, "invalid creation stage: domain hierarchy already created");
642
643 this->_watch_create_hierarchy.start();
644
645 // create the domain control
646#ifdef FEAT_HAVE_MPI
647 if(this->_comm.size() == 1)
648 {
649 // We've got just one process, so it's a simple choice:
650 this->_create_single_process(std::move(this->_base_mesh_node));
651 }
652 else if(this->_support_multi_layered && (this->_desired_levels.size() > std::size_t(2)))
653 {
654 // The application supports multi-layered domain controls and
655 // the user wants that, so create a multi-layered one:
656 this->_create_multi_layered(std::move(this->_base_mesh_node));
657 }
658 else
659 {
660 // Create a single-layered domain control:
661 this->_create_single_layered(std::move(this->_base_mesh_node));
662 }
663#else // not FEAT_HAVE_MPI
664 {
665 // In the non-MPI case, we always have only one process:
666 this->_create_single_process(std::move(this->_base_mesh_node));
667 }
668#endif // FEAT_HAVE_MPI
669
670 // compile virtual levels
671 this->compile_virtual_levels();
672
673 this->_watch_create_hierarchy.stop();
674
675 // collect statistics
676 FEAT::Statistics::toe_partition = this->_watch_create_base_mesh.elapsed() +
677 this->_watch_create_voxel_map.elapsed() + this->_watch_create_hierarchy.elapsed();
678
679 // update creation stage
680 this->_create_stage = 3;
681 this->_was_created = true;
682
683 // delete the voxel map unless we're meant to keep it
684 if(!this->_keep_voxel_map)
685 this->_voxel_map.reset();
686 }
687
690 {
691 return this->_watch_create_base_mesh;
692 }
693
696 {
697 return this->_watch_create_voxel_map;
698 }
699
702 {
703 return this->_watch_create_hierarchy;
704 }
705
710 {
711 return *this->_voxel_map;
712 }
713
723 std::vector<int> gather_vertex_voxel_map(Index level) const
724 {
725 XASSERTM(this->_create_stage == 3, "domain level hierarchy has to be created before the vertex mask can be computed");
726 XASSERT(level < this->size_physical());
727
728 const auto& vtx = this->at(level)->get_mesh().get_vertex_set();
729 const Index nv = vtx.get_num_vertices();
730 std::vector<int> mask(nv, 0);
731
732 for(Index i(0); i < nv; ++i)
733 mask[i] = this->_voxel_map->check_point_nearest(vtx[i]);
734
735 return mask;
736 }
737
751 std::vector<WeightType> gather_element_voxel_weights(Index level) const
752 {
753 XASSERT(level < this->size_physical());
754 return this->_gather_element_weights(this->at(level)->get_mesh());
755 }
756
761 {
762 String msg;
763 for(auto it = this->_layer_levels.begin(); it != this->_layer_levels.end(); ++it)
764 {
765 if(it != this->_layer_levels.begin())
766 msg += " |";
767 for(auto jt = it->begin(); jt != it->end(); ++jt)
768 {
769 msg += " " + stringify((*jt)->get_level_index());
770 msg += "[";
771 msg += stringify((*jt)->get_mesh().get_num_elements()).pad_front(4);
772 msg += "]";
773 if((*jt)->slag_level)
774 ((msg += "<[") += stringify((*jt)->slag_level->get_mesh().get_num_elements()).pad_front(4)) += "]";
775 }
776 }
777 return msg;
778 }
779
789 virtual void create(const std::deque<String>& filenames, String dirpath = "")
790 {
791 //dummy implementation
792 ASSERTM(false, "Thou shall not arrive here!");
793 (void) filenames;
794 (void) dirpath;
795 }
796
804 {
805 //dummy implementation
806 ASSERTM(false, "Thou shall not arrive here!");
807 }
808
815 virtual void create(std::unique_ptr<MeshNodeType>&)
816 {
817 //dummy implementation
818 ASSERTM(false, "Thou shall not arrive here!");
819 }
820
821 protected:
825 void _precreate_voxel_map(Real resolution)
826 {
827 // set voxel map bounding box based on domain's bounding box
828 if constexpr(shape_dim == 2)
829 this->_voxel_map->set_bounding_box_2d(this->_bbox_min[0], this->_bbox_max[0], this->_bbox_min[1], this->_bbox_max[1]);
830 else
831 this->_voxel_map->set_bounding_box_3d(this->_bbox_min[0], this->_bbox_max[0],
832 this->_bbox_min[1], this->_bbox_max[1], this->_bbox_min[2], this->_bbox_max[2]);
833
834 // is the resolution given?
835 if(resolution > Real(0))
836 {
837 this->_voxel_map->set_resolution(resolution);
838 return;
839 }
840 else if(_unstructered_mesh)
841 {
842 XABORTM("Required to provide resolution for voxel map");
843 }
844
845 XASSERTM(this->_base_slices[0] != Math::Limits<Index>::max(), "Set Resolution explicitly");
846 // compute the mesh width on the base mesh level
847 Real base_res = (this->_bbox_max[0] - this->_bbox_min[0]) / Real(this->_base_slices[0]);
848 for(int i(1); i < shape_dim; ++i)
849 base_res = Math::min(base_res, this->_bbox_max[i] - this->_bbox_min[i]) / Real(this->_base_slices[std::size_t(i)]);
850
851 // set resolution for voxel map to base resolution divided by 2^{desired-level-max+1)
852 this->_voxel_map->set_resolution(base_res / Real(2ull << this->get_desired_level_max()));
853 }
854
869 std::vector<Index> _gather_masked_elements(const MeshType& mesh) const
870 {
871 const Index num_elems = mesh.get_num_elements();
872
873 // reserve the vector
874 std::vector<Index> masked_elems;
875 masked_elems.reserve(num_elems);
876
877 // get the vertex set and the vertices-at-element index set
878 const auto& vtx = mesh.get_vertex_set();
879 const auto& verts_at_elem = mesh.template get_index_set<shape_dim, 0>();
880
881 // element bounding box coordinates
882 Tiny::Vector<CoordType, shape_dim> elbox_min, elbox_max;
883
884 // loop over all elements
885 for(Index ielem(0); ielem < num_elems; ++ielem)
886 {
887 // compute bounding box of element
888 // note: actually, we should always have
889 // elbox_min = vtx[verts_at_elem(ielem, 0)]
890 // elbox_max = vtx[verts_at_elem(ielem, 1 << shape_dim - 1)]
891 // because of the element orientation of the base mesh, but we'll compute the bounding
892 // box for each element manually anyways, just to be sure in case that a derived class
893 // might give us a mesh object that is not directly refined from the base mesh using
894 // the standard 2-level refinement...
895 elbox_min = elbox_max = vtx[verts_at_elem(ielem, 0)];
896 for(int j(1); j < verts_at_elem.num_indices; ++j)
897 {
898 const auto& v = vtx[verts_at_elem(ielem, j)];
899 for(int k(0); k < shape_dim; ++k)
900 Math::minimax(v[k], elbox_min[k], elbox_max[k]);
901 }
902
903 // check the bounding box against the voxel map
904 if(this->_voxel_map->check_box(elbox_min, elbox_max))
905 masked_elems.push_back(ielem);
906 }
907
908 // return the list of all elements that we found
909 return masked_elems;
910 }
911
925 std::vector<WeightType> _gather_element_weights(const MeshType& mesh) const
926 {
927 const Index num_elems = mesh.get_num_elements();
928
929 // allocate the weight vector
930 std::vector<WeightType> weights(num_elems, 0.0);
931
932 // get the vertex set and the vertices-at-element index set
933 const auto& vtx = mesh.get_vertex_set();
934 const auto& verts_at_elem = mesh.template get_index_set<shape_dim, 0>();
935
936 // element bounding box coordinates
937 Tiny::Vector<CoordType, shape_dim> elbox_min, elbox_max;
938
939 // loop over all elements
940 for(Index ielem(0); ielem < num_elems; ++ielem)
941 {
942 // compute bounding box of element
943 // note: actually, we should always have
944 // elbox_min = vtx[verts_at_elem(ielem, 0)]
945 // elbox_max = vtx[verts_at_elem(ielem, 1 << shape_dim - 1)]
946 // because of the element orientation of the base mesh, but we'll compute the bounding
947 // box for each element manually anyways, just to be sure in case that a derived class
948 // might give us a mesh object that is not directly refined from the base mesh using
949 // the standard 2-level refinement...
950 elbox_min = elbox_max = vtx[verts_at_elem(ielem, 0)];
951 for(int j(1); j < verts_at_elem.num_indices; ++j)
952 {
953 const auto& v = vtx[verts_at_elem(ielem, j)];
954 for(int k(0); k < shape_dim; ++k)
955 Math::minimax(v[k], elbox_min[k], elbox_max[k]);
956 }
957
958 // gather element weight based on the bounding box
959 weights[ielem] = this->_voxel_map->sample_box(elbox_min, elbox_max);
960 }
961
962 // returns the element weights
963 return weights;
964 }
965
981 virtual std::unique_ptr<MeshNodeType> _deslag_mesh_node(MeshNodeType& mesh_node)
982 {
983 XASSERTM(mesh_node.get_halo_map().empty(), "This function must not be used for partitioned mesh nodes!");
984 return mesh_node.extract_patch(_gather_masked_elements(*mesh_node.get_mesh()), true, false, false);
985 }
986
1005 virtual std::unique_ptr<MeshNodeType> _deslag_mesh_node(MeshNodeType& mesh_node, const Ancestor& ancestor, bool is_child)
1006 {
1007 std::unique_ptr<MeshNodeType> new_node = mesh_node.extract_patch(_gather_masked_elements(*mesh_node.get_mesh()), true, false, false);
1008 this->_deslag_patch_halos(*new_node, mesh_node, ancestor, is_child);
1009 return new_node;
1010 }
1011
1030 MeshNodeType& patch_mesh_node,
1031 const MeshNodeType& base_mesh_node,
1032 const Ancestor& ancestor,
1033 bool is_child)
1034 {
1035 // get the map of the base-mesh halos
1036 const std::map<int, std::unique_ptr<MeshPartType>>& base_halo_map = base_mesh_node.get_halo_map();
1037
1038 // if the base mesh has no halos, then we can jump out of here
1039 if(base_halo_map.empty())
1040 return;
1041
1042 // get number of halos
1043 const std::size_t num_halos = base_halo_map.size();
1044
1045 // create a halo splitter
1046 Geometry::PatchHaloSplitter<MeshType> halo_splitter(*base_mesh_node.get_mesh(), *base_mesh_node.get_patch(-1));
1047
1048 // add each base-mesh halo to our halo splitter and store the resulting split data size
1049 std::vector<int> halo_ranks;
1050 std::vector<std::size_t> halo_send_sizes;
1051 for(auto it = base_halo_map.begin(); it != base_halo_map.end(); ++it)
1052 {
1053 // store halo rank
1054 halo_ranks.push_back(it->first);
1055
1056 // add halo and compute send buffer size
1057 halo_send_sizes.push_back(halo_splitter.add_halo(it->first, *it->second));
1058 }
1059
1060 // This vector will receive the split halo data from all our potential neighbor processes
1061 std::vector<std::size_t> halo_recv_sizes(num_halos);
1062 std::vector<std::vector<Index>> halo_send_data(num_halos), halo_recv_data(num_halos);
1063 Dist::RequestVector halo_recv_reqs(num_halos), halo_send_reqs(num_halos);
1064
1065 // get the layer index of our comm layer; the ancestor layer unless we got the child ancestry during
1066 // multi-layered partitioning; this may be equal to -1 in either case if this process does not participate
1067 // in that corresponding layer
1068 const int layer_idx = is_child ? ancestor.layer_p : ancestor.layer;
1069
1070 // split and serialize halos and store sizes
1071 for(std::size_t i(0); i < num_halos; ++i)
1072 {
1073 // serialize the split halo into the send data buffer
1074 if(halo_send_sizes.at(i) > std::size_t(0))
1075 halo_send_data.at(i) = halo_splitter.serialize_split_halo(halo_ranks[i], -1);
1076 }
1077
1078 // exchange halo send data sizes over the corresponding layer communicator
1079 if(layer_idx >= 0)
1080 {
1081 // get the corresponding layer communicator
1082 const Dist::Comm& layer_comm = this->_layers.at(std::size_t(layer_idx))->comm();
1083
1084 // exchange halos sizes
1085 for(std::size_t i(0); i < num_halos; ++i)
1086 {
1087 // post receive requests
1088 halo_recv_reqs[i] = layer_comm.irecv(&halo_recv_sizes[i], std::size_t(1), halo_ranks[i]);
1089
1090 // post send requests
1091 halo_send_reqs[i] = layer_comm.isend(&halo_send_sizes[i], std::size_t(1), halo_ranks[i]);
1092 }
1093
1094 // wait for sends and receives to finish
1095 halo_recv_reqs.wait_all();
1096 halo_send_reqs.wait_all();
1097
1098 // exchange halo data
1099 for(std::size_t i(0); i < num_halos; ++i)
1100 {
1101 // do we receive any data from our neighbor?
1102 if(halo_recv_sizes[i] > Index(0))
1103 {
1104 // resize buffer and post receive
1105 halo_recv_data.at(i).resize(halo_recv_sizes[i]);
1106 halo_recv_reqs[i] = layer_comm.irecv(halo_recv_data.at(i).data(), halo_recv_sizes.at(i), halo_ranks[i]);
1107 }
1108
1109 // do we send any data to our neighbor?
1110 if(halo_send_sizes.at(i) > Index(0))
1111 {
1112 // post send of actual halo buffer
1113 halo_send_reqs[i] = layer_comm.isend(halo_send_data.at(i).data(), halo_send_sizes.at(i), halo_ranks[i]);
1114 }
1115 }
1116
1117 // wait for sends and receives to finish
1118 halo_recv_reqs.wait_all();
1119 halo_send_reqs.wait_all();
1120 }
1121
1122 // broadcast halo receive data over progeny comm in case of multi-layered partitioning
1123 if(is_child)
1124 {
1125 // broadcast sizes
1126 ancestor.progeny_comm.bcast(halo_recv_sizes.data(), num_halos, 0);
1127
1128 // allocate buffers
1129 if(ancestor.progeny_comm.rank() != 0)
1130 {
1131 for(std::size_t i(0); i < num_halos; ++i)
1132 halo_recv_data.at(i).resize(halo_recv_sizes[i]);
1133 }
1134
1135 // broadcast all halos
1136 for(std::size_t i(0); i < num_halos; ++i)
1137 {
1138 if(halo_recv_sizes[i] > std::size_t(0))
1139 ancestor.progeny_comm.bcast(halo_recv_data[i].data(), halo_recv_sizes[i], 0);
1140 }
1141 }
1142
1143 /*{
1144 String s;
1145 for(std::size_t i(0); i < num_halos; ++i)
1146 {
1147 s += stringify(halo_ranks[i]);
1148 s += " >";
1149 for(Index j(halo_recv_data[i]); j < halo_recv_data[i+1]; ++j)
1150 (s += " ") += stringify(halo_recv_data[j]);
1151 s += "\n";
1152 }
1153 this->_comm.allprint(s);
1154 }*/
1155
1156 // create a vector of split halo sizes
1157 std::vector<std::array<Index, shape_dim+1>> halo_send_intsec_sizes(num_halos), halo_recv_intsec_sizes(num_halos);
1158
1159 // process all halos
1160 for(std::size_t i(0); i < num_halos; ++i)
1161 {
1162 // did we receive any data from our neighbor?
1163 if(halo_recv_sizes.at(i) == Index(0))
1164 continue;
1165
1166 // intersect with our other halo
1167 if(!halo_splitter.intersect_split_halo(halo_ranks[i], halo_recv_data.at(i), 0u))
1168 continue; // no intersection between halos
1169
1170 // create the new halo
1171 std::unique_ptr<MeshPartType> split_halo = halo_splitter.make_unique();
1172
1173 // store the entity counts of the halo in our split sizes vector
1174 for(int j(0); j <= shape_dim; ++j)
1175 halo_send_intsec_sizes[i][Index(j)] = split_halo->get_num_entities(j);
1176
1177 // create new halo mesh-part
1178 patch_mesh_node.add_halo(halo_ranks[i], std::move(split_halo));
1179 }
1180
1181 // exchange halo intersected halo sizes over progeny comm; we do this just to ensure consistency
1182 if((ancestor.layer_p >= 0) || (!is_child && (ancestor.layer >= 0)))
1183 {
1184 // get the corresponding layer communicator
1185 const LayerType& layer = *this->_layers.at(std::size_t(is_child ? ancestor.layer_p : ancestor.layer));
1186
1187 // exchange intersected halo sizes
1188 for(std::size_t i(0); i < num_halos; ++i)
1189 {
1190 halo_recv_reqs[i] = layer.comm().irecv(halo_recv_intsec_sizes.at(i).data(), std::size_t(shape_dim+1), halo_ranks[i]);
1191 halo_send_reqs[i] = layer.comm().isend(halo_send_intsec_sizes.at(i).data(), std::size_t(shape_dim+1), halo_ranks[i]);
1192 }
1193
1194 // wait for sends and receives to finish
1195 halo_recv_reqs.wait_all();
1196 halo_send_reqs.wait_all();
1197
1198 // ensure that the halo sizes are consistent between neighboring processes
1199 for(std::size_t i(0); i < num_halos; ++i)
1200 {
1201 for(int j(0); j <= shape_dim; ++j)
1202 {
1203 if(halo_recv_intsec_sizes[i][Index(j)] != halo_send_intsec_sizes[i][Index(j)])
1204 {
1205 String msg = "Inconsistent deslagged halo size between process ";
1206 msg += stringify(this->_comm.rank());
1207 msg += " and neighbor with layer rank ";
1208 msg += stringify(halo_ranks[i]);
1209
1210 // abort execution
1211 XABORTM(msg.c_str());
1212 }
1213 }
1214 }
1215 }
1216 }
1217
1218 // virtual bool _parse_parti_type(const String& type) override
1219 // {
1220 // if(type.compare_no_case("extern") == 0)
1221 // return this->_allow_parti_extern = true;
1222 // // else if(type.compare_no_case("2level") == 0)
1223 // // return _allow_parti_2level = true;
1224 // else
1225 // return BaseClass::_parse_parti_type(type);
1226 // }
1227
1228 Adjacency::Coloring _compute_unstructered_coloring(const MeshNodeType& base_node) const
1229 {
1230 const auto& verts_at_elem = base_node.get_mesh()->template get_index_set<MeshType::shape_dim, 0>();
1231 Adjacency::Graph elems_at_vert(Adjacency::RenderType::transpose, verts_at_elem);
1232 Adjacency::Graph elems_at_elem(Adjacency::RenderType::injectify, verts_at_elem, elems_at_vert);
1233 return Adjacency::Coloring(elems_at_elem);
1234 }
1235
1236 Adjacency::Coloring _compute_unstructered_layering(const MeshNodeType& DOXY(base_node)) const
1237 {
1238 // ASSERTM(false, "Unstructured layering computation not implemented yet!");
1239 #ifdef FEAT_DEBUG_MODE
1240 std::cout << "Unstructered Layering Computation not implemented yet!";
1241 #endif
1242 return Adjacency::Coloring();
1243 }
1244
1255 {
1256 // get element target set of slag patch
1257 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1258 XASSERT(slag_part != nullptr);
1259 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1260
1261 // number of colors = 2^shape_dim
1262 const Index num_colors = Index(1) << MeshType::shape_dim;
1263 const Index num_elems = slag_target.get_num_entities();
1264
1265 // allocate coloring
1266 Adjacency::Coloring coloring(num_elems, num_colors);
1267 Index* colors = coloring.get_coloring();
1268
1269 // unrefined base mesh: structured numbering
1270 if constexpr(MeshType::shape_dim == 1)
1271 {
1272 for(Index i(0); i < num_elems; ++i)
1273 colors[i] = slag_target[i] & 1;
1274 }
1275 else if constexpr(MeshType::shape_dim == 2)
1276 {
1277 for(Index i(0); i < num_elems; ++i)
1278 {
1279 Index iel = slag_target[i];
1280 Index ix = iel % this->_base_slices[0];
1281 Index iy = iel / this->_base_slices[0];
1282 colors[i] = (ix & 1) | ((iy & 1) << 1);
1283 }
1284 }
1285 else if constexpr(MeshType::shape_dim == 3)
1286 {
1287 for(Index i(0); i < num_elems; ++i)
1288 {
1289 Index iel = slag_target[i];
1290 Index ix = iel % this->_base_slices[0];
1291 Index iy = (iel / this->_base_slices[0]) % this->_base_slices[1];
1292 Index iz = iel / (this->_base_slices[0] * this->_base_slices[1]);
1293 colors[i] = (ix & 1) | ((iy & 1) << 1) | ((iz & 1) << (1 << 1));
1294 }
1295 }
1296
1297 return coloring;
1298 }
1299
1310 {
1311 // get element target set of slag patch
1312 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1313 XASSERT(slag_part != nullptr);
1314 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1315
1316 // number of colors = 2^shape_dim
1317 const Index num_colors = Index(1) << MeshType::shape_dim;
1318 const Index num_elems = slag_target.get_num_entities();
1319
1320 // allocate coloring
1321 Adjacency::Coloring coloring(num_elems, num_colors);
1322 Index* colors = coloring.get_coloring();
1323
1324 // color of element i = i % num_colors
1325 for(Index i(0); i < num_elems; ++i)
1326 colors[i] = slag_target[i] % num_colors;
1327
1328 return coloring;
1329 }
1330
1346 Adjacency::Coloring _extract_patch_coloring(const MeshNodeType& slag_node, const Adjacency::Coloring& parent_coloring, int child_rank) const
1347 {
1348 // get element target set of patch
1349 const Geometry::MeshPart<MeshType>* patch_part = slag_node.get_patch(child_rank);
1350 XASSERT(patch_part != nullptr);
1351 const Geometry::TargetSet& patch_target = patch_part->template get_target_set<MeshType::shape_dim>();
1352
1353 // number of colors = 2^shape_dim
1354 const Index num_colors = parent_coloring.get_num_colors();
1355 const Index num_elems = patch_target.get_num_entities();
1356
1357 // allocate coloring
1358 Adjacency::Coloring coloring(num_elems, num_colors);
1359 Index* colors = coloring.get_coloring();
1360
1361 // color of element i = i % num_colors
1362 for(Index i(0); i < num_elems; ++i)
1363 colors[i] = parent_coloring[patch_target[i]];
1364
1365 return coloring;
1366 }
1367
1378 {
1379 // get element target set of slag patch
1380 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1381 XASSERT(slag_part != nullptr);
1382 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1383
1384 // maximum number of layers = number of slices in highest dimensions; this may be less than
1385 // base_slices[shape_dim], since entire layers might have been removed during the deslagging process
1386 const Index max_layers = this->_base_slices[shape_dim-1];
1387 const Index num_elems = slag_target.get_num_entities();
1388
1389 // compute denominator for layer index computation
1390 Index denom = 1u;
1391 for(int j(0); j+1 < shape_dim; ++j)
1392 denom *= this->_base_slices[Index(j)];
1393
1394 // compute number of elements for each potential layer
1395 std::vector<Index> aux(max_layers, 0u);
1396 for(Index i(0); i < num_elems; ++i)
1397 ++aux[slag_target[i] / denom];
1398
1399 // count number of actually used layers
1400 Index num_layers = 0u;
1401 for(Index i(0); i < max_layers; ++i)
1402 {
1403 Index k = num_layers;
1404 if(aux[i] > 0u)
1405 ++num_layers;
1406 aux[i] = k;
1407 }
1408
1409 // compute layering
1410 Adjacency::Coloring layering(num_elems, num_layers);
1411 Index* layers = layering.get_coloring();
1412 for(Index i(0); i < num_elems; ++i)
1413 layers[i] = aux[slag_target[i] / denom];
1414
1415 return layering;
1416 }
1417
1428 {
1430 {
1431 return Adjacency::Coloring();
1432 }
1433 // get element target set of slag patch
1434 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1435 XASSERT(slag_part != nullptr);
1436 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1437
1438 // maximum number of layers = twice the number of layers in coarse mesh; this may actually be less,
1439 // since entire layers might have been removed during the deslagging process
1440 const Index max_layers = 2 * coarse_layering.get_num_colors();
1441 const Index num_elems = slag_target.get_num_entities();
1442
1443 // compute number of elements for each potential layer
1444 std::vector<Index> aux(max_layers, 0u);
1445 for(Index i(0); i < num_elems; ++i)
1446 {
1447 Index qux = slag_target[i] >> (shape_dim - 1);
1448 ++aux[(coarse_layering[qux >> 1] << 1) | (qux & 1)]; // all hail to bit-shift magic!
1449 }
1450
1451 // count number of actually used layers
1452 Index num_layers = 0u;
1453 for(Index i(0); i < max_layers; ++i)
1454 {
1455 Index k = num_layers;
1456 if(aux[i] > 0u)
1457 ++num_layers;
1458 aux[i] = k;
1459 }
1460
1461 // compute layering
1462 Adjacency::Coloring layering(num_elems, num_layers);
1463 Index* layers = layering.get_coloring();
1464 for(Index i(0); i < num_elems; ++i)
1465 {
1466 Index qux = slag_target[i] >> (shape_dim - 1);
1467 layers[i] = aux[(coarse_layering[qux >> 1] << 1) | (qux & 1)];
1468 }
1469
1470 return layering;
1471 }
1472
1488 Adjacency::Coloring _extract_patch_layering(const MeshNodeType& slag_node, const Adjacency::Coloring& parent_layering, int child_rank) const
1489 {
1491 return Adjacency::Coloring();
1492 // get element target set of patch
1493 const Geometry::MeshPart<MeshType>* patch_part = slag_node.get_patch(child_rank);
1494 XASSERT(patch_part != nullptr);
1495 const Geometry::TargetSet& patch_target = patch_part->template get_target_set<MeshType::shape_dim>();
1496
1497 // maximum number of layers = number of layers in parent layering; this may actually be less,
1498 // since entire layers might have been removed during the deslagging process
1499 const Index max_layers = parent_layering.get_num_colors();
1500 const Index num_elems = patch_target.get_num_entities();
1501
1502 // compute number of elements for each potential layer
1503 std::vector<Index> aux(max_layers, 0u);
1504 for(Index i(0); i < num_elems; ++i)
1505 ++aux[parent_layering[patch_target[i]]];
1506
1507 // count number of actually used layers
1508 Index num_layers = 0u;
1509 for(Index i(0); i < max_layers; ++i)
1510 {
1511 Index k = num_layers;
1512 if(aux[i] > 0u)
1513 ++num_layers;
1514 aux[i] = k;
1515 }
1516
1517 // compute layering
1518 Adjacency::Coloring layering(num_elems, num_layers);
1519 Index* layers = layering.get_coloring();
1520 for(Index i(0); i < num_elems; ++i)
1521 layers[i] = aux[parent_layering[patch_target[i]]];
1522
1523 return layering;
1524 }
1525
1537 virtual void _create_single_process(std::unique_ptr<MeshNodeType> base_mesh_node)
1538 {
1539 // create and push single layer
1540 this->push_layer(std::make_shared<LayerType>(this->_comm.comm_dup(), 0));
1541
1542 // create single ancestry
1544 Ancestor& ancestor = this->_ancestry.front();
1545
1546 // use the whole base mesh here
1547 ancestor.parti_info = "Using base-mesh";
1548
1549 // save slagged base mesh node
1550 std::unique_ptr<MeshNodeType> base_slag_node = std::move(base_mesh_node);
1551 // deslag base mesh node
1552 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1553
1554 // compute base-mesh layering
1555 Adjacency::Coloring base_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*base_mesh_node) : this->_compute_base_mesh_layering(*base_slag_node);
1556
1557 // refine and deslag up to desired minimum level
1558 int lvl = 0;
1559 for(; lvl < ancestor.desired_level_min; ++lvl)
1560 {
1561 base_slag_node = base_mesh_node->refine_unique(this->_adapt_mode);
1562 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1563 base_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*base_mesh_node) : this->_compute_refined_mesh_layering(*base_slag_node, base_layering);
1564 }
1565
1566 // compute coloring for the coarse level
1567 Adjacency::Coloring base_coloring;
1569 {
1570 base_coloring = this->_compute_unstructered_coloring(*base_mesh_node);
1571 }
1572 else
1573 {
1574 if(lvl == 0)
1575 base_coloring = this->_compute_base_mesh_coloring(*base_slag_node);
1576 else
1577 base_coloring = this->_compute_refined_mesh_coloring(*base_slag_node);
1578 }
1579
1580 // save chosen minimum level if it is not equal to the desired maximum level
1581 if(lvl < ancestor.desired_level_max)
1582 this->_chosen_levels.push_front(std::make_pair(lvl, 0));
1583
1584 // refine up to maximum level and push to control
1585 for(; lvl < ancestor.desired_level_max; ++lvl)
1586 {
1587 // refine the base mesh
1588 auto refined_node = base_mesh_node->refine_unique(this->_adapt_mode);
1589
1590 // push this level
1591 std::shared_ptr<LevelType> level_ptr = std::make_shared<LevelType>(lvl, std::move(base_mesh_node),
1592 std::move(base_slag_node), std::move(base_coloring), std::move(base_layering));
1593 this->push_level_front(0, level_ptr);
1594
1595 // continue with refined node
1596 base_slag_node = std::move(refined_node);
1597 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1598 base_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*base_mesh_node) : this->_compute_refined_mesh_coloring(*base_slag_node);
1599 base_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*base_mesh_node) : this->_compute_refined_mesh_layering(*base_slag_node, level_ptr->element_layering);
1600 }
1601
1602 // save chosen maximum level
1603 this->_chosen_levels.push_front(std::make_pair(lvl, 1));
1604
1605 // push finest level
1606 this->push_level_front(0, std::make_shared<LevelType>(lvl, std::move(base_mesh_node),
1607 std::move(base_slag_node), std::move(base_coloring), std::move(base_layering)));
1608 }
1609
1610 // Note: all following member functions are only required for parallel builds,
1611 // so we enclose them in the following #if-block to reduce compile times.
1612
1613#if defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
1614
1621 virtual void _create_single_layered(std::unique_ptr<MeshNodeType> base_mesh_node)
1622 {
1623 // create and push single layer
1624 std::shared_ptr<LayerType> layer = std::make_shared<LayerType>(this->_comm.comm_dup(), 0);
1625 this->push_layer(layer);
1626
1627 // create single-layered ancestry
1629 Ancestor& ancestor = this->_ancestry.front();
1630
1631 XASSERTM(!this->_keep_base_levels, "VoxelDomainControl cannot keep base levels!");
1632
1633 // save slagged base mesh node
1634 std::unique_ptr<MeshNodeType> base_slag_node = std::move(base_mesh_node);
1635
1636 // deslag base mesh node
1637 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1638 Adjacency::Coloring base_coloring;
1639 Adjacency::Coloring base_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*base_mesh_node) : this->_compute_base_mesh_layering(*base_slag_node);
1640
1641 // refine up to chosen partition level
1642 int lvl = 0;
1643 for(; lvl <= ancestor.desired_level_max; ++lvl)
1644 {
1645 // can we apply a partitioner?
1646 if((lvl >= ancestor.parti_level) && this->_apply_parti(ancestor, *base_mesh_node))
1647 break;
1648
1649 // no partitioning found?
1650 if(lvl >= ancestor.desired_level_max)
1651 break;
1652
1653 // refine and deslag base mesh node
1654 base_slag_node = base_mesh_node->refine_unique(this->_adapt_mode);
1655 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1656 base_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*base_mesh_node) : this->_compute_refined_mesh_coloring(*base_slag_node);
1657 base_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*base_mesh_node) : this->_compute_refined_mesh_layering(*base_slag_node, base_layering);
1658 }
1659
1660 // no valid partitioning found?
1661 XASSERTM(ancestor.parti_found, "VoxelDomainControl failed to find a valid partitioning");
1662
1663 // set the selected partitioner level
1664 ancestor.parti_level = lvl;
1665
1666 // compute coloring if we're still on level 0; otherwise the coloring has been computed in the loop above
1667 if(lvl == 0)
1668 {
1669 base_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*base_mesh_node) : this->_compute_refined_mesh_coloring(*base_slag_node);
1670 }
1671
1672 // extract our patch
1673 std::vector<int> neighbor_ranks;
1674 std::unique_ptr<MeshNodeType> patch_mesh_node(
1675 base_mesh_node->extract_patch(neighbor_ranks, ancestor.parti_graph, this->_comm.rank()));
1676
1677 // extract our coloring (only if we need it on this level)
1678 Adjacency::Coloring patch_coloring;
1679 if(lvl == ancestor.desired_level_min)
1680 patch_coloring = this->_extract_patch_coloring(*base_mesh_node, base_coloring, this->_comm.rank());
1681
1682 // extract layering
1683 Adjacency::Coloring patch_layering = this->_extract_patch_layering(*base_mesh_node, base_layering, this->_comm.rank());
1684
1685 // set the neighbor ranks of our child layer
1686 layer->set_neighbor_ranks(neighbor_ranks);
1687
1688 // create an empty patch slag node
1689 std::unique_ptr<MeshNodeType> patch_slag_node;
1690
1691 // refine up to minimum level
1692 for(; lvl < ancestor.desired_level_min; ++lvl)
1693 {
1694 // refine the patch mesh
1695 patch_slag_node = patch_mesh_node->refine_unique(this->_adapt_mode);
1696 patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node, ancestor, false);
1697 patch_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*patch_mesh_node) : this->_compute_refined_mesh_layering(*patch_slag_node, patch_layering);
1698 }
1699
1700 // compute coloring if we don't have one yet
1701 if(patch_coloring.empty())
1702 patch_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*patch_mesh_node) : this->_compute_refined_mesh_coloring(*patch_slag_node);
1703
1704 // save chosen minimum level
1705 if(lvl < ancestor.desired_level_max)
1706 this->_chosen_levels.push_front(std::make_pair(lvl, 0));
1707
1708 // refine up to maximum level
1709 for(; lvl < ancestor.desired_level_max; ++lvl)
1710 {
1711 // refine the patch mesh
1712 auto refined_node = patch_mesh_node->refine_unique(this->_adapt_mode);
1713
1714 // create new level
1715 std::shared_ptr<LevelType> level_ptr = std::make_shared<LevelType>(lvl, std::move(patch_mesh_node),
1716 std::move(patch_slag_node), std::move(patch_coloring), std::move(patch_layering));
1717
1718 // push this (unrefined) level
1719 this->push_level_front(0, level_ptr);
1720
1721 // continue with refined node
1722 patch_slag_node = std::move(refined_node);
1723 patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node, ancestor, false);
1724 patch_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*patch_mesh_node) : this->_compute_refined_mesh_coloring(*patch_slag_node);
1725 patch_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*patch_mesh_node) : this->_compute_refined_mesh_layering(*patch_slag_node, level_ptr->element_layering);
1726 }
1727
1728 // save chosen maximum level
1729 this->_chosen_levels.push_front(std::make_pair(lvl, this->_comm.size()));
1730
1731 // push finest level
1732 this->push_level_front(0, std::make_shared<LevelType>(lvl, std::move(patch_mesh_node),
1733 std::move(patch_slag_node), std::move(patch_coloring), std::move(patch_layering)));
1734 }
1735
1742 virtual void _create_multi_layered(std::unique_ptr<MeshNodeType> base_mesh_node)
1743 {
1744 // create layers
1746
1747 // create ancestry
1749
1750 XASSERTM(!this->_keep_base_levels, "VoxelDomainControl cannot keep base levels!");
1751
1752 // we start counting at level 0
1753 int lvl = 0;
1754
1755 // deslag the base-mesh node and move pointer to a new parent-mesh pointer;
1756 // the base-mesh is always the one on the layer with only 1 process
1757 std::unique_ptr<MeshNodeType> parent_slag_node;
1758 std::unique_ptr<MeshNodeType> parent_mesh_node = this->_deslag_mesh_node(*base_mesh_node);
1759
1760 // create the coloring and layering for the unrefined base mesh
1761 Adjacency::Coloring parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_base_mesh_coloring(*base_mesh_node);
1762 Adjacency::Coloring parent_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*parent_mesh_node) : this->_compute_base_mesh_layering(*base_mesh_node);
1763
1764 base_mesh_node.reset();
1765
1766 // loop over all global layers in reverse order (coarse to fine)
1767 for(std::size_t slayer = this->_ancestry.size(); slayer > std::size_t(0); )
1768 {
1769 // is this the base-mesh layer aka the 1-process layer?
1770 const bool is_base_layer = (slayer == this->_ancestry.size());
1771
1772 --slayer;
1773
1774 // get the ancestor object
1775 Ancestor& ancestor = this->_ancestry.at(slayer);
1776
1777 // determine the minimum desired level of our parent layer
1778 int parent_min_lvl = -1;
1779 if(!is_base_layer)
1780 parent_min_lvl = this->_chosen_levels.front().first;
1781 else if(this->_ancestry.size() + 1u < this->_desired_levels.size())
1782 parent_min_lvl = this->_desired_levels.back().first;
1783
1784 // check available partitioning strategies
1785 this->_check_parti(ancestor, *parent_mesh_node, is_base_layer);
1786
1787 // the check_parti function returns the partitioning level w.r.t. the current
1788 // level (which may be > 0), so we have to compensate that by adding our current level:
1789 ancestor.parti_level += lvl;
1790 ancestor.parti_level = Math::max(ancestor.parti_level, ancestor.desired_level_min);
1791
1792 // Note: each progeny group within the main communicator may have chosen a different
1793 // partitioning level at this point. We will compensate this by adjusting the minimum
1794 // refinement level of the child layer after the partitioning step below.
1795
1796 // refine up to the chosen partitioning level
1797 //for(; lvl < ancestor.parti_level; ++lvl)
1798 for(; lvl <= ancestor.desired_level_max; ++lvl)
1799 {
1800 // can we apply a partitioner?
1801 if(lvl >= ancestor.parti_level)
1802 {
1803 // try to apply the partitioner
1804 this->_apply_parti(ancestor, *parent_mesh_node);
1805
1806 // partitioning successful?
1807 int parti_ok = ancestor.parti_found ? 1 : 0;
1808
1809 // check if all processes found a valid partitioning
1810 this->_comm.allreduce(&parti_ok, &parti_ok, std::size_t(1), Dist::op_min);
1811 if(parti_ok > 0)
1812 break;
1813
1814 // nope, at least one process did not receive a partition, so keep refining
1815 ancestor.parti_found = false;
1816 }
1817
1818 // no partitioning found?
1819 if(lvl >= ancestor.desired_level_max)
1820 break;
1821
1822 // refine and deslag the parent mesh node
1823 auto refined_node = parent_mesh_node->refine_unique(this->_adapt_mode);
1824
1825 // a level pointer for the unrefined level; we need this to access the parent layering
1826 std::shared_ptr<LevelType> level_ptr;
1827
1828 // push the base mesh into our parent layer if desired
1829 if((ancestor.layer_p >= 0) && (parent_min_lvl >= 0) && (lvl >= parent_min_lvl))
1830 {
1831 level_ptr = std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1832 std::move(parent_slag_node), std::move(parent_coloring), std::move(parent_layering));
1833 this->push_level_front(ancestor.layer_p, level_ptr);
1834 }
1835
1836 // continue with refined node
1837 parent_slag_node = std::move(refined_node);
1838 parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node, ancestor, !is_base_layer);
1839 parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_refined_mesh_coloring(*parent_slag_node);
1840 parent_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*parent_mesh_node) : this->_compute_refined_mesh_layering(*parent_slag_node,
1841 level_ptr ? level_ptr->element_layering : parent_layering);
1842 }
1843
1844 // no valid partitioning found?
1845 XASSERTM(ancestor.parti_found, "VoxelDomainControl failed to find a valid partitioning");
1846
1847 // set the selected partitioner level
1848 ancestor.parti_level = lvl;
1849
1850 // extract our patch
1851 std::vector<int> neighbor_ranks;
1852 std::unique_ptr<MeshNodeType> patch_mesh_node(
1853 parent_mesh_node->extract_patch(neighbor_ranks, ancestor.parti_graph, ancestor.progeny_child));
1854
1855 // extract our coloring
1856 Adjacency::Coloring patch_coloring = this->_extract_patch_coloring(*parent_mesh_node, parent_coloring, ancestor.progeny_child);
1857
1858 // extract our layering
1859 Adjacency::Coloring patch_layering = this->_extract_patch_layering(*parent_mesh_node, parent_layering, ancestor.progeny_child);
1860
1861 // create an empty patch slag node
1862 std::unique_ptr<MeshNodeType> patch_slag_node;
1863
1864 // translate neighbor ranks by progeny group to obtain the neighbor ranks
1865 // w.r.t. this layer's communicator
1866 {
1867 std::map<int,int> halo_map;
1868 for(auto& i : neighbor_ranks)
1869 {
1870 int old_i(i);
1871 halo_map.emplace(old_i, i += ancestor.progeny_group);
1872 }
1873 patch_mesh_node->rename_halos(halo_map);
1874 }
1875
1876 // does this process participate in the parent layer or do we need to keep the base-meshes anyways?
1877 if(ancestor.layer_p >= 0)
1878 {
1879 // Note: patch mesh-part for rank = 0 was already created by 'extract_patch' call
1880 for(int i(1); i < ancestor.num_parts; ++i)
1881 {
1882 parent_mesh_node->create_patch_meshpart(ancestor.parti_graph, i);
1883 }
1884 }
1885
1886 // make sure we choose the same minimum level for all processes, because we may
1887 // have chosen different partitioning levels for each patch
1888 int global_level_min = Math::max(ancestor.desired_level_min, ancestor.parti_level);
1889 this->_comm.allreduce(&global_level_min, &global_level_min, std::size_t(1), Dist::op_max);
1890
1891 // make sure our minimum level is greater than the minimum level of the previous layer,
1892 // because each layer must contain at least one non-ghost level
1893 if(!is_base_layer)
1894 global_level_min = Math::max(global_level_min, this->_chosen_levels.front().first+1);
1895
1896 // refine up to desired minimum level of this layer
1897 XASSERTM(lvl == global_level_min, "INTERNAL ERROR");
1898 /*for(; lvl < global_level_min; ++lvl)
1899 {
1900 // refine the base mesh node
1901 std::unique_ptr<MeshNodeType> coarse_slag_node(std::move(parent_slag_node));
1902 std::unique_ptr<MeshNodeType> coarse_mesh_node(std::move(parent_mesh_node));
1903 parent_slag_node = coarse_mesh_node->refine_unique(this->_adapt_mode);
1904 parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node);
1905
1906 // push base mesh to parent layer if desired
1907 if((ancestor.layer_p >= 0) && (parent_min_lvl >= 0) && (lvl >= parent_min_lvl))
1908 {
1909 // clear patches before pushing this node as they are redundant here
1910 //coarse_mesh_node->clear_patches();
1911 this->push_level_front(ancestor.layer_p, std::make_shared<LevelType>(lvl, std::move(coarse_mesh_node), std::move(coarse_slag_node)));
1912 }
1913
1914 // refine the patch mesh
1915 //patch_slag_node = patch_mesh_node->refine_unique(this->_adapt_mode);
1916 //patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node);
1917 patch_mesh_node = this->_deslag_mesh_node(*patch_mesh_node->refine_unique(this->_adapt_mode));
1918 }*/
1919
1920 // split the halos of our base-mesh and compute the halos of our patches from that
1921 //this->_split_basemesh_halos(ancestor, *parent_slag_node, *patch_slag_node, neighbor_ranks);
1922 this->_split_basemesh_halos(ancestor, *parent_mesh_node, *patch_mesh_node, neighbor_ranks);
1923
1924 // does this process participate in the child layer?
1925 if(ancestor.layer >= 0)
1926 {
1927 // set the neighbor ranks in our child layer
1928 this->_layers.at(std::size_t(ancestor.layer))->set_neighbor_ranks(neighbor_ranks);
1929 }
1930
1931 // set chosen minimum level for this layer
1932 if(!is_base_layer)
1933 this->_chosen_levels.push_front(std::make_pair(lvl, this->_ancestry.at(slayer+1u).num_procs));
1934 else if(parent_min_lvl < 0)
1935 this->_chosen_levels.push_front(std::make_pair(lvl, 0));
1936 else
1937 {
1938 this->_chosen_levels.push_front(std::make_pair(parent_min_lvl, 0));
1939 this->_chosen_levels.push_front(std::make_pair(lvl, 1));
1940 }
1941
1942 // a level pointer for the unrefined level; we need this to access the parent layering
1943 std::shared_ptr<LevelType> level_ptr;
1944
1945 // push the finest base-mesh
1946 if(ancestor.layer_p >= 0)
1947 {
1948 this->push_level_front(ancestor.layer_p, std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1949 std::move(parent_slag_node), std::move(parent_coloring), std::move(parent_layering)));
1950 }
1951
1952 // continue with the next layer
1953 parent_slag_node = std::move(patch_slag_node);
1954 parent_mesh_node = std::move(patch_mesh_node);
1955 parent_coloring = std::move(patch_coloring);
1956 parent_layering = std::move(patch_layering);
1957 }
1958
1959 // get the desired maximum level
1960 // if we have more than one layer, make sure that the finest one contains at
1961 // least one level, as otherwise the finest global level would be a ghost level
1962 int desired_level_max = Math::max(this->_ancestry.front().desired_level_max, lvl+1);
1963
1964 for(; lvl < desired_level_max; ++lvl)
1965 {
1966 // refine the patch mesh
1967 auto refined_node = parent_mesh_node->refine_unique(this->_adapt_mode);
1968
1969 // a level pointer for the unrefined level; we need this to access the parent layering
1970 std::shared_ptr<LevelType> level_ptr = std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1971 std::move(parent_slag_node), std::move(parent_coloring), std::move(parent_layering));
1972
1973
1974 // push patch mesh to this level
1975 this->push_level_front(0, level_ptr);
1976
1977 // continue with refined node
1978 parent_slag_node = std::move(refined_node);
1979 parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node, this->_ancestry.front(), false);
1980 parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_refined_mesh_coloring(*parent_slag_node);
1981 parent_layering = _unstructered_mesh ? this->_compute_unstructered_layering(*parent_mesh_node) : this->_compute_refined_mesh_layering(*parent_slag_node, level_ptr->element_layering);
1982 }
1983
1984 // set chosen maximum level for finest layer
1985 this->_chosen_levels.push_front(std::make_pair(lvl, this->_comm.size()));
1986
1987 // push finest level
1988 this->push_level_front(0, std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1989 std::move(parent_slag_node), std::move(parent_coloring), std::move(parent_layering)));
1990 }
1991
1992#endif // defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
1993 }; // class VoxelDomainControl<...>
1994 } // namespace Domain
1995 } // namespace Control
1996} // namespace FEAT
#define XABORTM(msg)
Abortion macro definition with custom message.
Definition: assertion.hpp:192
#define ASSERTM(expr, msg)
Debug-Assertion macro definition with custom message.
Definition: assertion.hpp:230
#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.
Coloring object implementation.
Definition: coloring.hpp:37
bool empty() const
Checks whether the coloring is empty.
Definition: coloring.hpp:223
Index * get_coloring()
Returns the coloring array.
Definition: coloring.hpp:175
Index get_num_colors() const
Returns the number of colors.
Definition: coloring.hpp:217
Adjacency Graph implementation.
Definition: graph.hpp:34
bool _keep_base_levels
keep base-mesh levels on root process?
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
String parti_info
a string containing some information about the chosen partitioning
bool parti_found
specifies whether a partitioning was found
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
Base-Class for Hierarchical Domain Control implementations.
std::deque< std::pair< int, int > > _desired_levels
desired level deque
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
std::deque< Ancestor > _ancestry
the partition ancestry deque
Geometry::RootMeshNode< MeshType > MeshNodeType
our root mesh node type
LevelType::PartType MeshPartType
our mesh-part type
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.
bool _was_created
specifies whether the domain control was already created
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.
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.
Hierarchical partitioned Voxel Domain Control.
void create_voxel_map_from_off(std::istream &is, bool invert, Real resolution)
Creates a voxel map based on a surface triangulation stored in an OFF file.
Adjacency::Coloring _extract_patch_coloring(const MeshNodeType &slag_node, const Adjacency::Coloring &parent_coloring, int child_rank) const
Extracts a patch coloring from the parent mesh coloring.
Adjacency::Coloring _compute_base_mesh_coloring(const MeshNodeType &slag_node) const
Computes the coloring for the unpartitioned base mesh.
virtual void _create_multi_layered(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a multi-layered mesh hierarchy.
std::unique_ptr< MeshNodeType > _base_mesh_node
the input base-mesh node on level 0
virtual std::unique_ptr< MeshNodeType > _deslag_mesh_node(MeshNodeType &mesh_node)
Deslags an unpartitioned mesh node including its mesh-parts.
std::vector< Index > _gather_masked_elements(const MeshType &mesh) const
Gather a vector of element indices that intersect the masked domain.
Geometry::VoxelMap::WriteResult write_voxel_map(const String &filename)
Writes the voxel map to a voxel map file.
void create_hierarchy()
Creates the domain level hierarchy.
bool _unstructered_mesh
specifies whether we use a non voxel base mesh
const StopWatch & get_watch_voxel_map() const
Returns a const reference to the StopWatch that measures the voxel-map creation phase.
Geometry::RootMeshNode< MeshType > MeshNodeType
our root mesh node type
static constexpr int shape_dim
shape dimension
void _precreate_voxel_map(Real resolution)
Pre-creates the voxel map by setting the bounding box and resolution.
Control::Domain::PartiDomainControlBase< DomainLevel_ > BaseClass
our base class
virtual void create(Geometry::MeshFileReader &)
Creates a domain control from a MeshFileReader.
const StopWatch & get_watch_hierarchy() const
Returns a const reference to the StopWatch that measures the base-mesh creation phase.
Adjacency::Coloring _compute_refined_mesh_layering(const MeshNodeType &slag_node, const Adjacency::Coloring &coarse_layering) const
Computes the layering for a refined mesh.
virtual void create(std::unique_ptr< MeshNodeType > &)
Creates a domain control from a base-mesh node.
Tiny::Vector< CoordType, shape_dim > _bbox_min
the bounding box of the structured domain
bool _keep_voxel_map
specifies whether to keep the voxel map after hierarchy creation
StopWatch _watch_create_base_mesh
a bunch of stop-watches
void create_voxel_map_from_lambda(Lambda_ &&lambda, Real resolution)
Creates a voxel map based on a lambda expression.
Adjacency::Coloring _compute_refined_mesh_coloring(const MeshNodeType &slag_node) const
Computes the coloring for a refined mesh.
MeshNodeType & create_base_mesh_2d(Index num_x, Index num_y, CoordType x_min, CoordType x_max, CoordType y_min, CoordType y_max)
Creates a 2D structured rectangle base mesh and returns its base-mesh node.
int _create_stage
creation stage to keep track what has already been initialized
void create_voxel_map(Geometry::VoxelMasker< CoordType, shape_dim > &masker, Real resolution=Real(0))
Creates a voxel map based on a VoxelMasker object.
virtual void create(const std::deque< String > &filenames, String dirpath="")
Creates a domain control from a list of filenames.
std::vector< WeightType > _gather_element_weights(const MeshType &mesh) const
Gathers the element slag weights for a given mesh.
const StopWatch & get_watch_base_mesh() const
Returns a const reference to the StopWatch that measures the base-mesh creation phase.
MeshType::CoordType CoordType
coordinate type
void set_voxel_map_out_of_bounds_balue(bool value)
Sets the out-of-bounds value for the voxel map.
std::vector< int > gather_vertex_voxel_map(Index level) const
Gathers the vertex voxel map on a given domain level.
virtual std::unique_ptr< MeshNodeType > _deslag_mesh_node(MeshNodeType &mesh_node, const Ancestor &ancestor, bool is_child)
Deslags a (potentially partitioned) mesh node including its mesh-parts and halos.
virtual void _create_single_layered(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a single-layered mesh hierarchy.
std::vector< WeightType > gather_element_voxel_weights(Index level) const
Gathers the element voxel map weights on a given domain level.
std::array< Index, shape_dim > _base_slices
number of element slices on base mesh level 0
Adjacency::Coloring _extract_patch_layering(const MeshNodeType &slag_node, const Adjacency::Coloring &parent_layering, int child_rank) const
Extracts a patch layering from the parent mesh layering.
VoxelDomainControl(const Dist::Comm &comm_, bool support_multi_layered)
Constructor.
Adjacency::Coloring _compute_base_mesh_layering(const MeshNodeType &slag_node) const
Computes the element layering for the unpartitioned base mesh.
MeshNodeType & create_base_mesh_3d(Index num_x, Index num_y, Index num_z, CoordType x_min, CoordType x_max, CoordType y_min, CoordType y_max, CoordType z_min, CoordType z_max)
Creates a 3D structured cuboid base mesh and returns its base-mesh node.
void create_voxel_map_from_chart(const Geometry::Atlas::ChartBase< MeshType > &chart, bool invert, Real resolution)
Creates a voxel map based on a chart.
Geometry::VoxelMap::ReadResult read_voxel_map(const String &filename)
Reads the voxel map from a voxel map file.
virtual void _create_single_process(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a single-layered mesh hierarchy for a single process.
const Geometry::VoxelMap & get_voxel_map() const
Returns a const reference to the internal voxel map.
void keep_voxel_map()
Instructs the domain controller to keep the voxel map after hierarchy creation.
void create_voxel_map_from_off(const String &filename, bool invert, Real resolution)
Creates a voxel map based on a surface triangulation stored in an OFF file.
std::unique_ptr< Geometry::VoxelMap > _voxel_map
the voxel map
virtual void _deslag_patch_halos(MeshNodeType &patch_mesh_node, const MeshNodeType &base_mesh_node, const Ancestor &ancestor, bool is_child)
Deslags the halos of a patch mesh node.
String dump_slag_layer_levels() const
Debugging function: Returns a string containing encoded slag layer level information.
Wrapper class for domain levels for VoxelDomainControl.
Adjacency::Coloring element_layering
element layering (stored as coloring)
DomainLevel_ BaseClass
our base-class; the actual domain level
std::shared_ptr< DomainLevel_ > slag_level
attachable slag level
Adjacency::Coloring element_coloring
element coloring
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
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
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
Communication Request vector class.
Definition: dist.hpp:640
void wait_all()
Blocks until all active requests are fulfilled.
Definition: dist.cpp:324
static void read_common(std::stringstream &stream, const String &filename, const Dist::Comm &comm, int root_rank=0)
Reads a common text file for all ranks.
MeshType * get_mesh()
Returns the mesh of this node.
Definition: mesh_node.hpp:225
Class template for partial meshes.
Definition: mesh_part.hpp:90
Base-Mesh Patch Halo splitter.
std::unique_ptr< RootMeshNode > extract_patch(std::vector< Index > &&elements, bool split_meshparts, bool split_halos, bool split_patches)
Extracts a patch from the root mesh as a new mesh node.
Definition: mesh_node.hpp:1151
const MeshPartType * get_patch(int rank) const
Returns a patch meshpart for a given child.
Definition: mesh_node.hpp:962
static std::unique_ptr< RootMeshNode > make_unique(std::unique_ptr< MeshType > mesh, MeshAtlasType *atlas=nullptr)
Creates a new RootMeshNode on the heap and returns a unique pointer to it.
Definition: mesh_node.hpp:837
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
Structured unit-cube mesh factory.
Target set class.
Definition: target_set.hpp:27
Index get_num_entities() const
Returns the number of entities.
Definition: target_set.hpp:92
helper class for read function result
Definition: voxel_map.hpp:502
helper class for write function result
Definition: voxel_map.hpp:462
Interface for voxel masker for the VoxelMap class.
Definition: voxel_map.hpp:71
Math Limits class template.
Definition: math.hpp:1475
static double toe_partition
time of partitioning in seconds, needs initialization
Definition: statistics.hpp:167
Stop-Watch class.
Definition: stop_watch.hpp:21
double elapsed() const
Returns the total elapsed time in seconds.
Definition: stop_watch.hpp:70
void start()
Starts the stop-watch.
Definition: stop_watch.hpp:43
void stop()
Stops the stop-watch and increments elapsed time.
Definition: stop_watch.hpp:51
String class implementation.
Definition: string.hpp:46
String pad_front(size_type len, char c=' ') const
Pads the front of the string up to a desired length.
Definition: string.hpp:392
Tiny Vector class template.
@ transpose
Render-Transpose mode.
@ injectify
Render-Injectified mode.
const Operation op_min(MPI_MIN)
Operation wrapper for MPI_MIN.
Definition: dist.hpp:275
const Operation op_max(MPI_MAX)
Operation wrapper for MPI_MAX.
Definition: dist.hpp:273
void minimax(T_ x, T_ &a, T_ &b)
Updates the minimum and maximum.
Definition: math.hpp:195
T_ min(T_ a, T_ b)
Returns the minimum of two values.
Definition: math.hpp:123
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:944
@ value
specifies whether the space should supply basis function values
std::uint64_t Index
Index data type.