libMesh
Public Types | Public Member Functions | Private Member Functions | Private Attributes | List of all members
libMesh::InfElemBuilder Class Reference

This class is used to build infinite elements on top of an existing mesh. More...

#include <inf_elem_builder.h>

Public Types

typedef std::pair< bool, double > InfElemOriginValue
 Useful typedef. More...
 

Public Member Functions

 InfElemBuilder (MeshBase &mesh)
 Constructor. More...
 
const Point build_inf_elem (const bool be_verbose=false)
 Build infinite elements atop a volume-based mesh, determine origin automatically. More...
 
const Point build_inf_elem (const InfElemOriginValue &origin_x, const InfElemOriginValue &origin_y, const InfElemOriginValue &origin_z, const bool x_sym=false, const bool y_sym=false, const bool z_sym=false, const bool be_verbose=false, std::vector< const Node *> *inner_boundary_nodes=nullptr)
 

Private Member Functions

void build_inf_elem (const Point &origin, const bool x_sym=false, const bool y_sym=false, const bool z_sym=false, const bool be_verbose=false, std::set< std::pair< dof_id_type, unsigned int >> *inner_faces=nullptr)
 Build infinite elements atop a volume-based mesh. More...
 

Private Attributes

MeshBase_mesh
 Reference to the mesh we're building infinite elements for. More...
 

Detailed Description

This class is used to build infinite elements on top of an existing mesh.

It only makes sense to use this if LIBMESH_ENABLE_INFINITE_ELEMENTS is true.

Author
Daniel Dreyer
John W. Peterson
Date
2004

Definition at line 53 of file inf_elem_builder.h.

Member Typedef Documentation

◆ InfElemOriginValue

typedef std::pair<bool, double> libMesh::InfElemBuilder::InfElemOriginValue

Useful typedef.

Definition at line 65 of file inf_elem_builder.h.

Constructor & Destructor Documentation

◆ InfElemBuilder()

libMesh::InfElemBuilder::InfElemBuilder ( MeshBase mesh)
inlineexplicit

Constructor.

Definition at line 60 of file inf_elem_builder.h.

60 : _mesh(mesh) {}
MeshBase & mesh
MeshBase & _mesh
Reference to the mesh we&#39;re building infinite elements for.

Member Function Documentation

◆ build_inf_elem() [1/3]

const Point libMesh::InfElemBuilder::build_inf_elem ( const bool  be_verbose = false)

Build infinite elements atop a volume-based mesh, determine origin automatically.

Returns
The origin as a const Point to make it more obvious that the origin should not change after the infinite elements have been built.

When symmetry planes are present, use the version with optional symmetry switches. The flag be_verbose enables some diagnostic output.

Definition at line 43 of file inf_elem_builder.C.

References _mesh, libMesh::MeshTools::create_bounding_box(), libMesh::out, libMesh::MeshBase::prepare_for_use(), libMesh::ParallelObject::processor_id(), and libMesh::TypeVector< T >::write_unformatted().

Referenced by build_inf_elem(), main(), InfFERadialTest::testInfQuants(), InfFERadialTest::testInfQuants_numericDeriv(), InfFERadialTest::testRefinement(), InfFERadialTest::testSides(), and InfFERadialTest::testSingleOrder().

44 {
45  // determine origin automatically,
46  // works only if the mesh has no symmetry planes.
47  const BoundingBox b_box = MeshTools::create_bounding_box(_mesh);
48  Point origin = (b_box.first + b_box.second) / 2;
49 
50  if (be_verbose && _mesh.processor_id() == 0)
51  {
52 #ifdef DEBUG
53  libMesh::out << " Determined origin for Infinite Elements:"
54  << std::endl
55  << " ";
56  origin.write_unformatted(libMesh::out);
57  libMesh::out << std::endl;
58 #endif
59  }
60 
61  // Call the protected implementation function with the
62  // automatically determined origin.
63  this->build_inf_elem(origin, false, false, false, be_verbose);
64 
65  // when finished with building the Ifems,
66  // it remains to prepare the mesh for use:
67  // find neighbors (again), partition (if needed)...
68  this->_mesh.prepare_for_use ();
69 
70  return origin;
71 }
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition: mesh_tools.C:566
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition: mesh_base.C:824
const Point build_inf_elem(const bool be_verbose=false)
Build infinite elements atop a volume-based mesh, determine origin automatically. ...
OStreamProxy out
processor_id_type processor_id() const
MeshBase & _mesh
Reference to the mesh we&#39;re building infinite elements for.

◆ build_inf_elem() [2/3]

const Point libMesh::InfElemBuilder::build_inf_elem ( const InfElemOriginValue origin_x,
const InfElemOriginValue origin_y,
const InfElemOriginValue origin_z,
const bool  x_sym = false,
const bool  y_sym = false,
const bool  z_sym = false,
const bool  be_verbose = false,
std::vector< const Node *> *  inner_boundary_nodes = nullptr 
)
Returns
The origin of the infinite elements. Builds infinite elements atop a volume-based mesh. Finds all faces on the outer boundary and build infinite elements on them. Using the InfElemOriginValue the user can prescribe only selected origin coordinates. The remaining coordinates are computed from the center of the bounding box of the mesh.

During the search for faces on which infinite elements are built, interior faces that are not on symmetry planes are found, too. When an (optional) pointer to inner_boundary_nodes is provided, then this vector will be filled with the nodes that lie on the inner boundary.

Faces which lie in at least one symmetry plane are skipped. The three optional booleans x_sym, y_sym, z_sym indicate symmetry planes (through the origin, obviously) perpendicular to the x, y and z direction, respectively. The flag be_verbose enables some diagnostic output.

Definition at line 84 of file inf_elem_builder.C.

References _mesh, libMesh::as_range(), build_inf_elem(), libMesh::Elem::build_side_ptr(), libMesh::MeshTools::create_bounding_box(), distance(), libMesh::MeshBase::elem_ref(), libMesh::MeshBase::node_ptr(), libMesh::out, libMesh::MeshBase::prepare_for_use(), libMesh::MeshBase::print_info(), and libMesh::TypeVector< T >::write_unformatted().

92 {
93  LOG_SCOPE("build_inf_elem()", "InfElemBuilder");
94 
95  // first determine the origin of the
96  // infinite elements. For this, the
97  // origin defaults to the given values,
98  // and may be overridden when the user
99  // provided values
100  Point origin(origin_x.second, origin_y.second, origin_z.second);
101 
102  // when only _one_ of the origin coordinates is _not_
103  // given, we have to determine it on our own
104  if ( !origin_x.first || !origin_y.first || !origin_z.first)
105  {
106  // determine origin
107  const BoundingBox b_box = MeshTools::create_bounding_box(_mesh);
108  const Point auto_origin = (b_box.first+b_box.second)/2;
109 
110  // override default values, if necessary
111  if (!origin_x.first)
112  origin(0) = auto_origin(0);
113 #if LIBMESH_DIM > 1
114  if (!origin_y.first)
115  origin(1) = auto_origin(1);
116 #endif
117 #if LIBMESH_DIM > 2
118  if (!origin_z.first)
119  origin(2) = auto_origin(2);
120 #endif
121 
122  if (be_verbose)
123  {
124  libMesh::out << " Origin for Infinite Elements:" << std::endl;
125 
126  if (!origin_x.first)
127  libMesh::out << " determined x-coordinate" << std::endl;
128  if (!origin_y.first)
129  libMesh::out << " determined y-coordinate" << std::endl;
130  if (!origin_z.first)
131  libMesh::out << " determined z-coordinate" << std::endl;
132 
133  libMesh::out << " coordinates: ";
134  origin.write_unformatted(libMesh::out);
135  libMesh::out << std::endl;
136  }
137  }
138 
139  else if (be_verbose)
140 
141  {
142  libMesh::out << " Origin for Infinite Elements:" << std::endl;
143  libMesh::out << " coordinates: ";
144  origin.write_unformatted(libMesh::out);
145  libMesh::out << std::endl;
146  }
147 
148 
149 
150  // Now that we have the origin, check if the user provided an \p
151  // inner_boundary_nodes. If so, we pass a std::set to the actual
152  // implementation of the build_inf_elem(), so that we can convert
153  // this to the Node * vector
154  if (inner_boundary_nodes != nullptr)
155  {
156  // note that the std::set that we will get
157  // from build_inf_elem() uses the index of
158  // the element in this->_elements vector,
159  // and the second entry is the side index
160  // for this element. Therefore, we do _not_
161  // need to renumber nodes and elements
162  // prior to building the infinite elements.
163  //
164  // However, note that this method here uses
165  // node id's... Do we need to renumber?
166 
167 
168  // Form the list of faces of elements which finally
169  // will tell us which nodes should receive boundary
170  // conditions (to form the std::vector<const Node *>)
171  std::set<std::pair<dof_id_type,
172  unsigned int>> inner_faces;
173 
174 
175  // build infinite elements
176  this->build_inf_elem(origin,
177  x_sym, y_sym, z_sym,
178  be_verbose,
179  &inner_faces);
180 
181  if (be_verbose)
182  {
183  this->_mesh.print_info();
184  libMesh::out << "Data pre-processing:" << std::endl
185  << " convert the <int,int> list to a Node * list..."
186  << std::endl;
187  }
188 
189  // First use a std::vector<dof_id_type> that holds
190  // the global node numbers. Then sort this vector,
191  // so that it can be made unique (no multiple occurrence
192  // of a node), and then finally insert the Node * in
193  // the vector inner_boundary_nodes.
194  //
195  // Reserve memory for the vector<> with
196  // 4 times the size of the number of elements in the
197  // std::set. This is a good bet for Quad4 face elements.
198  // For higher-order elements, this probably _has_ to lead
199  // to additional allocations...
200  // Practice has to show how this affects performance.
201  std::vector<dof_id_type> inner_boundary_node_numbers;
202  inner_boundary_node_numbers.reserve(4*inner_faces.size());
203 
204  // Now transform the set of pairs to a list of (possibly
205  // duplicate) global node numbers.
206  for (const auto & p : inner_faces)
207  {
208  // build a full-ordered side element to get _all_ the base nodes
209  std::unique_ptr<Elem> side(this->_mesh.elem_ref(p.first).build_side_ptr(p.second));
210 
211  // insert all the node numbers in inner_boundary_node_numbers
212  for (const Node & node : side->node_ref_range())
213  inner_boundary_node_numbers.push_back(node.id());
214  }
215 
216 
217  // inner_boundary_node_numbers now still holds multiple entries of
218  // node numbers. So first sort, then unique the vector.
219  // Note that \p std::unique only puts the new ones in
220  // front, while to leftovers are not deleted. Instead,
221  // it returns a pointer to the end of the unique range.
222  //TODO:[BSK] int_ibn_size_before is not the same type as unique_size!
223 #ifndef NDEBUG
224  const std::size_t ibn_size_before = inner_boundary_node_numbers.size();
225 #endif
226  std::sort (inner_boundary_node_numbers.begin(), inner_boundary_node_numbers.end());
227  auto unique_end =
228  std::unique (inner_boundary_node_numbers.begin(), inner_boundary_node_numbers.end());
229 
230  std::size_t unique_size = std::distance(inner_boundary_node_numbers.begin(), unique_end);
231  libmesh_assert_less_equal (unique_size, ibn_size_before);
232 
233  // Finally, create const Node * in the inner_boundary_nodes
234  // vector. Reserve, not resize (otherwise, the push_back
235  // would append the interesting nodes, while nullptr-nodes
236  // live in the resize'd area...
237  inner_boundary_nodes->reserve (unique_size);
238  inner_boundary_nodes->clear();
239 
240  for (const auto & dof : as_range(inner_boundary_node_numbers.begin(), unique_end))
241  {
242  const Node * node = this->_mesh.node_ptr(dof);
243  inner_boundary_nodes->push_back(node);
244  }
245 
246  if (be_verbose)
247  libMesh::out << " finished identifying " << unique_size
248  << " target nodes." << std::endl;
249  }
250 
251  else
252 
253  {
254  // There are no inner boundary nodes, so simply build the infinite elements
255  this->build_inf_elem(origin, x_sym, y_sym, z_sym, be_verbose);
256  }
257 
258  // when finished with building the Ifems,
259  // it remains to prepare the mesh for use:
260  // find neighbors again, partition (if needed)...
261  this->_mesh.prepare_for_use ();
262 
263  return origin;
264 }
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition: mesh_tools.C:566
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition: mesh_base.C:824
Real distance(const Point &p)
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition: mesh_base.C:1748
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
const Point build_inf_elem(const bool be_verbose=false)
Build infinite elements atop a volume-based mesh, determine origin automatically. ...
OStreamProxy out
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:778
virtual const Node * node_ptr(const dof_id_type i) const =0
MeshBase & _mesh
Reference to the mesh we&#39;re building infinite elements for.
uint8_t dof_id_type
Definition: id_types.h:67

◆ build_inf_elem() [3/3]

void libMesh::InfElemBuilder::build_inf_elem ( const Point origin,
const bool  x_sym = false,
const bool  y_sym = false,
const bool  z_sym = false,
const bool  be_verbose = false,
std::set< std::pair< dof_id_type, unsigned int >> *  inner_faces = nullptr 
)
private

Build infinite elements atop a volume-based mesh.

Actual implementation.

Definition at line 275 of file inf_elem_builder.C.

References _mesh, libMesh::MeshBase::add_elem(), libMesh::MeshBase::add_node(), libMesh::MeshBase::add_point(), TIMPI::Communicator::broadcast(), libMesh::Node::build(), libMesh::Elem::build(), libMesh::Elem::build_side_ptr(), libMesh::ParallelObject::comm(), libMesh::Elem::dim(), libMesh::EDGE2, libMesh::EDGE3, libMesh::MeshBase::elem_ref(), libMesh::MeshBase::find_neighbors(), libMesh::DofObject::id(), libMesh::INFHEX16, libMesh::INFHEX18, libMesh::INFHEX8, libMesh::INFPRISM12, libMesh::INFPRISM6, libMesh::INFQUAD4, libMesh::INFQUAD6, libMesh::MeshBase::is_serial(), libMesh::libmesh_assert(), libMesh::MeshBase::libmesh_assert_valid_parallel_ids(), libMesh::make_range(), TIMPI::Communicator::max(), libMesh::MeshBase::max_elem_id(), libMesh::MeshBase::max_node_id(), TIMPI::Communicator::maxloc(), libMesh::MeshBase::n_elem(), libMesh::Elem::n_sides(), libMesh::Elem::neighbor_ptr(), libMesh::MeshBase::node_ref(), libMesh::TypeVector< T >::norm(), libMesh::out, libMesh::MeshBase::parallel_max_unique_id(), libMesh::MeshBase::point(), libMesh::DofObject::processor_id(), libMesh::QUAD4, libMesh::QUAD8, libMesh::QUAD9, libMesh::MeshBase::query_node_ptr(), libMesh::Real, libMesh::remote_elem, libMesh::Elem::set_neighbor(), TIMPI::Communicator::set_union(), libMesh::Elem::side_index_range(), libMesh::TRI3, and libMesh::TRI6.

282 {
283  if (be_verbose)
284  {
285 #ifdef DEBUG
286  libMesh::out << " Building Infinite Elements:" << std::endl;
287  libMesh::out << " updating element neighbor tables..." << std::endl;
288 #else
289  libMesh::out << " Verbose mode disabled in non-debug mode." << std::endl;
290 #endif
291  }
292 
293 
294  // update element neighbors
295  this->_mesh.find_neighbors();
296 
297  LOG_SCOPE("build_inf_elem()", "InfElemBuilder");
298 
299  // A set for storing element number, side number pairs.
300  // pair.first == element number, pair.second == side number
301  std::set<std::pair<dof_id_type,unsigned int>> faces;
302  std::set<std::pair<dof_id_type,unsigned int>> ofaces;
303 
304  // A set for storing node numbers on the outer faces.
305  std::set<dof_id_type> onodes;
306 
307  // The distance to the farthest point in the mesh from the origin
308  Real max_r=0.;
309 
310  // The index of the farthest point in the mesh from the origin
311  int max_r_node = -1;
312 
313 #ifdef DEBUG
314  if (be_verbose)
315  {
316  libMesh::out << " collecting boundary sides";
317  if (x_sym || y_sym || z_sym)
318  libMesh::out << ", skipping sides in symmetry planes..." << std::endl;
319  else
320  libMesh::out << "..." << std::endl;
321  }
322 #endif
323 
324  // Iterate through all elements and sides, collect indices of all active
325  // boundary sides in the faces set. Skip sides which lie in symmetry planes.
326  // Later, sides of the inner boundary will be sorted out.
327  for (const auto & elem : _mesh.active_element_ptr_range())
328  for (auto s : elem->side_index_range())
329  if (elem->neighbor_ptr(s) == nullptr)
330  {
331  // note that it is safe to use the Elem::side() method,
332  // which gives a non-full-ordered element
333  std::unique_ptr<Elem> side(elem->build_side_ptr(s));
334 
335  // bool flags for symmetry detection
336  bool sym_side=false;
337  bool on_x_sym=true;
338  bool on_y_sym=true;
339  bool on_z_sym=true;
340 
341 
342  // Loop over the nodes to check whether they are on the symmetry planes,
343  // and therefore sufficient to use a non-full-ordered side element
344  for (const Node & node : side->node_ref_range())
345  {
346  const dof_id_type node_id = node.id();
347  const Point dist_from_origin =
348  this->_mesh.point(node_id) - origin;
349 
350  if (x_sym)
351  if (std::abs(dist_from_origin(0)) > 1.e-3)
352  on_x_sym=false;
353 
354  if (y_sym)
355  if (std::abs(dist_from_origin(1)) > 1.e-3)
356  on_y_sym=false;
357 
358  if (z_sym)
359  if (std::abs(dist_from_origin(2)) > 1.e-3)
360  on_z_sym=false;
361 
362  // if (x_sym)
363  // if (std::abs(dist_from_origin(0)) > 1.e-6)
364  // on_x_sym=false;
365 
366  // if (y_sym)
367  // if (std::abs(dist_from_origin(1)) > 1.e-6)
368  // on_y_sym=false;
369 
370  // if (z_sym)
371  // if (std::abs(dist_from_origin(2)) > 1.e-6)
372  // on_z_sym=false;
373 
374  //find the node most distant from origin
375 
376  Real r = dist_from_origin.norm();
377  if (r > max_r)
378  {
379  max_r = r;
380  max_r_node=node_id;
381  }
382 
383  }
384 
385  sym_side = (x_sym && on_x_sym) || (y_sym && on_y_sym) || (z_sym && on_z_sym);
386 
387  if (!sym_side)
388  faces.emplace(elem->id(), s);
389 
390  } // neighbor(s) == nullptr
391 
392  // On a distributed mesh it might be some other processor who sees
393  // the farthest node.
394  if (!this->_mesh.is_serial())
395  {
396  unsigned int rank;
397  this->_mesh.comm().maxloc(max_r, rank);
398  this->_mesh.comm().broadcast(max_r_node, rank);
399  }
400 
401  // If a boundary side has one node on the outer boundary,
402  // all points of this side are on the outer boundary.
403  // Start with the node most distant from origin, which has
404  // to be on the outer boundary, then recursively find all
405  // sides and nodes connected to it. Found sides are moved
406  // from faces to ofaces, nodes are collected in onodes.
407  // Here, the search is done iteratively, because, depending on
408  // the mesh, a very high level of recursion might be necessary.
409  if (max_r_node >= 0)
410  // include the possibility of the 1st element being most far away.
411  // Only the case of no outer boundary is to be excluded.
412  onodes.insert(max_r_node);
413 
414  // If we're not on a serial mesh, we'll need to synchronize that
415  // onodes list too.
416  bool did_parallel_update;
417 
418  do
419  {
420  did_parallel_update = false;
421 
422  auto face_it = faces.begin();
423  auto face_end = faces.end();
424  unsigned int facesfound=0;
425  while (face_it != face_end) {
426  std::pair<dof_id_type, unsigned int> p = *face_it;
427 
428  // This has to be a full-ordered side element,
429  // since we need the correct n_nodes,
430  std::unique_ptr<Elem> side(this->_mesh.elem_ref(p.first).build_side_ptr(p.second));
431 
432  bool found=false;
433  for (const Node & node : side->node_ref_range())
434  if (onodes.count(node.id()))
435  {
436  found=true;
437  break;
438  }
439 
440  // If a new oface is found, include its nodes in onodes
441  if (found)
442  {
443  for (const Node & node : side->node_ref_range())
444  onodes.insert(node.id());
445 
446  ofaces.insert(p);
447  face_it = faces.erase(face_it); // increment is done here
448 
449  facesfound++;
450  }
451 
452  else
453  ++face_it; // increment is done here
454 
455  // If at least one new oface was found in this cycle,
456  // do another search cycle.
457  if (facesfound>0 && face_it == faces.end())
458  {
459  facesfound = 0;
460  face_it = faces.begin();
461  }
462  }
463 
464  if (!this->_mesh.is_serial())
465  {
466  auto my_onodes_size = onodes.size();
467  this->_mesh.comm().set_union(onodes);
468  did_parallel_update = (onodes.size() > my_onodes_size);
469  this->_mesh.comm().max(did_parallel_update);
470  }
471  }
472  while (did_parallel_update);
473 
474 
475 #ifdef DEBUG
476  if (be_verbose)
477  libMesh::out << " found "
478  << faces.size()
479  << " inner and "
480  << ofaces.size()
481  << " outer boundary faces"
482  << std::endl;
483 #endif
484 
485  // When the user provided a non-null pointer to
486  // inner_faces, that implies he wants to have
487  // this std::set. For now, simply copy the data.
488  if (inner_faces != nullptr)
489  *inner_faces = faces;
490 
491  // free memory, clear our local variable, no need
492  // for it any more.
493  faces.clear();
494 
495 
496  // outer_nodes maps onodes to their duplicates
497  std::map<dof_id_type, Node *> outer_nodes;
498 
499  // We may need to pick our own object ids in parallel
500  dof_id_type old_max_node_id = _mesh.max_node_id();
501  dof_id_type old_max_elem_id = _mesh.max_elem_id();
502 
503  // Likewise with our unique_ids
504 #ifdef LIBMESH_ENABLE_UNIQUE_ID
505  unique_id_type old_max_unique_id = _mesh.parallel_max_unique_id();
506 #endif
507 
508  // for each boundary node, add an outer_node with
509  // double distance from origin.
510  for (const auto & n : onodes)
511  {
512  if (!this->_mesh.query_node_ptr(n))
513  {
515  continue;
516  }
517 
518  Point p = (Point(this->_mesh.point(n)) * 2) - origin;
519  if (_mesh.is_serial())
520  {
521  // Add with a default id in serial
522  outer_nodes[n]=this->_mesh.add_point(p);
523  }
524  else
525  {
526  // Pick a unique id in parallel
527  Node & bnode = _mesh.node_ref(n);
528  dof_id_type new_id = bnode.id() + old_max_node_id;
529  std::unique_ptr<Node> new_node = Node::build(p, new_id);
530  new_node->processor_id() = bnode.processor_id();
531 #ifdef LIBMESH_ENABLE_UNIQUE_ID
532  new_node->set_unique_id(old_max_unique_id + bnode.id());
533 #endif
534 
535  outer_nodes[n] =
536  this->_mesh.add_node(std::move(new_node));
537  }
538  }
539 
540 
541 #ifdef DEBUG
542  // for verbose, remember n_elem
543  dof_id_type n_conventional_elem = this->_mesh.n_elem();
544 #endif
545 
546 
547  // build Elems based on boundary side type
548  for (auto & p : ofaces)
549  {
550  Elem & belem = this->_mesh.elem_ref(p.first);
551 
552  // build a full-ordered side element to get the base nodes
553  std::unique_ptr<Elem> side(belem.build_side_ptr(p.second));
554 
555  // create cell depending on side type, assign nodes,
556  // use braces to force scope.
557  bool is_higher_order_elem = false;
558 
559  std::unique_ptr<Elem> el;
560  switch(side->type())
561  {
562  // 3D infinite elements
563  // TRIs
564  case TRI3:
565  el = Elem::build(INFPRISM6);
566  break;
567 
568  case TRI6:
569  el = Elem::build(INFPRISM12);
570  is_higher_order_elem = true;
571  break;
572 
573  // QUADs
574  case QUAD4:
575  el = Elem::build(INFHEX8);
576  break;
577 
578  case QUAD8:
579  el = Elem::build(INFHEX16);
580  is_higher_order_elem = true;
581  break;
582 
583  case QUAD9:
584  el = Elem::build(INFHEX18);
585 
586  // the method of assigning nodes (which follows below)
587  // omits in the case of QUAD9 the bubble node; therefore
588  // we assign these first by hand here.
589  el->set_node(16, side->node_ptr(8));
590  el->set_node(17, outer_nodes[side->node_id(8)]);
591  is_higher_order_elem=true;
592  break;
593 
594  // 2D infinite elements
595  case EDGE2:
596  el = Elem::build(INFQUAD4);
597  break;
598 
599  case EDGE3:
600  el = Elem::build(INFQUAD6);
601  el->set_node(4, side->node_ptr(2));
602  break;
603 
604  // 1D infinite elements not supported
605  default:
606  libMesh::out << "InfElemBuilder::build_inf_elem(Point, bool, bool, bool, bool): "
607  << "invalid face element "
608  << std::endl;
609  continue;
610  }
611 
612  const unsigned int n_base_vertices = side->n_vertices();
613 
614  // On a distributed mesh, manually assign unique ids to the new
615  // element, and make sure any RemoteElem neighbor links are set.
616  if (!_mesh.is_serial())
617  {
618  el->processor_id() = belem.processor_id();
619 
620  // We'd better not have elements with more than 6 sides
621  const unsigned int max_sides = 6;
622  libmesh_assert_less_equal(el->n_sides(), max_sides);
623  el->set_id (belem.id() * max_sides + p.second + old_max_elem_id);
624 
625 #ifdef LIBMESH_ENABLE_UNIQUE_ID
626  el->set_unique_id(old_max_unique_id + old_max_node_id +
627  belem.id() * max_sides + p.second);
628 #endif
629 
630  // If we have a remote neighbor on a boundary element side
631  if (belem.dim() > 1)
632  for (auto s : belem.side_index_range())
633  if (belem.neighbor_ptr(s) == remote_elem)
634  {
635  // Find any corresponding infinite element side
636  std::unique_ptr<const Elem> remote_side(belem.build_side_ptr(s));
637 
638  for (auto inf_s : el->side_index_range())
639  {
640  // The base side 0 shares all vertices but isn't
641  // remote
642  if (!inf_s)
643  continue;
644 
645  // But another side, one which shares enough
646  // vertices to show it's the same side, is.
647  unsigned int n_shared_vertices = 0;
648  for (unsigned int i = 0; i != n_base_vertices; ++i)
649  for (auto & node : remote_side->node_ref_range())
650  if (side->node_ptr(i) == &node &&
651  el->is_node_on_side(i,inf_s))
652  ++n_shared_vertices;
653 
654  if (n_shared_vertices + 1 >= belem.dim())
655  {
656  el->set_neighbor
657  (inf_s, const_cast<RemoteElem *>(remote_elem));
658  break;
659  }
660  }
661  }
662  }
663 
664  // assign vertices to the new infinite element
665  for (unsigned int i=0; i<n_base_vertices; i++)
666  {
667  el->set_node(i , side->node_ptr(i));
668  el->set_node(i+n_base_vertices, outer_nodes[side->node_id(i)]);
669  }
670 
671 
672  // when this is a higher order element,
673  // assign also the nodes in between
674  if (is_higher_order_elem)
675  {
676  // n_safe_base_nodes is the number of nodes in \p side
677  // that may be safely assigned using below for loop.
678  // Actually, n_safe_base_nodes is _identical_ with el->n_vertices(),
679  // since for QUAD9, the 9th node was already assigned above
680  const unsigned int n_safe_base_nodes = el->n_vertices();
681 
682  for (unsigned int i=n_base_vertices; i<n_safe_base_nodes; i++)
683  {
684  el->set_node(i+n_base_vertices, side->node_ptr(i));
685  el->set_node(i+n_safe_base_nodes,
686  outer_nodes[side->node_id(i)]);
687  }
688  }
689 
690 
691  // add infinite element to mesh
692  this->_mesh.add_elem(std::move(el));
693  } // for
694 
695  // If we're not in serial, we might not have all our remote_elem
696  // neighbors set up properly on the new elements: two new infinite
697  // elements which should be neighbors may not be rooted in finite
698  // elmeents which are neighbors, and we were only looking for
699  // remote_elem links on each root finite element. We'll fix the
700  // problem by first finding all the neighbors we can and then
701  // recognizing that any missing neighbors on infinite elements must
702  // be remote.
703  if (!_mesh.is_serial())
704  {
705  _mesh.find_neighbors(/*reset_remote_elements=*/ false,
706  /*reset_current_list=*/ true,
707  /*assert_valid=*/ false);
708 
709  for (auto & p : ofaces)
710  {
711  Elem & belem = this->_mesh.elem_ref(p.first);
712  Elem * inf_elem = belem.neighbor_ptr(p.second);
713  libmesh_assert(inf_elem);
714 
715  for (auto s : make_range(inf_elem->n_sides()))
716  if (!inf_elem->neighbor_ptr(s))
717  inf_elem->set_neighbor
718  (s, const_cast<RemoteElem *>(remote_elem));
719  }
720  }
721 
722 #ifdef DEBUG
724 
725  if (be_verbose)
726  libMesh::out << " added "
727  << this->_mesh.n_elem() - n_conventional_elem
728  << " infinite elements and "
729  << onodes.size()
730  << " nodes to the mesh"
731  << std::endl
732  << std::endl;
733 #endif
734 }
virtual unique_id_type parallel_max_unique_id() const =0
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true)=0
Locate element face (edge in 2D) neighbors.
const Parallel::Communicator & comm() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
virtual bool is_serial() const
Definition: mesh_base.h:347
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
virtual const Node * query_node_ptr(const dof_id_type i) const =0
virtual dof_id_type max_elem_id() const =0
libmesh_assert(ctx)
void maxloc(T &r, unsigned int &max_id) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
virtual Node * add_node(Node *n)=0
Add Node n to the end of the vertex array.
static std::unique_ptr< Node > build(const Node &n)
Definition: node.h:315
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void max(const T &r, T &o, Request &req) const
OStreamProxy out
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:778
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
virtual void libmesh_assert_valid_parallel_ids() const
Verify id and processor_id consistency of our elements and nodes containers.
Definition: mesh_base.h:1688
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:735
virtual const Point & point(const dof_id_type i) const =0
virtual dof_id_type max_node_id() const =0
virtual dof_id_type n_elem() const =0
MeshBase & _mesh
Reference to the mesh we&#39;re building infinite elements for.
uint8_t unique_id_type
Definition: id_types.h:86
uint8_t dof_id_type
Definition: id_types.h:67
void set_union(T &data, const unsigned int root_id) const
const RemoteElem * remote_elem
Definition: remote_elem.C:57

Member Data Documentation

◆ _mesh

MeshBase& libMesh::InfElemBuilder::_mesh
private

Reference to the mesh we're building infinite elements for.

Definition at line 130 of file inf_elem_builder.h.

Referenced by build_inf_elem().


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