deal.II version GIT relicensing-6839-g338455934c 2026-10-02 12:10: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
scalapack.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) 2017 - 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
15#include <deal.II/base/mpi.h>
16#include <deal.II/base/mpi.templates.h>
17
19#include <deal.II/lac/scalapack.templates.h>
20
21#ifdef DEAL_II_WITH_HDF5
22# include <hdf5.h>
23#endif
24
25#include <limits>
26#include <memory>
27
29
30#ifdef DEAL_II_WITH_HDF5
31
32namespace
33{
34 template <typename number>
35 hid_t
36 hdf5_type_id(const number *)
37 {
39 // don't know what to put here; it does not matter
40 return -1;
41 }
42
43 hid_t
44 hdf5_type_id(const double *)
45 {
46 return H5T_NATIVE_DOUBLE;
47 }
48
49 hid_t
50 hdf5_type_id(const float *)
51 {
52 return H5T_NATIVE_FLOAT;
53 }
54} // namespace
55#endif // DEAL_II_WITH_HDF5
56
57
58
59template <typename NumberType>
61 const size_type n_rows_,
62 const size_type n_columns_,
63 const std::shared_ptr<const Utilities::MPI::ProcessGrid> &process_grid,
64 const size_type row_block_size_,
65 const size_type column_block_size_,
66 const LAPACKSupport::Property property_)
67 : uplo('L')
68 , // for non-symmetric matrices this is not needed
69 first_process_row(0)
70 , first_process_column(0)
71 , submatrix_row(1)
72 , submatrix_column(1)
73{
74 reinit(n_rows_,
75 n_columns_,
76 process_grid,
77 row_block_size_,
78 column_block_size_,
79 property_);
80}
81
82
83
84template <typename NumberType>
86 const size_type size,
87 const std::shared_ptr<const Utilities::MPI::ProcessGrid> &process_grid,
88 const size_type block_size,
89 const LAPACKSupport::Property property)
90 : ScaLAPACKMatrix<NumberType>(size,
91 size,
92 process_grid,
93 block_size,
94 block_size,
95 property)
96{}
97
98
99
100template <typename NumberType>
102 const std::string &filename,
103 const std::shared_ptr<const Utilities::MPI::ProcessGrid> &process_grid,
104 const size_type row_block_size,
105 const size_type column_block_size)
106 : uplo('L')
107 , // for non-symmetric matrices this is not needed
108 first_process_row(0)
109 , first_process_column(0)
110 , submatrix_row(1)
111 , submatrix_column(1)
112{
113#ifndef DEAL_II_WITH_HDF5
114 (void)filename;
115 (void)process_grid;
116 (void)row_block_size;
117 (void)column_block_size;
118 Assert(
119 false,
121 "This function is only available when deal.II is configured with HDF5"));
122#else
123
124 const unsigned int this_mpi_process(
125 Utilities::MPI::this_mpi_process(process_grid->mpi_communicator));
126
127 // Before reading the content from disk the root process determines the
128 // dimensions of the matrix. Subsequently, memory is allocated by a call to
129 // reinit() and the matrix is loaded by a call to load().
130 if (this_mpi_process == 0)
131 {
132 herr_t status = 0;
133
134 // open file in read-only mode
135 hid_t file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
136 AssertThrow(file >= 0, ExcIO());
137
138 // get data set in file
139 hid_t dataset = H5Dopen2(file, "/matrix", H5P_DEFAULT);
140 AssertThrow(dataset >= 0, ExcIO());
141
142 // determine file space
143 hid_t filespace = H5Dget_space(dataset);
144
145 // get number of dimensions in data set
146 int rank = H5Sget_simple_extent_ndims(filespace);
147 AssertThrow(rank == 2, ExcIO());
148 hsize_t dims[2];
149 status = H5Sget_simple_extent_dims(filespace, dims, nullptr);
150 AssertThrow(status >= 0, ExcIO());
151
152 // due to ScaLAPACK's column-major memory layout the dimensions are
153 // swapped
154 n_rows = dims[1];
155 n_columns = dims[0];
156
157 // close/release resources
158 status = H5Sclose(filespace);
159 AssertThrow(status >= 0, ExcIO());
160 status = H5Dclose(dataset);
161 AssertThrow(status >= 0, ExcIO());
162 status = H5Fclose(file);
163 AssertThrow(status >= 0, ExcIO());
164 }
165 int ierr = MPI_Bcast(&n_rows,
166 1,
168 0 /*from root*/,
169 process_grid->mpi_communicator);
170 AssertThrowMPI(ierr);
171
172 ierr = MPI_Bcast(&n_columns,
173 1,
175 0 /*from root*/,
176 process_grid->mpi_communicator);
177 AssertThrowMPI(ierr);
178
179 // the property will be overwritten by the subsequent call to load()
181 n_columns,
182 process_grid,
186
187 load(filename.c_str());
188
189#endif // DEAL_II_WITH_HDF5
190}
191
192
193
194template <typename NumberType>
195void
197 const size_type n_rows_,
198 const size_type n_columns_,
199 const std::shared_ptr<const Utilities::MPI::ProcessGrid> &process_grid,
200 const size_type row_block_size_,
201 const size_type column_block_size_,
202 const LAPACKSupport::Property property_)
203{
204 Assert(row_block_size_ > 0, ExcMessage("Row block size has to be positive."));
205 Assert(column_block_size_ > 0,
206 ExcMessage("Column block size has to be positive."));
207 Assert(
208 row_block_size_ <= n_rows_,
210 "Row block size can not be greater than the number of rows of the matrix"));
211 Assert(
212 column_block_size_ <= n_columns_,
214 "Column block size can not be greater than the number of columns of the matrix"));
215
217 property = property_;
218 grid = process_grid;
219 n_rows = n_rows_;
220 n_columns = n_columns_;
221 row_block_size = row_block_size_;
222 column_block_size = column_block_size_;
223
224 if (grid->mpi_process_is_active)
225 {
226 // Get local sizes:
227 n_local_rows = numroc_(&n_rows,
228 &row_block_size,
229 &(grid->this_process_row),
230 &first_process_row,
231 &(grid->n_process_rows));
232 n_local_columns = numroc_(&n_columns,
233 &column_block_size,
234 &(grid->this_process_column),
235 &first_process_column,
236 &(grid->n_process_columns));
237
238 // LLD_A = MAX(1,NUMROC(M_A, MB_A, MYROW, RSRC_A, NPROW)), different
239 // between processes
240 int lda = std::max(1, n_local_rows);
241
242 int info = 0;
243 descinit_(descriptor,
244 &n_rows,
245 &n_columns,
246 &row_block_size,
247 &column_block_size,
248 &first_process_row,
249 &first_process_column,
250 &(grid->blacs_context),
251 &lda,
252 &info);
253 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("descinit_", info));
254
255 this->TransposeTable<NumberType>::reinit(n_local_rows, n_local_columns);
256 }
257 else
258 {
259 // set process-local variables to something telling:
260 n_local_rows = -1;
261 n_local_columns = -1;
262 std::fill(std::begin(descriptor), std::end(descriptor), -1);
263 }
264}
265
266
267
268template <typename NumberType>
269void
271 const size_type size,
272 const std::shared_ptr<const Utilities::MPI::ProcessGrid> &process_grid,
273 const size_type block_size,
274 const LAPACKSupport::Property property)
275{
276 reinit(size, size, process_grid, block_size, block_size, property);
277}
278
279
280
281template <typename NumberType>
282void
284 const LAPACKSupport::Property property_)
285{
286 property = property_;
287}
288
289
290
291template <typename NumberType>
294{
295 return property;
296}
297
298
299
300template <typename NumberType>
303{
304 return state;
305}
306
307
308
309template <typename NumberType>
312{
313 // FIXME: another way to copy is to use pdgeadd_ PBLAS routine.
314 // This routine computes the sum of two matrices B := a*A + b*B.
315 // Matrices can have different distribution,in particular matrix A can
316 // be owned by only one process, so we can set a=1 and b=0 to copy
317 // non-distributed matrix A into distributed matrix B.
318 Assert(n_rows == int(matrix.m()), ExcDimensionMismatch(n_rows, matrix.m()));
319 Assert(n_columns == int(matrix.n()),
320 ExcDimensionMismatch(n_columns, matrix.n()));
321
322 if (grid->mpi_process_is_active)
323 {
324 for (int i = 0; i < n_local_rows; ++i)
325 {
326 const int glob_i = global_row(i);
327 for (int j = 0; j < n_local_columns; ++j)
328 {
329 const int glob_j = global_column(j);
330 local_el(i, j) = matrix(glob_i, glob_j);
331 }
332 }
333 }
334 state = LAPACKSupport::matrix;
335 return *this;
336}
337
338
339
340template <typename NumberType>
341void
343 const unsigned int rank)
344{
345 if (n_rows * n_columns == 0)
346 return;
347
348 const unsigned int this_mpi_process(
349 Utilities::MPI::this_mpi_process(this->grid->mpi_communicator));
350
351 if constexpr (running_in_debug_mode())
352 {
353 Assert(Utilities::MPI::max(rank, this->grid->mpi_communicator) == rank,
355 "All processes have to call routine with identical rank"));
356 Assert(Utilities::MPI::min(rank, this->grid->mpi_communicator) == rank,
358 "All processes have to call routine with identical rank"));
359 }
360
361 // root process has to be active in the grid of A
362 if (this_mpi_process == rank)
363 {
364 Assert(grid->mpi_process_is_active, ExcInternalError());
365 Assert(n_rows == int(B.m()), ExcDimensionMismatch(n_rows, B.m()));
366 Assert(n_columns == int(B.n()), ExcDimensionMismatch(n_columns, B.n()));
367 }
368 // Create 1x1 grid for matrix B.
369 // The underlying grid for matrix B only contains the process #rank.
370 // This grid will be used to copy the serial matrix B to the distributed
371 // matrix using the ScaLAPACK routine pgemr2d.
372 MPI_Group group_A;
373 MPI_Comm_group(this->grid->mpi_communicator, &group_A);
374 const int n = 1;
375 const std::vector<int> ranks(n, rank);
376 MPI_Group group_B;
377 MPI_Group_incl(group_A, n, ranks.data(), &group_B);
378 MPI_Comm communicator_B;
379
381 const int ierr = MPI_Comm_create_group(this->grid->mpi_communicator,
382 group_B,
383 mpi_tag,
384 &communicator_B);
385 AssertThrowMPI(ierr);
386 int n_proc_rows_B = 1, n_proc_cols_B = 1;
387 int this_process_row_B = -1, this_process_column_B = -1;
388 int blacs_context_B = -1;
389 if (MPI_COMM_NULL != communicator_B)
390 {
391 // Initialize Cblas context from the provided communicator
392 blacs_context_B = Csys2blacs_handle(communicator_B);
393 const char *order = "Col";
394 Cblacs_gridinit(&blacs_context_B, order, n_proc_rows_B, n_proc_cols_B);
395 Cblacs_gridinfo(blacs_context_B,
396 &n_proc_rows_B,
397 &n_proc_cols_B,
398 &this_process_row_B,
399 &this_process_column_B);
400 Assert(n_proc_rows_B * n_proc_cols_B == 1, ExcInternalError());
401 // the active process of grid B has to be process #rank of the
402 // communicator attached to A
403 Assert(this_mpi_process == rank, ExcInternalError());
404 }
405 const bool mpi_process_is_active_B =
406 (this_process_row_B >= 0 && this_process_column_B >= 0);
407
408 // create descriptor for matrix B
409 std::vector<int> descriptor_B(9, -1);
410 const int first_process_row_B = 0, first_process_col_B = 0;
411
412 if (mpi_process_is_active_B)
413 {
414 // Get local sizes:
415 int n_local_rows_B = numroc_(&n_rows,
416 &n_rows,
417 &this_process_row_B,
418 &first_process_row_B,
419 &n_proc_rows_B);
420 int n_local_cols_B = numroc_(&n_columns,
421 &n_columns,
422 &this_process_column_B,
423 &first_process_col_B,
424 &n_proc_cols_B);
425 Assert(n_local_rows_B == n_rows, ExcInternalError());
426 Assert(n_local_cols_B == n_columns, ExcInternalError());
427
428 int lda = std::max(1, n_local_rows_B);
429 int info = 0;
430 descinit_(descriptor_B.data(),
431 &n_rows,
432 &n_columns,
433 &n_rows,
434 &n_columns,
435 &first_process_row_B,
436 &first_process_col_B,
437 &blacs_context_B,
438 &lda,
439 &info);
440 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("descinit_", info));
441 }
442 if (this->grid->mpi_process_is_active)
443 {
444 const int ii = 1;
445 NumberType *loc_vals_A =
446 this->values.size() > 0 ? this->values.data() : nullptr;
447 const NumberType *loc_vals_B =
448 mpi_process_is_active_B ? &(B(0, 0)) : nullptr;
449
450 // pgemr2d has to be called only for processes active on grid attached to
451 // matrix A
452 pgemr2d(&n_rows,
453 &n_columns,
454 loc_vals_B,
455 &ii,
456 &ii,
457 descriptor_B.data(),
458 loc_vals_A,
459 &ii,
460 &ii,
461 this->descriptor,
462 &(this->grid->blacs_context));
463 }
464 if (mpi_process_is_active_B)
465 Cblacs_gridexit(blacs_context_B);
466
467 MPI_Group_free(&group_A);
468 MPI_Group_free(&group_B);
469 if (MPI_COMM_NULL != communicator_B)
470 Utilities::MPI::free_communicator(communicator_B);
471
472 state = LAPACKSupport::matrix;
473}
474
475
476
477template <typename NumberType>
478unsigned int
479ScaLAPACKMatrix<NumberType>::global_row(const unsigned int loc_row) const
480{
481 Assert(n_local_rows >= 0 && loc_row < static_cast<unsigned int>(n_local_rows),
482 ExcIndexRange(loc_row, 0, n_local_rows));
483 const int i = loc_row + 1;
484 return indxl2g_(&i,
485 &row_block_size,
486 &(grid->this_process_row),
487 &first_process_row,
488 &(grid->n_process_rows)) -
489 1;
490}
491
492
493
494template <typename NumberType>
495unsigned int
496ScaLAPACKMatrix<NumberType>::global_column(const unsigned int loc_column) const
497{
498 Assert(n_local_columns >= 0 &&
499 loc_column < static_cast<unsigned int>(n_local_columns),
500 ExcIndexRange(loc_column, 0, n_local_columns));
501 const int j = loc_column + 1;
502 return indxl2g_(&j,
503 &column_block_size,
504 &(grid->this_process_column),
505 &first_process_column,
506 &(grid->n_process_columns)) -
507 1;
508}
509
510
511
512template <typename NumberType>
513void
515 const unsigned int rank) const
516{
517 if (n_rows * n_columns == 0)
518 return;
519
520 const unsigned int this_mpi_process(
521 Utilities::MPI::this_mpi_process(this->grid->mpi_communicator));
522
523 if constexpr (running_in_debug_mode())
524 {
525 Assert(Utilities::MPI::max(rank, this->grid->mpi_communicator) == rank,
527 "All processes have to call routine with identical rank"));
528 Assert(Utilities::MPI::min(rank, this->grid->mpi_communicator) == rank,
530 "All processes have to call routine with identical rank"));
531 }
532
533 if (this_mpi_process == rank)
534 {
535 // the process which gets the serial copy has to be in the process grid
536 Assert(this->grid->is_process_active(), ExcInternalError());
537 Assert(n_rows == int(B.m()), ExcDimensionMismatch(n_rows, B.m()));
538 Assert(n_columns == int(B.n()), ExcDimensionMismatch(n_columns, B.n()));
539 }
540
541 // Create 1x1 grid for matrix B.
542 // The underlying grid for matrix B only contains the process #rank.
543 // This grid will be used to copy to the distributed matrix to the serial
544 // matrix B using the ScaLAPACK routine pgemr2d.
545 MPI_Group group_A;
546 MPI_Comm_group(this->grid->mpi_communicator, &group_A);
547 const int n = 1;
548 const std::vector<int> ranks(n, rank);
549 MPI_Group group_B;
550 MPI_Group_incl(group_A, n, ranks.data(), &group_B);
551 MPI_Comm communicator_B;
552
554 const int ierr = MPI_Comm_create_group(this->grid->mpi_communicator,
555 group_B,
556 mpi_tag,
557 &communicator_B);
558 AssertThrowMPI(ierr);
559 int n_proc_rows_B = 1, n_proc_cols_B = 1;
560 int this_process_row_B = -1, this_process_column_B = -1;
561 int blacs_context_B = -1;
562 if (MPI_COMM_NULL != communicator_B)
563 {
564 // Initialize Cblas context from the provided communicator
565 blacs_context_B = Csys2blacs_handle(communicator_B);
566 const char *order = "Col";
567 Cblacs_gridinit(&blacs_context_B, order, n_proc_rows_B, n_proc_cols_B);
568 Cblacs_gridinfo(blacs_context_B,
569 &n_proc_rows_B,
570 &n_proc_cols_B,
571 &this_process_row_B,
572 &this_process_column_B);
573 Assert(n_proc_rows_B * n_proc_cols_B == 1, ExcInternalError());
574 // the active process of grid B has to be process #rank of the
575 // communicator attached to A
576 Assert(this_mpi_process == rank, ExcInternalError());
577 }
578 const bool mpi_process_is_active_B =
579 (this_process_row_B >= 0 && this_process_column_B >= 0);
580
581 // create descriptor for matrix B
582 std::vector<int> descriptor_B(9, -1);
583 const int first_process_row_B = 0, first_process_col_B = 0;
584
585 if (mpi_process_is_active_B)
586 {
587 // Get local sizes:
588 int n_local_rows_B = numroc_(&n_rows,
589 &n_rows,
590 &this_process_row_B,
591 &first_process_row_B,
592 &n_proc_rows_B);
593 int n_local_cols_B = numroc_(&n_columns,
594 &n_columns,
595 &this_process_column_B,
596 &first_process_col_B,
597 &n_proc_cols_B);
598 Assert(n_local_rows_B == n_rows, ExcInternalError());
599 Assert(n_local_cols_B == n_columns, ExcInternalError());
600
601 int lda = std::max(1, n_local_rows_B);
602 int info = 0;
603 // fill descriptor for matrix B
604 descinit_(descriptor_B.data(),
605 &n_rows,
606 &n_columns,
607 &n_rows,
608 &n_columns,
609 &first_process_row_B,
610 &first_process_col_B,
611 &blacs_context_B,
612 &lda,
613 &info);
614 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("descinit_", info));
615 }
616 // pgemr2d has to be called only for processes active on grid attached to
617 // matrix A
618 if (this->grid->mpi_process_is_active)
619 {
620 const int ii = 1;
621 const NumberType *loc_vals_A =
622 this->values.size() > 0 ? this->values.data() : nullptr;
623 NumberType *loc_vals_B = mpi_process_is_active_B ? &(B(0, 0)) : nullptr;
624
625 pgemr2d(&n_rows,
626 &n_columns,
627 loc_vals_A,
628 &ii,
629 &ii,
630 this->descriptor,
631 loc_vals_B,
632 &ii,
633 &ii,
634 descriptor_B.data(),
635 &(this->grid->blacs_context));
636 }
637 if (mpi_process_is_active_B)
638 Cblacs_gridexit(blacs_context_B);
639
640 MPI_Group_free(&group_A);
641 MPI_Group_free(&group_B);
642 if (MPI_COMM_NULL != communicator_B)
643 Utilities::MPI::free_communicator(communicator_B);
644}
645
646
647
648template <typename NumberType>
649void
651{
652 // FIXME: use PDGEMR2d for copying?
653 // PDGEMR2d copies a submatrix of A on a submatrix of B.
654 // A and B can have different distributions
655 // see http://icl.cs.utk.edu/lapack-forum/viewtopic.php?t=50
656 Assert(n_rows == int(matrix.m()), ExcDimensionMismatch(n_rows, matrix.m()));
657 Assert(n_columns == int(matrix.n()),
658 ExcDimensionMismatch(n_columns, matrix.n()));
659
660 matrix = 0.;
661 if (grid->mpi_process_is_active)
662 {
663 for (int i = 0; i < n_local_rows; ++i)
664 {
665 const int glob_i = global_row(i);
666 for (int j = 0; j < n_local_columns; ++j)
667 {
668 const int glob_j = global_column(j);
669 matrix(glob_i, glob_j) = local_el(i, j);
670 }
671 }
672 }
673 Utilities::MPI::sum(matrix, grid->mpi_communicator, matrix);
674
675 // we could move the following lines under the main loop above,
676 // but they would be dependent on glob_i and glob_j, which
677 // won't make it much prettier
678 if (state == LAPACKSupport::cholesky)
679 {
680 if (property == LAPACKSupport::lower_triangular)
681 for (unsigned int i = 0; i < matrix.n(); ++i)
682 for (unsigned int j = i + 1; j < matrix.m(); ++j)
683 matrix(i, j) = 0.;
684 else if (property == LAPACKSupport::upper_triangular)
685 for (unsigned int i = 0; i < matrix.n(); ++i)
686 for (unsigned int j = 0; j < i; ++j)
687 matrix(i, j) = 0.;
688 }
689 else if (property == LAPACKSupport::symmetric &&
691 {
692 if (uplo == 'L')
693 for (unsigned int i = 0; i < matrix.n(); ++i)
694 for (unsigned int j = i + 1; j < matrix.m(); ++j)
695 matrix(i, j) = matrix(j, i);
696 else if (uplo == 'U')
697 for (unsigned int i = 0; i < matrix.n(); ++i)
698 for (unsigned int j = 0; j < i; ++j)
699 matrix(i, j) = matrix(j, i);
700 }
701}
702
703
704
705template <typename NumberType>
706void
709 const std::pair<unsigned int, unsigned int> &offset_A,
710 const std::pair<unsigned int, unsigned int> &offset_B,
711 const std::pair<unsigned int, unsigned int> &submatrix_size) const
712{
713 // submatrix is empty
714 if (submatrix_size.first == 0 || submatrix_size.second == 0)
715 return;
716
717 // range checking for matrix A
718 AssertIndexRange(offset_A.first, n_rows - submatrix_size.first + 1);
719 AssertIndexRange(offset_A.second, n_columns - submatrix_size.second + 1);
720
721 // range checking for matrix B
722 AssertIndexRange(offset_B.first, B.n_rows - submatrix_size.first + 1);
723 AssertIndexRange(offset_B.second, B.n_columns - submatrix_size.second + 1);
724
725 // Currently, copying of matrices will only be supported if A and B share the
726 // same MPI communicator
727 int ierr, comparison;
728 ierr = MPI_Comm_compare(grid->mpi_communicator,
729 B.grid->mpi_communicator,
730 &comparison);
731 AssertThrowMPI(ierr);
732 Assert(comparison == MPI_IDENT,
733 ExcMessage("Matrix A and B must have a common MPI Communicator"));
734
735 /*
736 * The routine pgemr2d requires a BLACS context resembling at least the union
737 * of process grids described by the BLACS contexts held by the ProcessGrids
738 * of matrix A and B. As A and B share the same MPI communicator, there is no
739 * need to create a union MPI communicator to initialize the BLACS context
740 */
741 int union_blacs_context = Csys2blacs_handle(this->grid->mpi_communicator);
742 const char *order = "Col";
743 int union_n_process_rows =
744 Utilities::MPI::n_mpi_processes(this->grid->mpi_communicator);
745 int union_n_process_columns = 1;
746 Cblacs_gridinit(&union_blacs_context,
747 order,
748 union_n_process_rows,
749 union_n_process_columns);
750
751 int n_grid_rows_A, n_grid_columns_A, my_row_A, my_column_A;
752 Cblacs_gridinfo(this->grid->blacs_context,
753 &n_grid_rows_A,
754 &n_grid_columns_A,
755 &my_row_A,
756 &my_column_A);
757
758 // check whether process is in the BLACS context of matrix A
759 const bool in_context_A =
760 (my_row_A >= 0 && my_row_A < n_grid_rows_A) &&
761 (my_column_A >= 0 && my_column_A < n_grid_columns_A);
762
763 int n_grid_rows_B, n_grid_columns_B, my_row_B, my_column_B;
764 Cblacs_gridinfo(B.grid->blacs_context,
765 &n_grid_rows_B,
766 &n_grid_columns_B,
767 &my_row_B,
768 &my_column_B);
769
770 // check whether process is in the BLACS context of matrix B
771 const bool in_context_B =
772 (my_row_B >= 0 && my_row_B < n_grid_rows_B) &&
773 (my_column_B >= 0 && my_column_B < n_grid_columns_B);
774
775 const int n_rows_submatrix = submatrix_size.first;
776 const int n_columns_submatrix = submatrix_size.second;
777
778 // due to Fortran indexing one has to be added
779 int ia = offset_A.first + 1, ja = offset_A.second + 1;
780 int ib = offset_B.first + 1, jb = offset_B.second + 1;
781
782 std::array<int, 9> desc_A, desc_B;
783
784 const NumberType *loc_vals_A = nullptr;
785 NumberType *loc_vals_B = nullptr;
786
787 // Note: the function pgemr2d has to be called for all processes in the union
788 // BLACS context If the calling process is not part of the BLACS context of A,
789 // desc_A[1] has to be -1 and all other parameters do not have to be set If
790 // the calling process is not part of the BLACS context of B, desc_B[1] has to
791 // be -1 and all other parameters do not have to be set
792 if (in_context_A)
793 {
794 if (this->values.size() != 0)
795 loc_vals_A = this->values.data();
796
797 for (unsigned int i = 0; i < desc_A.size(); ++i)
798 desc_A[i] = this->descriptor[i];
799 }
800 else
801 desc_A[1] = -1;
802
803 if (in_context_B)
804 {
805 if (B.values.size() != 0)
806 loc_vals_B = B.values.data();
807
808 for (unsigned int i = 0; i < desc_B.size(); ++i)
809 desc_B[i] = B.descriptor[i];
810 }
811 else
812 desc_B[1] = -1;
813
814 pgemr2d(&n_rows_submatrix,
815 &n_columns_submatrix,
816 loc_vals_A,
817 &ia,
818 &ja,
819 desc_A.data(),
820 loc_vals_B,
821 &ib,
822 &jb,
823 desc_B.data(),
824 &union_blacs_context);
825
827
828 // releasing the union BLACS context
829 Cblacs_gridexit(union_blacs_context);
830}
831
832
833
834template <typename NumberType>
835void
837{
838 Assert(n_rows == dest.n_rows, ExcDimensionMismatch(n_rows, dest.n_rows));
839 Assert(n_columns == dest.n_columns,
840 ExcDimensionMismatch(n_columns, dest.n_columns));
841
842 if (this->grid->mpi_process_is_active)
844 this->descriptor[0] == 1,
846 "Copying of ScaLAPACK matrices only implemented for dense matrices"));
847 if (dest.grid->mpi_process_is_active)
849 dest.descriptor[0] == 1,
851 "Copying of ScaLAPACK matrices only implemented for dense matrices"));
852
853 /*
854 * just in case of different process grids or block-cyclic distributions
855 * inter-process communication is necessary
856 * if distributed matrices have the same process grid and block sizes, local
857 * copying is enough
858 */
859 if ((this->grid != dest.grid) || (row_block_size != dest.row_block_size) ||
860 (column_block_size != dest.column_block_size))
861 {
862 /*
863 * get the MPI communicator, which is the union of the source and
864 * destination MPI communicator
865 */
866 int ierr = 0;
867 MPI_Group group_source, group_dest, group_union;
868 ierr = MPI_Comm_group(this->grid->mpi_communicator, &group_source);
869 AssertThrowMPI(ierr);
870 ierr = MPI_Comm_group(dest.grid->mpi_communicator, &group_dest);
871 AssertThrowMPI(ierr);
872 ierr = MPI_Group_union(group_source, group_dest, &group_union);
873 AssertThrowMPI(ierr);
874 MPI_Comm mpi_communicator_union;
875
876 // to create a communicator representing the union of the source
877 // and destination MPI communicator we need a communicator that
878 // is guaranteed to contain all desired processes -- i.e.,
879 // MPI_COMM_WORLD. on the other hand, as documented in the MPI
880 // standard, MPI_Comm_create_group is not collective on all
881 // processes in the first argument, but instead is collective on
882 // only those processes listed in the group. in other words,
883 // there is really no harm in passing MPI_COMM_WORLD as the
884 // first argument, even if the program we are currently running
885 // and that is calling this function only works on a subset of
886 // processes. the same holds for the wrapper/fallback we are using here.
887
889 ierr = MPI_Comm_create_group(MPI_COMM_WORLD,
890 group_union,
891 mpi_tag,
892 &mpi_communicator_union);
893 AssertThrowMPI(ierr);
894
895 /*
896 * The routine pgemr2d requires a BLACS context resembling at least the
897 * union of process grids described by the BLACS contexts of matrix A and
898 * B
899 */
900 int union_blacs_context = Csys2blacs_handle(mpi_communicator_union);
901 const char *order = "Col";
902 int union_n_process_rows =
903 Utilities::MPI::n_mpi_processes(mpi_communicator_union);
904 int union_n_process_columns = 1;
905 Cblacs_gridinit(&union_blacs_context,
906 order,
907 union_n_process_rows,
908 union_n_process_columns);
909
910 const NumberType *loc_vals_source = nullptr;
911 NumberType *loc_vals_dest = nullptr;
912
913 if (this->grid->mpi_process_is_active && (this->values.size() > 0))
914 {
915 AssertThrow(this->values.size() > 0,
917 "source: process is active but local matrix empty"));
918 loc_vals_source = this->values.data();
919 }
920 if (dest.grid->mpi_process_is_active && (dest.values.size() > 0))
921 {
923 dest.values.size() > 0,
925 "destination: process is active but local matrix empty"));
926 loc_vals_dest = dest.values.data();
927 }
928 pgemr2d(&n_rows,
929 &n_columns,
930 loc_vals_source,
931 &submatrix_row,
932 &submatrix_column,
933 descriptor,
934 loc_vals_dest,
935 &dest.submatrix_row,
936 &dest.submatrix_column,
937 dest.descriptor,
938 &union_blacs_context);
939
940 Cblacs_gridexit(union_blacs_context);
941
942 if (mpi_communicator_union != MPI_COMM_NULL)
943 Utilities::MPI::free_communicator(mpi_communicator_union);
944 ierr = MPI_Group_free(&group_source);
945 AssertThrowMPI(ierr);
946 ierr = MPI_Group_free(&group_dest);
947 AssertThrowMPI(ierr);
948 ierr = MPI_Group_free(&group_union);
949 AssertThrowMPI(ierr);
950 }
951 else
952 // process is active in the process grid
953 if (this->grid->mpi_process_is_active)
954 dest.values = this->values;
955
956 dest.state = state;
957 dest.property = property;
958}
959
960
961
962template <typename NumberType>
963void
966{
967 add(B, 0, 1, true);
968}
969
970
971
972template <typename NumberType>
973void
975 const NumberType alpha,
976 const NumberType beta,
977 const bool transpose_B)
978{
979 if (transpose_B)
980 {
981 Assert(n_rows == B.n_columns, ExcDimensionMismatch(n_rows, B.n_columns));
982 Assert(n_columns == B.n_rows, ExcDimensionMismatch(n_columns, B.n_rows));
983 Assert(column_block_size == B.row_block_size,
984 ExcDimensionMismatch(column_block_size, B.row_block_size));
985 Assert(row_block_size == B.column_block_size,
986 ExcDimensionMismatch(row_block_size, B.column_block_size));
987 }
988 else
989 {
990 Assert(n_rows == B.n_rows, ExcDimensionMismatch(n_rows, B.n_rows));
991 Assert(n_columns == B.n_columns,
992 ExcDimensionMismatch(n_columns, B.n_columns));
993 Assert(column_block_size == B.column_block_size,
994 ExcDimensionMismatch(column_block_size, B.column_block_size));
995 Assert(row_block_size == B.row_block_size,
996 ExcDimensionMismatch(row_block_size, B.row_block_size));
997 }
998 Assert(this->grid == B.grid,
999 ExcMessage("The matrices A and B need to have the same process grid"));
1000
1001 if (this->grid->mpi_process_is_active)
1002 {
1003 char trans_b = transpose_B ? 'T' : 'N';
1004 NumberType *A_loc =
1005 (this->values.size() > 0) ? this->values.data() : nullptr;
1006 const NumberType *B_loc =
1007 (B.values.size() > 0) ? B.values.data() : nullptr;
1008
1009 pgeadd(&trans_b,
1010 &n_rows,
1011 &n_columns,
1012 &beta,
1013 B_loc,
1014 &B.submatrix_row,
1016 B.descriptor,
1017 &alpha,
1018 A_loc,
1019 &submatrix_row,
1020 &submatrix_column,
1021 descriptor);
1022 }
1023 state = LAPACKSupport::matrix;
1024}
1025
1026
1027
1028template <typename NumberType>
1029void
1032{
1033 add(B, 1, a, false);
1034}
1035
1036
1037
1038template <typename NumberType>
1039void
1042{
1043 add(B, 1, a, true);
1044}
1045
1046
1047
1048template <typename NumberType>
1049void
1052 const NumberType c,
1054 const bool transpose_A,
1055 const bool transpose_B) const
1056{
1057 Assert(this->grid == B.grid,
1058 ExcMessage("The matrices A and B need to have the same process grid"));
1059 Assert(C.grid == B.grid,
1060 ExcMessage("The matrices B and C need to have the same process grid"));
1061
1062 // see for further info:
1063 // https://www.ibm.com/support/knowledgecenter/SSNR5K_4.2.0/com.ibm.cluster.pessl.v4r2.pssl100.doc/am6gr_lgemm.htm
1064 if (!transpose_A && !transpose_B)
1065 {
1066 Assert(this->n_columns == B.n_rows,
1067 ExcDimensionMismatch(this->n_columns, B.n_rows));
1068 Assert(this->n_rows == C.n_rows,
1069 ExcDimensionMismatch(this->n_rows, C.n_rows));
1070 Assert(B.n_columns == C.n_columns,
1071 ExcDimensionMismatch(B.n_columns, C.n_columns));
1072 Assert(this->row_block_size == C.row_block_size,
1073 ExcDimensionMismatch(this->row_block_size, C.row_block_size));
1074 Assert(this->column_block_size == B.row_block_size,
1075 ExcDimensionMismatch(this->column_block_size, B.row_block_size));
1076 Assert(B.column_block_size == C.column_block_size,
1077 ExcDimensionMismatch(B.column_block_size, C.column_block_size));
1078 }
1079 else if (transpose_A && !transpose_B)
1080 {
1081 Assert(this->n_rows == B.n_rows,
1082 ExcDimensionMismatch(this->n_rows, B.n_rows));
1083 Assert(this->n_columns == C.n_rows,
1084 ExcDimensionMismatch(this->n_columns, C.n_rows));
1085 Assert(B.n_columns == C.n_columns,
1086 ExcDimensionMismatch(B.n_columns, C.n_columns));
1087 Assert(this->column_block_size == C.row_block_size,
1088 ExcDimensionMismatch(this->column_block_size, C.row_block_size));
1089 Assert(this->row_block_size == B.row_block_size,
1090 ExcDimensionMismatch(this->row_block_size, B.row_block_size));
1091 Assert(B.column_block_size == C.column_block_size,
1092 ExcDimensionMismatch(B.column_block_size, C.column_block_size));
1093 }
1094 else if (!transpose_A && transpose_B)
1095 {
1096 Assert(this->n_columns == B.n_columns,
1097 ExcDimensionMismatch(this->n_columns, B.n_columns));
1098 Assert(this->n_rows == C.n_rows,
1099 ExcDimensionMismatch(this->n_rows, C.n_rows));
1100 Assert(B.n_rows == C.n_columns,
1101 ExcDimensionMismatch(B.n_rows, C.n_columns));
1102 Assert(this->row_block_size == C.row_block_size,
1103 ExcDimensionMismatch(this->row_block_size, C.row_block_size));
1104 Assert(this->column_block_size == B.column_block_size,
1105 ExcDimensionMismatch(this->column_block_size,
1107 Assert(B.row_block_size == C.column_block_size,
1108 ExcDimensionMismatch(B.row_block_size, C.column_block_size));
1109 }
1110 else // if (transpose_A && transpose_B)
1111 {
1112 Assert(this->n_rows == B.n_columns,
1113 ExcDimensionMismatch(this->n_rows, B.n_columns));
1114 Assert(this->n_columns == C.n_rows,
1115 ExcDimensionMismatch(this->n_columns, C.n_rows));
1116 Assert(B.n_rows == C.n_columns,
1117 ExcDimensionMismatch(B.n_rows, C.n_columns));
1118 Assert(this->column_block_size == C.row_block_size,
1119 ExcDimensionMismatch(this->row_block_size, C.row_block_size));
1120 Assert(this->row_block_size == B.column_block_size,
1121 ExcDimensionMismatch(this->column_block_size, B.row_block_size));
1122 Assert(B.row_block_size == C.column_block_size,
1123 ExcDimensionMismatch(B.column_block_size, C.column_block_size));
1124 }
1125
1126 if (this->grid->mpi_process_is_active)
1127 {
1128 char trans_a = transpose_A ? 'T' : 'N';
1129 char trans_b = transpose_B ? 'T' : 'N';
1130
1131 const NumberType *A_loc =
1132 (this->values.size() > 0) ? this->values.data() : nullptr;
1133 const NumberType *B_loc =
1134 (B.values.size() > 0) ? B.values.data() : nullptr;
1135 NumberType *C_loc = (C.values.size() > 0) ? C.values.data() : nullptr;
1136 int m = C.n_rows;
1137 int n = C.n_columns;
1138 int k = transpose_A ? this->n_rows : this->n_columns;
1139
1140 pgemm(&trans_a,
1141 &trans_b,
1142 &m,
1143 &n,
1144 &k,
1145 &b,
1146 A_loc,
1147 &(this->submatrix_row),
1148 &(this->submatrix_column),
1149 this->descriptor,
1150 B_loc,
1151 &B.submatrix_row,
1153 B.descriptor,
1154 &c,
1155 C_loc,
1156 &C.submatrix_row,
1157 &C.submatrix_column,
1158 C.descriptor);
1159 }
1160 C.state = LAPACKSupport::matrix;
1161}
1162
1163
1164
1165template <typename NumberType>
1166void
1169 const bool adding) const
1170{
1171 if (adding)
1172 mult(1., B, 1., C, false, false);
1173 else
1174 mult(1., B, 0, C, false, false);
1175}
1176
1177
1178
1179template <typename NumberType>
1180void
1183 const bool adding) const
1184{
1185 if (adding)
1186 mult(1., B, 1., C, true, false);
1187 else
1188 mult(1., B, 0, C, true, false);
1189}
1190
1191
1192
1193template <typename NumberType>
1194void
1197 const bool adding) const
1198{
1199 if (adding)
1200 mult(1., B, 1., C, false, true);
1201 else
1202 mult(1., B, 0, C, false, true);
1203}
1204
1205
1206
1207template <typename NumberType>
1208void
1211 const bool adding) const
1212{
1213 if (adding)
1214 mult(1., B, 1., C, true, true);
1215 else
1216 mult(1., B, 0, C, true, true);
1217}
1218
1219
1220
1221template <typename NumberType>
1222void
1224{
1225 Assert(
1226 n_columns == n_rows && property == LAPACKSupport::Property::symmetric,
1227 ExcMessage(
1228 "Cholesky factorization can be applied to symmetric matrices only."));
1230 ExcMessage(
1231 "Matrix has to be in Matrix state before calling this function."));
1232
1233 if (grid->mpi_process_is_active)
1234 {
1235 int info = 0;
1236 NumberType *A_loc = this->values.data();
1237 // pdpotrf_(&uplo,&n_columns,A_loc,&submatrix_row,&submatrix_column,descriptor,&info);
1238 ppotrf(&uplo,
1239 &n_columns,
1240 A_loc,
1241 &submatrix_row,
1242 &submatrix_column,
1243 descriptor,
1244 &info);
1245 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("ppotrf", info));
1246 }
1248 property = (uplo == 'L' ? LAPACKSupport::lower_triangular :
1250}
1251
1252
1253
1254template <typename NumberType>
1255void
1257{
1259 ExcMessage(
1260 "Matrix has to be in Matrix state before calling this function."));
1261
1262 if (grid->mpi_process_is_active)
1263 {
1264 int info = 0;
1265 NumberType *A_loc = this->values.data();
1266
1267 const int iarow = indxg2p_(&submatrix_row,
1268 &row_block_size,
1269 &(grid->this_process_row),
1270 &first_process_row,
1271 &(grid->n_process_rows));
1272 const int mp = numroc_(&n_rows,
1273 &row_block_size,
1274 &(grid->this_process_row),
1275 &iarow,
1276 &(grid->n_process_rows));
1277 ipiv.resize(mp + row_block_size);
1278
1279 pgetrf(&n_rows,
1280 &n_columns,
1281 A_loc,
1282 &submatrix_row,
1283 &submatrix_column,
1284 descriptor,
1285 ipiv.data(),
1286 &info);
1287 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pgetrf", info));
1288 }
1291}
1292
1293
1294
1295template <typename NumberType>
1296void
1298{
1299 // Check whether matrix is symmetric and save flag.
1300 // If a Cholesky factorization has been applied previously,
1301 // the original matrix was symmetric.
1302 const bool is_symmetric = (property == LAPACKSupport::symmetric ||
1304
1305 // Check whether matrix is triangular and is in an unfactorized state.
1306 const bool is_triangular = (property == LAPACKSupport::upper_triangular ||
1307 property == LAPACKSupport::lower_triangular) &&
1308 (state == LAPACKSupport::State::matrix ||
1310
1311 if (is_triangular)
1312 {
1313 if (grid->mpi_process_is_active)
1314 {
1315 const char uploTriangular =
1316 property == LAPACKSupport::upper_triangular ? 'U' : 'L';
1317 const char diag = 'N';
1318 int info = 0;
1319 NumberType *A_loc = this->values.data();
1320 ptrtri(&uploTriangular,
1321 &diag,
1322 &n_columns,
1323 A_loc,
1324 &submatrix_row,
1325 &submatrix_column,
1326 descriptor,
1327 &info);
1328 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("ptrtri", info));
1329 // The inversion is stored in the same part as the triangular matrix,
1330 // so we don't need to re-set the property here.
1331 }
1332 }
1333 else
1334 {
1335 // Matrix is neither in Cholesky nor LU state.
1336 // Compute the required factorizations based on the property of the
1337 // matrix.
1338 if (!(state == LAPACKSupport::State::lu ||
1340 {
1341 if (is_symmetric)
1342 compute_cholesky_factorization();
1343 else
1344 compute_lu_factorization();
1345 }
1346 if (grid->mpi_process_is_active)
1347 {
1348 int info = 0;
1349 NumberType *A_loc = this->values.data();
1350
1351 if (is_symmetric)
1352 {
1353 ppotri(&uplo,
1354 &n_columns,
1355 A_loc,
1356 &submatrix_row,
1357 &submatrix_column,
1358 descriptor,
1359 &info);
1360 AssertThrow(info == 0,
1361 LAPACKSupport::ExcErrorCode("ppotri", info));
1363 }
1364 else
1365 {
1366 int lwork = -1, liwork = -1;
1367 work.resize(1);
1368 iwork.resize(1);
1369
1370 pgetri(&n_columns,
1371 A_loc,
1372 &submatrix_row,
1373 &submatrix_column,
1374 descriptor,
1375 ipiv.data(),
1376 work.data(),
1377 &lwork,
1378 iwork.data(),
1379 &liwork,
1380 &info);
1381
1382 AssertThrow(info == 0,
1383 LAPACKSupport::ExcErrorCode("pgetri", info));
1384 lwork = static_cast<int>(work[0]);
1385 liwork = iwork[0];
1386 work.resize(lwork);
1387 iwork.resize(liwork);
1388
1389 pgetri(&n_columns,
1390 A_loc,
1391 &submatrix_row,
1392 &submatrix_column,
1393 descriptor,
1394 ipiv.data(),
1395 work.data(),
1396 &lwork,
1397 iwork.data(),
1398 &liwork,
1399 &info);
1400
1401 AssertThrow(info == 0,
1402 LAPACKSupport::ExcErrorCode("pgetri", info));
1403 }
1404 }
1405 }
1407}
1408
1409
1410
1411template <typename NumberType>
1412std::vector<NumberType>
1414 const std::pair<unsigned int, unsigned int> &index_limits,
1415 const bool compute_eigenvectors)
1416{
1417 // check validity of index limits
1418 AssertIndexRange(index_limits.first, n_rows);
1419 AssertIndexRange(index_limits.second, n_rows);
1420
1421 std::pair<unsigned int, unsigned int> idx =
1422 std::make_pair(std::min(index_limits.first, index_limits.second),
1423 std::max(index_limits.first, index_limits.second));
1424
1425 // compute all eigenvalues/eigenvectors
1426 if (idx.first == 0 && idx.second == static_cast<unsigned int>(n_rows - 1))
1427 return eigenpairs_symmetric(compute_eigenvectors);
1428 else
1429 return eigenpairs_symmetric(compute_eigenvectors, idx);
1430}
1431
1432
1433
1434template <typename NumberType>
1435std::vector<NumberType>
1437 const std::pair<NumberType, NumberType> &value_limits,
1438 const bool compute_eigenvectors)
1439{
1440 Assert(!std::isnan(value_limits.first),
1441 ExcMessage("value_limits.first is NaN"));
1442 Assert(!std::isnan(value_limits.second),
1443 ExcMessage("value_limits.second is NaN"));
1444
1445 std::pair<unsigned int, unsigned int> indices =
1446 std::make_pair(numbers::invalid_unsigned_int,
1448
1449 return eigenpairs_symmetric(compute_eigenvectors, indices, value_limits);
1450}
1451
1452
1453
1454template <typename NumberType>
1455std::vector<NumberType>
1457 const bool compute_eigenvectors,
1458 const std::pair<unsigned int, unsigned int> &eigenvalue_idx,
1459 const std::pair<NumberType, NumberType> &eigenvalue_limits)
1460{
1462 ExcMessage(
1463 "Matrix has to be in Matrix state before calling this function."));
1464 Assert(property == LAPACKSupport::symmetric,
1465 ExcMessage("Matrix has to be symmetric for this operation."));
1466
1467 std::scoped_lock lock(mutex);
1468
1469 const bool use_values = (std::isnan(eigenvalue_limits.first) ||
1470 std::isnan(eigenvalue_limits.second)) ?
1471 false :
1472 true;
1473 const bool use_indices =
1474 ((eigenvalue_idx.first == numbers::invalid_unsigned_int) ||
1475 (eigenvalue_idx.second == numbers::invalid_unsigned_int)) ?
1476 false :
1477 true;
1478
1479 Assert(
1480 !(use_values && use_indices),
1481 ExcMessage(
1482 "Prescribing both the index and value range for the eigenvalues is ambiguous"));
1483
1484 // if computation of eigenvectors is not required use a sufficiently small
1485 // distributed matrix
1486 std::unique_ptr<ScaLAPACKMatrix<NumberType>> eigenvectors =
1487 compute_eigenvectors ?
1488 std::make_unique<ScaLAPACKMatrix<NumberType>>(n_rows,
1489 grid,
1490 row_block_size) :
1491 std::make_unique<ScaLAPACKMatrix<NumberType>>(
1492 grid->n_process_rows, grid->n_process_columns, grid, 1, 1);
1493
1494 eigenvectors->property = property;
1495 // number of eigenvalues to be returned from psyevx; upon successful exit ev
1496 // contains the m selected eigenvalues in ascending order set to all
1497 // eigenvaleus in case we will be using psyev.
1498 int m = n_rows;
1499 std::vector<NumberType> ev(n_rows);
1500
1501 if (grid->mpi_process_is_active)
1502 {
1503 int info = 0;
1504 /*
1505 * for jobz==N only eigenvalues are computed, for jobz='V' also the
1506 * eigenvectors of the matrix are computed
1507 */
1508 char jobz = compute_eigenvectors ? 'V' : 'N';
1509 char range = 'A';
1510 // default value is to compute all eigenvalues and optionally eigenvectors
1511 bool all_eigenpairs = true;
1512 NumberType vl = NumberType(), vu = NumberType();
1513 int il = 1, iu = 1;
1514 // number of eigenvectors to be returned;
1515 // upon successful exit the first m=nz columns contain the selected
1516 // eigenvectors (only if jobz=='V')
1517 int nz = 0;
1518 NumberType abstol = NumberType();
1519
1520 // orfac decides which eigenvectors should be reorthogonalized
1521 // see
1522 // http://www.netlib.org/scalapack/explore-html/df/d1a/pdsyevx_8f_source.html
1523 // for explanation to keeps simple no reorthogonalized will be done by
1524 // setting orfac to 0
1525 NumberType orfac = 0;
1526 // contains the indices of eigenvectors that failed to converge
1527 std::vector<int> ifail;
1528 // This array contains indices of eigenvectors corresponding to
1529 // a cluster of eigenvalues that could not be reorthogonalized
1530 // due to insufficient workspace
1531 // see
1532 // http://www.netlib.org/scalapack/explore-html/df/d1a/pdsyevx_8f_source.html
1533 // for explanation
1534 std::vector<int> iclustr;
1535 // This array contains the gap between eigenvalues whose
1536 // eigenvectors could not be reorthogonalized.
1537 // see
1538 // http://www.netlib.org/scalapack/explore-html/df/d1a/pdsyevx_8f_source.html
1539 // for explanation
1540 std::vector<NumberType> gap(n_local_rows * n_local_columns);
1541
1542 // index range for eigenvalues is not specified
1543 if (!use_indices)
1544 {
1545 // interval for eigenvalues is not specified and consequently all
1546 // eigenvalues/eigenpairs will be computed
1547 if (!use_values)
1548 {
1549 range = 'A';
1550 all_eigenpairs = true;
1551 }
1552 else
1553 {
1554 range = 'V';
1555 all_eigenpairs = false;
1556 vl = std::min(eigenvalue_limits.first, eigenvalue_limits.second);
1557 vu = std::max(eigenvalue_limits.first, eigenvalue_limits.second);
1558 }
1559 }
1560 else
1561 {
1562 range = 'I';
1563 all_eigenpairs = false;
1564 // as Fortran starts counting/indexing from 1 unlike C/C++, where it
1565 // starts from 0
1566 il = std::min(eigenvalue_idx.first, eigenvalue_idx.second) + 1;
1567 iu = std::max(eigenvalue_idx.first, eigenvalue_idx.second) + 1;
1568 }
1569 NumberType *A_loc = this->values.data();
1570 /*
1571 * by setting lwork to -1 a workspace query for optimal length of work is
1572 * performed
1573 */
1574 int lwork = -1;
1575 int liwork = -1;
1576 NumberType *eigenvectors_loc =
1577 (compute_eigenvectors ? eigenvectors->values.data() : nullptr);
1578 /*
1579 * According to the official "documentation" found on the internet
1580 * (aka source file ppsyevx.f [1]) the work array has to have a
1581 * minimal size of max(3, lwork). Because we query for optimal size
1582 * (lwork == -1) we have to guarantee at least three doubles. The
1583 * necessary size of iwork is not specified, so let's use three as
1584 * well.
1585 * [1]
1586 * https://netlib.org/scalapack/explore-html/df/d1a/pdsyevx_8f_source.html
1587 */
1588 work.resize(3);
1589 iwork.resize(3);
1590
1591 if (all_eigenpairs)
1592 {
1593 psyev(&jobz,
1594 &uplo,
1595 &n_rows,
1596 A_loc,
1597 &submatrix_row,
1598 &submatrix_column,
1599 descriptor,
1600 ev.data(),
1601 eigenvectors_loc,
1602 &eigenvectors->submatrix_row,
1603 &eigenvectors->submatrix_column,
1604 eigenvectors->descriptor,
1605 work.data(),
1606 &lwork,
1607 &info);
1608 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("psyev", info));
1609 }
1610 else
1611 {
1612 char cmach = compute_eigenvectors ? 'U' : 'S';
1613 plamch(&(this->grid->blacs_context), &cmach, abstol);
1614 abstol *= 2;
1615 ifail.resize(n_rows);
1616 iclustr.resize(2 * grid->n_process_rows * grid->n_process_columns);
1617 gap.resize(grid->n_process_rows * grid->n_process_columns);
1618
1619 psyevx(&jobz,
1620 &range,
1621 &uplo,
1622 &n_rows,
1623 A_loc,
1624 &submatrix_row,
1625 &submatrix_column,
1626 descriptor,
1627 &vl,
1628 &vu,
1629 &il,
1630 &iu,
1631 &abstol,
1632 &m,
1633 &nz,
1634 ev.data(),
1635 &orfac,
1636 eigenvectors_loc,
1637 &eigenvectors->submatrix_row,
1638 &eigenvectors->submatrix_column,
1639 eigenvectors->descriptor,
1640 work.data(),
1641 &lwork,
1642 iwork.data(),
1643 &liwork,
1644 ifail.data(),
1645 iclustr.data(),
1646 gap.data(),
1647 &info);
1648 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("psyevx", info));
1649 }
1650 lwork = static_cast<int>(work[0]);
1651 work.resize(lwork);
1652
1653 if (all_eigenpairs)
1654 {
1655 psyev(&jobz,
1656 &uplo,
1657 &n_rows,
1658 A_loc,
1659 &submatrix_row,
1660 &submatrix_column,
1661 descriptor,
1662 ev.data(),
1663 eigenvectors_loc,
1664 &eigenvectors->submatrix_row,
1665 &eigenvectors->submatrix_column,
1666 eigenvectors->descriptor,
1667 work.data(),
1668 &lwork,
1669 &info);
1670
1671 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("psyev", info));
1672 }
1673 else
1674 {
1675 liwork = iwork[0];
1676 AssertThrow(liwork > 0, ExcInternalError());
1677 iwork.resize(liwork);
1678
1679 psyevx(&jobz,
1680 &range,
1681 &uplo,
1682 &n_rows,
1683 A_loc,
1684 &submatrix_row,
1685 &submatrix_column,
1686 descriptor,
1687 &vl,
1688 &vu,
1689 &il,
1690 &iu,
1691 &abstol,
1692 &m,
1693 &nz,
1694 ev.data(),
1695 &orfac,
1696 eigenvectors_loc,
1697 &eigenvectors->submatrix_row,
1698 &eigenvectors->submatrix_column,
1699 eigenvectors->descriptor,
1700 work.data(),
1701 &lwork,
1702 iwork.data(),
1703 &liwork,
1704 ifail.data(),
1705 iclustr.data(),
1706 gap.data(),
1707 &info);
1708
1709 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("psyevx", info));
1710 }
1711 // if eigenvectors are queried copy eigenvectors to original matrix
1712 // as the temporary matrix eigenvectors has identical dimensions and
1713 // block-cyclic distribution we simply swap the local array
1714 if (compute_eigenvectors)
1715 this->values.swap(eigenvectors->values);
1716
1717 // adapt the size of ev to fit m upon return
1718 while (ev.size() > static_cast<size_type>(m))
1719 ev.pop_back();
1720 }
1721 /*
1722 * send number of computed eigenvalues to inactive processes
1723 */
1724 grid->send_to_inactive(&m, 1);
1725
1726 /*
1727 * inactive processes have to resize array of eigenvalues
1728 */
1729 if (!grid->mpi_process_is_active)
1730 ev.resize(m);
1731 /*
1732 * send the eigenvalues to processors not being part of the process grid
1733 */
1734 grid->send_to_inactive(ev.data(), ev.size());
1735
1736 /*
1737 * if only eigenvalues are queried the content of the matrix will be destroyed
1738 * if the eigenpairs are queried matrix A on exit stores the eigenvectors in
1739 * the columns
1740 */
1741 if (compute_eigenvectors)
1742 {
1745 }
1746 else
1748
1749 return ev;
1750}
1751
1752
1753
1754template <typename NumberType>
1755std::vector<NumberType>
1757 const std::pair<unsigned int, unsigned int> &index_limits,
1758 const bool compute_eigenvectors)
1759{
1760 // Check validity of index limits.
1761 AssertIndexRange(index_limits.first, static_cast<unsigned int>(n_rows));
1762 AssertIndexRange(index_limits.second, static_cast<unsigned int>(n_rows));
1763
1764 const std::pair<unsigned int, unsigned int> idx =
1765 std::make_pair(std::min(index_limits.first, index_limits.second),
1766 std::max(index_limits.first, index_limits.second));
1767
1768 // Compute all eigenvalues/eigenvectors.
1769 if (idx.first == 0 && idx.second == static_cast<unsigned int>(n_rows - 1))
1770 return eigenpairs_symmetric_MRRR(compute_eigenvectors);
1771 else
1772 return eigenpairs_symmetric_MRRR(compute_eigenvectors, idx);
1773}
1774
1775
1776
1777template <typename NumberType>
1778std::vector<NumberType>
1780 const std::pair<NumberType, NumberType> &value_limits,
1781 const bool compute_eigenvectors)
1782{
1783 AssertIsFinite(value_limits.first);
1784 AssertIsFinite(value_limits.second);
1785
1786 const std::pair<unsigned int, unsigned int> indices =
1787 std::make_pair(numbers::invalid_unsigned_int,
1789
1790 return eigenpairs_symmetric_MRRR(compute_eigenvectors, indices, value_limits);
1791}
1792
1793
1794
1795template <typename NumberType>
1796std::vector<NumberType>
1798 const bool compute_eigenvectors,
1799 const std::pair<unsigned int, unsigned int> &eigenvalue_idx,
1800 const std::pair<NumberType, NumberType> &eigenvalue_limits)
1801{
1803 ExcMessage(
1804 "Matrix has to be in Matrix state before calling this function."));
1805 Assert(property == LAPACKSupport::symmetric,
1806 ExcMessage("Matrix has to be symmetric for this operation."));
1807
1808 std::scoped_lock lock(mutex);
1809
1810 const bool use_values = (std::isnan(eigenvalue_limits.first) ||
1811 std::isnan(eigenvalue_limits.second)) ?
1812 false :
1813 true;
1814 const bool use_indices =
1815 ((eigenvalue_idx.first == numbers::invalid_unsigned_int) ||
1816 (eigenvalue_idx.second == numbers::invalid_unsigned_int)) ?
1817 false :
1818 true;
1819
1820 Assert(
1821 !(use_values && use_indices),
1822 ExcMessage(
1823 "Prescribing both the index and value range for the eigenvalues is ambiguous"));
1824
1825 // If computation of eigenvectors is not required, use a sufficiently small
1826 // distributed matrix.
1827 std::unique_ptr<ScaLAPACKMatrix<NumberType>> eigenvectors =
1828 compute_eigenvectors ?
1829 std::make_unique<ScaLAPACKMatrix<NumberType>>(n_rows,
1830 grid,
1831 row_block_size) :
1832 std::make_unique<ScaLAPACKMatrix<NumberType>>(
1833 grid->n_process_rows, grid->n_process_columns, grid, 1, 1);
1834
1835 eigenvectors->property = property;
1836 // Number of eigenvalues to be returned from psyevr; upon successful exit ev
1837 // contains the m selected eigenvalues in ascending order.
1838 int m = n_rows;
1839 std::vector<NumberType> ev(n_rows);
1840
1841 // Number of eigenvectors to be returned;
1842 // Upon successful exit the first m=nz columns contain the selected
1843 // eigenvectors (only if jobz=='V').
1844 int nz = 0;
1845
1846 if (grid->mpi_process_is_active)
1847 {
1848 int info = 0;
1849 /*
1850 * For jobz==N only eigenvalues are computed, for jobz='V' also the
1851 * eigenvectors of the matrix are computed.
1852 */
1853 char jobz = compute_eigenvectors ? 'V' : 'N';
1854 // Default value is to compute all eigenvalues and optionally
1855 // eigenvectors.
1856 char range = 'A';
1857 NumberType vl = NumberType(), vu = NumberType();
1858 int il = 1, iu = 1;
1859
1860 // Index range for eigenvalues is not specified.
1861 if (!use_indices)
1862 {
1863 // Interval for eigenvalues is not specified and consequently all
1864 // eigenvalues/eigenpairs will be computed.
1865 if (!use_values)
1866 {
1867 range = 'A';
1868 }
1869 else
1870 {
1871 range = 'V';
1872 vl = std::min(eigenvalue_limits.first, eigenvalue_limits.second);
1873 vu = std::max(eigenvalue_limits.first, eigenvalue_limits.second);
1874 }
1875 }
1876 else
1877 {
1878 range = 'I';
1879 // As Fortran starts counting/indexing from 1 unlike C/C++, where it
1880 // starts from 0.
1881 il = std::min(eigenvalue_idx.first, eigenvalue_idx.second) + 1;
1882 iu = std::max(eigenvalue_idx.first, eigenvalue_idx.second) + 1;
1883 }
1884 NumberType *A_loc = this->values.data();
1885
1886 /*
1887 * By setting lwork to -1 a workspace query for optimal length of work is
1888 * performed.
1889 */
1890 int lwork = -1;
1891 int liwork = -1;
1892 NumberType *eigenvectors_loc =
1893 (compute_eigenvectors ? eigenvectors->values.data() : nullptr);
1894 work.resize(1);
1895 iwork.resize(1);
1896
1897 psyevr(&jobz,
1898 &range,
1899 &uplo,
1900 &n_rows,
1901 A_loc,
1902 &submatrix_row,
1903 &submatrix_column,
1904 descriptor,
1905 &vl,
1906 &vu,
1907 &il,
1908 &iu,
1909 &m,
1910 &nz,
1911 ev.data(),
1912 eigenvectors_loc,
1913 &eigenvectors->submatrix_row,
1914 &eigenvectors->submatrix_column,
1915 eigenvectors->descriptor,
1916 work.data(),
1917 &lwork,
1918 iwork.data(),
1919 &liwork,
1920 &info);
1921
1922 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("psyevr", info));
1923
1924 lwork = static_cast<int>(work[0]);
1925 work.resize(lwork);
1926 liwork = iwork[0];
1927 iwork.resize(liwork);
1928
1929 psyevr(&jobz,
1930 &range,
1931 &uplo,
1932 &n_rows,
1933 A_loc,
1934 &submatrix_row,
1935 &submatrix_column,
1936 descriptor,
1937 &vl,
1938 &vu,
1939 &il,
1940 &iu,
1941 &m,
1942 &nz,
1943 ev.data(),
1944 eigenvectors_loc,
1945 &eigenvectors->submatrix_row,
1946 &eigenvectors->submatrix_column,
1947 eigenvectors->descriptor,
1948 work.data(),
1949 &lwork,
1950 iwork.data(),
1951 &liwork,
1952 &info);
1953
1954 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("psyevr", info));
1955
1956 if (compute_eigenvectors)
1958 m == nz,
1959 ExcMessage(
1960 "psyevr failed to compute all eigenvectors for the selected eigenvalues"));
1961
1962 // If eigenvectors are queried, copy eigenvectors to original matrix.
1963 // As the temporary matrix eigenvectors has identical dimensions and
1964 // block-cyclic distribution we simply swap the local array.
1965 if (compute_eigenvectors)
1966 this->values.swap(eigenvectors->values);
1967
1968 // Adapt the size of ev to fit m upon return.
1969 while (ev.size() > static_cast<size_type>(m))
1970 ev.pop_back();
1971 }
1972 /*
1973 * Send number of computed eigenvalues to inactive processes.
1974 */
1975 grid->send_to_inactive(&m, 1);
1976
1977 /*
1978 * Inactive processes have to resize array of eigenvalues.
1979 */
1980 if (!grid->mpi_process_is_active)
1981 ev.resize(m);
1982 /*
1983 * Send the eigenvalues to processors not being part of the process grid.
1984 */
1985 grid->send_to_inactive(ev.data(), ev.size());
1986
1987 /*
1988 * If only eigenvalues are queried, the content of the matrix will be
1989 * destroyed. If the eigenpairs are queried, matrix A on exit stores the
1990 * eigenvectors in the columns.
1991 */
1992 if (compute_eigenvectors)
1993 {
1996 }
1997 else
1999
2000 return ev;
2001}
2002
2003
2004
2005template <typename NumberType>
2006std::vector<NumberType>
2009{
2011 ExcMessage(
2012 "Matrix has to be in Matrix state before calling this function."));
2013 Assert(row_block_size == column_block_size,
2014 ExcDimensionMismatch(row_block_size, column_block_size));
2015
2016 const bool left_singular_vectors = (U != nullptr) ? true : false;
2017 const bool right_singular_vectors = (VT != nullptr) ? true : false;
2018
2019 if (left_singular_vectors)
2020 {
2021 Assert(n_rows == U->n_rows, ExcDimensionMismatch(n_rows, U->n_rows));
2022 Assert(U->n_rows == U->n_columns,
2023 ExcDimensionMismatch(U->n_rows, U->n_columns));
2024 Assert(row_block_size == U->row_block_size,
2025 ExcDimensionMismatch(row_block_size, U->row_block_size));
2026 Assert(column_block_size == U->column_block_size,
2027 ExcDimensionMismatch(column_block_size, U->column_block_size));
2028 Assert(grid->blacs_context == U->grid->blacs_context,
2029 ExcDimensionMismatch(grid->blacs_context, U->grid->blacs_context));
2030 }
2031 if (right_singular_vectors)
2032 {
2033 Assert(n_columns == VT->n_rows,
2034 ExcDimensionMismatch(n_columns, VT->n_rows));
2035 Assert(VT->n_rows == VT->n_columns,
2037 Assert(row_block_size == VT->row_block_size,
2038 ExcDimensionMismatch(row_block_size, VT->row_block_size));
2039 Assert(column_block_size == VT->column_block_size,
2040 ExcDimensionMismatch(column_block_size, VT->column_block_size));
2041 Assert(grid->blacs_context == VT->grid->blacs_context,
2042 ExcDimensionMismatch(grid->blacs_context,
2043 VT->grid->blacs_context));
2044 }
2045 std::scoped_lock lock(mutex);
2046
2047 std::vector<NumberType> sv(std::min(n_rows, n_columns));
2048
2049 if (grid->mpi_process_is_active)
2050 {
2051 char jobu = left_singular_vectors ? 'V' : 'N';
2052 char jobvt = right_singular_vectors ? 'V' : 'N';
2053 NumberType *A_loc = this->values.data();
2054 NumberType *U_loc = left_singular_vectors ? U->values.data() : nullptr;
2055 NumberType *VT_loc = right_singular_vectors ? VT->values.data() : nullptr;
2056 int info = 0;
2057 /*
2058 * by setting lwork to -1 a workspace query for optimal length of work is
2059 * performed
2060 */
2061 int lwork = -1;
2062 work.resize(1);
2063
2064 pgesvd(&jobu,
2065 &jobvt,
2066 &n_rows,
2067 &n_columns,
2068 A_loc,
2069 &submatrix_row,
2070 &submatrix_column,
2071 descriptor,
2072 &*sv.begin(),
2073 U_loc,
2074 &U->submatrix_row,
2075 &U->submatrix_column,
2076 U->descriptor,
2077 VT_loc,
2078 &VT->submatrix_row,
2079 &VT->submatrix_column,
2080 VT->descriptor,
2081 work.data(),
2082 &lwork,
2083 &info);
2084 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pgesvd", info));
2085
2086 lwork = static_cast<int>(work[0]);
2087 work.resize(lwork);
2088
2089 pgesvd(&jobu,
2090 &jobvt,
2091 &n_rows,
2092 &n_columns,
2093 A_loc,
2094 &submatrix_row,
2095 &submatrix_column,
2096 descriptor,
2097 &*sv.begin(),
2098 U_loc,
2099 &U->submatrix_row,
2100 &U->submatrix_column,
2101 U->descriptor,
2102 VT_loc,
2103 &VT->submatrix_row,
2104 &VT->submatrix_column,
2105 VT->descriptor,
2106 work.data(),
2107 &lwork,
2108 &info);
2109 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pgesvd", info));
2110 }
2111
2112 /*
2113 * send the singular values to processors not being part of the process grid
2114 */
2115 grid->send_to_inactive(sv.data(), sv.size());
2116
2119
2120 return sv;
2121}
2122
2123
2124
2125template <typename NumberType>
2126void
2128 const bool transpose)
2129{
2130 Assert(grid == B.grid,
2131 ExcMessage("The matrices A and B need to have the same process grid"));
2133 ExcMessage(
2134 "Matrix has to be in Matrix state before calling this function."));
2136 ExcMessage(
2137 "Matrix B has to be in Matrix state before calling this function."));
2138
2139 if (transpose)
2140 {
2141 Assert(n_columns == B.n_rows, ExcDimensionMismatch(n_columns, B.n_rows));
2142 }
2143 else
2144 {
2145 Assert(n_rows == B.n_rows, ExcDimensionMismatch(n_rows, B.n_rows));
2146 }
2147
2148 // see
2149 // https://www.ibm.com/support/knowledgecenter/en/SSNR5K_4.2.0/com.ibm.cluster.pessl.v4r2.pssl100.doc/am6gr_lgels.htm
2150 Assert(row_block_size == column_block_size,
2151 ExcMessage(
2152 "Use identical block sizes for rows and columns of matrix A"));
2154 ExcMessage(
2155 "Use identical block sizes for rows and columns of matrix B"));
2156 Assert(row_block_size == B.row_block_size,
2157 ExcMessage(
2158 "Use identical block-cyclic distribution for matrices A and B"));
2159
2160 std::scoped_lock lock(mutex);
2161
2162 if (grid->mpi_process_is_active)
2163 {
2164 char trans = transpose ? 'T' : 'N';
2165 NumberType *A_loc = this->values.data();
2166 NumberType *B_loc = B.values.data();
2167 int info = 0;
2168 /*
2169 * by setting lwork to -1 a workspace query for optimal length of work is
2170 * performed
2171 */
2172 int lwork = -1;
2173 work.resize(1);
2174
2175 pgels(&trans,
2176 &n_rows,
2177 &n_columns,
2178 &B.n_columns,
2179 A_loc,
2180 &submatrix_row,
2181 &submatrix_column,
2182 descriptor,
2183 B_loc,
2184 &B.submatrix_row,
2186 B.descriptor,
2187 work.data(),
2188 &lwork,
2189 &info);
2190 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pgels", info));
2191
2192 lwork = static_cast<int>(work[0]);
2193 work.resize(lwork);
2194
2195 pgels(&trans,
2196 &n_rows,
2197 &n_columns,
2198 &B.n_columns,
2199 A_loc,
2200 &submatrix_row,
2201 &submatrix_column,
2202 descriptor,
2203 B_loc,
2204 &B.submatrix_row,
2206 B.descriptor,
2207 work.data(),
2208 &lwork,
2209 &info);
2210 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pgels", info));
2211 }
2213}
2214
2215
2216
2217template <typename NumberType>
2218unsigned int
2220{
2222 ExcMessage(
2223 "Matrix has to be in Matrix state before calling this function."));
2224 Assert(row_block_size == column_block_size,
2225 ExcMessage(
2226 "Use identical block sizes for rows and columns of matrix A"));
2227 Assert(
2228 ratio > 0. && ratio < 1.,
2229 ExcMessage(
2230 "input parameter ratio has to be larger than zero and smaller than 1"));
2231
2233 n_rows,
2234 grid,
2235 row_block_size,
2236 row_block_size,
2238 ScaLAPACKMatrix<NumberType> VT(n_columns,
2239 n_columns,
2240 grid,
2241 row_block_size,
2242 row_block_size,
2244 std::vector<NumberType> sv = this->compute_SVD(&U, &VT);
2245 AssertThrow(sv[0] > std::numeric_limits<NumberType>::min(),
2246 ExcMessage("Matrix has rank 0"));
2247
2248 // Get number of singular values fulfilling the following: sv[i] > sv[0] *
2249 // ratio Obviously, 0-th element already satisfies sv[0] > sv[0] * ratio The
2250 // singular values in sv are ordered by descending value so we break out of
2251 // the loop if a singular value is smaller than sv[0] * ratio.
2252 unsigned int n_sv = 1;
2253 std::vector<NumberType> inv_sigma;
2254 inv_sigma.push_back(1 / sv[0]);
2255
2256 for (unsigned int i = 1; i < sv.size(); ++i)
2257 if (sv[i] > sv[0] * ratio)
2258 {
2259 ++n_sv;
2260 inv_sigma.push_back(1 / sv[i]);
2261 }
2262 else
2263 break;
2264
2265 // For the matrix multiplication we use only the columns of U and rows of VT
2266 // which are associated with singular values larger than the limit. That saves
2267 // computational time for matrices with rank significantly smaller than
2268 // min(n_rows,n_columns)
2269 ScaLAPACKMatrix<NumberType> U_R(n_rows,
2270 n_sv,
2271 grid,
2272 row_block_size,
2273 row_block_size,
2276 n_columns,
2277 grid,
2278 row_block_size,
2279 row_block_size,
2281 U.copy_to(U_R,
2282 std::make_pair(0, 0),
2283 std::make_pair(0, 0),
2284 std::make_pair(n_rows, n_sv));
2285 VT.copy_to(VT_R,
2286 std::make_pair(0, 0),
2287 std::make_pair(0, 0),
2288 std::make_pair(n_sv, n_columns));
2289
2290 VT_R.scale_rows(inv_sigma);
2291 this->reinit(n_columns,
2292 n_rows,
2293 this->grid,
2294 row_block_size,
2295 column_block_size,
2297 VT_R.mult(1, U_R, 0, *this, true, true);
2299 return n_sv;
2300}
2301
2302
2303
2304template <typename NumberType>
2305NumberType
2307 const NumberType a_norm) const
2308{
2310 ExcMessage(
2311 "Matrix has to be in Cholesky state before calling this function."));
2312 std::scoped_lock lock(mutex);
2313 NumberType rcond = 0.;
2314
2315 if (grid->mpi_process_is_active)
2316 {
2317 int liwork = n_local_rows;
2318 iwork.resize(liwork);
2319
2320 int info = 0;
2321 const NumberType *A_loc = this->values.data();
2322
2323 // by setting lwork to -1 a workspace query for optimal length of work is
2324 // performed
2325 int lwork = -1;
2326 work.resize(1);
2327 ppocon(&uplo,
2328 &n_columns,
2329 A_loc,
2330 &submatrix_row,
2331 &submatrix_column,
2332 descriptor,
2333 &a_norm,
2334 &rcond,
2335 work.data(),
2336 &lwork,
2337 iwork.data(),
2338 &liwork,
2339 &info);
2340 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pdpocon", info));
2341 lwork = static_cast<int>(std::ceil(work[0]));
2342 work.resize(lwork);
2343
2344 // now the actual run:
2345 ppocon(&uplo,
2346 &n_columns,
2347 A_loc,
2348 &submatrix_row,
2349 &submatrix_column,
2350 descriptor,
2351 &a_norm,
2352 &rcond,
2353 work.data(),
2354 &lwork,
2355 iwork.data(),
2356 &liwork,
2357 &info);
2358 AssertThrow(info == 0, LAPACKSupport::ExcErrorCode("pdpocon", info));
2359 }
2360 grid->send_to_inactive(&rcond);
2361 return rcond;
2362}
2363
2364
2365
2366template <typename NumberType>
2367NumberType
2369{
2370 const char type('O');
2371
2372 if (property == LAPACKSupport::symmetric)
2373 return norm_symmetric(type);
2374 else
2375 return norm_general(type);
2376}
2377
2378
2379
2380template <typename NumberType>
2381NumberType
2383{
2384 const char type('I');
2385
2386 if (property == LAPACKSupport::symmetric)
2387 return norm_symmetric(type);
2388 else
2389 return norm_general(type);
2390}
2391
2392
2393
2394template <typename NumberType>
2395NumberType
2397{
2398 const char type('F');
2399
2400 if (property == LAPACKSupport::symmetric)
2401 return norm_symmetric(type);
2402 else
2403 return norm_general(type);
2404}
2405
2406
2407
2408template <typename NumberType>
2409NumberType
2411{
2412 Assert(state == LAPACKSupport::matrix ||
2414 ExcMessage("norms can be called in matrix state only."));
2415 std::scoped_lock lock(mutex);
2416 NumberType res = 0.;
2417
2418 if (grid->mpi_process_is_active)
2419 {
2420 const int iarow = indxg2p_(&submatrix_row,
2421 &row_block_size,
2422 &(grid->this_process_row),
2423 &first_process_row,
2424 &(grid->n_process_rows));
2425 const int iacol = indxg2p_(&submatrix_column,
2426 &column_block_size,
2427 &(grid->this_process_column),
2428 &first_process_column,
2429 &(grid->n_process_columns));
2430 const int mp0 = numroc_(&n_rows,
2431 &row_block_size,
2432 &(grid->this_process_row),
2433 &iarow,
2434 &(grid->n_process_rows));
2435 const int nq0 = numroc_(&n_columns,
2436 &column_block_size,
2437 &(grid->this_process_column),
2438 &iacol,
2439 &(grid->n_process_columns));
2440
2441 // type='M': compute largest absolute value
2442 // type='F' || type='E': compute Frobenius norm
2443 // type='0' || type='1': compute infinity norm
2444 int lwork = 0; // for type == 'M' || type == 'F' || type == 'E'
2445 if (type == 'O' || type == '1')
2446 lwork = nq0;
2447 else if (type == 'I')
2448 lwork = mp0;
2449
2450 work.resize(lwork);
2451 const NumberType *A_loc = this->values.begin();
2452 res = plange(&type,
2453 &n_rows,
2454 &n_columns,
2455 A_loc,
2456 &submatrix_row,
2457 &submatrix_column,
2458 descriptor,
2459 work.data());
2460 }
2461 grid->send_to_inactive(&res);
2462 return res;
2463}
2464
2465
2466
2467template <typename NumberType>
2468NumberType
2470{
2471 Assert(state == LAPACKSupport::matrix ||
2473 ExcMessage("norms can be called in matrix state only."));
2474 Assert(property == LAPACKSupport::symmetric,
2475 ExcMessage("Matrix has to be symmetric for this operation."));
2476 std::scoped_lock lock(mutex);
2477 NumberType res = 0.;
2478
2479 if (grid->mpi_process_is_active)
2480 {
2481 // int IROFFA = MOD( IA-1, MB_A )
2482 // int ICOFFA = MOD( JA-1, NB_A )
2483 const int lcm =
2484 ilcm_(&(grid->n_process_rows), &(grid->n_process_columns));
2485 const int v2 = lcm / (grid->n_process_rows);
2486
2487 const int IAROW = indxg2p_(&submatrix_row,
2488 &row_block_size,
2489 &(grid->this_process_row),
2490 &first_process_row,
2491 &(grid->n_process_rows));
2492 const int IACOL = indxg2p_(&submatrix_column,
2493 &column_block_size,
2494 &(grid->this_process_column),
2495 &first_process_column,
2496 &(grid->n_process_columns));
2497 const int Np0 = numroc_(&n_columns /*+IROFFA*/,
2498 &row_block_size,
2499 &(grid->this_process_row),
2500 &IAROW,
2501 &(grid->n_process_rows));
2502 const int Nq0 = numroc_(&n_columns /*+ICOFFA*/,
2503 &column_block_size,
2504 &(grid->this_process_column),
2505 &IACOL,
2506 &(grid->n_process_columns));
2507
2508 const int v1 = iceil_(&Np0, &row_block_size);
2509 const int ldw = (n_local_rows == n_local_columns) ?
2510 0 :
2511 row_block_size * iceil_(&v1, &v2);
2512
2513 const int lwork =
2514 (type == 'M' || type == 'F' || type == 'E') ? 0 : 2 * Nq0 + Np0 + ldw;
2515 work.resize(lwork);
2516 const NumberType *A_loc = this->values.begin();
2517 res = plansy(&type,
2518 &uplo,
2519 &n_columns,
2520 A_loc,
2521 &submatrix_row,
2522 &submatrix_column,
2523 descriptor,
2524 work.data());
2525 }
2526 grid->send_to_inactive(&res);
2527 return res;
2528}
2529
2530
2531
2532#ifdef DEAL_II_WITH_HDF5
2533namespace internal
2534{
2535 namespace
2536 {
2537 void
2538 create_HDF5_state_enum_id(hid_t &state_enum_id)
2539 {
2540 // create HDF5 enum type for LAPACKSupport::State
2542 state_enum_id = H5Tcreate(H5T_ENUM, sizeof(LAPACKSupport::State));
2544 herr_t status = H5Tenum_insert(state_enum_id, "cholesky", &val);
2545 AssertThrow(status >= 0, ExcInternalError());
2547 status = H5Tenum_insert(state_enum_id, "eigenvalues", &val);
2548 AssertThrow(status >= 0, ExcInternalError());
2550 status = H5Tenum_insert(state_enum_id, "inverse_matrix", &val);
2551 AssertThrow(status >= 0, ExcInternalError());
2553 status = H5Tenum_insert(state_enum_id, "inverse_svd", &val);
2554 AssertThrow(status >= 0, ExcInternalError());
2556 status = H5Tenum_insert(state_enum_id, "lu", &val);
2557 AssertThrow(status >= 0, ExcInternalError());
2559 status = H5Tenum_insert(state_enum_id, "matrix", &val);
2560 AssertThrow(status >= 0, ExcInternalError());
2562 status = H5Tenum_insert(state_enum_id, "svd", &val);
2563 AssertThrow(status >= 0, ExcInternalError());
2565 status = H5Tenum_insert(state_enum_id, "unusable", &val);
2566 AssertThrow(status >= 0, ExcInternalError());
2567 }
2568
2569 void
2570 create_HDF5_property_enum_id(hid_t &property_enum_id)
2571 {
2572 // create HDF5 enum type for LAPACKSupport::Property
2573 property_enum_id = H5Tcreate(H5T_ENUM, sizeof(LAPACKSupport::Property));
2575 herr_t status = H5Tenum_insert(property_enum_id, "diagonal", &prop);
2576 AssertThrow(status >= 0, ExcInternalError());
2578 status = H5Tenum_insert(property_enum_id, "general", &prop);
2579 AssertThrow(status >= 0, ExcInternalError());
2581 status = H5Tenum_insert(property_enum_id, "hessenberg", &prop);
2582 AssertThrow(status >= 0, ExcInternalError());
2584 status = H5Tenum_insert(property_enum_id, "lower_triangular", &prop);
2585 AssertThrow(status >= 0, ExcInternalError());
2587 status = H5Tenum_insert(property_enum_id, "symmetric", &prop);
2588 AssertThrow(status >= 0, ExcInternalError());
2590 status = H5Tenum_insert(property_enum_id, "upper_triangular", &prop);
2591 AssertThrow(status >= 0, ExcInternalError());
2592 }
2593 } // namespace
2594} // namespace internal
2595#endif
2596
2597
2598
2599template <typename NumberType>
2600void
2602 const std::string &filename,
2603 const std::pair<unsigned int, unsigned int> &chunk_size) const
2604{
2605#ifndef DEAL_II_WITH_HDF5
2606 (void)filename;
2607 (void)chunk_size;
2608 AssertThrow(false, ExcNeedsHDF5());
2609#else
2610
2611 std::pair<unsigned int, unsigned int> chunks_size_ = chunk_size;
2612
2613 if (chunks_size_.first == numbers::invalid_unsigned_int ||
2614 chunks_size_.second == numbers::invalid_unsigned_int)
2615 {
2616 // default: store the matrix in chunks of columns
2617 chunks_size_.first = n_rows;
2618 chunks_size_.second = 1;
2619 }
2620 Assert(chunks_size_.first > 0,
2621 ExcMessage("The row chunk size must be larger than 0."));
2622 AssertIndexRange(chunks_size_.first, n_rows + 1);
2623 Assert(chunks_size_.second > 0,
2624 ExcMessage("The column chunk size must be larger than 0."));
2625 AssertIndexRange(chunks_size_.second, n_columns + 1);
2626
2627# ifdef H5_HAVE_PARALLEL
2628 // implementation for configurations equipped with a parallel file system
2629 save_parallel(filename, chunks_size_);
2630
2631# else
2632 // implementation for configurations with no parallel file system
2633 save_serial(filename, chunks_size_);
2634
2635# endif
2636#endif
2637}
2638
2639
2640
2641template <typename NumberType>
2642void
2644 const std::string &filename,
2645 const std::pair<unsigned int, unsigned int> &chunk_size) const
2646{
2647#ifndef DEAL_II_WITH_HDF5
2648 (void)filename;
2649 (void)chunk_size;
2651#else
2652
2653 /*
2654 * The content of the distributed matrix is copied to a matrix using a 1x1
2655 * process grid. Therefore, one process has all the data and can write it to a
2656 * file.
2657 *
2658 * Create a 1x1 column grid which will be used to initialize
2659 * an effectively serial ScaLAPACK matrix to gather the contents from the
2660 * current object
2661 */
2662 const auto column_grid =
2663 std::make_shared<Utilities::MPI::ProcessGrid>(this->grid->mpi_communicator,
2664 1,
2665 1);
2666
2667 const int MB = n_rows, NB = n_columns;
2668 ScaLAPACKMatrix<NumberType> tmp(n_rows, n_columns, column_grid, MB, NB);
2669 copy_to(tmp);
2670
2671 // the 1x1 grid has only one process and this one writes
2672 // the content of the matrix to the HDF5 file
2673 if (tmp.grid->mpi_process_is_active)
2674 {
2675 herr_t status;
2676
2677 // create a new file using default properties
2678 hid_t file_id =
2679 H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
2680
2681 // modify dataset creation properties, i.e. enable chunking
2682 hsize_t chunk_dims[2];
2683 // revert order of rows and columns as ScaLAPACK uses column-major
2684 // ordering
2685 chunk_dims[0] = chunk_size.second;
2686 chunk_dims[1] = chunk_size.first;
2687 hid_t data_property = H5Pcreate(H5P_DATASET_CREATE);
2688 status = H5Pset_chunk(data_property, 2, chunk_dims);
2689 AssertThrow(status >= 0, ExcIO());
2690
2691 // create the data space for the dataset
2692 hsize_t dims[2];
2693 // change order of rows and columns as ScaLAPACKMatrix uses column major
2694 // ordering
2695 dims[0] = n_columns;
2696 dims[1] = n_rows;
2697 hid_t dataspace_id = H5Screate_simple(2, dims, nullptr);
2698
2699 // create the dataset within the file using chunk creation properties
2700 hid_t type_id = hdf5_type_id(tmp.values.data());
2701 hid_t dataset_id = H5Dcreate2(file_id,
2702 "/matrix",
2703 type_id,
2704 dataspace_id,
2705 H5P_DEFAULT,
2706 data_property,
2707 H5P_DEFAULT);
2708
2709 // write the dataset
2710 status = H5Dwrite(
2711 dataset_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, tmp.values.data());
2712 AssertThrow(status >= 0, ExcIO());
2713
2714 // create HDF5 enum type for LAPACKSupport::State and
2715 // LAPACKSupport::Property
2716 hid_t state_enum_id, property_enum_id;
2717 internal::create_HDF5_state_enum_id(state_enum_id);
2718 internal::create_HDF5_property_enum_id(property_enum_id);
2719
2720 // create the data space for the state enum
2721 hsize_t dims_state[1];
2722 dims_state[0] = 1;
2723 hid_t state_enum_dataspace = H5Screate_simple(1, dims_state, nullptr);
2724 // create the dataset for the state enum
2725 hid_t state_enum_dataset = H5Dcreate2(file_id,
2726 "/state",
2727 state_enum_id,
2728 state_enum_dataspace,
2729 H5P_DEFAULT,
2730 H5P_DEFAULT,
2731 H5P_DEFAULT);
2732 // write the dataset for the state enum
2733 status = H5Dwrite(state_enum_dataset,
2734 state_enum_id,
2735 H5S_ALL,
2736 H5S_ALL,
2737 H5P_DEFAULT,
2738 &state);
2739 AssertThrow(status >= 0, ExcIO());
2740
2741 // create the data space for the property enum
2742 hsize_t dims_property[1];
2743 dims_property[0] = 1;
2744 hid_t property_enum_dataspace =
2745 H5Screate_simple(1, dims_property, nullptr);
2746 // create the dataset for the property enum
2747 hid_t property_enum_dataset = H5Dcreate2(file_id,
2748 "/property",
2749 property_enum_id,
2750 property_enum_dataspace,
2751 H5P_DEFAULT,
2752 H5P_DEFAULT,
2753 H5P_DEFAULT);
2754 // write the dataset for the property enum
2755 status = H5Dwrite(property_enum_dataset,
2756 property_enum_id,
2757 H5S_ALL,
2758 H5S_ALL,
2759 H5P_DEFAULT,
2760 &property);
2761 AssertThrow(status >= 0, ExcIO());
2762
2763 // end access to the datasets and release resources used by them
2764 status = H5Dclose(dataset_id);
2765 AssertThrow(status >= 0, ExcIO());
2766 status = H5Dclose(state_enum_dataset);
2767 AssertThrow(status >= 0, ExcIO());
2768 status = H5Dclose(property_enum_dataset);
2769 AssertThrow(status >= 0, ExcIO());
2770
2771 // terminate access to the data spaces
2772 status = H5Sclose(dataspace_id);
2773 AssertThrow(status >= 0, ExcIO());
2774 status = H5Sclose(state_enum_dataspace);
2775 AssertThrow(status >= 0, ExcIO());
2776 status = H5Sclose(property_enum_dataspace);
2777 AssertThrow(status >= 0, ExcIO());
2778
2779 // release enum data types
2780 status = H5Tclose(state_enum_id);
2781 AssertThrow(status >= 0, ExcIO());
2782 status = H5Tclose(property_enum_id);
2783 AssertThrow(status >= 0, ExcIO());
2784
2785 // release the creation property
2786 status = H5Pclose(data_property);
2787 AssertThrow(status >= 0, ExcIO());
2788
2789 // close the file.
2790 status = H5Fclose(file_id);
2791 AssertThrow(status >= 0, ExcIO());
2792 }
2793#endif
2794}
2795
2796
2797
2798template <typename NumberType>
2799void
2801 const std::string &filename,
2802 const std::pair<unsigned int, unsigned int> &chunk_size) const
2803{
2804#ifndef DEAL_II_WITH_HDF5
2805 (void)filename;
2806 (void)chunk_size;
2808#else
2809
2810 const unsigned int n_mpi_processes(
2811 Utilities::MPI::n_mpi_processes(this->grid->mpi_communicator));
2812 MPI_Info info = MPI_INFO_NULL;
2813 /*
2814 * The content of the distributed matrix is copied to a matrix using a
2815 * 1xn_processes process grid. Therefore, the processes hold contiguous chunks
2816 * of the matrix, which they can write to the file
2817 *
2818 * Create a 1xn_processes column grid
2819 */
2820 const auto column_grid =
2821 std::make_shared<Utilities::MPI::ProcessGrid>(this->grid->mpi_communicator,
2822 1,
2823 n_mpi_processes);
2824
2825 const int MB = n_rows;
2826 /*
2827 * If the ratio n_columns/n_mpi_processes is smaller than the column block
2828 * size of the original matrix, the redistribution and saving of the matrix
2829 * requires a significant amount of MPI communication. Therefore, it is better
2830 * to set a minimum value for the block size NB, causing only
2831 * ceil(n_columns/NB) processes being actively involved in saving the matrix.
2832 * Example: A 2*10^9 x 400 matrix is distributed on a 80 x 5 process grid
2833 * using block size 32. Instead of distributing the matrix on a 1 x 400
2834 * process grid with a row block size of 2*10^9 and a column block size of 1,
2835 * the minimum value for NB yields that only ceil(400/32)=13 processes will be
2836 * writing the matrix to disk.
2837 */
2838 const int NB = std::max(static_cast<int>(std::ceil(
2839 static_cast<double>(n_columns) / n_mpi_processes)),
2840 column_block_size);
2841
2842 ScaLAPACKMatrix<NumberType> tmp(n_rows, n_columns, column_grid, MB, NB);
2843 copy_to(tmp);
2844
2845 // get pointer to data held by the process
2846 NumberType *data = (tmp.values.size() > 0) ? tmp.values.data() : nullptr;
2847
2848 herr_t status;
2849 // dataset dimensions
2850 hsize_t dims[2];
2851
2852 // set up file access property list with parallel I/O access
2853 hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS);
2854 status = H5Pset_fapl_mpio(plist_id, tmp.grid->mpi_communicator, info);
2855 AssertThrow(status >= 0, ExcIO());
2856
2857 // create a new file collectively and release property list identifier
2858 hid_t file_id =
2859 H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, plist_id);
2860 status = H5Pclose(plist_id);
2861 AssertThrow(status >= 0, ExcIO());
2862
2863 // As ScaLAPACK, and therefore the class ScaLAPACKMatrix, uses column-major
2864 // ordering but HDF5 row-major ordering, we have to reverse entries related to
2865 // columns and rows in the following. create the dataspace for the dataset
2866 dims[0] = tmp.n_columns;
2867 dims[1] = tmp.n_rows;
2868
2869 hid_t filespace = H5Screate_simple(2, dims, nullptr);
2870
2871 // create the chunked dataset with default properties and close filespace
2872 hsize_t chunk_dims[2];
2873 // revert order of rows and columns as ScaLAPACK uses column-major ordering
2874 chunk_dims[0] = chunk_size.second;
2875 chunk_dims[1] = chunk_size.first;
2876 plist_id = H5Pcreate(H5P_DATASET_CREATE);
2877 H5Pset_chunk(plist_id, 2, chunk_dims);
2878 hid_t type_id = hdf5_type_id(data);
2879 hid_t dset_id = H5Dcreate2(
2880 file_id, "/matrix", type_id, filespace, H5P_DEFAULT, plist_id, H5P_DEFAULT);
2881
2882 status = H5Sclose(filespace);
2883 AssertThrow(status >= 0, ExcIO());
2884
2885 status = H5Pclose(plist_id);
2886 AssertThrow(status >= 0, ExcIO());
2887
2888 // gather the number of local rows and columns from all processes
2889 std::vector<int> proc_n_local_rows(n_mpi_processes),
2890 proc_n_local_columns(n_mpi_processes);
2891 int ierr = MPI_Allgather(&tmp.n_local_rows,
2892 1,
2893 MPI_INT,
2894 proc_n_local_rows.data(),
2895 1,
2896 MPI_INT,
2897 tmp.grid->mpi_communicator);
2898 AssertThrowMPI(ierr);
2899 ierr = MPI_Allgather(&tmp.n_local_columns,
2900 1,
2901 MPI_INT,
2902 proc_n_local_columns.data(),
2903 1,
2904 MPI_INT,
2905 tmp.grid->mpi_communicator);
2906 AssertThrowMPI(ierr);
2907
2908 const unsigned int my_rank(
2909 Utilities::MPI::this_mpi_process(tmp.grid->mpi_communicator));
2910
2911 // hyperslab selection parameters
2912 // each process defines dataset in memory and writes it to the hyperslab in
2913 // the file
2914 hsize_t count[2];
2915 count[0] = tmp.n_local_columns;
2916 count[1] = tmp.n_rows;
2917 hid_t memspace = H5Screate_simple(2, count, nullptr);
2918
2919 hsize_t offset[2] = {0};
2920 for (unsigned int i = 0; i < my_rank; ++i)
2921 offset[0] += proc_n_local_columns[i];
2922
2923 // select hyperslab in the file.
2924 filespace = H5Dget_space(dset_id);
2925 status = H5Sselect_hyperslab(
2926 filespace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
2927 AssertThrow(status >= 0, ExcIO());
2928
2929 // create property list for independent dataset write
2930 plist_id = H5Pcreate(H5P_DATASET_XFER);
2931 status = H5Pset_dxpl_mpio(plist_id, H5FD_MPIO_INDEPENDENT);
2932 AssertThrow(status >= 0, ExcIO());
2933
2934 // process with no data will not participate in writing to the file
2935 if (tmp.values.size() > 0)
2936 {
2937 status = H5Dwrite(dset_id, type_id, memspace, filespace, plist_id, data);
2938 AssertThrow(status >= 0, ExcIO());
2939 }
2940 // close/release sources
2941 status = H5Dclose(dset_id);
2942 AssertThrow(status >= 0, ExcIO());
2943 status = H5Sclose(filespace);
2944 AssertThrow(status >= 0, ExcIO());
2945 status = H5Sclose(memspace);
2946 AssertThrow(status >= 0, ExcIO());
2947 status = H5Pclose(plist_id);
2948 AssertThrow(status >= 0, ExcIO());
2949 status = H5Fclose(file_id);
2950 AssertThrow(status >= 0, ExcIO());
2951
2952 // before writing the state and property to file wait for
2953 // all processes to finish writing the matrix content to the file
2954 ierr = MPI_Barrier(tmp.grid->mpi_communicator);
2955 AssertThrowMPI(ierr);
2956
2957 // only root process will write state and property to the file
2958 if (tmp.grid->this_mpi_process == 0)
2959 {
2960 // open file using default properties
2961 hid_t file_id_reopen =
2962 H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
2963
2964 // create HDF5 enum type for LAPACKSupport::State and
2965 // LAPACKSupport::Property
2966 hid_t state_enum_id, property_enum_id;
2967 internal::create_HDF5_state_enum_id(state_enum_id);
2968 internal::create_HDF5_property_enum_id(property_enum_id);
2969
2970 // create the data space for the state enum
2971 hsize_t dims_state[1];
2972 dims_state[0] = 1;
2973 hid_t state_enum_dataspace = H5Screate_simple(1, dims_state, nullptr);
2974 // create the dataset for the state enum
2975 hid_t state_enum_dataset = H5Dcreate2(file_id_reopen,
2976 "/state",
2977 state_enum_id,
2978 state_enum_dataspace,
2979 H5P_DEFAULT,
2980 H5P_DEFAULT,
2981 H5P_DEFAULT);
2982 // write the dataset for the state enum
2983 status = H5Dwrite(state_enum_dataset,
2984 state_enum_id,
2985 H5S_ALL,
2986 H5S_ALL,
2987 H5P_DEFAULT,
2988 &state);
2989 AssertThrow(status >= 0, ExcIO());
2990
2991 // create the data space for the property enum
2992 hsize_t dims_property[1];
2993 dims_property[0] = 1;
2994 hid_t property_enum_dataspace =
2995 H5Screate_simple(1, dims_property, nullptr);
2996 // create the dataset for the property enum
2997 hid_t property_enum_dataset = H5Dcreate2(file_id_reopen,
2998 "/property",
2999 property_enum_id,
3000 property_enum_dataspace,
3001 H5P_DEFAULT,
3002 H5P_DEFAULT,
3003 H5P_DEFAULT);
3004 // write the dataset for the property enum
3005 status = H5Dwrite(property_enum_dataset,
3006 property_enum_id,
3007 H5S_ALL,
3008 H5S_ALL,
3009 H5P_DEFAULT,
3010 &property);
3011 AssertThrow(status >= 0, ExcIO());
3012
3013 status = H5Dclose(state_enum_dataset);
3014 AssertThrow(status >= 0, ExcIO());
3015 status = H5Dclose(property_enum_dataset);
3016 AssertThrow(status >= 0, ExcIO());
3017 status = H5Sclose(state_enum_dataspace);
3018 AssertThrow(status >= 0, ExcIO());
3019 status = H5Sclose(property_enum_dataspace);
3020 AssertThrow(status >= 0, ExcIO());
3021 status = H5Tclose(state_enum_id);
3022 AssertThrow(status >= 0, ExcIO());
3023 status = H5Tclose(property_enum_id);
3024 AssertThrow(status >= 0, ExcIO());
3025 status = H5Fclose(file_id_reopen);
3026 AssertThrow(status >= 0, ExcIO());
3027 }
3028
3029#endif
3030}
3031
3032
3033
3034template <typename NumberType>
3035void
3036ScaLAPACKMatrix<NumberType>::load(const std::string &filename)
3037{
3038#ifndef DEAL_II_WITH_HDF5
3039 (void)filename;
3040 AssertThrow(false, ExcNeedsHDF5());
3041#else
3042# ifdef H5_HAVE_PARALLEL
3043 // implementation for configurations equipped with a parallel file system
3044 load_parallel(filename);
3045
3046# else
3047 // implementation for configurations with no parallel file system
3048 load_serial(filename);
3049# endif
3050#endif
3051}
3052
3053
3054
3055template <typename NumberType>
3056void
3058{
3059#ifndef DEAL_II_WITH_HDF5
3060 (void)filename;
3062#else
3063
3064 /*
3065 * The content of the distributed matrix is copied to a matrix using a 1x1
3066 * process grid. Therefore, one process has all the data and can write it to a
3067 * file
3068 */
3069 // create a 1xP column grid with P being the number of MPI processes
3070 const auto one_grid =
3071 std::make_shared<Utilities::MPI::ProcessGrid>(this->grid->mpi_communicator,
3072 1,
3073 1);
3074
3075 const int MB = n_rows, NB = n_columns;
3076 ScaLAPACKMatrix<NumberType> tmp(n_rows, n_columns, one_grid, MB, NB);
3077
3078 int state_int = -1;
3079 int property_int = -1;
3080
3081 // the 1x1 grid has only one process and this one reads
3082 // the content of the matrix from the HDF5 file
3083 if (tmp.grid->mpi_process_is_active)
3084 {
3085 herr_t status;
3086
3087 // open the file in read-only mode
3088 hid_t file_id = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
3089
3090 // open the dataset in the file
3091 hid_t dataset_id = H5Dopen2(file_id, "/matrix", H5P_DEFAULT);
3092
3093 // check the datatype of the data in the file
3094 // datatype of source and destination must have the same class
3095 // see HDF User's Guide: 6.10. Data Transfer: Datatype Conversion and
3096 // Selection
3097 hid_t datatype = H5Dget_type(dataset_id);
3098 H5T_class_t t_class_in = H5Tget_class(datatype);
3099 H5T_class_t t_class = H5Tget_class(hdf5_type_id(tmp.values.data()));
3101 t_class_in == t_class,
3102 ExcMessage(
3103 "The data type of the matrix to be read does not match the archive"));
3104
3105 // get dataspace handle
3106 hid_t dataspace_id = H5Dget_space(dataset_id);
3107 // get number of dimensions
3108 const int ndims = H5Sget_simple_extent_ndims(dataspace_id);
3109 AssertThrow(ndims == 2, ExcIO());
3110 // get every dimension
3111 hsize_t dims[2];
3112 H5Sget_simple_extent_dims(dataspace_id, dims, nullptr);
3114 static_cast<int>(dims[0]) == n_columns,
3115 ExcMessage(
3116 "The number of columns of the matrix does not match the content of the archive"));
3118 static_cast<int>(dims[1]) == n_rows,
3119 ExcMessage(
3120 "The number of rows of the matrix does not match the content of the archive"));
3121
3122 // read data
3123 status = H5Dread(dataset_id,
3124 hdf5_type_id(tmp.values.data()),
3125 H5S_ALL,
3126 H5S_ALL,
3127 H5P_DEFAULT,
3128 tmp.values.data());
3129 AssertThrow(status >= 0, ExcIO());
3130
3131 // create HDF5 enum type for LAPACKSupport::State and
3132 // LAPACKSupport::Property
3133 hid_t state_enum_id, property_enum_id;
3134 internal::create_HDF5_state_enum_id(state_enum_id);
3135 internal::create_HDF5_property_enum_id(property_enum_id);
3136
3137 // open the datasets for the state and property enum in the file
3138 hid_t dataset_state_id = H5Dopen2(file_id, "/state", H5P_DEFAULT);
3139 hid_t datatype_state = H5Dget_type(dataset_state_id);
3140 H5T_class_t t_class_state = H5Tget_class(datatype_state);
3141 AssertThrow(t_class_state == H5T_ENUM, ExcIO());
3142
3143 hid_t dataset_property_id = H5Dopen2(file_id, "/property", H5P_DEFAULT);
3144 hid_t datatype_property = H5Dget_type(dataset_property_id);
3145 H5T_class_t t_class_property = H5Tget_class(datatype_property);
3146 AssertThrow(t_class_property == H5T_ENUM, ExcIO());
3147
3148 // get dataspace handles
3149 hid_t dataspace_state = H5Dget_space(dataset_state_id);
3150 hid_t dataspace_property = H5Dget_space(dataset_property_id);
3151 // get number of dimensions
3152 const int ndims_state = H5Sget_simple_extent_ndims(dataspace_state);
3153 AssertThrow(ndims_state == 1, ExcIO());
3154 const int ndims_property = H5Sget_simple_extent_ndims(dataspace_property);
3155 AssertThrow(ndims_property == 1, ExcIO());
3156 // get every dimension
3157 hsize_t dims_state[1];
3158 H5Sget_simple_extent_dims(dataspace_state, dims_state, nullptr);
3159 AssertThrow(static_cast<int>(dims_state[0]) == 1, ExcIO());
3160 hsize_t dims_property[1];
3161 H5Sget_simple_extent_dims(dataspace_property, dims_property, nullptr);
3162 AssertThrow(static_cast<int>(dims_property[0]) == 1, ExcIO());
3163
3164 // read data
3165 status = H5Dread(dataset_state_id,
3166 state_enum_id,
3167 H5S_ALL,
3168 H5S_ALL,
3169 H5P_DEFAULT,
3170 &tmp.state);
3171 AssertThrow(status >= 0, ExcIO());
3172 // To send the state from the root process to the other processes
3173 // the state enum is casted to an integer, that will be broadcasted and
3174 // subsequently casted back to the enum type
3175 state_int = static_cast<int>(tmp.state);
3176
3177 status = H5Dread(dataset_property_id,
3178 property_enum_id,
3179 H5S_ALL,
3180 H5S_ALL,
3181 H5P_DEFAULT,
3182 &tmp.property);
3183 AssertThrow(status >= 0, ExcIO());
3184 // To send the property from the root process to the other processes
3185 // the state enum is casted to an integer, that will be broadcasted and
3186 // subsequently casted back to the enum type
3187 property_int = static_cast<int>(tmp.property);
3188
3189 // terminate access to the data spaces
3190 status = H5Sclose(dataspace_id);
3191 AssertThrow(status >= 0, ExcIO());
3192 status = H5Sclose(dataspace_state);
3193 AssertThrow(status >= 0, ExcIO());
3194 status = H5Sclose(dataspace_property);
3195 AssertThrow(status >= 0, ExcIO());
3196
3197 // release data type handles
3198 status = H5Tclose(datatype);
3199 AssertThrow(status >= 0, ExcIO());
3200 status = H5Tclose(state_enum_id);
3201 AssertThrow(status >= 0, ExcIO());
3202 status = H5Tclose(property_enum_id);
3203 AssertThrow(status >= 0, ExcIO());
3204
3205 // end access to the data sets and release resources used by them
3206 status = H5Dclose(dataset_state_id);
3207 AssertThrow(status >= 0, ExcIO());
3208 status = H5Dclose(dataset_id);
3209 AssertThrow(status >= 0, ExcIO());
3210 status = H5Dclose(dataset_property_id);
3211 AssertThrow(status >= 0, ExcIO());
3212
3213 // close the file.
3214 status = H5Fclose(file_id);
3215 AssertThrow(status >= 0, ExcIO());
3216 }
3217 // so far only the root process has the correct state integer --> broadcasting
3218 tmp.grid->send_to_inactive(&state_int, 1);
3219 // so far only the root process has the correct property integer -->
3220 // broadcasting
3221 tmp.grid->send_to_inactive(&property_int, 1);
3222
3223 tmp.state = static_cast<LAPACKSupport::State>(state_int);
3224 tmp.property = static_cast<LAPACKSupport::Property>(property_int);
3225
3226 tmp.copy_to(*this);
3227
3228#endif // DEAL_II_WITH_HDF5
3229}
3230
3231
3232
3233template <typename NumberType>
3234void
3236{
3237#ifndef DEAL_II_WITH_HDF5
3238 (void)filename;
3240#else
3241# ifndef H5_HAVE_PARALLEL
3242 (void)filename;
3244# else
3245
3246 const unsigned int n_mpi_processes(
3247 Utilities::MPI::n_mpi_processes(this->grid->mpi_communicator));
3248 MPI_Info info = MPI_INFO_NULL;
3249 /*
3250 * The content of the distributed matrix is copied to a matrix using a
3251 * 1xn_processes process grid. Therefore, the processes hold contiguous chunks
3252 * of the matrix, which they can write to the file
3253 */
3254 // create a 1xP column grid with P being the number of MPI processes
3255 const auto column_grid =
3256 std::make_shared<Utilities::MPI::ProcessGrid>(this->grid->mpi_communicator,
3257 1,
3258 n_mpi_processes);
3259
3260 const int MB = n_rows;
3261 // for the choice of NB see explanation in save_parallel()
3262 const int NB = std::max(static_cast<int>(std::ceil(
3263 static_cast<double>(n_columns) / n_mpi_processes)),
3264 column_block_size);
3265
3266 ScaLAPACKMatrix<NumberType> tmp(n_rows, n_columns, column_grid, MB, NB);
3267
3268 // get pointer to data held by the process
3269 NumberType *data = (tmp.values.size() > 0) ? tmp.values.data() : nullptr;
3270
3271 herr_t status;
3272
3273 // set up file access property list with parallel I/O access
3274 hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS);
3275 status = H5Pset_fapl_mpio(plist_id, tmp.grid->mpi_communicator, info);
3276 AssertThrow(status >= 0, ExcIO());
3277
3278 // open file collectively in read-only mode and release property list
3279 // identifier
3280 hid_t file_id = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, plist_id);
3281 status = H5Pclose(plist_id);
3282 AssertThrow(status >= 0, ExcIO());
3283
3284 // open the dataset in the file collectively
3285 hid_t dataset_id = H5Dopen2(file_id, "/matrix", H5P_DEFAULT);
3286
3287 // check the datatype of the dataset in the file
3288 // if the classes of type of the dataset and the matrix do not match abort
3289 // see HDF User's Guide: 6.10. Data Transfer: Datatype Conversion and
3290 // Selection
3291 hid_t datatype = hdf5_type_id(data);
3292 hid_t datatype_inp = H5Dget_type(dataset_id);
3293 H5T_class_t t_class_inp = H5Tget_class(datatype_inp);
3294 H5T_class_t t_class = H5Tget_class(datatype);
3296 t_class_inp == t_class,
3297 ExcMessage(
3298 "The data type of the matrix to be read does not match the archive"));
3299
3300 // get the dimensions of the matrix stored in the file
3301 // get dataspace handle
3302 hid_t dataspace_id = H5Dget_space(dataset_id);
3303 // get number of dimensions
3304 const int ndims = H5Sget_simple_extent_ndims(dataspace_id);
3305 AssertThrow(ndims == 2, ExcIO());
3306 // get every dimension
3307 hsize_t dims[2];
3308 status = H5Sget_simple_extent_dims(dataspace_id, dims, nullptr);
3309 AssertThrow(status >= 0, ExcIO());
3311 static_cast<int>(dims[0]) == n_columns,
3312 ExcMessage(
3313 "The number of columns of the matrix does not match the content of the archive"));
3315 static_cast<int>(dims[1]) == n_rows,
3316 ExcMessage(
3317 "The number of rows of the matrix does not match the content of the archive"));
3318
3319 // gather the number of local rows and columns from all processes
3320 std::vector<int> proc_n_local_rows(n_mpi_processes),
3321 proc_n_local_columns(n_mpi_processes);
3322 int ierr = MPI_Allgather(&tmp.n_local_rows,
3323 1,
3324 MPI_INT,
3325 proc_n_local_rows.data(),
3326 1,
3327 MPI_INT,
3328 tmp.grid->mpi_communicator);
3329 AssertThrowMPI(ierr);
3330 ierr = MPI_Allgather(&tmp.n_local_columns,
3331 1,
3332 MPI_INT,
3333 proc_n_local_columns.data(),
3334 1,
3335 MPI_INT,
3336 tmp.grid->mpi_communicator);
3337 AssertThrowMPI(ierr);
3338
3339 const unsigned int my_rank(
3340 Utilities::MPI::this_mpi_process(tmp.grid->mpi_communicator));
3341
3342 // hyperslab selection parameters
3343 // each process defines dataset in memory and writes it to the hyperslab in
3344 // the file
3345 hsize_t count[2];
3346 count[0] = tmp.n_local_columns;
3347 count[1] = tmp.n_local_rows;
3348
3349 hsize_t offset[2] = {0};
3350 for (unsigned int i = 0; i < my_rank; ++i)
3351 offset[0] += proc_n_local_columns[i];
3352
3353 // select hyperslab in the file
3354 status = H5Sselect_hyperslab(
3355 dataspace_id, H5S_SELECT_SET, offset, nullptr, count, nullptr);
3356 AssertThrow(status >= 0, ExcIO());
3357
3358 // create a memory dataspace independently
3359 hid_t memspace = H5Screate_simple(2, count, nullptr);
3360
3361 // read data independently
3362 status =
3363 H5Dread(dataset_id, datatype, memspace, dataspace_id, H5P_DEFAULT, data);
3364 AssertThrow(status >= 0, ExcIO());
3365
3366 // create HDF5 enum type for LAPACKSupport::State and LAPACKSupport::Property
3367 hid_t state_enum_id, property_enum_id;
3368 internal::create_HDF5_state_enum_id(state_enum_id);
3369 internal::create_HDF5_property_enum_id(property_enum_id);
3370
3371 // open the datasets for the state and property enum in the file
3372 hid_t dataset_state_id = H5Dopen2(file_id, "/state", H5P_DEFAULT);
3373 hid_t datatype_state = H5Dget_type(dataset_state_id);
3374 H5T_class_t t_class_state = H5Tget_class(datatype_state);
3375 AssertThrow(t_class_state == H5T_ENUM, ExcIO());
3376
3377 hid_t dataset_property_id = H5Dopen2(file_id, "/property", H5P_DEFAULT);
3378 hid_t datatype_property = H5Dget_type(dataset_property_id);
3379 H5T_class_t t_class_property = H5Tget_class(datatype_property);
3380 AssertThrow(t_class_property == H5T_ENUM, ExcIO());
3381
3382 // get dataspace handles
3383 hid_t dataspace_state = H5Dget_space(dataset_state_id);
3384 hid_t dataspace_property = H5Dget_space(dataset_property_id);
3385 // get number of dimensions
3386 const int ndims_state = H5Sget_simple_extent_ndims(dataspace_state);
3387 AssertThrow(ndims_state == 1, ExcIO());
3388 const int ndims_property = H5Sget_simple_extent_ndims(dataspace_property);
3389 AssertThrow(ndims_property == 1, ExcIO());
3390 // get every dimension
3391 hsize_t dims_state[1];
3392 H5Sget_simple_extent_dims(dataspace_state, dims_state, nullptr);
3393 AssertThrow(static_cast<int>(dims_state[0]) == 1, ExcIO());
3394 hsize_t dims_property[1];
3395 H5Sget_simple_extent_dims(dataspace_property, dims_property, nullptr);
3396 AssertThrow(static_cast<int>(dims_property[0]) == 1, ExcIO());
3397
3398 // read data
3399 status = H5Dread(
3400 dataset_state_id, state_enum_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, &tmp.state);
3401 AssertThrow(status >= 0, ExcIO());
3402
3403 status = H5Dread(dataset_property_id,
3404 property_enum_id,
3405 H5S_ALL,
3406 H5S_ALL,
3407 H5P_DEFAULT,
3408 &tmp.property);
3409 AssertThrow(status >= 0, ExcIO());
3410
3411 // close/release sources
3412 status = H5Sclose(memspace);
3413 AssertThrow(status >= 0, ExcIO());
3414 status = H5Dclose(dataset_id);
3415 AssertThrow(status >= 0, ExcIO());
3416 status = H5Dclose(dataset_state_id);
3417 AssertThrow(status >= 0, ExcIO());
3418 status = H5Dclose(dataset_property_id);
3419 AssertThrow(status >= 0, ExcIO());
3420 status = H5Sclose(dataspace_id);
3421 AssertThrow(status >= 0, ExcIO());
3422 status = H5Sclose(dataspace_state);
3423 AssertThrow(status >= 0, ExcIO());
3424 status = H5Sclose(dataspace_property);
3425 AssertThrow(status >= 0, ExcIO());
3426 // status = H5Tclose(datatype);
3427 // AssertThrow(status >= 0, ExcIO());
3428 status = H5Tclose(state_enum_id);
3429 AssertThrow(status >= 0, ExcIO());
3430 status = H5Tclose(property_enum_id);
3431 AssertThrow(status >= 0, ExcIO());
3432 status = H5Fclose(file_id);
3433 AssertThrow(status >= 0, ExcIO());
3434
3435 // copying the distributed matrices
3436 tmp.copy_to(*this);
3437
3438# endif // H5_HAVE_PARALLEL
3439#endif // DEAL_II_WITH_HDF5
3440}
3441
3442
3443
3444namespace internal
3445{
3446 namespace
3447 {
3448 template <typename NumberType>
3449 void
3450 scale_columns(ScaLAPACKMatrix<NumberType> &matrix,
3451 const ArrayView<const NumberType> &factors)
3452 {
3453 Assert(matrix.n() == factors.size(),
3454 ExcDimensionMismatch(matrix.n(), factors.size()));
3455
3456 for (unsigned int i = 0; i < matrix.local_n(); ++i)
3457 {
3458 const NumberType s = factors[matrix.global_column(i)];
3459
3460 for (unsigned int j = 0; j < matrix.local_m(); ++j)
3461 matrix.local_el(j, i) *= s;
3462 }
3463 }
3464
3465 template <typename NumberType>
3466 void
3467 scale_rows(ScaLAPACKMatrix<NumberType> &matrix,
3468 const ArrayView<const NumberType> &factors)
3469 {
3470 Assert(matrix.m() == factors.size(),
3471 ExcDimensionMismatch(matrix.m(), factors.size()));
3472
3473 for (unsigned int i = 0; i < matrix.local_m(); ++i)
3474 {
3475 const NumberType s = factors[matrix.global_row(i)];
3476
3477 for (unsigned int j = 0; j < matrix.local_n(); ++j)
3478 matrix.local_el(i, j) *= s;
3479 }
3480 }
3481
3482 } // namespace
3483} // namespace internal
3484
3485
3486
3487template <typename NumberType>
3488template <class InputVector>
3489void
3491{
3492 if (this->grid->mpi_process_is_active)
3493 internal::scale_columns(*this, make_array_view(factors));
3494}
3495
3496
3497
3498template <typename NumberType>
3499template <class InputVector>
3500void
3502{
3503 if (this->grid->mpi_process_is_active)
3504 internal::scale_rows(*this, make_array_view(factors));
3505}
3506
3507
3508
3509// instantiations
3510#include "lac/scalapack.inst"
3511
3512
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
std::size_t size() const
Definition array_view.h:737
size_type m() const
size_type n() const
int descriptor[9]
Definition scalapack.h:935
std::vector< NumberType > compute_SVD(ScaLAPACKMatrix< NumberType > *U=nullptr, ScaLAPACKMatrix< NumberType > *VT=nullptr)
std::vector< NumberType > eigenpairs_symmetric_by_value(const std::pair< NumberType, NumberType > &value_limits, const bool compute_eigenvectors)
NumberType frobenius_norm() const
unsigned int pseudoinverse(const NumberType ratio)
std::vector< NumberType > eigenpairs_symmetric_by_value_MRRR(const std::pair< NumberType, NumberType > &value_limits, const bool compute_eigenvectors)
void copy_from(const LAPACKFullMatrix< NumberType > &matrix, const unsigned int rank)
Definition scalapack.cc:342
void save_parallel(const std::string &filename, const std::pair< unsigned int, unsigned int > &chunk_size) const
void least_squares(ScaLAPACKMatrix< NumberType > &B, const bool transpose=false)
ScaLAPACKMatrix< NumberType > & operator=(const FullMatrix< NumberType > &)
Definition scalapack.cc:311
void Tadd(const NumberType b, const ScaLAPACKMatrix< NumberType > &B)
void mTmult(ScaLAPACKMatrix< NumberType > &C, const ScaLAPACKMatrix< NumberType > &B, const bool adding=false) const
void add(const ScaLAPACKMatrix< NumberType > &B, const NumberType a=0., const NumberType b=1., const bool transpose_B=false)
Definition scalapack.cc:974
LAPACKSupport::State get_state() const
Definition scalapack.cc:302
LAPACKSupport::Property get_property() const
Definition scalapack.cc:293
void Tmmult(ScaLAPACKMatrix< NumberType > &C, const ScaLAPACKMatrix< NumberType > &B, const bool adding=false) const
std::vector< NumberType > eigenpairs_symmetric_MRRR(const bool compute_eigenvectors, const std::pair< unsigned int, unsigned int > &index_limits=std::make_pair(numbers::invalid_unsigned_int, numbers::invalid_unsigned_int), const std::pair< NumberType, NumberType > &value_limits=std::make_pair(std::numeric_limits< NumberType >::quiet_NaN(), std::numeric_limits< NumberType >::quiet_NaN()))
void scale_rows(const InputVector &factors)
std::shared_ptr< const Utilities::MPI::ProcessGrid > grid
Definition scalapack.h:900
ScaLAPACKMatrix(const size_type n_rows, const size_type n_columns, const std::shared_ptr< const Utilities::MPI::ProcessGrid > &process_grid, const size_type row_block_size=32, const size_type column_block_size=32, const LAPACKSupport::Property property=LAPACKSupport::Property::general)
Definition scalapack.cc:60
void load(const std::string &filename)
const int submatrix_column
Definition scalapack.h:981
const int submatrix_row
Definition scalapack.h:975
void mmult(ScaLAPACKMatrix< NumberType > &C, const ScaLAPACKMatrix< NumberType > &B, const bool adding=false) const
std::vector< NumberType > eigenpairs_symmetric(const bool compute_eigenvectors, const std::pair< unsigned int, unsigned int > &index_limits=std::make_pair(numbers::invalid_unsigned_int, numbers::invalid_unsigned_int), const std::pair< NumberType, NumberType > &value_limits=std::make_pair(std::numeric_limits< NumberType >::quiet_NaN(), std::numeric_limits< NumberType >::quiet_NaN()))
void save_serial(const std::string &filename, const std::pair< unsigned int, unsigned int > &chunk_size) const
NumberType norm_general(const char type) const
void save(const std::string &filename, const std::pair< unsigned int, unsigned int > &chunk_size=std::make_pair(numbers::invalid_unsigned_int, numbers::invalid_unsigned_int)) const
void load_parallel(const std::string &filename)
NumberType l1_norm() const
void compute_lu_factorization()
NumberType norm_symmetric(const char type) const
void mult(const NumberType b, const ScaLAPACKMatrix< NumberType > &B, const NumberType c, ScaLAPACKMatrix< NumberType > &C, const bool transpose_A=false, const bool transpose_B=false) const
LAPACKSupport::Property property
Definition scalapack.h:893
void set_property(const LAPACKSupport::Property property)
Definition scalapack.cc:283
void reinit(const size_type n_rows, const size_type n_columns, const std::shared_ptr< const Utilities::MPI::ProcessGrid > &process_grid, const size_type row_block_size=32, const size_type column_block_size=32, const LAPACKSupport::Property property=LAPACKSupport::Property::general)
Definition scalapack.cc:196
void load_serial(const std::string &filename)
std::vector< NumberType > eigenpairs_symmetric_by_index_MRRR(const std::pair< unsigned int, unsigned int > &index_limits, const bool compute_eigenvectors)
NumberType reciprocal_condition_number(const NumberType a_norm) const
LAPACKSupport::State state
Definition scalapack.h:887
void TmTmult(ScaLAPACKMatrix< NumberType > &C, const ScaLAPACKMatrix< NumberType > &B, const bool adding=false) const
std::vector< NumberType > eigenpairs_symmetric_by_index(const std::pair< unsigned int, unsigned int > &index_limits, const bool compute_eigenvectors)
unsigned int global_column(const unsigned int loc_column) const
Definition scalapack.cc:496
void copy_to(FullMatrix< NumberType > &matrix) const
Definition scalapack.cc:650
unsigned int global_row(const unsigned int loc_row) const
Definition scalapack.cc:479
void compute_cholesky_factorization()
void copy_transposed(const ScaLAPACKMatrix< NumberType > &B)
Definition scalapack.cc:964
NumberType linfty_norm() const
void scale_columns(const InputVector &factors)
AlignedVector< T > values
Definition table.h:793
void reinit(const size_type size1, const size_type size2, const bool omit_default_initialization=false)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#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()
const unsigned int v1
static ::ExceptionBase & ExcIO()
static ::ExceptionBase & ExcErrorCode(std::string arg1, types::blas_int arg2)
#define Assert(cond, exc)
#define AssertIsFinite(number)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcNeedsHDF5()
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
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
std::size_t size
Definition mpi.cc:733
@ cholesky
Contents is a Cholesky decomposition.
@ lu
Contents is an LU decomposition.
@ matrix
Contents is actually a matrix.
@ unusable
Contents is something useless.
@ inverse_matrix
Contents is the inverse of a matrix.
@ svd
Matrix contains singular value decomposition,.
@ inverse_svd
Matrix is the inverse of a singular value decomposition.
@ eigenvalues
Eigenvalue vector is filled.
@ symmetric
Matrix is symmetric.
@ hessenberg
Matrix is in upper Hessenberg form.
@ diagonal
Matrix is diagonal.
@ upper_triangular
Matrix is upper triangular.
@ lower_triangular
Matrix is lower triangular.
@ general
No special properties.
@ scalapack_copy_from
ScaLAPACKMatrix<NumberType>::copy_from.
Definition mpi_tags.h:119
@ scalapack_copy_to2
ScaLAPACKMatrix<NumberType>::copy_to.
Definition mpi_tags.h:117
@ scalapack_copy_to
ScaLAPACKMatrix<NumberType>::copy_to.
Definition mpi_tags.h:115
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
T max(const T &t, const MPI_Comm mpi_communicator)
T min(const T &t, const MPI_Comm mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
const MPI_Datatype mpi_type_id_for_type
Definition mpi.h:1685
void free_communicator(MPI_Comm mpi_communicator)
Definition mpi.cc:165
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
std::array< std::pair< Number, Tensor< 1, dim, Number > >, dim > eigenvectors(const SymmetricTensor< 2, dim, Number > &T, const SymmetricTensorEigenvectorMethod method=SymmetricTensorEigenvectorMethod::ql_implicit_shifts)