libMesh
Loading...
Searching...
No Matches
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
48template <class DerivedClass>
49class SparseMatrixTest : public CppUnit::TestCase
50{
51public:
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,
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
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
256 TestCommWorld->barrier();
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
265 TestCommWorld->barrier();
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
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
339protected:
340
342
344
345 std::unique_ptr<DerivedClass> matrix;
346
350
352};
353
354#endif // SPARSE_MATRIX_TEST_H
libMesh::numeric_index_type local_m
libMesh::numeric_index_type global_n
const libMesh::Real _tolerance
libMesh::numeric_index_type local_n
void testWriteAndRead(const std::string &filename)
libMesh::numeric_index_type nonsquare
libMesh::numeric_index_type global_m
libMesh::Parallel::Communicator * my_comm
std::string libmesh_suite_name
std::unique_ptr< DerivedClass > matrix
void testReadMatlab(const std::string &filename)
processor_id_type size() const
processor_id_type rank() const
Defines a dense matrix for use in Finite Element-type computations.
virtual void zero() override final
Sets all elements of the matrix to 0 and resets any decomposition flag which may have been previously...
Communicator * TestCommWorld
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
dof_id_type numeric_index_type
Definition id_types.h:99
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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