libMesh
write_sideset_data.C
Go to the documentation of this file.
1 // Basic include files
2 #include "libmesh/equation_systems.h"
3 #include "libmesh/exodusII_io.h"
4 #include "libmesh/nemesis_io.h"
5 #include "libmesh/mesh.h"
6 #include "libmesh/mesh_generation.h"
7 #include "libmesh/parallel.h" // set_union
8 #include "libmesh/string_to_enum.h"
9 #include "libmesh/boundary_info.h"
10 #include "libmesh/utility.h" // libmesh_map_find
11 
12 #include "test_comm.h"
13 #include "libmesh_cppunit.h"
14 
15 
16 // Bring in everything from the libMesh namespace
17 using namespace libMesh;
18 
19 class WriteSidesetData : public CppUnit::TestCase
20 {
24 public:
25  LIBMESH_CPPUNIT_TEST_SUITE(WriteSidesetData);
26 
27 #if LIBMESH_DIM > 1
28 #ifdef LIBMESH_HAVE_EXODUS_API
29  CPPUNIT_TEST(testWriteExodus);
30 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
31 #ifdef LIBMESH_HAVE_NEMESIS_API
32  // CPPUNIT_TEST(testWriteNemesis); // Not yet implemented
33 #endif
34 #endif
35 
36  CPPUNIT_TEST_SUITE_END();
37 
38  template <typename IOClass>
39  void testWriteImpl(const std::string & filename,
40  bool write_vars)
41  {
43 
44  // We set our initial conditions based on build_square node ids
45  mesh.allow_renumbering(false);
46 
48  /*nx=*/5, /*ny=*/5,
49  -1., 1.,
50  -1., 1.,
51  QUAD4);
52 
53  // Add an empty sideset
54  mesh.get_boundary_info().sideset_name(4) = "empty";
55 
56  // Only used if write_vars == true
57  std::vector<std::string> var_names;
58  std::vector<std::set<boundary_id_type>> side_ids;
59  std::vector<std::map<BoundaryInfo::BCTuple, Real>> bc_vals;
60 
61  if (write_vars)
62  {
63  // Get list of all (elem, side, id) tuples
64  std::vector<BoundaryInfo::BCTuple> all_bc_tuples =
66 
67  // Data structures to be passed to ExodusII_IO::write_sideset_data
68  var_names = {"var1", "var2", "var3"};
69  side_ids =
70  {
71  {0, 2}, // var1 is defined on sidesets 0 and 2
72  {1, 3}, // var2 is defined on sidesets 1 and 3
73  {4} // var3 is only defined on the empty sideset 4
74  };
75 
76  // Data structure mapping (elem, side, id) tuples to Real values that
77  // will be passed to Exodus.
78  bc_vals.resize(var_names.size());
79 
80  // For each var_names[i], construct bc_vals[i]
81  for (unsigned int i=0; i<var_names.size(); ++i)
82  {
83  // const auto & var_name = var_names[i];
84  auto & vals = bc_vals[i];
85 
86  for (const auto & t : all_bc_tuples)
87  {
88  // dof_id_type elem_id = std::get<0>(t);
89  // unsigned int side_id = std::get<1>(t);
90  boundary_id_type b_id = std::get<2>(t);
91 
92  if (side_ids[i].count(b_id))
93  {
94  // Compute a value. This could in theory depend on
95  // var_name, elem_id, side_id, and/or b_id.
96  Real val = static_cast<Real>(b_id);
97 
98  // Insert into the vals map.
99  vals.emplace(t, val);
100  }
101  }
102 
103  // If we have a distributed mesh, write_sideset_data wants our
104  // ghost data too; we'll just serialize everything here.
105  if (!mesh.is_serial())
106  TestCommWorld->set_union(vals);
107 
108  } // done constructing bc_vals
109 
110  // We write the file in the ExodusII format.
111  {
112  IOClass writer(mesh);
113  writer.write(filename);
114  writer.write_sideset_data (/*timestep=*/1, var_names, side_ids, bc_vals);
115  }
116  } // if (write_vars)
117 
118  // Make sure that the writing is done before the reading starts.
120 
121  // Now read it back in
122  Mesh read_mesh(*TestCommWorld);
123  IOClass reader(read_mesh);
124  reader.read(filename);
125 
126  if (write_vars)
127  {
128  std::vector<std::string> read_in_var_names;
129  std::vector<std::set<boundary_id_type>> read_in_side_ids;
130  std::vector<std::map<BoundaryInfo::BCTuple, Real>> read_in_bc_vals;
131  reader.read_sideset_data
132  (/*timestep=*/1, read_in_var_names, read_in_side_ids, read_in_bc_vals);
133 
134  // Assert that we got back out what we put in.
135  CPPUNIT_ASSERT(read_in_var_names == var_names);
136  CPPUNIT_ASSERT(read_in_side_ids == side_ids);
137  CPPUNIT_ASSERT(read_in_bc_vals == bc_vals);
138  } // if (write_vars)
139 
140  // Also check that the flat indices match those in the file
141  std::map<BoundaryInfo::BCTuple, unsigned int> bc_array_indices;
142  reader.get_sideset_data_indices(bc_array_indices);
143 
144  // Debugging
145  // for (const auto & [t, index] : bc_array_indices)
146  // {
147  // const auto & elem_id = std::get<0>(t);
148  // const auto & side_id = std::get<1>(t);
149  // const auto & boundary_id = std::get<2>(t);
150  // libMesh::out << "(elem, side, boundary_id) = "
151  // << "(" << elem_id << ", " << side_id << ", " << boundary_id << ") at index "
152  // << index << std::endl;
153  // }
154 
155  // For this test case, the sideset arrays are ordered as follows:
156  // elem_ss1 = 1, 2, 3, 4, 5 ;
157  // side_ss1 = 1, 1, 1, 1, 1 ;
158  // elem_ss2 = 5, 10, 15, 20, 25 ;
159  // side_ss2 = 2, 2, 2, 2, 2 ;
160  // elem_ss3 = 21, 22, 23, 24, 25 ;
161  // side_ss3 = 3, 3, 3, 3, 3 ;
162  // elem_ss4 = 1, 6, 11, 16, 21 ;
163  // side_ss4 = 4, 4, 4, 4, 4 ;
164 
165  // Check that the 0th side of the 0th element on boundary 0
166  // corresponds to array index 0.
167  // CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0),
168  // libmesh_map_find(bc_array_indices,
169  // std::make_tuple(/*elem id*/static_cast<dof_id_type>(0),
170  // /*side id*/static_cast<unsigned short int>(0),
171  // /*boundary id*/static_cast<boundary_id_type>(0))));
172 
173  // CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(0),
174  // libmesh_map_find(bc_array_indices, std::make_tuple(0,0,0)));
175  //
176  // CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(1),
177  // libmesh_map_find(bc_array_indices, std::make_tuple(1,0,0)));
178 
179  // Check the first five elements, which all have side 0 on
180  // boundary 0, and which happen to be in order in the BC array.
181  for (unsigned int i=0; i<5; ++i)
182  CPPUNIT_ASSERT_EQUAL
183  (static_cast<unsigned int>(i),
184  libmesh_map_find(bc_array_indices,
185  std::make_tuple(/*elem_id=*/cast_int<dof_id_type>(i),
186  /*side_id=*/0,
187  /*b_id=*/0)));
188 
189  // Check side 1 of every fifth element starting with element 4, they are all in sideset 1.
190  for (unsigned int i=0; i<5; ++i)
191  CPPUNIT_ASSERT_EQUAL
192  (static_cast<unsigned int>(i),
193  libmesh_map_find(bc_array_indices,
194  std::make_tuple(/*elem_id=*/cast_int<dof_id_type>(5*i + 4),
195  /*side_id*/1,
196  /*b_id=*/1)));
197 
198  // Check side 2 of the 5 consecutive elements starting with Elem 20. They are all in sideset 2.
199  for (unsigned int i=0; i<5; ++i)
200  CPPUNIT_ASSERT_EQUAL
201  (static_cast<unsigned int>(i),
202  libmesh_map_find(bc_array_indices,
203  std::make_tuple(/*elem_id=*/cast_int<dof_id_type>(20+i),
204  /*side_id*/2,
205  /*b_id=*/2)));
206 
207  // Check side 3 of every fifth element, they are all in sideset 3
208  for (unsigned int i=0; i<5; ++i)
209  CPPUNIT_ASSERT_EQUAL
210  (static_cast<unsigned int>(i),
211  libmesh_map_find(bc_array_indices,
212  std::make_tuple(/*elem_id=*/cast_int<dof_id_type>(5*i),
213  /*side_id*/3,
214  /*b_id=*/3)));
215  }
216 
218  {
219  LOG_UNIT_TEST;
220 
221  testWriteImpl<ExodusII_IO>("write_sideset_data.e", /*write_vars=*/true);
222  testWriteImpl<ExodusII_IO>("write_sideset_data.e", /*write_vars=*/false);
223  }
224 
226  {
227  // LOG_UNIT_TEST;
228 
229  // FIXME: Not yet implemented
230  // testWriteImpl<Nemesis_IO>("write_sideset_data.n", /*write_vars=*/true);
231  }
232 };
233 
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:1196
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:171
CPPUNIT_TEST_SUITE_REGISTRATION(WriteSidesetData)
void barrier() const
MeshBase & mesh
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.
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of element numbers, sides, and ids for those sides.
virtual bool is_serial() const
Definition: mesh_base.h:211
int8_t boundary_id_type
Definition: id_types.h:51
void testWriteImpl(const std::string &filename, bool write_vars)
std::string & sideset_name(boundary_id_type id)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
void set_union(T &data, const unsigned int root_id) const