libMesh
sparse_matrix.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 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 // Anonymous namespace for helper functions
56 namespace
57 {
58 #ifdef LIBMESH_HAVE_HDF5
59  void check_open(const std::string & filename,
60  hid_t id,
61  const std::string & objname)
62  {
63  if (id == H5I_INVALID_HID)
64  libmesh_error_msg("Couldn't open " + objname + " in " + filename);
65 
66  }
67 
68  template <typename T>
69  void check_hdf5(const std::string & filename, T hdf5val, const std::string & objname)
70  {
71  if (hdf5val < 0)
72  libmesh_error_msg("HDF5 error from " + objname + " in " + filename);
73  }
74 #endif
75 }
76 
77 
78 
79 namespace libMesh
80 {
81 
82 
83 //------------------------------------------------------------------
84 // SparseMatrix Methods
85 
86 
87 // Constructor
88 template <typename T>
90  ParallelObject(comm_in),
91  _dof_map(nullptr),
92  _sp(nullptr),
93  _is_initialized(false),
94  _use_hash_table(false)
95 {}
96 
97 
98 
99 template <typename T>
101 {
102  _dof_map = &dof_map;
103  if (!_sp)
104  _sp = dof_map.get_sparsity_pattern();
105 }
106 
107 
108 
109 template <typename T>
111 {
112  _sp = &sp;
113 }
114 
115 
116 
117 // default implementation is to fall back to non-blocked method
118 template <typename T>
120  const std::vector<numeric_index_type> & brows,
121  const std::vector<numeric_index_type> & bcols)
122 {
123  libmesh_assert_equal_to (dm.m() / brows.size(), dm.n() / bcols.size());
124 
125  const numeric_index_type blocksize = cast_int<numeric_index_type>
126  (dm.m() / brows.size());
127 
128  libmesh_assert_equal_to (dm.m()%blocksize, 0);
129  libmesh_assert_equal_to (dm.n()%blocksize, 0);
130 
131  std::vector<numeric_index_type> rows, cols;
132 
133  rows.reserve(blocksize*brows.size());
134  cols.reserve(blocksize*bcols.size());
135 
136  for (auto & row : brows)
137  {
138  numeric_index_type i = row * blocksize;
139 
140  for (unsigned int v=0; v<blocksize; v++)
141  rows.push_back(i++);
142  }
143 
144  for (auto & col : bcols)
145  {
146  numeric_index_type j = col * blocksize;
147 
148  for (unsigned int v=0; v<blocksize; v++)
149  cols.push_back(j++);
150  }
151 
152  this->add_matrix (dm, rows, cols);
153 }
154 
155 
156 
157 // Full specialization of print method for Complex datatypes
158 template <>
159 void SparseMatrix<Complex>::print(std::ostream & os, const bool sparse) const
160 {
161  // std::complex<>::operator<<() is defined, but use this form
162 
163  if (sparse)
164  {
165  libmesh_not_implemented();
166  }
167 
168  os << "Real part:" << std::endl;
169  for (auto i : make_range(this->m()))
170  {
171  for (auto j : make_range(this->n()))
172  os << std::setw(8) << (*this)(i,j).real() << " ";
173  os << std::endl;
174  }
175 
176  os << std::endl << "Imaginary part:" << std::endl;
177  for (auto i : make_range(this->m()))
178  {
179  for (auto j : make_range(this->n()))
180  os << std::setw(8) << (*this)(i,j).imag() << " ";
181  os << std::endl;
182  }
183 }
184 
185 
186 
187 
188 
189 
190 // Full specialization for Real datatypes
191 template <typename T>
192 std::unique_ptr<SparseMatrix<T>>
194  const SolverPackage solver_package,
195  const MatrixBuildType matrix_build_type /* = AUTOMATIC */)
196 {
197  // Avoid unused parameter warnings when no solver packages are enabled.
198  libmesh_ignore(comm);
199 
200  if (matrix_build_type == MatrixBuildType::DIAGONAL)
201  return std::make_unique<DiagonalMatrix<T>>(comm);
202 
203  // Build the appropriate vector
204  switch (solver_package)
205  {
206 
207 #ifdef LIBMESH_HAVE_LASPACK
208  case LASPACK_SOLVERS:
209  return std::make_unique<LaspackMatrix<T>>(comm);
210 #endif
211 
212 
213 #ifdef LIBMESH_HAVE_PETSC
214  case PETSC_SOLVERS:
215  return std::make_unique<PetscMatrix<T>>(comm);
216 #endif
217 
218 
219 #ifdef LIBMESH_TRILINOS_HAVE_EPETRA
220  case TRILINOS_SOLVERS:
221  return std::make_unique<EpetraMatrix<T>>(comm);
222 #endif
223 
224 
225 #ifdef LIBMESH_HAVE_EIGEN
226  case EIGEN_SOLVERS:
227  return std::make_unique<EigenSparseMatrix<T>>(comm);
228 #endif
229 
230  default:
231  libmesh_error_msg("ERROR: Unrecognized solver package: " << solver_package);
232  }
233 }
234 
235 
236 template <typename T>
238  const NumericVector<T> & arg) const
239 {
240  dest.zero();
241  this->vector_mult_add(dest,arg);
242 }
243 
244 
245 
246 template <typename T>
248  const NumericVector<T> & arg) const
249 {
250  /* This functionality is actually implemented in the \p
251  NumericVector class. */
252  dest.add_vector(arg,*this);
253 }
254 
255 
256 
257 template <typename T>
258 void SparseMatrix<T>::zero_rows (std::vector<numeric_index_type> &, T)
259 {
260  /* This functionality isn't implemented or stubbed in every subclass yet */
261  libmesh_not_implemented();
262 }
263 
264 
265 template <typename T>
266 std::size_t SparseMatrix<T>::n_nonzeros() const
267 {
268  if (!_sp)
269  return 0;
270  return _sp->n_nonzeros();
271 }
272 
273 
274 template <typename T>
275 void SparseMatrix<T>::print(std::ostream & os, const bool sparse) const
276 {
277  parallel_object_only();
278 
279  libmesh_assert (this->initialized());
280 
281  const numeric_index_type first_dof = this->row_start(),
282  end_dof = this->row_stop();
283 
284  // We'll print the matrix from processor 0 to make sure
285  // it's serialized properly
286  if (this->processor_id() == 0)
287  {
288  libmesh_assert_equal_to (first_dof, 0);
289  for (numeric_index_type i : make_range(end_dof))
290  {
291  if (sparse)
292  {
293  for (auto j : make_range(this->n()))
294  {
295  T c = (*this)(i,j);
296  if (c != static_cast<T>(0.0))
297  {
298  os << i << " " << j << " " << c << std::endl;
299  }
300  }
301  }
302  else
303  {
304  for (auto j : make_range(this->n()))
305  os << (*this)(i,j) << " ";
306  os << std::endl;
307  }
308  }
309 
310  std::vector<numeric_index_type> ibuf, jbuf;
311  std::vector<T> cbuf;
312  numeric_index_type currenti = end_dof;
313  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
314  {
315  this->comm().receive(p, ibuf);
316  this->comm().receive(p, jbuf);
317  this->comm().receive(p, cbuf);
318  libmesh_assert_equal_to (ibuf.size(), jbuf.size());
319  libmesh_assert_equal_to (ibuf.size(), cbuf.size());
320 
321  if (ibuf.empty())
322  continue;
323  libmesh_assert_greater_equal (ibuf.front(), currenti);
324  libmesh_assert_greater_equal (ibuf.back(), ibuf.front());
325 
326  std::size_t currentb = 0;
327  for (;currenti <= ibuf.back(); ++currenti)
328  {
329  if (sparse)
330  {
331  for (numeric_index_type j=0; j<this->n(); j++)
332  {
333  if (currentb < ibuf.size() &&
334  ibuf[currentb] == currenti &&
335  jbuf[currentb] == j)
336  {
337  os << currenti << " " << j << " " << cbuf[currentb] << std::endl;
338  currentb++;
339  }
340  }
341  }
342  else
343  {
344  for (auto j : make_range(this->n()))
345  {
346  if (currentb < ibuf.size() &&
347  ibuf[currentb] == currenti &&
348  jbuf[currentb] == j)
349  {
350  os << cbuf[currentb] << " ";
351  currentb++;
352  }
353  else
354  os << static_cast<T>(0.0) << " ";
355  }
356  os << std::endl;
357  }
358  }
359  }
360  if (!sparse)
361  {
362  for (; currenti != this->m(); ++currenti)
363  {
364  for (numeric_index_type j=0; j<this->n(); j++)
365  os << static_cast<T>(0.0) << " ";
366  os << std::endl;
367  }
368  }
369  }
370  else
371  {
372  std::vector<numeric_index_type> ibuf, jbuf;
373  std::vector<T> cbuf;
374 
375  // We'll assume each processor has access to entire
376  // matrix rows, so (*this)(i,j) is valid if i is a local index.
377  for (numeric_index_type i : make_range(first_dof, end_dof))
378  {
379  for (auto j : make_range(this->n()))
380  {
381  T c = (*this)(i,j);
382  if (c != static_cast<T>(0.0))
383  {
384  ibuf.push_back(i);
385  jbuf.push_back(j);
386  cbuf.push_back(c);
387  }
388  }
389  }
390  this->comm().send(0,ibuf);
391  this->comm().send(0,jbuf);
392  this->comm().send(0,cbuf);
393  }
394 }
395 
396 
397 template <typename T>
398 void SparseMatrix<T>::print_matlab(const std::string & name) const
399 {
400  parallel_object_only();
401 
402  libmesh_assert (this->initialized());
403 
404  const numeric_index_type first_dof = this->row_start(),
405  end_dof = this->row_stop();
406 
407  // We'll print the matrix from processor 0 to make sure
408  // it's serialized properly
409  if (this->processor_id() == 0)
410  {
411  std::unique_ptr<std::ofstream> file;
412 
413  if (name != "")
414  file = std::make_unique<std::ofstream>(name.c_str());
415 
416  std::ostream & os = (name == "") ? libMesh::out : *file;
417 
418  std::size_t sparsity_nonzeros = this->n_nonzeros();
419 
420  std::size_t real_nonzeros = 0;
421 
422  libmesh_assert_equal_to(first_dof, 0);
423  for (numeric_index_type i : make_range(end_dof))
424  {
425  for (auto j : make_range(this->n()))
426  {
427  T c = (*this)(i,j);
428  if (c != static_cast<T>(0.0))
429  ++real_nonzeros;
430  }
431  }
432 
433 
434  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
435  {
436  std::size_t nonzeros_on_p = 0;
437  this->comm().receive(p, nonzeros_on_p);
438  real_nonzeros += nonzeros_on_p;
439  }
440 
441  if (sparsity_nonzeros &&
442  sparsity_nonzeros != real_nonzeros)
443  libmesh_warning(sparsity_nonzeros <<
444  " nonzeros allocated, but " <<
445  real_nonzeros << " used.");
446 
447  // We probably want to be more consistent than that, if our
448  // sparsity is overallocated.
449 
450  // Print a header similar to PETSc's mat_view ascii_matlab
451  os << "%Mat Object: () " << this->n_processors() << " MPI processes\n"
452  << "% type: " << (this->n_processors() > 1 ? "mpi" : "seq") << "aij\n"
453  << "% Size = " << this->m() << ' ' << this->n() << '\n'
454  << "% Nonzeros = " << real_nonzeros << '\n'
455  << "zzz = zeros(" << real_nonzeros << ",3);\n"
456  << "zzz = [\n";
457 
458  for (numeric_index_type i : make_range(end_dof))
459  {
460  // FIXME - we need a base class way to iterate over a
461  // SparseMatrix row.
462  for (auto j : make_range(this->n()))
463  {
464  T c = (*this)(i,j);
465  if (c != static_cast<T>(0.0))
466  {
467  // Convert from 0-based to 1-based indexing
468  os << (i+1) << ' ' << (j+1) << " " << c << '\n';
469  }
470  }
471  }
472 
473  std::vector<numeric_index_type> ibuf, jbuf;
474  std::vector<T> cbuf;
475  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
476  {
477  this->comm().receive(p, ibuf);
478  this->comm().receive(p, jbuf);
479  this->comm().receive(p, cbuf);
480  libmesh_assert_equal_to (ibuf.size(), jbuf.size());
481  libmesh_assert_equal_to (ibuf.size(), cbuf.size());
482 
483  for (auto n : index_range(ibuf))
484  os << ibuf[n] << ' ' << jbuf[n] << " " << cbuf[n] << '\n';
485  }
486 
487  os << "];\n" << "Mat_sparse = spconvert(zzz);" << std::endl;
488  }
489  else
490  {
491  std::vector<numeric_index_type> ibuf, jbuf;
492  std::vector<T> cbuf;
493  std::size_t my_nonzeros = 0;
494 
495  // We'll assume each processor has access to entire
496  // matrix rows, so (*this)(i,j) is valid if i is a local index.
497  for (numeric_index_type i : make_range(first_dof, end_dof))
498  {
499  for (auto j : make_range(this->n()))
500  {
501  T c = (*this)(i,j);
502  if (c != static_cast<T>(0.0))
503  {
504  ibuf.push_back(i);
505  jbuf.push_back(j);
506  cbuf.push_back(c);
507  ++my_nonzeros;
508  }
509  }
510  }
511  this->comm().send(0,my_nonzeros);
512  this->comm().send(0,ibuf);
513  this->comm().send(0,jbuf);
514  this->comm().send(0,cbuf);
515  }
516 }
517 
518 
519 
520 template <typename T>
521 void SparseMatrix<T>::print_coreform_hdf5(const std::string & filename,
522  const std::string & groupname) const
523 {
524 #if defined(LIBMESH_USE_COMPLEX_NUMBERS) || !defined(LIBMESH_HAVE_HDF5)
525  // TODO: HDF5 version 2.0.0 and later adds native support for
526  // complex numbers with the H5T_NATIVE_DOUBLE_COMPLEX type [0], so
527  // we could consider supporting T==std::complex<Real> in the future.
528  // [0]: https://forum.hdfgroup.org/t/coming-in-the-next-hdf5-release-native-support-for-complex-number-datatypes/13543
529  libmesh_ignore(filename, groupname);
530  libmesh_error_msg("ERROR: need HDF5 support to handle .h5 files!!!");
531 #else
532  LOG_SCOPE("print_coreform_hdf5()", "SparseMatrix");
533 
534  // In this implementation, we copy the SparseMatrix entries into a
535  // std::vector<double>, so this won't work for any Number type for
536  // which sizeof(Number) > sizeof(double).
537  if constexpr (sizeof(T) > sizeof(double))
538  libmesh_not_implemented();
539 
540  const numeric_index_type first_dof = this->row_start(),
541  end_dof = this->row_stop();
542 
543  std::vector<std::size_t> cols, row_offsets;
544  std::vector<double> vals;
545 
546  if (this->processor_id() == 0)
547  row_offsets.push_back(0);
548 
549  for (numeric_index_type i : make_range(first_dof, end_dof))
550  {
551  for (auto j : make_range(this->n()))
552  {
553  T c = (*this)(i,j);
554  if (c != static_cast<T>(0.0))
555  {
556  cols.push_back(j);
557  vals.push_back(c);
558  }
559  }
560  // This is a *local* row offset; proc 0 may need to adjust later
561  row_offsets.push_back(cols.size());
562  }
563 
564  if (this->processor_id() == 0)
565  {
566  const hid_t file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC,
567  H5P_DEFAULT, H5P_DEFAULT);
568  if (file == H5I_INVALID_HID)
569  libmesh_file_error(filename);
570 
571  const hid_t group =
572  H5Gcreate2(file, groupname.c_str(), H5P_DEFAULT, H5P_DEFAULT,
573  H5P_DEFAULT);
574  check_open(filename, group, groupname);
575 
576  auto write_size_attribute = [&filename, &group]
577  (const std::string & attribute_name, unsigned long long writeval)
578  {
579  const hid_t fspace = H5Screate(H5S_SCALAR);
580  check_hdf5(filename, fspace, attribute_name + " fspace");
581 
582  const hid_t attr = H5Acreate2(group, attribute_name.c_str(),
583  H5T_STD_I64LE, fspace,
584  H5P_DEFAULT, H5P_DEFAULT);
585  check_hdf5(filename, attr, attribute_name);
586 
587  // HDF5 is supposed to handle both upscaling and endianness
588  // conversions here
589  const herr_t errval = H5Awrite(attr, H5T_NATIVE_ULLONG, &writeval);
590  check_hdf5(filename, errval, attribute_name + " write");
591 
592  H5Aclose(attr);
593  H5Sclose(fspace);
594  };
595 
596  write_size_attribute("num_cols", this->n());
597  write_size_attribute("num_rows", this->m());
598 
599  auto write_vector = [&filename, &group]
600  (const std::string & dataname, auto hdf5_file_type,
601  auto hdf5_native_type, auto & datavec)
602  {
603  const hsize_t len[1] = {cast_int<hsize_t>(datavec.size())};
604 
605  const hid_t space = H5Screate_simple(1, len, nullptr);
606  check_hdf5(filename, space, dataname + " space");
607 
608  const hid_t data =
609  H5Dcreate2(group, dataname.c_str(), hdf5_file_type, space,
610  H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
611  check_hdf5(filename, data, dataname + " data");
612 
613  const hid_t errval =
614  H5Dwrite(data, hdf5_native_type, H5S_ALL, H5S_ALL,
615  H5P_DEFAULT, datavec.data());
616  check_hdf5(filename, errval, dataname + " write");
617 
618  H5Dclose(data);
619  H5Sclose(space);
620  };
621 
622  std::vector<std::size_t> vals_sizes, first_dofs;
623  this->comm().gather(0, vals.size(), vals_sizes);
624  this->comm().gather(0, cast_int<std::size_t>(first_dof), first_dofs);
625  first_dofs.push_back(this->m());
626 
627  this->comm().allgather(cols);
628  this->comm().allgather(vals);
629  this->comm().allgather(row_offsets);
630 
631  libmesh_assert_equal_to(vals.size(),
632  cols.size());
633  libmesh_assert_equal_to(vals.size(),
634  std::accumulate(vals_sizes.begin(),
635  vals_sizes.end(),
636  std::size_t(0)));
637 
638  std::size_t extra_offset = 0;
639  for (auto p : make_range(processor_id_type(1), this->n_processors()))
640  {
641  extra_offset += vals_sizes[p-1];
642  for (auto i : make_range(first_dofs[p]+1, first_dofs[p+1]+1))
643  row_offsets[i] += extra_offset;
644  }
645 
646  write_vector("cols", H5T_STD_U64LE, H5T_NATIVE_ULLONG, cols);
647  write_vector("row_offsets", H5T_STD_U64LE, H5T_NATIVE_ULLONG, row_offsets);
648  write_vector("vals", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, vals);
649 
650  H5Gclose(group);
651  H5Fclose(file);
652  }
653  else
654  {
655  std::vector<std::size_t> dummy;
656  this->comm().gather(0, vals.size(), dummy);
657  this->comm().gather(0, cast_int<std::size_t>(first_dof), dummy);
658  this->comm().allgather(cols);
659  this->comm().allgather(vals);
660  this->comm().allgather(row_offsets);
661  }
662 #endif // LIBMESH_HAVE_HDF5
663 }
664 
665 
666 
667 template <typename T>
668 void SparseMatrix<T>::print_petsc_binary(const std::string &) const
669 {
670  libmesh_not_implemented_msg
671  ("libMesh cannot write PETSc binary-format files from non-PETSc matrices");
672 }
673 
674 
675 
676 template <typename T>
677 void SparseMatrix<T>::print_petsc_hdf5(const std::string &) const
678 {
679  libmesh_not_implemented_msg
680  ("libMesh cannot write PETSc HDF5-format files from non-PETSc matrices");
681 }
682 
683 
684 
685 template <typename T>
686 void SparseMatrix<T>::read(const std::string & filename)
687 {
688  {
689  std::ifstream in (filename.c_str());
690  libmesh_error_msg_if
691  (!in.good(), "ERROR: cannot read file:\n\t" <<
692  filename);
693  }
694 
695  std::string_view basename = Utility::basename_of(filename);
696 
697  const bool gzipped_file = Utility::ends_with(filename, ".gz");
698 
699  if (gzipped_file)
700  basename.remove_suffix(3);
701 
702  if (Utility::ends_with(basename, ".matlab") ||
703  Utility::ends_with(basename, ".m"))
704  this->read_matlab(filename);
705  else if (Utility::ends_with(basename, ".petsc64"))
706  {
707 #ifndef LIBMESH_HAVE_PETSC
708  libmesh_error_msg("Cannot load PETSc matrix file " <<
709  filename << " without PETSc-enabled libMesh.");
710 #elif LIBMESH_DOF_ID_BYTES != 8
711  libmesh_error_msg("Cannot load 64-bit PETSc matrix file " <<
712  filename << " with non-64-bit libMesh.");
713 #endif
714  if (gzipped_file)
715  libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
716  this->read_petsc_binary(filename);
717  }
718  else if (Utility::ends_with(basename, ".petsc32"))
719  {
720 #ifndef LIBMESH_HAVE_PETSC
721  libmesh_error_msg("Cannot load PETSc matrix file " <<
722  filename << " without PETSc-enabled libMesh.");
723 #elif LIBMESH_DOF_ID_BYTES != 4
724  libmesh_error_msg("Cannot load 32-bit PETSc matrix file " <<
725  filename << " with non-32-bit libMesh.");
726 #endif
727  if (gzipped_file)
728  libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
729  this->read_petsc_binary(filename);
730  }
731  else if (Utility::ends_with(basename, ".h5"))
732  {
733  if (gzipped_file)
734  libmesh_not_implemented_msg("Gzipped HDF5 matrices are not currently supported");
735  this->read_coreform_hdf5(filename);
736  }
737  else
738  libmesh_error_msg(" ERROR: Unrecognized matrix file extension on: "
739  << basename
740  << "\n I understand the following:\n\n"
741  << " *.h5 -- CoreForm HDF5 sparse matrix format\n"
742  << " *.matlab -- Matlab sparse matrix format\n"
743  << " *.matlab.gz -- Matlab sparse matrix format, gzipped\n"
744  << " *.m -- Matlab sparse matrix format\n"
745  << " *.m.gz -- Matlab sparse matrix format, gzipped\n"
746  << " *.petsc32 -- PETSc binary format, 32-bit\n"
747  << " *.petsc64 -- PETSc binary format, 64-bit\n"
748  );
749 }
750 
751 
752 
753 template <typename T>
754 void SparseMatrix<T>::print(const std::string & filename) const
755 {
756  {
757  std::ofstream outstr (filename.c_str());
758  libmesh_error_msg_if
759  (!outstr.good(), "ERROR: cannot write to file:\n\t" <<
760  filename);
761  }
762 
763  std::string_view basename = Utility::basename_of(filename);
764 
765  const bool gzipped_file = Utility::ends_with(filename, ".gz");
766 
767  if (gzipped_file)
768  basename.remove_suffix(3);
769 
770  if (Utility::ends_with(basename, ".matlab") ||
771  Utility::ends_with(basename, ".m"))
772  this->print_matlab(filename);
773  else if (Utility::ends_with(basename, ".petsc64"))
774  {
775 #ifndef LIBMESH_HAVE_PETSC
776  libmesh_error_msg("Cannot load PETSc matrix file " <<
777  filename << " without PETSc-enabled libMesh.");
778 #elif LIBMESH_DOF_ID_BYTES != 8
779  libmesh_error_msg("Cannot load 64-bit PETSc matrix file " <<
780  filename << " with non-64-bit libMesh.");
781 #endif
782  if (gzipped_file)
783  libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
784  this->print_petsc_binary(filename);
785  }
786  else if (Utility::ends_with(basename, ".petsc32"))
787  {
788 #ifndef LIBMESH_HAVE_PETSC
789  libmesh_error_msg("Cannot load PETSc matrix file " <<
790  filename << " without PETSc-enabled libMesh.");
791 #elif LIBMESH_DOF_ID_BYTES != 4
792  libmesh_error_msg("Cannot load 32-bit PETSc matrix file " <<
793  filename << " with non-32-bit libMesh.");
794 #endif
795  if (gzipped_file)
796  libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
797  this->print_petsc_binary(filename);
798  }
799  else if (Utility::ends_with(basename, ".h5"))
800  {
801  if (gzipped_file)
802  libmesh_not_implemented_msg("Gzipped HDF5 matrices are not currently supported");
803  this->print_coreform_hdf5(filename);
804  }
805  else
806  libmesh_error_msg(" ERROR: Unrecognized matrix file extension on: "
807  << basename
808  << "\n I understand the following:\n\n"
809  << " *.h5 -- CoreForm HDF5 sparse matrix format\n"
810  << " *.matlab -- Matlab sparse matrix format\n"
811  << " *.matlab.gz -- Matlab sparse matrix format, gzipped\n"
812  << " *.m -- Matlab sparse matrix format\n"
813  << " *.m.gz -- Matlab sparse matrix format, gzipped\n"
814  << " *.petsc32 -- PETSc binary format, 32-bit\n"
815  << " *.petsc64 -- PETSc binary format, 64-bit\n"
816  );
817 }
818 
819 
820 
821 template <typename T>
822 void SparseMatrix<T>::read_coreform_hdf5(const std::string & filename,
823  const std::string & groupname)
824 {
825 #ifndef LIBMESH_HAVE_HDF5
826  libmesh_ignore(filename, groupname);
827  libmesh_error_msg("ERROR: need HDF5 support to handle .h5 files!!!");
828 #else
829  LOG_SCOPE("read_coreform_hdf5()", "SparseMatrix");
830 
831  std::size_t num_rows = 0, num_cols = 0;
832 
833  // These are only used on pid 0, but avoid "uninitialized" warnings
834  hid_t group = H5I_INVALID_HID;
835  hid_t file = H5I_INVALID_HID;
836 
837  if (this->processor_id() == 0)
838  {
839  file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
840 
841  if (file == H5I_INVALID_HID)
842  libmesh_file_error(filename);
843 
844  group = H5Gopen(file, groupname.c_str(), H5P_DEFAULT);
845  check_open(filename, group, groupname);
846 
847  auto read_size_attribute = [&filename, &group]
848  (const std::string & attribute_name)
849  {
850  unsigned long long returnval = 0;
851 
852  const hid_t attr = H5Aopen(group, attribute_name.c_str(), H5P_DEFAULT);
853  check_open(filename, attr, attribute_name);
854 
855  const hid_t attr_type = H5Aget_type(attr);
856  check_hdf5(filename, attr_type, attribute_name + " type");
857 
858  // HDF5 will convert between the file's integer type and ours, but
859  // we do expect an integer type.
860  if (H5Tget_class(attr_type) != H5T_INTEGER)
861  libmesh_error_msg("Non-integer type for " + attribute_name + " in " + filename);
862 
863  H5Tclose(attr_type);
864 
865  // HDF5 is supposed to handle both upscaling and endianness
866  // conversions here
867  const herr_t errval = H5Aread(attr, H5T_NATIVE_ULLONG, &returnval);
868  check_hdf5(filename, errval, attribute_name + " read");
869 
870  H5Aclose(attr);
871 
872  return returnval;
873  };
874 
875  num_cols = read_size_attribute("num_cols");
876  num_rows = read_size_attribute("num_rows");
877 
878  this->comm().broadcast(num_cols);
879  this->comm().broadcast(num_rows);
880  }
881  else
882  {
883  this->comm().broadcast(num_cols);
884  this->comm().broadcast(num_rows);
885  }
886 
887  numeric_index_type new_row_start, new_row_stop,
888  new_col_start, new_col_stop;
889 
890  // If we need to reinit, we need to determine which rows+columns
891  // each processor is in charge of.
892  std::vector<numeric_index_type> new_row_starts, new_row_stops,
893  new_col_starts, new_col_stops;
894 
895  if (this->initialized() &&
896  num_cols == this->n() &&
897  num_rows == this->m())
898  {
899  new_row_start = this->row_start(),
900  new_row_stop = this->row_stop();
901 
902  new_col_start = this->col_start(),
903  new_col_stop = this->col_stop();
904  }
905  else
906  {
907  // Determine which rows/columns each processor will be in charge of
908  new_row_start = this->processor_id() * num_rows / this->n_processors(),
909  new_row_stop = (this->processor_id()+1) * num_rows / this->n_processors();
910 
911  new_col_start = this->processor_id() * num_cols / this->n_processors(),
912  new_col_stop = (this->processor_id()+1) * num_cols / this->n_processors();
913  }
914 
915  this->comm().gather(0, new_row_start, new_row_starts);
916  this->comm().gather(0, new_row_stop, new_row_stops);
917  this->comm().gather(0, new_col_start, new_col_starts);
918  this->comm().gather(0, new_col_stop, new_col_stops);
919 
920  numeric_index_type on_diagonal_nonzeros = 0,
921  off_diagonal_nonzeros = 0;
922 
923  std::vector<std::size_t> cols, row_offsets;
924  std::vector<double> vals;
925 
926  if (this->processor_id() == 0)
927  {
928  auto read_vector = [&filename, &group]
929  (const std::string & dataname, auto hdf5_class,
930  auto hdf5_type, auto & datavec)
931  {
932  const hid_t data = H5Dopen1(group, dataname.c_str());
933  check_open(filename, data, dataname.c_str());
934 
935  const hid_t data_type = H5Dget_type(data);
936  check_hdf5(filename, data_type, dataname + " type");
937 
938  // HDF5 will convert between the file's integer type and ours, but
939  // we do expect an integer type.
940  if (H5Tget_class(data_type) != hdf5_class)
941  libmesh_error_msg("Unexpected type for " + dataname + " in " + filename);
942 
943  H5Tclose(data_type);
944 
945  const hid_t dataspace = H5Dget_space(data);
946  check_hdf5(filename, dataspace, dataname + " space");
947 
948  int ndims = H5Sget_simple_extent_ndims(dataspace);
949  if (ndims != 1)
950  libmesh_error_msg("Non-vector space for " + dataname + " in " + filename);
951 
952  hsize_t len, maxlen;
953  herr_t errval = H5Sget_simple_extent_dims(dataspace, &len, &maxlen);
954  check_hdf5(filename, errval, dataname + " dims");
955 
956  datavec.resize(len);
957 
958  errval = H5Dread(data, hdf5_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, datavec.data());
959  check_hdf5(filename, errval, dataname + " read");
960 
961  H5Dclose(data);
962  };
963 
964  read_vector("cols", H5T_INTEGER, H5T_NATIVE_ULLONG, cols);
965  read_vector("row_offsets", H5T_INTEGER, H5T_NATIVE_ULLONG, row_offsets);
966  read_vector("vals", H5T_FLOAT, H5T_NATIVE_DOUBLE, vals);
967 
968  if (cols.size() != vals.size())
969  libmesh_error_msg("Inconsistent cols/vals sizes in " + filename);
970 
971  if (row_offsets.size() != num_rows + 1)
972  libmesh_error_msg("Inconsistent row_offsets size in " + filename);
973 
974  // Data for the row we're working on
975  numeric_index_type current_row = 0;
976  processor_id_type current_proc = 0;
977  numeric_index_type current_on_diagonal_nonzeros = 0;
978  numeric_index_type current_off_diagonal_nonzeros = 0;
979  if (row_offsets[0] != 0)
980  libmesh_error_msg("Unexpected row_offsets[0] in " + filename);
981 
982  for (auto i : index_range(cols))
983  {
984  while (row_offsets[current_row+1] <= i)
985  {
986  ++current_row;
987  if (row_offsets[current_row] < row_offsets[current_row-1])
988  libmesh_error_msg("Non-monotonic row_offsets in " + filename);
989  current_on_diagonal_nonzeros = 0;
990  current_off_diagonal_nonzeros = 0;
991  }
992 
993  while (current_row >= new_row_stops[current_proc])
994  ++current_proc;
995 
996  // 0-based indexing in file
997  if (cols[i] >= new_col_starts[current_proc] &&
998  cols[i] < new_col_stops[current_proc])
999  {
1000  ++current_on_diagonal_nonzeros;
1001  on_diagonal_nonzeros =
1002  std::max(on_diagonal_nonzeros,
1003  current_on_diagonal_nonzeros);
1004  }
1005  else
1006  {
1007  ++current_off_diagonal_nonzeros;
1008  off_diagonal_nonzeros =
1009  std::max(off_diagonal_nonzeros,
1010  current_off_diagonal_nonzeros);
1011  }
1012  }
1013  }
1014 
1015  this->comm().broadcast(on_diagonal_nonzeros);
1016  this->comm().broadcast(off_diagonal_nonzeros);
1017 
1018  this->init(num_rows, num_cols,
1019  new_row_stop-new_row_start,
1020  new_col_stop-new_col_start,
1021  on_diagonal_nonzeros,
1022  off_diagonal_nonzeros);
1023 
1024  // Set the matrix values last.
1025  if (this->processor_id() == 0)
1026  {
1027  numeric_index_type current_row = 0;
1028  for (auto i : index_range(cols))
1029  {
1030  while (row_offsets[current_row+1] <= i)
1031  {
1032  ++current_row;
1033  libmesh_assert_greater_equal (row_offsets[current_row],
1034  row_offsets[current_row-1]);
1035  }
1036  this->set(current_row, cols[i], vals[i]);
1037  }
1038 
1039  H5Gclose(group);
1040  H5Fclose(file);
1041  }
1042 
1043  this->close();
1044 #endif // LIBMESH_HAVE_HDF5
1045 }
1046 
1047 
1048 
1049 template <typename T>
1050 void SparseMatrix<T>::read_matlab(const std::string & filename)
1051 {
1052  LOG_SCOPE("read_matlab()", "SparseMatrix");
1053 
1054 #ifndef LIBMESH_HAVE_CXX11_REGEX
1055  libmesh_not_implemented(); // What is your compiler?!? Email us!
1056  libmesh_ignore(filename);
1057 #else
1058  parallel_object_only();
1059 
1060  const bool gzipped_file = Utility::ends_with(filename, ".gz");
1061 
1062  // The sizes we get from the file
1063  std::size_t m = 0,
1064  n = 0;
1065 
1066  // If we don't already have this size, we'll need to reinit, and
1067  // determine which rows+columns each processor is in charge of.
1068  std::vector<numeric_index_type> new_row_starts, new_row_stops,
1069  new_col_starts, new_col_stops;
1070 
1071  numeric_index_type new_row_start, new_row_stop,
1072  new_col_start, new_col_stop;
1073 
1074  // We'll read through the file three times: once to get a reliable
1075  // value for the matrix size (so we can divvy it up among
1076  // processors), then again to get the sparsity to send to each
1077  // processor, then a final time to get the entries to send to each
1078  // processor.
1079  //
1080  // We'll use an istream here; it might be an ifstream if we're
1081  // opening a raw ASCII file or a gzstream if we're opening a
1082  // compressed one.
1083  std::unique_ptr<std::istream> file;
1084 
1085  // We'll need a temporary structure to cache matrix entries, because
1086  // we need to read through the whole file before we know the size
1087  // and sparsity structure with which we can init().
1088  //
1089  // Reading through the file three times via `seekg` doesn't work
1090  // with our gzstream wrapper, and seems to take three times as long
1091  // even with a plain ifstream. What happened to disk caching!?
1092  std::vector<std::tuple<numeric_index_type, numeric_index_type, T>> entries;
1093 
1094  // First read through the file, saving size and entry data
1095  {
1096  // We'll read the matrix on processor 0 rather than try to juggle
1097  // parallel I/O.
1098  if (this->processor_id() == 0)
1099  {
1100  // We'll be using regular expressions to make ourselves slightly
1101  // more robust to formatting.
1102  const std::regex start_regex // assignment like "zzz = ["
1103  ("\\s*\\w+\\s*=\\s*\\[");
1104  const std::regex end_regex // end of assignment
1105  ("^[^%]*\\]");
1106 
1107  if (gzipped_file)
1108  {
1109 #ifdef LIBMESH_HAVE_GZSTREAM
1110  auto inf = std::make_unique<igzstream>();
1111  libmesh_assert(inf);
1112  inf->open(filename.c_str(), std::ios::in);
1113  file = std::move(inf);
1114 #else
1115  libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
1116 #endif
1117  }
1118  else
1119  {
1120  auto inf = std::make_unique<std::ifstream>();
1121  libmesh_assert(inf);
1122 
1123  std::string new_name = Utility::unzip_file(filename);
1124 
1125  inf->open(new_name.c_str(), std::ios::in);
1126  file = std::move(inf);
1127  }
1128 
1129  // If we have a matrix with all-zero trailing rows, the only
1130  // way to get the size is if it ended up in a comment
1131  const std::regex size_regex // comment like "% size = 8 8"
1132  ("%\\s*[Ss][Ii][Zz][Ee]\\s*=\\s*(\\d+)\\s+(\\d+)");
1133  const std::string whitespace = " \t";
1134 
1135  bool have_started = false;
1136  bool have_ended = false;
1137  std::size_t largest_i_seen = 0, largest_j_seen = 0;
1138 
1139  // Data for the row we're working on
1140  // Use 1-based indexing for current_row, as in the file
1141  std::size_t current_row = 1;
1142 
1143  for (std::string line; std::getline(*file, line);)
1144  {
1145  std::smatch sm;
1146 
1147  // First, try to match an entry. This is the most common
1148  // case so we won't rely on slow std::regex for it.
1149  // stringstream is at least an improvement over that.
1150 
1151  // Look for row/col/val like "1 1 -2.0e-4"
1152 
1153  std::istringstream l(line);
1154 
1155  std::size_t i, j;
1156  T value;
1157 
1158  l >> i >> j >> value;
1159 
1160  if (!l.fail())
1161  {
1162  libmesh_error_msg_if
1163  (!have_started, "Confused by premature entries in matrix file " << filename);
1164 
1165  entries.emplace_back(cast_int<numeric_index_type>(i),
1166  cast_int<numeric_index_type>(j),
1167  value);
1168 
1169  libmesh_error_msg_if
1170  (!i || !j, "Expected 1-based indexing in matrix file "
1171  << filename);
1172 
1173  current_row = std::max(current_row, i);
1174 
1175  libmesh_error_msg_if
1176  (i < current_row,
1177  "Can't handle out-of-order entries in matrix file "
1178  << filename);
1179 
1180  largest_i_seen = std::max(i, largest_i_seen);
1181  largest_j_seen = std::max(j, largest_j_seen);
1182  }
1183 
1184  else if (std::regex_search(line, sm, size_regex))
1185  {
1186  const std::string msize = sm[1];
1187  const std::string nsize = sm[2];
1188  m = std::stoull(msize);
1189  n = std::stoull(nsize);
1190  }
1191 
1192  else if (std::regex_search(line, start_regex))
1193  have_started = true;
1194 
1195  else if (std::regex_search(line, end_regex))
1196  {
1197  have_ended = true;
1198  break;
1199  }
1200  }
1201 
1202  libmesh_error_msg_if
1203  (!have_started, "Confused by missing assignment beginning in matrix file " << filename);
1204 
1205  libmesh_error_msg_if
1206  (!have_ended, "Confused by missing assignment ending in matrix file " << filename);
1207 
1208  libmesh_error_msg_if
1209  (m > largest_i_seen, "Confused by missing final row(s) in matrix file " << filename);
1210 
1211  libmesh_error_msg_if
1212  (m > 0 && m < largest_i_seen, "Confused by extra final row(s) in matrix file " << filename);
1213 
1214  if (!m)
1215  m = largest_i_seen;
1216 
1217  libmesh_error_msg_if
1218  (n > largest_j_seen, "Confused by missing final column(s) in matrix file " << filename);
1219 
1220  libmesh_error_msg_if
1221  (n > 0 && n < largest_j_seen, "Confused by extra final column(s) in matrix file " << filename);
1222 
1223  if (!n)
1224  n = largest_j_seen;
1225 
1226  this->comm().broadcast(m);
1227  this->comm().broadcast(n);
1228  }
1229  else
1230  {
1231  this->comm().broadcast(m);
1232  this->comm().broadcast(n);
1233  }
1234 
1235  if (this->initialized() &&
1236  m == this->m() &&
1237  n == this->n())
1238  {
1239  new_row_start = this->row_start(),
1240  new_row_stop = this->row_stop();
1241 
1242  new_col_start = this->col_start(),
1243  new_col_stop = this->col_stop();
1244  }
1245  else
1246  {
1247  // Determine which rows/columns each processor will be in charge of
1248  new_row_start = this->processor_id() * m / this->n_processors(),
1249  new_row_stop = (this->processor_id()+1) * m / this->n_processors();
1250 
1251  new_col_start = this->processor_id() * n / this->n_processors(),
1252  new_col_stop = (this->processor_id()+1) * n / this->n_processors();
1253  }
1254 
1255  this->comm().gather(0, new_row_start, new_row_starts);
1256  this->comm().gather(0, new_row_stop, new_row_stops);
1257  this->comm().gather(0, new_col_start, new_col_starts);
1258  this->comm().gather(0, new_col_stop, new_col_stops);
1259 
1260  } // Done reading entry data and broadcasting matrix size
1261 
1262  // Calculate the matrix sparsity and initialize it second
1263  {
1264  // Deduce the sparsity pattern, or at least the maximum number of
1265  // on- and off- diagonal non-zeros per row.
1266  numeric_index_type on_diagonal_nonzeros =0,
1267  off_diagonal_nonzeros =0;
1268 
1269  if (this->processor_id() == 0)
1270  {
1271  // Data for the row we're working on
1272  // Use 1-based indexing for current_row, as in the file
1273  numeric_index_type current_row = 1;
1274  processor_id_type current_proc = 0;
1275  numeric_index_type current_on_diagonal_nonzeros = 0;
1276  numeric_index_type current_off_diagonal_nonzeros = 0;
1277 
1278  for (auto [i, j, value] : entries)
1279  {
1280  if (i > current_row)
1281  {
1282  current_row = i;
1283  // +1 for 1-based indexing in file
1284  while (current_row >= (new_row_stops[current_proc]+1))
1285  ++current_proc;
1286  current_on_diagonal_nonzeros = 0;
1287  current_off_diagonal_nonzeros = 0;
1288  }
1289 
1290  // +1 for 1-based indexing in file
1291  if (j >= (new_col_starts[current_proc]+1) &&
1292  j < (new_col_stops[current_proc]+1))
1293  {
1294  ++current_on_diagonal_nonzeros;
1295  on_diagonal_nonzeros =
1296  std::max(on_diagonal_nonzeros,
1297  current_on_diagonal_nonzeros);
1298  }
1299  else
1300  {
1301  ++current_off_diagonal_nonzeros;
1302  off_diagonal_nonzeros =
1303  std::max(off_diagonal_nonzeros,
1304  current_off_diagonal_nonzeros);
1305  }
1306  }
1307  }
1308 
1309  this->comm().broadcast(on_diagonal_nonzeros);
1310  this->comm().broadcast(off_diagonal_nonzeros);
1311 
1312  this->init(m, n,
1313  new_row_stop-new_row_start,
1314  new_col_stop-new_col_start,
1315  on_diagonal_nonzeros,
1316  off_diagonal_nonzeros);
1317  }
1318 
1319  // Set the matrix values last.
1320  // Convert from 1-based to 0-based indexing
1321  if (this->processor_id() == 0)
1322  for (auto [i, j, value] : entries)
1323  this->set(i-1, j-1, value);
1324 
1325  this->close();
1326 #endif
1327 }
1328 
1329 
1330 
1331 template <typename T>
1332 void SparseMatrix<T>::read_petsc_binary(const std::string &)
1333 {
1334  libmesh_not_implemented_msg
1335  ("libMesh cannot read PETSc binary-format files into non-PETSc matrices");
1336 }
1337 
1338 
1339 
1340 template <typename T>
1341 void SparseMatrix<T>::read_petsc_hdf5(const std::string &)
1342 {
1343  libmesh_not_implemented_msg
1344  ("libMesh cannot read PETSc HDF5-format files into non-PETSc matrices");
1345 }
1346 
1347 
1348 
1349 template <typename T>
1351 {
1352  libmesh_assert(this->closed());
1353 
1354  for (const auto i : make_range(this->row_start(), this->row_stop()))
1355  for (const auto j : make_range(this->col_start(), this->col_stop()))
1356  this->set(i, j, (*this)(i, j) * scale);
1357 }
1358 
1359 
1360 
1361 template <typename T>
1363 {
1364  auto diff_mat = this->clone();
1365  diff_mat->add(-1.0, other_mat);
1366  return diff_mat->l1_norm();
1367 }
1368 
1369 //------------------------------------------------------------------
1370 // Explicit instantiations
1371 template class LIBMESH_EXPORT SparseMatrix<Number>;
1372 
1373 } // 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:331
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:89
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:305
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:176
bool initialized()
Checks that library initialization has been done.
Definition: libmesh.C:324
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:153