replicated_mesh.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 
20 // Local includes
21 #include "libmesh/boundary_info.h"
22 #include "libmesh/elem.h"
26 #include "libmesh/utility.h"
27 
28 // C++ includes
29 #include <unordered_map>
30 #include <unordered_set>
31 
32 namespace
33 {
34 using namespace libMesh;
35 
36 // A custom comparison function, based on Point::operator<,
37 // that tries to ignore floating point differences in components
38 // of the point
39 class FuzzyPointCompare
40 {
41 private:
42  Real _tol;
43 
44 public:
45  // Constructor takes the tolerance to be used in fuzzy comparisons
46  FuzzyPointCompare(Real tol) : _tol(tol) {}
47 
48  // This is inspired directly by Point::operator<
49  bool operator()(const Point & lhs, const Point & rhs)
50  {
51  for (unsigned i=0; i<LIBMESH_DIM; ++i)
52  {
53  // If the current components are within some tolerance
54  // of one another, then don't attempt the less-than comparison.
55  // Note that this may cause something strange to happen, as Roy
56  // believes he can prove it is not a total ordering...
57  Real rel_size = std::max(std::abs(lhs(i)), std::abs(rhs(i)));
58 
59  // Don't use relative tolerance if both numbers are already small.
60  // How small? Some possible options are:
61  // * std::numeric_limits<Real>::epsilon()
62  // * TOLERANCE
63  // * 1.0
64  // If we use std::numeric_limits<Real>::epsilon(), we'll
65  // do more relative comparisons for small numbers, but
66  // increase the chance for false positives? If we pick 1.0,
67  // we'll never "increase" the difference between small numbers
68  // in the test below.
69  if (rel_size < 1.)
70  rel_size = 1.;
71 
72  // Don't attempt the comparison if lhs(i) and rhs(i) are too close
73  // together.
74  if ( std::abs(lhs(i) - rhs(i)) / rel_size < _tol)
75  continue;
76 
77  if (lhs(i) < rhs(i))
78  return true;
79  if (lhs(i) > rhs(i))
80  return false;
81  }
82 
83  // We compared all the components without returning yet, so
84  // each component was neither greater than nor less than they other.
85  // They might be equal, so return false.
86  return false;
87  }
88 
89  // Needed by std::sort on vector<pair<Point,id>>
90  bool operator()(const std::pair<Point, dof_id_type> & lhs,
91  const std::pair<Point, dof_id_type> & rhs)
92  {
93  return (*this)(lhs.first, rhs.first);
94  }
95 
96  // comparison function where lhs is a Point and rhs is a pair<Point,dof_id_type>.
97  // This is used in routines like lower_bound, where a specific value is being
98  // searched for.
99  bool operator()(const Point & lhs, std::pair<Point, dof_id_type> & rhs)
100  {
101  return (*this)(lhs, rhs.first);
102  }
103 
104  // And the other way around...
105  bool operator()(std::pair<Point, dof_id_type> & lhs, const Point & rhs)
106  {
107  return (*this)(lhs.first, rhs);
108  }
109 };
110 }
111 
112 
113 
114 namespace libMesh
115 {
116 
117 // ------------------------------------------------------------
118 // ReplicatedMesh class member functions
120  unsigned char d) :
121  UnstructuredMesh (comm_in,d)
122 {
123 #ifdef LIBMESH_ENABLE_UNIQUE_ID
124  // In serial we just need to reset the next unique id to zero
125  // here in the constructor.
126  _next_unique_id = 0;
127 #endif
128  _partitioner = libmesh_make_unique<MetisPartitioner>();
129 }
130 
131 
132 
134 {
135  this->clear(); // Free nodes and elements
136 }
137 
138 
139 // This might be specialized later, but right now it's just here to
140 // make sure the compiler doesn't give us a default (non-deep) copy
141 // constructor instead.
143  UnstructuredMesh (other_mesh)
144 {
145  this->copy_nodes_and_elements(other_mesh);
146  this->get_boundary_info() = other_mesh.get_boundary_info();
147 #ifdef LIBMESH_ENABLE_UNIQUE_ID
148  this->_next_unique_id = other_mesh._next_unique_id;
149 #endif
150 }
151 
152 
154  UnstructuredMesh (other_mesh)
155 {
156  this->copy_nodes_and_elements(other_mesh);
157  this->get_boundary_info() = other_mesh.get_boundary_info();
158 }
159 
160 
161 const Point & ReplicatedMesh::point (const dof_id_type i) const
162 {
163  return this->node_ref(i);
164 }
165 
166 
167 
168 
170 {
171  libmesh_assert_less (i, this->n_nodes());
172  libmesh_assert(_nodes[i]);
173  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
174 
175  return _nodes[i];
176 }
177 
178 
179 
180 
182 {
183  libmesh_assert_less (i, this->n_nodes());
184  libmesh_assert(_nodes[i]);
185  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
186 
187  return _nodes[i];
188 }
189 
190 
191 
192 
194 {
195  if (i >= this->n_nodes())
196  return nullptr;
197  libmesh_assert (_nodes[i] == nullptr ||
198  _nodes[i]->id() == i); // This will change soon
199 
200  return _nodes[i];
201 }
202 
203 
204 
205 
207 {
208  if (i >= this->n_nodes())
209  return nullptr;
210  libmesh_assert (_nodes[i] == nullptr ||
211  _nodes[i]->id() == i); // This will change soon
212 
213  return _nodes[i];
214 }
215 
216 
217 
218 
220 {
221  libmesh_assert_less (i, this->n_elem());
222  libmesh_assert(_elements[i]);
223  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
224 
225  return _elements[i];
226 }
227 
228 
229 
230 
232 {
233  libmesh_assert_less (i, this->n_elem());
234  libmesh_assert(_elements[i]);
235  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
236 
237  return _elements[i];
238 }
239 
240 
241 
242 
244 {
245  if (i >= this->n_elem())
246  return nullptr;
247  libmesh_assert (_elements[i] == nullptr ||
248  _elements[i]->id() == i); // This will change soon
249 
250  return _elements[i];
251 }
252 
253 
254 
255 
257 {
258  if (i >= this->n_elem())
259  return nullptr;
260  libmesh_assert (_elements[i] == nullptr ||
261  _elements[i]->id() == i); // This will change soon
262 
263  return _elements[i];
264 }
265 
266 
267 
268 
270 {
271  libmesh_assert(e);
272 
273  // We no longer merely append elements with ReplicatedMesh
274 
275  // If the user requests a valid id that doesn't correspond to an
276  // existing element, let's give them that id, resizing the elements
277  // container if necessary.
278  if (!e->valid_id())
279  e->set_id (cast_int<dof_id_type>(_elements.size()));
280 
281 #ifdef LIBMESH_ENABLE_UNIQUE_ID
282  if (!e->valid_unique_id())
284 #endif
285 
286  const dof_id_type id = e->id();
287 
288  if (id < _elements.size())
289  {
290  // Overwriting existing elements is still probably a mistake.
291  libmesh_assert(!_elements[id]);
292  }
293  else
294  {
295  _elements.resize(id+1, nullptr);
296  }
297 
298  _elements[id] = e;
299 
300  return e;
301 }
302 
303 
304 
306 {
307 #ifdef LIBMESH_ENABLE_UNIQUE_ID
308  if (!e->valid_unique_id())
310 #endif
311 
312  dof_id_type eid = e->id();
313  libmesh_assert_less (eid, _elements.size());
314  Elem * oldelem = _elements[eid];
315 
316  if (oldelem)
317  {
318  libmesh_assert_equal_to (oldelem->id(), eid);
319  this->delete_elem(oldelem);
320  }
321 
322  _elements[e->id()] = e;
323 
324  return e;
325 }
326 
327 
328 
330 {
331  libmesh_assert(e);
332 
333  // Initialize an iterator to eventually point to the element we want to delete
334  std::vector<Elem *>::iterator pos = _elements.end();
335 
336  // In many cases, e->id() gives us a clue as to where e
337  // is located in the _elements vector. Try that first
338  // before trying the O(n_elem) search.
339  libmesh_assert_less (e->id(), _elements.size());
340 
341  if (_elements[e->id()] == e)
342  {
343  // We found it!
344  pos = _elements.begin();
345  std::advance(pos, e->id());
346  }
347 
348  else
349  {
350  // This search is O(n_elem)
351  pos = std::find (_elements.begin(),
352  _elements.end(),
353  e);
354  }
355 
356  // Huh? Element not in the vector?
357  libmesh_assert (pos != _elements.end());
358 
359  // Remove the element from the BoundaryInfo object
360  this->get_boundary_info().remove(e);
361 
362  // delete the element
363  delete e;
364 
365  // explicitly zero the pointer
366  *pos = nullptr;
367 }
368 
369 
370 
372  const dof_id_type new_id)
373 {
374  // This doesn't get used in serial yet
375  Elem * el = _elements[old_id];
376  libmesh_assert (el);
377 
378  el->set_id(new_id);
379  libmesh_assert (!_elements[new_id]);
380  _elements[new_id] = el;
381  _elements[old_id] = nullptr;
382 }
383 
384 
385 
387  const dof_id_type id,
388  const processor_id_type proc_id)
389 {
390  // // We only append points with ReplicatedMesh
391  // libmesh_assert(id == DofObject::invalid_id || id == _nodes.size());
392  // Node *n = Node::build(p, _nodes.size()).release();
393  // n->processor_id() = proc_id;
394  // _nodes.push_back (n);
395 
396  Node * n = nullptr;
397 
398  // If the user requests a valid id, either
399  // provide the existing node or resize the container
400  // to fit the new node.
401  if (id != DofObject::invalid_id)
402  if (id < _nodes.size())
403  n = _nodes[id];
404  else
405  _nodes.resize(id+1);
406  else
407  _nodes.push_back (static_cast<Node *>(nullptr));
408 
409  // if the node already exists, then assign new (x,y,z) values
410  if (n)
411  *n = p;
412  // otherwise build a new node, put it in the right spot, and return
413  // a valid pointer.
414  else
415  {
416  n = Node::build(p, (id == DofObject::invalid_id) ?
417  cast_int<dof_id_type>(_nodes.size()-1) : id).release();
418  n->processor_id() = proc_id;
419 
420 #ifdef LIBMESH_ENABLE_UNIQUE_ID
421  if (!n->valid_unique_id())
423 #endif
424 
425  if (id == DofObject::invalid_id)
426  _nodes.back() = n;
427  else
428  _nodes[id] = n;
429  }
430 
431  // better not pass back a nullptr.
432  libmesh_assert (n);
433 
434  return n;
435 }
436 
437 
438 
440 {
441  libmesh_assert(n);
442  // We only append points with ReplicatedMesh
443  libmesh_assert(!n->valid_id() || n->id() == _nodes.size());
444 
445  n->set_id (cast_int<dof_id_type>(_nodes.size()));
446 
447 #ifdef LIBMESH_ENABLE_UNIQUE_ID
448  if (!n->valid_unique_id())
450 #endif
451 
452  _nodes.push_back(n);
453 
454  return n;
455 }
456 
457 
458 
460 {
461  if (!n)
462  libmesh_error_msg("Error, attempting to insert nullptr node.");
463 
464  if (n->id() == DofObject::invalid_id)
465  libmesh_error_msg("Error, cannot insert node with invalid id.");
466 
467  if (n->id() < _nodes.size())
468  {
469  // Don't allow inserting on top of an existing Node.
470 
471  // Doing so doesn't have to be *error*, in the case where a
472  // redundant insert is done, but when that happens we ought to
473  // always be able to make the code more efficient by avoiding
474  // the redundant insert, so let's keep screaming "Error" here.
475  if (_nodes[ n->id() ] != nullptr)
476  libmesh_error_msg("Error, cannot insert node on top of existing node.");
477  }
478  else
479  {
480  // Allocate just enough space to store the new node. This will
481  // cause highly non-ideal memory allocation behavior if called
482  // repeatedly...
483  _nodes.resize(n->id() + 1);
484  }
485 
486 #ifdef LIBMESH_ENABLE_UNIQUE_ID
487  if (!n->valid_unique_id())
489 #endif
490 
491  // We have enough space and this spot isn't already occupied by
492  // another node, so go ahead and add it.
493  _nodes[ n->id() ] = n;
494 
495  // If we made it this far, we just inserted the node the user handed
496  // us, so we can give it right back.
497  return n;
498 }
499 
500 
501 
503 {
504  libmesh_assert(n);
505  libmesh_assert_less (n->id(), _nodes.size());
506 
507  // Initialize an iterator to eventually point to the element we want
508  // to delete
509  std::vector<Node *>::iterator pos;
510 
511  // In many cases, e->id() gives us a clue as to where e
512  // is located in the _elements vector. Try that first
513  // before trying the O(n_elem) search.
514  if (_nodes[n->id()] == n)
515  {
516  pos = _nodes.begin();
517  std::advance(pos, n->id());
518  }
519  else
520  {
521  pos = std::find (_nodes.begin(),
522  _nodes.end(),
523  n);
524  }
525 
526  // Huh? Node not in the vector?
527  libmesh_assert (pos != _nodes.end());
528 
529  // Delete the node from the BoundaryInfo object
530  this->get_boundary_info().remove(n);
531 
532  // delete the node
533  delete n;
534 
535  // explicitly zero the pointer
536  *pos = nullptr;
537 }
538 
539 
540 
542  const dof_id_type new_id)
543 {
544  // This doesn't get used in serial yet
545  Node * nd = _nodes[old_id];
546  libmesh_assert (nd);
547 
548  nd->set_id(new_id);
549  libmesh_assert (!_nodes[new_id]);
550  _nodes[new_id] = nd;
551  _nodes[old_id] = nullptr;
552 }
553 
554 
555 
557 {
558  // Call parent clear function
559  MeshBase::clear();
560 
561  // Clear our elements and nodes
562  // There is no need to remove the elements from
563  // the BoundaryInfo data structure since we
564  // already cleared it.
565  for (auto & elem : _elements)
566  delete elem;
567 
568  _elements.clear();
569 
570  // clear the nodes data structure
571  // There is no need to remove the nodes from
572  // the BoundaryInfo data structure since we
573  // already cleared it.
574  for (auto & node : _nodes)
575  delete node;
576 
577  _nodes.clear();
578 }
579 
580 
581 
583 {
584 #ifdef LIBMESH_ENABLE_UNIQUE_ID
586 #endif
587 }
588 
589 
590 
591 #ifdef LIBMESH_ENABLE_UNIQUE_ID
593 {
594  // This function must be run on all processors at once
595  parallel_object_only();
596 
597  unique_id_type max_local = _next_unique_id;
598  this->comm().max(max_local);
599  return max_local;
600 }
601 #endif
602 
603 
604 
606 {
607  LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
608 
609  // node and element id counters
610  dof_id_type next_free_elem = 0;
611  dof_id_type next_free_node = 0;
612 
613  // Will hold the set of nodes that are currently connected to elements
614  std::unordered_set<Node *> connected_nodes;
615 
616  // Loop over the elements. Note that there may
617  // be nullptrs in the _elements vector from the coarsening
618  // process. Pack the elements in to a contiguous array
619  // and then trim any excess.
620  {
621  std::vector<Elem *>::iterator in = _elements.begin();
622  std::vector<Elem *>::iterator out_iter = _elements.begin();
623  const std::vector<Elem *>::iterator end = _elements.end();
624 
625  for (; in != end; ++in)
626  if (*in != nullptr)
627  {
628  Elem * el = *in;
629 
630  *out_iter = *in;
631  ++out_iter;
632 
633  // Increment the element counter
634  el->set_id (next_free_elem++);
635 
637  {
638  // Add this elements nodes to the connected list
639  for (auto & n : el->node_ref_range())
640  connected_nodes.insert(&n);
641  }
642  else // We DO want node renumbering
643  {
644  // Loop over this element's nodes. Number them,
645  // if they have not been numbered already. Also,
646  // position them in the _nodes vector so that they
647  // are packed contiguously from the beginning.
648  for (auto & n : el->node_ref_range())
649  if (n.id() == next_free_node) // don't need to process
650  next_free_node++; // [(src == dst) below]
651 
652  else if (n.id() > next_free_node) // need to process
653  {
654  // The source and destination indices
655  // for this node
656  const dof_id_type src_idx = n.id();
657  const dof_id_type dst_idx = next_free_node++;
658 
659  // ensure we want to swap a valid nodes
660  libmesh_assert(_nodes[src_idx]);
661 
662  // Swap the source and destination nodes
663  std::swap(_nodes[src_idx],
664  _nodes[dst_idx] );
665 
666  // Set proper indices where that makes sense
667  if (_nodes[src_idx] != nullptr)
668  _nodes[src_idx]->set_id (src_idx);
669  _nodes[dst_idx]->set_id (dst_idx);
670  }
671  }
672  }
673 
674  // Erase any additional storage. These elements have been
675  // copied into nullptr voids by the procedure above, and are
676  // thus repeated and unnecessary.
677  _elements.erase (out_iter, end);
678  }
679 
680 
682  {
683  // Loop over the nodes. Note that there may
684  // be nullptrs in the _nodes vector from the coarsening
685  // process. Pack the nodes in to a contiguous array
686  // and then trim any excess.
687 
688  std::vector<Node *>::iterator in = _nodes.begin();
689  std::vector<Node *>::iterator out_iter = _nodes.begin();
690  const std::vector<Node *>::iterator end = _nodes.end();
691 
692  for (; in != end; ++in)
693  if (*in != nullptr)
694  {
695  // This is a reference so that if we change the pointer it will change in the vector
696  Node * & nd = *in;
697 
698  // If this node is still connected to an elem, put it in the list
699  if (connected_nodes.find(nd) != connected_nodes.end())
700  {
701  *out_iter = nd;
702  ++out_iter;
703 
704  // Increment the node counter
705  nd->set_id (next_free_node++);
706  }
707  else // This node is orphaned, delete it!
708  {
709  this->get_boundary_info().remove (nd);
710 
711  // delete the node
712  delete nd;
713  nd = nullptr;
714  }
715  }
716 
717  // Erase any additional storage. Whatever was
718  _nodes.erase (out_iter, end);
719  }
720  else // We really DO want node renumbering
721  {
722  // Any nodes in the vector >= _nodes[next_free_node]
723  // are not connected to any elements and may be deleted
724  // if desired.
725 
726  // Now, delete the unused nodes
727  {
728  std::vector<Node *>::iterator nd = _nodes.begin();
729  const std::vector<Node *>::iterator end = _nodes.end();
730 
731  std::advance (nd, next_free_node);
732 
733  for (auto & node : as_range(nd, end))
734  {
735  // Mesh modification code might have already deleted some
736  // nodes
737  if (node == nullptr)
738  continue;
739 
740  // remove any boundary information associated with
741  // this node
742  this->get_boundary_info().remove (node);
743 
744  // delete the node
745  delete node;
746  node = nullptr;
747  }
748 
749  _nodes.erase (nd, end);
750  }
751  }
752 
753  libmesh_assert_equal_to (next_free_elem, _elements.size());
754  libmesh_assert_equal_to (next_free_node, _nodes.size());
755 
757 }
758 
759 
760 
762 {
763  // Nodes first
764  for (std::size_t n=0; n<this->_nodes.size(); n++)
765  if (this->_nodes[n] != nullptr)
766  this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
767 
768  // Elements next
769  for (std::size_t e=0; e<this->_elements.size(); e++)
770  if (this->_elements[e] != nullptr)
771  this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
772 }
773 
774 
776  boundary_id_type this_mesh_boundary_id,
777  boundary_id_type other_mesh_boundary_id,
778  Real tol,
779  bool clear_stitched_boundary_ids,
780  bool verbose,
781  bool use_binary_search,
782  bool enforce_all_nodes_match_on_boundaries)
783 {
784  LOG_SCOPE("stitch_meshes()", "ReplicatedMesh");
785  stitching_helper(&other_mesh,
786  this_mesh_boundary_id,
787  other_mesh_boundary_id,
788  tol,
789  clear_stitched_boundary_ids,
790  verbose,
791  use_binary_search,
792  enforce_all_nodes_match_on_boundaries,
793  true);
794 }
795 
797  boundary_id_type boundary_id_2,
798  Real tol,
799  bool clear_stitched_boundary_ids,
800  bool verbose,
801  bool use_binary_search,
802  bool enforce_all_nodes_match_on_boundaries)
803 {
804  stitching_helper(nullptr,
805  boundary_id_1,
806  boundary_id_2,
807  tol,
808  clear_stitched_boundary_ids,
809  verbose,
810  use_binary_search,
811  enforce_all_nodes_match_on_boundaries,
812  true);
813 }
814 
816  boundary_id_type this_mesh_boundary_id,
817  boundary_id_type other_mesh_boundary_id,
818  Real tol,
819  bool clear_stitched_boundary_ids,
820  bool verbose,
821  bool use_binary_search,
822  bool enforce_all_nodes_match_on_boundaries,
823  bool skip_find_neighbors)
824 {
825  std::map<dof_id_type, dof_id_type> node_to_node_map, other_to_this_node_map; // The second is the inverse map of the first
826  std::map<dof_id_type, std::vector<dof_id_type>> node_to_elems_map;
827 
828  typedef dof_id_type key_type;
829  typedef std::pair<Elem *, unsigned char> val_type;
830  typedef std::pair<key_type, val_type> key_val_pair;
831  typedef std::unordered_multimap<key_type, val_type> map_type;
832  // Mapping between all side keys in this mesh and elements+side numbers relevant to the boundary in this mesh as well.
833  map_type side_to_elem_map;
834 
835  // If there is only one mesh (i.e. other_mesh == nullptr), then loop over this mesh twice
836  if (!other_mesh)
837  {
838  other_mesh = this;
839  }
840 
841  if ((this_mesh_boundary_id != BoundaryInfo::invalid_id) &&
842  (other_mesh_boundary_id != BoundaryInfo::invalid_id))
843  {
844  LOG_SCOPE("stitch_meshes node merging", "ReplicatedMesh");
845 
846  // While finding nodes on the boundary, also find the minimum edge length
847  // of all faces on both boundaries. This will later be used in relative
848  // distance checks when stitching nodes.
850  bool h_min_updated = false;
851 
852  // Loop below fills in these sets for the two meshes.
853  std::set<dof_id_type> this_boundary_node_ids, other_boundary_node_ids;
854 
855  // Pull objects out of the loop to reduce heap operations
856  std::unique_ptr<Elem> side;
857 
858  {
859  // Make temporary fixed-size arrays for loop
860  boundary_id_type id_array[2] = {this_mesh_boundary_id, other_mesh_boundary_id};
861  std::set<dof_id_type> * set_array[2] = {&this_boundary_node_ids, &other_boundary_node_ids};
862  const ReplicatedMesh * mesh_array[2] = {this, other_mesh};
863 
864  for (unsigned i=0; i<2; ++i)
865  {
866  // First we deal with node boundary IDs.
867  // We only enter this loop if we have at least one
868  // nodeset.
869  if (mesh_array[i]->get_boundary_info().n_nodeset_conds() > 0)
870  {
871  // build_node_list() returns a vector of (node-id, bc-id) tuples
872  for (const auto & t : mesh_array[i]->get_boundary_info().build_node_list())
873  {
874  boundary_id_type node_bc_id = std::get<1>(t);
875  if (node_bc_id == id_array[i])
876  {
877  dof_id_type this_node_id = std::get<0>(t);
878  set_array[i]->insert( this_node_id );
879 
880  // We need to set h_min to some value. It's too expensive to
881  // search for the element that actually contains this node,
882  // since that would require a PointLocator. As a result, we
883  // just use the first element in the mesh to give us hmin.
884  const Elem * first_active_elem = *mesh_array[i]->active_elements_begin();
885  h_min = first_active_elem->hmin();
886  h_min_updated = true;
887  }
888  }
889  }
890 
891  // Container to catch boundary IDs passed back from BoundaryInfo.
892  std::vector<boundary_id_type> bc_ids;
893 
894  for (auto & el : mesh_array[i]->element_ptr_range())
895  {
896  // Now check whether elem has a face on the specified boundary
897  for (auto side_id : el->side_index_range())
898  if (el->neighbor_ptr(side_id) == nullptr)
899  {
900  // Get *all* boundary IDs on this side, not just the first one!
901  mesh_array[i]->get_boundary_info().boundary_ids (el, side_id, bc_ids);
902 
903  if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
904  {
905  el->build_side_ptr(side, side_id);
906  for (auto & n : side->node_ref_range())
907  set_array[i]->insert(n.id());
908 
909  h_min = std::min(h_min, side->hmin());
910  h_min_updated = true;
911 
912  // This side is on the boundary, add its information to side_to_elem
913  if (skip_find_neighbors && (i==0))
914  {
915  key_type key = el->key(side_id);
916  val_type val;
917  val.first = el;
918  val.second = cast_int<unsigned char>(side_id);
919 
920  key_val_pair kvp;
921  kvp.first = key;
922  kvp.second = val;
923  side_to_elem_map.insert (kvp);
924  }
925  }
926 
927  // Also, check the edges on this side. We don't have to worry about
928  // updating neighbor info in this case since elements don't store
929  // neighbor info on edges.
930  for (auto edge_id : el->edge_index_range())
931  {
932  if (el->is_edge_on_side(edge_id, side_id))
933  {
934  // Get *all* boundary IDs on this edge, not just the first one!
935  mesh_array[i]->get_boundary_info().edge_boundary_ids (el, edge_id, bc_ids);
936 
937  if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
938  {
939  std::unique_ptr<Elem> edge (el->build_edge_ptr(edge_id));
940  for (auto & n : edge->node_ref_range())
941  set_array[i]->insert( n.id() );
942 
943  h_min = std::min(h_min, edge->hmin());
944  h_min_updated = true;
945  }
946  }
947  }
948  }
949  }
950  }
951  }
952 
953  if (verbose)
954  {
955  libMesh::out << "In ReplicatedMesh::stitch_meshes:\n"
956  << "This mesh has " << this_boundary_node_ids.size()
957  << " nodes on boundary " << this_mesh_boundary_id << ".\n"
958  << "Other mesh has " << other_boundary_node_ids.size()
959  << " nodes on boundary " << other_mesh_boundary_id << ".\n";
960 
961  if (h_min_updated)
962  {
963  libMesh::out << "Minimum edge length on both surfaces is " << h_min << ".\n";
964  }
965  else
966  {
967  libMesh::out << "No elements on specified surfaces." << std::endl;
968  }
969  }
970 
971 
972  if (use_binary_search)
973  {
974  // Store points from both stitched faces in sorted vectors for faster
975  // searching later.
976  typedef std::vector<std::pair<Point, dof_id_type>> PointVector;
977  PointVector
978  this_sorted_bndry_nodes(this_boundary_node_ids.size()),
979  other_sorted_bndry_nodes(other_boundary_node_ids.size());
980 
981  // Comparison object that will be used later. So far, I've had reasonable success
982  // with TOLERANCE...
983  FuzzyPointCompare mein_comp(TOLERANCE);
984 
985  // Create and sort the vectors we will use to do the geometric searching
986  {
987  std::set<dof_id_type> * set_array[2] = {&this_boundary_node_ids, &other_boundary_node_ids};
988  const ReplicatedMesh * mesh_array[2] = {this, other_mesh};
989  PointVector * vec_array[2] = {&this_sorted_bndry_nodes, &other_sorted_bndry_nodes};
990 
991  for (unsigned i=0; i<2; ++i)
992  {
993  std::set<dof_id_type>::iterator
994  set_it = set_array[i]->begin(),
995  set_it_end = set_array[i]->end();
996 
997  // Fill up the vector with the contents of the set...
998  for (unsigned ctr=0; set_it != set_it_end; ++set_it, ++ctr)
999  {
1000  (*vec_array[i])[ctr] = std::make_pair(mesh_array[i]->point(*set_it), // The geometric point
1001  *set_it); // Its ID
1002  }
1003 
1004  // Sort the vectors based on the FuzzyPointCompare struct op()
1005  std::sort(vec_array[i]->begin(), vec_array[i]->end(), mein_comp);
1006  }
1007  }
1008 
1009  // Build up the node_to_node_map and node_to_elems_map using the sorted vectors of Points.
1010  for (std::size_t i=0; i<this_sorted_bndry_nodes.size(); ++i)
1011  {
1012  // Current point we're working on
1013  Point this_point = this_sorted_bndry_nodes[i].first;
1014 
1015  // FuzzyPointCompare does a fuzzy equality comparison internally to handle
1016  // slight differences between the list of nodes on each mesh.
1017  PointVector::iterator other_iter = Utility::binary_find(other_sorted_bndry_nodes.begin(),
1018  other_sorted_bndry_nodes.end(),
1019  this_point,
1020  mein_comp);
1021 
1022  // Not every node on this_sorted_bndry_nodes will necessarily be stitched, so
1023  // if its pair is not found on other_mesh, just continue.
1024  if (other_iter != other_sorted_bndry_nodes.end())
1025  {
1026  // Check that the points do indeed match - should not be necessary unless something
1027  // is wrong with binary_find. To be on the safe side, we'll check.
1028  {
1029  // Grab the other point from the iterator
1030  Point other_point = other_iter->first;
1031 
1032  if (!this_point.absolute_fuzzy_equals(other_point, tol*h_min))
1033  libmesh_error_msg("Error: mismatched points: " << this_point << " and " << other_point);
1034  }
1035 
1036 
1037  // Associate these two nodes in both the node_to_node_map and the other_to_this_node_map
1038  dof_id_type
1039  this_node_id = this_sorted_bndry_nodes[i].second,
1040  other_node_id = other_iter->second;
1041  node_to_node_map[this_node_id] = other_node_id;
1042  other_to_this_node_map[other_node_id] = this_node_id;
1043  }
1044 
1045  }
1046  }
1047  else
1048  {
1049  // Otherwise, use a simple N^2 search to find the closest matching points. This can be helpful
1050  // in the case that we have tolerance issues which cause mismatch between the two surfaces
1051  // that are being stitched.
1052  for (const auto & this_node_id : this_boundary_node_ids)
1053  {
1054  Node & this_node = this->node_ref(this_node_id);
1055 
1056  bool found_matching_nodes = false;
1057 
1058  for (const auto & other_node_id : other_boundary_node_ids)
1059  {
1060  const Node & other_node = other_mesh->node_ref(other_node_id);
1061 
1062  Real node_distance = (this_node - other_node).norm();
1063 
1064  if (node_distance < tol*h_min)
1065  {
1066  // Make sure we didn't already find a matching node!
1067  if (found_matching_nodes)
1068  libmesh_error_msg("Error: Found multiple matching nodes in stitch_meshes");
1069 
1070  node_to_node_map[this_node_id] = other_node_id;
1071  other_to_this_node_map[other_node_id] = this_node_id;
1072 
1073  found_matching_nodes = true;
1074  }
1075  }
1076  }
1077  }
1078 
1079  // Build up the node_to_elems_map, using only one loop over other_mesh
1080  for (auto & el : other_mesh->element_ptr_range())
1081  {
1082  // For each node on the element, find the corresponding node
1083  // on "this" Mesh, 'this_node_id', if it exists, and push
1084  // the current element ID back onto node_to_elems_map[this_node_id].
1085  // For that we will use the reverse mapping we created at
1086  // the same time as the forward mapping.
1087  for (auto & n : el->node_ref_range())
1088  {
1089  dof_id_type other_node_id = n.id();
1090  std::map<dof_id_type, dof_id_type>::iterator it =
1091  other_to_this_node_map.find(other_node_id);
1092 
1093  if (it != other_to_this_node_map.end())
1094  {
1095  dof_id_type this_node_id = it->second;
1096  node_to_elems_map[this_node_id].push_back( el->id() );
1097  }
1098  }
1099  }
1100 
1101  if (verbose)
1102  {
1103  libMesh::out << "In ReplicatedMesh::stitch_meshes:\n"
1104  << "Found " << node_to_node_map.size()
1105  << " matching nodes.\n"
1106  << std::endl;
1107  }
1108 
1109  if (enforce_all_nodes_match_on_boundaries)
1110  {
1111  std::size_t n_matching_nodes = node_to_node_map.size();
1112  std::size_t this_mesh_n_nodes = this_boundary_node_ids.size();
1113  std::size_t other_mesh_n_nodes = other_boundary_node_ids.size();
1114  if ((n_matching_nodes != this_mesh_n_nodes) || (n_matching_nodes != other_mesh_n_nodes))
1115  libmesh_error_msg("Error: We expected the number of nodes to match.");
1116  }
1117  }
1118  else
1119  {
1120  if (verbose)
1121  {
1122  libMesh::out << "Skip node merging in ReplicatedMesh::stitch_meshes:" << std::endl;
1123  }
1124  }
1125 
1126  dof_id_type node_delta = this->max_node_id();
1127  dof_id_type elem_delta = this->max_elem_id();
1128 
1129  unique_id_type unique_delta =
1130 #ifdef LIBMESH_ENABLE_UNIQUE_ID
1131  this->parallel_max_unique_id();
1132 #else
1133  0;
1134 #endif
1135 
1136  // If other_mesh != nullptr, then we have to do a bunch of work
1137  // in order to copy it to this mesh
1138  if (this!=other_mesh)
1139  {
1140  LOG_SCOPE("stitch_meshes copying", "ReplicatedMesh");
1141 
1142  // Increment the node_to_node_map and node_to_elems_map
1143  // to account for id offsets
1144  for (auto & pr : node_to_node_map)
1145  pr.second += node_delta;
1146 
1147  for (auto & pr : node_to_elems_map)
1148  for (auto & entry : pr.second)
1149  entry += elem_delta;
1150 
1151  // Copy mesh data. If we skip the call to find_neighbors(), the lists
1152  // of neighbors will be copied verbatim from the other mesh
1153  this->copy_nodes_and_elements(*other_mesh, skip_find_neighbors,
1154  elem_delta, node_delta,
1155  unique_delta);
1156 
1157  // Copy BoundaryInfo from other_mesh too. We do this via the
1158  // list APIs rather than element-by-element for speed.
1159  BoundaryInfo & boundary = this->get_boundary_info();
1160  const BoundaryInfo & other_boundary = other_mesh->get_boundary_info();
1161 
1162  for (const auto & t : other_boundary.build_node_list())
1163  boundary.add_node(std::get<0>(t) + node_delta,
1164  std::get<1>(t));
1165 
1166  for (const auto & t : other_boundary.build_side_list())
1167  boundary.add_side(std::get<0>(t) + elem_delta,
1168  std::get<1>(t),
1169  std::get<2>(t));
1170 
1171  for (const auto & t : other_boundary.build_edge_list())
1172  boundary.add_edge(std::get<0>(t) + elem_delta,
1173  std::get<1>(t),
1174  std::get<2>(t));
1175 
1176  for (const auto & t : other_boundary.build_shellface_list())
1177  boundary.add_shellface(std::get<0>(t) + elem_delta,
1178  std::get<1>(t),
1179  std::get<2>(t));
1180 
1181  } // end if (other_mesh)
1182 
1183  // Finally, we need to "merge" the overlapping nodes
1184  // We do this by iterating over node_to_elems_map and updating
1185  // the elements so that they "point" to the nodes that came
1186  // from this mesh, rather than from other_mesh.
1187  // Then we iterate over node_to_node_map and delete the
1188  // duplicate nodes that came from other_mesh.
1189 
1190  {
1191  LOG_SCOPE("stitch_meshes node updates", "ReplicatedMesh");
1192 
1193  // Container to catch boundary IDs passed back from BoundaryInfo.
1194  std::vector<boundary_id_type> bc_ids;
1195 
1196  for (const auto & pr : node_to_elems_map)
1197  {
1198  dof_id_type target_node_id = pr.first;
1199  dof_id_type other_node_id = node_to_node_map[target_node_id];
1200  Node & target_node = this->node_ref(target_node_id);
1201 
1202  std::size_t n_elems = pr.second.size();
1203  for (std::size_t i=0; i<n_elems; i++)
1204  {
1205  dof_id_type elem_id = pr.second[i];
1206  Elem * el = this->elem_ptr(elem_id);
1207 
1208  // find the local node index that we want to update
1209  unsigned int local_node_index = el->local_node(other_node_id);
1210  libmesh_assert_not_equal_to(local_node_index, libMesh::invalid_uint);
1211 
1212  // We also need to copy over the nodeset info here,
1213  // because the node will get deleted below
1214  this->get_boundary_info().boundary_ids(el->node_ptr(local_node_index), bc_ids);
1215  el->set_node(local_node_index) = &target_node;
1216  this->get_boundary_info().add_node(&target_node, bc_ids);
1217  }
1218  }
1219  }
1220 
1221  {
1222  LOG_SCOPE("stitch_meshes node deletion", "ReplicatedMesh");
1223  for (const auto & pr : node_to_node_map)
1224  {
1225  // In the case that this==other_mesh, the two nodes might be the same (e.g. if
1226  // we're stitching a "sliver"), hence we need to skip node deletion in that case.
1227  if ((this == other_mesh) && (pr.second == pr.first))
1228  continue;
1229 
1230  dof_id_type this_node_id = pr.second;
1231  this->delete_node( this->node_ptr(this_node_id) );
1232  }
1233  }
1234 
1235  // If find_neighbors() wasn't called in prepare_for_use(), we need to
1236  // manually loop once more over all elements adjacent to the stitched boundary
1237  // and fix their lists of neighbors.
1238  // This is done according to the following steps:
1239  // 1. Loop over all copied elements adjacent to the boundary using node_to_elems_map (trying to avoid duplicates)
1240  // 2. Look at all their sides with a nullptr neighbor and update them using side_to_elem_map if necessary
1241  // 3. Update the corresponding side in side_to_elem_map as well
1242  if (skip_find_neighbors)
1243  {
1244  LOG_SCOPE("stitch_meshes neighbor fixes", "ReplicatedMesh");
1245 
1246  // Pull objects out of the loop to reduce heap operations
1247  std::unique_ptr<Elem> my_side, their_side;
1248 
1249  std::set<dof_id_type> fixed_elems;
1250  for (const auto & pr : node_to_elems_map)
1251  {
1252  std::size_t n_elems = pr.second.size();
1253  for (std::size_t i=0; i<n_elems; i++)
1254  {
1255  dof_id_type elem_id = pr.second[i];
1256  if (fixed_elems.find(elem_id) == fixed_elems.end())
1257  {
1258  Elem * el = this->elem_ptr(elem_id);
1259  fixed_elems.insert(elem_id);
1260  for (auto s : el->side_index_range())
1261  {
1262  if (el->neighbor_ptr(s) == nullptr)
1263  {
1264  key_type key = el->key(s);
1265  auto bounds = side_to_elem_map.equal_range(key);
1266 
1267  if (bounds.first != bounds.second)
1268  {
1269  // Get the side for this element
1270  el->side_ptr(my_side, s);
1271 
1272  // Look at all the entries with an equivalent key
1273  while (bounds.first != bounds.second)
1274  {
1275  // Get the potential element
1276  Elem * neighbor = bounds.first->second.first;
1277 
1278  // Get the side for the neighboring element
1279  const unsigned int ns = bounds.first->second.second;
1280  neighbor->side_ptr(their_side, ns);
1281  //libmesh_assert(my_side.get());
1282  //libmesh_assert(their_side.get());
1283 
1284  // If found a match with my side
1285  //
1286  // We need special tests here for 1D:
1287  // since parents and children have an equal
1288  // side (i.e. a node), we need to check
1289  // ns != ms, and we also check level() to
1290  // avoid setting our neighbor pointer to
1291  // any of our neighbor's descendants
1292  if ((*my_side == *their_side) &&
1293  (el->level() == neighbor->level()) &&
1294  ((el->dim() != 1) || (ns != s)))
1295  {
1296  // So share a side. Is this a mixed pair
1297  // of subactive and active/ancestor
1298  // elements?
1299  // If not, then we're neighbors.
1300  // If so, then the subactive's neighbor is
1301 
1302  if (el->subactive() ==
1303  neighbor->subactive())
1304  {
1305  // an element is only subactive if it has
1306  // been coarsened but not deleted
1307  el->set_neighbor (s,neighbor);
1308  neighbor->set_neighbor(ns,el);
1309  }
1310  else if (el->subactive())
1311  {
1312  el->set_neighbor(s,neighbor);
1313  }
1314  else if (neighbor->subactive())
1315  {
1316  neighbor->set_neighbor(ns,el);
1317  }
1318  // It's OK to invalidate the
1319  // bounds.first iterator here,
1320  // as we are immediately going
1321  // to break out of this while
1322  // loop. bounds.first will
1323  // therefore not be used for
1324  // anything else.
1325  side_to_elem_map.erase (bounds.first);
1326  break;
1327  }
1328 
1329  ++bounds.first;
1330  }
1331  }
1332  }
1333  }
1334  }
1335  }
1336  }
1337  }
1338 
1339  this->prepare_for_use( /*skip_renumber_nodes_and_elements= */ false, skip_find_neighbors);
1340 
1341  // After the stitching, we may want to clear boundary IDs from element
1342  // faces that are now internal to the mesh
1343  if (clear_stitched_boundary_ids)
1344  {
1345  LOG_SCOPE("stitch_meshes clear bcids", "ReplicatedMesh");
1346 
1347  // Container to catch boundary IDs passed back from BoundaryInfo.
1348  std::vector<boundary_id_type> bc_ids;
1349 
1350  for (auto & el : element_ptr_range())
1351  for (auto side_id : el->side_index_range())
1352  if (el->neighbor_ptr(side_id) != nullptr)
1353  {
1354  // Completely remove the side from the boundary_info object if it has either
1355  // this_mesh_boundary_id or other_mesh_boundary_id.
1356  this->get_boundary_info().boundary_ids (el, side_id, bc_ids);
1357 
1358  if (std::find(bc_ids.begin(), bc_ids.end(), this_mesh_boundary_id) != bc_ids.end() ||
1359  std::find(bc_ids.begin(), bc_ids.end(), other_mesh_boundary_id) != bc_ids.end())
1360  this->get_boundary_info().remove_side(el, side_id);
1361  }
1362 
1363  // Removing stitched-away boundary ids might have removed an id
1364  // *entirely*, so we need to recompute boundary id sets to check
1365  // for that.
1367  }
1368 }
1369 
1370 
1372 {
1373  return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
1374  this->active_elements_end()));
1375 }
1376 
1377 
1378 } // namespace libMesh
virtual void copy_nodes_and_elements(const UnstructuredMesh &other_mesh, const bool skip_find_neighbors=false, dof_id_type element_id_offset=0, dof_id_type node_id_offset=0, unique_id_type unique_id_offset=0)
virtual Elem * add_elem(Elem *e) override
double abs(double a)
unique_id_type & set_unique_id()
Definition: dof_object.h:685
virtual element_iterator active_elements_end() override
Mesh data structure replicated on all processors.
bool _skip_renumber_nodes_and_elements
Definition: mesh_base.h:1431
virtual const Elem * elem(const dof_id_type i) const
Definition: mesh_base.h:537
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2024
A geometric point in (x,y,z) space associated with a DOF.
Definition: node.h:52
const unsigned int invalid_uint
Definition: libmesh.h:245
std::vector< Elem * > _elements
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2166
void stitch_meshes(const ReplicatedMesh &other_mesh, boundary_id_type this_mesh_boundary, boundary_id_type other_mesh_boundary, Real tol=TOLERANCE, bool clear_stitched_boundary_ids=false, bool verbose=true, bool use_binary_search=true, bool enforce_all_nodes_match_on_boundaries=false)
virtual Node * insert_node(Node *n) override
void remove(const Node *node)
unsigned short int side
Definition: xdr_io.C:50
virtual dof_id_type n_nodes() const override
virtual dof_id_type n_elem() const override
The base class for all geometric element types.
Definition: elem.h:100
uint8_t processor_id_type
Definition: id_types.h:99
const Parallel::Communicator & comm() const
static const Real TOLERANCE
IterBase * end
virtual dof_id_type n_active_elem() const override
const BoundaryInfo & get_boundary_info() const
Definition: mesh_base.h:131
long double max(long double a, double b)
virtual dof_id_type max_elem_id() const override
dof_id_type & set_id()
Definition: dof_object.h:664
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
virtual unique_id_type parallel_max_unique_id() const override
std::vector< boundary_id_type > boundary_ids(const Node *node) const
virtual const Elem * query_elem_ptr(const dof_id_type i) const override
unique_id_type _next_unique_id
Definition: mesh_base.h:1418
void build_node_list(std::vector< dof_id_type > &node_id_list, std::vector< boundary_id_type > &bc_id_list) const
void add_node(const Node *node, const boundary_id_type id)
virtual Elem * insert_elem(Elem *e) override
int8_t boundary_id_type
Definition: id_types.h:51
static const boundary_id_type invalid_id
virtual const Elem * elem_ptr(const dof_id_type i) const override
dof_id_type id() const
Definition: dof_object.h:655
virtual Real hmin() const
Definition: elem.C:356
virtual void clear() override
Base class for Replicated and Distributed meshes.
virtual dof_id_type key(const unsigned int s) const =0
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) override
std::unique_ptr< Partitioner > _partitioner
Definition: mesh_base.h:1412
Used by the Mesh to keep track of boundary nodes and elements.
Definition: boundary_info.h:57
SimpleRange< I > as_range(const std::pair< I, I > &p)
Definition: simple_range.h:57
static const dof_id_type invalid_id
Definition: dof_object.h:347
std::vector< Node * > _nodes
virtual void delete_node(Node *n) override
void prepare_for_use(const bool skip_renumber_nodes_and_elements=false, const bool skip_find_neighbors=false)
Definition: mesh_base.C:152
bool valid_unique_id() const
Definition: dof_object.h:705
bool absolute_fuzzy_equals(const TypeVector< T > &rhs, Real tol=TOLERANCE) const
Definition: type_vector.h:965
void set_neighbor(const unsigned int i, Elem *n)
Definition: elem.h:2083
void build_shellface_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &shellface_list, std::vector< boundary_id_type > &bc_id_list) const
virtual void clear()
Definition: mesh_base.C:260
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id) override
virtual void renumber_nodes_and_elements() override
virtual const Point & point(const dof_id_type i) const override
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id) override
virtual const Node & node(const dof_id_type i) const
Definition: mesh_base.h:454
SimpleRange< NodeRefIter > node_ref_range()
Definition: elem.h:2130
virtual Node * add_node(Node *n) override
static std::unique_ptr< Node > build(const Node &n)
Definition: node.h:304
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2050
void remove_side(const Elem *elem, const unsigned short int side)
virtual dof_id_type max_node_id() const override
unsigned int level() const
Definition: elem.h:2521
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void update_parallel_id_counts() override
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual void fix_broken_node_and_element_numbering() override
bool valid_id() const
Definition: dof_object.h:697
virtual unsigned short dim() const =0
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:1957
virtual const Node * query_node_ptr(const dof_id_type i) const override
void build_edge_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &edge_list, std::vector< boundary_id_type > &bc_id_list) const
void swap(Iterator &lhs, Iterator &rhs)
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
void add_shellface(const dof_id_type elem, const unsigned short int shellface, const boundary_id_type id)
void stitch_surfaces(boundary_id_type boundary_id_1, boundary_id_type boundary_id_2, Real tol=TOLERANCE, bool clear_stitched_boundary_ids=false, bool verbose=true, bool use_binary_search=true, bool enforce_all_nodes_match_on_boundaries=false)
bool subactive() const
Definition: elem.h:2408
ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T &value)
Definition: utility.h:135
virtual const Node * node_ptr(const dof_id_type i) const override
void stitching_helper(const ReplicatedMesh *other_mesh, boundary_id_type boundary_id_1, boundary_id_type boundary_id_2, Real tol, bool clear_stitched_boundary_ids, bool verbose, bool use_binary_search, bool enforce_all_nodes_match_on_boundaries, bool skip_find_neighbors)
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:434
unsigned int local_node(const dof_id_type i) const
Definition: elem.h:1937
ReplicatedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
virtual void delete_elem(Elem *e) override
OStreamProxy out(std::cout)
virtual element_iterator active_elements_begin() override
processor_id_type processor_id() const
Definition: dof_object.h:717
long double min(long double a, double b)
A geometric point in (x,y,z) space.
Definition: point.h:38
uint8_t unique_id_type
Definition: id_types.h:79
uint8_t dof_id_type
Definition: id_types.h:64
void add_edge(const dof_id_type elem, const unsigned short int edge, const boundary_id_type id)
std::vector< boundary_id_type > edge_boundary_ids(const Elem *const elem, const unsigned short int edge) const
virtual SimpleRange< element_iterator > element_ptr_range() override