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

Generated by: LCOV version 1.14