1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264 | // *****************************************************************************
/*!
\file src/LinearSolver/CSR.cpp
\copyright 2012-2015 J. Bakosi,
2016-2018 Los Alamos National Security, LLC.,
2019-2021 Triad National Security, LLC.
All rights reserved. See the LICENSE file for details.
\brief Compressed sparse row (CSR) storage for a sparse matrix
\details Compressed sparse row (CSR) storage for a sparse matrix.
*/
// *****************************************************************************
#include "Exception.hpp"
#include "CSR.hpp"
using tk::CSR;
CSR::CSR( std::size_t nc,
const std::pair< std::vector< std::size_t >,
std::vector< std::size_t > >& psup )
try :<--- syntax error: keyword 'try' is not allowed in global scope
ncomp( nc ),
rnz( psup.second.size()-1 ),
ia( rnz.size()*ncomp+1 )
// *****************************************************************************
// Constructor: Create a CSR symmetric matrix with ncomp scalar components per
// non-zero matrix entry, storing only the upper triangular part
//! \param[in] nc Number of scalar components (degrees of freedom)
//! \param[in] psup Points surrounding points of mesh graph, see tk::genPsup
// *****************************************************************************
{
Assert( ncomp > 0, "Sparse matrix ncomp must be positive" );
Assert( rnz.size() > 0, "Sparse matrix size must be positive" );
const auto& psup1 = psup.first;
const auto& psup2 = psup.second;
// Calculate number of nonzeros in each block row (rnz), total number of
// nonzeros (nnz), and fill in row indices (ia)
std::size_t nnz, i;
for (ia[0]=1, nnz=i=0; i<psup2.size()-1; ++i) {
// add up and store nonzeros of row i (only upper triangular part)
std::size_t j;
for (rnz[i]=1, j=psup2[i]+1; j<=psup2[i+1]; ++j)
++rnz[i];
// add up total number of nonzeros
nnz += rnz[i] * ncomp;
// fill up row index
for (std::size_t k=0; k<ncomp; ++k)
ia[i*ncomp+k+1] = ia[i*ncomp+k] + rnz[i];
}
// Allocate storage for matrix values and column indices
a.resize( nnz, 0.0 );
ja.resize( nnz );
// fill column indices
for (i=0; i<rnz.size(); ++i)
for (std::size_t k=0; k<ncomp; ++k) {
auto itmp = i*ncomp+k;
ja[ia[itmp]-1] = itmp+1; // put in column index of diagonal
for (std::size_t n=1, j=psup2[i]+1; j<=psup2[i+1]; ++j) {
// put in column index of an off-diagonal
ja[ia[itmp]-1+(n++)] = psup1[j]*ncomp+k+1;
}
}
// (bubble-)sort column indices
for (i=0; i<rnz.size(); ++i)
for (std::size_t k=0; k<ncomp; ++k)
for (std::size_t j=psup2[i]+1; j<=psup2[i+1]; ++j)
for (std::size_t l=1; l<rnz[i]; ++l) // sort column indices of row i
for (std::size_t e=0; e<rnz[i]-l; ++e)
if (ja[ia[i*ncomp+k]-1+e] > ja[ia[i*ncomp+k]+e])
std::swap( ja[ia[i*ncomp+k]-1+e], ja[ia[i*ncomp+k]+e] );
} // Catch std::exception
catch (std::exception& se) {
// (re-)throw tk::Excpetion
Throw( std::string("RUNTIME ERROR in CSR constructor: ") + se.what() );
}
const tk::real&
CSR::operator()( std::size_t row, std::size_t col, std::size_t pos ) const
// *****************************************************************************
// Return const reference to sparse matrix entry at a position specified
// using relative addressing
//! \param[in] row Block row
//! \param[in] col Block column
//! \param[in] pos Position in block
//! \return Const reference to matrix entry at position specified
// *****************************************************************************
{
auto rncomp = row * ncomp;
for (std::size_t n=0, j=ia[rncomp+pos]-1; j<ia[rncomp+pos+1]-1; ++j, ++n)
if (col*ncomp+pos+1 == ja[j])
return a[ia[rncomp+pos]-1+n];
Throw("Sparse matrix index not found");
}
void
CSR::dirichlet( std::size_t i,
const std::vector< std::size_t >& gid,
const NodeCommMap& nodecommap,
std::size_t pos )
// *****************************************************************************
// Set Dirichlet boundary condition at a node
//! \param[in] i Local id at which to set Dirichlet BC
//! \param[in] gid Local->global node id map
//! \param[in] nodecommap Node communication map with global node ids
//! \param[in] pos Position in block
//! \details In parallel there can be multiple contributions to a single node
//! on the mesh, and correspondingly, a single matrix row can be partially
//! represented on multiple partitions. Setting a Dirichlet BC entails
//! zeroing out the row of the matrix and putting 1/N into the diagonal entry,
//! where N is the number of partitions that contribute to the mesh node
//! (matrix row). As a result, when the matrix participates in a matrix-vector
//! product, where the partial contributions across all partitions are
//! aggregated, the diagonal will contain 1 after the sum across partitions.
//! \note Both gid and nodecommap are optional - unused in serial. If nodecommap
//! is empty, serial is assumed and gid is unused.
// *****************************************************************************
{
auto incomp = i * ncomp;
auto diag = nodecommap.empty() ? 1.0 : 1.0/tk::count(nodecommap,gid[i]);
// zero column
for (std::size_t r=0; r<rnz.size()*ncomp; ++r)
for (std::size_t j=ia[r]-1; j<ia[r+1]-1; ++j)
if (incomp+pos+1==ja[j]) a[j] = 0.0;
// zero row and put in diagonal
for (std::size_t j=ia[incomp+pos]-1; j<ia[incomp+pos+1]-1; ++j)
if (incomp+pos+1==ja[j]) a[j] = diag; else a[j] = 0.0;
}
void
CSR::mult( const std::vector< real >& x,
std::vector< real >& r,
const std::vector< tk::real >& bcmask ) const
// *****************************************************************************
// Multiply CSR matrix with vector from the right: r = A * x
//! \param[in] x Vector to multiply matrix with from the right
//! \param[in] r Result vector of product r = A * x
//! \param[in] bcmask Dirichlet BC mask
//! \note This is only complete in serial. In parallel, this computes the own
//! contributions to the product, so it must be followed by communication
//! combining the rows stored on multiple partitions.
// *****************************************************************************
{
std::fill( begin(r), end(r), 0.0 );
for (std::size_t i=0; i<rnz.size()*ncomp; ++i)
for (std::size_t j=ia[i]-1; j<ia[i+1]-1; ++j)
r[i] += bcmask[i] * a[j] * x[ja[j]-1];
}
std::ostream&
CSR::write_stored( std::ostream& os ) const
// *****************************************************************************
// Write out CSR as stored
//! \param[in,out] os Output stream to write to
//! \return Updated output stream
// *****************************************************************************
{
os << "size (npoin) = " << rnz.size() << '\n';
os << "ncomp = " << ncomp << '\n';
os << "rsize (size*ncomp) = " << rnz.size() * ncomp << '\n';
os << "nnz = " << a.size() << '\n';
std::size_t i;
os << "rnz[npoin=" << rnz.size() << "] = { ";
for (i=0; i<rnz.size()-1; ++i) os << rnz[i] << ", ";
os << rnz[i] << " }\n";
os << "ia[rsize+1=" << rnz.size()*ncomp+1 << "] = { ";
for (i=0; i<ia.size()-1; ++i) os << ia[i] << ", ";
os << ia[i] << " }\n";
os << "ja[nnz=" << ja.size() << "] = { ";
for (i=0; i<ja.size()-1; ++i) os << ja[i] << ", ";
os << ja[i] << " }\n";
os << "a[nnz=" << a.size() << "] = { ";
for (i=0; i<a.size()-1; ++i) os << a[i] << ", ";
os << a[i] << " }\n";
return os;
}
std::ostream&
CSR::write_structure( std::ostream& os ) const
// *****************************************************************************
// Write out CSR nonzero structure
//! \param[in,out] os Output stream to write to
//! \return Updated output stream
// *****************************************************************************
{
for (std::size_t i=0; i<rnz.size()*ncomp; ++i) {
// leading zeros
for (std::size_t j=1; j<ja[ia[i]-1]; ++j) os << ". ";
for (std::size_t n=ia[i]-1; n<ia[i+1]-1; ++n) {
// zeros between nonzeros
if (n>ia[i]-1) for (std::size_t j=ja[n-1]; j<ja[n]-1; ++j) os << ". ";
// nonzero
os << "o ";
}
// trailing zeros
for (std::size_t j=ja[ia[i+1]-2]; j<rnz.size()*ncomp; ++j) os << ". ";
os << '\n';
}
return os;
}
std::ostream&
CSR::write_matrix( std::ostream& os ) const
// *****************************************************************************
// Write out CSR as a real matrix
//! \param[in,out] os Output stream to write to
//! \return Updated output stream
// *****************************************************************************
{
for (std::size_t i=0; i<rnz.size()*ncomp; ++i) {
for (std::size_t j=1; j<ja[ia[i]-1]; ++j) os << "0\t";
for (std::size_t n=ia[i]-1; n<ia[i+1]-1; ++n) {
if (n>ia[i]-1) for (std::size_t j=ja[n-1]; j<ja[n]-1; ++j ) os << "0\t";
os << a[n] << '\t';
}
for (std::size_t j=ja[ia[i+1]-2]; j<rnz.size()*ncomp; ++j) os << "0\t";
os << '\n';
}
return os;
}
std::ostream&
CSR::write_matlab( std::ostream& os ) const
// *****************************************************************************
// Write out CSR in Matlab/Octave format
//! \param[in,out] os Output stream to write to
//! \return Updated output stream
// *****************************************************************************
{
os << "A = [ ";
for (std::size_t i=0; i<rnz.size()*ncomp; ++i) {
for (std::size_t j=1; j<ja[ia[i]-1]; ++j) os << "0 ";
for ( std::size_t n=ia[i]-1; n<ia[i+1]-1; ++n) {
if (n > ia[i]-1)
for (std::size_t j=ja[n-1]; j<ja[n]-1; ++j) os << "0 ";
os << a[n] << ' ';
}
for (std::size_t j=ja[ia[i+1]-2]; j<rnz.size()*ncomp; ++j) os << "0 ";
os << ";\n";
}
os << "]\n";
return os;
}
|