1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 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/libmesh_config.h"
22
23 #ifdef LIBMESH_HAVE_EIGEN
24
25 #include "libmesh/eigen_sparse_vector.h"
26 #include "libmesh/eigen_sparse_matrix.h"
27 #include "libmesh/dense_matrix.h"
28 #include "libmesh/dof_map.h"
29 #include "libmesh/sparsity_pattern.h"
30 #include "libmesh/auto_ptr.h" // libmesh_make_unique
31
32 namespace libMesh
33 {
34
35
36 //-----------------------------------------------------------------------
37 // EigenSparseMatrix members
38 template <typename T>
init(const numeric_index_type m_in,const numeric_index_type n_in,const numeric_index_type libmesh_dbg_var (m_l),const numeric_index_type libmesh_dbg_var (n_l),const numeric_index_type nnz,const numeric_index_type,const numeric_index_type)39 void EigenSparseMatrix<T>::init (const numeric_index_type m_in,
40 const numeric_index_type n_in,
41 const numeric_index_type libmesh_dbg_var(m_l),
42 const numeric_index_type libmesh_dbg_var(n_l),
43 const numeric_index_type nnz,
44 const numeric_index_type,
45 const numeric_index_type)
46 {
47 // noz ignored... only used for multiple processors!
48 libmesh_assert_equal_to (m_in, m_l);
49 libmesh_assert_equal_to (n_in, n_l);
50 libmesh_assert_equal_to (m_in, n_in);
51 libmesh_assert_greater (nnz, 0);
52
53 _mat.resize(m_in, n_in);
54 _mat.reserve(Eigen::Matrix<numeric_index_type, Eigen::Dynamic, 1>::Constant(m_in,nnz));
55
56 this->_is_initialized = true;
57 }
58
59
60
61 template <typename T>
init(const ParallelType)62 void EigenSparseMatrix<T>::init (const ParallelType)
63 {
64 // Ignore calls on initialized objects
65 if (this->initialized())
66 return;
67
68 // We need the DofMap for this!
69 libmesh_assert(this->_dof_map);
70
71 // Clear initialized matrices
72 if (this->initialized())
73 this->clear();
74
75 const numeric_index_type n_rows = this->_dof_map->n_dofs();
76 const numeric_index_type n_cols = n_rows;
77
78 #ifndef NDEBUG
79 // The following variables are only used for assertions,
80 // so avoid declaring them when asserts are inactive.
81 const numeric_index_type n_l = this->_dof_map->n_dofs_on_processor(0);
82 const numeric_index_type m_l = n_l;
83 #endif
84
85 // Laspack Matrices only work for uniprocessor cases
86 libmesh_assert_equal_to (n_rows, n_cols);
87 libmesh_assert_equal_to (m_l, n_rows);
88 libmesh_assert_equal_to (n_l, n_cols);
89
90 const std::vector<numeric_index_type> & n_nz = this->_dof_map->get_n_nz();
91
92 #ifndef NDEBUG
93 // The following variables are only used for assertions,
94 // so avoid declaring them when asserts are inactive.
95 const std::vector<numeric_index_type> & n_oz = this->_dof_map->get_n_oz();
96 #endif
97
98 // Make sure the sparsity pattern isn't empty
99 libmesh_assert_equal_to (n_nz.size(), n_l);
100 libmesh_assert_equal_to (n_oz.size(), n_l);
101
102 if (n_rows==0)
103 {
104 _mat.resize(0,0);
105 return;
106 }
107
108 _mat.resize(n_rows,n_cols);
109 _mat.reserve(n_nz);
110
111 this->_is_initialized = true;
112
113 libmesh_assert_equal_to (n_rows, this->m());
114 libmesh_assert_equal_to (n_cols, this->n());
115 }
116
117
118
119 template <typename T>
add_matrix(const DenseMatrix<T> & dm,const std::vector<numeric_index_type> & rows,const std::vector<numeric_index_type> & cols)120 void EigenSparseMatrix<T>::add_matrix(const DenseMatrix<T> & dm,
121 const std::vector<numeric_index_type> & rows,
122 const std::vector<numeric_index_type> & cols)
123
124 {
125 libmesh_assert (this->initialized());
126 unsigned int n_rows = cast_int<unsigned int>(rows.size());
127 unsigned int n_cols = cast_int<unsigned int>(cols.size());
128 libmesh_assert_equal_to (dm.m(), n_rows);
129 libmesh_assert_equal_to (dm.n(), n_cols);
130
131
132 for (unsigned int i=0; i<n_rows; i++)
133 for (unsigned int j=0; j<n_cols; j++)
134 this->add(rows[i],cols[j],dm(i,j));
135 }
136
137
138
139 template <typename T>
get_diagonal(NumericVector<T> & dest_in)140 void EigenSparseMatrix<T>::get_diagonal (NumericVector<T> & dest_in) const
141 {
142 EigenSparseVector<T> & dest = cast_ref<EigenSparseVector<T> &>(dest_in);
143
144 dest._vec = _mat.diagonal();
145 }
146
147
148
149 template <typename T>
get_transpose(SparseMatrix<T> & dest_in)150 void EigenSparseMatrix<T>::get_transpose (SparseMatrix<T> & dest_in) const
151 {
152 EigenSparseMatrix<T> & dest = cast_ref<EigenSparseMatrix<T> &>(dest_in);
153
154 dest._mat = _mat.transpose();
155 }
156
157
158
159 template <typename T>
EigenSparseMatrix(const Parallel::Communicator & comm_in)160 EigenSparseMatrix<T>::EigenSparseMatrix (const Parallel::Communicator & comm_in) :
161 SparseMatrix<T>(comm_in),
162 _closed (false)
163 {
164 }
165
166
167
168 template <typename T>
clear()169 void EigenSparseMatrix<T>::clear ()
170 {
171 _mat.resize(0,0);
172
173 _closed = false;
174 this->_is_initialized = false;
175 }
176
177
178
179 template <typename T>
zero()180 void EigenSparseMatrix<T>::zero ()
181 {
182 // This doesn't just zero, it clears the entire non-zero structure!
183 _mat.setZero();
184
185 // Re-reserve our non-zero structure
186 if (this->_dof_map)
187 {
188 const std::vector<numeric_index_type> & n_nz = this->_dof_map->get_n_nz();
189 _mat.reserve(n_nz);
190 }
191 }
192
193
194
195 template <typename T>
zero_clone()196 std::unique_ptr<SparseMatrix<T>> EigenSparseMatrix<T>::zero_clone () const
197 {
198 // TODO: If there is a more efficient way to make a zeroed-out copy
199 // of an EigenSM, we should call that instead.
200 auto ret = libmesh_make_unique<EigenSparseMatrix<T>>(*this);
201 ret->zero();
202
203 // Work around an issue on older compilers. We are able to simply
204 // "return ret;" on newer compilers
205 return std::unique_ptr<SparseMatrix<T>>(ret.release());
206 }
207
208
209
210 template <typename T>
clone()211 std::unique_ptr<SparseMatrix<T>> EigenSparseMatrix<T>::clone () const
212 {
213 return libmesh_make_unique<EigenSparseMatrix<T>>(*this);
214 }
215
216
217
218 template <typename T>
m()219 numeric_index_type EigenSparseMatrix<T>::m () const
220 {
221 libmesh_assert (this->initialized());
222
223 return cast_int<numeric_index_type>(_mat.rows());
224 }
225
226
227
228 template <typename T>
n()229 numeric_index_type EigenSparseMatrix<T>::n () const
230 {
231 libmesh_assert (this->initialized());
232
233 return cast_int<numeric_index_type>(_mat.cols());
234 }
235
236
237
238 template <typename T>
row_start()239 numeric_index_type EigenSparseMatrix<T>::row_start () const
240 {
241 return 0;
242 }
243
244
245
246 template <typename T>
row_stop()247 numeric_index_type EigenSparseMatrix<T>::row_stop () const
248 {
249 return this->m();
250 }
251
252
253
254 template <typename T>
set(const numeric_index_type i,const numeric_index_type j,const T value)255 void EigenSparseMatrix<T>::set (const numeric_index_type i,
256 const numeric_index_type j,
257 const T value)
258 {
259 libmesh_assert (this->initialized());
260 libmesh_assert_less (i, this->m());
261 libmesh_assert_less (j, this->n());
262
263 _mat.coeffRef(i,j) = value;
264 }
265
266
267
268 template <typename T>
add(const numeric_index_type i,const numeric_index_type j,const T value)269 void EigenSparseMatrix<T>::add (const numeric_index_type i,
270 const numeric_index_type j,
271 const T value)
272 {
273 libmesh_assert (this->initialized());
274 libmesh_assert_less (i, this->m());
275 libmesh_assert_less (j, this->n());
276
277 _mat.coeffRef(i,j) += value;
278 }
279
280
281
282 template <typename T>
add_matrix(const DenseMatrix<T> & dm,const std::vector<numeric_index_type> & dof_indices)283 void EigenSparseMatrix<T>::add_matrix(const DenseMatrix<T> & dm,
284 const std::vector<numeric_index_type> & dof_indices)
285 {
286 this->add_matrix (dm, dof_indices, dof_indices);
287 }
288
289
290
291 template <typename T>
add(const T a_in,const SparseMatrix<T> & X_in)292 void EigenSparseMatrix<T>::add (const T a_in, const SparseMatrix<T> & X_in)
293 {
294 libmesh_assert (this->initialized());
295 libmesh_assert_equal_to (this->m(), X_in.m());
296 libmesh_assert_equal_to (this->n(), X_in.n());
297
298 const EigenSparseMatrix<T> & X =
299 cast_ref<const EigenSparseMatrix<T> &> (X_in);
300
301 _mat += X._mat*a_in;
302 }
303
304
305
306
307 template <typename T>
operator()308 T EigenSparseMatrix<T>::operator () (const numeric_index_type i,
309 const numeric_index_type j) const
310 {
311 libmesh_assert (this->initialized());
312 libmesh_assert_less (i, this->m());
313 libmesh_assert_less (j, this->n());
314
315 return _mat.coeff(i,j);
316 }
317
318
319
320 template <typename T>
l1_norm()321 Real EigenSparseMatrix<T>::l1_norm () const
322 {
323 // There does not seem to be a straightforward way to iterate over
324 // the columns of an EigenSparseMatrix. So we use some extra
325 // storage and keep track of the column sums while going over the
326 // row entries...
327 std::vector<Real> abs_col_sums(this->n());
328
329 // For a row-major Eigen SparseMatrix like we're using, the
330 // InnerIterator iterates over the non-zero entries of rows.
331 for (auto row : make_range(this->m()))
332 {
333 EigenSM::InnerIterator it(_mat, row);
334 for (; it; ++it)
335 abs_col_sums[it.col()] += std::abs(it.value());
336 }
337
338 return *(std::max_element(abs_col_sums.begin(), abs_col_sums.end()));
339 }
340
341
342
343 template <typename T>
linfty_norm()344 Real EigenSparseMatrix<T>::linfty_norm () const
345 {
346 Real max_abs_row_sum = 0.;
347
348 // For a row-major Eigen SparseMatrix like we're using, the
349 // InnerIterator iterates over the non-zero entries of rows.
350 for (auto row : make_range(this->m()))
351 {
352 Real current_abs_row_sum = 0.;
353 EigenSM::InnerIterator it(_mat, row);
354 for (; it; ++it)
355 current_abs_row_sum += std::abs(it.value());
356
357 max_abs_row_sum = std::max(max_abs_row_sum, current_abs_row_sum);
358 }
359
360 return max_abs_row_sum;
361 }
362
363
364 //------------------------------------------------------------------
365 // Explicit instantiations
366 template class EigenSparseMatrix<Number>;
367
368 } // namespace libMesh
369
370
371 #endif // #ifdef LIBMESH_HAVE_EIGEN
372