FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
amavanka.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/lafem/null_matrix.hpp>
9#include <kernel/lafem/sparse_matrix_bcsr.hpp>
10#include <kernel/lafem/sparse_matrix_csr.hpp>
11#include <kernel/lafem/saddle_point_matrix.hpp>
12#include <kernel/lafem/tuple_matrix.hpp>
13#include <kernel/global/matrix.hpp>
14#include <kernel/solver/base.hpp>
15#include <kernel/solver/amavanka_base.hpp>
16#include <kernel/util/stop_watch.hpp>
17
18namespace FEAT
19{
20 namespace Solver
21 {
62 template<typename Matrix_, typename Filter_>
63 class AmaVanka :
64 public Solver::SolverBase<typename Matrix_::VectorTypeL>
65 {
66 public:
69
71 typedef typename Matrix_::DataType DataType;
73 typedef typename Matrix_::IndexType IndexType;
75 typedef typename Matrix_::VectorTypeL VectorType;
76
77 protected:
79 typedef typename Intern::AmaVankaMatrixHelper<Matrix_>::VankaMatrix VankaMatrixType;
80
82 const Matrix_& _matrix;
84 const Filter_& _filter;
92 std::vector<Adjacency::Graph> _macro_dofs, _dof_macros;
94 std::vector<int> _macro_mask;
101
102 // stop watch for symbolic factorization
103 StopWatch watch_init_symbolic;
104 // stop watch for numeric factorization
105 StopWatch watch_init_numeric;
106 // stop watch for apply time
107 StopWatch watch_apply;
108
111
112 public:
128 explicit AmaVanka(const Matrix_& matrix, const Filter_& filter,
129 const DataType omega = DataType(1), const Index num_steps = Index(1)) :
130 _matrix(matrix),
131 _filter(filter),
132 _vanka(),
133 _auto_macros(true),
134 _skip_singular(false),
135 _num_steps(num_steps),
136 _omega(omega)
137 {
138 }
139
144 {
145 this->_macro_dofs.clear();
146 _auto_macros = true;
147 }
148
156 {
157 _auto_macros = false;
158
159 // make sure that we do not push more graphs than we have blocks
160 if(int(_macro_dofs.size()) >= Intern::AmaVankaMatrixHelper<Matrix_>::num_blocks)
161 XABORTM("all macro-dofs graphs have already been added");
162
163 // make sure that the number of macros matches our previous graph
164 if(!_macro_dofs.empty() && (_macro_dofs.back().get_num_nodes_domain() != dofs.get_num_nodes_domain()))
165 XABORTM("macro count mismatch");
166
167 // push graph into the list
168 _macro_dofs.emplace_back(std::forward<Adjacency::Graph>(dofs));
169
170 // sort the dof indices
171 _macro_dofs.back().sort_indices();
172 }
173
180 void set_num_steps(Index num_steps)
181 {
182 XASSERT(num_steps > Index(0));
183 this->_num_steps = num_steps;
184 }
185
192 void set_omega(DataType omega)
193 {
194 XASSERT(omega > DataType(0));
195 this->_omega = omega;
196 }
197
204 void set_skip_singular(bool skip_sing)
205 {
206 this->_skip_singular = skip_sing;
207 }
208
212 std::size_t bytes() const
213 {
214 std::size_t s = _vanka.bytes();
215 for(const auto& g : _macro_dofs)
216 s += sizeof(Index) * std::size_t(g.get_num_nodes_domain() + g.get_num_indices());
217 for(const auto& g : _dof_macros)
218 s += sizeof(Index) * std::size_t(g.get_num_nodes_domain() + g.get_num_indices());
219 s += _vec_c.bytes();
220 s += _vec_d.bytes();
221 return s;
222 }
223
230 std::size_t data_size() const
231 {
232 return std::size_t(_vanka.template used_elements<LAFEM::Perspective::pod>());
233 }
234
239 {
240 watch_init_symbolic.reset();
241 watch_init_numeric.reset();
242 watch_apply.reset();
243 }
244
248 double time_init_symbolic() const
249 {
250 return watch_init_symbolic.elapsed();
251 }
252
256 double time_init_numeric() const
257 {
258 return watch_init_numeric.elapsed();
259 }
260
264 double time_apply() const
265 {
266 return watch_apply.elapsed();
267 }
268
270 virtual String name() const override
271 {
272 return "AmaVanka";
273 }
274
276 virtual void init_symbolic() override
277 {
278 watch_init_symbolic.start();
279
281
282 // we also need two vectors if we have to perform multiple steps
283 if(this->_num_steps > Index(1))
284 {
285 this->_vec_c = this->_matrix.create_vector_l();
286 this->_vec_d = this->_matrix.create_vector_l();
287 }
288
289 // automatically deduct macros?
290 if(this->_auto_macros)
291 {
292 // try to deduct macros by pressure matrices in SaddlePointMatrix
293 if(!Intern::AmaVankaCore::deduct_macro_dofs(this->_matrix, this->_macro_dofs))
294 XABORTM("Cannot auto-deduct macros for this matrix type");
295 }
296
297 // make sure we have one macro-dof graph for each matrix block
298 XASSERTM(int(_macro_dofs.size()) == Intern::AmaVankaMatrixHelper<Matrix_>::num_blocks,
299 "invalid number of macro-dof graphs; did you push all of them?");
300
301 // compute dof-macro graphs by transposing
302 this->_dof_macros.resize(this->_macro_dofs.size());
303 for(std::size_t i(0); i < this->_macro_dofs.size(); ++i)
304 {
305 // ensure that we have the same number of macros in all graphs
306 XASSERT(this->_macro_dofs.at(i).get_num_nodes_domain() == this->_macro_dofs.front().get_num_nodes_domain());
307
308 // transpose macro-dofs graph
309 this->_dof_macros.at(i) = Adjacency::Graph(Adjacency::RenderType::transpose, this->_macro_dofs.at(i));
310 }
311
312 // allocate macro skip mask?
313 if(this->_skip_singular)
314 this->_macro_mask.resize(this->_macro_dofs.front().get_num_nodes_domain(), 0);
315
316 Solver::Intern::AmaVankaCore::alloc(this->_vanka, this->_dof_macros, this->_macro_dofs, Index(0), Index(0));
317
318 watch_init_symbolic.stop();
319 }
320
322 virtual void done_symbolic() override
323 {
324 this->_vanka.clear();
325 this->_macro_mask.clear();
326 this->_dof_macros.clear();
327 if(this->_auto_macros)
328 this->_macro_dofs.clear();
329 if(this->_num_steps > Index(1))
330 {
331 this->_vec_d.clear();
332 this->_vec_c.clear();
333 }
335 }
336
338 virtual void init_numeric() override
339 {
340 const DataType eps = Math::eps<DataType>();
341
342 watch_init_numeric.start();
344
345 this->_vanka.format();
346
347 // get maximum macro size
348 const Index num_macros = Index(this->_macro_dofs.front().get_num_nodes_domain());
349 const Index stride = Intern::AmaVankaCore::calc_stride(this->_vanka, this->_macro_dofs);
350
351 FEAT_PRAGMA_OMP(parallel)
352 {
353 // allocate arrays for local matrix
354 std::vector<DataType> vec_local(stride*stride, DataType(0)), vec_local_t(stride*stride, DataType(0));
355 std::vector<Index> vec_pivot(stride);
356 DataType* local = vec_local.data();
357 DataType* local_t = vec_local_t.data();
358 Index* pivot = vec_pivot.data();
359
360 // loop over all macros
361 FEAT_PRAGMA_OMP(for)
362 for(Index imacro = 0; imacro < num_macros; ++imacro)
363 {
364 // gather local matrix
365 const std::pair<Index,Index> nrc = Intern::AmaVankaCore::gather(this->_matrix,
366 local, stride, imacro, this->_macro_dofs, Index(0), Index(0), Index(0), Index(0));
367
368 // make sure we have gathered a square matrix
369 XASSERTM(nrc.first == nrc.second, "local matrix is not square");
370
371 // assume a non-singular matrix
372 bool singular = false;
373
374 // do we check for singular macros?
375 if(this->_skip_singular)
376 {
377 // the approach used for checking the regularity of the local matrix is to check whether
378 //
379 // || I - A*A^{-1} ||_F^2 < eps
380 //
381 // we could try to analyse the pivots returned by invert_matrix function instead, but
382 // unfortunately this approach sometimes leads to false positives
383
384 // make a backup if checking for singularity
385 for(Index i(0); i < nrc.first; ++i)
386 for(Index j(0); j < nrc.second; ++j)
387 local_t[i*stride+j] = local[i*stride+j];
388
389 // invert local matrix
390 Math::invert_matrix(nrc.first, stride, local, pivot);
391
392 // compute (squared) Frobenius norm of (I - A*A^{-1})
393 DataType norm = DataType(0);
394 for(Index i(0); i < nrc.first; ++i)
395 {
396 for(Index j(0); j < nrc.first; ++j)
397 {
398 DataType xij = DataType(i == j ? 1 : 0);
399 for(Index k(0); k < nrc.first; ++k)
400 xij -= local_t[i*stride+k] * local[k*stride+j]; // A_ik * (A^{-1})_kj
401 norm += xij * xij;
402 }
403 }
404
405 // is the matrix block singular?
406 // Note: we check for !(norm < eps) instead of (norm >= eps),
407 // because the latter one evaluates to false if norm is NaN,
408 // which would result in a false negative
409 singular = !(norm < eps);
410
411 // set macro regularity mask
412 this->_macro_mask[imacro] = (singular ? 0 : 1);
413 }
414 else // no singularity check
415 {
416 // invert local matrix
417 Math::invert_matrix(nrc.first, stride, local, pivot);
418 }
419
420 // scatter local matrix
421 if(!singular)
422 {
423 FEAT_PRAGMA_OMP(critical)
424 {
425 Intern::AmaVankaCore::scatter_add(this->_vanka, local, stride, imacro, this->_macro_dofs,
426 Index(0), Index(0), Index(0), Index(0));
427 }
428 }
429
430 // reformat local matrix
431 for(Index i(0); i < nrc.first; ++i)
432 for(Index j(0); j < nrc.second; ++j)
433 local[i*stride+j] = DataType(0);
434 } // #pragma omp for
435 } // #pragma omp parallel
436
437 // scale rows of Vanka matrix
438 Solver::Intern::AmaVankaCore::scale_rows(this->_vanka, this->_omega, this->_dof_macros, this->_macro_mask, Index(0), Index(0));
439
440 watch_init_numeric.stop();
441 }
442
444 virtual Status apply(VectorType& vec_x, const VectorType& vec_b) override
445 {
446 watch_apply.start();
447
448 // first step
449 this->_vanka.apply(vec_x, vec_b);
450 this->_filter.filter_cor(vec_x);
451
452 // steps 2, ..., n (if any)
453 for(Index step(1); step < _num_steps; ++step)
454 {
455 // compute defect
456 this->_matrix.apply(this->_vec_d, vec_x, vec_b, -DataType(1));
457 // filter defect
458 this->_filter.filter_def(this->_vec_d);
459 // apply Vanka matrix
460 this->_vanka.apply(this->_vec_c, this->_vec_d);
461 // filter correct
462 this->_filter.filter_cor(this->_vec_c);
463 // update solution
464 vec_x.axpy(this->_vec_c);
465 }
466
467 watch_apply.stop();
468
469 return Status::success;
470 }
471
472 bool compare(const AmaVanka* other) const
473 {
474 return Intern::AmaVankaMatrixHelper<VankaMatrixType>::compare(this->_vanka, other->_vanka);
475 }
476 }; // class AmaVanka
477
496 template<typename Matrix_, typename Filter_>
497 std::shared_ptr<AmaVanka<Matrix_, Filter_>> new_amavanka(const Matrix_& matrix, const Filter_& filter,
498 typename Matrix_::DataType omega = typename Matrix_::DataType(1), Index num_steps = Index(1))
499 {
500 return std::make_shared<AmaVanka<Matrix_, Filter_>>(matrix, filter, omega, num_steps);
501 }
502 } // namespace Solver
503} // namespace FEAT
#define XABORTM(msg)
Abortion macro definition with custom message.
Definition: assertion.hpp:192
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Adjacency Graph implementation.
Definition: graph.hpp:34
Additive Macro-wise Matrix-based Vanka preconditioner/smoother.
Definition: amavanka.hpp:65
std::size_t bytes() const
Returns the total number of bytes currently allocated in this object.
Definition: amavanka.hpp:212
virtual String name() const override
Returns the name of the solver.
Definition: amavanka.hpp:270
const Matrix_ & _matrix
the system matrix
Definition: amavanka.hpp:82
std::vector< int > _macro_mask
the macro mask
Definition: amavanka.hpp:94
Matrix_::DataType DataType
our data type
Definition: amavanka.hpp:71
virtual Status apply(VectorType &vec_x, const VectorType &vec_b) override
applies the preconditioner
Definition: amavanka.hpp:444
double time_apply() const
Returns the total accumulated time for the solver application.
Definition: amavanka.hpp:264
Index _num_steps
number of steps
Definition: amavanka.hpp:96
void set_omega(DataType omega)
Sets the damping parameter omega.
Definition: amavanka.hpp:192
VectorType _vec_c
temporary vectors
Definition: amavanka.hpp:100
Matrix_::IndexType IndexType
our index type
Definition: amavanka.hpp:73
bool _auto_macros
deduct macro dofs automatically?
Definition: amavanka.hpp:88
DataType _omega
damping parameter
Definition: amavanka.hpp:98
bool _skip_singular
skip singular macros?
Definition: amavanka.hpp:90
int _num_threads
number of threads for numeric factorization
Definition: amavanka.hpp:110
virtual void init_numeric() override
Performs numeric factorization.
Definition: amavanka.hpp:338
void reset_timings()
Resets the internal stop watches for time measurement.
Definition: amavanka.hpp:238
virtual void init_symbolic() override
Performs symbolic factorization.
Definition: amavanka.hpp:276
double time_init_symbolic() const
Returns the total accumulated time for symbolic initialization.
Definition: amavanka.hpp:248
std::size_t data_size() const
Returns the total data size used by the AmaVanka smoother.
Definition: amavanka.hpp:230
void set_skip_singular(bool skip_sing)
Sets whether singular macros are to be skipped.
Definition: amavanka.hpp:204
const Filter_ & _filter
the system filter
Definition: amavanka.hpp:84
Matrix_::VectorTypeL VectorType
our vector type
Definition: amavanka.hpp:75
AmaVanka(const Matrix_ &matrix, const Filter_ &filter, const DataType omega=DataType(1), const Index num_steps=Index(1))
Constructor.
Definition: amavanka.hpp:128
double time_init_numeric() const
Returns the total accumulated time for numeric initialization.
Definition: amavanka.hpp:256
VankaMatrixType _vanka
the Vanka preconditioner matrix
Definition: amavanka.hpp:86
virtual void done_symbolic() override
Releases the symbolic factorization data.
Definition: amavanka.hpp:322
void set_num_steps(Index num_steps)
Sets the number of smoothing steps.
Definition: amavanka.hpp:180
Solver::SolverBase< typename Matrix_::VectorTypeL > BaseClass
our base-class
Definition: amavanka.hpp:68
void push_macro_dofs(Adjacency::Graph &&dofs)
Pushes the dofs-at-macro graph of the next block.
Definition: amavanka.hpp:155
void clear_macro_dofs()
Clears the macro dofs graphs.
Definition: amavanka.hpp:143
Intern::AmaVankaMatrixHelper< Matrix_ >::VankaMatrix VankaMatrixType
the type of our Vanka matrix
Definition: amavanka.hpp:79
std::vector< Adjacency::Graph > _macro_dofs
the DOF-macro graphs
Definition: amavanka.hpp:92
Polymorphic solver interface.
Definition: base.hpp:183
virtual void init_symbolic()
Symbolic initialization method.
Definition: base.hpp:227
virtual void init_numeric()
Numeric initialization method.
Definition: base.hpp:237
virtual void done_symbolic()
Symbolic finalization method.
Definition: base.hpp:255
Stop-Watch class.
Definition: stop_watch.hpp:21
double elapsed() const
Returns the total elapsed time in seconds.
Definition: stop_watch.hpp:70
void start()
Starts the stop-watch.
Definition: stop_watch.hpp:43
void reset()
Resets the elapsed time.
Definition: stop_watch.hpp:36
void stop()
Stops the stop-watch and increments elapsed time.
Definition: stop_watch.hpp:51
String class implementation.
Definition: string.hpp:46
@ transpose
Render-Transpose mode.
DT_ invert_matrix(const IT_ n, const IT_ stride, DT_ a[], IT_ p[])
Inverts a matrix and returns its determinant.
Definition: math.hpp:1292
std::shared_ptr< AmaVanka< Matrix_, Filter_ > > new_amavanka(const Matrix_ &matrix, const Filter_ &filter, typename Matrix_::DataType omega=typename Matrix_::DataType(1), Index num_steps=Index(1))
Creates a new AmaVanka smoother object.
Definition: amavanka.hpp:497
Status
Solver status return codes enumeration.
Definition: base.hpp:47
@ success
solving successful (convergence criterion fulfilled)
FEAT namespace.
Definition: adjactor.hpp:12
std::uint64_t Index
Index data type.