FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
mesh_distortion.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// includes, FEAT
9#include <kernel/geometry/factory.hpp>
10#include <kernel/geometry/mesh_permutation.hpp>
11#include <kernel/geometry/mesh_node.hpp>
12#include <kernel/geometry/intern/facet_neighbors.hpp>
13#include <kernel/geometry/intern/standard_index_refiner.hpp>
14#include <kernel/geometry/intern/standard_vertex_refiner.hpp>
15#include <kernel/geometry/index_calculator.hpp>
16#include <kernel/util/dist.hpp>
17
18#include <kernel/util/random.hpp>
19
20#include <kernel/util/tiny_algebra.hpp>
21
22
23namespace FEAT
24{
25 namespace Geometry
26 {
40 template<typename MeshType_>
42 {
43 public:
44 typedef typename MeshType_::CoordType CoordType;
45 static constexpr int shape_dim = MeshType_::shape_dim;
46 static constexpr int world_dim = MeshType_::world_dim;
47
48 protected:
50 MeshType_& _mesh;
54 std::vector<int> _boundary_vertices;
56 std::vector<int> _boundary_facets;
58 std::vector<CoordType> _shortest_edge;
60 const CoordType _pi_val = Math::pi<CoordType>();
61
62 public:
69 explicit MeshDistortion(MeshType_& mesh) :
70 _mesh(mesh),
71 _rand(),
72 _boundary_vertices(_mesh.get_num_vertices(), 0),
73 _boundary_facets(_mesh.get_num_entities(shape_dim - 1), 0),
74 _shortest_edge(_mesh.get_num_vertices(), Math::huge<CoordType>())
75 {
76 }
87 explicit MeshDistortion(MeshType_& mesh, Random::SeedType seed) :
88 _mesh(mesh),
89 _rand(seed),
90 _boundary_vertices(_mesh.get_num_vertices(), 0),
91 _boundary_facets(_mesh.get_num_entities(shape_dim - 1), 0),
92 _shortest_edge(_mesh.get_num_vertices(), Math::huge<CoordType>())
93 {
94 }
95
96 virtual ~MeshDistortion()
97 {
98 }
99
111 void distort_uniform(CoordType rad)
112 {
114
115 auto& vtx = _mesh.get_vertex_set();
116
117 if(world_dim == 2)
118 {
119 for (Index i(0); i < vtx.get_num_vertices(); ++i)
120 {
121 if(_boundary_vertices[i] == 0)
122 {
123 CoordType t(0);
124 _rand >> t;
125 t *= CoordType(2) * _pi_val;
126 vtx[i][0] += rad * Math::cos(t);
127 vtx[i][1] += rad * Math::sin(t);
128 }
129 }
130 }
131 else if (world_dim == 3)
132 {
133 for (Index i(0); i < vtx.get_num_vertices(); ++i)
134 {
135 if(_boundary_vertices[i] == 0)
136 {
137 CoordType theta(0), phi(0);
138 _rand >> theta;
139 _rand >> phi;
140 theta *= _pi_val;
141 phi *= CoordType(2) * _pi_val;
142 vtx[i][0] += rad * Math::sin(theta) * Math::cos(phi);
143 vtx[i][1] += rad * Math::sin(theta) * Math::sin(phi);
144 vtx[i][2] += rad * Math::cos(theta);
145 }
146 }
147 }
148
149 // synchronize over all processes, if necessary
150 _synchronize();
151 }
152
167 void distort_shortest_edge_uniform(CoordType scale = CoordType(0.1))
168 {
171 CoordType min = *std::min_element(_shortest_edge.begin(), _shortest_edge.end());
172 distort_uniform(min * scale);
173 }
174
188 void distort_shortest_edge_local(CoordType scale = CoordType(0.1))
189 {
192
193 CoordType rad;
194
195 auto& vtx = _mesh.get_vertex_set();
196
197 if(world_dim == 2)
198 {
199 for (Index i(0); i < vtx.get_num_vertices(); ++i)
200 {
201 if(_boundary_vertices[i] == 0)
202 {
203 rad = scale * _shortest_edge[i];
204 CoordType t(0);
205 _rand >> t;
206 t *= CoordType(2) * _pi_val;
207 vtx[i][0] += rad * Math::cos(t);
208 vtx[i][1] += rad * Math::sin(t);
209 }
210 }
211 }
212 else if (world_dim == 3)
213 {
214 for (Index i(0); i < vtx.get_num_vertices(); ++i)
215 {
216 if(_boundary_vertices[i] == 0)
217 {
218 rad = scale * _shortest_edge[i];
219 CoordType theta(0), phi(0);
220 _rand >> theta;
221 _rand >> phi;
222 theta *= _pi_val;
223 phi *= CoordType(2) * _pi_val;
224 vtx[i][0] += rad * Math::sin(theta) * Math::cos(phi);
225 vtx[i][1] += rad * Math::sin(theta) * Math::sin(phi);
226 vtx[i][2] += rad * Math::cos(theta);
227 }
228 }
229 }
230
231 // synchronize over all processes, if necessary
232 _synchronize();
233 }
234
235 protected:
238 {
239 const auto& vtx_neighbors = _mesh.get_neighbors();
240
241 const auto& faces_at_elem = _mesh.template get_index_set<shape_dim, shape_dim - 1>();
242
243 int num_faces = faces_at_elem.num_indices;
244
245 Index num_elements(_mesh.get_num_elements());
246
247 // loop over all elements
248 for (Index i = 0; i < num_elements; ++i)
249 {
250 // loop over faces of each element
251 for (int j = 0; j < num_faces; ++j)
252 {
253 // check whether there is a neighbor at that edge
254 if (vtx_neighbors[i][j] > num_elements)
255 {
256 // if not: facet is at the boundary
257 _boundary_facets[faces_at_elem[i][j]] = 1;
258 }
259 }
260 }
261 }
262
265 {
267 const auto& verts_at_face = _mesh.template get_index_set<shape_dim - 1, 0>();
268
269 int vert_per_face = verts_at_face.num_indices;
270
271 // loop over all facets
272 for (Index i = 0; i < _mesh.get_num_entities(shape_dim - 1); ++i)
273 {
274 // check if facet is at boundary
275 if(_boundary_facets[i])
276 {
277 // loop over adjacent vertices
278 for (int j = 0; j < vert_per_face; ++j)
279 {
280 _boundary_vertices[verts_at_face[i][j]] = 1;
281 }
282 }
283 }
284 }
285
287 virtual void _calc_shortest_edge()
288 {
289 auto& vtx = _mesh.get_vertex_set();
290
291 const auto& verts_at_edg = _mesh.template get_index_set<1, 0>();
292
293 // loop over all edges
294 for (Index i = 0; i < verts_at_edg.get_num_entities(); ++i)
295 {
296 Index ind_vert1 = verts_at_edg[i][0];
297 Index ind_vert2 = verts_at_edg[i][1];
298 CoordType edge_length = (vtx[ind_vert1] - vtx[ind_vert2]).norm_euclid();
299
300 // update the two nodes belonging to the edge
301 _shortest_edge[ind_vert1] = Math::min(_shortest_edge[ind_vert1], edge_length);
302 _shortest_edge[ind_vert2] = Math::min(_shortest_edge[ind_vert2], edge_length);
303 }
304 }
305
307 virtual void _synchronize()
308 {
309 // nothing to do here
310 }
311 }; // class MeshDistortion<...>
312
330 template<typename MeshType_>
332 public MeshDistortion<MeshType_>
333 {
334 public:
336 typedef MeshType_ MeshType;
339
340 static constexpr int world_dim = MeshType::world_dim;
341 static constexpr int facet_dim = MeshType::shape_dim - 1;
342
343 typedef typename BaseClass::CoordType CoordType;
344
345 protected:
351 std::vector<int> _vertex_owners;
352
353 public:
363 explicit DistributedMeshDistortion(const Dist::Comm& comm, RootMeshNodeType& root_mesh_node) :
364 BaseClass(*root_mesh_node.get_mesh()),
365 _comm(comm),
366 _root_mesh_node(root_mesh_node),
368 {
369 }
382 explicit DistributedMeshDistortion(const Dist::Comm& comm, RootMeshNodeType& root_mesh_node, Random::SeedType seed) :
383 BaseClass(*root_mesh_node.get_mesh(), seed),
384 _comm(comm),
385 _root_mesh_node(root_mesh_node),
387 {
388 }
389
392 {
393 }
394
395 protected:
397 virtual void _calc_boundary_facets() override
398 {
399 // call base class to determine all boundary facets
401
402 // Each process has already calculated its local boundary facets, so now we need to loop
403 // over all halos and remove all halo facets from the set of boundary facets, because
404 // halo facets are by definition not a part of the actual domain boundary.
405
406 // loop over all halos and kick out all halo facets
407 const auto& halo_map = _root_mesh_node.get_halo_map();
408 for(const auto& v : halo_map)
409 {
410 // loop over all facets of the halo and remove them from the boundary set
411 const TargetSet& halo_facets = v.second->template get_target_set<facet_dim>();
412 for(Index i(0); i < halo_facets.get_num_entities(); ++i)
413 this->_boundary_facets.at(halo_facets[i]) = 0;
414 }
415 }
416
418 virtual void _calc_shortest_edge() override
419 {
420 // call base class to determine the shortest edges for this patch
422
423 // Each process has already calculated the shortest edge for each of its vertices, so now
424 // we need to loop over all halos and exchange the shortest edge length for each halo
425 // vertex with the corresponding neighbor process and determine for each vertex whether
426 // that neighbor has found a shorter edge for it.
427
428 // get our halo map
429 const auto& halo_map = _root_mesh_node.get_halo_map();
430 const std::size_t num_halos = halo_map.size();
431
432 // nothing to do here?
433 if(halo_map.empty())
434 return;
435
436 // create buffers
437 std::vector<int> ranks;
438 std::vector<std::vector<CoordType>> send_bufs, recv_bufs;
439 Dist::RequestVector send_reqs, recv_reqs;
440 ranks.reserve(num_halos);
441 send_bufs.resize(num_halos);
442 recv_bufs.resize(num_halos);
443 send_reqs.reserve(num_halos);
444 recv_reqs.reserve(num_halos);
445
446 // loop over all our halos/neighbors
447 for(auto it = halo_map.begin(); it != halo_map.end(); ++it)
448 {
449 // get halo and the number of halo vertices
450 const int halo_rank = it->first;
451 const TargetSet& halo_vtx = it->second->template get_target_set<0>();
452 const Index halo_size = halo_vtx.get_num_entities();
453
454 // allocate buffers
455 auto& sbuf = send_bufs.at(ranks.size());
456 auto& rbuf = recv_bufs.at(ranks.size());
457 sbuf.resize(halo_size);
458 rbuf.resize(halo_size);
459 ranks.push_back(halo_rank);
460
461 // post receive
462 recv_reqs.push_back(_comm.irecv(rbuf.data(), rbuf.size(), halo_rank));
463
464 // compute source buffer = shortest edge for each halo vertex
465 for(Index k(0); k < halo_size; ++k)
466 sbuf.at(k) = this->_shortest_edge.at(halo_vtx[k]);
467
468 // post send
469 send_reqs.push_back(_comm.isend(sbuf.data(), sbuf.size(), halo_rank));
470 }
471
472 // process all pending receives
473 for(std::size_t idx(0u); recv_reqs.wait_any(idx); )
474 {
475 // get the halo rank
476 const int halo_rank = ranks.at(idx);
477
478 // find the halo mesh part
479 auto it = halo_map.find(halo_rank);
480 XASSERT(it != halo_map.end());
481
482 // get the halo vertex target set
483 const TargetSet& halo_vtx = it->second->template get_target_set<0>();
484 const Index halo_size = halo_vtx.get_num_entities();
485 auto& rbuf = recv_bufs.at(idx);
486
487 // update the shortest edge length for each halo vertex
488 for(Index k(0); k < halo_size; ++k)
489 this->_shortest_edge.at(halo_vtx[k]) = Math::min(this->_shortest_edge.at(halo_vtx[k]), rbuf.at(k));
490 }
491
492 // wait for all sends to finish
493 send_reqs.wait_all();
494 }
495
497 virtual void _calc_vertex_owners()
498 {
499 if(!_vertex_owners.empty())
500 return;
501
502 // initially assume that we own all our vertices
503 _vertex_owners.resize(_root_mesh_node.get_mesh()->get_num_vertices(), _comm.rank());
504
505 // loop over all our halos/neighbors
506 const auto& halo_map = _root_mesh_node.get_halo_map();
507 for(auto it = halo_map.begin(); it != halo_map.end(); ++it)
508 {
509 const int halo_rank = it->first;
510 const TargetSet& halo_vtx = it->second->template get_target_set<0>();
511
512 // update vertex owner ranks
513 for(Index i(0); i < halo_vtx.get_num_entities(); ++i)
514 {
515 _vertex_owners[halo_vtx[i]] = Math::min(_vertex_owners[halo_vtx[i]], halo_rank);
516 }
517 }
518 }
519
520
521 // synchronize over all processes, if necessary
522 virtual void _synchronize() override
523 {
524 // Each process has already disturbed its local mesh, so now we need to ensure that the
525 // coordinates of all vertices, which are shared by more than one process, have consistent
526 // (= identical) coordinates on all sharing process. We do this by employing the convention
527 // that the coordinates of a shared vertex are defined by the process with the smallest rank
528 // that shares this particular vertex and we call that process the 'owner' of that vertex.
529 // With this definition, our approach is as follows: Loop over all halos/neighbors and
530 // determine whether we have small rank than that neighbor and if...
531 // yes: then we collect the vertex coordinates for this halo and send these coordinates
532 // to our neighbor.
533 // no: then we post a receive request for the vertex coordinates and once that receive
534 // request is fulfilled, we loop over all vertices in that halo, check for each
535 // have vertex if that neighbor is the owner and if yes, then we overwrite our
536 // vertex coordinates with the ones we receives from our (owner) neighbor.
537
538 // get our halo map
539 const auto& halo_map = _root_mesh_node.get_halo_map();
540 const std::size_t num_halos = halo_map.size();
541
542 // nothing to do here?
543 if(halo_map.empty())
544 return;
545
546 // compute the vertex owner ranks
548
549 auto& vertex_set = _root_mesh_node.get_mesh()->get_vertex_set();
550
551 const int my_rank = _comm.rank();
552
553 // create buffers
554 std::vector<int> ranks;
555 std::vector<std::vector<CoordType>> buffers;
556 Dist::RequestVector requests;
557 ranks.reserve(num_halos);
558 buffers.resize(num_halos);
559 requests.reserve(num_halos);
560
561 // loop over all our halos/neighbors
562 for(auto it = halo_map.begin(); it != halo_map.end(); ++it)
563 {
564 // get halo and buffer size
565 const int halo_rank = it->first;
566 const TargetSet& halo_vtx = it->second->template get_target_set<0>();
567 const Index halo_size = halo_vtx.get_num_entities();
568 const std::size_t buf_size = halo_size * Index(world_dim);
569
570 // allocate buffers
571 auto& buf = buffers.at(ranks.size());
572 buf.resize(buf_size);
573 ranks.push_back(halo_rank);
574
575 // should we send or receive the vertex coordinates?
576 if(my_rank < halo_rank)
577 {
578 // copy vertex coordinates into buffer
579 for(Index i(0), j(0); i < halo_size; ++i)
580 {
581 const auto& vtx = vertex_set[halo_vtx[i]];
582 for(int k(0); k < world_dim; ++j, ++k)
583 buf[j] = vtx[k];
584 }
585
586 // post send
587 requests.push_back(_comm.isend(buf.data(), buf.size(), halo_rank));
588 }
589 else
590 {
591 // post receive
592 requests.push_back(_comm.irecv(buf.data(), buf.size(), halo_rank));
593 }
594 }
595
596 // process all pending receives
597 for(std::size_t idx(0u); requests.wait_any(idx); )
598 {
599 // get the halo rank
600 const int halo_rank = ranks.at(idx);
601
602 // was this a send-request?
603 if(my_rank < halo_rank)
604 continue; // nothing to do after a send
605
606 // find the halo mesh part
607 auto it = halo_map.find(halo_rank);
608 XASSERT(it != halo_map.end());
609
610 // get the halo vertex target set
611 const TargetSet& halo_vtx = it->second->template get_target_set<0>();
612 const Index halo_size = halo_vtx.get_num_entities();
613 auto& buf = buffers.at(idx);
614
615 // loop over all halo vertices
616 for(Index i(0), j(0); i < halo_size; ++i, j += world_dim)
617 {
618 // is the sender process the actual owner of the vertex?
619 if(_vertex_owners[halo_vtx[i]] == halo_rank)
620 {
621 // yup, so accept the owner's vertex coordinates
622 auto& vtx = vertex_set[halo_vtx[i]];
623 for(int k(0); k < world_dim; ++k)
624 vtx[k] = buf[j+Index(k)];
625 }
626 }
627 }
628 }
629 }; // class DistributedMeshDistortion
630 } // namespace Geometry
631} // namespace FEAT
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
Communicator class.
Definition: dist.hpp:1349
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
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 push_back(Request &&request)
Inserts a new request at the end of the request vector.
Definition: dist.hpp:886
void reserve(std::size_t size_)
Reserves sufficient space for a specified number of requests.
Definition: dist.hpp:813
bool wait_any(std::size_t &idx, Status &status)
Blocks until one of the active requests has been fulfilled.
Definition: dist.cpp:329
void wait_all()
Blocks until all active requests are fulfilled.
Definition: dist.cpp:324
Distributed mesh distortion class template.
DistributedMeshDistortion(const Dist::Comm &comm, RootMeshNodeType &root_mesh_node, Random::SeedType seed)
Constructor.
virtual void _calc_vertex_owners()
determines for each vertex the length of the shortest adjacent edge
virtual ~DistributedMeshDistortion()
virtual destructor
virtual void _calc_boundary_facets() override
determines for each facet whether it is a boundary facet or an inner facet
const Dist::Comm & _comm
a reference to our communicator
std::vector< int > _vertex_owners
a vector of vertex owner ranks
RootMeshNodeType & _root_mesh_node
a reference to our root mesh node
virtual void _calc_shortest_edge() override
determines for each vertex whether it is a boundary vertex or an inner vertex
DistributedMeshDistortion(const Dist::Comm &comm, RootMeshNodeType &root_mesh_node)
Constructor.
virtual void _synchronize() override
synchronizes the distortion over all processes; is overridden in DistributedMeshDistortion
(Unpartitioned) mesh distortion class template
Random _rand
random number generator for random distortion
const CoordType _pi_val
have a guess...
virtual void _synchronize()
synchronizes the distortion over all processes; is overridden in DistributedMeshDistortion
MeshDistortion(MeshType_ &mesh, Random::SeedType seed)
Constructor.
std::vector< int > _boundary_vertices
vector to store which vertex is at the boundary
virtual void _calc_boundary_vertices()
determines for each vertex whether it is a boundary vertex or an inner vertex
MeshType_ & _mesh
the mesh we want to distort
void distort_uniform(CoordType rad)
Distorts the mesh uniformly.
std::vector< CoordType > _shortest_edge
vector to store the length of the shortest edge belonging to each vertex
virtual void _calc_boundary_facets()
determines for each facet whether it is a boundary facet or an inner facet
MeshDistortion(MeshType_ &mesh)
Constructor.
void distort_shortest_edge_local(CoordType scale=CoordType(0.1))
Distorts the mesh according to the shortest local edge.
void distort_shortest_edge_uniform(CoordType scale=CoordType(0.1))
Distorts the mesh according to the shortest edge.
std::vector< int > _boundary_facets
vector to story which facet is at the boundary
virtual void _calc_shortest_edge()
determines for each vertex the length of the shortest adjacent edge
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
const std::map< int, std::unique_ptr< MeshPartType > > & get_halo_map() const
Definition: mesh_node.hpp:896
Target set class.
Definition: target_set.hpp:27
Index get_num_entities() const
Returns the number of entities.
Definition: target_set.hpp:92
Pseudo-Random Number Generator.
Definition: random.hpp:54
std::uint64_t SeedType
seed type
Definition: random.hpp:57
T_ sin(T_ x)
Returns the sine of a value.
Definition: math.hpp:344
T_ min(T_ a, T_ b)
Returns the minimum of two values.
Definition: math.hpp:123
T_ cos(T_ x)
Returns the cosine of a value.
Definition: math.hpp:386
FEAT namespace.
Definition: adjactor.hpp:12
std::uint64_t Index
Index data type.