libMesh
sparse_matrix_test.h
Go to the documentation of this file.
1 #ifndef SPARSE_MATRIX_TEST_H
2 #define SPARSE_MATRIX_TEST_H
3 
4 // test includes
5 #include "test_comm.h"
6 
7 // libMesh includes
8 #include <libmesh/dense_matrix.h>
9 #include <libmesh/sparse_matrix.h>
10 #include <libmesh/fuzzy_equals.h>
11 
12 #include "libmesh_cppunit.h"
13 
14 // C++ includes
15 #include <vector>
16 #ifdef LIBMESH_HAVE_CXX11_THREAD
17 #include <thread>
18 #include <algorithm>
19 #endif
20 
21 
22 #ifdef LIBMESH_HAVE_HDF5
23 #define SPARSEMATRIXTEST \
24  CPPUNIT_TEST(testGetAndSet); \
25  CPPUNIT_TEST(testReadHDF5); \
26  CPPUNIT_TEST(testReadMatlab1); \
27  CPPUNIT_TEST(testReadMatlab2); \
28  CPPUNIT_TEST(testReadMatlab4); \
29  CPPUNIT_TEST(testTransposeNorms); \
30  CPPUNIT_TEST(testWriteAndReadHDF5); \
31  CPPUNIT_TEST(testWriteAndReadMatlab); \
32  CPPUNIT_TEST(testWriteAndReadZippedMatlab);\
33  CPPUNIT_TEST(testClone);
34 #else // LIBMESH_HAVE_HDF5
35 #define SPARSEMATRIXTEST \
36  CPPUNIT_TEST(testGetAndSet); \
37  CPPUNIT_TEST(testReadMatlab1); \
38  CPPUNIT_TEST(testReadMatlab2); \
39  CPPUNIT_TEST(testReadMatlab4); \
40  CPPUNIT_TEST(testTransposeNorms); \
41  CPPUNIT_TEST(testWriteAndReadMatlab); \
42  CPPUNIT_TEST(testWriteAndReadZippedMatlab);\
43  CPPUNIT_TEST(testClone);
44 #endif // LIBMESH_HAVE_HDF5
45 
46 
47 
48 template <class DerivedClass>
49 class SparseMatrixTest : public CppUnit::TestCase
50 {
51 public:
52 
53  void setUp()
54  {
55  // By default we'll use the whole communicator in parallel;
56  // Serial-only NumericVector subclasses will need to override
57  // this and set something else first.
58  if (!my_comm)
60 
61  matrix = std::make_unique<DerivedClass>(*my_comm);
62 
63  // Use the same even partitioning that we'll auto-deduce in matrix
64  // read, to make it easier to test parallel reads
65 
66  global_m = global_n = my_comm->size() * 5.75 - 1;
67  if (nonsquare)
68  global_n *= 1.3;
69 
71  row_start = my_comm->rank() * global_m / my_comm->size(),
72  row_stop = (my_comm->rank()+1) * global_m / my_comm->size();
74  col_start = my_comm->rank() * global_n / my_comm->size(),
75  col_stop = (my_comm->rank()+1) * global_n / my_comm->size();
76 
77  local_m = row_stop - row_start;
78  local_n = col_stop - col_start;
79 
80  // Let's just play around with locally dense blocks for now
81  matrix->init(global_m,
82  global_n,
83  local_m,
84  local_n,
85  /*nnz=*/local_n,
86  /*noz=*/5);
87  }
88 
89 
90  void tearDown() {}
91 
92 
93  void setValues()
94  {
95  std::vector<libMesh::numeric_index_type> rows(local_m);
96  std::iota(rows.begin(), rows.end(), matrix->row_start());
97  std::vector<libMesh::numeric_index_type> cols(local_n);
98  std::iota(cols.begin(), cols.end(), matrix->col_start());
99 
101 
102  for (auto i : libMesh::make_range(local_m))
103  for (auto j : libMesh::make_range(local_n))
104  local(i, j) = (i + 1) * (j + 1) * (my_comm->rank() + 1);
105 
106  matrix->zero();
107 
108  matrix->add_matrix(local, rows, cols);
109  matrix->close();
110  }
111 
112 
113  void testValues()
114  {
115  auto functor = [this]()
116  {
117  std::vector<libMesh::numeric_index_type> cols_to_get;
118  std::vector<libMesh::Number> values;
119  const libMesh::numeric_index_type col_start =
120  matrix->col_start();
122  libMesh::make_range(matrix->row_start(),
123  matrix->row_stop()))
124  {
125  matrix->get_row(i, cols_to_get, values);
126  for (libMesh::numeric_index_type col_j :
127  libMesh::index_range(cols_to_get))
128  {
129  CPPUNIT_ASSERT_EQUAL(cols_to_get[col_j], col_start + col_j);
130  LIBMESH_ASSERT_NUMBERS_EQUAL
131  ((i - matrix->row_start() + 1) * (col_j + 1) * (my_comm->rank() + 1),
132  values[col_j], _tolerance);
133  }
134  }
135  };
136 
137 #ifdef LIBMESH_HAVE_CXX11_THREAD
138  auto num_threads = std::min(unsigned(2),
139  std::max(
140  std::thread::hardware_concurrency(),
141  unsigned(1)));
142  std::vector<std::thread> threads(num_threads);
143  for (unsigned int thread = 0; thread < num_threads; ++thread)
144  threads[thread] = std::thread(functor);
145  std::for_each(threads.begin(), threads.end(),
146  [](std::thread & x){x.join();});
147 #else
148  functor();
149 #endif
150  }
151 
152 
154  {
155  LOG_UNIT_TEST;
156 
157  setValues();
158 
159  testValues();
160  }
161 
162 
163  void testReadMatlab(const std::string & filename)
164  {
165  // Laspack doesn't handle non-square matrices)
166  if (matrix->solver_package() == libMesh::LASPACK_SOLVERS)
167  return;
168 
169  matrix->clear();
170 
171  auto matrix2 = std::make_unique<DerivedClass>(*my_comm);
172 
173  matrix->read(filename);
174 
175 #ifndef LIBMESH_HAVE_GZSTREAM
176  return;
177 #endif
178 
179  matrix2->read(std::string(filename)+".gz");
180 
181  // We need some more SparseMatrix operators, but not today
182  CPPUNIT_ASSERT(matrix->l1_norm() == matrix2->l1_norm());
183  CPPUNIT_ASSERT(matrix->linfty_norm() == matrix2->linfty_norm());
184  }
185 
186 
188  {
189  LOG_UNIT_TEST;
190  testReadMatlab("matrices/geom_1_extraction_op.m");
191  }
192 
193 
195  {
196  LOG_UNIT_TEST;
197  testReadMatlab("matrices/geom_2_extraction_op.m");
198  }
199 
200 
202  {
203  LOG_UNIT_TEST;
204  testReadMatlab("matrices/geom_4_extraction_op.m");
205  }
206 
207 
208 #ifdef LIBMESH_HAVE_HDF5
210  {
211  LOG_UNIT_TEST;
212 
213  matrix->clear();
214  auto matrix2 = std::make_unique<DerivedClass>(*my_comm);
215  matrix->read("matrices/geom_1_extraction_op.m");
216  matrix2->read("matrices/geom_1_extraction_op.h5");
217 
218  // We need some more SparseMatrix operators, but not today
219  CPPUNIT_ASSERT(matrix->l1_norm() == matrix2->l1_norm());
220  CPPUNIT_ASSERT(matrix->linfty_norm() == matrix2->linfty_norm());
221  }
222 #endif // LIBMESH_HAVE_HDF5
223 
224 
226  {
227  LOG_UNIT_TEST;
228 
229  setValues();
230 
231  auto matrix2 = std::make_unique<DerivedClass>(*my_comm);
232  matrix->get_transpose(*matrix2);
233  LIBMESH_ASSERT_FP_EQUAL(matrix->l1_norm(), matrix2->linfty_norm(), _tolerance);
234  LIBMESH_ASSERT_FP_EQUAL(matrix2->l1_norm(), matrix->linfty_norm(), _tolerance);
235  }
236 
237 
238  void testWriteAndRead(const std::string & filename)
239  {
240  LOG_UNIT_TEST;
241 
242  setValues();
243 
244  // If we're working with serial matrices then just print one of
245  // them so they don't step on the others' toes.
246  if (matrix->n_processors() > 1 ||
247  TestCommWorld->rank() == 0)
248  matrix->print(filename);
249 
250  matrix->clear();
251 
252  // Let's make sure we don't have any race conditions; we have
253  // multiple SparseMatrix subclasses that might be trying to read
254  // and write the same file.
255 
257 
258 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
259  // We're not supporting complex reads quite yet
260  return;
261 #endif
262 
263  matrix->read(filename);
264 
266 
267  testValues();
268  }
269 
270 #ifdef LIBMESH_HAVE_HDF5
272  {
273  // This capability is not yet supported when libmesh is compiled
274  // with complex number support
275 #ifndef LIBMESH_USE_COMPLEX_NUMBERS
276  testWriteAndRead("M.h5");
277 #endif
278  }
279 #endif // LIBMESH_HAVE_HDF5
280 
281 
283  {
284  // Use a very short filename, because we had a bug with that and
285  // we want to test it.
286  testWriteAndRead("M.m");
287  }
288 
290  {
291 #ifdef LIBMESH_HAVE_GZSTREAM
292  testWriteAndRead("Mzipped.m.gz");
293 #endif
294  }
295 
296  void testClone()
297  {
298  LOG_UNIT_TEST;
299 
300  setValues();
301 
302  // Matrix must be closed before it can be cloned.
303  matrix->close();
304 
305  {
306  // Create copy, test that it can go out of scope
307  auto copy = matrix->clone();
308 
309  // Check that matrices have the same local/global sizes
310  CPPUNIT_ASSERT_EQUAL(copy->m(), matrix->m());
311  CPPUNIT_ASSERT_EQUAL(copy->n(), matrix->n());
312  CPPUNIT_ASSERT_EQUAL(copy->local_m(), matrix->local_m());
313  CPPUNIT_ASSERT_EQUAL(copy->row_start(), matrix->row_start());
314  CPPUNIT_ASSERT_EQUAL(copy->row_stop(), matrix->row_stop());
315 
316  // Check that copy has same values as original
317  LIBMESH_ASSERT_FP_EQUAL(copy->l1_norm(), matrix->l1_norm(), _tolerance);
318  CPPUNIT_ASSERT(relative_fuzzy_equals(*matrix, *copy));
319  copy->scale(2);
320  CPPUNIT_ASSERT(!relative_fuzzy_equals(*matrix, *copy));
321  }
322 
323  {
324  // Create zero copy
325  auto zero_copy = matrix->zero_clone();
326 
327  // Check that matrices have the same local/global sizes
328  CPPUNIT_ASSERT_EQUAL(zero_copy->m(), matrix->m());
329  CPPUNIT_ASSERT_EQUAL(zero_copy->n(), matrix->n());
330  CPPUNIT_ASSERT_EQUAL(zero_copy->local_m(), matrix->local_m());
331  CPPUNIT_ASSERT_EQUAL(zero_copy->row_start(), matrix->row_start());
332  CPPUNIT_ASSERT_EQUAL(zero_copy->row_stop(), matrix->row_stop());
333 
334  // Check that zero_copy has same values as original
335  LIBMESH_ASSERT_FP_EQUAL(0.0, zero_copy->l1_norm(), _tolerance);
336  }
337  }
338 
339 protected:
340 
341  std::string libmesh_suite_name;
342 
344 
345  std::unique_ptr<DerivedClass> matrix;
346 
348  local_m, local_n,
350 
352 };
353 
354 #endif // SPARSE_MATRIX_TEST_H
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:218
static constexpr Real TOLERANCE
void barrier() const
libMesh::Parallel::Communicator * my_comm
processor_id_type rank() const
libMesh::numeric_index_type local_n
processor_id_type size() const
void testWriteAndRead(const std::string &filename)
dof_id_type numeric_index_type
Definition: id_types.h:99
void testReadMatlab(const std::string &filename)
std::unique_ptr< DerivedClass > matrix
void testWriteAndReadZippedMatlab()
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
libMesh::numeric_index_type global_m
std::string libmesh_suite_name
const libMesh::Real _tolerance
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
libMesh::numeric_index_type nonsquare
bool relative_fuzzy_equals(const T &var1, const T2 &var2, const Real tol=TOLERANCE *TOLERANCE)
Function to check whether two variables are equal within a relative tolerance.
Definition: fuzzy_equals.h:78
libMesh::numeric_index_type global_n
Defines a dense matrix for use in Finite Element-type computations.
Definition: dof_map.h:75
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
libMesh::numeric_index_type local_m
uint8_t dof_id_type
Definition: id_types.h:67