OOFEM 3.0
Loading...
Searching...
No Matches
petscsparsemtrx.C
Go to the documentation of this file.
1/*
2 *
3 * ##### ##### ###### ###### ### ###
4 * ## ## ## ## ## ## ## ### ##
5 * ## ## ## ## #### #### ## # ##
6 * ## ## ## ## ## ## ## ##
7 * ## ## ## ## ## ## ## ##
8 * ##### ##### ## ###### ## ##
9 *
10 *
11 * OOFEM : Object Oriented Finite Element Code
12 *
13 * Copyright (C) 1993 - 2025 Borek Patzak
14 *
15 *
16 *
17 * Czech Technical University, Faculty of Civil Engineering,
18 * Department of Structural Mechanics, 166 29 Prague, Czech Republic
19 *
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 */
34
35#include "petscsparsemtrx.h"
36#include "engngm.h"
37#include "activebc.h"
38#include "element.h"
39#include "dofmanager.h"
40#include "sparsemtrxtype.h"
41#include "classfactory.h"
42
43#include <vector>
44#include <petscksp.h>
45#include <petscvec.h>
46#include <petscmat.h>
47
48namespace oofem {
50
51
52PetscSparseMtrx :: PetscSparseMtrx(int n, int m) : SparseMtrx(n, m),
53 mtrx(NULL),
54 symmFlag(false),
55 leqs(0),
56 geqs(0),
57 blocksize(1),
58 di(0),
59 kspInit(false),
60 newValues(true),
61 localIS(NULL),
62 globalIS(NULL)
63{ }
64
65
66PetscSparseMtrx :: ~PetscSparseMtrx()
67{
68 MatDestroy(& this->mtrx);
69 if ( this->kspInit ) {
70 KSPDestroy(& this->ksp);
71 }
72 if ( localIS ) {
73 ISDestroy(& localIS);
74 ISDestroy(& globalIS);
75 }
76}
77
78
79std::unique_ptr<SparseMtrx>
80PetscSparseMtrx :: clone() const
81{
82 std::unique_ptr<PetscSparseMtrx> answer = std::make_unique<PetscSparseMtrx>(nRows, nColumns);
83 MatDuplicate( this->mtrx, MAT_COPY_VALUES, & ( answer->mtrx ) );
84 answer->symmFlag = this->symmFlag;
85 answer->mType = this->mType;
86 answer->leqs = this->leqs;
87 answer->geqs = this->geqs;
88 answer->di = this->di;
89 answer->emodel = this->emodel;
90 answer->kspInit = false;
91 answer->newValues = this->newValues;
92
93 return std::move(answer);
94}
95
96void
97PetscSparseMtrx :: times(const FloatArray &x, FloatArray &answer) const
98{
99 if ( emodel->isParallel() ) {
100 answer.resize(x.giveSize());
101
102 Vec globX;
103 Vec globY;
104
105 /*
106 * scatter and gather x to global representation
107 */
108 this->createVecGlobal(& globX);
109 this->scatterL2G(x, globX);
110
111 VecDuplicate(globX, & globY);
112
113 MatMult(this->mtrx, globX, globY);
114
115 this->scatterG2L(globY, answer);
116
117 VecDestroy(& globX);
118 VecDestroy(& globY);
119 } else {
120 if ( this->giveNumberOfColumns() != x.giveSize() ) {
121 OOFEM_ERROR("Dimension mismatch");
122 }
123
124 Vec globX, globY;
125 VecCreateSeqWithArray(PETSC_COMM_SELF, 1, x.giveSize(), x.givePointer(), & globX);
126 VecCreate(PETSC_COMM_SELF, & globY);
127 VecSetType(globY, VECSEQ);
128 VecSetSizes(globY, PETSC_DECIDE, this->nRows);
129
130 MatMult(this->mtrx, globX, globY);
131 double *ptr;
132 VecGetArray(globY, & ptr);
133 answer.resize(this->nRows);
134 for ( int i = 0; i < this->nRows; i++ ) {
135 answer[i] = ptr [ i ];
136 }
137
138 VecRestoreArray(globY, & ptr);
139 VecDestroy(& globX);
140 VecDestroy(& globY);
141 }
142}
143
144void
145PetscSparseMtrx :: timesT(const FloatArray &x, FloatArray &answer) const
146{
147 if ( this->giveNumberOfRows() != x.giveSize() ) {
148 OOFEM_ERROR("Dimension mismatch");
149 }
150
151 if ( emodel->isParallel() ) {
152 OOFEM_ERROR("Not implemented");
153 }
154
155 Vec globX, globY;
156 VecCreateSeqWithArray(PETSC_COMM_SELF, 1, x.giveSize(), x.givePointer(), & globX);
157 VecCreate(PETSC_COMM_SELF, & globY);
158 VecSetType(globY, VECSEQ);
159 VecSetSizes(globY, PETSC_DECIDE, this->nColumns);
160
161 MatMultTranspose(this->mtrx, globX, globY);
162 double *ptr;
163 VecGetArray(globY, & ptr);
164 answer.resize(this->nColumns);
165 for ( int i = 0; i < this->nColumns; i++ ) {
166 answer[i] = ptr [ i ];
167 }
168
169 VecRestoreArray(globY, & ptr);
170 VecDestroy(& globX);
171 VecDestroy(& globY);
172}
173
174
175void
176PetscSparseMtrx :: times(const FloatMatrix &B, FloatMatrix &answer) const
177{
178 if ( this->giveNumberOfColumns() != B.giveNumberOfRows() ) {
179 OOFEM_ERROR("Dimension mismatch");
180 }
181
182 if ( emodel->isParallel() ) {
183 OOFEM_ERROR("Not implemented");
184 }
185
186 // I'm opting to work with a set of vectors, as i think it might be faster and more robust. / Mikael
187
188 int nr = this->giveNumberOfRows();
189 int nc = B.giveNumberOfColumns();
190 answer.resize(nr, nc);
191
192#if 1
193 // Using several vectors are necessary because PETSc is missing most of it operations on symmetric matrices.
194 // If PETSc ever adds the missing "MatMatMult_***_***" implementations, we can use the second approach below
195 Vec globX, globY;
196 VecCreate(PETSC_COMM_SELF, & globY);
197 VecSetType(globY, VECSEQ);
198 VecSetSizes(globY, PETSC_DECIDE, nr);
199 int nrB = B.giveNumberOfRows();
200 FloatArray colVals(nrB);
201 double *resultsPtr;
202 for ( int k = 0; k < nc; ++k ) {
203 B.copyColumn(colVals, k + 1);
204 VecCreateSeqWithArray(PETSC_COMM_SELF, 1, nrB, colVals.givePointer(), & globX);
205 MatMult(this->mtrx, globX, globY);
206 VecGetArray(globY, & resultsPtr);
207 for ( int i = 0; i < nr; ++i ) {
208 answer(i, k) = resultsPtr [ i ];
209 }
210 VecRestoreArray(globY, & resultsPtr);
211 VecDestroy(& globX);
212 }
213 VecDestroy(& globY);
214#else
215 double *aptr = answer.givePointer();
216 Mat globB, globC;
217 MatCreateSeqDense(PETSC_COMM_SELF, B.giveNumberOfRows(), B.giveNumberOfColumns(), B.givePointer(), & globB);
218 MatMatMult(this->mtrx, globB, MAT_INITIAL_MATRIX, PETSC_DEFAULT, & globC);
219 const double *vals;
220 for ( int r = 0; r < nr; r++ ) {
221 MatGetRow(globC, r, NULL, NULL, & vals);
222 for ( int i = 0, i2 = r; i < nc; i++, i2 += nr ) {
223 aptr [ i2 ] = vals [ i ];
224 }
225 MatRestoreRow(globC, r, NULL, NULL, & vals);
226 }
227
228 MatDestroy(& globB);
229 MatDestroy(& globC);
230#endif
231}
232
233void
234PetscSparseMtrx :: timesT(const FloatMatrix &B, FloatMatrix &answer) const
235{
236 if ( this->giveNumberOfRows() != B.giveNumberOfRows() ) {
237 OOFEM_ERROR("Dimension mismatch");
238 }
239
240 if ( emodel->isParallel() ) {
241 OOFEM_ERROR("Not implemented");
242 }
243
244 int nr = this->giveNumberOfColumns();
245 int nc = B.giveNumberOfColumns();
246 answer.resize(nr, nc);
247 double *aptr = answer.givePointer();
248
249 // For some reason SEQAIJ and SEQDENSE are incompatible with each other for MatMatMultTranspose (MatMatMult is OK). I don't know why.
250 // Either way, this is not to bad, except for an extra transposition.
251
252 Mat globB, globC;
253 FloatMatrix BT;
254 BT.beTranspositionOf(B);
255 MatCreateSeqDense(PETSC_COMM_SELF, BT.giveNumberOfRows(), BT.giveNumberOfColumns(), BT.givePointer(), & globB);
256 MatMatMult(globB, this->mtrx, MAT_INITIAL_MATRIX, PETSC_DEFAULT, & globC);
257 for ( int r = 0; r < nc; r++ ) {
258 const double *vals;
259 MatGetRow(globC, r, nullptr, nullptr, & vals);
260 for ( int i = 0; i < nr; i++ ) {
261 * aptr++ = vals [ i ];
262 }
263 MatRestoreRow(globC, r, nullptr, nullptr, & vals);
264 }
265
266 MatDestroy(& globB);
267 MatDestroy(& globC);
268}
269
270void
271PetscSparseMtrx :: times(double x)
272{
273 MatScale(this->mtrx, x);
274}
275
276
277void
278PetscSparseMtrx :: add(double x, SparseMtrx &m)
279{
280 PetscSparseMtrx *M = dynamic_cast< PetscSparseMtrx * >( &m );
281 MatAXPY(*this->giveMtrx(), x, *M->giveMtrx(), SAME_NONZERO_PATTERN);
282}
283
284
285void
286PetscSparseMtrx :: addDiagonal(double x, FloatArray &m)
287{
288 OOFEM_WARNING("Calling function that has not been tested (remove this message when it is verified)");
289 Vec globM;
290 if ( emodel->isParallel() ) {
291 this->createVecGlobal(& globM);
292 this->scatterL2G(m, globM);
293 } else {
294 VecCreateSeqWithArray(PETSC_COMM_SELF, 1, m.giveSize(), m.givePointer(), & globM);
295 }
296 MatDiagonalSet(this->mtrx, globM, ADD_VALUES);
297 VecDestroy(& globM);
298}
299
300int
301PetscSparseMtrx :: buildInternalStructure(EngngModel *eModel, int n, int m, const IntArray &I, const IntArray &J)
302{
303 if ( mtrx ) {
304 MatDestroy(& mtrx);
305 }
306
307 if ( this->kspInit ) {
308 KSPDestroy(& ksp);
309 this->kspInit = false; // force ksp to be initialized
310 }
311
312 this->emodel = eModel;
313 this->di = 0;
314
315 if ( emodel->isParallel() ) {
316 OOFEM_ERROR("Parallel is not supported in manual matrix creation");
317 }
318
319 nRows = geqs = leqs = n;
320 nColumns = m;
321 blocksize = 1;
322
323 int total_nnz;
324 IntArray d_nnz(leqs), d_nnz_sym(leqs);
325 {
326 //determine nonzero structure of matrix
327 std :: vector< IntArray > rows_upper(nRows), rows_lower(nRows);
328
329 for ( int ii : I ) {
330 if ( ii > 0 ) {
331 for ( int jj : J ) {
332 if ( jj > 0 ) {
333 if ( jj >= ii ) {
334 rows_upper [ ii - 1 ].insertSortedOnce(jj - 1);
335 } else {
336 rows_lower [ ii - 1 ].insertSortedOnce(jj - 1);
337 }
338 }
339 }
340 }
341 }
342
343 total_nnz = 0;
344 for ( int i = 0; i < leqs; i++ ) {
345 d_nnz[i] = rows_upper [ i ].giveSize() + rows_lower [ i ].giveSize();
346 }
347 }
348
349 // create PETSc mat
350 MatCreate(PETSC_COMM_SELF, & mtrx);
351 MatSetSizes(mtrx, nRows, nColumns, nRows, nColumns);
352 MatSetFromOptions(mtrx);
353
354 if ( total_nnz / nColumns > nRows / 10 ) { // More than 10% nnz, then we just force the dense matrix.
355 MatSetType(mtrx, MATDENSE);
356 } else {
357 MatSetType(mtrx, MATSEQAIJ);
358 }
359
360 //The incompatible preallocations are ignored automatically.
361 MatSetUp(mtrx);
362 MatSeqAIJSetPreallocation( mtrx, 0, d_nnz.givePointer() );
363
364 MatSetOption(mtrx, MAT_ROW_ORIENTED, PETSC_FALSE); // To allow the insertion of values using MatSetValues in column major order
365 MatSetOption(mtrx, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE);
366
367 this->newValues = true;
368 return true;
369}
370
372int
373PetscSparseMtrx :: buildInternalStructure(EngngModel *eModel, int di, const UnknownNumberingScheme &r_s, const UnknownNumberingScheme &c_s)
374{
375 IntArray loc;
376 Domain *domain = eModel->giveDomain(di);
377 blocksize = 1;
378
379 if ( mtrx ) {
380 MatDestroy(& mtrx);
381 }
382
383 if ( this->kspInit ) {
384 KSPDestroy(& ksp);
385 this->kspInit = false; // force ksp to be initialized
386 }
387
388 this->emodel = eModel;
389 this->di = di;
390
391 if ( emodel->isParallel() ) {
392 OOFEM_ERROR("Not implemented");
393 }
394
395 nRows = eModel->giveNumberOfDomainEquations(di, r_s);
397
398 geqs = leqs = nRows;
399
400 int total_nnz;
401 IntArray d_nnz(leqs), d_nnz_sym(leqs);
402
403 {
404 //determine nonzero structure of matrix
405 IntArray r_loc, c_loc;
406 std :: vector< IntArray > rows_upper(nRows), rows_lower(nRows);
407
408 for ( auto &elem : domain->giveElements() ) {
409 elem->giveLocationArray(r_loc, r_s);
410 elem->giveLocationArray(c_loc, c_s);
411 for ( int ii : r_loc ) {
412 if ( ii > 0 ) {
413 for ( int jj : c_loc ) {
414 if ( jj > 0 ) {
415 if ( jj >= ii ) {
416 rows_upper [ ii - 1 ].insertSortedOnce(jj - 1, c_loc.giveSize() / 2);
417 } else {
418 rows_lower [ ii - 1 ].insertSortedOnce(jj - 1, c_loc.giveSize() / 2);
419 }
420 }
421 }
422 }
423 }
424 }
425 // Structure from active boundary conditions.
426 std :: vector< IntArray >r_locs, c_locs;
427 for ( auto &gbc : domain->giveBcs() ) {
428 ActiveBoundaryCondition *activebc = dynamic_cast< ActiveBoundaryCondition * >( gbc.get() );
429 if ( activebc ) {
430 activebc->giveLocationArrays(r_locs, c_locs, TangentStiffnessMatrix, r_s, c_s);
431 for ( std :: size_t k = 0; k < r_locs.size(); k++ ) {
432 IntArray &krloc = r_locs [ k ];
433 IntArray &kcloc = c_locs [ k ];
434 for ( int ii : krloc ) {
435 if ( ii > 0 ) {
436 for ( int jj : kcloc ) {
437 if ( jj > 0 ) {
438 if ( jj >= ii ) {
439 rows_upper [ ii - 1 ].insertSortedOnce(jj - 1, kcloc.giveSize() / 2);
440 } else {
441 rows_lower [ ii - 1 ].insertSortedOnce(jj - 1, kcloc.giveSize() / 2);
442 }
443 }
444 }
445 }
446 }
447 }
448 }
449 }
450
451 total_nnz = 0;
452 for ( int i = 0; i < leqs; i++ ) {
453 d_nnz[i] = rows_upper [ i ].giveSize() + rows_lower [ i ].giveSize();
454 total_nnz += d_nnz[i];
455 }
456 }
457
458 // create PETSc mat
459 MatCreate(PETSC_COMM_SELF, & mtrx);
460 MatSetSizes(mtrx, nRows, nColumns, nRows, nColumns);
461 MatSetFromOptions(mtrx);
462
463 if ( total_nnz > (double)nRows * (double)nColumns / 10.0 ) { // More than 10% nnz, then we just force the dense matrix.
464 MatSetType(mtrx, MATDENSE);
465 } else {
466 MatSetType(mtrx, MATSEQAIJ);
467 }
468
469 //The incompatible preallocations are ignored automatically.
470 MatSetUp(mtrx);
471 MatSeqAIJSetPreallocation( mtrx, 0, d_nnz.givePointer() );
472
473 MatSetOption(mtrx, MAT_ROW_ORIENTED, PETSC_FALSE); // To allow the insertion of values using MatSetValues in column major order
474 MatSetOption(mtrx, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE);
475
476 this->newValues = true;
477 return true;
478}
479
480int
481PetscSparseMtrx :: buildInternalStructure(EngngModel *eModel, int di, const UnknownNumberingScheme &s)
482{
483 IntArray loc;
484 Domain *domain = eModel->giveDomain(di);
485 blocksize = 1;
486
487 // Delete old stuff first;
488 if ( mtrx ) {
489 MatDestroy(& mtrx);
490 }
491
492 if ( this->kspInit ) {
493 KSPDestroy(& ksp);
494 this->kspInit = false; // force ksp to be initialized
495 }
496
497 if ( localIS ) {
498 ISDestroy(& localIS);
499 ISDestroy(& globalIS);
500 localIS = nullptr;
501 globalIS = nullptr;
502 }
503
504 this->emodel = eModel;
505 this->di = di;
506
507
508#ifdef __MPI_PARALLEL_MODE
509 if ( eModel->isParallel() ) {
512 ParallelContext *context = eModel->giveParallelContext(di);
513 n2g = context->giveN2Gmap();
514 n2l = context->giveN2Lmap();
515
516 n2l->init(eModel, di, s);
517 n2g->init(eModel, di, s);
518
519 #ifdef __VERBOSE_PARALLEL
520 VERBOSEPARALLEL_PRINT( "PetscSparseMtrx:: buildInternalStructure", "", eModel->giveRank() );
521 #endif
522
523 leqs = n2g->giveNumberOfLocalEqs();
525
526 //printf("%d, leqs = %d, geqs = %d\n", this->emodel->giveRank(), leqs, geqs);
527
528 #ifdef __VERBOSE_PARALLEL
529 OOFEM_LOG_INFO( "[%d]PetscSparseMtrx:: buildInternalStructure: l_eqs = %d, g_eqs = %d, n_eqs = %d\n", eModel->giveRank(), leqs, geqs, eModel->giveNumberOfDomainEquations(di, s) );
530 #endif
531
532 IntArray d_nnz(leqs), o_nnz(leqs), d_nnz_sym(leqs), o_nnz_sym(leqs);
533
534 {
535 // determine nonzero structure of a "local (maintained)" part of matrix, and the off-diagonal part
536 // allocation map
538 std :: vector< IntArray > d_rows_upper(leqs), d_rows_lower(leqs); // diagonal sub-matrix allocation
539 std :: vector< IntArray > o_rows_upper(leqs), o_rows_lower(leqs); // off-diagonal allocation
540
541 IntArray lloc, gloc;
542
543 //fprintf (stderr,"[%d] n2l map: ",rank);
544 //for (n=1; n<=n2l.giveN2Lmap()->giveSize(); n++) fprintf (stderr, "%d ", n2l.giveN2Lmap()->at(n));
545
546 for ( auto &elem : domain->giveElements() ) {
547 //fprintf (stderr, "(elem %d) ", n);
548 elem->giveLocationArray(loc, s);
549 n2l->map2New(lloc, loc, 0); // translate natural->local numbering (remark, 1-based indexing)
550 n2g->map2New(gloc, loc, 0); // translate natural->global numbering (remark, 0-based indexing)
551 // See the petsc manual for details on how this allocation is constructed.
552 int ii, jj;
553 for ( int i = 1; i <= lloc.giveSize(); i++ ) {
554 if ( ( ii = lloc.at(i) ) ) {
555 for ( int j = 1; j <= lloc.giveSize(); j++ ) {
556 if ( ( jj = gloc.at(j) ) >= 0 ) { // if negative, then it is prescribed
557 if ( lloc.at(j) ) { // if true, then its the local part (the diagonal block matrix)
558 if ( jj >= ( ii - 1 ) ) { // NOTE: ii (the rows) is in 1-based indexing, jj is in 0-base (!)
559 d_rows_upper [ ii - 1 ].insertSortedOnce(jj, loc.giveSize() / 2);
560 } else {
561 d_rows_lower [ ii - 1 ].insertSortedOnce(jj, loc.giveSize() / 2);
562 }
563 } else { // Otherwise it must be off-diagonal
564 if ( jj >= ( ii - 1 ) ) {
565 o_rows_upper [ ii - 1 ].insertSortedOnce(jj, loc.giveSize() / 2);
566 } else {
567 o_rows_lower [ ii - 1 ].insertSortedOnce(jj, loc.giveSize() / 2);
568 }
569 }
570 }
571 }
572 }
573 }
574 }
576
577 // Diagonal must always be allocated; this code ensures that for every local line, it adds the global column number
578 IntArray *n2gmap = n2g->giveN2Gmap();
579 IntArray *n2lmap = n2l->giveN2Lmap();
580 for ( int n = 1; n <= n2lmap->giveSize(); ++n ) {
581 if ( n2lmap->at(n) ) {
582 d_rows_upper [ n2lmap->at(n) - 1 ].insertSortedOnce( n2gmap->at(n) );
583 }
584 }
585
586 for ( int i = 0; i < leqs; i++ ) {
587 d_nnz[i] = d_rows_upper [ i ].giveSize() + d_rows_lower [ i ].giveSize();
588 o_nnz[i] = o_rows_upper [ i ].giveSize() + o_rows_lower [ i ].giveSize();
589
590 d_nnz_sym[i] = d_rows_upper [ i ].giveSize();
591 o_nnz_sym[i] = o_rows_upper [ i ].giveSize();
592 }
593 }
594
595 //fprintf (stderr,"\n[%d]PetscSparseMtrx: Profile ...",rank);
596 //for (i=0; i<leqs; i++) fprintf(stderr, "%d ", d_nnz[i]);
597 //fprintf (stderr,"\n[%d]PetscSparseMtrx: Creating MPIAIJ Matrix ...\n",rank);
598
599 // create PETSc mat
600 MatCreate(this->emodel->giveParallelComm(), & mtrx);
601 MatSetSizes(mtrx, leqs, leqs, geqs, geqs);
602 MatSetType(mtrx, MATMPIAIJ);
603 MatSetFromOptions(mtrx);
604 MatMPIAIJSetPreallocation( mtrx, 0, d_nnz.givePointer(), 0, o_nnz.givePointer() );
605 //MatMPIBAIJSetPreallocation( mtrx, 1, 0, d_nnz.givePointer(), 0, o_nnz.givePointer() );
606 //MatMPISBAIJSetPreallocation( mtrx, 1, 0, d_nnz_sym.givePointer(), 0, o_nnz_sym.givePointer() );
607 //MatXAIJSetPreallocation( mtrx, 1, d_nnz.givePointer(), o_nnz.givePointer(), d_nnz_sym.givePointer(), o_nnz_sym.givePointer());
608
609 // Creates scatter context for PETSc.
610 ISCreateGeneral(this->emodel->giveParallelComm(), context->giveNumberOfNaturalEqs(), n2g->giveN2Gmap()->givePointer(), PETSC_USE_POINTER, & globalIS);
611 ISCreateStride(this->emodel->giveParallelComm(), context->giveNumberOfNaturalEqs(), 0, 1, & localIS);
612
613 #ifdef __VERBOSE_PARALLEL
614 VERBOSEPARALLEL_PRINT("PetscSparseMtrx:: buildInternalStructure", "done", eModel->giveRank());
615 #endif
616 } else
617#endif
618 {
619 leqs = geqs = eModel->giveNumberOfDomainEquations(di, s);
620 IntArray d_nnz, d_nnz_sym, indices, rowstart;
621
622 MatType type = MATSEQBAIJ;
623
624 MatCreate(PETSC_COMM_SELF, & mtrx);
625 MatSetSizes(mtrx, leqs, leqs, geqs, geqs);
626 MatSetType(mtrx, type);
627 MatSetFromOptions(mtrx);
628 MatGetType( mtrx, &type );
629
630 if ( strcmp(type, MATDENSE) != 0 ) {
631 // Dry assembly (computes the nonzero structure) (and the blocks)
632 std :: vector< IntArray > rows_upper(leqs), blocks;
633
634 for ( auto &elem : domain->giveElements() ) {
635 elem->giveLocationArray(loc, s);
636 for ( int ii : loc ) {
637 if ( ii > 0 ) {
638 for ( int jj : loc ) {
639 if ( jj >= ii ) {
640 rows_upper [ ii - 1 ].insertSortedOnce( jj - 1, loc.giveSize() );
641 }
642 }
643 }
644 }
645 }
646
647 // Structure from active boundary conditions.
648 std :: vector< IntArray > r_locs, c_locs;
649 for ( auto &gbc : domain->giveBcs() ) {
650 ActiveBoundaryCondition *activebc = dynamic_cast< ActiveBoundaryCondition * >( gbc.get() );
651 if ( activebc ) {
652 activebc->giveLocationArrays(r_locs, c_locs, TangentStiffnessMatrix, s, s);
653 for ( std :: size_t k = 0; k < r_locs.size(); k++ ) {
654 for ( int ii : r_locs [ k ] ) {
655 if ( ii > 0 ) {
656 for ( int jj : c_locs [ k ] ) {
657 if ( jj >= ii ) {
658 rows_upper [ ii - 1 ].insertSortedOnce( jj - 1, loc.giveSize() );
659 }
660 }
661 }
662 }
663 }
664 }
665 }
666
667
668 int nnz = 0;
669 for ( auto &row_upper : rows_upper ) {
670 nnz += row_upper.giveSize();
671 }
672
673 if ( strcmp(type, MATSEQAIJ) != 0 ) {
674 // Compute optimal block size (test various sizes and compute the "wasted" zeros).
675 int maxblock = domain->giveNumberOfDofManagers() > 0 ? domain->giveDofManager(1)->giveNumberOfDofs() : 0;
676 for ( int bsize = maxblock; bsize > 1; --bsize ) {
677 int nblocks = ceil(rows_upper.size() / (double)bsize);
678 blocks.clear();
679 blocks.resize(nblocks);
680 for ( int i = 0; i < leqs; i++ ) {
681 int blockrow = i / bsize;
682 for ( int j : rows_upper [ i ] ) {
683 int blockcol = j / bsize;
684 blocks [ blockrow ].insertSortedOnce( blockcol );
685 }
686 }
687 // See how much space (and operations) we risk wasting with this block size:
688 int bnnz = 0;
689 for ( auto &block : blocks ) {
690 bnnz += block.giveSize() * bsize * bsize;
691 }
692 OOFEM_LOG_DEBUG("Block size %d resulted in nnz = %d -> %d using %d^2 blocks\n", bsize, nnz, bnnz, nblocks);
693 if ( bnnz <= nnz * 1.2 ) {
694 blocksize = bsize;
695 break;
696 }
697 }
698 }
699
700 if ( blocksize == 1 ) {
701 blocks = rows_upper;
702 }
703
704 int nblocks = ceil(rows_upper.size() / (double)blocksize);
705 d_nnz_sym.resize(nblocks);
706 d_nnz.resize(nblocks);
707 for ( int i = 0; i < nblocks; i++ ) {
708 d_nnz_sym[i] = blocks [ i ].giveSize();
709 // We can optimize to use only upper half by using the fact that the problem has symmetric nonzero-structure.
710 d_nnz[i] += d_nnz_sym[i];
711 for ( int jj : blocks [ i ] ) {
712 if ( jj != i ) {
713 d_nnz[ jj ]++;
714 }
715 }
716 }
717 }
718
719 // Based on the user selected type, determine the block size;
720 if ( strcmp(type, MATSEQAIJ) == 0 ) {
721 MatSeqAIJSetPreallocation( mtrx, 0, d_nnz.givePointer() );
722 } else if ( strcmp(type, MATSEQBAIJ) == 0 ) {
723 MatSeqBAIJSetPreallocation( mtrx, blocksize, 0, d_nnz.givePointer() );
724 } else if ( strcmp(type, MATSEQSBAIJ) == 0 ) {
725 MatSeqSBAIJSetPreallocation( mtrx, blocksize, 0, d_nnz_sym.givePointer() );
726 }
727 }
728
729 MatSetOption(mtrx, MAT_ROW_ORIENTED, PETSC_FALSE); // To allow the insertion of values using MatSetValues in column major order
730 MatSetOption(mtrx, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE);
731 MatSetOption(mtrx, MAT_STRUCTURALLY_SYMMETRIC, PETSC_TRUE);
732
733 nRows = nColumns = geqs;
734 this->newValues = true;
735 return true;
736}
737
738int
739PetscSparseMtrx :: assemble(const IntArray &loc, const FloatMatrix &mat)
740{
741 int ndofe = mat.giveNumberOfRows();
742 IntArray gloc(ndofe);
743
744#ifdef __MPI_PARALLEL_MODE
745 if ( emodel->isParallel() ) {
746 // translate local code numbers to global ones
747 emodel->giveParallelContext(this->di)->giveN2Gmap()->map2New(gloc, loc, 0);
748
749 //fprintf (stderr, "[?] gloc=");
750 //for (int i=1; i<=ndofe; i++) fprintf (stderr, "%d ", gloc.at(i));
751 } else
752#endif
753 {
754 for ( int i = 1; i <= ndofe; i++ ) {
755 gloc.at(i) = loc.at(i) - 1;
756 }
757 }
758
759 this->version++;
760 this->newValues = true;
761
762 // Block'ify the matrix in order to use MatSetValuesBlocked (doing this work really is faster)
763 if ( this->blocksize > 1 && ndofe % this->blocksize == 0 ) {
764 int nblocke = ndofe / this->blocksize;
765 IntArray bloc(nblocke);
766 for ( int b = 0; b < nblocke; ++b ) {
767 int i = b * blocksize;
768 // Have to check alignment inside the block:
769 if ( gloc[i] != -1 && gloc[i] % this->blocksize != 0 ) {
770 MatSetValues(this->mtrx, ndofe, gloc.givePointer(), ndofe, gloc.givePointer(), mat.givePointer(), ADD_VALUES);
771 return 1;
772 }
773 // Have to verify that this is indeed a series of increments in blocks:
774 for ( int k = 1; k < this->blocksize; ++k ) {
775 bool ok_seq = gloc[i] + k == gloc[i+k];
776 bool ok_bc = gloc[i] == -1 && gloc[i+k] == -1;
777 if ( !(ok_bc || ok_seq) ) {
778 // Then we can't use block assembling for this element.
779 MatSetValues(this->mtrx, ndofe, gloc.givePointer(), ndofe, gloc.givePointer(), mat.givePointer(), ADD_VALUES);
780 return 1;
781 }
782 }
783 bloc[b] = gloc[i] == -1 ? -1 : gloc[i] / blocksize;
784 }
785 MatSetValuesBlocked(this->mtrx, nblocke, bloc.givePointer(), nblocke, bloc.givePointer(), mat.givePointer(), ADD_VALUES);
786 } else {
787 MatSetValues(this->mtrx, ndofe, gloc.givePointer(), ndofe, gloc.givePointer(), mat.givePointer(), ADD_VALUES);
788 }
789
790 //MatView(this->mtrx,PETSC_VIEWER_STDOUT_SELF);
791 return 1;
792}
793
794int
795PetscSparseMtrx :: assemble(const IntArray &rloc, const IntArray &cloc, const FloatMatrix &mat)
796{
797#ifdef __MPI_PARALLEL_MODE
798 if ( emodel->isParallel() ) {
799 // translate eq numbers
800 IntArray grloc( rloc.giveSize() ), gcloc( cloc.giveSize() );
801 emodel->giveParallelContext(this->di)->giveN2Gmap()->map2New(grloc, rloc, 0);
802 emodel->giveParallelContext(this->di)->giveN2Gmap()->map2New(gcloc, cloc, 0);
803
804 MatSetValues(this->mtrx, grloc.giveSize(), grloc.givePointer(),
805 gcloc.giveSize(), gcloc.givePointer(), mat.givePointer(), ADD_VALUES);
806 } else {
807#endif
808 int rsize = rloc.giveSize(), csize = cloc.giveSize();
809 IntArray grloc(rsize), gcloc(csize);
810 for ( int i = 1; i <= rsize; i++ ) {
811 grloc.at(i) = rloc.at(i) - 1;
812 }
813
814 for ( int i = 1; i <= csize; i++ ) {
815 gcloc.at(i) = cloc.at(i) - 1;
816 }
817
818 MatSetValues(this->mtrx, rsize, grloc.givePointer(),
819 csize, gcloc.givePointer(), mat.givePointer(), ADD_VALUES);
820
821#ifdef __MPI_PARALLEL_MODE
822}
823#endif
824 // increment version
825 this->version++;
826 this->newValues = true;
827 return 1;
828}
829
830int
831PetscSparseMtrx :: assembleBegin()
832{
833 return MatAssemblyBegin(this->mtrx, MAT_FINAL_ASSEMBLY);
834}
835
836int
837PetscSparseMtrx :: assembleEnd()
838{
839 this->newValues = true;
840 int val = MatAssemblyEnd(this->mtrx, MAT_FINAL_ASSEMBLY);
841 //MatShift(mtrx, 0);
842 return val;
843}
844
845
846void
847PetscSparseMtrx :: zero()
848{
849 // test if receiver is already assembled
850 PetscBool assembled;
851 MatAssembled(this->mtrx, & assembled);
852 if ( assembled ) {
853 MatZeroEntries(this->mtrx);
854 }
855 this->newValues = true;
856}
857
858double
859PetscSparseMtrx :: computeNorm() const
860{
861 double norm;
862 MatNorm(this->mtrx, NORM_1, & norm);
863 return norm;
864}
865
866double &
867PetscSparseMtrx :: at(int i, int j)
868{
869 static double a;
870 OOFEM_ERROR("unsupported");
871 return a;
872}
873
874double
875PetscSparseMtrx :: at(int i, int j) const
876{
877 OOFEM_ERROR("unsupported");
878 return 0;
879 //double value;
880 //int row = i-1, col = j-1;
881 //MatGetValues(this->mtrx, 1, &row, 1, &col, &value);
882 //return value;
883}
884
885std::unique_ptr<SparseMtrx>
886PetscSparseMtrx :: giveSubMatrix(const IntArray &rows, const IntArray &cols)
887{
888#ifdef __MPI_PARALLEL_MODE
889 auto comm = this->emodel->giveParallelComm();
890#else
891 auto comm = PETSC_COMM_SELF;
892#endif
893 IntArray prows = rows, pcols = cols;
894 prows.add(-1);
895 pcols.add(-1);
896
897 std::unique_ptr<PetscSparseMtrx> answer = std::make_unique<PetscSparseMtrx>(prows.giveSize(), pcols.giveSize());
898 answer->emodel = this->emodel;
899 IS is_rows;
900 IS is_cols;
901
902 ISCreateGeneral(comm, prows.giveSize(), prows.givePointer(), PETSC_USE_POINTER, & is_rows);
903 ISCreateGeneral(comm, pcols.giveSize(), pcols.givePointer(), PETSC_USE_POINTER, & is_cols);
904 MatGetSubMatrix(this->mtrx, is_rows, is_cols, MAT_INITIAL_MATRIX, & answer->mtrx);
905 ISDestroy(& is_rows);
906 ISDestroy(& is_cols);
907
908 return std::move(answer);
909}
910
911void
912PetscSparseMtrx :: toFloatMatrix(FloatMatrix &answer) const
913{
914 IntArray rows, cols;
915 rows.enumerate(this->giveNumberOfRows());
916 rows.add(-1);
917 cols.enumerate(this->giveNumberOfColumns());
918 cols.add(-1);
919
920 FloatMatrix ansT(this->giveNumberOfColumns(), this->giveNumberOfRows());
921 MatGetValues(this->mtrx, rows.giveSize(), rows.givePointer(), cols.giveSize(), cols.givePointer(), ansT.givePointer());
922 answer.beTranspositionOf(ansT);
923}
924
925void
926PetscSparseMtrx :: printStatistics() const
927{
928 PetscViewerPushFormat(PETSC_VIEWER_STDOUT_SELF, PETSC_VIEWER_ASCII_INFO);
929 MatView(this->mtrx, PETSC_VIEWER_STDOUT_SELF);
930 PetscViewerPopFormat(PETSC_VIEWER_STDOUT_SELF);
931}
932
933void
934PetscSparseMtrx :: printYourself() const
935{
936 PetscViewerPushFormat(PETSC_VIEWER_STDOUT_SELF, PETSC_VIEWER_ASCII_DENSE);
937 MatView(this->mtrx, PETSC_VIEWER_STDOUT_SELF);
938 PetscViewerPopFormat(PETSC_VIEWER_STDOUT_SELF);
939}
940
941void
942PetscSparseMtrx :: printMatlab() const
943{
944 PetscViewerPushFormat(PETSC_VIEWER_STDOUT_SELF, PETSC_VIEWER_ASCII_MATLAB);
945 MatView(this->mtrx, PETSC_VIEWER_STDOUT_SELF);
946 PetscViewerPopFormat(PETSC_VIEWER_STDOUT_SELF);
947}
948
949void
950PetscSparseMtrx :: writeToFile(const char *fname) const
951{
952 PetscViewer viewer;
953#ifdef __MPI_PARALLEL_MODE
954 PetscViewerASCIIOpen(this->emodel->giveParallelComm(), fname, & viewer);
955#else
956 PetscViewerASCIIOpen(PETSC_COMM_SELF, fname, & viewer);
957#endif
958 MatView(this->mtrx, viewer);
959 PetscViewerDestroy(& viewer);
960}
961
962
963void
964PetscSparseMtrx :: createVecGlobal(Vec *answer) const
965{
966#ifdef __MPI_PARALLEL_MODE
967 if ( emodel->isParallel() ) {
968 VecCreate(this->emodel->giveParallelComm(), answer);
969 VecSetSizes(* answer, this->leqs, this->geqs);
970 VecSetFromOptions(* answer);
971 } else {
972#endif
973 VecCreateSeq(PETSC_COMM_SELF, this->giveNumberOfRows(), answer);
974#ifdef __MPI_PARALLEL_MODE
975}
976#endif
977}
978
979
980int
981PetscSparseMtrx :: scatterG2L(Vec src, FloatArray &dest) const
982{
983 PetscScalar *ptr;
984
985#ifdef __MPI_PARALLEL_MODE
986 if ( emodel->isParallel() ) {
987 ParallelContext *context = emodel->giveParallelContext(di);
988 int neqs = context->giveNumberOfNaturalEqs();
989 Vec locVec;
990 VecCreateSeq(PETSC_COMM_SELF, neqs, & locVec);
991
992 VecScatter n2gvecscat;
993 VecScatterCreate(locVec, localIS, src, globalIS, & n2gvecscat);
994 VecScatterBegin(n2gvecscat, src, locVec, INSERT_VALUES, SCATTER_REVERSE);
995 VecScatterEnd(n2gvecscat, src, locVec, INSERT_VALUES, SCATTER_REVERSE);
996 VecScatterDestroy(& n2gvecscat);
997
998 dest.resize(neqs);
999 VecGetArray(locVec, & ptr);
1000 for ( int i = 0; i < neqs; i++ ) {
1001 dest.at(i + 1) = ptr [ i ];
1002 }
1003
1004 VecRestoreArray(locVec, & ptr);
1005 VecDestroy(& locVec);
1006 } else {
1007#endif
1008 int neqs = this->giveNumberOfRows();
1009 dest.resize(neqs);
1010 VecGetArray(src, & ptr);
1011 for ( int i = 0; i < neqs; i++ ) {
1012 dest.at(i + 1) = ptr [ i ];
1013 }
1014 VecRestoreArray(src, & ptr);
1015#ifdef __MPI_PARALLEL_MODE
1016}
1017#endif
1018 return 1;
1019}
1020
1021
1022int
1023PetscSparseMtrx :: scatterL2G(const FloatArray &src, Vec dest) const
1024{
1025 const PetscScalar *ptr = src.givePointer();
1026
1027#ifdef __MPI_PARALLEL_MODE
1028 if ( emodel->isParallel() ) {
1029 ParallelContext *context = this->emodel->giveParallelContext(di);
1030 int size = src.giveSize();
1031
1032 Natural2LocalOrdering *n2l = context->giveN2Lmap();
1033 Natural2GlobalOrdering *n2g = context->giveN2Gmap();
1034 for ( int i = 0; i < size; i++ ) {
1035 if ( n2l->giveNewEq(i + 1) ) {
1036 int eqg = n2g->giveNewEq(i + 1);
1037 VecSetValue(dest, eqg, ptr [ i ], INSERT_VALUES);
1038 }
1039 }
1040
1041 VecAssemblyBegin(dest);
1042 VecAssemblyEnd(dest);
1043 } else {
1044#endif
1045
1046 int size = src.giveSize();
1047 for ( int i = 0; i < size; i++ ) {
1048 //VecSetValues(dest, 1, & i, ptr + i, mode);
1049 VecSetValue(dest, i, ptr [ i ], INSERT_VALUES);
1050 }
1051
1052 VecAssemblyBegin(dest);
1053 VecAssemblyEnd(dest);
1054#ifdef __MPI_PARALLEL_MODE
1055}
1056#endif
1057 return 1;
1058}
1059
1060
1061SparseMtrxType PetscSparseMtrx :: giveType() const
1062{
1063 return SMT_PetscMtrx;
1064}
1065
1066bool PetscSparseMtrx :: isAsymmetric() const
1067{
1068 return !symmFlag;
1069}
1070
1071Mat *PetscSparseMtrx :: giveMtrx()
1072{
1073 return & this->mtrx;
1074}
1075
1076bool PetscSparseMtrx :: giveSymmetryFlag() const
1077{
1078 return symmFlag;
1079}
1080
1081int PetscSparseMtrx :: setOption(MatOption op, PetscBool flag)
1082{
1083 return MatSetOption(this->mtrx, op, flag);
1084}
1085
1086int PetscSparseMtrx :: giveLeqs()
1087{
1088 return leqs;
1089}
1090
1091int PetscSparseMtrx :: giveDomainIndex() const
1092{
1093 return di;
1094}
1095} // end namespace oofem
#define REGISTER_SparseMtrx(class, type)
virtual void giveLocationArrays(std ::vector< IntArray > &rows, std ::vector< IntArray > &cols, CharType type, const UnknownNumberingScheme &r_s, const UnknownNumberingScheme &c_s)
Definition activebc.h:137
int giveNumberOfDofs() const
Definition dofmanager.C:287
int giveNumberOfDofManagers() const
Returns number of dof managers in domain.
Definition domain.h:461
std ::vector< std ::unique_ptr< GeneralBoundaryCondition > > & giveBcs()
Definition domain.h:349
DofManager * giveDofManager(int n)
Definition domain.C:317
std ::vector< std ::unique_ptr< Element > > & giveElements()
Definition domain.h:294
virtual int giveNumberOfDomainEquations(int di, const UnknownNumberingScheme &num)
Definition engngm.C:452
int giveRank() const
Returns domain rank in a group of collaborating processes (0..groupSize-1).
Definition engngm.h:1154
Domain * giveDomain(int n)
Definition engngm.C:1936
virtual ParallelContext * giveParallelContext(int n)
Definition engngm.C:1961
bool isParallel() const
Returns true if receiver in parallel mode.
Definition engngm.h:1152
void resize(Index s)
Definition floatarray.C:94
double & at(Index i)
Definition floatarray.h:202
Index giveSize() const
Returns the size of receiver.
Definition floatarray.h:261
const double * givePointer() const
Definition floatarray.h:506
const double * givePointer() const
void copyColumn(FloatArray &dest, int c) const
void resize(Index rows, Index cols)
Definition floatmatrix.C:79
int giveNumberOfColumns() const
Returns number of columns of receiver.
void beTranspositionOf(const FloatMatrix &src)
int giveNumberOfRows() const
Returns number of rows of receiver.
void enumerate(int maxVal)
Definition intarray.C:85
void add(int val)
Definition intarray.C:58
void resize(int n)
Definition intarray.C:73
const int * givePointer() const
Definition intarray.h:345
int & at(std::size_t i)
Definition intarray.h:104
int giveSize() const
Definition intarray.h:211
void init(EngngModel *, int di, const UnknownNumberingScheme &n) override
int giveNewEq(int leq) override
void map2New(IntArray &answer, const IntArray &src, int baseOffset=0) override
void map2New(IntArray &answer, const IntArray &src, int baseOffset=0) override
int giveNewEq(int leq) override
void init(EngngModel *, int di, const UnknownNumberingScheme &n) override
Natural2GlobalOrdering * giveN2Gmap()
Natural2LocalOrdering * giveN2Lmap()
IS localIS
Context or scattering/collecting parallel PETSc vectors.
PetscSparseMtrx(int n=0, int m=0)
int scatterL2G(const FloatArray &src, Vec dest) const
bool kspInit
Flag if context initialized.
void createVecGlobal(Vec *answer) const
Creates a global vector that fits the instance of this matrix.
KSP ksp
Linear solver context.
bool newValues
Flag if matrix has changed since last solve.
int scatterG2L(Vec src, FloatArray &dest) const
Scatters global vector to natural one.
int nColumns
Number of columns.
Definition sparsemtrx.h:71
SparseMtrxVersionType version
Definition sparsemtrx.h:82
int giveNumberOfRows() const
Returns number of rows of receiver.
Definition sparsemtrx.h:114
int giveNumberOfColumns() const
Returns number of columns of receiver.
Definition sparsemtrx.h:116
SparseMtrx(int n=0, int m=0)
Definition sparsemtrx.h:89
int nRows
Number of rows.
Definition sparsemtrx.h:69
#define OOFEM_WARNING(...)
Definition error.h:80
#define OOFEM_ERROR(...)
Definition error.h:79
#define OOFEM_LOG_INFO(...)
Definition logger.h:143
#define OOFEM_LOG_DEBUG(...)
Definition logger.h:144
double norm(const FloatArray &x)
@ SMT_PetscMtrx
PETSc library mtrx representation.
#define VERBOSEPARALLEL_PRINT(service, str, rank)
Definition parallel.h:50

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011