FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
umbrella_smoother.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 <exception>
10#include <kernel/geometry/factory.hpp>
11#include <kernel/geometry/mesh_permutation.hpp>
12#include <kernel/geometry/mesh_node.hpp>
13#include <kernel/geometry/intern/facet_neighbors.hpp>
14#include <kernel/geometry/intern/standard_index_refiner.hpp>
15#include <kernel/geometry/intern/standard_vertex_refiner.hpp>
16#include <kernel/geometry/index_calculator.hpp>
17#include <kernel/util/dist.hpp>
18
19#include <kernel/util/random.hpp>
20
21#include <kernel/util/tiny_algebra.hpp>
22
23
24namespace FEAT
25{
26 namespace Geometry
27 {
45 template<typename MeshType_>
47 {
48 public:
49 typedef typename MeshType_::CoordType CoordType;
50 typedef typename MeshType_::VertexSetType VertexSetType;
51 static constexpr int shape_dim = MeshType_::shape_dim;
52 static constexpr int world_dim = MeshType_::world_dim;
53
54 protected:
56 MeshType_& _mesh;
58 std::vector<int> _boundary_vertices;
60 std::vector<int> _boundary_facets;
61 // optional per-edge weights; empty in serial case
62 std::vector<CoordType> _edge_weights;
63 // vertex set of the mesh
64 VertexSetType& _vtx;
65
66 public:
74 explicit UmbrellaSmoother(MeshType_& mesh) :
75 _mesh(mesh),
78 _edge_weights(),
79 _vtx(_mesh.get_vertex_set())
80 {
81 }
82
83 // virtual destructor
84 virtual ~UmbrellaSmoother()
85 {
86 }
87
88 void compile()
89 {
90 if(_boundary_vertices.empty())
92 }
93
115 void smooth(Index max_iter, CoordType tol, bool uniform = true)
116 {
117 if(_boundary_vertices.empty())
119
120 const auto& verts_at_edge = _mesh.template get_index_set<1, 0>();
121 const Index num_edges = _mesh.get_num_entities(1);
122 const Index num_vtx = _mesh.get_num_vertices();
123
124 // make sure |e_ij| != 0
125 const CoordType eps = Math::sqrt(Math::eps<CoordType>());
126
127 // sum of neighbors for every vertex
128 std::vector<Tiny::Vector<CoordType, world_dim>> sum_neigh(num_vtx);
129
130 // number of neighbors for every vertex
131 std::vector<CoordType> deg(num_vtx, CoordType(0));
132
133 for (Index iter = 0; iter < max_iter; ++iter)
134 {
135 // format vectors
136 for (Index i(0); i < num_vtx; ++i)
137 {
138 sum_neigh[i].format();
139 deg[i] = CoordType(0);
140 }
141
142 // loop over all edges
143 for (Index e(0); e < num_edges; ++e)
144 {
145 // find vertices on edge e
146 const Index i = verts_at_edge(e,0);
147 const Index j = verts_at_edge(e,1);
148
149 CoordType weight = CoordType(1);
150
151 // calculate edge length for scale dependent smoothing
152 if(!uniform)
153 {
154 weight = CoordType(1) / std::max((_vtx[i] - _vtx[j]).norm_euclid(), eps);
155 }
156
157 // multiply by precomputed per-edge weight, if provided
158 if (!_edge_weights.empty())
159 {
160 weight *= _edge_weights[e];
161 }
162
163 // add to counter
164 if (_boundary_vertices[i] == 0)
165 {
166 sum_neigh[i].axpy(weight, _vtx[j]);
167 deg[i] += weight;
168 }
169 if (_boundary_vertices[j] == 0)
170 {
171 sum_neigh[j].axpy(weight, _vtx[i]);
172 deg[j] += weight;
173 }
174 }
175
176 // synchronize over all processes, if necessary
177 _synchronize(sum_neigh, deg);
178
179 CoordType max_change = 0;
180
181 // loop over vertices to update location
182 for (Index i(0); i < num_vtx; ++i)
183 {
184 if ((_boundary_vertices[i] == 0) && (std::abs(deg[i]) > eps))
185 {
186 // compute new vertex coordinates
187 auto x_old = _vtx[i];
188 _vtx[i] = (CoordType(1) / deg[i]) * sum_neigh[i];
189
190 // update maximum change
191 Math::maxi(max_change, (_vtx[i] - x_old).norm_euclid_sqr());
192 }
193 }
194
195 // calculate maximum over all processes if necessary
196 _calc_max(max_change);
197
198 // abort if we reached tolerance
199 if (max_change < tol*tol)
200 {
201 break;
202 }
203 }
204 }
205
206 protected:
209 {
210 _boundary_facets.resize(_mesh.get_num_entities(shape_dim - 1), 0);
211
212 const auto& vtx_neighbors = _mesh.get_neighbors();
213
214 const auto& faces_at_elem = _mesh.template get_index_set<shape_dim, shape_dim - 1>();
215
216 int num_faces = faces_at_elem.num_indices;
217
218 Index num_elements(_mesh.get_num_elements());
219
220 // loop over all elements
221 for (Index i = 0; i < num_elements; ++i)
222 {
223 // loop over faces of each element
224 for (int j = 0; j < num_faces; ++j)
225 {
226 // check whether there is a neighbor at that edge
227 if (vtx_neighbors[i][j] > num_elements)
228 {
229 // if not: facet is at the boundary
230 _boundary_facets[faces_at_elem[i][j]] = 1;
231 }
232 }
233 }
234 }
235
238 {
240
241 _boundary_vertices.resize(_mesh.get_num_vertices(), 0);
242
243 const auto& verts_at_face = _mesh.template get_index_set<shape_dim - 1, 0>();
244
245 int vert_per_face = verts_at_face.num_indices;
246
247 // loop over all facets
248 for (Index i = 0; i < _mesh.get_num_entities(shape_dim - 1); ++i)
249 {
250 // check if facet is at boundary
251 if(_boundary_facets[i])
252 {
253 // loop over adjacent vertices
254 for (int j = 0; j < vert_per_face; ++j)
255 {
256 _boundary_vertices[verts_at_face[i][j]] = 1;
257 }
258 }
259 }
260 }
261
263 virtual void _synchronize(std::vector<Tiny::Vector<CoordType, world_dim>>& , std::vector<CoordType>&)
264 {
265 // nothing to do here
266 }
267
269 virtual void _calc_max(CoordType&)
270 {
271 // nothing to do here
272 }
273 }; // class UmbrellaSmoother<...>
274
291 template<typename MeshType_>
293 public UmbrellaSmoother<MeshType_>
294 {
295 public:
297 typedef MeshType_ MeshType;
300
301 static constexpr int world_dim = MeshType::world_dim;
302 static constexpr int facet_dim = MeshType::shape_dim - 1;
303
304 typedef typename BaseClass::CoordType CoordType;
305
306 protected:
311
312 public:
322 explicit DistributedUmbrellaSmoother(const Dist::Comm& comm, RootMeshNodeType& root_mesh_node) :
323 BaseClass(*root_mesh_node.get_mesh()),
324 _comm(comm),
325 _root_mesh_node(root_mesh_node)
326 {
328 }
329
330 // virtual destructor
332 {
333 }
334
335 protected:
337 virtual void _calc_boundary_facets() override
338 {
339 // call base class to determine all boundary facets
341
342 // Each process has already calculated its local boundary facets, so now we need to loop
343 // over all halos and remove all halo facets from the set of boundary facets, because
344 // halo facets are by definition not a part of the actual domain boundary.
345
346 // loop over all halos and kick out all halo facets
347 const auto& halo_map = _root_mesh_node.get_halo_map();
348 for(const auto& v : halo_map)
349 {
350 // loop over all facets of the halo and remove them from the boundary set
351 const TargetSet& halo_facets = v.second->template get_target_set<facet_dim>();
352 for(Index i(0); i < halo_facets.get_num_entities(); ++i)
353 this->_boundary_facets.at(halo_facets[i]) = 0;
354 }
355 }
362 {
363 if(_comm.size() == 1)
364 {
365 return;
366 }
367 const Index num_edges = this->_mesh.get_num_entities(1);
368
369 // initialize edge weights and multiplicity with 1
370 this->_edge_weights.assign(num_edges, CoordType(1.0));
371 std::vector<int> multiplicity(num_edges, 1);
372
373 // get our halo map
374 const auto& halo_map = _root_mesh_node.get_halo_map();
375
376 // loop over all halos
377 for (const auto& hm : halo_map)
378 {
379 const int neighbor_rank = hm.first;
380 const std::unique_ptr<MeshPartType>& part_ptr = hm.second;
381 if (!part_ptr)
382 {
383 continue;
384 }
385
386 // skip if neighboring rank is our rank
387 if (neighbor_rank == _comm.rank())
388 {
389 continue;
390 }
391
392 // get edges that are shared with other halos
393 const auto& target_edges = part_ptr->template get_target_set<1>();
394
395 for (Index k(0); k < target_edges.get_num_entities(); ++k)
396 {
397 // get edge number
398 const Index e = target_edges[k];
399 // increase multiplicity
400 multiplicity[e] += 1;
401 }
402 }
403 for (Index e(0); e < num_edges; ++e)
404 {
405 // make sure we don't divide by zero
406 const int m = std::max(1, multiplicity[e]);
407 this->_edge_weights[e] = CoordType(1.0) / CoordType(m);
408 }
409 }
410
411 // synchronize over all processes, if necessary
412 void _synchronize(std::vector<Tiny::Vector<CoordType, world_dim>>& sum_neigh, std::vector<CoordType>& deg) override
413 {
414 // get our halo map
415 const auto& halo_map = _root_mesh_node.get_halo_map();
416
417 // if our halo map is empty or it's only one process -> abort
418 if (halo_map.empty() || _comm.size() == 1) return;
419
420 // sum_neigh has dimension world_dim and deg has dimension 1
421 const std::size_t stride = std::size_t(world_dim + 1);
422
423 // create buffers
424 std::vector<int> ranks;
425 std::vector<std::vector<CoordType>> send_bufs, recv_bufs;
426 Dist::RequestVector send_reqs, recv_reqs;
427 ranks.reserve(halo_map.size());
428 send_bufs.resize(halo_map.size());
429 recv_bufs.resize(halo_map.size());
430 send_reqs.reserve(halo_map.size());
431 recv_reqs.reserve(halo_map.size());
432
433 // post receives
434 for (auto it = halo_map.begin(); it != halo_map.end(); ++it)
435 {
436 // get halo rank and mesh part
437 const int halo_rank = it->first;
438 const auto& part = it->second;
439 if (!part)
440 continue;
441 if (halo_rank == _comm.rank())
442 continue;
443
444 // get halo vertices and size
445 const auto& halo_vtx = part->template get_target_set<0>();
446 const Index halo_size = halo_vtx.get_num_entities();
447
448 // calculate necessary receive buffer length
449 const std::size_t len = std::size_t(halo_size) * stride;
450
451 // allocate buffers
452 const std::size_t idx = ranks.size();
453 ranks.push_back(halo_rank);
454 auto& rbuf = recv_bufs[idx];
455 rbuf.resize(len);
456
457 // post receive
458 recv_reqs.push_back(_comm.irecv(rbuf.data(), rbuf.size(), halo_rank));
459 }
460
461 // post sends
462 for (std::size_t idx = 0; idx < ranks.size(); ++idx)
463 {
464 // get halo rank
465 const int halo_rank = ranks[idx];
466 auto it = halo_map.find(halo_rank);
467 XASSERT(it != halo_map.end());
468
469 // get halo vertices and size
470 const auto& halo_vtx = it->second->template get_target_set<0>();
471 const Index halo_size = halo_vtx.get_num_entities();
472
473 // calculate necessary send buffer length
474 const std::size_t len = std::size_t(halo_size) * stride;
475
476 // allocate buffers
477 auto& sbuf = send_bufs[idx];
478 sbuf.resize(len);
479
480 // loop over all halos
481 for (Index k(0); k < halo_size; ++k)
482 {
483 const Index v = halo_vtx[k];
484 const std::size_t base = std::size_t(k) * stride;
485 // copy sum_neigh and deg in send buffer
486 for (int d(0); d < world_dim; ++d)
487 {
488 sbuf[base + std::size_t(d)] = sum_neigh[v][d];
489 }
490 sbuf[base + std::size_t(world_dim)] = deg[v];
491 }
492
493 // post send
494 send_reqs.push_back(_comm.isend(sbuf.data(), sbuf.size(), halo_rank));
495 }
496
497 // merge entries
498 for (std::size_t idx(0); recv_reqs.wait_any(idx); )
499 {
500 // get halo rank
501 const int halo_rank = ranks.at(idx);
502 auto it = halo_map.find(halo_rank);
503 XASSERT(it != halo_map.end());
504
505 // get halo vertices and size
506 const auto& halo_vtx = it->second->template get_target_set<0>();
507 const Index halo_size = halo_vtx.get_num_entities();
508 const auto& rbuf = recv_bufs.at(idx);
509
510 // check if the size is right
511 XASSERT(rbuf.size() == std::size_t(halo_size) * stride);
512
513 // loop over all halos
514 for (Index k(0); k < halo_size; ++k)
515 {
516 const Index v = halo_vtx[k];
517 const std::size_t base = std::size_t(k) * stride;
518
519 // add up sum_neigh and deg
520 for (int d(0); d < world_dim; ++d)
521 {
522 sum_neigh[v][d] += rbuf[base + std::size_t(d)];
523 }
524 deg[v] += rbuf[base + std::size_t(world_dim)];
525 }
526 }
527
528 // wait for all sends
529 send_reqs.wait_all();
530 }
531
532 // calculate global maximum, if necessary
533 void _calc_max(CoordType& global_max) override
534 {
535 _comm.allreduce(&global_max, &global_max, std::size_t(1), Dist::op_max);
536 }
537 }; // class DistributedUmbrellaSmoother
538 } // namespace Geometry
539} // namespace FEAT
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
Communicator class.
Definition: dist.hpp:1349
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
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
DistributedUmbrellaSmoother(const Dist::Comm &comm, RootMeshNodeType &root_mesh_node)
Constructor.
virtual void _calc_boundary_facets() override
determines for each facet whether it is a boundary facet or an inner facet
void _calc_max(CoordType &global_max) override
calculates global maximum over all processes; is overridden in DistributedUmbrellaSmoother
RootMeshNodeType & _root_mesh_node
a reference to our root mesh node
void _init_edge_multiplicity()
Computes per-edge weights for distributed smoothing.
const Dist::Comm & _comm
a reference to our communicator
void _synchronize(std::vector< Tiny::Vector< CoordType, world_dim > > &sum_neigh, std::vector< CoordType > &deg) override
synchronizes the smoother over all processes; is overridden in DistributedUmbrellaSmoother
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
Umbrella mesh smoother with both uniform and scale-dependent neighbor averaging For more information ...
UmbrellaSmoother(MeshType_ &mesh)
Constructor.
std::vector< int > _boundary_facets
vector to store which facet is at the boundary
MeshType_ & _mesh
the mesh we want to smooth
void smooth(Index max_iter, CoordType tol, bool uniform=true)
Smooths the mesh with an umbrella operator.
virtual void _calc_boundary_vertices()
determines for each vertex whether it is a boundary vertex or an inner vertex
virtual void _calc_boundary_facets()
determines for each facet whether it is a boundary facet or an inner facet
std::vector< int > _boundary_vertices
vector to store which vertex is at the boundary
virtual void _synchronize(std::vector< Tiny::Vector< CoordType, world_dim > > &, std::vector< CoordType > &)
synchronizes the smoother over all processes; is overridden in DistributedUmbrellaSmoother
virtual void _calc_max(CoordType &)
calculates global maximum over all processes; is overridden in DistributedUmbrellaSmoother
Tiny Vector class template.
const Operation op_max(MPI_MAX)
Operation wrapper for MPI_MAX.
Definition: dist.hpp:273
T_ sqrt(T_ x)
Returns the square-root of a value.
Definition: math.hpp:300
void maxi(T_ &xmax, T_ x)
Updates the maximum of a set of values.
Definition: math.hpp:172
FEAT namespace.
Definition: adjactor.hpp:12
std::uint64_t Index
Index data type.