v0.16.3
Loading...
Searching...
No Matches
Schur.cpp
Go to the documentation of this file.
1/**
2 * @file Schur.cpp
3 * @brief Implementation of Schur Complement
4 * @date 2023-02-03
5 *
6 * @copyright Copyright (c) 2023
7 *
8 */
9
10namespace MoFEM {
11
12constexpr bool debug_schur = false;
13
16
17protected:
19
20 /**
21 * @brief Assemble Schur complement
22 *
23 */
25
26 Mat S,
27
28 const UId &uid_row, VectorInt &row_ind,
29
30 const UId &uid_col, VectorInt &col_ind,
31
32 MatrixDouble &m, InsertMode iora
33
34 );
35};
36
37// Assemble specialisations
39
40 Mat S,
41
42 const UId &uid_row, VectorInt &row_ind,
43
44 const UId &uid_col, VectorInt &col_ind,
45
46 MatrixDouble &m, InsertMode iora
47
48) {
49 return ::MatSetValues(
50
51 S, row_ind.size(), &*row_ind.begin(), col_ind.size(), &*col_ind.begin(),
52 &*m.data().begin(), iora
53
54 );
55}
56
57/**
58 * @brief Clear Schur complement internal data
59 *
60 */
62
64
65 MoFEMErrorCode doWork(int side, EntityType type,
67};
68
70
72 boost::shared_ptr<std::vector<unsigned char>> marker_ptr,
73 double diag_val);
74
75 MoFEMErrorCode doWork(int side, EntityType type,
77
78private:
79 boost::shared_ptr<std::vector<unsigned char>> markerPtr;
80 double diagVal;
81};
82
83/**
84 * @brief Assemble Schur complement (Implementation)
85 *
86 */
87template <typename OP_SCHUR_ASSEMBLE_BASE>
89
91
92 /**
93 * @brief Construct a new Op Schur Assemble End object
94 *
95 * @param fields_name list of fields
96 * @param field_ents list of entities on which schur complement is applied
97 * (can be empty)
98 * @param schur_ao map indices to schur matrix indices
99 * @param schur_mat schur matrix
100 * @param sym_schur true if schur is symmetric
101 * @param symm_op true if block diagonal is symmetric
102 */
104
105 std::vector<std::string> fields_name,
106 std::vector<boost::shared_ptr<Range>> field_ents,
107
108 SmartPetscObj<AO> schur_ao, SmartPetscObj<Mat> schur_mat,
109
110 bool sym_schur, bool symm_op);
111
112protected:
113 template <typename I>
115
116 int side, EntityType type, EntitiesFieldData::EntData &data
117
118 );
119
120 std::vector<std::string> fieldsName;
121 std::vector<boost::shared_ptr<Range>> fieldEnts;
125
129};
130
131struct SchurDSYSV; ///< SY symmetric
132struct SchurDGESV; ///< GE general (i.e., nonsymmetric, in some cases
133 ///< rectangular)
134
135/**
136 * @brief Assemble Schur complement
137 *
138 */
139template <typename I> struct OpSchurAssembleEnd;
140
141template <>
143 : public OpSchurAssembleEndImpl<OpSchurAssembleBaseImpl> {
144 using OpSchurAssembleEndImpl<OpSchurAssembleBaseImpl>::OpSchurAssembleEndImpl;
145 MoFEMErrorCode doWork(int side, EntityType type,
147};
148
149template <>
151 : public OpSchurAssembleEndImpl<OpSchurAssembleBaseImpl> {
152 using OpSchurAssembleEndImpl<OpSchurAssembleBaseImpl>::OpSchurAssembleEndImpl;
153 MoFEMErrorCode doWork(int side, EntityType type,
155};
156
157/**
158 * @brief Schur complement data storage
159 *
160 */
161struct SchurElemMats : public boost::enable_shared_from_this<SchurElemMats> {
162
163 SchurElemMats(const size_t idx, const UId uid_row, const UId uid_col);
164 virtual ~SchurElemMats() = default;
165
166 const size_t iDX;
169
170 inline auto &getMat() const { return locMats[iDX]; }
171 inline auto &getRowInd() const { return rowIndices[iDX]; }
172 inline auto &getColInd() const { return colIndices[iDX]; }
173
174 static MoFEMErrorCode MatSetValues(Mat M,
175 const EntitiesFieldData::EntData &row_data,
176 const EntitiesFieldData::EntData &col_data,
177 const MatrixDouble &mat, InsertMode iora);
178
179protected:
180 static MoFEMErrorCode
182 const EntitiesFieldData::EntData &col_data,
183 const MatrixDouble &mat, InsertMode iora);
184
185 friend struct OpSchurAssembleBegin;
187 template <typename OP_SCHUR_ASSEMBLE_BASE>
189
190 struct uid_mi_tag {};
191 struct col_mi_tag {};
192
193 using SchurElemStorage = multi_index_container<
194 const SchurElemMats *,
195 indexed_by<
196
197 ordered_unique<
198 tag<uid_mi_tag>,
199 composite_key<
201
202 member<SchurElemMats, const UId, &SchurElemMats::uidRow>,
203 member<SchurElemMats, const UId, &SchurElemMats::uidCol>
204
205 >>,
206
207 ordered_non_unique<tag<col_mi_tag>, member<SchurElemMats, const UId,
209
210 >>;
211
212 static boost::ptr_vector<MatrixDouble> locMats;
213 static boost::ptr_vector<VectorInt> rowIndices;
214 static boost::ptr_vector<VectorInt> colIndices;
215 static boost::ptr_vector<SchurElemMats> schurElemMats;
216 static size_t maxIndexCounter;
217
219};
220
221/**
222 * @brief Storage of block matrix, shared by block matrix and its nested
223 * (Schur) sub-matrices
224 *
225 * Block is dense sub-matrix coupling DOFs on one row entity with DOFs on one
226 * column entity, stored row-major at mat_shift with leading dimension ld.
227 *
228 * Blocks which are not part of A00 are stored in data. When nested Schur
229 * structure is created (see createSchurNestedMatrixStruture), blocks of A00
230 * which belong to one finite element are stored in a buffer of that element.
231 * Elements with the same block pattern (entities, numbers of DOFs and listed
232 * pairs) share a Pattern, which defines the layout of element buffers.
233 *
234 * Element buffers start in listed layout (listed blocks only). At the first
235 * factorisation each pattern is analysed on a probe element: nodes (entities)
236 * whose diagonal block is a safe pivot are eliminated one by one (block LU in
237 * place, fill only where the pattern creates it), the remaining nodes form a
238 * dense core factorised with partial pivoting. Element buffers are then relaid
239 * element by element, and factors are kept until MatZeroEntries. Elements
240 * failing the pivot test at a later factorisation are factorised dense in a
241 * buffer of their own. See doc/development/schur.md.
242 *
243 * Factorisation works on M = A^T, which is how LAPACK reads row-major element
244 * blocks. Slot (r, c), i.e. block of A coupling row node r with column node c,
245 * read column-major is block (c, r) of M.
246 *
247 * If preconditioner blocks are requested, they are allocated for A00 element
248 * buffers only, with the same layout. Then factors of (A00 + preconditioner)
249 * are stored in place of preconditioner blocks, and A00 blocks are intact.
250*/
252
253 /**
254 * @brief block data indexes
255 *
256 */
257 struct Indexes {
258
264
265 inline UId getRowUId() const { return uid_row; }
266 inline UId getColUId() const { return uid_col; }
267 inline UId getFEUId() const { return uid_fe; }
268 inline int getRow() const { return row; }
269 inline int getCol() const { return col; }
270 inline int getNbRows() const { return nb_rows; }
271 inline int getNbCols() const { return nb_cols; }
272 inline int getLocRow() const { return loc_row; }
273 inline int getLocCol() const { return loc_col; }
274 inline size_t getMatShift() const { return mat_shift; }
275 inline int getLd() const { return ld; }
276 inline int getElem() const { return elem; }
277
278 inline void setStorage(size_t shift, int leading_dim, int element) {
279 mat_shift = shift;
280 ld = leading_dim;
281 elem = element;
282 }
283
284 private:
288 int row;
289 int col;
294 size_t mat_shift; ///< shift in data, or in element buffer if elem >= 0
295 int ld; ///< leading dimension of block in storage
296 int elem; ///< index of A00 element, -1 if none
297 };
298
299 /// DOFs on one entity (node) of A00 element
300 struct ElemEntity {
302 int nb; ///< number of DOFs
303 int offset; ///< offset in element vector (layout of pattern)
304 int locParent; ///< local index in block matrix numbering
305 int globParent; ///< global index in block matrix numbering
306 int locBlock; ///< local index in A00 sub-problem numbering
307 int globBlock; ///< global index in A00 sub-problem numbering
308 };
309
310 /**
311 * @brief Block pattern of A00 element and layout of element buffer
312 *
313 * Node is an entity of the element. Slot (r, c) couples row node r with
314 * column node c. It is stored row-major at slotShift with leading dimension
315 * slotLd, which read column-major is block (c, r) of M = A^T.
316 */
317 struct Pattern {
318
319 enum State {
320 LISTED, ///< listed blocks only, not analysed
321 BLOCK, ///< block elimination of order nodes, then dense core
322 DENSE ///< dense element matrix (core contains all nodes)
323 };
324
325 int nbNodes = 0;
326 int size = 0; ///< DOFs per element
327 int nbElems = 0;
328 int nbListed = 0;
329 std::vector<int> nodeSize;
330 std::vector<std::string> nodeName;
331 std::vector<char> listed; ///< nbNodes x nbNodes, slot (r, c) is listed
332
334 std::vector<long> slotShift; ///< nbNodes x nbNodes, -1 if not stored
335 std::vector<int> slotLd;
336 std::vector<int> nodeOffset; ///< offset of node DOFs in element vector
337 std::vector<int> order; ///< nodes eliminated block by block
338 std::vector<int> core; ///< nodes of dense core
339 size_t coreShift = 0; ///< shift of dense core in element buffer
340 int coreSize = 0; ///< DOFs in dense core
341 int coreOffset = 0; ///< offset of core DOFs in element vector
342 size_t elemSize = 0; ///< doubles in element buffer
343
344 int nbFallback = 0; ///< elements factorised dense in last factorisation
345 double beRef = 0; ///< backward error of dense solve on probe
346 double beMax = 0; ///< largest backward error in last factorisation
347 bool warned = false;
348
349 inline long sShift(int r, int c) const {
350 return slotShift[r * nbNodes + c];
351 }
352 inline int sLd(int r, int c) const { return slotLd[r * nbNodes + c]; }
353 inline bool isListed(int r, int c) const { return listed[r * nbNodes + c]; }
354 /// shift of block (a, b) of M = A^T, column-major
355 inline long mShift(int a, int b) const { return sShift(b, a); }
356 /// leading dimension of block (a, b) of M = A^T
357 inline int mLd(int a, int b) const { return sLd(b, a); }
358 };
359
360 /// A00 element
361 struct Elem {
362 int pattern; ///< index of pattern
363 int size; ///< DOFs of element
364 int entBegin; ///< first entity in elemEntities
365 int entEnd; ///< one past last entity in elemEntities
366 int pivShift; ///< shift in pivots
367 bool local = false; ///< all entities have local index on this process
368 bool owned = false; ///< all entities are owned by this process
369 bool factorised = false; ///< factors are stored for this element
370 bool dense = false; ///< factors are in elemDense (pivot test failed)
371 };
372
373 std::vector<Indexes> blockIndex; ///< blocks sorted by (row uid, col uid)
374 std::vector<int> feOrder; ///< blocks sorted by (fe uid, row uid, col uid)
375 std::vector<int> colOrder; ///< blocks sorted by (col uid, row uid)
376
377 std::vector<Pattern> patterns;
378 std::vector<Elem> elems;
379 std::vector<ElemEntity> elemEntities;
380 std::vector<std::vector<double>> elemData; ///< A00 element buffers
381 std::vector<std::vector<double>> elemPrec; ///< preconditioner buffers
382 std::vector<std::vector<double>> elemDense; ///< dense factors (fallback)
383 int maxElemSize = 0; ///< maximal DOFs of element
384 size_t maxElemBuffer = 0; ///< maximal size of element buffer
385
386 size_t dataSize = 0; ///< required size of data
387 std::vector<double> data; ///< blocks not in A00, allocated on first use
388 std::vector<__CLPK_integer> pivots;
389
390 /// data is allocated on first use, such that storage is not allocated twice
391 /// when layout is changed by createSchurNestedMatrixStruture
392 inline void ensureAllocated() {
393 if (data.size() != dataSize)
394 data.assign(dataSize, 0.);
395 }
396
401 FACTOR_FAILED ///< factorisation failed, data is corrupted
402 };
404 bool nested = false; ///< nested Schur structure has been created
405 bool hasPreconditioner = false; ///< preconditioner blocks are enabled
406
407 // options, see createSchurNestedMatrixStruture
408 bool optDense = false; ///< -schur_a00_dense
409 double optPivotThreshold = 0; ///< -schur_a00_pivot_threshold
410 double optRcondMin = 1e-12; ///< -schur_a00_rcond_min
411 double optBackwardError = 1e-12; ///< -schur_a00_backward_error
412
413 inline bool isFactorised() const { return factorStorage != FACTOR_NONE; }
414
415 /// Fails if factorisation failed, storage has to be zeroed
419 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
420 "A00 factorisation failed and block matrix data is corrupted, "
421 "call MatZeroEntries and assemble again");
423 }
424
425 /// Call before modification of data, fails if factors are stored in place
426 inline MoFEMErrorCode modified() const {
429 if (isFactorised())
430 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
431 "A00 element matrices are factorised in place, call "
432 "MatZeroEntries before assembling or modifying block matrix");
434 }
435
436 inline void resetFactors() {
438 for (auto &e : elems) {
439 e.factorised = false;
440 e.dense = false;
441 }
442 for (auto &d : elemDense)
443 std::vector<double>().swap(d);
444 }
445
446 /// values of block, preconditioner buffer if prec is true
447 inline double *blockPtr(const Indexes &b, bool prec = false) {
448 if (b.getElem() >= 0)
449 return (prec ? elemPrec : elemData)[b.getElem()].data() + b.getMatShift();
450 return data.data() + b.getMatShift();
451 }
452
453 static inline bool lessRowCol(const Indexes &a, const Indexes &b) {
454 if (a.getRowUId() != b.getRowUId())
455 return a.getRowUId() < b.getRowUId();
456 return a.getColUId() < b.getColUId();
457 }
458
459 /// find block by row and column entity uid, return nullptr if not found
460 inline const Indexes *find(const UId &row, const UId &col) const {
461 auto it = std::lower_bound(
462 blockIndex.begin(), blockIndex.end(), std::make_pair(row, col),
463 [](const Indexes &a, const std::pair<UId, UId> &p) {
464 if (a.getRowUId() != p.first)
465 return a.getRowUId() < p.first;
466 return a.getColUId() < p.second;
467 });
468 if (it != blockIndex.end() && it->getRowUId() == row &&
469 it->getColUId() == col)
470 return &*it;
471 return nullptr;
472 }
473
474 inline Indexes *findMutable(const UId &row, const UId &col) {
475 return const_cast<Indexes *>(find(row, col));
476 }
477
478 struct RowUIdLess {
479 bool operator()(const Indexes &a, const UId &b) const {
480 return a.getRowUId() < b;
481 }
482 bool operator()(const UId &a, const Indexes &b) const {
483 return a < b.getRowUId();
484 }
485 };
486
487 /// range of blocks with given row entity uid
488 inline auto rowRange(const UId &row) const {
489 return std::equal_range(blockIndex.begin(), blockIndex.end(), row,
490 RowUIdLess());
491 }
492
493 /// range (in colOrder) of blocks with given column entity uid
494 inline auto colRange(const UId &col) const {
495 auto lo = std::lower_bound(
496 colOrder.begin(), colOrder.end(), col,
497 [this](int i, const UId &c) { return blockIndex[i].getColUId() < c; });
498 auto hi = std::upper_bound(
499 colOrder.begin(), colOrder.end(), col,
500 [this](const UId &c, int i) { return c < blockIndex[i].getColUId(); });
501 return std::make_pair(lo, hi);
502 }
503};
504
505/**
506 * @brief Context of block matrix shell
507 *
508 * Block matrix, and each of nested (Schur) sub-matrices, keeps list of blocks
509 * in its own local numbering. Storage of block values is shared between
510 * matrices created from the same block structure.
511 */
513
514 /// numbering of A00 element matrix entities used by this matrix
516
517 /// block in numbering of this matrix (blocks not stored in element buffers)
518 struct Block {
519 size_t shift;
520 int ld;
521 int row;
522 int col;
527 };
528
529 boost::shared_ptr<BlockStorage> storage;
530 std::vector<Block> blocks;
531 bool hasElems = false; ///< A00 elements are part of this matrix
533
536
538
539 inline int locIdx(const BlockStorage::ElemEntity &e) const {
540 return numbering == BLOCK ? e.locBlock : e.locParent;
541 }
542 inline int globIdx(const BlockStorage::ElemEntity &e) const {
543 return numbering == BLOCK ? e.globBlock : e.globParent;
544 }
545};
546
554
556 PetscLogEventRegister("schurMatSetVal", 0, &MOFEM_EVENT_schurMatSetValues);
557 PetscLogEventRegister("opSchurAsmEnd", 0, &MOFEM_EVENT_opSchurAssembleEnd);
558 PetscLogEventRegister("blockSetVal", 0, &MOFEM_EVENT_BlockStructureSetValues);
559 PetscLogEventRegister("blockMult", 0, &MOFEM_EVENT_BlockStructureMult);
560 PetscLogEventRegister("blockSolve", 0, &MOFEM_EVENT_BlockStructureSolve);
561 PetscLogEventRegister("schurZeroRandC", 0, &MOFEM_EVENT_zeroRowsAndCols);
562 PetscLogEventRegister("assembleSchurMat", 0, &MOFEM_EVENT_AssembleSchurMat);
563}
564
566boost::ptr_vector<MatrixDouble> SchurElemMats::locMats;
567boost::ptr_vector<VectorInt> SchurElemMats::rowIndices;
568boost::ptr_vector<VectorInt> SchurElemMats::colIndices;
569boost::ptr_vector<SchurElemMats> SchurElemMats::schurElemMats;
571
572SchurElemMats::SchurElemMats(const size_t idx, const UId uid_row,
573 const UId uid_col)
574 : iDX(idx), uidRow(uid_row), uidCol(uid_col) {}
575
578 const EntitiesFieldData::EntData &col_data,
579 const MatrixDouble &mat, InsertMode iora) {
581
582#ifndef NDEBUG
583 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_schurMatSetValues, 0, 0, 0, 0);
584#endif // NDEBUG
585
586 // get row indices, in case of store, get indices from storage
587 // storage keeps marked indices to manage boundary conditions
588 auto get_row_indices = [&]() -> const VectorInt & {
589 if (auto e_ptr = row_data.getFieldEntities()[0]) {
590 if (auto stored_data_ptr =
591 e_ptr->getSharedStoragePtr<EssentialBcStorage>()) {
592 return stored_data_ptr->entityIndices;
593 }
594 }
595 return row_data.getIndices();
596 };
597
598 const auto &row_ind = get_row_indices();
599 const auto &col_ind = col_data.getIndices();
600
601 const auto nb_rows = row_ind.size();
602 const auto nb_cols = col_ind.size();
603
604#ifndef NDEBUG
605 if (mat.size1() != nb_rows) {
606 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
607 "Wrong mat size %zu != %zu", mat.size1(), nb_rows);
608 }
609 if (mat.size2() != nb_cols) {
610 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
611 "Wrong mat size %zu != %zu", mat.size2(), nb_cols);
612 }
613#endif // NDEBUG
614
615 // get entity uid
616 auto get_uid = [](auto &data) {
617 if (data.getFieldEntities().size() == 1) {
618
619 return data.getFieldEntities()[0]->getLocalUniqueId();
620
621 } else {
622
623 // Is assumed that sum of entities ids gives unique id, that is not true,
624 // but corner case is improbable.
625
626 // @todo: debug version should have test
627
628 auto &uid0 = data.getFieldEntities()[0]->getLocalUniqueId();
629 auto field_id0 = FieldEntity::getFieldBitNumberFromUniqueId(uid0);
630 auto ent0 = FieldEntity::getHandleFromUniqueId(uid0);
631 auto type0 = type_from_handle(ent0);
632 auto id = id_from_handle(ent0);
633
634 for (auto i = 1; i < data.getFieldEntities().size(); ++i) {
635
636 // get entity id from ent
637 id += id_from_handle(
638
639 // get entity handle from unique uid
641 data.getFieldEntities()[i]->getLocalUniqueId())
642
643 );
644 }
645
647 field_id0,
648
649 ent_form_type_and_id(type0, id)
650
651 );
652 }
653 };
654
655 auto uid_row = get_uid(row_data);
656 auto uid_col = get_uid(col_data);
657
658 auto it =
659 SchurElemMats::schurL2Storage.template get<SchurElemMats::uid_mi_tag>()
660 .find(boost::make_tuple(uid_row, uid_col));
661
662 if (it ==
663 SchurElemMats::schurL2Storage.template get<SchurElemMats::uid_mi_tag>()
664 .end()) {
665
666 // get size of arrays of matrices
667 const auto size = SchurElemMats::locMats.size();
668
669 // expand memory allocation
671 SchurElemMats::locMats.push_back(new MatrixDouble());
672 SchurElemMats::rowIndices.push_back(new VectorInt());
673 SchurElemMats::colIndices.push_back(new VectorInt());
675 new SchurElemMats(SchurElemMats::maxIndexCounter, uid_row, uid_col));
676 } else {
678 uid_row;
680 uid_col;
681 }
682
683 // add matrix to storage
684 auto p = SchurElemMats::schurL2Storage.emplace(
686#ifndef NDEBUG
687 if (!p.second) {
688 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY, "Failed to insert");
689 }
690#endif // NDEBUG
691
692 auto asmb = [&](auto &sm) {
693 sm.resize(nb_rows, nb_cols, false);
694 noalias(sm) = mat;
695 };
696
697 asmb((*p.first)->getMat());
698
699 auto add_indices = [](auto &storage, auto &ind) {
700 storage.resize(ind.size(), false);
701 noalias(storage) = ind;
702 };
703
704 add_indices((*p.first)->getRowInd(), row_ind);
705 add_indices((*p.first)->getColInd(), col_ind);
706
707 } else {
708 // entry (submatrix) already exists
709
710 auto asmb = [&](auto &sm) {
712
713#ifndef NDEBUG
714 if (sm.size1() != nb_rows) {
715 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
716 "Wrong mat or storage size %zu != %zu", sm.size1(), nb_rows);
717 }
718 if (sm.size2() != nb_cols) {
719 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
720 "Wrong mat or storage size %zu != %zu", sm.size2(), nb_cols);
721 }
722#endif // NDEBUG
723
724 switch (iora) {
725 case ADD_VALUES:
726 sm += mat;
727 break;
728 case INSERT_VALUES:
729 noalias(sm) = mat;
730 break;
731 default:
732 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED,
733 "Assembly type not implemented");
734 }
736 };
737
738 CHKERR asmb((*it)->getMat());
739
740 // no need to set indices
741 }
742
743#ifndef NDEBUG
744 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_schurMatSetValues, 0, 0, 0, 0);
745#endif // NDEBUG
746
748}
749
752
756#ifndef NDEBUG
757 if constexpr (debug_schur)
758 MOFEM_LOG("SELF", Sev::noisy) << "Schur assemble begin";
759#endif
762
764}
765
767 boost::shared_ptr<std::vector<unsigned char>> marker_ptr, double diag_val)
768 : OpSchurAssembleBaseImpl(NOSPACE, OPSPACE), markerPtr(marker_ptr),
769 diagVal(diag_val) {}
770
775
776 auto fe_ptr = getFEMethod();
777 auto prb_ptr = fe_ptr->problemPtr;
778 auto &dof = prb_ptr->getNumeredRowDofsPtr()->get<PetscGlobalIdx_mi_tag>();
779
780 auto find_loc_dof_index = [&](auto glob) {
781 auto it = dof.find(glob);
782 if (it != dof.end()) {
783 return (*it)->getPetscLocalDofIdx();
784 }
785 MOFEM_LOG("SELF", Sev::error) << "Global dof " << glob;
788 "Global dof index not found in local numered dofs storage");
789 return -1;
790 };
791
792 for (auto &s : SchurElemMats::schurL2Storage) {
793
794 auto zero_ind = [&](auto &ind) {
795 for (auto i = 0; i < ind.size(); ++i) {
796 if (ind[i] >= 0) {
797 if ((*markerPtr)[find_loc_dof_index(ind[i])]) {
798 ind[i] = -1;
799 }
800 }
801 }
802 };
803
804 zero_ind(s->getRowInd());
805 zero_ind(s->getColInd());
806 }
807
809};
810
811template <typename OP_SCHUR_ASSEMBLE_BASE>
813 std::vector<std::string> fields_name,
814 std::vector<boost::shared_ptr<Range>> field_ents,
815 SmartPetscObj<AO> schur_ao, SmartPetscObj<Mat> schur_mat, bool sym_schur,
816 bool symm_op)
817 : OP(NOSPACE, OP::OPSPACE, symm_op), fieldsName(fields_name),
818 fieldEnts(field_ents), schurAO(schur_ao), schurMat(schur_mat),
819 symSchur(sym_schur) {}
820
821template <typename OP_SCHUR_ASSEMBLE_BASE>
822template <typename I>
824 int side, EntityType type, EntitiesFieldData::EntData &data) {
825
827
828#ifndef NDEBUG
829 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_opSchurAssembleEnd, 0, 0, 0, 0);
830#endif
831
832#ifndef NDEBUG
833 if constexpr (debug_schur)
834 MOFEM_LOG("SELF", Sev::noisy) << "Schur assemble begin -> end";
835#endif
836
837 auto get_a00_uids = [&]() {
838 auto get_field_bit = [&](auto &name) {
839 return OP::getPtrFE()->mField.get_field_bit_number(name);
840 };
841
842 std::vector<std::pair<UId, UId>> a00_uids;
843 a00_uids.reserve(fieldsName.size());
844 for (auto ss = 0; ss != fieldsName.size(); ++ss) {
845 auto field_bit = get_field_bit(fieldsName[ss]);
846 auto row_ents = fieldEnts[ss];
847 if (row_ents) {
848 for (auto p = row_ents->pair_begin(); p != row_ents->pair_end(); ++p) {
849 auto lo_uid =
850 FieldEntity::getLoLocalEntityBitNumber(field_bit, p->first);
851 auto hi_uid =
852 FieldEntity::getHiLocalEntityBitNumber(field_bit, p->second);
853 a00_uids.push_back(std::make_pair(lo_uid, hi_uid));
854 }
855 } else {
857 field_bit, get_id_for_min_type<MBVERTEX>());
859 field_bit, get_id_for_max_type<MBENTITYSET>());
860 a00_uids.push_back(std::make_pair(lo_uid, hi_uid));
861 }
862 }
863 return a00_uids;
864 };
865
866#ifndef NDEBUG
867 auto get_field_name = [&](auto uid) {
868 return OP::getPtrFE()->mField.get_field_name(field_bit_from_bit_number(
870 };
871 auto list_storage = [&](auto &storage) {
873 int i = 0;
874 for (auto &p : storage) {
875 MOFEM_LOG("SELF", Sev::noisy)
876 << "List schur storage: " << i << " " << p->iDX << ": "
877 << get_field_name(p->uidRow) << " " << get_field_name(p->uidCol)
878 << " : " << p->getMat().size1() << " " << p->getMat().size2();
879 ++i;
880 }
882 };
883#endif // NDEBUG
884
885 auto assemble_dense_blocks = [&]() {
886 using matrix_range = ublas::matrix_range<MatrixDouble>;
887 using range = ublas::range;
889 auto &storage = SchurElemMats::schurL2Storage;
890
891 auto assemble_schur = [this](auto &m, auto &uid_row, auto &uid_col,
892 auto *row_ind_ptr, auto *col_ind_ptr) {
894
895 if (schurMat) {
896
897 if (auto ierr = this->assembleSchurMat(
898
899 schurMat, uid_row, *row_ind_ptr, uid_col, *col_ind_ptr, m,
900 ADD_VALUES
901
902 )) {
903#ifndef NDEBUG
904 auto field_ents = OP::getPtrFE()->mField.get_field_ents();
905 auto row_ent_it = field_ents->find(uid_row);
906 auto col_ent_it = field_ents->find(uid_col);
907 MOFEM_LOG_CHANNEL("SELF");
908 if (row_ent_it != field_ents->end())
909 MOFEM_LOG("SELF", Sev::error)
910 << "Assemble row entity: " << (*row_ent_it)->getName() << " "
911 << (*col_ent_it)->getEntTypeName() << " side "
912 << (*row_ent_it)->getSideNumber();
913 if (col_ent_it != field_ents->end())
914 MOFEM_LOG("SELF", Sev::error)
915 << "Assemble col entity: " << (*col_ent_it)->getName() << " "
916 << (*col_ent_it)->getEntTypeName() << " side "
917 << (*col_ent_it)->getSideNumber();
918#endif // NDEBUG
919 CHK_THROW_MESSAGE(ierr, "MatSetValues");
920 }
921 }
922
924 };
925
926 auto a00_uids = get_a00_uids();
927
928 auto get_block_indexing = [&](auto &a00_uids) {
929 // iterate over a00 uids and find blocks
930 std::vector<const SchurElemMats *> block_list;
931 block_list.reserve(storage.size());
932 for (auto &rp_uid : a00_uids) {
933 auto [rlo_uid, rhi_uid] = rp_uid;
934 for (auto &cp_uid : a00_uids) {
935 auto [clo_uid, chi_uid] = cp_uid;
936
937 auto it =
938 storage.template get<SchurElemMats::uid_mi_tag>().lower_bound(
939 boost::make_tuple(rlo_uid, clo_uid));
940 auto hi_it =
941 storage.template get<SchurElemMats::uid_mi_tag>().upper_bound(
942 boost::make_tuple(rhi_uid, chi_uid));
943
944 for (; it != hi_it; ++it) {
945 if ((*it)->uidRow >= rlo_uid && (*it)->uidRow < rhi_uid &&
946 (*it)->uidCol >= clo_uid && (*it)->uidCol < chi_uid) {
947 block_list.push_back(*it);
948 }
949 }
950 }
951 }
952
953 // create block indexes map for blockMat
954 std::map<UId, std::pair<size_t, const VectorInt *>>
955 block_indexing; // uid block map
956 for (auto d : block_list) {
957 if (block_indexing.find(d->uidRow) == block_indexing.end()) {
958 block_indexing[d->uidRow] =
959 std::make_pair(d->getRowInd().size(), &(d->getRowInd()));
960 }
961 }
962
963 // set indexes to block
964 int mat_block_size = 0; // size of block matrix
965 for (auto &p_uid : a00_uids) {
966 auto [lo_uid, hi_uid] = p_uid;
967 auto lo = block_indexing.lower_bound(lo_uid);
968 auto up = block_indexing.upper_bound(hi_uid);
969 for (; lo != up; ++lo) {
970 lo->second.first = mat_block_size;
971 mat_block_size += lo->second.second->size();
972 }
973 }
974
975 return std::make_tuple(block_list, block_indexing, mat_block_size);
976 };
977
978 auto get_schur_block_list = [&](auto &block_indexing) {
979 std::vector<const SchurElemMats *> block_list;
980 block_list.reserve(storage.size());
981 for (auto &s : storage) {
982 if (block_indexing.find(s->uidRow) == block_indexing.end() &&
983 block_indexing.find(s->uidCol) == block_indexing.end()) {
984 block_list.push_back(s);
985 }
986 }
987 return block_list;
988 };
989
990 auto bi = get_block_indexing(a00_uids);
991 auto &[block_list, block_indexing, block_mat_size] = bi;
992
993 if (block_mat_size == 0) {
994
995 if (schurAO) {
996 for (auto &s : storage) {
997 CHKERR AOApplicationToPetsc(schurAO, s->getRowInd().size(),
998 &*s->getRowInd().begin());
999 CHKERR AOApplicationToPetsc(schurAO, s->getColInd().size(),
1000 &*s->getColInd().begin());
1001 }
1002 }
1003
1004 for (auto &s : storage) {
1005 auto &m = s->getMat();
1006 CHKERR assemble_schur(m, s->uidRow, s->uidCol, &(s->getRowInd()),
1007 &(s->getColInd()));
1008 }
1010 }
1011
1012 blockMat.resize(block_mat_size, block_mat_size, false);
1013 blockMat.clear();
1014
1015 auto get_range = [](auto &bi) {
1016 return range(bi.first, bi.first + bi.second->size());
1017 };
1018
1019 for (auto &s : block_list) {
1020 auto &m = s->getMat();
1021#ifndef NDEBUG
1022 if (block_indexing.find(s->uidRow) == block_indexing.end())
1023 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY, "Wrong rlo_uid");
1024 if (block_indexing.find(s->uidCol) == block_indexing.end())
1025 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY, "Wrong clo_uid");
1026#endif // NDEBUG
1027
1028 auto &rbi = block_indexing.at(s->uidRow);
1029 auto &cbi = block_indexing.at(s->uidCol);
1030
1031 auto sub_mat = matrix_range(blockMat, get_range(rbi), get_range(cbi));
1032 sub_mat = m;
1033 }
1034
1035 auto get_zeroed_indices = [&](auto extractor_uid, auto extractor_ind) {
1036 auto &[block_list, block_indexing, block_mat_size] = bi;
1037 std::vector<int> zeroed_indices;
1038 zeroed_indices.reserve(block_mat_size);
1039 for (auto &s : block_list) {
1040 auto &bi = block_indexing.at(extractor_uid(s));
1041 auto &ind = extractor_ind(s);
1042 for (auto it = ind.begin(); it != ind.end(); ++it) {
1043 if (*it < 0) {
1044 auto idx = bi.first + std::distance(ind.begin(), it);
1045 zeroed_indices.push_back(idx);
1046 }
1047 }
1048 }
1049 std::sort(zeroed_indices.begin(), zeroed_indices.end());
1050 auto it = std::unique(zeroed_indices.begin(), zeroed_indices.end());
1051 zeroed_indices.resize(std::distance(zeroed_indices.begin(), it));
1052 return zeroed_indices;
1053 };
1054 auto zero_rows = get_zeroed_indices(
1055 [](auto &s) { return s->uidRow; },
1056 [](auto &s) -> VectorInt & { return s->getRowInd(); });
1057 auto zero_cols = get_zeroed_indices(
1058 [](auto &s) { return s->uidCol; },
1059 [](auto &s) -> VectorInt & { return s->getColInd(); });
1060
1061 for (auto i : zero_rows) {
1062 for (auto j = 0; j != blockMat.size2(); ++j) {
1063 blockMat(i, j) = 0;
1064 }
1065 }
1066 for (auto j : zero_cols) {
1067 for (auto i = 0; i != blockMat.size1(); ++i) {
1068 blockMat(i, j) = 0;
1069 }
1070 }
1071 for (auto i : zero_rows) {
1072 blockMat(i, i) = 1;
1073 }
1074
1075 CHKERR I::invertMat(blockMat, invMat);
1076
1077 // clear storage and block list from a00 blocks, no more needed
1078 for (auto &s : block_list) {
1079 auto it = storage.template get<SchurElemMats::uid_mi_tag>().find(
1080 boost::make_tuple(s->uidRow, s->uidCol));
1081 storage.template get<SchurElemMats::uid_mi_tag>().erase(it);
1082 }
1083 block_list.clear();
1084
1085 if (schurAO) {
1086 for (auto &s : storage) {
1087 if (block_indexing.find(s->uidRow) == block_indexing.end()) {
1088 CHKERR AOApplicationToPetsc(schurAO, s->getRowInd().size(),
1089 &*s->getRowInd().begin());
1090 }
1091 if (block_indexing.find(s->uidCol) == block_indexing.end()) {
1092 CHKERR AOApplicationToPetsc(schurAO, s->getColInd().size(),
1093 &*s->getColInd().begin());
1094 }
1095 }
1096 }
1097
1098 auto schur_block_list = get_schur_block_list(block_indexing);
1099 for (auto &s : schur_block_list) {
1100 auto &m = s->getMat();
1101 CHKERR assemble_schur(m, s->uidRow, s->uidCol, &(s->getRowInd()),
1102 &(s->getColInd()));
1103 }
1104 for (auto &s : schur_block_list) {
1105 auto it = storage.template get<SchurElemMats::uid_mi_tag>().find(
1106 boost::make_tuple(s->uidRow, s->uidCol));
1107 storage.template get<SchurElemMats::uid_mi_tag>().erase(it);
1108 }
1109 schur_block_list.clear();
1110
1111 for (
1112
1113 auto rp_uid_it = a00_uids.begin(); rp_uid_it != a00_uids.end();
1114 ++rp_uid_it
1115
1116 ) {
1117 auto [rlo_uid, rhi_uid] = *rp_uid_it;
1118 for (auto rm = block_indexing.lower_bound(rlo_uid);
1119 rm != block_indexing.upper_bound(rhi_uid); ++rm) {
1120 auto &rbi = rm->second;
1121
1122 auto a_lo_tmp =
1123 storage.template get<SchurElemMats::col_mi_tag>().lower_bound(
1124 rm->first);
1125 auto a_hi =
1126 storage.template get<SchurElemMats::col_mi_tag>().upper_bound(
1127 rm->first);
1128
1129 for (
1130
1131 auto cp_uid_it = (symSchur) ? rp_uid_it : a00_uids.begin();
1132 cp_uid_it != a00_uids.end(); ++cp_uid_it
1133
1134 ) {
1135 auto [clo_uid, chi_uid] = *cp_uid_it;
1136 for (auto cm = block_indexing.lower_bound(clo_uid);
1137 cm != block_indexing.upper_bound(chi_uid); ++cm) {
1138 auto &cbi = cm->second;
1139
1140 auto c_lo_tmp =
1141 storage.template get<SchurElemMats::uid_mi_tag>().lower_bound(
1142 boost::make_tuple(cm->first, 0));
1143 auto c_hi =
1144 storage.template get<SchurElemMats::uid_mi_tag>().upper_bound(
1145 boost::make_tuple(cm->first, FieldEntity::getHiBitNumberUId(
1146 BITFIELDID_SIZE - 1)));
1147
1148 auto sub_inv_mat =
1149 matrix_range(invMat, get_range(rbi), get_range(cbi));
1150 bM.resize(sub_inv_mat.size1(), sub_inv_mat.size2());
1151 noalias(bM) = sub_inv_mat;
1152
1153 for (auto a_lo = a_lo_tmp; a_lo != a_hi; ++a_lo) {
1154#ifndef NDEBUG
1155 if (block_indexing.find((*a_lo)->uidRow) != block_indexing.end())
1156 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1157 "Wrong a_lo->uidRow");
1158#endif
1159
1160 auto &a = (*a_lo)->getMat();
1161 abM.resize(a.size1(), bM.size2(), false);
1162 abM.clear();
1163 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
1164
1165 a.size1(), bM.size2(), a.size2(), 1.,
1166
1167 &*a.data().begin(), a.size2(),
1168
1169 &*bM.data().begin(), bM.size2(), 0.,
1170
1171 &*abM.data().begin(), abM.size2());
1172
1173 for (auto c_lo = c_lo_tmp; c_lo != c_hi; ++c_lo) {
1174#ifndef NDEBUG
1175 if (block_indexing.find((*c_lo)->uidCol) !=
1176 block_indexing.end())
1177 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1178 "Wrong a_lo->uidRow");
1179#endif
1180
1181 auto &c = (*c_lo)->getMat();
1182
1183 abcM.resize(abM.size1(), c.size2(), false);
1184 abcM.clear();
1185
1186 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
1187 abM.size1(), c.size2(), abM.size2(), -1.,
1188
1189 &*abM.data().begin(), abM.size2(),
1190
1191 &*c.data().begin(), c.size2(), 0.,
1192
1193 &*abcM.data().begin(), abcM.size2());
1194
1195 CHKERR assemble_schur(abcM, (*a_lo)->uidRow, (*c_lo)->uidCol,
1196 &((*a_lo)->getRowInd()),
1197 &((*c_lo)->getColInd()));
1198
1199 if (symSchur && rp_uid_it != cp_uid_it) {
1200 abcM = trans(abcM);
1201 CHKERR assemble_schur(abcM, (*c_lo)->uidCol, (*a_lo)->uidRow,
1202 &((*c_lo)->getColInd()),
1203 &((*a_lo)->getRowInd()));
1204 }
1205 }
1206 }
1207 }
1208 }
1209 }
1210 }
1211
1213 };
1214
1215 // Assemble Schur complements
1216 CHKERR assemble_dense_blocks();
1217
1218#ifndef NDEBUG
1219 if constexpr (debug_schur)
1220 MOFEM_LOG("SELF", Sev::noisy) << "Schur assemble done";
1221#endif
1222
1223#ifndef NDEBUG
1224 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_opSchurAssembleEnd, 0, 0, 0, 0);
1225#endif
1226
1228}
1229
1231
1232 static auto invertMat(MatrixDouble &m, MatrixDouble &inv) {
1234
1235 const auto nb = m.size1();
1236#ifndef NDEBUG
1237 if (nb != m.size2()) {
1238 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1239 "It should be square matrix %zu != %zu", nb, m.size2());
1240 }
1241#endif
1242
1243 inv.resize(nb, nb, false);
1244 inv.swap(m);
1245 m.resize(2 * nb, 2 * nb, false);
1246
1247 VectorInt ipiv(nb);
1248 int info;
1249
1250 // FIXME: add lapack_dsytrf and lapack_dsytri
1251
1252 info = lapack_dgetrf(nb, nb, &*inv.data().begin(), nb, &*ipiv.begin());
1253 if (info)
1254 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1255 "lapack error info = %d", info);
1256 info = lapack_dgetri(nb, &*inv.data().begin(), nb, &*ipiv.begin(),
1257 &*m.data().begin(), m.data().size());
1258 if (info)
1259 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1260 "lapack error info = %d", info);
1261
1263 }
1264};
1265
1267
1268 static auto invertMat(MatrixDouble &m, MatrixDouble &inv) {
1270
1271 const auto nb = m.size1();
1272#ifndef NDEBUG
1273 if (nb != m.size2()) {
1274 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1275 "It should be square matrix %zu != %zu", nb, m.size2());
1276 }
1277#endif
1278
1279 inv.resize(nb, nb, false);
1280 inv.swap(m);
1281
1282 VectorInt ipiv(nb);
1283 int info;
1284
1285 info = lapack_dgetrf(nb, nb, &*inv.data().begin(), nb, &*ipiv.begin());
1286 if (info)
1287 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1288 "lapack error info = %d", info);
1289 info = lapack_dgetri(nb, &*inv.data().begin(), nb, &*ipiv.begin(),
1290 &*m.data().begin(), m.data().size());
1291 if (info)
1292 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
1293 "lapack error info = %d", info);
1294
1296 }
1297};
1298
1302 return doWorkImpl<SchurDSYSV>(side, type, data);
1303}
1304
1308 return doWorkImpl<SchurDGESV>(side, type, data);
1309}
1310
1311/**
1312 * @brief Apply in place LU factors of element matrix
1313 *
1314 * Element matrix A is stored row-major, i.e. read column-major by LAPACK it is
1315 * M = A^T = P L U. For trans == false y = A x = U^T L^T P^T x, for trans ==
1316 * true y = A^T x = P L U x. Vector t is overwritten by result.
1317 */
1318static inline void apply_lu_factors(int n, const double *lu,
1319 const __CLPK_integer *ipiv, double *t,
1320 bool trans) {
1321 if (!n)
1322 return;
1323 if (!trans) {
1324 for (int k = 0; k != n; ++k) {
1325 const int p = ipiv[k] - 1;
1326 if (p != k)
1327 std::swap(t[k], t[p]);
1328 }
1329 cblas_dtrmv(CblasColMajor, CblasLower, CblasTrans, CblasUnit, n, lu, n, t,
1330 1);
1331 cblas_dtrmv(CblasColMajor, CblasUpper, CblasTrans, CblasNonUnit, n, lu, n,
1332 t, 1);
1333 } else {
1334 cblas_dtrmv(CblasColMajor, CblasUpper, CblasNoTrans, CblasNonUnit, n, lu, n,
1335 t, 1);
1336 cblas_dtrmv(CblasColMajor, CblasLower, CblasNoTrans, CblasUnit, n, lu, n, t,
1337 1);
1338 for (int k = n - 1; k >= 0; --k) {
1339 const int p = ipiv[k] - 1;
1340 if (p != k)
1341 std::swap(t[k], t[p]);
1342 }
1343 }
1344}
1345
1346/// copy row-major block into row-major matrix at (ridx, cidx)
1347static inline void copy_block(double *dst, int ld_dst, int ridx, int cidx,
1348 const double *src, int ld_src, int nb_rows,
1349 int nb_cols) {
1350 for (int i = 0; i != nb_rows; ++i)
1351 cblas_dcopy(nb_cols, src + static_cast<size_t>(i) * ld_src, 1,
1352 dst + static_cast<size_t>(ridx + i) * ld_dst + cidx, 1);
1353}
1354
1355static inline bool is_zero_matrix(const double *ptr, size_t size) {
1356 return std::all_of(ptr, ptr + size, [](double v) { return v == 0.; });
1357}
1358
1359extern "C" {
1361 __CLPK_integer *lda, __CLPK_doublereal *anorm,
1363 __CLPK_integer *iwork, __CLPK_integer *info);
1364}
1365
1366/// 1-norm of column-major m x n block, infinity if block is not finite
1367static inline double norm1_block(int m, int n, const double *a, int ld) {
1368 double r = 0;
1369 for (int j = 0; j != n; ++j) {
1370 // note that std::max ignores NaN as second argument
1371 const double s = cblas_dasum(m, a + static_cast<size_t>(j) * ld, 1);
1372 if (!std::isfinite(s))
1373 return std::numeric_limits<double>::infinity();
1374 r = std::max(r, s);
1375 }
1376 return r;
1377}
1378
1379/// y += op(A) x, op(A) is m x kk, A is column-major
1380static inline void add_gemv(bool trans_a, int m, int kk, const double *a,
1381 int lda, const double *x, double *y) {
1382 if (!m || !kk)
1383 return;
1384 cblas_dgemv(CblasColMajor, trans_a ? CblasTrans : CblasNoTrans,
1385 trans_a ? kk : m, trans_a ? m : kk, 1., a, lda, x, 1, 1., y, 1);
1386}
1387
1388/// C -= op(A) B, op(A) is m x kk, A, B and C are column-major
1389static inline void sub_gemm(bool trans_a, int m, int kk, int nrhs,
1390 const double *a, int lda, const double *b, int ldb,
1391 double *c, int ldc) {
1392 if (!m || !kk || !nrhs)
1393 return;
1394 if (nrhs == 1)
1395 cblas_dgemv(CblasColMajor, trans_a ? CblasTrans : CblasNoTrans,
1396 trans_a ? kk : m, trans_a ? m : kk, -1., a, lda, b, 1, 1., c,
1397 1);
1398 else
1399 cblas_dgemm(CblasColMajor, trans_a ? CblasTrans : CblasNoTrans,
1400 CblasNoTrans, m, nrhs, kk, -1., a, lda, b, ldb, 1., c, ldc);
1401}
1402
1403/**
1404 * @brief Factorise diagonal block in place and test it as block pivot
1405 *
1406 * Block is admissible pivot if it is not singular and its reciprocal condition
1407 * number (1-norm estimate) is not below rcond_min. If u > 0, its smallest
1408 * singular value (estimated as rcond * norm) has also to be not below u times
1409 * the largest norm of its couplings with nodes not yet eliminated (block
1410 * threshold pivoting). That test is off by default, since it rejects mass
1411 * blocks of hierarchical bases, which are ill-conditioned but safe pivots.
1412 * Stability is verified by backward error, see elem_backward_error.
1413 */
1414static bool factorise_pivot_block(int n, double *d, int ld, __CLPK_integer *piv,
1415 double coupling, double u, double rcond_min,
1416 std::vector<double> &work,
1417 std::vector<__CLPK_integer> &iwork,
1418 double *rcond_out = nullptr,
1419 double *anorm_out = nullptr) {
1420 const double anorm = norm1_block(n, n, d, ld);
1421 double rcond = 0;
1422 if (rcond_out)
1423 *rcond_out = rcond;
1424 if (anorm_out)
1425 *anorm_out = anorm;
1426 if (anorm == 0 || !std::isfinite(anorm))
1427 return false;
1428 if (lapack_dgetrf(n, n, d, ld, piv) != 0)
1429 return false;
1430 work.resize(4 * n);
1431 iwork.resize(n);
1432 char norm = '1';
1433 __CLPK_integer nn = n, lda = ld, info = 0;
1434 double an = anorm;
1435 dgecon_(&norm, &nn, d, &lda, &an, &rcond, work.data(), iwork.data(), &info);
1436 if (rcond_out)
1437 *rcond_out = rcond;
1438 if (info != 0 || !std::isfinite(rcond))
1439 return false;
1440 if (rcond < rcond_min)
1441 return false;
1442 if (u > 0 && rcond * anorm < u * coupling)
1443 return false;
1444 return true;
1445}
1446
1448
1449/// offsets of nodes in element vector in order of nodes
1450static std::vector<int> natural_offsets(const BlockPattern &p) {
1451 std::vector<int> off(p.nbNodes);
1452 int o = 0;
1453 for (int r = 0; r != p.nbNodes; ++r) {
1454 off[r] = o;
1455 o += p.nodeSize[r];
1456 }
1457 return off;
1458}
1459
1460/// doubles in listed blocks of one element
1461static size_t listed_size(const BlockPattern &p) {
1462 size_t s = 0;
1463 for (int r = 0; r != p.nbNodes; ++r)
1464 for (int c = 0; c != p.nbNodes; ++c)
1465 if (p.isListed(r, c))
1466 s += static_cast<size_t>(p.nodeSize[r]) * p.nodeSize[c];
1467 return s;
1468}
1469
1470/// layout with listed blocks only
1472 const int k = p.nbNodes;
1474 p.order.clear();
1475 p.core.clear();
1477 p.slotShift.assign(k * k, -1);
1478 p.slotLd.assign(k * k, 0);
1479 size_t shift = 0;
1480 for (int r = 0; r != k; ++r)
1481 for (int c = 0; c != k; ++c)
1482 if (p.isListed(r, c)) {
1483 p.slotShift[r * k + c] = shift;
1484 p.slotLd[r * k + c] = p.nodeSize[c];
1485 shift += static_cast<size_t>(p.nodeSize[r]) * p.nodeSize[c];
1486 }
1487 p.coreShift = shift;
1488 p.coreSize = 0;
1489 p.coreOffset = p.size;
1490 p.elemSize = shift;
1491}
1492
1493/**
1494 * @brief Blocks of M = A^T present after eliminating nodes in given order
1495 *
1496 * @return nbNodes x nbNodes flags, (a, b) is block a, b of M
1497 */
1498static std::vector<char> symbolic_elimination(const BlockPattern &p,
1499 const std::vector<int> &order) {
1500 const int k = p.nbNodes;
1501 std::vector<char> pm(k * k, 0);
1502 for (int r = 0; r != k; ++r)
1503 for (int c = 0; c != k; ++c)
1504 pm[c * k + r] = p.isListed(r, c);
1505 std::vector<char> done(k, 0);
1506 for (auto e : order) {
1507 done[e] = 1;
1508 for (int i = 0; i != k; ++i)
1509 if (!done[i] && pm[i * k + e])
1510 for (int j = 0; j != k; ++j)
1511 if (!done[j] && pm[e * k + j])
1512 pm[i * k + j] = 1;
1513 }
1514 return pm;
1515}
1516
1517/**
1518 * @brief Layout for block elimination of nodes in order, followed by dense
1519 * core of remaining nodes
1520 *
1521 * Element vector is ordered as eliminated nodes in order, then core nodes.
1522 * Slots with at least one eliminated node, listed or fill, are contiguous with
1523 * leading dimension equal to number of columns. Slots with both nodes in core
1524 * are parts of dense core, which is (core size)^2 matrix. With empty order
1525 * the element matrix is dense.
1526 */
1527static void make_block_layout(BlockPattern &p, const std::vector<int> &order) {
1528 const int k = p.nbNodes;
1529 auto pm = symbolic_elimination(p, order);
1530 std::vector<char> in_core(k, 1);
1531 for (auto e : order)
1532 in_core[e] = 0;
1533 p.order = order;
1534 p.core.clear();
1535 for (int i = 0; i != k; ++i)
1536 if (in_core[i])
1537 p.core.push_back(i);
1538
1539 p.nodeOffset.assign(k, 0);
1540 std::vector<int> pos(k);
1541 int off = 0, t = 0;
1542 for (auto e : p.order) {
1543 p.nodeOffset[e] = off;
1544 off += p.nodeSize[e];
1545 pos[e] = t++;
1546 }
1547 p.coreOffset = off;
1548 for (auto c : p.core) {
1549 p.nodeOffset[c] = off;
1550 off += p.nodeSize[c];
1551 pos[c] = t++;
1552 }
1553 p.coreSize = off - p.coreOffset;
1554
1555 // slots with eliminated node, ordered by elimination
1556 std::vector<std::array<int, 4>> slots;
1557 for (int r = 0; r != k; ++r)
1558 for (int c = 0; c != k; ++c)
1559 if (!(in_core[r] && in_core[c]) && pm[c * k + r])
1560 slots.push_back({std::min(pos[r], pos[c]), pos[r], pos[c], r * k + c});
1561 std::sort(slots.begin(), slots.end());
1562
1563 p.slotShift.assign(k * k, -1);
1564 p.slotLd.assign(k * k, 0);
1565 size_t shift = 0;
1566 for (auto &s : slots) {
1567 const int r = s[3] / k, c = s[3] % k;
1568 p.slotShift[s[3]] = shift;
1569 p.slotLd[s[3]] = p.nodeSize[c];
1570 shift += static_cast<size_t>(p.nodeSize[r]) * p.nodeSize[c];
1571 }
1572 p.coreShift = shift;
1573 for (auto r : p.core)
1574 for (auto c : p.core) {
1575 p.slotShift[r * k + c] =
1576 p.coreShift +
1577 static_cast<size_t>(p.nodeOffset[r] - p.coreOffset) * p.coreSize +
1578 (p.nodeOffset[c] - p.coreOffset);
1579 p.slotLd[r * k + c] = p.coreSize;
1580 }
1581 p.elemSize = p.coreShift + static_cast<size_t>(p.coreSize) * p.coreSize;
1582 p.state = order.empty() ? BlockPattern::DENSE : BlockPattern::BLOCK;
1583}
1584
1585/// elimination order by structure only (minimal fill, diagonal present)
1586static std::vector<int> structural_order(const BlockPattern &p) {
1587 const int k = p.nbNodes;
1588 auto pm = symbolic_elimination(p, {});
1589 std::vector<char> rem(k, 1);
1590 std::vector<int> order;
1591 while (true) {
1592 int best = -1;
1593 double best_fill = 0;
1594 for (int e = 0; e != k; ++e) {
1595 if (!rem[e] || !pm[e * k + e])
1596 continue;
1597 double fill = 0;
1598 for (int i = 0; i != k; ++i)
1599 if (rem[i] && i != e && pm[i * k + e])
1600 for (int j = 0; j != k; ++j)
1601 if (rem[j] && j != e && pm[e * k + j] && !pm[i * k + j])
1602 fill += static_cast<double>(p.nodeSize[i]) * p.nodeSize[j];
1603 if (best < 0 || fill < best_fill) {
1604 best = e;
1605 best_fill = fill;
1606 }
1607 }
1608 if (best < 0)
1609 break;
1610 for (int i = 0; i != k; ++i)
1611 if (rem[i] && i != best && pm[i * k + best])
1612 for (int j = 0; j != k; ++j)
1613 if (rem[j] && j != best && pm[best * k + j])
1614 pm[i * k + j] = 1;
1615 rem[best] = 0;
1616 order.push_back(best);
1617 }
1618 return order;
1619}
1620
1621/**
1622 * @brief Decide elimination order on probe element
1623 *
1624 * Greedy block elimination: among nodes whose updated diagonal block passes
1625 * the pivot test (see factorise_pivot_block), the node creating the least
1626 * fill (weighted by block sizes) is eliminated next. Nodes never admissible
1627 * form the dense core.
1628 *
1629 * @param w dense element matrix, row-major A (column-major M), in natural
1630 * node offsets, overwritten
1631 */
1632static std::vector<int> analyse_pattern(const BlockPattern &p, double *w,
1633 double u, double rcond_min) {
1634 const int k = p.nbNodes, n = p.size;
1635 auto off = natural_offsets(p);
1636 auto &ns = p.nodeSize;
1637 // block (a, b) of M in w, leading dimension n
1638 auto mb = [&](int a, int b) {
1639 return w + static_cast<size_t>(off[b]) * n + off[a];
1640 };
1641 auto pm = symbolic_elimination(p, {});
1642 std::vector<char> rem(k, 1);
1643 std::vector<int> order;
1644 std::vector<double> tmp, work;
1645 std::vector<__CLPK_integer> piv, iwork;
1646
1647 while (true) {
1648 int best = -1;
1649 double best_fill = 0;
1650 for (int e = 0; e != k; ++e) {
1651 if (!rem[e] || !pm[e * k + e])
1652 continue;
1653 const int ne = ns[e];
1654 double coupling = 0;
1655 for (int j = 0; j != k; ++j) {
1656 if (!rem[j] || j == e)
1657 continue;
1658 if (pm[e * k + j])
1659 coupling = std::max(coupling, norm1_block(ne, ns[j], mb(e, j), n));
1660 if (pm[j * k + e])
1661 coupling = std::max(coupling, norm1_block(ns[j], ne, mb(j, e), n));
1662 }
1663 tmp.resize(static_cast<size_t>(ne) * ne);
1664 for (int c = 0; c != ne; ++c)
1665 cblas_dcopy(ne, mb(e, e) + static_cast<size_t>(c) * n, 1,
1666 tmp.data() + static_cast<size_t>(c) * ne, 1);
1667 piv.resize(ne);
1668 double rcond, anorm;
1669 const bool admissible =
1670 factorise_pivot_block(ne, tmp.data(), ne, piv.data(), coupling, u,
1671 rcond_min, work, iwork, &rcond, &anorm);
1672 MOFEM_TAG_AND_LOG("SELF", Sev::noisy, "BlockMat")
1673 << "A00 analysis step " << order.size() << " node " << p.nodeName[e]
1674 << "(" << ne << ") rcond " << rcond << " smin " << rcond * anorm
1675 << " norm " << anorm << " coupling " << coupling
1676 << (admissible ? " admissible" : " rejected");
1677 if (!admissible)
1678 continue;
1679 double fill = 0;
1680 for (int i = 0; i != k; ++i)
1681 if (rem[i] && i != e && pm[i * k + e])
1682 for (int j = 0; j != k; ++j)
1683 if (rem[j] && j != e && pm[e * k + j] && !pm[i * k + j])
1684 fill += static_cast<double>(ns[i]) * ns[j];
1685 if (best < 0 || fill < best_fill) {
1686 best = e;
1687 best_fill = fill;
1688 }
1689 }
1690 if (best < 0)
1691 break;
1692
1693 const int e = best, ne = ns[e];
1694 piv.resize(ne);
1695 lapack_dgetrf(ne, ne, mb(e, e), n, piv.data());
1696 for (int j = 0; j != k; ++j)
1697 if (rem[j] && j != e && pm[e * k + j])
1698 lapack_dgetrs('N', ne, ns[j], mb(e, e), n, piv.data(), mb(e, j), n);
1699 for (int i = 0; i != k; ++i)
1700 if (rem[i] && i != e && pm[i * k + e])
1701 for (int j = 0; j != k; ++j)
1702 if (rem[j] && j != e && pm[e * k + j]) {
1703 cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, ns[i], ns[j],
1704 ne, -1., mb(i, e), n, mb(e, j), n, 1., mb(i, j), n);
1705 pm[i * k + j] = 1;
1706 }
1707 rem[e] = 0;
1708 order.push_back(e);
1709 }
1710 return order;
1711}
1712
1713/// copy listed blocks of element buffer from one layout to another
1714static void copy_listed(const BlockPattern &from, const double *src,
1715 const BlockPattern &to, double *dst) {
1716 for (int r = 0; r != from.nbNodes; ++r)
1717 for (int c = 0; c != from.nbNodes; ++c)
1718 if (from.isListed(r, c))
1719 copy_block(dst + to.sShift(r, c), to.sLd(r, c), 0, 0,
1720 src + from.sShift(r, c), from.sLd(r, c), from.nodeSize[r],
1721 from.nodeSize[c]);
1722}
1723
1724/// dense element matrix (row-major A, size x size) from listed blocks
1725static void elem_gather_dense(const BlockPattern &p, const double *buf,
1726 double *dense) {
1727 const int n = p.size;
1728 for (int r = 0; r != p.nbNodes; ++r)
1729 for (int c = 0; c != p.nbNodes; ++c)
1730 if (p.isListed(r, c))
1731 copy_block(dense, n, p.nodeOffset[r], p.nodeOffset[c],
1732 buf + p.sShift(r, c), p.sLd(r, c), p.nodeSize[r],
1733 p.nodeSize[c]);
1734}
1735
1736/// y += A x (trans false) or y += A^T x (trans true) from listed blocks
1737static void elem_mult_listed(const BlockPattern &p, const double *buf,
1738 const double *x, double *y, bool trans) {
1739 for (int r = 0; r != p.nbNodes; ++r)
1740 for (int c = 0; c != p.nbNodes; ++c)
1741 if (p.isListed(r, c)) {
1742 const int nr = p.nodeSize[r], nc = p.nodeSize[c];
1743 const double *a = buf + p.sShift(r, c);
1744 if (!trans)
1745 cblas_dgemv(CblasRowMajor, CblasNoTrans, nr, nc, 1., a, p.sLd(r, c),
1746 x + p.nodeOffset[c], 1, 1., y + p.nodeOffset[r], 1);
1747 else
1748 cblas_dgemv(CblasRowMajor, CblasTrans, nr, nc, 1., a, p.sLd(r, c),
1749 x + p.nodeOffset[r], 1, 1., y + p.nodeOffset[c], 1);
1750 }
1751}
1752
1753/**
1754 * @brief Factorise element buffer in place
1755 *
1756 * Block LU of M = A^T: for each node e in order, D_e = M_ee is factorised and
1757 * tested as pivot, rows M_ej are overwritten by D_e^{-1} M_ej, and M_ij -=
1758 * M_ie D_e^{-1} M_ej for remaining nodes i, j. Columns M_ie are kept. Then
1759 * dense core is factorised with partial pivoting. M = L U, L block lower with
1760 * diagonal D, U block upper with unit diagonal.
1761 *
1762 * @return 0 on success, 1 if pivot test failed, 2 if core is singular
1763 */
1764static int elem_factorise(const BlockPattern &p, double *buf,
1765 __CLPK_integer *piv, double u, double rcond_min,
1766 std::vector<double> &work,
1767 std::vector<__CLPK_integer> &iwork) {
1768 const int m = p.order.size();
1769 std::vector<int> seq(p.order);
1770 seq.insert(seq.end(), p.core.begin(), p.core.end());
1771 auto &ns = p.nodeSize;
1772
1773 for (int t = 0; t != m; ++t) {
1774 const int e = seq[t], ne = ns[e];
1775 double *d = buf + p.mShift(e, e);
1776 const int ldd = p.mLd(e, e);
1777
1778 double coupling = 0;
1779 for (int q = t + 1; u > 0 && q < static_cast<int>(seq.size()); ++q) {
1780 const int j = seq[q];
1781 if (p.mShift(e, j) >= 0)
1782 coupling =
1783 std::max(coupling,
1784 norm1_block(ne, ns[j], buf + p.mShift(e, j), p.mLd(e, j)));
1785 if (p.mShift(j, e) >= 0)
1786 coupling =
1787 std::max(coupling,
1788 norm1_block(ns[j], ne, buf + p.mShift(j, e), p.mLd(j, e)));
1789 }
1790 if (!factorise_pivot_block(ne, d, ldd, piv + p.nodeOffset[e], coupling, u,
1791 rcond_min, work, iwork))
1792 return 1;
1793
1794 for (int q = t + 1; q < static_cast<int>(seq.size()); ++q) {
1795 const int j = seq[q];
1796 if (p.mShift(e, j) >= 0)
1797 lapack_dgetrs('N', ne, ns[j], d, ldd, piv + p.nodeOffset[e],
1798 buf + p.mShift(e, j), p.mLd(e, j));
1799 }
1800 for (int qi = t + 1; qi < static_cast<int>(seq.size()); ++qi) {
1801 const int i = seq[qi];
1802 if (p.mShift(i, e) < 0)
1803 continue;
1804 for (int qj = t + 1; qj < static_cast<int>(seq.size()); ++qj) {
1805 const int j = seq[qj];
1806 if (p.mShift(e, j) < 0)
1807 continue;
1808 cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, ns[i], ns[j], ne,
1809 -1., buf + p.mShift(i, e), p.mLd(i, e),
1810 buf + p.mShift(e, j), p.mLd(e, j), 1., buf + p.mShift(i, j),
1811 p.mLd(i, j));
1812 }
1813 }
1814 }
1815
1816 if (p.coreSize) {
1817 if (lapack_dgetrf(p.coreSize, p.coreSize, buf + p.coreShift, p.coreSize,
1818 piv + p.coreOffset) != 0)
1819 return 2;
1820 }
1821 return 0;
1822}
1823
1824/**
1825 * @brief Solve with factorised element buffer
1826 *
1827 * trans false solves A X = R, i.e. M^T X = R = U^T L^T X, trans true solves
1828 * A^T X = R, i.e. M X = R = L U X. R is column-major (size x nrhs), with rows
1829 * in element vector layout, overwritten by X.
1830 *
1831 * @param nz_in optional flags of nodes with non-zero rows of R, nodes with
1832 * zero rows are skipped where possible
1833 */
1834static MoFEMErrorCode elem_solve(const BlockPattern &p, const double *buf,
1835 const __CLPK_integer *piv, double *r, int ldr,
1836 int nrhs, bool trans,
1837 const std::vector<char> *nz_in) {
1839 const int k = p.nbNodes, m = p.order.size();
1840 std::vector<int> seq(p.order);
1841 seq.insert(seq.end(), p.core.begin(), p.core.end());
1842 auto &ns = p.nodeSize;
1843 auto &off = p.nodeOffset;
1844 std::vector<char> nz = nz_in ? *nz_in : std::vector<char>(k, 1);
1845 auto blk = [&](int a, int b) { return buf + p.mShift(a, b); };
1846 auto rr = [&](int a) { return r + off[a]; };
1847 auto lu = [&](int e) { return const_cast<double *>(blk(e, e)); };
1848 auto pv = [&](int e) { return const_cast<__CLPK_integer *>(piv + off[e]); };
1849 auto core_solve = [&](char t) {
1851 if (!p.coreSize)
1853 bool any = false;
1854 for (auto c : p.core)
1855 any = any || nz[c];
1856 if (!any)
1858 auto info = lapack_dgetrs(
1859 t, p.coreSize, nrhs, const_cast<double *>(buf + p.coreShift),
1860 p.coreSize, const_cast<__CLPK_integer *>(piv + p.coreOffset),
1861 r + p.coreOffset, ldr);
1862 if (info)
1863 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
1864 "error lapack dgetrs info = %d", static_cast<int>(info));
1865 for (auto c : p.core)
1866 nz[c] = 1;
1868 };
1869 auto diag_solve = [&](int e, char t) {
1871 if (!nz[e])
1873 auto info =
1874 lapack_dgetrs(t, ns[e], nrhs, lu(e), p.mLd(e, e), pv(e), rr(e), ldr);
1875 if (info)
1876 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
1877 "error lapack dgetrs info = %d", static_cast<int>(info));
1879 };
1880
1881 if (!trans) {
1882 // forward with U^T (unit diagonal)
1883 for (int t = 0; t != static_cast<int>(seq.size()); ++t) {
1884 const int e = seq[t];
1885 for (int q = 0; q != std::min(t, m); ++q) {
1886 const int j = seq[q];
1887 if (nz[j] && p.mShift(j, e) >= 0) {
1888 sub_gemm(true, ns[e], ns[j], nrhs, blk(j, e), p.mLd(j, e), rr(j), ldr,
1889 rr(e), ldr);
1890 nz[e] = 1;
1891 }
1892 }
1893 }
1894 // backward with L^T
1895 CHKERR core_solve('T');
1896 for (int t = m - 1; t >= 0; --t) {
1897 const int e = seq[t];
1898 for (int q = t + 1; q != static_cast<int>(seq.size()); ++q) {
1899 const int i = seq[q];
1900 if (nz[i] && p.mShift(i, e) >= 0) {
1901 sub_gemm(true, ns[e], ns[i], nrhs, blk(i, e), p.mLd(i, e), rr(i), ldr,
1902 rr(e), ldr);
1903 nz[e] = 1;
1904 }
1905 }
1906 CHKERR diag_solve(e, 'T');
1907 }
1908 } else {
1909 // forward with L
1910 for (int t = 0; t != static_cast<int>(seq.size()); ++t) {
1911 const int e = seq[t];
1912 for (int q = 0; q != std::min(t, m); ++q) {
1913 const int i = seq[q];
1914 if (nz[i] && p.mShift(e, i) >= 0) {
1915 sub_gemm(false, ns[e], ns[i], nrhs, blk(e, i), p.mLd(e, i), rr(i),
1916 ldr, rr(e), ldr);
1917 nz[e] = 1;
1918 }
1919 }
1920 if (t < m)
1921 CHKERR diag_solve(e, 'N');
1922 }
1923 CHKERR core_solve('N');
1924 // backward with U (unit diagonal)
1925 for (int t = m - 1; t >= 0; --t) {
1926 const int e = seq[t];
1927 for (int q = t + 1; q != static_cast<int>(seq.size()); ++q) {
1928 const int j = seq[q];
1929 if (nz[j] && p.mShift(e, j) >= 0) {
1930 sub_gemm(false, ns[e], ns[j], nrhs, blk(e, j), p.mLd(e, j), rr(j),
1931 ldr, rr(e), ldr);
1932 nz[e] = 1;
1933 }
1934 }
1935 }
1936 }
1938}
1939
1940/**
1941 * @brief Product with factorised element buffer, in place
1942 *
1943 * trans false computes A v = M^T v = U^T (L^T v), trans true computes A^T v =
1944 * M v = L (U v). Every stored block is used once.
1945 */
1946static void elem_mult_factors(const BlockPattern &p, const double *buf,
1947 const __CLPK_integer *piv, double *v,
1948 bool trans) {
1949 const int m = p.order.size();
1950 std::vector<int> seq(p.order);
1951 seq.insert(seq.end(), p.core.begin(), p.core.end());
1952 const int nseq = seq.size();
1953 auto &ns = p.nodeSize;
1954 auto &off = p.nodeOffset;
1955 auto blk = [&](int a, int b) { return buf + p.mShift(a, b); };
1956 auto vv = [&](int a) { return v + off[a]; };
1957
1958 if (!trans) {
1959 // t = L^T v
1960 for (int t = 0; t != m; ++t) {
1961 const int e = seq[t];
1962 apply_lu_factors(ns[e], blk(e, e), piv + off[e], vv(e), false);
1963 for (int q = t + 1; q != nseq; ++q) {
1964 const int i = seq[q];
1965 if (p.mShift(i, e) >= 0)
1966 add_gemv(true, ns[e], ns[i], blk(i, e), p.mLd(i, e), vv(i), vv(e));
1967 }
1968 }
1969 apply_lu_factors(p.coreSize, buf + p.coreShift, piv + p.coreOffset,
1970 v + p.coreOffset, false);
1971 // y = U^T t
1972 for (int t = nseq - 1; t >= 0; --t) {
1973 const int e = seq[t];
1974 for (int q = 0; q != std::min(t, m); ++q) {
1975 const int j = seq[q];
1976 if (p.mShift(j, e) >= 0)
1977 add_gemv(true, ns[e], ns[j], blk(j, e), p.mLd(j, e), vv(j), vv(e));
1978 }
1979 }
1980 } else {
1981 // t = U v
1982 for (int t = 0; t != m; ++t) {
1983 const int e = seq[t];
1984 for (int q = t + 1; q != nseq; ++q) {
1985 const int j = seq[q];
1986 if (p.mShift(e, j) >= 0)
1987 add_gemv(false, ns[e], ns[j], blk(e, j), p.mLd(e, j), vv(j), vv(e));
1988 }
1989 }
1990 // y = L t
1991 apply_lu_factors(p.coreSize, buf + p.coreShift, piv + p.coreOffset,
1992 v + p.coreOffset, true);
1993 for (int t = nseq - 1; t >= 0; --t) {
1994 const int e = seq[t];
1995 if (t < m)
1996 apply_lu_factors(ns[e], blk(e, e), piv + off[e], vv(e), true);
1997 for (int q = 0; q != std::min(t, m); ++q) {
1998 const int i = seq[q];
1999 if (p.mShift(e, i) >= 0)
2000 add_gemv(false, ns[e], ns[i], blk(e, i), p.mLd(e, i), vv(i), vv(e));
2001 }
2002 }
2003 }
2004}
2005
2006/// factor of dense backward error accepted for block factors
2007constexpr double backward_error_factor = 100;
2008
2009/// right-hand side used to measure backward error
2010static inline double backward_error_rhs(int i) {
2011 return 1. + 0.5 * std::sin(1. + i);
2012}
2013
2014/**
2015 * @brief Normwise backward error of solve with factors of element
2016 *
2017 * ||A x - b||_inf / (||A||_inf ||x||_inf + ||b||_inf), where x is obtained
2018 * with factors lu, and A x is computed from listed blocks of orig (matrix
2019 * before factorisation, in the same layout). Cost is a few passes over element
2020 * buffer.
2021 */
2022static double elem_backward_error(const BlockPattern &p, const double *orig,
2023 const double *lu, const __CLPK_integer *piv,
2024 std::vector<double> &x,
2025 std::vector<double> &r,
2026 std::vector<double> &rs) {
2027 const int n = p.size;
2028 x.resize(n);
2029 r.resize(n);
2030 rs.assign(n, 0.);
2031 double b_norm = 0;
2032 for (int i = 0; i != n; ++i) {
2033 x[i] = backward_error_rhs(i);
2034 r[i] = -x[i];
2035 b_norm = std::max(b_norm, std::abs(x[i]));
2036 }
2037 constexpr double inf = std::numeric_limits<double>::infinity();
2038 if (elem_solve(p, lu, piv, x.data(), n, 1, false, nullptr))
2039 return inf;
2040 // note that std::max ignores NaN as second argument, thus non-finite values
2041 // are rejected explicitly
2042 for (int i = 0; i != n; ++i)
2043 if (!std::isfinite(x[i]))
2044 return inf;
2045 elem_mult_listed(p, orig, x.data(), r.data(), false);
2046 // row sums of |A|
2047 for (int a = 0; a != p.nbNodes; ++a)
2048 for (int c = 0; c != p.nbNodes; ++c)
2049 if (p.isListed(a, c)) {
2050 const double *blk = orig + p.sShift(a, c);
2051 for (int i = 0; i != p.nodeSize[a]; ++i)
2052 rs[p.nodeOffset[a] + i] += cblas_dasum(
2053 p.nodeSize[c], blk + static_cast<size_t>(i) * p.sLd(a, c), 1);
2054 }
2055 double r_norm = 0, x_norm = 0, a_norm = 0;
2056 for (int i = 0; i != n; ++i) {
2057 if (!std::isfinite(r[i]) || !std::isfinite(rs[i]))
2058 return inf;
2059 r_norm = std::max(r_norm, std::abs(r[i]));
2060 x_norm = std::max(x_norm, std::abs(x[i]));
2061 a_norm = std::max(a_norm, rs[i]);
2062 }
2063 const double den = a_norm * x_norm + b_norm;
2064 if (!std::isfinite(den) || !(den > 0))
2065 return inf;
2066 const double be = r_norm / den;
2067 return std::isfinite(be) ? be : inf;
2068}
2069
2070/// backward error accepted for block factors, given dense backward error
2071static inline double accepted_backward_error(double be_dense, double be_tol) {
2072 return std::isfinite(be_dense)
2073 ? std::max(backward_error_factor * be_dense, be_tol)
2074 : be_tol;
2075}
2076
2077/// true if backward error is finite and not larger than accepted
2078static inline bool backward_error_accepted(double be, double be_dense,
2079 double be_tol) {
2080 return std::isfinite(be) && be <= accepted_backward_error(be_dense, be_tol);
2081}
2082
2083/**
2084 * @brief Verify block layout on probe element
2085 *
2086 * Normwise backward errors of a solve with block factors and with dense
2087 * factors are compared. Block layout is accepted if its backward error is
2088 * finite and not larger than backward_error_factor times dense backward error
2089 * (if that is finite), or than be_tol.
2090 *
2091 * @param mat matrix of probe in layout nl (not factorised)
2092 */
2093static bool verify_probe(const BlockPattern &nl, const std::vector<double> &mat,
2094 double u, double rcond_min, double be_tol,
2095 double &be_block, double &be_dense) {
2096 const int n = nl.size;
2097 std::vector<double> x, r, rs, f, work;
2098 std::vector<__CLPK_integer> piv(n), iwork;
2099
2100 // dense factors of probe, as element with dense layout
2101 BlockPattern dl = nl;
2102 make_block_layout(dl, {});
2103 std::vector<double> dense(dl.elemSize, 0.);
2104 copy_listed(nl, mat.data(), dl, dense.data());
2105 f = dense;
2106 be_dense = std::numeric_limits<double>::infinity();
2107 if (elem_factorise(dl, f.data(), piv.data(), u, rcond_min, work, iwork) == 0)
2108 be_dense =
2109 elem_backward_error(dl, dense.data(), f.data(), piv.data(), x, r, rs);
2110
2111 be_block = std::numeric_limits<double>::infinity();
2112 f = mat;
2113 if (elem_factorise(nl, f.data(), piv.data(), u, rcond_min, work, iwork))
2114 return false;
2115 be_block =
2116 elem_backward_error(nl, mat.data(), f.data(), piv.data(), x, r, rs);
2117 return backward_error_accepted(be_block, be_dense, be_tol);
2118}
2119
2120static std::string describe_nodes(const BlockPattern &p,
2121 const std::vector<int> &nodes) {
2122 std::string s;
2123 for (auto r : nodes) {
2124 if (!s.empty())
2125 s += " ";
2126 s += p.nodeName[r] + "(" + std::to_string(p.nodeSize[r]) + ")";
2127 }
2128 return s.empty() ? std::string("none") : s;
2129}
2130
2131/**
2132 * @brief Relayout element buffers of pattern pi, element by element, in place
2133 *
2134 * Element buffers have capacity of dense element matrix (reserved, pages not
2135 * used are not touched), which is upper bound of any layout. Buffers are
2136 * relaid within their capacity through a copy of one element, so nothing is
2137 * reallocated or freed. Freed buffers would be kept resident by allocators.
2138 */
2139static void relayout_pattern(BlockStorage &s, int pi, BlockPattern nl) {
2140 auto &p = s.patterns[pi];
2141 const int k = p.nbNodes;
2142 std::vector<double> scratch;
2143 for (int ei = 0; ei != static_cast<int>(s.elems.size()); ++ei) {
2144 auto &e = s.elems[ei];
2145 if (e.pattern != pi)
2146 continue;
2147 auto relayout_buf = [&](std::vector<double> &buf) {
2148 scratch.assign(buf.begin(), buf.end());
2149 if (nl.elemSize > buf.capacity())
2150 buf.reserve(static_cast<size_t>(nl.size) * nl.size);
2151 buf.resize(nl.elemSize);
2152 std::fill(buf.begin(), buf.end(), 0.);
2153 copy_listed(p, scratch.data(), nl, buf.data());
2154 };
2155 relayout_buf(s.elemData[ei]);
2156 if (s.hasPreconditioner)
2157 relayout_buf(s.elemPrec[ei]);
2158 for (int r = 0; r != k; ++r)
2159 for (int c = 0; c != k; ++c)
2160 if (p.isListed(r, c)) {
2161 auto idx = s.findMutable(s.elemEntities[e.entBegin + r].uid,
2162 s.elemEntities[e.entBegin + c].uid);
2163 idx->setStorage(nl.sShift(r, c), nl.sLd(r, c), ei);
2164 }
2165 for (int r = 0; r != k; ++r)
2166 s.elemEntities[e.entBegin + r].offset = nl.nodeOffset[r];
2167 }
2168 p = nl;
2169 s.maxElemBuffer = 0;
2170 for (auto &q : s.patterns)
2171 s.maxElemBuffer = std::max(s.maxElemBuffer, q.elemSize);
2172}
2173
2174/**
2175 * @brief Factorise A00 elements in place
2176 *
2177 * Factors are stored in element buffers (operator A00), or in preconditioner
2178 * buffers (operator A00 + preconditioner). In the second case A00 blocks are
2179 * intact. Patterns in listed layout are analysed first, see analyse_pattern.
2180 */
2184 CHKERR storage.checkNotFailed();
2185 if (storage.isFactorised()) {
2186 if (storage.factorStorage == where)
2188 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
2189 "A00 element matrices are already factorised in other storage");
2190 }
2191 const bool in_prec = where == BlockStorage::FACTOR_IN_PRECONDITIONER;
2192 if (in_prec && !storage.hasPreconditioner)
2193 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
2194 "No preconditioner blocks");
2195
2196 const double u = storage.optPivotThreshold;
2197 const double rcond_min = storage.optRcondMin;
2198
2199 // matrix which is factorised
2200 auto get_matrix = [&](int ei, std::vector<double> &out) {
2201 out = storage.elemData[ei];
2202 if (in_prec)
2203 cblas_daxpy(out.size(), 1., storage.elemPrec[ei].data(), 1, out.data(),
2204 1);
2205 };
2206
2207 std::vector<double> mat, w;
2208 for (int pi = 0; pi != static_cast<int>(storage.patterns.size()); ++pi) {
2209 auto &p = storage.patterns[pi];
2210 if (p.state != BlockPattern::LISTED)
2211 continue;
2212
2213 // probe is first element with non-zero matrix, owned if possible
2214 int probe = -1;
2215 for (int pass = 0; pass != 2 && probe < 0; ++pass)
2216 for (int ei = 0; ei != static_cast<int>(storage.elems.size()); ++ei) {
2217 auto &e = storage.elems[ei];
2218 if (e.pattern != pi || !e.local || (pass == 0 && !e.owned))
2219 continue;
2220 get_matrix(ei, mat);
2221 if (!is_zero_matrix(mat.data(), mat.size())) {
2222 probe = ei;
2223 break;
2224 }
2225 }
2226 if (probe < 0)
2227 continue;
2228
2229 const int n = p.size;
2230 w.assign(static_cast<size_t>(n) * n, 0.);
2231 elem_gather_dense(p, mat.data(), w.data());
2232
2233 BlockPattern nl = p;
2234 make_block_layout(nl, analyse_pattern(p, w.data(), u, rcond_min));
2235 double be_block = 0, be_dense = 0;
2236 bool verified = true;
2237 if (nl.state == BlockPattern::BLOCK) {
2238 std::vector<double> probe_mat(nl.elemSize, 0.);
2239 copy_listed(p, mat.data(), nl, probe_mat.data());
2240 verified = verify_probe(nl, probe_mat, u, rcond_min,
2241 storage.optBackwardError, be_block, be_dense);
2242 nl.beRef = be_dense;
2243 if (!verified) {
2244 MOFEM_TAG_AND_LOG("SELF", Sev::warning, "BlockMat")
2245 << "A00 pattern " << pi << ": block elimination rejected on probe "
2246 << "(backward error " << be_block << ", dense " << be_dense
2247 << "), element matrices are dense";
2248 make_block_layout(nl, {});
2249 }
2250 }
2251
2252 MOFEM_TAG_AND_LOG("WORLD", Sev::verbose, "BlockMat")
2253 << "A00 pattern " << pi << " (" << p.nbElems
2254 << " elements): eliminated " << describe_nodes(nl, nl.order)
2255 << "; core " << describe_nodes(nl, nl.core)
2256 << "; doubles per element: listed " << listed_size(p) << ", layout "
2257 << nl.elemSize << ", dense " << static_cast<size_t>(n) * n
2258 << "; probe backward error " << be_block << " (dense " << be_dense
2259 << ")";
2260
2261 relayout_pattern(storage, pi, nl);
2262 }
2263
2264 // from here data is modified, on failure storage is marked as corrupted
2265 storage.factorStorage = BlockStorage::FACTOR_FAILED;
2266 for (auto &p : storage.patterns)
2267 p.nbFallback = 0;
2268 for (auto &p : storage.patterns)
2269 p.beMax = 0;
2270 std::vector<double> scratch, work, be_x, be_r, be_rs;
2271 std::vector<__CLPK_integer> iwork;
2272 for (int ei = 0; ei != static_cast<int>(storage.elems.size()); ++ei) {
2273 auto &e = storage.elems[ei];
2274 e.factorised = false;
2275 e.dense = false;
2276 std::vector<double>().swap(storage.elemDense[ei]);
2277 if (!e.size || !e.local)
2278 continue;
2279 auto &p = storage.patterns[e.pattern];
2280 auto &buf = in_prec ? storage.elemPrec[ei] : storage.elemData[ei];
2281 if (in_prec)
2282 cblas_daxpy(buf.size(), 1., storage.elemData[ei].data(), 1, buf.data(),
2283 1);
2284 // matrices of elements which are not assembled on this process (copies
2285 // of elements owned by other processes) are zero and are skipped
2286 if (is_zero_matrix(buf.data(), buf.size())) {
2287 if (e.owned)
2288 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
2289 "A00 element matrix is zero (not assembled or singular)");
2290 continue;
2291 }
2292 if (p.state == BlockPattern::LISTED)
2293 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
2294 "A00 pattern not analysed");
2295
2296 auto piv = storage.pivots.data() + e.pivShift;
2297 if (p.state == BlockPattern::BLOCK)
2298 scratch.assign(buf.begin(), buf.end());
2299 auto ret = elem_factorise(p, buf.data(), piv, u, rcond_min, work, iwork);
2300 if (!ret && p.state == BlockPattern::BLOCK) {
2301 // block factors are accepted if solve with them is backward stable
2302 const double be = elem_backward_error(p, scratch.data(), buf.data(), piv,
2303 be_x, be_r, be_rs);
2304 p.beMax = std::max(p.beMax, be);
2305 if (!backward_error_accepted(be, p.beRef, storage.optBackwardError))
2306 ret = 3;
2307 }
2308 if (ret) {
2309 if (p.state != BlockPattern::BLOCK)
2310 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
2311 "error lapack dgetrf, A00 element matrix is singular");
2312 // element factorised dense, block buffer is not used
2313 auto &dense = storage.elemDense[ei];
2314 dense.assign(static_cast<size_t>(e.size) * e.size, 0.);
2315 elem_gather_dense(p, scratch.data(), dense.data());
2316 if (lapack_dgetrf(e.size, e.size, dense.data(), e.size, piv) != 0)
2317 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
2318 "error lapack dgetrf, A00 element matrix is singular");
2319 e.dense = true;
2320 ++p.nbFallback;
2321 }
2322 e.factorised = true;
2323 }
2324 for (int pi = 0; pi != static_cast<int>(storage.patterns.size()); ++pi) {
2325 auto &p = storage.patterns[pi];
2326 if (!p.nbFallback)
2327 continue;
2328 MOFEM_TAG_AND_LOG("SELF", p.warned ? Sev::verbose : Sev::warning,
2329 "BlockMat")
2330 << "A00 pattern " << pi << ": " << p.nbFallback << " of " << p.nbElems
2331 << " elements failed block pivot or backward error test and are "
2332 "factorised dense";
2333 p.warned = true;
2334 }
2335 for (int pi = 0; pi != static_cast<int>(storage.patterns.size()); ++pi) {
2336 auto &p = storage.patterns[pi];
2337 if (p.state == BlockPattern::BLOCK) {
2338 MOFEM_TAG_AND_LOG("SELF", Sev::noisy, "BlockMat")
2339 << "A00 pattern " << pi << ": largest backward error " << p.beMax
2340 << " (dense on probe " << p.beRef << "), dense fallback "
2341 << p.nbFallback;
2342 }
2343 }
2344 storage.factorStorage = where;
2346}
2347
2348/// set orders of blocks by finite element and by column entity
2349static void set_block_orders(BlockStorage &storage) {
2350 auto &index = storage.blockIndex;
2351 auto &fe_order = storage.feOrder;
2352 fe_order.resize(index.size());
2353 std::iota(fe_order.begin(), fe_order.end(), 0);
2354 std::sort(fe_order.begin(), fe_order.end(), [&index](int a, int b) {
2355 if (index[a].getFEUId() != index[b].getFEUId())
2356 return index[a].getFEUId() < index[b].getFEUId();
2357 return a < b;
2358 });
2359 auto &col_order = storage.colOrder;
2360 col_order.resize(index.size());
2361 std::iota(col_order.begin(), col_order.end(), 0);
2362 std::sort(col_order.begin(), col_order.end(), [&index](int a, int b) {
2363 if (index[a].getColUId() != index[b].getColUId())
2364 return index[a].getColUId() < index[b].getColUId();
2365 return a < b;
2366 });
2367}
2368
2369/// set blocks of block matrix (parent numbering)
2371 s.blocks.clear();
2372 for (auto &d : s.storage->blockIndex) {
2373 if (d.getElem() == -1)
2374 s.blocks.push_back({d.getMatShift(), d.getLd(), d.getRow(), d.getCol(),
2375 d.getNbRows(), d.getNbCols(), d.getLocRow(),
2376 d.getLocCol()});
2377 }
2378 s.blocks.shrink_to_fit();
2379 s.hasElems = !s.storage->elems.empty();
2380 s.numbering = BlockStructure::PARENT;
2381}
2382
2383/**
2384 * @brief Iterate all blocks of matrix, including listed blocks of A00
2385 * elements
2386 *
2387 * fun(ptr, ld, row, col, nb_rows, nb_cols, loc_row, loc_col)
2388 */
2389template <typename FUN>
2390static inline void for_each_block(const BlockStructure &ctx, FUN &&fun) {
2391 auto &storage = *ctx.storage;
2392 double *data = storage.data.data();
2393 for (auto &b : ctx.blocks)
2394 fun(data + b.shift, b.ld, b.row, b.col, b.nbRows, b.nbCols, b.locRow,
2395 b.locCol);
2396 if (ctx.hasElems) {
2397 for (int ei = 0; ei != static_cast<int>(storage.elems.size()); ++ei) {
2398 auto &e = storage.elems[ei];
2399 auto &p = storage.patterns[e.pattern];
2400 double *buf = storage.elemData[ei].data();
2401 for (int r = 0; r != p.nbNodes; ++r) {
2402 auto &re = storage.elemEntities[e.entBegin + r];
2403 for (int c = 0; c != p.nbNodes; ++c) {
2404 if (!p.isListed(r, c))
2405 continue;
2406 auto &ce = storage.elemEntities[e.entBegin + c];
2407 fun(buf + p.sShift(r, c), p.sLd(r, c), ctx.globIdx(re),
2408 ctx.globIdx(ce), re.nb, ce.nb, ctx.locIdx(re), ctx.locIdx(ce));
2409 }
2410 }
2411 }
2412 }
2413}
2414
2415boost::shared_ptr<BlockStructure> createBlockMatStructure(
2416
2417 DM dm, //< dm
2418 SchurFEOpsFEandFields schur_fe_op_vec //< block elements
2419
2420) {
2421
2422 auto cmp_uid_lo = [](const boost::weak_ptr<FieldEntity> &a, const UId &b) {
2423 if (auto a_ptr = a.lock()) {
2424 if (a_ptr->getLocalUniqueId() < b)
2425 return true;
2426 else
2427 return false;
2428 } else {
2429 return false;
2430 }
2431 };
2432
2433 auto cmp_uid_hi = [](const UId &b, const boost::weak_ptr<FieldEntity> &a) {
2434 if (auto a_ptr = a.lock()) {
2435 if (b < a_ptr->getLocalUniqueId())
2436 return true;
2437 else
2438 return false;
2439 } else {
2440 return true;
2441 }
2442 };
2443
2444 // get uids for fields
2445 auto get_uid_pair = [](const auto &field_id) {
2446 auto lo_uid = FieldEntity::getLocalUniqueIdCalculate(
2447 field_id, get_id_for_min_type<MBVERTEX>());
2448 auto hi_uid = FieldEntity::getLocalUniqueIdCalculate(
2449 field_id, get_id_for_max_type<MBENTITYSET>());
2450 return std::make_pair(lo_uid, hi_uid);
2451 };
2452
2453 // get uids pair
2454 auto get_it_pair = [cmp_uid_lo, cmp_uid_hi](auto &&field_ents, auto &&p_uid) {
2455 auto lo = std::lower_bound(field_ents.begin(), field_ents.end(),
2456 p_uid.first, cmp_uid_lo);
2457 auto hi = std::upper_bound(field_ents.begin(), field_ents.end(),
2458 p_uid.second, cmp_uid_hi);
2459 return std::make_pair(lo, hi);
2460 };
2461
2462 // extract DOFs for rows/columns. DOFs are associated with fields entities
2463 // for given problem.
2464 auto row_extractor = [](auto &e) { return e->entityCacheRowDofs; };
2465 auto col_extractor = [](auto &e) { return e->entityCacheColDofs; };
2466
2467 auto extract_data = [](auto &&its, auto extractor) {
2468 std::vector<std::tuple<UId, int, int, int>> data;
2469 data.reserve(std::distance(its.first, its.second));
2470 // iterate field dofs
2471 for (; its.first != its.second; ++its.first) {
2472 if (auto e = its.first->lock()) {
2473 if (auto cache = extractor(e).lock()) {
2474 int nb_dofs = 0;
2475 for (auto dof = cache->loHi[0]; dof != cache->loHi[1]; ++dof) {
2476 if ((*dof)->getPetscGlobalDofIdx() != -1)
2477 ++nb_dofs;
2478 }
2479 auto uid = e->getLocalUniqueId();
2480 if (nb_dofs) {
2481
2482 auto glob = (*cache->loHi[0])->getPetscGlobalDofIdx();
2483 auto loc = (*cache->loHi[0])->getPetscLocalDofIdx();
2484 while (glob == -1 && cache->loHi[0] != cache->loHi[1]) {
2485 ++cache->loHi[0];
2486 glob = (*cache->loHi[0])->getPetscGlobalDofIdx();
2487 loc = (*cache->loHi[0])->getPetscLocalDofIdx();
2488 }
2489 data.emplace_back(uid, glob, nb_dofs, loc);
2490
2491#ifndef NDEBUG
2492
2493 for (auto lo = cache->loHi[0]; lo != cache->loHi[1]; ++lo) {
2494 auto glob = (*lo)->getPetscGlobalDofIdx();
2495 if (glob == -1) {
2497 "Wrong global index");
2498 }
2499 }
2500
2501#endif
2502 } else {
2503 data.emplace_back(uid, -1, 0, -1);
2504 }
2505 }
2506 }
2507 }
2508 return data;
2509 };
2510
2511 auto data_ptr = boost::make_shared<BlockStructure>();
2512 auto storage = boost::make_shared<BlockStorage>();
2513 data_ptr->storage = storage;
2514 auto &index = storage->blockIndex;
2515 auto m_field_ptr = getInterfacePtr(dm);
2516
2517 // create element to extract data
2518 auto fe_method = boost::shared_ptr<MoFEM::FEMethod>(new MoFEM::FEMethod());
2519
2520 // first pass counts blocks, such that index is allocated once, second pass
2521 // fills index
2522 size_t nb_pairs = 0;
2523 for (auto count_only : {true, false}) {
2524
2525 if (!count_only)
2526 index.reserve(nb_pairs);
2527
2528 for (auto &d : schur_fe_op_vec) {
2529
2530 // extract bit numbers for row and column
2531 auto get_bit_numbers = [&d](auto op) {
2532 std::vector<FieldBitNumber> bit_numbers(d.second.size());
2533 std::transform(d.second.begin(), d.second.end(), bit_numbers.begin(),
2534 op);
2535 return bit_numbers;
2536 };
2537
2538 // extract bit numbers for row
2539 auto row_bit_numbers = get_bit_numbers(
2540 [&](auto &p) { return m_field_ptr->get_field_bit_number(p.first); });
2541 // extract bit numbers for row
2542 auto col_bit_numbers = get_bit_numbers(
2543 [&](auto &p) { return m_field_ptr->get_field_bit_number(p.second); });
2544
2545 fe_method->preProcessHook = []() { return 0; };
2546 fe_method->postProcessHook = []() { return 0; };
2547 fe_method->operatorHook = [&]() {
2549
2550 auto fe_uid = fe_method->numeredEntFiniteElementPtr->getLocalUniqueId();
2551
2552 for (auto f = 0; f != row_bit_numbers.size(); ++f) {
2553
2554 auto row_data =
2555 extract_data(get_it_pair(fe_method->getRowFieldEnts(),
2556 get_uid_pair(row_bit_numbers[f])),
2557 row_extractor);
2558 auto col_data =
2559 extract_data(get_it_pair(fe_method->getColFieldEnts(),
2560 get_uid_pair(col_bit_numbers[f])),
2561 col_extractor);
2562
2563 for (auto &r : row_data) {
2564 auto [r_uid, r_glob, r_nb_dofs, r_loc] = r;
2565 if (!r_nb_dofs)
2566 continue;
2567 for (auto &c : col_data) {
2568 auto [c_uid, c_glob, c_nb_dofs, c_loc] = c;
2569 if (!c_nb_dofs)
2570 continue;
2571 if (count_only)
2572 ++nb_pairs;
2573 else
2574 index.emplace_back(r_uid, c_uid, fe_uid, r_glob, c_glob,
2575 r_nb_dofs, c_nb_dofs, r_loc, c_loc);
2576 }
2577 }
2578 }
2579
2581 };
2582
2583 CHKERR DMoFEMLoopFiniteElementsUpAndLowRank(dm, d.first, fe_method, 0,
2584 m_field_ptr->get_comm_size());
2585 };
2586 }
2587
2588 // unique blocks, block from first element which lists it is kept
2589 std::stable_sort(index.begin(), index.end(), BlockStorage::lessRowCol);
2590 index.erase(std::unique(index.begin(), index.end(),
2591 [](auto &a, auto &b) {
2592 return a.getRowUId() == b.getRowUId() &&
2593 a.getColUId() == b.getColUId();
2594 }),
2595 index.end());
2596 index.shrink_to_fit();
2597
2598 // blocks are stored in order of row entities
2599 size_t mem_size = 0;
2600 for (auto &v : index) {
2601 v.setStorage(mem_size, v.getNbCols(), -1);
2602 mem_size += static_cast<size_t>(v.getNbRows()) * v.getNbCols();
2603 }
2604
2605 if (mem_size > storage->data.max_size())
2607 storage->dataSize = mem_size;
2608
2609 set_block_orders(*storage);
2610 set_parent_blocks(*data_ptr);
2611
2612 auto ghost_x = createDMVector(dm);
2613 auto ghost_y = createDMVector(dm);
2614 CHKERR VecSetDM(ghost_x, PETSC_NULLPTR);
2615 CHKERR VecSetDM(ghost_y, PETSC_NULLPTR);
2616
2617 data_ptr->ghostX = ghost_x;
2618 data_ptr->ghostY = ghost_y;
2619
2620 MPI_Comm comm;
2621 CHKERR PetscObjectGetComm((PetscObject)dm, &comm);
2622 std::array<size_t, 2> loc_stats = {index.size(), mem_size * sizeof(double)};
2623 std::array<size_t, 2> sum_stats, max_stats;
2624 MPI_Allreduce(loc_stats.data(), sum_stats.data(), 2, MPIU_SIZE_T, MPI_SUM,
2625 comm);
2626 MPI_Allreduce(loc_stats.data(), max_stats.data(), 2, MPIU_SIZE_T, MPI_MAX,
2627 comm);
2628 constexpr double mb = 1024. * 1024.;
2629 MOFEM_TAG_AND_LOG("WORLD", Sev::verbose, "BlockMat")
2630 << "Blocks " << sum_stats[0] << " (max per process " << max_stats[0]
2631 << "), data " << sum_stats[1] / mb << " MB (max per process "
2632 << max_stats[1] / mb << " MB), index "
2633 << (sum_stats[0] * (sizeof(BlockStorage::Indexes) + 2 * sizeof(int) +
2634 sizeof(BlockStructure::Block))) /
2635 mb
2636 << " MB";
2637
2638 return data_ptr;
2639}
2640
2641/**
2642 * @brief Matrix-vector product of block matrix
2643 *
2644 * Blocks with owned columns are multiplied while ghost values of x are
2645 * communicated, then blocks with ghost columns. A00 elements are applied as
2646 * whole, from listed blocks, or from factors if elements are factorised.
2647 */
2648static MoFEMErrorCode mult_block_structure(Mat mat, Vec x, Vec y,
2649 InsertMode iora, bool trans) {
2651 BlockStructure *ctx;
2652 CHKERR MatShellGetContext(mat, (void **)&ctx);
2653 auto &storage = *ctx->storage;
2654 storage.ensureAllocated();
2655
2656 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_BlockStructureMult, 0, 0, 0, 0);
2657
2658 int x_loc_size;
2659 CHKERR VecGetLocalSize(x, &x_loc_size);
2660
2661 Vec ghost_x = ctx->ghostX;
2662 Vec ghost_y = ctx->ghostY;
2663 if (trans)
2664 std::swap(ghost_x, ghost_y);
2665
2666 CHKERR storage.checkNotFailed();
2667 const bool with_p = ctx->hasElems && ctx->multiplyByPreconditioner;
2668 if (with_p && !storage.hasPreconditioner)
2669 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
2670 "No preconditioner blocks");
2671 const bool use_lu =
2672 ctx->hasElems && storage.isFactorised() &&
2673 (storage.factorStorage == BlockStorage::FACTOR_IN_DATA || with_p);
2674 auto &lu_bufs =
2675 storage.factorStorage == BlockStorage::FACTOR_IN_PRECONDITIONER
2676 ? storage.elemPrec
2677 : storage.elemData;
2678
2679 CHKERR VecCopy(x, ghost_x);
2680 {
2681 Vec loc_ghost_y;
2682 CHKERR VecGhostGetLocalForm(ghost_y, &loc_ghost_y);
2683 CHKERR VecSet(loc_ghost_y, 0.);
2684 CHKERR VecGhostRestoreLocalForm(ghost_y, &loc_ghost_y);
2685 }
2686
2687 std::vector<double> xe, ye;
2688 if (ctx->hasElems) {
2689 xe.resize(storage.maxElemSize);
2690 ye.resize(storage.maxElemSize);
2691 }
2692
2693 auto mult_pass = [&](bool ghost_pass) {
2695
2696 const double *x_array;
2697 Vec loc_ghost_x;
2698 CHKERR VecGhostGetLocalForm(ghost_x, &loc_ghost_x);
2699 CHKERR VecGetArrayRead(loc_ghost_x, &x_array);
2700
2701 double *y_array;
2702 Vec loc_ghost_y;
2703 CHKERR VecGhostGetLocalForm(ghost_y, &loc_ghost_y);
2704 CHKERR VecGetArray(loc_ghost_y, &y_array);
2705
2706 const double *data = storage.data.data();
2707 const auto cblas_trans = trans ? CblasTrans : CblasNoTrans;
2708
2709 for (auto &b : ctx->blocks) {
2710 const int x_loc = trans ? b.locRow : b.locCol;
2711 const int y_loc = trans ? b.locCol : b.locRow;
2712 // blocks with entities not available on this process are skipped
2713 if (x_loc < 0 || y_loc < 0)
2714 continue;
2715 if ((x_loc >= x_loc_size) != ghost_pass)
2716 continue;
2717 cblas_dgemv(CblasRowMajor, cblas_trans, b.nbRows, b.nbCols, 1.0,
2718 data + b.shift, b.ld, x_array + x_loc, 1, 1.0,
2719 y_array + y_loc, 1);
2720 }
2721
2722 if (ctx->hasElems) {
2723 for (int ei = 0; ei != static_cast<int>(storage.elems.size()); ++ei) {
2724 auto &e = storage.elems[ei];
2725 const int n = e.size;
2726 if (!n || !e.local)
2727 continue;
2728 // factorised storage holds factors only for assembled elements,
2729 // other element matrices are zero
2730 if (use_lu && !e.factorised)
2731 continue;
2732 auto ent_begin = storage.elemEntities.data() + e.entBegin;
2733 auto ent_end = storage.elemEntities.data() + e.entEnd;
2734
2735 // element with any ghost column is multiplied after ghost update,
2736 // element with entity not available on this process is skipped
2737 bool any_ghost = false;
2738 bool available = true;
2739 for (auto ent = ent_begin; ent != ent_end; ++ent) {
2740 if (!ent->nb)
2741 continue;
2742 const int loc = ctx->locIdx(*ent);
2743 if (loc < 0)
2744 available = false;
2745 else if (loc >= x_loc_size)
2746 any_ghost = true;
2747 }
2748 if (!available || any_ghost != ghost_pass)
2749 continue;
2750
2751 for (auto ent = ent_begin; ent != ent_end; ++ent)
2752 if (ent->nb)
2753 cblas_dcopy(ent->nb, x_array + ctx->locIdx(*ent), 1,
2754 xe.data() + ent->offset, 1);
2755
2756 auto &p = storage.patterns[e.pattern];
2757 auto piv = storage.pivots.data() + e.pivShift;
2758 double *y_e;
2759 if (use_lu) {
2760 if (e.dense)
2761 apply_lu_factors(n, storage.elemDense[ei].data(), piv, xe.data(),
2762 trans);
2763 else
2764 elem_mult_factors(p, lu_bufs[ei].data(), piv, xe.data(), trans);
2765 y_e = xe.data();
2766 } else {
2767 std::fill(ye.begin(), ye.begin() + n, 0.);
2768 elem_mult_listed(p, storage.elemData[ei].data(), xe.data(), ye.data(),
2769 trans);
2770 if (with_p)
2771 elem_mult_listed(p, storage.elemPrec[ei].data(), xe.data(),
2772 ye.data(), trans);
2773 y_e = ye.data();
2774 }
2775
2776 for (auto ent = ent_begin; ent != ent_end; ++ent)
2777 if (ent->nb)
2778 cblas_daxpy(ent->nb, 1.0, y_e + ent->offset, 1,
2779 y_array + ctx->locIdx(*ent), 1);
2780 }
2781 }
2782
2783 CHKERR VecRestoreArrayRead(loc_ghost_x, &x_array);
2784 CHKERR VecRestoreArray(loc_ghost_y, &y_array);
2785 CHKERR VecGhostRestoreLocalForm(ghost_x, &loc_ghost_x);
2786 CHKERR VecGhostRestoreLocalForm(ghost_y, &loc_ghost_y);
2788 };
2789
2790 CHKERR VecGhostUpdateBegin(ghost_x, INSERT_VALUES, SCATTER_FORWARD);
2791 CHKERR mult_pass(false);
2792 CHKERR VecGhostUpdateEnd(ghost_x, INSERT_VALUES, SCATTER_FORWARD);
2793 CHKERR mult_pass(true);
2794
2795 CHKERR VecGhostUpdateBegin(ghost_y, ADD_VALUES, SCATTER_REVERSE);
2796 CHKERR VecGhostUpdateEnd(ghost_y, ADD_VALUES, SCATTER_REVERSE);
2797
2798 switch (iora) {
2799 case INSERT_VALUES:
2800 CHKERR VecCopy(ghost_y, y);
2801 break;
2802 case ADD_VALUES:
2803 CHKERR VecAXPY(y, 1., ghost_y);
2804 break;
2805 default:
2806 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED, "Wrong InsertMode");
2807 }
2808
2809 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_BlockStructureMult, 0, 0, 0, 0);
2810
2812}
2813
2814/**
2815 * @brief Solve with A00, i.e. x = A00^{-1} f, element by element
2816 *
2817 * Elements are factorised in place on first call, and factors are reused
2818 * until matrix is zeroed.
2819 */
2820static MoFEMErrorCode solve_block_structure(Mat mat, Vec f, Vec x,
2821 InsertMode iora, bool trans) {
2823 BlockStructure *ctx;
2824 CHKERR MatShellGetContext(mat, (void **)&ctx);
2825 auto &storage = *ctx->storage;
2826 storage.ensureAllocated();
2827
2828 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_BlockStructureSolve, 0, 0, 0, 0);
2829
2830 if (ctx->numbering != BlockStructure::BLOCK)
2831 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED,
2832 "Solve is implemented only for A00 block of nested Schur matrix, "
2833 "see createSchurNestedMatrixStruture");
2834
2835 CHKERR storage.checkNotFailed();
2836 const bool with_p = ctx->multiplyByPreconditioner;
2837 if (with_p && !storage.hasPreconditioner)
2838 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
2839 "No preconditioner blocks");
2840
2841 bool use_lu = true;
2842 if (with_p) {
2843 CHKERR factorise_elems(storage, BlockStorage::FACTOR_IN_PRECONDITIONER);
2844 } else if (!storage.hasPreconditioner) {
2845 CHKERR factorise_elems(storage, BlockStorage::FACTOR_IN_DATA);
2846 } else {
2847 // operator without preconditioner while preconditioner blocks exist,
2848 // element matrices are factorised on the fly
2849 use_lu = false;
2850 }
2851 auto &lu_bufs = with_p ? storage.elemPrec : storage.elemData;
2852
2853 int nb_local;
2854 CHKERR VecGetLocalSize(x, &nb_local);
2855
2856 if (iora == INSERT_VALUES)
2857 CHKERR VecZeroEntries(x);
2858
2859 double *x_array;
2860 CHKERR VecGetArray(x, &x_array);
2861 const double *f_array;
2862 CHKERR VecGetArrayRead(f, &f_array);
2863
2864 std::vector<double> t(storage.maxElemSize);
2865 std::vector<double> scratch;
2866 std::vector<__CLPK_integer> scratch_piv;
2867 if (!use_lu) {
2868 scratch.resize(static_cast<size_t>(storage.maxElemSize) *
2869 storage.maxElemSize);
2870 scratch_piv.resize(storage.maxElemSize);
2871 }
2872
2873 const char lapack_trans = trans ? 'N' : 'T';
2874
2875 for (int ei = 0; ei != static_cast<int>(storage.elems.size()); ++ei) {
2876 auto &e = storage.elems[ei];
2877 const int n = e.size;
2878 if (!n || !e.local)
2879 continue;
2880 if (use_lu && !e.factorised)
2881 continue;
2882 auto ent_begin = storage.elemEntities.data() + e.entBegin;
2883 auto ent_end = storage.elemEntities.data() + e.entEnd;
2884
2885 // only elements with owned DOFs are solved
2886 bool owned = true;
2887 for (auto ent = ent_begin; ent != ent_end; ++ent) {
2888 if (ent->nb && (ent->locBlock < 0 || ent->locBlock >= nb_local)) {
2889 owned = false;
2890 break;
2891 }
2892 }
2893 if (!owned)
2894 continue;
2895
2896 for (auto ent = ent_begin; ent != ent_end; ++ent)
2897 if (ent->nb)
2898 cblas_dcopy(ent->nb, f_array + ent->locBlock, 1, t.data() + ent->offset,
2899 1);
2900
2901 auto &p = storage.patterns[e.pattern];
2902 auto piv = storage.pivots.data() + e.pivShift;
2903 __CLPK_integer info = 0;
2904 if (use_lu) {
2905 if (e.dense)
2906 info = lapack_dgetrs(lapack_trans, n, 1, storage.elemDense[ei].data(),
2907 n, piv, t.data(), n);
2908 else
2909 CHKERR elem_solve(p, lu_bufs[ei].data(), piv, t.data(), n, 1, trans,
2910 nullptr);
2911 } else {
2912 std::fill(scratch.begin(), scratch.begin() + static_cast<size_t>(n) * n,
2913 0.);
2914 elem_gather_dense(p, storage.elemData[ei].data(), scratch.data());
2915 info = lapack_dgetrf(n, n, scratch.data(), n, scratch_piv.data());
2916 if (info == 0)
2917 info = lapack_dgetrs(lapack_trans, n, 1, scratch.data(), n,
2918 scratch_piv.data(), t.data(), n);
2919 }
2920 if (info != 0)
2921 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
2922 "error lapack solve info = %d", static_cast<int>(info));
2923
2924 for (auto ent = ent_begin; ent != ent_end; ++ent)
2925 if (ent->nb)
2926 cblas_daxpy(ent->nb, 1.0, t.data() + ent->offset, 1,
2927 x_array + ent->locBlock, 1);
2928 }
2929
2930 CHKERR VecRestoreArray(x, &x_array);
2931 CHKERR VecRestoreArrayRead(f, &f_array);
2932
2933 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_BlockStructureSolve, 0, 0, 0, 0);
2934
2936}
2937
2938static PetscErrorCode mult(Mat mat, Vec x, Vec y) {
2939 return mult_block_structure(mat, x, y, INSERT_VALUES, false);
2940}
2941static PetscErrorCode mult_add(Mat mat, Vec x, Vec y) {
2942 return mult_block_structure(mat, x, y, ADD_VALUES, false);
2943}
2944static PetscErrorCode mult_transpose(Mat mat, Vec x, Vec y) {
2945 return mult_block_structure(mat, x, y, INSERT_VALUES, true);
2946}
2947static PetscErrorCode mult_transpose_add(Mat mat, Vec x, Vec y) {
2948 return mult_block_structure(mat, x, y, ADD_VALUES, true);
2949}
2950
2951static PetscErrorCode solve(Mat mat, Vec x, Vec y) {
2952 return solve_block_structure(mat, x, y, INSERT_VALUES, false);
2953}
2954static PetscErrorCode solve_add(Mat mat, Vec x, Vec y) {
2955 return solve_block_structure(mat, x, y, ADD_VALUES, false);
2956}
2957static PetscErrorCode solve_transpose(Mat mat, Vec x, Vec y) {
2958 return solve_block_structure(mat, x, y, INSERT_VALUES, true);
2959}
2960static PetscErrorCode solve_transpose_add(Mat mat, Vec x, Vec y) {
2961 return solve_block_structure(mat, x, y, ADD_VALUES, true);
2962}
2963
2964static PetscErrorCode zero_rows_columns(Mat A, PetscInt N,
2965 const PetscInt rows[], PetscScalar diag,
2966 Vec x, Vec b) {
2967
2969 BlockStructure *ctx;
2970 CHKERR MatShellGetContext(A, (void **)&ctx);
2971 auto &storage = *ctx->storage;
2972 CHKERR storage.modified();
2973 storage.ensureAllocated();
2974
2975 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_zeroRowsAndCols, 0, 0, 0, 0);
2976
2977 // rows are gathered from all processes, since blocks with ghost rows or
2978 // columns are stored on this process
2979 std::vector<int> zero_idx;
2980 {
2981 MPI_Comm comm;
2982 CHKERR PetscObjectGetComm((PetscObject)A, &comm);
2983 int size;
2984 MPI_Comm_size(comm, &size);
2985 if (size > 1) {
2986 auto is = createISGeneral(comm, N, rows, PETSC_USE_POINTER);
2987 auto is_local = isAllGather(is);
2988 int is_nb_rows;
2989 CHKERR ISGetSize(is_local, &is_nb_rows);
2990 const int *ptr;
2991 CHKERR ISGetIndices(is_local, &ptr);
2992 zero_idx.assign(ptr, ptr + is_nb_rows);
2993 CHKERR ISRestoreIndices(is_local, &ptr);
2994 } else {
2995 zero_idx.assign(rows, rows + N);
2996 }
2997 }
2998 std::sort(zero_idx.begin(), zero_idx.end());
2999 zero_idx.erase(std::unique(zero_idx.begin(), zero_idx.end()), zero_idx.end());
3000
3001 int loc_m, loc_n;
3002 CHKERR MatGetLocalSize(A, &loc_m, &loc_n);
3003
3004 for_each_block(*ctx, [&](double *ptr, int ld, int row, int col, int nb_rows,
3005 int nb_cols, int loc_row, int loc_col) {
3006 if (!nb_rows || !nb_cols)
3007 return;
3008
3009 auto lo = std::lower_bound(zero_idx.begin(), zero_idx.end(), row);
3010 for (; lo != zero_idx.end() && *lo < row + nb_rows; ++lo) {
3011 auto *row_ptr = ptr + static_cast<size_t>(*lo - row) * ld;
3012 std::fill(row_ptr, row_ptr + nb_cols, 0.);
3013 }
3014
3015 const bool diag_block = row == col && loc_row >= 0 && loc_row < loc_m &&
3016 loc_col >= 0 && loc_col < loc_n;
3017 lo = std::lower_bound(zero_idx.begin(), zero_idx.end(), col);
3018 for (; lo != zero_idx.end() && *lo < col + nb_cols; ++lo) {
3019 const int c = *lo - col;
3020 for (int i = 0; i != nb_rows; ++i)
3021 ptr[static_cast<size_t>(i) * ld + c] = 0.;
3022 if (diag_block && c < nb_rows)
3023 ptr[static_cast<size_t>(c) * ld + c] = diag;
3024 }
3025 });
3026
3027 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_zeroRowsAndCols, 0, 0, 0, 0);
3028
3030}
3031
3032static PetscErrorCode mat_zero(Mat m) {
3034 BlockStructure *ctx;
3035 CHKERR MatShellGetContext(m, (void **)&ctx);
3036 auto &storage = *ctx->storage;
3037 storage.ensureAllocated();
3038 std::fill(storage.data.begin(), storage.data.end(), 0.);
3039 for (auto &buf : storage.elemData)
3040 std::fill(buf.begin(), buf.end(), 0.);
3041 for (auto &buf : storage.elemPrec)
3042 std::fill(buf.begin(), buf.end(), 0.);
3043 storage.resetFactors();
3045}
3046
3049 CHKERR MatShellSetManageScalingShifts(mat_raw);
3050 CHKERR MatShellSetOperation(mat_raw, MATOP_MULT, (void (*)(void))mult);
3051 CHKERR MatShellSetOperation(mat_raw, MATOP_MULT_ADD,
3052 (void (*)(void))mult_add);
3053 CHKERR MatShellSetOperation(mat_raw, MATOP_MULT_TRANSPOSE,
3054 (void (*)(void))mult_transpose);
3055 CHKERR MatShellSetOperation(mat_raw, MATOP_MULT_TRANSPOSE_ADD,
3056 (void (*)(void))mult_transpose_add);
3057 CHKERR MatShellSetOperation(mat_raw, MATOP_SOLVE, (void (*)(void))solve);
3058 CHKERR MatShellSetOperation(mat_raw, MATOP_SOLVE_ADD,
3059 (void (*)(void))solve_add);
3060 CHKERR MatShellSetOperation(mat_raw, MATOP_SOLVE_TRANSPOSE,
3061 (void (*)(void))solve_transpose);
3062 CHKERR MatShellSetOperation(mat_raw, MATOP_SOLVE_TRANSPOSE_ADD,
3063 (void (*)(void))solve_transpose_add);
3064 CHKERR MatShellSetOperation(mat_raw, MATOP_ZERO_ENTRIES,
3065 (void (*)(void))mat_zero);
3066 CHKERR MatShellSetOperation(mat_raw, MATOP_ZERO_ROWS_COLUMNS,
3067 (void (*)(void))zero_rows_columns);
3068
3070};
3071
3073 boost::shared_ptr<BlockStructure> data) {
3074
3075 auto problem_ptr = getProblemPtr(dm);
3076 auto nb_local = problem_ptr->nbLocDofsRow;
3077 auto nb_global = problem_ptr->nbDofsRow;
3078
3079 // check in nb, rows is equal to nb. columns.
3080 if (nb_local != problem_ptr->nbLocDofsCol) {
3081 MOFEM_LOG("SELF", Sev::error)
3082 << "Wrong size " << nb_local << " != " << problem_ptr->nbLocDofsCol;
3084 "nb. cols is inconsistent with nb. rows");
3085 }
3086 if (nb_global != problem_ptr->nbDofsCol) {
3087 MOFEM_LOG("SELF", Sev::error)
3088 << "Wrong size " << nb_global << " != " << problem_ptr->nbDofsCol;
3090 "nb. cols is inconsistent with nb. rows");
3091 }
3092
3093 // get comm from DM
3094 MPI_Comm comm;
3095 CHKERR PetscObjectGetComm((PetscObject)dm, &comm);
3096
3097 Mat mat_raw;
3098 CHKERR MatCreateShell(comm, nb_local, nb_local, nb_global, nb_global,
3099 (void *)data.get(), &mat_raw);
3100 CHKERR setSchurBlockMatOps(mat_raw);
3101 // CHKERR PetscObjectSetName((PetscObject)mat_raw, MoFEM_BLOCK_MAT);
3102
3103 return std::make_pair(SmartPetscObj<Mat>(mat_raw), data);
3104}
3105
3106MoFEMErrorCode
3108 std::vector<std::string> fields_name,
3109 std::vector<boost::shared_ptr<Range>> field_ents,
3110 SmartPetscObj<AO> ao) {
3112 BlockStructure *ctx;
3113 CHKERR MatShellGetContext(B, (void **)&ctx);
3114 auto &storage = *ctx->storage;
3115 auto &index = storage.blockIndex;
3116 storage.ensureAllocated();
3117
3118 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_AssembleSchurMat, 0, 0, 0, 0);
3119
3120 if (ao.use_count() == 0) {
3121 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY, "No AO set");
3122 }
3123 CHKERR storage.checkNotFailed();
3124
3125 const bool with_p = ctx->multiplyByPreconditioner;
3126 if (with_p && !storage.hasPreconditioner)
3127 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3128 "No preconditioner blocks");
3129
3130 if (fields_name.size() != field_ents.size()) {
3131 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3132 "fields_name.size() != field_ents.size()");
3133 }
3134
3135 std::vector<std::pair<UId, UId>> a00_fields_uids;
3136 a00_fields_uids.reserve(fields_name.size());
3137 auto it_field_ents = field_ents.begin();
3138 for (auto &f : fields_name) {
3139 int bn = m_field.get_field_bit_number(f);
3140 if (*it_field_ents) {
3141 for (auto p = (*it_field_ents)->pair_begin();
3142 p != (*it_field_ents)->pair_end(); ++p) {
3143 a00_fields_uids.emplace_back(
3144 DofEntity::getLoFieldEntityUId(bn, p->first),
3145 DofEntity::getHiFieldEntityUId(bn, p->second));
3146 }
3147 } else {
3148 a00_fields_uids.emplace_back(FieldEntity::getLoBitNumberUId(bn),
3149 FieldEntity::getHiBitNumberUId(bn));
3150 }
3151 ++it_field_ents;
3152 }
3153
3154 auto is_a00 = [&](const UId &uid) {
3155 for (auto &uid_pair : a00_fields_uids) {
3156 if (uid >= uid_pair.first && uid < uid_pair.second)
3157 return true;
3158 }
3159 return false;
3160 };
3161
3162 using Idx = BlockStorage::Indexes;
3163 std::vector<const Idx *> a00_blocks, a01_blocks, a10_blocks, a11_blocks;
3164 struct LocIdx {
3165 int index; ///< index in local matrix
3166 int size; ///< number of DOFs
3167 int glob; ///< global index in block matrix numbering
3168 };
3169 std::map<UId, LocIdx> a00_indexing, a11_indexing;
3170 std::vector<UId> col_uids;
3171 std::vector<double> a00_mat, a01t_mat, a10_mat, s_mat, tmp_block;
3172 std::vector<__CLPK_integer> ipiv;
3173 std::vector<int> glob_idx, row_glob_idx, col_glob_idx;
3174 std::vector<char> nz;
3175
3176 // assemble block directly to Schur matrix
3177 auto set_values_block = [&](const Idx *b) {
3179 const int nb_rows = b->getNbRows();
3180 const int nb_cols = b->getNbCols();
3181 row_glob_idx.resize(nb_rows);
3182 col_glob_idx.resize(nb_cols);
3183 std::iota(row_glob_idx.begin(), row_glob_idx.end(), b->getRow());
3184 std::iota(col_glob_idx.begin(), col_glob_idx.end(), b->getCol());
3185 CHKERR AOApplicationToPetsc(ao, nb_rows, row_glob_idx.data());
3186 CHKERR AOApplicationToPetsc(ao, nb_cols, col_glob_idx.data());
3187 const double *ptr = storage.blockPtr(*b);
3188 if (b->getLd() != nb_cols) {
3189 tmp_block.resize(static_cast<size_t>(nb_rows) * nb_cols);
3190 copy_block(tmp_block.data(), nb_cols, 0, 0, ptr, b->getLd(), nb_rows,
3191 nb_cols);
3192 ptr = tmp_block.data();
3193 }
3194 CHKERR MatSetValues(S, nb_rows, row_glob_idx.data(), nb_cols,
3195 col_glob_idx.data(), ptr, ADD_VALUES);
3197 };
3198
3199 // local indices of entities, ordered by uid
3200 auto set_local_indices = [](auto &indexing, auto &row_blocks,
3201 auto &col_blocks) {
3202 indexing.clear();
3203 for (auto b : row_blocks)
3204 indexing.emplace(b->getRowUId(), LocIdx{0, b->getNbRows(), b->getRow()});
3205 for (auto b : col_blocks)
3206 indexing.emplace(b->getColUId(), LocIdx{0, b->getNbCols(), b->getCol()});
3207 int size = 0;
3208 for (auto &p : indexing) {
3209 p.second.index = size;
3210 size += p.second.size;
3211 }
3212 return size;
3213 };
3214
3215 const auto &fe_order = storage.feOrder;
3216
3217 // classify blocks of finite element starting at position p in fe_order,
3218 // advance p to next element
3219 auto classify_fe = [&](size_t &p) {
3220 a00_blocks.clear();
3221 a01_blocks.clear();
3222 a10_blocks.clear();
3223 a11_blocks.clear();
3224 const auto fe_uid = index[fe_order[p]].getFEUId();
3225 for (; p != fe_order.size() && index[fe_order[p]].getFEUId() == fe_uid;
3226 ++p) {
3227 auto b = &index[fe_order[p]];
3228 const bool r00 = is_a00(b->getRowUId());
3229 const bool c00 = is_a00(b->getColUId());
3230 if (r00 && c00)
3231 a00_blocks.push_back(b);
3232 else if (r00)
3233 a01_blocks.push_back(b);
3234 else if (c00)
3235 a10_blocks.push_back(b);
3236 else
3237 a11_blocks.push_back(b);
3238 }
3239 return fe_uid;
3240 };
3241
3242 // return index of A00 element if eliminated blocks of finite element are
3243 // exactly listed blocks of that element, otherwise -1
3244 auto element_of = [&]() {
3245 const int elem = a00_blocks.front()->getElem();
3246 if (elem < 0)
3247 return -1;
3248 auto &p = storage.patterns[storage.elems[elem].pattern];
3249 if (static_cast<int>(a00_blocks.size()) != p.nbListed)
3250 return -1;
3251 for (auto b : a00_blocks)
3252 if (b->getElem() != elem)
3253 return -1;
3254 return elem;
3255 };
3256
3257 // Factors stored in place can be used only if for every element the
3258 // eliminated blocks coincide with A00 element. That is checked before any
3259 // destructive factorisation.
3260 // process without A00 elements (nested structure exists) is consistent
3261 bool all_consistent = storage.nested;
3262 for (size_t p = 0; all_consistent && p != fe_order.size();) {
3263 classify_fe(p);
3264 // retained block which is part of A00 element means that eliminated set
3265 // is smaller than nested A00
3266 for (auto blocks : {&a01_blocks, &a10_blocks, &a11_blocks})
3267 for (auto b : *blocks)
3268 if (b->getElem() >= 0)
3269 all_consistent = false;
3270 if (!a00_blocks.empty() && element_of() < 0)
3271 all_consistent = false;
3272 }
3273
3274 enum Mode { SCRATCH, DATA_FACTORS, PRECONDITIONER_FACTORS } mode = SCRATCH;
3275 if (with_p) {
3276 if (!all_consistent)
3277 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED,
3278 "Schur complement with preconditioner blocks requires eliminated "
3279 "fields equal to A00 of nested matrix");
3280 CHKERR factorise_elems(storage, BlockStorage::FACTOR_IN_PRECONDITIONER);
3281 mode = PRECONDITIONER_FACTORS;
3282 } else if (storage.nested && !storage.hasPreconditioner) {
3283 if (all_consistent) {
3284 CHKERR factorise_elems(storage, BlockStorage::FACTOR_IN_DATA);
3285 mode = DATA_FACTORS;
3286 } else if (storage.isFactorised()) {
3287 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3288 "A00 is factorised in place, eliminated fields have to be equal "
3289 "to A00 of nested matrix");
3290 }
3291 }
3292
3293 for (size_t p = 0; p != fe_order.size();) {
3294
3295 const auto fe_uid = classify_fe(p);
3296
3297 for (auto b : a11_blocks)
3298 CHKERR set_values_block(b);
3299
3300 if (a00_blocks.empty())
3301 continue;
3302
3303 // couplings of A00 entities assembled by other elements, e.g. skeleton
3304 // elements of hybridised problem. Blocks of A00 are sorted by row uid.
3305 for (size_t i = 0; i != a00_blocks.size(); ++i) {
3306 const auto r_uid = a00_blocks[i]->getRowUId();
3307 if (i && a00_blocks[i - 1]->getRowUId() == r_uid)
3308 continue;
3309 auto range = storage.rowRange(r_uid);
3310 for (auto it = range.first; it != range.second; ++it) {
3311 if (it->getFEUId() != fe_uid && !is_a00(it->getColUId()))
3312 a01_blocks.push_back(&*it);
3313 }
3314 }
3315 col_uids.clear();
3316 for (auto b : a00_blocks)
3317 col_uids.push_back(b->getColUId());
3318 std::sort(col_uids.begin(), col_uids.end());
3319 col_uids.erase(std::unique(col_uids.begin(), col_uids.end()),
3320 col_uids.end());
3321 for (auto &c_uid : col_uids) {
3322 auto range = storage.colRange(c_uid);
3323 for (auto it = range.first; it != range.second; ++it) {
3324 auto &b = index[*it];
3325 if (b.getFEUId() != fe_uid && !is_a00(b.getRowUId()))
3326 a10_blocks.push_back(&b);
3327 }
3328 }
3329
3330 const int a11_size =
3331 set_local_indices(a11_indexing, a10_blocks, a01_blocks);
3332 if (!a11_size)
3333 continue;
3334
3335 // A01 is gathered transposed (i.e. column-major), then X = A00^{-1} A01 is
3336 // obtained directly by solve with factors of M = A00^T
3337 auto gather_a01t = [&](int a00_size) {
3338 a01t_mat.assign(static_cast<size_t>(a11_size) * a00_size, 0.);
3339 for (auto b : a01_blocks) {
3340 const auto &ri = a00_indexing.at(b->getRowUId());
3341 const auto &ci = a11_indexing.at(b->getColUId());
3342 const double *ptr = storage.blockPtr(*b);
3343 const int ld = b->getLd();
3344 for (int i = 0; i != b->getNbRows(); ++i)
3345 for (int j = 0; j != b->getNbCols(); ++j)
3346 a01t_mat[static_cast<size_t>(ci.index + j) * a00_size + ri.index +
3347 i] = ptr[static_cast<size_t>(i) * ld + j];
3348 }
3349 };
3350
3351 int a00_size;
3352 if (mode != SCRATCH) {
3353 const int elem = element_of();
3354 if (elem < 0)
3355 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3356 "Inconsistent A00 element");
3357 auto &e = storage.elems[elem];
3358 // element matrix not assembled on this process
3359 if (!e.factorised)
3360 continue;
3361 auto &pat = storage.patterns[e.pattern];
3362 // A00 rows and columns in layout of element vector
3363 a00_indexing.clear();
3364 for (int r = e.entBegin; r != e.entEnd; ++r) {
3365 auto &ent = storage.elemEntities[r];
3366 a00_indexing.emplace(ent.uid,
3367 LocIdx{ent.offset, ent.nb, ent.globParent});
3368 }
3369 a00_size = e.size;
3370 gather_a01t(a00_size);
3371 auto piv = storage.pivots.data() + e.pivShift;
3372 if (e.dense) {
3373 auto info = lapack_dgetrs('T', a00_size, a11_size,
3374 storage.elemDense[elem].data(), a00_size, piv,
3375 a01t_mat.data(), a00_size);
3376 if (info != 0)
3377 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
3378 "error lapack dgetrs info = %d", static_cast<int>(info));
3379 } else {
3380 // nodes coupled to retained entities, other rows of A01 are zero
3381 nz.assign(pat.nbNodes, 0);
3382 for (auto b : a01_blocks)
3383 for (int r = 0; r != pat.nbNodes; ++r)
3384 if (storage.elemEntities[e.entBegin + r].uid == b->getRowUId())
3385 nz[r] = 1;
3386 auto &buf = mode == DATA_FACTORS ? storage.elemData[elem]
3387 : storage.elemPrec[elem];
3388 CHKERR elem_solve(pat, buf.data(), piv, a01t_mat.data(), a00_size,
3389 a11_size, false, &nz);
3390 }
3391 } else {
3392 a00_size = set_local_indices(a00_indexing, a00_blocks, a00_blocks);
3393 const int elem = a00_blocks.front()->getElem();
3394 if (elem >= 0 && !storage.elems[elem].local)
3395 continue;
3396 a00_mat.assign(static_cast<size_t>(a00_size) * a00_size, 0.);
3397 for (auto b : a00_blocks) {
3398 const auto &ri = a00_indexing.at(b->getRowUId());
3399 const auto &ci = a00_indexing.at(b->getColUId());
3400 copy_block(a00_mat.data(), a00_size, ri.index, ci.index,
3401 storage.blockPtr(*b), b->getLd(), b->getNbRows(),
3402 b->getNbCols());
3403 }
3404 // copy of element owned by other process
3405 if (elem >= 0 && !storage.elems[elem].owned &&
3406 is_zero_matrix(a00_mat.data(), a00_mat.size()))
3407 continue;
3408 ipiv.resize(a00_size);
3409 auto info = lapack_dgetrf(a00_size, a00_size, a00_mat.data(), a00_size,
3410 ipiv.data());
3411 if (info != 0)
3412 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
3413 "error lapack dgetrf info = %d", static_cast<int>(info));
3414 gather_a01t(a00_size);
3415 info = lapack_dgetrs('T', a00_size, a11_size, a00_mat.data(), a00_size,
3416 ipiv.data(), a01t_mat.data(), a00_size);
3417 if (info != 0)
3418 SETERRQ(PETSC_COMM_SELF, MOFEM_OPERATION_UNSUCCESSFUL,
3419 "error lapack dgetrs info = %d", static_cast<int>(info));
3420 }
3421
3422 a10_mat.assign(static_cast<size_t>(a11_size) * a00_size, 0.);
3423 for (auto b : a10_blocks) {
3424 const auto &ri = a11_indexing.at(b->getRowUId());
3425 const auto &ci = a00_indexing.at(b->getColUId());
3426 copy_block(a10_mat.data(), a00_size, ri.index, ci.index,
3427 storage.blockPtr(*b), b->getLd(), b->getNbRows(),
3428 b->getNbCols());
3429 }
3430
3431 // S = -A10 A00^{-1} A01
3432 s_mat.resize(static_cast<size_t>(a11_size) * a11_size);
3433 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasTrans, a11_size, a11_size,
3434 a00_size, -1., a10_mat.data(), a00_size, a01t_mat.data(),
3435 a00_size, 0., s_mat.data(), a11_size);
3436
3437 glob_idx.resize(a11_size);
3438 for (auto &pi : a11_indexing) {
3439 auto &[idx, size, glob] = pi.second;
3440 std::iota(glob_idx.begin() + idx, glob_idx.begin() + idx + size, glob);
3441 }
3442 CHKERR AOApplicationToPetsc(ao, a11_size, glob_idx.data());
3443 CHKERR MatSetValues(S, a11_size, glob_idx.data(), a11_size, glob_idx.data(),
3444 s_mat.data(), ADD_VALUES);
3445 }
3446
3447 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_AssembleSchurMat, 0, 0, 0, 0);
3448
3450}
3451
3452boost::shared_ptr<std::vector<double>> getBlockMatStorageMat(Mat B) {
3453 BlockStructure *ctx;
3454 CHKERR MatShellGetContext(B, (void **)&ctx);
3455 ctx->storage->ensureAllocated();
3456 return boost::shared_ptr<std::vector<double>>(ctx->storage,
3457 &ctx->storage->data);
3458}
3459
3460boost::shared_ptr<std::vector<double>>
3462 // preconditioner blocks are stored in buffers of A00 elements
3463 return boost::make_shared<std::vector<double>>();
3464}
3465
3467 BlockStructure *ctx, const EntitiesFieldData::EntData &row_data,
3468 const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat,
3469 InsertMode iora, bool is_preconditioner) {
3470
3472
3473 if (row_data.getIndices().empty())
3475 if (col_data.getIndices().empty())
3477
3478#ifndef NDEBUG
3479
3480 PetscLogEventBegin(SchurEvents::MOFEM_EVENT_BlockStructureSetValues, 0, 0, 0,
3481 0);
3482
3483#endif // NDEBUG
3484
3485 switch (iora) {
3486 case ADD_VALUES:
3487 case INSERT_VALUES:
3488 break;
3489 default:
3490 SETERRQ(PETSC_COMM_SELF, MOFEM_NOT_IMPLEMENTED, "Wrong InsertMode");
3491 }
3492
3493 auto &storage = *ctx->storage;
3494 CHKERR storage.modified();
3495 storage.ensureAllocated();
3496
3497 auto get_row_data = [&]() -> std::pair<bool, VectorInt> {
3498 if (auto e_ptr = row_data.getFieldEntities()[0]) {
3499 if (auto stored_data_ptr =
3500 e_ptr->getSharedStoragePtr<EssentialBcStorage>()) {
3501 MOFEM_LOG("SELF", Sev::warning) << "Can lead to unhandled behaviour";
3502 return std::make_pair(true, stored_data_ptr->entityIndices);
3503 }
3504 }
3505 return std::make_pair(false, row_data.getIndices());
3506 };
3507
3508 auto row_indices = get_row_data();
3509 const auto &row_ind = row_indices.second;
3510 const auto &col_ind = col_data.getIndices();
3511
3512 std::vector<int> ent_row_indices;
3513 std::vector<int> ent_col_indices;
3514
3515 auto get_ent_indices = [](auto &cache, auto &ind) {
3516 ind.clear();
3517 ind.reserve(std::distance(cache->loHi[0], cache->loHi[1]));
3518 for (auto e = cache->loHi[0]; e != cache->loHi[1]; ++e) {
3519 auto glob = (*e)->getPetscGlobalDofIdx();
3520 if (glob != -1)
3521 ind.push_back(glob);
3522 }
3523 };
3524
3525 for (auto &rent : row_data.getFieldEntities()) {
3526 auto r_cache = rent->entityCacheRowDofs.lock();
3527 if (!r_cache)
3528 continue;
3529 get_ent_indices(r_cache, ent_row_indices);
3530 if (ent_row_indices.empty())
3531 continue;
3532 const auto &row_uid = rent->getLocalUniqueId();
3533
3534 for (auto &cent : col_data.getFieldEntities()) {
3535 auto c_cache = cent->entityCacheColDofs.lock();
3536 if (!c_cache)
3537 continue;
3538 get_ent_indices(c_cache, ent_col_indices);
3539 if (ent_col_indices.empty())
3540 continue;
3541 const auto &col_uid = cent->getLocalUniqueId();
3542
3543 auto idx = storage.find(row_uid, col_uid);
3544 if (!idx) {
3545 MOFEM_LOG_CHANNEL("SELF");
3546 MOFEM_TAG_AND_LOG("SELF", Sev::error, "BlockMat")
3547 << "missing block: row " << row_data.getFieldDofs()[0]->getName()
3548 << " col " << col_data.getFieldDofs()[0]->getName();
3549 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3550 "Block not allocated");
3551 }
3552 if (is_preconditioner && idx->getElem() == -1)
3553 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3554 "Preconditioner blocks are allocated only for A00 element "
3555 "matrices");
3556
3557 const int nb_rows = idx->getNbRows();
3558 const int nb_cols = idx->getNbCols();
3559 const int ld = idx->getLd();
3560 double *s_mat = storage.blockPtr(*idx, is_preconditioner);
3561
3562 if (mat.size1() == ent_row_indices.size() &&
3563 mat.size2() == ent_col_indices.size()) {
3564
3565 const double *m = mat.data().data();
3566 if (ld == nb_cols) {
3567 if (iora == ADD_VALUES)
3568 cblas_daxpy(nb_rows * nb_cols, 1.0, m, 1, s_mat, 1);
3569 else
3570 cblas_dcopy(nb_rows * nb_cols, m, 1, s_mat, 1);
3571 } else {
3572 for (int i = 0; i != nb_rows; ++i) {
3573 if (iora == ADD_VALUES)
3574 cblas_daxpy(nb_cols, 1.0, m + static_cast<size_t>(i) * nb_cols, 1,
3575 s_mat + static_cast<size_t>(i) * ld, 1);
3576 else
3577 cblas_dcopy(nb_cols, m + static_cast<size_t>(i) * nb_cols, 1,
3578 s_mat + static_cast<size_t>(i) * ld, 1);
3579 }
3580 }
3581
3582 } else {
3583
3584 auto ri = row_ind.begin();
3585 auto rie = row_ind.end();
3586
3587 int row = 0;
3588 for (auto re : ent_row_indices) {
3589 ri = std::find(ri, rie, re);
3590 if (ri != rie) {
3591
3592 auto ci = col_ind.begin();
3593 auto cie = col_ind.end();
3594
3595 int col = 0;
3596 for (auto ce : ent_col_indices) {
3597 ci = std::find(ci, cie, ce);
3598 if (ci != cie) {
3599 auto &m = s_mat[static_cast<size_t>(row) * ld + col];
3600 if (iora == ADD_VALUES) {
3601 m += mat(std::distance(row_ind.begin(), ri),
3602 std::distance(col_ind.begin(), ci));
3603 } else {
3604 m = mat(std::distance(row_ind.begin(), ri),
3605 std::distance(col_ind.begin(), ci));
3606 }
3607 }
3608 ++col;
3609 } // cols
3610 }
3611 ++row;
3612 } // rows
3613 }
3614
3615 } // iterate entities
3616 }
3617
3618#ifndef NDEBUG
3619 PetscLogEventEnd(SchurEvents::MOFEM_EVENT_BlockStructureSetValues, 0, 0, 0,
3620 0);
3621#endif // NDEBUG
3622
3624}
3625
3626MoFEMErrorCode
3628 const EntitiesFieldData::EntData &col_data,
3629 const MatrixDouble &mat, InsertMode iora) {
3631 PetscBool is_mat_shell = PETSC_FALSE;
3632 PetscObjectTypeCompare((PetscObject)M, MATSHELL, &is_mat_shell);
3633 if (is_mat_shell) {
3634 BlockStructure *ctx;
3635 CHKERR MatShellGetContext(M, (void **)&ctx);
3636 CHKERR shell_block_mat_asmb_wrap_impl(ctx, row_data, col_data, mat, iora,
3637 false);
3638 } else {
3639 CHKERR MatSetValues<AssemblyTypeSelector<PETSC>>(M, row_data, col_data, mat,
3640 iora);
3641 }
3643}
3644
3646 Mat M, const EntitiesFieldData::EntData &row_data,
3647 const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat,
3648 InsertMode iora) {
3650 PetscBool is_mat_shell = PETSC_FALSE;
3651 PetscObjectTypeCompare((PetscObject)M, MATSHELL, &is_mat_shell);
3652 if (is_mat_shell) {
3653 BlockStructure *ctx;
3654 CHKERR MatShellGetContext(M, (void **)&ctx);
3655 if (!ctx->storage->hasPreconditioner)
3656 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
3657 "No preconditioner blocks");
3658 CHKERR shell_block_mat_asmb_wrap_impl(ctx, row_data, col_data, mat, iora,
3659 true);
3660 } else {
3661 CHKERR MatSetValues<AssemblyTypeSelector<PETSC>>(M, row_data, col_data, mat,
3662 iora);
3663 }
3665}
3666
3667boost::shared_ptr<NestSchurData> createSchurNestedMatrixStruture(
3668
3669 std::pair<SmartPetscObj<DM>, SmartPetscObj<DM>> dms,
3670 boost::shared_ptr<BlockStructure> block_mat_data_ptr,
3671
3672 std::vector<std::string> fields_names, //< a00 fields
3673 std::vector<boost::shared_ptr<Range>>
3674 field_ents, //< a00 ranges (can be null),
3675 bool add_preconditioner_block) {
3676
3677 if (!block_mat_data_ptr) {
3678 CHK_THROW_MESSAGE(MOFEM_DATA_INCONSISTENCY, "Block data not set");
3679 }
3680
3681 if (fields_names.size() != field_ents.size())
3683 "fields_names.size() != field_ents.size()");
3684
3685 auto &storage = *block_mat_data_ptr->storage;
3686 auto &index = storage.blockIndex;
3687 const int nb_blocks = index.size();
3688
3689 if (storage.isFactorised())
3691 "Block matrix is factorised in place");
3692 // layout of storage is changed, that would invalidate nested matrices
3693 // created before
3694 if (storage.nested)
3696 "Nested Schur structure already created for this block "
3697 "matrix, create new one with createBlockMatStructure");
3698
3699 {
3700 PetscBool dense = PETSC_FALSE;
3701 CHKERR PetscOptionsGetBool(PETSC_NULLPTR, "", "-schur_a00_dense", &dense,
3702 PETSC_NULLPTR);
3703 storage.optDense = dense == PETSC_TRUE;
3704 CHKERR ::PetscOptionsGetReal(PETSC_NULLPTR, "",
3705 "-schur_a00_pivot_threshold",
3706 &storage.optPivotThreshold, PETSC_NULLPTR);
3707 CHKERR ::PetscOptionsGetReal(PETSC_NULLPTR, "", "-schur_a00_rcond_min",
3708 &storage.optRcondMin, PETSC_NULLPTR);
3709 CHKERR ::PetscOptionsGetReal(PETSC_NULLPTR, "", "-schur_a00_backward_error",
3710 &storage.optBackwardError, PETSC_NULLPTR);
3711 }
3712
3713 int nb_local_parent;
3714 CHKERR VecGetLocalSize(block_mat_data_ptr->ghostX, &nb_local_parent);
3715
3716 auto [schur_dm, block_dm] = dms;
3717 auto schur_prb = getProblemPtr(schur_dm);
3718 auto block_prb = getProblemPtr(block_dm);
3719 auto m_field_ptr = getInterfacePtr(block_dm);
3720
3721 auto schur_vec_x = createDMVector(schur_dm);
3722 auto block_vec_x = createDMVector(block_dm);
3723 auto schur_vec_y = vectorDuplicate(schur_vec_x);
3724 auto block_vec_y = vectorDuplicate(block_vec_x);
3725 CHKERR VecSetDM(schur_vec_x, PETSC_NULLPTR);
3726 CHKERR VecSetDM(block_vec_x, PETSC_NULLPTR);
3727 CHKERR VecSetDM(schur_vec_y, PETSC_NULLPTR);
3728 CHKERR VecSetDM(block_vec_y, PETSC_NULLPTR);
3729
3730 auto find_field_ent = [&](auto uid, auto prb, auto rc) {
3731 boost::shared_ptr<NumeredDofEntity_multiIndex> dofs;
3732
3733 switch (rc) {
3734 case ROW:
3735 dofs = prb->getNumeredRowDofsPtr();
3736 break;
3737 case COL:
3738 dofs = prb->getNumeredColDofsPtr();
3739 break;
3740 default:
3742 break;
3743 }
3744
3745 auto lo = dofs->get<Unique_mi_tag>().lower_bound(uid);
3746 if (lo == dofs->get<Unique_mi_tag>().end())
3747 return boost::shared_ptr<NumeredDofEntity>();
3748 auto hi = dofs->get<Unique_mi_tag>().upper_bound(
3749 DofEntity::getUniqueIdCalculate(MAX_DOFS_ON_ENTITY - 1, uid));
3750 if (lo != hi)
3751 return *lo;
3752
3753 return boost::shared_ptr<NumeredDofEntity>();
3754 };
3755
3756 // indices of block entities in sub-problems, -1 if entity is not in
3757 // sub-problem
3758 struct SubIdx {
3759 int glob = -1;
3760 int loc = -1;
3761 };
3762 struct SubIdxs {
3763 SubIdx schurRow, schurCol, blockRow, blockCol;
3764 };
3765 std::vector<SubIdxs> sub(nb_blocks);
3766 auto get_sub = [&](auto uid, auto prb, auto rc) {
3767 SubIdx s;
3768 if (auto dof = find_field_ent(uid, prb, rc)) {
3769 s.glob = dof->getPetscGlobalDofIdx();
3770 s.loc = dof->getPetscLocalDofIdx();
3771 }
3772 return s;
3773 };
3774 for (int i = 0; i != nb_blocks; ++i) {
3775 auto &d = index[i];
3776 sub[i].schurRow = get_sub(d.getRowUId(), schur_prb, ROW);
3777 sub[i].schurCol = get_sub(d.getColUId(), schur_prb, COL);
3778 sub[i].blockRow = get_sub(d.getRowUId(), block_prb, ROW);
3779 sub[i].blockCol = get_sub(d.getColUId(), block_prb, COL);
3780 }
3781
3782 // A00 elements: blocks with both entities in block sub-problem, grouped by
3783 // finite element, stored in element buffers
3784 std::vector<std::pair<size_t, int>> old_layout(nb_blocks);
3785 for (int i = 0; i != nb_blocks; ++i) {
3786 old_layout[i] = std::make_pair(index[i].getMatShift(), index[i].getLd());
3787 index[i].setStorage(0, index[i].getNbCols(), -1);
3788 }
3789
3790 std::vector<int> a00;
3791 for (int i = 0; i != nb_blocks; ++i) {
3792 if (sub[i].blockRow.glob != -1 && sub[i].blockCol.glob != -1)
3793 a00.push_back(i);
3794 }
3795 std::sort(a00.begin(), a00.end(), [&index](int a, int b) {
3796 if (index[a].getFEUId() != index[b].getFEUId())
3797 return index[a].getFEUId() < index[b].getFEUId();
3798 return a < b;
3799 });
3800
3801 storage.patterns.clear();
3802 storage.elems.clear();
3803 storage.elemEntities.clear();
3804 storage.maxElemSize = 0;
3805 int nb_pivots = 0;
3806 std::map<std::vector<long>, int> pattern_map;
3807 std::vector<BlockStorage::ElemEntity> ents;
3808 std::vector<long> key;
3809
3810 for (size_t p = 0; p != a00.size();) {
3811 const auto fe_uid = index[a00[p]].getFEUId();
3812 const size_t p_begin = p;
3813 ents.clear();
3814 for (; p != a00.size() && index[a00[p]].getFEUId() == fe_uid; ++p) {
3815 const auto i = a00[p];
3816 auto &d = index[i];
3817 ents.push_back({d.getRowUId(), d.getNbRows(), 0, d.getLocRow(),
3818 d.getRow(), sub[i].blockRow.loc, sub[i].blockRow.glob});
3819 ents.push_back({d.getColUId(), d.getNbCols(), 0, d.getLocCol(),
3820 d.getCol(), sub[i].blockCol.loc, sub[i].blockCol.glob});
3821 }
3822 std::sort(ents.begin(), ents.end(),
3823 [](auto &a, auto &b) { return a.uid < b.uid; });
3824 ents.erase(std::unique(ents.begin(), ents.end(),
3825 [](auto &a, auto &b) { return a.uid == b.uid; }),
3826 ents.end());
3827 const int k = ents.size();
3828 auto node_of = [&](const UId &uid) {
3829 return static_cast<int>(
3830 std::lower_bound(ents.begin(), ents.end(), uid,
3831 [](auto &e, auto &u) { return e.uid < u; }) -
3832 ents.begin());
3833 };
3834
3835 // pattern: fields, entity types and sizes of nodes, listed pairs
3836 std::vector<char> listed(k * k, 0);
3837 for (size_t q = p_begin; q != p; ++q) {
3838 auto &d = index[a00[q]];
3839 listed[node_of(d.getRowUId()) * k + node_of(d.getColUId())] = 1;
3840 }
3841 key.clear();
3842 key.push_back(k);
3843 int n = 0;
3844 for (auto &e : ents) {
3845 key.push_back(FieldEntity::getFieldBitNumberFromUniqueId(e.uid));
3846 key.push_back(
3847 type_from_handle(FieldEntity::getHandleFromUniqueId(e.uid)));
3848 key.push_back(e.nb);
3849 n += e.nb;
3850 }
3851 key.insert(key.end(), listed.begin(), listed.end());
3852 auto pit = pattern_map.find(key);
3853 if (pit == pattern_map.end()) {
3855 pat.nbNodes = k;
3856 pat.size = n;
3857 pat.listed = listed;
3858 pat.nbListed = std::count(listed.begin(), listed.end(), 1);
3859 for (auto &e : ents) {
3860 pat.nodeSize.push_back(e.nb);
3861 pat.nodeName.push_back(
3862 m_field_ptr->get_field_name(field_bit_from_bit_number(
3863 FieldEntity::getFieldBitNumberFromUniqueId(e.uid))));
3864 }
3865 if (storage.optDense)
3866 make_block_layout(pat, {});
3867 else
3868 make_listed_layout(pat);
3869 pit = pattern_map.emplace(key, storage.patterns.size()).first;
3870 storage.patterns.push_back(pat);
3871 }
3872 const int pi = pit->second;
3873 auto &pat = storage.patterns[pi];
3874 ++pat.nbElems;
3875
3876 const int elem_idx = storage.elems.size();
3877 const int ent_begin = storage.elemEntities.size();
3878 for (int r = 0; r != k; ++r)
3879 ents[r].offset = pat.nodeOffset[r];
3880 storage.elems.push_back({pi, n, ent_begin, ent_begin + k, nb_pivots});
3881 storage.elemEntities.insert(storage.elemEntities.end(), ents.begin(),
3882 ents.end());
3883 for (size_t q = p_begin; q != p; ++q) {
3884 auto &d = index[a00[q]];
3885 const int r = node_of(d.getRowUId()), c = node_of(d.getColUId());
3886 d.setStorage(pat.sShift(r, c), pat.sLd(r, c), elem_idx);
3887 }
3888
3889 nb_pivots += n;
3890 storage.maxElemSize = std::max(storage.maxElemSize, n);
3891
3892 auto &elem = storage.elems.back();
3893 elem.local = true;
3894 elem.owned = true;
3895 for (auto &e : ents) {
3896 if (!e.nb)
3897 continue;
3898 if (e.locParent < 0)
3899 elem.local = false;
3900 if (e.locParent < 0 || e.locParent >= nb_local_parent)
3901 elem.owned = false;
3902 }
3903 }
3904
3905 // eliminated entities have to be element local, i.e. each A00 entity
3906 // belongs to exactly one element
3907 {
3908 std::vector<UId> uids;
3909 uids.reserve(storage.elemEntities.size());
3910 for (auto &e : storage.elemEntities)
3911 uids.push_back(e.uid);
3912 std::sort(uids.begin(), uids.end());
3913 if (std::adjacent_find(uids.begin(), uids.end()) != uids.end())
3915 "Entity of A00 field is shared by more than one "
3916 "element, eliminated fields have to be element local "
3917 "(L2, broken spaces, or interior DOFs)");
3918 }
3919
3920 // blocks not in A00 elements are stored in data
3921 size_t mem_size = 0;
3922 for (auto &d : index) {
3923 if (d.getElem() == -1) {
3924 d.setStorage(mem_size, d.getNbCols(), -1);
3925 mem_size += static_cast<size_t>(d.getNbRows()) * d.getNbCols();
3926 }
3927 }
3928
3929 const int nb_elems = storage.elems.size();
3930 storage.hasPreconditioner = add_preconditioner_block;
3931 storage.elemData.assign(nb_elems, std::vector<double>());
3932 storage.elemPrec.assign(add_preconditioner_block ? nb_elems : 0,
3933 std::vector<double>());
3934 storage.elemDense.assign(nb_elems, std::vector<double>());
3935 storage.maxElemBuffer = 0;
3936 for (int ei = 0; ei != nb_elems; ++ei) {
3937 const auto sz = storage.patterns[storage.elems[ei].pattern].elemSize;
3938 // capacity of dense matrix, which is upper bound of any layout, is
3939 // reserved, such that buffers are relaid in place, see relayout_pattern
3940 const auto n = storage.elems[ei].size;
3941 storage.elemData[ei].reserve(static_cast<size_t>(n) * n);
3942 storage.elemData[ei].assign(sz, 0.);
3943 if (add_preconditioner_block) {
3944 storage.elemPrec[ei].reserve(static_cast<size_t>(n) * n);
3945 storage.elemPrec[ei].assign(sz, 0.);
3946 }
3947 storage.maxElemBuffer = std::max(storage.maxElemBuffer, sz);
3948 }
3949
3950 // move data to new layout, data is allocated on first use, thus usually
3951 // nothing is moved
3952 auto &data = storage.data;
3953 const bool has_values =
3954 std::any_of(data.begin(), data.end(), [](double v) { return v != 0.; });
3955 if (has_values) {
3956 std::vector<double> new_data(mem_size, 0.);
3957 for (int i = 0; i != nb_blocks; ++i) {
3958 auto &d = index[i];
3959 auto [old_shift, old_ld] = old_layout[i];
3960 double *dst = d.getElem() >= 0
3961 ? storage.elemData[d.getElem()].data() + d.getMatShift()
3962 : new_data.data() + d.getMatShift();
3963 copy_block(dst, d.getLd(), 0, 0, data.data() + old_shift, old_ld,
3964 d.getNbRows(), d.getNbCols());
3965 }
3966 data.swap(new_data);
3967 } else {
3968 std::vector<double>().swap(data);
3969 }
3970 storage.dataSize = mem_size;
3971 storage.ensureAllocated();
3972 storage.pivots.assign(nb_pivots, 0);
3973 storage.factorStorage = BlockStorage::FACTOR_NONE;
3974
3975 set_parent_blocks(*block_mat_data_ptr);
3976
3977 // nested matrices share storage, each keeps blocks in its own numbering
3978 std::array<boost::shared_ptr<BlockStructure>, 4> data_ptrs;
3979 for (auto r = 0; r != 4; ++r) {
3980 data_ptrs[r] = boost::make_shared<BlockStructure>();
3981 data_ptrs[r]->storage = block_mat_data_ptr->storage;
3982 }
3983 data_ptrs[3]->hasElems = !storage.elems.empty();
3984 data_ptrs[3]->numbering = BlockStructure::BLOCK;
3985
3986 for (int i = 0; i != nb_blocks; ++i) {
3987 auto &d = index[i];
3988 auto &s = sub[i];
3989 auto add = [&](auto &m, const SubIdx &r, const SubIdx &c) {
3990 m->blocks.push_back({d.getMatShift(), d.getLd(), r.glob, c.glob,
3991 d.getNbRows(), d.getNbCols(), r.loc, c.loc});
3992 };
3993 if (s.schurRow.glob != -1 && s.schurCol.glob != -1)
3994 add(data_ptrs[0], s.schurRow, s.schurCol);
3995 if (s.schurRow.glob != -1 && s.blockCol.glob != -1)
3996 add(data_ptrs[1], s.schurRow, s.blockCol);
3997 if (s.blockRow.glob != -1 && s.schurCol.glob != -1)
3998 add(data_ptrs[2], s.blockRow, s.schurCol);
3999 // blocks with both entities in block sub-problem are element blocks
4000 if (s.blockRow.glob != -1 && s.blockCol.glob != -1 && d.getElem() == -1)
4001 CHK_THROW_MESSAGE(MOFEM_DATA_INCONSISTENCY, "Block not in element");
4002 }
4003 for (auto r = 0; r != 3; ++r)
4004 data_ptrs[r]->blocks.shrink_to_fit();
4005
4006 data_ptrs[0]->ghostX = schur_vec_x;
4007 data_ptrs[0]->ghostY = schur_vec_y;
4008 data_ptrs[1]->ghostX = block_vec_x;
4009 data_ptrs[1]->ghostY = schur_vec_y;
4010 data_ptrs[2]->ghostX = schur_vec_x;
4011 data_ptrs[2]->ghostY = block_vec_y;
4012 data_ptrs[3]->ghostX = block_vec_x;
4013 data_ptrs[3]->ghostY = block_vec_y;
4014
4015 if (add_preconditioner_block) {
4016 data_ptrs[3]->multiplyByPreconditioner = true;
4017 block_mat_data_ptr->multiplyByPreconditioner = false;
4018 }
4019
4020 MPI_Comm comm;
4021 CHKERR PetscObjectGetComm((PetscObject)schur_dm, &comm);
4022
4023 auto create_shell_mat = [&](auto nb_r_loc, auto nb_c_loc, auto nb_r_glob,
4024 auto nb_c_glob, auto data_ptr) {
4025 Mat mat_raw;
4026 CHKERR MatCreateShell(comm, nb_r_loc, nb_c_loc, nb_r_glob, nb_c_glob,
4027 (void *)data_ptr.get(), &mat_raw);
4028 CHKERR setSchurBlockMatOps(mat_raw);
4029 return SmartPetscObj<Mat>(mat_raw);
4030 };
4031
4032 auto schur_nb_global = schur_prb->getNbDofsRow();
4033 auto block_nb_global = block_prb->getNbDofsRow();
4034 auto schur_nb_local = schur_prb->getNbLocalDofsRow();
4035 auto block_nb_local = block_prb->getNbLocalDofsRow();
4036
4037 std::array<SmartPetscObj<Mat>, 4> mats_array;
4038 mats_array[0] =
4039 create_shell_mat(schur_nb_local, schur_nb_local, schur_nb_global,
4040 schur_nb_global, data_ptrs[0]);
4041 mats_array[1] =
4042 create_shell_mat(schur_nb_local, block_nb_local, schur_nb_global,
4043 block_nb_global, data_ptrs[1]);
4044 mats_array[2] =
4045 create_shell_mat(block_nb_local, schur_nb_local, block_nb_global,
4046 schur_nb_global, data_ptrs[2]);
4047 mats_array[3] =
4048 create_shell_mat(block_nb_local, block_nb_local, block_nb_global,
4049 block_nb_global, data_ptrs[3]);
4050
4051 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "NestedSchur")
4052 << "(0, 0) " << schur_nb_local << " " << schur_nb_global << " "
4053 << data_ptrs[0]->blocks.size();
4054 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "NestedSchur")
4055 << "(0, 1) " << schur_nb_local << " " << block_nb_local << " "
4056 << schur_nb_global << " " << block_nb_global << " "
4057 << data_ptrs[1]->blocks.size();
4058 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "NestedSchur")
4059 << "(1, 0) " << block_nb_local << " " << schur_nb_local << " "
4060 << block_nb_global << " " << schur_nb_global << " "
4061 << data_ptrs[2]->blocks.size();
4062 MOFEM_TAG_AND_LOG("SYNC", Sev::verbose, "NestedSchur")
4063 << "(1, 1) " << block_nb_local << " " << block_nb_global << " "
4064 << storage.elems.size() << " elements (" << a00.size() << " blocks, "
4065 << storage.patterns.size() << " patterns) max size "
4066 << storage.maxElemSize;
4067
4068 MOFEM_LOG_SEVERITY_SYNC(comm, Sev::verbose);
4069
4070 {
4071 // element storage: listed blocks, dense element matrices, and block
4072 // elimination estimated from pattern structure only
4073 size_t listed_d = 0, dense_d = 0, struct_d = 0, buffers_d = 0;
4074 for (auto &pat : storage.patterns) {
4075 auto est = pat;
4077 listed_d += pat.nbElems * listed_size(pat);
4078 dense_d += pat.nbElems * static_cast<size_t>(pat.size) * pat.size;
4079 struct_d += pat.nbElems * est.elemSize;
4080 buffers_d += pat.nbElems * pat.elemSize;
4081 MOFEM_TAG_AND_LOG("WORLD", Sev::verbose, "NestedSchur")
4082 << "A00 pattern (" << pat.nbElems << " elements): "
4083 << describe_nodes(pat,
4084 [&]() {
4085 std::vector<int> all(pat.nbNodes);
4086 std::iota(all.begin(), all.end(), 0);
4087 return all;
4088 }())
4089 << "; listed pairs " << pat.nbListed << " of "
4090 << pat.nbNodes * pat.nbNodes << "; doubles per element: listed "
4091 << listed_size(pat) << ", dense "
4092 << static_cast<size_t>(pat.size) * pat.size
4093 << ", structural estimate " << est.elemSize << " (eliminated "
4094 << describe_nodes(est, est.order) << ")";
4095 }
4096 // data of elements and blocks with all rows and columns not owned by this
4097 // process (such blocks are not assembled by this process)
4098 size_t ghost_elems = 0, ghost_elems_data = 0, ghost_blocks_data = 0;
4099 for (int ei = 0; ei != nb_elems; ++ei) {
4100 auto &e = storage.elems[ei];
4101 bool all_ghost = true;
4102 for (int k = e.entBegin; k != e.entEnd; ++k)
4103 if (storage.elemEntities[k].locParent < nb_local_parent)
4104 all_ghost = false;
4105 if (all_ghost) {
4106 ++ghost_elems;
4107 ghost_elems_data += storage.elemData[ei].size();
4108 }
4109 }
4110 for (auto &d : index)
4111 if (d.getElem() == -1 && d.getLocRow() >= nb_local_parent &&
4112 d.getLocCol() >= nb_local_parent)
4113 ghost_blocks_data += static_cast<size_t>(d.getNbRows()) * d.getNbCols();
4114
4115 constexpr size_t dsz = sizeof(double);
4116 std::array<size_t, 10> loc_stats = {
4117 storage.elems.size(),
4118 buffers_d * dsz,
4119 listed_d * dsz,
4120 dense_d * dsz,
4121 struct_d * dsz,
4122 mem_size * dsz,
4123 (add_preconditioner_block ? buffers_d : 0) * dsz,
4124 ghost_elems,
4125 ghost_elems_data * dsz,
4126 ghost_blocks_data * dsz};
4127 std::array<size_t, 10> sum_stats, max_stats;
4128 MPI_Allreduce(loc_stats.data(), sum_stats.data(), 10, MPIU_SIZE_T, MPI_SUM,
4129 comm);
4130 MPI_Allreduce(loc_stats.data(), max_stats.data(), 10, MPIU_SIZE_T, MPI_MAX,
4131 comm);
4132 constexpr double mb = 1024. * 1024.;
4133 MOFEM_TAG_AND_LOG("WORLD", Sev::verbose, "NestedSchur")
4134 << "A00 elements " << sum_stats[0] << " (max per process "
4135 << max_stats[0] << "), element buffers " << sum_stats[1] / mb
4136 << " MB (max per process " << max_stats[1] / mb
4137 << " MB); listed blocks " << sum_stats[2] / mb
4138 << " MB, dense element matrices would be " << sum_stats[3] / mb
4139 << " MB, structural estimate of block elimination " << sum_stats[4] / mb
4140 << " MB; other blocks data " << sum_stats[5] / mb
4141 << " MB (max per process " << max_stats[5] / mb
4142 << " MB); preconditioner blocks " << sum_stats[6] / mb << " MB";
4143 MOFEM_TAG_AND_LOG("WORLD", Sev::verbose, "NestedSchur")
4144 << "Not owned (ghost) elements " << sum_stats[7] << " data "
4145 << sum_stats[8] / mb << " MB, not owned other blocks data "
4146 << sum_stats[9] / mb << " MB";
4147 }
4148
4149 auto schur_is = schur_prb->getSubData()->getSmartRowIs();
4150 auto block_is = block_prb->getSubData()->getSmartRowIs();
4151
4152 storage.nested = true;
4153
4154 return boost::make_shared<NestSchurData>(
4155
4156 mats_array, data_ptrs, block_mat_data_ptr,
4157 std::make_pair(schur_is, block_is)
4158
4159 );
4160}
4161
4162std::pair<SmartPetscObj<Mat>, boost::shared_ptr<NestSchurData>>
4163createSchurNestedMatrix(boost::shared_ptr<NestSchurData> schur_net_data_ptr) {
4164
4165 if (!schur_net_data_ptr)
4167
4168 auto [mat_arrays, data_ptrs, block_mat_data_ptr, iss] = *schur_net_data_ptr;
4169 auto [schur_is, block_is] = iss;
4170
4171 std::array<IS, 2> is_a = {schur_is, block_is};
4172 std::array<Mat, 4> mats_a = {
4173
4174 mat_arrays[0], mat_arrays[1], mat_arrays[2], mat_arrays[3]
4175
4176 };
4177
4178 MPI_Comm comm;
4179 CHKERR PetscObjectGetComm((PetscObject)mat_arrays[0], &comm);
4180
4181 Mat mat_raw;
4182 CHKERR MatCreateNest(
4183
4184 comm, 2, is_a.data(), 2, is_a.data(), mats_a.data(), &mat_raw
4185
4186 );
4187
4188 return std::make_pair(SmartPetscObj<Mat>(mat_raw), schur_net_data_ptr);
4189}
4190
4194
4195OpSchurAssembleBase *
4196createOpSchurAssembleEnd(std::vector<std::string> fields_name,
4197 std::vector<boost::shared_ptr<Range>> field_ents,
4199 bool sym_schur, bool symm_op) {
4200 if (symm_op)
4201 return new OpSchurAssembleEnd<SchurDSYSV>(fields_name, field_ents, ao,
4202 schur, sym_schur, symm_op);
4203 else
4204 return new OpSchurAssembleEnd<SchurDGESV>(fields_name, field_ents, ao,
4205 schur, sym_schur, symm_op);
4206}
4207
4209 boost::shared_ptr<std::vector<unsigned char>> marker_ptr, double diag_val) {
4210 return new OpSchurZeroRowsAndCols(marker_ptr, diag_val);
4211}
4212
4213OpSchurAssembleBase *
4214createOpSchurAssembleEnd(std::vector<std::string> fields_name,
4215 std::vector<boost::shared_ptr<Range>> field_ents,
4216 std::vector<SmartPetscObj<AO>> sequence_of_aos,
4217 std::vector<SmartPetscObj<Mat>> sequence_of_mats,
4218 std::vector<bool> sym_schur, bool symm_op,
4219 boost::shared_ptr<BlockStructure> diag_blocks) {
4221 fields_name, field_ents, sequence_of_aos.back(), sequence_of_mats.back(),
4222 sym_schur.back(), symm_op);
4223}
4224
4225OpSchurAssembleBase *
4226createOpSchurAssembleEnd(std::vector<std::string> fields_name,
4227 std::vector<boost::shared_ptr<Range>> field_ents,
4228 std::vector<SmartPetscObj<AO>> sequence_of_aos,
4229 std::vector<SmartPetscObj<Mat>> sequence_of_mats,
4230 std::vector<bool> sym_schur,
4231 std::vector<double> diag_eps, bool symm_op,
4232 boost::shared_ptr<BlockStructure> diag_blocks) {
4234 fields_name, field_ents, sequence_of_aos.back(), sequence_of_mats.back(),
4235 sym_schur.back(), symm_op);
4236}
4237
4240
4241 auto apply = [](PC pc, Vec f, Vec x) {
4243 Mat A, P;
4244 CHKERR PCGetOperators(pc, &A, &P);
4245 CHKERR MatSolve(P, f, x);
4247 };
4248
4249 auto apply_transpose = [](PC pc, Vec f, Vec x) {
4251 Mat A, P;
4252 CHKERR PCGetOperators(pc, &A, &P);
4253 CHKERR MatSolveTranspose(P, f, x);
4255 };
4256
4257 CHKERR PCSetType(pc, PCSHELL);
4258 CHKERR PCShellSetApply(pc, apply);
4259 CHKERR PCShellSetApplyTranspose(pc, apply_transpose);
4260 CHKERR PCShellSetName(pc, "MoFEMSchurBlockPC");
4261
4263}
4264
4265// This is used to assemble local element matrices for Schur complement
4266// and matrix for KSP
4267template <>
4269MatSetValues<SchurElemMats>(Mat M, const EntitiesFieldData::EntData &row_data,
4270 const EntitiesFieldData::EntData &col_data,
4271 const MatrixDouble &mat, InsertMode iora) {
4272 return SchurElemMats::MatSetValues(M, row_data, col_data, mat, iora);
4273}
4274
4275MoFEMErrorCode
4277 const EntitiesFieldData::EntData &col_data,
4278 const MatrixDouble &mat, InsertMode iora) {
4279 return MatSetValues<AssemblyTypeSelector<PETSC>>(M, row_data, col_data, mat,
4280 iora);
4281}
4282
4283// Is assumed that standard PETSc assembly works for matrices used by KSP
4284SchurBackendMatSetValuesPtr::MatSetValuesPtr
4285 SchurBackendMatSetValuesPtr::matSetValuesPtr = schur_mat_set_values_wrap;
4286
4287// We assemble matrix for KSP and store local matrices for Schur complement.
4288// Schur complement is calculated and assembled in OpSchurAssembleEnd.
4289MoFEMErrorCode
4290SchurElemMats::MatSetValues(Mat M, const EntitiesFieldData::EntData &row_data,
4291 const EntitiesFieldData::EntData &col_data,
4292 const MatrixDouble &mat, InsertMode iora) {
4294 CHKERR assembleStorage(row_data, col_data, mat, iora);
4295 CHKERR SchurBackendMatSetValuesPtr::matSetValuesPtr(M, row_data, col_data,
4296 mat, iora);
4298}
4299
4300// All is now wrapped in specialisation of
4301// MatSetValues<AssemblyTypeSelector<SCHUR>>
4302template <>
4303MoFEMErrorCode MatSetValues<AssemblyTypeSelector<SCHUR>>(
4304 Mat M, const EntitiesFieldData::EntData &row_data,
4305 const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat,
4306 InsertMode iora) {
4307 return MatSetValues<SchurElemMats>(M, row_data, col_data, mat, iora);
4308}
4309
4310// Standard (PETSc) assembly block matrices do noe work
4311template <>
4313MatSetValues<BlockStructure>(Mat M, const EntitiesFieldData::EntData &row_data,
4314 const EntitiesFieldData::EntData &col_data,
4315 const MatrixDouble &mat, InsertMode iora) {
4316 return shell_block_mat_asmb_wrap(M, row_data, col_data, mat, iora);
4317}
4318
4320
4321 static MoFEMErrorCode MatSetValues(Mat M,
4322 const EntitiesFieldData::EntData &row_data,
4323 const EntitiesFieldData::EntData &col_data,
4324 const MatrixDouble &mat, InsertMode iora);
4325};
4326
4328 SchurBackendMatSetValuesPtr::matSetValuesBlockPtr =
4330
4332SchurElemMatsBlock::MatSetValues(Mat M,
4333 const EntitiesFieldData::EntData &row_data,
4334 const EntitiesFieldData::EntData &col_data,
4335 const MatrixDouble &mat, InsertMode iora) {
4337 CHKERR assembleStorage(row_data, col_data, mat, iora);
4338 CHKERR SchurBackendMatSetValuesPtr::matSetValuesBlockPtr(M, row_data,
4339 col_data, mat, iora);
4341}
4342
4343template <>
4345MatSetValues<SchurElemMatsBlock>(Mat M,
4346 const EntitiesFieldData::EntData &row_data,
4347 const EntitiesFieldData::EntData &col_data,
4348 const MatrixDouble &mat, InsertMode iora) {
4349 return SchurElemMatsBlock::MatSetValues(M, row_data, col_data, mat, iora);
4350}
4351
4353
4354 static MoFEMErrorCode MatSetValues(Mat M,
4355 const EntitiesFieldData::EntData &row_data,
4356 const EntitiesFieldData::EntData &col_data,
4357 const MatrixDouble &mat, InsertMode iora);
4358};
4359
4361 SchurBackendMatSetValuesPtr::matSetValuesPreconditionedBlockPtr =
4363
4364MoFEMErrorCode SchurElemMatsPreconditionedBlock::MatSetValues(
4365 Mat M, const EntitiesFieldData::EntData &row_data,
4366 const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat,
4367 InsertMode iora) {
4369 CHKERR assembleStorage(row_data, col_data, mat, iora);
4370 CHKERR SchurBackendMatSetValuesPtr::matSetValuesPreconditionedBlockPtr(
4371 M, row_data, col_data, mat, iora);
4373}
4374
4375template <>
4376MoFEMErrorCode MatSetValues<SchurElemMatsPreconditionedBlock>(
4377 Mat M, const EntitiesFieldData::EntData &row_data,
4378 const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat,
4379 InsertMode iora) {
4380 return SchurElemMatsPreconditionedBlock::MatSetValues(M, row_data, col_data,
4381 mat, iora);
4382}
4383
4384MoFEMErrorCode
4385schurSwitchPreconditioner(boost::shared_ptr<BlockStructure> block_mat_data) {
4387 if (block_mat_data->multiplyByPreconditioner) {
4388 block_mat_data->multiplyByPreconditioner = false;
4389 } else {
4390 if (!block_mat_data->storage->hasPreconditioner) {
4391 SETERRQ(PETSC_COMM_SELF, MOFEM_DATA_INCONSISTENCY,
4392 "preconditioner blocks not set");
4393 }
4394 block_mat_data->multiplyByPreconditioner = true;
4395 }
4397}
4398
4399MoFEMErrorCode
4400schurSaveBlockMesh(boost::shared_ptr<BlockStructure> block_mat_data,
4401 std::string filename) {
4403
4404 moab::Core core;
4405 moab::Interface &moab = core;
4406
4407 ReadUtilIface *iface;
4408 CHKERR moab.query_interface(iface);
4409
4410 size_t size = 0;
4411 for_each_block(*block_mat_data,
4412 [&](double *, int, int, int, int, int, int, int) { ++size; });
4413
4414 EntityHandle starting_vertex;
4415 vector<double *> arrays_coord;
4416 CHKERR iface->get_node_coords(3, 4 * size, 0, starting_vertex, arrays_coord);
4417 EntityHandle starting_handle;
4418 EntityHandle *array;
4419 CHKERR iface->get_element_connect(size, 4, MBQUAD, 0, starting_handle, array);
4420
4421 double def_val_nrm2 = 0;
4422 Tag th_nrm2;
4423 CHKERR moab.tag_get_handle("nrm2", 1, MB_TYPE_DOUBLE, th_nrm2,
4424 MB_TAG_CREAT | MB_TAG_DENSE, &def_val_nrm2);
4425
4426 int def_val_mat_shift = 0;
4427 Tag th_mat_shift;
4428 CHKERR moab.tag_get_handle("mat_shift", 1, MB_TYPE_INTEGER, th_mat_shift,
4429 MB_TAG_CREAT | MB_TAG_DENSE, &def_val_mat_shift);
4430
4431 block_mat_data->storage->ensureAllocated();
4432
4433 int i = 0;
4434 for_each_block(*block_mat_data, [&](double *ptr, int ld, int row, int col,
4435 int nb_rows, int nb_cols, int, int) {
4436 // q0
4437 arrays_coord[0][4 * i + 0] = row;
4438 arrays_coord[1][4 * i + 0] = col;
4439 arrays_coord[2][4 * i + 0] = 0;
4440
4441 // q1
4442 arrays_coord[0][4 * i + 1] = row + nb_rows;
4443 arrays_coord[1][4 * i + 1] = col;
4444 arrays_coord[2][4 * i + 1] = 0;
4445
4446 // q2
4447 arrays_coord[0][4 * i + 2] = row + nb_rows;
4448 arrays_coord[1][4 * i + 2] = col + nb_cols;
4449 arrays_coord[2][4 * i + 2] = 0;
4450
4451 // q3
4452 arrays_coord[0][4 * i + 3] = row;
4453 arrays_coord[1][4 * i + 3] = col + nb_cols;
4454 arrays_coord[2][4 * i + 3] = 0;
4455
4456 // ele conn
4457 array[4 * i + 0] = starting_vertex + 4 * i + 0;
4458 array[4 * i + 1] = starting_vertex + 4 * i + 1;
4459 array[4 * i + 2] = starting_vertex + 4 * i + 2;
4460 array[4 * i + 3] = starting_vertex + 4 * i + 3;
4461
4462 double nrm2 = 0;
4463 for (int r = 0; r != nb_rows; ++r) {
4464 auto row_nrm2 =
4465 cblas_dnrm2(nb_cols, ptr + static_cast<size_t>(r) * ld, 1);
4466 nrm2 += row_nrm2 * row_nrm2;
4467 }
4468 nrm2 = std::sqrt(nrm2);
4469 int mat_shift = i;
4470 EntityHandle ele = starting_handle + i;
4471 CHKERR moab.tag_set_data(th_nrm2, &ele, 1, &nrm2);
4472 CHKERR moab.tag_set_data(th_mat_shift, &ele, 1, &mat_shift);
4473
4474 ++i;
4475 });
4476
4477 CHKERR iface->update_adjacencies(starting_handle, size, 4, array);
4478 CHKERR moab.write_file(filename.c_str(), "VTK", "");
4479
4481}
4482
4483} // namespace MoFEM
MPI_Comm comm
std::string type
std::string key
#define MOFEM_LOG_SEVERITY_SYNC(comm, severity)
Synchronise "SYNC" on curtain severity level.
#define MOFEM_TAG_AND_LOG(channel, severity, tag)
Tag and log in channel.
SmartPetscObj< Vec > work
PetscInt size
SmartPetscObj< IS > rows
std::string name
constexpr double a
@ COL
@ ROW
#define MAX_DOFS_ON_ENTITY
Maximal number of DOFs on entity.
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define MoFEMFunctionReturnHot(a)
Last executable line of each PETSc function used for error handling. Replaces return()
@ NOSPACE
Definition definitions.h:83
#define MoFEMFunctionBegin
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
#define BITFIELDID_SIZE
max number of fields
@ MOFEM_OPERATION_UNSUCCESSFUL
Definition definitions.h:34
@ MOFEM_DATA_INCONSISTENCY
Definition definitions.h:31
@ MOFEM_NOT_IMPLEMENTED
Definition definitions.h:32
#define MoFEMFunctionReturn(a)
Last executable line of each PETSc function used for error handling. Replaces return()
#define CHKERR
Inline error check.
#define MoFEMFunctionBeginHot
First executable line of each MoFEM function, used for error handling. Final line of MoFEM functions ...
auto fun
constexpr int order
auto createDMVector(DM dm, RowColData rc=RowColData::COL)
Get smart vector from DM.
Definition DMMoFEM.hpp:1250
PetscErrorCode DMoFEMLoopFiniteElementsUpAndLowRank(DM dm, const char fe_name[], MoFEM::FEMethod *method, int low_rank, int up_rank, CacheTupleWeakPtr cache_ptr=CacheTupleSharedPtr())
Executes FEMethod for finite elements in DM.
Definition DMMoFEM.cpp:586
#define MOFEM_LOG(channel, severity)
Log.
#define MOFEM_LOG_CHANNEL(channel)
Set and reset channel.
FTensor::Index< 'i', SPACE_DIM > i
const double c
speed of light (cm/ns)
const double v
phase velocity of light in medium (cm/ns)
const double n
refractive index of diffusive medium
long int __CLPK_integer
Definition lapack_wrap.h:23
static __CLPK_integer lapack_dgetrf(__CLPK_integer m, __CLPK_integer n, __CLPK_doublereal *a, __CLPK_integer lda, __CLPK_integer *ipiv)
static __CLPK_integer lapack_dgetri(__CLPK_integer n, __CLPK_doublereal *a, __CLPK_integer lda, __CLPK_integer *ipiv, __CLPK_doublereal *work, __CLPK_integer lwork)
static __CLPK_integer lapack_dgetrs(char trans, __CLPK_integer n, __CLPK_integer nrhs, __CLPK_doublereal *a, __CLPK_integer lda, __CLPK_integer *ipiv, __CLPK_doublereal *b, __CLPK_integer ldb)
FTensor::Index< 'j', 3 > j
FTensor::Index< 'k', 3 > k
static MoFEMErrorCodeGeneric< PetscErrorCode > ierr
PetscErrorCode MoFEMErrorCode
MoFEM/PETSc error code.
uint128_t UId
Unique Id.
Definition Types.hpp:31
UBlasMatrix< double > MatrixDouble
Definition Types.hpp:77
UBlasVector< int > VectorInt
Definition Types.hpp:67
implementation of Data Operators for Forces and Sources
Definition Common.hpp:10
auto type_from_handle(const EntityHandle h)
get type from entity handle
MoFEMErrorCode schur_mat_set_values_wrap(Mat M, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora)
Definition Schur.cpp:4276
static void add_gemv(bool trans_a, int m, int kk, const double *a, int lda, const double *x, double *y)
y += op(A) x, op(A) is m x kk, A is column-major
Definition Schur.cpp:1380
MoFEMErrorCode shell_block_mat_asmb_wrap_impl(BlockStructure *ctx, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora, bool is_preconditioner)
Definition Schur.cpp:3466
static void elem_mult_listed(const BlockPattern &p, const double *buf, const double *x, double *y, bool trans)
y += A x (trans false) or y += A^T x (trans true) from listed blocks
Definition Schur.cpp:1737
static PetscErrorCode solve_add(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2954
OpSchurAssembleBase * createOpSchurAssembleEnd(std::vector< std::string > fields_name, std::vector< boost::shared_ptr< Range > > field_ents, SmartPetscObj< AO > ao, SmartPetscObj< Mat > schur, bool sym_schur, bool symm_op)
Construct a new Op Schur Assemble End object.
Definition Schur.cpp:4196
static MoFEMErrorCode mult_block_structure(Mat mat, Vec x, Vec y, InsertMode iora, bool trans)
Matrix-vector product of block matrix.
Definition Schur.cpp:2648
static bool verify_probe(const BlockPattern &nl, const std::vector< double > &mat, double u, double rcond_min, double be_tol, double &be_block, double &be_dense)
Verify block layout on probe element.
Definition Schur.cpp:2093
auto isAllGather(IS is)
IS All gather.
static void apply_lu_factors(int n, const double *lu, const __CLPK_integer *ipiv, double *t, bool trans)
Apply in place LU factors of element matrix.
Definition Schur.cpp:1318
MoFEMErrorCode schurSwitchPreconditioner(boost::shared_ptr< BlockStructure > block_mat_data)
Switch preconditioner.
Definition Schur.cpp:4385
static void for_each_block(const BlockStructure &ctx, FUN &&fun)
Iterate all blocks of matrix, including listed blocks of A00 elements.
Definition Schur.cpp:2390
MoFEMErrorCode setSchurA00MatSolvePC(SmartPetscObj< PC > pc)
Set PC for A00 block.
Definition Schur.cpp:4238
constexpr bool debug_schur
Definition Schur.cpp:12
static PetscErrorCode solve_transpose_add(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2960
static int elem_factorise(const BlockPattern &p, double *buf, __CLPK_integer *piv, double u, double rcond_min, std::vector< double > &work, std::vector< __CLPK_integer > &iwork)
Factorise element buffer in place.
Definition Schur.cpp:1764
auto id_from_handle(const EntityHandle h)
static void elem_gather_dense(const BlockPattern &p, const double *buf, double *dense)
dense element matrix (row-major A, size x size) from listed blocks
Definition Schur.cpp:1725
static size_t listed_size(const BlockPattern &p)
doubles in listed blocks of one element
Definition Schur.cpp:1461
std::pair< SmartPetscObj< Mat >, boost::shared_ptr< BlockStructure > > SchurShellMatData
Definition Schur.hpp:106
static std::vector< int > analyse_pattern(const BlockPattern &p, double *w, double u, double rcond_min)
Decide elimination order on probe element.
Definition Schur.cpp:1632
std::pair< SmartPetscObj< Mat >, boost::shared_ptr< NestSchurData > > createSchurNestedMatrix(boost::shared_ptr< NestSchurData > schur_net_data_ptr)
Create a Mat Diag Blocks object.
Definition Schur.cpp:4163
static PetscErrorCode mult_transpose_add(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2947
PetscErrorCode PetscOptionsGetBool(PetscOptions *, const char pre[], const char name[], PetscBool *bval, PetscBool *set)
MoFEMErrorCode shell_block_mat_asmb_wrap(Mat M, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora)
Definition Schur.cpp:3627
static double accepted_backward_error(double be_dense, double be_tol)
backward error accepted for block factors, given dense backward error
Definition Schur.cpp:2071
static void sub_gemm(bool trans_a, int m, int kk, int nrhs, const double *a, int lda, const double *b, int ldb, double *c, int ldc)
C -= op(A) B, op(A) is m x kk, A, B and C are column-major.
Definition Schur.cpp:1389
static MoFEMErrorCode factorise_elems(BlockStorage &storage, BlockStorage::FactorStorage where)
Factorise A00 elements in place.
Definition Schur.cpp:2181
static bool is_zero_matrix(const double *ptr, size_t size)
Definition Schur.cpp:1355
auto createISGeneral(MPI_Comm comm, PetscInt n, const PetscInt idx[], PetscCopyMode mode)
Creates a data structure for an index set containing a list of integers.
SmartPetscObj< Vec > vectorDuplicate(Vec vec)
Create duplicate vector of smart vector.
static std::vector< char > symbolic_elimination(const BlockPattern &p, const std::vector< int > &order)
Blocks of M = A^T present after eliminating nodes in given order.
Definition Schur.cpp:1498
static void copy_block(double *dst, int ld_dst, int ridx, int cidx, const double *src, int ld_src, int nb_rows, int nb_cols)
copy row-major block into row-major matrix at (ridx, cidx)
Definition Schur.cpp:1347
static bool factorise_pivot_block(int n, double *d, int ld, __CLPK_integer *piv, double coupling, double u, double rcond_min, std::vector< double > &work, std::vector< __CLPK_integer > &iwork, double *rcond_out=nullptr, double *anorm_out=nullptr)
Factorise diagonal block in place and test it as block pivot.
Definition Schur.cpp:1414
void dgecon_(char *norm, __CLPK_integer *n, __CLPK_doublereal *a, __CLPK_integer *lda, __CLPK_doublereal *anorm, __CLPK_doublereal *rcond, __CLPK_doublereal *work, __CLPK_integer *iwork, __CLPK_integer *info)
static PetscErrorCode solve(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2951
static MoFEMErrorCode elem_solve(const BlockPattern &p, const double *buf, const __CLPK_integer *piv, double *r, int ldr, int nrhs, bool trans, const std::vector< char > *nz_in)
Solve with factorised element buffer.
Definition Schur.cpp:1834
boost::shared_ptr< std::vector< double > > getBlockMatPrconditionerStorageMat(Mat B)
Get preconditioner blocks storage.
Definition Schur.cpp:3461
static MoFEMErrorCode solve_block_structure(Mat mat, Vec f, Vec x, InsertMode iora, bool trans)
Solve with A00, i.e. x = A00^{-1} f, element by element.
Definition Schur.cpp:2820
static double elem_backward_error(const BlockPattern &p, const double *orig, const double *lu, const __CLPK_integer *piv, std::vector< double > &x, std::vector< double > &r, std::vector< double > &rs)
Normwise backward error of solve with factors of element.
Definition Schur.cpp:2022
static bool backward_error_accepted(double be, double be_dense, double be_tol)
true if backward error is finite and not larger than accepted
Definition Schur.cpp:2078
static std::string describe_nodes(const BlockPattern &p, const std::vector< int > &nodes)
Definition Schur.cpp:2120
static void make_block_layout(BlockPattern &p, const std::vector< int > &order)
Layout for block elimination of nodes in order, followed by dense core of remaining nodes.
Definition Schur.cpp:1527
static PetscErrorCode zero_rows_columns(Mat A, PetscInt N, const PetscInt rows[], PetscScalar diag, Vec x, Vec b)
Definition Schur.cpp:2964
static std::vector< int > natural_offsets(const BlockPattern &p)
offsets of nodes in element vector in order of nodes
Definition Schur.cpp:1450
boost::shared_ptr< BlockStructure > createBlockMatStructure(DM dm, SchurFEOpsFEandFields schur_fe_op_vec)
Create block matrix structure.
Definition Schur.cpp:2415
boost::shared_ptr< NestSchurData > createSchurNestedMatrixStruture(std::pair< SmartPetscObj< DM >, SmartPetscObj< DM > > dms, boost::shared_ptr< BlockStructure > block_mat_data_ptr, std::vector< std::string > fields_names, std::vector< boost::shared_ptr< Range > > field_ents, bool add_preconditioner_block)
Create structure of nested matrix [A11 A10; A01 A00].
Definition Schur.cpp:3667
static PetscErrorCode mult_add(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2941
MoFEMErrorCode MatSetValues(Mat M, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const double *ptr, InsertMode iora)
Assemble PETSc matrix.
static void set_block_orders(BlockStorage &storage)
set orders of blocks by finite element and by column entity
Definition Schur.cpp:2349
static PetscErrorCode mult_transpose(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2944
auto field_bit_from_bit_number(const int bit_number)
get field bit id from bit number
OpSchurAssembleBase * createOpSchurZeroRowsAndCols(boost::shared_ptr< std::vector< unsigned char > > marker_ptr, double diag_val)
Create a Op Schur Zero Rows And Cols object.
Definition Schur.cpp:4208
static PetscErrorCode mat_zero(Mat m)
Definition Schur.cpp:3032
MoFEMErrorCode schurSaveBlockMesh(boost::shared_ptr< BlockStructure > block_mat_data, std::string filename)
Save block matrix as a mesh.
Definition Schur.cpp:4400
auto getInterfacePtr(DM dm)
Get the Interface Ptr object.
Definition DMMoFEM.hpp:1184
static PetscErrorCode mult(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2938
static PetscErrorCode solve_transpose(Mat mat, Vec x, Vec y)
Definition Schur.cpp:2957
static double norm1_block(int m, int n, const double *a, int ld)
1-norm of column-major m x n block, infinity if block is not finite
Definition Schur.cpp:1367
static double backward_error_rhs(int i)
right-hand side used to measure backward error
Definition Schur.cpp:2010
static void copy_listed(const BlockPattern &from, const double *src, const BlockPattern &to, double *dst)
copy listed blocks of element buffer from one layout to another
Definition Schur.cpp:1714
static void elem_mult_factors(const BlockPattern &p, const double *buf, const __CLPK_integer *piv, double *v, bool trans)
Product with factorised element buffer, in place.
Definition Schur.cpp:1946
static void set_parent_blocks(BlockStructure &s)
set blocks of block matrix (parent numbering)
Definition Schur.cpp:2370
static void make_listed_layout(BlockPattern &p)
layout with listed blocks only
Definition Schur.cpp:1471
constexpr double backward_error_factor
factor of dense backward error accepted for block factors
Definition Schur.cpp:2007
std::vector< std::pair< std::string, std::vector< SchurFieldPair > > > SchurFEOpsFEandFields
Definition Schur.hpp:83
static MoFEMErrorCode setSchurBlockMatOps(Mat mat_raw)
Definition Schur.cpp:3047
MoFEMErrorCode shell_block_preconditioner_mat_asmb_wrap(Mat M, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora)
Definition Schur.cpp:3645
auto getProblemPtr(DM dm)
get problem pointer from DM
Definition DMMoFEM.hpp:1195
static void relayout_pattern(BlockStorage &s, int pi, BlockPattern nl)
Relayout element buffers of pattern pi, element by element, in place.
Definition Schur.cpp:2139
static std::vector< int > structural_order(const BlockPattern &p)
elimination order by structure only (minimal fill, diagonal present)
Definition Schur.cpp:1586
SchurShellMatData createBlockMat(DM dm, boost::shared_ptr< BlockStructure > data)
Create a Schur Mat object.
Definition Schur.cpp:3072
MoFEMErrorCode assembleBlockMatSchur(MoFEM::Interface &m_field, Mat B, Mat S, std::vector< std::string > fields_name, std::vector< boost::shared_ptr< Range > > field_ents, SmartPetscObj< AO > ao)
Assemble Schur complement S = A11 - A10 A00^{-1} A01 from block matrix.
Definition Schur.cpp:3107
OpSchurAssembleBase * createOpSchurAssembleBegin()
Definition Schur.cpp:4191
auto ent_form_type_and_id(const EntityType type, const EntityID id)
get entity handle from type and id
boost::shared_ptr< std::vector< double > > getBlockMatStorageMat(Mat B)
Get block values storage.
Definition Schur.cpp:3452
int r
Definition sdf.py:205
constexpr AssemblyType A
constexpr double t
plate stiffness
Definition plate.cpp:58
double q
FTensor::Index< 'm', 3 > m
const int N
Definition speed_test.cpp:3
DOFs on one entity (node) of A00 element.
Definition Schur.cpp:300
int globParent
global index in block matrix numbering
Definition Schur.cpp:305
int offset
offset in element vector (layout of pattern)
Definition Schur.cpp:303
int locParent
local index in block matrix numbering
Definition Schur.cpp:304
int locBlock
local index in A00 sub-problem numbering
Definition Schur.cpp:306
int globBlock
global index in A00 sub-problem numbering
Definition Schur.cpp:307
int size
DOFs of element.
Definition Schur.cpp:363
bool local
all entities have local index on this process
Definition Schur.cpp:367
int entBegin
first entity in elemEntities
Definition Schur.cpp:364
bool owned
all entities are owned by this process
Definition Schur.cpp:368
int pivShift
shift in pivots
Definition Schur.cpp:366
int entEnd
one past last entity in elemEntities
Definition Schur.cpp:365
int pattern
index of pattern
Definition Schur.cpp:362
bool factorised
factors are stored for this element
Definition Schur.cpp:369
bool dense
factors are in elemDense (pivot test failed)
Definition Schur.cpp:370
block data indexes
Definition Schur.cpp:257
Indexes(UId uid_row, UId uid_col, UId uid_fe, int row, int col, int nb_rows, int nb_cols, int loc_row, int loc_col)
Definition Schur.cpp:259
size_t mat_shift
shift in data, or in element buffer if elem >= 0
Definition Schur.cpp:294
void setStorage(size_t shift, int leading_dim, int element)
Definition Schur.cpp:278
size_t getMatShift() const
Definition Schur.cpp:274
int ld
leading dimension of block in storage
Definition Schur.cpp:295
int elem
index of A00 element, -1 if none
Definition Schur.cpp:296
Block pattern of A00 element and layout of element buffer.
Definition Schur.cpp:317
long mShift(int a, int b) const
shift of block (a, b) of M = A^T, column-major
Definition Schur.cpp:355
int coreOffset
offset of core DOFs in element vector
Definition Schur.cpp:341
long sShift(int r, int c) const
Definition Schur.cpp:349
int size
DOFs per element.
Definition Schur.cpp:326
std::vector< int > nodeSize
Definition Schur.cpp:329
double beRef
backward error of dense solve on probe
Definition Schur.cpp:345
std::vector< std::string > nodeName
Definition Schur.cpp:330
int coreSize
DOFs in dense core.
Definition Schur.cpp:340
std::vector< char > listed
nbNodes x nbNodes, slot (r, c) is listed
Definition Schur.cpp:331
int nbFallback
elements factorised dense in last factorisation
Definition Schur.cpp:344
int mLd(int a, int b) const
leading dimension of block (a, b) of M = A^T
Definition Schur.cpp:357
@ LISTED
listed blocks only, not analysed
Definition Schur.cpp:320
@ DENSE
dense element matrix (core contains all nodes)
Definition Schur.cpp:322
@ BLOCK
block elimination of order nodes, then dense core
Definition Schur.cpp:321
size_t coreShift
shift of dense core in element buffer
Definition Schur.cpp:339
size_t elemSize
doubles in element buffer
Definition Schur.cpp:342
int sLd(int r, int c) const
Definition Schur.cpp:352
std::vector< int > order
nodes eliminated block by block
Definition Schur.cpp:337
bool isListed(int r, int c) const
Definition Schur.cpp:353
std::vector< long > slotShift
nbNodes x nbNodes, -1 if not stored
Definition Schur.cpp:334
double beMax
largest backward error in last factorisation
Definition Schur.cpp:346
std::vector< int > core
nodes of dense core
Definition Schur.cpp:338
std::vector< int > nodeOffset
offset of node DOFs in element vector
Definition Schur.cpp:336
std::vector< int > slotLd
Definition Schur.cpp:335
bool operator()(const UId &a, const Indexes &b) const
Definition Schur.cpp:482
bool operator()(const Indexes &a, const UId &b) const
Definition Schur.cpp:479
Storage of block matrix, shared by block matrix and its nested (Schur) sub-matrices.
Definition Schur.cpp:251
bool hasPreconditioner
preconditioner blocks are enabled
Definition Schur.cpp:405
MoFEMErrorCode modified() const
Call before modification of data, fails if factors are stored in place.
Definition Schur.cpp:426
size_t maxElemBuffer
maximal size of element buffer
Definition Schur.cpp:384
double optRcondMin
-schur_a00_rcond_min
Definition Schur.cpp:410
auto rowRange(const UId &row) const
range of blocks with given row entity uid
Definition Schur.cpp:488
bool isFactorised() const
Definition Schur.cpp:413
double * blockPtr(const Indexes &b, bool prec=false)
values of block, preconditioner buffer if prec is true
Definition Schur.cpp:447
std::vector< ElemEntity > elemEntities
Definition Schur.cpp:379
std::vector< int > feOrder
blocks sorted by (fe uid, row uid, col uid)
Definition Schur.cpp:374
std::vector< int > colOrder
blocks sorted by (col uid, row uid)
Definition Schur.cpp:375
std::vector< Elem > elems
Definition Schur.cpp:378
bool optDense
-schur_a00_dense
Definition Schur.cpp:408
FactorStorage factorStorage
Definition Schur.cpp:403
std::vector< double > data
blocks not in A00, allocated on first use
Definition Schur.cpp:387
std::vector< std::vector< double > > elemPrec
preconditioner buffers
Definition Schur.cpp:381
std::vector< std::vector< double > > elemDense
dense factors (fallback)
Definition Schur.cpp:382
double optBackwardError
-schur_a00_backward_error
Definition Schur.cpp:411
std::vector< std::vector< double > > elemData
A00 element buffers.
Definition Schur.cpp:380
MoFEMErrorCode checkNotFailed() const
Fails if factorisation failed, storage has to be zeroed.
Definition Schur.cpp:416
std::vector< Indexes > blockIndex
blocks sorted by (row uid, col uid)
Definition Schur.cpp:373
double optPivotThreshold
-schur_a00_pivot_threshold
Definition Schur.cpp:409
size_t dataSize
required size of data
Definition Schur.cpp:386
@ FACTOR_FAILED
factorisation failed, data is corrupted
Definition Schur.cpp:401
int maxElemSize
maximal DOFs of element
Definition Schur.cpp:383
static bool lessRowCol(const Indexes &a, const Indexes &b)
Definition Schur.cpp:453
const Indexes * find(const UId &row, const UId &col) const
find block by row and column entity uid, return nullptr if not found
Definition Schur.cpp:460
Indexes * findMutable(const UId &row, const UId &col)
Definition Schur.cpp:474
std::vector< Pattern > patterns
Definition Schur.cpp:377
std::vector< __CLPK_integer > pivots
Definition Schur.cpp:388
bool nested
nested Schur structure has been created
Definition Schur.cpp:404
void ensureAllocated()
Definition Schur.cpp:392
auto colRange(const UId &col) const
range (in colOrder) of blocks with given column entity uid
Definition Schur.cpp:494
block in numbering of this matrix (blocks not stored in element buffers)
Definition Schur.cpp:518
Context of block matrix shell.
Definition Schur.cpp:512
std::vector< Block > blocks
Definition Schur.cpp:530
Numbering
numbering of A00 element matrix entities used by this matrix
Definition Schur.cpp:515
bool multiplyByPreconditioner
Definition Schur.cpp:537
boost::shared_ptr< BlockStorage > storage
Definition Schur.cpp:529
int locIdx(const BlockStorage::ElemEntity &e) const
Definition Schur.cpp:539
SmartPetscObj< Vec > ghostX
Definition Schur.cpp:534
SmartPetscObj< Vec > ghostY
Definition Schur.cpp:535
int globIdx(const BlockStorage::ElemEntity &e) const
Definition Schur.cpp:542
Numbering numbering
Definition Schur.cpp:532
bool hasElems
A00 elements are part of this matrix.
Definition Schur.cpp:531
virtual FieldBitNumber get_field_bit_number(const std::string name) const =0
get field bit number
Deprecated interface functions.
Data on single entity (This is passed as argument to DataOperator::doWork)
const VectorFieldEntities & getFieldEntities() const
Get field entities (const version)
const VectorDofs & getFieldDofs() const
Get DOF data structures (const version)
const VectorInt & getIndices() const
Get global indices of degrees of freedom on entity.
[Storage and set boundary conditions]
Structure for user loop methods on finite elements.
static UId getHiBitNumberUId(const FieldBitNumber bit_number)
static auto getHandleFromUniqueId(const UId uid)
Get the Handle From Unique Id.
static auto getFieldBitNumberFromUniqueId(const UId uid)
Get the Field Bit Number From Unique Id.
UId getLocalUniqueIdCalculate()
Get the Local Unique Id Calculate object.
static UId getLoLocalEntityBitNumber(const char bit_number, const EntityHandle ent)
static UId getHiLocalEntityBitNumber(const char bit_number, const EntityHandle ent)
const FEMethod * getFEMethod() const
Return raw pointer to Finite Element Method object.
MoFEMErrorCode assembleSchurMat(Mat S, const UId &uid_row, VectorInt &row_ind, const UId &uid_col, VectorInt &col_ind, MatrixDouble &m, InsertMode iora)
Assemble Schur complement.
Definition Schur.cpp:38
Clear Schur complement internal data.
Definition Schur.cpp:61
MoFEMErrorCode doWork(int side, EntityType type, EntitiesFieldData::EntData &data)
Operator for linear form, usually to calculate values on right hand side.
Definition Schur.cpp:753
Assemble Schur complement (Implementation)
Definition Schur.cpp:88
SmartPetscObj< AO > schurAO
Definition Schur.cpp:122
MoFEMErrorCode doWorkImpl(int side, EntityType type, EntitiesFieldData::EntData &data)
Definition Schur.cpp:823
SmartPetscObj< Mat > schurMat
Definition Schur.cpp:123
std::vector< boost::shared_ptr< Range > > fieldEnts
Definition Schur.cpp:121
OpSchurAssembleEndImpl(std::vector< std::string > fields_name, std::vector< boost::shared_ptr< Range > > field_ents, SmartPetscObj< AO > schur_ao, SmartPetscObj< Mat > schur_mat, bool sym_schur, bool symm_op)
Construct a new Op Schur Assemble End object.
Definition Schur.cpp:812
std::vector< std::string > fieldsName
Definition Schur.cpp:120
Assemble Schur complement.
Definition Schur.cpp:139
OpSchurZeroRowsAndCols(boost::shared_ptr< std::vector< unsigned char > > marker_ptr, double diag_val)
Definition Schur.cpp:766
boost::shared_ptr< std::vector< unsigned char > > markerPtr
Definition Schur.cpp:79
MoFEMErrorCode doWork(int side, EntityType type, EntitiesFieldData::EntData &data)
Operator for linear form, usually to calculate values on right hand side.
Definition Schur.cpp:772
boost::function< MoFEMErrorCode(Mat M, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora)> MatSetValuesPtr
Definition Schur.hpp:295
static auto invertMat(MatrixDouble &m, MatrixDouble &inv)
Definition Schur.cpp:1268
static auto invertMat(MatrixDouble &m, MatrixDouble &inv)
Definition Schur.cpp:1232
Schur complement data storage.
Definition Schur.cpp:161
static MoFEMErrorCode assembleStorage(const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora)
Definition Schur.cpp:577
static boost::ptr_vector< VectorInt > colIndices
Definition Schur.cpp:214
static boost::ptr_vector< MatrixDouble > locMats
Definition Schur.cpp:212
SchurElemMats(const size_t idx, const UId uid_row, const UId uid_col)
Definition Schur.cpp:572
virtual ~SchurElemMats()=default
static size_t maxIndexCounter
Definition Schur.cpp:216
static boost::ptr_vector< VectorInt > rowIndices
Definition Schur.cpp:213
auto & getMat() const
Definition Schur.cpp:170
static MoFEMErrorCode MatSetValues(Mat M, const EntitiesFieldData::EntData &row_data, const EntitiesFieldData::EntData &col_data, const MatrixDouble &mat, InsertMode iora)
Definition Schur.cpp:4290
auto & getColInd() const
Definition Schur.cpp:172
static boost::ptr_vector< SchurElemMats > schurElemMats
Definition Schur.cpp:215
auto & getRowInd() const
Definition Schur.cpp:171
static SchurElemStorage schurL2Storage
Definition Schur.cpp:218
const size_t iDX
Definition Schur.cpp:166
multi_index_container< const SchurElemMats *, indexed_by< ordered_unique< tag< uid_mi_tag >, composite_key< SchurElemMats, member< SchurElemMats, const UId, &SchurElemMats::uidRow >, member< SchurElemMats, const UId, &SchurElemMats::uidCol > > >, ordered_non_unique< tag< col_mi_tag >, member< SchurElemMats, const UId, &SchurElemMats::uidCol > > > > SchurElemStorage
Definition Schur.cpp:210
static PetscLogEvent MOFEM_EVENT_BlockStructureSetValues
Definition Schur.hpp:29
static PetscLogEvent MOFEM_EVENT_BlockStructureMult
Definition Schur.hpp:30
static PetscLogEvent MOFEM_EVENT_zeroRowsAndCols
Definition Schur.hpp:33
static PetscLogEvent MOFEM_EVENT_opSchurAssembleEnd
Definition Schur.hpp:28
static PetscLogEvent MOFEM_EVENT_BlockStructureSolve
Definition Schur.hpp:31
static PetscLogEvent MOFEM_EVENT_schurMatSetValues
Definition Schur.hpp:27
static PetscLogEvent MOFEM_EVENT_AssembleSchurMat
Definition Schur.hpp:32
intrusive_ptr for managing petsc objects