libMesh
Loading...
Searching...
No Matches
Public Member Functions | List of all members
WriteElemsetData Class Reference
Inheritance diagram for WriteElemsetData:
[legend]

Public Member Functions

 LIBMESH_CPPUNIT_TEST_SUITE (WriteElemsetData)
 
 CPPUNIT_TEST (testWriteExodus)
 
 CPPUNIT_TEST_SUITE_END ()
 
void checkByCentroid (const PointLocatorBase &pl, const Point &centroid, unsigned int elemset_index, dof_id_type expected_elemset_code)
 
void checkElemsetCodes (const MeshBase &mesh)
 
template<typename IOClass >
void testWriteImpl (const std::string &filename)
 
void testWriteExodus ()
 
void testWriteNemesis ()
 

Detailed Description

Definition at line 21 of file write_elemset_data.C.

Member Function Documentation

◆ checkByCentroid()

void WriteElemsetData::checkByCentroid ( const PointLocatorBase pl,
const Point centroid,
unsigned int  elemset_index,
dof_id_type  expected_elemset_code 
)
inline

Definition at line 37 of file write_elemset_data.C.

41 {
42 const Elem * elem = pl(centroid);
43
44 // For ReplicatedMesh, this Elem should be found on all procs, but
45 // in case this test is ever run with a DistributedMesh, the Elem
46 // won't be found on all procs, so we only test it on procs where
47 // it is found.
48 if (elem)
49 CPPUNIT_ASSERT_EQUAL(/*expected=*/expected_elemset_code, /*actual=*/elem->get_extra_integer(elemset_index));
50 }
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...
This is the base class from which all geometric element types are derived.
Definition elem.h:96

References libMesh::DofObject::get_extra_integer().

Referenced by checkElemsetCodes().

◆ checkElemsetCodes()

void WriteElemsetData::checkElemsetCodes ( const MeshBase mesh)
inline

Definition at line 52 of file write_elemset_data.C.

53 {
54 // Make sure that the mesh actually has an extra_integer for "elemset_code"
55 CPPUNIT_ASSERT(mesh.has_elem_integer("elemset_code"));
56
57 // Check that the elements in mesh are in the correct elemsets.
58 // The elemset_codes will not in general match because they are
59 // created by a generic algorithm in the Exodus reader while above
60 // they were hard-coded.
61 unsigned int elemset_index = mesh.get_elem_integer_index("elemset_code");
62
63 // Make sure the elemset_codes match what we are expecting.
64 // The Exodus reader assigns the codes based on operator<
65 // for std::sets, which gives us the ordering {1}, {1,2}, {2}
66 CPPUNIT_ASSERT_EQUAL(/*expected=*/static_cast<dof_id_type>(0), /*actual=*/mesh.get_elemset_code({1}));
67 CPPUNIT_ASSERT_EQUAL(/*expected=*/static_cast<dof_id_type>(1), /*actual=*/mesh.get_elemset_code({1,2}));
68 CPPUNIT_ASSERT_EQUAL(/*expected=*/static_cast<dof_id_type>(2), /*actual=*/mesh.get_elemset_code({2}));
69
70 // Debugging: print vertex_average() for some elements. Perhaps we can use this to uniquely identify
71 // elements for the test...
72 // for (auto id : {8,14,3,24,9,15})
73 // libMesh::out << "Elem " << id
74 // << ", vertex average = " << mesh.elem_ptr(id)->vertex_average()
75 // << std::endl;
76
77 // We'll use a PointLocator to quickly find elements by centroid
78 auto pl = mesh.sub_point_locator();
79
80 // Return nullptr when Points are not located in any element
81 // rather than crashing. When running in parallel, this happens
82 // quite often.
83 pl->enable_out_of_mesh_mode();
84
85 // Test that elements have the same elemset codes they did prior to being written to file.
86 checkByCentroid(*pl, Point(0.4, -0.4, 0), elemset_index, /*expected_elemset_code=*/0); // original Elem 8
87 checkByCentroid(*pl, Point(0.8, 0, 0), elemset_index, /*expected_elemset_code=*/0); // original Elem 14
88 checkByCentroid(*pl, Point(0.4, -0.8, 0), elemset_index, /*expected_elemset_code=*/1); // original Elem 3
89 checkByCentroid(*pl, Point(0.8, 0.8, 0), elemset_index, /*expected_elemset_code=*/1); // original Elem 24
90 checkByCentroid(*pl, Point(0.8, -0.4, 0), elemset_index, /*expected_elemset_code=*/2); // original Elem 9
91 checkByCentroid(*pl, Point(-0.8, 0.4, 0), elemset_index, /*expected_elemset_code=*/2); // original Elem 15
92 }
void checkByCentroid(const PointLocatorBase &pl, const Point &centroid, unsigned int elemset_index, dof_id_type expected_elemset_code)
unsigned int get_elem_integer_index(std::string_view name) const
Definition mesh_base.C:689
dof_id_type get_elemset_code(const MeshBase::elemset_type &id_set) const
Definition mesh_base.C:497
bool has_elem_integer(std::string_view name) const
Definition mesh_base.C:701
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition mesh_base.C:1833
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
MeshBase & mesh
uint8_t dof_id_type
Definition id_types.h:67

References checkByCentroid(), libMesh::MeshBase::get_elem_integer_index(), libMesh::MeshBase::get_elemset_code(), libMesh::MeshBase::has_elem_integer(), mesh, and libMesh::MeshBase::sub_point_locator().

Referenced by testWriteImpl().

◆ CPPUNIT_TEST()

WriteElemsetData::CPPUNIT_TEST ( testWriteExodus  )

◆ CPPUNIT_TEST_SUITE_END()

WriteElemsetData::CPPUNIT_TEST_SUITE_END ( )

◆ LIBMESH_CPPUNIT_TEST_SUITE()

WriteElemsetData::LIBMESH_CPPUNIT_TEST_SUITE ( WriteElemsetData  )

◆ testWriteExodus()

void WriteElemsetData::testWriteExodus ( )
inline

Definition at line 311 of file write_elemset_data.C.

312 {
313 LOG_UNIT_TEST;
314
315 testWriteImpl<ExodusII_IO>("write_elemset_data.e");
316 }

◆ testWriteImpl()

template<typename IOClass >
void WriteElemsetData::testWriteImpl ( const std::string &  filename)
inline

Definition at line 95 of file write_elemset_data.C.

96 {
97 // TODO: Currently this test only works for ReplicatedMesh. It
98 // should be updated so that it works for DistributedMesh as well,
99 // and then we can just set MeshType == Mesh.
100 typedef ReplicatedMesh MeshType;
101
102 // Construct mesh for writing
103 MeshType mesh(*TestCommWorld);
104
105 // Allocate space for an extra integer on each element to store a "code" which
106 // determines which sets an Elem belongs to. We do this before building the Mesh.
107 //
108 // extra_integer val & sets elem belongs to
109 // DofObject::invalid_id (default) & elem belongs to no sets
110 // 1 & elem belongs to set A only
111 // 2 & elem belongs to set B only
112 // 3 & elem belongs to sets A and B
113 unsigned int elemset_index =
114 mesh.add_elem_integer("elemset_code",
115 /*allocate_data=*/true);
116
117 // We are generating this mesh, so it should not be renumbered.
118 // No harm in being explicit about it, however.
119 mesh.allow_renumbering(false);
120
122 /*nx=*/5, /*ny=*/5,
123 -1., 1.,
124 -1., 1.,
125 QUAD4);
126
127 // Set ids for elements in elemsets 1, 2
128 std::set<dof_id_type> set1 = {3, 8, 14, 24};
129 std::set<dof_id_type> set2 = {3, 9, 15, 24};
130
131 // Loop over Elems and set "elemset_code" values
132 for (const auto & elem : mesh.element_ptr_range())
133 {
134 bool
135 in1 = set1.count(elem->id()),
136 in2 = set2.count(elem->id());
137
139 if (in1)
140 val = 1;
141 if (in2)
142 val = 2;
143 if (in1 && in2)
144 val = 3;
145
146 elem->set_extra_integer(elemset_index, val);
147 }
148
149 // Tell the Mesh about these elemsets
150 mesh.add_elemset_code(1, {1});
151 mesh.add_elemset_code(2, {2});
152 mesh.add_elemset_code(3, {1,2});
153
154 // Debugging: print valid elemset_codes values
155 // for (const auto & elem : mesh.element_ptr_range())
156 // {
157 // dof_id_type elemset_code =
158 // elem->get_extra_integer(elemset_index);
159 //
160 // if (elemset_code != DofObject::invalid_id)
161 // libMesh::out << "Elem " << elem->id() << ", elemset_code = " << elemset_code << std::endl;
162 // }
163
164 // Set up variables defined on these elemsets
165 std::vector<std::string> var_names = {"var1", "var2", "var3"};
166 std::vector<std::set<elemset_id_type>> elemset_ids =
167 {
168 {1}, // var1 is defined on elemset 1
169 {2}, // var2 is defined on elemset 2
170 {1,2} // var3 is defined on elemsets 1 and 2
171 };
172 std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> elemset_vals(var_names.size());
173
174 // To catch values handed back by MeshBase::get_elemsets()
175 std::set<elemset_id_type> id_set_to_fill;
176
177 for (const auto & elem : mesh.element_ptr_range())
178 {
179 // Get list of elemset ids to which this element belongs
180 mesh.get_elemsets(elem->get_extra_integer(elemset_index), id_set_to_fill);
181
182 bool
183 in1 = id_set_to_fill.count(1),
184 in2 = id_set_to_fill.count(2);
185
186 // Set the value for var1 == 1.0 on all elements in elemset 1
187 if (in1)
188 elemset_vals[/*var1 index=*/0].emplace( std::make_pair(elem->id(), /*elemset_id=*/1), 1.0);
189
190 // Set the value of var2 == 2.0 on all elements in elemset 2
191 if (in2)
192 elemset_vals[/*var2 index=*/1].emplace( std::make_pair(elem->id(), /*elemset_id=*/2), 2.0);
193
194 // Set the value of var3 == 3.0 on elements in the union of sets 1 and 2
195 if (in1 || in2)
196 for (const auto & id : id_set_to_fill)
197 elemset_vals[/*var3 index=*/2].emplace( std::make_pair(elem->id(), /*elemset_id=*/id), 3.0);
198 }
199
200 // Sanity check: we should have 8 total elements in set1 and set2 combined
201 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(8), elemset_vals[/*var3 index=*/2].size());
202
203 // Lambda to help with debugging
204 // auto print_map = [](const std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> & input)
205 // {
206 // for (auto i : index_range(input))
207 // {
208 // libMesh::out << "Map " << i << " = " << std::endl;
209 // for (const auto & [pr, val] : input[i])
210 // {
211 // const auto & elem_id = pr.first;
212 // const auto & elemset_id = pr.second;
213 // libMesh::out << "(" << elem_id << ", " << elemset_id << ") = " << val << std::endl;
214 // }
215 // }
216 // };
217
218 // Debugging: print the elemset_vals struct we just built
219 // print_map(elemset_vals);
220
221 // Write the file in the ExodusII format, including the element set information.
222 // Note: elemsets should eventually be written during ExodusII_IO::write(), this
223 // would match the behavior of sidesets and nodesets.
224 {
225 IOClass writer(mesh);
226 writer.write(filename);
227 writer.write_elemset_data(/*timestep=*/1, var_names, elemset_ids, elemset_vals);
228 }
229
230 // Make sure that the writing is done before the reading starts.
231 TestCommWorld->barrier();
232
233 // Now read it back in
234 MeshType read_mesh(*TestCommWorld);
235
236 // Do not allow renumbering on this mesh either.
237 read_mesh.allow_renumbering(false);
238
239 IOClass reader(read_mesh);
240 // reader.verbose(true); // additional messages while debugging
241 reader.read(filename);
242
243 // When reading in a Mesh using an "IOClass" object, it is not
244 // automatically prepared for use, so do that now.
245 read_mesh.prepare_for_use();
246
247 // Do generic checks that are independent of IOClass
248 checkElemsetCodes(read_mesh);
249
250 // Read in the elemset variables from file. This is currently a
251 // feature that is only supported by the Exodus IOClass, so it is
252 // not part of the checkElemsetCodes() function.
253 std::vector<std::string> read_in_var_names;
254 std::vector<std::set<elemset_id_type>> read_in_elemset_ids;
255 std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> read_in_elemset_vals;
256 reader.read_elemset_data(/*timestep=*/1, read_in_var_names, read_in_elemset_ids, read_in_elemset_vals);
257
258 // Debugging
259 // print_map(read_in_elemset_vals);
260
261 // Assert that the data we read in matches what we wrote out
262 CPPUNIT_ASSERT(read_in_var_names == var_names);
263 CPPUNIT_ASSERT(read_in_elemset_ids == elemset_ids);
264 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(8), read_in_elemset_vals[/*var3 index=*/2].size());
265 CPPUNIT_ASSERT(read_in_elemset_vals == elemset_vals);
266
267 // Also check that the flat indices match those in the file
268 std::map<std::pair<dof_id_type, elemset_id_type>, unsigned int> elemset_array_indices;
269 reader.get_elemset_data_indices(elemset_array_indices);
270
271 // Verify that we have the following (Exodus-based) elem ids in the following indices.
272 // These indices were copied from an ncdump of the exo file.
273 std::vector<dof_id_type> elem_els1 = {4, 9, 15, 25};
274 std::vector<dof_id_type> elem_els2 = {4, 10, 16, 25};
275
276 for (auto i : index_range(elem_els1))
277 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(i),
278 elemset_array_indices[std::make_pair(/*elem id=*/elem_els1[i] - 1, // convert to libmesh id
279 /*set id*/1)]);
280 for (auto i : index_range(elem_els2))
281 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(i),
282 elemset_array_indices[std::make_pair(/*elem id=*/elem_els2[i] - 1, // convert to libmesh id
283 /*set id*/2)]);
284
285#ifdef LIBMESH_HAVE_XDR
286 // Also test that we can successfully write elemset codes to
287 // XDR/XDA files. Only do this if XDR is enabled. In theory, we
288 // could still test that the ASCII (xda) file writing capability
289 // still works even when the binary (xdr) file writing capability
290 // is disabled; in practice this is probably not worth the extra
291 // hassle.
292
293 // Now write an xda file so that we can test that elemset codes
294 // are preserved when reading the Mesh back in.
295 read_mesh.write("write_elemset_data.xda");
296
297 // Make sure that the writing is done before the reading starts.
298 TestCommWorld->barrier();
299
300 // Now read it back in and do generic checks that are independent of IOClass.
301 Mesh read_mesh2(*TestCommWorld);
302 // XDR files implicitly renumber mesh files in parallel, so setting this flag
303 // does not have the desired effect of preventing renumbering in that case.
304 read_mesh2.allow_renumbering(false);
305 read_mesh2.read("write_elemset_data.xda");
306 checkElemsetCodes(read_mesh2);
307
308#endif // LIBMESH_HAVE_XDR
309 }
void ErrorVector unsigned int
void checkElemsetCodes(const MeshBase &mesh)
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
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
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
void add_elemset_code(dof_id_type code, MeshBase::elemset_type id_set)
Tabulate a user-defined "code" for elements which belong to the element sets specified in id_set.
Definition mesh_base.C:456
void get_elemsets(dof_id_type elemset_code, MeshBase::elemset_type &id_set_to_fill) const
Look up the element sets for a given elemset code and vice-versa.
Definition mesh_base.C:487
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
Communicator * TestCommWorld
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.
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 libMesh::MeshBase::add_elem_integer(), libMesh::MeshBase::add_elemset_code(), libMesh::MeshBase::allow_renumbering(), libMesh::MeshTools::Generation::build_square(), checkElemsetCodes(), libMesh::MeshBase::get_elemsets(), libMesh::index_range(), libMesh::DofObject::invalid_id, mesh, libMesh::QUAD4, libMesh::UnstructuredMesh::read(), libMesh::Real, and TestCommWorld.

◆ testWriteNemesis()

void WriteElemsetData::testWriteNemesis ( )
inline

Definition at line 318 of file write_elemset_data.C.

319 {
320 // LOG_UNIT_TEST;
321
322 // FIXME: Not yet implemented
323 // testWriteImpl<Nemesis_IO>("write_elemset_data.n");
324 }

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