1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 #include "axom/mint/config.hpp" // for compile-time definitions
6
7 // Mint includes
8 #include "axom/mint/mesh/blueprint.hpp" // for blueprint functions
9 #include "axom/mint/mesh/CellTypes.hpp" // for CellType enum
10 #include "axom/mint/mesh/RectilinearMesh.hpp" // for RectilinearMesh
11 #include "axom/mint/mesh/ParticleMesh.hpp" // for ParticleMesh
12 #include "StructuredMesh_helpers.hpp" // for StructuredMesh test helpers
13
14 // Slic includes
15 #include "axom/slic/interface/slic.hpp" // for slic macros
16
17 // Sidre includes
18 #ifdef AXOM_MINT_USE_SIDRE
19 #include "axom/sidre/core/sidre.hpp"
20 namespace sidre = axom::sidre;
21 #endif
22
23 #include "gtest/gtest.h" // for gtest macros
24
25 // C/C++ includes
26 #include <cmath> // for exp
27 using namespace axom::mint;
28 using IndexType = axom::IndexType;
29
30 // globals
31 const char* IGNORE_OUTPUT = ".*";
32
33 //------------------------------------------------------------------------------
34 // HELPER METHODS
35 //------------------------------------------------------------------------------
36 namespace
37 {
exponential_distribution(double origin,IndexType N,double * x)38 void exponential_distribution(double origin, IndexType N, double* x)
39 {
40 EXPECT_TRUE(x != nullptr);
41
42 constexpr double beta = 0.05;
43 const double expbeta = exp(beta);
44 const double invf = 1 / (expbeta - 1.0);
45
46 x[0] = origin;
47 for(int i = 1; i < N; ++i)
48 {
49 const double prev = x[i - 1];
50 const double dx = (exp(i * beta) - 1.0) * invf;
51 x[i] = prev + dx;
52 }
53 }
54
55 //------------------------------------------------------------------------------
check_coordinate(const double * x,const double * expected,IndexType N)56 void check_coordinate(const double* x, const double* expected, IndexType N)
57 {
58 EXPECT_TRUE(x != nullptr);
59 EXPECT_TRUE(expected != nullptr);
60 EXPECT_TRUE(N >= 0);
61
62 for(IndexType i = 0; i < N; ++i)
63 {
64 EXPECT_DOUBLE_EQ(x[i], expected[i]);
65 }
66 }
67
68 //------------------------------------------------------------------------------
check_fill_coords(RectilinearMesh * m)69 void check_fill_coords(RectilinearMesh* m)
70 {
71 EXPECT_TRUE(m != nullptr);
72
73 const int ndims = m->getDimension();
74 for(int idim = 0; idim < ndims; ++idim)
75 {
76 const IndexType N = m->getNodeResolution(idim);
77 double* x = m->getCoordinateArray(idim);
78 exponential_distribution(42.0, N, x);
79 }
80 }
81
82 //------------------------------------------------------------------------------
83
84 } // END anonymous namespace
85
86 //------------------------------------------------------------------------------
87 // UNIT TESTS
88 //------------------------------------------------------------------------------
TEST(mint_mesh_rectilinear_mesh_DeathTest,invalid_construction)89 TEST(mint_mesh_rectilinear_mesh_DeathTest, invalid_construction)
90 {
91 const IndexType N[] = {5, 5, 5};
92 double x[5];
93
94 // check 1st native constructor
95 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(2, nullptr), IGNORE_OUTPUT);
96
97 // check 2nd native constructor
98 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(-1, N[1], N[2]), IGNORE_OUTPUT);
99
100 // check external constructor
101 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(5, nullptr), IGNORE_OUTPUT);
102 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(-1, x), IGNORE_OUTPUT);
103
104 #ifdef AXOM_MINT_USE_SIDRE
105
106 sidre::DataStore ds;
107 sidre::Group* root = ds.getRoot();
108 sidre::Group* valid_group = root->createGroup("mesh");
109 sidre::Group* particle_mesh = root->createGroup("particle_mesh");
110 ParticleMesh(3, 10, particle_mesh);
111
112 // check pull constructor
113 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(nullptr, ""), IGNORE_OUTPUT);
114 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(root, ""), IGNORE_OUTPUT);
115 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(particle_mesh, ""), IGNORE_OUTPUT);
116
117 // check 2nd push constructor
118 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(nullptr, N[0]), IGNORE_OUTPUT);
119
120 EXPECT_DEATH_IF_SUPPORTED(RectilinearMesh(valid_group, -1), IGNORE_OUTPUT);
121
122 #endif
123 }
124
125 //------------------------------------------------------------------------------
TEST(mint_mesh_rectilinear_mesh,native_constructor)126 TEST(mint_mesh_rectilinear_mesh, native_constructor)
127 {
128 constexpr int NDIMS = 3;
129 const IndexType N[] = {5, 6, 7};
130 const int64 extent[] = {0, 4, 10, 15, 7, 13};
131
132 for(int idim = 1; idim <= NDIMS; ++idim)
133 {
134 RectilinearMesh* m;
135 switch(idim)
136 {
137 case 1:
138 m = new RectilinearMesh(N[0]);
139 break;
140 case 2:
141 m = new RectilinearMesh(N[0], N[1]);
142 break;
143 default:
144 EXPECT_EQ(idim, 3);
145 m = new RectilinearMesh(N[0], N[1], N[2]);
146 }
147
148 internal::check_constructor(m, STRUCTURED_RECTILINEAR_MESH, idim, N);
149 EXPECT_FALSE(m->isExternal());
150 EXPECT_FALSE(m->hasSidreGroup());
151 m->setExtent(idim, extent);
152 internal::check_node_extent(m, extent);
153 check_fill_coords(m);
154 internal::check_create_fields(m);
155
156 delete m;
157 } // END for all dimensions
158 }
159
160 //------------------------------------------------------------------------------
TEST(mint_mesh_rectilinear_mesh,external_costructor)161 TEST(mint_mesh_rectilinear_mesh, external_costructor)
162 {
163 constexpr int NDIMS = 3;
164 const IndexType N[] = {5, 6, 7};
165 const int64 extent[] = {0, 4, 10, 15, 7, 13};
166 constexpr double MAGIC_VAL = 42.0;
167
168 double* X = new double[N[0]];
169 double* Y = new double[N[1]];
170 double* Z = new double[N[2]];
171 exponential_distribution(MAGIC_VAL, N[0], X);
172 exponential_distribution(MAGIC_VAL, N[1], Y);
173 exponential_distribution(MAGIC_VAL, N[2], Z);
174
175 double* x = new double[N[0]];
176 double* y = new double[N[1]];
177 double* z = new double[N[2]];
178 exponential_distribution(MAGIC_VAL, N[0], x);
179 exponential_distribution(MAGIC_VAL, N[1], y);
180 exponential_distribution(MAGIC_VAL, N[2], z);
181
182 for(int idim = 1; idim <= NDIMS; ++idim)
183 {
184 RectilinearMesh* m = nullptr;
185
186 switch(idim)
187 {
188 case 1:
189 m = new RectilinearMesh(N[0], x);
190 EXPECT_EQ(m->getCoordinateArray(X_COORDINATE), x);
191 check_coordinate(m->getCoordinateArray(X_COORDINATE), x, N[0]);
192 break;
193 case 2:
194 m = new RectilinearMesh(N[0], x, N[1], y);
195 EXPECT_EQ(m->getCoordinateArray(X_COORDINATE), x);
196 EXPECT_EQ(m->getCoordinateArray(Y_COORDINATE), y);
197 check_coordinate(m->getCoordinateArray(X_COORDINATE), x, N[0]);
198 check_coordinate(m->getCoordinateArray(Y_COORDINATE), y, N[1]);
199 break;
200 default:
201 m = new RectilinearMesh(N[0], x, N[1], y, N[2], z);
202 EXPECT_EQ(m->getCoordinateArray(X_COORDINATE), x);
203 EXPECT_EQ(m->getCoordinateArray(Y_COORDINATE), y);
204 EXPECT_EQ(m->getCoordinateArray(Z_COORDINATE), z);
205 check_coordinate(m->getCoordinateArray(X_COORDINATE), x, N[0]);
206 check_coordinate(m->getCoordinateArray(Y_COORDINATE), y, N[1]);
207 check_coordinate(m->getCoordinateArray(Z_COORDINATE), z, N[2]);
208
209 } // END switch
210
211 EXPECT_TRUE(m != nullptr);
212 internal::check_constructor(m, STRUCTURED_RECTILINEAR_MESH, idim, N);
213 EXPECT_FALSE(m->hasSidreGroup());
214 EXPECT_TRUE(m->isExternal());
215 m->setExtent(idim, extent);
216 internal::check_node_extent(m, extent);
217
218 // deallocate
219 delete m;
220 m = nullptr;
221
222 // ensure coordinates are not changed after mesh gets deleted
223 EXPECT_TRUE(x != nullptr);
224 EXPECT_TRUE(y != nullptr);
225 EXPECT_TRUE(z != nullptr);
226 check_coordinate(x, X, N[0]);
227 check_coordinate(y, Y, N[1]);
228 check_coordinate(z, Z, N[2]);
229
230 } // END for all dimensions
231
232 delete[] x;
233 delete[] X;
234 delete[] y;
235 delete[] Y;
236 delete[] z;
237 delete[] Z;
238 }
239
240 //------------------------------------------------------------------------------
241 #ifdef AXOM_MINT_USE_SIDRE
242
TEST(mint_mesh_rectilinear_mesh,sidre_constructor)243 TEST(mint_mesh_rectilinear_mesh, sidre_constructor)
244 {
245 constexpr int NDIMS = 3;
246 const IndexType N[] = {5, 6, 7};
247 const IndexType maxDim = 7;
248 const int64 extent[] = {0, 4, 10, 15, 7, 13};
249 constexpr double MAGIC_VAL = 42.0;
250
251 double* expected_coords = new double[maxDim];
252 exponential_distribution(MAGIC_VAL, maxDim, expected_coords);
253
254 for(int idim = 1; idim <= NDIMS; ++idim)
255 {
256 // STEP 0: create a data-store with two groups
257 sidre::DataStore ds;
258 sidre::Group* root = ds.getRoot();
259 sidre::Group* meshGroup = root->createGroup("mesh");
260
261 // STEP 1: populate meshes in Sidre using the 2 sidre constructors
262 RectilinearMesh* m;
263 switch(idim)
264 {
265 case 1:
266 m = new RectilinearMesh(meshGroup, N[I_DIRECTION]);
267 break;
268 case 2:
269 m = new RectilinearMesh(meshGroup, N[I_DIRECTION], N[J_DIRECTION]);
270 break;
271 default:
272 EXPECT_EQ(idim, 3);
273 m = new RectilinearMesh(meshGroup,
274 N[I_DIRECTION],
275 N[J_DIRECTION],
276 N[K_DIRECTION]);
277 } // END switch
278
279 internal::check_constructor(m, STRUCTURED_RECTILINEAR_MESH, idim, N);
280 EXPECT_TRUE(m->hasSidreGroup());
281 EXPECT_FALSE(m->isExternal());
282 internal::check_constructor(m, STRUCTURED_RECTILINEAR_MESH, idim, N);
283 m->setExtent(idim, extent);
284 internal::check_node_extent(m, extent);
285 check_fill_coords(m);
286 internal::check_create_fields(m);
287
288 delete m;
289 m = nullptr;
290
291 // STEP 2: pull the mesh from sidre and check correctness
292 m = new RectilinearMesh(meshGroup);
293 internal::check_constructor(m, STRUCTURED_RECTILINEAR_MESH, idim, N);
294 EXPECT_TRUE(m->hasSidreGroup());
295 EXPECT_FALSE(m->isExternal());
296
297 for(int ii = 0; ii < idim; ++ii)
298 {
299 check_coordinate(m->getCoordinateArray(ii),
300 expected_coords,
301 m->getNodeResolution(ii));
302 }
303
304 internal::check_fields(m, true);
305 delete m;
306
307 // STEP 3: ensure data is persistent in Sidre
308 EXPECT_TRUE(blueprint::isValidRootGroup(meshGroup));
309
310 } // END for all dimensions
311
312 delete[] expected_coords;
313 }
314
315 #endif /* ENDIF AXOM_MINT_USE_SIDRE */
316
317 //------------------------------------------------------------------------------
TEST(mint_mesh_rectilinear_mesh,get_node)318 TEST(mint_mesh_rectilinear_mesh, get_node)
319 {
320 // initialize coordinates
321 const IndexType N[] = {5, 5, 5};
322 double x[5];
323 double y[5];
324 double z[5];
325 exponential_distribution(42.0, 5, x);
326 exponential_distribution(0.0, 5, y);
327 exponential_distribution(0.0, 5, z);
328
329 RectilinearMesh m(N[0], x, N[1], y, N[2], z);
330 internal::check_constructor(&m, STRUCTURED_RECTILINEAR_MESH, 3, N);
331
332 const IndexType kp = m.nodeKp();
333 const IndexType jp = m.nodeJp();
334 const double* xx = m.getCoordinateArray(X_COORDINATE);
335 const double* yy = m.getCoordinateArray(Y_COORDINATE);
336 const double* zz = m.getCoordinateArray(Z_COORDINATE);
337 EXPECT_EQ(x, xx);
338 EXPECT_EQ(y, yy);
339 EXPECT_EQ(z, zz);
340
341 for(IndexType k = 0; k < N[K_DIRECTION]; ++k)
342 {
343 const IndexType k_offset = k * kp;
344 for(IndexType j = 0; j < N[J_DIRECTION]; ++j)
345 {
346 const IndexType j_offset = j * jp;
347 for(IndexType i = 0; i < N[I_DIRECTION]; ++i)
348 {
349 const IndexType nodeID = i + j_offset + k_offset;
350
351 double node[3];
352 m.getNode(nodeID, node);
353
354 EXPECT_DOUBLE_EQ(xx[i], node[X_COORDINATE]);
355 EXPECT_DOUBLE_EQ(yy[j], node[Y_COORDINATE]);
356 EXPECT_DOUBLE_EQ(zz[k], node[Z_COORDINATE]);
357
358 } // END for all i
359 } // END for all j
360 } // END for all k
361 }
362
363 //------------------------------------------------------------------------------
364 #include "axom/slic/core/SimpleLogger.hpp"
365 using axom::slic::SimpleLogger;
366
main(int argc,char * argv[])367 int main(int argc, char* argv[])
368 {
369 int result = 0;
370
371 ::testing::InitGoogleTest(&argc, argv);
372
373 SimpleLogger logger; // create & initialize test logger,
374
375 // finalized when exiting main scope
376 result = RUN_ALL_TESTS();
377 return result;
378 }
379