libMesh
exodus_test.C
Go to the documentation of this file.
1 #include "../geom/elem_test.h"
2 
3 #ifdef LIBMESH_HAVE_EXODUS_API
4 
5 #include "libmesh/enum_to_string.h"
6 #include "libmesh/exodusII_io.h"
7 #include "libmesh/mesh_communication.h"
8 #include "libmesh/mesh_serializer.h"
9 
10 using namespace libMesh;
11 
12 template <ElemType elem_type>
13 class ExodusTest : public PerElemTest<elem_type> {
14 
15 private:
16 
17  bool meshes_equal_enough(Mesh & other_mesh)
18  {
19  // We'll need to fix up processor_id() and unique_id() values
20  // before we can operator== these meshes. But worse: our gold
21  // meshes might have been numbered differently to our generated
22  // meshes. Some of our generated mesh options practically
23  // *require* renumbering (e.g. after interior HEX20 nodes are
24  // deleted, ExodusII still wants to see a contiguous numbering),
25  // but ReplicatedMesh and DistributedMesh renumber differently.
26  //
27  // So, let's renumber too.
28 
29  MeshSerializer serialthis(*this->_mesh);
30  MeshSerializer serialother(other_mesh);
31 
32  const dof_id_type max_elem_id = this->_mesh->max_elem_id();
33  const dof_id_type max_node_id = this->_mesh->max_node_id();
34 
35  CPPUNIT_ASSERT_EQUAL(max_elem_id, other_mesh.max_elem_id());
36  CPPUNIT_ASSERT_EQUAL(max_node_id, other_mesh.max_node_id());
37 
38  auto locator = other_mesh.sub_point_locator();
39 
40  for (Elem * e1 : this->_mesh->element_ptr_range())
41  {
42  const Elem * e2c = (*locator)(e1->vertex_average());
43  CPPUNIT_ASSERT(e2c);
44  Elem & e2 = other_mesh.elem_ref(e2c->id());
45  e1->processor_id() = 0;
46  e2.processor_id() = 0;
47 
48  const dof_id_type e1_id = e1->id();
49  const dof_id_type e2_id = e2.id();
50  // Do a swap if necessary, using a free temporary id
51  if (e1_id != e2_id)
52  {
53  other_mesh.renumber_elem(e1_id, max_elem_id);
54  other_mesh.renumber_elem(e2_id, e1_id);
55  other_mesh.renumber_elem(max_elem_id, e2_id);
56  }
57 
58 #ifdef LIBMESH_ENABLE_UNIQUE_ID
59  e2.set_unique_id(e1->unique_id());
60 #endif
61  }
62 
63  for (Node * n1 : this->_mesh->node_ptr_range())
64  {
65  const Elem * e1c = (*locator)(*n1);
66  Node * n2 = nullptr;
67  for (const Node & n : e1c->node_ref_range())
68  {
69 #if LIBMESH_DEFAULT_QUADRUPLE_PRECISION
70  const Point diff = Point(*n1)-Point(n);
71 
72  // We're testing against ExodusII input, and if we're in
73  // triple or quadruple precision that means our lovely
74  // higher-precision node coordinates got truncated to double
75  // to be written. We need to adjust ours or they won't
76  // satisfy operator== later.
77 
78  // We're *also* testing against gold files that were
79  // calculated at double precision, so just casting a higher
80  // precision calculation to double won't give the exact same
81  // result, we have to account for error.
82  if (diff.norm() < 1e-15)
83  for (auto d : make_range(LIBMESH_DIM))
84  (*n1)(d) = double(n(d));
85 #endif
86  if (Point(*n1) == Point(n))
87  n2 = other_mesh.node_ptr(n.id());
88  }
89  CPPUNIT_ASSERT(n2);
90  n1->processor_id() = 0;
91  n2->processor_id() = 0;
92 
93  const dof_id_type n1_id = n1->id();
94  const dof_id_type n2_id = n2->id();
95  // Do a swap if necessary, using a free temporary id
96  if (n1_id != n2_id)
97  {
98  other_mesh.renumber_node(n1_id,max_node_id);
99  other_mesh.renumber_node(n2_id,n1_id);
100  other_mesh.renumber_node(max_node_id, n2_id);
101  }
102 
103 #ifdef LIBMESH_ENABLE_UNIQUE_ID
104  n2->set_unique_id(n1->unique_id());
105 #endif
106  }
107 
108 #ifdef LIBMESH_ENABLE_UNIQUE_ID
109  other_mesh.set_next_unique_id(this->_mesh->parallel_max_unique_id());
110  this->_mesh->set_next_unique_id(this->_mesh->parallel_max_unique_id());
111 #endif
112 
113  return *this->_mesh == other_mesh;
114  }
115 
116 public:
117 
119  {
120  LOG_UNIT_TEST;
121 
122  Mesh input_mesh(*TestCommWorld);
123 
124  ExodusII_IO exii(input_mesh);
125  if (input_mesh.processor_id() == 0)
126  exii.read("meshes/exodus_elements/read_exodus_" +
127  Utility::enum_to_string(elem_type) + ".e");
128 
129  MeshCommunication().broadcast(input_mesh);
130  input_mesh.prepare_for_use();
131 
132  CPPUNIT_ASSERT(this->meshes_equal_enough(input_mesh));
133  }
134 
135  void test_write()
136  {
137  LOG_UNIT_TEST;
138 
139  // This is a *buffered* write; we use scope to make sure the
140  // ExodusII_IO object gets destructed (and thus is guaranteed to
141  // finish writing and close the file) before we try to read what
142  // was written.
143  {
144  ExodusII_IO exii(*this->_mesh);
145  exii.write("write_exodus_" +
146  Utility::enum_to_string(elem_type) + ".e");
147  }
148 
149  Mesh input_mesh(*TestCommWorld);
150  ExodusII_IO exii_input(input_mesh);
151  if (input_mesh.processor_id() == 0)
152  exii_input.read("write_exodus_" +
153  Utility::enum_to_string(elem_type) + ".e");
154 
155  MeshCommunication().broadcast(input_mesh);
156  input_mesh.prepare_for_use();
157 
158  CPPUNIT_ASSERT(this->meshes_equal_enough(input_mesh));
159  }
160 };
161 
162 #define EXODUSTEST \
163  CPPUNIT_TEST( test_read_gold ); \
164  CPPUNIT_TEST( test_write );
165 
166 #define INSTANTIATE_EXODUSTEST(elemtype) \
167  class ExodusTest_##elemtype : public ExodusTest<elemtype> { \
168  public: \
169  ExodusTest_##elemtype() : \
170  ExodusTest<elemtype>() { \
171  if (unitlog->summarized_logs_enabled()) \
172  this->libmesh_suite_name = "ExodusTest"; \
173  else \
174  this->libmesh_suite_name = "ExodusTest_" #elemtype; \
175  } \
176  CPPUNIT_TEST_SUITE( ExodusTest_##elemtype ); \
177  EXODUSTEST; \
178  CPPUNIT_TEST_SUITE_END(); \
179  }; \
180  \
181  CPPUNIT_TEST_SUITE_REGISTRATION( ExodusTest_##elemtype )
182 
186 
187 #if LIBMESH_DIM > 1
192 
199 #endif // LIBMESH_DIM > 1
200 
201 #if LIBMESH_DIM > 2
205 
209 
215 
216 // These tests use PointLocator, which uses contains_point(), which
217 // uses inverse_map(), which doesn't play nicely on Pyramids unless we
218 // have exceptions support
219 #ifdef LIBMESH_ENABLE_EXCEPTIONS
224 #endif
225 #endif // LIBMESH_DIM > 2
226 
227 #endif // LIBMESH_HAVE_EXODUS_API
unique_id_type & set_unique_id()
Definition: dof_object.h:858
A Node is like a Point, but with more information.
Definition: node.h:52
auto norm() const -> decltype(std::norm(T()))
Definition: type_vector.h:907
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition: mesh_base.C:1638
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id) override final
Changes the id of element old_id, both by changing elem(old_id)->id() and by moving elem(old_id) in t...
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:171
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly ecreated (or read) mesh for use.
Definition: mesh_base.C:759
bool meshes_equal_enough(Mesh &other_mesh)
Definition: exodus_test.C:17
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
The libMesh namespace provides an interface to certain functionality in the library.
This is the MeshCommunication class.
dof_id_type id() const
Definition: dof_object.h:828
void test_read_gold()
Definition: exodus_test.C:118
virtual void set_next_unique_id(unique_id_type id) override
Sets the next available unique id to be used.
virtual void read(const std::string &name) override
This method implements reading a mesh from a specified file.
Definition: exodusII_io.C:244
std::string enum_to_string(const T e)
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition: elem.h:2665
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id) override final
Changes the id of node old_id, both by changing node(old_id)->id() and by moving node(old_id) in the ...
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
virtual void write(const std::string &fname) override
This method implements writing a mesh to a specified file.
Definition: exodusII_io.C:2180
virtual dof_id_type max_node_id() const override final
void broadcast(MeshBase &) const
Finds all the processors that may contain elements that neighbor my elements.
INSTANTIATE_EXODUSTEST(EDGE2)
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:639
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:140
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
processor_id_type processor_id() const
virtual dof_id_type max_elem_id() const override final
processor_id_type processor_id() const
Definition: dof_object.h:905
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
uint8_t dof_id_type
Definition: id_types.h:67
virtual const Node * node_ptr(const dof_id_type i) const override final
void test_write()
Definition: exodus_test.C:135