FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
unit_cube_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
6#pragma once
7
8#include <kernel/geometry/unit_cube_patch_generator.hpp>
9#include <control/domain/domain_control.hpp>
10
11#include <deque>
12#include <vector>
13
14namespace FEAT
15{
16 namespace Control
17 {
18 namespace Domain
19 {
20 template<typename DomainLevel_>
22 public DomainControl<DomainLevel_>
23 {
24 public:
26 typedef typename BaseClass::LayerType LayerType;
27 typedef typename BaseClass::LevelType LevelType;
28 typedef typename BaseClass::MeshType MeshType;
29 typedef typename BaseClass::AtlasType AtlasType;
30 typedef typename BaseClass::MeshNodeType MeshNodeType;
31
32 public:
33 explicit UnitCubeDomainControl(const Dist::Comm& comm_, int lvl_max, int lvl_min = -1) :
34 BaseClass(comm_)
35 {
36 int rank = comm_.rank();
37 int nprocs = comm_.size();
38
39 this->_layers.push_back(std::make_shared<LayerType>(comm_.comm_dup(), 0));
40 this->_layer_levels.resize(std::size_t(1));
41
42 // communication data structures
43 std::vector<int> ranks;
44
45 // create root mesh node
46 std::unique_ptr<MeshNodeType> mesh_node;
47 int lvl = (int)Geometry::UnitCubePatchGenerator<MeshType>::create_unique(rank, Math::max(nprocs,1), mesh_node, ranks);
48
49 // set front layer neighbor ranks
50 this->_layers.front()->set_neighbor_ranks(ranks);
51
52 // adjust lvl_max and lvl_min
53 lvl_max = Math::max(lvl_max, 0);
54 if(lvl_min < 0)
55 lvl_min = Math::max(lvl_max + lvl_min + 1, 0);
56 else
57 lvl_min = Math::min(lvl_min, lvl_max);
58
59 // refine up to desired minimum level
60 for(; lvl < lvl_min; ++lvl)
61 {
62 mesh_node = mesh_node->refine_unique();
63 }
64
65 // add coarse mesh node
66 this->_layer_levels.front().push_front(std::make_shared<LevelType>(lvl, std::move(mesh_node)));
67
68 // refine up to desired maximum level
69 for(; lvl < lvl_max;)
70 {
71 //mesh_node = mesh_node->refine_unique();
72 //this->_layer_levels.front().push_front(std::make_shared<LevelType>(++lvl, mesh_node));
73 this->_layer_levels.front().push_front(std::make_shared<LevelType>(++lvl,
74 this->_layer_levels.front().front()->get_mesh_node()->refine_unique()));
75 }
76
77 // compile the virtual level list
78 this->compile_virtual_levels();
79 }
80 }; // class UnitCubeDomainControl<...>
81
82
83 template<typename DomainLevel_>
85 public DomainControl<DomainLevel_>
86 {
87 public:
89 typedef typename BaseClass::LayerType LayerType;
90 typedef typename BaseClass::LevelType LevelType;
91 typedef typename BaseClass::MeshType MeshType;
92 typedef typename BaseClass::AtlasType AtlasType;
95
96 static_assert(MeshType::shape_dim == 2, "HierarchUnitCubeDomainControl works only for 2D meshes");
97
98 public:
99 explicit HierarchUnitCubeDomainControl(const Dist::Comm& comm_, const std::deque<int>& lvls) :
100 BaseClass(comm_)
101 {
102 _create(lvls);
103 }
104
105 explicit HierarchUnitCubeDomainControl(const Dist::Comm& comm_, const std::vector<String>& lvls) :
106 BaseClass(comm_)
107 {
108 std::deque<int> ilvls;
109 XASSERT(!lvls.empty());
110
111 ilvls.resize(lvls.size());
112 for(std::size_t i(0); i < lvls.size(); ++i)
113 {
114 if(!lvls.at(i).parse(ilvls.at(i)))
115 {
116 comm_.print(std::cerr, "ERROR: failed to parse '" + lvls.at(i) + "' as level");
118 }
119 }
120 if(ilvls.size() < std::size_t(2))
121 ilvls.push_back(0);
122
123 _create(ilvls);
124 }
125
126 protected:
127 static int _ilog4(int x)
128 {
129 int r = 0;
130 for(; x > 1; ++r)
131 {
132 if((x % 4) > 0)
133 return -1;
134 x /= 4;
135 }
136 return r;
137 }
138
139 // converts a rank from 2-level to lexi ordering
140 static int _2lvl2lexi(int rank, int size)
141 {
142 const int ls = _ilog4(size);
143 int ix(0), iy(0);
144 for(int i(0); i < ls; ++i)
145 {
146 ix |= (rank >> (i+0)) & (1 << i);
147 iy |= (rank >> (i+1)) & (1 << i);
148 }
149 return iy*(1 << ls) + ix;
150 }
151
152 // converts a vector of ranks from lexi to 2-level ordering
153 static void _lexi22lvl(std::vector<int>& ranks, std::map<int,int>& reranks, int size)
154 {
155 const int ls = _ilog4(size);
156 for(auto& rank : ranks)
157 {
158 const int ix = rank % (1 << ls);
159 const int iy = rank / (1 << ls);
160 int rr = 0;
161 for(int i(0); i < ls; ++i)
162 {
163 rr |= (ix & (1 << i)) << (i+0);
164 rr |= (iy & (1 << i)) << (i+1);
165 }
166 reranks.insert(std::make_pair(rank, rr));
167 rank = rr;
168 }
169 }
170
171 void _create(const std::deque<int>& lvls)
172 {
173 const int log4n = _ilog4(this->_comm.size());
174 XASSERTM(log4n >= 0, "number of processes must be a power of 4");
175
176 XASSERT(!lvls.empty());
177
178 // create layers for this process
179 _create_layers(log4n, int(lvls.size())-1);
180
181 this->_layer_levels.resize(this->_layers.size());
182
183 // loop over all layers
184 for(std::size_t i(0); i < this->_layers.size(); ++i)
185 {
186 auto& layer = *this->_layers.at(i);
187 auto& laylevs = this->_layer_levels.at(i);
188
189 const int csize = layer.comm().size();
190 const int crank = _2lvl2lexi(layer.comm().rank(), csize);
191
192 std::vector<int> ranks;
193 std::map<int,int> reranks; // neighbor rank map: lexi -> 2lvl
194
195 // create root mesh node
196 std::unique_ptr<MeshNodeType> mesh_node;
197 const int base_lvl = (int)Geometry::UnitCubePatchGenerator<MeshType>::create_unique(crank, csize, mesh_node, ranks);
198
199 // translate neighbor ranks from lexi to 2lvl
200 _lexi22lvl(ranks, reranks, csize);
201
202 // rename halos
203 mesh_node->rename_halos(reranks);
204
205 // set layer neighbors
206 layer.set_neighbor_ranks(ranks);
207
208 // create domain level
209 laylevs.push_front(std::make_shared<LevelType>(base_lvl, mesh_node));
210
211 // refine once and create child mesh parts
212 if(i > std::size_t(0))
213 {
214 std::shared_ptr<MeshNodeType> ref_node = std::shared_ptr<MeshNodeType>(laylevs.front()->get_mesh_node()->refine());
215 this->_create_child_meshparts(layer, ref_node);
216 laylevs.push_front(std::make_shared<LevelType>(base_lvl+1, ref_node));
217 }
218
219 const int head_lvl = laylevs.front()->get_level_index();
220 const int fin_lvl = lvls.at(i);
221 const int crs_lvl = lvls.at(i+1);
222
223 // refine up the desired fine level
224 for(int l(head_lvl); l < fin_lvl; ++l)
225 {
226 std::shared_ptr<MeshNodeType> ref_node = std::shared_ptr<MeshNodeType>(laylevs.front()->get_mesh_node()->refine());
227 laylevs.push_front(std::make_shared<LevelType>(l+1, ref_node));
228 }
229
230 // drop all levels below desired coarse level
231 for(int l(base_lvl); l < crs_lvl; ++l)
232 {
233 laylevs.pop_back();
234 }
235 }
236
237 // compile the virtual level list
238 this->compile_virtual_levels();
239 }
240
241 void _create_layers(int /*log4n*/, int nlayers)
242 {
243 // create main layer
244 this->_layers.push_back(std::make_shared<LayerType>(this->_comm.comm_dup(), 0));
245
246 // the world comm layer is already pushed
247 for(int i(0); (i+1) < nlayers; ++i)
248 {
249 // get child layer
250 DomainLayer& child = *this->_layers.back();
251
252 // get the child comm
253 const Dist::Comm& comm_c = child.comm();
254
255 // my rank in child comm
256 const int rank = comm_c.rank();
257
258 // create sibling comm
259 Dist::Comm comm_s = comm_c.comm_create_range_incl(4, rank & ~3);
260 XASSERT(comm_s.size() == 4);
261
262 // set sibling comm in child layer
263 child.set_parent(std::move(comm_s), 0); // rank 0 is parent
264
265 // create parent comm
266 Dist::Comm comm_p = comm_c.comm_create_range_incl(comm_c.size()/4, 0, 4);
267 if(comm_p.is_null())
268 break;
269
270 // finally, add to our layer deque
271 this->_layers.push_back(std::make_shared<DomainLayer>(std::move(comm_p), i+1));
272 }
273 }
274
275 void _create_child_meshparts(const DomainLayer& /*layer*/, std::shared_ptr<MeshNodeType> mesh_node)
276 {
277 Index num_elems = mesh_node->get_mesh()->get_num_elements();
278 XASSERT(num_elems == 4);
279
280 const Index num_entities[] = {4, 4, 1};
281
282 const Index ivert[4][4] =
283 {
284 { 0, 4, 6, 8},
285 { 4, 1, 8, 7},
286 { 6, 8, 2, 5},
287 { 8, 7, 5, 3}
288 };
289 const Index iedge[4][4] =
290 {
291 { 0, 10, 4, 8},
292 { 1, 11, 8, 6},
293 { 10, 2, 5, 9},
294 { 11, 3, 9, 7}
295 };
296
297 for(int i(0); i < 4; ++i)
298 {
299 MeshPartType* part = new MeshPartType(num_entities, false);
300
301 // manually override target indices
302 Index* iv = part->template get_target_set<0>().get_indices();
303 Index* ie = part->template get_target_set<1>().get_indices();
304 Index* iq = part->template get_target_set<2>().get_indices();
305 for(int j(0); j < 4; ++j)
306 {
307 iv[j] = ivert[i][j];
308 ie[j] = iedge[i][j];
309 }
310 iq[0] = Index(i);
311
312 mesh_node->add_patch(i, part);
313 }
314 }
315 }; // class HierarchUnitCubeDomainControl<...>
316
317
318 template<typename DomainLevel_>
320 public DomainControl<DomainLevel_>
321 {
322 public:
324 typedef typename BaseClass::LayerType LayerType;
325 typedef typename BaseClass::LevelType LevelType;
326 typedef typename BaseClass::MeshType MeshType;
327 typedef typename BaseClass::AtlasType AtlasType;
330
331 static_assert(MeshType::shape_dim == 2, "HierarchUnitCubeDomainControl works only for 2D meshes");
332
333 public:
334 explicit HierarchUnitCubeDomainControl2(const Dist::Comm& comm_, const std::deque<int>& lvls, const std::deque<int>& lyrs) :
335 BaseClass(comm_)
336 {
337 _create(lvls, lyrs);
338 }
339
340 explicit HierarchUnitCubeDomainControl2(const Dist::Comm& comm_, const std::vector<String>& lvls) :
341 BaseClass(comm_)
342 {
343 std::deque<int> ilvls, ilyrs;
344 XASSERT(!lvls.empty());
345
346 ilvls.resize(lvls.size());
347 ilyrs.resize(lvls.size(), 1);
348 for(std::size_t i(0); i < lvls.size(); ++i)
349 {
350 std::deque<String> parts = lvls.at(i).split_by_string(":");
351 if(!parts.front().parse(ilvls.at(i)))
352 {
353 comm_.print(std::cerr, "ERROR: failed to parse '" + lvls.at(i) + "' as level");
355 }
356 if((parts.size() > std::size_t(1)) && !parts.back().parse(ilyrs.at(i)))
357 {
358 comm_.print(std::cerr, "ERROR: failed to parse '" + lvls.at(i) + "' as layer");
360 }
361 }
362 if(ilvls.size() < std::size_t(2))
363 {
364 ilvls.push_back(0);
365 ilyrs.push_back(0);
366 }
367
368 _create(ilvls, ilyrs);
369 }
370
371 explicit HierarchUnitCubeDomainControl2(const Dist::Comm& comm_, const std::deque<String>& lvls) :
372 BaseClass(comm_)
373 {
374 std::deque<int> ilvls, ilyrs;
375 XASSERT(!lvls.empty());
376
377 ilvls.resize(lvls.size());
378 ilyrs.resize(lvls.size(), 1);
379 for(std::size_t i(0); i < lvls.size(); ++i)
380 {
381 std::deque<String> parts = lvls.at(i).split_by_string(":");
382 if(!parts.front().parse(ilvls.at(i)))
383 {
384 comm_.print(std::cerr, "ERROR: failed to parse '" + lvls.at(i) + "' as level");
386 }
387 if((parts.size() > std::size_t(1)) && !parts.back().parse(ilyrs.at(i)))
388 {
389 comm_.print(std::cerr, "ERROR: failed to parse '" + lvls.at(i) + "' as layer");
391 }
392 }
393 if(ilvls.size() < std::size_t(2))
394 {
395 ilvls.push_back(0);
396 ilyrs.push_back(0);
397 }
398
399 _create(ilvls, ilyrs);
400 }
401
402 std::deque<std::pair<int,int>> get_level_indices() const
403 {
404 std::deque<std::pair<int,int>> lvs;
405 for(std::size_t i(0); i < this->_layer_levels.size(); ++i)
406 lvs.push_back(
407 std::make_pair(
408 this->_layer_levels.at(i).front()->get_level_index(),
409 this->_layers.at(i)->comm().size()));
410 lvs.push_back(
411 std::make_pair(
412 this->_layer_levels.back().back()->get_level_index(),
413 this->_layers.back()->comm().size()));
414 return lvs;
415 }
416
417 protected:
418 static int _ilog4(int x)
419 {
420 int r = 0;
421 for(; x > 1; ++r)
422 {
423 if((x % 4) > 0)
424 return -1;
425 x /= 4;
426 }
427 return r;
428 }
429
430 // converts a rank from 2-level to lexi ordering
431 static int _2lvl2lexi(int rank, int size)
432 {
433 const int ls = _ilog4(size);
434 int ix(0), iy(0);
435 for(int i(0); i < ls; ++i)
436 {
437 ix |= (rank >> (i+0)) & (1 << i);
438 iy |= (rank >> (i+1)) & (1 << i);
439 }
440 return iy*(1 << ls) + ix;
441 }
442
443 // converts a vector of ranks from lexi to 2-level ordering
444 static void _lexi22lvl(std::vector<int>& ranks, std::map<int,int>& reranks, int size)
445 {
446 const int ls = _ilog4(size);
447 for(auto& rank : ranks)
448 {
449 const int ix = rank % (1 << ls);
450 const int iy = rank / (1 << ls);
451 int rr = 0;
452 for(int i(0); i < ls; ++i)
453 {
454 rr |= (ix & (1 << i)) << (i+0);
455 rr |= (iy & (1 << i)) << (i+1);
456 }
457 reranks.insert(std::make_pair(rank, rr));
458 rank = rr;
459 }
460 }
461
462 void _create(const std::deque<int>& lvls, const std::deque<int>& lyrs)
463 {
464 const int log4n = _ilog4(this->_comm.size());
465 XASSERTM(log4n >= 0, "number of processes must be a power of 4");
466
467 XASSERT(!lvls.empty());
468 XASSERT(lvls.size() == lyrs.size());
469
470 // create layers for this process
471 _create_layers(lyrs);
472
473 this->_layer_levels.resize(this->_layers.size());
474
475 // loop over all layers
476 for(std::size_t i(0); i < this->_layers.size(); ++i)
477 {
478 auto& layer = *this->_layers.at(i);
479 auto& laylevs = this->_layer_levels.at(i);
480
481 const int csize = layer.comm().size();
482 const int crank = _2lvl2lexi(layer.comm().rank(), csize);
483
484 std::vector<int> ranks;
485 std::map<int,int> reranks; // neighbor rank map: lexi -> 2lvl
486
487 // create root mesh node
488 std::unique_ptr<MeshNodeType> mesh_node;
489 const int base_lvl = (int)Geometry::UnitCubePatchGenerator<MeshType>::create_unique(crank, csize, mesh_node, ranks);
490
491 // translate neighbor ranks from lexi to 2lvl
492 _lexi22lvl(ranks, reranks, csize);
493
494 // rename halos
495 mesh_node->rename_halos(reranks);
496
497 // set layer neighbors
498 layer.set_neighbor_ranks(ranks);
499
500 // create domain level
501 laylevs.push_front(std::make_shared<LevelType>(base_lvl, std::move(mesh_node)));
502
503 // refine and create child mesh parts
504 if(i > std::size_t(0))
505 {
506 int children = this->_layers.at(i-1)->comm().size() / csize;
507 int crs_lvl = base_lvl;
508 for(int nel(1); nel < children; nel *= 4)
509 {
510 //std::shared_ptr<MeshNodeType> ref_node = std::shared_ptr<MeshNodeType>(laylevs.front()->get_mesh_node()->refine());
511 //laylevs.push_front(std::make_shared<LevelType>(++crs_lvl, ref_node));
513 laylevs.push_front(std::make_shared<LevelType>(++crs_lvl, laylevs.front()->get_mesh_node()->refine_unique()));
514 }
515 this->_create_child_meshparts(*laylevs.front()->get_mesh_node());
516 }
517
518 const int head_lvl = laylevs.front()->get_level_index();
519 const int fin_lvl = lvls.at(i);
520 const int crs_lvl = lvls.at(i+1);
521
522 // refine up the desired fine level
523 for(int l(head_lvl); l < fin_lvl; ++l)
524 {
525 //std::shared_ptr<MeshNodeType> ref_node = std::shared_ptr<MeshNodeType>(laylevs.front()->get_mesh_node()->refine());
526 //laylevs.push_front(std::make_shared<LevelType>(l+1, ref_node));
527 laylevs.push_front(std::make_shared<LevelType>(l+1, laylevs.front()->get_mesh_node()->refine_unique()));
528 }
529
530 // drop all levels below desired coarse level
531 for(int l(base_lvl); l < crs_lvl; ++l)
532 {
533 laylevs.pop_back();
534 }
535 }
536
537 // compile the virtual level list
538 this->compile_virtual_levels();
539 }
540
541 void _create_layers(const std::deque<int>& lyrs)
542 {
543 // create main layer
544 this->_layers.push_back(std::make_shared<LayerType>(this->_comm.comm_dup(), 0));
545
546 // the world comm layer is already pushed
547 for(int ilay(1); (ilay+1) < int(lyrs.size()); ++ilay)
548 {
549 // get child layer
550 DomainLayer& child = *this->_layers.back();
551
552 // get the child comm
553 const Dist::Comm& comm_c = child.comm();
554
555 // my rank in child comm
556 const int rank = comm_c.rank();
557
558 // compute number of siblings
559 const int nsib = Math::sqr(1 << lyrs.at(std::size_t(ilay)));
560 XASSERT(nsib <= comm_c.size());
561
562 // create sibling comm
563 Dist::Comm comm_s = comm_c.comm_create_range_incl(nsib, rank & ~(nsib-1));
564 XASSERT(comm_s.size() == nsib);
565
566 // set sibling comm in child layer
567 child.set_parent(std::move(comm_s), 0); // rank 0 is parent
568
569 // create parent comm
570 Dist::Comm comm_p = comm_c.comm_create_range_incl(comm_c.size()/nsib, 0, nsib);
571 if(comm_p.is_null())
572 break;
573
574 // finally, add to our layer deque
575 this->_layers.push_back(std::make_shared<DomainLayer>(std::move(comm_p), ilay));
576 }
577 }
578
579 void _create_child_meshparts(MeshNodeType& mesh_node)
580 {
581 const Index num_elems = mesh_node.get_mesh()->get_num_elements();
582
583 const Index num_entities[] = {4, 4, 1};
584 const auto& ivert = mesh_node.get_mesh()->template get_index_set<2,0>();
585 const auto& iedge = mesh_node.get_mesh()->template get_index_set<2,1>();
586
587 for(Index i(0); i < num_elems; ++i)
588 {
589 std::unique_ptr<MeshPartType> part(new MeshPartType(num_entities, false));
590
591 // manually override target indices
592 Index* iv = part->template get_target_set<0>().get_indices();
593 Index* ie = part->template get_target_set<1>().get_indices();
594 Index* iq = part->template get_target_set<2>().get_indices();
595
596 for(int j(0); j < 4; ++j)
597 {
598 iv[j] = ivert[i][j];
599 ie[j] = iedge[i][j];
600 }
601 iq[0] = i;
602
603 mesh_node.add_patch(int(i), std::move(part));
604 }
605 }
606 }; // class HierarchUnitCubeDomainControl2<...>
607 } // namespace Domain
608 } // namespace Control
609} // namespace FEAT
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Domain control base-class template.
Communicator class.
Definition: dist.hpp:1349
Comm comm_create_range_incl(int count, int first=0, int stride=1) const
Creates a new sub-communicator from a strided range of ranks.
Definition: dist.cpp:472
int size() const
Returns the size of this communicator.
Definition: dist.hpp:1506
Comm comm_dup() const
Creates a copy of this communicator.
Definition: dist.cpp:459
int rank() const
Returns the rank of this process in this communicator.
Definition: dist.hpp:1494
bool is_null() const
Checks whether this communicator is a null communicator.
Definition: dist.cpp:454
void print(std::ostream &os, const String &msg, int root=0) const
Prints a message line to an output stream.
Definition: dist.cpp:782
MeshType * get_mesh()
Returns the mesh of this node.
Definition: mesh_node.hpp:225
Class template for partial meshes.
Definition: mesh_part.hpp:90
Root mesh node class template.
Definition: mesh_node.hpp:748
MeshPartType * add_patch(int rank, std::unique_ptr< MeshPartType > patch_part)
Adds a patch mesh part to this mesh node.
Definition: mesh_node.hpp:947
static void abort(bool dump_call_stack=true)
FEAT abortion.
Definition: runtime.cpp:413
@ child
indicates that the level is a child level
T_ sqr(T_ x)
Returns the square of a value.
Definition: math.hpp:95
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
std::uint64_t Index
Index data type.