GetFEM  5.5
gmm_MUMPS_interface.h
Go to the documentation of this file.
1 /* -*- c++ -*- (enables emacs c++ mode) */
2 /*===========================================================================
3 
4  Copyright (C) 2003-2026 Yves Renard, Julien Pommier
5 
6  This file is a part of GetFEM
7 
8  GetFEM is free software; you can redistribute it and/or modify it
9  under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 3 of the License, or
11  (at your option) any later version along with the GCC Runtime Library
12  Exception either version 3.1 or (at your option) any later version.
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16  License and GCC Runtime Library Exception for more details.
17  You should have received a copy of the GNU Lesser General Public License
18  along with this program. If not, see https://www.gnu.org/licenses/.
19 
20  As a special exception, you may use this file as it is a part of a free
21  software library without restriction. Specifically, if other files
22  instantiate templates or use macros or inline functions from this file,
23  or you compile this file and link it with other files to produce an
24  executable, this file does not by itself cause the resulting executable
25  to be covered by the GNU Lesser General Public License. This exception
26  does not however invalidate any other reasons why the executable file
27  might be covered by the GNU Lesser General Public License.
28 
29 ===========================================================================*/
30 
31 /**@file gmm_MUMPS_interface.h
32  @author Yves Renard <Yves.Renard@insa-lyon.fr>,
33  @author Julien Pommier <Julien.Pommier@insa-toulouse.fr>
34  @date December 8, 2005.
35  @brief Interface with MUMPS (LU direct solver for sparse matrices).
36 */
37 #if defined(GMM_USES_MUMPS)
38 
39 #ifndef GMM_MUMPS_INTERFACE_H
40 #define GMM_MUMPS_INTERFACE_H
41 
42 #include "gmm_kernel.h"
43 
44 
45 extern "C" {
46 
47 #include <smumps_c.h>
48 #undef F_INT
49 #undef F_DOUBLE
50 #undef F_DOUBLE2
51 #include <dmumps_c.h>
52 #undef F_INT
53 #undef F_DOUBLE
54 #undef F_DOUBLE2
55 #include <cmumps_c.h>
56 #undef F_INT
57 #undef F_DOUBLE
58 #undef F_DOUBLE2
59 #include <zmumps_c.h>
60 #undef F_INT
61 #undef F_DOUBLE
62 #undef F_DOUBLE2
63 
64 }
65 
66 namespace gmm {
67 
68  template <typename T>
69  struct ij_sparse_matrix {
70  typedef typename number_traits<T>::magnitude_type R;
71  std::vector<int> irn;
72  std::vector<int> jcn;
73  std::vector<T> a;
74 
75  static const std::vector<size_type> no_sel;
76 
77  // input 0-based, output 1-based
78  void build_indices_vector(const std::vector<size_type> &inp,
79  std::vector<int> &out) {
80  if (inp.empty())
81  for (size_type i = 0; i < out.size(); ++i) out[i] = int(i+1);
82  else
83  for (size_type i = 0; i < inp.size(); ++i) out[inp[i]] = int(i+1);
84  }
85 
86  // build an i,j,a matrix from matrix A, optionally performing row and
87  // column selection/permutation on A, and optionally keeping only the
88  // lower triangle part of the resulting matrix (after permutations)
89  template <typename L>
90  void build_from(const L& A, row_major,
91  bool lower_triangular=false,
92  const std::vector<size_type> &rows=no_sel,
93  const std::vector<size_type> &cols=no_sel) {
94  std::vector<int> row_ind(mat_nrows(A),0), col_ind(mat_nrows(A),0);
95  build_indices_vector(rows, row_ind);
96  build_indices_vector(cols, col_ind);
97  for (size_type i = 0; i < mat_nrows(A); ++i) {
98  const int ir = row_ind[i];
99  if (ir > 0) {
100  auto row = mat_const_row(A, i);
101  auto it = vect_const_begin(row), ite = vect_const_end(row);
102  for (; it != ite; ++it) {
103  const int jc = col_ind[it.index()];
104  if (jc > 0 && (*it != T(0))
105  && (!lower_triangular || ir >= jc)) {
106  irn.push_back(ir);
107  jcn.push_back(jc);
108  a.push_back(*it);
109  }
110  }
111  }
112  }
113  }
114 
115  template <typename L>
116  void build_from(const L& A, col_major,
117  bool lower_triangular=false,
118  const std::vector<size_type> &rows=no_sel,
119  const std::vector<size_type> &cols=no_sel) {
120  std::vector<int> row_ind(mat_nrows(A),0), col_ind(mat_nrows(A),0);
121  build_indices_vector(rows, row_ind);
122  build_indices_vector(cols, col_ind);
123  for (size_type j = 0; j < mat_ncols(A); ++j) {
124  const int jc = col_ind[j];
125  if (jc >0) {
126  auto col = mat_const_col(A, j);
127  auto it = vect_const_begin(col), ite = vect_const_end(col);
128  for (; it != ite; ++it) {
129  const int ir = row_ind[it.index()];
130  if (ir > 0 && (*it != T(0))
131  && (!lower_triangular || ir >= jc)) {
132  irn.push_back(ir);
133  jcn.push_back(jc);
134  a.push_back(*it);
135  }
136  }
137  }
138  }
139  }
140 
141  template <typename L>
142  ij_sparse_matrix(const L& A, bool lower_triangular=false,
143  const std::vector<size_type> &rows=no_sel,
144  const std::vector<size_type> &cols=no_sel)
145  { // do not reserve nnz(A) entires in case only a sub-matrix is used
146  //size_type nz = nnz(A);
147  //irn.reserve(nz); jcn.reserve(nz); a.reserve(nz);
148  build_from(A, typename principal_orientation_type
149  <typename linalg_traits<L>::sub_orientation>
150  ::potype(),
151  lower_triangular, rows, cols);
152  }
153  };
154 
155  template<typename T> const std::vector<size_type>
156  ij_sparse_matrix<T>::no_sel= std::vector<size_type>();
157 
158 
159  /* ********************************************************************* */
160  /* MUMPS solve interface */
161  /* ********************************************************************* */
162 
163  template <typename T> struct mumps_interf {};
164 
165  template <> struct mumps_interf<float> {
166  typedef SMUMPS_STRUC_C MUMPS_STRUC_C;
167  typedef float value_type;
168 
169  static void mumps_c(MUMPS_STRUC_C &id) { smumps_c(&id); }
170  };
171 
172  template <> struct mumps_interf<double> {
173  typedef DMUMPS_STRUC_C MUMPS_STRUC_C;
174  typedef double value_type;
175  static void mumps_c(MUMPS_STRUC_C &id) { dmumps_c(&id); }
176  };
177 
178  template <> struct mumps_interf<std::complex<float> > {
179  typedef CMUMPS_STRUC_C MUMPS_STRUC_C;
180  typedef mumps_complex value_type;
181  static void mumps_c(MUMPS_STRUC_C &id) { cmumps_c(&id); }
182  };
183 
184  template <> struct mumps_interf<std::complex<double> > {
185  typedef ZMUMPS_STRUC_C MUMPS_STRUC_C;
186  typedef mumps_double_complex value_type;
187  static void mumps_c(MUMPS_STRUC_C &id) { zmumps_c(&id); }
188  };
189 
190 
191  static inline bool
192  mumps_error_check(int INFO1, int INFO2) {
193  if (INFO1 < 0) {
194  switch (INFO1) {
195  case -2:
196  GMM_ASSERT1(false, "Solve with MUMPS failed: NZ = " << INFO2
197  << " is out of range");
198  break;
199  case -6 : case -10 :
200  GMM_WARNING1("Solve with MUMPS failed: matrix is singular");
201  return false;
202  case -9:
203  GMM_ASSERT1(false, "Solve with MUMPS failed: error "
204  << INFO1 << ", increase ICNTL(14)");
205  break;
206  case -13 :
207  GMM_ASSERT1(false, "Solve with MUMPS failed: not enough memory");
208  break;
209  default :
210  GMM_ASSERT1(false, "Solve with MUMPS failed with error "
211  << INFO1);
212  break;
213  }
214  }
215  return true;
216  }
217 
218  template <typename MUMPS_STRUCT>
219  [[deprecated("Use updated error handling mechanism")]]
220  static inline bool mumps_error_check(MUMPS_STRUCT &id) {
221  return mumps_error_check(id.info[0], id.info[1]);
222  }
223 
224 
225  /**
226  * The MUMPS interface is controlled by the JOB parameter.
227  *
228  * The following values are possible:
229  * - JOB= 1: performs the analysis phase.
230  * - JOB= 2: performs the factorization phase.
231  * - JOB= 3: computes the solution.
232  * - JOB= 4: combines the actions of JOB= 1 with those of JOB= 2.
233  * - JOB= 5: combines the actions of JOB=2 and JOB= 3.
234  * - JOB= 6: combines the actions of calls with JOB= 1, JOB= 2, and JOB= 3.
235  * - JOB= 7: save / restore feature: saves MUMPS internal data to disk.
236  * - JOB= 8: save / restore feature: restores MUMPS internal data from disk.
237  * - JOB= 9: computes before the solution phase a possible distribution for
238  * the right-hand sides.
239  * - JOB=-1: initializes an instance of the package.
240  * - JOB=-2: terminates an instance of the package.
241  * - JOB=-3: save / restore feature: removes data saved to disk.
242  * - JOB=-4: after factorization or solve phases, frees all MUMPS internal
243  * data structures except the ones from analysis.
244  *
245  * MUMPS structure parameter and information vector sizes
246  * ICNTL(60) CNTL(15) INFO(80) INFOG(80) RINFO(20) RINFOG(20)
247  */
248 
249  /** MUMPS context
250 
251  This class encapsulates the context of a MUMPS computation. It allocates a
252  a copy of the system matrix and vector (used both for right-hand sides and
253  solutions) and stores the MUMPS internal data structure.
254 
255  The symmetry option, 0 for unsymmetric (default), 1 for symmetric positive
256  definite, and 2 for general symmetric, is passed to the constructor and
257  cannot be changed later.
258 
259  To solve a linear system in one step, the "analyze_factorize_and_solve"
260  function has to be used. Calling just "solve" will not run the analysis
261  and factorization phases.
262 
263  */
264  template <typename T>
265  class mumps_context {
266  typedef typename mumps_interf<T>::value_type MUMPS_T;
267  typedef typename number_traits<T>::magnitude_type MUMPS_R;
268  typedef typename mumps_interf<T>::MUMPS_STRUC_C MUMPS_STRUC;
269  MUMPS_STRUC id;
270  std::unique_ptr<ij_sparse_matrix<T> > pK;
271  std::vector<T> rhs_or_sol;
272  int rank; // MPI rank
273  int nrows_;
274 
275  public:
276 
277  inline void run_job(int job) {
278  id.job = job;
279  mumps_interf<T>::mumps_c(id);
280  }
281 
282  mumps_context(int sym=0) : id(), rank(0), nrows_(0) {
283 #ifdef GMM_USES_MPI
284  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
285 #endif
286  id.job = -1;
287  id.par = 1;
288  id.sym = sym;
289  id.comm_fortran = -987654; // USE_COMM_WORLD
290  run_job(-1); // JOB_INIT
291  }
292 
293  ~mumps_context() { run_job(-2); } // JOB_END
294 
295  inline int nrows() const { return nrows_; }
296  inline int &ICNTL(int I) { return id.icntl[I-1]; }
297  inline MUMPS_R &CNTL(int I) { return id.cntl[I-1]; }
298  inline const int &INFO(int I) { return id.info[I-1]; }
299  inline const int &INFOG(int I) { return id.infog[I-1]; }
300  inline const MUMPS_R &RINFO(int I) { return id.rinfo[I-1]; }
301  inline const MUMPS_R &RINFOG(int I) { return id.rinfog[I-1]; }
302  //inline int MPI_rank() const { return rank; }
303 
304  // Row/column index vectors are expected to be 0-based, the conversion to
305  // 1-based indexing, required by MUMPS, is done inside ij_sparse_matrix
306  template <typename MAT>
307  inline void set_matrix(const MAT &K, bool distributed,
308  const std::vector<size_type> &
309  rows=ij_sparse_matrix<T>::no_sel,
310  const std::vector<size_type> &
311  cols=ij_sparse_matrix<T>::no_sel) {
312  static_assert(std::is_same<typename linalg_traits<MAT>::value_type,
313  T>::value,
314  "value_type of MAT and T must be the same");
315  GMM_ASSERT2(gmm::mat_nrows(K) == gmm::mat_ncols(K), "Non-square matrix");
316  nrows_ = int(gmm::mat_nrows(K));
317  if (!distributed && rank != 0)
318  return;
319  id.n = nrows_;
320  pK = std::make_unique< ij_sparse_matrix<T> >(K, id.sym > 0, rows, cols);
321  if (distributed) {
322  id.nz_loc = int(pK->irn.size());
323  id.irn_loc = &(pK->irn[0]);
324  id.jcn_loc = &(pK->jcn[0]);
325  id.a_loc = (MUMPS_T*)(&(pK->a[0]));
326  } else {
327  id.nz = int(pK->irn.size());
328  id.irn = &(pK->irn[0]);
329  id.jcn = &(pK->jcn[0]);
330  id.a = (MUMPS_T *)(&(pK->a[0]));
331  }
332  }
333 
334  template <typename VEC>
335  inline void set_vector(const VEC &rhs) {
336  static_assert(std::is_same<typename linalg_traits<VEC>::value_type,
337  T>::value,
338  "value_type of MAT and T must be the same");
339  GMM_ASSERT2(nrows() > 0,
340  "System size not defined, need to call set_matrix first.");
341  const int nrhs = int(gmm::vect_size(rhs)/nrows());
342  GMM_ASSERT2(size_type(nrhs*nrows()) == gmm::vect_size(rhs),
343  "Size of rhs (" << gmm::vect_size(rhs) << ") must be an "
344  "integer multiple of the matrix size (" << id.n << ")");
345  rhs_or_sol.resize(gmm::vect_size(rhs));
346  gmm::copy(rhs, rhs_or_sol);
347  if (rank == 0) {
348  id.nrhs = nrhs;
349  id.rhs = (MUMPS_T*)(&rhs_or_sol[0]);
350  id.lrhs = id.n;
351  }
352  }
353 
354  const std::vector<T> &vector() const { return rhs_or_sol; }
355 
356  inline void analyze() { run_job(1); }
357  inline void factorize() { run_job(2); }
358  inline void analyze_and_factorize() { run_job(4); }
359  inline void solve() { run_job(3); }
360  inline void factorize_and_solve() { run_job(5); }
361  inline void analyze_factorize_and_solve() { run_job(6); }
362  inline bool error_check() { return mumps_error_check(INFO(1), INFO(2)); }
363 
364  inline void mpi_broadcast() {
365 #ifdef GMM_USES_MPI
366  MPI_Bcast(&(rhs_or_sol[0]), int(rhs_or_sol.size()),
367  gmm::mpi_type(T()), 0, MPI_COMM_WORLD);
368 #endif
369  }
370  };
371 
372 
373  /** MUMPS solve interface
374  * Works only with sparse or skyline matrices
375  */
376  template <typename MAT, typename VECTX, typename VECTB>
377  bool MUMPS_solve(const MAT &A, VECTX &X, const VECTB &B,
378  bool sym = false, bool distributed = false) {
379 
380  typedef typename linalg_traits<MAT>::value_type T;
381 
382  bool ok=false;
383  {
384  mumps_context<T> mumps_ctx(sym ? 2 : 0); // General symmetric (2) or unsymmetric (0)
385  mumps_ctx.set_matrix(A, distributed);
386  mumps_ctx.set_vector(B);
387  mumps_ctx.ICNTL(1) = -1; // output stream for error messages
388  mumps_ctx.ICNTL(2) = -1; // output stream for other messages
389  mumps_ctx.ICNTL(3) = -1; // output stream for global information
390  mumps_ctx.ICNTL(4) = 0; // verbosity level
391  if (distributed)
392  mumps_ctx.ICNTL(5) = 0; // assembled input matrix (default)
393  mumps_ctx.ICNTL(14) += 80; // small boost to the workspace size as we have encountered
394  // some problems that did not fit in the default settings of
395  // mumps... ICNTL(14) = 15 or 20
396  if (distributed)
397  mumps_ctx.ICNTL(18) = 3; // strategy for distributed input matrix
398  //mumps_ctx.ICNTL(22) = 1; // enables out-of-core support
399  mumps_ctx.analyze_factorize_and_solve();
400  ok = mumps_ctx.error_check();
401  mumps_ctx.mpi_broadcast();
402  gmm::copy(mumps_ctx.vector(), X);
403  } // end scope of mumps_ctx, destructor calls mumps job=-2
404  return ok;
405  }
406 
407 
408  /** MUMPS solve interface for distributed matrices
409  * Works only with sparse or skyline matrices
410  */
411  template <typename MAT, typename VECTX, typename VECTB>
412  bool MUMPS_distributed_matrix_solve(const MAT &A, VECTX &X_,
413  const VECTB &B, bool sym=false) {
414  return MUMPS_solve(A, X_, B, sym, true);
415  }
416 
417 
418  template<typename T>
419  inline T real_or_complex(std::complex<T> a) { return a.real(); }
420  template<typename T>
421  inline T real_or_complex(T &a) { return a; }
422 
423 
424  /** Evaluate matrix determinant with MUMPS
425  * Works only with sparse or skyline matrices
426  */
427  template <typename MAT, typename T=typename linalg_traits<MAT>::value_type>
428  T MUMPS_determinant(const MAT &A, int &exponent,
429  bool sym=false, bool distributed=false) {
430  exponent = 0;
431  typedef typename number_traits<T>::magnitude_type R;
432 
433  mumps_context<T> mumps_ctx(sym ? 2 : 0); // General symmetric (2)
434  mumps_ctx.set_matrix(A, distributed); // or unsymmetric (0)
435  mumps_ctx.ICNTL(4) = 0; // verbosity level
436  if (distributed)
437  mumps_ctx.ICNTL(5) = 0; // assembled input matrix (default)
438  mumps_ctx.ICNTL(14) += 80; // small boost to the workspace size
439  if (distributed)
440  mumps_ctx.ICNTL(18) = 3; // strategy for distributed input matrix
441  mumps_ctx.ICNTL(31) = 1; // only factorization, no solution to follow
442  mumps_ctx.ICNTL(33) = 1; // request determinant calculation
443 
444  mumps_ctx.analyze_and_factorize();
445 
446  T det = real_or_complex(std::complex<R>(mumps_ctx.RINFOG(12),
447  mumps_ctx.RINFOG(13)));
448  exponent = mumps_ctx.INFOG(34);
449  return det;
450  }
451 
452 }
453 
454 #endif // GMM_MUMPS_INTERFACE_H
455 
456 #endif // GMM_USES_MUMPS
void copy(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:976
Include the base gmm files.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48