tet_mesh.h
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 // Common base class for all Tet Meshes
31 #ifndef OOMPH_TETMESH_HEADER
32 #define OOMPH_TETMESH_HEADER
33 
34 // Config header generated by autoconfig
35 #ifdef HAVE_CONFIG_H
36  #include <oomph-lib-config.h>
37 #endif
38 
39 //oomph-lib includes
40 #include "Vector.h"
41 #include "nodes.h"
42 #include "matrices.h"
43 #include "mesh.h"
44 #include "geom_obj_with_boundary.h"
45 
46 namespace oomph
47 {
48 
49 
50 //=======================================================================
51 /// Vertex for Tet mesh generation. Can lie on multiple boundaries
52 /// (identified via one-based enumeration!) and can have intrinisic
53 /// coordinates in a DiskLikeGeomObjectWithBoundaries.
54 //=======================================================================
56 {
57  public:
58 
59  /// Only friends can set boundary ID -- the facet is my only friend!
60  friend class TetMeshFacet;
61 
62  /// Constructor: Pass coordinates (length 3!)
64  {
65 #ifdef PARANOID
66  if (X.size()!=3)
67  {
68  std::ostringstream error_stream;
69  error_stream
70  << "TetMeshVertex should only be used in 3D!\n"
71  << "Your Vector of coordinates, contains data for "
72  << x.size()
73  << "-dimensional coordinates." << std::endl;
74  throw OomphLibError(error_stream.str(),
75  OOMPH_CURRENT_FUNCTION,
76  OOMPH_EXCEPTION_LOCATION);
77  }
78 #endif
79  }
80 
81 
82  /// \short Set intrinisic coordinates in GeomObject
84  {
85 #ifdef PARANOID
86  if (zeta.size()!=2)
87  {
88  std::ostringstream error_stream;
89  error_stream
90  << "TetMeshVertex should only be used in 3D!\n"
91  << "Your Vector of intrinisic coordinates, contains data for "
92  << zeta.size()
93  << "-dimensional coordinates but should be 2!" << std::endl;
94  throw OomphLibError(error_stream.str(),
95  OOMPH_CURRENT_FUNCTION,
96  OOMPH_EXCEPTION_LOCATION);
97  }
98 #endif
99 
100  Zeta_in_geom_object=zeta;
101  }
102 
103  /// \short Get intrinisic coordinates in GeomObject (returns zero sized
104  /// vector if no such coordinates have been specified)
106  {
107  return Zeta_in_geom_object;
108  }
109 
110  /// i-th coordinate
111  double x(const unsigned& i) const
112  {
113  return X[i];
114  }
115 
116  /// First (of possibly multiple) one-based boundary id
117  unsigned one_based_boundary_id() const
118  {
119  if (One_based_boundary_id.size()==0)
120  {
121  return 0;
122  }
123  return *(One_based_boundary_id.begin());
124  }
125 
126  private:
127 
128  /// Set of (one-based!) boundary IDs this vertex lives on
129  void set_one_based_boundary_id(const unsigned& id)
130  {
131  One_based_boundary_id.insert(id);
132  }
133 
134  /// Coordinate vector
136 
137  /// Set of (one-based!) boundary IDs this vertex lives on
138  std::set<unsigned> One_based_boundary_id;
139 
140  /// \short Intrinisic coordinates in GeomObject (zero sized if there
141  /// isn't one.
143 
144 
145 };
146 
147 ////////////////////////////////////////////////////////////////////////
148 ////////////////////////////////////////////////////////////////////////
149 ////////////////////////////////////////////////////////////////////////
150 
151 
152 //=======================================================================
153 /// Facet for Tet mesh generation. Can lie on boundary
154 /// (identified via one-based enumeration!) and can have
155 /// GeomObject associated with those boundaries.
156 //=======================================================================
158 {
159  public:
160 
161  /// Constructor: Specify number of vertices
162  TetMeshFacet(const unsigned& nvertex) :
163  One_based_boundary_id(0), // Initialision implies not on any boundary
164  One_based_region_id_that_facet_is_embedded_in(0) // Initialisation implies
165  // not embedded in any region
166  {
167  Vertex_pt.resize(nvertex,0);
168  }
169 
170  /// Number of vertices
171  unsigned nvertex() const
172  {
173  return Vertex_pt.size();
174  }
175 
176  /// Pointer to j-th vertex (const)
177  TetMeshVertex* vertex_pt(const unsigned& j) const
178  {
179  return Vertex_pt[j];
180  }
181 
182  /// Set pointer to j-th vertex and pass one-based boundary id that
183  /// may already have been set for this facet.
184  void set_vertex_pt(const unsigned& j, TetMeshVertex* vertex_pt)
185  {
186  Vertex_pt[j]=vertex_pt;
187  // If not set yet, this is harmless since it simply over-writes
188  // the dummy value in vertex
189  TetMeshVertex* v_pt=Vertex_pt[j];
190  if (v_pt!=0)
191  {
193  }
194  }
195 
196  /// Constant access to (one-based!) boundary IDs this facet lives on
197  unsigned one_based_boundary_id() const
198  {
199  return One_based_boundary_id;
200  }
201 
202  /// \short Set (one-based!) boundary IDs this facet lives on. Passed to any
203  /// existing vertices and to any future ones
204  void set_one_based_boundary_id(const unsigned& one_based_id)
205  {
206  One_based_boundary_id=one_based_id;
207  unsigned nv=Vertex_pt.size();
208  for (unsigned j=0;j<nv;j++)
209  {
210  TetMeshVertex* v_pt=Vertex_pt[j];
211  if (v_pt!=0)
212  {
213  v_pt->set_one_based_boundary_id(one_based_id);
214  }
215  }
216  }
217 
218  /// \short Set (one-based!) region ID this facet is adjacent to.
219  /// Specification of zero argument is harmless as it indicates that
220  ///that facet is not adjacent to any region.
221  void set_one_based_adjacent_region_id(const unsigned& one_based_id)
222  {
223  One_based_adjacent_region_id.insert(one_based_id);
224  }
225 
226  /// Return set of (one-based!) region IDs this facet is adjacent to
227  std::set<unsigned> one_based_adjacent_region_id() const
228  {
229  return One_based_adjacent_region_id;
230  }
231 
232  /// Boolean indicating that facet is embedded in a specified region
234  {
235  return (One_based_region_id_that_facet_is_embedded_in!=0);
236  }
237 
238  /// Facet is to be embedded in specified one-based region
240  one_based_region_id)
241  {
242  One_based_region_id_that_facet_is_embedded_in=one_based_region_id;
243  }
244 
245  /// Which (one-based) region is facet embedded in (if zero, it's not
246  /// embedded in any region)
248  {
249  return One_based_region_id_that_facet_is_embedded_in;
250  }
251 
252  /// Output
253  void output(std::ostream &outfile) const
254  {
255  unsigned n=Vertex_pt.size();
256  outfile << "ZONE I=" << n+1 << std::endl;
257  for (unsigned j=0;j<n;j++)
258  {
259  outfile << Vertex_pt[j]->x(0) << " "
260  << Vertex_pt[j]->x(1) << " "
261  << Vertex_pt[j]->x(2) << " "
263  << std::endl;
264  }
265  outfile << Vertex_pt[0]->x(0) << " "
266  << Vertex_pt[0]->x(1) << " "
267  << Vertex_pt[0]->x(2) << " "
269  << std::endl;
270  }
271 
272  private:
273 
274  /// Pointer to vertices
276 
277  /// (One-based!) boundary IDs this facet lives on
279 
280  /// \short Set of one-based adjacent region ids; no adjacent region if
281  /// it's zero.
282  std::set<unsigned> One_based_adjacent_region_id;
283 
284 
285  /// \short Facet is to be embedded in specified one-based region.
286  /// Defaults to zero, indicating that its not embedded.
288 
289 };
290 
291 
292 
293 ////////////////////////////////////////////////////////////////////////
294 ////////////////////////////////////////////////////////////////////////
295 ////////////////////////////////////////////////////////////////////////
296 
297 
298 //========================================================================
299 /// Base class for tet mesh boundary defined by polygonal
300 /// planar facets
301 //========================================================================
303 {
304  public:
305 
306  ///\short Constructor:
307  TetMeshFacetedSurface(): Boundaries_can_be_split_in_tetgen(true),
308  Geom_object_with_boundaries_pt(0)
309  {}
310 
311  /// Empty destructor
313 
314  /// Number of vertices
315  unsigned nvertex() const
316  {
317  return Vertex_pt.size();
318  }
319 
320  /// Number of facets
321  unsigned nfacet() const
322  {
323  return Facet_pt.size();
324  }
325 
326  /// One-based boundary id of j-th facet
327  unsigned one_based_facet_boundary_id(const unsigned &j) const
328  {
329  return Facet_pt[j]->one_based_boundary_id();
330  }
331 
332  /// First (of possibly multiple) one-based boundary id of j-th vertex
333  unsigned one_based_vertex_boundary_id(const unsigned &j) const
334  {
335  return Vertex_pt[j]->one_based_boundary_id();
336  }
337 
338  /// i-th coordinate of j-th vertex
339  double vertex_coordinate(const unsigned &j, const unsigned &i) const
340  {
341  return Vertex_pt[j]->x(i);
342  }
343 
344  /// Number of vertices defining the j-th facet
345  unsigned nvertex_on_facet(const unsigned &j) const
346  {
347  return Facet_pt[j]->nvertex();
348  }
349 
350  /// Test whether boundary can be split in tetgen
352  {
353  return Boundaries_can_be_split_in_tetgen;
354  }
355 
356  /// Test whether boundaries can be split in tetgen
358  {
359  Boundaries_can_be_split_in_tetgen=true;
360  }
361 
362  /// Test whether boundaries can be split in tetgen
364  {
365  Boundaries_can_be_split_in_tetgen=false;
366  }
367 
368  /// Pointer to j-th facet
369  TetMeshFacet* facet_pt(const unsigned& j) const
370  {
371  return Facet_pt[j];
372  }
373 
374  /// Pointer to j-th vertex
375  TetMeshVertex* vertex_pt(const unsigned& j) const
376  {
377  return Vertex_pt[j];
378  }
379 
380  /// \short Access to GeomObject with boundaries associated with this
381  /// surface (Null if there isn't one!)
383  {
384  return Geom_object_with_boundaries_pt;
385  }
386 
387  /// Output
388  void output(std::ostream &outfile) const
389  {
390  unsigned n=Facet_pt.size();
391  for (unsigned j=0;j<n;j++)
392  {
393  Facet_pt[j]->output(outfile);
394  }
395  }
396 
397 
398  /// Output
399  void output(const std::string& filename) const
400  {
401  std::ofstream outfile;
402  outfile.open(filename.c_str());
403  output(outfile);
404  outfile.close();
405  }
406 
407  /// \short Virtual function that specifies the variation of the
408  /// zeta coordinates in the GeomObject along the
409  /// boundary connecting vertices 0 and 1 in
410  /// facet facet_id. Default implementation: Linear interpolation
411  /// between edges. zeta_boundary=0.0: we're on vertex 0;
412  /// zeta_boundary=1.0: we're on vertex 1.
413  virtual void boundary_zeta01(const unsigned& facet_id,
414  const double& zeta_boundary,
415  Vector<double>& zeta)
416  {
417  Vector<Vector<double> > zeta_vertex(2);
418  zeta_vertex[0]=Facet_pt[facet_id]->vertex_pt(0)->zeta_in_geom_object();
419  zeta_vertex[1]=Facet_pt[facet_id]->vertex_pt(1)->zeta_in_geom_object();
420  zeta[0]=zeta_vertex[0][0]+(zeta_vertex[1][0]-zeta_vertex[0][0])*zeta_boundary;
421  zeta[1]=zeta_vertex[0][1]+(zeta_vertex[1][1]-zeta_vertex[0][1])*zeta_boundary;
422  }
423 
424  /// \short Virtual function that specifies the variation of the
425  /// zeta coordinates in the GeomObject along the
426  /// boundary connecting vertices 1 and 2 in
427  /// facet facet_id. Default implementation: Linear interpolation
428  /// between edges. zeta_boundary=0.0: we're on vertex 1;
429  /// zeta_boundary=1.0: we're on vertex 2.
430  virtual void boundary_zeta12(const unsigned& facet_id,
431  const double& zeta_boundary,
432  Vector<double>& zeta)
433  {
434  Vector<Vector<double> > zeta_vertex(2);
435  zeta_vertex[0]=Facet_pt[facet_id]->vertex_pt(1)->zeta_in_geom_object();
436  zeta_vertex[1]=Facet_pt[facet_id]->vertex_pt(2)->zeta_in_geom_object();
437  zeta[0]=zeta_vertex[0][0]+(zeta_vertex[1][0]-zeta_vertex[0][0])*zeta_boundary;
438  zeta[1]=zeta_vertex[0][1]+(zeta_vertex[1][1]-zeta_vertex[0][1])*zeta_boundary;
439  }
440 
441  /// \short Virtual function that specifies the variation of the
442  /// zeta coordinates in the GeomObject along the
443  /// boundary connecting vertices 2 and 0 in
444  /// facet facet_id. Default implementation: Linear interpolation
445  /// between edges. zeta_boundary=0.0: we're on vertex 2;
446  /// zeta_boundary=1.0: we're on vertex 0.
447  virtual void boundary_zeta20(const unsigned& facet_id,
448  const double& zeta_boundary,
449  Vector<double>& zeta)
450  {
451  Vector<Vector<double> > zeta_vertex(2);
452  zeta_vertex[0]=Facet_pt[facet_id]->vertex_pt(2)->zeta_in_geom_object();
453  zeta_vertex[1]=Facet_pt[facet_id]->vertex_pt(0)->zeta_in_geom_object();
454  zeta[0]=zeta_vertex[0][0]+(zeta_vertex[1][0]-zeta_vertex[0][0])*zeta_boundary;
455  zeta[1]=zeta_vertex[0][1]+(zeta_vertex[1][1]-zeta_vertex[0][1])*zeta_boundary;
456  }
457 
458 
459  /// \short Facet connectivity: vertex_index[j] is the index of the
460  /// j-th vertex (in the Vertex_pt vector) in facet f. Bit of an obscure
461  /// functionality that's only needed for setup tetgen_io.
463  {
464  if (Facet_vertex_index_in_tetgen.size()!=nfacet())
465  {
466  setup_facet_connectivity_for_tetgen();
467  }
468  return Facet_vertex_index_in_tetgen[f];
469  }
470 
471 protected:
472 
473  /// Vector pointers to vertices
475 
476  /// Vector of pointers to facets
478 
479  /// \short Boolean to indicate whether extra vertices can be added
480  /// on the boundary in tetgen
482 
483  /// \short Facet connectivity: Facet_vertex_index[f][j] is the index of the
484  /// j-th vertex (in the Vertex_pt vector) in facet f.
486 
487  /// \short GeomObject with boundaries associated with this surface
489 
490 
491  private:
492 
493 
494  /// Setup facet connectivity for tetgen
496  {
497  unsigned nv_overall=Vertex_pt.size();
498  unsigned nf=nfacet();
499  Facet_vertex_index_in_tetgen.resize(nf);
500  for (unsigned f=0;f<nf;f++)
501  {
502  unsigned nv=Facet_pt[f]->nvertex();
503  Facet_vertex_index_in_tetgen[f].resize(nv);
504  for (unsigned v=0;v<nv;v++)
505  {
506  TetMeshVertex* my_vertex_pt=Facet_pt[f]->vertex_pt(v);
507  for (unsigned j=0;j<nv_overall;j++)
508  {
509  if (my_vertex_pt==Vertex_pt[j])
510  {
511  Facet_vertex_index_in_tetgen[f][v]=j;
512  break;
513  }
514  }
515  }
516  }
517  }
518 
519 
520 };
521 
522 
523 
524 ////////////////////////////////////////////////////////////////////////
525 ////////////////////////////////////////////////////////////////////////
526 ////////////////////////////////////////////////////////////////////////
527 
528 
529 
530 //========================================================================
531 /// Base class for closed tet mesh boundary bounded by polygonal
532 /// planar facets
533 //========================================================================
535 {
536  public:
537 
538  ///\short Constructor:
540  Faceted_volume_represents_hole_for_gmsh(false)
541  {}
542 
543  /// Empty destructor
545 
546  /// Declare closed surface to represent hole for gmsh
548  {
549  Faceted_volume_represents_hole_for_gmsh=true;
550  }
551 
552  /// Declare closed surface NOT to represent hole for gmsh
554  {
555  Faceted_volume_represents_hole_for_gmsh=false;
556  }
557 
558  /// Does closed surface represent hole for gmsh?
560  {
561  return Faceted_volume_represents_hole_for_gmsh;
562  }
563 
564  /// i=th coordinate of the j-th internal point for tetgen
565  const double &internal_point_for_tetgen(const unsigned& j,
566  const unsigned &i) const
567  {
568  return (Internal_point_for_tetgen[j].first)[i];
569  }
570 
571  /// Specify coordinate of hole for tetgen
572  void set_hole_for_tetgen(const Vector<double> &hole_point)
573  {
574  Internal_point_for_tetgen.push_back(std::make_pair(hole_point,-1));
575  }
576 
577  /// \short Specify a region; pass (zero-based) region ID and coordinate
578  /// of point in region for tetgen
579  void set_region_for_tetgen(const unsigned& region_id,
580  const Vector<double> &region_point)
581  {
582  Internal_point_for_tetgen.push_back(std::make_pair(region_point,region_id));
583  }
584 
585  /// \short Number of internal points (identifying either regions or holes)
586  /// for tetgen
588  {
589  return Internal_point_for_tetgen.size();
590  }
591 
592  /// \short Return the (zero-based) region ID of j-th internal point for tetgen.
593  /// Negative if it's actually a hole.
594  const int &region_id_for_tetgen(const unsigned& j) const
595  {
596  return Internal_point_for_tetgen[j].second;
597  }
598 
599 
600  /// Is j-th internal point for tetgen associated with a hole?
602  {
603  return (Internal_point_for_tetgen[j].second<0);
604  }
605 
606  /// Is j-th internal point for tetgen associated with a region?
608  {
609  return (Internal_point_for_tetgen[j].second>=0);
610  }
611 
612 
613  private:
614 
615  /// Storage for internal points for tetgen. Stores pair of:
616  /// -- Vector containing coordinates of internal point
617  /// -- region ID (negative if it's a hole)
619 
620  /// Does closed surface represent hole for gmsh?
622 
623 };
624 
625 ///////////////////////////////////////////////////////////////////////
626 ///////////////////////////////////////////////////////////////////////
627 ///////////////////////////////////////////////////////////////////////
628 
629 
630 
631 ///////////////////////////////////////////////////////////////////////
632 ///////////////////////////////////////////////////////////////////////
633 ///////////////////////////////////////////////////////////////////////
634 
635 
636 
637 ///////////////////////////////////////////////////////////////////////
638 ///////////////////////////////////////////////////////////////////////
639 ///////////////////////////////////////////////////////////////////////
640 
641 
642 //================================================================
643 /// Base class for tet meshes (meshes made of 3D tet elements).
644 //================================================================
645 class TetMeshBase : public virtual Mesh
646 {
647 
648 public:
649 
650  /// Constructor
651  TetMeshBase() : Outer_boundary_pt(0){}
652 
653  /// Broken copy constructor
654  TetMeshBase(const TetMeshBase& node)
655  {
656  BrokenCopy::broken_copy("TetMeshBase");
657  }
658 
659  /// Broken assignment operator
660  void operator=(const TetMeshBase&)
661  {
662  BrokenCopy::broken_assign("TetMeshBase");
663  }
664 
665  /// Destructor (empty)
666  virtual ~TetMeshBase(){}
667 
668  /// \short Global static data that specifies the permitted
669  /// error in the setup of the boundary coordinates
671 
672  /// \short Assess mesh quality: Ratio of max. edge length to min. height,
673  /// so if it's very large it's BAAAAAD.
674  void assess_mesh_quality(std::ofstream& some_file);
675 
676  /// \short Setup boundary coordinate on boundary b which is
677  /// assumed to be planar. Boundary coordinates are the
678  /// x-y coordinates in the plane of that boundary, with the
679  /// x-axis along the line from the (lexicographically)
680  /// "lower left" to the "upper right" node. The y axis
681  /// is obtained by taking the cross-product of the positive
682  /// x direction with the outer unit normal computed by
683  /// the face elements (or its negative if switch_normal is set
684  /// to true). Doc faces in output file (if it's open).
685  ///
686  /// Note 1: Setup of boundary coordinates is not done if the boundary in
687  /// question turns out to be nonplanar.
688  ///
689  /// Note 2: If a triangular TetMeshFacet is associated with a boundary we also
690  /// store the boundary coordinates of its vertices. They are needed
691  /// to interpolated intrinsic coordinates of an associated GeomObject
692  /// (if any) into the interior.
693  template<class ELEMENT>
694  void setup_boundary_coordinates(const unsigned& b)
695  {
696  // Dummy file
697  std::ofstream some_file;
698 
699  // Don't switch the normal
700  bool switch_normal=false;
701  this->setup_boundary_coordinates<ELEMENT>
702  (b,switch_normal,some_file);
703  }
704 
705  /// \short Setup boundary coordinate on boundary b which is
706  /// assumed to be planar. Boundary coordinates are the
707  /// x-y coordinates in the plane of that boundary, with the
708  /// x-axis along the line from the (lexicographically)
709  /// "lower left" to the "upper right" node. The y axis
710  /// is obtained by taking the cross-product of the positive
711  /// x direction with the outer unit normal computed by
712  /// the face elements (or its negative if switch_normal is set
713  /// to true). Doc faces in output file (if it's open).
714  ///
715  /// Note 1: Setup of boundary coordinates is not done if the boundary in
716  /// question turns out to be nonplanar.
717  ///
718  /// Note 2: If a triangular TetMeshFacet is associated with a boundary we also
719  /// store the boundary coordinates of its vertices. They are needed
720  /// to interpolated intrinsic coordinates of an associated GeomObject
721  /// (if any) into the interior.
722  /// Final boolean argument allows switching of the direction of the outer
723  /// unit normal.
724  template<class ELEMENT>
725  void setup_boundary_coordinates(const unsigned& b, const bool& switch_normal)
726  {
727  // Dummy file
728  std::ofstream some_file;
729 
730  this->setup_boundary_coordinates<ELEMENT>
731  (b,switch_normal,some_file);
732  }
733 
734 
735  /// \short Setup boundary coordinate on boundary b which is
736  /// assumed to be planar. Boundary coordinates are the
737  /// x-y coordinates in the plane of that boundary, with the
738  /// x-axis along the line from the (lexicographically)
739  /// "lower left" to the "upper right" node. The y axis
740  /// is obtained by taking the cross-product of the positive
741  /// x direction with the outer unit normal computed by
742  /// the face elements (or its negative if switch_normal is set
743  /// to true). Doc faces in output file (if it's open).
744  ///
745  /// Note 1: Setup of boundary coordinates is not done if the boundary in
746  /// question turns out to be nonplanar.
747  ///
748  /// Note 2: If a triangular TetMeshFacet is associated with a boundary we also
749  /// store the boundary coordinates of its vertices. They are needed
750  /// to interpolated intrinsic coordinates of an associated GeomObject
751  /// (if any) into the interior.
752  /// Boolean argument allows switching of the direction of the outer
753  /// unit normal. Output file for doc.
754  template<class ELEMENT>
755  void setup_boundary_coordinates(const unsigned& b,
756  const bool& switch_normal,
757  std::ofstream& outfile);
758 
759  /// \short Setup boundary coordinate on boundary b which is
760  /// assumed to be planar. Boundary coordinates are the
761  /// x-y coordinates in the plane of that boundary, with the
762  /// x-axis along the line from the (lexicographically)
763  /// "lower left" to the "upper right" node. The y axis
764  /// is obtained by taking the cross-product of the positive
765  /// x direction with the outer unit normal computed by
766  /// the face elements (or its negative if switch_normal is set
767  /// to true). Doc faces in output file (if it's open).
768  ///
769  /// Note 1: Setup of boundary coordinates is not done if the boundary in
770  /// question turns out to be nonplanar.
771  ///
772  /// Note 2: If a triangular TetMeshFacet is associated with a boundary we also
773  /// store the boundary coordinates of its vertices. They are needed
774  /// to interpolated intrinsic coordinates of an associated GeomObject
775  /// (if any) into the interior.
776  /// Output file for doc.
777  template<class ELEMENT>
778  void setup_boundary_coordinates(const unsigned& b,
779  std::ofstream& outfile)
780  {
781  // Don't switch the normal
782  bool switch_normal=false;
783  this->setup_boundary_coordinates<ELEMENT>
784  (b,switch_normal,outfile);
785  }
786 
787 
788  /// Return the number of elements adjacent to boundary b in region r
789  inline unsigned nboundary_element_in_region(const unsigned &b,
790  const unsigned &r) const
791  {
792  //Need to use a constant iterator here to keep the function "const"
793  //Return an iterator to the appropriate entry, if we find it
794  std::map<unsigned,Vector<FiniteElement*> >::const_iterator it =
795  Boundary_region_element_pt[b].find(r);
796  if(it!=Boundary_region_element_pt[b].end())
797  {
798  return (it->second).size();
799  }
800  //Otherwise there are no elements adjacent to boundary b in the region r
801  else
802  {
803  return 0;
804  }
805  }
806 
807  /// Return pointer to the e-th element adjacent to boundary b in region r
809  const unsigned &r,
810  const unsigned &e) const
811  {
812  //Use a constant iterator here to keep function "const" overall
813  std::map<unsigned,Vector<FiniteElement*> >::const_iterator it =
814  Boundary_region_element_pt[b].find(r);
815  if(it!=Boundary_region_element_pt[b].end())
816  {
817  return (it->second)[e];
818  }
819  else
820  {
821  return 0;
822  }
823  }
824 
825  /// Return face index of the e-th element adjacent to boundary b in region r
826  int face_index_at_boundary_in_region(const unsigned &b,
827  const unsigned &r,
828  const unsigned &e) const
829  {
830  //Use a constant iterator here to keep function "const" overall
831  std::map<unsigned,Vector<int> >::const_iterator it =
832  Face_index_region_at_boundary[b].find(r);
833  if(it!=Face_index_region_at_boundary[b].end())
834  {
835  return (it->second)[e];
836  }
837  else
838  {
839  std::ostringstream error_message;
840  error_message << "Face indices not set up for boundary "
841  << b << " in region " << r << "\n";
842  error_message
843  << "This probably means that the boundary is not adjacent to region\n";
844  throw OomphLibError(error_message.str(),
845  OOMPH_CURRENT_FUNCTION,
846  OOMPH_EXCEPTION_LOCATION);
847  }
848  }
849 
850 
851  /// Return the number of regions specified by attributes
852  unsigned nregion()
853  {
854  return Region_element_pt.size();
855  }
856 
857  /// Return the number of elements in region r
858  unsigned nregion_element(const unsigned &r)
859  {
860  unsigned entry=0;
861  bool found=false;
862  unsigned n=Region_attribute.size();
863  for (unsigned i=0;i<n;i++)
864  {
865 #ifdef PARANOID
866  double diff=fabs(
867  Region_attribute[i]-
868  static_cast<double>(static_cast<unsigned>(Region_attribute[i])));
869  if (diff>0.0)
870  {
871  std::ostringstream error_message;
872  error_message
873  << "Region attributes should be unsigneds because we \n"
874  << "only use them to set region ids\n";
875  throw OomphLibError(error_message.str(),
876  OOMPH_CURRENT_FUNCTION,
877  OOMPH_EXCEPTION_LOCATION);
878  }
879 #endif
880  if (static_cast<unsigned>(Region_attribute[i])==r)
881  {
882  entry=i;
883  found=true;
884  break;
885  }
886  }
887  if (found)
888  {
889  return Region_element_pt[entry].size();
890  }
891  else
892  {
893  return 0;
894  }
895  }
896 
897  /// \short Return the i-th region attribute (here only used as the
898  /// (assumed to be unsigned) region id
899  double region_attribute(const unsigned &i)
900  {
901  return Region_attribute[i];
902  }
903 
904  /// Return the e-th element in the r-th region
905  FiniteElement* region_element_pt(const unsigned &r,
906  const unsigned &e)
907  {
908  unsigned entry=0;
909  bool found=false;
910  unsigned n=Region_attribute.size();
911  for (unsigned i=0;i<n;i++)
912  {
913 #ifdef PARANOID
914  double diff=fabs(
915  Region_attribute[i]-
916  static_cast<double>(static_cast<unsigned>(Region_attribute[i])));
917  if (diff>0.0)
918  {
919  std::ostringstream error_message;
920  error_message
921  << "Region attributes should be unsigneds because we \n"
922  << "only use the to set region ids\n";
923  throw OomphLibError(error_message.str(),
924  OOMPH_CURRENT_FUNCTION,
925  OOMPH_EXCEPTION_LOCATION);
926  }
927 #endif
928  if (static_cast<unsigned>(Region_attribute[i])==r)
929  {
930  entry=i;
931  found=true;
932  break;
933  }
934  }
935  if (found)
936  {
937  return Region_element_pt[entry][e];
938  }
939  else
940  {
941  return 0;
942  }
943  }
944 
945  /// \short Snap boundaries specified by the IDs listed in boundary_id to
946  /// a quadratric surface, specified in the file
947  /// quadratic_surface_file_name. This is usually used with vmtk-based
948  /// meshes for which oomph-lib's xda to poly conversion code produces the files
949  /// "quadratic_fsi_boundary.dat" and "quadratic_outer_solid_boundary.dat"
950  /// which specify the quadratic FSI boundary (for the fluid and the solid)
951  /// and the quadratic representation of the outer boundary of the solid.
952  /// When used with these files, the flag switch_normal should be
953  /// set to true when calling the function for the outer boundary of the
954  /// solid. The DocInfo object can be used to label optional output
955  /// files. (Uses directory and label).
956  template<class ELEMENT>
957  void snap_to_quadratic_surface(const Vector<unsigned>& boundary_id,
958  const std::string& quadratic_surface_file_name,
959  const bool& switch_normal,
960  DocInfo& doc_info);
961 
962  /// \short Snap boundaries specified by the IDs listed in boundary_id to
963  /// a quadratric surface, specified in the file
964  /// quadratic_surface_file_name. This is usually used with vmtk-based
965  /// meshes for which oomph-lib's xda to poly conversion code produces the files
966  /// "quadratic_fsi_boundary.dat" and "quadratic_outer_solid_boundary.dat"
967  /// which specify the quadratic FSI boundary (for the fluid and the solid)
968  /// and the quadratic representation of the outer boundary of the solid.
969  /// When used with these files, the flag switch_normal should be
970  /// set to true when calling the function for the outer boundary of the
971  /// solid.
972  template<class ELEMENT>
974  const std::string& quadratic_surface_file_name,
975  const bool& switch_normal)
976  {
977  // Dummy doc info
978  DocInfo doc_info;
979  doc_info.disable_doc();
980  snap_to_quadratic_surface<ELEMENT>
981  (boundary_id,quadratic_surface_file_name,
982  switch_normal,doc_info);
983 
984  }
985 
986  /// \short Move the nodes on boundaries with associated GeomObjects so
987  /// that they exactly coincide with the geometric object. This requires
988  /// that the boundary coordinates are set up consistently.
989  void snap_nodes_onto_geometric_objects();
990 
991 
992  /// \short Non-Delaunay split elements that have three faces on a boundary
993  /// into sons. Timestepper species timestepper for new nodes; defaults
994  /// to to steady timestepper.
995  template<class ELEMENT>
996  void split_elements_in_corners(TimeStepper* time_stepper_pt=
998 
999 
1000 
1001 
1002  /// Setup lookup schemes which establish which elements are located
1003  /// next to mesh's boundaries (wrapper to suppress doc).
1005  {
1006  std::ofstream outfile;
1007  this->setup_boundary_element_info(outfile);
1008  }
1009 
1010 
1011  /// \short Setup lookup schemes which establish which elements are located
1012  /// next to mesh's boundaries. Doc in outfile (if it's open).
1013  void setup_boundary_element_info(std::ostream &outfile);
1014 
1015 
1016  protected:
1017 
1018  /// \short Vectors of vectors of elements in each region (note: this just
1019  /// stores them; the region IDs are contained in Region_attribute!)
1021 
1022  /// \short Vector of attributes associated with the elements in each region
1023  /// NOTE: double is enforced on us by tetgen. We use it as an unsigned
1024  /// to indicate the actual (zero-based) region ID
1026 
1027  /// Storage for elements adjacent to a boundary in a particular region
1030 
1031  /// \short Storage for the face index adjacent to a boundary in a particular
1032  /// region
1034 
1035  /// Faceted surface that defines outer boundaries
1037 
1038  /// Vector to faceted surfaces that define internal boundaries
1040 
1041  /// \short Reverse lookup scheme: Pointer to faceted surface (if any!)
1042  /// associated with boundary b
1043  std::map<unsigned,TetMeshFacetedSurface*> Tet_mesh_faceted_surface_pt;
1044 
1045  /// \short Reverse lookup scheme: Pointer to facet (if any!)
1046  /// associated with boundary b
1047  std::map<unsigned,TetMeshFacet*> Tet_mesh_facet_pt;
1048 
1049  /// \short Boundary coordinates of vertices in triangular facets
1050  /// associated with given boundary. Is only set up for triangular facets!
1051  std::map<unsigned,Vector<Vector<double> > >
1053 
1054  /// Timestepper used to build nodes
1056 
1057 
1058 };
1059 
1060 
1061 
1062 
1063 //////////////////////////////////////////////////////////////////////////////
1064 //////////////////////////////////////////////////////////////////////////////
1065 //////////////////////////////////////////////////////////////////////////////
1066 
1067 
1068 //###########################################################################
1069 // Templated member functions
1070 //###########################################################################
1071 
1072 
1073 
1074 //======================================================================
1075 /// Setup boundary coordinate on boundary b which is
1076 /// assumed to be planar. Boundary coordinates are the
1077 /// x-y coordinates in the plane of that boundary, with the
1078 /// x-axis along the line from the (lexicographically)
1079 /// "lower left" to the "upper right" node. The y axis
1080 /// is obtained by taking the cross-product of the positive
1081 /// x direction with the outer unit normal computed by
1082 /// the face elements (or its negative if switch_normal is set
1083 /// to true). Doc faces in output file (if it's open).
1084 ///
1085 /// Note 1: Setup of boundary coordinates is not done if the boundary in
1086 /// question turns out to be nonplanar.
1087 ///
1088 /// Note 2: If a triangular TetMeshFacet is associated with a boundary we also
1089 /// store the boundary coordinates of its vertices. They are needed
1090 /// to interpolated intrinsic coordinates of an associated GeomObject
1091 /// (if any) into the interior.
1092 //======================================================================
1093 template <class ELEMENT>
1095  const unsigned& b,
1096  const bool& switch_normal,
1097  std::ofstream& outfile)
1098  {
1099  Node* lower_left_node_pt=0;
1100  Node* upper_right_node_pt=0;
1101 
1102  // Unit vector connecting lower left and upper right nodes
1103  Vector<double> b0(3);
1104 
1105  // ...and a vector normal to it
1106  Vector<double> b1(3);
1107 
1108 
1109  // Facet?
1110  TetMeshFacet* f_pt=0;
1111  std::map<unsigned,TetMeshFacet*>::iterator it=
1112  Tet_mesh_facet_pt.find(b);
1113  if (it!=Tet_mesh_facet_pt.end())
1114  {
1115  f_pt=(*it).second;
1116  }
1117 
1118  // Number of vertices
1119  unsigned nv=0;
1120  if (f_pt!=0)
1121  {
1122  nv=f_pt->nvertex();
1123  }
1124 
1125  // Check for planarity and bail out if nodes are not in the same plane
1126  bool vertices_are_in_same_plane=true;
1127  for (unsigned do_for_real=0;do_for_real<2;do_for_real++)
1128  {
1129 
1130  // Temporary storage for face elements
1131  Vector<FiniteElement*> face_el_pt;
1132 
1133  // Loop over all elements on boundaries
1134  unsigned nel=this->nboundary_element(b);
1135 
1136  if (nel>0)
1137  {
1138  // Loop over the bulk elements adjacent to boundary b
1139  for(unsigned e=0;e<nel;e++)
1140  {
1141  // Get pointer to the bulk element that is adjacent to boundary b
1142  FiniteElement* bulk_elem_pt = this->boundary_element_pt(b,e);
1143 
1144  //Find the index of the face of element e along boundary b
1145  int face_index = this->face_index_at_boundary(b,e);
1146 
1147  // Create new face element
1148  face_el_pt.push_back(new DummyFaceElement<ELEMENT>(
1149  bulk_elem_pt,face_index));
1150 
1151  // Output faces?
1152  if (outfile.is_open())
1153  {
1154  face_el_pt[face_el_pt.size()-1]->output(outfile);
1155  }
1156  }
1157 
1158  // Loop over all nodes to find the lower left and upper right ones
1159  lower_left_node_pt=this->boundary_node_pt(b,0);
1160  upper_right_node_pt=this->boundary_node_pt(b,0);
1161 
1162  // Loop over all nodes on the boundary
1163  unsigned nnod=this->nboundary_node(b);
1164  for (unsigned j=0;j<nnod;j++)
1165  {
1166 
1167  //Get node
1168  Node* nod_pt=this->boundary_node_pt(b,j);
1169 
1170  // Primary criterion for lower left: Does it have a lower z-coordinate?
1171  if (nod_pt->x(2)<lower_left_node_pt->x(2))
1172  {
1173  lower_left_node_pt=nod_pt;
1174  }
1175  // ...or is it a draw?
1176  else if (nod_pt->x(2)==lower_left_node_pt->x(2))
1177  {
1178  // If it's a draw: Does it have a lower y-coordinate?
1179  if (nod_pt->x(1)<lower_left_node_pt->x(1))
1180  {
1181  lower_left_node_pt=nod_pt;
1182  }
1183  // ...or is it a draw?
1184  else if (nod_pt->x(1)==lower_left_node_pt->x(1))
1185  {
1186 
1187  // If it's a draw: Does it have a lower x-coordinate?
1188  if (nod_pt->x(0)<lower_left_node_pt->x(0))
1189  {
1190  lower_left_node_pt=nod_pt;
1191  }
1192  }
1193  }
1194 
1195  // Primary criterion for upper right: Does it have a higher z-coordinate?
1196  if (nod_pt->x(2)>upper_right_node_pt->x(2))
1197  {
1198  upper_right_node_pt=nod_pt;
1199  }
1200  // ...or is it a draw?
1201  else if (nod_pt->x(2)==upper_right_node_pt->x(2))
1202  {
1203  // If it's a draw: Does it have a higher y-coordinate?
1204  if (nod_pt->x(1)>upper_right_node_pt->x(1))
1205  {
1206  upper_right_node_pt=nod_pt;
1207  }
1208  // ...or is it a draw?
1209  else if (nod_pt->x(1)==upper_right_node_pt->x(1))
1210  {
1211 
1212  // If it's a draw: Does it have a higher x-coordinate?
1213  if (nod_pt->x(0)>upper_right_node_pt->x(0))
1214  {
1215  upper_right_node_pt=nod_pt;
1216  }
1217  }
1218  }
1219  }
1220 
1221  // Prepare storage for boundary coordinates
1222  Vector<double> zeta(2);
1223 
1224  // Unit vector connecting lower left and upper right nodes
1225  b0[0]=upper_right_node_pt->x(0)-lower_left_node_pt->x(0);
1226  b0[1]=upper_right_node_pt->x(1)-lower_left_node_pt->x(1);
1227  b0[2]=upper_right_node_pt->x(2)-lower_left_node_pt->x(2);
1228 
1229  // Normalise
1230  double inv_length=1.0/sqrt(b0[0]*b0[0]+b0[1]*b0[1]+b0[2]*b0[2]);
1231  b0[0]*=inv_length;
1232  b0[1]*=inv_length;
1233  b0[2]*=inv_length;
1234 
1235  // Get (outer) unit normal to first face element
1236  Vector<double> normal(3);
1237  Vector<double> s(2,0.0);
1238  if (nv!=3)
1239  {
1240  dynamic_cast<DummyFaceElement<ELEMENT>*>(face_el_pt[0])->
1241  outer_unit_normal(s,normal);
1242  }
1243  else
1244  {
1245  double t1x=
1246  f_pt->vertex_pt(1)->x(0)-
1247  f_pt->vertex_pt(0)->x(0);
1248 
1249  double t1y=
1250  f_pt->vertex_pt(1)->x(1)-
1251  f_pt->vertex_pt(0)->x(1);
1252 
1253  double t1z=
1254  f_pt->vertex_pt(1)->x(2)-
1255  f_pt->vertex_pt(0)->x(2);
1256 
1257  double t2x=
1258  f_pt->vertex_pt(2)->x(0)-
1259  f_pt->vertex_pt(0)->x(0);
1260 
1261  double t2y=
1262  f_pt->vertex_pt(2)->x(1)-
1263  f_pt->vertex_pt(0)->x(1);
1264 
1265  double t2z=
1266  f_pt->vertex_pt(2)->x(2)-
1267  f_pt->vertex_pt(0)->x(2);
1268 
1269  normal[0]=t1y*t2z-t1z*t2y;
1270  normal[1]=t1z*t2x-t1x*t2z;
1271  normal[2]=t1x*t2y-t1y*t2x;
1272  double inv_length=1.0/sqrt(normal[0]*normal[0]+
1273  normal[1]*normal[1]+
1274  normal[2]*normal[2]);
1275  normal[0]*=inv_length;
1276  normal[1]*=inv_length;
1277  normal[2]*=inv_length;
1278  }
1279 
1280  if (switch_normal)
1281  {
1282  normal[0]=-normal[0];
1283  normal[1]=-normal[1];
1284  normal[2]=-normal[2];
1285  }
1286 
1287  // Check that all elements have the same normal
1288  for(unsigned e=0;e<nel;e++)
1289  {
1290  // Get (outer) unit normal to face element
1291  Vector<double> my_normal(3);
1292  dynamic_cast<DummyFaceElement<ELEMENT>*>(face_el_pt[e])->
1293  outer_unit_normal(s,my_normal);
1294 
1295  // Dot product should be one!
1296  double dot_prod=
1297  normal[0]*my_normal[0]+
1298  normal[1]*my_normal[1]+
1299  normal[2]*my_normal[2];
1300 
1301 
1302  double error=abs(dot_prod)-1.0;
1303  if (abs(error)>Tolerance_for_boundary_finding)
1304  {
1305  vertices_are_in_same_plane=false;
1306  }
1307  }
1308 
1309  // Bail out?
1310  if (vertices_are_in_same_plane)
1311  {
1312 
1313  // Cross-product to get second in-plane vector, normal to b0
1314  b1[0]=b0[1]*normal[2]-b0[2]*normal[1];
1315  b1[1]=b0[2]*normal[0]-b0[0]*normal[2];
1316  b1[2]=b0[0]*normal[1]-b0[1]*normal[0];
1317 
1318 
1319 
1320  // Assign boundary coordinates: projection onto the axes
1321  for (unsigned j=0;j<nnod;j++)
1322  {
1323  //Get node
1324  Node* nod_pt=this->boundary_node_pt(b,j);
1325 
1326  // Difference vector to lower left corner
1327  Vector<double> delta(3);
1328  delta[0]=nod_pt->x(0)-lower_left_node_pt->x(0);
1329  delta[1]=nod_pt->x(1)-lower_left_node_pt->x(1);
1330  delta[2]=nod_pt->x(2)-lower_left_node_pt->x(2);
1331 
1332  // Project
1333  zeta[0]=delta[0]*b0[0]+delta[1]*b0[1]+delta[2]*b0[2];
1334  zeta[1]=delta[0]*b1[0]+delta[1]*b1[1]+delta[2]*b1[2];
1335 
1336  // Check:
1337  double error=
1338  pow(lower_left_node_pt->x(0)+zeta[0]*b0[0]+zeta[1]*b1[0]-nod_pt->x(0),2)+
1339  pow(lower_left_node_pt->x(1)+zeta[0]*b0[1]+zeta[1]*b1[1]-nod_pt->x(1),2)+
1340  pow(lower_left_node_pt->x(2)+zeta[0]*b0[2]+zeta[1]*b1[2]-nod_pt->x(2),2);
1341 
1342  if (sqrt(error)>Tolerance_for_boundary_finding)
1343  {
1344  std::ostringstream error_message;
1345  error_message
1346  << "Error in projection of boundary coordinates = "
1347  << sqrt(error) << " > Tolerance_for_boundary_finding = "
1348  << Tolerance_for_boundary_finding << std::endl
1349  << "nv = " << nv << std::endl
1350  << "Lower left: "
1351  << lower_left_node_pt->x(0) << " "
1352  << lower_left_node_pt->x(1) << " "
1353  << lower_left_node_pt->x(2) << " "
1354  << std::endl
1355  << "Upper right: "
1356  << upper_right_node_pt->x(0) << " "
1357  << upper_right_node_pt->x(1) << " "
1358  << upper_right_node_pt->x(2) << " "
1359  << std::endl
1360  << "Nodes: ";
1361  for (unsigned j=0;j<nnod;j++)
1362  {
1363  //Get node
1364  Node* nod_pt=this->boundary_node_pt(b,j);
1365  error_message << nod_pt->x(0) << " "
1366  << nod_pt->x(1) << " "
1367  << nod_pt->x(2) << " "
1368  << std::endl;
1369  }
1370  throw OomphLibError(error_message.str(),
1371  OOMPH_CURRENT_FUNCTION,
1372  OOMPH_EXCEPTION_LOCATION);
1373  }
1374 
1375  // Set boundary coordinate
1376  if (do_for_real==1)
1377  {
1378  nod_pt->set_coordinates_on_boundary(b,zeta);
1379  }
1380  }
1381  }
1382  }
1383 
1384 
1385  // Indicate that boundary coordinate has been set up
1386  if (do_for_real==1)
1387  {
1388  Boundary_coordinate_exists[b]=true;
1389 
1390  // Facet associated with this boundary?
1391  if (f_pt!=0)
1392  {
1393  // Triangular facet: Set coordinates at vertices
1394  if (nv==3)
1395  {
1396  Triangular_facet_vertex_boundary_coordinate[b].resize(3);
1397  for (unsigned j=0;j<3;j++)
1398  {
1399  // Two surface coordinates
1400  Triangular_facet_vertex_boundary_coordinate[b][j].resize(2);
1401 
1402  // Difference vector to lower left corner
1403  Vector<double> delta(3);
1404  delta[0]=
1405  f_pt->vertex_pt(j)->x(0)-
1406  lower_left_node_pt->x(0);
1407  delta[1]=
1408  f_pt->vertex_pt(j)->x(1)-
1409  lower_left_node_pt->x(1);
1410  delta[2]=
1411  f_pt->vertex_pt(j)->x(2)-
1412  lower_left_node_pt->x(2);
1413 
1414  // Project
1415  Vector<double> zeta(2);
1416  zeta[0]=delta[0]*b0[0]+delta[1]*b0[1]+delta[2]*b0[2];
1417  zeta[1]=delta[0]*b1[0]+delta[1]*b1[1]+delta[2]*b1[2];
1418 
1419  for (unsigned ii=0;ii<2;ii++)
1420  {
1421  Triangular_facet_vertex_boundary_coordinate[b][j][ii]=
1422  zeta[ii];
1423  }
1424  }
1425  }
1426  }
1427  }
1428 
1429  // Cleanup
1430  unsigned n=face_el_pt.size();
1431  for (unsigned e=0;e<n;e++)
1432  {
1433  delete face_el_pt[e];
1434  }
1435 
1436  // Bail out?
1437  if (!vertices_are_in_same_plane)
1438  {
1439  /* oomph_info << "Vertices on boundary " << b */
1440  /* << " are not in the same plane; bailing out\n"; */
1441  return;
1442  }
1443  }
1444  }
1445 
1446 
1447 
1448 //======================================================================
1449 /// Snap boundaries specified by the IDs listed in boundary_id to
1450 /// a quadratric surface, specified in the file
1451 /// quadratic_surface_file_name. This is usually used with vmtk-based
1452 /// meshes for which oomph-lib's xda to poly conversion code produces the files
1453 /// "quadratic_fsi_boundary.dat" and "quadratic_outer_solid_boundary.dat"
1454 /// which specify the quadratic FSI boundary (for the fluid and the solid)
1455 /// and the quadratic representation of the outer boundary of the solid.
1456 /// When used with these files, the flag switch_normal should be
1457 /// set to true when calling the function for the outer boundary of the
1458 /// solid. The DocInfo object can be used to label optional output
1459 /// files. (Uses directory and label).
1460 //======================================================================
1461  template<class ELEMENT>
1463  const Vector<unsigned>& boundary_id,
1464  const std::string& quadratic_surface_file_name,
1465  const bool& switch_normal,
1466  DocInfo& doc_info)
1467 {
1468 
1469  // Aux storage for processing input
1470  char dummy[101];
1471 
1472  // Prepare to doc nodes that couldn't be snapped
1473  std::ofstream the_file_non_snapped;
1474  std::string non_snapped_filename="non_snapped_nodes_"+doc_info.label()+".dat";
1475 
1476  // Read the number of nodes and elements (quadratic facets)
1477  std::ifstream infile(quadratic_surface_file_name.c_str(),std::ios_base::in);
1478  unsigned n_node;
1479  infile >> n_node;
1480 
1481  // Ignore rest of line
1482  infile.getline(dummy, 100);
1483 
1484  // Number of quadratic facets
1485  unsigned nel;
1486  infile>> nel;
1487 
1488  // Ignore rest of line
1489  infile.getline(dummy, 100);
1490 
1491  // Check that the number of elements matches
1492  if (nel!=boundary_id.size())
1493  {
1494  std::ostringstream error_message;
1495  error_message
1496  << "Number of quadratic facets specified in "
1497  << quadratic_surface_file_name << "is: " << nel
1498  << "\nThis doesn't match the number of planar boundaries \n"
1499  << "specified in boundary_id which is " << boundary_id.size()
1500  << std::endl;
1501  throw OomphLibError(error_message.str(),
1502  OOMPH_CURRENT_FUNCTION,
1503  OOMPH_EXCEPTION_LOCATION);
1504  }
1505 
1506  // Temporary storage for face elements
1508 
1509  // Loop over quadratic face elements -- one for each facet
1510  for(unsigned e=0;e<nel;e++)
1511  {
1512  face_el_pt.push_back(new FreeStandingFaceElement<ELEMENT>);
1513  }
1514 
1515 
1516  // Now build nodes
1517  unsigned n_dim=3;
1518  unsigned n_position_type=1;
1519  unsigned initial_n_value=0;
1520  Vector<Node*> nod_pt(n_node);
1521  unsigned node_nmbr;
1522  std::map<unsigned,unsigned> node_number;
1523  std::ofstream node_file;
1524  for (unsigned j=0;j<n_node;j++)
1525  {
1526  nod_pt[j]=new BoundaryNode<Node>(n_dim,n_position_type,initial_n_value);
1527  infile >> nod_pt[j]->x(0);
1528  infile >> nod_pt[j]->x(1);
1529  infile >> nod_pt[j]->x(2);
1530  infile >> node_nmbr;
1531  node_number[node_nmbr]=j;
1532  }
1533 
1534 
1535  // Now assign nodes to elements -- each element represents
1536  // distinct boundary; assign enumeration as specified by
1537  // boundary_id.
1538  for(unsigned e=0;e<nel;e++)
1539  {
1540  unsigned nnod_el=face_el_pt[e]->nnode();
1541  unsigned j_global;
1542  for (unsigned j=0;j<nnod_el;j++)
1543  {
1544  infile >> j_global;
1545  face_el_pt[e]->node_pt(j)=nod_pt[node_number[j_global]];
1546  face_el_pt[e]->node_pt(j)->add_to_boundary(boundary_id[e]);
1547  }
1548  face_el_pt[e]->set_boundary_number_in_bulk_mesh(boundary_id[e]);
1549  face_el_pt[e]->set_nodal_dimension(3);
1550  }
1551 
1552 
1553  // Setup boundary coordinates for each facet, using
1554  // the same strategy as for the simplex boundaries
1555  // (there's some code duplication here but it doesn't
1556  // seem worth it to rationlise this as the interface would
1557  // be pretty clunky).
1558  for (unsigned e=0;e<nel;e++)
1559  {
1560  Vector<Vector<double> >vertex_boundary_coord(3);
1561 
1562  // Loop over all nodes to find the lower left and upper right ones
1563  Node* lower_left_node_pt=face_el_pt[e]->node_pt(0);
1564  Node* upper_right_node_pt=face_el_pt[e]->node_pt(0);
1565 
1566  // Loop over all vertex nodes
1567  for (unsigned j=0;j<3;j++)
1568  {
1569  //Get node
1570  Node* nod_pt=face_el_pt[e]->node_pt(j);
1571 
1572  // Primary criterion for lower left: Does it have a lower z-coordinate?
1573  if (nod_pt->x(2)<lower_left_node_pt->x(2))
1574  {
1575  lower_left_node_pt=nod_pt;
1576  }
1577  // ...or is it a draw?
1578  else if (nod_pt->x(2)==lower_left_node_pt->x(2))
1579  {
1580  // If it's a draw: Does it have a lower y-coordinate?
1581  if (nod_pt->x(1)<lower_left_node_pt->x(1))
1582  {
1583  lower_left_node_pt=nod_pt;
1584  }
1585  // ...or is it a draw?
1586  else if (nod_pt->x(1)==lower_left_node_pt->x(1))
1587  {
1588 
1589  // If it's a draw: Does it have a lower x-coordinate?
1590  if (nod_pt->x(0)<lower_left_node_pt->x(0))
1591  {
1592  lower_left_node_pt=nod_pt;
1593  }
1594  }
1595  }
1596 
1597  // Primary criterion for upper right: Does it have a higher z-coordinate?
1598  if (nod_pt->x(2)>upper_right_node_pt->x(2))
1599  {
1600  upper_right_node_pt=nod_pt;
1601  }
1602  // ...or is it a draw?
1603  else if (nod_pt->x(2)==upper_right_node_pt->x(2))
1604  {
1605  // If it's a draw: Does it have a higher y-coordinate?
1606  if (nod_pt->x(1)>upper_right_node_pt->x(1))
1607  {
1608  upper_right_node_pt=nod_pt;
1609  }
1610  // ...or is it a draw?
1611  else if (nod_pt->x(1)==upper_right_node_pt->x(1))
1612  {
1613 
1614  // If it's a draw: Does it have a higher x-coordinate?
1615  if (nod_pt->x(0)>upper_right_node_pt->x(0))
1616  {
1617  upper_right_node_pt=nod_pt;
1618  }
1619  }
1620  }
1621  }
1622 
1623  // Prepare storage for boundary coordinates
1624  Vector<double> zeta(2);
1625 
1626  // Unit vector connecting lower left and upper right nodes
1627  Vector<double> b0(3);
1628  b0[0]=upper_right_node_pt->x(0)-lower_left_node_pt->x(0);
1629  b0[1]=upper_right_node_pt->x(1)-lower_left_node_pt->x(1);
1630  b0[2]=upper_right_node_pt->x(2)-lower_left_node_pt->x(2);
1631 
1632  // Normalise
1633  double inv_length=1.0/sqrt(b0[0]*b0[0]+b0[1]*b0[1]+b0[2]*b0[2]);
1634  b0[0]*=inv_length;
1635  b0[1]*=inv_length;
1636  b0[2]*=inv_length;
1637 
1638  // Get (outer) unit normal to face element -- note that
1639  // with the enumeration chosen in oomph-lib's xda to poly
1640  // conversion code the sign below is correct for the outer
1641  // unit normal on the FSI interface.
1642  Vector<double> tang1(3);
1643  Vector<double> tang2(3);
1644  Vector<double> normal(3);
1645  tang1[0]=face_el_pt[e]->node_pt(1)->x(0)-face_el_pt[e]->node_pt(0)->x(0);
1646  tang1[1]=face_el_pt[e]->node_pt(1)->x(1)-face_el_pt[e]->node_pt(0)->x(1);
1647  tang1[2]=face_el_pt[e]->node_pt(1)->x(2)-face_el_pt[e]->node_pt(0)->x(2);
1648  tang2[0]=face_el_pt[e]->node_pt(2)->x(0)-face_el_pt[e]->node_pt(0)->x(0);
1649  tang2[1]=face_el_pt[e]->node_pt(2)->x(1)-face_el_pt[e]->node_pt(0)->x(1);
1650  tang2[2]=face_el_pt[e]->node_pt(2)->x(2)-face_el_pt[e]->node_pt(0)->x(2);
1651  normal[0]=-(tang1[1]*tang2[2]-tang1[2]*tang2[1]);
1652  normal[1]=-(tang1[2]*tang2[0]-tang1[0]*tang2[2]);
1653  normal[2]=-(tang1[0]*tang2[1]-tang1[1]*tang2[0]);
1654 
1655  // Normalise
1656  inv_length=
1657  1.0/sqrt(normal[0]*normal[0]+normal[1]*normal[1]+normal[2]*normal[2]);
1658  normal[0]*=inv_length;
1659  normal[1]*=inv_length;
1660  normal[2]*=inv_length;
1661 
1662  // Change direction -- usually for outer boundary of solid
1663  if (switch_normal)
1664  {
1665  normal[0]=-normal[0];
1666  normal[1]=-normal[1];
1667  normal[2]=-normal[2];
1668  }
1669 
1670  // Cross-product to get second in-plane vector, normal to b0
1671  Vector<double> b1(3);
1672  b1[0]=b0[1]*normal[2]-b0[2]*normal[1];
1673  b1[1]=b0[2]*normal[0]-b0[0]*normal[2];
1674  b1[2]=b0[0]*normal[1]-b0[1]*normal[0];
1675 
1676  // Assign boundary coordinates for vertex nodes: projection onto the axes
1677  for (unsigned j=0;j<3;j++)
1678  {
1679  //Get node
1680  Node* nod_pt=face_el_pt[e]->node_pt(j);
1681 
1682  // Difference vector to lower left corner
1683  Vector<double> delta(3);
1684  delta[0]=nod_pt->x(0)-lower_left_node_pt->x(0);
1685  delta[1]=nod_pt->x(1)-lower_left_node_pt->x(1);
1686  delta[2]=nod_pt->x(2)-lower_left_node_pt->x(2);
1687 
1688  // Project
1689  zeta[0]=delta[0]*b0[0]+delta[1]*b0[1]+delta[2]*b0[2];
1690  zeta[1]=delta[0]*b1[0]+delta[1]*b1[1]+delta[2]*b1[2];
1691 
1692  vertex_boundary_coord[j].resize(2);
1693  vertex_boundary_coord[j][0]=zeta[0];
1694  vertex_boundary_coord[j][1]=zeta[1];
1695 
1696 #ifdef PARANOID
1697 
1698  // Check:
1699  double error=
1700  pow(lower_left_node_pt->x(0)+zeta[0]*b0[0]+zeta[1]*b1[0]-nod_pt->x(0),2)+
1701  pow(lower_left_node_pt->x(1)+zeta[0]*b0[1]+zeta[1]*b1[1]-nod_pt->x(1),2)+
1702  pow(lower_left_node_pt->x(2)+zeta[0]*b0[2]+zeta[1]*b1[2]-nod_pt->x(2),2);
1703 
1704  if (sqrt(error)>Tolerance_for_boundary_finding)
1705  {
1706  std::ostringstream error_message;
1707  error_message
1708  << "Error in setup of boundary coordinate "
1709  << sqrt(error) << std::endl
1710  << "exceeds tolerance specified by the static member data\n"
1711  << "TetMeshBase::Tolerance_for_boundary_finding = "
1712  << Tolerance_for_boundary_finding << std::endl
1713  << "This usually means that the boundary is not planar.\n\n"
1714  << "You can suppress this error message by recompiling \n"
1715  << "recompiling without PARANOID or by changing the tolerance.\n";
1716  throw OomphLibError(error_message.str(),
1717  OOMPH_CURRENT_FUNCTION,
1718  OOMPH_EXCEPTION_LOCATION);
1719  }
1720 #endif
1721 
1722  // Set boundary coordinate
1723  nod_pt->set_coordinates_on_boundary(boundary_id[e],zeta);
1724 
1725  }
1726 
1727  // Assign boundary coordinates of three midside nodes by linear
1728  // interpolation (corresponding to a flat facet)
1729 
1730  // Node 3 is between 0 and 1
1731  zeta[0]=0.5*(vertex_boundary_coord[0][0]+vertex_boundary_coord[1][0]);
1732  zeta[1]=0.5*(vertex_boundary_coord[0][1]+vertex_boundary_coord[1][1]);
1733  face_el_pt[e]->node_pt(3)->set_coordinates_on_boundary(boundary_id[e],zeta);
1734 
1735  // Node 4 is between 1 and 2
1736  zeta[0]=0.5*(vertex_boundary_coord[1][0]+vertex_boundary_coord[2][0]);
1737  zeta[1]=0.5*(vertex_boundary_coord[1][1]+vertex_boundary_coord[2][1]);
1738  face_el_pt[e]->node_pt(4)->set_coordinates_on_boundary(boundary_id[e],zeta);
1739 
1740  // Node 5 is between 2 and 0
1741  zeta[0]=0.5*(vertex_boundary_coord[2][0]+vertex_boundary_coord[0][0]);
1742  zeta[1]=0.5*(vertex_boundary_coord[2][1]+vertex_boundary_coord[0][1]);
1743  face_el_pt[e]->node_pt(5)->set_coordinates_on_boundary(boundary_id[e],zeta);
1744 
1745  }
1746 
1747 
1748  // Loop over elements/facets = boundaries to snap
1749  bool success=true;
1750  for(unsigned b=0;b<nel;b++)
1751  {
1752 
1753  //Doc boundary coordinates on quadratic patches
1754  std::ofstream the_file;
1755  std::ofstream the_file_before;
1756  std::ofstream the_file_after;
1757  if (doc_info.is_doc_enabled())
1758  {
1759  std::ostringstream filename;
1760  filename << doc_info.directory() << "/quadratic_coordinates_"
1761  << doc_info.label() << b << ".dat";
1762  the_file.open(filename.str().c_str());
1763 
1764  std::ostringstream filename1;
1765  filename1 << doc_info.directory() << "/quadratic_nodes_before_"
1766  << doc_info.label() << b << ".dat";
1767  the_file_before.open(filename1.str().c_str());
1768 
1769  std::ostringstream filename2;
1770  filename2 << doc_info.directory() << "/quadratic_nodes_after_"
1771  << doc_info.label() << b << ".dat";
1772  the_file_after.open(filename2.str().c_str());
1773  }
1774 
1775  //Cast the element pointer
1776  FreeStandingFaceElement<ELEMENT>* el_pt=face_el_pt[b];
1777 
1778  // Doc boundary coordinate on quadratic facet representation
1779  if (doc_info.is_doc_enabled())
1780  {
1781  Vector<double> s(2);
1782  Vector<double> zeta(2);
1783  Vector<double> x(3);
1784  unsigned n_plot=3;
1785  the_file << el_pt->tecplot_zone_string(n_plot);
1786 
1787  // Loop over plot points
1788  unsigned num_plot_points=el_pt->nplot_points(n_plot);
1789  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
1790  {
1791  // Get local coordinates of plot point
1792  el_pt->get_s_plot(iplot,n_plot,s);
1793  el_pt->interpolated_zeta(s,zeta);
1794  el_pt->interpolated_x(s,x);
1795  for (unsigned i=0;i<3;i++)
1796  {
1797  the_file << x[i] << " ";
1798  }
1799  for (unsigned i=0;i<2;i++)
1800  {
1801  the_file << zeta[i] << " ";
1802  }
1803  the_file << std::endl;
1804  }
1805  el_pt->write_tecplot_zone_footer(the_file,n_plot);
1806 
1807 // std::cout << "Facet " << b << " corresponds to quadratic boundary "
1808 // << boundary_id[b] << " which contains "
1809 // << this->nboundary_element(boundary_id[b])
1810 // << " element[s] " << std::endl;
1811  }
1812 
1813 
1814  // Loop over bulk elements that are adjacent to quadratic boundary
1815  Vector<double> boundary_zeta(2);
1816  Vector<double> quadratic_coordinates(3);
1817  GeomObject* geom_obj_pt=0;
1818  Vector<double> s_geom_obj(2);
1819  unsigned nel=this->nboundary_element(boundary_id[b]);
1820  for (unsigned e=0;e<nel;e++)
1821  {
1822  // Get pointer to the bulk element that is adjacent to boundary
1823  FiniteElement* bulk_elem_pt=this->boundary_element_pt(boundary_id[b],e);
1824 
1825  // Loop over nodes
1826  unsigned nnod=bulk_elem_pt->nnode();
1827  for (unsigned j=0;j<nnod;j++)
1828  {
1829  Node* nod_pt=bulk_elem_pt->node_pt(j);
1830  if (nod_pt->is_on_boundary(boundary_id[b]))
1831  {
1832  nod_pt->get_coordinates_on_boundary(boundary_id[b],boundary_zeta);
1833 
1834  // Doc it?
1835  if (doc_info.is_doc_enabled())
1836  {
1837  the_file_before << nod_pt->x(0) << " "
1838  << nod_pt->x(1) << " "
1839  << nod_pt->x(2) << " "
1840  << boundary_zeta[0] << " "
1841  << boundary_zeta[1] << " "
1842  << std::endl;
1843  }
1844 
1845  // Find local coordinate in quadratic facet
1846  el_pt->locate_zeta(boundary_zeta,geom_obj_pt,s_geom_obj);
1847  if (geom_obj_pt!=0)
1848  {
1849  geom_obj_pt->position(s_geom_obj,quadratic_coordinates);
1850  nod_pt->x(0)=quadratic_coordinates[0];
1851  nod_pt->x(1)=quadratic_coordinates[1];
1852  nod_pt->x(2)=quadratic_coordinates[2];
1853  }
1854  else
1855  {
1856  // Get ready to bail out below...
1857  success=false;
1858 
1859  std::ostringstream error_message;
1860  error_message
1861  << "Warning: Couldn't find GeomObject during snapping to\n"
1862  << "quadratic surface for boundary " << boundary_id[b]
1863  << ". I'm leaving the node where it was. Will bail out later.\n";
1865  error_message.str(),
1866  "TetgenMesh::snap_to_quadratic_surface()",
1867  OOMPH_EXCEPTION_LOCATION);
1868  if (!the_file_non_snapped.is_open())
1869  {
1870  the_file_non_snapped.open(non_snapped_filename.c_str());
1871  }
1872  the_file_non_snapped << nod_pt->x(0) << " "
1873  << nod_pt->x(1) << " "
1874  << nod_pt->x(2) << " "
1875  << boundary_zeta[0] << " "
1876  << boundary_zeta[1] << " "
1877  << std::endl;
1878  }
1879 
1880  // Doc it?
1881  if (doc_info.is_doc_enabled())
1882  {
1883  the_file_after << nod_pt->x(0) << " "
1884  << nod_pt->x(1) << " "
1885  << nod_pt->x(2) << " "
1886  << boundary_zeta[0] << " "
1887  << boundary_zeta[1] << " "
1888  << std::endl;
1889  }
1890  }
1891  }
1892  }
1893 
1894  // Close doc file
1895  the_file.close();
1896  the_file_before.close();
1897  the_file_after.close();
1898  }
1899 
1900  // Bail out?
1901  if (!success)
1902  {
1903  std::ostringstream error_message;
1904  error_message
1905  << "Warning: Couldn't find GeomObject during snapping to\n"
1906  << "quadratic surface. Bailing out.\n"
1907  << "Nodes that couldn't be snapped are contained in \n"
1908  << "file: " << non_snapped_filename << ".\n"
1909  << "This problem may arise because the switch_normal flag was \n"
1910  << "set wrongly.\n";
1911  throw OomphLibError(
1912  error_message.str(),
1913  OOMPH_CURRENT_FUNCTION,
1914  OOMPH_EXCEPTION_LOCATION);
1915  }
1916 
1917  // Close
1918  if (!the_file_non_snapped.is_open())
1919  {
1920  the_file_non_snapped.close();
1921  }
1922 
1923  // Kill auxiliary FreeStandingFaceElements
1924  for(unsigned e=0;e<nel;e++)
1925  {
1926  delete face_el_pt[e];
1927  face_el_pt[e]=0;
1928  }
1929 
1930  // Kill boundary nodes
1931  unsigned nn=nod_pt.size();
1932  for (unsigned j=0;j<nn;j++)
1933  {
1934  delete nod_pt[j];
1935  }
1936 
1937 }
1938 
1939 
1940 
1941 //========================================================================
1942 /// Non-delaunay split elements that have three faces on a boundary
1943 /// into sons.
1944 //========================================================================
1945 template <class ELEMENT>
1947 {
1948 
1949  // Setup boundary lookup scheme if required
1950  if (!Lookup_for_elements_next_boundary_is_setup)
1951  {
1952  setup_boundary_element_info();
1953  }
1954 
1955  // Find out how many nodes we have along each element edge
1956  unsigned nnode_1d=finite_element_pt(0)->nnode_1d();
1957  // Find out the total number of nodes
1958  unsigned nnode = this->finite_element_pt(0)->nnode();
1959 
1960  // At the moment we're only able to deal with nnode_1d=2 or 3.
1961  if ((nnode_1d!=2)&&(nnode_1d!=3))
1962  {
1963  std::ostringstream error_message;
1964  error_message << "Mesh generation from tetgen currently only works\n";
1965  error_message << "for nnode_1d = 2 or 3. You're trying to use it\n";
1966  error_message << "for nnode_1d=" << nnode_1d << std::endl;
1967 
1968  throw OomphLibError(error_message.str(),
1969  OOMPH_CURRENT_FUNCTION,
1970  OOMPH_EXCEPTION_LOCATION);
1971  }
1972 
1973  // Map to store how many boundaries elements are located on
1974  std::map<FiniteElement*,unsigned> count;
1975 
1976  // Loop over all boundaries
1977  unsigned nb=this->nboundary();
1978  for (unsigned b=0;b<nb;b++)
1979  {
1980  // Loop over elements next to that boundary
1981  unsigned nel=this->nboundary_element(b);
1982  for (unsigned e=0;e<nel;e++)
1983  {
1984  // Get pointer to element
1985  FiniteElement* el_pt=boundary_element_pt(b,e);
1986 
1987  // Bump up counter
1988  count[el_pt]++;
1989  }
1990  }
1991 
1992  // To avoid having to check the map for all elements (which will
1993  // inflate it to the size of all elements!), move offending elements
1994  // into set
1995  std::set<FiniteElement*> elements_to_be_split;
1996  for (std::map<FiniteElement*,unsigned>::iterator it=count.begin();
1997  it!=count.end();it++)
1998  {
1999  // Get pointer to element: Key
2000  FiniteElement* el_pt=it->first;
2001 
2002  // Does it have to be split?
2003  if (it->second>2)
2004  {
2005  elements_to_be_split.insert(el_pt);
2006  }
2007  }
2008 
2009  // Vector for retained or newly built elements
2010  unsigned nel=this->nelement();
2011  Vector<FiniteElement*> new_or_retained_el_pt;
2012  new_or_retained_el_pt.reserve(nel);
2013 
2014  // Map which returns the 4 newly created elements for each old corner element
2015  std::map<FiniteElement*, Vector<FiniteElement*> > old_to_new_corner_element_map;
2016 
2017  // Now loop over all elements
2018  for (unsigned e=0;e<nel;e++)
2019  {
2020 
2021  // Get pointer to element
2022  FiniteElement* el_pt=this->finite_element_pt(e);
2023 
2024  // Does it have to be split? I.e. is it in the set?
2025  std::set<FiniteElement*>::iterator it=
2026  std::find(elements_to_be_split.begin(),elements_to_be_split.end(),el_pt);
2027 
2028  // It's not in the set, so iterator has reached end
2029  if (it==elements_to_be_split.end())
2030  {
2031  // Carry it across
2032  new_or_retained_el_pt.push_back(el_pt);
2033  }
2034  // It's in the set of elements to be split
2035  else
2036  {
2037  // Storage for new nodes -- Note: All new nodes are interior and
2038  // therefore cannot be boundary nodes!
2039  Node* node0_pt=0;
2040  Node* node1_pt=0;
2041  Node* node2_pt=0;
2042  Node* node3_pt=0;
2043  Node* node4_pt=0;
2044  Node* node5_pt=0;
2045  Node* node6_pt=0;
2046  Node* node7_pt=0;
2047  Node* node8_pt=0;
2048  Node* node9_pt=0;
2049  Node* node10_pt=0;
2050 
2051  // Create first new element
2052  FiniteElement* el1_pt=new ELEMENT;
2053 
2054  // Copy existing nodes
2055  el1_pt->node_pt(0)=el_pt->node_pt(0);
2056  el1_pt->node_pt(1)=el_pt->node_pt(1);
2057  el1_pt->node_pt(3)=el_pt->node_pt(3);
2058  if (nnode_1d==3)
2059  {
2060  el1_pt->node_pt(4)=el_pt->node_pt(4);
2061  el1_pt->node_pt(6)=el_pt->node_pt(6);
2062  el1_pt->node_pt(9)=el_pt->node_pt(9);
2063  }
2064 
2065  // Create new nodes
2066  // If we have an enriched element then don't need to construct centroid
2067  // node
2068  if(nnode==15)
2069  {
2070  node0_pt = el_pt->node_pt(14);
2071  el1_pt->node_pt(2) = node0_pt;
2072  node5_pt = el1_pt->construct_node(11,time_stepper_pt);
2073  node6_pt = el1_pt->construct_node(13,time_stepper_pt);
2074  node9_pt = el1_pt->construct_node(12,time_stepper_pt);
2075 
2076  //Copy others over
2077  el1_pt->node_pt(10) = el_pt->node_pt(10);
2078  }
2079  //If not enriched we do
2080  else
2081  {
2082  node0_pt=el1_pt->construct_node(2,time_stepper_pt);
2083  }
2084  if (nnode_1d==3)
2085  {
2086  node1_pt=el1_pt->construct_boundary_node(5,time_stepper_pt);
2087  node2_pt=el1_pt->construct_boundary_node(7,time_stepper_pt);
2088  node4_pt=el1_pt->construct_boundary_node(8,time_stepper_pt);
2089  }
2090 
2091 
2092  // Create second new element
2093  FiniteElement* el2_pt=new ELEMENT;
2094 
2095  // Copy existing nodes
2096  el2_pt->node_pt(0)=el_pt->node_pt(0);
2097  el2_pt->node_pt(1)=el_pt->node_pt(1);
2098  el2_pt->node_pt(2)=el_pt->node_pt(2);
2099  if (nnode_1d==3)
2100  {
2101  el2_pt->node_pt(4)=el_pt->node_pt(4);
2102  el2_pt->node_pt(5)=el_pt->node_pt(5);
2103  el2_pt->node_pt(7)=el_pt->node_pt(7);
2104  }
2105 
2106  // Create new node
2107  if (nnode_1d==3)
2108  {
2109  node3_pt=el2_pt->construct_boundary_node(8,time_stepper_pt);
2110  }
2111 
2112  // Copy existing new nodes
2113  el2_pt->node_pt(3)=node0_pt;
2114  if (nnode_1d==3)
2115  {
2116  el2_pt->node_pt(6)=node1_pt;
2117  el2_pt->node_pt(9)=node2_pt;
2118  }
2119 
2120  //Copy and constuct other nodes for enriched elements
2121  if(nnode==15)
2122  {
2123  el2_pt->node_pt(10) = node5_pt;
2124  el2_pt->node_pt(11) = el_pt->node_pt(11);
2125  node8_pt = el2_pt->construct_node(12,time_stepper_pt);
2126  node10_pt = el2_pt->construct_node(13,time_stepper_pt);
2127  }
2128 
2129  // Create third new element
2130  FiniteElement* el3_pt=new ELEMENT;
2131 
2132  // Copy existing nodes
2133  el3_pt->node_pt(1)=el_pt->node_pt(1);
2134  el3_pt->node_pt(2)=el_pt->node_pt(2);
2135  el3_pt->node_pt(3)=el_pt->node_pt(3);
2136  if (nnode_1d==3)
2137  {
2138  el3_pt->node_pt(7)=el_pt->node_pt(7);
2139  el3_pt->node_pt(8)=el_pt->node_pt(8);
2140  el3_pt->node_pt(9)=el_pt->node_pt(9);
2141  }
2142 
2143  // Copy existing new nodes
2144  el3_pt->node_pt(0)=node0_pt;
2145  if (nnode_1d==3)
2146  {
2147  el3_pt->node_pt(4)=node2_pt;
2148  el3_pt->node_pt(5)=node3_pt;
2149  el3_pt->node_pt(6)=node4_pt;
2150  }
2151 
2152  //Copy and constuct other nodes for enriched elements
2153  if(nnode==15)
2154  {
2155  el3_pt->node_pt(10) = node6_pt;
2156  el3_pt->node_pt(11) = node10_pt;
2157  node7_pt = el3_pt->construct_node(12,time_stepper_pt);
2158  el3_pt->node_pt(13) = el_pt->node_pt(13);
2159  }
2160 
2161 
2162  // Create fourth new element
2163  FiniteElement* el4_pt=new ELEMENT;
2164 
2165  // Copy existing nodes
2166  el4_pt->node_pt(0)=el_pt->node_pt(0);
2167  el4_pt->node_pt(2)=el_pt->node_pt(2);
2168  el4_pt->node_pt(3)=el_pt->node_pt(3);
2169  if (nnode_1d==3)
2170  {
2171  el4_pt->node_pt(5)=el_pt->node_pt(5);
2172  el4_pt->node_pt(6)=el_pt->node_pt(6);
2173  el4_pt->node_pt(8)=el_pt->node_pt(8);
2174  }
2175 
2176  // Copy existing new nodes
2177  el4_pt->node_pt(1)=node0_pt;
2178  if (nnode_1d==3)
2179  {
2180  el4_pt->node_pt(4)=node1_pt;
2181  el4_pt->node_pt(7)=node3_pt;
2182  el4_pt->node_pt(9)=node4_pt;
2183  }
2184 
2185  //Copy all other nodes
2186  if(nnode==15)
2187  {
2188  el4_pt->node_pt(10) = node9_pt;
2189  el4_pt->node_pt(11) = node8_pt;
2190  el4_pt->node_pt(12) = el_pt->node_pt(12);
2191  el4_pt->node_pt(13) = node7_pt;;
2192  }
2193 
2194 
2195  // Add new elements and nodes
2196  new_or_retained_el_pt.push_back(el1_pt);
2197  new_or_retained_el_pt.push_back(el2_pt);
2198  new_or_retained_el_pt.push_back(el3_pt);
2199  new_or_retained_el_pt.push_back(el4_pt);
2200 
2201  // create a vector of the newly created elements
2202  Vector<FiniteElement*> temp_vec(4);
2203  temp_vec[0] = el1_pt;
2204  temp_vec[1] = el2_pt;
2205  temp_vec[2] = el3_pt;
2206  temp_vec[3] = el4_pt;
2207 
2208  // add the vector to the map
2209  old_to_new_corner_element_map.insert(
2210  std::pair<FiniteElement*, Vector<FiniteElement*> >(el_pt,temp_vec));
2211 
2212  if(nnode!=15)
2213  {
2214  this->add_node_pt(node0_pt);
2215  }
2216  this->add_node_pt(node1_pt);
2217  this->add_node_pt(node2_pt);
2218  this->add_node_pt(node3_pt);
2219  this->add_node_pt(node4_pt);
2220  if(nnode==15)
2221  {
2222  this->add_node_pt(node5_pt);
2223  this->add_node_pt(node6_pt);
2224  this->add_node_pt(node7_pt);
2225  this->add_node_pt(node8_pt);
2226  this->add_node_pt(node9_pt);
2227  }
2228 
2229  // Set nodal positions
2230  for (unsigned i=0;i<3;i++)
2231  {
2232  //Only bother to set centroid if does not already exist
2233  if(nnode!=15)
2234  {
2235  node0_pt->x(i)=0.25*(el_pt->node_pt(0)->x(i)+
2236  el_pt->node_pt(1)->x(i)+
2237  el_pt->node_pt(2)->x(i)+
2238  el_pt->node_pt(3)->x(i));
2239  }
2240 
2241  if (nnode_1d==3)
2242  {
2243  node1_pt->x(i)=0.5*(el_pt->node_pt(0)->x(i)+node0_pt->x(i));
2244  node2_pt->x(i)=0.5*(el_pt->node_pt(1)->x(i)+node0_pt->x(i));
2245  node3_pt->x(i)=0.5*(el_pt->node_pt(2)->x(i)+node0_pt->x(i));
2246  node4_pt->x(i)=0.5*(el_pt->node_pt(3)->x(i)+node0_pt->x(i));
2247  }
2248  }
2249 
2250 
2251  //Construct the four interior nodes if needed
2252  //and add to the list
2253  if(nnode==15)
2254  {
2255  //Set the positions of the newly created mid-face nodes
2256  //New Node 5 lies in the plane between original nodes 0 1 centroid
2257  for(unsigned i=0;i<3;++i)
2258  {
2259  node5_pt->x(i) =
2260  (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i)
2261  + el_pt->node_pt(14)->x(i))/3.0;
2262  }
2263 
2264  //New Node 6 lies in the plane between original nodes 1 3 centroid
2265  for(unsigned i=0;i<3;++i)
2266  {
2267  node6_pt->x(i) =
2268  (el_pt->node_pt(1)->x(i) + el_pt->node_pt(3)->x(i)
2269  + el_pt->node_pt(14)->x(i))/3.0;
2270  }
2271 
2272  //New Node 7 lies in the plane between original nodes 2 3 centroid
2273  for(unsigned i=0;i<3;++i)
2274  {
2275  node7_pt->x(i) =
2276  (el_pt->node_pt(2)->x(i) + el_pt->node_pt(3)->x(i)
2277  + el_pt->node_pt(14)->x(i))/3.0;
2278  }
2279 
2280  //New Node 8 lies in the plane between original nodes 0 2 centroid
2281  for(unsigned i=0;i<3;++i)
2282  {
2283  node8_pt->x(i) =
2284  (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i)
2285  + el_pt->node_pt(14)->x(i))/3.0;
2286  }
2287 
2288  //New Node 9 lies in the plane between original nodes 0 3 centroid
2289  for(unsigned i=0;i<3;++i)
2290  {
2291  node9_pt->x(i) =
2292  (el_pt->node_pt(0)->x(i) + el_pt->node_pt(3)->x(i)
2293  + el_pt->node_pt(14)->x(i))/3.0;
2294  }
2295 
2296  //New Node 10 lies in the plane between original nodes 1 2 centroid
2297  for(unsigned i=0;i<3;++i)
2298  {
2299  node10_pt->x(i) =
2300  (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i)
2301  + el_pt->node_pt(14)->x(i))/3.0;
2302  }
2303 
2304  //Now create the new centroid nodes
2305 
2306  //First element
2307  Node* temp_nod_pt = el1_pt->construct_node(14,time_stepper_pt);
2308  for(unsigned i=0;i<3;++i)
2309  {
2310  double av_pos = 0.0;
2311  for(unsigned j=0;j<4;j++)
2312  {
2313  av_pos += el1_pt->node_pt(j)->x(i);
2314  }
2315 
2316  temp_nod_pt->x(i) = 0.25*av_pos;
2317  }
2318 
2319  this->add_node_pt(temp_nod_pt);
2320 
2321  //Second element
2322  temp_nod_pt = el2_pt->construct_node(14,time_stepper_pt);
2323  for(unsigned i=0;i<3;++i)
2324  {
2325  double av_pos = 0.0;
2326  for(unsigned j=0;j<4;j++)
2327  {
2328  av_pos += el2_pt->node_pt(j)->x(i);
2329  }
2330  temp_nod_pt->x(i) = 0.25*av_pos;
2331  }
2332  this->add_node_pt(temp_nod_pt);
2333 
2334  //Third element
2335  temp_nod_pt = el3_pt->construct_node(14,time_stepper_pt);
2336  for(unsigned i=0;i<3;++i)
2337  {
2338  double av_pos = 0.0;
2339  for(unsigned j=0;j<4;j++)
2340  {
2341  av_pos += el3_pt->node_pt(j)->x(i);
2342  }
2343  temp_nod_pt->x(i) = 0.25*av_pos;
2344  }
2345  this->add_node_pt(temp_nod_pt);
2346 
2347  //Fourth element
2348  temp_nod_pt = el4_pt->construct_node(14,time_stepper_pt);
2349  for(unsigned i=0;i<3;++i)
2350  {
2351  double av_pos = 0.0;
2352  for(unsigned j=0;j<4;j++)
2353  {
2354  av_pos += el4_pt->node_pt(j)->x(i);
2355  }
2356  temp_nod_pt->x(i) = 0.25*av_pos;
2357  }
2358  this->add_node_pt(temp_nod_pt);
2359  }
2360 
2361  // Kill the old element
2362  delete el_pt;
2363 
2364  }
2365  }
2366 
2367  // Flush element storage
2368  Element_pt.clear();
2369 
2370  // Copy across
2371  nel=new_or_retained_el_pt.size();
2372  Element_pt.resize(nel);
2373  for (unsigned e=0;e<nel;e++)
2374  {
2375  Element_pt[e]=new_or_retained_el_pt[e];
2376  }
2377 
2378  // Setup boundary lookup scheme again
2379  setup_boundary_element_info();
2380 
2381  // -------------------------------------------------------------------------
2382  // The various boundary/region lookups now need updating to account for the
2383  // newly added/removed elements. This will be done in two stages:
2384  // Step 1: Add the new elements to the vector of elements in the same region
2385  // as the original corner element, and then delete the originals.
2386  // Updating this lookup makes things easier in the following step.
2387  // Step 2: Regenerate the two more specific lookups: One which gives the
2388  // elements on a given boundary in a given region, and the other
2389  // which maps elements on a given boundary in a given region to the
2390  // element's face index on that boundary.
2391  //
2392  // N.B. the lookup Triangular_facet_vertex_boundary_coordinate is setup in
2393  // the call to setup_boundary_element_info() above so doesn't need additional
2394  // work.
2395 
2396  // if we have no regions then we have no lookups to update so we're done here
2397  if(Region_attribute.size() == 0)
2398  {
2399  return;
2400  }
2401  // if we haven't had to split any corner elements then don't need to fiddle
2402  // with the lookups
2403  if(old_to_new_corner_element_map.size() == 0)
2404  {
2405  oomph_info << "\nNo corner elements need splitting\n\n";
2406  return;
2407  }
2408 
2409  // ------------------------------------------
2410  // Step 1: update the region element lookup
2411 
2412  // loop over the map of old corner elements which have been split
2413  for(std::map<FiniteElement*, Vector<FiniteElement*> >::iterator map_it =
2414  old_to_new_corner_element_map.begin();
2415  map_it != old_to_new_corner_element_map.end(); map_it++)
2416  {
2417  // extract the old and new elements from the map
2418  FiniteElement* original_el_pt = map_it->first;
2419  Vector<FiniteElement*> new_el_pt = map_it->second;
2420 
2421  unsigned original_region_index = 0;
2422 
2423 #ifdef PARANOID
2424  // flag for paranoia, if for some reason we don't find the original corner
2425  // element in any of the regions
2426  bool found = false;
2427 #endif
2428 
2429  Vector<FiniteElement*>::iterator region_element_it;
2430 
2431  // loop over the regions and look for this original corner element to find
2432  // out which region it used to be in, so we can add the new elements to
2433  // the same region.
2434  for(unsigned region_index=0;
2435  region_index<Region_element_pt.size(); region_index++)
2436  {
2437  // for each region, search the vector of elements in this region for the
2438  // original corner element
2439  region_element_it = std::find(Region_element_pt[region_index].begin(),
2440  Region_element_pt[region_index].end(),
2441  original_el_pt);
2442 
2443  // if the iterator hasn't reached the end then we've found it
2444  if(region_element_it != Region_element_pt[region_index].end())
2445  {
2446  // save the region index we're at
2447  original_region_index = region_index;
2448 
2449 #ifdef PARANOID
2450  // update the paranoid flag
2451  found = true;
2452 #endif
2453 
2454  // regions can't overlap, so once we've found one we're done
2455  break;
2456  }
2457  }
2458 
2459 #ifdef PARANOID
2460  if(!found)
2461  {
2462  std::ostringstream error_message;
2463  error_message
2464  << "The corner element being split does not appear to be in any "
2465  << "region, so something has gone wrong with the region lookup scheme\n";
2466 
2467  throw OomphLibError(error_message.str(),
2468  OOMPH_CURRENT_FUNCTION,
2469  OOMPH_EXCEPTION_LOCATION);
2470  }
2471 #endif
2472 
2473  // Now update the basic region lookup. The iterator will still point to the
2474  // original corner element in the lookup, so we can delete this easily
2475  Region_element_pt[original_region_index].erase(region_element_it);
2476  for(unsigned i=0; i<4; i++)
2477  {
2478  Region_element_pt[original_region_index].push_back(new_el_pt[i]);
2479  }
2480  }
2481  // ------------------------------------------
2482  // Step 2: Clear and regenerate lookups
2483 
2484  Face_index_region_at_boundary.clear();
2485  Boundary_region_element_pt.clear();
2486 
2487  Face_index_region_at_boundary.resize(nboundary());
2488  Boundary_region_element_pt.resize(nboundary());
2489 
2490  for(unsigned b=0; b<nboundary(); b++)
2491  {
2492  // Loop over elements next to that boundary
2493  unsigned nel = this->nboundary_element(b);
2494  for (unsigned e=0; e<nel; e++)
2495  {
2496  FiniteElement* el_pt = boundary_element_pt(b,e);
2497 
2498  // now search for it in each region
2499  for(unsigned r_index=0; r_index<Region_attribute.size(); r_index++)
2500  {
2501  unsigned region_id = static_cast<unsigned>(
2502  Region_attribute[r_index]);
2503 
2505  std::find(Region_element_pt[r_index].begin(),
2506  Region_element_pt[r_index].end(), el_pt);
2507 
2508  // if we find this element in the current region, then update our
2509  // lookups
2510  if(it != Region_element_pt[r_index].end())
2511  {
2512  Boundary_region_element_pt[b][region_id].push_back(el_pt);
2513 
2514  unsigned face_index = face_index_at_boundary(b,e);
2515  Face_index_region_at_boundary[b][region_id].push_back(face_index);
2516  }
2517  }
2518  }
2519  }
2520 
2521  oomph_info << "\nNumber of outer corner elements split: "
2522  << old_to_new_corner_element_map.size() << "\n\n";
2523 
2524 } // end split_elements_in_corners()
2525 }
2526 
2527 #endif
2528 
unsigned one_based_boundary_id() const
First (of possibly multiple) one-based boundary id.
Definition: tet_mesh.h:117
bool boundaries_can_be_split_in_tetgen()
Test whether boundary can be split in tetgen.
Definition: tet_mesh.h:351
void broken_copy(const std::string &class_name)
Issue error message and terminate execution.
virtual void set_coordinates_on_boundary(const unsigned &b, const unsigned &k, const Vector< double > &boundary_zeta)
Set the vector of the k-th generalised boundary coordinates on mesh boundary b. Broken virtual interf...
Definition: nodes.cc:2315
static double Tolerance_for_boundary_finding
Global static data that specifies the permitted error in the setup of the boundary coordinates...
Definition: tet_mesh.h:670
TetMeshVertex(const Vector< double > &x)
Constructor: Pass coordinates (length 3!)
Definition: tet_mesh.h:63
bool faceted_volume_represents_hole_for_gmsh() const
Does closed surface represent hole for gmsh?
Definition: tet_mesh.h:559
void set_hole_for_tetgen(const Vector< double > &hole_point)
Specify coordinate of hole for tetgen.
Definition: tet_mesh.h:572
bool is_doc_enabled() const
Are we documenting?
static Steady< 0 > Default_TimeStepper
Default Steady Timestepper, to be used in default arguments to Mesh constructors. ...
Definition: mesh.h:85
void enable_boundaries_can_be_split_in_tetgen()
Test whether boundaries can be split in tetgen.
Definition: tet_mesh.h:357
std::map< unsigned, Vector< Vector< double > > > Triangular_facet_vertex_boundary_coordinate
Boundary coordinates of vertices in triangular facets associated with given boundary. Is only set up for triangular facets!
Definition: tet_mesh.h:1052
void set_vertex_pt(const unsigned &j, TetMeshVertex *vertex_pt)
Definition: tet_mesh.h:184
unsigned nvertex() const
Number of vertices.
Definition: tet_mesh.h:315
Information for documentation of results: Directory and file number to enable output in the form RESL...
unsigned nregion()
Return the number of regions specified by attributes.
Definition: tet_mesh.h:852
unsigned nvertex() const
Number of vertices.
Definition: tet_mesh.h:171
TimeStepper * Time_stepper_pt
Timestepper used to build nodes.
Definition: tet_mesh.h:1055
std::string directory() const
Output directory.
cstr elem_len * i
Definition: cfortran.h:607
TetMeshVertex * vertex_pt(const unsigned &j) const
Pointer to j-th vertex.
Definition: tet_mesh.h:375
bool Boundaries_can_be_split_in_tetgen
Boolean to indicate whether extra vertices can be added on the boundary in tetgen.
Definition: tet_mesh.h:481
Vector< double > X
Coordinate vector.
Definition: tet_mesh.h:135
FiniteElement * boundary_element_in_region_pt(const unsigned &b, const unsigned &r, const unsigned &e) const
Return pointer to the e-th element adjacent to boundary b in region r.
Definition: tet_mesh.h:808
DiskLikeGeomObjectWithBoundaries * Geom_object_with_boundaries_pt
GeomObject with boundaries associated with this surface.
Definition: tet_mesh.h:488
std::string & label()
String used (e.g.) for labeling output files.
Vector< std::pair< Vector< double >, int > > Internal_point_for_tetgen
Definition: tet_mesh.h:618
A general Finite Element class.
Definition: elements.h:1274
Base class for tet meshes (meshes made of 3D tet elements).
Definition: tet_mesh.h:645
std::set< unsigned > One_based_adjacent_region_id
Set of one-based adjacent region ids; no adjacent region if it&#39;s zero.
Definition: tet_mesh.h:282
void set_one_based_adjacent_region_id(const unsigned &one_based_id)
Set (one-based!) region ID this facet is adjacent to. Specification of zero argument is harmless as i...
Definition: tet_mesh.h:221
bool facet_is_embedded_in_a_specified_region()
Boolean indicating that facet is embedded in a specified region.
Definition: tet_mesh.h:233
bool Faceted_volume_represents_hole_for_gmsh
Does closed surface represent hole for gmsh?
Definition: tet_mesh.h:621
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition: elements.h:2420
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
double x(const unsigned &i) const
i-th coordinate
Definition: tet_mesh.h:111
TetMeshFacet * facet_pt(const unsigned &j) const
Pointer to j-th facet.
Definition: tet_mesh.h:369
virtual void position(const Vector< double > &zeta, Vector< double > &r) const =0
Parametrised position on object at current time: r(zeta).
OomphInfo oomph_info
virtual void boundary_zeta12(const unsigned &facet_id, const double &zeta_boundary, Vector< double > &zeta)
Virtual function that specifies the variation of the zeta coordinates in the GeomObject along the bou...
Definition: tet_mesh.h:430
e
Definition: cfortran.h:575
void set_one_based_region_that_facet_is_embedded_in(const unsigned &one_based_region_id)
Facet is to be embedded in specified one-based region.
Definition: tet_mesh.h:239
Vector< Vector< FiniteElement * > > Region_element_pt
Vectors of vectors of elements in each region (note: this just stores them; the region IDs are contai...
Definition: tet_mesh.h:1020
double size() const
Definition: elements.cc:4207
void set_region_for_tetgen(const unsigned &region_id, const Vector< double > &region_point)
Specify a region; pass (zero-based) region ID and coordinate of point in region for tetgen...
Definition: tet_mesh.h:579
virtual void get_coordinates_on_boundary(const unsigned &b, const unsigned &k, Vector< double > &boundary_zeta)
Return the vector of the k-th generalised boundary coordinates on mesh boundary b. Broken virtual interface provides run-time error checking.
Definition: nodes.cc:2301
Vector< TetMeshVertex * > Vertex_pt
Pointer to vertices.
Definition: tet_mesh.h:275
void output(std::ostream &outfile) const
Output.
Definition: tet_mesh.h:388
TetMeshFacet(const unsigned &nvertex)
Constructor: Specify number of vertices.
Definition: tet_mesh.h:162
int face_index_at_boundary_in_region(const unsigned &b, const unsigned &r, const unsigned &e) const
Return face index of the e-th element adjacent to boundary b in region r.
Definition: tet_mesh.h:826
void split_elements_in_corners(TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Non-Delaunay split elements that have three faces on a boundary into sons. Timestepper species timest...
Definition: tet_mesh.h:1946
Vector< TetMeshFacet * > Facet_pt
Vector of pointers to facets.
Definition: tet_mesh.h:477
bool internal_point_identifies_region_for_tetgen(const unsigned &j)
Is j-th internal point for tetgen associated with a region?
Definition: tet_mesh.h:607
unsigned One_based_boundary_id
(One-based!) boundary IDs this facet lives on
Definition: tet_mesh.h:278
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
void setup_boundary_element_info()
Definition: tet_mesh.h:1004
virtual ~TetMeshFacetedSurface()
Empty destructor.
Definition: tet_mesh.h:312
Vector< Vector< unsigned > > Facet_vertex_index_in_tetgen
Facet connectivity: Facet_vertex_index[f][j] is the index of the j-th vertex (in the Vertex_pt vector...
Definition: tet_mesh.h:485
void set_one_based_boundary_id(const unsigned &one_based_id)
Set (one-based!) boundary IDs this facet lives on. Passed to any existing vertices and to any future ...
Definition: tet_mesh.h:204
unsigned one_based_region_that_facet_is_embedded_in()
Definition: tet_mesh.h:247
void setup_boundary_coordinates(const unsigned &b, const bool &switch_normal)
Setup boundary coordinate on boundary b which is assumed to be planar. Boundary coordinates are the x...
Definition: tet_mesh.h:725
Vector< std::map< unsigned, Vector< FiniteElement * > > > Boundary_region_element_pt
Storage for elements adjacent to a boundary in a particular region.
Definition: tet_mesh.h:1029
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object. ...
Definition: elements.h:2392
unsigned one_based_vertex_boundary_id(const unsigned &j) const
First (of possibly multiple) one-based boundary id of j-th vertex.
Definition: tet_mesh.h:333
A template Class for BoundaryNodes; that is Nodes that MAY live on the boundary of a Mesh...
Definition: nodes.h:67
TetMeshFacetedClosedSurface * Outer_boundary_pt
Faceted surface that defines outer boundaries.
Definition: tet_mesh.h:1036
std::map< unsigned, TetMeshFacetedSurface * > Tet_mesh_faceted_surface_pt
Reverse lookup scheme: Pointer to faceted surface (if any!) associated with boundary b...
Definition: tet_mesh.h:1043
static char t char * s
Definition: cfortran.h:572
Vector< std::map< unsigned, Vector< int > > > Face_index_region_at_boundary
Storage for the face index adjacent to a boundary in a particular region.
Definition: tet_mesh.h:1033
Vector< TetMeshVertex * > Vertex_pt
Vector pointers to vertices.
Definition: tet_mesh.h:474
bool internal_point_identifies_hole_for_tetgen(const unsigned &j)
Is j-th internal point for tetgen associated with a hole?
Definition: tet_mesh.h:601
void snap_to_quadratic_surface(const Vector< unsigned > &boundary_id, const std::string &quadratic_surface_file_name, const bool &switch_normal, DocInfo &doc_info)
Snap boundaries specified by the IDs listed in boundary_id to a quadratric surface, specified in the file quadratic_surface_file_name. This is usually used with vmtk-based meshes for which oomph-lib&#39;s xda to poly conversion code produces the files "quadratic_fsi_boundary.dat" and "quadratic_outer_solid_boundary.dat" which specify the quadratic FSI boundary (for the fluid and the solid) and the quadratic representation of the outer boundary of the solid. When used with these files, the flag switch_normal should be set to true when calling the function for the outer boundary of the solid. The DocInfo object can be used to label optional output files. (Uses directory and label).
Definition: tet_mesh.h:1462
virtual void boundary_zeta20(const unsigned &facet_id, const double &zeta_boundary, Vector< double > &zeta)
Virtual function that specifies the variation of the zeta coordinates in the GeomObject along the bou...
Definition: tet_mesh.h:447
void setup_boundary_coordinates(const unsigned &b)
Setup boundary coordinate on boundary b which is assumed to be planar. Boundary coordinates are the x...
Definition: tet_mesh.h:694
std::set< unsigned > one_based_adjacent_region_id() const
Return set of (one-based!) region IDs this facet is adjacent to.
Definition: tet_mesh.h:227
DiskLikeGeomObjectWithBoundaries * geom_object_with_boundaries_pt()
Access to GeomObject with boundaries associated with this surface (Null if there isn&#39;t one!) ...
Definition: tet_mesh.h:382
void set_zeta_in_geom_object(const Vector< double > &zeta)
Set intrinisic coordinates in GeomObject.
Definition: tet_mesh.h:83
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
unsigned nfacet() const
Number of facets.
Definition: tet_mesh.h:321
void output(std::ostream &outfile)
Output with default number of plot points.
void output(std::ostream &outfile) const
Output.
Definition: tet_mesh.h:253
virtual ~TetMeshBase()
Destructor (empty)
Definition: tet_mesh.h:666
TetMeshFacetedClosedSurface()
Constructor:
Definition: tet_mesh.h:539
Vector< double > Zeta_in_geom_object
Intrinisic coordinates in GeomObject (zero sized if there isn&#39;t one.
Definition: tet_mesh.h:142
void disable_doc()
Disable documentation.
const int & region_id_for_tetgen(const unsigned &j) const
Return the (zero-based) region ID of j-th internal point for tetgen. Negative if it&#39;s actually a hole...
Definition: tet_mesh.h:594
virtual void boundary_zeta01(const unsigned &facet_id, const double &zeta_boundary, Vector< double > &zeta)
Virtual function that specifies the variation of the zeta coordinates in the GeomObject along the bou...
Definition: tet_mesh.h:413
Vector< unsigned > vertex_index_in_tetgen(const unsigned &f)
Facet connectivity: vertex_index[j] is the index of the j-th vertex (in the Vertex_pt vector) in face...
Definition: tet_mesh.h:462
void enable_faceted_volume_represents_hole_for_gmsh()
Declare closed surface to represent hole for gmsh.
Definition: tet_mesh.h:547
void broken_assign(const std::string &class_name)
Issue error message and terminate execution.
void operator=(const TetMeshBase &)
Broken assignment operator.
Definition: tet_mesh.h:660
void output(const std::string &filename) const
Output.
Definition: tet_mesh.h:399
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
unsigned one_based_facet_boundary_id(const unsigned &j) const
One-based boundary id of j-th facet.
Definition: tet_mesh.h:327
double region_attribute(const unsigned &i)
Return the i-th region attribute (here only used as the (assumed to be unsigned) region id...
Definition: tet_mesh.h:899
void setup_facet_connectivity_for_tetgen()
Setup facet connectivity for tetgen.
Definition: tet_mesh.h:495
virtual bool is_on_boundary() const
Test whether the Node lies on a boundary. The "bulk" Node cannot lie on a boundary, so return false. This will be overloaded by BoundaryNodes.
Definition: nodes.h:1290
void setup_boundary_coordinates(const unsigned &b, std::ofstream &outfile)
Setup boundary coordinate on boundary b which is assumed to be planar. Boundary coordinates are the x...
Definition: tet_mesh.h:778
unsigned One_based_region_id_that_facet_is_embedded_in
Facet is to be embedded in specified one-based region. Defaults to zero, indicating that its not embe...
Definition: tet_mesh.h:287
TetMeshBase()
Constructor.
Definition: tet_mesh.h:651
std::set< unsigned > One_based_boundary_id
Set of (one-based!) boundary IDs this vertex lives on.
Definition: tet_mesh.h:138
double vertex_coordinate(const unsigned &j, const unsigned &i) const
i-th coordinate of j-th vertex
Definition: tet_mesh.h:339
unsigned nregion_element(const unsigned &r)
Return the number of elements in region r.
Definition: tet_mesh.h:858
TetMeshFacetedSurface()
Constructor:
Definition: tet_mesh.h:307
Vector< double > Region_attribute
Vector of attributes associated with the elements in each region NOTE: double is enforced on us by te...
Definition: tet_mesh.h:1025
virtual ~TetMeshFacetedClosedSurface()
Empty destructor.
Definition: tet_mesh.h:544
unsigned nvertex_on_facet(const unsigned &j) const
Number of vertices defining the j-th facet.
Definition: tet_mesh.h:345
TetMeshVertex * vertex_pt(const unsigned &j) const
Pointer to j-th vertex (const)
Definition: tet_mesh.h:177
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
Vector< double > zeta_in_geom_object() const
Get intrinisic coordinates in GeomObject (returns zero sized vector if no such coordinates have been ...
Definition: tet_mesh.h:105
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
Definition: timesteppers.h:219
FiniteElement * region_element_pt(const unsigned &r, const unsigned &e)
Return the e-th element in the r-th region.
Definition: tet_mesh.h:905
void set_one_based_boundary_id(const unsigned &id)
Set of (one-based!) boundary IDs this vertex lives on.
Definition: tet_mesh.h:129
Vector< TetMeshFacetedSurface * > Internal_surface_pt
Vector to faceted surfaces that define internal boundaries.
Definition: tet_mesh.h:1039
std::map< unsigned, TetMeshFacet * > Tet_mesh_facet_pt
Reverse lookup scheme: Pointer to facet (if any!) associated with boundary b.
Definition: tet_mesh.h:1047
unsigned ninternal_point_for_tetgen()
Number of internal points (identifying either regions or holes) for tetgen.
Definition: tet_mesh.h:587
TetMeshBase(const TetMeshBase &node)
Broken copy constructor.
Definition: tet_mesh.h:654
unsigned nboundary_element_in_region(const unsigned &b, const unsigned &r) const
Return the number of elements adjacent to boundary b in region r.
Definition: tet_mesh.h:789
A general mesh class.
Definition: mesh.h:74
unsigned one_based_boundary_id() const
Constant access to (one-based!) boundary IDs this facet lives on.
Definition: tet_mesh.h:197
void disable_boundaries_can_be_split_in_tetgen()
Test whether boundaries can be split in tetgen.
Definition: tet_mesh.h:363
const double & internal_point_for_tetgen(const unsigned &j, const unsigned &i) const
i=th coordinate of the j-th internal point for tetgen
Definition: tet_mesh.h:565
void snap_to_quadratic_surface(const Vector< unsigned > &boundary_id, const std::string &quadratic_surface_file_name, const bool &switch_normal)
Snap boundaries specified by the IDs listed in boundary_id to a quadratric surface, specified in the file quadratic_surface_file_name. This is usually used with vmtk-based meshes for which oomph-lib&#39;s xda to poly conversion code produces the files "quadratic_fsi_boundary.dat" and "quadratic_outer_solid_boundary.dat" which specify the quadratic FSI boundary (for the fluid and the solid) and the quadratic representation of the outer boundary of the solid. When used with these files, the flag switch_normal should be set to true when calling the function for the outer boundary of the solid.
Definition: tet_mesh.h:973
void disable_faceted_volume_represents_hole_for_gmsh()
Declare closed surface NOT to represent hole for gmsh.
Definition: tet_mesh.h:553