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