FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
transfer_voxel_asm.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#include <kernel/lafem/dense_vector.hpp>
9#include <kernel/lafem/dense_vector_blocked.hpp>
10#include <kernel/lafem/vector_mirror.hpp>
11#include <kernel/global/gate.hpp>
12#include <kernel/global/transfer.hpp>
13#include <kernel/assembly/symbolic_assembler.hpp>
14#include <kernel/assembly/grid_transfer.hpp>
15
16#include <control/domain/voxel_domain_control.hpp>
17
18namespace FEAT
19{
20 namespace Control
21 {
22 namespace Asm
23 {
24 namespace VoxelAux
25 {
27 template<typename Vector_, typename Mirror_>
28 static Vector_ deslag_vector(const Vector_& vector, const Mirror_& mirror)
29 {
30 const Index num_idx = mirror.num_indices();
31 Vector_ vector_x(num_idx);
32 const auto* v = vector.elements();
33 auto* x = vector_x.elements();
34 const auto* idx_f = mirror.indices();
35 for(Index i(0); i < num_idx; ++i)
36 x[i] = v[idx_f[i]];
37 return vector_x;
38 }
39
41 template<typename Matrix_, typename Mirror_>
42 static Matrix_ deslag_matrix_rows(const Matrix_& matrix, const Mirror_& row_mirror)
43 {
44 typedef typename Matrix_::IndexType IndexType;
45 const Index num_idx = row_mirror.num_indices();
46 const auto* idx_f = row_mirror.indices();
47
48 const IndexType* row_ptr_s = matrix.row_ptr();
49 const IndexType* col_idx_s = matrix.col_ind();
50 Index num_nze = 0u;
51 for(Index i(0); i < num_idx; ++i)
52 num_nze += row_ptr_s[idx_f[i] + 1u] - row_ptr_s[idx_f[i]];
53
54 Matrix_ matrix_x(num_idx, matrix.columns(), num_nze);
55 IndexType* row_ptr_x = matrix_x.row_ptr();
56 IndexType* col_idx_x = matrix_x.col_ind();
57 auto* val_x = matrix_x.val();
58 const auto* val_s = matrix.val();
59 row_ptr_x[0] = 0u;
60 for(Index i(0); i < num_idx; ++i)
61 {
62 Index row_s = idx_f[i];
63 IndexType k(row_ptr_x[i]);
64 for(auto j(row_ptr_s[row_s]); j < row_ptr_s[row_s+1u]; ++j, ++k)
65 {
66 col_idx_x[k] = col_idx_s[j];
67 val_x[k] = val_s[j];
68 }
69 row_ptr_x[i+1u] = k;
70 }
71 return matrix_x;
72 }
73
75 template<typename Matrix_, typename Mirror_>
76 static Matrix_ deslag_matrix_cols(const Matrix_& matrix, const Mirror_& col_mirror)
77 {
78 typedef typename Matrix_::IndexType IndexType;
79 const Index num_idx = col_mirror.num_indices();
80 const auto* idx_f = col_mirror.indices();
81
82 std::vector<Index> col_map(matrix.columns(), ~Index(0));
83 for(Index i(0); i < num_idx; ++i)
84 col_map[idx_f[i]] = i;
85
86 const Index num_rows = matrix.rows();
87 const IndexType* row_ptr_s = matrix.row_ptr();
88 const IndexType* col_idx_s = matrix.col_ind();
89 Index used_elems = matrix.used_elements();
90 Index num_nze = 0u;
91 for(Index i(0); i < used_elems; ++i)
92 num_nze += (col_map[col_idx_s[i]] != ~Index(0));
93
94 Matrix_ matrix_x(num_rows, num_idx, num_nze);
95 IndexType* row_ptr_x = matrix_x.row_ptr();
96 IndexType* col_idx_x = matrix_x.col_ind();
97 auto* val_x = matrix_x.val();
98 const auto* val_s = matrix.val();
99 row_ptr_x[0] = 0u;
100 for(Index i(0); i < num_rows; ++i)
101 {
102 IndexType k(row_ptr_x[i]);
103 for(auto j(row_ptr_s[i]); j < row_ptr_s[i+1u]; ++j)
104 {
105 if(col_map[col_idx_s[j]] != ~Index(0))
106 {
107 col_idx_x[k] = IndexType(col_map[col_idx_s[j]]);
108 val_x[k] = val_s[j];
109 ++k;
110 }
111 }
112 row_ptr_x[i+1u] = k;
113 }
114 return matrix_x;
115 }
116 } // namespace VoxelAux
117
156 template<typename DomainLevel_, typename SpaceLambda_, typename Transfer_, typename Muxer_, typename Gate_>
157 void asm_transfer_voxel_scalar(
158 const Control::Domain::VirtualLevel<Control::Domain::VoxelDomainLevelWrapper<DomainLevel_>>& virt_lvl_f,
159 const Control::Domain::VirtualLevel<Control::Domain::VoxelDomainLevelWrapper<DomainLevel_>>& virt_lvl_c,
160 const String& cubature, bool trunc, bool shrink,
161 SpaceLambda_&& space_lambda, Transfer_& transfer, const Muxer_& muxer, const Gate_& gate_f, const Gate_& gate_c)
162 {
163 typedef typename Transfer_::DataType DataType;
164 typedef typename Transfer_::IndexType IndexType;
165 typedef typename Gate_::MirrorType MirrorType;
166 typedef LAFEM::DenseVector<DataType, IndexType> ScalarVectorType;
167
168 // do we have a slag level?
169 const bool have_slag(virt_lvl_f->slag_level);
170
171 // we have: C -> S >> F, where '->' is the prolongation and '>>' is the deslagging operator
172 const DomainLevel_& level_f = have_slag ? *virt_lvl_f->slag_level : static_cast<const DomainLevel_&>(*virt_lvl_f);
173 const DomainLevel_& level_c = virt_lvl_c.is_child() ? virt_lvl_c.level_c() : *virt_lvl_c;
174
175 //const auto& space_f = level_f.space;
176 const auto& space_f = *space_lambda(level_f);
177 const auto& space_c = *space_lambda(level_c);
178
179 // get local transfer matrices
180 auto& loc_prol = transfer.get_mat_prol();
181 auto& loc_rest = transfer.get_mat_rest();
182 auto& loc_trunc = transfer.get_mat_trunc();
183
184 // assemble structure?
185 if (loc_prol.empty())
186 Assembly::SymbolicAssembler::assemble_matrix_2lvl(loc_prol, space_f, space_c);
187
188 // create local weight vectors
189 auto loc_vec_weight_p = loc_prol.create_vector_l();
190 auto loc_vec_weight_t = loc_prol.create_vector_r();
191
192 // assemble prolongation matrix
193 loc_prol.format();
194 loc_vec_weight_p.format();
195 loc_vec_weight_t.format();
196
197 // if we need a truncation matrix, then compute its sparsity pattern now
198 if(trunc)
199 loc_trunc.transpose(loc_prol);
200
201 // assemble prolongation matrix
202 Assembly::GridTransfer::assemble_prolongation(loc_prol, loc_vec_weight_p, space_f, space_c, cubature);
203
204 // assemble truncation matrix if desired
205 if(trunc)
206 Assembly::GridTransfer::assemble_truncation(loc_trunc, loc_vec_weight_t, space_f, space_c, cubature);
207
208 // do we have a slag level?
209 if(have_slag)
210 {
211 // assemble a slag mirror
212 const auto* slag_part = level_f.find_patch_part(-1);
213 XASSERTM(slag_part != nullptr, "slag patch part not found!");
214 MirrorType slag_mirror;
215 Assembly::MirrorAssembler::assemble_mirror(slag_mirror, space_f, *slag_part);
216
217 loc_vec_weight_p = VoxelAux::deslag_vector(loc_vec_weight_p, slag_mirror);
218 loc_prol = VoxelAux::deslag_matrix_rows(loc_prol, slag_mirror);
219 if(trunc)
220 {
221 loc_trunc = VoxelAux::deslag_matrix_cols(loc_trunc, slag_mirror);
222 }
223 }
224
225 // synchronize weight vector using the gate
226 gate_f.sync_0(loc_vec_weight_p);
227
228 // invert components
229 loc_vec_weight_p.component_invert(loc_vec_weight_p);
230
231 // scale prolongation matrix
232 loc_prol.scale_rows(loc_prol, loc_vec_weight_p);
233
234 // shrink prolongation matrix if desired
235 if(shrink)
236 loc_prol.shrink(DataType(1E-3) * loc_prol.max_abs_element());
237
238 // copy and transpose
239 loc_rest = loc_prol.transpose();
240
241 // do we need a truncation operator?
242 if(!trunc)
243 return;
244
245 // We now need to synchronize the weight vector in analogy to the prolongation matrix assembly.
246 // Note that the weight vector is now a coarse-level vector, so we need to synchronize over
247 // the coarse-level gate. This may be a bit more complicated if the coarse level is a ghost
248 // level, as in this case we have to join/split around the synchronize operation.
249
250 // synchronize weight vector using the muxer/gate
251 if(!virt_lvl_c.is_child())
252 {
253 XASSERT(&gate_f != &gate_c);
254
255 // The coarse level is a simple (non-child) level that exists on all processes,
256 // so simply synchronize over the coarse-level gate:
257 gate_c.sync_0(loc_vec_weight_t);
258 }
259 else if(virt_lvl_c.is_parent())
260 {
261 XASSERT(&gate_f != &gate_c);
262
263 // The coarse level is a child level and this is one of the parent processes which contain
264 // the coarse-level gate. So we first need to join the weight vector onto the parent processes
265 // first, then synch that joined vector over the parent gate and finally split the result
266 // to all child processes -- this "emulates" a synch over the (non-existent) coarse-level
267 // child gate, which is what we actually require here...
268
269 // create temporary vector on parent partitioning
270 ScalarVectorType loc_tmp(gate_c.get_freqs().size());
271
272 // join child weights over muxer
273 muxer.join(loc_vec_weight_t, loc_tmp);
274
275 // sync over coarse gate
276 gate_c.sync_0(loc_tmp);
277
278 // split over muxer
279 muxer.split(loc_vec_weight_t, loc_tmp);
280 }
281 else // ghost
282 {
283 // The coarse level is a ghost level, i.e. a child but not a parent. In this case, we
284 // only have to participate in the join/send operations of the muxer, which are part
285 // of the operations executed on the parents handled by the else-if case above.
286
287 muxer.join_send(loc_vec_weight_t);
288
289 // parent performs sync over its gate here (see above else-if)
290
291 muxer.split_recv(loc_vec_weight_t);
292 }
293
294 // invert components
295 loc_vec_weight_t.component_invert(loc_vec_weight_t);
296
297 // scale prolongation matrix
298 loc_trunc.scale_rows(loc_trunc, loc_vec_weight_t);
299
300 // shrink prolongation matrix if desired
301 if(shrink)
302 loc_trunc.shrink(DataType(1E-3) * loc_trunc.max_abs_element());
303 }
304
343 template<typename DomainLevel_, typename SpaceLambda_, typename Transfer_, typename Muxer_, typename Gate_>
344 static void asm_transfer_voxel_blocked(
345 const Control::Domain::VirtualLevel<Control::Domain::VoxelDomainLevelWrapper<DomainLevel_>>& virt_lvl_f,
346 const Control::Domain::VirtualLevel<Control::Domain::VoxelDomainLevelWrapper<DomainLevel_>>& virt_lvl_c,
347 const String& cubature, bool trunc, bool shrink,
348 SpaceLambda_&& space_lambda, Transfer_& transfer, const Muxer_& muxer, const Gate_& gate_f, const Gate_& gate_c)
349 {
350 typedef typename Transfer_::DataType DataType;
351 typedef typename Transfer_::IndexType IndexType;
352 typedef typename Gate_::MirrorType MirrorType;
353 typedef LAFEM::DenseVector<DataType, IndexType> ScalarVectorType;
354
355 // do we have a slag level?
356 const bool have_slag(virt_lvl_f->slag_level);
357
358 // we have: C -> S >> F, where '->' is the prolongation and '>>' is the deslagging operator
359 const DomainLevel_& level_f = have_slag ? *virt_lvl_f->slag_level : static_cast<const DomainLevel_&>(*virt_lvl_f);
360 const DomainLevel_& level_c = virt_lvl_c.is_child() ? virt_lvl_c.level_c() : *virt_lvl_c;
361
362 //const auto& space_f = level_f.space;
363 const auto& space_f = *space_lambda(level_f);
364 const auto& space_c = *space_lambda(level_c);
365
366 // get local transfer matrices
367 auto& loc_prol_wrapped = transfer.get_mat_prol();
368 auto& loc_rest_wrapped = transfer.get_mat_rest();
369 auto& loc_trunc_wrapped = transfer.get_mat_trunc();
370
371 // get the unwrapped types
372 auto& loc_prol = loc_prol_wrapped.unwrap();
373 auto& loc_rest = loc_rest_wrapped.unwrap();
374 auto& loc_trunc = loc_trunc_wrapped.unwrap();
375
376 // assemble structure?
377 if (loc_prol.empty())
378 Assembly::SymbolicAssembler::assemble_matrix_2lvl(loc_prol, space_f, space_c);
379
380 // create local weight vector
381 auto loc_vec_weight_p = loc_prol.create_vector_l();
382 auto loc_vec_weight_t = loc_prol.create_vector_r();
383
384 // assemble prolongation matrix
385 loc_prol.format();
386 loc_vec_weight_p.format();
387 loc_vec_weight_t.format();
388
389 // if we need a truncation matrix, then compute its sparsity pattern now
390 if(trunc)
391 loc_trunc.transpose(loc_prol);
392
393 // assemble prolongation matrix
394 Assembly::GridTransfer::assemble_prolongation(loc_prol, loc_vec_weight_p, space_f, space_c, cubature);
395
396 // assemble truncation matrix if desired
397 if(trunc)
398 Assembly::GridTransfer::assemble_truncation(loc_trunc, loc_vec_weight_t, space_f, space_c, cubature);
399
400 // do we have a slag level?
401 if(have_slag)
402 {
403 // assemble a slag mirror
404 const auto* slag_part = level_f.find_patch_part(-1);
405 XASSERTM(slag_part != nullptr, "slag patch part not found!");
406 MirrorType slag_mirror;
407 Assembly::MirrorAssembler::assemble_mirror(slag_mirror, space_f, *slag_part);
408
409 loc_vec_weight_p = VoxelAux::deslag_vector(loc_vec_weight_p, slag_mirror);
410 loc_prol = VoxelAux::deslag_matrix_rows(loc_prol, slag_mirror);
411 if(trunc)
412 {
413 loc_trunc = VoxelAux::deslag_matrix_cols(loc_trunc, slag_mirror);
414 }
415 }
416
417 // create a temporary scalar gate
418 Global::Gate<ScalarVectorType, MirrorType> scalar_gate;
419 scalar_gate.convert(gate_f, loc_vec_weight_p.clone(LAFEM::CloneMode::Allocate));
420
421 // synchronize weight vector using the scalar gate
422 scalar_gate.sync_0(loc_vec_weight_p);
423
424 // invert components
425 loc_vec_weight_p.component_invert(loc_vec_weight_p);
426
427 // scale prolongation matrix
428 loc_prol.scale_rows(loc_prol, loc_vec_weight_p);
429
430 // shrink prolongation matrix if desired
431 if(shrink)
432 loc_prol.shrink(DataType(1E-3) * loc_prol.max_abs_element());
433
434 // copy and transpose
435 loc_rest = loc_prol.transpose();
436
437 // do we need a truncation operator?
438 if(!trunc)
439 return;
440
441 // We now need to synchronize the weight vector in analogy to the prolongation matrix assembly.
442 // Note that the weight vector is now a coarse-level vector, so we need to synchronize over
443 // the coarse-level gate. This may be a bit more complicated if the coarse level is a ghost
444 // level, as in this case we have to join/split around the synchronize operation.
445
446 // synchronize weight vector using the muxer/gate
447 if(!virt_lvl_c.is_child())
448 {
449 XASSERT(&gate_f != &gate_c);
450
451 // The coarse level is a simple (non-child) level that exists on all processes,
452 // so simply synchronize over the coarse-level gate:
453
454 // create a temporary scalar gate on the coarse level
455 Global::Gate<ScalarVectorType, MirrorType> scalar_gate_c;
456 scalar_gate_c.convert(gate_c, ScalarVectorType(space_c.get_num_dofs()));
457
458 // sync over scalar coarse gate
459 scalar_gate_c.sync_0(loc_vec_weight_t);
460 }
461 else if(virt_lvl_c.is_parent())
462 {
463 XASSERT(&gate_f != &gate_c);
464
465 // The coarse level is a child level and this is one of the parent processes which contain
466 // the coarse-level gate. So we first need to join the weight vector onto the parent processes
467 // first, then synch that joined vector over the parent gate and finally split the result
468 // to all child processes -- this "emulates" a synch over the (non-existent) coarse-level
469 // child gate, which is what we actually require here...
470
471 // get the number of the coarse level dofs from the gate's frequency vector, which is different
472 // than space_c.get_num_dofs(), because the first one is defined on the parent patch, whereas
473 // the latter one is defined on the child patch
474 const Index num_dofs_c = gate_c.get_freqs().size();
475
476 // create a temporary scalar gate on the coarse level
477 Global::Gate<ScalarVectorType, MirrorType> scalar_gate_c;
478 scalar_gate_c.convert(gate_c, ScalarVectorType(num_dofs_c));
479
480 // create a temporary scalar muxer on the fine level
481 Global::Muxer<ScalarVectorType, MirrorType> scalar_muxer_f;
482 scalar_muxer_f.convert(muxer, ScalarVectorType(space_f.get_num_dofs()));
483
484 // create temporary vector on parent partitioning
485 ScalarVectorType loc_tmp(num_dofs_c);
486
487 // join child weights over muxer
488 scalar_muxer_f.join(loc_vec_weight_t, loc_tmp);
489
490 // sync over scalar coarse gate
491 scalar_gate_c.sync_0(loc_tmp);
492
493 // split over muxer
494 scalar_muxer_f.split(loc_vec_weight_t, loc_tmp);
495 }
496 else // ghost
497 {
498 // The coarse level is a ghost level, i.e. a child but not a parent. In this case, we
499 // only have to participate in the join/send operations of the muxer, which are part
500 // of the operations executed on the parents handled by the else-if case above.
501
502 // create a temporary scalar muxer on the fine level
503 Global::Muxer<ScalarVectorType, MirrorType> scalar_muxer_f;
504 scalar_muxer_f.convert(muxer, ScalarVectorType(space_f.get_num_dofs()));
505
506 scalar_muxer_f.join_send(loc_vec_weight_t);
507
508 // parent performs sync over its gate here (see above else-if)
509
510 scalar_muxer_f.split_recv(loc_vec_weight_t);
511 }
512
513 // invert components
514 loc_vec_weight_t.component_invert(loc_vec_weight_t);
515
516 // scale prolongation matrix
517 loc_trunc.scale_rows(loc_trunc, loc_vec_weight_t);
518
519 // shrink prolongation matrix if desired
520 if(shrink)
521 loc_trunc.shrink(DataType(1E-3) * loc_trunc.max_abs_element());
522 }
523 } // namespace Asm
524 } // namespace Control
525} // 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
FEAT Kernel base header.
static void assemble_truncation(Matrix_ &matrix, Vector_ &vector, const FineSpace_ &fine_space, const CoarseSpace_ &coarse_space, const String &cubature_name)
Assembles a truncation matrix and its corresponding weight vector.
static void assemble_prolongation(Matrix_ &matrix, Vector_ &vector, const FineSpace_ &fine_space, const CoarseSpace_ &coarse_space, const String &cubature_name)
Assembles a prolongation matrix and its corresponding weight vector.
static void assemble_mirror(LAFEM::VectorMirror< DataType_, IndexType_ > &vec_mirror, const Space_ &space, const MeshPart_ &mesh_part)
Assembles a VectorMirror from a space and a mesh-part.
static void assemble_matrix_2lvl(MatrixType_ &matrix, const FineSpace_ &fine_space, const CoarseSpace_ &coarse_space)
Assembles a 2-level matrix structure from a fine-coarse-space pair.
FEAT namespace.
Definition: adjactor.hpp:12
std::uint64_t Index
Index data type.