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 // C++ includes
43 #include <memory>
44 #include <fstream>
45 
46 #ifdef LIBMESH_HAVE_CXX11_REGEX
47 #include <regex>
48 #endif
49 
50 
51 namespace libMesh
52 {
53 
54 
55 //------------------------------------------------------------------
56 // SparseMatrix Methods
57 
58 
59 // Constructor
60 template <typename T>
62  ParallelObject(comm_in),
63  _dof_map(nullptr),
64  _sp(nullptr),
65  _is_initialized(false),
66  _use_hash_table(false)
67 {}
68 
69 
70 
71 template <typename T>
73 {
74  _dof_map = &dof_map;
75  if (!_sp)
76  _sp = dof_map.get_sparsity_pattern();
77 }
78 
79 
80 
81 template <typename T>
83 {
84  _sp = &sp;
85 }
86 
87 
88 
89 // default implementation is to fall back to non-blocked method
90 template <typename T>
92  const std::vector<numeric_index_type> & brows,
93  const std::vector<numeric_index_type> & bcols)
94 {
95  libmesh_assert_equal_to (dm.m() / brows.size(), dm.n() / bcols.size());
96 
97  const numeric_index_type blocksize = cast_int<numeric_index_type>
98  (dm.m() / brows.size());
99 
100  libmesh_assert_equal_to (dm.m()%blocksize, 0);
101  libmesh_assert_equal_to (dm.n()%blocksize, 0);
102 
103  std::vector<numeric_index_type> rows, cols;
104 
105  rows.reserve(blocksize*brows.size());
106  cols.reserve(blocksize*bcols.size());
107 
108  for (auto & row : brows)
109  {
110  numeric_index_type i = row * blocksize;
111 
112  for (unsigned int v=0; v<blocksize; v++)
113  rows.push_back(i++);
114  }
115 
116  for (auto & col : bcols)
117  {
118  numeric_index_type j = col * blocksize;
119 
120  for (unsigned int v=0; v<blocksize; v++)
121  cols.push_back(j++);
122  }
123 
124  this->add_matrix (dm, rows, cols);
125 }
126 
127 
128 
129 // Full specialization of print method for Complex datatypes
130 template <>
131 void SparseMatrix<Complex>::print(std::ostream & os, const bool sparse) const
132 {
133  // std::complex<>::operator<<() is defined, but use this form
134 
135  if (sparse)
136  {
137  libmesh_not_implemented();
138  }
139 
140  os << "Real part:" << std::endl;
141  for (auto i : make_range(this->m()))
142  {
143  for (auto j : make_range(this->n()))
144  os << std::setw(8) << (*this)(i,j).real() << " ";
145  os << std::endl;
146  }
147 
148  os << std::endl << "Imaginary part:" << std::endl;
149  for (auto i : make_range(this->m()))
150  {
151  for (auto j : make_range(this->n()))
152  os << std::setw(8) << (*this)(i,j).imag() << " ";
153  os << std::endl;
154  }
155 }
156 
157 
158 
159 
160 
161 
162 // Full specialization for Real datatypes
163 template <typename T>
164 std::unique_ptr<SparseMatrix<T>>
166  const SolverPackage solver_package,
167  const MatrixBuildType matrix_build_type /* = AUTOMATIC */)
168 {
169  // Avoid unused parameter warnings when no solver packages are enabled.
170  libmesh_ignore(comm);
171 
172  if (matrix_build_type == MatrixBuildType::DIAGONAL)
173  return std::make_unique<DiagonalMatrix<T>>(comm);
174 
175  // Build the appropriate vector
176  switch (solver_package)
177  {
178 
179 #ifdef LIBMESH_HAVE_LASPACK
180  case LASPACK_SOLVERS:
181  return std::make_unique<LaspackMatrix<T>>(comm);
182 #endif
183 
184 
185 #ifdef LIBMESH_HAVE_PETSC
186  case PETSC_SOLVERS:
187  return std::make_unique<PetscMatrix<T>>(comm);
188 #endif
189 
190 
191 #ifdef LIBMESH_TRILINOS_HAVE_EPETRA
192  case TRILINOS_SOLVERS:
193  return std::make_unique<EpetraMatrix<T>>(comm);
194 #endif
195 
196 
197 #ifdef LIBMESH_HAVE_EIGEN
198  case EIGEN_SOLVERS:
199  return std::make_unique<EigenSparseMatrix<T>>(comm);
200 #endif
201 
202  default:
203  libmesh_error_msg("ERROR: Unrecognized solver package: " << solver_package);
204  }
205 }
206 
207 
208 template <typename T>
210  const NumericVector<T> & arg) const
211 {
212  dest.zero();
213  this->vector_mult_add(dest,arg);
214 }
215 
216 
217 
218 template <typename T>
220  const NumericVector<T> & arg) const
221 {
222  /* This functionality is actually implemented in the \p
223  NumericVector class. */
224  dest.add_vector(arg,*this);
225 }
226 
227 
228 
229 template <typename T>
230 void SparseMatrix<T>::zero_rows (std::vector<numeric_index_type> &, T)
231 {
232  /* This functionality isn't implemented or stubbed in every subclass yet */
233  libmesh_not_implemented();
234 }
235 
236 
237 template <typename T>
238 std::size_t SparseMatrix<T>::n_nonzeros() const
239 {
240  if (!_sp)
241  return 0;
242  return _sp->n_nonzeros();
243 }
244 
245 
246 template <typename T>
247 void SparseMatrix<T>::print(std::ostream & os, const bool sparse) const
248 {
249  parallel_object_only();
250 
251  libmesh_assert (this->initialized());
252 
253  const numeric_index_type first_dof = this->row_start(),
254  end_dof = this->row_stop();
255 
256  // We'll print the matrix from processor 0 to make sure
257  // it's serialized properly
258  if (this->processor_id() == 0)
259  {
260  libmesh_assert_equal_to (first_dof, 0);
261  for (numeric_index_type i : make_range(end_dof))
262  {
263  if (sparse)
264  {
265  for (auto j : make_range(this->n()))
266  {
267  T c = (*this)(i,j);
268  if (c != static_cast<T>(0.0))
269  {
270  os << i << " " << j << " " << c << std::endl;
271  }
272  }
273  }
274  else
275  {
276  for (auto j : make_range(this->n()))
277  os << (*this)(i,j) << " ";
278  os << std::endl;
279  }
280  }
281 
282  std::vector<numeric_index_type> ibuf, jbuf;
283  std::vector<T> cbuf;
284  numeric_index_type currenti = end_dof;
285  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
286  {
287  this->comm().receive(p, ibuf);
288  this->comm().receive(p, jbuf);
289  this->comm().receive(p, cbuf);
290  libmesh_assert_equal_to (ibuf.size(), jbuf.size());
291  libmesh_assert_equal_to (ibuf.size(), cbuf.size());
292 
293  if (ibuf.empty())
294  continue;
295  libmesh_assert_greater_equal (ibuf.front(), currenti);
296  libmesh_assert_greater_equal (ibuf.back(), ibuf.front());
297 
298  std::size_t currentb = 0;
299  for (;currenti <= ibuf.back(); ++currenti)
300  {
301  if (sparse)
302  {
303  for (numeric_index_type j=0; j<this->n(); j++)
304  {
305  if (currentb < ibuf.size() &&
306  ibuf[currentb] == currenti &&
307  jbuf[currentb] == j)
308  {
309  os << currenti << " " << j << " " << cbuf[currentb] << std::endl;
310  currentb++;
311  }
312  }
313  }
314  else
315  {
316  for (auto j : make_range(this->n()))
317  {
318  if (currentb < ibuf.size() &&
319  ibuf[currentb] == currenti &&
320  jbuf[currentb] == j)
321  {
322  os << cbuf[currentb] << " ";
323  currentb++;
324  }
325  else
326  os << static_cast<T>(0.0) << " ";
327  }
328  os << std::endl;
329  }
330  }
331  }
332  if (!sparse)
333  {
334  for (; currenti != this->m(); ++currenti)
335  {
336  for (numeric_index_type j=0; j<this->n(); j++)
337  os << static_cast<T>(0.0) << " ";
338  os << std::endl;
339  }
340  }
341  }
342  else
343  {
344  std::vector<numeric_index_type> ibuf, jbuf;
345  std::vector<T> cbuf;
346 
347  // We'll assume each processor has access to entire
348  // matrix rows, so (*this)(i,j) is valid if i is a local index.
349  for (numeric_index_type i : make_range(first_dof, end_dof))
350  {
351  for (auto j : make_range(this->n()))
352  {
353  T c = (*this)(i,j);
354  if (c != static_cast<T>(0.0))
355  {
356  ibuf.push_back(i);
357  jbuf.push_back(j);
358  cbuf.push_back(c);
359  }
360  }
361  }
362  this->comm().send(0,ibuf);
363  this->comm().send(0,jbuf);
364  this->comm().send(0,cbuf);
365  }
366 }
367 
368 
369 template <typename T>
370 void SparseMatrix<T>::print_matlab(const std::string & name) const
371 {
372  parallel_object_only();
373 
374  libmesh_assert (this->initialized());
375 
376  const numeric_index_type first_dof = this->row_start(),
377  end_dof = this->row_stop();
378 
379  // We'll print the matrix from processor 0 to make sure
380  // it's serialized properly
381  if (this->processor_id() == 0)
382  {
383  std::unique_ptr<std::ofstream> file;
384 
385  if (name != "")
386  file = std::make_unique<std::ofstream>(name.c_str());
387 
388  std::ostream & os = (name == "") ? libMesh::out : *file;
389 
390  std::size_t sparsity_nonzeros = this->n_nonzeros();
391 
392  std::size_t real_nonzeros = 0;
393 
394  libmesh_assert_equal_to(first_dof, 0);
395  for (numeric_index_type i : make_range(end_dof))
396  {
397  for (auto j : make_range(this->n()))
398  {
399  T c = (*this)(i,j);
400  if (c != static_cast<T>(0.0))
401  ++real_nonzeros;
402  }
403  }
404 
405 
406  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
407  {
408  std::size_t nonzeros_on_p = 0;
409  this->comm().receive(p, nonzeros_on_p);
410  real_nonzeros += nonzeros_on_p;
411  }
412 
413  if (sparsity_nonzeros &&
414  sparsity_nonzeros != real_nonzeros)
415  libmesh_warning(sparsity_nonzeros <<
416  " nonzeros allocated, but " <<
417  real_nonzeros << " used.");
418 
419  // We probably want to be more consistent than that, if our
420  // sparsity is overallocated.
421 
422  // Print a header similar to PETSc's mat_view ascii_matlab
423  os << "%Mat Object: () " << this->n_processors() << " MPI processes\n"
424  << "% type: " << (this->n_processors() > 1 ? "mpi" : "seq") << "aij\n"
425  << "% Size = " << this->m() << ' ' << this->n() << '\n'
426  << "% Nonzeros = " << real_nonzeros << '\n'
427  << "zzz = zeros(" << real_nonzeros << ",3);\n"
428  << "zzz = [\n";
429 
430  for (numeric_index_type i : make_range(end_dof))
431  {
432  // FIXME - we need a base class way to iterate over a
433  // SparseMatrix row.
434  for (auto j : make_range(this->n()))
435  {
436  T c = (*this)(i,j);
437  if (c != static_cast<T>(0.0))
438  {
439  // Convert from 0-based to 1-based indexing
440  os << (i+1) << ' ' << (j+1) << " " << c << '\n';
441  }
442  }
443  }
444 
445  std::vector<numeric_index_type> ibuf, jbuf;
446  std::vector<T> cbuf;
447  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
448  {
449  this->comm().receive(p, ibuf);
450  this->comm().receive(p, jbuf);
451  this->comm().receive(p, cbuf);
452  libmesh_assert_equal_to (ibuf.size(), jbuf.size());
453  libmesh_assert_equal_to (ibuf.size(), cbuf.size());
454 
455  for (auto n : index_range(ibuf))
456  os << ibuf[n] << ' ' << jbuf[n] << " " << cbuf[n] << '\n';
457  }
458 
459  os << "];\n" << "Mat_sparse = spconvert(zzz);" << std::endl;
460  }
461  else
462  {
463  std::vector<numeric_index_type> ibuf, jbuf;
464  std::vector<T> cbuf;
465  std::size_t my_nonzeros = 0;
466 
467  // We'll assume each processor has access to entire
468  // matrix rows, so (*this)(i,j) is valid if i is a local index.
469  for (numeric_index_type i : make_range(first_dof, end_dof))
470  {
471  for (auto j : make_range(this->n()))
472  {
473  T c = (*this)(i,j);
474  if (c != static_cast<T>(0.0))
475  {
476  ibuf.push_back(i);
477  jbuf.push_back(j);
478  cbuf.push_back(c);
479  ++my_nonzeros;
480  }
481  }
482  }
483  this->comm().send(0,my_nonzeros);
484  this->comm().send(0,ibuf);
485  this->comm().send(0,jbuf);
486  this->comm().send(0,cbuf);
487  }
488 }
489 
490 
491 
492 template <typename T>
493 void SparseMatrix<T>::print_petsc_binary(const std::string &)
494 {
495  libmesh_not_implemented_msg
496  ("libMesh cannot write PETSc binary-format files from non-PETSc matrices");
497 }
498 
499 
500 
501 template <typename T>
502 void SparseMatrix<T>::print_petsc_hdf5(const std::string &)
503 {
504  libmesh_not_implemented_msg
505  ("libMesh cannot write PETSc HDF5-format files from non-PETSc matrices");
506 }
507 
508 
509 
510 template <typename T>
511 void SparseMatrix<T>::read(const std::string & filename)
512 {
513  {
514  std::ifstream in (filename.c_str());
515  libmesh_error_msg_if
516  (!in.good(), "ERROR: cannot read file:\n\t" <<
517  filename);
518  }
519 
520  std::string_view basename = Utility::basename_of(filename);
521 
522  const bool gzipped_file = (basename.rfind(".gz") == basename.size() - 3);
523 
524  if (gzipped_file)
525  basename.remove_suffix(3);
526 
527  if (basename.rfind(".matlab") == basename.size() - 7 ||
528  basename.rfind(".m") == basename.size() - 2)
529  this->read_matlab(filename);
530  else if (basename.rfind(".petsc64") == basename.size() - 8)
531  {
532 #ifndef LIBMESH_HAVE_PETSC
533  libmesh_error_msg("Cannot load PETSc matrix file " <<
534  filename << " without PETSc-enabled libMesh.");
535 #endif
536 #if LIBMESH_DOF_ID_BYTES != 8
537  libmesh_error_msg("Cannot load 64-bit PETSc matrix file " <<
538  filename << " with non-64-bit libMesh.");
539 #endif
540  this->read_petsc_binary(filename);
541  }
542  else if (basename.rfind(".petsc32") == basename.size() - 8)
543  {
544 #ifndef LIBMESH_HAVE_PETSC
545  libmesh_error_msg("Cannot load PETSc matrix file " <<
546  filename << " without PETSc-enabled libMesh.");
547 #endif
548 #if LIBMESH_DOF_ID_BYTES != 4
549  libmesh_error_msg("Cannot load 32-bit PETSc matrix file " <<
550  filename << " with non-32-bit libMesh.");
551 #endif
552  this->read_petsc_binary(filename);
553  }
554  else
555  libmesh_error_msg(" ERROR: Unrecognized matrix file extension on: "
556  << basename
557  << "\n I understand the following:\n\n"
558  << " *.matlab -- Matlab sparse matrix format\n"
559  << " *.m -- Matlab sparse matrix format\n"
560  << " *.petsc32 -- PETSc binary format, 32-bit\n"
561  << " *.petsc64 -- PETSc binary format, 64-bit\n"
562  );
563 }
564 
565 
566 template <typename T>
567 void SparseMatrix<T>::read_matlab(const std::string & filename)
568 {
569  LOG_SCOPE("read_matlab()", "SparseMatrix");
570 
571 #ifndef LIBMESH_HAVE_CXX11_REGEX
572  libmesh_not_implemented(); // What is your compiler?!? Email us!
573  libmesh_ignore(filename);
574 #else
575  parallel_object_only();
576 
577  const bool gzipped_file = (filename.rfind(".gz") == filename.size() - 3);
578 
579  // The sizes we get from the file
580  std::size_t m = 0,
581  n = 0;
582 
583  // If we don't already have this size, we'll need to reinit, and
584  // determine which rows+columns each processor is in charge of.
585  std::vector<numeric_index_type> new_row_starts, new_row_stops,
586  new_col_starts, new_col_stops;
587 
588  numeric_index_type new_row_start, new_row_stop,
589  new_col_start, new_col_stop;
590 
591  // We'll read through the file three times: once to get a reliable
592  // value for the matrix size (so we can divvy it up among
593  // processors), then again to get the sparsity to send to each
594  // processor, then a final time to get the entries to send to each
595  // processor.
596  //
597  // We'll use an istream here; it might be an ifstream if we're
598  // opening a raw ASCII file or a gzstream if we're opening a
599  // compressed one.
600  std::unique_ptr<std::istream> file;
601 
602  // We'll need a temporary structure to cache matrix entries, because
603  // we need to read through the whole file before we know the size
604  // and sparsity structure with which we can init().
605  //
606  // Reading through the file three times via `seekg` doesn't work
607  // with our gzstream wrapper, and seems to take three times as long
608  // even with a plain ifstream. What happened to disk caching!?
609  std::vector<std::tuple<numeric_index_type, numeric_index_type, T>> entries;
610 
611  // First read through the file, saving size and entry data
612  {
613  // We'll read the matrix on processor 0 rather than try to juggle
614  // parallel I/O.
615  if (this->processor_id() == 0)
616  {
617  // We'll be using regular expressions to make ourselves slightly
618  // more robust to formatting.
619  const std::regex start_regex // assignment like "zzz = ["
620  ("\\s*\\w+\\s*=\\s*\\[");
621  const std::regex end_regex // end of assignment
622  ("^[^%]*\\]");
623 
624  if (gzipped_file)
625  {
626 #ifdef LIBMESH_HAVE_GZSTREAM
627  auto inf = std::make_unique<igzstream>();
628  libmesh_assert(inf);
629  inf->open(filename.c_str(), std::ios::in);
630  file = std::move(inf);
631 #else
632  libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
633 #endif
634  }
635  else
636  {
637  auto inf = std::make_unique<std::ifstream>();
638  libmesh_assert(inf);
639 
640  std::string new_name = Utility::unzip_file(filename);
641 
642  inf->open(new_name.c_str(), std::ios::in);
643  file = std::move(inf);
644  }
645 
646  // If we have a matrix with all-zero trailing rows, the only
647  // way to get the size is if it ended up in a comment
648  const std::regex size_regex // comment like "% size = 8 8"
649  ("%\\s*[Ss][Ii][Zz][Ee]\\s*=\\s*(\\d+)\\s+(\\d+)");
650  const std::string whitespace = " \t";
651 
652  bool have_started = false;
653  bool have_ended = false;
654  std::size_t largest_i_seen = 0, largest_j_seen = 0;
655 
656  // Data for the row we're working on
657  // Use 1-based indexing for current_row, as in the file
658  std::size_t current_row = 1;
659 
660  for (std::string line; std::getline(*file, line);)
661  {
662  std::smatch sm;
663 
664  // First, try to match an entry. This is the most common
665  // case so we won't rely on slow std::regex for it.
666  // stringstream is at least an improvement over that.
667 
668  // Look for row/col/val like "1 1 -2.0e-4"
669 
670  std::istringstream l(line);
671 
672  std::size_t i, j;
673  T value;
674 
675  l >> i >> j >> value;
676 
677  if (!l.fail())
678  {
679  libmesh_error_msg_if
680  (!have_started, "Confused by premature entries in matrix file " << filename);
681 
682  entries.emplace_back(cast_int<numeric_index_type>(i),
683  cast_int<numeric_index_type>(j),
684  value);
685 
686  libmesh_error_msg_if
687  (!i || !j, "Expected 1-based indexing in matrix file "
688  << filename);
689 
690  current_row = std::max(current_row, i);
691 
692  libmesh_error_msg_if
693  (i < current_row,
694  "Can't handle out-of-order entries in matrix file "
695  << filename);
696 
697  largest_i_seen = std::max(i, largest_i_seen);
698  largest_j_seen = std::max(j, largest_j_seen);
699  }
700 
701  else if (std::regex_search(line, sm, size_regex))
702  {
703  const std::string msize = sm[1];
704  const std::string nsize = sm[2];
705  m = std::stoull(msize);
706  n = std::stoull(nsize);
707  }
708 
709  else if (std::regex_search(line, start_regex))
710  have_started = true;
711 
712  else if (std::regex_search(line, end_regex))
713  {
714  have_ended = true;
715  break;
716  }
717  }
718 
719  libmesh_error_msg_if
720  (!have_started, "Confused by missing assignment beginning in matrix file " << filename);
721 
722  libmesh_error_msg_if
723  (!have_ended, "Confused by missing assignment ending in matrix file " << filename);
724 
725  libmesh_error_msg_if
726  (m > largest_i_seen, "Confused by missing final row(s) in matrix file " << filename);
727 
728  libmesh_error_msg_if
729  (m > 0 && m < largest_i_seen, "Confused by extra final row(s) in matrix file " << filename);
730 
731  if (!m)
732  m = largest_i_seen;
733 
734  libmesh_error_msg_if
735  (n > largest_j_seen, "Confused by missing final column(s) in matrix file " << filename);
736 
737  libmesh_error_msg_if
738  (n > 0 && n < largest_j_seen, "Confused by extra final column(s) in matrix file " << filename);
739 
740  if (!n)
741  n = largest_j_seen;
742 
743  this->comm().broadcast(m);
744  this->comm().broadcast(n);
745  }
746  else
747  {
748  this->comm().broadcast(m);
749  this->comm().broadcast(n);
750  }
751 
752  if (this->initialized() &&
753  m == this->m() &&
754  n == this->n())
755  {
756  new_row_start = this->row_start(),
757  new_row_stop = this->row_stop();
758 
759  new_col_start = this->col_start(),
760  new_col_stop = this->col_stop();
761  }
762  else
763  {
764  // Determine which rows/columns each processor will be in charge of
765  new_row_start = this->processor_id() * m / this->n_processors(),
766  new_row_stop = (this->processor_id()+1) * m / this->n_processors();
767 
768  new_col_start = this->processor_id() * n / this->n_processors(),
769  new_col_stop = (this->processor_id()+1) * n / this->n_processors();
770  }
771 
772  this->comm().gather(0, new_row_start, new_row_starts);
773  this->comm().gather(0, new_row_stop, new_row_stops);
774  this->comm().gather(0, new_col_start, new_col_starts);
775  this->comm().gather(0, new_col_stop, new_col_stops);
776 
777  } // Done reading entry data and broadcasting matrix size
778 
779  // Calculate the matrix sparsity and initialize it second
780  {
781  // Deduce the sparsity pattern, or at least the maximum number of
782  // on- and off- diagonal non-zeros per row.
783  numeric_index_type on_diagonal_nonzeros =0,
784  off_diagonal_nonzeros =0;
785 
786  if (this->processor_id() == 0)
787  {
788  // Data for the row we're working on
789  // Use 1-based indexing for current_row, as in the file
790  numeric_index_type current_row = 1;
791  processor_id_type current_proc = 0;
792  numeric_index_type current_on_diagonal_nonzeros = 0;
793  numeric_index_type current_off_diagonal_nonzeros = 0;
794 
795  for (auto [i, j, value] : entries)
796  {
797  if (i > current_row)
798  {
799  current_row = i;
800  // +1 for 1-based indexing in file
801  while (current_row >= (new_row_stops[current_proc]+1))
802  ++current_proc;
803  current_on_diagonal_nonzeros = 0;
804  current_off_diagonal_nonzeros = 0;
805  }
806 
807  // +1 for 1-based indexing in file
808  if (j >= (new_col_starts[current_proc]+1) &&
809  j < (new_col_stops[current_proc]+1))
810  {
811  ++current_on_diagonal_nonzeros;
812  on_diagonal_nonzeros =
813  std::max(on_diagonal_nonzeros,
814  current_on_diagonal_nonzeros);
815  }
816  else
817  {
818  ++current_off_diagonal_nonzeros;
819  off_diagonal_nonzeros =
820  std::max(off_diagonal_nonzeros,
821  current_off_diagonal_nonzeros);
822  }
823  }
824  }
825 
826  this->comm().broadcast(on_diagonal_nonzeros);
827  this->comm().broadcast(off_diagonal_nonzeros);
828 
829  this->init(m, n,
830  new_row_stop-new_row_start,
831  new_col_stop-new_col_start,
832  on_diagonal_nonzeros,
833  off_diagonal_nonzeros);
834  }
835 
836  // Set the matrix values last.
837  // Convert from 1-based to 0-based indexing
838  if (this->processor_id() == 0)
839  for (auto [i, j, value] : entries)
840  this->set(i-1, j-1, value);
841 
842  this->close();
843 #endif
844 }
845 
846 
847 
848 template <typename T>
849 void SparseMatrix<T>::read_petsc_binary(const std::string &)
850 {
851  libmesh_not_implemented_msg
852  ("libMesh cannot read PETSc binary-format files into non-PETSc matrices");
853 }
854 
855 
856 
857 template <typename T>
858 void SparseMatrix<T>::read_petsc_hdf5(const std::string &)
859 {
860  libmesh_not_implemented_msg
861  ("libMesh cannot read PETSc HDF5-format files into non-PETSc matrices");
862 }
863 
864 
865 
866 template <typename T>
868 {
869  libmesh_assert(this->closed());
870 
871  for (const auto i : make_range(this->row_start(), this->row_stop()))
872  for (const auto j : make_range(this->col_start(), this->col_stop()))
873  this->set(i, j, (*this)(i, j) * scale);
874 }
875 
876 
877 
878 template <typename T>
880 {
881  auto diff_mat = this->clone();
882  diff_mat->add(-1.0, other_mat);
883  return diff_mat->l1_norm();
884 }
885 
886 //------------------------------------------------------------------
887 // Explicit instantiations
888 template class LIBMESH_EXPORT SparseMatrix<Number>;
889 
890 } // 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:283
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:61
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)
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
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:563
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:257
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:54
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:276
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