MAST
level_set_intersection.cpp
Go to the documentation of this file.
1 /*
2  * MAST: Multidisciplinary-design Adaptation and Sensitivity Toolkit
3  * Copyright (C) 2013-2019 Manav Bhatia
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 // C++ includes
21 #include <memory>
22 #include <map>
23 #include <numeric>
24 
25 // MAST includes
28 
29 
30 
32 _tol (1.e-8),
33 _max_iters (10),
34 _max_mesh_elem_id (0),
35 _max_mesh_node_id (0),
36 _max_elem_divs (4),
37 _elem (nullptr),
38 _initialized (false),
39 _if_elem_on_positive_phi (false),
40 _if_elem_on_negative_phi (false),
41 _mode (MAST::NO_INTERSECTION),
42 _node_num_on_boundary (0),
43 _edge_num_on_boundary (0){
44 
45 }
46 
47 
49 
50  this->clear();
51 }
52 
53 
54 const libMesh::Elem&
56 
57  libmesh_assert(_initialized);
58 
59  return *_elem;
60 }
61 
62 
65 
66  libmesh_assert(_initialized);
67 
68  return _mode;
69 }
70 
71 
72 
73 unsigned int
75 
76  libmesh_assert(_initialized);
77  libmesh_assert(_mode == MAST::THROUGH_NODE);
78 
79  return _node_num_on_boundary;
80 }
81 
82 
83 
84 unsigned int
86 
87  libmesh_assert(_initialized);
88  libmesh_assert(_mode == MAST::COLINEAR_EDGE);
89 
90  return _edge_num_on_boundary;
91 }
92 
93 
94 
95 
96 bool
98 
99  libmesh_assert(_initialized);
100 
102 }
103 
104 
105 bool
107 
108  libmesh_assert(_initialized);
109 
111 }
112 
113 
114 
115 bool
117 
118  libmesh_assert(_initialized);
119 
121 }
122 
123 
124 
125 bool
127 
128  return (this->if_intersection_through_elem() ||
130 }
131 
132 
133 
134 
135 bool
137 
138  return ((_mode == MAST::OPPOSITE_EDGES) ||
141  (_mode == MAST::NODE_AND_EDGE) ||
144 }
145 
146 
147 
148 Real
150 
151  libmesh_assert(_initialized);
152 
153  std::vector<const libMesh::Elem*>::const_iterator
154  it = _positive_phi_elems.begin(),
155  end = _positive_phi_elems.end();
156 
157  Real
158  v = 0.,
159  v0 = _elem->volume();
160 
161  for ( ; it != end; it++)
162  v += (*it)->volume();
163 
164  return v/v0;
165 }
166 
167 
168 
169 Real
170 MAST::LevelSetIntersection::get_node_phi_value(const libMesh::Node* n) const {
171 
172  libmesh_assert(_initialized);
173  std::map<const libMesh::Node*, std::pair<Real, bool> >::const_iterator
174  it = _node_phi_vals.find(n),
175  end = _node_phi_vals.end();
176 
177  libmesh_assert(it != end);
178  return it->second.first;
179 }
180 
181 
182 
183 void
185 
186  _tol = 1.e-8;
187  _max_iters = 10;
188  _elem = nullptr;
189  _initialized = false;
190  _if_elem_on_positive_phi = false;
191  _if_elem_on_negative_phi = false;
193  _max_mesh_elem_id = 0;
194  _max_mesh_node_id = 0;
197  _positive_phi_elems.clear();
198  _negative_phi_elems.clear();
199  _elem_sides_on_interface.clear();
200  _node_local_coords.clear();
201 
202  std::vector<libMesh::Elem*>::iterator
203  e_it = _new_elems.begin(),
204  e_end = _new_elems.end();
205 
206  for ( ; e_it != e_end; e_it++)
207  delete *e_it;
208 
209 
210  std::vector<libMesh::Node*>::iterator
211  n_it = _new_nodes.begin(),
212  n_end = _new_nodes.end();
213 
214  for ( ; n_it != n_end; n_it++)
215  delete *n_it;
216 
217  _new_nodes.clear();
218  _new_elems.clear();
219 }
220 
221 
222 void
224  const libMesh::Elem& e,
225  const Real t,
226  unsigned int max_elem_id,
227  unsigned int max_node_id) {
228 
229  // make sure that this has not been initialized already
230  libmesh_assert(!_initialized);
231  libmesh_assert_equal_to(e.dim(), 2); // this is only for 2D elements
232 
233  _max_mesh_elem_id = max_elem_id;
234  _max_mesh_node_id = max_node_id;
235 
236  _elem = &e;
237 
238  switch (e.type()) {
239  case libMesh::QUAD4:
241  break;
242 
243  case libMesh::QUAD9: {
244 
245  std::unique_ptr<libMesh::Elem>
246  quad4(_first_order_elem(e));
247  _init_on_first_order_ref_elem(phi, *quad4, t);
248  }
249  break;
250 
251  default:
252  // currently only QUAD4/9 are handled.
253  libmesh_error();
254  }
255 }
256 
257 
258 
259 std::unique_ptr<libMesh::Elem>
261 
262  std::unique_ptr<libMesh::Elem>
263  first_order_elem;
264 
265  switch (e.type()) {
266  case libMesh::QUAD9: {
267 
268  first_order_elem.reset(libMesh::Elem::build(libMesh::QUAD4).release());
269 
270  for (unsigned int i=0; i<4; i++)
271  first_order_elem->set_node(i) = const_cast<libMesh::Node*>(e.node_ptr(i));
272 
273  first_order_elem->set_id() = e.id();
274  }
275  break;
276 
277  default:
278  // currently only QUAD4/9 are handled.
279  libmesh_error();
280  }
281 
282  return first_order_elem;
283 }
284 
285 
286 
287 
288 void
291  const libMesh::Elem& e,
292  const Real t) {
293 
294  // make sure that this has not been initialized already
295  libmesh_assert(!_initialized);
296  libmesh_assert_equal_to(e.dim(), 2); // this is only for 2D elements
297  libmesh_assert(_elem);
298 
299  // this assumes that the phi=0 interface is not fully contained inside
300  // the element. That is, the interface is assumed to intersect with the
301  // element edges.
302 
303  // check for each mode of intersection. It is assumed that an element only
304  // sees a single mode of intersection, which is hopefully true for a fine
305  // mesh. However, with larger high-order elements this assumption will
306  // not be valid and more accurate implementations will be needed.
307 
308  _node_phi_vals.clear();
309 
310  unsigned int
311  n_node_intersection = 0;
312 
313  Real
314  val = 0.,
315  min_val = 1.e12, // set arbitrary high values
316  max_val = -1.e12;
317 
318  bool
319  on_level_set = false;
320 
321  // the shape function values are checked on all nodes of the reference elem
322  for (unsigned int i=0; i<_elem->n_nodes(); i++) {
323 
324  const libMesh::Node& n = _elem->node_ref(i);
325  phi(n, t, val);
326  on_level_set = fabs(val) <= _tol;
327  _node_phi_vals[&n] = std::pair<Real, bool>(val, on_level_set);
328  // only check the corner nodes for intersection to maintain the
329  // assumption of simple intersections on quad4. This will be
330  // consistent for a quad4 reference elem, but for a quad8/9 elem
331  // this helps in simplifying the process.
332  if (i < e.n_nodes() && on_level_set) {
334  n_node_intersection++;
335  }
336  min_val = (min_val > val)? val : min_val;
337  max_val = (max_val < val)? val : max_val;
338  }
339 
340 
342  // Check to see if the whole element is on either side of the function
344 
345  // if the sign of function on all nodes is the same, then it is assumed
346  // that the element is not intersected
347  if (min_val > _tol &&
348  max_val > _tol) {
349  // element is completely on the positive side with no intersection
350 
352  _positive_phi_elems.push_back(_elem);
354  _if_elem_on_negative_phi = false;
355  _initialized = true;
356  return;
357  }
358  else if (min_val < _tol &&
359  max_val < _tol) {
360 
361  // element is completely on the negative side, with no intersection
362 
364  _negative_phi_elems.push_back(_elem);
365  _if_elem_on_positive_phi = false;
367  _initialized = true;
368  return;
369  }
370  else if (min_val < _tol &&
371  max_val > _tol) {
372  // if it did not get caught in the previous two cases, then there is
373  // an intersection.
374  // If it got here, then there is an intersection in the domain and the
375  // element is not on the positive side completely.
376 
377  _if_elem_on_positive_phi = false;
378  _if_elem_on_negative_phi = false;
379  }
380 
382  // Check to see if the function passes through one node
384  // if only one node intersection was found, then the level set
385  // passes through a node. Node intersection happens when the level set on
386  // a node is zero and the level set does not change sign on the element.
387  Real
388  val1 = std::fabs(min_val)<_tol ? 0.: min_val,
389  val2 = std::fabs(max_val)<_tol ? 0.: max_val;
390  bool
391  sign_change = (val1*val2 < 0.);
392  if (n_node_intersection == 1 &&
393  !sign_change) {
394 
396 
397  // this means that the whole element is going to be on either the
398  // positive or the negative side of the level-set function
399  // now figure out which side of the level-set function the
400  // element lies on
401 
402  if (min_val <= _tol &&
403  max_val <= _tol ) { // element is on the negative side
404  _negative_phi_elems.push_back(_elem);
405  }
406  else {
407 
408  libmesh_assert_greater(max_val, _tol);
409  _positive_phi_elems.push_back(_elem);
410  }
411 
412  std::vector<std::pair<libMesh::Point, libMesh::Point> >
413  side_nondim_points;
414  _add_node_local_coords(e, side_nondim_points, _node_local_coords);
415  _initialized = true;
416  return;
417  }
418 
419 
421  // Check to see if the function is conliniear with an edge
423  // Check to see if all nodes on an edge are on the level set
424  for (unsigned int i=0; i<e.n_sides(); i++) {
425 
426  std::unique_ptr<const libMesh::Elem>
427  s(e.side_ptr(i).release());
428 
429  unsigned int
430  n_nodes_on_level_set = 0;
431 
432  for (unsigned int j=0; j<s->n_nodes(); j++)
433  n_nodes_on_level_set += _node_phi_vals[s->node_ptr(j)].second;
434 
435  if (n_nodes_on_level_set == s->n_nodes()) {
436 
437  // side is on the level set
440 
441  // now that we know that one of the edges is on the function,
442  // we need to identify if the element is on the positive or
443  // the negative side of the function. It is on the positive side
444  // if the maximum value of the function on the nodes is positive.
445  // It is on the negative side if the minimum value of the function
446  // is negative.
447  if (max_val > _tol)
448  _positive_phi_elems.push_back(_elem);
449  else if (min_val < _tol)
450  _negative_phi_elems.push_back(_elem);
451 
452  std::vector<std::pair<libMesh::Point, libMesh::Point> >
453  side_nondim_points;
454  _add_node_local_coords(e, side_nondim_points, _node_local_coords);
456  _initialized = true;
457  return;
458  }
459  }
460 
462  // Identify the edges and locations where intersection occurs
464  // now that we are here, this means that none of the previous modes
465  // identify the intersection of the level set with this element.
466  // Therefore, we will now identify the sides of the element where
467  // intersectio occurs and create the sub-elements for integration.
468  // This is done separately for each element type.
469  switch (e.type()) {
470 
471  case libMesh::QUAD4:
473  break;
474 
475  default:
476  libmesh_error_msg("level-set intersections for elem type not handled.");
477  break;
478  }
479 
480  // set the IDs of the new elements. Both the subdomain ID and
481  // element IDs are set here.
482  //
483  // Note that the subdomain IDs are needed
484  // for querying the BCs, Loads and properties from the physics. We
485  // use the same subdomain ID as its parent ID
486  //
487  // The element id is a little more tricky. Strictly speaking, this
488  // should not be necessary, since we are only creating temporary
489  // elements to compute element quantities. However, the current stress
490  // storage implementation is storing this for element ids. So, we provide
491  // a unique element ID for each new element.
492  //
493 
494  libmesh_assert_less_equal(_new_elems.size(), _max_elem_divs);
495 
496  for (unsigned int i=0; i<_new_elems.size(); i++) {
497 
498  _new_elems[i]->subdomain_id() = e.subdomain_id();
499  _new_elems[i]->set_id(_max_mesh_elem_id+e.id()*_max_elem_divs+i);
500  }
501 
502  _initialized = true;
503 }
504 
505 
506 
507 
508 const std::vector<const libMesh::Elem*>&
510 
511  return _positive_phi_elems;
512 }
513 
514 
515 
516 const std::vector<const libMesh::Elem*>&
518 
519  return _negative_phi_elems;
520 }
521 
522 
523 void
525 get_corner_nodes_on_negative_phi(std::set<const libMesh::Node*>& nodes) const {
526 
527  nodes.clear();
528 
529  // iterate over the corner nodes and return the ones that had a negative phi
530  // value
531  unsigned int
532  n_corner_nodes = 0;
533 
534  switch (_elem->type()) {
535  case libMesh::QUAD4:
536  case libMesh::QUAD8:
537  case libMesh::QUAD9:
538  n_corner_nodes = 4;
539  break;
540 
541  default:
542  libmesh_error(); // other cases not yet handled
543  }
544 
545  std::map<const libMesh::Node*, std::pair<Real, bool>>::const_iterator
546  it,
547  end = _node_phi_vals.end();
548 
549  for (unsigned int i=0; i<n_corner_nodes; i++) {
550 
551  const libMesh::Node* nd = _elem->node_ptr(i);
552 
553  it = _node_phi_vals.find(nd);
554 
555  libmesh_assert(it != end);
556 
557  if (it->second.first < 0.)
558  nodes.insert(it->first);
559  }
560 }
561 
562 
563 
564 bool
566 has_side_on_interface(const libMesh::Elem& e) const {
567 
568  std::map<const libMesh::Elem*, int>::const_iterator it;
569  it = _elem_sides_on_interface.find(&e);
570 
571  libmesh_assert(it != _elem_sides_on_interface.end());
572 
573  return it->second >= 0;
574 }
575 
576 
577 
578 unsigned int
580 get_side_on_interface(const libMesh::Elem& e) const {
581 
582  std::map<const libMesh::Elem*, int>::const_iterator it;
583  it = _elem_sides_on_interface.find(&e);
584 
585  libmesh_assert(it != _elem_sides_on_interface.end());
586  libmesh_assert(it->second >= 0);
587 
588  return it->second;
589 }
590 
591 
592 void
594 ( const libMesh::Elem& e,
595  std::vector<std::pair<libMesh::Point, libMesh::Point> >& side_nondim_points,
596  std::map<const libMesh::Node*, libMesh::Point>& node_coord_map) {
597 
598  switch (e.type()) {
599 
600  case libMesh::QUAD4: {
601 
602  // We create a vector storing the nondimensional locations of nodes
603  // on each side of the element. This is used later to identify the
604  // nondimensional location of all new nodes
605  side_nondim_points.resize(4);
606  // side 0: eta =-1, xi=[-1, 1]
607  side_nondim_points[0] =
608  std::pair<libMesh::Point, libMesh::Point>
609  (libMesh::Point(-1, -1, 0.),
610  libMesh::Point(+1, -1, 0.));
611  side_nondim_points[1] =
612  std::pair<libMesh::Point, libMesh::Point>
613  (libMesh::Point(+1, -1, 0.),
614  libMesh::Point(+1, +1, 0.));
615  side_nondim_points[2] =
616  std::pair<libMesh::Point, libMesh::Point>
617  (libMesh::Point(+1, +1, 0.),
618  libMesh::Point(-1, +1, 0.));
619  side_nondim_points[3] =
620  std::pair<libMesh::Point, libMesh::Point>
621  (libMesh::Point(-1, +1, 0.),
622  libMesh::Point(-1, -1, 0.));
623 
624  // populate the node to nondimensional coordinate map with the
625  // original nodes of the element
626  for (unsigned int i=0; i<4; i++)
627  node_coord_map[e.node_ptr(i)] = side_nondim_points[i].first;
628  }
629  break;
630 
631  default:
632  libmesh_assert(false); // not handled.
633  }
634 
635 }
636 
637 
638 void
641  const libMesh::Elem& e,
642  const Real t,
643  const std::map<const libMesh::Node*, std::pair<Real, bool> >&
644  node_phi_vals) {
645 
646  libmesh_assert_equal_to(e.type(), libMesh::QUAD4);
647  libmesh_assert(!_initialized);
648 
649  std::vector<std::pair<libMesh::Point, libMesh::Point> >
650  side_nondim_points;
651  _add_node_local_coords(e, side_nondim_points, _node_local_coords);
652 
653  const unsigned int
654  n_nodes = 4; // this is equal to n_sides for QUAD4
655 
656  std::vector<std::pair<bool, Real> >
657  side_intersection(n_nodes, std::pair<bool, Real>(false, 0.));
658  std::vector<bool>
659  node_intersection(n_nodes, false);
660 
661  std::map<const libMesh::Node*, std::pair<Real, bool> >::const_iterator
662  it0,
663  it1,
664  it_end = node_phi_vals.end();
665 
666  Real
667  v0 = 0.,
668  v1 = 0.;
669 
670 
671  for (unsigned int i=0; i<n_nodes; i++) {
672 
673  it0 = node_phi_vals.find(e.node_ptr(i));
674  libmesh_assert(it0 != it_end);
675 
676  // identify intersection with nodes
677  if (std::fabs(it0->second.first) < _tol)
678  node_intersection[i] = true;
679  else { // if there is node intersection, then there won't be edge interseciton
680 
681  // identify intersection with edges
682  it1 = node_phi_vals.find(e.node_ptr((i+1)%n_nodes));
683  libmesh_assert(it1 != it_end);
684  v0 = it0->second.first;
685  v1 = it1->second.first;
686  if (std::fabs(v0) > _tol &&
687  std::fabs(v1) > _tol &&
688  v0 * v1 < 0.) {
689 
690  // intersection lies somewhere in the interior of the edge
691  side_intersection[i] = std::pair<bool, Real>
692  (true,
694  *it1->first,
695  phi,
696  t));
697  }
698  }
699  }
700 
701 
702 
703  // currently, we only handle two intersections
704  unsigned int
705  n_intersection = 0;
706 
707  // add up intersections on edges
708  {
709  std::vector<std::pair<bool, Real> >::const_iterator
710  it = side_intersection.begin(),
711  end = side_intersection.end();
712 
713  for (; it != end; it++)
714  n_intersection += it->first;
715  }
716 
717  // add up intersections on nodes
718  {
719  std::vector<bool>::const_iterator
720  it = node_intersection.begin(),
721  end = node_intersection.end();
722 
723  for (; it != end; it++)
724  n_intersection += *it;
725  }
726 
727  //
728  // identify the sets of intersections, which will be processed
729  // individually for creation of the subelements.
730  // The current implementation of linear quad4s only permits one intersection
731  // per edge. This will have to be chanegd for high-order elements.
732  //
733  // As a result of this assumption, the following should hold:
734  // -- OPPOSITE_EDGES: only two intersections can exist
735  // -- OPPOSITE_NODES: only two nodes can intersect that ways
736  // -- NODE_AND_EDGE: two such intersections can occur
737  // -- ADJACENT_EDGES: two such intersections can occur
738  //
739 
740  // now identify which approch to use for creation of the sub-elements.
741  // one case is where two adjacent sides of the element have a
742  unsigned int
743  ref_side = 0,
744  ref_node = 0;
745 
746  switch (n_intersection) {
747  case 2: {
748  for (unsigned int i=0; i<n_nodes; i++) {
749 
750  if (side_intersection[i].first &&
751  side_intersection[(i+1)%n_nodes].first) {
752  // found adjacent mode with ref edge i
754  ref_side = i;
755  break;
756  }
757  else if (side_intersection[i].first &&
758  side_intersection[(i+2)%n_nodes].first) {
759  // found adjacent mode with ref edge i
761  ref_side = i;
762  break;
763  }
764  else if (node_intersection[i] &&
765  node_intersection[(i+2)%n_nodes]) {
766  // found intersection on two opposite nodes
768  ref_node = i;
769  break;
770  }
771  else if (node_intersection[i] &&
772  side_intersection[(i+1)%n_nodes].first) {
773  // found intersection on node and edge
775  ref_node = i;
776  ref_side = i+1;
777  }
778  else if (node_intersection[i] &&
779  side_intersection[(i+2)%n_nodes].first) {
780  // found intersection on node and edge
782  ref_node = i;
783  ref_side = i+2;
784  }
785  }
786  }
787  break;
788 
789  case 3: {
790  // this must be a node with two edge intersection
791  // we first identify the node
792  for (unsigned int i=0; i<n_nodes; i++) {
793  if (node_intersection[i] &&
794  side_intersection[(i+1)%n_nodes].first &&
795  side_intersection[(i+2)%n_nodes].first) {
796 
798  ref_node = i;
799  ref_side = i+1;
800  break;
801  }
802  }
803  }
804  break;
805 
806  case 4: {
807  // this must be two adjacent edge intersections, where all four edges
808  // should have intersections
809  if (side_intersection[0].first &&
810  side_intersection[1].first &&
811  side_intersection[2].first &&
812  side_intersection[3].first) {
813 
815 
816  //
817  // this means that all four edges have an intersection, one
818  // per edge. So, now we identify how to connect the edges.
819  // The choices are: intersection in sides 0-1 and 2-3, or
820  // intersction in side 0-3 and 1-2.
821  //
822  // For this, we need to figure out the gradient. We will
823  // do so by checking the value of the level-set function
824  // in the middle of the element. If it is the same sign as
825  // the zero-th node, then the intersection will be 0-1 and 2-3,
826  // if it is the opposite sign, then the intersection is
827  // 0-3 and 1-2.
828  //
829 
830  Real mid_phi = 0.;
831  phi(e.centroid(), t, mid_phi);
832  it0 = node_phi_vals.find(e.node_ptr(0));
833  v0 = it0->second.first;
834 
835  if (mid_phi * v0 > 0.) // same sign
836  ref_side = 0;
837  else
838  ref_side = 1;
839  }
840  }
841  break;
842 
843  default:
844  // if it gets here, then we have a mode that we have not
845  // created
846  assert(false);
847  }
848 
849  // make sure that atleast one of the modes was found
850  libmesh_assert(_mode == MAST::ADJACENT_EDGES ||
856 
858  // now create the sub elements
860  // For intersection with opposite edges, both sides of the element
861  // will be represented by QUAD4 elements.
862  //
863  // When the intersection on adjacent elements happens, then
864  // one side will be represented with triangles and the other
865  // side is represented with a combination of two quads.
866  //
867  // for opposite nodes, one triangle is used for each side of the
868  // intersection
869  //
870  // for node and edge, one side is represented with a triangle,
871  // and the other with a quad.
872  //
873 
874  Real
875  xi_ref = 0.,
876  xi_other = 0.;
877  libMesh::Point
878  p,
879  side_p0,
880  side_p1;
881 
882  // this should pass, but just to be sure, we will check.
883  libmesh_assert_equal_to(_new_nodes.size(), 0);
884 
885  libMesh::Node
886  *nd = nullptr;
887 
888  std::unique_ptr<const libMesh::Elem>
889  s;
890 
891  bool
892  ref_side_node0_positive = false;
893 
894  // used this to set unique node ids, since some of libMesh's operations
895  // depend on valid and unique ids for nodes.
896  unsigned int
897  node_id_incr = 1;
898 
900  // Create a new node at each intersection: first, on ref_side
901  //
902  // New nodes are needed only when intersection does not exist
903  // opposite nodes. In this case, the current nodes are used for
904  // the new elements.
906  if (_mode == MAST::ADJACENT_EDGES ||
912 
913  xi_ref = side_intersection[ref_side%n_nodes].second;
914  s.reset(e.side_ptr(ref_side%n_nodes).release());
915  p = s->point(0) + xi_ref * (s->point(1) - s->point(0));
916  nd = new libMesh::Node(p);
917  nd->set_id(_max_mesh_node_id + node_id_incr);
918  node_id_incr++;
919  _new_nodes.push_back(nd);
920  side_p0 = side_nondim_points[ref_side%n_nodes].first;
921  side_p1 = side_nondim_points[ref_side%n_nodes].second;
922  _node_local_coords[nd] = side_p0 + xi_ref * (side_p1 - side_p0);
923 
924 
926  // before we create the new elements, we will figure out
927  // which portion of the side is on the positive or negative side of
928  // the level set function
930 
931  it0 = node_phi_vals.find(s->node_ptr(0));
932  v0 = it0->second.first;
933 
934  if (v0 > 0.)
935  ref_side_node0_positive = true;
936  }
937  else {
938 
939  it0 = node_phi_vals.find(e.node_ptr(ref_node+1));
940  v0 = it0->second.first;
941 
942  if (v0 > 0.)
943  ref_side_node0_positive = true;
944 
945  }
946 
947 
948 
949  switch (_mode) {
950 
951  case MAST::OPPOSITE_EDGES: {
952 
953  // create a new node at each intersection: next, on ref_side + 2
954  xi_other = side_intersection[(ref_side+2)%n_nodes].second;
955  s.reset(e.side_ptr((ref_side+2)%n_nodes).release());
956  p = s->point(0) + xi_other * (s->point(1) - s->point(0));
957  nd = new libMesh::Node(p);
958  nd->set_id(_max_mesh_node_id + node_id_incr);
959  node_id_incr++;
960  _new_nodes.push_back(nd);
961  side_p0 = side_nondim_points[(ref_side+2)%n_nodes].first;
962  side_p1 = side_nondim_points[(ref_side+2)%n_nodes].second;
963  _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0);
964 
965  libMesh::Elem
966  *e_p = const_cast<libMesh::Elem*>(&e),
967  *e1 = libMesh::Elem::build(libMesh::QUAD4, e_p).release(),
968  *e2 = libMesh::Elem::build(libMesh::QUAD4, e_p).release();
969 
970  _new_elems.push_back(e1);
971  _new_elems.push_back(e2);
972 
973  // create the elements. We create the elements such that:
974  // node(0) = new node on ref_side
975  // node(1) = elem node (ref_side+1)%n_sides
976  // node(2) = elem node (ref_side+2)%n_sides
977  // node(3) = new node on ref_side+2
978  // this way, the element has the same orientation as the original
979  // element and the sides have the following association:
980  // side(0) = coincident with ref_side of original element
981  // side(1) = coincident side with ref_side+1 of original element
982  // side(2) = coincident side with ref_side+2 of original element
983  // side(3) = coincident side with level-set interface
984 
985  e1->set_node(0) = _new_nodes[0];
986  e1->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+1)%n_nodes));
987  e1->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+2)%n_nodes));
988  e1->set_node(3) = _new_nodes[1];
989 
990  _elem_sides_on_interface[e1] = 3;
991 
992  // create a second element and set its nodes so that:
993  // node(0) = new node on ref_side+2
994  // node(1) = elem node (ref_side+3)%n_nodes
995  // node(2) = elem node (ref_side+4)%n_nodes
996  // node(3) = new node on ref_side
997  // this way, the element has the same orientation as the original
998  // element and the sides have the following association:
999  // side(0) = coincident with ref_side+2 of original element
1000  // side(1) = coincident side with (ref_side+3)%n_sides of original element
1001  // side(2) = coincident side with ref_side of original element
1002  // side(3) = coincident side with level-set interface
1003 
1004  e2->set_node(0) = _new_nodes[1];
1005  e2->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+3)%n_nodes));
1006  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr(ref_side));
1007  e2->set_node(3) = _new_nodes[0];
1008 
1009  _elem_sides_on_interface[e2] = 3;
1010 
1011  if (ref_side_node0_positive) {
1012 
1013  _positive_phi_elems.push_back(e2);
1014  _negative_phi_elems.push_back(e1);
1015  }
1016  else {
1017 
1018  _positive_phi_elems.push_back(e1);
1019  _negative_phi_elems.push_back(e2);
1020  }
1021  }
1022  break;
1023 
1024  case MAST::ADJACENT_EDGES: {
1025 
1026 
1027  // create a new node at each intersection: second on ref_side+1
1028  xi_other = side_intersection[(ref_side+1)%n_nodes].second;
1029  s.reset(e.side_ptr((ref_side+1)%n_nodes).release());
1030  p = s->point(0) + xi_other * (s->point(1) - s->point(0));
1031  nd = new libMesh::Node(p);
1032  nd->set_id(_max_mesh_node_id + node_id_incr);
1033  node_id_incr++;
1034  _new_nodes.push_back(nd);
1035  side_p0 = side_nondim_points[(ref_side+1)%n_nodes].first;
1036  side_p1 = side_nondim_points[(ref_side+1)%n_nodes].second;
1037  _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0);
1038 
1039 
1040  // create a new node on the opposite side, which will be used to create
1041  // the QUAD4 elements: second on ref_side+2
1042  s.reset(e.side_ptr((ref_side+2)%n_nodes).release());
1043  p = s->point(1) + xi_ref * (s->point(0) - s->point(1));
1044  nd = new libMesh::Node(p);
1045  nd->set_id(_max_mesh_node_id + node_id_incr);
1046  node_id_incr++;
1047  _new_nodes.push_back(nd);
1048  side_p0 = side_nondim_points[(ref_side+2)%n_nodes].first;
1049  side_p1 = side_nondim_points[(ref_side+2)%n_nodes].second;
1050  _node_local_coords[nd] = side_p1 + xi_ref * (side_p0 - side_p1);
1051 
1052 
1053  // the algorithm of identifying the ref_side always ensures that the
1054  // region with xi > x0 on ref_side will be triangular. The other side
1055  // will be modeled with two QUAD4 elements
1056 
1057  libMesh::Elem
1058  *e_p = const_cast<libMesh::Elem*>(&e),
1059  *e1 = libMesh::Elem::build( libMesh::TRI3, e_p).release(),
1060  *e2 = libMesh::Elem::build(libMesh::QUAD4, e_p).release(),
1061  *e3 = libMesh::Elem::build(libMesh::QUAD4, e_p).release();
1062 
1063  _new_elems.push_back(e1);
1064  _new_elems.push_back(e2);
1065  _new_elems.push_back(e3);
1066 
1067  // create the elements. We create the elements such that:
1068  // node(0) = new node on ref_side
1069  // node(1) = elem node (ref_side+1)%n_sides
1070  // node(2) = new node on (ref_side+1)%n_sides
1071  // this way, the element has the same orientation as the original
1072  // element and the sides have the following association:
1073  // side(0) = coincident with ref_side of original element
1074  // side(1) = coincident side with ref_side+1 of original element
1075  // side(2) = coincident side with level-set interface
1076 
1077  e1->set_node(0) = _new_nodes[0];
1078  e1->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+1)%n_nodes));
1079  e1->set_node(2) = _new_nodes[1];
1080 
1081  _elem_sides_on_interface[e1] = 2;
1082 
1083  // create a second element and set its nodes so that:
1084  // node(0) = new node on ref_side
1085  // node(1) = new node on (ref_side+2)%n_nodes
1086  // node(2) = elem node (ref_side+3)%n_nodes
1087  // node(3) = elem node ref_side
1088  // this way, the element has the same orientation as the original
1089  // element and the sides have the following association:
1090  // side(0) = new edge connecting ref_side to new node on ref_side+2
1091  // side(1) = coincident side with (ref_side+2)%n_sides of original element
1092  // side(2) = coincident side with (ref_side+3)%n_sides of original element
1093  // side(3) = coincident with ref_side of original element
1094 
1095  e2->set_node(0) = _new_nodes[0];
1096  e2->set_node(1) = _new_nodes[2];
1097  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+3)%n_nodes));
1098  e2->set_node(3) = const_cast<libMesh::Node*>(e.node_ptr(ref_side));
1099 
1100  // this element does not have a side on the interface
1101  _elem_sides_on_interface[e2] = -1;
1102 
1103  // create a third element and set its nodes so that:
1104  // node(0) = new node on ref_side+1
1105  // node(1) = elem node (ref_side+2)%n_nodes
1106  // node(2) = new node on (ref_side+2)%n_nodes
1107  // node(3) = new node on ref_side
1108  // this way, the element has the same orientation as the original
1109  // element and the sides have the following association:
1110  // side(0) = coincident side with (ref_side+1)%n_sides of original element
1111  // side(1) = coincident side with (ref_side+2)%n_sides of original element
1112  // side(2) = new edge connecting ref_side to new node on (ref_side+2)%n_sides
1113  // side(3) = coincident side with level-set interface
1114 
1115  e3->set_node(0) = _new_nodes[1];
1116  e3->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+2)%n_nodes));
1117  e3->set_node(2) = _new_nodes[2];
1118  e3->set_node(3) = _new_nodes[0];
1119 
1120  _elem_sides_on_interface[e3] = 3;
1121 
1122  if (ref_side_node0_positive) {
1123 
1124  _positive_phi_elems.push_back(e2);
1125  _positive_phi_elems.push_back(e3);
1126  _negative_phi_elems.push_back(e1);
1127  }
1128  else {
1129 
1130  _positive_phi_elems.push_back(e1);
1131  _negative_phi_elems.push_back(e2);
1132  _negative_phi_elems.push_back(e3);
1133  }
1134  }
1135  break;
1136 
1137 
1138 
1139  case MAST::NODE_AND_EDGE: {
1140 
1141  libMesh::Elem
1142  *e_p = const_cast<libMesh::Elem*>(&e),
1143  *e1 = libMesh::Elem::build(libMesh::TRI3, e_p).release(),
1144  *e2 = libMesh::Elem::build(libMesh::QUAD4, e_p).release();
1145 
1146  _new_elems.push_back(e1);
1147  _new_elems.push_back(e2);
1148 
1149  if (ref_side-ref_node == 1) {
1150 
1151  // create the elements. We create the elements such that:
1152  // node(0) = elem node (ref_node)
1153  // node(1) = elem node (ref_node+1)%n_nodes
1154  // node(2) = new node on ref_side%n_nodes
1155  // this way, the element has the same orientation as the original
1156  // element and the sides have the following association:
1157  // side(0) = coincident with ref_node of original element
1158  // side(1) = coincident side with ref_node+1 of original element
1159  // side(2) = coincident side with level-set interface
1160 
1161  e1->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1162  e1->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+1)%n_nodes));
1163  e1->set_node(2) = _new_nodes[0];
1164 
1165  _elem_sides_on_interface[e1] = 2;
1166 
1167  // create a second element and set its nodes so that:
1168  // node(0) = new node on ref_side%n_nodes
1169  // node(1) = elem node (ref_side+1)%n_nodes
1170  // node(2) = elem node (ref_side+2)%n_nodes
1171  // node(3) = elem node (ref_node)
1172  // this way, the element has the same orientation as the original
1173  // element and the sides have the following association:
1174  // side(0) = coincident with ref_side of original element
1175  // side(1) = coincident side with (ref_side+1)%n_sides of original element
1176  // side(2) = coincident side with (ref_side+2)%n_sides of original element
1177  // side(3) = coincident side with level-set interface
1178 
1179  e2->set_node(0) = _new_nodes[0];
1180  e2->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+1)%n_nodes));
1181  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+2)%n_nodes));
1182  e2->set_node(3) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1183 
1184  _elem_sides_on_interface[e2] = 3;
1185 
1186 
1187  // node0 is the first node of ref_side
1188  if (ref_side_node0_positive) {
1189 
1190  _positive_phi_elems.push_back(e1);
1191  _negative_phi_elems.push_back(e2);
1192  }
1193  else {
1194 
1195  _positive_phi_elems.push_back(e2);
1196  _negative_phi_elems.push_back(e1);
1197  }
1198  }
1199  else if (ref_side-ref_node==2) {
1200 
1201  // create the elements. We create the elements such that:
1202  // node(0) = elem node (ref_node)
1203  // node(1) = new node on ref_side%n_nodes
1204  // node(2) = elem node (ref_node+3)%n_nodes
1205  // this way, the element has the same orientation as the original
1206  // element and the sides have the following association:
1207  // side(0) = coincident side with level-set interface
1208  // side(1) = coincident side with ref_side%n_nodes of original element
1209  // side(2) = coincident side with (ref_side+1)%n_nodes of original element
1210 
1211  e1->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1212  e1->set_node(1) = _new_nodes[0];
1213  e1->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+3)%n_nodes));
1214 
1215  _elem_sides_on_interface[e1] = 0;
1216 
1217  // create a second element and set its nodes so that:
1218  // node(0) = elem node (ref_node)
1219  // node(1) = elem node (ref_node+1)%n_nodes
1220  // node(2) = elem node (ref_node+2)%n_nodes
1221  // node(3) = new node on ref_side%n_nodes
1222  // this way, the element has the same orientation as the original
1223  // element and the sides have the following association:
1224  // side(0) = coincident with ref_node of original element
1225  // side(1) = coincident side with (ref_node+1)%n_sides of original element
1226  // side(2) = coincident side with (ref_node+2)%n_sides of original element
1227  // side(3) = coincident side with level-set interface
1228 
1229  e2->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1230  e2->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+1)%n_nodes));
1231  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+2)%n_nodes));
1232  e2->set_node(3) = _new_nodes[0];
1233 
1234  _elem_sides_on_interface[e2] = 3;
1235 
1236 
1237  if (ref_side_node0_positive) {
1238 
1239  _positive_phi_elems.push_back(e2);
1240  _negative_phi_elems.push_back(e1);
1241  }
1242  else {
1243 
1244  _positive_phi_elems.push_back(e1);
1245  _negative_phi_elems.push_back(e2);
1246  }
1247  }
1248  else
1249  libmesh_error(); // shoudl not get here
1250 
1251  }
1252  break;
1253 
1254 
1255  case MAST::OPPOSITE_NODES: {
1256 
1257  libMesh::Elem
1258  *e_p = const_cast<libMesh::Elem*>(&e),
1259  *e1 = libMesh::Elem::build(libMesh::TRI3, e_p).release(),
1260  *e2 = libMesh::Elem::build(libMesh::TRI3, e_p).release();
1261 
1262  _new_elems.push_back(e1);
1263  _new_elems.push_back(e2);
1264 
1265  // create the elements. We create the elements such that:
1266  // node(0) = elem node (ref_node)
1267  // node(1) = elem node (ref_node+1)%n_nodes
1268  // node(2) = elem node (ref_node+2)%n_nodes
1269  // this way, the element has the same orientation as the original
1270  // element and the sides have the following association:
1271  // side(0) = coincident with ref_node of original element
1272  // side(1) = coincident side with ref_node+1 of original element
1273  // side(2) = coincident side with level-set interface
1274 
1275  e1->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1276  e1->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+1)%n_nodes));
1277  e1->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+2)%n_nodes));
1278 
1279  _elem_sides_on_interface[e1] = 2;
1280 
1281  // create a second element and set its nodes so that:
1282  // node(0) = elem node on (ref_node+2)%n_nodes
1283  // node(1) = elem node on (ref_node+3)%n_nodes
1284  // node(2) = elem node on (ref_node+4)%n_nodes
1285  // this way, the element has the same orientation as the original
1286  // element and the sides have the following association:
1287  // side(0) = coincident with (ref_node+2)%n_nodes of original element
1288  // side(1) = coincident with (ref_node+3)%n_nodes of original element
1289  // side(2) = coincident side with level-set interface
1290 
1291  e2->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+2)%n_nodes));
1292  e2->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+3)%n_nodes));
1293  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr(ref_node));
1294 
1295  _elem_sides_on_interface[e2] = 2;
1296 
1297  if (ref_side_node0_positive) {
1298 
1299  _positive_phi_elems.push_back(e1);
1300  _negative_phi_elems.push_back(e2);
1301  }
1302  else {
1303 
1304  _positive_phi_elems.push_back(e2);
1305  _negative_phi_elems.push_back(e1);
1306  }
1307  }
1308  break;
1309 
1310 
1311  case MAST::NODE_AND_TWO_EDGES: {
1312 
1313  // create a new node at each intersection: second on ref_side+1
1314  xi_other = side_intersection[(ref_side+1)%n_nodes].second;
1315  s.reset(e.side_ptr((ref_side+1)%n_nodes).release());
1316  p = s->point(0) + xi_other * (s->point(1) - s->point(0));
1317  nd = new libMesh::Node(p);
1318  nd->set_id(_max_mesh_node_id + node_id_incr);
1319  node_id_incr++;
1320  _new_nodes.push_back(nd);
1321  side_p0 = side_nondim_points[(ref_side+1)%n_nodes].first;
1322  side_p1 = side_nondim_points[(ref_side+1)%n_nodes].second;
1323  _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0);
1324 
1325 
1326  libMesh::Elem
1327  *e_p = const_cast<libMesh::Elem*>(&e),
1328  *e1 = libMesh::Elem::build(libMesh::TRI3, e_p).release(),
1329  *e2 = libMesh::Elem::build(libMesh::TRI3, e_p).release(),
1330  *e3 = libMesh::Elem::build(libMesh::TRI3, e_p).release(),
1331  *e4 = libMesh::Elem::build(libMesh::TRI3, e_p).release();
1332 
1333  _new_elems.push_back(e1);
1334  _new_elems.push_back(e2);
1335  _new_elems.push_back(e3);
1336  _new_elems.push_back(e4);
1337 
1338  // create the elements. We create the elements such that:
1339  // node(0) = elem node (ref_node)
1340  // node(1) = elem node (ref_node+1)%n_nodes
1341  // node(2) = new node on ref_side%n_nodes
1342  // this way, the element has the same orientation as the original
1343  // element and the sides have the following association:
1344  // side(0) = coincident with ref_node of original element
1345  // side(1) = coincident side with ref_node+1 of original element
1346  // side(2) = coincident side with level-set interface
1347 
1348  e1->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1349  e1->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+1)%n_nodes));
1350  e1->set_node(2) = _new_nodes[0];
1351 
1352  _elem_sides_on_interface[e1] = 2;
1353 
1354 
1355  // create the elements. We create the elements such that:
1356  // node(0) = elem node (ref_node)
1357  // node(1) = new node on (ref_side+1)%n_nodes
1358  // node(2) = elem node (ref_node+3)%n_nodes
1359  // this way, the element has the same orientation as the original
1360  // element and the sides have the following association:
1361  // side(0) = coincident side with level-set interface
1362  // side(1) = coincident side with (ref_side+1)%n_nodes of original element
1363  // side(2) = coincident side with (ref_side+2)%n_nodes of original element
1364 
1365  e2->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1366  e2->set_node(1) = _new_nodes[1];
1367  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+3)%n_nodes));
1368 
1369  _elem_sides_on_interface[e2] = 0;
1370 
1371 
1372  // create a third element and set its nodes so that:
1373  // node(0) = elem node ref_node
1374  // node(1) = new node on (ref_side)%n_nodes
1375  // node(2) = elem node (ref_node+2)%n_nodes
1376  // this way, the element has the same orientation as the original
1377  // element and the sides have the following association:
1378  // side(0) = coincident side with level-set interface
1379  // side(1) = coincident side with (ref_side)%n_sides of original element
1380  // side(2) = diagonal between elem nodes (ref_node) and (ref_node+2)%n_sides
1381 
1382  e3->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1383  e3->set_node(1) = _new_nodes[0];
1384  e3->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+2)%n_nodes));
1385 
1386  _elem_sides_on_interface[e3] = 0;
1387 
1388 
1389  // create a fourth element and set its nodes so that:
1390  // node(0) = elem node ref_node
1391  // node(1) = new node on (ref_side)%n_nodes
1392  // node(2) = elem node (ref_node+2)%n_nodes
1393  // this way, the element has the same orientation as the original
1394  // element and the sides have the following association:
1395  // side(0) = coincident side with level-set interface
1396  // side(1) = coincident side with (ref_side)%n_sides of original element
1397  // side(2) = diagonal between elem nodes (ref_node) and (ref_node+2)%n_sides
1398 
1399  e4->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr((ref_node)%n_nodes));
1400  e4->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_node+2)%n_nodes));
1401  e4->set_node(2) = _new_nodes[1];
1402 
1403  _elem_sides_on_interface[e4] = 2;
1404 
1405 
1406  // node0 is the first node of ref_side
1407  if (ref_side_node0_positive) {
1408 
1409  _positive_phi_elems.push_back(e1);
1410  _positive_phi_elems.push_back(e2);
1411  _negative_phi_elems.push_back(e3);
1412  _negative_phi_elems.push_back(e4);
1413  }
1414  else {
1415 
1416  _positive_phi_elems.push_back(e3);
1417  _positive_phi_elems.push_back(e4);
1418  _negative_phi_elems.push_back(e1);
1419  _negative_phi_elems.push_back(e2);
1420  }
1421  }
1422  break;
1423 
1424  case MAST::TWO_ADJACENT_EDGES: {
1425 
1426  // create a new node at each intersection: second on ref_side+1
1427  for (unsigned int i=1; i<4; i++) {
1428 
1429  xi_other = side_intersection[(ref_side+i)%n_nodes].second;
1430  s.reset(e.side_ptr((ref_side+i)%n_nodes).release());
1431  p = s->point(0) + xi_other * (s->point(1) - s->point(0));
1432  nd = new libMesh::Node(p);
1433  nd->set_id(_max_mesh_node_id + node_id_incr);
1434  node_id_incr++;
1435  _new_nodes.push_back(nd);
1436  side_p0 = side_nondim_points[(ref_side+i)%n_nodes].first;
1437  side_p1 = side_nondim_points[(ref_side+i)%n_nodes].second;
1438  _node_local_coords[nd] = side_p0 + xi_other * (side_p1 - side_p0);
1439  }
1440 
1441  // the algorithm of identifying the ref_side always ensures that the
1442  // region with xi > x0 on ref_side will be triangular. The other side
1443  // will be modeled with two QUAD4 elements
1444 
1445  libMesh::Elem
1446  *e_p = const_cast<libMesh::Elem*>(&e),
1447  *e1 = libMesh::Elem::build( libMesh::TRI3, e_p).release(),
1448  *e2 = libMesh::Elem::build( libMesh::TRI3, e_p).release(),
1449  *e3 = libMesh::Elem::build(libMesh::QUAD4, e_p).release(),
1450  *e4 = libMesh::Elem::build(libMesh::QUAD4, e_p).release();
1451 
1452  _new_elems.push_back(e1);
1453  _new_elems.push_back(e2);
1454  _new_elems.push_back(e3);
1455  _new_elems.push_back(e4);
1456 
1457  // create the elements. We create the elements such that:
1458  // node(0) = new node on ref_side
1459  // node(1) = elem node (ref_side+1)%n_sides
1460  // node(2) = new node on (ref_side+1)%n_sides
1461  // this way, the element has the same orientation as the original
1462  // element and the sides have the following association:
1463  // side(0) = coincident with ref_side of original element
1464  // side(1) = coincident side with ref_side+1 of original element
1465  // side(2) = coincident side with level-set interface
1466 
1467  e1->set_node(0) = _new_nodes[0];
1468  e1->set_node(1) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+1)%n_nodes));
1469  e1->set_node(2) = _new_nodes[1];
1470 
1471  _elem_sides_on_interface[e1] = 2;
1472 
1473 
1474  // create the second element. We create the elements such that:
1475  // node(0) = new node on ref_side+3
1476  // node(1) = new node on (ref_side+2)%n_sides
1477  // node(2) = elem node (ref_side+3)%n_sides
1478  // this way, the element has the same orientation as the original
1479  // element and the sides have the following association:
1480  // side(0) = coincident with ref_side of original element
1481  // side(1) = coincident side with ref_side+1 of original element
1482  // side(2) = coincident side with level-set interface
1483 
1484  e2->set_node(0) = _new_nodes[3];
1485  e2->set_node(1) = _new_nodes[2];
1486  e2->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+3)%n_nodes));
1487 
1488  _elem_sides_on_interface[e2] = 0;
1489 
1490 
1491  // create a third element and set its nodes so that:
1492  // node(0) = elem node ref_side
1493  // node(1) = new node on (ref_side)%n_nodes
1494  // node(2) = new node on (ref_side+2)%n_nodes
1495  // node(3) = new node on (ref_side+3)%n_nodes
1496  // this way, the element has the same orientation as the original
1497  // element and the sides have the following association:
1498  // side(0) = coincident side with ref_side of original element
1499  // side(1) = new edge connecting new node on ref_side to new node on ref_side+2
1500  // side(2) = coincident side with level-set interface
1501  // side(3) = coincident side with (ref_side+3)%n_sides of original element
1502 
1503  e3->set_node(0) = const_cast<libMesh::Node*>(e.node_ptr(ref_side));
1504  e3->set_node(1) = _new_nodes[0];
1505  e3->set_node(2) = _new_nodes[2];
1506  e3->set_node(3) = _new_nodes[3];
1507 
1508  _elem_sides_on_interface[e3] = 2;
1509 
1510  // create a fourth element and set its nodes so that:
1511  // node(0) = new node on ref_side
1512  // node(1) = new node on ref_side+1
1513  // node(2) = elem node ref_side+2
1514  // node(3) = new node on ref_side+2
1515  // this way, the element has the same orientation as the original
1516  // element and the sides have the following association:
1517  // side(0) = coincident side with level-set interface
1518  // side(1) = coincident side with (ref_side+1)%n_sides of original element
1519  // side(2) = coincident side with (ref_side+2)%n_sides of original element
1520  // side(3) = new edge connecting new node on ref_side to new node on ref_side+2
1521 
1522  e4->set_node(0) = _new_nodes[0];
1523  e4->set_node(1) = _new_nodes[1];
1524  e4->set_node(2) = const_cast<libMesh::Node*>(e.node_ptr((ref_side+2)%n_nodes));
1525  e4->set_node(3) = _new_nodes[2];
1526 
1527  _elem_sides_on_interface[e4] = 0;
1528 
1529  if (ref_side_node0_positive) {
1530 
1531  _positive_phi_elems.push_back(e3);
1532  _positive_phi_elems.push_back(e4);
1533  _negative_phi_elems.push_back(e1);
1534  _negative_phi_elems.push_back(e2);
1535  }
1536  else {
1537 
1538  _positive_phi_elems.push_back(e1);
1539  _positive_phi_elems.push_back(e2);
1540  _negative_phi_elems.push_back(e3);
1541  _negative_phi_elems.push_back(e4);
1542  }
1543  }
1544  break;
1545 
1546  default:
1547  // should not get here
1548  libmesh_error();
1549  }
1550 }
1551 
1552 
1553 
1554 Real
1556 (const libMesh::Point& p0,
1557  const libMesh::Point& p1,
1558  const MAST::FieldFunction<Real>& phi,
1559  const Real t) {
1560 
1561  // this uses the bisection search to find the location of intersection
1562  libMesh::Point
1563  pt,
1564  pt0 = p0,
1565  pt1 = p1;
1566 
1567  Real
1568  xi = 0.,
1569  v = 1.e10, // arbitrarily high value to get the algorithm going
1570  v0 = 0.,
1571  v1 = 0.;
1572 
1573  phi(pt0, t, v0);
1574  phi(pt1, t, v1);
1575 
1576  unsigned int
1577  n_iters = 0;
1578 
1579  //
1580  // a0 + a1 xi = 0 (a0 = v0, a1 = (v1-v0))
1581  // or, xi = -a0/a1
1582  //
1583 
1584  while (fabs(v) > _tol &&
1585  n_iters < _max_iters) {
1586 
1587  xi = -v0 / (v1-v0);
1588  pt = pt0 + (pt1 - pt0)*xi;
1589 
1590  phi(pt, t, v);
1591 
1592  if (v*v1 < 0.) {
1593 
1594  v0 = v;
1595  pt0 = pt;
1596  }
1597  else {
1598 
1599  v1 = v;
1600  pt1 = pt;
1601  }
1602 
1603  n_iters++;
1604  }
1605 
1606  // now find the xi location based on the distance of the new
1607  // point from the old points
1608  pt -= p0;
1609  xi = pt.norm();
1610  pt = p1-p0;
1611  xi /= pt.norm();
1612 
1613  return xi;
1614 }
1615 
1616 
1617 const libMesh::Point&
1619 (const libMesh::Node& n) const {
1620 
1621  libmesh_assert(_initialized);
1622 
1623  std::map<const libMesh::Node*, libMesh::Point>::const_iterator
1624  it = _node_local_coords.find(&n);
1625 
1626  libmesh_assert(it != _node_local_coords.end());
1627 
1628  return it->second;
1629 }
1630 
MAST::LevelSet2DIntersectionMode _mode
Real _find_intersection_on_straight_edge(const libMesh::Point &p0, const libMesh::Point &p1, const MAST::FieldFunction< Real > &phi, const Real t)
bool _if_elem_on_negative_phi
true if element is completely on the negative side of level set with no intersection ...
unsigned int edge_on_boundary() const
unsigned int get_side_on_interface(const libMesh::Elem &e) const
std::unique_ptr< libMesh::Elem > _first_order_elem(const libMesh::Elem &e)
creates a first order element from the given high-order element.
bool _if_elem_on_positive_phi
true if element is completely on the positive side of level set with no intersection ...
std::map< const libMesh::Elem *, int > _elem_sides_on_interface
unsigned int node_on_boundary() const
const libMesh::Point & get_nondimensional_coordinate_for_node(const libMesh::Node &n) const
const std::vector< const libMesh::Elem * > & get_sub_elems_negative_phi() const
void _add_node_local_coords(const libMesh::Elem &e, std::vector< std::pair< libMesh::Point, libMesh::Point > > &side_nondim_points, std::map< const libMesh::Node *, libMesh::Point > &node_coord_map)
MAST::LevelSet2DIntersectionMode get_intersection_mode() const
void _find_quad4_intersections(const MAST::FieldFunction< Real > &phi, const libMesh::Elem &e, const Real t, const std::map< const libMesh::Node *, std::pair< Real, bool > > &node_phi_vals)
libMesh::Real Real
void init(const MAST::FieldFunction< Real > &phi, const libMesh::Elem &e, const Real t, unsigned int max_elem_id, unsigned int max_node_id)
std::vector< libMesh::Elem * > _new_elems
const libMesh::Elem & elem() const
std::vector< const libMesh::Elem * > _positive_phi_elems
Real get_node_phi_value(const libMesh::Node *n) const
std::map< const libMesh::Node *, libMesh::Point > _node_local_coords
std::vector< libMesh::Node * > _new_nodes
bool has_side_on_interface(const libMesh::Elem &e) const
void get_corner_nodes_on_negative_phi(std::set< const libMesh::Node * > &nodes) const
const std::vector< const libMesh::Elem * > & get_sub_elems_positive_phi() const
std::vector< const libMesh::Elem * > _negative_phi_elems
std::map< const libMesh::Node *, std::pair< Real, bool > > _node_phi_vals
void _init_on_first_order_ref_elem(const MAST::FieldFunction< Real > &phi, const libMesh::Elem &e, const Real t)
initializes on a reference element that is a first-order counterpart of the given high-order element...
void clear()
clears the data structures