libMesh
write_nodeset_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 WriteNodesetData : public CppUnit::TestCase
20 {
21 public:
22  LIBMESH_CPPUNIT_TEST_SUITE(WriteNodesetData);
23 
24 #if LIBMESH_DIM > 1
25 #ifdef LIBMESH_HAVE_EXODUS_API
26  CPPUNIT_TEST(testWriteExodus);
27 #endif // #ifdef LIBMESH_HAVE_EXODUS_API
28 #ifdef LIBMESH_HAVE_NEMESIS_API
29  // CPPUNIT_TEST(testWriteNemesis); // Not yet implemented
30 #endif
31 #endif
32 
33  CPPUNIT_TEST_SUITE_END();
34 
35  template <typename IOClass>
36  void testWriteImpl(const std::string & filename,
37  bool write_vars)
38  {
40  mesh.allow_renumbering(false);
42  /*nx=*/5, /*ny=*/5,
43  -1., 1.,
44  -1., 1.,
45  QUAD4);
46 
47  // Add an empty nodeset
48  mesh.get_boundary_info().nodeset_name(4) = "empty";
49 
50  // Only used if write_vars == true
51  std::vector<std::string> var_names;
52  std::vector<std::set<boundary_id_type>> node_boundary_ids;
53  std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> bc_vals;
54 
55  if (write_vars)
56  {
58 
59  // Meshes created via build_square() don't have any nodesets
60  // defined on them by default, so let's create some now. Note that
61  // this function handles the synchronization of ghost element and
62  // node ids in parallel internally.
64 
65  // Get list of all (node, id) tuples
66  std::vector<BoundaryInfo::NodeBCTuple> all_bc_tuples = bi.build_node_list();
67 
68  // Data structures to be passed to ExodusII_IO::write_nodeset_data()
69  var_names = {"var1", "var2", "var3"};
70  node_boundary_ids =
71  {
72  {0, 2}, // var1 is defined on nodesets 0 and 2
73  {1, 3}, // var2 is defined on nodesets 1 and 3
74  {4} // var3 is only defined on the empty nodeset 4
75  };
76 
77  // Data structure mapping (node, id) tuples to Real values that
78  // will be passed to Exodus.
79  bc_vals.resize(var_names.size());
80 
81  // For each var_names[i], construct bc_vals[i]
82  for (unsigned int i=0; i<var_names.size(); ++i)
83  {
84  // const auto & var_name = var_names[i];
85  auto & vals = bc_vals[i];
86 
87  for (const auto & t : all_bc_tuples)
88  {
89  boundary_id_type b_id = std::get<1>(t);
90 
91  if (node_boundary_ids[i].count(b_id))
92  {
93  // Compute a value. This could in theory depend on
94  // var_name, node_id, and/or b_id.
95  Real val = static_cast<Real>(b_id);
96 
97  // Insert into the vals map.
98  vals.emplace(t, val);
99  }
100  }
101 
102  // If we have a distributed mesh, the ExodusII writer
103  // serializes it before writing, so we need to serialize our
104  // nodeset data as well so that it can all be written from
105  // processor 0.
106  if (!mesh.is_serial())
107  TestCommWorld->set_union(vals);
108 
109  } // done constructing bc_vals
110 
111  // We write the file in the ExodusII format.
112  {
113  IOClass writer(mesh);
114  writer.write(filename);
115  writer.write_nodeset_data (/*timestep=*/1, var_names, node_boundary_ids, bc_vals);
116  }
117  }
118 
119  // Make sure that the writing is done before the reading starts.
121 
122  // Now read it back in
123  Mesh read_mesh(*TestCommWorld);
124  IOClass reader(read_mesh);
125  reader.read(filename);
126 
127  if (write_vars)
128  {
129  std::vector<std::string> read_in_var_names;
130  std::vector<std::set<boundary_id_type>> read_in_node_boundary_ids;
131  std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> read_in_bc_vals;
132  reader.read_nodeset_data
133  (/*timestep=*/1, read_in_var_names, read_in_node_boundary_ids, read_in_bc_vals);
134 
135  // Assert that we got back out what we put in.
136  CPPUNIT_ASSERT(read_in_var_names == var_names);
137  CPPUNIT_ASSERT(read_in_node_boundary_ids == node_boundary_ids);
138  CPPUNIT_ASSERT(read_in_bc_vals == bc_vals);
139  } // if (write_vars)
140 
141  // Also check that the flat indices match those in the file
142  std::map<BoundaryInfo::NodeBCTuple, unsigned int> bc_array_indices;
143  reader.get_nodeset_data_indices(bc_array_indices);
144 
145  // Debugging
146  // for (const auto & [t, index] : bc_array_indices)
147  // {
148  // const auto & node_id = std::get<0>(t);
149  // const auto & boundary_id = std::get<1>(t);
150  // libMesh::out << "(node, boundary_id) = "
151  // << "(" << node_id << ", " << boundary_id << ")"
152  // << " is at array index " << index
153  // << std::endl;
154  // }
155 
156  // For this test case, the nodeset arrays are ordered as follows:
157  // ns_prop1 = 0, 1, 2, 3, 4 ;
158  // ns_names = "bottom", "right", "top", "left", "empty" ;
159  //
160  // Exodus (1-based) node ids
161  // node_ns1 = 1, 2, 3, 4, 5, 6 ;
162  // node_ns2 = 6, 12, 18, 24, 30, 36 ;
163  // node_ns3 = 31, 32, 33, 34, 35, 36 ;
164  // node_ns4 = 1, 7, 13, 19, 25, 31 ;
165 
166  // Check the node ids in the "bottom" nodeset, which contains the
167  // first six nodes in order.
168  for (unsigned int i=0; i<6; ++i)
169  CPPUNIT_ASSERT_EQUAL
170  (static_cast<unsigned int>(i),
171  libmesh_map_find(bc_array_indices, std::make_tuple(/*node_id=*/cast_int<dof_id_type>(i), /*b_id=*/0)));
172 
173  // Check the node ids in the "right" sideset, which contains every
174  // 6th node starting from 5, i.e. 5, 11, 17
175  for (unsigned int i=0; i<6; ++i)
176  CPPUNIT_ASSERT_EQUAL
177  (static_cast<unsigned int>(i),
178  libmesh_map_find(bc_array_indices, std::make_tuple(/*node_id=*/cast_int<dof_id_type>(6*i + 5), /*b_id=*/1)));
179 
180  // Check the node ids in the "top" sideset, which contains the last
181  // six nodes in order.
182  for (unsigned int i=0; i<6; ++i)
183  CPPUNIT_ASSERT_EQUAL
184  (static_cast<unsigned int>(i),
185  libmesh_map_find(bc_array_indices, std::make_tuple(/*node_id=*/cast_int<dof_id_type>(30 + i), /*b_id=*/2)));
186 
187  // Check the node ids in the "left" sideset, which contains every
188  // 6th node starting from 0, i.e. 0, 6, 12
189  for (unsigned int i=0; i<6; ++i)
190  CPPUNIT_ASSERT_EQUAL
191  (static_cast<unsigned int>(i),
192  libmesh_map_find(bc_array_indices, std::make_tuple(/*node_id=*/cast_int<dof_id_type>(6*i), /*b_id=*/3)));
193  }
194 
196  {
197  LOG_UNIT_TEST;
198 
199  testWriteImpl<ExodusII_IO>("write_nodeset_data.e", /*write_vars=*/true);
200  testWriteImpl<ExodusII_IO>("write_nodeset_data.e", /*write_vars=*/false);
201  }
202 
204  {
205  LOG_UNIT_TEST;
206 
207  // FIXME: Not yet implemented
208  // testWriteImpl<Nemesis_IO>("write_nodeset_data.n");
209  }
210 };
211 
std::string & nodeset_name(boundary_id_type id)
void build_node_list_from_side_list()
Adds nodes with boundary ids based on the side&#39;s boundary ids they are connected to.
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
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
virtual bool is_serial() const
Definition: mesh_base.h:211
void build_node_list(std::vector< dof_id_type > &node_id_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of nodes and ids for those nodes.
void testWriteImpl(const std::string &filename, bool write_vars)
int8_t boundary_id_type
Definition: id_types.h:51
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
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
CPPUNIT_TEST_SUITE_REGISTRATION(WriteNodesetData)
void set_union(T &data, const unsigned int root_id) const