FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
direct_stokes_solver.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
10#include <kernel/backend.hpp>
11#include <kernel/lafem/saddle_point_matrix.hpp>
12#include <kernel/lafem/sparse_matrix_bcsr.hpp>
13#include <kernel/lafem/sparse_matrix_csr.hpp>
14#include <kernel/lafem/dense_vector.hpp>
15#include <kernel/lafem/dense_vector_blocked.hpp>
16#include <kernel/lafem/tuple_vector.hpp>
17#include <kernel/lafem/mean_filter.hpp>
18#include <kernel/lafem/none_filter.hpp>
19#include <kernel/lafem/slip_filter.hpp>
20#include <kernel/lafem/unit_filter.hpp>
21#include <kernel/lafem/unit_filter_blocked.hpp>
22#include <kernel/lafem/tuple_filter.hpp>
23#include <kernel/lafem/filter_chain.hpp>
24#include <kernel/lafem/filter_sequence.hpp>
25#include <kernel/global/matrix.hpp>
26#include <kernel/global/vector.hpp>
27#include <kernel/global/filter.hpp>
28#include <kernel/global/mean_filter.hpp>
29#include <kernel/solver/base.hpp>
30#include <kernel/solver/cudss.hpp>
31#include <kernel/solver/mkl_dss.hpp>
32#include <kernel/solver/umfpack.hpp>
33
34// includes, system
35#include <deque>
36
37namespace FEAT
38{
39 namespace Solver
40 {
48 template<
49 typename SolverDT_, typename SolverIT_,
50 typename MatrixA_, typename MatrixB_, typename MatrixD_,
51 typename MatrixS_ = LAFEM::SparseMatrixCSR<typename MatrixA_::DataType, typename MatrixA_::IndexType>>
53
118 template<typename SolverDT_, typename SolverIT_, typename DT_, typename IT_, int dim_>
120 SolverDT_, SolverIT_,
121 LAFEM::SparseMatrixBCSR<DT_, IT_, dim_, dim_>,
122 LAFEM::SparseMatrixBCSR<DT_, IT_, dim_, 1>,
123 LAFEM::SparseMatrixBCSR<DT_, IT_, 1, dim_>,
124 LAFEM::SparseMatrixCSR<DT_, IT_>>
125 {
126 public:
135
140
147
150 //typedef LAFEM::MeanFilter<DT_, IT_> MeanFilterTypeP;
151 //typedef Global::MeanFilter<DT_, IT_> MeanFilterTypePG;
154
156 typedef SolverDT_ SolverDataType;
158 typedef SolverIT_ SolverIndexType;
159
166
167 protected:
176
181
183 std::deque<SlipFilterTypeV> _slip_filters_v;
185 std::deque<UnitFilterTypeV> _unit_filters_v;
187 std::deque<UnitFilterTypeP> _unit_filters_p;
190
192 bool _filters_already_set = false;
193
195 bool _have_mean_filter_p = false;
196
198 bool _need_main_diagonal = false;
199
201 std::vector<int> _unit_mask_v, _unit_mask_p;
202
204 std::vector<IT_> _slip_row_ptr, _slip_col_idx, _slip_fil_dqu, _slip_fil_idx;
205
207 typedef std::array<std::size_t, 3u> FilterUpdateCounter;
208
209 public:
223 explicit DirectStokesCore(const MatrixTypeA& matrix_a, const MatrixTypeB& matrix_b,
224 const MatrixTypeD& matrix_d, const MatrixTypeS* matrix_s = nullptr) :
225 _matrix_a(matrix_a),
226 _matrix_b(matrix_b),
227 _matrix_d(matrix_d),
228 _matrix_s(matrix_s)
229 {
230 }
231
234 {
235 }
236
241
249 void set_need_main_diagonal(bool need_it)
250 {
251 this->_need_main_diagonal = need_it;
252 }
253
258 {
259 return this->_solver_matrix;
260 }
261
266 {
267 return this->_solver_filter;
268 }
269
274 {
275 return this->_solver_matrix.create_vector_r();
276 }
277
287 virtual void upload_vector(SolverVectorType& vector, const VectorTypeV& vector_v, const VectorTypeP& vector_p) const
288 {
289 const IT_ nv = IT_(vector_v.template size<LAFEM::Perspective::pod>());
290 const IT_ np = IT_(vector_p.size());
291 const IT_ nx = IT_(vector.size());
292
293 XASSERTM(nx == IT_(this->_solver_matrix.rows()), "invalid solver vector size");
294 XASSERTM(nv + np <= nx, "invalid velocity/pressure vector size");
295
296 SolverDataType* vec = vector.elements();
297 const DT_* vec_v = vector_v.template elements<LAFEM::Perspective::pod>();
298 const DT_* vec_p = vector_p.elements();
299 IT_ j(0u);
300 // copy velocity DOFs
301 for(IT_ i(0); i < nv; ++i, ++j)
302 vec[j] = SolverDataType(vec_v[i]);
303 // copy pressure DOFs
304 for(IT_ i(0); i < np; ++i, ++j)
305 vec[j] = SolverDataType(vec_p[i]);
306 // format Lagrange multipliers
307 for(; j < nx; ++j)
308 vec[j] = SolverDataType(0);
309 }
310
320 virtual void download_vector(const SolverVectorType& vector, VectorTypeV& vector_v, VectorTypeP& vector_p) const
321 {
322 const IT_ nv = IT_(vector_v.template size<LAFEM::Perspective::pod>());
323 const IT_ np = IT_(vector_p.size());
324 const IT_ nx = IT_(vector.size());
325
326 XASSERTM(nx == IT_(this->_solver_matrix.rows()), "invalid solver vector size");
327 XASSERTM(nv + np <= nx, "invalid velocity/pressure vector size");
328
329 const SolverDataType* vec = vector.elements();
330 DT_* vec_v = vector_v.template elements<LAFEM::Perspective::pod>();
331 DT_* vec_p = vector_p.elements();
332 IT_ j(0u);
333 // copy velocity DOFs
334 for(IT_ i(0); i < nv; ++i, ++j)
335 vec_v[i] = DT_(vec[j]);
336 // copy pressure DOFs
337 for(IT_ i(0); i < np; ++i, ++j)
338 vec_p[i] = DT_(vec[j]);
339 // Lagrange multipliers are ignored
340 }
341
350 template<typename FilterV_, typename FilterP_>
352 {
353 XASSERTM(!this->_filters_already_set, "Filter have already been set");
354 this->_add_filter_velo(filter.template at<0>(), nullptr);
355 this->_add_filter_pres(filter.template at<1>(), nullptr);
356 this->_filters_already_set = true;
357 }
358
368 template<typename FilterV_, typename FilterP_>
370 {
371 XASSERTM(this->_filters_already_set, "Filters have not been set yet");
372 FilterUpdateCounter fuc{0u, 0u, 0u};
373 this->_add_filter_velo(filter.template at<0>(), &fuc);
374 this->_add_filter_pres(filter.template at<1>(), &fuc);
375 }
376
382 virtual void init_symbolic()
383 {
384 // get the number of DOFs
385 const IT_ idim = IT_(dim_);
386 const IT_ num_dofs_v = IT_(_matrix_a.rows());
387 const IT_ num_dofs_p = IT_(_matrix_d.rows());
388
389 XASSERT(_matrix_a.columns() == num_dofs_v);
390 XASSERT(_matrix_b.columns() == num_dofs_p);
391 XASSERT(_matrix_d.columns() == num_dofs_v);
392 XASSERT(_matrix_b.rows() == num_dofs_v);
393 XASSERT(_matrix_d.rows() == num_dofs_p);
394
395 if(_matrix_s != nullptr)
396 {
397 XASSERT(_matrix_s->rows() == num_dofs_p);
398 XASSERT(_matrix_s->columns() == num_dofs_p);
399 }
400
401 // is the mean filter dual vector non-empty?
402 this->_have_mean_filter_p = !this->_mean_filter_p_dual.empty();
403
404 // compute number of slip DOFs
405 const IT_ num_slip_dofs = this->_compute_slip_matrix_layout();
406
407 // mask velocity and pressure unit dofs
408 this->_compute_unit_mask_v();
409 this->_compute_unit_mask_p();
410
411 // compute total number of scalar DOFs
412 const IT_ num_total_dofs = idim*num_dofs_v + num_dofs_p + num_slip_dofs + IT_(_have_mean_filter_p ? 1 : 0);
413
414 // get number of matrix NZEs
415 const IT_ num_nze_a = IT_(_matrix_a.used_elements());
416 const IT_ num_nze_b = IT_(_matrix_b.used_elements());
417 const IT_ num_nze_d = IT_(_matrix_d.used_elements());
418 const IT_ num_nze_s = IT_(_matrix_s != nullptr ? _matrix_s->used_elements() : 0u);
419
420 // compute upper bound for non-zero entry count
421 IT_ max_nze = idim*idim*num_nze_a + idim*(num_nze_b + num_nze_d) + num_nze_s;
422
423 // Lagrange multiplier for velocity slip filters
424 max_nze += IT_(2)*num_slip_dofs*idim;
425
426 // Lagrange multiplier for pressure mean filter (when necessary)
427 if(this->_have_mean_filter_p)
428 max_nze += IT_(2)*num_dofs_p;
429
430 // need a main diagonal?
431 if(this->_need_main_diagonal)
432 max_nze += num_dofs_p + IT_(2)*num_slip_dofs*idim + IT_(1);
433
434 // allocate row-pointer and column index arrays
435 std::vector<IT_> row_ptr, col_idx;
436 row_ptr.resize(num_total_dofs + IT_(1u), ~IT_(0));
437 col_idx.resize(max_nze, ~IT_(0));
438 row_ptr[0u] = IT_(0u);
439
440 // the system has the following structure:
441 // | A B Q 0 |
442 // | D S 0 P |
443 // | Q^T 0 0 0 |
444 // | 0 P^T 0 0 |
445 //
446 // where:
447 // - A, B, D and S are the original matrices from the Stokes system
448 // - Q is the Lagrange multiplier for the velocity slip DOFs
449 // - P is the Lagrange multiplier for the pressure mean filter
450
451 // get all the matrix arrays
452 const IT_* row_ptr_a = _matrix_a.row_ptr();
453 const IT_* col_idx_a = _matrix_a.col_ind();
454 const IT_* row_ptr_b = _matrix_b.row_ptr();
455 const IT_* col_idx_b = _matrix_b.col_ind();
456 const IT_* row_ptr_d = _matrix_d.row_ptr();
457 const IT_* col_idx_d = _matrix_d.col_ind();
458 const IT_* row_ptr_s = (_matrix_s != nullptr ? _matrix_s->row_ptr() : nullptr);
459 const IT_* col_idx_s = (_matrix_s != nullptr ? _matrix_s->col_ind() : nullptr);
460
461 IT_ row(0u), op(0u);
462
463 // okay, loop over all rows of A/B/Q
464 for(IT_ i(0); i < num_dofs_v; ++i)
465 {
466 // is this a unit row?
467 if(_unit_mask_v[i] != 0)
468 {
469 // set the entire block to an identity block
470 for(IT_ k(0); k < idim; ++k)
471 {
472 col_idx[op] = i*idim + k;
473 row_ptr[++row] = ++op;
474 }
475 continue;
476 }
477
478 // not a unit row, so copy rows of A, B and Q
479 for(IT_ j(0); j < idim; ++j)
480 {
481 // copy row of A
482 for(IT_ ip(row_ptr_a[i]); ip < row_ptr_a[i+1]; ++ip)
483 for(IT_ k(0); k < idim; ++k, ++op)
484 col_idx[op] = idim* col_idx_a[ip] + k;
485 // copy row of B
486 for(IT_ ip(row_ptr_b[i]); ip < row_ptr_b[i+1]; ++ip, ++op)
487 col_idx[op] = idim*num_dofs_v + col_idx_b[ip];
488 // copy row of Q
489 for(IT_ ip(_slip_row_ptr[i]); ip < _slip_row_ptr[i+1]; ++ip, ++op)
490 col_idx[op] = idim*num_dofs_v + num_dofs_p + _slip_col_idx[ip];
491 // set row pointer
492 row_ptr[++row] = op;
493 }
494 }
495
496 XASSERT(row == idim*num_dofs_v);
497
498 // next, loop over all rows of D/S/P
499 for(IT_ i(0); i < num_dofs_p; ++i)
500 {
501 // is this a unit row?
502 if(_unit_mask_p[i] != 0)
503 {
504 col_idx[op] = idim*num_dofs_v + i;
505 row_ptr[++row] = ++op;
506 continue;
507 }
508
509 // not a unit row, so copy rows of D, S and P
510 // copy row of D
511 for(IT_ ip(row_ptr_d[i]); ip < row_ptr_d[i+1]; ++ip)
512 for(IT_ k(0); k < idim; ++k, ++op)
513 col_idx[op] = idim*col_idx_d[ip] + k;
514 // copy row of S (if it exists)
515 if(row_ptr_s != nullptr)
516 {
517 if(this->_need_main_diagonal)
518 {
519 // copy row i of S and insert a main diagonal entry if it is not present in S
520 bool have_diag = false;
521 for(IT_ ip(row_ptr_s[i]); ip < row_ptr_s[i+1]; ++ip, ++op)
522 {
523 if(!have_diag && (col_idx_s[ip] > i))
524 {
525 col_idx[op++] = row;
526 have_diag = true;
527 }
528 col_idx[op] = idim*num_dofs_v + col_idx_s[ip];
529 have_diag = have_diag || (col_idx_s[ip] == i);
530 }
531 }
532 else
533 {
534 for(IT_ ip(row_ptr_s[i]); ip < row_ptr_s[i+1]; ++ip, ++op)
535 col_idx[op] = idim*num_dofs_v + col_idx_s[ip];
536 }
537 }
538 else if(this->_need_main_diagonal)
539 {
540 // insert a main diagonal entry
541 col_idx[op++] = row;
542 }
543 // copy row of P (if we have a pressure mean filter)
544 if(this->_have_mean_filter_p)
545 col_idx[op++] = idim*num_dofs_v + num_slip_dofs + num_dofs_p;
546 // set row pointer
547 row_ptr[++row] = op;
548 }
549
550 // next, add all slip filter entries Q^T
551 for(auto it(_slip_filters_v.begin()); it != _slip_filters_v.end(); ++it)
552 {
553 const IT_ nsx = IT_(it->used_elements());
554 const IT_* idx = it->get_indices();
555 for(IT_ j(0); j < nsx; ++j)
556 {
557 for(IT_ k(0); k < idim; ++k, ++op)
558 col_idx[op] = idim*idx[j] + k;
559 if(this->_need_main_diagonal)
560 col_idx[op++] = row;
561 // set row pointer
562 row_ptr[++row] = op;
563 }
564 }
565
566 // finally, add the pressure mean filter vector P^T (if it exists)
567 if(this->_have_mean_filter_p)
568 {
569 for(IT_ j(0); j < num_dofs_p; ++j, ++op)
570 col_idx[op] = idim*num_dofs_v + j;
571 if(this->_need_main_diagonal)
572 col_idx[op++] = row;
573 row_ptr[++row] = op;
574 }
575
576 // the number of rows must match, but we may have less non-zeros due to unit filtering
577 XASSERT(row == num_total_dofs);
578 XASSERT(op <= max_nze);
579
580 const Index num_nze = op;
581
582 // ok, allocate the matrix
583 this->_solver_matrix = SolverMatrixType(num_total_dofs, num_total_dofs, num_nze);
584
585 // copy row pointer
586 SolverIndexType* row_ptr_x = this->_solver_matrix.row_ptr();
587 for(IT_ i(0); i <= num_total_dofs; ++i)
588 row_ptr_x[i] = SolverIndexType(row_ptr[i]);
589
590 // copy column indices
591 SolverIndexType* col_idx_x = this->_solver_matrix.col_ind();
592 for(IT_ i(0); i < num_nze; ++i)
593 col_idx_x[i] = SolverIndexType(col_idx[i]);
594 }
595
601 virtual void init_numeric()
602 {
603 // get the number of DOFs
604 const IT_ num_dofs_v = IT_(_matrix_a.rows());
605 const IT_ num_dofs_p = IT_(_matrix_d.rows());
606
607 // the system has the following structure:
608 // | A B Q 0 |
609 // | D S 0 P |
610 // | Q^T 0 0 0 |
611 // | 0 P^T 0 0 |
612 //
613 // where:
614 // - A, B, D and S are the original matrices from the Stokes system
615 // - Q is the Lagrange multiplier for the velocity slip DOFs
616 // - P is the Lagrange multiplier for the pressure mean filter
617
618 // get all the matrix arrays
619 const IT_* row_ptr_a = _matrix_a.row_ptr();
620 const auto* val_a = _matrix_a.val();
621 const IT_* row_ptr_b = _matrix_b.row_ptr();
622 const auto* val_b = _matrix_b.val();
623 const IT_* row_ptr_d = _matrix_d.row_ptr();
624 const auto* val_d = _matrix_d.val();
625 const IT_* row_ptr_s = (_matrix_s != nullptr ? _matrix_s->row_ptr() : nullptr);
626 const IT_* col_idx_s = (_matrix_s != nullptr ? _matrix_s->col_ind() : nullptr);
627 const auto* val_s = (_matrix_s != nullptr ? _matrix_s->val() : nullptr);
628 SolverDataType* val_x = _solver_matrix.val();
629
630 IT_ op(0u);
631
632 // okay, loop over all rows of A/B/Q
633 for(IT_ i(0); i < num_dofs_v; ++i)
634 {
635 // is this a unit row?
636 if(_unit_mask_v[i] != 0)
637 {
638 // set the entire block to an identity block
639 for(int k(0); k < dim_; ++k, ++op)
640 val_x[op] = SolverDataType(1);
641 continue;
642 }
643
644 // not a unit row, so copy rows of A, B and Q
645 for(int j(0); j < dim_; ++j)
646 {
647 // copy row of A
648 for(IT_ ip(row_ptr_a[i]); ip < row_ptr_a[i+1]; ++ip)
649 for(int k(0); k < dim_; ++k, ++op)
650 val_x[op] = SolverDataType(val_a[ip][j][k]);
651 // copy row of B
652 for(IT_ ip(row_ptr_b[i]); ip < row_ptr_b[i+1]; ++ip, ++op)
653 val_x[op] = SolverDataType(val_b[ip][j][0]);
654 // copy row of Q
655 for(IT_ ip(_slip_row_ptr[i]); ip < _slip_row_ptr[i+1]; ++ip, ++op)
656 {
657 // get the normal vector of this slip filter entry
658 const auto& nu = this->_slip_filters_v.at(_slip_fil_dqu[ip]).get_values()[_slip_fil_idx[ip]];
659 // set the normal vector component
660 val_x[op] = SolverDataType(nu[j]);
661 }
662 }
663 }
664
665 // next, loop over all rows of D/S/P
666 for(IT_ i(0); i < num_dofs_p; ++i)
667 {
668 // is this a unit row?
669 if(_unit_mask_p[i] != 0)
670 {
671 val_x[op++] = SolverDataType(1);
672 continue;
673 }
674
675 // not a unit row, so copy rows of D, S and P
676 // copy row of D
677 for(IT_ ip(row_ptr_d[i]); ip < row_ptr_d[i+1]; ++ip)
678 for(int k(0); k < dim_; ++k, ++op)
679 val_x[op] = SolverDataType(val_d[ip][0][k]);
680 // copy row of S (if it exists)
681 if(row_ptr_s != nullptr)
682 {
683 if(this->_need_main_diagonal)
684 {
685 bool have_diag = false;
686 for(IT_ ip(row_ptr_s[i]); ip < row_ptr_s[i+1]; ++ip, ++op)
687 {
688 if(!have_diag && (col_idx_s[ip] > i))
689 {
690 val_x[op++] = SolverDataType(0);
691 have_diag = true;
692 }
693 val_x[op] = SolverDataType(val_s[ip]);
694 have_diag = have_diag || (col_idx_s[ip] == i);
695 }
696 }
697 else
698 {
699 for(IT_ ip(row_ptr_s[i]); ip < row_ptr_s[i+1]; ++ip, ++op)
700 val_x[op] = SolverDataType(val_s[ip]);
701 }
702 }
703 else if(this->_need_main_diagonal)
704 {
705 // insert a main diagonal entry
706 val_x[op++] = SolverDataType(0);
707 }
708
709 // copy row of P (if we have a pressure mean filter)
710 if(this->_have_mean_filter_p)
711 {
712 const auto* v = _mean_filter_p_dual.elements();
713 val_x[op++] = SolverDataType(v[i]);
714 }
715 }
716
717 // next, add all slip filter entries Q^T
718 for(auto it(_slip_filters_v.begin()); it != _slip_filters_v.end(); ++it)
719 {
720 const IT_ nsx = IT_(it->used_elements());
721 const auto* nu = it->get_values();
722 for(IT_ j(0); j < nsx; ++j)
723 {
724 for(int k(0); k < dim_; ++k, ++op)
725 val_x[op] = SolverDataType(nu[j][k]);
726 if(this->_need_main_diagonal)
727 val_x[op++] = SolverDataType(0);
728 }
729 }
730
731 // finally, add the pressure mean filter vector P^T (if it exists)
732 if(this->_have_mean_filter_p)
733 {
734 const auto* v = _mean_filter_p_dual.elements();
735 for(IT_ j(0); j < num_dofs_p; ++j, ++op)
736 val_x[op] = SolverDataType(v[j]);
737 if(this->_need_main_diagonal)
738 val_x[op++] = SolverDataType(0);
739 }
740
741 // sanity check
742 XASSERT(op == _solver_matrix.used_elements());
743 }
744
746 virtual void done_numeric()
747 {
748 // nothing to do here
749 }
750
758 virtual void done_symbolic()
759 {
760 _slip_row_ptr.clear();
761 _slip_col_idx.clear();
762 _slip_fil_dqu.clear();
763 _slip_fil_idx.clear();
764 _slip_filters_v.clear();
765 _unit_filters_v.clear();
766 _unit_filters_p.clear();
767 _mean_filter_p_dual.clear();
768 _unit_mask_v.clear();
769 _unit_mask_p.clear();
770 _have_mean_filter_p = false;
771 _filters_already_set = false;
772 }
773
774 protected:
777 {
778 if(fuc != nullptr)
779 {
780 // get the old unit filter
781 UnitFilterTypeV& old_filter = this->_unit_filters_v.at(fuc->at(0u)++);
782
783 // compare layout of old and new filter
784 if(!old_filter.get_filter_vector().same_layout(filter.get_filter_vector()))
785 XABORTM("velocity unit filter layout has changed");
786
787 // update the filter
788 old_filter.clone(filter, LAFEM::CloneMode::Shallow);
789 }
790 else
791 {
792 // push new filter
793 this->_unit_filters_v.push_back(filter.clone(LAFEM::CloneMode::Shallow));
794 }
795 }
796
799 {
800 if(fuc != nullptr)
801 {
802 // get the old slip filter
803 SlipFilterTypeV& old_filter = this->_slip_filters_v.at(fuc->at(1u)++);
804
805 // compare layout of old and new filter
806 if(!old_filter.get_filter_vector().same_layout(filter.get_filter_vector()))
807 XABORTM("velocity slip filter layout has changed");
808
809 // update the filter
810 old_filter.clone(filter, LAFEM::CloneMode::Shallow);
811 }
812 else
813 {
814 // push new filter
815 this->_slip_filters_v.push_back(filter.clone(LAFEM::CloneMode::Shallow));
816 }
817 }
818
821 {
822 // nothing to do here
823 }
824
826 template<typename First_>
828 {
829 this->_add_filter_velo(filter.first(), fuc);
830 }
831
833 template<typename First_, typename Second_, typename... Rest_>
835 {
836 this->_add_filter_velo(filter.first(), fuc);
837 this->_add_filter_velo(filter.rest(), fuc);
838 }
839
841 template<typename SubFilter_>
843 {
844 for(auto it = filter.begin(); it != filter.end(); ++it)
845 {
846 this->_add_filter_velo(it->second, fuc);
847 }
848 }
849
851 template<typename LocFilter_, typename Mirror_>
853 {
854 this->_add_filter_velo(filter.local(), fuc);
855 }
856
859 {
860 if(fuc != nullptr)
861 {
862 // get the old unit filter
863 UnitFilterTypeP& old_filter = this->_unit_filters_p.at(fuc->at(2u)++);
864
865 // compare layout of old and new filter
866 if(!old_filter.get_filter_vector().same_layout(filter.get_filter_vector()))
867 XABORTM("pressure unit filter layout has changed");
868
869 // update the filter
870 old_filter.clone(filter, LAFEM::CloneMode::Shallow);
871 }
872 else
873 {
874 this->_unit_filters_p.push_back(filter.clone(LAFEM::CloneMode::Shallow));
875 }
876 }
877
880 {
881 if(fuc != nullptr)
882 {
883 XASSERTM(this->_mean_filter_p_dual.empty() == filter.get_vec_dual().empty(), "pressure mean filter has changed");
884 }
885 this->_mean_filter_p_dual.clone(filter.get_vec_dual(), LAFEM::CloneMode::Shallow);
886 }
887
890 {
891 if(fuc != nullptr)
892 {
893 XASSERTM(this->_mean_filter_p_dual.empty() == filter.get_vec_dual().empty(), "pressure mean filter has changed");
894 }
895 this->_mean_filter_p_dual.clone(filter.get_vec_dual(), LAFEM::CloneMode::Shallow);
896 }
897
900 {
901 // nothing to do here
902 }
903
905 template<typename First_>
907 {
908 this->_add_filter_pres(filter.first(), fuc);
909 }
910
912 template<typename First_, typename Second_, typename... Rest_>
914 {
915 this->_add_filter_pres(filter.first(), fuc);
916 this->_add_filter_pres(filter.rest(), fuc);
917 }
918
920 template<typename SubFilter_>
922 {
923 for(auto it = filter.begin(); it != filter.end(); ++it)
924 {
925 this->_add_filter_pres(it->second, fuc);
926 }
927 }
928
930 template<typename LocFilter_, typename Mirror_>
932 {
933 this->_add_filter_pres(filter.local(), fuc);
934 }
935
938 {
939 const IT_ nv = IT_(_matrix_a.rows());
940
941 _slip_row_ptr.clear();
942 _slip_col_idx.clear();
943 _slip_fil_dqu.clear();
944 _slip_fil_idx.clear();
945 _slip_row_ptr.resize(nv+1u, IT_(0));
946
947 if(this->_slip_filters_v.empty())
948 return IT_(0);
949
950 // count the total number of slip filter entries
951 IT_ count(0u);
952 for(std::size_t it(0u); it < this->_slip_filters_v.size(); ++it)
953 {
954 const IT_ ni = IT_(this->_slip_filters_v.at(it).used_elements());
955 const IT_* idx = this->_slip_filters_v.at(it).get_indices();
956 for(IT_ i(0); i < ni; ++i)
957 ++_slip_row_ptr.at(idx[i]+1);
958 count += ni;
959 }
960
961 // only empty filters?
962 if(count == IT_(0))
963 return count;
964
965 // compute row pointer array from aux
966 for(IT_ i(0); i < nv; ++i)
967 {
968 _slip_row_ptr[i+1u] += _slip_row_ptr[i];
969 }
970
971 // allocate vectors
972 _slip_col_idx.resize(count);
973 _slip_fil_dqu.resize(count);
974 _slip_fil_idx.resize(count);
975
976 // copy row pointer
977 std::vector<IT_> aux(_slip_row_ptr);
978
979 // loop over all slip filters
980 IT_ offset(0u);
981 for(std::size_t it(0u); it < this->_slip_filters_v.size(); ++it)
982 {
983 const IT_ ni = IT_(this->_slip_filters_v.at(it).used_elements());
984 const IT_* idx = this->_slip_filters_v.at(it).get_indices();
985
986 // and emplace them
987 for(IT_ i(0); i < ni; ++i)
988 {
989 IT_& px = aux[idx[i]];
990 _slip_col_idx[px] = offset + i;
991 _slip_fil_dqu[px] = IT_(it);
992 _slip_fil_idx[px] = i;
993 ++px;
994 }
995 offset += ni;
996 }
997
998 XASSERT(count == offset);
999 return count;
1000 }
1001
1004 {
1005 // reset mask to 0
1006 _unit_mask_v.clear();
1007 _unit_mask_v.resize(_matrix_a.rows(), 0);
1008
1009 for(const auto& it : this->_unit_filters_v)
1010 {
1011 const IT_ n = IT_(it.used_elements());
1012 const IT_* idx = it.get_indices();
1013 for(IT_ k(0); k < n; ++k)
1014 _unit_mask_v.at(idx[k]) = 1;
1015 }
1016 }
1017
1020 {
1021 // reset mask to 0
1022 _unit_mask_p.clear();
1023 _unit_mask_p.resize(_matrix_d.rows(), 0);
1024
1025 for(const auto& it : this->_unit_filters_p)
1026 {
1027 const IT_ n = IT_(it.used_elements());
1028 const IT_* idx = it.get_indices();
1029 for(IT_ k(0); k < n; ++k)
1030 _unit_mask_p.at(idx[k]) = 1;
1031 }
1032 }
1033 }; // class DirectStokesCore
1034
1036 static constexpr bool direct_stokes_solver_available =
1037#ifdef FEAT_HAVE_CUDSS
1038 true ||
1039#endif
1040#ifdef FEAT_HAVE_MKL
1041 true ||
1042#endif
1043#ifdef FEAT_HAVE_UMFPACK
1044 true ||
1045#endif
1046 false;
1047
1055 template<typename Matrix_, typename Filter_>
1057
1066 template<typename MatrixA_, typename MatrixB_, typename MatrixD_, typename FilterV_, typename FilterP_>
1067 class DirectStokesSolver<LAFEM::SaddlePointMatrix<MatrixA_, MatrixB_, MatrixD_>, LAFEM::TupleFilter<FilterV_, FilterP_>> :
1068 public Solver::SolverBase<LAFEM::TupleVector<typename MatrixB_::VectorTypeL, typename MatrixD_::VectorTypeL>>
1069 {
1070 public:
1077
1080
1083
1085 typedef typename StokesCoreType::SolverMatrixType SolverMatrixType;
1087 typedef typename StokesCoreType::SolverVectorType SolverVectorType;
1089 typedef typename StokesCoreType::SolverFilterType SolverFilterType;
1090
1092#if defined(FEAT_HAVE_CUDSS) || defined(DOXYGEN)
1093 static constexpr bool have_backend_cudss = true;
1094#else
1095 static constexpr bool have_backend_cudss = false;
1096#endif // FEAT_HAVE_CUDSS
1097
1099#if defined(FEAT_HAVE_MKL) || defined(DOXYGEN)
1100 static constexpr bool have_backend_mkldss = true;
1101#else
1102 static constexpr bool have_backend_mkldss = false;
1103#endif // FEAT_HAVE_MKL
1104
1106#if defined(FEAT_HAVE_UMFPACK) || defined(DOXYGEN)
1107 static constexpr bool have_backend_umfpack = true;
1108#else
1109 static constexpr bool have_backend_umfpack = false;
1110#endif // FEAT_HAVE_UMFPACK
1111
1112 protected:
1117
1120
1123
1125 std::shared_ptr<Solver::SolverBase<SolverVectorType>> _direct_solver;
1126
1127#if defined(FEAT_HAVE_CUDSS) || defined(DOXYGEN)
1129 std::shared_ptr<Solver::CUDSS> _cudss_solver;
1130#endif // FEAT_HAVE_CUDSS
1131#if defined(FEAT_HAVE_MKL) || defined(DOXYGEN)
1133 std::shared_ptr<Solver::MKLDSS> _mkldss_solver;
1134#endif // FEAT_HAVE_MKL
1135#if defined(FEAT_HAVE_UMFPACK) || defined(DOXYGEN)
1137 std::shared_ptr<Solver::Umfpack> _umfpack_solver;
1138#endif // FEAT_HAVE_UMFPACK
1139
1140 public:
1162 explicit DirectStokesSolver(const SystemMatrixType& matrix_sys, const SystemFilterType& filter_sys, PreferredBackend backend) :
1163 _matrix_sys(matrix_sys),
1164 _filter_sys(filter_sys),
1165 _stokes_core(_matrix_sys.block_a(), _matrix_sys.block_b(), _matrix_sys.block_d()),
1166 _direct_solver()
1167 {
1168#ifdef FEAT_HAVE_CUDSS
1169 // create a CUDSS solver if this is the desired backend or if the desired backend is not available
1170 if((backend == PreferredBackend::cuda) ||
1171 ((backend == PreferredBackend::mkl) && !have_backend_mkldss) ||
1172 ((backend == PreferredBackend::generic) && !have_backend_umfpack))
1173 {
1174 _cudss_solver = Solver::new_cudss(_stokes_core.get_solver_matrix());
1175 _direct_solver = _cudss_solver;
1176 // main diagonal is not absolutely necessary for cuDSS, but it seems to improve precision a bit
1177 _stokes_core.set_need_main_diagonal(true);
1178 }
1179 else
1180#endif // FEAT_HAVE_CUDSS
1181#ifdef FEAT_HAVE_MKL
1182 // create a MKL DSS solver if this is the desired backend or if the desired backend is not available
1183 if((backend == PreferredBackend::mkl) ||
1184 ((backend == PreferredBackend::generic) && !have_backend_umfpack))
1185 {
1186 _mkldss_solver = Solver::new_mkl_dss(_stokes_core.get_solver_matrix());
1187 _direct_solver = _mkldss_solver;
1188 // MKL emphasizes that main diagonal is absolutely mandatory
1189 _stokes_core.set_need_main_diagonal(true);
1190 }
1191 else
1192#endif // FEAT_HAVE_MKL
1193#ifdef FEAT_HAVE_UMFPACK
1194 // fallback or generic: create an UMFPACK solver
1195 {
1196 // create an UMFPACK solver
1197 _umfpack_solver = Solver::new_umfpack(_stokes_core.get_solver_matrix());
1198 _direct_solver = _umfpack_solver;
1199 }
1200#else
1201 {
1202 XABORTM("DirectStokesSolver can only be used if at least one of {cuDSS, MKL DSS, UMFPACK} is available");
1203 }
1204#endif
1205 // ensure that compilers don't warn about unused parameters
1206 (void)backend;
1207 }
1208
1220 explicit DirectStokesSolver(const SystemMatrixType& matrix_sys, const SystemFilterType& filter_sys) :
1221 DirectStokesSolver(matrix_sys, filter_sys, Backend::get_preferred_backend())
1222 {
1223 }
1224
1226 virtual String name() const override
1227 {
1228#ifdef FEAT_HAVE_CUDSS
1229 if(_cudss_solver)
1230 return "DirectStokesSolver<SaddlePointMatrix>[CUDSS]";
1231#endif
1232#ifdef FEAT_HAVE_MKL
1233 if(_mkldss_solver)
1234 return "DirectStokesSolver<SaddlePointMatrix>[MKLDSS]";
1235#endif
1236#ifdef FEAT_HAVE_UMFPACK
1237 if(_umfpack_solver)
1238 return "DirectStokesSolver<SaddlePointMatrix>[UMFPACK]";
1239#endif
1240 return "DirectStokesSolver<SaddlePointMatrix>[???]";
1241 }
1242
1244 virtual void init_symbolic() override
1245 {
1246 BaseClass::init_symbolic();
1247
1248 // add the filter
1249 _stokes_core.set_filters(_filter_sys);
1250
1251 // initialize
1252 _stokes_core.init_symbolic();
1253 if(_direct_solver)
1254 _direct_solver->init_symbolic();
1255
1256 // create vectors
1257 _vec_sol = _stokes_core.create_solver_vector();
1258 _vec_rhs = _stokes_core.create_solver_vector();
1259 }
1260
1262 virtual void init_numeric() override
1263 {
1264 BaseClass::init_numeric();
1265
1266 // update filter
1267 _stokes_core.update_filters(_filter_sys);
1268
1269 // initialize
1270 _stokes_core.init_numeric();
1271 if(_direct_solver)
1272 _direct_solver->init_numeric();
1273 }
1274
1276 virtual void done_numeric() override
1277 {
1278 if(_direct_solver)
1279 _direct_solver->done_numeric();
1280 _stokes_core.done_numeric();
1281 BaseClass::done_numeric();
1282 }
1283
1285 virtual void done_symbolic() override
1286 {
1287 _vec_rhs.clear();
1288 _vec_sol.clear();
1289 if(_direct_solver)
1290 _direct_solver->done_symbolic();
1291 _stokes_core.done_symbolic();
1292 BaseClass::done_symbolic();
1293 }
1294
1310 virtual Solver::Status apply(SystemVectorType& vec_cor, const SystemVectorType& vec_def) override
1311 {
1312 // upload RHS vector
1313 _stokes_core.upload_vector(this->_vec_rhs, vec_def.template at<0>(), vec_def.template at<1>());
1314
1315 // apply the actual solver
1316 Solver::Status status = _direct_solver->apply(this->_vec_sol, this->_vec_rhs);
1317
1318 // download solution vector
1319 _stokes_core.download_vector(this->_vec_sol, vec_cor.template at<0>(), vec_cor.template at<1>());
1320
1321 // return status
1322 return status;
1323 }
1324 }; // class DirectStokesSolver<LAFEM::SaddlePointMatrix<...>, LAFEM::TupleFilter<...>>
1325
1339 template<typename LocalMatrix_, typename LocalFilter_, typename Mirror_>
1340 class DirectStokesSolver<Global::Matrix<LocalMatrix_, Mirror_, Mirror_>, Global::Filter<LocalFilter_, Mirror_>> :
1341 public Solver::SolverBase<Global::Vector<typename LocalMatrix_::VectorTypeR, Mirror_>>
1342 {
1343 public:
1350
1353
1354 protected:
1359
1362
1363 public:
1364 explicit DirectStokesSolver(const SystemMatrixType& matrix_sys, const SystemFilterType& filter_sys) :
1365 _matrix_sys(matrix_sys),
1366 _filter_sys(filter_sys),
1367 _local_solver(_matrix_sys.local(), _filter_sys.local())
1368 {
1369 }
1370
1372 virtual String name() const override
1373 {
1374 return "DirectStokesSolver<Global::Matrix>";
1375 }
1376
1378 virtual void init_symbolic() override
1379 {
1380 // ensure that we are on one process only...
1381 const Dist::Comm* comm = _matrix_sys.get_comm();
1382 if(comm != nullptr)
1383 {
1384 XASSERTM(comm->size() <= 1, "DirectStokesSolver can only work on one process");
1385 }
1386
1387 BaseClass::init_symbolic();
1388 _local_solver.init_symbolic();
1389 }
1390
1392 virtual void init_numeric() override
1393 {
1394 BaseClass::init_numeric();
1395 _local_solver.init_numeric();
1396 }
1397
1399 virtual void done_numeric() override
1400 {
1401 _local_solver.done_numeric();
1402 BaseClass::done_numeric();
1403 }
1404
1406 virtual void done_symbolic() override
1407 {
1408 _local_solver.done_symbolic();
1409 BaseClass::done_symbolic();
1410 }
1411
1427 virtual Solver::Status apply(SystemVectorType& vec_sol, const SystemVectorType& vec_rhs) override
1428 {
1429 return _local_solver.apply(vec_sol.local(), vec_rhs.local());
1430 }
1431 }; // class DirectStokesSolver<Global::Matrix<...>, Global::Filter<...>>
1432
1445 template<typename Matrix_, typename Filter_>
1446 inline std::shared_ptr<DirectStokesSolver<Matrix_, Filter_>> new_direct_stokes_solver(const Matrix_& matrix, const Filter_& filter)
1447 {
1448 return std::make_shared<DirectStokesSolver<Matrix_, Filter_>>(matrix, filter);
1449 }
1450 } // namespace Solver
1451} // 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
FEAT Kernel base header.
Backend support class.
Definition: backend.hpp:136
Communicator class.
Definition: dist.hpp:1349
int size() const
Returns the size of this communicator.
Definition: dist.hpp:1506
Global Filter wrapper class template.
Definition: filter.hpp:21
Global Matrix wrapper class template.
Definition: matrix.hpp:40
const Dist::Comm * get_comm() const
Returns a const pointer to the internal communicator of the gates of the matrix.
Definition: matrix.hpp:174
Mean Filter class template.
Definition: mean_filter.hpp:23
Global vector wrapper class template.
Definition: vector.hpp:68
LocalVector_ & local()
Returns a reference to the internal local LAFEM vector object.
Definition: vector.hpp:122
bool empty() const
Checks whether the container is empty.
Definition: container.hpp:1165
Index size() const
Returns the containers size.
Definition: container.hpp:1136
virtual void clear()
Free all allocated arrays.
Definition: container.hpp:875
Blocked Dense data vector class template.
Dense data vector class template.
DT_ * elements()
Get a pointer to the data array.
DenseVector clone(CloneMode clone_mode=CloneMode::Deep) const
Clone operation.
Filter Chainclass template.
Sequence of filters of the same type.
Mean Filter class template.
Definition: mean_filter.hpp:22
Blocked None Filter class template.
None Filter class template.
Definition: none_filter.hpp:29
Saddle-Point matrix meta class template.
Slip Filter class template.
Definition: slip_filter.hpp:67
SlipFilter clone(CloneMode clone_mode=CloneMode::Deep) const
Creates a clone of itself.
CSR based blocked sparse matrix.
Index rows() const
Retrieve matrix row count.
IT_ * col_ind()
Retrieve column indices array.
IT_ * row_ptr()
Retrieve row start index array.
Index columns() const
Retrieve matrix column count.
Index used_elements() const
Retrieve non zero element count.
auto val() const -> const typename Intern::BCSRPerspectiveHelper< DT_, BlockHeight_, BlockWidth_, perspective_ >::Type *
Retrieve non zero element array.
CSR based sparse matrix.
VectorTypeR create_vector_r() const
Returns a new compatible R-Vector.
IT_ * col_ind()
Retrieve column indices array.
DT_ * val()
Retrieve non zero element array.
Index rows() const
Retrieve matrix row count.
Index columns() const
Retrieve matrix column count.
Index used_elements() const
Retrieve non zero element count.
IT_ * row_ptr()
Retrieve row start index array.
TupleVector meta-filter class template.
Variadic TupleVector class template.
Unit Filter Blocked class template.
UnitFilterBlocked clone(CloneMode clone_mode=CloneMode::Deep) const
Creates a clone of itself.
Unit Filter class template.
Definition: unit_filter.hpp:29
UnitFilter clone(CloneMode clone_mode=CloneMode::Deep) const
Creates a clone of itself.
virtual void upload_vector(SolverVectorType &vector, const VectorTypeV &vector_v, const VectorTypeP &vector_p) const
Uploads a velocity/pressure vector pair to a solver vector.
virtual void download_vector(const SolverVectorType &vector, VectorTypeV &vector_v, VectorTypeP &vector_p) const
Downloads a velocity/pressure vector pair from a solver vector.
Core class for conversion of Stokes systems to CSR format for direct solvers.
virtual Solver::Status apply(SystemVectorType &vec_sol, const SystemVectorType &vec_rhs) override
Applies the direct Stokes solver.
virtual Solver::Status apply(SystemVectorType &vec_cor, const SystemVectorType &vec_def) override
Applies the direct Stokes solver.
std::shared_ptr< Solver::Umfpack > _umfpack_solver
our UMFPACK solver object; used if no other direct solver was chosen
std::shared_ptr< Solver::CUDSS > _cudss_solver
our CUDSS solver object; used if preferred backend is PreferredBackend::cuda
std::shared_ptr< Solver::MKLDSS > _mkldss_solver
our MKLDSS solver object; used if preferred backend is PreferredBackend::mkl
DirectStokesSolver(const SystemMatrixType &matrix_sys, const SystemFilterType &filter_sys)
Constructor for preferred backend.
LAFEM::TupleVector< typename MatrixB_::VectorTypeL, typename MatrixD_::VectorTypeL > SystemVectorType
our system vector type
DirectStokesCore< double, Index, MatrixA_, MatrixB_, MatrixD_ > StokesCoreType
the direct Stokes core class type
std::shared_ptr< Solver::SolverBase< SolverVectorType > > _direct_solver
the actual direct solver; opaque SolverBase pointer
DirectStokesSolver(const SystemMatrixType &matrix_sys, const SystemFilterType &filter_sys, PreferredBackend backend)
Constructor.
Direct Stokes solver class template.
Polymorphic solver interface.
Definition: base.hpp:183
String class implementation.
Definition: string.hpp:47
std::shared_ptr< MKLDSS > new_mkl_dss(const LAFEM::SparseMatrixCSR< double, Index > &matrix)
Creates a new MKLDSS solver object.
Definition: mkl_dss.hpp:112
std::shared_ptr< DirectStokesSolver< Matrix_, Filter_ > > new_direct_stokes_solver(const Matrix_ &matrix, const Filter_ &filter)
Creates a new DirectStokesSolver object.
std::shared_ptr< CUDSS > new_cudss(const LAFEM::SparseMatrixCSR< double, Index > &matrix)
Creates a new CUDSS solver object.
Definition: cudss.hpp:107
Status
Solver status return codes enumeration.
Definition: base.hpp:47
static constexpr bool direct_stokes_solver_available
specifies whether at least one backend for the DirectStokesSolver class is available
std::shared_ptr< Umfpack > new_umfpack(const LAFEM::SparseMatrixCSR< double, Index > &matrix)
Creates a new Umfpack solver object.
Definition: umfpack.hpp:116
FEAT namespace.
Definition: adjactor.hpp:12
PreferredBackend
The backend that shall be used in all compute heavy calculations.
Definition: backend.hpp:124
std::uint64_t Index
Index data type.