libMesh
Loading...
Searching...
No Matches
extra_integers.C
Go to the documentation of this file.
1#include <libmesh/libmesh.h>
2#include <libmesh/mesh.h>
3#include <libmesh/elem.h>
4#include <libmesh/mesh_generation.h>
5#include <libmesh/mesh_refinement.h>
6
7#include <libmesh/exodusII_io.h>
8
9#include "test_comm.h"
10#include "libmesh_cppunit.h"
11
12#include <array>
13#include <cstddef> // std::ptrdiff_t
14
15using namespace libMesh;
16
17class ExtraIntegersTest : public CppUnit::TestCase
18{
24public:
26
29
30#ifdef LIBMESH_HAVE_EXODUS_API
32#endif
33#if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_ENABLE_EXCEPTIONS)
35#endif
36
37#ifdef LIBMESH_HAVE_XDR
40#endif
41
43
44protected:
45 // Helper functions called by the test implementations, saves a few lines of code.
46 std::array<unsigned int, 6>
47 build_mesh(Mesh & mesh, ElemType elem_type, unsigned int n_elem_per_side)
48 {
49 // Request some extra integers before building
50 unsigned int i1 = mesh.add_elem_integer("i1");
51 unsigned int r1 = mesh.add_elem_datum<Real>("r1");
52 unsigned int ni1 = mesh.add_node_integer("ni1");
53 unsigned int ni2 = mesh.add_node_integer("ni2");
54 unsigned int nr1 = mesh.add_node_datum<Real>("nr1");
55 unsigned int nr2 = mesh.add_node_datum<Real>("nr2");
56
57 const std::unique_ptr<Elem> test_elem = Elem::build(elem_type);
58 const unsigned int ymax = test_elem->dim() > 1;
59 const unsigned int zmax = test_elem->dim() > 2;
60 const unsigned int ny = ymax * n_elem_per_side;
61 const unsigned int nz = zmax * n_elem_per_side;
62
64 n_elem_per_side,
65 ny,
66 nz,
67 0., 1.,
68 0., ymax,
69 0., zmax,
70 elem_type);
71 return {{i1, r1, ni1, ni2, nr1, nr2}};
72 }
73
74
76 (Mesh & mesh, std::array<unsigned int, 6> ini)
77 {
78 const unsigned int i1 = ini[0],
79 r1 = ini[1],
80 ni1 = ini[2],
81 ni2 = ini[3],
82 nr1 = ini[4];
83 for (const auto & elem : mesh.element_ptr_range())
84 {
85 const unsigned int expected_extra_ints =
86 2 + (sizeof(Real)-1)/sizeof(dof_id_type);
87 CPPUNIT_ASSERT_EQUAL(elem->n_extra_integers(), expected_extra_ints);
88 CPPUNIT_ASSERT_EQUAL(elem->get_extra_integer(i1), DofObject::invalid_id);
89 elem->set_extra_integer(i1, dof_id_type(std::round(elem->point(0)(0)*100)));
90 CPPUNIT_ASSERT_EQUAL(elem->get_extra_integer(i1),
91 dof_id_type(std::round(elem->point(0)(0)*100)));
92 elem->set_extra_datum<Real>(r1, elem->point(0)(0)*1000);
93 CPPUNIT_ASSERT_EQUAL(elem->get_extra_datum<Real>(r1), elem->point(0)(0)*1000);
94 }
95
96 for (const auto & node : mesh.node_ptr_range())
97 {
98 const unsigned int expected_extra_ints =
99 4 + (sizeof(Real)-1)/sizeof(dof_id_type)*2;
100 CPPUNIT_ASSERT_EQUAL(node->n_extra_integers(), expected_extra_ints);
101 CPPUNIT_ASSERT_EQUAL(node->get_extra_integer(ni1), DofObject::invalid_id);
102 CPPUNIT_ASSERT_EQUAL(node->get_extra_integer(ni2), DofObject::invalid_id);
103 node->set_extra_datum<Real>(nr1, (*node)(0)*1000);
104 CPPUNIT_ASSERT_EQUAL(node->get_extra_datum<Real>(nr1), (*node)(0)*1000);
105 }
106
107 }
108
109
110 void test_final_integers(Mesh & mesh, unsigned int i1)
111 {
112 // Make sure old (level 0) elements have the same integers and any new
113 // elements have inherited their ancestors' settings
114 for (const auto & elem : mesh.element_ptr_range())
115 {
116 const Elem * top_parent = elem;
117#ifdef LIBMESH_ENABLE_AMR
118 top_parent = elem->top_parent();
119
120 if (!elem->level())
121#endif
122 for (auto & node : elem->node_ref_range())
123 {
124 const unsigned int expected_extra_ints =
125 4 + (sizeof(Real)-1)/sizeof(dof_id_type)*2;
126 CPPUNIT_ASSERT_EQUAL(node.n_extra_integers(), expected_extra_ints);
127 }
128
129 const unsigned int expected_extra_ints =
130 2 + (sizeof(Real)-1)/sizeof(dof_id_type);
131 CPPUNIT_ASSERT_EQUAL(elem->n_extra_integers(), expected_extra_ints);
132 CPPUNIT_ASSERT_EQUAL(elem->get_extra_integer(i1),
133 dof_id_type(std::round(top_parent->point(0)(0)*100)));
134 }
135 }
136
137
138 void test_helper(ElemType elem_type, unsigned int n_elem_per_side)
139 {
141
142 std::array<unsigned int, 6> ini = build_mesh(mesh, elem_type, n_elem_per_side);
143 const unsigned int i1 = ini[0],
144 ni1 = ini[2],
145 ni2 = ini[3];
146
148
149 // Force (in parallel) a different partitioning - we'll simply put
150 // everything on rank 0, which hopefully is not what our default
151 // partitioner did!
152 mesh.partition(1);
153
154 CPPUNIT_ASSERT_EQUAL(i1, mesh.add_elem_integer("i1"));
155 CPPUNIT_ASSERT_EQUAL(ni1, mesh.add_node_integer("ni1"));
156 CPPUNIT_ASSERT_EQUAL(ni2, mesh.add_node_integer("ni2"));
157
158 // Make sure we didn't screw up any extra integers thereby.
160
161 // Try out switching to 2nd order and back
163
165
167
169
170 // And try refining if we can
171#ifdef LIBMESH_ENABLE_AMR
173 mr.uniformly_refine(1);
174
176
177 // Test writing an XDR file for a refined mesh with extra elem integers.
178 // Note: without the fix in libMesh/libmesh@de15955c5, this test produced
179 // a segfault for me.
180 // TODO: currently we are only testing writing binary (XDR) files,
181 // should probably also test writing ASCII (XDA) files.
182#ifdef LIBMESH_HAVE_XDR
183 const std::string xdr_filename = "test_helper.xdr";
184 mesh.write(xdr_filename);
185 TestCommWorld->barrier();
186
187 // And test that we can read extra integers from refined XDR/XDA
188 // files. Use a freshly constructed Mesh for this.
189 Mesh mesh2(*TestCommWorld);
190 mesh2.read(xdr_filename);
191 test_final_integers(mesh2, i1);
192#endif
193
194#endif
195 }
196
197 void checkpoint_helper(ElemType elem_type, unsigned int n_elem_per_side, bool binary)
198 {
200
201 std::array<unsigned int, 6> ini = build_mesh(mesh, elem_type, n_elem_per_side);
202 const unsigned int i1 = ini[0], ni1 = ini[2], ni2 = ini[3];
203
205
206 const std::string filename =
207 std::string("extra_integers.cp") + (binary ? "r" : "a");
208
209 mesh.write(filename);
210
211 TestCommWorld->barrier();
212
213 Mesh mesh2(*TestCommWorld);
214
215 mesh2.read(filename);
216
217 // Make sure the integers got transferred to the second mesh
218 CPPUNIT_ASSERT_EQUAL(i1, mesh2.add_elem_integer("i1"));
219 CPPUNIT_ASSERT_EQUAL(ni1, mesh2.add_node_integer("ni1"));
220 CPPUNIT_ASSERT_EQUAL(ni2, mesh2.add_node_integer("ni2"));
221
222 // Make sure we didn't screw up any extra integers thereby.
223 test_final_integers(mesh2, i1);
224
225#ifdef LIBMESH_HAVE_XDR
226 // Also test that we can successfully write extra integers to
227 // XDR/XDA files. Only do this if XDR is enabled. In theory, we
228 // could still test that the ASCII (xda) file writing capability
229 // still works even when the binary (xdr) file writing capability
230 // is disabled; in practice this is probably not worth the extra
231 // hassle.
232 const std::string xdr_filename =
233 std::string("extra_integers.xd") + (binary ? "r" : "a");
234 mesh.write(xdr_filename);
235 TestCommWorld->barrier();
236
237 // And test that we can read extra integers from XDR/XDA
238 // files. Use a freshly constructed Mesh for this, so we don't
239 // conflict with any information just read in from the
240 // CheckpointIO file.
241 Mesh mesh3(*TestCommWorld);
242 mesh3.read(xdr_filename);
243
244 // Make sure mesh3 has the same Elem integer and Real values
245 // defined as the original. Note: Elem/Node data that are added via
246 // MeshBase::add_{elem,node}_datum<T>() calls can still be checked
247 // for via MeshBase::has_{elem,node}_integer() calls; there is no
248 // MeshBase::has_elem_datum() or DofObject::has_extra_datum()
249 // function...
250 CPPUNIT_ASSERT(mesh3.has_elem_integer("i1"));
251 CPPUNIT_ASSERT(mesh3.has_node_integer("ni1"));
252 CPPUNIT_ASSERT(mesh3.has_node_integer("ni2"));
253
254 // These values are *not* integers, but the "has_" functions should
255 // still return true for them because all extra Node/Elem data
256 // names are stored together, regardless of type. Real-valued data
257 // take up multiple indices in the integer storage, and have mangled
258 // names for each index they occupy beyond the first.
259 CPPUNIT_ASSERT(mesh3.has_elem_integer("r1"));
260 CPPUNIT_ASSERT(mesh3.has_node_integer("nr1"));
261 CPPUNIT_ASSERT(mesh3.has_node_integer("nr2"));
262
263 // This function only compares element integer values, not node
264 // integer values.
265 test_final_integers(mesh3, i1);
266
267 // Loop over mesh3 Elems, check that values of the r1 datum match
268 // the expected values. The test_final_integers() helper function is
269 // specific to integers and does not check the value of the r1 datum.
270 for (const auto & elem : mesh3.element_ptr_range())
271 {
272 auto r1_actual = elem->get_extra_datum<Real>(/*r1=*/ini[1]);
273 CPPUNIT_ASSERT_EQUAL(r1_actual, elem->point(0)(0)*1000);
274 }
275
276 // Loop over mesh3 Nodes, check that the Real-valued data match the
277 // expected values.
278 for (const auto & node : mesh3.node_ptr_range())
279 {
280 auto nr1_actual = node->get_extra_datum<Real>(/*nr1=*/ini[4]);
281 CPPUNIT_ASSERT_EQUAL(/*expected=*/(*node)(0)*1000, /*actual=*/nr1_actual);
282
283 // Note: nr2 is never set to anything and there is no default
284 // value provided, so the datum values get set to 2 copies
285 // (when sizeof(Real)==8 and sizeof(dof_id_type)==4) of
286 // DofObject::invalid_id. Therefore in order to test that this
287 // survived the write/read process, we treat nr2 as an extra
288 // integer here.
289 // auto nr2_actual = node->get_extra_datum<Real>(/*nr2=*/ini[5]); // NaN
290 auto nr2_actual = node->get_extra_integer(/*nr2=*/ini[5]);
291 CPPUNIT_ASSERT_EQUAL(/*expected=*/DofObject::invalid_id, /*actual=*/nr2_actual);
292 }
293#endif // LIBMESH_HAVE_XDR
294 }
295
296public:
297 void setUp() {}
298
299 void tearDown() {}
300
301 void testExtraIntegersEdge2() { LOG_UNIT_TEST; test_helper(EDGE2, 5); }
302
303 void testExtraIntegersTri6() { LOG_UNIT_TEST; test_helper(TRI6, 4); }
304
305 void testExtraIntegersCheckpointEdge3() { LOG_UNIT_TEST; checkpoint_helper(EDGE3, 5, /*binary=*/false); }
306
307 void testExtraIntegersCheckpointHex8() { LOG_UNIT_TEST; checkpoint_helper(HEX8, 2, /*binary=*/true); }
308
309#ifdef LIBMESH_HAVE_EXODUS_API
311 {
312 LOG_UNIT_TEST;
313
315 mesh.allow_renumbering(false);
316
317 // This 3-by-3 mesh contains the following element integers:
318 std::vector<std::ptrdiff_t> material_id = {0, -1, 2,
319 1, 3, 450359962,
320 2, 3, 450359963};
321 const std::string filename = "meshes/good_32bit_elem_integers.e";
322 ExodusII_IO exreader(mesh);
323 exreader.set_extra_integer_vars({"material_id"});
324 exreader.read(filename);
325
326 // Test that the ExodusII_IO::get_{elem,node}_num_map() APIs give
327 // us something sensible. Note: this is unrelated to reading
328 // extra integers, but, in my opinion, it does not warrant its own
329 // standalone test either.
330 const auto & elem_num_map = exreader.get_elem_num_map();
331 const auto & node_num_map = exreader.get_node_num_map();
332
333 // This mesh has trivial elem_num_map and node_num_map
334 CPPUNIT_ASSERT_EQUAL(int(elem_num_map.size()), 9);
335 CPPUNIT_ASSERT_EQUAL(int(node_num_map.size()), 16);
336 for (int i=0; i != 9; ++i)
337 CPPUNIT_ASSERT_EQUAL(elem_num_map[i], i+1);
338 for (int i=0; i != 16; ++i)
339 CPPUNIT_ASSERT_EQUAL(node_num_map[i], i+1);
340
341 CPPUNIT_ASSERT(mesh.has_elem_integer("material_id"));
342 unsigned int int_idx = mesh.get_elem_integer_index("material_id");
343 for (dof_id_type i=0; i != 9; ++i)
344 {
345 Elem * elem = mesh.query_elem_ptr(i);
346 if (!elem)
347 continue;
348 if (material_id[i] == -1)
349 CPPUNIT_ASSERT_EQUAL(DofObject::invalid_id, elem->get_extra_integer(int_idx));
350 else
351 CPPUNIT_ASSERT_EQUAL(dof_id_type(material_id[i]), elem->get_extra_integer(int_idx));
352 }
353 }
354#endif
355
356#if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_ENABLE_EXCEPTIONS)
358 {
359 LOG_UNIT_TEST;
360
362 /*
363 This 3-by-3 mesh contains the following element integers:
364 material_id = '0 -1 (unsigned long long)(-2) (bad)
365 1 3 4503599627370496
366 2 3 4503599627370497'
367 Real(-2) != Real(-1) (used for invalid_id), so we can tell
368 this is an error.
369 */
370 const std::string filename = "meshes/bad_64bit_elem_integers.e";
371 ExodusII_IO exreader(mesh);
372 exreader.set_extra_integer_vars({"material_id"});
373 CPPUNIT_ASSERT_THROW_MESSAGE("Bad elem integer not detected",
374 exreader.read(filename),
376 }
377#endif
378};
379
380
CPPUNIT_TEST(testExtraIntegersEdge2)
void testBadExtraIntegersExodusReading()
std::array< unsigned int, 6 > build_mesh(Mesh &mesh, ElemType elem_type, unsigned int n_elem_per_side)
void testExtraIntegersCheckpointHex8()
void test_helper(ElemType elem_type, unsigned int n_elem_per_side)
CPPUNIT_TEST(testExtraIntegersCheckpointEdge3)
void testExtraIntegersExodusReading()
void checkpoint_helper(ElemType elem_type, unsigned int n_elem_per_side, bool binary)
void testExtraIntegersCheckpointEdge3()
CPPUNIT_TEST(testExtraIntegersExodusReading)
CPPUNIT_TEST(testExtraIntegersTri6)
LIBMESH_CPPUNIT_TEST_SUITE(ExtraIntegersTest)
The goal of this test is to verify the ability to add extra integer storage to a Mesh object,...
void test_and_set_initial_data(Mesh &mesh, std::array< unsigned int, 6 > ini)
CPPUNIT_TEST(testExtraIntegersCheckpointHex8)
CPPUNIT_TEST(testBadExtraIntegersExodusReading)
void test_final_integers(Mesh &mesh, unsigned int i1)
dof_id_type get_extra_integer(const unsigned int index) const
Gets the value on this object of the extra integer associated with index, which should have been obta...
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
This is the base class from which all geometric element types are derived.
Definition elem.h:96
const Point & point(const unsigned int i) const
Definition elem.h:2462
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
const Elem * top_parent() const
Definition elem.h:3073
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
const std::vector< int > & get_node_num_map() const
Identical to the behavior of get_elem_num_map(), but for the node_num_map instead.
const std::vector< int > & get_elem_num_map() const
Returns a const reference to the elem_num_map, which is a vector that is created when a Mesh is read ...
virtual void read(const std::string &name) override
This method implements reading a mesh from a specified file.
void set_extra_integer_vars(const std::vector< std::string > &extra_integer_vars)
Set the elemental variables in the Exodus file to be read into extra element integers.
A class to represent the internal "this should never happen" errors, to be thrown by "libmesh_error()...
virtual void all_first_order()=0
Converts a mesh with higher-order elements into a mesh with linear elements.
bool has_node_integer(std::string_view name) const
Definition mesh_base.C:790
unsigned int get_elem_integer_index(std::string_view name) const
Definition mesh_base.C:689
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use.
Definition mesh_base.h:1355
void all_second_order(const bool full_ordered=true)
Calls the range-based version of this function with a range consisting of all elements in the mesh.
Definition mesh_base.C:1803
virtual void write(const std::string &name) const =0
unsigned int add_node_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each node in the mesh.
Definition mesh_base.C:712
unsigned int add_elem_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each element in the mesh.
Definition mesh_base.C:623
bool has_elem_integer(std::string_view name) const
Definition mesh_base.C:701
unsigned int add_node_datum(const std::string &name, bool allocate_data=true, const T *default_value=nullptr)
Register a datum (of type T) to be added to each node in the mesh.
Definition mesh_base.h:2679
virtual const Elem * query_elem_ptr(const dof_id_type i) const =0
unsigned int add_elem_datum(const std::string &name, bool allocate_data=true, const T *default_value=nullptr)
Register a datum (of type T) to be added to each element in the mesh.
Definition mesh_base.h:2630
virtual void partition(const unsigned int n_parts)
Call the default partitioner (currently metis_partition()).
Definition mesh_base.C:1769
Implements (adaptive) mesh refinement algorithms for a MeshBase.
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false, bool skip_detect_interior_parents=false) override
Reads the file specified by name.
Communicator * TestCommWorld
CPPUNIT_TEST_SUITE_REGISTRATION(ExtraIntegersTest)
MeshBase & mesh
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
The libMesh namespace provides an interface to certain functionality in the library.
ElemType
Defines an enum for geometric element types.
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real