libMesh
boundary_mesh.C
Go to the documentation of this file.
1 #include <libmesh/mesh.h>
2 #include <libmesh/mesh_generation.h>
3 #include <libmesh/mesh_refinement.h>
4 #include <libmesh/remote_elem.h>
5 #include <libmesh/replicated_mesh.h>
6 #include <libmesh/boundary_info.h>
7 
8 #include "test_comm.h"
9 #include "libmesh_cppunit.h"
10 
11 #include <memory>
12 
13 using namespace libMesh;
14 
15 class BoundaryMeshTest : public CppUnit::TestCase {
20 public:
21  LIBMESH_CPPUNIT_TEST_SUITE( BoundaryMeshTest );
22 
23 #if LIBMESH_DIM > 1
24  CPPUNIT_TEST( testMesh );
25 #endif
26 
27  CPPUNIT_TEST_SUITE_END();
28 
29 protected:
30 
31  std::unique_ptr<Mesh> _mesh;
32  std::unique_ptr<Mesh> _all_boundary_mesh;
33  std::unique_ptr<Mesh> _left_boundary_mesh;
34  std::unique_ptr<Mesh> _internal_boundary_mesh;
35 
36  void build_mesh()
37  {
38  _mesh = std::make_unique<Mesh>(*TestCommWorld);
39  _all_boundary_mesh = std::make_unique<Mesh>(*TestCommWorld);
40  _left_boundary_mesh = std::make_unique<Mesh>(*TestCommWorld);
41  _internal_boundary_mesh = std::make_unique<Mesh>(*TestCommWorld);
42 
44  0.2, 0.8, 0.2, 0.7, QUAD9);
45 
46  // We'll need to skip most repartitioning with DistributedMesh for
47  // now; otherwise the boundary meshes' interior parents might get
48  // shuffled off to different processors.
49  if (!_mesh->is_serial())
50  {
51  _mesh->skip_noncritical_partitioning(true);
52  _left_boundary_mesh->skip_noncritical_partitioning(true);
53  _all_boundary_mesh->skip_noncritical_partitioning(true);
54  _internal_boundary_mesh->skip_noncritical_partitioning(true);
55  }
56 
57  // Set subdomain ids for specific elements. This allows us to later
58  // build an internal sideset with respect to a given
59  // subdomain. The element subdomains look like:
60  // ___________________
61  // | 2 | 2 | 2 |
62  // |_____|_____|_____|
63  // | 2 | 2 | 2 |
64  // |_____|_____|_____|
65  // | 2 | 2 | 2 |
66  // |_____|_____|_____|
67  // | 1 | 1 | 2 |
68  // |_____|_____|_____|
69  // | 1 | 1 | 2 |
70  // |_____|_____|_____|
71  //
72  // and we will create an internal sideset along the border between
73  // subdomains 1 and 2.
74 
75  for (auto & elem : _mesh->active_element_ptr_range())
76  {
77  const Point c = elem->vertex_average();
78  if (c(0) < 0.6 && c(1) < 0.4)
79  elem->subdomain_id() = 1;
80  else
81  elem->subdomain_id() = 2;
82  }
83 
84  // Get the border of the square
85  _mesh->get_boundary_info().sync(*_all_boundary_mesh);
86 
87  // Check that the interior_parent side indices that we set on the
88  // BoundaryMesh as extra integers agree with the side returned
89  // by which_side_am_i().
90  unsigned int parent_side_index_tag =
91  _all_boundary_mesh->get_elem_integer_index("parent_side_index");
92 
93  for (const auto & belem : _all_boundary_mesh->element_ptr_range())
94  {
95  dof_id_type parent_side_index =
96  belem->get_extra_integer(parent_side_index_tag);
97 
98  CPPUNIT_ASSERT_EQUAL
99  (static_cast<dof_id_type>(belem->interior_parent()->which_side_am_i(belem)),
100  parent_side_index);
101  }
102 
103  std::set<boundary_id_type> left_id, right_id;
104  left_id.insert(3);
105  right_id.insert(1);
106 
107  // Add the right side of the square to the square; this should
108  // make it a mixed dimension mesh. We skip storing the parent
109  // side ids (which is the default) since they are not needed
110  // in this particular test.
111  _mesh->get_boundary_info().add_elements
112  (right_id, *_mesh, /*store_parent_side_ids=*/false);
113  _mesh->prepare_for_use();
114 
115  // Add the left side of the square to its own boundary mesh.
116  _mesh->get_boundary_info().sync(left_id, *_left_boundary_mesh);
117 
118  // We create an internal sideset ID that does not conflict with
119  // sidesets 0-3 that get created by build_square().
120  boundary_id_type bid = 5;
121 
122  // To test the "relative to" feature, we add the same sides to the
123  // same sideset twice, from elements in subdomain 2 the second
124  // time. These should not show up in the BoundaryMesh, i.e. there
125  // should not be overlapped elems in the BoundaryMesh.
126  BoundaryInfo & bi = _mesh->get_boundary_info();
127 
128  for (auto & elem : _mesh->active_element_ptr_range())
129  {
130  const Point c = elem->vertex_average();
131  if (c(0) < 0.6 && c(1) < 0.4)
132  {
133  if (c(0) > 0.4)
134  bi.add_side(elem, 1, bid);
135  if (c(1) > 0.3)
136  bi.add_side(elem, 2, bid);
137  }
138  else
139  {
140  if (c(0) < 0.75 && c(1) < 0.4)
141  bi.add_side(elem, 3, bid);
142  if (c(0) < 0.6 && c(1) < 0.5)
143  bi.add_side(elem, 0, bid);
144  }
145  }
146 
147 
148  // Create a BoundaryMesh from the internal sideset relative to subdomain 1.
149  {
150  std::set<boundary_id_type> requested_boundary_ids;
151  requested_boundary_ids.insert(bid);
152  std::set<subdomain_id_type> subdomains_relative_to;
153  subdomains_relative_to.insert(1);
154  _mesh->get_boundary_info().sync(requested_boundary_ids,
155  *_internal_boundary_mesh,
156  subdomains_relative_to);
157  }
158  }
159 
160 public:
161  void setUp()
162  {
163 #if LIBMESH_DIM > 1
164  this->build_mesh();
165 #endif
166  }
167 
168  void testMesh()
169  {
170  LOG_UNIT_TEST;
171 
172  // There'd better be 3*5 + 5 elements in the interior plus right
173  // boundary
174  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(20),
175  _mesh->n_elem());
176 
177  // There'd better be only 7*11 nodes in the interior
178  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(77),
179  _mesh->n_nodes());
180 
181  // There'd better be only 2*(3+5) elements on the full boundary
182  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(16),
183  _all_boundary_mesh->n_elem());
184 
185  // There'd better be only 2*2*(3+5) nodes on the full boundary
186  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32),
187  _all_boundary_mesh->n_nodes());
188 
189  // There'd better be only 5 elements on the left boundary
190  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(5),
191  _left_boundary_mesh->n_elem());
192 
193  // There'd better be only 2*5+1 nodes on the left boundary
194  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(11),
195  _left_boundary_mesh->n_nodes());
196 
197  // There are only four elements in the internal sideset mesh.
198  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(4),
199  _internal_boundary_mesh->n_elem());
200 
201  // There are 2*n_elem + 1 nodes in the internal sideset mesh.
202  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(9),
203  _internal_boundary_mesh->n_nodes());
204 
205  this->sanityCheck();
206  }
207 
208  void sanityCheck()
209  {
210  // Sanity check all the elements
211  for (const auto & elem : _mesh->active_element_ptr_range())
212  {
213  const Elem * pip = elem->dim() < 2 ? elem->interior_parent() : nullptr;
214 
215  // On a DistributedMesh we might not be able to see the
216  // interior_parent of a non-local element
217  if (pip == remote_elem)
218  {
219  CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
220  continue;
221  }
222 
223  // All the edges should have interior parents; none of the
224  // quads should.
225  if (pip)
226  {
227  CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);
228  CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
229  CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());
230 
231  // We only added right edges
232  LIBMESH_ASSERT_FP_EQUAL(0.8, elem->vertex_average()(0),
234  }
235  else
236  {
237  CPPUNIT_ASSERT_EQUAL(elem->type(), QUAD9);
238  }
239  }
240 
241  for (const auto & elem : _left_boundary_mesh->active_element_ptr_range())
242  {
243  CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);
244 
245  const Elem * pip = elem->interior_parent();
246 
247  // On a DistributedMesh we might not be able to see the
248  // interior_parent of a non-local element
249  if (pip == remote_elem)
250  {
251  CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
252  continue;
253  }
254 
255  // All the edges should have interior parents
256  CPPUNIT_ASSERT(pip);
257  CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
258  CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());
259 
260  // We only added left edges
261  LIBMESH_ASSERT_FP_EQUAL(0.2, elem->vertex_average()(0),
263  }
264 
265  for (const auto & elem : _left_boundary_mesh->active_element_ptr_range())
266  {
267  CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);
268 
269  const Elem * pip = elem->interior_parent();
270 
271  // On a DistributedMesh we might not be able to see the
272  // interior_parent of a non-local element
273  if (pip == remote_elem)
274  {
275  CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
276  continue;
277  }
278 
279  // All the edges should have interior parents
280  CPPUNIT_ASSERT(pip);
281  CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
282  CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());
283  }
284 
285  // Sanity check for the internal sideset mesh.
286  for (const auto & elem : _internal_boundary_mesh->active_element_ptr_range())
287  {
288  CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);
289 
290  // All of the elements in the internal sideset mesh should
291  // have the same subdomain id as the parent Elems (i.e. 1)
292  // they came from.
293  CPPUNIT_ASSERT_EQUAL(static_cast<subdomain_id_type>(1),
294  elem->subdomain_id());
295  }
296  }
297 
298 };
299 
301 
302 #ifdef LIBMESH_ENABLE_AMR
303 
311 public:
312  LIBMESH_CPPUNIT_TEST_SUITE( BoundaryRefinedMeshTest );
313 
314 #if LIBMESH_DIM > 1
315  CPPUNIT_TEST( testMesh );
316 #endif
317 
318  CPPUNIT_TEST_SUITE_END();
319 
320  // Yes, this is necessary. Somewhere in those macros is a protected/private
321 public:
322 
323  void setUp()
324  {
325 #if LIBMESH_DIM > 1
326  this->build_mesh();
327 
328  // Need to refine interior mesh before separate boundary meshes,
329  // if we want to get interior_parent links right.
330  MeshRefinement(*_mesh).uniformly_refine(1);
331  MeshRefinement(*_left_boundary_mesh).uniformly_refine(1);
332  MeshRefinement(*_all_boundary_mesh).uniformly_refine(1);
333 #endif
334  }
335 
336  void testMesh()
337  {
338  LOG_UNIT_TEST;
339 
340  // There'd better be 3*5*4 + 5*2 active elements in the interior
341  // plus right boundary
342  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(70),
343  _mesh->n_active_elem());
344 
345  // Plus the original 20 now-inactive elements
346  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(90),
347  _mesh->n_elem());
348 
349  // There'd better be only 13*21 nodes in the interior
350  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(273),
351  _mesh->n_nodes());
352 
353  // There'd better be only 2*2*(3+5) active elements on the full boundary
354  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32),
355  _all_boundary_mesh->n_active_elem());
356 
357  // Plus the original 16 now-inactive elements
358  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(48),
359  _all_boundary_mesh->n_elem());
360 
361  // There'd better be only 2*2*2*(3+5) nodes on the full boundary
362  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(64),
363  _all_boundary_mesh->n_nodes());
364 
365  // There'd better be only 2*5 active elements on the left boundary
366  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(10),
367  _left_boundary_mesh->n_active_elem());
368 
369  // Plus the original 5 now-inactive elements
370  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(15),
371  _left_boundary_mesh->n_elem());
372 
373  // There'd better be only 2*2*5+1 nodes on the left boundary
374  CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(21),
375  _left_boundary_mesh->n_nodes());
376 
377  this->sanityCheck();
378  }
379 
380 };
381 
383 
384 #endif
std::unique_ptr< Mesh > _internal_boundary_mesh
Definition: boundary_mesh.C:34
const Elem * interior_parent() const
Definition: elem.C:994
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:159
static constexpr Real TOLERANCE
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
processor_id_type rank() const
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.
std::unique_ptr< Mesh > _mesh
Definition: boundary_mesh.C:31
Implements (adaptive) mesh refinement algorithms for a MeshBase.
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
std::unique_ptr< Mesh > _left_boundary_mesh
Definition: boundary_mesh.C:33
CPPUNIT_TEST_SUITE_REGISTRATION(BoundaryMeshTest)
unsigned int level() const
Definition: elem.h:2911
virtual unsigned short dim() const =0
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
virtual ElemType type() const =0
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
std::unique_ptr< Mesh > _all_boundary_mesh
Definition: boundary_mesh.C:32
void uniformly_refine(unsigned int n=1)
Uniformly refines the mesh n times.
const RemoteElem * remote_elem
Definition: remote_elem.C:54