FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
cgal_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/trafo/standard/mapping.hpp>
29
30#include <control/domain/parti_domain_control_base.hpp>
31
32#if defined(FEAT_HAVE_CGAL) || defined(DOXYGEN)
33namespace FEAT
34{
35 namespace Control
36 {
37 namespace Domain
38 {
49 template<typename DomainLevel_>
51 public DomainLevel_
52 {
53 public:
55 typedef DomainLevel_ BaseClass;
57 using typename BaseClass::MeshType;
59 using typename BaseClass::MeshNodeType;
60
62 std::shared_ptr<DomainLevel_> slag_level;
63
66
67 explicit CGALDomainLevelWrapper(int _lvl_idx, std::unique_ptr<MeshNodeType> node,
68 Adjacency::Coloring&& coloring) :
69 BaseClass(_lvl_idx, std::move(node)),
70 slag_level(),
71 element_coloring(std::forward<Adjacency::Coloring>(coloring))
72 {
73 }
74
75 explicit CGALDomainLevelWrapper(int _lvl_idx, std::unique_ptr<MeshNodeType> node,
76 std::unique_ptr<MeshNodeType> slag_node, Adjacency::Coloring&& coloring) :
77 BaseClass(_lvl_idx, std::move(node)),
78 slag_level(),
79 element_coloring(std::forward<Adjacency::Coloring>(coloring))
80 {
81 if(slag_node)
82 slag_level = std::make_shared<DomainLevel_>(_lvl_idx, std::move(slag_node));
83 }
84 }; // class CGALDomainLevelWrapper<...>
85
91 template<typename DomainLevel_>
93 public Control::Domain::PartiDomainControlBase< DomainLevel_ >
94 {
95 public:
99 using typename BaseClass::LevelType;
101 using typename BaseClass::LayerType;
103 using typename BaseClass::MeshType;
105 using typename BaseClass::AtlasType;
107 using typename BaseClass::MeshNodeType;
109 using typename BaseClass::MeshPartType;
111 using typename BaseClass::WeightType;
113 using typename BaseClass::Ancestor;
114
116 static constexpr int shape_dim = MeshType::shape_dim;
117 static_assert(shape_dim == 3);
118
120 typedef typename MeshType::CoordType CoordType;
121
122 protected:
125
129 std::array<Index, shape_dim> _base_slices;
130
132 std::unique_ptr<Geometry::CGALWrapper<CoordType>> _cgal_wrapper;
133
135 std::unique_ptr<MeshNodeType> _base_mesh_node;
136
138 mutable StopWatch _watch_create_base_mesh, _watch_create_cgal_wrapper, _watch_create_hierarchy, _watch_calc_weights;
139
142
144 bool _unstructered_mesh = false;
145
146 bool _cheap_weights = true;
147
148 public:
158 explicit CGALDomainControl(const Dist::Comm& comm_, bool support_multi_layered) :
159 BaseClass(comm_, support_multi_layered),
160 _create_stage(0),
161 _keep_cgal_wrapper(false),
162 _unstructered_mesh(false),
163 _cheap_weights(true)
164 {
165 }
166
169 {
170 }
171
185 {
186 this->_keep_cgal_wrapper = true;
187 }
188
190 void cheap_weight_calculation(bool option)
191 {
192 this->_cheap_weights = option;
193 }
194
195 Tiny::Vector<CoordType, shape_dim> get_bounding_box_min() const
196 {
197 return _bbox_min;
198 }
199
200 Tiny::Vector<CoordType, shape_dim> get_bounding_box_max() const
201 {
202 return _bbox_max;
203 }
204
231 CoordType y_min, CoordType y_max, CoordType z_min, CoordType z_max)
232 {
233 XASSERTM(_create_stage == 0, "base mesh already created!");
234
235 XASSERT(num_x > Index(0));
236 XASSERT(num_y > Index(0));
237 XASSERT(num_z > Index(0));
238 XASSERT(x_min < x_max);
239 XASSERT(y_min < y_max);
240 XASSERT(z_min < z_max);
241
243
244 _base_slices[0] = num_x;
245 _base_slices[1] = num_y;
246 _base_slices[2] = num_z;
247 _bbox_min[0] = x_min;
248 _bbox_max[0] = x_max;
249 _bbox_min[1] = y_min;
250 _bbox_max[1] = y_max;
251 _bbox_min[2] = z_min;
252 _bbox_max[2] = z_max;
253
254 // create a structured mesh from factory
256 Geometry::StructUnitCubeFactory<MeshType>::make_unique_from(num_x, num_y, num_z), &this->_atlas);
257
258 // get the node's vertex set
259 auto& vtx = _base_mesh_node->get_mesh()->get_vertex_set();
260 Index n = vtx.get_num_vertices();
261 for(Index i(0); i < n; ++i)
262 {
263 vtx[i][0] = x_min + (x_max - x_min) * vtx[i][0];
264 vtx[i][1] = y_min + (y_max - y_min) * vtx[i][1];
265 vtx[i][2] = z_min + (z_max - z_min) * vtx[i][2];
266 }
267
269
270 // update creation stage
271 this->_create_stage = 1;
272
273 // return the base-mesh node
274 return *_base_mesh_node.get();
275 }
276
282 MeshNodeType& set_base_mesh(std::unique_ptr<MeshNodeType>&& mesh_node)
283 {
284 XASSERTM(_create_stage == 0, "base mesh already created!");
285
287 _base_mesh_node = std::move(mesh_node);
288
289 for(std::size_t k = 0; k < std::size_t(shape_dim); ++k)
290 {
292 }
293
294 for(int k = 0; k < shape_dim; ++k)
295 {
297 _bbox_max[k] = Math::Limits<CoordType>::lowest();
298 }
299
300 auto& vtx = _base_mesh_node->get_mesh()->get_vertex_set();
301 Index n = vtx.get_num_vertices();
302 for(Index i(0); i < n; ++i)
303 {
304 for(int k = 0; k < shape_dim; ++k)
305 {
306 _bbox_min[k] = Math::min(_bbox_min[k], vtx[i][k]);
307 _bbox_max[k] = Math::max(_bbox_max[k], vtx[i][k]);
308 }
309 }
310
312
313 // update creation stage
314 this->_create_stage = 1;
315
316 // update unstructered mesh flag
317 this->_unstructered_mesh = true;
318
319 // return the base-mesh node
320 return *_base_mesh_node.get();
321 }
322
341 void create_cgal_wrapper(std::istream& file, Geometry::CGALFileMode file_mode)
342 {
343 XASSERTM(this->_create_stage <= 1, "invalid creation stage: hirarchy already created");
344
345 this->_watch_create_cgal_wrapper.start();
346#ifdef FEAT_COMPILER_CLANG
347 this->_cgal_wrapper.reset(new Geometry::CGALWrapper<CoordType>(file, file_mode));
348#else
349 this->_cgal_wrapper = std::make_unique<Geometry::CGALWrapper<CoordType>>(file, file_mode);
350#endif
351 this->_watch_create_cgal_wrapper.stop();
352 }
353
356 {
357 // read in istream in an mpi friendly matter
358 this->_watch_create_cgal_wrapper.start();
359 std::stringstream cgal_input;
360 DistFileIO::read_common(cgal_input, file, this->_comm);
361 this->_watch_create_cgal_wrapper.stop();
362 create_cgal_wrapper(cgal_input, file_mode);
363 }
364
372 {
373 XASSERTM(this->_create_stage >= 1, "invalid creation stage; create the base-mesh and set the cgal wrapper first");
374 XASSERTM(this->_create_stage <= 1, "invalid creation stage: domain hierarchy already created");
375
376 this->_watch_create_hierarchy.start();
377
378 // create the domain control
379#ifdef FEAT_HAVE_MPI
380 if(this->_comm.size() == 1)
381 {
382 // We've got just one process, so it's a simple choice:
383 this->_create_single_process(std::move(this->_base_mesh_node));
384 }
385 else if(this->_support_multi_layered && (this->_desired_levels.size() > std::size_t(2)))
386 {
387 // The application supports multi-layered domain controls and
388 // the user wants that, so create a multi-layered one:
389 this->_create_multi_layered(std::move(this->_base_mesh_node));
390 }
391 else
392 {
393 // Create a single-layered domain control:
394 this->_create_single_layered(std::move(this->_base_mesh_node));
395 }
396#else // not FEAT_HAVE_MPI
397 {
398 // In the non-MPI case, we always have only one process:
399 this->_create_single_process(std::move(this->_base_mesh_node));
400 }
401#endif // FEAT_HAVE_MPI
402
403 // compile virtual levels
404 this->compile_virtual_levels();
405
406 this->_watch_create_hierarchy.stop();
407
408 // collect statistics
409 FEAT::Statistics::toe_partition = this->_watch_create_base_mesh.elapsed() +
410 this->_watch_create_cgal_wrapper.elapsed() + this->_watch_create_hierarchy.elapsed();
411
412 // update creation stage
413 this->_create_stage = 2;
414 this->_was_created = true;
415
416 // delete the voxel map unless we're meant to keep it
417 if(!this->_keep_cgal_wrapper)
418 this->_cgal_wrapper.reset();
419 }
420
430 {
431 return this->_cgal_wrapper.release();
432 }
433
436 {
437 return this->_cgal_wrapper.get();
438 }
439
442 {
443 return this->_cgal_wrapper.get();
444 }
445
446 void set_cgal_wrapper(std::unique_ptr<Geometry::CGALWrapper<CoordType>>&& cgal_wrapper)
447 {
448 this->_cgal_wrapper = std::move(cgal_wrapper);
449 }
450
451 bool cgal_wrapper_empty() const
452 {
453 return !this->_cgal_wrapper;
454 }
455
458 {
459 return this->_watch_create_base_mesh;
460 }
461
464 {
465 return this->_watch_create_cgal_wrapper;
466 }
467
470 {
471 return this->_watch_create_hierarchy;
472 }
473
474 const StopWatch& get_watch_weights() const
475 {
476 return this->_watch_calc_weights;
477 }
478
491 std::vector<WeightType> gather_element_weights(Index level) const
492 {
493 XASSERT(level < this->size_physical());
494 return this->_gather_element_weights(this->at(level)->get_mesh());
495 }
496
501 {
502 String msg;
503 for(auto it = this->_layer_levels.begin(); it != this->_layer_levels.end(); ++it)
504 {
505 if(it != this->_layer_levels.begin())
506 msg += " |";
507 for(auto jt = it->begin(); jt != it->end(); ++jt)
508 {
509 msg += " " + stringify((*jt)->get_level_index());
510 msg += "[";
511 msg += stringify((*jt)->get_mesh().get_num_elements()).pad_front(4);
512 msg += "]";
513 if((*jt)->slag_level)
514 ((msg += "<[") += stringify((*jt)->slag_level->get_mesh().get_num_elements()).pad_front(4)) += "]";
515 }
516 }
517 return msg;
518 }
519
529 virtual void create(const std::deque<String>& filenames, String dirpath = "")
530 {
531 //dummy implementation
532 ASSERTM(false, "Thou shall not arrive here!");
533 (void) filenames;
534 (void) dirpath;
535 }
536
544 {
545 //dummy implementation
546 ASSERTM(false, "Thou shall not arrive here!");
547 }
548
555 virtual void create(std::unique_ptr<MeshNodeType>&)
556 {
557 //dummy implementation
558 ASSERTM(false, "Thou shall not arrive here!");
559 }
560
561 protected:
576 std::vector<Index> _gather_masked_elements(const MeshType& mesh) const
577 {
578 const Index num_elems = mesh.get_num_elements();
579
580 // reserve the vector
581 std::vector<Index> masked_elems;
582 masked_elems.reserve(num_elems);
583
584 // get the vertex set and the vertices-at-element index set
585 const auto& vtx = mesh.get_vertex_set();
586 const auto& verts_at_elem = mesh.template get_index_set<shape_dim, 0>();
587
588 // loop over all elements
589 FEAT_PRAGMA_OMP(parallel for)
590 for(Index ielem = 0; ielem < num_elems; ++ielem)
591 {
592 std::array<Tiny::Vector<CoordType, shape_dim>, std::decay_t<decltype(verts_at_elem)>::num_indices> verts;
593 for(std::size_t k = 0; k < verts.size(); ++k)
594 {
595 verts[k] = vtx[verts_at_elem(ielem, int(k))];
596 }
597
598 if(this->_cgal_wrapper->intersects_polygon(verts))
599 masked_elems.push_back(ielem);
600 }
601
602 // return the list of all elements that we found
603 return masked_elems;
604 }
605
622 std::vector<Index> _gather_masked_elements(const MeshType& mesh, const Dist::Comm& sibling_comm) const
623 {
624 const Index num_elems = mesh.get_num_elements();
625 const Index num_procs = Index(sibling_comm.size());
626 const Index cur_rank = Index(sibling_comm.rank());
627 const Index elems_per_rank = num_elems/num_procs + Index((num_elems%num_procs)>0u);
628
629 // reserve the vector
630 std::vector<Index> masked_elems;
631 masked_elems.reserve(elems_per_rank);
632
633 // get the vertex set and the vertices-at-element index set
634 const auto& vtx = mesh.get_vertex_set();
635 const auto& verts_at_elem = mesh.template get_index_set<shape_dim, 0>();
636
637 // loop over all elements
638 for(Index ielem = cur_rank*elems_per_rank; ielem < Math::min((cur_rank+1u)*elems_per_rank, num_elems); ++ielem)
639 {
640 std::array<Tiny::Vector<CoordType, shape_dim>, std::decay_t<decltype(verts_at_elem)>::num_indices> verts;
641 for(std::size_t k = 0; k < verts.size(); ++k)
642 {
643 verts[k] = vtx[verts_at_elem(ielem, int(k))];
644 }
645
646 if(this->_cgal_wrapper->intersects_polygon(verts))
647 masked_elems.push_back(ielem);
648 }
649
650 // communicate sizes of buffers
651 std::vector<int> recv_sizes(num_procs);
652 recv_sizes.at(cur_rank) = int(masked_elems.size());
653
654 sibling_comm.allgather(recv_sizes.data(), 1, recv_sizes.data(), 1);
655
656 // and now, create big buffer
657 std::vector<Index> gathered_mask(std::size_t(std::accumulate(recv_sizes.begin(), recv_sizes.end(), 0)), 0u);
658
659 // and also, we have to caclulate our displacements into our big buffer
660 std::vector<int> displacements;
661 displacements.reserve(recv_sizes.size());
662 std::exclusive_scan(recv_sizes.begin(), recv_sizes.end(), std::back_inserter(displacements), 0);
663
664 // and now gather
665 sibling_comm.allgatherv(masked_elems.data(), masked_elems.size(), gathered_mask.data(), recv_sizes.data(), displacements.data());
666
667 // return the list of all elements that we found
668 return gathered_mask;
669 }
670
683 std::vector<WeightType> _gather_element_weights(const MeshType& mesh) const
684 {
685 this->_watch_calc_weights.start();
686 const Index num_elems = mesh.get_num_elements();
687
688 // allocate the weight vector
689 std::vector<WeightType> weights(num_elems, 0.0);
690
691 if(!_cgal_wrapper)
692 return weights;
693
694 Cubature::DynamicFactory cub_fac("refine*4:gauss-legendre:3");
696 cub_fac.create(rule);
697
698 // guarantee that cgal wrapper is initialized
699 this->_cgal_wrapper->point_not_outside(CoordType(0), CoordType(0), CoordType(0));
700
701 // create trafo
702 typedef Trafo::Standard::Mapping<MeshType> TrafoType;
703 typedef typename TrafoType::template Evaluator<typename MeshType::ShapeType, CoordType>::Type TrafoEvaluator;
704 static constexpr TrafoTags trafo_config = TrafoTags::dom_point | TrafoTags::img_point | TrafoTags::jac_det;
705
707 typedef typename TrafoEvaluator::template ConfigTraits<trafo_config>::EvalDataType TrafoEvalData;
708
709 const TrafoType trafo(const_cast<MeshType&>(mesh));
710
711 FEAT_PRAGMA_OMP(parallel)
712 {
713 TrafoEvaluator trafo_eval(trafo);
714 TrafoEvalData trafo_data;
715
716 // loop over all elements
717 FEAT_PRAGMA_OMP(for)
718 for(Index ielem = 0; ielem < num_elems; ++ielem)
719 {
720 trafo_eval.prepare(ielem);
721 CoordType loc_sum = CoordType(0);
722 for(int k = 0; k < rule.get_num_points(); ++k)
723 {
724 const auto ref_point = rule.get_point(k);
725 trafo_eval(trafo_data, ref_point);
726 const auto& img_point = trafo_data.img_point;
727 const CoordType weight = trafo_data.jac_det * rule.get_weight(k);
728 loc_sum += weight * CoordType(this->_cgal_wrapper->point_not_outside(img_point[0], img_point[1], img_point[2]));
729 }
730 loc_sum /= trafo_eval.volume();
731 ASSERTM(loc_sum >= CoordType(0) && loc_sum <= CoordType(1), "Local sum not between zero and one");
732
733 // gather element weight based on the bounding box
734 weights[ielem] = loc_sum;
735 }
736 }
737 this->_watch_calc_weights.stop();
738
739 // returns the element weights
740 return weights;
741 }
742
743 std::vector<WeightType> _compute_weights(Ancestor& DOXY(ancestor), const MeshNodeType& base_mesh_node) override
744 {
745 std::vector<WeightType> weights;
746 if(!this->_cheap_weights)
747 weights = this->_gather_element_weights(*base_mesh_node.get_mesh());
748
749 return weights;
750 }
751
767 virtual std::unique_ptr<MeshNodeType> _deslag_mesh_node(MeshNodeType& mesh_node)
768 {
769 XASSERTM(mesh_node.get_halo_map().empty(), "This function must not be used for partitioned mesh nodes!");
770 // it is always silenetly assumed that we call this function on the complete communicator
771 return mesh_node.extract_patch(_gather_masked_elements(*mesh_node.get_mesh(), this->_comm), true, false, false);
772 }
773
792 virtual std::unique_ptr<MeshNodeType> _deslag_mesh_node(MeshNodeType& mesh_node, const Ancestor& ancestor, bool is_child)
793 {
794 const auto* prod_comm = is_child ? &ancestor.progeny_comm : nullptr;
795 std::unique_ptr<MeshNodeType> new_node = prod_comm ? mesh_node.extract_patch(_gather_masked_elements(*mesh_node.get_mesh(), *prod_comm), true, false, false):
796 mesh_node.extract_patch(_gather_masked_elements(*mesh_node.get_mesh()), true, false, false);
797 this->_deslag_patch_halos(*new_node, mesh_node, ancestor, is_child);
798 return new_node;
799 }
800
819 MeshNodeType& patch_mesh_node,
820 const MeshNodeType& base_mesh_node,
821 const Ancestor& ancestor,
822 bool is_child)
823 {
824 // get the map of the base-mesh halos
825 const std::map<int, std::unique_ptr<MeshPartType>>& base_halo_map = base_mesh_node.get_halo_map();
826
827 // if the base mesh has no halos, then we can jump out of here
828 if(base_halo_map.empty())
829 return;
830
831 // get number of halos
832 const std::size_t num_halos = base_halo_map.size();
833
834 // create a halo splitter
835 Geometry::PatchHaloSplitter<MeshType> halo_splitter(*base_mesh_node.get_mesh(), *base_mesh_node.get_patch(-1));
836
837 // add each base-mesh halo to our halo splitter and store the resulting split data size
838 std::vector<int> halo_ranks;
839 std::vector<std::size_t> halo_send_sizes;
840 for(auto it = base_halo_map.begin(); it != base_halo_map.end(); ++it)
841 {
842 // store halo rank
843 halo_ranks.push_back(it->first);
844
845 // add halo and compute send buffer size
846 halo_send_sizes.push_back(halo_splitter.add_halo(it->first, *it->second));
847 }
848
849 // This vector will receive the split halo data from all our potential neighbor processes
850 std::vector<std::size_t> halo_recv_sizes(num_halos);
851 std::vector<std::vector<Index>> halo_send_data(num_halos), halo_recv_data(num_halos);
852 Dist::RequestVector halo_recv_reqs(num_halos), halo_send_reqs(num_halos);
853
854 // get the layer index of our comm layer; the ancestor layer unless we got the child ancestry during
855 // multi-layered partitioning; this may be equal to -1 in either case if this process does not participate
856 // in that corresponding layer
857 const int layer_idx = is_child ? ancestor.layer_p : ancestor.layer;
858
859 // split and serialize halos and store sizes
860 for(std::size_t i(0); i < num_halos; ++i)
861 {
862 // serialize the split halo into the send data buffer
863 if(halo_send_sizes.at(i) > std::size_t(0))
864 halo_send_data.at(i) = halo_splitter.serialize_split_halo(halo_ranks[i], -1);
865 }
866
867 // exchange halo send data sizes over the corresponding layer communicator
868 if(layer_idx >= 0)
869 {
870 // get the corresponding layer communicator
871 const Dist::Comm& layer_comm = this->_layers.at(std::size_t(layer_idx))->comm();
872
873 // exchange halos sizes
874 for(std::size_t i(0); i < num_halos; ++i)
875 {
876 // post receive requests
877 halo_recv_reqs[i] = layer_comm.irecv(&halo_recv_sizes[i], std::size_t(1), halo_ranks[i]);
878
879 // post send requests
880 halo_send_reqs[i] = layer_comm.isend(&halo_send_sizes[i], std::size_t(1), halo_ranks[i]);
881 }
882
883 // wait for sends and receives to finish
884 halo_recv_reqs.wait_all();
885 halo_send_reqs.wait_all();
886
887 // exchange halo data
888 for(std::size_t i(0); i < num_halos; ++i)
889 {
890 // do we receive any data from our neighbor?
891 if(halo_recv_sizes[i] > Index(0))
892 {
893 // resize buffer and post receive
894 halo_recv_data.at(i).resize(halo_recv_sizes[i]);
895 halo_recv_reqs[i] = layer_comm.irecv(halo_recv_data.at(i).data(), halo_recv_sizes.at(i), halo_ranks[i]);
896 }
897
898 // do we send any data to our neighbor?
899 if(halo_send_sizes.at(i) > Index(0))
900 {
901 // post send of actual halo buffer
902 halo_send_reqs[i] = layer_comm.isend(halo_send_data.at(i).data(), halo_send_sizes.at(i), halo_ranks[i]);
903 }
904 }
905
906 // wait for sends and receives to finish
907 halo_recv_reqs.wait_all();
908 halo_send_reqs.wait_all();
909 }
910
911 // broadcast halo receive data over progeny comm in case of multi-layered partitioning
912 if(is_child)
913 {
914 // broadcast sizes
915 ancestor.progeny_comm.bcast(halo_recv_sizes.data(), num_halos, 0);
916
917 // allocate buffers
918 if(ancestor.progeny_comm.rank() != 0)
919 {
920 for(std::size_t i(0); i < num_halos; ++i)
921 halo_recv_data.at(i).resize(halo_recv_sizes[i]);
922 }
923
924 // broadcast all halos
925 for(std::size_t i(0); i < num_halos; ++i)
926 {
927 if(halo_recv_sizes[i] > std::size_t(0))
928 ancestor.progeny_comm.bcast(halo_recv_data[i].data(), halo_recv_sizes[i], 0);
929 }
930 }
931
932 // create a vector of split halo sizes
933 std::vector<std::array<Index, shape_dim+1>> halo_send_intsec_sizes(num_halos), halo_recv_intsec_sizes(num_halos);
934
935 // process all halos
936 for(std::size_t i(0); i < num_halos; ++i)
937 {
938 // did we receive any data from our neighbor?
939 if(halo_recv_sizes.at(i) == Index(0))
940 continue;
941
942 // intersect with our other halo
943 if(!halo_splitter.intersect_split_halo(halo_ranks[i], halo_recv_data.at(i), 0u))
944 continue; // no intersection between halos
945
946 // create the new halo
947 std::unique_ptr<MeshPartType> split_halo = halo_splitter.make_unique();
948
949 // store the entity counts of the halo in our split sizes vector
950 for(int j(0); j <= shape_dim; ++j)
951 halo_send_intsec_sizes[i][Index(j)] = split_halo->get_num_entities(j);
952
953 // create new halo mesh-part
954 patch_mesh_node.add_halo(halo_ranks[i], std::move(split_halo));
955 }
956
957 // exchange halo intersected halo sizes over progeny comm; we do this just to ensure consistency
958 if((ancestor.layer_p >= 0) || (!is_child && (ancestor.layer >= 0)))
959 {
960 // get the corresponding layer communicator
961 const LayerType& layer = *this->_layers.at(std::size_t(is_child ? ancestor.layer_p : ancestor.layer));
962
963 // exchange intersected halo sizes
964 for(std::size_t i(0); i < num_halos; ++i)
965 {
966 halo_recv_reqs[i] = layer.comm().irecv(halo_recv_intsec_sizes.at(i).data(), std::size_t(shape_dim+1), halo_ranks[i]);
967 halo_send_reqs[i] = layer.comm().isend(halo_send_intsec_sizes.at(i).data(), std::size_t(shape_dim+1), halo_ranks[i]);
968 }
969
970 // wait for sends and receives to finish
971 halo_recv_reqs.wait_all();
972 halo_send_reqs.wait_all();
973
974 // ensure that the halo sizes are consistent between neighboring processes
975 for(std::size_t i(0); i < num_halos; ++i)
976 {
977 for(int j(0); j <= shape_dim; ++j)
978 {
979 if(halo_recv_intsec_sizes[i][Index(j)] != halo_send_intsec_sizes[i][Index(j)])
980 {
981 String msg = "Inconsistent deslagged halo size between process ";
982 msg += stringify(this->_comm.rank());
983 msg += " and neighbor with layer rank ";
984 msg += stringify(halo_ranks[i]);
985
986 // abort execution
987 XABORTM(msg.c_str());
988 }
989 }
990 }
991 }
992 }
993
994 // virtual bool _parse_parti_type(const String& type) override
995 // {
996 // if(type.compare_no_case("extern") == 0)
997 // return this->_allow_parti_extern = true;
998 // // else if(type.compare_no_case("2level") == 0)
999 // // return _allow_parti_2level = true;
1000 // else
1001 // return BaseClass::_parse_parti_type(type);
1002 // }
1003
1004 Adjacency::Coloring _compute_unstructered_coloring(const MeshNodeType& base_node) const
1005 {
1006 const auto& verts_at_elem = base_node.get_mesh()->template get_index_set<MeshType::shape_dim, 0>();
1007 Adjacency::Graph elems_at_vert(Adjacency::RenderType::transpose, verts_at_elem);
1008 Adjacency::Graph elems_at_elem(Adjacency::RenderType::injectify, verts_at_elem, elems_at_vert);
1009 return Adjacency::Coloring(elems_at_elem);
1010 }
1011
1022 {
1023 // get element target set of slag patch
1024 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1025 XASSERT(slag_part != nullptr);
1026 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1027
1028 // number of colors = 2^shape_dim
1029 const Index num_colors = Index(1) << MeshType::shape_dim;
1030 const Index num_elems = slag_target.get_num_entities();
1031
1032 // allocate coloring
1033 Adjacency::Coloring coloring(num_elems, num_colors);
1034 Index* colors = coloring.get_coloring();
1035
1036 // unrefined base mesh: structured numbering
1037 if constexpr(MeshType::shape_dim == 1)
1038 {
1039 for(Index i(0); i < num_elems; ++i)
1040 colors[i] = slag_target[i] & 1;
1041 }
1042 else if constexpr(MeshType::shape_dim == 2)
1043 {
1044 for(Index i(0); i < num_elems; ++i)
1045 {
1046 Index iel = slag_target[i];
1047 Index ix = iel % this->_base_slices[0];
1048 Index iy = iel / this->_base_slices[0];
1049 colors[i] = (ix & 1) | ((iy & 1) << 1);
1050 }
1051 }
1052 else if constexpr(MeshType::shape_dim == 3)
1053 {
1054 for(Index i(0); i < num_elems; ++i)
1055 {
1056 Index iel = slag_target[i];
1057 Index ix = iel % this->_base_slices[0];
1058 Index iy = (iel / this->_base_slices[0]) % this->_base_slices[1];
1059 Index iz = iel / (this->_base_slices[0] * this->_base_slices[1]);
1060 colors[i] = (ix & 1) | ((iy & 1) << 1) | ((iz & 1) << (1 << 1));
1061 }
1062 }
1063
1064 return coloring;
1065 }
1066
1077 {
1078 // get element target set of slag patch
1079 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1080 XASSERT(slag_part != nullptr);
1081 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1082
1083 // number of colors = 2^shape_dim
1084 const Index num_colors = Index(1) << MeshType::shape_dim;
1085 const Index num_elems = slag_target.get_num_entities();
1086
1087 // allocate coloring
1088 Adjacency::Coloring coloring(num_elems, num_colors);
1089 Index* colors = coloring.get_coloring();
1090
1091 // color of element i = i % num_colors
1092 for(Index i(0); i < num_elems; ++i)
1093 colors[i] = slag_target[i] % num_colors;
1094
1095 return coloring;
1096 }
1097
1113 Adjacency::Coloring _extract_patch_coloring(const MeshNodeType& slag_node, const Adjacency::Coloring& parent_coloring, int child_rank) const
1114 {
1115 // get element target set of patch
1116 const Geometry::MeshPart<MeshType>* patch_part = slag_node.get_patch(child_rank);
1117 XASSERT(patch_part != nullptr);
1118 const Geometry::TargetSet& patch_target = patch_part->template get_target_set<MeshType::shape_dim>();
1119
1120 // number of colors = 2^shape_dim
1121 const Index num_colors = parent_coloring.get_num_colors();
1122 const Index num_elems = patch_target.get_num_entities();
1123
1124 // allocate coloring
1125 Adjacency::Coloring coloring(num_elems, num_colors);
1126 Index* colors = coloring.get_coloring();
1127
1128 // color of element i = i % num_colors
1129 for(Index i(0); i < num_elems; ++i)
1130 colors[i] = parent_coloring[patch_target[i]];
1131
1132 return coloring;
1133 }
1134
1145 {
1146 // get element target set of slag patch
1147 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1148 XASSERT(slag_part != nullptr);
1149 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1150
1151 // maximum number of layers = number of slices in highest dimensions; this may be less than
1152 // base_slices[shape_dim], since entire layers might have been removed during the deslagging process
1153 const Index max_layers = this->_base_slices[shape_dim-1];
1154 const Index num_elems = slag_target.get_num_entities();
1155
1156 // compute denominator for layer index computation
1157 Index denom = 1u;
1158 for(int j(0); j+1 < shape_dim; ++j)
1159 denom *= this->_base_slices[Index(j)];
1160
1161 // compute number of elements for each potential layer
1162 std::vector<Index> aux(max_layers, 0u);
1163 for(Index i(0); i < num_elems; ++i)
1164 ++aux[slag_target[i] / denom];
1165
1166 // count number of actually used layers
1167 Index num_layers = 0u;
1168 for(Index i(0); i < max_layers; ++i)
1169 {
1170 Index k = num_layers;
1171 if(aux[i] > 0u)
1172 ++num_layers;
1173 aux[i] = k;
1174 }
1175
1176 // compute layering
1177 Adjacency::Coloring layering(num_elems, num_layers);
1178 Index* layers = layering.get_coloring();
1179 for(Index i(0); i < num_elems; ++i)
1180 layers[i] = aux[slag_target[i] / denom];
1181
1182 return layering;
1183 }
1184
1195 {
1197 {
1198 return Adjacency::Coloring();
1199 }
1200 // get element target set of slag patch
1201 const Geometry::MeshPart<MeshType>* slag_part = slag_node.get_patch(-1);
1202 XASSERT(slag_part != nullptr);
1203 const Geometry::TargetSet& slag_target = slag_part->template get_target_set<MeshType::shape_dim>();
1204
1205 // maximum number of layers = twice the number of layers in coarse mesh; this may actually be less,
1206 // since entire layers might have been removed during the deslagging process
1207 const Index max_layers = 2 * coarse_layering.get_num_colors();
1208 const Index num_elems = slag_target.get_num_entities();
1209
1210 // compute number of elements for each potential layer
1211 std::vector<Index> aux(max_layers, 0u);
1212 for(Index i(0); i < num_elems; ++i)
1213 {
1214 Index qux = slag_target[i] >> (shape_dim - 1);
1215 ++aux[(coarse_layering[qux >> 1] << 1) | (qux & 1)]; // all hail to bit-shift magic!
1216 }
1217
1218 // count number of actually used layers
1219 Index num_layers = 0u;
1220 for(Index i(0); i < max_layers; ++i)
1221 {
1222 Index k = num_layers;
1223 if(aux[i] > 0u)
1224 ++num_layers;
1225 aux[i] = k;
1226 }
1227
1228 // compute layering
1229 Adjacency::Coloring layering(num_elems, num_layers);
1230 Index* layers = layering.get_coloring();
1231 for(Index i(0); i < num_elems; ++i)
1232 {
1233 Index qux = slag_target[i] >> (shape_dim - 1);
1234 layers[i] = aux[(coarse_layering[qux >> 1] << 1) | (qux & 1)];
1235 }
1236
1237 return layering;
1238 }
1239
1255 Adjacency::Coloring _extract_patch_layering(const MeshNodeType& slag_node, const Adjacency::Coloring& parent_layering, int child_rank) const
1256 {
1258 return Adjacency::Coloring();
1259 // get element target set of patch
1260 const Geometry::MeshPart<MeshType>* patch_part = slag_node.get_patch(child_rank);
1261 XASSERT(patch_part != nullptr);
1262 const Geometry::TargetSet& patch_target = patch_part->template get_target_set<MeshType::shape_dim>();
1263
1264 // maximum number of layers = number of layers in parent layering; this may actually be less,
1265 // since entire layers might have been removed during the deslagging process
1266 const Index max_layers = parent_layering.get_num_colors();
1267 const Index num_elems = patch_target.get_num_entities();
1268
1269 // compute number of elements for each potential layer
1270 std::vector<Index> aux(max_layers, 0u);
1271 for(Index i(0); i < num_elems; ++i)
1272 ++aux[parent_layering[patch_target[i]]];
1273
1274 // count number of actually used layers
1275 Index num_layers = 0u;
1276 for(Index i(0); i < max_layers; ++i)
1277 {
1278 Index k = num_layers;
1279 if(aux[i] > 0u)
1280 ++num_layers;
1281 aux[i] = k;
1282 }
1283
1284 // compute layering
1285 Adjacency::Coloring layering(num_elems, num_layers);
1286 Index* layers = layering.get_coloring();
1287 for(Index i(0); i < num_elems; ++i)
1288 layers[i] = aux[parent_layering[patch_target[i]]];
1289
1290 return layering;
1291 }
1292
1304 virtual void _create_single_process(std::unique_ptr<MeshNodeType> base_mesh_node)
1305 {
1306 // create and push single layer
1307 this->push_layer(std::make_shared<LayerType>(this->_comm.comm_dup(), 0));
1308
1309 // create single ancestry
1311 Ancestor& ancestor = this->_ancestry.front();
1312
1313 // use the whole base mesh here
1314 ancestor.parti_info = "Using base-mesh";
1315
1316 // save slagged base mesh node
1317 std::unique_ptr<MeshNodeType> base_slag_node = std::move(base_mesh_node);
1318 // deslag base mesh node
1319 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1320
1321 // refine and deslag up to desired minimum level
1322 int lvl = 0;
1323 for(; lvl < ancestor.desired_level_min; ++lvl)
1324 {
1325 base_slag_node = base_mesh_node->refine_unique(this->_adapt_mode);
1326 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1327 }
1328
1329 // compute coloring for the coarse level
1330 Adjacency::Coloring base_coloring;
1332 {
1333 base_coloring = this->_compute_unstructered_coloring(*base_mesh_node);
1334 }
1335 else
1336 {
1337 if(lvl == 0)
1338 base_coloring = this->_compute_base_mesh_coloring(*base_slag_node);
1339 else
1340 base_coloring = this->_compute_refined_mesh_coloring(*base_slag_node);
1341 }
1342
1343 // save chosen minimum level if it is not equal to the desired maximum level
1344 if(lvl < ancestor.desired_level_max)
1345 this->_chosen_levels.push_front(std::make_pair(lvl, 0));
1346
1347 // refine up to maximum level and push to control
1348 for(; lvl < ancestor.desired_level_max; ++lvl)
1349 {
1350 // refine the base mesh
1351 auto refined_node = base_mesh_node->refine_unique(this->_adapt_mode);
1352
1353 // push this level
1354 std::shared_ptr<LevelType> level_ptr = std::make_shared<LevelType>(lvl, std::move(base_mesh_node),
1355 std::move(base_slag_node), std::move(base_coloring));
1356 this->push_level_front(0, level_ptr);
1357
1358 // continue with refined node
1359 base_slag_node = std::move(refined_node);
1360 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1361 base_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*base_mesh_node) : this->_compute_refined_mesh_coloring(*base_slag_node);
1362 }
1363
1364 // save chosen maximum level
1365 this->_chosen_levels.push_front(std::make_pair(lvl, 1));
1366
1367 // push finest level
1368 this->push_level_front(0, std::make_shared<LevelType>(lvl, std::move(base_mesh_node),
1369 std::move(base_slag_node), std::move(base_coloring)));
1370 }
1371
1372 // Note: all following member functions are only required for parallel builds,
1373 // so we enclose them in the following #if-block to reduce compile times.
1374
1375#if defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
1376
1383 virtual void _create_single_layered(std::unique_ptr<MeshNodeType> base_mesh_node)
1384 {
1385 // create and push single layer
1386 std::shared_ptr<LayerType> layer = std::make_shared<LayerType>(this->_comm.comm_dup(), 0);
1387 this->push_layer(layer);
1388
1389 // create single-layered ancestry
1391 Ancestor& ancestor = this->_ancestry.front();
1392
1393 XASSERTM(!this->_keep_base_levels, "VoxelDomainControl cannot keep base levels!");
1394
1395 // save slagged base mesh node
1396 std::unique_ptr<MeshNodeType> base_slag_node = std::move(base_mesh_node);
1397
1398 // deslag base mesh node
1399 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1400 Adjacency::Coloring base_coloring;
1401
1402 // refine up to chosen partition level
1403 int lvl = 0;
1404 for(; lvl <= ancestor.desired_level_max; ++lvl)
1405 {
1406 // can we apply a partitioner?
1407 if((lvl >= ancestor.parti_level) && this->_apply_parti(ancestor, *base_mesh_node))
1408 break;
1409
1410 // no partitioning found?
1411 if(lvl >= ancestor.desired_level_max)
1412 break;
1413
1414 // refine and deslag base mesh node
1415 base_slag_node = base_mesh_node->refine_unique(this->_adapt_mode);
1416 base_mesh_node = this->_deslag_mesh_node(*base_slag_node);
1417 base_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*base_mesh_node) : this->_compute_refined_mesh_coloring(*base_slag_node);
1418 }
1419
1420 // no valid partitioning found?
1421 XASSERTM(ancestor.parti_found, "VoxelDomainControl failed to find a valid partitioning");
1422
1423 // set the selected partitioner level
1424 ancestor.parti_level = lvl;
1425
1426 // compute coloring if we're still on level 0; otherwise the coloring has been computed in the loop above
1427 if(lvl == 0)
1428 {
1429 base_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*base_mesh_node) : this->_compute_refined_mesh_coloring(*base_slag_node);
1430 }
1431
1432 // extract our patch
1433 std::vector<int> neighbor_ranks;
1434 std::unique_ptr<MeshNodeType> patch_mesh_node(
1435 base_mesh_node->extract_patch(neighbor_ranks, ancestor.parti_graph, this->_comm.rank()));
1436
1437 // extract our coloring (only if we need it on this level)
1438 Adjacency::Coloring patch_coloring;
1439 if(lvl == ancestor.desired_level_min)
1440 patch_coloring = this->_extract_patch_coloring(*base_mesh_node, base_coloring, this->_comm.rank());
1441
1442 // set the neighbor ranks of our child layer
1443 layer->set_neighbor_ranks(neighbor_ranks);
1444
1445 // create an empty patch slag node
1446 std::unique_ptr<MeshNodeType> patch_slag_node;
1447
1448 // refine up to minimum level
1449 for(; lvl < ancestor.desired_level_min; ++lvl)
1450 {
1451 // refine the patch mesh
1452 patch_slag_node = patch_mesh_node->refine_unique(this->_adapt_mode);
1453 patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node, ancestor, false);
1454 }
1455
1456 // compute coloring if we don't have one yet
1457 if(patch_coloring.empty())
1458 patch_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*patch_mesh_node) : this->_compute_refined_mesh_coloring(*patch_slag_node);
1459
1460 // save chosen minimum level
1461 if(lvl < ancestor.desired_level_max)
1462 this->_chosen_levels.push_front(std::make_pair(lvl, 0));
1463
1464 // refine up to maximum level
1465 for(; lvl < ancestor.desired_level_max; ++lvl)
1466 {
1467 // refine the patch mesh
1468 auto refined_node = patch_mesh_node->refine_unique(this->_adapt_mode);
1469
1470 // create new level
1471 std::shared_ptr<LevelType> level_ptr = std::make_shared<LevelType>(lvl, std::move(patch_mesh_node),
1472 std::move(patch_slag_node), std::move(patch_coloring));
1473
1474 // push this (unrefined) level
1475 this->push_level_front(0, level_ptr);
1476
1477 // continue with refined node
1478 patch_slag_node = std::move(refined_node);
1479 patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node, ancestor, false);
1480 patch_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*patch_mesh_node) : this->_compute_refined_mesh_coloring(*patch_slag_node);
1481 }
1482
1483 // save chosen maximum level
1484 this->_chosen_levels.push_front(std::make_pair(lvl, this->_comm.size()));
1485
1486 // push finest level
1487 this->push_level_front(0, std::make_shared<LevelType>(lvl, std::move(patch_mesh_node),
1488 std::move(patch_slag_node), std::move(patch_coloring)));
1489 }
1490
1497 virtual void _create_multi_layered(std::unique_ptr<MeshNodeType> base_mesh_node)
1498 {
1499 // create layers
1501
1502 // create ancestry
1504
1505 XASSERTM(!this->_keep_base_levels, "VoxelDomainControl cannot keep base levels!");
1506
1507 // we start counting at level 0
1508 int lvl = 0;
1509
1510 // deslag the base-mesh node and move pointer to a new parent-mesh pointer;
1511 // the base-mesh is always the one on the layer with only 1 process
1512 std::unique_ptr<MeshNodeType> parent_slag_node;
1513 std::unique_ptr<MeshNodeType> parent_mesh_node = this->_deslag_mesh_node(*base_mesh_node);
1514
1515 // create the coloring and layering for the unrefined base mesh
1516 Adjacency::Coloring parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_base_mesh_coloring(*base_mesh_node);
1517
1518 base_mesh_node.reset();
1519
1520 // loop over all global layers in reverse order (coarse to fine)
1521 for(std::size_t slayer = this->_ancestry.size(); slayer > std::size_t(0); )
1522 {
1523 // is this the base-mesh layer aka the 1-process layer?
1524 const bool is_base_layer = (slayer == this->_ancestry.size());
1525
1526 --slayer;
1527
1528 // get the ancestor object
1529 Ancestor& ancestor = this->_ancestry.at(slayer);
1530
1531 // determine the minimum desired level of our parent layer
1532 int parent_min_lvl = -1;
1533 if(!is_base_layer)
1534 parent_min_lvl = this->_chosen_levels.front().first;
1535 else if(this->_ancestry.size() + 1u < this->_desired_levels.size())
1536 parent_min_lvl = this->_desired_levels.back().first;
1537
1538 // check available partitioning strategies
1539 this->_check_parti(ancestor, *parent_mesh_node, is_base_layer);
1540
1541 // the check_parti function returns the partitioning level w.r.t. the current
1542 // level (which may be > 0), so we have to compensate that by adding our current level:
1543 // note: contrary to the standard parti domain control, we calculate our partition always to the level
1544 // we split to the subdomains, i.e. on the level we actually change our layer
1545 // due to this, we enforce, that we at least win 1 real level to the parent
1546 ancestor.parti_level += lvl;
1547 ancestor.parti_level = Math::max(ancestor.parti_level, ancestor.desired_level_min);
1548 ancestor.parti_level = Math::max(ancestor.parti_level,parent_min_lvl + int(!is_base_layer));
1549
1550 // Note: each progeny group within the main communicator may have chosen a different
1551 // partitioning level at this point. We will compensate this by adjusting the minimum
1552 // refinement level of the child layer after the partitioning step below.
1553
1554 // refine up to the chosen partitioning level
1555 //for(; lvl < ancestor.parti_level; ++lvl)
1556 for(; lvl <= ancestor.desired_level_max; ++lvl)
1557 {
1558 // can we apply a partitioner?
1559 if(lvl >= ancestor.parti_level)
1560 {
1561 // try to apply the partitioner
1562 this->_apply_parti(ancestor, *parent_mesh_node);
1563
1564 // partitioning successful?
1565 int parti_ok = ancestor.parti_found ? 1 : 0;
1566
1567 // check if all processes found a valid partitioning
1568 this->_comm.allreduce(&parti_ok, &parti_ok, std::size_t(1), Dist::op_min);
1569 if(parti_ok > 0)
1570 break;
1571
1572 // nope, at least one process did not receive a partition, so keep refining
1573 ancestor.parti_found = false;
1574 }
1575
1576 // no partitioning found?
1577 if(lvl >= ancestor.desired_level_max)
1578 break;
1579
1580 // refine and deslag the parent mesh node
1581 auto refined_node = parent_mesh_node->refine_unique(this->_adapt_mode);
1582
1583 // a level pointer for the unrefined level; we need this to access the parent layering
1584 std::shared_ptr<LevelType> level_ptr;
1585
1586 // push the base mesh into our parent layer if desired
1587 if((ancestor.layer_p >= 0) && (parent_min_lvl >= 0) && (lvl >= parent_min_lvl))
1588 {
1589 level_ptr = std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1590 std::move(parent_slag_node), std::move(parent_coloring));
1591 this->push_level_front(ancestor.layer_p, level_ptr);
1592 }
1593
1594 // continue with refined node
1595 parent_slag_node = std::move(refined_node);
1596 parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node, ancestor, !is_base_layer);
1597 parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_refined_mesh_coloring(*parent_slag_node);
1598 }
1599
1600 // no valid partitioning found?
1601 XASSERTM(ancestor.parti_found, "VoxelDomainControl failed to find a valid partitioning");
1602
1603 // set the selected partitioner level
1604 ancestor.parti_level = lvl;
1605
1606 // extract our patch
1607 std::vector<int> neighbor_ranks;
1608 std::unique_ptr<MeshNodeType> patch_mesh_node(
1609 parent_mesh_node->extract_patch(neighbor_ranks, ancestor.parti_graph, ancestor.progeny_child));
1610
1611 // create an empty patch slag node (which we might need, if we refine further for this partition)
1612 std::unique_ptr<MeshNodeType> patch_slag_node;
1613
1614 // translate neighbor ranks by progeny group to obtain the neighbor ranks
1615 // w.r.t. this layer's communicator
1616 {
1617 std::map<int,int> halo_map;
1618 for(auto& i : neighbor_ranks)
1619 {
1620 int old_i(i);
1621 halo_map.emplace(old_i, i += ancestor.progeny_group);
1622 }
1623 patch_mesh_node->rename_halos(halo_map);
1624 }
1625
1626 // does this process participate in the parent layer or do we need to keep the base-meshes anyways?
1627 if(ancestor.layer_p >= 0)
1628 {
1629 // Note: patch mesh-part for rank = 0 was already created by 'extract_patch' call
1630 for(int i(1); i < ancestor.num_parts; ++i)
1631 {
1632 parent_mesh_node->create_patch_meshpart(ancestor.parti_graph, i);
1633 }
1634 }
1635
1636 // make sure we choose the same minimum level for all processes, because we may
1637 // have chosen different partitioning levels for each patch
1638 int global_level_min = Math::max(ancestor.desired_level_min, ancestor.parti_level);
1639 this->_comm.allreduce(&global_level_min, &global_level_min, std::size_t(1), Dist::op_max);
1640
1641 // make sure our minimum level is greater than the minimum level of the previous layer,
1642 // because each layer must contain at least one non-ghost level
1643 if(!is_base_layer)
1644 global_level_min = Math::max(global_level_min, this->_chosen_levels.front().first+1);
1645
1646 // refine up to desired minimum level of this layer
1647 XASSERTM(lvl == global_level_min, "INTERNAL ERROR");
1648 // for(; lvl < global_level_min; ++lvl)
1649 // {
1650 // // refine the base mesh node
1651 // std::unique_ptr<MeshNodeType> coarse_slag_node(std::move(parent_slag_node));
1652 // std::unique_ptr<MeshNodeType> coarse_mesh_node(std::move(parent_mesh_node));
1653 // parent_slag_node = coarse_mesh_node->refine_unique(this->_adapt_mode);
1654 // // parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node);
1655 // parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node, ancestor, !is_base_layer);
1656
1657 // // push base mesh to parent layer if desired
1658 // if((ancestor.layer_p >= 0) && (parent_min_lvl >= 0) && (lvl >= parent_min_lvl))
1659 // {
1660 // // clear patches before pushing this node as they are redundant here
1661 // coarse_mesh_node->clear_patches();
1662 // this->push_level_front(ancestor.layer_p, std::make_shared<LevelType>(lvl, std::move(coarse_mesh_node), std::move(coarse_slag_node), std::move(parent_coloring)));
1663 // }
1664 // parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_refined_mesh_coloring(*parent_slag_node);
1665
1666 // // refine the patch mesh
1667 // patch_slag_node = patch_mesh_node->refine_unique(this->_adapt_mode);
1668 // patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node, ancestor, !is_base_layer);
1669 // // patch_mesh_node = this->_deslag_mesh_node(*patch_slag_node);
1670 // // patch_mesh_node = this->_deslag_mesh_node(*patch_mesh_node->refine_unique(this->_adapt_mode));
1671 // }
1672
1673 // extract our coloring
1674 Adjacency::Coloring patch_coloring = this->_extract_patch_coloring(*parent_mesh_node, parent_coloring, ancestor.progeny_child);
1675
1676 // split the halos of our base-mesh and compute the halos of our patches from that
1677 this->_split_basemesh_halos(ancestor, *parent_mesh_node, *patch_mesh_node, neighbor_ranks);
1678
1679 // does this process participate in the child layer?
1680 if(ancestor.layer >= 0)
1681 {
1682 // set the neighbor ranks in our child layer
1683 this->_layers.at(std::size_t(ancestor.layer))->set_neighbor_ranks(neighbor_ranks);
1684 }
1685
1686 // set chosen minimum level for this layer
1687 if(!is_base_layer)
1688 this->_chosen_levels.push_front(std::make_pair(lvl, this->_ancestry.at(slayer+1u).num_procs));
1689 else if(parent_min_lvl < 0)
1690 this->_chosen_levels.push_front(std::make_pair(lvl, 0));
1691 else
1692 {
1693 this->_chosen_levels.push_front(std::make_pair(parent_min_lvl, 0));
1694 this->_chosen_levels.push_front(std::make_pair(lvl, 1));
1695 }
1696
1697 // a level pointer for the unrefined level; we need this to access the parent layering
1698 std::shared_ptr<LevelType> level_ptr;
1699
1700 // push the finest base-mesh
1701 if(ancestor.layer_p >= 0)
1702 {
1703 this->push_level_front(ancestor.layer_p, std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1704 std::move(parent_slag_node), std::move(parent_coloring)));
1705 }
1706
1707 // continue with the next layer
1708 parent_slag_node = std::move(patch_slag_node);
1709 parent_mesh_node = std::move(patch_mesh_node);
1710 parent_coloring = std::move(patch_coloring);
1711 }
1712
1713 // get the desired maximum level
1714 // int desired_level_max = Math::max(this->_ancestry.front().desired_level_max, lvl+1);
1715 int desired_level_max = this->_ancestry.front().desired_level_max;
1716 XASSERTM(desired_level_max > lvl, "Trying to refine larger than provided level hirarchy");
1717
1718 for(; lvl < desired_level_max; ++lvl)
1719 {
1720 // refine the patch mesh
1721 auto refined_node = parent_mesh_node->refine_unique(this->_adapt_mode);
1722
1723 // a level pointer for the unrefined level; we need this to access the parent layering
1724 std::shared_ptr<LevelType> level_ptr = std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1725 std::move(parent_slag_node), std::move(parent_coloring));
1726
1727
1728 // push patch mesh to this level
1729 this->push_level_front(0, level_ptr);
1730
1731 // continue with refined node
1732 parent_slag_node = std::move(refined_node);
1733 parent_mesh_node = this->_deslag_mesh_node(*parent_slag_node, this->_ancestry.front(), false);
1734 parent_coloring = _unstructered_mesh ? this->_compute_unstructered_coloring(*parent_mesh_node) : this->_compute_refined_mesh_coloring(*parent_slag_node);
1735 }
1736
1737 // set chosen maximum level for finest layer
1738 this->_chosen_levels.push_front(std::make_pair(lvl, this->_comm.size()));
1739
1740 // push finest level
1741 this->push_level_front(0, std::make_shared<LevelType>(lvl, std::move(parent_mesh_node),
1742 std::move(parent_slag_node), std::move(parent_coloring)));
1743 }
1744
1745#endif // defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
1746 }; // class CGALDomainControl<...>
1747 } // namespace Domain
1748 } // namespace Control
1749} // namespace FEAT
1750#endif
#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
Hierarchical partitioned Voxel Domain Control.
std::vector< Index > _gather_masked_elements(const MeshType &mesh) const
Gather a vector of element indices that intersect the masked domain.
virtual std::unique_ptr< MeshNodeType > _deslag_mesh_node(MeshNodeType &mesh_node)
Deslags an unpartitioned mesh node including its mesh-parts.
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.
virtual void _create_single_process(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a single-layered mesh hierarchy for a single process.
int _create_stage
creation stage to keep track what has already been initialized
virtual void _create_single_layered(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a single-layered mesh hierarchy.
void keep_cgal_wrapper()
Instructs the domain controller to keep the voxel map after hierarchy creation.
void create_cgal_wrapper(std::istream &file, Geometry::CGALFileMode file_mode)
Creates a voxel map based on a VoxelMasker object.
StopWatch _watch_create_base_mesh
a bunch of stop-watches
Tiny::Vector< CoordType, shape_dim > _bbox_min
the bounding box of the structured domain
virtual void create(std::unique_ptr< MeshNodeType > &)
Creates a domain control from a base-mesh node.
Adjacency::Coloring _compute_refined_mesh_layering(const MeshNodeType &slag_node, const Adjacency::Coloring &coarse_layering) const
Computes the layering for a refined mesh.
bool _keep_cgal_wrapper
specifies whether to keep the voxel map after hierarchy creation
Geometry::RootMeshNode< MeshType > MeshNodeType
our root mesh node type
bool _unstructered_mesh
specifies whether we use a non voxel base mesh
void create_cgal_wrapper(const FEAT::String &file, Geometry::CGALFileMode file_mode)
has to be called from each process part of the domain hirarchy
MeshType::CoordType CoordType
coordinate type
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.
virtual void create(Geometry::MeshFileReader &)
Creates a domain control from a MeshFileReader.
static constexpr int shape_dim
shape dimension
CGALDomainControl(const Dist::Comm &comm_, bool support_multi_layered)
Constructor.
Adjacency::Coloring _compute_base_mesh_coloring(const MeshNodeType &slag_node) const
Computes the coloring for the unpartitioned base mesh.
std::vector< WeightType > gather_element_weights(Index level) const
Gathers the element map weights on a given domain level.
String dump_slag_layer_levels() const
Debugging function: Returns a string containing encoded slag layer level information.
virtual ~CGALDomainControl()
virtual destructor
std::array< Index, shape_dim > _base_slices
number of element slices on base mesh level 0
Adjacency::Coloring _compute_base_mesh_layering(const MeshNodeType &slag_node) const
Computes the element layering for the unpartitioned base mesh.
Adjacency::Coloring _compute_refined_mesh_coloring(const MeshNodeType &slag_node) const
Computes the coloring for a refined mesh.
std::vector< WeightType > _compute_weights(Ancestor &ancestor, const MeshNodeType &base_mesh_node) override
Computes the element partitioning weights.
const Geometry::CGALWrapper< CoordType > * get_cgal_wrapper() const
std::unique_ptr< MeshNodeType > _base_mesh_node
the input base-mesh node on 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.
virtual void create(const std::deque< String > &filenames, String dirpath="")
Creates a domain control from a list of filenames.
const StopWatch & get_watch_hierarchy() const
Returns a const reference to the StopWatch that measures the base-mesh creation phase.
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.
Control::Domain::PartiDomainControlBase< DomainLevel_ > BaseClass
our base class
std::vector< WeightType > _gather_element_weights(const MeshType &mesh) const
Gathers the element slag weights for a given mesh.
std::unique_ptr< Geometry::CGALWrapper< CoordType > > _cgal_wrapper
cgal wrapper
void create_hierarchy()
Creates the domain level hierarchy.
void cheap_weight_calculation(bool option)
set whether we use an expensive or cheap method to calculate the element weights
Geometry::CGALWrapper< CoordType > * get_cgal_wrapper()
const StopWatch & get_watch_cgal_wrapper() const
Returns a const reference to the StopWatch that measures the voxel-map creation phase.
const StopWatch & get_watch_base_mesh() const
Returns a const reference to the StopWatch that measures the base-mesh creation phase.
virtual void _create_multi_layered(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a multi-layered mesh hierarchy.
std::vector< Index > _gather_masked_elements(const MeshType &mesh, const Dist::Comm &sibling_comm) const
Gather a vector of element indices that intersect the masked domain in a copperative manner.
Geometry::CGALWrapper< CoordType > * release_cgal_wrapper()
Releases the internal cgal wrapper.
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.
MeshNodeType & set_base_mesh(std::unique_ptr< MeshNodeType > &&mesh_node)
Set another mesh as base mesh.
Wrapper class for domain levels for VoxelDomainControl.
std::shared_ptr< DomainLevel_ > slag_level
attachable slag level
DomainLevel_ BaseClass
our base-class; the actual domain level
Adjacency::Coloring element_coloring
element coloring
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
int progeny_child
Which direct child this process belongs to in its progeny group.
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
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.
Cubature Rule class template.
Definition: rule.hpp:38
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
void allgatherv(const void *sendbuf, std::size_t sendcount, const Datatype &sendtype, void *recvbuf, const int *recvcounts, const int *displs, const Datatype &recvtype) const
Blocking gather-to-all.
Definition: dist.cpp:607
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
void allgather(const void *sendbuf, std::size_t sendcount, const Datatype &sendtype, void *recvbuf, std::size_t recvcount, const Datatype &recvtype) const
Blocking gather-to-all.
Definition: dist.cpp:589
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.
Wrapper for the CGAL Library.
Definition: cgal.hpp:173
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
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:47
String pad_front(size_type len, char c=' ') const
Pads the front of the string up to a desired length.
Definition: string.hpp:393
Tiny Vector class template.
Standard transformation mapping class template.
Definition: mapping.hpp:39
@ 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
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
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:993
std::uint64_t Index
Index data type.
TrafoTags
Trafo configuration tags enum.
Definition: eval_tags.hpp:22
@ img_point
specifies whether the trafo should supply image point coordinates
@ dom_point
specifies whether the trafo should supply domain point coordinates
@ jac_det
specifies whether the trafo should supply jacobian determinants