FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
adaptive_parti_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
9#include <kernel/geometry/adaptive_mesh.hpp>
10#include <kernel/geometry/adaptive_mesh_layer.hpp>
11#include <kernel/geometry/mesh_node.hpp>
12#include <kernel/geometry/mesh_permutation.hpp>
13#include <kernel/geometry/subdivision_levels.hpp>
14#include <kernel/util/dist.hpp>
15
16#include <kernel/geometry/boundary_factory.hpp>
17#include <kernel/geometry/common_factories.hpp>
18#include <kernel/geometry/mesh_file_reader.hpp>
19
20#include <control/domain/parti_domain_control.hpp>
21#include <memory>
22#include <optional>
23#include <utility>
24
26{
35 template<typename DomainLevel_, typename TemplateSet_>
36 class AdaptiveLevelWrapper : public DomainLevel_
37 {
38 public:
40 using BaseClass = DomainLevel_;
42 using typename BaseClass::MeshType;
44 using typename BaseClass::MeshNodeType;
49
51 std::optional<AdaptiveMeshLayerType> mesh_layer;
52
54 explicit AdaptiveLevelWrapper(int _lvl_idx, std::unique_ptr<MeshNodeType> node) :
55 BaseClass(_lvl_idx, std::move(node)),
56 mesh_layer(std::nullopt)
57 {
58 }
59
61 explicit AdaptiveLevelWrapper(int _lvl_idx, std::unique_ptr<MeshNodeType> node, AdaptiveMeshLayerType&& layer) :
62 BaseClass(_lvl_idx, std::move(node)),
63 mesh_layer(std::make_optional<AdaptiveMeshLayerType>(std::move(layer)))
64 {
65 }
66
69 {
70 return bool(mesh_layer);
71 }
72 }; // class AdaptiveLevelWrapper<...>
73
95 template<typename DomainLevel_, typename TemplateSet_>
97 {
98 public:
102 using typename BaseClass::LevelType;
104 using typename BaseClass::LayerType;
106 using typename BaseClass::MeshType;
108 using typename BaseClass::AtlasType;
110 using typename BaseClass::MeshNodeType;
112 using typename BaseClass::MeshPartType;
114 using typename BaseClass::Ancestor;
116 using typename BaseClass::WeightType;
117
119
120 protected:
122 std::function<void(Geometry::SubdivisionLevels&, const MeshType&)> _refinement_strategy;
123
124 std::shared_ptr<AdaptiveMeshType> _adaptive_mesh;
125
126 public:
137 const Dist::Comm& comm_,
138 bool support_multi_layered,
139 std::function<void(Geometry::SubdivisionLevels&, const MeshType&)> refinement_strategy) :
140 BaseClass(comm_, support_multi_layered),
141 _refinement_strategy(std::move(refinement_strategy))
142 {
143 this->_allow_parti_2level = false;
144 }
145
147 virtual ~AdaptivePartiDomainControl() = default;
148
151 {
152 return *_adaptive_mesh;
153 }
154
155 protected:
157
164 std::vector<WeightType> _compute_weights(Ancestor& DOXY(ancestor), const MeshNodeType& base_mesh_node) override
165 {
166 static constexpr int dim = MeshType::ShapeType::dimension;
167 const WeightType average_elements_per_marking =
168 WeightType(TemplateSet_::template average_elements_per_marking<dim>());
169
170 const MeshType& mesh = *base_mesh_node.get_mesh();
171 const auto& v_at_c = mesh.template get_index_set<dim, 0>();
172
173 Geometry::SubdivisionLevels sdls(mesh.get_num_vertices());
174 std::vector<WeightType> weights(mesh.get_num_elements());
175
176 // Get subdivision levels for mesh
177 _refinement_strategy(sdls, mesh);
178
179 // Determine weights from subdivision levels
180 for(Index cell(0); cell < weights.size(); cell++)
181 {
182 std::uint64_t markings(0);
183 for(auto i = v_at_c.image_begin(cell); i != v_at_c.image_end(cell); ++i)
184 {
185 markings += sdls[*i];
186 }
187 // Minimum weight is 1
188 weights[cell] = Math::max(WeightType(1), WeightType(markings) * average_elements_per_marking);
189 }
190
191 return weights;
192 }
193
205 void _create_single_process(std::unique_ptr<MeshNodeType> base_mesh_node) override
206 {
207 // Perform regular refinement of conformal mesh
208 BaseClass::_create_single_process(std::move(base_mesh_node));
210 }
211
212 // Note: all following member functions are only required for parallel builds,
213 // so we enclose them in the following #if-block to reduce compile times.
214
215#if defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
216
223 void _create_single_layered(std::unique_ptr<MeshNodeType> base_mesh_node) override
224 {
225 BaseClass::_create_single_layered(std::move(base_mesh_node));
227 }
228
235 void _create_multi_layered(std::unique_ptr<MeshNodeType> base_mesh_node) override
236 {
237 BaseClass::_create_multi_layered(std::move(base_mesh_node));
239 }
240
241#endif // defined(FEAT_HAVE_MPI) || defined(DOXYGEN)
249 void refine_partially()
250 {
251 const MeshNodeType& mesh_node = *this->_layer_levels.at(0).front()->get_mesh_node();
252 Geometry::SubdivisionLevels sdls(mesh_node.get_mesh()->get_num_vertices());
253
254 // Get requested subdivision levels
255 _refinement_strategy(sdls, *mesh_node.get_mesh());
256
257 // Sync subdivision levels across processes
258 sync_subdivision_levels(sdls, mesh_node);
259
260 // Sync maximum subdivision level across ranks
261 // This value will determine how many copy-layers we need to add
262 const std::uint64_t sdl_max_local = sdls.maximum_level();
263 std::uint64_t sdl_max;
264 this->comm().allreduce(&sdl_max_local, &sdl_max, std::size_t(1), Dist::op_max);
265
266 // Create adaptive mesh
267 _adaptive_mesh = std::make_shared<AdaptiveMeshType>(*mesh_node.get_mesh());
268 _adaptive_mesh->adapt(sdls, AdaptiveMeshType::ImportBehaviour::All);
269
270 int lvl = this->_chosen_levels.front().first;
271 for(Index i(1); i <= sdl_max; i++)
272 {
273 // Either push the next layer of the adaptive mesh or a copy of the last layer.
274 // We push a copy to ensure we correctly participate in global operations on all levels.
275 // TODO: Use a more efficient method, rather than copying the previous level
276 Geometry::Layer layer_idx{Math::min(i, _adaptive_mesh->num_layers() - 1)};
277 Geometry::AdaptiveMeshLayer<AdaptiveMeshType> layer(_adaptive_mesh, layer_idx);
278 this->push_level_front(
279 0,
280 std::make_shared<LevelType>(
281 lvl++,
282 project_mesh_node(*_adaptive_mesh, layer_idx, mesh_node),
283 std::move(layer)));
284 }
285
286 this->_chosen_levels.emplace_front(lvl, this->_comm.size());
287 }
288
306 std::unique_ptr<MeshNodeType> project_mesh_node(
307 const AdaptiveMeshType& mesh,
309 const MeshNodeType& root_node,
310 Geometry::AdaptMode adapt_mode = Geometry::AdaptMode::chart)
311 {
312
313 auto cmesh = std::make_unique<MeshType>(mesh.to_conformal_mesh(l));
314 auto result = std::make_unique<MeshNodeType>(std::move(cmesh));
315
316 // Projects mesh parts
317 for(const auto& name : root_node.get_mesh_part_names())
318 {
319 const auto& mesh_part_node = root_node.find_mesh_part_node(name);
320 const auto* mesh_part = mesh_part_node->get_mesh();
321
322 std::unique_ptr<MeshPartType> new_mesh_part;
323 if(mesh_part)
324 {
325 new_mesh_part = std::make_unique<MeshPartType>(mesh.template project_meshpart<MeshType>(l, *mesh_part));
326 }
327 else
328 {
329 new_mesh_part = std::unique_ptr<MeshPartType>(nullptr);
330 }
331 auto new_mesh_part_node = std::make_unique<Geometry::MeshPartNode<MeshType>>(std::move(new_mesh_part));
332
333 result->add_mesh_part_node(
334 name,
335 std::move(new_mesh_part_node),
336 mesh_part_node->find_mesh_part_chart_name(name),
337 mesh_part_node->find_mesh_part_chart(name));
338 }
339
340 // refine our halos
341 for(const auto& [rank, part] : root_node.get_halo_map())
342 {
343 if(part)
344 {
345 XASSERT(bool(part));
346 auto new_halo = std::make_unique<MeshPartType>(mesh.template project_meshpart<MeshType>(l, *part));
347 result->add_halo(rank, std::move(new_halo));
348 }
349 else
350 {
351 result->add_halo(rank, nullptr);
352 }
353 }
354
355 // refine our patch mesh-parts
356 for(const auto& [rank, patch] : root_node.get_patch_map())
357 {
358 if(patch)
359 {
360 XASSERT(bool(patch));
361 auto new_patch = std::make_unique<MeshPartType>(mesh.template project_meshpart<MeshType>(l, *patch));
362 result->add_patch(rank, std::move(new_patch));
363 }
364 else
365 {
366 result->add_patch(rank, nullptr);
367 }
368 }
369
370 // adapt by chart?
371 if((adapt_mode & Geometry::AdaptMode::chart) != Geometry::AdaptMode::none)
372 {
373 result->adapt(true);
374 }
375
376 // TODO: Adapt dual
377
378 return result;
379 }
380
397 {
398 XASSERT(mesh_node.get_mesh()->get_num_vertices() == sdls.size());
399
400 std::unordered_map<int, Dist::RequestVector> sends;
401 std::unordered_map<int, Dist::RequestVector> receives;
402
403 Geometry::SubdivisionLevels received_sdls(sdls.size(), 0);
404
405 // Post send and receives
406 for(const auto& [rank, halo] : mesh_node.get_halo_map())
407 {
408 const auto& vertices = halo->template get_target_set<0>();
409
410 for(Index i(0); i < vertices.get_num_entities(); i++)
411 {
412 sends[rank].push_back(this->_comm.isend(&sdls[vertices[i]], 1, rank));
413 receives[rank].push_back(this->_comm.irecv(&received_sdls[vertices[i]], 1, rank));
414 }
415 }
416
417 // Wait for all receives and map sdls
418 for(const auto& [rank, halo] : mesh_node.get_halo_map())
419 {
420 const auto& vertices = halo->template get_target_set<0>();
421 for(Index idx(0); receives[rank].wait_any(idx);)
422 {
423 const Index vidx = vertices[idx];
424 // Subdivision level is maximum of all processes values
425 sdls[vidx] = Math::max(sdls[vidx], received_sdls[vidx]);
426 }
427 }
428
429 // Wait for all sends to finish
430 for(const auto& [rank, halo] : mesh_node.get_halo_map())
431 {
432 sends[rank].wait_all();
433 }
434 }
435 };
436} // namespace FEAT::Control::Domain
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
FEAT Kernel base header.
Wrapper class for domain levels for AdaptivePartiDomainControl.
DomainLevel_ BaseClass
our base-class; the actual domain level
bool is_partial_level()
Returns true if this level belongs a potentially partial refinement.
AdaptiveLevelWrapper(int _lvl_idx, std::unique_ptr< MeshNodeType > node, AdaptiveMeshLayerType &&layer)
Constructor for partially refined levels.
AdaptiveLevelWrapper(int _lvl_idx, std::unique_ptr< MeshNodeType > node)
Contructor for fully refined levels.
std::optional< AdaptiveMeshLayerType > mesh_layer
View into adaptive mesh. If empty, this layer was created by a full refinement.
const AdaptiveMeshType & get_adaptive_mesh() const
Accessor for underlying adaptive mesh.
void _create_single_process(std::unique_ptr< MeshNodeType > base_mesh_node) override
Creates a single-layered mesh hierarchy for a single process.
void sync_subdivision_levels(Geometry::SubdivisionLevels &sdls, const MeshNodeType &mesh_node)
Sync subdivision levels across processes by taking the maximum across processes.
void _create_multi_layered(std::unique_ptr< MeshNodeType > base_mesh_node) override
Creates a multi-layered mesh hierarchy.
AdaptivePartiDomainControl(const Dist::Comm &comm_, bool support_multi_layered, std::function< void(Geometry::SubdivisionLevels &, const MeshType &)> refinement_strategy)
Constructor.
std::function< void(Geometry::SubdivisionLevels &, const MeshType &)> _refinement_strategy
the refinement strategy for the partially refined parts of the domain
std::unique_ptr< MeshNodeType > project_mesh_node(const AdaptiveMeshType &mesh, Geometry::Layer l, const MeshNodeType &root_node, Geometry::AdaptMode adapt_mode=Geometry::AdaptMode::chart)
Project a MeshNode from a regular mesh onto a layer of an adaptive mesh.
void _create_single_layered(std::unique_ptr< MeshNodeType > base_mesh_node) override
Creates a single-layered mesh hierarchy.
void refine_partially()
Create partially refined levels on top of the current finest level.
virtual ~AdaptivePartiDomainControl()=default
virtual destructor
std::vector< WeightType > _compute_weights(Ancestor &ancestor, const MeshNodeType &base_mesh_node) override
Compute weights for a-posteriori partitioners.
std::deque< std::pair< int, int > > _chosen_levels
chosen level deque
Recursively Partitioned Domain Control.
Real WeightType
weight type for partitioner element weights; always Real
Geometry::RootMeshNode< MeshType > MeshNodeType
our root mesh node type
virtual void _create_multi_layered(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a multi-layered mesh hierarchy.
LevelType::PartType MeshPartType
our mesh-part type
bool _allow_parti_2level
allow 2-level partitioner?
virtual void _create_single_process(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a single-layered mesh hierarchy for a single process.
virtual void _create_single_layered(std::unique_ptr< MeshNodeType > base_mesh_node)
Creates a single-layered mesh hierarchy.
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
Dynamic mesh data structure.
FoundationMeshType to_conformal_mesh(Layer layer) const
Create a ConformalMesh from a layer.
@ All
Import all entities. Useful for exporting the complete mesh later.
ConformalMesh interface for adaptive meshes.
std::deque< String > get_mesh_part_names(bool no_internals=false) const
Returns the names of all mesh parts of this node.
Definition: mesh_node.hpp:251
MeshType * get_mesh()
Returns the mesh of this node.
Definition: mesh_node.hpp:225
MeshPartNodeType * find_mesh_part_node(const String &part_name)
Searches this container for a MeshPartNode.
Definition: mesh_node.hpp:376
const std::map< int, std::unique_ptr< MeshPartType > > & get_patch_map() const
Definition: mesh_node.hpp:969
const std::map< int, std::unique_ptr< MeshPartType > > & get_halo_map() const
Definition: mesh_node.hpp:896
Subdivision level markings for meshes.
std::uint64_t maximum_level()
Returns the maximum refinement level assigned to any vertex.
const Operation op_max(MPI_MAX)
Operation wrapper for MPI_MAX.
Definition: dist.hpp:273
AdaptMode
Adapt mode enumeration.
Definition: mesh_node.hpp:53
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
std::uint64_t Index
Index data type.
Newtype wrapper for mesh layers.