LCOV - code coverage report
Current view: top level - src/numerics - sparse_matrix.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4308 (b969c4) with base 7aa2c3 Lines: 385 564 68.3 %
Date: 2025-11-07 13:38:09 Functions: 36 55 65.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14