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