libMesh
replicated_mesh.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2017 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"
23 #include "libmesh/libmesh_logging.h"
24 #include "libmesh/metis_partitioner.h"
25 #include "libmesh/replicated_mesh.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
129 }
130 
131 
132 
133 #ifndef LIBMESH_DISABLE_COMMWORLD
134 #ifdef LIBMESH_ENABLE_DEPRECATED
136  UnstructuredMesh (d)
137 {
138  libmesh_deprecated();
139 #ifdef LIBMESH_ENABLE_UNIQUE_ID
140  // In serial we just need to reset the next unique id to zero
141  // here in the constructor.
142  _next_unique_id = 0;
143 #endif
145 }
146 #endif
147 #endif
148 
149 
151 {
152  this->clear(); // Free nodes and elements
153 }
154 
155 
156 // This might be specialized later, but right now it's just here to
157 // make sure the compiler doesn't give us a default (non-deep) copy
158 // constructor instead.
160  UnstructuredMesh (other_mesh)
161 {
162  this->copy_nodes_and_elements(other_mesh);
163  this->get_boundary_info() = other_mesh.get_boundary_info();
164 #ifdef LIBMESH_ENABLE_UNIQUE_ID
165  this->_next_unique_id = other_mesh._next_unique_id;
166 #endif
167 }
168 
169 
171  UnstructuredMesh (other_mesh)
172 {
173  this->copy_nodes_and_elements(other_mesh);
174  this->get_boundary_info() = other_mesh.get_boundary_info();
175 }
176 
177 
178 const Point & ReplicatedMesh::point (const dof_id_type i) const
179 {
180  return this->node_ref(i);
181 }
182 
183 
184 
185 
187 {
188  libmesh_assert_less (i, this->n_nodes());
190  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
191 
192  return _nodes[i];
193 }
194 
195 
196 
197 
199 {
200  libmesh_assert_less (i, this->n_nodes());
202  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
203 
204  return _nodes[i];
205 }
206 
207 
208 
209 
211 {
212  if (i >= this->n_nodes())
213  return libmesh_nullptr;
215  _nodes[i]->id() == i); // This will change soon
216 
217  return _nodes[i];
218 }
219 
220 
221 
222 
224 {
225  if (i >= this->n_nodes())
226  return libmesh_nullptr;
228  _nodes[i]->id() == i); // This will change soon
229 
230  return _nodes[i];
231 }
232 
233 
234 
235 
237 {
238  libmesh_assert_less (i, this->n_elem());
240  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
241 
242  return _elements[i];
243 }
244 
245 
246 
247 
249 {
250  libmesh_assert_less (i, this->n_elem());
252  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
253 
254  return _elements[i];
255 }
256 
257 
258 
259 
261 {
262  if (i >= this->n_elem())
263  return libmesh_nullptr;
265  _elements[i]->id() == i); // This will change soon
266 
267  return _elements[i];
268 }
269 
270 
271 
272 
274 {
275  if (i >= this->n_elem())
276  return libmesh_nullptr;
278  _elements[i]->id() == i); // This will change soon
279 
280  return _elements[i];
281 }
282 
283 
284 
285 
287 {
288  libmesh_assert(e);
289 
290  // We no longer merely append elements with ReplicatedMesh
291 
292  // If the user requests a valid id that doesn't correspond to an
293  // existing element, let's give them that id, resizing the elements
294  // container if necessary.
295  if (!e->valid_id())
296  e->set_id (cast_int<dof_id_type>(_elements.size()));
297 
298 #ifdef LIBMESH_ENABLE_UNIQUE_ID
299  if (!e->valid_unique_id())
301 #endif
302 
303  const dof_id_type id = e->id();
304 
305  if (id < _elements.size())
306  {
307  // Overwriting existing elements is still probably a mistake.
309  }
310  else
311  {
312  _elements.resize(id+1, libmesh_nullptr);
313  }
314 
315  _elements[id] = e;
316 
317  return e;
318 }
319 
320 
321 
323 {
324 #ifdef LIBMESH_ENABLE_UNIQUE_ID
325  if (!e->valid_unique_id())
327 #endif
328 
329  dof_id_type eid = e->id();
330  libmesh_assert_less (eid, _elements.size());
331  Elem * oldelem = _elements[eid];
332 
333  if (oldelem)
334  {
335  libmesh_assert_equal_to (oldelem->id(), eid);
336  this->delete_elem(oldelem);
337  }
338 
339  _elements[e->id()] = e;
340 
341  return e;
342 }
343 
344 
345 
347 {
348  libmesh_assert(e);
349 
350  // Initialize an iterator to eventually point to the element we want to delete
351  std::vector<Elem *>::iterator pos = _elements.end();
352 
353  // In many cases, e->id() gives us a clue as to where e
354  // is located in the _elements vector. Try that first
355  // before trying the O(n_elem) search.
356  libmesh_assert_less (e->id(), _elements.size());
357 
358  if (_elements[e->id()] == e)
359  {
360  // We found it!
361  pos = _elements.begin();
362  std::advance(pos, e->id());
363  }
364 
365  else
366  {
367  // This search is O(n_elem)
368  pos = std::find (_elements.begin(),
369  _elements.end(),
370  e);
371  }
372 
373  // Huh? Element not in the vector?
374  libmesh_assert (pos != _elements.end());
375 
376  // Remove the element from the BoundaryInfo object
377  this->get_boundary_info().remove(e);
378 
379  // delete the element
380  delete e;
381 
382  // explicitly NULL the pointer
383  *pos = libmesh_nullptr;
384 }
385 
386 
387 
389  const dof_id_type new_id)
390 {
391  // This doesn't get used in serial yet
392  Elem * el = _elements[old_id];
393  libmesh_assert (el);
394 
395  el->set_id(new_id);
396  libmesh_assert (!_elements[new_id]);
397  _elements[new_id] = el;
398  _elements[old_id] = libmesh_nullptr;
399 }
400 
401 
402 
404  const dof_id_type id,
405  const processor_id_type proc_id)
406 {
407  // // We only append points with ReplicatedMesh
408  // libmesh_assert(id == DofObject::invalid_id || id == _nodes.size());
409  // Node *n = Node::build(p, _nodes.size()).release();
410  // n->processor_id() = proc_id;
411  // _nodes.push_back (n);
412 
413  Node * n = libmesh_nullptr;
414 
415  // If the user requests a valid id, either
416  // provide the existing node or resize the container
417  // to fit the new node.
418  if (id != DofObject::invalid_id)
419  if (id < _nodes.size())
420  n = _nodes[id];
421  else
422  _nodes.resize(id+1);
423  else
424  _nodes.push_back (static_cast<Node *>(libmesh_nullptr));
425 
426  // if the node already exists, then assign new (x,y,z) values
427  if (n)
428  *n = p;
429  // otherwise build a new node, put it in the right spot, and return
430  // a valid pointer.
431  else
432  {
433  n = Node::build(p, (id == DofObject::invalid_id) ?
434  cast_int<dof_id_type>(_nodes.size()-1) : id).release();
435  n->processor_id() = proc_id;
436 
437 #ifdef LIBMESH_ENABLE_UNIQUE_ID
438  if (!n->valid_unique_id())
440 #endif
441 
442  if (id == DofObject::invalid_id)
443  _nodes.back() = n;
444  else
445  _nodes[id] = n;
446  }
447 
448  // better not pass back a NULL pointer.
449  libmesh_assert (n);
450 
451  return n;
452 }
453 
454 
455 
457 {
458  libmesh_assert(n);
459  // We only append points with ReplicatedMesh
460  libmesh_assert(!n->valid_id() || n->id() == _nodes.size());
461 
462  n->set_id (cast_int<dof_id_type>(_nodes.size()));
463 
464 #ifdef LIBMESH_ENABLE_UNIQUE_ID
465  if (!n->valid_unique_id())
467 #endif
468 
469  _nodes.push_back(n);
470 
471  return n;
472 }
473 
474 
475 
477 {
478  if (!n)
479  libmesh_error_msg("Error, attempting to insert NULL node.");
480 
481  if (n->id() == DofObject::invalid_id)
482  libmesh_error_msg("Error, cannot insert node with invalid id.");
483 
484  if (n->id() < _nodes.size())
485  {
486  // Don't allow inserting on top of an existing Node.
487 
488  // Doing so doesn't have to be *error*, in the case where a
489  // redundant insert is done, but when that happens we ought to
490  // always be able to make the code more efficient by avoiding
491  // the redundant insert, so let's keep screaming "Error" here.
492  if (_nodes[ n->id() ] != libmesh_nullptr)
493  libmesh_error_msg("Error, cannot insert node on top of existing node.");
494  }
495  else
496  {
497  // Allocate just enough space to store the new node. This will
498  // cause highly non-ideal memory allocation behavior if called
499  // repeatedly...
500  _nodes.resize(n->id() + 1);
501  }
502 
503 #ifdef LIBMESH_ENABLE_UNIQUE_ID
504  if (!n->valid_unique_id())
506 #endif
507 
508  // We have enough space and this spot isn't already occupied by
509  // another node, so go ahead and add it.
510  _nodes[ n->id() ] = n;
511 
512  // If we made it this far, we just inserted the node the user handed
513  // us, so we can give it right back.
514  return n;
515 }
516 
517 
518 
520 {
521  libmesh_assert(n);
522  libmesh_assert_less (n->id(), _nodes.size());
523 
524  // Initialize an iterator to eventually point to the element we want
525  // to delete
526  std::vector<Node *>::iterator pos;
527 
528  // In many cases, e->id() gives us a clue as to where e
529  // is located in the _elements vector. Try that first
530  // before trying the O(n_elem) search.
531  if (_nodes[n->id()] == n)
532  {
533  pos = _nodes.begin();
534  std::advance(pos, n->id());
535  }
536  else
537  {
538  pos = std::find (_nodes.begin(),
539  _nodes.end(),
540  n);
541  }
542 
543  // Huh? Node not in the vector?
544  libmesh_assert (pos != _nodes.end());
545 
546  // Delete the node from the BoundaryInfo object
547  this->get_boundary_info().remove(n);
548 
549  // delete the node
550  delete n;
551 
552  // explicitly NULL the pointer
553  *pos = libmesh_nullptr;
554 }
555 
556 
557 
559  const dof_id_type new_id)
560 {
561  // This doesn't get used in serial yet
562  Node * nd = _nodes[old_id];
563  libmesh_assert (nd);
564 
565  nd->set_id(new_id);
566  libmesh_assert (!_nodes[new_id]);
567  _nodes[new_id] = nd;
568  _nodes[old_id] = libmesh_nullptr;
569 }
570 
571 
572 
574 {
575  // Call parent clear function
576  MeshBase::clear();
577 
578 
579  // Clear our elements and nodes
580  {
581  std::vector<Elem *>::iterator it = _elements.begin();
582  const std::vector<Elem *>::iterator end = _elements.end();
583 
584  // There is no need to remove the elements from
585  // the BoundaryInfo data structure since we
586  // already cleared it.
587  for (; it != end; ++it)
588  delete *it;
589 
590  _elements.clear();
591  }
592 
593  // clear the nodes data structure
594  {
595  std::vector<Node *>::iterator it = _nodes.begin();
596  const std::vector<Node *>::iterator end = _nodes.end();
597 
598  // There is no need to remove the nodes from
599  // the BoundaryInfo data structure since we
600  // already cleared it.
601  for (; it != end; ++it)
602  delete *it;
603 
604  _nodes.clear();
605  }
606 }
607 
608 
609 
611 {
612 #ifdef LIBMESH_ENABLE_UNIQUE_ID
614 #endif
615 }
616 
617 
618 
619 #ifdef LIBMESH_ENABLE_UNIQUE_ID
621 {
622  // This function must be run on all processors at once
623  parallel_object_only();
624 
625  unique_id_type max_local = _next_unique_id;
626  this->comm().max(max_local);
627  return max_local;
628 }
629 #endif
630 
631 
632 
634 {
635  LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
636 
637  // node and element id counters
638  dof_id_type next_free_elem = 0;
639  dof_id_type next_free_node = 0;
640 
641  // Will hold the set of nodes that are currently connected to elements
642  std::unordered_set<Node *> connected_nodes;
643 
644  // Loop over the elements. Note that there may
645  // be NULLs in the _elements vector from the coarsening
646  // process. Pack the elements in to a contiguous array
647  // and then trim any excess.
648  {
649  std::vector<Elem *>::iterator in = _elements.begin();
650  std::vector<Elem *>::iterator out_iter = _elements.begin();
651  const std::vector<Elem *>::iterator end = _elements.end();
652 
653  for (; in != end; ++in)
654  if (*in != libmesh_nullptr)
655  {
656  Elem * el = *in;
657 
658  *out_iter = *in;
659  ++out_iter;
660 
661  // Increment the element counter
662  el->set_id (next_free_elem++);
663 
665  {
666  // Add this elements nodes to the connected list
667  for (auto & n : el->node_ref_range())
668  connected_nodes.insert(&n);
669  }
670  else // We DO want node renumbering
671  {
672  // Loop over this element's nodes. Number them,
673  // if they have not been numbered already. Also,
674  // position them in the _nodes vector so that they
675  // are packed contiguously from the beginning.
676  for (auto & n : el->node_ref_range())
677  if (n.id() == next_free_node) // don't need to process
678  next_free_node++; // [(src == dst) below]
679 
680  else if (n.id() > next_free_node) // need to process
681  {
682  // The source and destination indices
683  // for this node
684  const dof_id_type src_idx = n.id();
685  const dof_id_type dst_idx = next_free_node++;
686 
687  // ensure we want to swap a valid nodes
688  libmesh_assert(_nodes[src_idx]);
689 
690  // Swap the source and destination nodes
691  std::swap(_nodes[src_idx],
692  _nodes[dst_idx] );
693 
694  // Set proper indices where that makes sense
695  if (_nodes[src_idx] != libmesh_nullptr)
696  _nodes[src_idx]->set_id (src_idx);
697  _nodes[dst_idx]->set_id (dst_idx);
698  }
699  }
700  }
701 
702  // Erase any additional storage. These elements have been
703  // copied into NULL voids by the procedure above, and are
704  // thus repeated and unnecessary.
705  _elements.erase (out_iter, end);
706  }
707 
708 
710  {
711  // Loop over the nodes. Note that there may
712  // be NULLs in the _nodes vector from the coarsening
713  // process. Pack the nodes in to a contiguous array
714  // and then trim any excess.
715 
716  std::vector<Node *>::iterator in = _nodes.begin();
717  std::vector<Node *>::iterator out_iter = _nodes.begin();
718  const std::vector<Node *>::iterator end = _nodes.end();
719 
720  for (; in != end; ++in)
721  if (*in != libmesh_nullptr)
722  {
723  // This is a reference so that if we change the pointer it will change in the vector
724  Node * & nd = *in;
725 
726  // If this node is still connected to an elem, put it in the list
727  if (connected_nodes.find(nd) != connected_nodes.end())
728  {
729  *out_iter = nd;
730  ++out_iter;
731 
732  // Increment the node counter
733  nd->set_id (next_free_node++);
734  }
735  else // This node is orphaned, delete it!
736  {
737  this->get_boundary_info().remove (nd);
738 
739  // delete the node
740  delete nd;
741  nd = libmesh_nullptr;
742  }
743  }
744 
745  // Erase any additional storage. Whatever was
746  _nodes.erase (out_iter, end);
747  }
748  else // We really DO want node renumbering
749  {
750  // Any nodes in the vector >= _nodes[next_free_node]
751  // are not connected to any elements and may be deleted
752  // if desired.
753 
754  // (This code block will erase the unused nodes)
755  // Now, delete the unused nodes
756  {
757  std::vector<Node *>::iterator nd = _nodes.begin();
758  const std::vector<Node *>::iterator end = _nodes.end();
759 
760  std::advance (nd, next_free_node);
761 
762  for (std::vector<Node *>::iterator it=nd;
763  it != end; ++it)
764  {
765  // Mesh modification code might have already deleted some
766  // nodes
767  if (*it == libmesh_nullptr)
768  continue;
769 
770  // remove any boundary information associated with
771  // this node
772  this->get_boundary_info().remove (*it);
773 
774  // delete the node
775  delete *it;
776  *it = libmesh_nullptr;
777  }
778 
779  _nodes.erase (nd, end);
780  }
781  }
782 
783  libmesh_assert_equal_to (next_free_elem, _elements.size());
784  libmesh_assert_equal_to (next_free_node, _nodes.size());
785 
787 }
788 
789 
790 
792 {
793  // Nodes first
794  for (std::size_t n=0; n<this->_nodes.size(); n++)
795  if (this->_nodes[n] != libmesh_nullptr)
796  this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
797 
798  // Elements next
799  for (std::size_t e=0; e<this->_elements.size(); e++)
800  if (this->_elements[e] != libmesh_nullptr)
801  this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
802 }
803 
804 
806  boundary_id_type this_mesh_boundary_id,
807  boundary_id_type other_mesh_boundary_id,
808  Real tol,
809  bool clear_stitched_boundary_ids,
810  bool verbose,
811  bool use_binary_search,
812  bool enforce_all_nodes_match_on_boundaries)
813 {
814  LOG_SCOPE("stitch_meshes()", "ReplicatedMesh");
815  stitching_helper(&other_mesh,
816  this_mesh_boundary_id,
817  other_mesh_boundary_id,
818  tol,
819  clear_stitched_boundary_ids,
820  verbose,
821  use_binary_search,
822  enforce_all_nodes_match_on_boundaries,
823  true);
824 }
825 
827  boundary_id_type boundary_id_2,
828  Real tol,
829  bool clear_stitched_boundary_ids,
830  bool verbose,
831  bool use_binary_search,
832  bool enforce_all_nodes_match_on_boundaries)
833 {
835  boundary_id_1,
836  boundary_id_2,
837  tol,
838  clear_stitched_boundary_ids,
839  verbose,
840  use_binary_search,
841  enforce_all_nodes_match_on_boundaries,
842  true);
843 }
844 
846  boundary_id_type this_mesh_boundary_id,
847  boundary_id_type other_mesh_boundary_id,
848  Real tol,
849  bool clear_stitched_boundary_ids,
850  bool verbose,
851  bool use_binary_search,
852  bool enforce_all_nodes_match_on_boundaries,
853  bool skip_find_neighbors)
854 {
855  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
856  std::map<dof_id_type, std::vector<dof_id_type>> node_to_elems_map;
857 
858  typedef dof_id_type key_type;
859  typedef std::pair<Elem *, unsigned char> val_type;
860  typedef std::pair<key_type, val_type> key_val_pair;
861  typedef std::unordered_multimap<key_type, val_type> map_type;
862  // Mapping between all side keys in this mesh and elements+side numbers relevant to the boundary in this mesh as well.
863  map_type side_to_elem_map;
864 
865  // If there is only one mesh (i.e. other_mesh==NULL), then loop over this mesh twice
866  if (!other_mesh)
867  {
868  other_mesh = this;
869  }
870 
871  if ((this_mesh_boundary_id != BoundaryInfo::invalid_id) &&
872  (other_mesh_boundary_id != BoundaryInfo::invalid_id))
873  {
874  LOG_SCOPE("stitch_meshes node merging", "ReplicatedMesh");
875 
876  // While finding nodes on the boundary, also find the minimum edge length
877  // of all faces on both boundaries. This will later be used in relative
878  // distance checks when stitching nodes.
880  bool h_min_updated = false;
881 
882  // Loop below fills in these sets for the two meshes.
883  std::set<dof_id_type> this_boundary_node_ids, other_boundary_node_ids;
884  {
885  // Make temporary fixed-size arrays for loop
886  boundary_id_type id_array[2] = {this_mesh_boundary_id, other_mesh_boundary_id};
887  std::set<dof_id_type> * set_array[2] = {&this_boundary_node_ids, &other_boundary_node_ids};
888  ReplicatedMesh * mesh_array[2] = {this, other_mesh};
889 
890  for (unsigned i=0; i<2; ++i)
891  {
892  // First we deal with node boundary IDs.
893  // We only enter this loop if we have at least one
894  // nodeset.
895  if (mesh_array[i]->get_boundary_info().n_nodeset_conds() > 0)
896  {
897  std::vector<numeric_index_type> node_id_list;
898  std::vector<boundary_id_type> bc_id_list;
899 
900  // Get the list of nodes with associated boundary IDs
901  mesh_array[i]->get_boundary_info().build_node_list(node_id_list, bc_id_list);
902 
903  for (std::size_t node_index=0; node_index<bc_id_list.size(); node_index++)
904  {
905  boundary_id_type node_bc_id = bc_id_list[node_index];
906  if (node_bc_id == id_array[i])
907  {
908  dof_id_type this_node_id = node_id_list[node_index];
909  set_array[i]->insert( this_node_id );
910 
911  // We need to set h_min to some value. It's too expensive to
912  // search for the element that actually contains this node,
913  // since that would require a PointLocator. As a result, we
914  // just use the first element in the mesh to give us hmin.
915  const Elem * first_active_elem = *mesh_array[i]->active_elements_begin();
916  h_min = first_active_elem->hmin();
917  h_min_updated = true;
918  }
919  }
920  }
921 
922  // Container to catch boundary IDs passed back from BoundaryInfo.
923  std::vector<boundary_id_type> bc_ids;
924 
925  MeshBase::element_iterator elem_it = mesh_array[i]->elements_begin();
926  MeshBase::element_iterator elem_end = mesh_array[i]->elements_end();
927  for ( ; elem_it != elem_end; ++elem_it)
928  {
929  Elem * el = *elem_it;
930 
931  // Now check whether elem has a face on the specified boundary
932  for (auto side_id : el->side_index_range())
933  if (el->neighbor_ptr(side_id) == libmesh_nullptr)
934  {
935  // Get *all* boundary IDs on this side, not just the first one!
936  mesh_array[i]->get_boundary_info().boundary_ids (el, side_id, bc_ids);
937 
938  if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
939  {
940  UniquePtr<Elem> side (el->build_side_ptr(side_id));
941  for (auto & n : side->node_ref_range())
942  set_array[i]->insert(n.id());
943 
944  h_min = std::min(h_min, side->hmin());
945  h_min_updated = true;
946 
947  // This side is on the boundary, add its information to side_to_elem
948  if (skip_find_neighbors && (i==0))
949  {
950  key_type key = el->key(side_id);
951  val_type val;
952  val.first = el;
953  val.second = side_id;
954 
955  key_val_pair kvp;
956  kvp.first = key;
957  kvp.second = val;
958  side_to_elem_map.insert (kvp);
959  }
960  }
961 
962  // Also, check the edges on this side. We don't have to worry about
963  // updating neighbor info in this case since elements don't store
964  // neighbor info on edges.
965  for (auto edge_id : el->edge_index_range())
966  {
967  if (el->is_edge_on_side(edge_id, side_id))
968  {
969  // Get *all* boundary IDs on this edge, not just the first one!
970  mesh_array[i]->get_boundary_info().edge_boundary_ids (el, edge_id, bc_ids);
971 
972  if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
973  {
974  UniquePtr<Elem> edge (el->build_edge_ptr(edge_id));
975  for (auto & n : edge->node_ref_range())
976  set_array[i]->insert( n.id() );
977 
978  h_min = std::min(h_min, edge->hmin());
979  h_min_updated = true;
980  }
981  }
982  }
983  }
984  }
985  }
986  }
987 
988  if (verbose)
989  {
990  libMesh::out << "In ReplicatedMesh::stitch_meshes:\n"
991  << "This mesh has " << this_boundary_node_ids.size()
992  << " nodes on boundary " << this_mesh_boundary_id << ".\n"
993  << "Other mesh has " << other_boundary_node_ids.size()
994  << " nodes on boundary " << other_mesh_boundary_id << ".\n";
995 
996  if (h_min_updated)
997  {
998  libMesh::out << "Minimum edge length on both surfaces is " << h_min << ".\n";
999  }
1000  else
1001  {
1002  libMesh::out << "No elements on specified surfaces." << std::endl;
1003  }
1004  }
1005 
1006 
1007  if (use_binary_search)
1008  {
1009  // Store points from both stitched faces in sorted vectors for faster
1010  // searching later.
1011  typedef std::vector<std::pair<Point, dof_id_type>> PointVector;
1012  PointVector
1013  this_sorted_bndry_nodes(this_boundary_node_ids.size()),
1014  other_sorted_bndry_nodes(other_boundary_node_ids.size());
1015 
1016  // Comparison object that will be used later. So far, I've had reasonable success
1017  // with TOLERANCE...
1018  FuzzyPointCompare mein_comp(TOLERANCE);
1019 
1020  // Create and sort the vectors we will use to do the geometric searching
1021  {
1022  std::set<dof_id_type> * set_array[2] = {&this_boundary_node_ids, &other_boundary_node_ids};
1023  ReplicatedMesh * mesh_array[2] = {this, other_mesh};
1024  PointVector * vec_array[2] = {&this_sorted_bndry_nodes, &other_sorted_bndry_nodes};
1025 
1026  for (unsigned i=0; i<2; ++i)
1027  {
1028  std::set<dof_id_type>::iterator
1029  set_it = set_array[i]->begin(),
1030  set_it_end = set_array[i]->end();
1031 
1032  // Fill up the vector with the contents of the set...
1033  for (unsigned ctr=0; set_it != set_it_end; ++set_it, ++ctr)
1034  {
1035  (*vec_array[i])[ctr] = std::make_pair(mesh_array[i]->point(*set_it), // The geometric point
1036  *set_it); // Its ID
1037  }
1038 
1039  // Sort the vectors based on the FuzzyPointCompare struct op()
1040  std::sort(vec_array[i]->begin(), vec_array[i]->end(), mein_comp);
1041  }
1042  }
1043 
1044  // Build up the node_to_node_map and node_to_elems_map using the sorted vectors of Points.
1045  for (std::size_t i=0; i<this_sorted_bndry_nodes.size(); ++i)
1046  {
1047  // Current point we're working on
1048  Point this_point = this_sorted_bndry_nodes[i].first;
1049 
1050  // FuzzyPointCompare does a fuzzy equality comparison internally to handle
1051  // slight differences between the list of nodes on each mesh.
1052  PointVector::iterator other_iter = Utility::binary_find(other_sorted_bndry_nodes.begin(),
1053  other_sorted_bndry_nodes.end(),
1054  this_point,
1055  mein_comp);
1056 
1057  // Not every node on this_sorted_bndry_nodes will necessarily be stitched, so
1058  // if its pair is not found on other_mesh, just continue.
1059  if (other_iter != other_sorted_bndry_nodes.end())
1060  {
1061  // Check that the points do indeed match - should not be necessary unless something
1062  // is wrong with binary_find. To be on the safe side, we'll check.
1063  {
1064  // Grab the other point from the iterator
1065  Point other_point = other_iter->first;
1066 
1067  if (!this_point.absolute_fuzzy_equals(other_point, tol*h_min))
1068  libmesh_error_msg("Error: mismatched points: " << this_point << " and " << other_point);
1069  }
1070 
1071 
1072  // Associate these two nodes in both the node_to_node_map and the other_to_this_node_map
1073  dof_id_type
1074  this_node_id = this_sorted_bndry_nodes[i].second,
1075  other_node_id = other_iter->second;
1076  node_to_node_map[this_node_id] = other_node_id;
1077  other_to_this_node_map[other_node_id] = this_node_id;
1078  }
1079 
1080  }
1081  }
1082  else
1083  {
1084  // Otherwise, use a simple N^2 search to find the closest matching points. This can be helpful
1085  // in the case that we have tolerance issues which cause mismatch between the two surfaces
1086  // that are being stitched.
1087 
1088  std::set<dof_id_type>::iterator set_it = this_boundary_node_ids.begin();
1089  std::set<dof_id_type>::iterator set_it_end = this_boundary_node_ids.end();
1090  for ( ; set_it != set_it_end; ++set_it)
1091  {
1092  dof_id_type this_node_id = *set_it;
1093  Node & this_node = this->node_ref(this_node_id);
1094 
1095  bool found_matching_nodes = false;
1096 
1097  std::set<dof_id_type>::iterator other_set_it = other_boundary_node_ids.begin();
1098  std::set<dof_id_type>::iterator other_set_it_end = other_boundary_node_ids.end();
1099  for ( ; other_set_it != other_set_it_end; ++other_set_it)
1100  {
1101  dof_id_type other_node_id = *other_set_it;
1102  Node & other_node = other_mesh->node_ref(other_node_id);
1103 
1104  Real node_distance = (this_node - other_node).norm();
1105 
1106  if (node_distance < tol*h_min)
1107  {
1108  // Make sure we didn't already find a matching node!
1109  if (found_matching_nodes)
1110  libmesh_error_msg("Error: Found multiple matching nodes in stitch_meshes");
1111 
1112  node_to_node_map[this_node_id] = other_node_id;
1113  other_to_this_node_map[other_node_id] = this_node_id;
1114 
1115  found_matching_nodes = true;
1116  }
1117  }
1118  }
1119  }
1120 
1121  // Build up the node_to_elems_map, using only one loop over other_mesh
1122  {
1123  MeshBase::element_iterator other_elem_it = other_mesh->elements_begin();
1124  MeshBase::element_iterator other_elem_end = other_mesh->elements_end();
1125  for (; other_elem_it != other_elem_end; ++other_elem_it)
1126  {
1127  Elem * el = *other_elem_it;
1128 
1129  // For each node on the element, find the corresponding node
1130  // on "this" Mesh, 'this_node_id', if it exists, and push
1131  // the current element ID back onto node_to_elems_map[this_node_id].
1132  // For that we will use the reverse mapping we created at
1133  // the same time as the forward mapping.
1134  for (auto & n : el->node_ref_range())
1135  {
1136  dof_id_type other_node_id = n.id();
1137  std::map<dof_id_type, dof_id_type>::iterator it =
1138  other_to_this_node_map.find(other_node_id);
1139 
1140  if (it != other_to_this_node_map.end())
1141  {
1142  dof_id_type this_node_id = it->second;
1143  node_to_elems_map[this_node_id].push_back( el->id() );
1144  }
1145  }
1146  }
1147  }
1148 
1149  if (verbose)
1150  {
1151  libMesh::out << "In ReplicatedMesh::stitch_meshes:\n"
1152  << "Found " << node_to_node_map.size()
1153  << " matching nodes.\n"
1154  << std::endl;
1155  }
1156 
1157  if (enforce_all_nodes_match_on_boundaries)
1158  {
1159  std::size_t n_matching_nodes = node_to_node_map.size();
1160  std::size_t this_mesh_n_nodes = this_boundary_node_ids.size();
1161  std::size_t other_mesh_n_nodes = other_boundary_node_ids.size();
1162  if ((n_matching_nodes != this_mesh_n_nodes) || (n_matching_nodes != other_mesh_n_nodes))
1163  libmesh_error_msg("Error: We expected the number of nodes to match.");
1164  }
1165  }
1166  else
1167  {
1168  if (verbose)
1169  {
1170  libMesh::out << "Skip node merging in ReplicatedMesh::stitch_meshes:" << std::endl;
1171  }
1172  }
1173 
1174 
1175 
1176  dof_id_type node_delta = this->max_node_id();
1177  dof_id_type elem_delta = this->max_elem_id();
1178 
1179  // If other_mesh!=NULL, then we have to do a bunch of work
1180  // in order to copy it to this mesh
1181  if (this!=other_mesh)
1182  {
1183  LOG_SCOPE("stitch_meshes copying", "ReplicatedMesh");
1184 
1185  // need to increment node and element IDs of other_mesh before copying to this mesh
1186  MeshBase::node_iterator node_it = other_mesh->nodes_begin();
1187  MeshBase::node_iterator node_end = other_mesh->nodes_end();
1188  for (; node_it != node_end; ++node_it)
1189  {
1190  Node * nd = *node_it;
1191  dof_id_type new_id = nd->id() + node_delta;
1192  nd->set_id(new_id);
1193  }
1194 
1195  MeshBase::element_iterator elem_it = other_mesh->elements_begin();
1196  MeshBase::element_iterator elem_end = other_mesh->elements_end();
1197  for (; elem_it != elem_end; ++elem_it)
1198  {
1199  Elem * el = *elem_it;
1200  dof_id_type new_id = el->id() + elem_delta;
1201  el->set_id(new_id);
1202  }
1203 
1204  // Also, increment the node_to_node_map and node_to_elems_map
1205  std::map<dof_id_type, dof_id_type>::iterator node_map_it = node_to_node_map.begin();
1206  std::map<dof_id_type, dof_id_type>::iterator node_map_it_end = node_to_node_map.end();
1207  for ( ; node_map_it != node_map_it_end; ++node_map_it)
1208  {
1209  node_map_it->second += node_delta;
1210  }
1211  std::map<dof_id_type, std::vector<dof_id_type>>::iterator elem_map_it = node_to_elems_map.begin();
1212  std::map<dof_id_type, std::vector<dof_id_type>>::iterator elem_map_it_end = node_to_elems_map.end();
1213  for ( ; elem_map_it != elem_map_it_end; ++elem_map_it)
1214  {
1215  std::size_t n_elems = elem_map_it->second.size();
1216  for (std::size_t i=0; i<n_elems; i++)
1217  {
1218  (elem_map_it->second)[i] += elem_delta;
1219  }
1220  }
1221 
1222  // Copy mesh data. If we skip the call to find_neighbors(), the lists
1223  // of neighbors will be copied verbatim from the other mesh
1224  this->copy_nodes_and_elements(*other_mesh, skip_find_neighbors);
1225 
1226  // Decrement node IDs of mesh to return to original state
1227  node_it = other_mesh->nodes_begin();
1228  node_end = other_mesh->nodes_end();
1229  for (; node_it != node_end; ++node_it)
1230  {
1231  Node * nd = *node_it;
1232  dof_id_type new_id = nd->id() - node_delta;
1233  nd->set_id(new_id);
1234  }
1235 
1236  // Container to catch boundary IDs passed back from BoundaryInfo.
1237  std::vector<boundary_id_type> bc_ids;
1238 
1239  elem_it = other_mesh->elements_begin();
1240  elem_end = other_mesh->elements_end();
1241  for (; elem_it != elem_end; ++elem_it)
1242  {
1243  Elem * other_elem = *elem_it;
1244 
1245  // Decrement elem IDs of other_mesh to return it to original state
1246  dof_id_type new_id = other_elem->id() - elem_delta;
1247  other_elem->set_id(new_id);
1248  }
1249 
1250  // Copy BoundaryInfo from other_mesh too. We do this via the
1251  // list APIs rather than element-by-element for speed.
1252  BoundaryInfo & boundary = this->get_boundary_info();
1253  const BoundaryInfo & other_boundary = other_mesh->get_boundary_info();
1254 
1255  {
1256  std::vector<dof_id_type> node_id_list;
1257  std::vector<boundary_id_type> bc_id_list;
1258 
1259  other_boundary.build_node_list(node_id_list, bc_id_list);
1260  for (std::size_t i=0; i != node_id_list.size(); ++i)
1261  {
1262  const dof_id_type our_id = node_id_list[i] + node_delta;
1263  boundary.add_node(our_id, bc_id_list[i]);
1264  }
1265  }
1266 
1267  {
1268  std::vector<dof_id_type> elem_id_list;
1269  std::vector<unsigned short int> side_list;
1270  std::vector<boundary_id_type> bc_id_list;
1271 
1272  other_boundary.build_side_list(elem_id_list, side_list, bc_id_list);
1273  for (std::size_t i=0; i != elem_id_list.size(); ++i)
1274  {
1275  const dof_id_type our_id = elem_id_list[i] + elem_delta;
1276  boundary.add_side(our_id, side_list[i], bc_id_list[i]);
1277  }
1278  }
1279 
1280  {
1281  std::vector<dof_id_type> elem_id_list;
1282  std::vector<unsigned short int> edge_list;
1283  std::vector<boundary_id_type> bc_id_list;
1284 
1285  other_boundary.build_edge_list(elem_id_list, edge_list, bc_id_list);
1286  for (std::size_t i=0; i != elem_id_list.size(); ++i)
1287  {
1288  const dof_id_type our_id = elem_id_list[i] + elem_delta;
1289  boundary.add_edge(our_id, edge_list[i], bc_id_list[i]);
1290  }
1291  }
1292 
1293  {
1294  std::vector<dof_id_type> elem_id_list;
1295  std::vector<unsigned short int> shellface_list;
1296  std::vector<boundary_id_type> bc_id_list;
1297 
1298  other_boundary.build_shellface_list(elem_id_list, shellface_list, bc_id_list);
1299  for (std::size_t i=0; i != elem_id_list.size(); ++i)
1300  {
1301  const dof_id_type our_id = elem_id_list[i] + elem_delta;
1302  boundary.add_shellface(our_id, shellface_list[i], bc_id_list[i]);
1303  }
1304  }
1305  } // end if (other_mesh)
1306 
1307  // Finally, we need to "merge" the overlapping nodes
1308  // We do this by iterating over node_to_elems_map and updating
1309  // the elements so that they "point" to the nodes that came
1310  // from this mesh, rather than from other_mesh.
1311  // Then we iterate over node_to_node_map and delete the
1312  // duplicate nodes that came from other_mesh.
1313 
1314  // Container to catch boundary IDs passed back from BoundaryInfo.
1315  std::vector<boundary_id_type> bc_ids;
1316 
1317  std::map<dof_id_type, std::vector<dof_id_type>>::iterator elem_map_it = node_to_elems_map.begin();
1318  std::map<dof_id_type, std::vector<dof_id_type>>::iterator elem_map_it_end = node_to_elems_map.end();
1319  {
1320  LOG_SCOPE("stitch_meshes node updates", "ReplicatedMesh");
1321 
1322  for ( ; elem_map_it != elem_map_it_end; ++elem_map_it)
1323  {
1324  dof_id_type target_node_id = elem_map_it->first;
1325  dof_id_type other_node_id = node_to_node_map[target_node_id];
1326  Node & target_node = this->node_ref(target_node_id);
1327 
1328  std::size_t n_elems = elem_map_it->second.size();
1329  for (std::size_t i=0; i<n_elems; i++)
1330  {
1331  dof_id_type elem_id = elem_map_it->second[i];
1332  Elem * el = this->elem_ptr(elem_id);
1333 
1334  // find the local node index that we want to update
1335  unsigned int local_node_index = el->local_node(other_node_id);
1336 
1337  // We also need to copy over the nodeset info here,
1338  // because the node will get deleted below
1339  this->get_boundary_info().boundary_ids(el->node_ptr(local_node_index), bc_ids);
1340  el->set_node(local_node_index) = &target_node;
1341  this->get_boundary_info().add_node(&target_node, bc_ids);
1342  }
1343  }
1344  }
1345 
1346  std::map<dof_id_type, dof_id_type>::iterator node_map_it = node_to_node_map.begin();
1347  std::map<dof_id_type, dof_id_type>::iterator node_map_it_end = node_to_node_map.end();
1348  {
1349  LOG_SCOPE("stitch_meshes node deletion", "ReplicatedMesh");
1350  for ( ; node_map_it != node_map_it_end; ++node_map_it)
1351  {
1352  // In the case that this==other_mesh, the two nodes might be the same (e.g. if
1353  // we're stitching a "sliver"), hence we need to skip node deletion in that case.
1354  if ((this == other_mesh) && (node_map_it->second == node_map_it->first))
1355  continue;
1356 
1357  dof_id_type this_node_id = node_map_it->second;
1358  this->delete_node( this->node_ptr(this_node_id) );
1359  }
1360  }
1361 
1362  // If find_neighbors() wasn't called in prepare_for_use(), we need to
1363  // manually loop once more over all elements adjacent to the stitched boundary
1364  // and fix their lists of neighbors.
1365  // This is done according to the following steps:
1366  // 1. Loop over all copied elements adjacent to the boundary using node_to_elems_map (trying to avoid duplicates)
1367  // 2. Look at all their sides with a NULL neighbor and update them using side_to_elem_map if necessary
1368  // 3. Update the corresponding side in side_to_elem_map as well
1369  if (skip_find_neighbors)
1370  {
1371  LOG_SCOPE("stitch_meshes neighbor fixes", "ReplicatedMesh");
1372 
1373  elem_map_it = node_to_elems_map.begin();
1374  elem_map_it_end = node_to_elems_map.end();
1375  std::set<dof_id_type> fixed_elems;
1376  for ( ; elem_map_it != elem_map_it_end; ++elem_map_it)
1377  {
1378  std::size_t n_elems = elem_map_it->second.size();
1379  for (std::size_t i=0; i<n_elems; i++)
1380  {
1381  dof_id_type elem_id = elem_map_it->second[i];
1382  if (fixed_elems.find(elem_id) == fixed_elems.end())
1383  {
1384  Elem * el = this->elem_ptr(elem_id);
1385  fixed_elems.insert(elem_id);
1386  for (auto s : el->side_index_range())
1387  {
1388  if (el->neighbor_ptr(s) == libmesh_nullptr)
1389  {
1390  key_type key = el->key(s);
1391  typedef map_type::iterator key_val_it_type;
1392  std::pair<key_val_it_type, key_val_it_type>
1393  bounds = side_to_elem_map.equal_range(key);
1394 
1395  if (bounds.first != bounds.second)
1396  {
1397  // Get the side for this element
1398  const UniquePtr<Elem> my_side(el->side_ptr(s));
1399 
1400  // Look at all the entries with an equivalent key
1401  while (bounds.first != bounds.second)
1402  {
1403  // Get the potential element
1404  Elem * neighbor = bounds.first->second.first;
1405 
1406  // Get the side for the neighboring element
1407  const unsigned int ns = bounds.first->second.second;
1408  const UniquePtr<Elem> their_side(neighbor->side_ptr(ns));
1409  //libmesh_assert(my_side.get());
1410  //libmesh_assert(their_side.get());
1411 
1412  // If found a match with my side
1413  //
1414  // We need special tests here for 1D:
1415  // since parents and children have an equal
1416  // side (i.e. a node), we need to check
1417  // ns != ms, and we also check level() to
1418  // avoid setting our neighbor pointer to
1419  // any of our neighbor's descendants
1420  if ((*my_side == *their_side) &&
1421  (el->level() == neighbor->level()) &&
1422  ((el->dim() != 1) || (ns != s)))
1423  {
1424  // So share a side. Is this a mixed pair
1425  // of subactive and active/ancestor
1426  // elements?
1427  // If not, then we're neighbors.
1428  // If so, then the subactive's neighbor is
1429 
1430  if (el->subactive() ==
1431  neighbor->subactive())
1432  {
1433  // an element is only subactive if it has
1434  // been coarsened but not deleted
1435  el->set_neighbor (s,neighbor);
1436  neighbor->set_neighbor(ns,el);
1437  }
1438  else if (el->subactive())
1439  {
1440  el->set_neighbor(s,neighbor);
1441  }
1442  else if (neighbor->subactive())
1443  {
1444  neighbor->set_neighbor(ns,el);
1445  }
1446  side_to_elem_map.erase (bounds.first);
1447  break;
1448  }
1449 
1450  ++bounds.first;
1451  }
1452  }
1453  }
1454  }
1455  }
1456  }
1457  }
1458  }
1459 
1460  this->prepare_for_use( /*skip_renumber_nodes_and_elements= */ false, skip_find_neighbors);
1461 
1462  // After the stitching, we may want to clear boundary IDs from element
1463  // faces that are now internal to the mesh
1464  if (clear_stitched_boundary_ids)
1465  {
1466  LOG_SCOPE("stitch_meshes clear bcids", "ReplicatedMesh");
1467 
1468  // Container to catch boundary IDs passed back from BoundaryInfo.
1469  std::vector<boundary_id_type> bc_ids;
1470 
1471  MeshBase::element_iterator elem_it = this->elements_begin();
1472  MeshBase::element_iterator elem_end = this->elements_end();
1473  for (; elem_it != elem_end; ++elem_it)
1474  {
1475  Elem * el = *elem_it;
1476 
1477  for (auto side_id : el->side_index_range())
1478  {
1479  if (el->neighbor_ptr(side_id) != libmesh_nullptr)
1480  {
1481  // Completely remove the side from the boundary_info object if it has either
1482  // this_mesh_boundary_id or other_mesh_boundary_id.
1483  this->get_boundary_info().boundary_ids (el, side_id, bc_ids);
1484 
1485  if (std::find(bc_ids.begin(), bc_ids.end(), this_mesh_boundary_id) != bc_ids.end() ||
1486  std::find(bc_ids.begin(), bc_ids.end(), other_mesh_boundary_id) != bc_ids.end())
1487  this->get_boundary_info().remove_side(el, side_id);
1488  }
1489  }
1490  }
1491 
1492  // Removing stitched-away boundary ids might have removed an id
1493  // *entirely*, so we need to recompute boundary id sets to check
1494  // for that.
1496  }
1497 }
1498 
1499 
1501 {
1502  return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
1503  this->active_elements_end()));
1504 }
1505 
1506 
1507 } // namespace libMesh
The definition of the element_iterator struct.
Definition: mesh_base.h:1476
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) libmesh_override
functions for adding /deleting nodes elements.
virtual dof_id_type max_elem_id() const libmesh_override
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:117
double abs(double a)
unique_id_type & set_unique_id()
Definition: dof_object.h:662
virtual const Elem * elem_ptr(const dof_id_type i) const libmesh_override
virtual const Node * node_ptr(const dof_id_type i) const libmesh_override
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
virtual UniquePtr< Elem > build_side_ptr(const unsigned int i, bool proxy=true)=0
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition: mesh_base.h:1389
virtual const Node * query_node_ptr(const dof_id_type i) const libmesh_override
bool valid_id() const
Definition: dof_object.h:674
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:1941
A Node is like a Point, but with more information.
Definition: node.h:52
Encapsulates the MPI_Comm object.
Definition: parallel.h:657
bool subactive() const
Definition: elem.h:2275
virtual void copy_nodes_and_elements(const UnstructuredMesh &other_mesh, const bool skip_find_neighbors=false)
Deep copy of another unstructured mesh class (used by subclass copy constructors) ...
virtual Elem * add_elem(Elem *e) libmesh_override
Add elem e to the end of the element array.
std::vector< Elem * > _elements
The elements in the mesh.
virtual bool is_edge_on_side(const unsigned int e, const unsigned int s) const =0
void max(T &r) const
Take a local variable and replace it with the maximum of it&#39;s values on all processors.
virtual Elem * insert_elem(Elem *e) libmesh_override
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
virtual ~ReplicatedMesh()
Destructor.
virtual void delete_elem(Elem *e) libmesh_override
Removes element e from the mesh.
virtual const Elem * query_elem_ptr(const dof_id_type i) const libmesh_override
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id) libmesh_override
Changes the id of node old_id, both by changing node(old_id)->id() and by moving node(old_id) in the ...
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2083
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:420
virtual void renumber_nodes_and_elements() libmesh_override
Remove NULL elements from arrays.
virtual Node * add_node(Node *n) libmesh_override
Add Node n to the end of the vertex array.
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
unsigned short int side
Definition: xdr_io.C:49
virtual void clear() libmesh_override
Clear all internal data.
This is the base class from which all geometric element types are derived.
Definition: elem.h:89
virtual Real hmin() const
Definition: elem.C:458
uint8_t processor_id_type
Definition: id_types.h:99
const class libmesh_nullptr_t libmesh_nullptr
static UniquePtr< Node > build(const Node &n)
Definition: node.h:298
UniquePtr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition: mesh_base.h:1370
static const Real TOLERANCE
IterBase * end
Also have a polymorphic pointer to the end object, this prevents iterating past the end...
The MetisPartitioner uses the Metis graph partitioner to partition the elements.
The libMesh namespace provides an interface to certain functionality in the library.
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of element numbers, sides, and ids for those sides.
long double max(long double a, double b)
Real distance(const Point &p)
virtual void update_parallel_id_counts() libmesh_override
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors...
IntRange< unsigned short > edge_index_range() const
Definition: elem.h:2074
dof_id_type & set_id()
Definition: dof_object.h:641
bool valid_unique_id() const
Definition: dof_object.h:682
libmesh_assert(j)
std::unique_ptr< T > UniquePtr
Definition: auto_ptr.h:46
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id) libmesh_override
Changes the id of element old_id, both by changing elem(old_id)->id() and by moving elem(old_id) in t...
virtual dof_id_type n_elem() const libmesh_override
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:1967
std::vector< boundary_id_type > boundary_ids(const Node *node) const
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition: mesh_base.h:1376
virtual element_iterator elements_begin() libmesh_override
Elem iterator accessor functions.
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
virtual Node * insert_node(Node *n) libmesh_override
Insert Node n into the Mesh at a location consistent with n->id(), allocating extra storage if necess...
virtual element_iterator elements_end() libmesh_override
int8_t boundary_id_type
Definition: id_types.h:51
static const boundary_id_type invalid_id
Number used for internal use.
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:1874
virtual void fix_broken_node_and_element_numbering() libmesh_override
There is no reason for a user to ever call this function.
The UnstructuredMesh class is derived from the MeshBase class.
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:56
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:324
std::vector< Node * > _nodes
The vertices (spatial coordinates) of the mesh.
void prepare_for_use(const bool skip_renumber_nodes_and_elements=false, const bool skip_find_neighbors=false)
Prepare a newly created (or read) mesh for use.
Definition: mesh_base.C:174
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition: elem.h:2000
virtual node_iterator nodes_begin() libmesh_override
Node iterator accessor functions.
void build_node_list(std::vector< dof_id_type > &node_id_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of nodes and ids for those nodes.
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
virtual void clear()
Deletes all the data that are currently stored.
Definition: mesh_base.C:285
virtual void delete_node(Node *n) libmesh_override
Removes the Node n from the mesh.
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition: elem.h:2047
virtual unique_id_type parallel_max_unique_id() const libmesh_override
The definition of the node_iterator struct.
Definition: mesh_base.h:1528
virtual dof_id_type n_nodes() const libmesh_override
void remove_side(const Elem *elem, const unsigned short int side)
Removes all boundary conditions associated with side side of element elem, if any exist...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual dof_id_type n_active_elem() const libmesh_override
bool absolute_fuzzy_equals(const TypeVector< T > &rhs, Real tol=TOLERANCE) const
Definition: type_vector.h:962
virtual node_iterator nodes_end() libmesh_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
Creates a list of element numbers, edges, and boundary ids for those edges.
void stitching_helper(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)
Helper function for stitch_meshes and stitch_surfaces that does the mesh stitching.
const Parallel::Communicator & comm() const
OStreamProxy out
std::vector< boundary_id_type > edge_boundary_ids(const Elem *const elem, const unsigned short int edge) const
void swap(Iterator &lhs, Iterator &rhs)
swap, used to implement op=
virtual dof_id_type key(const unsigned int s) 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 element_iterator active_elements_begin() libmesh_override
Active, local, and negation forms of the element iterators described above.
void add_shellface(const dof_id_type elem, const unsigned short int shellface, const boundary_id_type id)
Add shell face shellface of element number elem with boundary id id to the boundary information data ...
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)
Similar to stitch_meshes, except that we stitch two adjacent surfaces within this mesh...
virtual unsigned int dim() const =0
unsigned int level() const
Definition: elem.h:2388
ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T &value)
The STL provides std::binary_search() which returns true or false depending on whether the searched-f...
Definition: utility.h:132
virtual UniquePtr< Elem > build_edge_ptr(const unsigned int i)=0
void stitch_meshes(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)
Stitch other_mesh to this mesh so that this mesh is the union of the two meshes.
virtual element_iterator active_elements_end() libmesh_override
virtual dof_id_type max_node_id() const libmesh_override
unsigned int local_node(const dof_id_type i) const
Definition: elem.h:1854
ReplicatedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
dof_id_type id() const
Definition: dof_object.h:632
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
Creates a list of element numbers, shellfaces, and boundary ids for those shellfaces.
long double min(long double a, double b)
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:38
uint8_t unique_id_type
Definition: id_types.h:79
virtual UniquePtr< Elem > side_ptr(unsigned int i)=0
virtual const Point & point(const dof_id_type i) const libmesh_override
uint8_t dof_id_type
Definition: id_types.h:64
processor_id_type processor_id() const
Definition: dof_object.h:694
void add_edge(const dof_id_type elem, const unsigned short int edge, const boundary_id_type id)
Add edge edge of element number elem with boundary id id to the boundary information data structure...