Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 :
20 : // Local Includes
21 : #include "libmesh/sparse_matrix.h"
22 :
23 : // libMesh includes
24 : #include "libmesh/dof_map.h"
25 : #include "libmesh/dense_matrix.h"
26 : #include "libmesh/diagonal_matrix.h"
27 : #include "libmesh/laspack_matrix.h"
28 : #include "libmesh/libmesh_logging.h"
29 : #include "libmesh/eigen_sparse_matrix.h"
30 : #include "libmesh/parallel.h"
31 : #include "libmesh/petsc_matrix.h"
32 : #include "libmesh/trilinos_epetra_matrix.h"
33 : #include "libmesh/numeric_vector.h"
34 : #include "libmesh/enum_solver_package.h"
35 :
36 :
37 : // gzstream for reading compressed files as a stream
38 : #ifdef LIBMESH_HAVE_GZSTREAM
39 : # include "gzstream.h"
40 : #endif
41 :
42 : #ifdef LIBMESH_HAVE_HDF5
43 : // Sticking to the C API to be safest on portability
44 : # include "hdf5.h"
45 : #endif
46 :
47 : // C++ includes
48 : #include <memory>
49 : #include <fstream>
50 :
51 : #ifdef LIBMESH_HAVE_CXX11_REGEX
52 : #include <regex>
53 : #endif
54 :
55 : // Anonymous namespace for helper functions
56 : namespace
57 : {
58 : #ifdef LIBMESH_HAVE_HDF5
59 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 114547 : SparseMatrix<T>::SparseMatrix (const Parallel::Communicator & comm_in) :
90 : ParallelObject(comm_in),
91 108351 : _dof_map(nullptr),
92 108351 : _sp(nullptr),
93 108351 : _is_initialized(false),
94 114547 : _use_hash_table(false)
95 114547 : {}
96 :
97 :
98 :
99 : template <typename T>
100 53339 : void SparseMatrix<T>::attach_dof_map (const DofMap & dof_map)
101 : {
102 53339 : _dof_map = &dof_map;
103 53339 : if (!_sp)
104 24508 : _sp = dof_map.get_sparsity_pattern();
105 53339 : }
106 :
107 :
108 :
109 : template <typename T>
110 42783 : void SparseMatrix<T>::attach_sparsity_pattern (const SparsityPattern::Build & sp)
111 : {
112 42783 : _sp = &sp;
113 42783 : }
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 29447 : 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 814 : libmesh_ignore(comm);
199 :
200 29447 : if (matrix_build_type == MatrixBuildType::DIAGONAL)
201 0 : return std::make_unique<DiagonalMatrix<T>>(comm);
202 :
203 : // Build the appropriate vector
204 29447 : 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 29187 : case PETSC_SOLVERS:
215 29187 : 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 260 : case EIGEN_SOLVERS:
227 260 : 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 : #if defined(LIBMESH_USE_COMPLEX_NUMBERS) || !defined(LIBMESH_HAVE_HDF5)
525 : // TODO: HDF5 version 2.0.0 and later adds native support for
526 : // complex numbers with the H5T_NATIVE_DOUBLE_COMPLEX type [0], so
527 : // we could consider supporting T==std::complex<Real> in the future.
528 : // [0]: https://forum.hdfgroup.org/t/coming-in-the-next-hdf5-release-native-support-for-complex-number-datatypes/13543
529 0 : libmesh_ignore(filename, groupname);
530 0 : libmesh_error_msg("ERROR: need HDF5 support to handle .h5 files!!!");
531 : #else
532 : LOG_SCOPE("print_coreform_hdf5()", "SparseMatrix");
533 :
534 : // In this implementation, we copy the SparseMatrix entries into a
535 : // std::vector<double>, so this won't work for any Number type for
536 : // which sizeof(Number) > sizeof(double).
537 : if constexpr (sizeof(T) > sizeof(double))
538 : libmesh_not_implemented();
539 :
540 3 : const numeric_index_type first_dof = this->row_start(),
541 3 : end_dof = this->row_stop();
542 :
543 : std::vector<std::size_t> cols, row_offsets;
544 : std::vector<double> vals;
545 :
546 3 : if (this->processor_id() == 0)
547 2 : row_offsets.push_back(0);
548 :
549 17 : for (numeric_index_type i : make_range(first_dof, end_dof))
550 : {
551 174 : for (auto j : make_range(this->n()))
552 : {
553 146 : T c = (*this)(i,j);
554 146 : if (c != static_cast<T>(0.0))
555 : {
556 81 : cols.push_back(j);
557 81 : vals.push_back(c);
558 : }
559 : }
560 : // This is a *local* row offset; proc 0 may need to adjust later
561 14 : row_offsets.push_back(cols.size());
562 : }
563 :
564 3 : if (this->processor_id() == 0)
565 : {
566 2 : const hid_t file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC,
567 : H5P_DEFAULT, H5P_DEFAULT);
568 2 : if (file == H5I_INVALID_HID)
569 0 : libmesh_file_error(filename);
570 :
571 2 : const hid_t group =
572 2 : H5Gcreate2(file, groupname.c_str(), H5P_DEFAULT, H5P_DEFAULT,
573 : H5P_DEFAULT);
574 2 : check_open(filename, group, groupname);
575 :
576 8 : auto write_size_attribute = [&filename, &group]
577 : (const std::string & attribute_name, unsigned long long writeval)
578 : {
579 4 : const hid_t fspace = H5Screate(H5S_SCALAR);
580 4 : check_hdf5(filename, fspace, attribute_name + " fspace");
581 :
582 4 : const hid_t attr = H5Acreate2(group, attribute_name.c_str(),
583 4 : H5T_STD_I64LE, fspace,
584 : H5P_DEFAULT, H5P_DEFAULT);
585 4 : check_hdf5(filename, attr, attribute_name);
586 :
587 : // HDF5 is supposed to handle both upscaling and endianness
588 : // conversions here
589 4 : const herr_t errval = H5Awrite(attr, H5T_NATIVE_ULLONG, &writeval);
590 4 : check_hdf5(filename, errval, attribute_name + " write");
591 :
592 4 : H5Aclose(attr);
593 4 : H5Sclose(fspace);
594 : };
595 :
596 2 : write_size_attribute("num_cols", this->n());
597 2 : write_size_attribute("num_rows", this->m());
598 :
599 14 : auto write_vector = [&filename, &group]
600 : (const std::string & dataname, auto hdf5_file_type,
601 : auto hdf5_native_type, auto & datavec)
602 : {
603 6 : const hsize_t len[1] = {cast_int<hsize_t>(datavec.size())};
604 :
605 6 : const hid_t space = H5Screate_simple(1, len, nullptr);
606 12 : check_hdf5(filename, space, dataname + " space");
607 :
608 : const hid_t data =
609 6 : H5Dcreate2(group, dataname.c_str(), hdf5_file_type, space,
610 : H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
611 12 : check_hdf5(filename, data, dataname + " data");
612 :
613 6 : const hid_t errval =
614 6 : H5Dwrite(data, hdf5_native_type, H5S_ALL, H5S_ALL,
615 : H5P_DEFAULT, datavec.data());
616 6 : check_hdf5(filename, errval, dataname + " write");
617 :
618 6 : H5Dclose(data);
619 6 : H5Sclose(space);
620 : };
621 :
622 : std::vector<std::size_t> vals_sizes, first_dofs;
623 2 : this->comm().gather(0, vals.size(), vals_sizes);
624 2 : this->comm().gather(0, cast_int<std::size_t>(first_dof), first_dofs);
625 2 : first_dofs.push_back(this->m());
626 :
627 2 : this->comm().allgather(cols);
628 2 : this->comm().allgather(vals);
629 2 : this->comm().allgather(row_offsets);
630 :
631 : libmesh_assert_equal_to(vals.size(),
632 : cols.size());
633 : libmesh_assert_equal_to(vals.size(),
634 : std::accumulate(vals_sizes.begin(),
635 : vals_sizes.end(),
636 : std::size_t(0)));
637 :
638 : std::size_t extra_offset = 0;
639 3 : for (auto p : make_range(processor_id_type(1), this->n_processors()))
640 : {
641 1 : extra_offset += vals_sizes[p-1];
642 6 : for (auto i : make_range(first_dofs[p]+1, first_dofs[p+1]+1))
643 5 : row_offsets[i] += extra_offset;
644 : }
645 :
646 2 : write_vector("cols", H5T_STD_U64LE, H5T_NATIVE_ULLONG, cols);
647 2 : write_vector("row_offsets", H5T_STD_U64LE, H5T_NATIVE_ULLONG, row_offsets);
648 2 : write_vector("vals", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, vals);
649 :
650 2 : H5Gclose(group);
651 2 : H5Fclose(file);
652 : }
653 : else
654 : {
655 : std::vector<std::size_t> dummy;
656 1 : this->comm().gather(0, vals.size(), dummy);
657 1 : this->comm().gather(0, cast_int<std::size_t>(first_dof), dummy);
658 1 : this->comm().allgather(cols);
659 1 : this->comm().allgather(vals);
660 1 : this->comm().allgather(row_offsets);
661 : }
662 : #endif // LIBMESH_HAVE_HDF5
663 3 : }
664 :
665 :
666 :
667 : template <typename T>
668 0 : void SparseMatrix<T>::print_petsc_binary(const std::string &) const
669 : {
670 0 : libmesh_not_implemented_msg
671 : ("libMesh cannot write PETSc binary-format files from non-PETSc matrices");
672 : }
673 :
674 :
675 :
676 : template <typename T>
677 0 : void SparseMatrix<T>::print_petsc_hdf5(const std::string &) const
678 : {
679 0 : libmesh_not_implemented_msg
680 : ("libMesh cannot write PETSc HDF5-format files from non-PETSc matrices");
681 : }
682 :
683 :
684 :
685 : template <typename T>
686 1216 : void SparseMatrix<T>::read(const std::string & filename)
687 : {
688 : {
689 1292 : std::ifstream in (filename.c_str());
690 1216 : libmesh_error_msg_if
691 : (!in.good(), "ERROR: cannot read file:\n\t" <<
692 : filename);
693 1140 : }
694 :
695 1216 : std::string_view basename = Utility::basename_of(filename);
696 :
697 1216 : const bool gzipped_file = Utility::ends_with(filename, ".gz");
698 :
699 1216 : if (gzipped_file)
700 18 : basename.remove_suffix(3);
701 :
702 2432 : if (Utility::ends_with(basename, ".matlab") ||
703 1330 : Utility::ends_with(basename, ".m"))
704 1138 : this->read_matlab(filename);
705 78 : else if (Utility::ends_with(basename, ".petsc64"))
706 : {
707 : #ifndef LIBMESH_HAVE_PETSC
708 0 : libmesh_error_msg("Cannot load PETSc matrix file " <<
709 : filename << " without PETSc-enabled libMesh.");
710 : #elif LIBMESH_DOF_ID_BYTES != 8
711 0 : libmesh_error_msg("Cannot load 64-bit PETSc matrix file " <<
712 : filename << " with non-64-bit libMesh.");
713 : #endif
714 66 : if (gzipped_file)
715 0 : libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
716 66 : this->read_petsc_binary(filename);
717 : }
718 12 : else if (Utility::ends_with(basename, ".petsc32"))
719 : {
720 : #ifndef LIBMESH_HAVE_PETSC
721 0 : libmesh_error_msg("Cannot load PETSc matrix file " <<
722 : filename << " without PETSc-enabled libMesh.");
723 : #elif LIBMESH_DOF_ID_BYTES != 4
724 0 : libmesh_error_msg("Cannot load 32-bit PETSc matrix file " <<
725 : filename << " with non-32-bit libMesh.");
726 : #endif
727 4 : if (gzipped_file)
728 0 : libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
729 4 : this->read_petsc_binary(filename);
730 : }
731 8 : else if (Utility::ends_with(basename, ".h5"))
732 : {
733 8 : if (gzipped_file)
734 0 : libmesh_not_implemented_msg("Gzipped HDF5 matrices are not currently supported");
735 16 : this->read_coreform_hdf5(filename);
736 : }
737 : else
738 0 : libmesh_error_msg(" ERROR: Unrecognized matrix file extension on: "
739 : << basename
740 : << "\n I understand the following:\n\n"
741 : << " *.h5 -- CoreForm HDF5 sparse matrix format\n"
742 : << " *.matlab -- Matlab sparse matrix format\n"
743 : << " *.matlab.gz -- Matlab sparse matrix format, gzipped\n"
744 : << " *.m -- Matlab sparse matrix format\n"
745 : << " *.m.gz -- Matlab sparse matrix format, gzipped\n"
746 : << " *.petsc32 -- PETSc binary format, 32-bit\n"
747 : << " *.petsc64 -- PETSc binary format, 64-bit\n"
748 : );
749 1216 : }
750 :
751 :
752 :
753 : template <typename T>
754 171 : void SparseMatrix<T>::print(const std::string & filename) const
755 : {
756 : {
757 187 : std::ofstream outstr (filename.c_str());
758 171 : libmesh_error_msg_if
759 : (!outstr.good(), "ERROR: cannot write to file:\n\t" <<
760 : filename);
761 155 : }
762 :
763 171 : std::string_view basename = Utility::basename_of(filename);
764 :
765 171 : const bool gzipped_file = Utility::ends_with(filename, ".gz");
766 :
767 171 : if (gzipped_file)
768 4 : basename.remove_suffix(3);
769 :
770 342 : if (Utility::ends_with(basename, ".matlab") ||
771 182 : Utility::ends_with(basename, ".m"))
772 168 : this->print_matlab(filename);
773 3 : else if (Utility::ends_with(basename, ".petsc64"))
774 : {
775 : #ifndef LIBMESH_HAVE_PETSC
776 0 : libmesh_error_msg("Cannot load PETSc matrix file " <<
777 : filename << " without PETSc-enabled libMesh.");
778 : #elif LIBMESH_DOF_ID_BYTES != 8
779 0 : libmesh_error_msg("Cannot load 64-bit PETSc matrix file " <<
780 : filename << " with non-64-bit libMesh.");
781 : #endif
782 0 : if (gzipped_file)
783 0 : libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
784 0 : this->print_petsc_binary(filename);
785 : }
786 3 : else if (Utility::ends_with(basename, ".petsc32"))
787 : {
788 : #ifndef LIBMESH_HAVE_PETSC
789 0 : libmesh_error_msg("Cannot load PETSc matrix file " <<
790 : filename << " without PETSc-enabled libMesh.");
791 : #elif LIBMESH_DOF_ID_BYTES != 4
792 0 : libmesh_error_msg("Cannot load 32-bit PETSc matrix file " <<
793 : filename << " with non-32-bit libMesh.");
794 : #endif
795 0 : if (gzipped_file)
796 0 : libmesh_not_implemented_msg("Gzipped PETSc matrices are not currently supported");
797 0 : this->print_petsc_binary(filename);
798 : }
799 3 : else if (Utility::ends_with(basename, ".h5"))
800 : {
801 3 : if (gzipped_file)
802 0 : libmesh_not_implemented_msg("Gzipped HDF5 matrices are not currently supported");
803 6 : this->print_coreform_hdf5(filename);
804 : }
805 : else
806 0 : libmesh_error_msg(" ERROR: Unrecognized matrix file extension on: "
807 : << basename
808 : << "\n I understand the following:\n\n"
809 : << " *.h5 -- CoreForm HDF5 sparse matrix format\n"
810 : << " *.matlab -- Matlab sparse matrix format\n"
811 : << " *.matlab.gz -- Matlab sparse matrix format, gzipped\n"
812 : << " *.m -- Matlab sparse matrix format\n"
813 : << " *.m.gz -- Matlab sparse matrix format, gzipped\n"
814 : << " *.petsc32 -- PETSc binary format, 32-bit\n"
815 : << " *.petsc64 -- PETSc binary format, 64-bit\n"
816 : );
817 171 : }
818 :
819 :
820 :
821 : template <typename T>
822 8 : void SparseMatrix<T>::read_coreform_hdf5(const std::string & filename,
823 : const std::string & groupname)
824 : {
825 : #ifndef LIBMESH_HAVE_HDF5
826 0 : libmesh_ignore(filename, groupname);
827 0 : libmesh_error_msg("ERROR: need HDF5 support to handle .h5 files!!!");
828 : #else
829 : LOG_SCOPE("read_coreform_hdf5()", "SparseMatrix");
830 :
831 8 : std::size_t num_rows = 0, num_cols = 0;
832 :
833 : // These are only used on pid 0, but avoid "uninitialized" warnings
834 8 : hid_t group = H5I_INVALID_HID;
835 : hid_t file = H5I_INVALID_HID;
836 :
837 8 : if (this->processor_id() == 0)
838 : {
839 6 : file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
840 :
841 6 : if (file == H5I_INVALID_HID)
842 0 : libmesh_file_error(filename);
843 :
844 6 : group = H5Gopen(file, groupname.c_str(), H5P_DEFAULT);
845 6 : check_open(filename, group, groupname);
846 :
847 54 : auto read_size_attribute = [&filename, &group]
848 : (const std::string & attribute_name)
849 : {
850 12 : unsigned long long returnval = 0;
851 :
852 12 : const hid_t attr = H5Aopen(group, attribute_name.c_str(), H5P_DEFAULT);
853 12 : check_open(filename, attr, attribute_name);
854 :
855 12 : const hid_t attr_type = H5Aget_type(attr);
856 12 : check_hdf5(filename, attr_type, attribute_name + " type");
857 :
858 : // HDF5 will convert between the file's integer type and ours, but
859 : // we do expect an integer type.
860 12 : if (H5Tget_class(attr_type) != H5T_INTEGER)
861 0 : libmesh_error_msg("Non-integer type for " + attribute_name + " in " + filename);
862 :
863 12 : H5Tclose(attr_type);
864 :
865 : // HDF5 is supposed to handle both upscaling and endianness
866 : // conversions here
867 12 : const herr_t errval = H5Aread(attr, H5T_NATIVE_ULLONG, &returnval);
868 12 : check_hdf5(filename, errval, attribute_name + " read");
869 :
870 12 : H5Aclose(attr);
871 :
872 12 : return returnval;
873 : };
874 :
875 6 : num_cols = read_size_attribute("num_cols");
876 6 : num_rows = read_size_attribute("num_rows");
877 :
878 6 : this->comm().broadcast(num_cols);
879 6 : this->comm().broadcast(num_rows);
880 : }
881 : else
882 : {
883 2 : this->comm().broadcast(num_cols);
884 2 : this->comm().broadcast(num_rows);
885 : }
886 :
887 : numeric_index_type new_row_start, new_row_stop,
888 : new_col_start, new_col_stop;
889 :
890 : // If we need to reinit, we need to determine which rows+columns
891 : // each processor is in charge of.
892 : std::vector<numeric_index_type> new_row_starts, new_row_stops,
893 : new_col_starts, new_col_stops;
894 :
895 8 : if (this->initialized() &&
896 8 : num_cols == this->n() &&
897 0 : num_rows == this->m())
898 : {
899 0 : new_row_start = this->row_start(),
900 0 : new_row_stop = this->row_stop();
901 :
902 0 : new_col_start = this->col_start(),
903 0 : new_col_stop = this->col_stop();
904 : }
905 : else
906 : {
907 : // Determine which rows/columns each processor will be in charge of
908 8 : new_row_start = this->processor_id() * num_rows / this->n_processors(),
909 8 : new_row_stop = (this->processor_id()+1) * num_rows / this->n_processors();
910 :
911 8 : new_col_start = this->processor_id() * num_cols / this->n_processors(),
912 8 : new_col_stop = (this->processor_id()+1) * num_cols / this->n_processors();
913 : }
914 :
915 8 : this->comm().gather(0, new_row_start, new_row_starts);
916 8 : this->comm().gather(0, new_row_stop, new_row_stops);
917 8 : this->comm().gather(0, new_col_start, new_col_starts);
918 8 : this->comm().gather(0, new_col_stop, new_col_stops);
919 :
920 8 : numeric_index_type on_diagonal_nonzeros = 0,
921 8 : off_diagonal_nonzeros = 0;
922 :
923 : std::vector<std::size_t> cols, row_offsets;
924 : std::vector<double> vals;
925 :
926 8 : if (this->processor_id() == 0)
927 : {
928 42 : auto read_vector = [&filename, &group]
929 : (const std::string & dataname, auto hdf5_class,
930 : auto hdf5_type, auto & datavec)
931 : {
932 18 : const hid_t data = H5Dopen1(group, dataname.c_str());
933 18 : check_open(filename, data, dataname.c_str());
934 :
935 18 : const hid_t data_type = H5Dget_type(data);
936 18 : check_hdf5(filename, data_type, dataname + " type");
937 :
938 : // HDF5 will convert between the file's integer type and ours, but
939 : // we do expect an integer type.
940 18 : if (H5Tget_class(data_type) != hdf5_class)
941 0 : libmesh_error_msg("Unexpected type for " + dataname + " in " + filename);
942 :
943 18 : H5Tclose(data_type);
944 :
945 18 : const hid_t dataspace = H5Dget_space(data);
946 18 : check_hdf5(filename, dataspace, dataname + " space");
947 :
948 18 : int ndims = H5Sget_simple_extent_ndims(dataspace);
949 18 : if (ndims != 1)
950 0 : libmesh_error_msg("Non-vector space for " + dataname + " in " + filename);
951 :
952 : hsize_t len, maxlen;
953 18 : herr_t errval = H5Sget_simple_extent_dims(dataspace, &len, &maxlen);
954 18 : check_hdf5(filename, errval, dataname + " dims");
955 :
956 18 : datavec.resize(len);
957 :
958 18 : errval = H5Dread(data, hdf5_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, datavec.data());
959 18 : check_hdf5(filename, errval, dataname + " read");
960 :
961 18 : H5Dclose(data);
962 : };
963 :
964 6 : read_vector("cols", H5T_INTEGER, H5T_NATIVE_ULLONG, cols);
965 6 : read_vector("row_offsets", H5T_INTEGER, H5T_NATIVE_ULLONG, row_offsets);
966 12 : read_vector("vals", H5T_FLOAT, H5T_NATIVE_DOUBLE, vals);
967 :
968 6 : if (cols.size() != vals.size())
969 0 : libmesh_error_msg("Inconsistent cols/vals sizes in " + filename);
970 :
971 6 : if (row_offsets.size() != num_rows + 1)
972 0 : libmesh_error_msg("Inconsistent row_offsets size in " + filename);
973 :
974 : // Data for the row we're working on
975 : numeric_index_type current_row = 0;
976 : processor_id_type current_proc = 0;
977 6 : numeric_index_type current_on_diagonal_nonzeros = 0;
978 6 : numeric_index_type current_off_diagonal_nonzeros = 0;
979 6 : if (row_offsets[0] != 0)
980 0 : libmesh_error_msg("Unexpected row_offsets[0] in " + filename);
981 :
982 481 : for (auto i : index_range(cols))
983 : {
984 568 : while (row_offsets[current_row+1] <= i)
985 : {
986 : ++current_row;
987 93 : if (row_offsets[current_row] < row_offsets[current_row-1])
988 0 : libmesh_error_msg("Non-monotonic row_offsets in " + filename);
989 93 : current_on_diagonal_nonzeros = 0;
990 93 : current_off_diagonal_nonzeros = 0;
991 : }
992 :
993 477 : while (current_row >= new_row_stops[current_proc])
994 2 : ++current_proc;
995 :
996 : // 0-based indexing in file
997 475 : if (cols[i] >= new_col_starts[current_proc] &&
998 449 : cols[i] < new_col_stops[current_proc])
999 : {
1000 414 : ++current_on_diagonal_nonzeros;
1001 414 : on_diagonal_nonzeros =
1002 : std::max(on_diagonal_nonzeros,
1003 : current_on_diagonal_nonzeros);
1004 : }
1005 : else
1006 : {
1007 61 : ++current_off_diagonal_nonzeros;
1008 61 : off_diagonal_nonzeros =
1009 : std::max(off_diagonal_nonzeros,
1010 : current_off_diagonal_nonzeros);
1011 : }
1012 : }
1013 : }
1014 :
1015 8 : this->comm().broadcast(on_diagonal_nonzeros);
1016 8 : this->comm().broadcast(off_diagonal_nonzeros);
1017 :
1018 8 : this->init(num_rows, num_cols,
1019 : new_row_stop-new_row_start,
1020 : new_col_stop-new_col_start,
1021 : on_diagonal_nonzeros,
1022 : off_diagonal_nonzeros);
1023 :
1024 : // Set the matrix values last.
1025 8 : if (this->processor_id() == 0)
1026 : {
1027 : numeric_index_type current_row = 0;
1028 481 : for (auto i : index_range(cols))
1029 : {
1030 568 : while (row_offsets[current_row+1] <= i)
1031 : {
1032 : ++current_row;
1033 : libmesh_assert_greater_equal (row_offsets[current_row],
1034 : row_offsets[current_row-1]);
1035 : }
1036 475 : this->set(current_row, cols[i], vals[i]);
1037 : }
1038 :
1039 6 : H5Gclose(group);
1040 6 : H5Fclose(file);
1041 : }
1042 :
1043 8 : this->close();
1044 : #endif // LIBMESH_HAVE_HDF5
1045 8 : }
1046 :
1047 :
1048 :
1049 : template <typename T>
1050 1559 : void SparseMatrix<T>::read_matlab(const std::string & filename)
1051 : {
1052 96 : LOG_SCOPE("read_matlab()", "SparseMatrix");
1053 :
1054 : #ifndef LIBMESH_HAVE_CXX11_REGEX
1055 : libmesh_not_implemented(); // What is your compiler?!? Email us!
1056 : libmesh_ignore(filename);
1057 : #else
1058 48 : parallel_object_only();
1059 :
1060 1559 : const bool gzipped_file = Utility::ends_with(filename, ".gz");
1061 :
1062 : // The sizes we get from the file
1063 1559 : std::size_t m = 0,
1064 1559 : n = 0;
1065 :
1066 : // If we don't already have this size, we'll need to reinit, and
1067 : // determine which rows+columns each processor is in charge of.
1068 96 : std::vector<numeric_index_type> new_row_starts, new_row_stops,
1069 96 : new_col_starts, new_col_stops;
1070 :
1071 : numeric_index_type new_row_start, new_row_stop,
1072 : new_col_start, new_col_stop;
1073 :
1074 : // We'll read through the file three times: once to get a reliable
1075 : // value for the matrix size (so we can divvy it up among
1076 : // processors), then again to get the sparsity to send to each
1077 : // processor, then a final time to get the entries to send to each
1078 : // processor.
1079 : //
1080 : // We'll use an istream here; it might be an ifstream if we're
1081 : // opening a raw ASCII file or a gzstream if we're opening a
1082 : // compressed one.
1083 1511 : std::unique_ptr<std::istream> file;
1084 :
1085 : // We'll need a temporary structure to cache matrix entries, because
1086 : // we need to read through the whole file before we know the size
1087 : // and sparsity structure with which we can init().
1088 : //
1089 : // Reading through the file three times via `seekg` doesn't work
1090 : // with our gzstream wrapper, and seems to take three times as long
1091 : // even with a plain ifstream. What happened to disk caching!?
1092 96 : std::vector<std::tuple<numeric_index_type, numeric_index_type, T>> entries;
1093 :
1094 : // First read through the file, saving size and entry data
1095 : {
1096 : // We'll read the matrix on processor 0 rather than try to juggle
1097 : // parallel I/O.
1098 1607 : if (this->processor_id() == 0)
1099 : {
1100 : // We'll be using regular expressions to make ourselves slightly
1101 : // more robust to formatting.
1102 800 : const std::regex start_regex // assignment like "zzz = ["
1103 : ("\\s*\\w+\\s*=\\s*\\[");
1104 800 : const std::regex end_regex // end of assignment
1105 : ("^[^%]*\\]");
1106 :
1107 732 : if (gzipped_file)
1108 : {
1109 : #ifdef LIBMESH_HAVE_GZSTREAM
1110 345 : auto inf = std::make_unique<igzstream>();
1111 14 : libmesh_assert(inf);
1112 14 : inf->open(filename.c_str(), std::ios::in);
1113 317 : file = std::move(inf);
1114 : #else
1115 : libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
1116 : #endif
1117 303 : }
1118 : else
1119 : {
1120 421 : auto inf = std::make_unique<std::ifstream>();
1121 20 : libmesh_assert(inf);
1122 :
1123 421 : std::string new_name = Utility::unzip_file(filename);
1124 :
1125 401 : inf->open(new_name.c_str(), std::ios::in);
1126 381 : file = std::move(inf);
1127 361 : }
1128 :
1129 : // If we have a matrix with all-zero trailing rows, the only
1130 : // way to get the size is if it ended up in a comment
1131 800 : const std::regex size_regex // comment like "% size = 8 8"
1132 : ("%\\s*[Ss][Ii][Zz][Ee]\\s*=\\s*(\\d+)\\s+(\\d+)");
1133 766 : const std::string whitespace = " \t";
1134 :
1135 34 : bool have_started = false;
1136 34 : bool have_ended = false;
1137 732 : std::size_t largest_i_seen = 0, largest_j_seen = 0;
1138 :
1139 : // Data for the row we're working on
1140 : // Use 1-based indexing for current_row, as in the file
1141 732 : std::size_t current_row = 1;
1142 :
1143 343420 : for (std::string line; std::getline(*file, line);)
1144 : {
1145 7780 : std::smatch sm;
1146 :
1147 : // First, try to match an entry. This is the most common
1148 : // case so we won't rely on slow std::regex for it.
1149 : // stringstream is at least an improvement over that.
1150 :
1151 : // Look for row/col/val like "1 1 -2.0e-4"
1152 :
1153 187219 : std::istringstream l(line);
1154 :
1155 : std::size_t i, j;
1156 1822 : T value;
1157 :
1158 9602 : l >> i >> j >> value;
1159 :
1160 179473 : if (!l.fail())
1161 : {
1162 174349 : libmesh_error_msg_if
1163 : (!have_started, "Confused by premature entries in matrix file " << filename);
1164 :
1165 331841 : entries.emplace_back(cast_int<numeric_index_type>(i),
1166 181891 : cast_int<numeric_index_type>(j),
1167 : value);
1168 :
1169 174349 : libmesh_error_msg_if
1170 : (!i || !j, "Expected 1-based indexing in matrix file "
1171 : << filename);
1172 :
1173 174349 : current_row = std::max(current_row, i);
1174 :
1175 174349 : libmesh_error_msg_if
1176 : (i < current_row,
1177 : "Can't handle out-of-order entries in matrix file "
1178 : << filename);
1179 :
1180 174349 : largest_i_seen = std::max(i, largest_i_seen);
1181 174349 : largest_j_seen = std::max(j, largest_j_seen);
1182 : }
1183 :
1184 5124 : else if (std::regex_search(line, sm, size_regex))
1185 : {
1186 766 : const std::string msize = sm[1];
1187 732 : const std::string nsize = sm[2];
1188 732 : m = std::stoull(msize);
1189 732 : n = std::stoull(nsize);
1190 : }
1191 :
1192 4392 : else if (std::regex_search(line, start_regex))
1193 34 : have_started = true;
1194 :
1195 3660 : else if (std::regex_search(line, end_regex))
1196 : {
1197 34 : have_ended = true;
1198 68 : break;
1199 : }
1200 : }
1201 :
1202 732 : libmesh_error_msg_if
1203 : (!have_started, "Confused by missing assignment beginning in matrix file " << filename);
1204 :
1205 732 : libmesh_error_msg_if
1206 : (!have_ended, "Confused by missing assignment ending in matrix file " << filename);
1207 :
1208 732 : libmesh_error_msg_if
1209 : (m > largest_i_seen, "Confused by missing final row(s) in matrix file " << filename);
1210 :
1211 732 : libmesh_error_msg_if
1212 : (m > 0 && m < largest_i_seen, "Confused by extra final row(s) in matrix file " << filename);
1213 :
1214 732 : if (!m)
1215 0 : m = largest_i_seen;
1216 :
1217 732 : libmesh_error_msg_if
1218 : (n > largest_j_seen, "Confused by missing final column(s) in matrix file " << filename);
1219 :
1220 732 : libmesh_error_msg_if
1221 : (n > 0 && n < largest_j_seen, "Confused by extra final column(s) in matrix file " << filename);
1222 :
1223 732 : if (!n)
1224 0 : n = largest_j_seen;
1225 :
1226 732 : this->comm().broadcast(m);
1227 732 : this->comm().broadcast(n);
1228 664 : }
1229 : else
1230 : {
1231 813 : this->comm().broadcast(m);
1232 827 : this->comm().broadcast(n);
1233 : }
1234 :
1235 1559 : if (this->initialized() &&
1236 1561 : m == this->m() &&
1237 70 : n == this->n())
1238 : {
1239 70 : new_row_start = this->row_start(),
1240 70 : new_row_stop = this->row_stop();
1241 :
1242 72 : new_col_start = this->col_start(),
1243 70 : new_col_stop = this->col_stop();
1244 : }
1245 : else
1246 : {
1247 : // Determine which rows/columns each processor will be in charge of
1248 1535 : new_row_start = this->processor_id() * m / this->n_processors(),
1249 1489 : new_row_stop = (this->processor_id()+1) * m / this->n_processors();
1250 :
1251 1535 : new_col_start = this->processor_id() * n / this->n_processors(),
1252 1489 : new_col_stop = (this->processor_id()+1) * n / this->n_processors();
1253 : }
1254 :
1255 1559 : this->comm().gather(0, new_row_start, new_row_starts);
1256 1559 : this->comm().gather(0, new_row_stop, new_row_stops);
1257 1559 : this->comm().gather(0, new_col_start, new_col_starts);
1258 1559 : this->comm().gather(0, new_col_stop, new_col_stops);
1259 :
1260 : } // Done reading entry data and broadcasting matrix size
1261 :
1262 : // Calculate the matrix sparsity and initialize it second
1263 : {
1264 : // Deduce the sparsity pattern, or at least the maximum number of
1265 : // on- and off- diagonal non-zeros per row.
1266 1559 : numeric_index_type on_diagonal_nonzeros =0,
1267 1559 : off_diagonal_nonzeros =0;
1268 :
1269 1607 : if (this->processor_id() == 0)
1270 : {
1271 : // Data for the row we're working on
1272 : // Use 1-based indexing for current_row, as in the file
1273 34 : numeric_index_type current_row = 1;
1274 34 : processor_id_type current_proc = 0;
1275 732 : numeric_index_type current_on_diagonal_nonzeros = 0;
1276 732 : numeric_index_type current_off_diagonal_nonzeros = 0;
1277 :
1278 175081 : for (auto [i, j, value] : entries)
1279 : {
1280 174349 : if (i > current_row)
1281 : {
1282 897 : current_row = i;
1283 : // +1 for 1-based indexing in file
1284 23009 : while (current_row >= (new_row_stops[current_proc]+1))
1285 827 : ++current_proc;
1286 21271 : current_on_diagonal_nonzeros = 0;
1287 21271 : current_off_diagonal_nonzeros = 0;
1288 : }
1289 :
1290 : // +1 for 1-based indexing in file
1291 188557 : if (j >= (new_col_starts[current_proc]+1) &&
1292 169430 : j < (new_col_stops[current_proc]+1))
1293 : {
1294 147732 : ++current_on_diagonal_nonzeros;
1295 147732 : on_diagonal_nonzeros =
1296 : std::max(on_diagonal_nonzeros,
1297 5684 : current_on_diagonal_nonzeros);
1298 : }
1299 : else
1300 : {
1301 26617 : ++current_off_diagonal_nonzeros;
1302 26617 : off_diagonal_nonzeros =
1303 : std::max(off_diagonal_nonzeros,
1304 1858 : current_off_diagonal_nonzeros);
1305 : }
1306 : }
1307 : }
1308 :
1309 1559 : this->comm().broadcast(on_diagonal_nonzeros);
1310 1559 : this->comm().broadcast(off_diagonal_nonzeros);
1311 :
1312 1559 : this->init(m, n,
1313 : new_row_stop-new_row_start,
1314 : new_col_stop-new_col_start,
1315 : on_diagonal_nonzeros,
1316 : off_diagonal_nonzeros);
1317 : }
1318 :
1319 : // Set the matrix values last.
1320 : // Convert from 1-based to 0-based indexing
1321 1607 : if (this->processor_id() == 0)
1322 175081 : for (auto [i, j, value] : entries)
1323 174349 : this->set(i-1, j-1, value);
1324 :
1325 1559 : this->close();
1326 : #endif
1327 3022 : }
1328 :
1329 :
1330 :
1331 : template <typename T>
1332 0 : void SparseMatrix<T>::read_petsc_binary(const std::string &)
1333 : {
1334 0 : libmesh_not_implemented_msg
1335 : ("libMesh cannot read PETSc binary-format files into non-PETSc matrices");
1336 : }
1337 :
1338 :
1339 :
1340 : template <typename T>
1341 0 : void SparseMatrix<T>::read_petsc_hdf5(const std::string &)
1342 : {
1343 0 : libmesh_not_implemented_msg
1344 : ("libMesh cannot read PETSc HDF5-format files into non-PETSc matrices");
1345 : }
1346 :
1347 :
1348 :
1349 : template <typename T>
1350 75 : void SparseMatrix<T>::scale(const T scale)
1351 : {
1352 4 : libmesh_assert(this->closed());
1353 :
1354 375 : for (const auto i : make_range(this->row_start(), this->row_stop()))
1355 1500 : for (const auto j : make_range(this->col_start(), this->col_stop()))
1356 1200 : this->set(i, j, (*this)(i, j) * scale);
1357 75 : }
1358 :
1359 :
1360 :
1361 : template <typename T>
1362 290 : Real SparseMatrix<T>::l1_norm_diff(const SparseMatrix<T> & other_mat) const
1363 : {
1364 290 : auto diff_mat = this->clone();
1365 290 : diff_mat->add(-1.0, other_mat);
1366 580 : return diff_mat->l1_norm();
1367 266 : }
1368 :
1369 : //------------------------------------------------------------------
1370 : // Explicit instantiations
1371 : template class LIBMESH_EXPORT SparseMatrix<Number>;
1372 :
1373 : } // namespace libMesh
|