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