BELFEM 0.9.0
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
cl_SpMatrix.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef BELFEM_CL_SPMATRIX_HPP
13#define BELFEM_CL_SPMATRIX_HPP
14
15#include <cstring>
16
17#include "typedefs.hpp"
18#include "cl_Cell.hpp"
19#include "cl_Graph_Vertex.hpp"
20#include "cl_Vector.hpp"
21#include "filetools.hpp"
22#include "hdf5_tools.hpp"
23
24namespace belfem
25{
26//------------------------------------------------------------------------------
27
28 enum class SpMatrixType
29 {
30 CSC = 0, // compressed sparse column
31 CSR = 1, // compressed sparse row
32 UNDEFINED = 2
33 };
34
35//------------------------------------------------------------------------------
36
38 {
39 Cpp = 0,
41 };
42
43//------------------------------------------------------------------------------
44
52 {
53//------------------------------------------------------------------------------
54 private:
55//------------------------------------------------------------------------------
56
57 SpMatrix * mParent = nullptr ;
58 SpMatrix * mChild = nullptr ;
59
60 // type of matrix, CSC or CSR
61 SpMatrixType mType;
62
63 // size of matrix
64 int_t mNumRows = 0;
65 int_t mNumCols = 0;
66
67 // number of nonzeros
68 int_t mNumNonZeros = 0;
69
70 // size of pointer array
71 int_t mPointerSize = 0;
72
73 // container for pointers
74 int_t * mPointers = nullptr;
75
76 int_t * mRows = nullptr;
77
78 int_t * mColumns = nullptr;
79
80 // values array
81 real * mValues = nullptr;
82
83 bool mHaveCooIndices = false;
84
85#ifdef BELFEM_NETLIB
86 Vector< real > mSwap ;
87 int_t mSwapSize = 0;
88#endif
89
90 // pointer with zero value
91 real mZero = 0.0;
92
93 // Note: the lookup used to dispatch through a member-function
94 // pointer over four near-identical search functions, one per
95 // ( type, base ) pair. It is now the inline position() below, which
96 // reads the base from mPointers[ 0 ] on every call. That is not only
97 // faster - it removes a stale-state bug: a parent and its child share
98 // the index arrays, and rebasing the parent did not update a child's
99 // cached function pointer.
100
101//------------------------------------------------------------------------------
102 public:
103//------------------------------------------------------------------------------
104
105 // empty constructor
106 SpMatrix() = default;
107
108 // raw-owning class: copy and move construction are not supported,
109 // use the assignment operators on an empty matrix instead
110 SpMatrix( const SpMatrix & ) = delete;
111 SpMatrix( SpMatrix && ) = delete;
112
113//------------------------------------------------------------------------------
114
115 // standard constructor using a graph
116 SpMatrix( Graph & aGraph,
117 const enum SpMatrixType aType = SpMatrixType::CSC,
118 const index_t aNumRows = 0,
119 const index_t aNumCols = 0,
120 const bool aSortGraph = true );
121
122//------------------------------------------------------------------------------
123
124 SpMatrix( SpMatrixType aType,
125 const index_t aNumRows,
126 const index_t aNumCols,
127 const index_t aNumNonZeros,
128 const int_t * aIndices,
129 const int_t * aPointers );
130
131//------------------------------------------------------------------------------
132
133 // file constructor using a HDF5 file
134 SpMatrix( const string & aHDF5Path, const string aLabel="Matrix" );
135
136//------------------------------------------------------------------------------
137
138 SpMatrix( const hid_t aParent, const string aLabel="Matrix" );
139
140//------------------------------------------------------------------------------
141
142 // constructor for testing purposes using dense Matrix
143 SpMatrix( const Matrix< real > & aMatrix, const SpMatrixType aType = SpMatrixType::CSC );
144
145//------------------------------------------------------------------------------
146
147 // child constructor: shares the sparsity structure of the parent,
148 // owns only its value array
149 explicit SpMatrix( SpMatrix * aParent );
150
151//------------------------------------------------------------------------------
152
153 ~SpMatrix();
154
155//------------------------------------------------------------------------------
156// Access to data containers
157//------------------------------------------------------------------------------
158
162 const SpMatrixType &
163 type() const;
164
165//------------------------------------------------------------------------------
166
170 void
171 fill( const real aValue );
172
173//------------------------------------------------------------------------------
174
178 index_t
179 n_rows() const;
180
181//------------------------------------------------------------------------------
182
186 index_t
187 n_cols() const;
188
189//------------------------------------------------------------------------------
190
191 index_t
192 n_pointers() const ;
193
194//------------------------------------------------------------------------------
195
196 size_t
197 memory() const ;
198
199//------------------------------------------------------------------------------
200
201 bool
202 have_coo_indices() const ;
203
204//------------------------------------------------------------------------------
205
209 void
210 set_type( const SpMatrixType aType );
211
212//------------------------------------------------------------------------------
213
217 index_t
218 number_of_nonzeros() const;
219
220//------------------------------------------------------------------------------
221
225 int_t *
226 pointers();
227
228//------------------------------------------------------------------------------
229
233 const int_t *
234 pointers() const;
235
236//------------------------------------------------------------------------------
237
241 int_t *
242 indices();
243
244//------------------------------------------------------------------------------
245
249 const int_t *
250 indices() const;
251
252//------------------------------------------------------------------------------
253
257 int_t *
258 rows();
259
260//------------------------------------------------------------------------------
261
265 const int_t *
266 rows() const;
267
268//------------------------------------------------------------------------------
269
273 int_t *
274 cols();
275
276//------------------------------------------------------------------------------
277
281 const int_t *
282 cols() const;
283
284//------------------------------------------------------------------------------
285
289 real *
290 data();
291
292//------------------------------------------------------------------------------
293
297 const real *
298 data() const;
299
300//------------------------------------------------------------------------------
301
305 real &
306 data( const index_t aIndex );
307
308//------------------------------------------------------------------------------
309
313 const real &
314 data( const index_t aIndex ) const;
315
316
317//------------------------------------------------------------------------------
318
319 /*
320 * get the index of a specific row and col
321 *
322 * Returns the position into the value array, or number_of_nonzeros()
323 * when the entry is not part of the sparsity pattern. Thin wrapper
324 * around position().
325 * */
326 int_t
327 index( const index_t aRowIndex,
328 const index_t aColIndex ) const;
329
330//------------------------------------------------------------------------------
331
340 int_t
341 position( const index_t aRowIndex,
342 const index_t aColIndex ) const;
343
344//------------------------------------------------------------------------------
345
361 void
362 positions_in_slice( const index_t aSlice,
363 const int_t * aCols,
364 const uint aNumCols,
365 int_t * aPos ) const;
366
367//------------------------------------------------------------------------------
368// Utilities
369//------------------------------------------------------------------------------
370
380 void
381 sort_entries();
382
383//------------------------------------------------------------------------------
384
388 void
389 set_indexing_base( const enum SpMatrixIndexingBase & aBasis );
390
391//------------------------------------------------------------------------------
392
396 void
398
399//------------------------------------------------------------------------------
400
404 void
406
407//------------------------------------------------------------------------------
408
412 void
413 print( const string aLabel="SparseMatrix" );
414
415//------------------------------------------------------------------------------
416
420 void
421 print2( const string aLabel="SparseMatrix" );
422
423//------------------------------------------------------------------------------
424
430 int_t
431 indexing_base() const;
432
433//------------------------------------------------------------------------------
434
445 void
446 multiply( const Vector< real > & aX,
447 Vector< real > & aY,
448 const real aAlpha,
449 const real aBeta,
450 const bool aTransposedFlag=false );
451
452//------------------------------------------------------------------------------
453
457 void
458 multiply( const Vector< real > & aX,
459 Vector< real > & aY );
460
461//------------------------------------------------------------------------------
462
463 void
464 transpose();
465
466//------------------------------------------------------------------------------
467// Saving and Loading
468//------------------------------------------------------------------------------
469
477 void
478 save( const string & aPath,
479 const string aLabel="Matrix",
480 const enum FileMode aMode=FileMode::NEW );
481
482//------------------------------------------------------------------------------
483
487 void
488 save( hid_t & aGroup,
489 herr_t & aStatus );
490
491//------------------------------------------------------------------------------
492
499 void
500 load( const string & aPath,
501 const string aLabel="Matrix"
502 );
503
504//------------------------------------------------------------------------------
505
509 void
510 load( hid_t & aGroup,
511 herr_t & aStatus );
512
513
514//------------------------------------------------------------------------------
515// Operators
516//------------------------------------------------------------------------------
517
521 real &
522 operator()( const index_t & aRowIndex,
523 const index_t & aColIndex );
524
525//------------------------------------------------------------------------------
526
530 const real &
531 operator()( const index_t & aRowIndex,
532 const index_t & aColIndex ) const;
533//------------------------------------------------------------------------------
534
538 SpMatrix &
539 operator=( const SpMatrix & aMatrix );
540
541//------------------------------------------------------------------------------
542
546 SpMatrix &
547 operator=( SpMatrix && aMatrix );
548
549//------------------------------------------------------------------------------
550 private:
551//------------------------------------------------------------------------------
552// Initialization
553//------------------------------------------------------------------------------
554
558 void
559 check_graph( Graph & aGraph );
560
561//------------------------------------------------------------------------------
562
566 index_t
567 tidy_graph( Graph & aGraph );
568
569//------------------------------------------------------------------------------
570
571 void
572 create_csr_indices( Graph & aGraph );
573
574//------------------------------------------------------------------------------
575
576 void
577 create_csc_indices( Graph & aGraph );
578
579//------------------------------------------------------------------------------
580
584 void
585 set_sizes(
586 const index_t aNumRows,
587 const index_t aNumCols );
588
589 void
590 set_nnz( const index_t aNumberOfNonzeros );
591
592//------------------------------------------------------------------------------
593
597 void
598 deallocate();
599
600//------------------------------------------------------------------------------
601
605 void
606 allocate_values();
607
608//------------------------------------------------------------------------------
609
610#ifdef BELFEM_NETLIB
614 void
615 allocate_swap();
616#endif
617
618//------------------------------------------------------------------------------
619// Parent/Child logic
620//------------------------------------------------------------------------------
621
622 void
623 set_child( SpMatrix * aChild );
624
625//------------------------------------------------------------------------------
626
627 void
628 update_from_parent();
629
630//------------------------------------------------------------------------------
631 };
632
633
634//------------------------------------------------------------------------------
635// external operators
636//------------------------------------------------------------------------------
637
642 inline operator * ( SpMatrix & aA,
643 const Vector<real> & aX )
644 {
645 Vector<real> aY( aA.n_rows(), 0.0 );
646 aA.multiply( aX, aY );
647 return aY ;
648 }
649
650//------------------------------------------------------------------------------
651
652 inline const SpMatrixType &
654 {
655 return mType;
656 }
657
658//------------------------------------------------------------------------------
659
660 inline index_t
662 {
663 return ( index_t ) mNumRows;
664 }
665
666//------------------------------------------------------------------------------
667
668 inline index_t
670 {
671 return ( index_t ) mNumCols;
672 }
673
674//------------------------------------------------------------------------------
675
676 inline index_t
678 {
679 return ( index_t ) mPointerSize;
680 }
681
682//------------------------------------------------------------------------------
683
684 inline bool
686 {
687 return mHaveCooIndices;
688 }
689
690//------------------------------------------------------------------------------
691
692 inline index_t
694 {
695 return ( index_t ) mNumNonZeros;
696 }
697
698//------------------------------------------------------------------------------
699
700 inline int_t *
702 {
703 if( mType == SpMatrixType::CSC )
704 {
705 return mRows;
706 }
707 else if ( mType == SpMatrixType::CSR )
708 {
709 return mColumns;
710 }
711 else
712 {
713 return nullptr;
714 }
715 }
716
717//------------------------------------------------------------------------------
718
719 inline const int_t *
721 {
722 if( mType == SpMatrixType::CSC )
723 {
724 return mRows;
725 }
726 else if ( mType == SpMatrixType::CSR )
727 {
728 return mColumns;
729 }
730 else
731 {
732 return nullptr;
733 }
734 }
735
736//------------------------------------------------------------------------------
737
738 inline int_t *
740 {
741 return mRows;
742 }
743
744//------------------------------------------------------------------------------
745
746 inline const int_t *
748 {
749 return mRows;
750 }
751
752//------------------------------------------------------------------------------
753
754 inline int_t *
756 {
757 return mColumns;
758 }
759
760//------------------------------------------------------------------------------
761
762 inline const int_t *
764 {
765 return mColumns;
766 }
767
768//------------------------------------------------------------------------------
769
770 inline int_t *
772 {
773 return mPointers;
774 }
775
776//------------------------------------------------------------------------------
777
778 const inline int_t *
780 {
781 return mPointers;
782 }
783
784//------------------------------------------------------------------------------
785
786 inline real *
788 {
789 return mValues;
790 }
791
792//------------------------------------------------------------------------------
793
794 inline const real *
796 {
797 return mValues;
798 }
799
800//------------------------------------------------------------------------------
801
802 inline real &
803 SpMatrix::data( const index_t aIndex )
804 {
805 BELFEM_ASSERT( aIndex < ( index_t ) mNumNonZeros,
806 "Index %lu for sparse matix out of bounds ( must be less than %lu )",
807 ( long unsigned int ) aIndex,
808 ( long unsigned int ) mNumNonZeros );
809
810 return mValues[ aIndex ];
811 }
812
813//------------------------------------------------------------------------------
814
815 inline const real &
816 SpMatrix::data( const index_t aIndex ) const
817 {
818 BELFEM_ASSERT( aIndex < ( index_t ) mNumNonZeros,
819 "Index %lu for sparse matix out of bounds ( must be less than %lu )",
820 ( long unsigned int ) aIndex,
821 ( long unsigned int ) mNumNonZeros );
822
823 return mValues[ aIndex ];
824 }
825
826//------------------------------------------------------------------------------
827
828 inline int_t
830 {
831 return mPointers != nullptr ? mPointers[ 0 ] : 0;
832 }
833
834//------------------------------------------------------------------------------
835
836 inline int_t
837 SpMatrix::position( const index_t aRowIndex, const index_t aColIndex ) const
838 {
839 BELFEM_ASSERT( aRowIndex < ( index_t ) mNumRows, "aRowIndex out of bounds ( %lu >= %lu )",
840 ( long unsigned int ) aRowIndex ,
841 ( long unsigned int ) mNumRows );
842
843 BELFEM_ASSERT( aColIndex < ( index_t ) mNumCols, "aColIndex out of bounds ( %lu >= %lu )",
844 ( long unsigned int ) aColIndex ,
845 ( long unsigned int ) mNumCols );
846
847 // CSR searches a row for a column, CSC a column for a row. Both the
848 // type and the base are loop-invariant in every caller, so these
849 // branches predict perfectly and the whole function inlines.
850 const int_t tBase = mPointers[ 0 ];
851 const bool tIsCsr = ( mType == SpMatrixType::CSR );
852
853 const index_t tSlice = tIsCsr ? aRowIndex : aColIndex ;
854 const int_t tTarget = ( int_t )( tIsCsr ? aColIndex : aRowIndex ) + tBase ;
855
856 const int_t * tIndices = tIsCsr ? mColumns : mRows ;
857
858 const int_t tBegin = mPointers[ tSlice ] - tBase ;
859 const int_t tEnd = mPointers[ tSlice + 1 ] - tBase ;
860
861 // Binary search, always. A linear scan with an early exit was tried
862 // for short slices on the theory that FE rows are only tens of
863 // entries long. On the machine it was measured on it lost at every
864 // row length ( 8, 16, 24, 32, 50 ): a slice sits in one or two cache
865 // lines, so lower_bound costs ~6 well-predicted steps while the scan
866 // averages k/2 iterations behind a data-dependent branch.
867 //
868 // A second reviewer running the same experiment on different hardware
869 // did see linear win at 8 entries per row, so the crossover is
870 // microarchitecture-dependent and this is not a universal result. The
871 // threshold knob was removed rather than guessed at; if it is ever
872 // revisited, re-measure on the production nodes rather than trusting
873 // either of those numbers. Both agree the difference is small next to
874 // what the assembly restructure targets.
875 // See devlog/dl20260818_spmatrix_accessor.md.
876 //
877 // ( indices within a slice are sorted, see sort_entries )
878 const int_t * tFound = std::lower_bound( tIndices + tBegin,
879 tIndices + tEnd,
880 tTarget );
881
882 return ( tFound < tIndices + tEnd && *tFound == tTarget )
883 ? ( int_t )( tFound - tIndices )
884 : mNumNonZeros ;
885 }
886
887//------------------------------------------------------------------------------
888
889 inline int_t
890 SpMatrix::index( const index_t aRowIndex, const index_t aColIndex ) const
891 {
892 return this->position( aRowIndex, aColIndex );
893 }
894
895//------------------------------------------------------------------------------
896
900 inline real &
901 SpMatrix::operator()( const index_t & aRowIndex,
902 const index_t & aColIndex )
903 {
904 BELFEM_ASSERT( this->indexing_base() == 0,
905 "operator() called while matrix is in Fortran indexing mode. "
906 "Call set_indexing_base( SpMatrixIndexingBase::Cpp ) first." );
907
908 const int_t tIndex = this->position( aRowIndex, aColIndex );
909
910 BELFEM_ASSERT( tIndex < mNumNonZeros, "tried to access zero value in writable mode( %lu, %lu )",
911 ( long unsigned int ) aRowIndex,
912 ( long unsigned int ) aColIndex );
913
914 return mValues[ tIndex ];
915 }
916
917//------------------------------------------------------------------------------
918
919 inline const real &
920 SpMatrix::operator()( const index_t & aRowIndex,
921 const index_t & aColIndex ) const
922 {
923 BELFEM_ASSERT( this->indexing_base() == 0,
924 "operator() called while matrix is in Fortran indexing mode. "
925 "Call set_indexing_base( SpMatrixIndexingBase::Cpp ) first." );
926
927 const int_t tIndex = this->position( aRowIndex, aColIndex );
928
929 if( tIndex < mNumNonZeros )
930 {
931 return mValues[ tIndex ];
932 }
933 else
934 {
935 return mZero;
936 }
937 }
938
939//------------------------------------------------------------------------------
940}
941
942#endif //BELFEM_CL_SPMATRIX_HPP
#define BELFEM_ASSERT(aCheck,...)
Definition assert.hpp:244
Sparse matrix in CSR or CSC format.
Definition cl_SpMatrix.hpp:52
void fill(const real aValue)
write a specific value into all entries of the value container
Definition cl_SpMatrix.cpp:722
index_t n_cols() const
number of columns of this matrix
Definition cl_SpMatrix.hpp:669
bool have_coo_indices() const
Definition cl_SpMatrix.hpp:685
SpMatrix(const SpMatrix &)=delete
void set_indexing_base(const enum SpMatrixIndexingBase &aBasis)
change the indexing base
Definition cl_SpMatrix.cpp:814
void load(const string &aPath, const string aLabel="Matrix")
load matrix from a hdf5 file
Definition cl_SpMatrix.cpp:1248
index_t n_rows() const
number of rows of this matrix
Definition cl_SpMatrix.hpp:661
int_t * indices()
expose the index array
Definition cl_SpMatrix.hpp:701
real * data()
expose the data container
Definition cl_SpMatrix.hpp:787
void free_coo_indices()
delete additional indices that are needed by MUMPS
Definition cl_SpMatrix.cpp:1020
real & operator()(const index_t &aRowIndex, const index_t &aColIndex)
access a specific value with write access
Definition cl_SpMatrix.hpp:901
index_t n_pointers() const
Definition cl_SpMatrix.hpp:677
int_t * cols()
expose the col indices
Definition cl_SpMatrix.hpp:755
void set_type(const SpMatrixType aType)
sets the type, must be called after set_sizes
Definition cl_SpMatrix.cpp:1981
SpMatrix()=default
index_t number_of_nonzeros() const
number of nonzero values in this matrix
Definition cl_SpMatrix.hpp:693
void multiply(const Vector< real > &aX, Vector< real > &aY, const real aAlpha, const real aBeta, const bool aTransposedFlag=false)
performs a matrix-vector multiplication
Definition cl_SpMatrix.cpp:1617
int_t * pointers()
expose the pointers
Definition cl_SpMatrix.hpp:771
int_t indexing_base() const
returns the basis type of the matrix 0: c++ indexing 1: fortran indexing
Definition cl_SpMatrix.hpp:829
int_t index(const index_t aRowIndex, const index_t aColIndex) const
Definition cl_SpMatrix.hpp:890
void create_coo_indices()
create addidional indices that are needed by MUMPS
Definition cl_SpMatrix.cpp:902
size_t memory() const
Definition cl_SpMatrix.cpp:1991
void print(const string aLabel="SparseMatrix")
print_t the matrix to the screen ( for debugging )
Definition cl_SpMatrix.cpp:1094
void positions_in_slice(const index_t aSlice, const int_t *aCols, const uint aNumCols, int_t *aPos) const
Batched lookup: positions of aNumCols entries of one slice ( a row for CSR, a column for CSC ) in a s...
Definition cl_SpMatrix.cpp:1488
SpMatrix(SpMatrix &&)=delete
void print2(const string aLabel="SparseMatrix")
print_t the container indices on the screen ( for debugging )
Definition cl_SpMatrix.cpp:1133
SpMatrix & operator=(const SpMatrix &aMatrix)
copy operator
Definition cl_SpMatrix.cpp:1775
int_t * rows()
expose the row indices
Definition cl_SpMatrix.hpp:739
void transpose()
Definition cl_SpMatrix.cpp:1925
const SpMatrixType & type() const
return the data type
Definition cl_SpMatrix.hpp:653
void sort_entries()
Ensure that the indices within each row (CSR) or column (CSC) are sorted in ascending order.
Definition cl_SpMatrix.cpp:730
int_t position(const index_t aRowIndex, const index_t aColIndex) const
Position of ( aRowIndex, aColIndex ) in the value array, or mNumNonZeros when the entry is not in the...
Definition cl_SpMatrix.hpp:837
void save(const string &aPath, const string aLabel="Matrix", const enum FileMode aMode=FileMode::NEW)
save matrix to a hdf5 file
Definition cl_SpMatrix.cpp:1162
USER GUIDES:
Definition cl_Capacitor.cpp:16
Cell< graph::Vertex * > Graph
Definition cl_Graph_Vertex.hpp:329
int hid_t
Definition hdf5_types.hpp:20
SpMatrixType
Definition cl_SpMatrix.hpp:29
@ CSC
Definition cl_SpMatrix.hpp:30
@ CSR
Definition cl_SpMatrix.hpp:31
SpMatrixIndexingBase
Definition cl_SpMatrix.hpp:38
@ Fortran
Definition cl_SpMatrix.hpp:40
@ Cpp
Definition cl_SpMatrix.hpp:39
unsigned int uint
Definition typedefs.hpp:30
auto operator*(const Matrix< T > &aA, const Matrix< T > &aB) -> decltype(aA.matrix_data() *aB.matrix_data())
Definition op_MatrixTimes.hpp:24
FileMode
Definition filetools.hpp:27
@ NEW
Definition filetools.hpp:28
uint32_t index_t
Definition typedefs.hpp:52
int herr_t
Definition hdf5_types.hpp:21
double real
Definition typedefs.hpp:36
int32_t int_t
Definition typedefs.hpp:51