deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
sparse_direct.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2001 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
16
22#include <deal.II/lac/vector.h>
23
24#include <complex>
25#include <type_traits>
26#include <vector>
27
28#ifdef DEAL_II_WITH_UMFPACK
29# include <umfpack.h>
30#endif
31
32#ifdef DEAL_II_WITH_MUMPS
33# include <dmumps_c.h>
34#endif
35
37
38namespace PETScWrappers
39{
40 namespace MPI
41 {
42 class SparseMatrix;
43 class Vector;
44 } // namespace MPI
45} // namespace PETScWrappers
46
47namespace
48{
56 template <typename SparseMatrixType>
57 unsigned int
58 parallel_grainsize(const SparseMatrixType &matrix)
59 {
60 const unsigned int avg_entries_per_row =
61 matrix.n_nonzero_elements() / matrix.m();
62 return std::max(1000 / avg_entries_per_row, 1u);
63 }
64} // namespace
65
66
71
72
73void
76
77
78#ifdef DEAL_II_WITH_UMFPACK
79
81 : n_rows(0)
82 , n_cols(0)
83 , symbolic_decomposition(nullptr)
84 , numeric_decomposition(nullptr)
85 , control(UMFPACK_CONTROL)
86{
87 umfpack_dl_defaults(control.data());
88}
89
90
91
92void
94{
95 // delete objects that haven't been deleted yet
96 if (symbolic_decomposition != nullptr)
97 {
98 umfpack_dl_free_symbolic(&symbolic_decomposition);
99 symbolic_decomposition = nullptr;
100 }
101
102 if (numeric_decomposition != nullptr)
103 {
104 umfpack_dl_free_numeric(&numeric_decomposition);
105 numeric_decomposition = nullptr;
106 }
107
108 {
109 std::vector<types::suitesparse_index> tmp;
110 tmp.swap(Ap);
111 }
112
113 {
114 std::vector<types::suitesparse_index> tmp;
115 tmp.swap(Ai);
116 }
117
118 {
119 std::vector<double> tmp;
120 tmp.swap(Ax);
121 }
122
123 {
124 std::vector<double> tmp;
125 tmp.swap(Az);
126 }
127
128 umfpack_dl_defaults(control.data());
129}
130
131
132
133template <typename number>
134void
136{
137 // do the copying around of entries so that the diagonal entry is in the
138 // right place. note that this is easy to detect: since all entries apart
139 // from the diagonal entry are sorted, we know that the diagonal entry is
140 // in the wrong place if and only if its column index is larger than the
141 // column index of the second entry in a row
142 //
143 // ignore rows with only one or no entry
145 0,
146 matrix.m(),
147 [this](const size_type row_begin, const size_type row_end) {
148 for (size_type row = row_begin; row < row_end; ++row)
149 {
150 // we may have to move some elements that are left of the diagonal
151 // but presently after the diagonal entry to the left, whereas the
152 // diagonal entry has to move to the right. we could first figure out
153 // where to move everything to, but for simplicity we just make a
154 // series of swaps instead (this is kind of a single run of
155 // bubble-sort, which gives us the desired result since the array is
156 // already "almost" sorted)
157 //
158 // in the first loop, the condition in the while-header also checks
159 // that the row has at least two entries and that the diagonal entry
160 // is really in the wrong place
161 long int cursor = Ap[row];
162 while ((cursor < Ap[row + 1] - 1) && (Ai[cursor] > Ai[cursor + 1]))
163 {
164 std::swap(Ai[cursor], Ai[cursor + 1]);
165
166 std::swap(Ax[cursor], Ax[cursor + 1]);
167 if (numbers::NumberTraits<number>::is_complex == true)
168 std::swap(Az[cursor], Az[cursor + 1]);
169
170 ++cursor;
171 }
172 }
173 },
174 parallel_grainsize(matrix));
175}
176
177
178
179template <typename number>
180void
182{
183 // same thing for SparseMatrixEZ
185 0,
186 matrix.m(),
187 [this](const size_type row_begin, const size_type row_end) {
188 for (size_type row = row_begin; row < row_end; ++row)
189 {
190 long int cursor = Ap[row];
191 while ((cursor < Ap[row + 1] - 1) && (Ai[cursor] > Ai[cursor + 1]))
192 {
193 std::swap(Ai[cursor], Ai[cursor + 1]);
194
195 std::swap(Ax[cursor], Ax[cursor + 1]);
196 if (numbers::NumberTraits<number>::is_complex == true)
197 std::swap(Az[cursor], Az[cursor + 1]);
198
199 ++cursor;
200 }
201 }
202 },
203 parallel_grainsize(matrix));
204}
205
206
207
208template <typename number>
209void
211{
212 // the case for block matrices is a bit more difficult, since all we know
213 // is that *within each block*, the diagonal of that block may come
214 // first. however, that means that there may be as many entries per row
215 // in the wrong place as there are block columns. we can do the same
216 // thing as above, but we have to do it multiple times
218 0,
219 matrix.m(),
220 [this, &matrix](const size_type row_begin, const size_type row_end) {
221 for (size_type row = row_begin; row < row_end; ++row)
222 {
223 long int cursor = Ap[row];
224 for (size_type block = 0; block < matrix.n_block_cols(); ++block)
225 {
226 // find the next out-of-order element
227 while ((cursor < Ap[row + 1] - 1) &&
228 (Ai[cursor] < Ai[cursor + 1]))
229 ++cursor;
230
231 // if there is none, then just go on
232 if (cursor == Ap[row + 1] - 1)
233 break;
234
235 // otherwise swap this entry with successive ones as long as
236 // necessary
237 long int element = cursor;
238 while ((element < Ap[row + 1] - 1) &&
239 (Ai[element] > Ai[element + 1]))
240 {
241 std::swap(Ai[element], Ai[element + 1]);
242
243 std::swap(Ax[element], Ax[element + 1]);
244 if (numbers::NumberTraits<number>::is_complex == true)
245 std::swap(Az[element], Az[element + 1]);
246
247 ++element;
248 }
249 }
250 }
251 },
252 parallel_grainsize(matrix));
253}
254
255
256
257template <class Matrix>
258void
260{
261 Assert(matrix.m() == matrix.n(), ExcNotQuadratic());
262
263 clear();
264
265 using number = typename Matrix::value_type;
266
267 n_rows = matrix.m();
268 n_cols = matrix.n();
269
270 const size_type N = matrix.m();
271
272 // copy over the data from the matrix to the data structures UMFPACK
273 // wants. note two things: first, UMFPACK wants compressed column storage
274 // whereas we always do compressed row storage; we work around this by,
275 // rather than shuffling things around, copy over the data we have, but
276 // then call the umfpack_dl_solve function with the UMFPACK_At argument,
277 // meaning that we want to solve for the transpose system
278 //
279 // second: the data we have in the sparse matrices is "almost" right
280 // already; UMFPACK wants the entries in each row (i.e. really: column)
281 // to be sorted in ascending order. we almost have that, except that we
282 // usually store the diagonal first in each row to allow for some
283 // optimizations. thus, we have to resort things a little bit, but only
284 // within each row
285 //
286 // final note: if the matrix has entries in the sparsity pattern that are
287 // actually occupied by entries that have a zero numerical value, then we
288 // keep them anyway. people are supposed to provide accurate sparsity
289 // patterns.
290 Ap.resize(N + 1);
291 Ai.resize(matrix.n_nonzero_elements());
292 Ax.resize(matrix.n_nonzero_elements());
294 Az.resize(matrix.n_nonzero_elements());
295
296 // first fill row lengths array
297 Ap[0] = 0;
298 for (size_type row = 1; row <= N; ++row)
299 Ap[row] = Ap[row - 1] + matrix.get_row_length(row - 1);
300 Assert(static_cast<size_type>(Ap.back()) == Ai.size(), ExcInternalError());
301
302 // then copy over matrix elements. note that for sparse matrices,
303 // iterators are sorted so that they traverse each row from start to end
304 // before moving on to the next row.
306 0,
307 matrix.m(),
308 [this, &matrix](const size_type row_begin, const size_type row_end) {
309 for (size_type row = row_begin; row < row_end; ++row)
310 {
311 long int index = Ap[row];
312 for (typename Matrix::const_iterator p = matrix.begin(row);
313 p != matrix.end(row);
314 ++p)
315 {
316 // write entry into the first free one for this row
317 Ai[index] = p->column();
318 Ax[index] = std::real(p->value());
319 if (numbers::NumberTraits<number>::is_complex == true)
320 Az[index] = std::imag(p->value());
321
322 // then move pointer ahead
323 ++index;
324 }
325 Assert(index == Ap[row + 1], ExcInternalError());
326 }
327 },
328 parallel_grainsize(matrix));
329
330 // make sure that the elements in each row are sorted. we have to be more
331 // careful for block sparse matrices, so ship this task out to a
332 // different function
333 sort_arrays(matrix);
334
335 int status;
337 status = umfpack_dl_symbolic(N,
338 N,
339 Ap.data(),
340 Ai.data(),
341 Ax.data(),
342 &symbolic_decomposition,
343 control.data(),
344 nullptr);
345 else
346 status = umfpack_zl_symbolic(N,
347 N,
348 Ap.data(),
349 Ai.data(),
350 Ax.data(),
351 Az.data(),
352 &symbolic_decomposition,
353 control.data(),
354 nullptr);
355 AssertThrow(status == UMFPACK_OK,
356 ExcUMFPACKError("umfpack_dl_symbolic", status));
357
359 status = umfpack_dl_numeric(Ap.data(),
360 Ai.data(),
361 Ax.data(),
362 symbolic_decomposition,
363 &numeric_decomposition,
364 control.data(),
365 nullptr);
366 else
367 status = umfpack_zl_numeric(Ap.data(),
368 Ai.data(),
369 Ax.data(),
370 Az.data(),
371 symbolic_decomposition,
372 &numeric_decomposition,
373 control.data(),
374 nullptr);
375
376 // Clean up before we deal with the error code from the calls above:
377 umfpack_dl_free_symbolic(&symbolic_decomposition);
378 if (status == UMFPACK_WARNING_singular_matrix)
379 {
380 // UMFPACK sometimes warns that the matrix is singular, but that a
381 // factorization was successful nonetheless. Report this by
382 // throwing an exception that can be caught at a higher level:
383 AssertThrow(false,
385 "UMFPACK reports that the matrix is singular, "
386 "but that the factorization was successful anyway. "
387 "You can try and see whether you can still "
388 "solve a linear system with such a factorization "
389 "by catching and ignoring this exception, "
390 "though in practice this will typically not "
391 "work."));
392 }
393 else
394 AssertThrow(status == UMFPACK_OK,
395 ExcUMFPACKError("umfpack_dl_numeric", status));
396}
397
398
399
400void
402 const bool transpose /*=false*/) const
403{
404 // make sure that some kind of factorize() call has happened before
405 Assert(Ap.size() != 0, ExcNotInitialized());
406 Assert(Ai.size() != 0, ExcNotInitialized());
407 Assert(Ai.size() == Ax.size(), ExcNotInitialized());
408
409 Assert(Az.empty(),
410 ExcMessage("You have previously factored a matrix using this class "
411 "that had complex-valued entries. This then requires "
412 "applying the factored matrix to a complex-valued "
413 "vector, but you are only providing a real-valued vector "
414 "here."));
415
416 Vector<double> rhs(rhs_and_solution.size());
417 rhs = rhs_and_solution;
418
419 // solve the system. note that since UMFPACK wants compressed column
420 // storage instead of the compressed row storage format we use in
421 // deal.II's SparsityPattern classes, we solve for UMFPACK's A^T instead
422
423 // Conversely, if we solve for the transpose, we have to use UMFPACK_A
424 // instead.
425 const int status = umfpack_dl_solve(transpose ? UMFPACK_A : UMFPACK_At,
426 Ap.data(),
427 Ai.data(),
428 Ax.data(),
429 rhs_and_solution.begin(),
430 rhs.begin(),
432 control.data(),
433 nullptr);
434 AssertThrow(status == UMFPACK_OK,
435 ExcUMFPACKError("umfpack_dl_solve", status));
436}
437
438
439
440void
441SparseDirectUMFPACK::solve(Vector<std::complex<double>> &rhs_and_solution,
442 const bool transpose /*=false*/) const
443{
444# ifdef DEAL_II_WITH_COMPLEX_VALUES
445 // make sure that some kind of factorize() call has happened before
446 Assert(Ap.size() != 0, ExcNotInitialized());
447 Assert(Ai.size() != 0, ExcNotInitialized());
448 Assert(Ai.size() == Ax.size(), ExcNotInitialized());
449
450 // First see whether the matrix that was factorized was complex-valued.
451 // If so, just apply the complex factorization to the vector.
452 if (Az.size() != 0)
453 {
454 Assert(Ax.size() == Az.size(), ExcInternalError());
455
456 // It would be nice if we could just present a pointer to the
457 // first element of the complex-valued solution vector and let
458 // UMFPACK fill both the real and imaginary parts of the solution
459 // vector at that address. UMFPACK calls this the 'packed' format,
460 // and in those cases it only takes one pointer to the entire
461 // vector, rather than a pointer to the real and one pointer to
462 // the imaginary parts of the vector. The problem is that if we
463 // want to pack, then we also need to pack the matrix, and the
464 // functions above have already decided that we don't want to pack
465 // the matrix but instead deal with split format for the matrix,
466 // and then UMFPACK decides that it can't deal with a split matrix
467 // and a packed vector. We have to choose one or the other, not
468 // mix.
469 //
470 // So create four vectors, one each for the real and imaginary parts
471 // of the right hand side and solution.
472 Vector<double> rhs_re(rhs_and_solution.size());
473 Vector<double> rhs_im(rhs_and_solution.size());
474 for (unsigned int i = 0; i < rhs_and_solution.size(); ++i)
475 {
476 rhs_re(i) = std::real(rhs_and_solution(i));
477 rhs_im(i) = std::imag(rhs_and_solution(i));
478 }
479
480 Vector<double> solution_re(rhs_and_solution.size());
481 Vector<double> solution_im(rhs_and_solution.size());
482
483 // Solve the system. note that since UMFPACK wants compressed column
484 // storage instead of the compressed row storage format we use in
485 // deal.II's SparsityPattern classes, we solve for UMFPACK's A^T instead
486 //
487 // Conversely, if we solve for the transpose, we have to use UMFPACK_A
488 // instead.
489 //
490 // Note that for the complex case here, the transpose is selected using
491 // UMFPACK_Aat, not UMFPACK_At.
492 const int status = umfpack_zl_solve(transpose ? UMFPACK_A : UMFPACK_Aat,
493 Ap.data(),
494 Ai.data(),
495 Ax.data(),
496 Az.data(),
497 solution_re.data(),
498 solution_im.data(),
499 rhs_re.data(),
500 rhs_im.data(),
502 control.data(),
503 nullptr);
504 AssertThrow(status == UMFPACK_OK,
505 ExcUMFPACKError("umfpack_dl_solve", status));
506
507 // Now put things back together into the output vector
508 for (unsigned int i = 0; i < rhs_and_solution.size(); ++i)
509 rhs_and_solution(i) = {solution_re(i), solution_im(i)};
510 }
511 else
512 {
513 // We have factorized a real-valued matrix, but the rhs and solution
514 // vectors are complex-valued. UMFPACK does not natively support this
515 // case, but we can just apply the factorization to real and imaginary
516 // parts of the right hand side separately
517 const Vector<std::complex<double>> rhs = rhs_and_solution;
518
519 // Get the real part of the right hand side, solve with it, and copy the
520 // results into the result vector by just copying the real output
521 // into the complex-valued result vector (which implies setting the
522 // imaginary parts to zero):
523 Vector<double> rhs_real_or_imag(rhs_and_solution.size());
524 for (unsigned int i = 0; i < rhs.size(); ++i)
525 rhs_real_or_imag(i) = std::real(rhs(i));
526
527 solve(rhs_real_or_imag, transpose);
528
529 rhs_and_solution = rhs_real_or_imag;
530
531 // Then repeat the whole thing with the imaginary part. The copying step
532 // is more complicated now because we can only touch the imaginary
533 // component of the output vector:
534 for (unsigned int i = 0; i < rhs.size(); ++i)
535 rhs_real_or_imag(i) = std::imag(rhs(i));
536
537 solve(rhs_real_or_imag, transpose);
538
539 for (unsigned int i = 0; i < rhs.size(); ++i)
540 rhs_and_solution(i).imag(rhs_real_or_imag(i));
541 }
542
543# else
544
545 (void)rhs_and_solution;
546 (void)transpose;
547 Assert(false,
549 "This function can't be called if deal.II has been configured "
550 "with DEAL_II_WITH_COMPLEX_VALUES=FALSE."));
551# endif
552}
553
554
555void
557 const bool transpose /*=false*/) const
558{
559 // the UMFPACK functions want a contiguous array of elements, so
560 // there is no way around copying data around. thus, just copy the
561 // data into a regular vector and back
562 Vector<double> tmp(rhs_and_solution.size());
563 tmp = rhs_and_solution;
564 solve(tmp, transpose);
565 rhs_and_solution = tmp;
566}
567
568
569
570void
571SparseDirectUMFPACK::solve(BlockVector<std::complex<double>> &rhs_and_solution,
572 const bool transpose /*=false*/) const
573{
574# ifdef DEAL_II_WITH_COMPLEX_VALUES
575 // the UMFPACK functions want a contiguous array of elements, so
576 // there is no way around copying data around. thus, just copy the
577 // data into a regular vector and back
578 Vector<std::complex<double>> tmp(rhs_and_solution.size());
579 tmp = rhs_and_solution;
580 solve(tmp, transpose);
581 rhs_and_solution = tmp;
582
583# else
584 (void)rhs_and_solution;
585 (void)transpose;
586 Assert(false,
588 "This function can't be called if deal.II has been configured "
589 "with DEAL_II_WITH_COMPLEX_VALUES=FALSE."));
590# endif
591}
592
593
594
595template <class Matrix>
596void
597SparseDirectUMFPACK::solve(const Matrix &matrix,
598 Vector<double> &rhs_and_solution,
599 const bool transpose /*=false*/)
600{
601 factorize(matrix);
602 solve(rhs_and_solution, transpose);
603}
604
605
606
607template <class Matrix>
608void
609SparseDirectUMFPACK::solve(const Matrix &matrix,
610 Vector<std::complex<double>> &rhs_and_solution,
611 const bool transpose /*=false*/)
612{
613# ifdef DEAL_II_WITH_COMPLEX_VALUES
614 factorize(matrix);
615 solve(rhs_and_solution, transpose);
616
617# else
618
619 (void)matrix;
620 (void)rhs_and_solution;
621 (void)transpose;
622 Assert(false,
624 "This function can't be called if deal.II has been configured "
625 "with DEAL_II_WITH_COMPLEX_VALUES=FALSE."));
626# endif
627}
628
629
630
631template <class Matrix>
632void
633SparseDirectUMFPACK::solve(const Matrix &matrix,
634 BlockVector<double> &rhs_and_solution,
635 const bool transpose /*=false*/)
636{
637 factorize(matrix);
638 solve(rhs_and_solution, transpose);
639}
640
641
642
643template <class Matrix>
644void
645SparseDirectUMFPACK::solve(const Matrix &matrix,
646 BlockVector<std::complex<double>> &rhs_and_solution,
647 const bool transpose /*=false*/)
648{
649# ifdef DEAL_II_WITH_COMPLEX_VALUES
650 factorize(matrix);
651 solve(rhs_and_solution, transpose);
652
653# else
654
655 (void)matrix;
656 (void)rhs_and_solution;
657 (void)transpose;
658 Assert(false,
660 "This function can't be called if deal.II has been configured "
661 "with DEAL_II_WITH_COMPLEX_VALUES=FALSE."));
662# endif
663}
664
665
666#else
667
668
670 : n_rows(0)
671 , n_cols(0)
672 , symbolic_decomposition(nullptr)
673 , numeric_decomposition(nullptr)
674 , control(0)
675{}
676
677
678void
680{}
681
682
683template <class Matrix>
684void
685SparseDirectUMFPACK::factorize(const Matrix &)
686{
688 false,
690 "To call this function you need UMFPACK, but you configured deal.II "
691 "without passing the necessary switch to 'cmake'. Please consult the "
692 "installation instructions at https://dealii.org/current/readme.html"));
693}
694
695
696void
698{
700 false,
702 "To call this function you need UMFPACK, but you configured deal.II "
703 "without passing the necessary switch to 'cmake'. Please consult the "
704 "installation instructions at https://dealii.org/current/readme.html"));
705}
706
707
708
709void
710SparseDirectUMFPACK::solve(Vector<std::complex<double>> &, const bool) const
711{
713 false,
715 "To call this function you need UMFPACK, but you configured deal.II "
716 "without passing the necessary switch to 'cmake'. Please consult the "
717 "installation instructions at https://dealii.org/current/readme.html"));
718}
719
720
721
722void
724{
726 false,
728 "To call this function you need UMFPACK, but you configured deal.II "
729 "without passing the necessary switch to 'cmake'. Please consult the "
730 "installation instructions at https://dealii.org/current/readme.html"));
731}
732
733
734
735void
736SparseDirectUMFPACK::solve(BlockVector<std::complex<double>> &,
737 const bool) const
738{
740 false,
742 "To call this function you need UMFPACK, but you configured deal.II "
743 "without passing the necessary switch to 'cmake'. Please consult the "
744 "installation instructions at https://dealii.org/current/readme.html"));
745}
746
747
748
749template <class Matrix>
750void
751SparseDirectUMFPACK::solve(const Matrix &, Vector<double> &, const bool)
752{
754 false,
756 "To call this function you need UMFPACK, but you configured deal.II "
757 "without passing the necessary switch to 'cmake'. Please consult the "
758 "installation instructions at https://dealii.org/current/readme.html"));
759}
760
761
762
763template <class Matrix>
764void
765SparseDirectUMFPACK::solve(const Matrix &,
766 Vector<std::complex<double>> &,
767 const bool)
768{
770 false,
772 "To call this function you need UMFPACK, but you configured deal.II "
773 "without passing the necessary switch to 'cmake'. Please consult the "
774 "installation instructions at https://dealii.org/current/readme.html"));
775}
776
777
778
779template <class Matrix>
780void
781SparseDirectUMFPACK::solve(const Matrix &, BlockVector<double> &, const bool)
782{
784 false,
786 "To call this function you need UMFPACK, but you configured deal.II "
787 "without passing the necessary switch to 'cmake'. Please consult the "
788 "installation instructions at https://dealii.org/current/readme.html"));
789}
790
791
792
793template <class Matrix>
794void
795SparseDirectUMFPACK::solve(const Matrix &,
796 BlockVector<std::complex<double>> &,
797 const bool)
798{
800 false,
802 "To call this function you need UMFPACK, but you configured deal.II "
803 "without passing the necessary switch to 'cmake'. Please consult the "
804 "installation instructions at https://dealii.org/current/readme.html"));
805}
806
807#endif
808
809
810template <class Matrix>
811void
813{
814 this->factorize(M);
815}
816
817
818void
820{
821 dst = src;
822 this->solve(dst);
823}
824
825
826
827void
829 const BlockVector<double> &src) const
830{
831 dst = src;
832 this->solve(dst);
833}
834
835
836void
838 const Vector<double> &src) const
839{
840 dst = src;
841 this->solve(dst, /*transpose=*/true);
842}
843
844
845
846void
848 const BlockVector<double> &src) const
849{
850 dst = src;
851 this->solve(dst, /*transpose=*/true);
852}
853
856{
858 return n_rows;
859}
860
863{
865 return n_cols;
866}
867
868
869
870#ifdef DEAL_II_WITH_MUMPS
871
873 const MPI_Comm &communicator)
874 : additional_data(data)
875 , mpi_communicator(communicator)
876{
877 // Initialize MUMPS instance:
878 id.job = -1;
879 id.par = 1;
880
881 Assert(!(additional_data.symmetric == false &&
882 additional_data.posdef == true),
884 "You can't have a positive definite matrix that is not symmetric."));
885
886 if (additional_data.symmetric == true)
887 {
888 if (additional_data.posdef == true)
889 id.sym = 1;
890 else
891 id.sym = 2;
892 }
893 else
894 id.sym = 0;
895
896 id.comm_fortran = (MUMPS_INT)MPI_Comm_c2f(mpi_communicator);
897 dmumps_c(&id);
898
899 if (additional_data.output_details == false)
900 {
901 // No outputs
902 id.icntl[0] = -1;
903 id.icntl[1] = -1;
904 id.icntl[2] = -1;
905 id.icntl[3] = 0;
906 }
907
909 id.icntl[10] = 2;
910
912 {
913 id.icntl[34] = 2;
914 if (additional_data.blr.blr_ucfs == true)
915 id.icntl[35] = 1;
917 ExcMessage("Lowrank threshold must be positive."));
919 }
920}
921
922
923
925{
926 // MUMPS destructor
927 id.job = -2;
928 dmumps_c(&id);
929}
930
931
932
933template <class Matrix>
934void
936{
937 Assert(matrix.n() == matrix.m(), ExcMessage("Matrix needs to be square."));
938
939 n = matrix.n();
940 id.n = n;
941
942 if constexpr (std::is_same_v<Matrix, SparseMatrix<double>>)
943 {
944 // Serial matrix: hand over matrix to MUMPS as centralized assembled
945 // matrix
947 {
948 // number of nonzero elements in matrix
949 nnz = matrix.n_actually_nonzero_elements();
950
951 // representation of the matrix
952 a = std::make_unique<double[]>(nnz);
953
954 // matrix indices pointing to the row and column dimensions
955 // respectively of the matrix representation above (a): ie. a[k] is
956 // the matrix element (irn[k], jcn[k])
957 irn = std::make_unique<MUMPS_INT[]>(nnz);
958 jcn = std::make_unique<MUMPS_INT[]>(nnz);
959
960 size_type n_non_zero_elements = 0;
961
962 // loop over the elements of the matrix row by row, as suggested in
963 // the documentation of the sparse matrix iterator class
964 if (additional_data.symmetric == true)
965 {
966 for (size_type row = 0; row < matrix.m(); ++row)
967 {
968 for (typename Matrix::const_iterator ptr = matrix.begin(row);
969 ptr != matrix.end(row);
970 ++ptr)
971 if (std::abs(ptr->value()) > 0.0 && ptr->column() >= row)
972 {
973 a[n_non_zero_elements] = ptr->value();
974 irn[n_non_zero_elements] = row + 1;
975 jcn[n_non_zero_elements] = ptr->column() + 1;
976
977 ++n_non_zero_elements;
978 }
979 }
980 }
981 else
982 {
983 for (size_type row = 0; row < matrix.m(); ++row)
984 {
985 for (typename Matrix::const_iterator ptr = matrix.begin(row);
986 ptr != matrix.end(row);
987 ++ptr)
988 if (std::abs(ptr->value()) > 0.0)
989 {
990 a[n_non_zero_elements] = ptr->value();
991 irn[n_non_zero_elements] = row + 1;
992 jcn[n_non_zero_elements] = ptr->column() + 1;
993 ++n_non_zero_elements;
994 }
995 }
996 }
997 id.n = n;
998 id.nnz = n_non_zero_elements;
999 id.irn = irn.get();
1000 id.jcn = jcn.get();
1001 id.a = a.get();
1002 }
1003 }
1004 else if constexpr (
1005# ifdef DEAL_II_WITH_TRILINOS
1006 std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix> ||
1007# endif
1008 std::is_same_v<Matrix, PETScWrappers::MPI::SparseMatrix>)
1009 {
1010 int result;
1011 MPI_Comm_compare(mpi_communicator,
1012 matrix.get_mpi_communicator(),
1013 &result);
1014 AssertThrow(result == MPI_IDENT,
1015 ExcMessage("The matrix communicator must match the MUMPS "
1016 "communicator."));
1017
1018 // Distributed matrix case
1019 id.icntl[17] = 3; // distributed matrix assembly
1020 id.nnz = matrix.n_nonzero_elements();
1021 nnz = id.nnz;
1022 size_type n_non_zero_local = 0;
1023
1024 // Get the range of rows owned by this process
1025 locally_owned_rows = matrix.locally_owned_range_indices();
1026 size_type local_non_zeros = 0;
1027
1028# ifdef DEAL_II_WITH_TRILINOS
1029 if constexpr (std::is_same_v<Matrix, TrilinosWrappers::SparseMatrix>)
1030 {
1031 const auto &trilinos_matrix = matrix.trilinos_matrix();
1032# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1033 local_non_zeros = trilinos_matrix.NumMyNonzeros();
1034# else
1035# if DEAL_II_TRILINOS_VERSION_GTE(13, 4, 0)
1036 local_non_zeros = trilinos_matrix.getLocalNumEntries();
1037# else
1038 local_non_zeros = trilinos_matrix.getNodeNumEntries();
1039# endif
1040# endif
1041 }
1042 else
1043# endif
1044 if constexpr (std::is_same_v<Matrix, PETScWrappers::MPI::SparseMatrix>)
1045 {
1046# ifdef DEAL_II_WITH_PETSC
1047 Mat &petsc_matrix =
1048 const_cast<PETScWrappers::MPI::SparseMatrix &>(matrix)
1049 .petsc_matrix();
1050 MatInfo info;
1051 MatGetInfo(petsc_matrix, MAT_LOCAL, &info);
1052 local_non_zeros = (size_type)info.nz_used;
1053# endif
1054 }
1055
1056
1057 // We allocate enough entries for the general, nonsymmetric, case. If case
1058 // of a symmetric matrix, we will end up with fewer entries.
1059 irn = std::make_unique<MUMPS_INT[]>(local_non_zeros);
1060 jcn = std::make_unique<MUMPS_INT[]>(local_non_zeros);
1061 a = std::make_unique<double[]>(local_non_zeros);
1063
1064 if (additional_data.symmetric == true)
1065 {
1066 if constexpr (std::is_same_v<Matrix,
1068 {
1069# ifdef DEAL_II_WITH_PETSC
1070 Mat &petsc_matrix =
1071 const_cast<PETScWrappers::MPI::SparseMatrix &>(matrix)
1072 .petsc_matrix();
1073
1074 PetscInt rstart, rend;
1075 MatGetOwnershipRange(petsc_matrix, &rstart, &rend);
1076 for (PetscInt i = rstart; i < rend; i++)
1077 {
1078 PetscInt n_cols;
1079 const PetscInt *cols;
1080 const PetscScalar *values;
1081 MatGetRow(petsc_matrix, i, &n_cols, &cols, &values);
1082
1083 for (PetscInt j = 0; j < n_cols; j++)
1084 {
1085 if (cols[j] >= i)
1086 {
1087 irn[n_non_zero_local] = i + 1;
1088 jcn[n_non_zero_local] = cols[j] + 1;
1089 a[n_non_zero_local] = values[j];
1090
1091 // Count local non-zeros
1092 n_non_zero_local++;
1093 }
1094 }
1095 MatRestoreRow(petsc_matrix, i, &n_cols, &cols, &values);
1096
1097 // Store the row index for the rhs vector
1098 const types::global_cell_index local_index =
1100 irhs_loc[local_index] = i + 1;
1101 }
1102
1103 id.a_loc = a.get();
1104# endif
1105 }
1106# ifdef DEAL_II_WITH_TRILINOS
1107 else if constexpr (std::is_same_v<Matrix,
1109 {
1110 const auto &trilinos_matrix = matrix.trilinos_matrix();
1111# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1112 const unsigned int n_local_rows = trilinos_matrix.NumMyRows();
1113# else
1114# if DEAL_II_TRILINOS_VERSION_GTE(13, 4, 0)
1115 const unsigned int n_local_rows =
1116 trilinos_matrix.getLocalNumRows();
1117# else
1118 const unsigned int n_local_rows =
1119 trilinos_matrix.getNodeNumRows();
1120# endif
1121# endif
1122 for (unsigned int local_row = 0; local_row < n_local_rows;
1123 ++local_row)
1124 {
1125 int num_entries;
1126# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1127 double *values;
1128 int *local_cols;
1129 int ierr = trilinos_matrix.ExtractMyRowView(local_row,
1130 num_entries,
1131 values,
1132 local_cols);
1133 (void)ierr;
1134 Assert(
1135 ierr == 0,
1136 ExcMessage(
1137 "Error extracting global row view from Trilinos matrix. Error code " +
1138 std::to_string(ierr) + "."));
1139
1140 const auto global_row =
1141 TrilinosWrappers::global_row_index(trilinos_matrix,
1142 local_row);
1143# else
1144 typename std::decay_t<decltype(trilinos_matrix)>::
1145 local_inds_host_view_type local_cols;
1146 typename std::decay_t<
1147 decltype(trilinos_matrix)>::values_host_view_type values;
1148
1149 trilinos_matrix.getLocalRowView(local_row,
1150 local_cols,
1151 values);
1152 num_entries = local_cols.size();
1153
1154 const auto global_row =
1155 trilinos_matrix.getRowMap()->getGlobalElement(local_row);
1156# endif
1157
1158 for (int j = 0; j < num_entries; ++j)
1159 {
1160# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1161 const auto global_column_id =
1163 local_cols[j]);
1164# else
1165 const auto global_column_id =
1166 trilinos_matrix.getColMap()->getGlobalElement(
1167 local_cols[j]);
1168# endif
1169 if (global_column_id >= global_row)
1170 {
1171 irn[n_non_zero_local] = global_row + 1;
1172# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1173 jcn[n_non_zero_local] =
1175 trilinos_matrix, local_cols[j]) +
1176 1;
1177# else
1178 jcn[n_non_zero_local] =
1179 trilinos_matrix.getColMap()->getGlobalElement(
1180 local_cols[j]) +
1181 1;
1182# endif
1183 a[n_non_zero_local] = values[j];
1184
1185 // Count local non-zeros
1186 n_non_zero_local++;
1187 }
1188 }
1189
1190 // Store the row index for the rhs vector
1191 irhs_loc[local_row] = global_row + 1;
1192 }
1193 id.a_loc = a.get();
1194 }
1195# endif
1196 else
1197 {
1199 }
1200 }
1201 else
1202 {
1203 // Unsymmetric case
1204 if constexpr (std::is_same_v<Matrix,
1206 {
1207# ifdef DEAL_II_WITH_PETSC
1208 Mat &petsc_matrix =
1209 const_cast<PETScWrappers::MPI::SparseMatrix &>(matrix)
1210 .petsc_matrix();
1211
1212 PetscInt rstart, rend;
1213 MatGetOwnershipRange(petsc_matrix, &rstart, &rend);
1214 for (PetscInt i = rstart; i < rend; i++)
1215 {
1216 PetscInt n_cols;
1217 const PetscInt *cols;
1218 const PetscScalar *values;
1219 MatGetRow(petsc_matrix, i, &n_cols, &cols, &values);
1220
1221 for (PetscInt j = 0; j < n_cols; j++)
1222 {
1223 irn[n_non_zero_local] = i + 1;
1224 jcn[n_non_zero_local] = cols[j] + 1;
1225 a[n_non_zero_local] = values[j];
1226
1227 // Count local non-zeros
1228 n_non_zero_local++;
1229 }
1230 MatRestoreRow(petsc_matrix, i, &n_cols, &cols, &values);
1231
1232 // Store the row index for the rhs vector
1233 const types::global_cell_index local_index =
1235 irhs_loc[local_index] = i + 1;
1236 }
1237
1238 id.a_loc = a.get();
1239# endif
1240 }
1241# ifdef DEAL_II_WITH_TRILINOS
1242 else if constexpr (std::is_same_v<Matrix,
1244 {
1245 const auto &trilinos_matrix = matrix.trilinos_matrix();
1246# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1247 const unsigned int n_local_rows = trilinos_matrix.NumMyRows();
1248# else
1249# if DEAL_II_TRILINOS_VERSION_GTE(13, 4, 0)
1250 const unsigned int n_local_rows =
1251 trilinos_matrix.getLocalNumRows();
1252# else
1253 const unsigned int n_local_rows =
1254 trilinos_matrix.getNodeNumRows();
1255# endif
1256# endif
1257 for (unsigned int local_row = 0; local_row < n_local_rows;
1258 ++local_row)
1259 {
1260 int num_entries;
1261# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1262 double *values;
1263 int *local_cols;
1264 int ierr = trilinos_matrix.ExtractMyRowView(local_row,
1265 num_entries,
1266 values,
1267 local_cols);
1268 (void)ierr;
1269 Assert(
1270 ierr == 0,
1271 ExcMessage(
1272 "Error extracting global row view from Trilinos matrix. Error code " +
1273 std::to_string(ierr) + "."));
1274# else
1275 typename std::decay_t<decltype(trilinos_matrix)>::
1276 local_inds_host_view_type local_cols;
1277 typename std::decay_t<
1278 decltype(trilinos_matrix)>::values_host_view_type values;
1279
1280 trilinos_matrix.getLocalRowView(local_row,
1281 local_cols,
1282 values);
1283 num_entries = local_cols.size();
1284# endif
1285
1286# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1287 const auto global_row =
1288 TrilinosWrappers::global_row_index(trilinos_matrix,
1289 local_row);
1290# else
1291 const auto global_row =
1292 trilinos_matrix.getRowMap()->getGlobalElement(local_row);
1293# endif
1294 for (int j = 0; j < num_entries; ++j)
1295 {
1296 irn[n_non_zero_local] = global_row + 1;
1297# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1298 jcn[n_non_zero_local] =
1300 local_cols[j]) +
1301 1;
1302# else
1303 jcn[n_non_zero_local] =
1304 trilinos_matrix.getColMap()->getGlobalElement(
1305 local_cols[j]) +
1306 1;
1307# endif
1308 a[n_non_zero_local] = values[j];
1309
1310 // Count local non-zeros
1311 n_non_zero_local++;
1312 }
1313
1314 // Store the row index for the rhs vector
1315 irhs_loc[local_row] = global_row + 1;
1316 }
1317 id.a_loc = a.get();
1318 }
1319# endif
1320 else
1321 {
1323 }
1324 }
1325
1326 // Hand over local arrays to MUMPS
1327 id.nnz_loc = n_non_zero_local;
1328 id.irn_loc = irn.get();
1329 id.jcn_loc = jcn.get();
1330 id.a_loc = a.get();
1331 id.irhs_loc = irhs_loc.data();
1332
1333 // rhs parameters
1334 id.icntl[19] = 10; // distributed rhs
1335 id.icntl[20] = 0; // centralized solution, stored on rank 0 by MUMPS
1336 id.nrhs = 1;
1337 id.lrhs_loc = n;
1338 id.nloc_rhs = locally_owned_rows.n_elements();
1339 }
1340 else
1341 {
1343 }
1344}
1345
1346
1347
1348void
1350{
1351 Assert(n == new_rhs.size(),
1352 ExcMessage("Matrix size and rhs length must be equal."));
1353
1355 {
1356 rhs.resize(n);
1357 for (size_type i = 0; i < n; ++i)
1358 rhs[i] = new_rhs(i);
1359
1360 id.rhs = &rhs[0];
1361 }
1362}
1363
1364
1365
1366void
1368{
1369 Assert(n == vector.size(),
1370 ExcMessage("Matrix size and solution vector length must be equal."));
1371 Assert(n == rhs.size(),
1372 ExcMessage("Class not initialized with a rhs vector."));
1373
1374 // Copy solution into the given vector
1376 {
1377 for (size_type i = 0; i < n; ++i)
1378 vector(i) = rhs[i];
1379
1380 rhs.resize(0); // remove rhs again
1381 }
1382}
1383
1384
1385
1386template <class Matrix>
1387void
1389{
1390 // Initialize MUMPS instance:
1391 initialize_matrix(matrix);
1392
1393 // Start analysis + factorization
1394 id.job = 4;
1395 dmumps_c(&id);
1396}
1397
1398
1399
1400template <typename VectorType>
1401void
1402SparseDirectMUMPS::vmult(VectorType &dst, const VectorType &src) const
1403{
1404 // Check that the matrix has at least one nonzero element:
1405 Assert(nnz != 0, ExcNotInitialized());
1406 Assert(n == dst.size(), ExcMessage("Destination vector has the wrong size."));
1407 Assert(n == src.size(), ExcMessage("Source vector has the wrong size."));
1408
1409
1410 if constexpr (std::is_same_v<VectorType, Vector<double>>)
1411 {
1412 // Centralized assembly for serial vectors.
1413
1414 // Hand over right-hand side
1415 copy_rhs_to_mumps(src);
1416
1417 // Start solver
1418 id.job = 3;
1419 dmumps_c(&id);
1420 copy_solution(dst);
1421 }
1422 else if constexpr (
1423# ifdef DEAL_II_WITH_TRILINOS
1424 std::is_same_v<VectorType, TrilinosWrappers::MPI::Vector> ||
1425# endif
1426 std::is_same_v<VectorType, PETScWrappers::MPI::Vector> ||
1427 std::is_same_v<VectorType, LinearAlgebra::distributed::Vector<double>>)
1428 {
1429# ifdef DEAL_II_WITH_TRILINOS
1430 if constexpr (std::is_same_v<VectorType, TrilinosWrappers::MPI::Vector>)
1431 {
1432# ifdef DEAL_II_TRILINOS_WITH_EPETRA
1433 id.rhs_loc = const_cast<double *>(src.begin());
1434# else
1435 Assert(false, ExcNotImplemented());
1436# endif
1437 }
1438 else
1439# endif
1440 if constexpr (std::is_same_v<
1441 VectorType,
1443 id.rhs_loc = const_cast<double *>(src.begin());
1444 else if constexpr (std::is_same_v<VectorType, PETScWrappers::MPI::Vector>)
1445 {
1446# ifdef DEAL_II_WITH_PETSC
1447 PetscScalar *local_array;
1448 VecGetArray(
1449 const_cast<PETScWrappers::MPI::Vector &>(src).petsc_vector(),
1450 &local_array);
1451 id.rhs_loc = local_array;
1452 VecRestoreArray(
1453 const_cast<PETScWrappers::MPI::Vector &>(src).petsc_vector(),
1454 &local_array);
1455# endif
1456 }
1457 else
1459
1460
1461
1463 {
1464 rhs.resize(id.lrhs_loc);
1465 id.rhs = rhs.data();
1466 }
1467
1468 // Start solver
1469 id.job = 3;
1470 dmumps_c(&id);
1471
1472 // Copy solution into the given vector
1473 // For MUMPS with centralized solution (icntl[20]=0), the solution is only
1474 // on the root process (0) and needs to be distributed to all processes
1475
1476 // Get locally owned range for this process
1477 const IndexSet &locally_owned = dst.locally_owned_elements();
1478 const size_type local_size = locally_owned.n_elements();
1479
1480 const unsigned int my_rank =
1482
1483 const std::vector<size_type> sizes =
1485 const std::vector<types::global_dof_index> displs =
1487 locally_owned.nth_index_in_set(0),
1488 0);
1489
1490 // create a vector of vectors to send the local parts to each process
1491 std::vector<std::vector<double>> objects_to_send;
1492 if (my_rank == 0)
1493 {
1494 const unsigned int n_procs =
1496 objects_to_send.resize(n_procs);
1497 for (unsigned int proc = 0; proc < n_procs; ++proc)
1498 {
1499 objects_to_send[proc].resize(sizes[proc]);
1500 for (types::global_dof_index i = 0; i < sizes[proc]; ++i)
1501 objects_to_send[proc][i] = rhs[displs[proc] + i];
1502 }
1503 }
1504
1505
1506 // distribute the solution from rank 0 to other processes
1507 const std::vector<double> local_values =
1508 Utilities::MPI::scatter(mpi_communicator, objects_to_send, 0);
1509
1510 // Set local values in dst vector
1511 size_type idx = 0;
1512 for (const types::global_dof_index local_idx : locally_owned)
1513 dst[local_idx] = local_values[idx++];
1514 dst.compress(VectorOperation::insert);
1515
1516 rhs.resize(0); // remove rhs again
1517 }
1518 else
1519 {
1521 }
1522}
1523
1524
1525template <typename VectorType>
1526void
1527SparseDirectMUMPS::Tvmult(VectorType &dst, const VectorType &src) const
1528{
1529 // The matrix has at least one nonzero element:
1530 Assert(nnz != 0, ExcNotInitialized());
1531 Assert(n == dst.size(), ExcMessage("Destination vector has the wrong size."));
1532 Assert(n == src.size(), ExcMessage("Source vector has the wrong size."));
1533
1534 id.icntl[8] = 2; // transpose
1535 vmult(dst, src);
1536 id.icntl[8] = 1; // reset to default
1537}
1538
1539
1540
1541int *
1543{
1544 return id.icntl;
1545}
1546
1547
1548
1549#else
1550
1551
1552SparseDirectMUMPS::SparseDirectMUMPS(const AdditionalData &, const MPI_Comm &)
1553 : mpi_communicator(MPI_COMM_SELF)
1554{
1556 false,
1557 ExcMessage(
1558 "To call this function you need MUMPS, but you configured deal.II "
1559 "without passing the necessary switch to 'cmake'. Please consult the "
1560 "installation instructions at https://dealii.org/current/readme.html"));
1561}
1562
1563
1564
1566{}
1567
1568
1569#endif // DEAL_II_WITH_MUMPS
1570
1571
1572
1573// explicit instantiations for SparseMatrixUMFPACK
1574#define InstantiateUMFPACK(MatrixType) \
1575 template void SparseDirectUMFPACK::factorize(const MatrixType &); \
1576 template void SparseDirectUMFPACK::solve(const MatrixType &, \
1577 Vector<double> &, \
1578 const bool); \
1579 template void SparseDirectUMFPACK::solve(const MatrixType &, \
1580 Vector<std::complex<double>> &, \
1581 const bool); \
1582 template void SparseDirectUMFPACK::solve(const MatrixType &, \
1583 BlockVector<double> &, \
1584 const bool); \
1585 template void SparseDirectUMFPACK::solve( \
1586 const MatrixType &, BlockVector<std::complex<double>> &, const bool); \
1587 template void SparseDirectUMFPACK::initialize(const MatrixType &, \
1588 const AdditionalData)
1589
1590// Instantiate everything for real-valued matrices
1597
1598// Now also for complex-valued matrices
1599#ifdef DEAL_II_WITH_COMPLEX_VALUES
1600InstantiateUMFPACK(SparseMatrix<std::complex<double>>);
1601InstantiateUMFPACK(SparseMatrix<std::complex<float>>);
1604#endif
1605
1606// explicit instantiations for SparseDirectMUMPS
1607#ifdef DEAL_II_WITH_MUMPS
1608
1609# define InstantiateMUMPSMatVec(VECTOR) \
1610 template void SparseDirectMUMPS::vmult(VECTOR &, const VECTOR &) const; \
1611 template void SparseDirectMUMPS::Tvmult(VECTOR &, const VECTOR &) const;
1612# ifdef DEAL_II_WITH_TRILINOS
1614# endif
1615# ifdef DEAL_II_WITH_PETSC
1617# endif
1620
1621# define InstantiateMUMPS(MATRIX) \
1622 template void SparseDirectMUMPS::initialize(const MATRIX &);
1623
1626# ifdef DEAL_II_WITH_TRILINOS
1628# endif
1629# ifdef DEAL_II_WITH_PETSC
1632# endif
1633 // InstantiateMUMPS(SparseMatrixEZ<double>)
1634 // InstantiateMUMPS(SparseMatrixEZ<float>)
1637#endif
1638
virtual size_type size() const override
size_type index_within_set(const size_type global_index) const
Definition index_set.h:1977
size_type n_elements() const
Definition index_set.h:1917
size_type nth_index_in_set(const size_type local_index) const
Definition index_set.h:1958
void copy_solution(Vector< double > &vector) const
void initialize_matrix(const Matrix &matrix)
void initialize(const Matrix &matrix)
std::vector< double > rhs
std::unique_ptr< double[]> a
void copy_rhs_to_mumps(const Vector< double > &rhs) const
types::global_dof_index n
const MPI_Comm mpi_communicator
SparseDirectMUMPS(const AdditionalData &additional_data=AdditionalData(), const MPI_Comm &communicator=MPI_COMM_WORLD)
std::unique_ptr< types::mumps_index[]> irn
std::unique_ptr< types::mumps_index[]> jcn
std::vector< types::mumps_index > irhs_loc
types::global_dof_index size_type
void vmult(VectorType &dst, const VectorType &src) const
types::mumps_nnz nnz
IndexSet locally_owned_rows
AdditionalData additional_data
void Tvmult(VectorType &, const VectorType &src) const
std::vector< double > Az
~SparseDirectUMFPACK() override
void initialize(const SparsityPattern &sparsity_pattern)
void Tvmult(Vector< double > &dst, const Vector< double > &src) const
size_type m() const
void solve(Vector< double > &rhs_and_solution, const bool transpose=false) const
std::vector< double > Ax
void sort_arrays(const SparseMatrixEZ< number > &)
size_type n() const
void factorize(const Matrix &matrix)
std::vector< double > control
void vmult(Vector< double > &dst, const Vector< double > &src) const
std::vector< types::suitesparse_index > Ap
std::vector< types::suitesparse_index > Ai
pointer data()
virtual size_type size() const override
iterator begin()
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
DerivativeForm< 1, spacedim, dim, Number > transpose(const DerivativeForm< 1, dim, spacedim, Number > &DF)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
static ::ExceptionBase & ExcUMFPACKError(std::string arg1, int arg2)
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcNotInitialized()
static ::ExceptionBase & ExcNotQuadratic()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
const unsigned int my_rank
Definition mpi.cc:917
std::vector< index_type > data
Definition mpi.cc:734
const unsigned int n_procs
Definition mpi.cc:923
@ matrix
Contents is actually a matrix.
TrilinosWrappers::types::int_type global_column_index(const Epetra_CrsMatrix &matrix, const ::types::global_dof_index i)
TrilinosWrappers::types::int_type global_row_index(const Epetra_CrsMatrix &matrix, const ::types::global_dof_index i)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
std::vector< T > gather(const MPI_Comm comm, const T &object_to_send, const unsigned int root_process=0)
T scatter(const MPI_Comm comm, const std::vector< T > &objects_to_send, const unsigned int root_process=0)
void apply_to_subranges(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, const Function &f, const unsigned int grainsize)
Definition parallel.h:266
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
#define InstantiateUMFPACK(MatrixType)
#define InstantiateMUMPS(MATRIX)
#define InstantiateMUMPSMatVec(VECTOR)