libMesh
Loading...
Searching...
No Matches
Public Member Functions | Protected Member Functions | Protected Attributes | List of all members
ExodusTest< elem_type > Class Template Reference
Inheritance diagram for ExodusTest< elem_type >:
[legend]

Public Member Functions

void test_read_gold ()
 
void test_write ()
 
void setUp ()
 

Protected Member Functions

bool meshes_equal_enough (Mesh &other_mesh, bool double_precision)
 

Protected Attributes

std::unique_ptr< Mesh_mesh
 
std::string libmesh_suite_name
 

Detailed Description

template<ElemType elem_type>
class ExodusTest< elem_type >

Definition at line 21 of file exodus_test.C.

Member Function Documentation

◆ meshes_equal_enough()

template<ElemType elem_type>
bool MeshPerElemTest< elem_type >::meshes_equal_enough ( Mesh other_mesh,
bool  double_precision 
)
inlineprotectedinherited

Definition at line 15 of file mesh_elem_test.h.

16 {
17 // We'll need to fix up processor_id() and unique_id() values
18 // before we can operator== these meshes. But worse: our gold
19 // meshes might have been numbered differently to our generated
20 // meshes. Some of our generated mesh options practically
21 // *require* renumbering (e.g. after interior HEX20 nodes are
22 // deleted, ExodusII still wants to see a contiguous numbering),
23 // but ReplicatedMesh and DistributedMesh renumber differently.
24 //
25 // So, let's renumber too.
26
27 MeshSerializer serialthis(*this->_mesh);
28 MeshSerializer serialother(other_mesh);
29
30 const dof_id_type max_elem_id = this->_mesh->max_elem_id();
31 const dof_id_type max_node_id = this->_mesh->max_node_id();
32
33 CPPUNIT_ASSERT_EQUAL(max_elem_id, other_mesh.max_elem_id());
34 CPPUNIT_ASSERT_EQUAL(max_node_id, other_mesh.max_node_id());
35
36 auto locator = other_mesh.sub_point_locator();
37
38 for (Elem * e1 : this->_mesh->element_ptr_range())
39 {
40 const Elem * e2c = (*locator)(e1->vertex_average());
41 CPPUNIT_ASSERT(e2c);
42 Elem & e2 = other_mesh.elem_ref(e2c->id());
43 e1->processor_id() = 0;
44 e2.processor_id() = 0;
45
46 const dof_id_type e1_id = e1->id();
47 const dof_id_type e2_id = e2.id();
48 // Do a swap if necessary, using a free temporary id
49 if (e1_id != e2_id)
50 {
51 other_mesh.renumber_elem(e1_id, max_elem_id);
52 other_mesh.renumber_elem(e2_id, e1_id);
53 other_mesh.renumber_elem(max_elem_id, e2_id);
54 }
55
56#ifdef LIBMESH_ENABLE_UNIQUE_ID
57 e2.set_unique_id(e1->unique_id());
58#endif
59 }
60
61 for (Node * n1 : this->_mesh->node_ptr_range())
62 {
63 const Elem * e1c = (*locator)(*n1);
64 Node * n2 = nullptr;
65 for (const Node & n : e1c->node_ref_range())
66 {
67#if defined(LIBMESH_DEFAULT_QUADRUPLE_PRECISION) || defined(LIBMESH_DEFAULT_TRIPLE_PRECISION)
68 if (double_precision)
69 {
70 const Point diff = Point(*n1)-Point(n);
71
72 // We may be testing against ExodusII input, and if
73 // we're in triple or quadruple precision that means our
74 // lovely higher-precision node coordinates got
75 // truncated to double to be written. We need to adjust
76 // ours or they won't satisfy operator== later.
77
78 // We're *also* testing against gold files that were
79 // calculated at double precision, so just casting a
80 // higher precision calculation to double won't give the
81 // exact same 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 }
86#else
87 libmesh_ignore(double_precision);
88#endif
89 if (Point(*n1) == Point(n))
90 n2 = other_mesh.node_ptr(n.id());
91 }
92 CPPUNIT_ASSERT(n2);
93 n1->processor_id() = 0;
94 n2->processor_id() = 0;
95
96 const dof_id_type n1_id = n1->id();
97 const dof_id_type n2_id = n2->id();
98 // Do a swap if necessary, using a free temporary id
99 if (n1_id != n2_id)
100 {
101 other_mesh.renumber_node(n1_id,max_node_id);
102 other_mesh.renumber_node(n2_id,n1_id);
103 other_mesh.renumber_node(max_node_id, n2_id);
104 }
105
106#ifdef LIBMESH_ENABLE_UNIQUE_ID
107 n2->set_unique_id(n1->unique_id());
108#endif
109 }
110
111#ifdef LIBMESH_ENABLE_UNIQUE_ID
112 other_mesh.set_next_unique_id(this->_mesh->parallel_max_unique_id());
113 this->_mesh->set_next_unique_id(this->_mesh->parallel_max_unique_id());
114#endif
115
116 return *this->_mesh == other_mesh;
117 }
std::unique_ptr< Mesh > _mesh
Definition elem_test.h:26
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...
virtual dof_id_type max_node_id() const override final
virtual void set_next_unique_id(unique_id_type id) override
Sets the next available unique id to be used.
virtual dof_id_type max_elem_id() const override final
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 ...
virtual const Node * node_ptr(const dof_id_type i) const override final
processor_id_type processor_id() const
Definition dof_object.h:881
dof_id_type id() const
Definition dof_object.h:819
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:848
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition mesh_base.C:1833
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
auto norm() const
void libmesh_ignore(const Args &...)
uint8_t dof_id_type
Definition id_types.h:67
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

References PerElemTest< elem_type >::_mesh, libMesh::MeshBase::elem_ref(), libMesh::DofObject::id(), libMesh::libmesh_ignore(), libMesh::make_range(), libMesh::DistributedMesh::max_elem_id(), libMesh::DistributedMesh::max_node_id(), libMesh::DistributedMesh::node_ptr(), libMesh::Elem::node_ref_range(), libMesh::TypeVector< T >::norm(), libMesh::DofObject::processor_id(), libMesh::DistributedMesh::renumber_elem(), libMesh::DistributedMesh::renumber_node(), libMesh::DistributedMesh::set_next_unique_id(), libMesh::DofObject::set_unique_id(), and libMesh::MeshBase::sub_point_locator().

Referenced by ExodusTest< elem_type >::test_read_gold(), XdrIOTest< elem_type >::test_read_gold(), ExodusTest< elem_type >::test_write(), and XdrIOTest< elem_type >::test_write().

◆ setUp()

template<ElemType elem_type>
void PerElemTest< elem_type >::setUp ( )
inlineinherited

Definition at line 30 of file elem_test.h.

31 {
32 const Real minpos = 1.5, maxpos = 5.5;
33 const unsigned int N = 2;
34
35 _mesh = std::make_unique<Mesh>(*TestCommWorld);
36
37 std::unique_ptr<Elem> test_elem;
38
39 if (elem_type != C0POLYHEDRON)
40 test_elem = Elem::build(elem_type);
41
42#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
43#if LIBMESH_DIM > 1
44 if (test_elem.get() && test_elem->infinite())
45 {
46 Elem * elem = _mesh->add_elem(std::move(test_elem));
47
48 const auto add_point =
49 [this, elem](const unsigned int i,
50 const Real x,
51 const Real y,
52 const Real
53#if LIBMESH_DIM == 3
54 z
55#endif
56 )
57 {
58#if LIBMESH_DIM == 2
59 auto node = _mesh->add_point(Point(x, y), i);
60#else
61 auto node = _mesh->add_point(Point(x, y, z), i);
62#endif
63 elem->set_node(i, node);
64 };
65
66 const Real halfpos = (minpos + maxpos) / 2.;
67
68 if (elem_type == INFQUAD4 || elem_type == INFQUAD6 ||
69 elem_type == INFHEX8 || elem_type == INFHEX16 || elem_type == INFHEX18)
70 {
71 const bool is_quad = (elem_type == INFQUAD4 || elem_type == INFQUAD6);
72
73 add_point(0, minpos, minpos, minpos);
74 add_point(1, maxpos, minpos, minpos);
75 add_point(2+is_quad, maxpos, maxpos, minpos);
76 add_point(3-is_quad, minpos, maxpos, minpos);
77
78 if (elem_type == INFQUAD6)
79 {
80 add_point(4, halfpos, minpos, minpos);
81 add_point(5, halfpos, maxpos, minpos);
82 }
83 }
84 if (elem_type == INFHEX8 || elem_type == INFHEX16 || elem_type == INFHEX18)
85 {
86 add_point(4, minpos, minpos, maxpos);
87 add_point(5, maxpos, minpos, maxpos);
88 add_point(6, maxpos, maxpos, maxpos);
89 add_point(7, minpos, maxpos, maxpos);
90
91 if (elem_type == INFHEX16 || elem_type == INFHEX18)
92 {
93 add_point(8, halfpos, minpos, minpos);
94 add_point(9, maxpos, halfpos, minpos);
95 add_point(10, halfpos, maxpos, minpos);
96 add_point(11, minpos, halfpos, minpos);
97 add_point(12, halfpos, minpos, maxpos);
98 add_point(13, maxpos, halfpos, maxpos);
99 add_point(14, halfpos, maxpos, maxpos);
100 add_point(15, minpos, halfpos, maxpos);
101 }
102 if (elem_type == INFHEX18)
103 {
104 add_point(16, halfpos, halfpos, minpos);
105 add_point(17, halfpos, halfpos, maxpos);
106 }
107 }
108 if (elem_type == INFPRISM6 || elem_type == INFPRISM12)
109 {
110 add_point(0, minpos, minpos, minpos);
111 add_point(1, maxpos, minpos, minpos);
112 add_point(2, halfpos, maxpos, minpos);
113 add_point(3, minpos, minpos, maxpos);
114 add_point(4, maxpos, minpos, maxpos);
115 add_point(5, halfpos, maxpos, maxpos);
116
117 if (elem_type == INFPRISM12)
118 {
119 add_point(6, halfpos, minpos, minpos);
120 add_point(7, (halfpos + maxpos) / 2., halfpos, minpos);
121 add_point(8, (halfpos + minpos) / 2., halfpos, minpos);
122 add_point(9, halfpos, minpos, maxpos);
123 add_point(10, (halfpos + maxpos) / 2., halfpos, maxpos);
124 add_point(11, (halfpos + minpos) / 2., halfpos, maxpos);
125 }
126 }
127
128 _mesh->prepare_for_use();
129 }
130 else
131#endif // LIBMESH_DIM > 1
132#endif // LIBMESH_ENABLE_INFINITE_ELEMENTS
133 if (elem_type == C0POLYGON)
134 {
135 // We're not going to implement build_square for e.g.
136 // pentagons any time soon.
137 //
138 // We should probably implement build_dual_mesh, though, and
139 // run it on a perturbed or triangle input so we don't just
140 // get quads in the output...
141
142 _mesh->add_point(Point(0, 0), 0);
143 _mesh->add_point(Point(1, 0), 1);
144 _mesh->add_point(Point(1.5, 0.5), 2);
145 _mesh->add_point(Point(1, 1), 3);
146 _mesh->add_point(Point(0, 1), 4);
147
148 std::unique_ptr<Elem> polygon = std::make_unique<C0Polygon>(5);
149 for (auto i : make_range(5))
150 polygon->set_node(i, _mesh->node_ptr(i));
151 polygon->set_id() = 0;
152
153 _mesh->add_elem(std::move(polygon));
154 _mesh->prepare_for_use();
155 }
156 else if (elem_type == C0POLYHEDRON)
157 {
158 // There's even less point in having a build_cube for general
159 // polyhedra, so again we'll hand-make one for testing and
160 // we'll plan on making a build_dual_mesh for the future.
161
162#if 0
163 // Try a truncated cube for relatively simple verification.
164
165 // We have some differing orientations on the top sides, to
166 // test handling of that.
167 // Lower octagon points, counterclockwise
168 _mesh->add_point(Point(1/Real(3), 0, 0), 0);
169 _mesh->add_point(Point(2/Real(3), 0, 0), 1);
170 _mesh->add_point(Point(1, 1/Real(3), 0), 2);
171 _mesh->add_point(Point(1, 2/Real(3), 0), 3);
172 _mesh->add_point(Point(2/Real(3), 1, 0), 4);
173 _mesh->add_point(Point(1/Real(3), 1, 0), 5);
174 _mesh->add_point(Point(0, 2/Real(3), 0), 6);
175 _mesh->add_point(Point(0, 1/Real(3), 0), 7);
176
177 // Lower-middle points, counterclockwise
178 _mesh->add_point(Point(0, 0, 1/Real(3)), 8);
179 _mesh->add_point(Point(1, 0, 1/Real(3)), 9);
180 _mesh->add_point(Point(1, 1, 1/Real(3)), 10);
181 _mesh->add_point(Point(0, 1, 1/Real(3)), 11);
182
183 // Upper-middle points, counterclockwise
184 _mesh->add_point(Point(0, 0, 2/Real(3)), 12);
185 _mesh->add_point(Point(1, 0, 2/Real(3)), 13);
186 _mesh->add_point(Point(1, 1, 2/Real(3)), 14);
187 _mesh->add_point(Point(0, 1, 2/Real(3)), 15);
188
189 // Upper octagon points, counterclockwise
190 _mesh->add_point(Point(1/Real(3), 0, 1), 16);
191 _mesh->add_point(Point(2/Real(3), 0, 1), 17);
192 _mesh->add_point(Point(1, 1/Real(3), 1), 18);
193 _mesh->add_point(Point(1, 2/Real(3), 1), 19);
194 _mesh->add_point(Point(2/Real(3), 1, 1), 20);
195 _mesh->add_point(Point(1/Real(3), 1, 1), 21);
196 _mesh->add_point(Point(0, 2/Real(3), 1), 22);
197 _mesh->add_point(Point(0, 1/Real(3), 1), 23);
198
199 const std::vector<std::vector<unsigned int>> nodes_on_side =
200 { {0, 1, 2, 3, 4, 5, 6, 7}, // min z
201 {0, 1, 9, 13, 17, 16, 12, 8}, // min y
202 {2, 3, 10, 14, 19, 18, 13, 9}, // max x
203 {4, 5, 11, 15, 21, 20, 14, 10}, // max y
204 {6, 7, 8, 12, 23, 22, 15, 11}, // min x
205 {16, 17, 18, 19, 20, 21, 22, 23}, // max z
206 {7, 0, 8}, // max nothing
207 {1, 2, 9}, // max x
208 {3, 4, 10}, // max xy
209 {5, 6, 11}, // max y
210 {23, 16, 12}, // max z
211 {17, 18, 13}, // max xz
212 {19, 20, 14}, // max xyz
213 {21, 22, 15} }; // max yz
214#endif
215
216#if 1
217 // Or try an affine-skewed box. Unlike a unit cube, its
218 // non-unit quality values cannot be confused with the generic
219 // fallback value of 1.
220 _mesh->add_point(Point(0, 0, 0), 0);
221 _mesh->add_point(Point(2, 0, 0), 1);
222 _mesh->add_point(Point(2.25, 1, 0), 2);
223 _mesh->add_point(Point(0.25, 1, 0), 3);
224 _mesh->add_point(Point(0, 0, 1), 4);
225 _mesh->add_point(Point(2, 0, 1), 5);
226 _mesh->add_point(Point(2.25, 1, 1), 6);
227 _mesh->add_point(Point(0.25, 1, 1), 7);
228
229 // With some combinations of face triangulations, even a
230 // simple box has no tetrahedralization that doesn't have
231 // either interior discontinuities or a 0-volume pancake tet!
232 //
233 // The initial "natural" way to orient our sides is commented
234 // out here; permutations that fix diagonals for us are used
235 // instead.
236 // Listing both z sides first also tests nonconsecutive local
237 // side edges in the polyhedron edge lookup.
238 const std::vector<std::vector<unsigned int>> nodes_on_side =
239 { {0, 1, 2, 3}, // min z
240 {5, 6, 7, 4}, // max z
241 {0, 1, 5, 4}, // min y
242 // {1, 2, 6, 5}, // max x - bad
243 {2, 6, 5, 1}, // max x
244 {2, 3, 7, 6}, // max y
245 // {3, 0, 4, 7}, // min x - bad
246 {0, 4, 7, 3} }; // min x
247#endif
248
249 // Build all the sides.
250 std::vector<std::shared_ptr<Polygon>> sides(nodes_on_side.size());
251
252 for (auto s : index_range(nodes_on_side))
253 {
254 const auto & nodes_on_s = nodes_on_side[s];
255 sides[s] = std::make_shared<C0Polygon>(nodes_on_s.size());
256 for (auto i : index_range(nodes_on_s))
257 sides[s]->set_node(i, _mesh->node_ptr(nodes_on_s[i]));
258 }
259
260 std::unique_ptr<libMesh::Node> mid_elem_node;
261 std::unique_ptr<Elem> polyhedron = std::make_unique<C0Polyhedron>(sides, mid_elem_node);
262 _mesh->add_elem(std::move(polyhedron));
263 if (mid_elem_node)
264 _mesh->add_node(std::move(mid_elem_node));
265 _mesh->prepare_for_use();
266 }
267 else
268 {
269 const unsigned int dim = test_elem->dim();
270 const unsigned int use_x = dim > 0;
271 const unsigned int use_y = dim > 1;
272 const unsigned int use_z = dim > 2;
273
275 N*use_x, N*use_y, N*use_z,
276 minpos, maxpos,
277 minpos, use_y*maxpos,
278 minpos, use_z*maxpos,
279 elem_type);
280 }
281
282 // Use non-default subdomain ids so we can properly test their
283 // preservation.
284 //
285 // Use long subdomain names so we can test that we're not
286 // truncating as badly as we used to in ExodusII.
287 for (const auto & elem :
288 this->_mesh->element_ptr_range())
289 {
290 const subdomain_id_type sbdid = 10 + (elem->id() % 10);
291 elem->subdomain_id() = sbdid;
292 std::ostringstream sbdname;
293 sbdname <<
294 "a_very_long_subdomain_name_for_the_subdomain_with_number_" << sbdid;
295 this->_mesh->set_subdomain_name(sbdid, sbdname.str());
296 }
297
298 // Make sure our mesh's cache knows about them all for later
299 // test comparisons
300 this->_mesh->cache_elem_data();
301
302 // We may have updated only a portion of subdomain names if we're
303 // on a distributed mesh, but every processor should know about
304 // every name.
305 this->_mesh->sync_subdomain_name_map();
306 }
unsigned int dim
virtual Node *& set_node(const unsigned int i)
Definition elem.h:2567
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
subdomain_id_type subdomain_id() const
Definition elem.h:2591
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.
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
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

References PerElemTest< elem_type >::_mesh, libMesh::Elem::build(), libMesh::MeshTools::Generation::build_cube(), libMesh::C0POLYGON, libMesh::C0POLYHEDRON, dim, libMesh::index_range(), libMesh::INFHEX16, libMesh::INFHEX18, libMesh::INFHEX8, libMesh::INFPRISM12, libMesh::INFPRISM6, libMesh::INFQUAD4, libMesh::INFQUAD6, libMesh::make_range(), libMesh::Real, libMesh::Elem::set_node(), and TestCommWorld.

◆ test_read_gold()

template<ElemType elem_type>
void ExodusTest< elem_type >::test_read_gold ( )
inline

Definition at line 24 of file exodus_test.C.

25 {
26 LOG_UNIT_TEST;
27
28 Mesh input_mesh(*TestCommWorld);
29
30 ExodusII_IO exii(input_mesh);
31 if (input_mesh.processor_id() == 0)
32 exii.read("meshes/exodus_elements/read_exodus_" +
33 Utility::enum_to_string(elem_type) + ".e");
34
35 MeshCommunication().broadcast(input_mesh);
36 input_mesh.prepare_for_use();
37
38 CPPUNIT_ASSERT(this->meshes_equal_enough(input_mesh, true));
39 }
bool meshes_equal_enough(Mesh &other_mesh, bool double_precision)
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
This is the MeshCommunication class.
void broadcast(MeshBase &) const
This method takes a mesh (which is assumed to reside on processor 0) and broadcasts it to all the oth...
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
std::string enum_to_string(const T e)

References libMesh::MeshCommunication::broadcast(), libMesh::Utility::enum_to_string(), MeshPerElemTest< elem_type >::meshes_equal_enough(), libMesh::MeshBase::prepare_for_use(), libMesh::ParallelObject::processor_id(), libMesh::ExodusII_IO::read(), and TestCommWorld.

◆ test_write()

template<ElemType elem_type>
void ExodusTest< elem_type >::test_write ( )
inline

Definition at line 41 of file exodus_test.C.

42 {
43 LOG_UNIT_TEST;
44
45 // This is a *buffered* write; we use scope to make sure the
46 // ExodusII_IO object gets destructed (and thus is guaranteed to
47 // finish writing and close the file) before we try to read what
48 // was written.
49 {
50 ExodusII_IO exii(*this->_mesh);
51
52 // We still default to 32-char names for backwards
53 // compatibility, but we're writing a mesh with extra-long names
54 // in it for testing, so we manually enable longer names.
55 exii.set_max_name_length(80);
56
57 exii.write("write_exodus_" +
58 Utility::enum_to_string(elem_type) + ".e");
59 }
60
61 Mesh input_mesh(*TestCommWorld);
62 ExodusII_IO exii_input(input_mesh);
63 if (input_mesh.processor_id() == 0)
64 exii_input.read("write_exodus_" +
65 Utility::enum_to_string(elem_type) + ".e");
66
67 MeshCommunication().broadcast(input_mesh);
68 input_mesh.prepare_for_use();
69
70 CPPUNIT_ASSERT(this->meshes_equal_enough(input_mesh, true));
71 }

References PerElemTest< elem_type >::_mesh, libMesh::MeshCommunication::broadcast(), libMesh::Utility::enum_to_string(), MeshPerElemTest< elem_type >::meshes_equal_enough(), libMesh::MeshBase::prepare_for_use(), libMesh::ParallelObject::processor_id(), libMesh::ExodusII_IO::read(), libMesh::ExodusII_IO::set_max_name_length(), TestCommWorld, and libMesh::ExodusII_IO::write().

Member Data Documentation

◆ _mesh

template<ElemType elem_type>
std::unique_ptr<Mesh> PerElemTest< elem_type >::_mesh
protectedinherited

◆ libmesh_suite_name

template<ElemType elem_type>
std::string PerElemTest< elem_type >::libmesh_suite_name
protectedinherited

Definition at line 27 of file elem_test.h.


The documentation for this class was generated from the following file: