libMesh
sparse_matrix.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // Local Includes
21 #include "libmesh/sparse_matrix.h"
22 
23 // libMesh includes
24 #include "libmesh/dof_map.h"
25 #include "libmesh/dense_matrix.h"
26 #include "libmesh/diagonal_matrix.h"
27 #include "libmesh/laspack_matrix.h"
28 #include "libmesh/libmesh_logging.h"
29 #include "libmesh/eigen_sparse_matrix.h"
30 #include "libmesh/parallel.h"
31 #include "libmesh/petsc_matrix.h"
32 #include "libmesh/trilinos_epetra_matrix.h"
33 #include "libmesh/numeric_vector.h"
34 #include "libmesh/enum_solver_package.h"
35 
36 
37 // gzstream for reading compressed files as a stream
38 #ifdef LIBMESH_HAVE_GZSTREAM
39 # include "gzstream.h"
40 #endif
41 
42 #ifdef LIBMESH_HAVE_HDF5
43 // Sticking to the C API to be safest on portability
44 # include "hdf5.h"
45 #endif
46 
47 // C++ includes
48 #include <memory>
49 #include <fstream>
50 
51 #ifdef LIBMESH_HAVE_CXX11_REGEX
52 #include <regex>
53 #endif
54 
55 
56 namespace libMesh
57 {
58 
59 
60 //------------------------------------------------------------------
61 // SparseMatrix Methods
62 
63 
64 // Constructor
65 template <typename T>
67  ParallelObject(comm_in),
68  _dof_map(nullptr),
69  _sp(nullptr),
70  _is_initialized(false),
71  _use_hash_table(false)
72 {}
73 
74 
75 
76 template <typename T>
78 {
79  _dof_map = &dof_map;
80  if (!_sp)
81  _sp = dof_map.get_sparsity_pattern();
82 }
83 
84 
85 
86 template <typename T>
88 {
89  _sp = &sp;
90 }
91 
92 
93 
94 // default implementation is to fall back to non-blocked method
95 template <typename T>
97  const std::vector<numeric_index_type> & brows,
98  const std::vector<numeric_index_type> & bcols)
99 {
100  libmesh_assert_equal_to (dm.m() / brows.size(), dm.n() / bcols.size());
101 
102  const numeric_index_type blocksize = cast_int<numeric_index_type>
103  (dm.m() / brows.size());
104 
105  libmesh_assert_equal_to (dm.m()%blocksize, 0);
106  libmesh_assert_equal_to (dm.n()%blocksize, 0);
107 
108  std::vector<numeric_index_type> rows, cols;
109 
110  rows.reserve(blocksize*brows.size());
111  cols.reserve(blocksize*bcols.size());
112 
113  for (auto & row : brows)
114  {
115  numeric_index_type i = row * blocksize;
116 
117  for (unsigned int v=0; v<blocksize; v++)
118  rows.push_back(i++);
119  }
120 
121  for (auto & col : bcols)
122  {
123  numeric_index_type j = col * blocksize;
124 
125  for (unsigned int v=0; v<blocksize; v++)
126  cols.push_back(j++);
127  }
128 
129  this->add_matrix (dm, rows, cols);
130 }
131 
132 
133 
134 // Full specialization of print method for Complex datatypes
135 template <>
136 void SparseMatrix<Complex>::print(std::ostream & os, const bool sparse) const
137 {
138  // std::complex<>::operator<<() is defined, but use this form
139 
140  if (sparse)
141  {
142  libmesh_not_implemented();
143  }
144 
145  os << "Real part:" << std::endl;
146  for (auto i : make_range(this->m()))
147  {
148  for (auto j : make_range(this->n()))
149  os << std::setw(8) << (*this)(i,j).real() << " ";
150  os << std::endl;
151  }
152 
153  os << std::endl << "Imaginary part:" << std::endl;
154  for (auto i : make_range(this->m()))
155  {
156  for (auto j : make_range(this->n()))
157  os << std::setw(8) << (*this)(i,j).imag() << " ";
158  os << std::endl;
159  }
160 }
161 
162 
163 
164 
165 
166 
167 // Full specialization for Real datatypes
168 template <typename T>
169 std::unique_ptr<SparseMatrix<T>>
171  const SolverPackage solver_package,
172  const MatrixBuildType matrix_build_type /* = AUTOMATIC */)
173 {
174  // Avoid unused parameter warnings when no solver packages are enabled.
175  libmesh_ignore(comm);
176 
177  if (matrix_build_type == MatrixBuildType::DIAGONAL)
178  return std::make_unique<DiagonalMatrix<T>>(comm);
179 
180  // Build the appropriate vector
181  switch (solver_package)
182  {
183 
184 #ifdef LIBMESH_HAVE_LASPACK
185  case LASPACK_SOLVERS:
186  return std::make_unique<LaspackMatrix<T>>(comm);
187 #endif
188 
189 
190 #ifdef LIBMESH_HAVE_PETSC
191  case PETSC_SOLVERS:
192  return std::make_unique<PetscMatrix<T>>(comm);
193 #endif
194 
195 
196 #ifdef LIBMESH_TRILINOS_HAVE_EPETRA
197  case TRILINOS_SOLVERS:
198  return std::make_unique<EpetraMatrix<T>>(comm);
199 #endif
200 
201 
202 #ifdef LIBMESH_HAVE_EIGEN
203  case EIGEN_SOLVERS:
204  return std::make_unique<EigenSparseMatrix<T>>(comm);
205 #endif
206 
207  default:
208  libmesh_error_msg("ERROR: Unrecognized solver package: " << solver_package);
209  }
210 }
211 
212 
213 template <typename T>
215  const NumericVector<T> & arg) const
216 {
217  dest.zero();
218  this->vector_mult_add(dest,arg);
219 }
220 
221 
222 
223 template <typename T>
225  const NumericVector<T> & arg) const
226 {
227  /* This functionality is actually implemented in the \p
228  NumericVector class. */
229  dest.add_vector(arg,*this);
230 }
231 
232 
233 
234 template <typename T>
235 void SparseMatrix<T>::zero_rows (std::vector<numeric_index_type> &, T)
236 {
237  /* This functionality isn't implemented or stubbed in every subclass yet */
238  libmesh_not_implemented();
239 }
240 
241 
242 template <typename T>
243 std::size_t SparseMatrix<T>::n_nonzeros() const
244 {
245  if (!_sp)
246  return 0;
247  return _sp->n_nonzeros();
248 }
249 
250 
251 template <typename T>
252 void SparseMatrix<T>::print(std::ostream & os, const bool sparse) const
253 {
254  parallel_object_only();
255 
256  libmesh_assert (this->initialized());
257 
258  const numeric_index_type first_dof = this->row_start(),
259  end_dof = this->row_stop();
260 
261  // We'll print the matrix from processor 0 to make sure
262  // it's serialized properly
263  if (this->processor_id() == 0)
264  {
265  libmesh_assert_equal_to (first_dof, 0);
266  for (numeric_index_type i : make_range(end_dof))
267  {
268  if (sparse)
269  {
270  for (auto j : make_range(this->n()))
271  {
272  T c = (*this)(i,j);
273  if (c != static_cast<T>(0.0))
274  {
275  os << i << " " << j << " " << c << std::endl;
276  }
277  }
278  }
279  else
280  {
281  for (auto j : make_range(this->n()))
282  os << (*this)(i,j) << " ";
283  os << std::endl;
284  }
285  }
286 
287  std::vector<numeric_index_type> ibuf, jbuf;
288  std::vector<T> cbuf;
289  numeric_index_type currenti = end_dof;
290  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
291  {
292  this->comm().receive(p, ibuf);
293  this->comm().receive(p, jbuf);
294  this->comm().receive(p, cbuf);
295  libmesh_assert_equal_to (ibuf.size(), jbuf.size());
296  libmesh_assert_equal_to (ibuf.size(), cbuf.size());
297 
298  if (ibuf.empty())
299  continue;
300  libmesh_assert_greater_equal (ibuf.front(), currenti);
301  libmesh_assert_greater_equal (ibuf.back(), ibuf.front());
302 
303  std::size_t currentb = 0;
304  for (;currenti <= ibuf.back(); ++currenti)
305  {
306  if (sparse)
307  {
308  for (numeric_index_type j=0; j<this->n(); j++)
309  {
310  if (currentb < ibuf.size() &&
311  ibuf[currentb] == currenti &&
312  jbuf[currentb] == j)
313  {
314  os << currenti << " " << j << " " << cbuf[currentb] << std::endl;
315  currentb++;
316  }
317  }
318  }
319  else
320  {
321  for (auto j : make_range(this->n()))
322  {
323  if (currentb < ibuf.size() &&
324  ibuf[currentb] == currenti &&
325  jbuf[currentb] == j)
326  {
327  os << cbuf[currentb] << " ";
328  currentb++;
329  }
330  else
331  os << static_cast<T>(0.0) << " ";
332  }
333  os << std::endl;
334  }
335  }
336  }
337  if (!sparse)
338  {
339  for (; currenti != this->m(); ++currenti)
340  {
341  for (numeric_index_type j=0; j<this->n(); j++)
342  os << static_cast<T>(0.0) << " ";
343  os << std::endl;
344  }
345  }
346  }
347  else
348  {
349  std::vector<numeric_index_type> ibuf, jbuf;
350  std::vector<T> cbuf;
351 
352  // We'll assume each processor has access to entire
353  // matrix rows, so (*this)(i,j) is valid if i is a local index.
354  for (numeric_index_type i : make_range(first_dof, end_dof))
355  {
356  for (auto j : make_range(this->n()))
357  {
358  T c = (*this)(i,j);
359  if (c != static_cast<T>(0.0))
360  {
361  ibuf.push_back(i);
362  jbuf.push_back(j);
363  cbuf.push_back(c);
364  }
365  }
366  }
367  this->comm().send(0,ibuf);
368  this->comm().send(0,jbuf);
369  this->comm().send(0,cbuf);
370  }
371 }
372 
373 
374 template <typename T>
375 void SparseMatrix<T>::print_matlab(const std::string & name) const
376 {
377  parallel_object_only();
378 
379  libmesh_assert (this->initialized());
380 
381  const numeric_index_type first_dof = this->row_start(),
382  end_dof = this->row_stop();
383 
384  // We'll print the matrix from processor 0 to make sure
385  // it's serialized properly
386  if (this->processor_id() == 0)
387  {
388  std::unique_ptr<std::ofstream> file;
389 
390  if (name != "")
391  file = std::make_unique<std::ofstream>(name.c_str());
392 
393  std::ostream & os = (name == "") ? libMesh::out : *file;
394 
395  std::size_t sparsity_nonzeros = this->n_nonzeros();
396 
397  std::size_t real_nonzeros = 0;
398 
399  libmesh_assert_equal_to(first_dof, 0);
400  for (numeric_index_type i : make_range(end_dof))
401  {
402  for (auto j : make_range(this->n()))
403  {
404  T c = (*this)(i,j);
405  if (c != static_cast<T>(0.0))
406  ++real_nonzeros;
407  }
408  }
409 
410 
411  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
412  {
413  std::size_t nonzeros_on_p = 0;
414  this->comm().receive(p, nonzeros_on_p);
415  real_nonzeros += nonzeros_on_p;
416  }
417 
418  if (sparsity_nonzeros &&
419  sparsity_nonzeros != real_nonzeros)
420  libmesh_warning(sparsity_nonzeros <<
421  " nonzeros allocated, but " <<
422  real_nonzeros << " used.");
423 
424  // We probably want to be more consistent than that, if our
425  // sparsity is overallocated.
426 
427  // Print a header similar to PETSc's mat_view ascii_matlab
428  os << "%Mat Object: () " << this->n_processors() << " MPI processes\n"
429  << "% type: " << (this->n_processors() > 1 ? "mpi" : "seq") << "aij\n"
430  << "% Size = " << this->m() << ' ' << this->n() << '\n'
431  << "% Nonzeros = " << real_nonzeros << '\n'
432  << "zzz = zeros(" << real_nonzeros << ",3);\n"
433  << "zzz = [\n";
434 
435  for (numeric_index_type i : make_range(end_dof))
436  {
437  // FIXME - we need a base class way to iterate over a
438  // SparseMatrix row.
439  for (auto j : make_range(this->n()))
440  {
441  T c = (*this)(i,j);
442  if (c != static_cast<T>(0.0))
443  {
444  // Convert from 0-based to 1-based indexing
445  os << (i+1) << ' ' << (j+1) << " " << c << '\n';
446  }
447  }
448  }
449 
450  std::vector<numeric_index_type> ibuf, jbuf;
451  std::vector<T> cbuf;
452  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
453  {
454  this->comm().receive(p, ibuf);
455  this->comm().receive(p, jbuf);
456  this->comm().receive(p, cbuf);
457  libmesh_assert_equal_to (ibuf.size(), jbuf.size());
458  libmesh_assert_equal_to (ibuf.size(), cbuf.size());
459 
460  for (auto n : index_range(ibuf))
461  os << ibuf[n] << ' ' << jbuf[n] << " " << cbuf[n] << '\n';
462  }
463 
464  os << "];\n" << "Mat_sparse = spconvert(zzz);" << std::endl;
465  }
466  else
467  {
468  std::vector<numeric_index_type> ibuf, jbuf;
469  std::vector<T> cbuf;
470  std::size_t my_nonzeros = 0;
471 
472  // We'll assume each processor has access to entire
473  // matrix rows, so (*this)(i,j) is valid if i is a local index.
474  for (numeric_index_type i : make_range(first_dof, end_dof))
475  {
476  for (auto j : make_range(this->n()))
477  {
478  T c = (*this)(i,j);
479  if (c != static_cast<T>(0.0))
480  {
481  ibuf.push_back(i);
482  jbuf.push_back(j);
483  cbuf.push_back(c);
484  ++my_nonzeros;
485  }
486  }
487  }
488  this->comm().send(0,my_nonzeros);
489  this->comm().send(0,ibuf);
490  this->comm().send(0,jbuf);
491  this->comm().send(0,cbuf);
492  }
493 }
494 
495 
496 
497 template <typename T>
498 void SparseMatrix<T>::print_petsc_binary(const std::string &)
499 {
500  libmesh_not_implemented_msg
501  ("libMesh cannot write PETSc binary-format files from non-PETSc matrices");
502 }
503 
504 
505 
506 template <typename T>
507 void SparseMatrix<T>::print_petsc_hdf5(const std::string &)
508 {
509  libmesh_not_implemented_msg
510  ("libMesh cannot write PETSc HDF5-format files from non-PETSc matrices");
511 }
512 
513 
514 
515 template <typename T>
516 void SparseMatrix<T>::read(const std::string & filename)
517 {
518  {
519  std::ifstream in (filename.c_str());
520  libmesh_error_msg_if
521  (!in.good(), "ERROR: cannot read file:\n\t" <<
522  filename);
523  }
524 
525  std::string_view basename = Utility::basename_of(filename);
526 
527  const bool gzipped_file = Utility::ends_with(filename, ".gz");
528 
529  if (gzipped_file)
530  basename.remove_suffix(3);
531 
532  if (Utility::ends_with(basename, ".matlab") ||
533  Utility::ends_with(basename, ".m"))
534  this->read_matlab(filename);
535  else if (Utility::ends_with(basename, ".petsc64"))
536  {
537 #ifndef LIBMESH_HAVE_PETSC
538  libmesh_error_msg("Cannot load PETSc matrix file " <<
539  filename << " without PETSc-enabled libMesh.");
540 #endif
541 #if LIBMESH_DOF_ID_BYTES != 8
542  libmesh_error_msg("Cannot load 64-bit PETSc matrix file " <<
543  filename << " with non-64-bit libMesh.");
544 #endif
545  this->read_petsc_binary(filename);
546  }
547  else if (Utility::ends_with(basename, ".petsc32"))
548  {
549 #ifndef LIBMESH_HAVE_PETSC
550  libmesh_error_msg("Cannot load PETSc matrix file " <<
551  filename << " without PETSc-enabled libMesh.");
552 #endif
553 #if LIBMESH_DOF_ID_BYTES != 4
554  libmesh_error_msg("Cannot load 32-bit PETSc matrix file " <<
555  filename << " with non-32-bit libMesh.");
556 #endif
557  this->read_petsc_binary(filename);
558  }
559  else if (Utility::ends_with(basename, ".h5"))
560  {
561  this->read_coreform_hdf5(filename);
562  }
563  else
564  libmesh_error_msg(" ERROR: Unrecognized matrix file extension on: "
565  << basename
566  << "\n I understand the following:\n\n"
567  << " *.h5 -- CoreForm HDF5 sparse matrix format\n"
568  << " *.matlab -- Matlab sparse matrix format\n"
569  << " *.m -- Matlab sparse matrix format\n"
570  << " *.petsc32 -- PETSc binary format, 32-bit\n"
571  << " *.petsc64 -- PETSc binary format, 64-bit\n"
572  );
573 }
574 
575 
576 
577 template <typename T>
578 void SparseMatrix<T>::read_coreform_hdf5(const std::string & filename,
579  const std::string & groupname)
580 {
581 #ifndef LIBMESH_HAVE_HDF5
582  libmesh_ignore(filename, groupname);
583  libmesh_error_msg("ERROR: need HDF5 support to handle .h5 files!!!");
584 #else
585  LOG_SCOPE("read_coreform_hdf5()", "SparseMatrix");
586 
587  std::size_t num_rows, num_cols;
588 
589  hid_t group;
590 
591  auto check_open = [&filename](hid_t id, const std::string & objname)
592  {
593  if (id == H5I_INVALID_HID)
594  libmesh_error_msg("Couldn't open " + objname + " in " + filename);
595  };
596 
597  auto check_hdf5 = [&filename](auto hdf5val, const std::string & objname)
598  {
599  if (hdf5val < 0)
600  libmesh_error_msg("HDF5 error from " + objname + " in " + filename);
601  };
602 
603 
604  if (this->processor_id() == 0)
605  {
606  const hid_t file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
607 
608  if (file == H5I_INVALID_HID)
609  libmesh_file_error(filename);
610 
611  group = H5Gopen(file, groupname.c_str(), H5P_DEFAULT);
612  check_open(group, groupname);
613 
614  auto read_size_attribute = [&filename, &group, &check_open, &check_hdf5]
615  (const std::string & attribute_name)
616  {
617  unsigned long long returnval = 0;
618 
619  const hid_t attr = H5Aopen(group, attribute_name.c_str(), H5P_DEFAULT);
620  check_open(attr, attribute_name);
621 
622  const hid_t attr_type = H5Aget_type(attr);
623  check_hdf5(attr_type, attribute_name + " type");
624 
625  // HDF5 will convert between the file's integer type and ours, but
626  // we do expect an integer type.
627  if (H5Tget_class(attr_type) != H5T_INTEGER)
628  libmesh_error_msg("Non-integer type for " + attribute_name + " in " + filename);
629 
630  H5Tclose(attr_type);
631 
632  // HDF5 is supposed to handle both upscaling and endianness
633  // conversions here
634  herr_t errval = H5Aread(attr, H5T_NATIVE_ULLONG, &returnval);
635  check_hdf5(errval, attribute_name + " read");
636 
637  H5Aclose(attr);
638 
639  return returnval;
640  };
641 
642  num_cols = read_size_attribute("num_cols");
643  num_rows = read_size_attribute("num_rows");
644 
645  this->comm().broadcast(num_cols);
646  this->comm().broadcast(num_rows);
647  }
648  else
649  {
650  this->comm().broadcast(num_cols);
651  this->comm().broadcast(num_rows);
652  }
653 
654  numeric_index_type new_row_start, new_row_stop,
655  new_col_start, new_col_stop;
656 
657  // If we need to reinit, we need to determine which rows+columns
658  // each processor is in charge of.
659  std::vector<numeric_index_type> new_row_starts, new_row_stops,
660  new_col_starts, new_col_stops;
661 
662  if (this->initialized() &&
663  num_cols == this->m() &&
664  num_rows == this->n())
665  {
666  new_row_start = this->row_start(),
667  new_row_stop = this->row_stop();
668 
669  new_col_start = this->col_start(),
670  new_col_stop = this->col_stop();
671  }
672  else
673  {
674  // Determine which rows/columns each processor will be in charge of
675  new_row_start = this->processor_id() * num_rows / this->n_processors(),
676  new_row_stop = (this->processor_id()+1) * num_rows / this->n_processors();
677 
678  new_col_start = this->processor_id() * num_cols / this->n_processors(),
679  new_col_stop = (this->processor_id()+1) * num_cols / this->n_processors();
680  }
681 
682  this->comm().gather(0, new_row_start, new_row_starts);
683  this->comm().gather(0, new_row_stop, new_row_stops);
684  this->comm().gather(0, new_col_start, new_col_starts);
685  this->comm().gather(0, new_col_stop, new_col_stops);
686 
687  numeric_index_type on_diagonal_nonzeros = 0,
688  off_diagonal_nonzeros = 0;
689 
690  std::vector<std::size_t> cols, row_offsets;
691  std::vector<double> vals;
692 
693  if (this->processor_id() == 0)
694  {
695  auto read_vector = [&filename, &group, &check_open, &check_hdf5]
696  (const std::string & dataname, auto hdf5_class,
697  auto hdf5_type, auto & datavec)
698  {
699  const hid_t data = H5Dopen1(group, dataname.c_str());
700  check_open(data, dataname.c_str());
701 
702  const hid_t data_type = H5Dget_type(data);
703  check_hdf5(data_type, dataname + " type");
704 
705  // HDF5 will convert between the file's integer type and ours, but
706  // we do expect an integer type.
707  if (H5Tget_class(data_type) != hdf5_class)
708  libmesh_error_msg("Unexpected type for " + dataname + " in " + filename);
709 
710  H5Tclose(data_type);
711 
712  const hid_t dataspace = H5Dget_space(data);
713  check_hdf5(dataspace, dataname + " space");
714 
715  int ndims = H5Sget_simple_extent_ndims(dataspace);
716  if (ndims != 1)
717  libmesh_error_msg("Non-vector space for " + dataname + " in " + filename);
718 
719  hsize_t len, maxlen;
720  herr_t errval = H5Sget_simple_extent_dims(dataspace, &len, &maxlen);
721  check_hdf5(errval, dataname + " dims");
722 
723  datavec.resize(len);
724 
725  errval = H5Dread(data, hdf5_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, datavec.data());
726  check_hdf5(errval, dataname + " read");
727  };
728 
729  read_vector("cols", H5T_INTEGER, H5T_NATIVE_ULLONG, cols);
730  read_vector("row_offsets", H5T_INTEGER, H5T_NATIVE_ULLONG, row_offsets);
731  read_vector("vals", H5T_FLOAT, H5T_NATIVE_DOUBLE, vals);
732 
733  if (cols.size() != vals.size())
734  libmesh_error_msg("Inconsistent cols/vals sizes in " + filename);
735 
736  if (row_offsets.size() != num_rows + 1)
737  libmesh_error_msg("Inconsistent row_offsets size in " + filename);
738 
739  // Data for the row we're working on
740  numeric_index_type current_row = 0;
741  processor_id_type current_proc = 0;
742  numeric_index_type current_on_diagonal_nonzeros = 0;
743  numeric_index_type current_off_diagonal_nonzeros = 0;
744  if (row_offsets[0] != 0)
745  libmesh_error_msg("Unexpected row_offsets[0] in " + filename);
746 
747  for (auto i : index_range(cols))
748  {
749  while (row_offsets[current_row+1] <= i)
750  {
751  ++current_row;
752  if (row_offsets[current_row] < row_offsets[current_row-1])
753  libmesh_error_msg("Non-monotonic row_offsets in " + filename);
754  current_on_diagonal_nonzeros = 0;
755  current_off_diagonal_nonzeros = 0;
756  }
757 
758  while (current_row >= (new_row_stops[current_proc]+1))
759  ++current_proc;
760 
761  // 0-based indexing in file
762  if (cols[i] >= new_col_starts[current_proc] &&
763  cols[i] < new_col_stops[current_proc])
764  {
765  ++current_on_diagonal_nonzeros;
766  on_diagonal_nonzeros =
767  std::max(on_diagonal_nonzeros,
768  current_on_diagonal_nonzeros);
769  }
770  else
771  {
772  ++current_off_diagonal_nonzeros;
773  off_diagonal_nonzeros =
774  std::max(off_diagonal_nonzeros,
775  current_off_diagonal_nonzeros);
776  }
777  }
778  }
779 
780  this->comm().broadcast(on_diagonal_nonzeros);
781  this->comm().broadcast(off_diagonal_nonzeros);
782 
783  this->init(num_rows, num_cols,
784  new_row_stop-new_row_start,
785  new_col_stop-new_col_start,
786  on_diagonal_nonzeros,
787  off_diagonal_nonzeros);
788 
789  // Set the matrix values last.
790  if (this->processor_id() == 0)
791  {
792  numeric_index_type current_row = 0;
793  for (auto i : index_range(cols))
794  {
795  while (row_offsets[current_row+1] <= i)
796  {
797  ++current_row;
798  libmesh_assert_greater_equal (row_offsets[current_row],
799  row_offsets[current_row-1]);
800  }
801  this->set(current_row, cols[i], vals[i]);
802  }
803  }
804 
805  this->close();
806 #endif // LIBMESH_HAVE_HDF5
807 }
808 
809 
810 
811 template <typename T>
812 void SparseMatrix<T>::read_matlab(const std::string & filename)
813 {
814  LOG_SCOPE("read_matlab()", "SparseMatrix");
815 
816 #ifndef LIBMESH_HAVE_CXX11_REGEX
817  libmesh_not_implemented(); // What is your compiler?!? Email us!
818  libmesh_ignore(filename);
819 #else
820  parallel_object_only();
821 
822  const bool gzipped_file = Utility::ends_with(filename, ".gz");
823 
824  // The sizes we get from the file
825  std::size_t m = 0,
826  n = 0;
827 
828  // If we don't already have this size, we'll need to reinit, and
829  // determine which rows+columns each processor is in charge of.
830  std::vector<numeric_index_type> new_row_starts, new_row_stops,
831  new_col_starts, new_col_stops;
832 
833  numeric_index_type new_row_start, new_row_stop,
834  new_col_start, new_col_stop;
835 
836  // We'll read through the file three times: once to get a reliable
837  // value for the matrix size (so we can divvy it up among
838  // processors), then again to get the sparsity to send to each
839  // processor, then a final time to get the entries to send to each
840  // processor.
841  //
842  // We'll use an istream here; it might be an ifstream if we're
843  // opening a raw ASCII file or a gzstream if we're opening a
844  // compressed one.
845  std::unique_ptr<std::istream> file;
846 
847  // We'll need a temporary structure to cache matrix entries, because
848  // we need to read through the whole file before we know the size
849  // and sparsity structure with which we can init().
850  //
851  // Reading through the file three times via `seekg` doesn't work
852  // with our gzstream wrapper, and seems to take three times as long
853  // even with a plain ifstream. What happened to disk caching!?
854  std::vector<std::tuple<numeric_index_type, numeric_index_type, T>> entries;
855 
856  // First read through the file, saving size and entry data
857  {
858  // We'll read the matrix on processor 0 rather than try to juggle
859  // parallel I/O.
860  if (this->processor_id() == 0)
861  {
862  // We'll be using regular expressions to make ourselves slightly
863  // more robust to formatting.
864  const std::regex start_regex // assignment like "zzz = ["
865  ("\\s*\\w+\\s*=\\s*\\[");
866  const std::regex end_regex // end of assignment
867  ("^[^%]*\\]");
868 
869  if (gzipped_file)
870  {
871 #ifdef LIBMESH_HAVE_GZSTREAM
872  auto inf = std::make_unique<igzstream>();
873  libmesh_assert(inf);
874  inf->open(filename.c_str(), std::ios::in);
875  file = std::move(inf);
876 #else
877  libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
878 #endif
879  }
880  else
881  {
882  auto inf = std::make_unique<std::ifstream>();
883  libmesh_assert(inf);
884 
885  std::string new_name = Utility::unzip_file(filename);
886 
887  inf->open(new_name.c_str(), std::ios::in);
888  file = std::move(inf);
889  }
890 
891  // If we have a matrix with all-zero trailing rows, the only
892  // way to get the size is if it ended up in a comment
893  const std::regex size_regex // comment like "% size = 8 8"
894  ("%\\s*[Ss][Ii][Zz][Ee]\\s*=\\s*(\\d+)\\s+(\\d+)");
895  const std::string whitespace = " \t";
896 
897  bool have_started = false;
898  bool have_ended = false;
899  std::size_t largest_i_seen = 0, largest_j_seen = 0;
900 
901  // Data for the row we're working on
902  // Use 1-based indexing for current_row, as in the file
903  std::size_t current_row = 1;
904 
905  for (std::string line; std::getline(*file, line);)
906  {
907  std::smatch sm;
908 
909  // First, try to match an entry. This is the most common
910  // case so we won't rely on slow std::regex for it.
911  // stringstream is at least an improvement over that.
912 
913  // Look for row/col/val like "1 1 -2.0e-4"
914 
915  std::istringstream l(line);
916 
917  std::size_t i, j;
918  T value;
919 
920  l >> i >> j >> value;
921 
922  if (!l.fail())
923  {
924  libmesh_error_msg_if
925  (!have_started, "Confused by premature entries in matrix file " << filename);
926 
927  entries.emplace_back(cast_int<numeric_index_type>(i),
928  cast_int<numeric_index_type>(j),
929  value);
930 
931  libmesh_error_msg_if
932  (!i || !j, "Expected 1-based indexing in matrix file "
933  << filename);
934 
935  current_row = std::max(current_row, i);
936 
937  libmesh_error_msg_if
938  (i < current_row,
939  "Can't handle out-of-order entries in matrix file "
940  << filename);
941 
942  largest_i_seen = std::max(i, largest_i_seen);
943  largest_j_seen = std::max(j, largest_j_seen);
944  }
945 
946  else if (std::regex_search(line, sm, size_regex))
947  {
948  const std::string msize = sm[1];
949  const std::string nsize = sm[2];
950  m = std::stoull(msize);
951  n = std::stoull(nsize);
952  }
953 
954  else if (std::regex_search(line, start_regex))
955  have_started = true;
956 
957  else if (std::regex_search(line, end_regex))
958  {
959  have_ended = true;
960  break;
961  }
962  }
963 
964  libmesh_error_msg_if
965  (!have_started, "Confused by missing assignment beginning in matrix file " << filename);
966 
967  libmesh_error_msg_if
968  (!have_ended, "Confused by missing assignment ending in matrix file " << filename);
969 
970  libmesh_error_msg_if
971  (m > largest_i_seen, "Confused by missing final row(s) in matrix file " << filename);
972 
973  libmesh_error_msg_if
974  (m > 0 && m < largest_i_seen, "Confused by extra final row(s) in matrix file " << filename);
975 
976  if (!m)
977  m = largest_i_seen;
978 
979  libmesh_error_msg_if
980  (n > largest_j_seen, "Confused by missing final column(s) in matrix file " << filename);
981 
982  libmesh_error_msg_if
983  (n > 0 && n < largest_j_seen, "Confused by extra final column(s) in matrix file " << filename);
984 
985  if (!n)
986  n = largest_j_seen;
987 
988  this->comm().broadcast(m);
989  this->comm().broadcast(n);
990  }
991  else
992  {
993  this->comm().broadcast(m);
994  this->comm().broadcast(n);
995  }
996 
997  if (this->initialized() &&
998  m == this->m() &&
999  n == this->n())
1000  {
1001  new_row_start = this->row_start(),
1002  new_row_stop = this->row_stop();
1003 
1004  new_col_start = this->col_start(),
1005  new_col_stop = this->col_stop();
1006  }
1007  else
1008  {
1009  // Determine which rows/columns each processor will be in charge of
1010  new_row_start = this->processor_id() * m / this->n_processors(),
1011  new_row_stop = (this->processor_id()+1) * m / this->n_processors();
1012 
1013  new_col_start = this->processor_id() * n / this->n_processors(),
1014  new_col_stop = (this->processor_id()+1) * n / this->n_processors();
1015  }
1016 
1017  this->comm().gather(0, new_row_start, new_row_starts);
1018  this->comm().gather(0, new_row_stop, new_row_stops);
1019  this->comm().gather(0, new_col_start, new_col_starts);
1020  this->comm().gather(0, new_col_stop, new_col_stops);
1021 
1022  } // Done reading entry data and broadcasting matrix size
1023 
1024  // Calculate the matrix sparsity and initialize it second
1025  {
1026  // Deduce the sparsity pattern, or at least the maximum number of
1027  // on- and off- diagonal non-zeros per row.
1028  numeric_index_type on_diagonal_nonzeros =0,
1029  off_diagonal_nonzeros =0;
1030 
1031  if (this->processor_id() == 0)
1032  {
1033  // Data for the row we're working on
1034  // Use 1-based indexing for current_row, as in the file
1035  numeric_index_type current_row = 1;
1036  processor_id_type current_proc = 0;
1037  numeric_index_type current_on_diagonal_nonzeros = 0;
1038  numeric_index_type current_off_diagonal_nonzeros = 0;
1039 
1040  for (auto [i, j, value] : entries)
1041  {
1042  if (i > current_row)
1043  {
1044  current_row = i;
1045  // +1 for 1-based indexing in file
1046  while (current_row >= (new_row_stops[current_proc]+1))
1047  ++current_proc;
1048  current_on_diagonal_nonzeros = 0;
1049  current_off_diagonal_nonzeros = 0;
1050  }
1051 
1052  // +1 for 1-based indexing in file
1053  if (j >= (new_col_starts[current_proc]+1) &&
1054  j < (new_col_stops[current_proc]+1))
1055  {
1056  ++current_on_diagonal_nonzeros;
1057  on_diagonal_nonzeros =
1058  std::max(on_diagonal_nonzeros,
1059  current_on_diagonal_nonzeros);
1060  }
1061  else
1062  {
1063  ++current_off_diagonal_nonzeros;
1064  off_diagonal_nonzeros =
1065  std::max(off_diagonal_nonzeros,
1066  current_off_diagonal_nonzeros);
1067  }
1068  }
1069  }
1070 
1071  this->comm().broadcast(on_diagonal_nonzeros);
1072  this->comm().broadcast(off_diagonal_nonzeros);
1073 
1074  this->init(m, n,
1075  new_row_stop-new_row_start,
1076  new_col_stop-new_col_start,
1077  on_diagonal_nonzeros,
1078  off_diagonal_nonzeros);
1079  }
1080 
1081  // Set the matrix values last.
1082  // Convert from 1-based to 0-based indexing
1083  if (this->processor_id() == 0)
1084  for (auto [i, j, value] : entries)
1085  this->set(i-1, j-1, value);
1086 
1087  this->close();
1088 #endif
1089 }
1090 
1091 
1092 
1093 template <typename T>
1094 void SparseMatrix<T>::read_petsc_binary(const std::string &)
1095 {
1096  libmesh_not_implemented_msg
1097  ("libMesh cannot read PETSc binary-format files into non-PETSc matrices");
1098 }
1099 
1100 
1101 
1102 template <typename T>
1103 void SparseMatrix<T>::read_petsc_hdf5(const std::string &)
1104 {
1105  libmesh_not_implemented_msg
1106  ("libMesh cannot read PETSc HDF5-format files into non-PETSc matrices");
1107 }
1108 
1109 
1110 
1111 template <typename T>
1113 {
1114  libmesh_assert(this->closed());
1115 
1116  for (const auto i : make_range(this->row_start(), this->row_stop()))
1117  for (const auto j : make_range(this->col_start(), this->col_stop()))
1118  this->set(i, j, (*this)(i, j) * scale);
1119 }
1120 
1121 
1122 
1123 template <typename T>
1125 {
1126  auto diff_mat = this->clone();
1127  diff_mat->add(-1.0, other_mat);
1128  return diff_mat->l1_norm();
1129 }
1130 
1131 //------------------------------------------------------------------
1132 // Explicit instantiations
1133 template class LIBMESH_EXPORT SparseMatrix<Number>;
1134 
1135 } // namespace libMesh
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
bool closed()
Checks that the library has been closed.
Definition: libmesh.C:341
virtual std::size_t n_nonzeros() const
SparseMatrix(const Parallel::Communicator &comm)
Constructor; initializes the matrix to be empty, without any structure, i.e.
Definition: sparse_matrix.C:66
This helper class can be called on multiple threads to compute the sparsity pattern (or graph) of the...
boost::multiprecision::float128 real(const boost::multiprecision::float128 in)
MPI_Datatype data_type
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
bool ends_with(std::string_view superstring, std::string_view suffix)
Look for a substring at the very end of a string.
Definition: utility.C:213
void scale(MeshBase &mesh, const Real xs, const Real ys=0., const Real zs=0.)
Scales the mesh.
virtual void add_vector(const T *v, const std::vector< numeric_index_type > &dof_indices)
Computes , where v is a pointer and each dof_indices[i] specifies where to add value v[i]...
Provides a uniform interface to vector storage schemes for different linear algebra libraries...
Definition: vector_fe_ex5.C:44
unsigned int m() const
The libMesh namespace provides an interface to certain functionality in the library.
const SparsityPattern::Build * get_sparsity_pattern() const
Definition: dof_map.h:570
uint8_t processor_id_type
Definition: id_types.h:104
virtual void zero()=0
Set all entries to zero.
Generic sparse matrix.
Definition: vector_fe_ex5.C:46
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
void libmesh_ignore(const Args &...)
dof_id_type numeric_index_type
Definition: id_types.h:99
bool _is_initialized
Flag that tells if init() has been called.
Definition: libmesh.C:315
auto l1_norm_diff(const NumericVector< T > &vec1, const NumericVector< T > &vec2)
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
std::string unzip_file(std::string_view name)
Create an unzipped copy of a bz2 or xz file, returning the name of the now-unzipped file that can be ...
Definition: utility.C:164
libmesh_assert(ctx)
An object whose state is distributed along a set of processors.
std::string_view basename_of(const std::string &fullname)
Definition: utility.C:108
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
OStreamProxy out
static const bool value
Definition: xdr_io.C:55
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
bool initialized()
Checks that library initialization has been done.
Definition: libmesh.C:334
boost::multiprecision::float128 imag(const boost::multiprecision::float128)
SolverPackage
Defines an enum for various linear solver packages.
unsigned int n() const
MatrixBuildType
Defines an enum for matrix build types.
Defines a dense matrix for use in Finite Element-type computations.
Definition: dof_map.h:75
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117