unstructured_two_d_mesh_geometry_base.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: 1182 $
7 //LIC//
8 //LIC// $LastChangedDate: 2016-05-20 16:50:20 +0100 (Fri, 20 May 2016) $
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 // Contains the definition of a TriangulateIO object. This is used to
31 // define the complex geometry of a two-dimensional mesh which is why
32 // it resides here. The definition of things like TriangleMeshPolygons
33 // and other classes which define geometrical aspects of a 2D mesh can
34 // also be found here. The class UnstructuredTwoDMeshGeometryBase is
35 // defined here. It forms the base class for QuadFromTriangleMesh and
36 // TriangleMeshBase. This class makes use of previously written code
37 // to create TriangleScaffoldMeshes and avoid a large amount of code
38 // duplication.
39 #ifndef OOMPH_UNSTRUCTURED_TWO_D_MESH_GEOMETRY_BASE_HEADER
40 #define OOMPH_UNSTRUCTURED_TWO_D_MESH_GEOMETRY_BASE_HEADER
41 
42 // The scaffold mesh
43 #include "mesh.h"
44 
45 using namespace oomph;
46 using namespace std;
47 
48 
49 ////////////////////////////////////////////////////////////////////
50 ////////////////////////////////////////////////////////////////////
51 ////////////////////////////////////////////////////////////////////
52 
53 
54 namespace oomph
55 {
56 
57 #ifdef OOMPH_HAS_TRIANGLE_LIB
58 
59 //=====================================================================
60 /// The Triangle data structure, modified from the triangle.h header
61 /// supplied with triangle 1.6. by J. R. Schewchuk. We need to define
62 /// this here separately because we can't include a c header directly
63 /// into C++ code!
64 //=====================================================================
65  struct TriangulateIO
66  {
67  ///Pointer to list of points x coordinate followed by y coordinate
68  double *pointlist;
69 
70  ///Pointer to list of point attributes
72 
73  ///Pointer to list of point markers
77 
85 
89 
90  double *holelist;
92 
93  double *regionlist;
95 
96  int *edgelist;
97  int *edgemarkerlist; // <---- contains boundary ID (offset by one)
98  double *normlist;
100 
101  };
102 
103 
104 ////////////////////////////////////////////////////////////////////
105 ////////////////////////////////////////////////////////////////////
106 ////////////////////////////////////////////////////////////////////
107 
108 
109 //==================================================================
110 /// Helper namespace for triangle meshes
111 //==================================================================
112  namespace TriangleHelper
113  {
114  /// Clear TriangulateIO structure
115  extern void clear_triangulateio(TriangulateIO& triangulate_io,
116  const bool& clear_hole_data=true);
117 
118  /// Initialise TriangulateIO structure
119  extern void initialise_triangulateio(TriangulateIO& triangle_io);
120 
121  /// \short Make (partial) deep copy of TriangulateIO object. We only copy
122  /// those items we need within oomph-lib's adaptation procedures.
123  /// Warnings are issued if triangulate_io contains data that is not
124  /// not copied, unless quiet=true;
126  TriangulateIO& triangle_io,const bool& quiet);
127 
128  /// \short Write the triangulateio data to disk as a poly file,
129  /// mainly used for debugging
130  extern void write_triangulateio_to_polyfile(TriangulateIO &triangle_io,
131  std::ostream &poly_file);
132 
133  /// Create a triangulateio data file from ele node and poly
134  /// files. This is used if the mesh is generated by using Triangle externally.
135  /// The triangulateio structure is required to dump the mesh topology for
136  /// restarts.
138  const std::string& node_file_name,
139  const std::string& element_file_name,
140  const std::string& poly_file_name,
141  TriangulateIO &triangle_io,
142  bool &use_attributes);
143 
144  /// \short Write all the triangulateio data to disk in a dump file
145  /// that can then be used to restart simulations
146  extern void dump_triangulateio(TriangulateIO &triangle_io,
147  std::ostream &dump_file);
148 
149  /// \short Read the triangulateio data from a dump file on
150  /// disk, which can then be used to restart simulations
151  extern void read_triangulateio(std::istream &restart_file,
152  TriangulateIO &triangle_io);
153 
154  } // End of TriangleHelper namespace
155 
156 #endif
157 
158 
159 /////////////////////////////////////////////////////////////////////////
160 /////////////////////////////////////////////////////////////////////////
161 /////////////////////////////////////////////////////////////////////////
162 
163 
164  class TriangleMeshPolyLine;
165  class TriangleMeshCurviLine;
166 
167 //=====================================================================
168 /// Base class for defining a triangle mesh boundary, this class has the
169 /// methods that allow to connect the initial and final ends to other
170 /// triangle mesh boundaries
171 //=====================================================================
173  {
174 
175  public:
176 
177  /// Empty constructor. Initialises the curve section as non connected
179  Initial_vertex_connected(false),
180  Final_vertex_connected(false),
181  Initial_vertex_connected_suspended(false),
182  Final_vertex_connected_suspended(false),
183  Initial_vertex_connected_to_curviline(false),
184  Final_vertex_connected_to_curviline(false),
185  Refinement_tolerance(0.08),
186  Unrefinement_tolerance(0.04),
187  Maximum_length(-1.0)
188  { }
189 
190  /// Empty destructor
192 
193  /// \short Number of segments that this part of the
194  /// boundary is to be represented by. This corresponds
195  /// to the number of straight-line segments in triangle
196  /// representation.
197  virtual unsigned nsegment() const = 0;
198 
199  /// Boundary id
200  virtual unsigned boundary_id()const = 0;
201 
202  /// Boundary chunk (Used when a boundary is represented by more
203  /// than one polyline
204  virtual unsigned boundary_chunk()const = 0;
205 
206  /// Number of vertices
207  virtual unsigned nvertex() const = 0;
208 
209  /// Output the curve_section
210  virtual void output(std::ostream &outfile, const unsigned& n_sample=50) = 0;
211 
212  /// \short Enable refinement of curve section to create a better
213  /// representation of curvilinear boundaries (e.g. in free-surface
214  /// problems). See tutorial for
215  /// interpretation of the optional argument which specifies the
216  /// refinement tolerance. It defaults to 0.08 and the smaller the
217  /// number the finer the surface representation.
218  void enable_refinement_tolerance(const double& tolerance=0.08)
219  {
220  Refinement_tolerance=tolerance;
221  }
222 
223  /// \short Set tolerance for refinement of curve sections to create a better
224  /// representation of curvilinear boundaries (e.g. in free-surface
225  /// problems). See tutorial for
226  /// interpretation of the refinement tolerance. (The smaller the
227  /// number the finer the surface representation). If set to
228  /// a negative value, we're switching off refinement --
229  /// equivalent to calling disable_polyline_refinement()
230  void set_refinement_tolerance(const double& tolerance)
231  {
232  Refinement_tolerance=tolerance;
233  }
234 
235  /// \short Get tolerance for refinement of curve sections to create a better
236  /// representation of curvilinear boundaries (e.g. in free-surface
237  /// problems). See tutorial for
238  /// interpretation. If it's negative refinement is disabled.
240  {
241  return Refinement_tolerance;
242  }
243 
244  /// \short Disable refinement of curve section
246  {
247  Refinement_tolerance=-1.0;
248  }
249 
250  /// \short Enable unrefinement of curve sections to avoid unnecessarily large
251  /// numbers of elements on of curvilinear boundaries (e.g. in free-surface
252  /// problems). See tutorial for
253  /// interpretation of the optional argument which specifies the
254  /// unrefinement tolerance. It defaults to 0.04 and the larger the number
255  /// the more agressive we are when removing unnecessary vertices on
256  /// gently curved polylines.
257  void enable_unrefinement_tolerance(const double& tolerance=0.04)
258  {
259  Unrefinement_tolerance=tolerance;
260  }
261 
262  /// \short Set tolerance for unrefinement of curve sections
263  /// to avoid unnecessarily large
264  /// numbers of elements on of curvilinear boundaries (e.g. in free-surface
265  /// problems). See tutorial for
266  /// interpretation of the optional argument which specifies the
267  /// unrefinement tolerance. It defaults to 0.04 and the larger the number
268  /// the more agressive we are when removing unnecessary vertices on
269  /// gently curved polylines. If set to
270  /// a negative value, we're switching off unrefinement --
271  /// equivalent to calling disable_curve_section_unrefinement()
272  void set_unrefinement_tolerance(const double& tolerance)
273  {
274  Unrefinement_tolerance=tolerance;
275  }
276 
277  /// \short Get tolerance for unrefinement of curve section to create a better
278  /// representation of curvilinear boundaries (e.g. in free-surface
279  /// problems). See tutorial for
280  /// interpretation. If it's negative unrefinement is disabled.
282  {
283  return Unrefinement_tolerance;
284  }
285 
286  /// \short Disable unrefinement of curve sections
288  {
289  Unrefinement_tolerance=-1.0;
290  }
291 
292  /// \short Allows to specify the maximum distance between two vertices
293  /// that define the associated polyline of the curve section, it only
294  /// takes effect on the unrefinement and refinement steps
295  void set_maximum_length(const double &maximum_length)
296  {Maximum_length = maximum_length;}
297 
298  /// \short Disables the use of the maximum length criteria on the unrefinement
299  /// or refinement steps
301  {Maximum_length=-1.0;}
302 
303  /// \short Gets access to the maximum length variable
304  double maximum_length()
305  {return Maximum_length;}
306 
307  /// Get first vertex coordinates
308  virtual void initial_vertex_coordinate(Vector<double> &vertex) = 0;
309 
310  /// Get last vertex coordinates
311  virtual void final_vertex_coordinate(Vector<double> &vertex) = 0;
312 
313  /// \short Connects the initial vertex of the curve section to a desired
314  /// target polyline by specifying the vertex number. There is a checking
315  /// which verifies that the initial vertex is close enough to the
316  /// destination vertex on the target polyline by no more than the specified
317  /// tolerance
318  void connect_initial_vertex_to_polyline(
319  TriangleMeshPolyLine *polyline_pt,
320  const unsigned &vertex_number,
321  const double &tolerance_for_connection = 1.0e-14);
322 
323  /// \short Connects the final vertex of the curve section to a desired
324  /// target polyline by specifying the vertex number. There is a checking
325  /// which verifies that the final vertex is close enough to the
326  /// destination vertex on the target polyline by no more than the specified
327  /// tolerance
328  void connect_final_vertex_to_polyline(
329  TriangleMeshPolyLine *polyline_pt,
330  const unsigned &vertex_number,
331  const double &tolerance_for_connection = 1.0e-14);
332 
333  /// \short Connects the initial vertex of the curve section to a desired
334  /// target curviline by specifying the s value (intrinsic value on the
335  /// geometric object of the curviline) where to connect on the target
336  /// curviline. There is a checking which verifies that the initial vertex
337  /// and the coordinates on the given s value are close enough by no more
338  /// than the given tolerance
339  void connect_initial_vertex_to_curviline(
340  TriangleMeshCurviLine *curviline_pt,
341  const double &s_value,
342  const double &tolerance_for_connection = 1.0e-14);
343 
344  /// \short Connects the final vertex of the curve section to a desired
345  /// target curviline by specifying the s value (intrinsic value on the
346  /// geometric object of the curviline) where to connect on the target
347  /// curviline. There is a checking which verifies that the final vertex
348  /// and the coordinates on the given s value are close enough by no more
349  /// than the given tolerance
350  void connect_final_vertex_to_curviline(
351  TriangleMeshCurviLine *curviline_pt,
352  const double &s_value,
353  const double &tolerance_for_connection = 1.0e-14);
354 
355  /// Test whether initial vertex is connected or not
357  {return Initial_vertex_connected;}
358 
359  /// Sets the initial vertex as connected
361  {Initial_vertex_connected = true;}
362 
363  /// Sets the initial vertex as non connected
365  {Initial_vertex_connected = false;}
366 
367  /// Set the initial vertex connection as suspended, it will be
368  /// resumed when the method to resume the connections is called
369  /// This method is only used in a distributed context, when the
370  /// boundary to connect is no longer part of the domain in the
371  /// processor
373  {
374  if (Initial_vertex_connected)
375  {
376  Initial_vertex_connected = false;
377  Initial_vertex_connected_suspended = true;
378  }
379  }
380 
381  /// Resumes the initial vertex connection, it may be that after load
382  /// balancing the boundary to which the connection was suspended be part
383  /// of the domain
385  {
386  if (Initial_vertex_connected_suspended)
387  {
388  Initial_vertex_connected = true;
389  Initial_vertex_connected_suspended = false;
390  }
391  }
392 
393  /// Test whether final vertex is connected or not
395  {return Final_vertex_connected;}
396 
397  /// Sets the final vertex as connected
399  {Final_vertex_connected = true;}
400 
401  /// Sets the final vertex as non connected
403  {Final_vertex_connected = false;}
404 
405  /// Set the final vertex connection as suspended, it will be
406  /// resumed when the method to resume the connections is called
407  /// This method is only used in a distributed context, when the
408  /// boundary to connect is no longer part of the domain in the
409  /// processor
411  {
412  if (Final_vertex_connected)
413  {
414  Final_vertex_connected = false;
415  Final_vertex_connected_suspended = true;
416  }
417  }
418 
419  /// Resumes the final vertex connection, it may be that after load
420  /// balancing the boundary to which the connection was suspended be part
421  /// of the domain
423  {
424  if (Final_vertex_connected_suspended)
425  {
426  Final_vertex_connected = true;
427  Final_vertex_connected_suspended = false;
428  }
429  }
430 
431  /// Gets the id to which the initial end is connected
433  {return Initial_vertex_connected_bnd_id;}
434 
435  /// Sets the id to which the initial end is connected
437  {return Initial_vertex_connected_bnd_id;}
438 
439  /// Gets the vertex number to which the initial end is connected
441  {return Initial_vertex_connected_n_vertex;}
442 
443  /// Sets the vertex number to which the initial end is connected
445  {return Initial_vertex_connected_n_vertex;}
446 
447  /// Gets the boundary chunk to which the initial end is connected
449  {return Initial_vertex_connected_n_chunk;}
450 
451  /// Sets the boundary chunk to which the initial end is connected
453  {return Initial_vertex_connected_n_chunk;}
454 
455  /// Gets the id to which the final end is connected
457  {return Final_vertex_connected_bnd_id;}
458 
459  /// Sets the id to which the final end is connected
461  {return Final_vertex_connected_bnd_id;}
462 
463  /// Sets the vertex number to which the final end is connected
465  {return Final_vertex_connected_n_vertex;}
466 
467  /// Gets the vertex number to which the final end is connected
469  {return Final_vertex_connected_n_vertex;}
470 
471  /// Gets the boundary chunk to which the final end is connected
473  {return Final_vertex_connected_n_chunk;}
474 
475  /// Sets the boundary chunk to which the final end is connected
477  {return Final_vertex_connected_n_chunk;}
478 
479  /// Test whether the initial vertex is connected to a curviline
481  {return Initial_vertex_connected_to_curviline;}
482 
483  /// Sets the initial vertex as connected to a curviline
485  {Initial_vertex_connected_to_curviline = true;}
486 
487  /// Sets the initial vertex as non connected to a curviline
489  {Initial_vertex_connected_to_curviline = false;}
490 
491  /// Test whether the final vertex is connected to a curviline
493  {return Final_vertex_connected_to_curviline;}
494 
495  /// Sets the final vertex as connected to a curviline
497  {Final_vertex_connected_to_curviline = true;}
498 
499  /// Sets the final vertex as non connected to a curviline
501  {Final_vertex_connected_to_curviline = false;}
502 
503  /// \short Gets the s value to which the initial end is connected
505  {return Initial_s_connection_value;}
506 
507  /// \short Sets the s value to which the initial end is connected
509  {return Initial_s_connection_value;}
510 
511  /// \short Gets the s value to which the final end is connected
513  {return Final_s_connection_value;}
514 
515  /// \short Sets the s value to which the final end is connected
517  {return Final_s_connection_value;}
518 
519  /// \short Gets the tolerance value for connections among
520  /// curvilines
522  {return Tolerance_for_s_connection;}
523 
524  /// \short Sets the tolerance value for connections among
525  /// curvilines
527  {return Tolerance_for_s_connection;}
528 
529  protected:
530 
531  /// \short Used for stating if the initial end is connected
532  /// to another boundary
534 
535  /// \short Used for stating if the final end is connected
536  /// to another boundary
538 
539  /// \short Indicates if the connection is suspended because the
540  /// boundary to connect is no longer part of the domain (only used in
541  /// a distributed context)
543 
544  /// \short Indicates if the connection is suspended because the
545  /// boundary to connect is no longer part of the domain (only used in
546  /// a distributed context)
548 
549  /// Stores the id to which the initial end is connected
551 
552  /// \short Stores the vertex number used for connection with
553  /// the initial end
555 
556  /// \short Stores the chunk number of the boundary to which is
557  /// connected th initial end
559 
560  /// Stores the id to which the initial end is connected
562 
563  /// \short Stores the vertex number used for connection with
564  /// the final end
566 
567  /// \short Stores the chunk number of the boundary to which is
568  /// connected th initial end
570 
571  /// States if the initial vertex is connected to a curviline
573 
574  /// States if the final vertex is connected to a curviline
576 
577  /// \short Stores the s value used for connecting the
578  /// initial end with a curviline
580 
581  /// \short Stores the s value used for connecting the
582  /// final end with a curviline
584 
585  /// Tolerance used for connecting the ends to a curviline
587 
588  private:
589 
590  /// Tolerance for refinement of curve sections (neg if refinement is
591  /// disabled)
593 
594  /// Tolerance for unrefinement of curve sections (neg if refinement
595  /// is disabled)
597 
598  /// Maximum allowed distance between two vertices on the polyline
599  /// representation of the curve section
601 
602  };
603 
604 
605 //=====================================================================
606 /// Class definining a curvilinear triangle mesh boundary in terms
607 /// of a GeomObject. Curvlinear equivalent of PolyLine.
608 //=====================================================================
610  {
611 
612  public:
613 
614  /// \short Constructor: Specify GeomObject, the start and end coordinates
615  /// of the relevant boundary in terms of the GeomObject's intrinsic
616  /// coordinate, the number of (initially straight-line) segments that
617  /// this GeomObject is to be split up into, and the boundary ID.
618  /// The final optional boolean argument specifies if vertices in
619  /// polygonhal represenation are spaced
620  /// (approximately) evenly in arclength along the GeomObject [true,
621  /// default] or in equal increments in zeta.
622  /// This is the curvlinear equivalent of PolyLine.
624  const double& zeta_start,
625  const double& zeta_end,
626  const unsigned& nsegment,
627  const unsigned& boundary_id,
628  const bool&
629  space_vertices_evenly_in_arclength=true,
630  const unsigned &boundary_chunk = 0)
632  Geom_object_pt(geom_object_pt),
633  Zeta_start(zeta_start),
634  Zeta_end(zeta_end),
635  Nsegment(nsegment),
636  Boundary_id(boundary_id),
637  Space_vertices_evenly_in_arclength(
638  space_vertices_evenly_in_arclength),
639  Boundary_chunk(boundary_chunk)
640  { }
641 
642 
643  /// \short Empty Destuctor
645 
646  /// Pointer to GeomObject that represents this part of the boundary
647  GeomObject* geom_object_pt(){return Geom_object_pt;}
648 
649  /// Start coordinate in terms of the GeomObject's intrinsic coordinate
650  double zeta_start(){return Zeta_start;}
651 
652  /// End coordinate in terms of the GeomObject's intrinsic coordinate
653  double zeta_end(){return Zeta_end;}
654 
655  /// \short Number of (initially straight-line) segments that this part of the
656  /// boundary is to be represented by
657  unsigned nsegment() const {return Nsegment;}
658 
659  /// \short Number of (initially straight-line) segments that this part of the
660  /// boundary is to be represented by. This version allows the change of the
661  /// number of segments
662  unsigned &nsegment() {return Nsegment;}
663 
664  /// Boundary ID
665  unsigned boundary_id() const {return Boundary_id;}
666 
667  /// Boundary chunk (Used when a boundary is represented by more
668  /// than one polyline
669  unsigned boundary_chunk() const {return Boundary_chunk;}
670 
671  /// Output curvilinear boundary at n_sample (default: 50) points
672  void output(std::ostream &outfile, const unsigned& n_sample=50)
673  {
674  outfile << "ZONE T=\"Boundary " << Boundary_id << "\"\n";
675  Vector<double> zeta(1);
676  Vector<double> r(2);
677  for (unsigned i=0;i<n_sample;i++)
678  {
679  zeta[0]=Zeta_start+(Zeta_end-Zeta_start)*double(i)/double(n_sample-1);
680  Geom_object_pt->position(zeta,r);
681  outfile << r[0] << " " << r[1] << std::endl;
682  }
683  }
684 
685  /// \short Boolean to indicate if vertices in polygonal representation
686  /// of the Curvline are spaced (approximately) evenly in arclength
687  /// along the GeomObject [true] or in equal increments in zeta [false]
689  {
690  return Space_vertices_evenly_in_arclength;
691  }
692 
693  /// Number of vertices
694  unsigned nvertex() const {return 2;}
695 
696  /// Get first vertex coordinates
698  {
699  Vector<double> z(1);
700  z[0] = Zeta_start;
701  Geom_object_pt->position(z, vertex);
702  }
703 
704  /// Get last vertex coordinates
706  {
707  Vector<double> z(1);
708  z[0] = Zeta_end;
709  Geom_object_pt->position(z, vertex);
710  }
711 
712  /// \short Does the vector for storing connections has elements?
714  {return !Connection_points_pt.empty();}
715 
716  /// \short Returns the connection points vector
718  {return &Connection_points_pt;}
719 
720  /// Add the connection point (z_value) to the connection
721  /// points that receive the curviline
723  const double &z_value,
724  const double &tol = 1.0e-12)
725  {
726 
727  // If we are trying to connect to the initial or final
728  // point then it is not necessary to add this point
729  // to the list since it will explicitly be created when
730  // transforming the curviline to polyline
731  if (std::fabs(z_value - Zeta_start) < tol ||
732  std::fabs(z_value - Zeta_end) < tol)
733  {return;}
734 
735  // We need to deal with repeated connection points,
736  // if the connection point is already in the list then
737  // we will not add it!!!
738  // Search for repeated elements
739  unsigned n_size = Connection_points_pt.size();
740  for (unsigned i = 0; i < n_size; i++)
741  {
742  if (fabs(z_value - Connection_points_pt[i]) < tol)
743  {return;}
744  }
745 
746  // Only add the connection point if it is not the
747  // initial or final zeta value and it is not already on the
748  // list
749  Connection_points_pt.push_back(z_value);
750 
751  }
752 
753  private:
754 
755  /// Pointer to GeomObject that represents this part of the boundary
757 
758  /// Start coordinate in terms of the GeomObject's intrinsic coordinate
759  double Zeta_start;
760 
761  /// End coordinate in terms of the GeomObject's intrinsic coordinate
762  double Zeta_end;
763 
764  /// Number of (initially straight-line) segments that this part of the
765  /// boundary is to be represented by
766  unsigned Nsegment;
767 
768  /// Boundary ID
769  unsigned Boundary_id;
770 
771  /// \short Boolean to indicate if vertices in polygonal representation
772  /// of the Curviline are spaced (approximately) evenly in arclength
773  /// along the GeomObject [true] or in equal increments in zeta [false]
775 
776  /// Boundary chunk (Used when a boundary is represented by more
777  /// than one polyline
778  unsigned Boundary_chunk;
779 
780  /// \short Stores the information for connections received on the
781  /// curviline. Used when converting to polyline
783 
784  };
785 
786 
787 
788 //=====================================================================
789 /// Class defining a polyline for use in Triangle Mesh generation
790 //=====================================================================
792  {
793 
794  public:
795 
796  /// \short Constructor: Takes vectors of vertex coordinates in order
797  /// Also allows the optional specification of a boundary ID -- useful
798  /// in a mesh generation context. If not specified it defaults to zero.
799  TriangleMeshPolyLine(const Vector<Vector<double> >& vertex_coordinate,
800  const unsigned &boundary_id,
801  const unsigned &boundary_chunk = 0) :
803  Vertex_coordinate(vertex_coordinate),
804  Boundary_id(boundary_id),
805  Boundary_chunk(boundary_chunk)
806  {
807 #ifdef PARANOID
808  unsigned nvert=Vertex_coordinate.size();
809  for (unsigned i=0;i<nvert;i++)
810  {
811  if (Vertex_coordinate[i].size()!=2)
812  {
813  std::ostringstream error_stream;
814  error_stream
815  << "TriangleMeshPolyLine should only be used in 2D!\n"
816  << "Your Vector of coordinates, contains data for "
817  << Vertex_coordinate[i].size()
818  << "-dimensional coordinates." << std::endl;
819  throw OomphLibError(error_stream.str(),
820  OOMPH_CURRENT_FUNCTION,
821  OOMPH_EXCEPTION_LOCATION);
822  }
823  }
824 #endif
825  }
826 
827  /// Empty destructor
828  virtual ~TriangleMeshPolyLine() { }
829 
830  /// Number of vertices
831  unsigned nvertex() const {return Vertex_coordinate.size();}
832 
833  /// Number of segments
834  unsigned nsegment() const {return Vertex_coordinate.size()-1;}
835 
836  /// Boundary id
837  unsigned boundary_id() const {return Boundary_id;}
838 
839  /// Boundary chunk (Used when a boundary is represented by more
840  /// than one polyline
841  unsigned boundary_chunk() const{return Boundary_chunk;}
842 
843  /// Coordinate vector of i-th vertex (const version)
844  Vector<double> vertex_coordinate(const unsigned& i) const
845  {return Vertex_coordinate[i];}
846 
847  /// Coordinate vector of i-th vertex
849  {return Vertex_coordinate[i];}
850 
851  /// Get first vertex coordinates
853  {vertex = Vertex_coordinate[0];}
854 
855  /// Get last vertex coordinates
857  {vertex = Vertex_coordinate[nvertex()-1];}
858 
859  /// Output the polyline -- n_sample is ignored
860  void output(std::ostream &outfile, const unsigned& n_sample=50)
861  {
862  outfile <<"ZONE T=\"TriangleMeshPolyLine with boundary ID"
863  << Boundary_id<<"\""<<std::endl;
864  unsigned nvert=Vertex_coordinate.size();
865  for(unsigned i=0;i<nvert;i++)
866  {
867  outfile << Vertex_coordinate[i][0] << " "
868  << Vertex_coordinate[i][1] << std::endl;
869  }
870  }
871 
872  /// Reverse the polyline, this includes the connection information
873  /// and the vertices order
874  void reverse()
875  {
876  // Do the reversing of the connection information
877 
878  // Is there a connection to the initial vertex
879  const bool initial_connection = is_initial_vertex_connected();
880 
881  // Is there a connection to the initial vertex
882  const bool final_connection = is_final_vertex_connected();
883 
884  // If there are any connection at the ends that info. needs to be
885  // reversed
886  if (initial_connection || final_connection)
887  {
888  // Backup the connection information
889 
890  // -------------------------------------------------------------------
891  // Backup the initial vertex connection information
892  // The boundary id
893  const unsigned backup_initial_vertex_connected_bnd_id =
894  initial_vertex_connected_bnd_id();
895  // The chunk number
896  const unsigned backup_initial_vertex_connected_chunk =
897  initial_vertex_connected_n_chunk();
898  // The vertex number
899  const unsigned backup_initial_vertex_connected_n_vertex =
900  initial_vertex_connected_n_vertex();
901  // Is it connected to a curviline
902  const bool backup_initial_vertex_connected_to_curviline =
903  is_initial_vertex_connected_to_curviline();
904  // The s value for the curviline connection
905  const double backup_initial_s_connection = initial_s_connection_value();
906  // The tolerance
907  const double backup_initial_s_tolerance = tolerance_for_s_connection();
908 
909  // -------------------------------------------------------------------
910  // Backup the final vertex connection information
911  // The boundary id
912  const unsigned backup_final_vertex_connected_bnd_id =
913  final_vertex_connected_bnd_id();
914  // The chunk number
915  const unsigned backup_final_vertex_connected_chunk =
916  final_vertex_connected_n_chunk();
917  // The vertex number
918  const unsigned backup_final_vertex_connected_n_vertex =
919  final_vertex_connected_n_vertex();
920  // Is it connected to a curviline
921  const bool backup_final_vertex_connected_to_curviline =
922  is_final_vertex_connected_to_curviline();
923  // The s value for the curviline connection
924  const double backup_final_s_connection = final_s_connection_value();
925  // The tolerance
926  const double backup_final_s_tolerance = tolerance_for_s_connection();
927  // -------------------------------------------------------------------
928 
929  // Disconnect the polyline
930 
931  // Disconnect the initial vertex
932  unset_initial_vertex_connected();
933  unset_initial_vertex_connected_to_curviline();
934 
935  // Disconnect the final vertex
936  unset_final_vertex_connected();
937  unset_final_vertex_connected_to_curviline();
938 
939  // Now reconnected but in inverted order
940  if (initial_connection)
941  {
942  // Set the final vertex as connected
943  set_final_vertex_connected();
944  // Copy the boundary id
945  final_vertex_connected_bnd_id() =
946  backup_initial_vertex_connected_bnd_id;
947  // Copy the chunk number
948  final_vertex_connected_n_chunk() =
949  backup_initial_vertex_connected_chunk;
950  // Copy the vertex number
951  final_vertex_connected_n_vertex() =
952  backup_initial_vertex_connected_n_vertex;
953  // Is it connected to a curviline
954  if (backup_initial_vertex_connected_to_curviline)
955  {
956  // Set the final vertex as connected to curviline
957  set_final_vertex_connected_to_curviline();
958  // Copy the s value to connected
959  final_s_connection_value() = backup_initial_s_connection;
960  // Copy the tolerance
961  tolerance_for_s_connection() = backup_initial_s_tolerance;
962  } // if (backup_initial_vertex_connected_to_curviline)
963 
964  } // if (initial_connection)
965 
966  if (final_connection)
967  {
968  // Set the initial vertex as connected
969  set_initial_vertex_connected();
970  // Copy the boundary id
971  initial_vertex_connected_bnd_id() =
972  backup_final_vertex_connected_bnd_id;
973  // Copy the chunk number
974  initial_vertex_connected_n_chunk() =
975  backup_final_vertex_connected_chunk;
976  // Copy the vertex number
977  initial_vertex_connected_n_vertex() =
978  backup_final_vertex_connected_n_vertex;
979  // Is it connected to a curviline
980  if (backup_final_vertex_connected_to_curviline)
981  {
982  // Set the initial vertex as connected to curviline
983  set_initial_vertex_connected_to_curviline();
984  // Copy the s value to connected
985  initial_s_connection_value() = backup_final_s_connection;
986  // Copy the tolerance
987  tolerance_for_s_connection() = backup_final_s_tolerance;
988  } // if (backup_final_vertex_connected_to_curviline)
989 
990  } // if (final_connection)
991 
992  } // if (initial_connection || final_connection)
993 
994  // Do the reversing of the vertices
995  std::reverse(Vertex_coordinate.begin(), Vertex_coordinate.end());
996 
997  }
998 
999  private:
1000 
1001  /// Vector of Vector of vertex coordinates
1003 
1004  /// Boundary ID
1005  unsigned Boundary_id;
1006 
1007  /// Boundary chunk (Used when a boundary is represented by more
1008  /// than one polyline
1009  unsigned Boundary_chunk;
1010 
1011  };
1012 
1013 
1014 
1015 ///////////////////////////////////////////////////////////////////////
1016 ///////////////////////////////////////////////////////////////////////
1017 ///////////////////////////////////////////////////////////////////////
1018 
1019 
1020 //===================================================================
1021 /// \short Namespace that allows the specification of a tolerance
1022 /// between vertices at the ends of polylines that are supposed
1023 /// to be at the same position.
1024 //===================================================================
1025  namespace ToleranceForVertexMismatchInPolygons
1026  {
1027 
1028  /// \short Acceptable discrepancy for mismatch in vertex coordinates.
1029  /// In paranoid mode, the code will die if the beginning/end of
1030  /// two adjacent polylines differ by more than that. If the
1031  /// discrepancy is smaller (but nonzero) one of the vertex coordinates
1032  /// get adjusted to match perfectly; without paranoia the vertex
1033  /// coordinates are taken as they come...
1034  extern double Tolerable_error;
1035  }
1036 
1037 ///////////////////////////////////////////////////////////////////////
1038 ///////////////////////////////////////////////////////////////////////
1039 ///////////////////////////////////////////////////////////////////////
1040 
1041 
1042 //=====================================================================
1043 // \short Class defining triangle mesh curves. Abstract class for
1044 /// closed curves and open curves. All TriangleMeshCurves are composed
1045 /// of a Vector of TriangleMeshCurveSections
1046 //=====================================================================
1048  {
1049 
1050  public:
1051 
1052  /// Empty constructor
1054  : Curve_section_pt(curve_section_pt),
1055  Polyline_refinement_tolerance(0.08),
1056  Polyline_unrefinement_tolerance(0.04)
1057  {
1058 
1059  }
1060 
1061  /// Empty destructor
1062  virtual ~TriangleMeshCurve() { }
1063 
1064  /// Number of vertices
1065  virtual unsigned nvertices() const = 0;
1066 
1067  /// Total number of segments
1068  virtual unsigned nsegments() const = 0;
1069 
1070  /// Return max boundary id of associated curves
1071  unsigned max_boundary_id()
1072  {
1073  unsigned max=0;
1074  unsigned n_curve_section = ncurve_section();
1075  for(unsigned i=0; i<n_curve_section; i++)
1076  {
1077  unsigned boundary_id=Curve_section_pt[i]->boundary_id();
1078  if (boundary_id>max) {max=boundary_id;}
1079  }
1080  return max;
1081  }
1082 
1083  /// Number of constituent curves
1084  virtual unsigned ncurve_section() const
1085  {return Curve_section_pt.size();}
1086 
1087  /// \short Enable refinement of polylines to create a better
1088  /// representation of curvilinear boundaries (e.g. in free-surface
1089  /// problems). See tutorial for
1090  /// interpretation of the optional argument which specifies the
1091  /// refinement tolerance. It defaults to 0.08 and the smaller the
1092  /// number the finer the surface representation.
1093  void enable_polyline_refinement(const double& tolerance=0.08)
1094  {
1095  Polyline_refinement_tolerance=tolerance;
1096  // Establish the refinement tolerance for all the
1097  // curve sections on the TriangleMeshCurve
1098  unsigned n_curve_sections = Curve_section_pt.size();
1099  for (unsigned i = 0; i < n_curve_sections; i++)
1100  {
1101  Curve_section_pt[i]->set_refinement_tolerance(
1102  Polyline_refinement_tolerance);
1103  }
1104  }
1105 
1106  /// \short Set tolerance for refinement of polylines to create a better
1107  /// representation of curvilinear boundaries (e.g. in free-surface
1108  /// problems). See tutorial for
1109  /// interpretation of the refinement tolerance. (The smaller the
1110  /// number the finer the surface representation). If set to
1111  /// a negative value, we're switching off refinement --
1112  /// equivalent to calling disable_polyline_refinement()
1113  void set_polyline_refinement_tolerance(const double& tolerance)
1114  {
1115  Polyline_refinement_tolerance=tolerance;
1116  // Establish the refinement tolerance for all the
1117  // curve sections on the TriangleMeshCurve
1118  unsigned n_curve_sections = Curve_section_pt.size();
1119  for (unsigned i = 0; i < n_curve_sections; i++)
1120  {
1121  Curve_section_pt[i]->set_refinement_tolerance(
1122  Polyline_refinement_tolerance);
1123  }
1124  }
1125 
1126  /// \short Get tolerance for refinement of polylines to create a better
1127  /// representation of curvilinear boundaries (e.g. in free-surface
1128  /// problems). See tutorial for
1129  /// interpretation. If it's negative refinement is disabled.
1131  {
1132  return Polyline_refinement_tolerance;
1133  }
1134 
1135  /// \short Disable refinement of polylines
1137  {
1138  Polyline_refinement_tolerance=-1.0;
1139  // Disable the refinement tolerance for all the
1140  // curve sections on the TriangleMeshCurve
1141  unsigned n_curve_sections = Curve_section_pt.size();
1142  for (unsigned i = 0; i < n_curve_sections; i++)
1143  {Curve_section_pt[i]->disable_refinement_tolerance();}
1144  }
1145 
1146  /// \short Enable unrefinement of polylines to avoid unnecessarily large
1147  /// numbers of elements on of curvilinear boundaries (e.g. in free-surface
1148  /// problems). See tutorial for
1149  /// interpretation of the optional argument which specifies the
1150  /// unrefinement tolerance. It defaults to 0.04 and the larger the number
1151  /// the more agressive we are when removing unnecessary vertices on
1152  /// gently curved polylines.
1153  void enable_polyline_unrefinement(const double& tolerance=0.04)
1154  {
1155  Polyline_unrefinement_tolerance=tolerance;
1156  // Establish the unrefinement tolerance for all the
1157  // curve sections on the TriangleMeshCurve
1158  unsigned n_curve_sections = Curve_section_pt.size();
1159  for (unsigned i = 0; i < n_curve_sections; i++)
1160  {
1161  Curve_section_pt[i]->set_unrefinement_tolerance(
1162  Polyline_unrefinement_tolerance);
1163  }
1164  }
1165 
1166  /// \short Set tolerance for unrefinement of polylines
1167  /// to avoid unnecessarily large
1168  /// numbers of elements on of curvilinear boundaries (e.g. in free-surface
1169  /// problems). See tutorial for
1170  /// interpretation of the optional argument which specifies the
1171  /// unrefinement tolerance. It defaults to 0.04 and the larger the number
1172  /// the more agressive we are when removing unnecessary vertices on
1173  /// gently curved polylines. If set to
1174  /// a negative value, we're switching off unrefinement --
1175  /// equivalent to calling disable_polyline_unrefinement()
1176  void set_polyline_unrefinement_tolerance(const double& tolerance)
1177  {
1178  Polyline_unrefinement_tolerance=tolerance;
1179  // Establish the unrefinement tolerance for all the
1180  // curve sections on the TriangleMeshCurve
1181  unsigned n_curve_sections = Curve_section_pt.size();
1182  for (unsigned i = 0; i < n_curve_sections; i++)
1183  {
1184  Curve_section_pt[i]->set_unrefinement_tolerance(
1185  Polyline_unrefinement_tolerance);
1186  }
1187  }
1188 
1189  /// \short Get tolerance for unrefinement of polylines to create a better
1190  /// representation of curvilinear boundaries (e.g. in free-surface
1191  /// problems). See tutorial for
1192  /// interpretation. If it's negative unrefinement is disabled.
1194  {
1195  return Polyline_unrefinement_tolerance;
1196  }
1197 
1198  /// \short Disable unrefinement of polylines
1200  {
1201  Polyline_unrefinement_tolerance=-1.0;
1202  // Disable the unrefinement tolerance for all the
1203  // curve sections on the TriangleMeshCurve
1204  unsigned n_curve_sections = Curve_section_pt.size();
1205  for (unsigned i = 0; i < n_curve_sections; i++)
1206  {Curve_section_pt[i]->disable_unrefinement_tolerance();}
1207  }
1208 
1209  /// Output each sub-boundary at n_sample (default: 50) points
1210  virtual void output(std::ostream &outfile, const unsigned& n_sample=50) = 0;
1211 
1212  /// Pointer to i-th constituent curve section
1213  virtual TriangleMeshCurveSection* curve_section_pt(const unsigned& i) const
1214  {return Curve_section_pt[i];}
1215 
1216  /// Pointer to i-th constituent curve section
1217  virtual TriangleMeshCurveSection* &curve_section_pt(const unsigned& i)
1218  {return Curve_section_pt[i];}
1219 
1220  protected:
1221 
1222  /// Vector of curve sections
1224 
1225  private:
1226 
1227  /// Tolerance for refinement of polylines (neg if refinement is disabled)
1229 
1230  /// Tolerance for unrefinement of polylines (neg if refinement is disabled)
1232 
1233  };
1234 
1235 ///////////////////////////////////////////////////////////////////////
1236 ///////////////////////////////////////////////////////////////////////
1237 ///////////////////////////////////////////////////////////////////////
1238 
1239 //=====================================================================
1240 /// Base class defining a closed curve for the Triangle mesh generation
1241 //=====================================================================
1243  {
1244 
1245  public:
1246 
1247  /// Constructor prototype
1249  const Vector<TriangleMeshCurveSection*> &curve_section_pt,
1250  const Vector<double>& internal_point_pt = Vector<double>(0),
1251  const bool &is_internal_point_fixed=false);
1252 
1253  /// Empty destructor
1255 
1256  /// Number of vertices
1257  unsigned nvertices() const
1258  {
1259  unsigned n_curve_section=ncurve_section();
1260  unsigned n_vertices=0;
1261  for(unsigned j=0;j<n_curve_section;j++)
1262  {
1263  // Storing the number of the vertices
1264  n_vertices+=Curve_section_pt[j]->nvertex()-1;
1265  }
1266  // If there's just one boundary. All the vertices should be counted
1267  if(n_curve_section==1) {n_vertices+=1;}
1268  return n_vertices;
1269  }
1270 
1271  /// Total number of segments
1272  unsigned nsegments() const
1273  {
1274  unsigned n_curve_section=ncurve_section();
1275  unsigned nseg=0;
1276  for(unsigned j=0;j<n_curve_section;j++)
1277  {nseg+=Curve_section_pt[j]->nsegment();}
1278  // If there's just one boundary poly line we have another segment
1279  // connecting the last vertex to the first one
1280  if(n_curve_section==1) {nseg+=1;}
1281  return nseg;
1282  }
1283 
1284  /// Output each sub-boundary at n_sample (default: 50) points
1285  void output(std::ostream &outfile, const unsigned& n_sample=50)
1286  {
1287  unsigned nb=Curve_section_pt.size();
1288  for (unsigned i=0;i<nb;i++)
1289  {
1290  Curve_section_pt[i]->output(outfile,n_sample);
1291  }
1292 
1293  if (!Internal_point_pt.empty())
1294  {
1295  outfile << "ZONE T=\"Internal point\"\n";
1296  outfile << Internal_point_pt[0] << " "
1297  << Internal_point_pt[1] << "\n";
1298  }
1299 
1300  }
1301 
1302  /// Coordinates of the internal point
1303  Vector<double> internal_point() const {return Internal_point_pt;}
1304 
1305  /// Coordinates of the internal point
1306  Vector<double> &internal_point() {return Internal_point_pt;}
1307 
1308  /// Fix the internal point (i.e. do not allow our automatic machinery
1309  /// to update it)
1310  void fix_internal_point() {Is_internal_point_fixed = true;}
1311 
1312  /// Unfix the internal point (i.e. allow our automatic machinery
1313  /// to update it)
1314  void unfix_internal_point() {Is_internal_point_fixed = false;}
1315 
1316  /// Test whether the internal point is fixed
1317  bool is_internal_point_fixed() const {return Is_internal_point_fixed;}
1318 
1319  protected:
1320 
1321  /// Vector of vertex coordinates
1323 
1324  /// Indicate whether the internal point should be updated automatically
1326 
1327  };
1328 
1329 ///////////////////////////////////////////////////////////////////////
1330 ///////////////////////////////////////////////////////////////////////
1331 ///////////////////////////////////////////////////////////////////////
1332 
1333 
1334 //=====================================================================
1335 /// Class defining a closed polygon for the Triangle mesh generation
1336 //=====================================================================
1338  {
1339 
1340  public:
1341 
1342  /// \short Constructor: Specify vector of pointers to TriangleMeshCurveSection
1343  /// that define the boundary of the segments of the polygon.
1344  /// Each TriangleMeshCurveSection has its own boundary ID and can contain
1345  /// multiple (straight-line) segments. For consistency across the
1346  /// various uses of this class, we insist that the closed boundary
1347  /// is represented by at least two separate TriangleMeshCurveSection
1348  /// whose joint vertices must be specified in both.
1349  /// (This is to allow the setup of unique boundary coordinate(s)
1350  /// around the polygon.) This may seem slightly annoying
1351  /// in cases where a polygon really only represents a single
1352  /// boundary, but...
1353  /// Note: The specified vector of pointers must consist of only
1354  /// TriangleMeshPolyLine elements. There is a checking on the PARANOID
1355  /// mode for this constraint
1357  boundary_polyline_pt,
1358  const Vector<double>& internal_point_pt=Vector<double>(0),
1359  const bool &is_internal_point_fixed=false);
1360 
1361  /// Empty virtual destructor
1362  virtual ~TriangleMeshPolygon() { }
1363 
1364  /// Number of constituent curves
1365  unsigned ncurve_section() const
1366  {return npolyline();}
1367 
1368  /// Number of constituent polylines
1369  unsigned npolyline() const {return Curve_section_pt.size();}
1370 
1371  /// Pointer to i-th constituent polyline
1372  TriangleMeshPolyLine* polyline_pt(const unsigned& i) const
1373  {
1374  TriangleMeshPolyLine *tmp_polyline =
1375  dynamic_cast<TriangleMeshPolyLine*>(Curve_section_pt[i]);
1376 #ifdef PARANOID
1377  if (tmp_polyline == NULL)
1378  {
1379  std::ostringstream error_stream;
1380  error_stream
1381  << "The (" << i << ") TriangleMeshCurveSection is not a "
1382  << "TriangleMeshPolyLine\nThe TriangleMeshPolygon object"
1383  << "is constituent of only TriangleMeshPolyLine objects.\n"
1384  << "The problem could be generated when changing the constituent "
1385  << "objects of the TriangleMeshPolygon.\nCheck where you got "
1386  << "access to this objects and review that you are not introducing "
1387  << "any other objects than TriangleMeshPolyLines" << std::endl;
1388  throw OomphLibError(error_stream.str(),
1389  OOMPH_CURRENT_FUNCTION,
1390  OOMPH_EXCEPTION_LOCATION);
1391  }
1392 #endif
1393  return tmp_polyline;
1394  }
1395 
1396  /// Pointer to i-th constituent polyline
1398  {
1399  TriangleMeshPolyLine *tmp_polyline =
1400  dynamic_cast<TriangleMeshPolyLine*>(Curve_section_pt[i]);
1401 #ifdef PARANOID
1402  if (tmp_polyline == NULL)
1403  {
1404  std::ostringstream error_stream;
1405  error_stream
1406  << "The (" << i << ") TriangleMeshCurveSection is not a "
1407  << "TriangleMeshPolyLine\nThe TriangleMeshPolygon object"
1408  << "is constituent of only TriangleMeshPolyLine objects.\n"
1409  << "The problem could be generated when changing the constituent "
1410  << "objects of the TriangleMeshPolygon.\nCheck where you got "
1411  << "access to this objects and review that you are not introducing "
1412  << "any other objects than TriangleMeshPolyLines" << std::endl;
1413  throw OomphLibError(error_stream.str(),
1414  OOMPH_CURRENT_FUNCTION,
1415  OOMPH_EXCEPTION_LOCATION);
1416  }
1417 #endif
1418  return tmp_polyline;
1419  }
1420 
1421  /// Return vector of boundary ids of associated polylines
1423  {
1424  // Get the number of polylines
1425  unsigned nline=npolyline();
1426  Vector<unsigned> boundary_id(nline);
1427 
1428  // Loop over the polyline to get the id
1429  for(unsigned iline=0;iline<nline;iline++)
1430  {
1431  boundary_id[iline]=Curve_section_pt[iline]->boundary_id();
1432  }
1433  return boundary_id;
1434  }
1435 
1436  /// \short Is re-distribution of polyline segments in the curve
1437  /// between different boundaries during adaptation enabled?
1439  {return Enable_redistribution_of_segments_between_polylines;}
1440 
1441  /// \short Enable re-distribution of polyline segments in the curve
1442  /// between different boundaries during adaptation
1444  {Enable_redistribution_of_segments_between_polylines=true;}
1445 
1446  /// \short Disable re-distribution of polyline segments in the curve
1447  /// between different boundaries during adaptation
1449  {Enable_redistribution_of_segments_between_polylines=false;}
1450 
1451  /// \short Test whether curve can update reference
1453  {return Can_update_configuration;}
1454 
1455  /// \short Virtual function that should be overloaded to update the polygons
1456  /// reference configuration
1458  {
1459  std::ostringstream error_stream;
1460  error_stream
1461  << "Broken Default Called\n"
1462  << "This function should be overloaded if Can_update_configuration = true,"
1463  << "\nwhich indicates that the curve in it polylines parts can update its "
1464  << "own position (i.e. it\n"
1465  << "may be a rigid body. Otherwise the update will be via a FaceMesh \n"
1466  << "representation of the boundary which is appropriate for \n"
1467  << "general deforming surfaces\n";
1468 
1469  throw OomphLibError(
1470  error_stream.str(),
1471  OOMPH_CURRENT_FUNCTION,
1472  OOMPH_EXCEPTION_LOCATION);
1473  }
1474 
1475  /// \short Test whether the polygon is fixed or not
1476  bool is_fixed() const {return Polygon_fixed;}
1477 
1478  /// \short Set the polygon to be fixed
1479  void set_fixed() {Polygon_fixed = true;}
1480 
1481  /// \short Set the polygon to be allowed to move (default)
1482  void set_unfixed() {Polygon_fixed = false;}
1483 
1484  protected:
1485 
1486  /// \short Is re-distribution of polyline segments between different
1487  /// boundaries during adaptation enabled? (Default: false)
1489 
1490  ///\short Boolean flag to indicate whether the polygon can update its
1491  ///own reference configuration after it has moved i.e. if it is
1492  ///upgraded to a rigid body rather than being a free surface (default false)
1494 
1495  private:
1496 
1497  ///\short Boolean flag to indicate whether the polygon can move
1498  /// (default false because if it doesn't move this will just lead to
1499  /// wasted work)
1501 
1502  };
1503 
1504 /////////////////////////////////////////////////////////////////////////
1505 /////////////////////////////////////////////////////////////////////////
1506 /////////////////////////////////////////////////////////////////////////
1507 
1508 //=====================================================================
1509 /// Base class defining an open curve for the Triangle mesh generation
1510 /// Basically used to define internal boundaries on the mesh
1511 //=====================================================================
1513  {
1514 
1515  public:
1516 
1517  /// Constructor
1519  curve_section_pt);
1520 
1521  /// Empty destructor
1523 
1524  /// Number of vertices
1525  unsigned nvertices() const
1526  {
1527  unsigned n_vertices = 0;
1528  unsigned n_curve_section=ncurve_section();
1529  for (unsigned i = 0; i < n_curve_section; i++)
1530  n_vertices+=Curve_section_pt[i]->nvertex();
1531  // If there's just one boundary. All the vertices should be counted
1532  if (n_curve_section==1) {n_vertices+=1;}
1533  return n_vertices;
1534  }
1535 
1536  /// Total number of segments
1537  unsigned nsegments() const
1538  {
1539  unsigned n_curve_section=ncurve_section();
1540  unsigned nseg=0;
1541  for(unsigned j=0;j<n_curve_section;j++)
1542  {nseg+=Curve_section_pt[j]->nsegment();}
1543  return nseg;
1544  }
1545 
1546  /// Output each sub-boundary at n_sample (default: 50) points
1547  void output(std::ostream &outfile, const unsigned& n_sample=50)
1548  {
1549  unsigned nb=Curve_section_pt.size();
1550  for (unsigned i=0;i<nb;i++)
1551  {
1552  Curve_section_pt[i]->output(outfile,n_sample);
1553  }
1554 
1555  }
1556 
1557  /// Pointer to i-th constituent polyline
1558  TriangleMeshPolyLine* polyline_pt(const unsigned& i) const
1559  {
1560  TriangleMeshPolyLine *tmp_polyline =
1561  dynamic_cast<TriangleMeshPolyLine*>(Curve_section_pt[i]);
1562 #ifdef PARANOID
1563  if (tmp_polyline == NULL)
1564  {
1565  std::ostringstream error_stream;
1566  error_stream
1567  << "The (" << i << ")-th TriangleMeshCurveSection is not a "
1568  << "TriangleMeshPolyLine.\nPlease make sure that when you"
1569  << "first created this object the (" << i << ")-th\n"
1570  << "TriangleCurveSection is a TriangleMeshPolyLine."
1571  << std::endl;
1572  throw OomphLibError(error_stream.str(),
1573  OOMPH_CURRENT_FUNCTION,
1574  OOMPH_EXCEPTION_LOCATION);
1575  }
1576 #endif
1577  return tmp_polyline;
1578  }
1579 
1580  /// Pointer to i-th constituent polyline
1582  {
1583  TriangleMeshPolyLine *tmp_polyline =
1584  dynamic_cast<TriangleMeshPolyLine*>(Curve_section_pt[i]);
1585 #ifdef PARANOID
1586  if (tmp_polyline == NULL)
1587  {
1588  std::ostringstream error_stream;
1589  error_stream
1590  << "The (" << i << ")-th TriangleMeshCurveSection is not a "
1591  << "TriangleMeshPolyLine.\nPlease make sure that when you"
1592  << "first created this object the (" << i << ")-th\n"
1593  << "TriangleCurveSection is a TriangleMeshPolyLine."
1594  << std::endl;
1595  throw OomphLibError(error_stream.str(),
1596  OOMPH_CURRENT_FUNCTION,
1597  OOMPH_EXCEPTION_LOCATION);
1598  }
1599 #endif
1600  return tmp_polyline;
1601  }
1602 
1603  };
1604 
1605 //==============start_of_geometry_helper_functions_class================
1606 /// Contains functions which define the geometry of the mesh, i.e.
1607 /// regions, boundaries, etc.
1608 //======================================================================
1609  class UnstructuredTwoDMeshGeometryBase : public virtual Mesh
1610  {
1611  public:
1612 
1613  /// Public static flag to suppress warning; defaults to false
1615 
1616  /// \short Empty constructor
1618 
1619  /// Broken copy constructor
1621  {
1622  BrokenCopy::broken_copy("UnstructuredTwoDMeshGeometryBase");
1623  }
1624 
1625  /// Broken assignment operator
1627  {
1628  BrokenCopy::broken_assign("UnstructuredTwoDMeshGeometryBase");
1629  }
1630 
1631 
1632  /// Empty destructor
1634 
1635  /// Return the number of regions specified by attributes
1636  unsigned nregion()
1637  {
1638  return Region_element_pt.size();
1639  }
1640 
1641  /// Return the number of elements in the i-th region
1642  unsigned nregion_element(const unsigned &i)
1643  {
1644  // Create an iterator to iterate over Region_element_pt
1645  std::map<unsigned,Vector<FiniteElement* > >::iterator it;
1646 
1647  // Find the entry of Region_element_pt associated with the i-th region
1648  it=Region_element_pt.find(i);
1649 
1650  // If there is an entry associated with the i-th region
1651  if (it!=Region_element_pt.end())
1652  {
1653  return (it->second).size();
1654  }
1655  else
1656  {
1657  return 0;
1658  }
1659  } // End of nregion_element
1660 
1661  /// Return the e-th element in the i-th region
1663  const unsigned &e)
1664  {
1665  // Create an iterator to iterate over Region_element_pt
1666  std::map<unsigned,Vector<FiniteElement* > >::iterator it;
1667 
1668  // Find the entry of Region_element_pt associated with the i-th region
1669  it=Region_element_pt.find(i);
1670 
1671  // If there is an entry associated with the i-th region
1672  if (it!=Region_element_pt.end())
1673  {
1674  // Return a pointer to the e-th element in the i-th region
1675  return (it->second)[e];
1676  }
1677  else
1678  {
1679  // Create a stringstream object
1680  std::stringstream error_message;
1681 
1682  // Create the error message
1683  error_message << "There are no regions associated with "
1684  << "region ID " << i << ".";
1685 
1686  // Throw an error
1687  throw OomphLibError(error_message.str(),
1688  OOMPH_CURRENT_FUNCTION,
1689  OOMPH_EXCEPTION_LOCATION);
1690  }
1691  } // End of region_element_pt
1692 
1693  /// Return the number of attributes used in the mesh
1695  {
1696  return Region_attribute.size();
1697  }
1698 
1699  /// Return the attribute associated with region i
1700  double region_attribute(const unsigned &i)
1701  {
1702  return Region_attribute[i];
1703  }
1704 
1705  /// \short Return the geometric object associated with the b-th boundary or
1706  /// null if the boundary has associated geometric object.
1708  {
1709  std::map<unsigned,GeomObject*>::iterator it;
1710  it=Boundary_geom_object_pt.find(b);
1711  if (it==Boundary_geom_object_pt.end())
1712  {
1713  return 0;
1714  }
1715  else
1716  {
1717  return (*it).second;
1718  }
1719  }
1720 
1721  /// \short Return direct access to the geometric object storage
1722  std::map<unsigned,GeomObject*>& boundary_geom_object_pt()
1723  {
1724  return Boundary_geom_object_pt;
1725  }
1726 
1727  /// \short Return access to the vector of boundary coordinates associated
1728  /// with each geometric object
1729  std::map<unsigned,Vector<double> > &boundary_coordinate_limits()
1730  {
1731  return Boundary_coordinate_limits;
1732  }
1733 
1734  /// \short Return access to the coordinate limits associated with
1735  /// the geometric object associated with boundary b
1737  {
1738  std::map<unsigned,Vector<double> >::iterator it;
1739  it=Boundary_coordinate_limits.find(b);
1740  if (it==Boundary_coordinate_limits.end())
1741  {
1742  throw OomphLibError("No coordinate limits associated with this boundary\n",
1743  OOMPH_CURRENT_FUNCTION,
1744  OOMPH_EXCEPTION_LOCATION);
1745  }
1746  else
1747  {
1748  return (*it).second;
1749  }
1750  }
1751 
1752  /// Return the number of elements adjacent to boundary b in region r
1753  inline unsigned nboundary_element_in_region(const unsigned &b,
1754  const unsigned &r) const
1755  {
1756  // Need to use a constant iterator here to keep the function "const".
1757  // Return an iterator to the appropriate entry, if we find it
1758  std::map<unsigned,Vector<FiniteElement*> >::const_iterator it=
1759  Boundary_region_element_pt[b].find(r);
1760  if (it!=Boundary_region_element_pt[b].end())
1761  {
1762  return (it->second).size();
1763  }
1764  // Otherwise there are no elements adjacent to boundary b in the region r
1765  else
1766  {
1767  return 0;
1768  }
1769  }
1770 
1771  /// Return pointer to the e-th element adjacent to boundary b in region r
1773  const unsigned &r,
1774  const unsigned &e) const
1775  {
1776  // Use a constant iterator here to keep function "const" overall
1777  std::map<unsigned,Vector<FiniteElement*> >::const_iterator it=
1778  Boundary_region_element_pt[b].find(r);
1779  if (it!=Boundary_region_element_pt[b].end())
1780  {
1781  return (it->second)[e];
1782  }
1783  else
1784  {
1785  return 0;
1786  }
1787  }
1788 
1789  /// Return face index of the e-th element adjacent to boundary b in region r
1790  int face_index_at_boundary_in_region(const unsigned &b,
1791  const unsigned &r,
1792  const unsigned &e) const
1793  {
1794  // Use a constant iterator here to keep function "const" overall
1795  std::map<unsigned,Vector<int> >::const_iterator it=
1796  Face_index_region_at_boundary[b].find(r);
1797  if (it!=Face_index_region_at_boundary[b].end())
1798  {
1799  return (it->second)[e];
1800  }
1801  else
1802  {
1803  std::ostringstream error_message;
1804  error_message << "Face indices not set up for boundary "
1805  << b << " in region " << r << "\n";
1806  error_message
1807  << "This probably means that the boundary is not adjacent to region\n";
1808  throw OomphLibError(error_message.str(),
1809  OOMPH_CURRENT_FUNCTION,
1810  OOMPH_EXCEPTION_LOCATION);
1811  }
1812  }
1813 
1814  /// \short Return pointer to the current polyline that describes
1815  /// the b-th mesh boundary
1817  {
1818  std::map<unsigned,TriangleMeshCurveSection*>::iterator it=
1819  Boundary_curve_section_pt.find(b);
1820  // Search for the polyline associated with the given boundary
1821  if (it!=Boundary_curve_section_pt.end())
1822  {
1823  return dynamic_cast<TriangleMeshPolyLine*>(Boundary_curve_section_pt[b]);
1824  }
1825  // If the boundary was not established then return 0, or a null pointer
1826  return 0;
1827  }
1828 
1829  /// \short Gets a pointer to a set with all the nodes related with a boundary
1830  std::map<unsigned,std::set<Node*> >& nodes_on_boundary_pt()
1831  {
1832  return Nodes_on_boundary_pt;
1833  }
1834 
1835  /// \short Gets the vertex number on the destination polyline (used
1836  /// to create the connections among shared boundaries)
1837  const bool get_connected_vertex_number_on_destination_polyline(
1838  TriangleMeshPolyLine* dst_polyline_pt,
1839  Vector<double>& vertex_coordinates,
1840  unsigned& vertex_number);
1841 
1842  /// \short Sort the polylines coming from joining them. Check whether
1843  /// it is necessary to reverse them or not. Used when joining two curve
1844  /// polylines in order to create a polygon
1845  void check_contiguousness_on_polylines_helper(
1846  Vector<TriangleMeshPolyLine* > &polylines_pt,unsigned &index);
1847 
1848  /// \short Sort the polylines coming from joining them. Check whether
1849  /// it is necessary to reverse them or not. Used when joining polylines
1850  /// and they still do not create a polygon
1851  void check_contiguousness_on_polylines_helper(
1852  Vector<TriangleMeshPolyLine* > &polylines_pt,
1853  unsigned &index_halo_start,unsigned &index_halo_end);
1854 
1855  /// Helper function that checks if a given point is inside a polygon
1856  /// (a set of sorted vertices that connected create a polygon)
1857  bool is_point_inside_polygon_helper(
1858  Vector<Vector<double> > polygon_vertices,Vector<double> point);
1859 
1860  /// Enables the creation of points (by Triangle) on the outer and
1861  /// internal boundaries
1863  {
1864  Allow_automatic_creation_of_vertices_on_boundaries = true;
1865  }
1866 
1867  /// Disables the creation of points (by Triangle) on the outer and
1868  /// internal boundaries
1870  {
1871  Allow_automatic_creation_of_vertices_on_boundaries=false;
1872  }
1873 
1874  /// Returns the status of the variable
1875  /// Allow_automatic_creation_of_vertices_on_boundaries
1877  {
1878  return Allow_automatic_creation_of_vertices_on_boundaries;
1879  }
1880 
1881 #ifdef OOMPH_HAS_MPI
1882 
1883  /// Flush the boundary segment node storage
1884  void flush_boundary_segment_node(const unsigned &b)
1885  {
1886  Boundary_segment_node_pt[b].clear();
1887  }
1888 
1889  /// Set the number of segments associated with a boundary
1890  void set_nboundary_segment_node(const unsigned &b,const unsigned &s)
1891  {
1892  Boundary_segment_node_pt[b].resize(s);
1893  }
1894 
1895  /// Return the number of segments associated with a boundary
1896  unsigned nboundary_segment(const unsigned &b)
1897  {
1898  return Boundary_segment_node_pt[b].size();
1899  }
1900 
1901  /// Return the number of segments associated with a boundary
1902  unsigned long nboundary_segment_node(const unsigned &b)
1903  {
1904  unsigned ntotal_nodes = 0;
1905  unsigned nsegments = Boundary_segment_node_pt[b].size();
1906  for (unsigned is = 0; is < nsegments; is++)
1907  {
1908  ntotal_nodes+=nboundary_segment_node(b,is);
1909  }
1910  return ntotal_nodes;
1911  }
1912 
1913  /// Return the number of nodes associated with a given segment of a
1914  /// boundary
1915  unsigned long nboundary_segment_node(const unsigned &b,const unsigned &s)
1916  {
1917  return Boundary_segment_node_pt[b][s].size();
1918  }
1919 
1920  /// Add the node node_pt to the b-th boundary and the s-th segment of
1921  /// the mesh
1922  void add_boundary_segment_node(const unsigned &b,
1923  const unsigned &s,
1924  Node* const &node_pt)
1925  {
1926  // Get the size of the Boundary_node_pt vector
1927  unsigned nbound_seg_node=nboundary_segment_node(b,s);
1928  bool node_already_on_this_boundary_segment=false;
1929 
1930  // Loop over the vector
1931  for (unsigned n=0; n<nbound_seg_node; n++)
1932  {
1933  // Is the current node here already?
1934  if (node_pt==Boundary_segment_node_pt[b][s][n])
1935  {
1936  node_already_on_this_boundary_segment=true;
1937  }
1938  }
1939 
1940  // Add the base node pointer to the vector if it's not there already
1941  if (!node_already_on_this_boundary_segment)
1942  {
1943  Boundary_segment_node_pt[b][s].push_back(node_pt);
1944  }
1945  }
1946 
1947  /// \short Flag used at the setup_boundary_coordinate function to know
1948  /// if initial zeta values for segments have been assigned
1949  std::map<unsigned,bool> Assigned_segments_initial_zeta_values;
1950 
1951  /// \short Return direct access to the initial coordinates of a boundary
1952  std::map<unsigned, Vector<double> >& boundary_initial_coordinate()
1953  {
1954  return Boundary_initial_coordinate;
1955  }
1956 
1957  /// \short Return direct access to the final coordinates of a boundary
1958  std::map<unsigned, Vector<double> >& boundary_final_coordinate()
1959  {
1960  return Boundary_final_coordinate;
1961  }
1962 
1963  /// \short Return direct access to the initial zeta coordinate of a
1964  /// boundary
1965  std::map<unsigned, Vector<double> >& boundary_initial_zeta_coordinate()
1966  {
1967  return Boundary_initial_zeta_coordinate;
1968  }
1969 
1970  /// \short Return direct access to the final zeta coordinates of a
1971  /// boundary
1972  std::map<unsigned, Vector<double> >& boundary_final_zeta_coordinate()
1973  {
1974  return Boundary_final_zeta_coordinate;
1975  }
1976 
1977  /// \short Return the info. to know if it is necessary to reverse the
1978  /// segment based on a previous mesh
1979  std::map<unsigned, Vector<unsigned> >& boundary_segment_inverted()
1980  {
1981  return Boundary_segment_inverted;
1982  }
1983 
1984  /// \short Return direct access to the initial coordinates for the
1985  /// segments that are part of a boundary
1986  std::map<unsigned, Vector<Vector<double> > >&
1988  {
1989  return Boundary_segment_initial_coordinate;
1990  }
1991 
1992  /// \short Return direct access to the final coordinates for the
1993  /// segments that are part of a boundary
1994  std::map<unsigned, Vector<Vector<double> > >&
1996  {
1997  return Boundary_segment_final_coordinate;
1998  }
1999 
2000  /// \short Return direct access to the initial arclength for the
2001  /// segments that are part of a boundary
2002  std::map<unsigned, Vector<double> >& boundary_segment_initial_arclength()
2003  {
2004  return Boundary_segment_initial_arclength;
2005  }
2006 
2007  /// \short Return direct access to the final arclength for the
2008  /// segments that are part of a boundary
2009  std::map<unsigned, Vector<double> >& boundary_segment_final_arclength()
2010  {
2011  return Boundary_segment_final_arclength;
2012  }
2013 
2014  /// \short Return direct access to the initial zeta for the
2015  /// segments that are part of a boundary
2016  std::map<unsigned, Vector<double> >& boundary_segment_initial_zeta()
2017  {
2018  return Boundary_segment_initial_zeta;
2019  }
2020 
2021  /// \short Return direct access to the final zeta for the
2022  /// segments that are part of a boundary
2023  std::map<unsigned, Vector<double> >& boundary_segment_final_zeta()
2024  {
2025  return Boundary_segment_final_zeta;
2026  }
2027 
2028  /// \short Return the initial zeta for the segments that are
2029  /// part of a boundary
2031  {
2032  std::map<unsigned, Vector<double> >::iterator it =
2033  Boundary_segment_initial_zeta.find(b);
2034 
2035 #ifdef PARANOID
2036 
2037  if(it==Boundary_segment_initial_zeta.end())
2038  {
2039  std::stringstream error_message;
2040  error_message
2041  << "The boundary ("<<b<<") has no segments associated with it!!\n\n";
2042  throw OomphLibError(error_message.str(),
2043  OOMPH_CURRENT_FUNCTION,
2044  OOMPH_EXCEPTION_LOCATION);
2045  }
2046 
2047 #endif // PARANOID
2048 
2049  return (*it).second;
2050  }
2051 
2052  /// \short Return the final zeta for the segments that are
2053  /// part of a boundary
2055  {
2056  std::map<unsigned, Vector<double> >::iterator it =
2057  Boundary_segment_final_zeta.find(b);
2058 
2059 #ifdef PARANOID
2060 
2061  if(it==Boundary_segment_final_zeta.end())
2062  {
2063  std::stringstream error_message;
2064  error_message
2065  << "The boundary ("<<b<<") has no segments associated with it!!\n\n";
2066  throw OomphLibError(error_message.str(),
2067  OOMPH_CURRENT_FUNCTION,
2068  OOMPH_EXCEPTION_LOCATION);
2069  }
2070 
2071 #endif // PARANOID
2072 
2073  return (*it).second;
2074  }
2075 
2076  /// \short Return the initial coordinates for the boundary
2078  {
2079  std::map<unsigned, Vector<double> >::iterator it =
2080  Boundary_initial_coordinate.find(b);
2081 
2082 #ifdef PARANOID
2083 
2084  if(it==Boundary_initial_coordinate.end())
2085  {
2086  std::stringstream error_message;
2087  error_message
2088  << "The boundary (" << b << ") has not established initial coordinates\n";
2089  throw OomphLibError(error_message.str(),
2090  OOMPH_CURRENT_FUNCTION,
2091  OOMPH_EXCEPTION_LOCATION);
2092  }
2093 
2094 #endif
2095  return (*it).second;
2096  }
2097 
2098  /// \short Return the final coordinates for the boundary
2100  {
2101  std::map<unsigned, Vector<double> >::iterator it =
2102  Boundary_final_coordinate.find(b);
2103 
2104 #ifdef PARANOID
2105 
2106  if(it==Boundary_final_coordinate.end())
2107  {
2108  std::stringstream error_message;
2109  error_message
2110  << "The boundary (" << b << ") has not established final coordinates\n";
2111  throw OomphLibError(error_message.str(),
2112  OOMPH_CURRENT_FUNCTION,
2113  OOMPH_EXCEPTION_LOCATION);
2114  }
2115 
2116 #endif
2117 
2118  return (*it).second;
2119  }
2120 
2121  /// \short Return the info. to know if it is necessary to reverse the
2122  /// segment based on a previous mesh
2123  const Vector<unsigned> boundary_segment_inverted(const unsigned &b) const
2124  {
2125  std::map<unsigned, Vector<unsigned> >::const_iterator it =
2126  Boundary_segment_inverted.find(b);
2127 
2128 #ifdef PARANOID
2129 
2130  if(it==Boundary_segment_inverted.end())
2131  {
2132  std::stringstream error_message;
2133  error_message
2134  << "The boundary (" << b << ") has not established inv. segments info\n";
2135  throw OomphLibError(error_message.str(),
2136  OOMPH_CURRENT_FUNCTION,
2137  OOMPH_EXCEPTION_LOCATION);
2138  }
2139 
2140 #endif
2141 
2142  return (*it).second;
2143  }
2144 
2145  /// \short Return the info. to know if it is necessary to reverse the
2146  /// segment based on a previous mesh
2148  {
2149  std::map<unsigned, Vector<unsigned> >::iterator it =
2150  Boundary_segment_inverted.find(b);
2151 
2152 #ifdef PARANOID
2153 
2154  if(it==Boundary_segment_inverted.end())
2155  {
2156  std::stringstream error_message;
2157  error_message
2158  << "The boundary (" << b << ") has not established inv. segments info\n";
2159  throw OomphLibError(error_message.str(),
2160  OOMPH_CURRENT_FUNCTION,
2161  OOMPH_EXCEPTION_LOCATION);
2162  }
2163 
2164 #endif
2165 
2166  return (*it).second;
2167  }
2168 
2169  /// \short Return the initial zeta coordinate for the boundary
2171  {
2172  std::map<unsigned, Vector<double> >::iterator it =
2173  Boundary_initial_zeta_coordinate.find(b);
2174 
2175 #ifdef PARANOID
2176 
2177  if(it==Boundary_initial_zeta_coordinate.end())
2178  {
2179  std::stringstream error_message;
2180  error_message
2181  << "The boundary ("<<b<<") has not established initial zeta "
2182  << "coordinate\n";
2183  throw OomphLibError(error_message.str(),
2184  OOMPH_CURRENT_FUNCTION,
2185  OOMPH_EXCEPTION_LOCATION);
2186  }
2187 
2188 #endif
2189 
2190  return (*it).second;
2191  }
2192 
2193  /// \short Return the final zeta coordinate for the boundary
2195  {
2196  std::map<unsigned, Vector<double> >::iterator it =
2197  Boundary_final_zeta_coordinate.find(b);
2198 
2199 #ifdef PARANOID
2200 
2201  if(it==Boundary_final_zeta_coordinate.end())
2202  {
2203  std::stringstream error_message;
2204  error_message
2205  << "The boundary ("<<b<<") has not established final zeta coordinate\n";
2206  throw OomphLibError(error_message.str(),
2207  OOMPH_CURRENT_FUNCTION,
2208  OOMPH_EXCEPTION_LOCATION);
2209  }
2210 
2211 #endif
2212 
2213  return (*it).second;
2214  }
2215 
2216  /// \short Return the initial arclength for the segments that are
2217  /// part of a boundary
2219  {
2220  std::map<unsigned, Vector<double> >::iterator it =
2221  Boundary_segment_initial_arclength.find(b);
2222 
2223 #ifdef PARANOID
2224 
2225  if(it==Boundary_segment_initial_arclength.end())
2226  {
2227  std::stringstream error_message;
2228  error_message
2229  << "The boundary ("<<b<<") has no segments associated with it!!\n\n";
2230  throw OomphLibError(error_message.str(),
2231  OOMPH_CURRENT_FUNCTION,
2232  OOMPH_EXCEPTION_LOCATION);
2233  }
2234 
2235 #endif
2236 
2237  return (*it).second;
2238  }
2239 
2240  /// \short Return the final arclength for the segments that are
2241  /// part of a boundary
2243  {
2244  std::map<unsigned, Vector<double> >::iterator it =
2245  Boundary_segment_final_arclength.find(b);
2246 
2247 #ifdef PARANOID
2248 
2249  if(it==Boundary_segment_final_arclength.end())
2250  {
2251  std::stringstream error_message;
2252  error_message
2253  << "The boundary ("<<b<<") has no segments associated with it!!\n\n";
2254  throw OomphLibError(error_message.str(),
2255  OOMPH_CURRENT_FUNCTION,
2256  OOMPH_EXCEPTION_LOCATION);
2257  }
2258 
2259 #endif
2260 
2261  return (*it).second;
2262  }
2263 
2264  /// \short Return the initial coordinates for the segments that are
2265  /// part of a boundary
2267  {
2268  std::map<unsigned, Vector<Vector<double> > >::iterator it =
2269  Boundary_segment_initial_coordinate.find(b);
2270 
2271 #ifdef PARANOID
2272 
2273  if(it==Boundary_segment_initial_coordinate.end())
2274  {
2275  std::stringstream error_message;
2276  error_message
2277  << "The boundary ("<<b<<") has no segments associated with it!!\n\n";
2278  throw OomphLibError(error_message.str(),
2279  OOMPH_CURRENT_FUNCTION,
2280  OOMPH_EXCEPTION_LOCATION);
2281  }
2282 
2283 #endif
2284 
2285  return (*it).second;
2286  }
2287 
2288  /// \short Return the final coordinates for the segments that are
2289  /// part of a boundary
2291  {
2292  std::map<unsigned, Vector<Vector<double> > >::iterator it =
2293  Boundary_segment_final_coordinate.find(b);
2294 
2295 #ifdef PARANOID
2296 
2297  if(it==Boundary_segment_final_coordinate.end())
2298  {
2299  std::stringstream error_message;
2300  error_message
2301  << "The boundary ("<<b<<") has no segments associated with it!!\n\n";
2302  throw OomphLibError(error_message.str(),
2303  OOMPH_CURRENT_FUNCTION,
2304  OOMPH_EXCEPTION_LOCATION);
2305  }
2306 
2307 #endif
2308 
2309  return (*it).second;
2310  }
2311 
2312 #endif // OOMPH_HAS_MPI
2313 
2314  /// \short Setup boundary coordinate on boundary b.
2315  /// Boundary coordinate increases continously along
2316  /// polygonal boundary. It's zero at the lowest left
2317  /// node on the boundary.
2318  template<class ELEMENT>
2319  void setup_boundary_coordinates(const unsigned& b)
2320  {
2321  // Dummy file
2322  std::ofstream some_file;
2323  setup_boundary_coordinates<ELEMENT>(b,some_file);
2324  }
2325 
2326  /// \short Setup boundary coordinate on boundary b. Doc Faces
2327  /// in outfile.
2328  /// Boundary coordinate increases continously along
2329  /// polygonal boundary. It's zero at the lowest left
2330  /// node on the boundary.
2331  template<class ELEMENT>
2332  void setup_boundary_coordinates(const unsigned& b,std::ofstream& outfile);
2333 
2334 
2335  protected:
2336 
2337 
2338 #ifdef OOMPH_HAS_TRIANGLE_LIB
2339 
2340  /// \short Create TriangulateIO object from outer boundaries,
2341  /// internal boundaries, and open curves. Add the holes and regions
2342  /// information as well
2343  void build_triangulateio(Vector<TriangleMeshPolygon*> &outer_polygons_pt,
2344  Vector<TriangleMeshPolygon*> &internal_polygons_pt,
2345  Vector<TriangleMeshOpenCurve*> &open_curves_pt,
2346  Vector<Vector<double> > &extra_holes_coordinates,
2347  std::map<unsigned, Vector<double> >
2348  &regions_coordinates,
2349  std::map<unsigned, double>
2350  &regions_areas,
2351  TriangulateIO& triangulate_io);
2352 
2353  /// \short Data structure filled when the connection matrix is created, for
2354  /// each polyline, there are two vertex_connection_info structures,
2355  /// one for each end
2357  {
2362  }; // vertex_connection_info
2363 
2364  /// \short Data structure to store the base vertex info, initial or final
2365  /// vertex in the polylines have an associated base vertex
2369  unsigned boundary_id;
2370  unsigned boundary_chunk;
2371  unsigned vertex_number;
2372  }; // base_vertex_info
2373 
2374  /// \short Helps to add information to the connection matrix of the
2375  /// given polyline
2376  void add_connection_matrix_info_helper(
2377  TriangleMeshPolyLine* polyline_pt,
2378  std::map<unsigned,std::map<unsigned,Vector<vertex_connection_info > > >
2379  &connection_matrix,
2380  TriangleMeshPolyLine* next_polyline_pt = 0);
2381 
2382  /// \short Initialise the base vertex structure, set every vertex to
2383  /// no visited and not being a base vertex
2384  void initialise_base_vertex(
2385  TriangleMeshPolyLine* polyline_pt,
2386  std::map<unsigned,std::map<unsigned,Vector<base_vertex_info> > >
2387  &base_vertices);
2388 
2389  /// \short Helps to identify the base vertex of the given polyline
2390  void add_base_vertex_info_helper(
2391  TriangleMeshPolyLine* polyline_pt,
2392  std::map<unsigned,std::map<unsigned,Vector<base_vertex_info> > >
2393  &base_vertices,
2394  std::map<unsigned,std::map<unsigned,Vector<vertex_connection_info> > >
2395  &connection_matrix,
2396  std::map<unsigned, std::map<unsigned, unsigned> >
2397  &boundary_chunk_nvertices);
2398 
2399 #endif
2400 
2401 #ifdef OOMPH_HAS_MPI
2402 
2403  /// \short Used to store the nodes associated to a boundary and to an
2404  /// specific segment (this only applies in distributed meshes where the
2405  /// boundary is splitted in segments)
2406  std::map<unsigned,Vector<Vector<Node*> > > Boundary_segment_node_pt;
2407 
2408  /// \short Stores the initial zeta coordinate for the segments that
2409  /// appear when a boundary is splited among processors
2410  std::map<unsigned,Vector<double> > Boundary_segment_initial_zeta;
2411 
2412  /// \short Stores the final zeta coordinate for the segments that
2413  /// appear when a boundary is splited among processors
2414  std::map<unsigned,Vector<double> > Boundary_segment_final_zeta;
2415 
2416  /// \short Stores the initial coordinates for the boundary
2417  std::map<unsigned,Vector<double> > Boundary_initial_coordinate;
2418 
2419  /// \short Stores the final coordinates for the boundary
2420  std::map<unsigned,Vector<double> > Boundary_final_coordinate;
2421 
2422  /// \short Stores the info. to know if it is necessary to reverse the
2423  /// segment based on a previous mesh
2424  std::map<unsigned,Vector<unsigned> > Boundary_segment_inverted;
2425 
2426  /// \short Stores the initial zeta coordinate for the boundary
2427  std::map<unsigned,Vector<double> > Boundary_initial_zeta_coordinate;
2428 
2429  /// \short Stores the final zeta coordinate for the boundary
2430  std::map<unsigned,Vector<double> > Boundary_final_zeta_coordinate;
2431 
2432  /// \short Stores the initial arclength for the segments that appear when
2433  /// a boundary is splited among processors
2434  std::map<unsigned,Vector<double> > Boundary_segment_initial_arclength;
2435 
2436  /// \short Stores the final arclength for the segments that appear when
2437  /// a boundary is splited among processors
2438  std::map<unsigned,Vector<double> > Boundary_segment_final_arclength;
2439 
2440  /// \short Stores the initial coordinates for the segments that appear
2441  /// when a boundary is splited among processors
2442  std::map<unsigned,Vector<Vector<double> > > Boundary_segment_initial_coordinate;
2443 
2444  /// \short Stores the final coordinates for the segments that appear
2445  /// when a boundary is splited among processors
2446  std::map<unsigned,Vector<Vector<double> > > Boundary_segment_final_coordinate;
2447 
2448 #endif
2449 
2450  /// \short Flag to indicate whether the automatic creation of vertices
2451  /// along the boundaries by Triangle is allowed
2453 
2454  /// \short Snap the boundary nodes onto any curvilinear boundaries
2455  /// defined by geometric objects
2456  void snap_nodes_onto_geometric_objects();
2457 
2458  /// \short Vector of elements in each region differentiated by attribute
2459  /// (the key of the map is the attribute)
2460  std::map<unsigned,Vector<FiniteElement* > > Region_element_pt;
2461 
2462  /// Vector of attributes associated with the elements in each region
2464 
2465  /// \short Storage for the geometric objects associated with any boundaries
2466  std::map<unsigned,GeomObject*> Boundary_geom_object_pt;
2467 
2468  /// Storage for the limits of the boundary coordinates defined by the use
2469  /// of geometric objects. Only used for curvilinear boundaries.
2470  std::map<unsigned,Vector<double> > Boundary_coordinate_limits;
2471 
2472  /// Polygon that defines outer boundaries
2474 
2475  /// Vector of polygons that define internal polygons
2477 
2478  /// Vector of open polylines that define internal curves
2480 
2481  /// Storage for extra coordinates for holes
2483 
2484  /// Storage for extra coordinates for regions. The key on the map
2485  /// is the region id
2486  std::map<unsigned,Vector<double> > Regions_coordinates;
2487 
2488  /// A map that stores the associated curve section of the specified boundary id
2489  std::map<unsigned,TriangleMeshCurveSection*> Boundary_curve_section_pt;
2490 
2491  /// Storage for elements adjacent to a boundary in a particular region
2493 
2494  /// Storage for the face index adjacent to a boundary in a particular region
2496 
2497  /// \short Storage for pairs of doubles representing:
2498  /// .first: the arclength along the polygonal representation of
2499  /// the curviline
2500  /// .second: the corresponding intrinsic coordinate on the associated
2501  /// geometric object
2502  /// at which the vertices on the specified boundary are located.
2503  /// Only used for boundaries represented by geom objects.
2504  std::map<unsigned,Vector<std::pair<double,double> > >
2506 
2507  /// \short Stores a pointer to a set with all the nodes
2508  /// related with a boundary
2509  std::map<unsigned,std::set<Node*> > Nodes_on_boundary_pt;
2510 
2511  /// \short A set that contains the curve sections created by this object
2512  /// therefore it is necessary to free their associated memory
2513  std::set<TriangleMeshCurveSection*> Free_curve_section_pt;
2514 
2515  /// \short A set that contains the polygons created by this object
2516  /// therefore it is necessary to free their associated memory
2517  std::set<TriangleMeshPolygon*> Free_polygon_pt;
2518 
2519  /// \short A set that contains the open curves created by this
2520  /// object therefore it is necessary to free their associated memory
2521  std::set<TriangleMeshOpenCurve*> Free_open_curve_pt;
2522 
2523  /// \short Helper function to copy the connection information from
2524  /// the input curve(polyline or curviline) to the output polyline
2525  void copy_connection_information(TriangleMeshCurveSection* input_curve_pt,
2526  TriangleMeshCurveSection* output_curve_pt);
2527 
2528  /// \short Helper function to copy the connection information from
2529  /// the input curve(polyline or curviline) to the output sub-polyline
2530  void copy_connection_information_to_sub_polylines(
2531  TriangleMeshCurveSection* input_curve_pt,
2532  TriangleMeshCurveSection* output_curve_pt);
2533 
2534 
2535 #ifdef PARANOID
2536 
2537  // Used to verify if any of the polygons (closedcurves) that define
2538  // the mesh are of type ImmersedRigidBodyTriangleMeshPolygon, if
2539  // that is the case it may lead to problems in case of using load
2540  // balance
2542 
2543 #endif
2544 
2545 #ifdef OOMPH_HAS_TRIANGLE_LIB
2546 
2547  /// \short Helper function to create polyline vertex coordinates for
2548  /// curvilinear boundary specified by boundary_pt, using either
2549  /// equal increments in zeta or in (approximate) arclength
2550  /// along the curviline. vertex_coord[i_vertex][i_dim] stores
2551  /// i_dim-th coordinate of i_vertex-th vertex.
2552  /// polygonal_vertex_arclength_info[i_vertex] contains the pair of doubles
2553  /// made of the arclength of the i_vertex-th vertex along the polygonal
2554  /// representation (.first), and the corresponding coordinate on the
2555  /// GeomObject (.second)
2557  TriangleMeshCurviLine* boundary_pt,
2558  Vector<Vector<double> >& vertex_coord,
2559  Vector<std::pair<double,double> >& polygonal_vertex_arclength_info)
2560  {
2561  // Intrinsic coordinate along GeomObjects
2562  Vector<double> zeta(1);
2563 
2564  // Position vector to point on GeomObject
2565  Vector<double> posn(2);
2566 
2567  // Start coordinate
2568  double zeta_initial = boundary_pt->zeta_start();
2569 
2570  //How many segments do we want on this polyline?
2571  unsigned n_seg = boundary_pt->nsegment();
2572  vertex_coord.resize(n_seg+1);
2573  polygonal_vertex_arclength_info.resize(n_seg+1);
2574  polygonal_vertex_arclength_info[0].first=0.0;
2575  polygonal_vertex_arclength_info[0].second=zeta_initial;
2576 
2577  // Vertices placed in equal zeta increments
2578  if (!(boundary_pt->space_vertices_evenly_in_arclength()))
2579  {
2580  //Read the values of the limiting coordinates, assuming equal
2581  //spacing of the nodes
2582  double zeta_increment =
2583  (boundary_pt->zeta_end()-boundary_pt->zeta_start())/(double(n_seg));
2584 
2585  //Loop over the n_seg+1 points bounding the segments
2586  for(unsigned s=0;s<n_seg+1;s++)
2587  {
2588  // Get the coordinates
2589  zeta[0]= zeta_initial + zeta_increment*double(s);
2590  boundary_pt->geom_object_pt()->position(zeta,posn);
2591  vertex_coord[s]=posn;
2592 
2593  // Bump up the polygonal arclength
2594  if (s>0)
2595  {
2596  polygonal_vertex_arclength_info[s].first=
2597  polygonal_vertex_arclength_info[s-1].first+
2598  sqrt(pow(vertex_coord[s][0]-vertex_coord[s-1][0],2)+
2599  pow(vertex_coord[s][1]-vertex_coord[s-1][1],2));
2600  polygonal_vertex_arclength_info[s].second=zeta[0];
2601 
2602  }
2603  }
2604  }
2605  // Vertices placed in equal increments in (approximate) arclength
2606  else
2607  {
2608  // Number of sampling points to compute arclength and
2609  // arclength increments
2610  unsigned nsample_per_segment=100;
2611  unsigned nsample=nsample_per_segment*n_seg;
2612 
2613  // Work out start and increment
2614  double zeta_increment=(boundary_pt->zeta_end()-
2615  boundary_pt->zeta_start())/(double(nsample));
2616 
2617  // Get coordinate of first point
2618  Vector<double> start_point(2);
2619  zeta[0]=zeta_initial;
2620 
2621  boundary_pt->geom_object_pt()->position(zeta,start_point);
2622 
2623  // Storage for coordinates of end point
2624  Vector<double> end_point(2);
2625 
2626  // Compute total arclength
2627  double total_arclength=0.0;
2628  for (unsigned i=1;i<nsample;i++)
2629  {
2630  // Next point
2631  zeta[0]+=zeta_increment;
2632 
2633  // Get coordinate of end point
2634  boundary_pt->geom_object_pt()->position(zeta,end_point);
2635 
2636  // Increment arclength
2637  total_arclength+=sqrt(pow(end_point[0]-start_point[0],2)+
2638  pow(end_point[1]-start_point[1],2));
2639 
2640  // Shift back
2641  start_point=end_point;
2642  }
2643 
2644  // Desired arclength increment
2645  double target_s_increment=total_arclength/(double(n_seg));
2646 
2647  // Get coordinate of first point again
2648  zeta[0]=zeta_initial;
2649  boundary_pt->geom_object_pt()->position(zeta,start_point);
2650 
2651  // Assign as coordinate
2652  vertex_coord[0]=start_point;
2653 
2654  // Start sampling point
2655  unsigned i_lo=1;
2656 
2657  //Loop over the n_seg-1 internal points bounding the segments
2658  for(unsigned s=1;s<n_seg;s++)
2659  {
2660  // Visit potentially all sample points until we've found
2661  // the one at which we exceed the target arclength increment
2662  double arclength_increment=0.0;
2663  for (unsigned i=i_lo;i<nsample;i++)
2664  {
2665  // Next point
2666  zeta[0]+=zeta_increment;
2667 
2668  // Get coordinate of end point
2669  boundary_pt->geom_object_pt()->position(zeta,end_point);
2670 
2671  // Increment arclength increment
2672  arclength_increment+=sqrt(pow(end_point[0]-start_point[0],2)+
2673  pow(end_point[1]-start_point[1],2));
2674 
2675  // Shift back
2676  start_point=end_point;
2677 
2678  // Are we there yet?
2679  if (arclength_increment>target_s_increment)
2680  {
2681  // Remember how far we've got
2682  i_lo=i;
2683 
2684  // And bail out
2685  break;
2686  }
2687  }
2688 
2689  // Store the coordinates
2690  vertex_coord[s]=end_point;
2691 
2692  // Bump up the polygonal arclength
2693  if (s>0)
2694  {
2695  polygonal_vertex_arclength_info[s].first=
2696  polygonal_vertex_arclength_info[s-1].first+
2697  sqrt(pow(vertex_coord[s][0]-vertex_coord[s-1][0],2)+
2698  pow(vertex_coord[s][1]-vertex_coord[s-1][1],2));
2699  polygonal_vertex_arclength_info[s].second=zeta[0];
2700  }
2701  }
2702 
2703  // Final point
2704  unsigned s=n_seg;
2705  zeta[0]=boundary_pt->zeta_end();
2706  boundary_pt->geom_object_pt()->position(zeta,end_point);
2707  vertex_coord[s]=end_point;
2708  polygonal_vertex_arclength_info[s].first=
2709  polygonal_vertex_arclength_info[s-1].first+
2710  sqrt(pow(vertex_coord[s][0]-vertex_coord[s-1][0],2)+
2711  pow(vertex_coord[s][1]-vertex_coord[s-1][1],2));
2712  polygonal_vertex_arclength_info[s].second=zeta[0];
2713  }
2714  }
2715 
2716  /// \short Helper function to create polyline vertex coordinates for
2717  /// curvilinear boundary specified by boundary_pt, using either
2718  /// equal increments in zeta or in (approximate) arclength
2719  /// along the curviline. vertex_coord[i_vertex][i_dim] stores
2720  /// i_dim-th coordinate of i_vertex-th vertex.
2721  /// polygonal_vertex_arclength_info[i_vertex] contains the pair of doubles
2722  /// made of the arclength of the i_vertex-th vertex along the polygonal
2723  /// representation (.first), and the corresponding coordinate on the
2724  /// GeomObject (.second)
2726  TriangleMeshCurviLine* boundary_pt,
2727  Vector<Vector<double> >& vertex_coord,
2728  Vector<std::pair<double,double> >& polygonal_vertex_arclength_info)
2729  {
2730  // Start coordinate
2731  double zeta_initial = boundary_pt->zeta_start();
2732  // Final coordinate
2733  double zeta_final = boundary_pt->zeta_end();
2734 
2735  Vector<double> *connection_points_pt =
2736  boundary_pt->connection_points_pt();
2737 
2738  unsigned n_connections = connection_points_pt->size();
2739 
2740  // We need to sort the connection points
2741  if (n_connections > 1)
2742  {
2743  std::sort(connection_points_pt->begin(), connection_points_pt->end());
2744  }
2745 
2746 #ifdef PARANOID
2747  // Are the connection points out of range of the polyline
2748  bool out_of_range_connection_points = false;
2749  std::ostringstream error_message;
2750  // Check if the curviline should be created on a reversed way
2751  bool reversed = false;
2752  if (zeta_final < zeta_initial)
2753  {reversed = true;}
2754  if(!reversed)
2755  {
2756  if (zeta_initial > (*connection_points_pt)[0])
2757  {
2758  error_message
2759  << "One of the specified connection points is out of the\n"
2760  << "curviline limits. We found that the point ("
2761  << (*connection_points_pt)[0] << ") is\n" << "less than the"
2762  << "initial s value which is (" << zeta_initial << ").\n"
2763  << "Initial value: ("<<zeta_initial<<")\n"
2764  << "Final value: ("<<zeta_final<<")\n"
2765  << std::endl;
2766  out_of_range_connection_points = true;
2767  }
2768 
2769  if (zeta_final < (*connection_points_pt)[n_connections-1])
2770  {
2771  error_message
2772  << "One of the specified connection points is out of the\n"
2773  << "curviline limits. We found that the point ("
2774  << (*connection_points_pt)[n_connections-1] << ") is\n"
2775  << "greater than the final s value which is ("
2776  << zeta_final << ").\n"
2777  << "Initial value: ("<<zeta_initial<<")\n"
2778  << "Final value: ("<<zeta_final<<")\n"
2779  << std::endl;
2780  out_of_range_connection_points = true;
2781  }
2782  }
2783  else
2784  {
2785  if (zeta_initial < (*connection_points_pt)[0])
2786  {
2787  error_message
2788  << "One of the specified connection points is out of the\n"
2789  << "curviline limits. We found that the point ("
2790  << (*connection_points_pt)[0] << ") is\n" << "greater than the"
2791  << "initial s value which is (" << zeta_initial << ").\n"
2792  << "Initial value: ("<<zeta_initial<<")\n"
2793  << "Final value: ("<<zeta_final<<")\n"
2794  << std::endl;
2795  out_of_range_connection_points = true;
2796  }
2797 
2798  if (zeta_final > (*connection_points_pt)[n_connections-1])
2799  {
2800  error_message
2801  << "One of the specified connection points is out of the\n"
2802  << "curviline limits. We found that the point ("
2803  << (*connection_points_pt)[n_connections-1] << ") is\n"
2804  << "less than the final s value which is ("
2805  << zeta_final << ").\n"
2806  << "Initial value: ("<<zeta_initial<<")\n"
2807  << "Final value: ("<<zeta_final<<")\n"
2808  << std::endl;
2809  out_of_range_connection_points = true;
2810  }
2811  }
2812 
2813  if (out_of_range_connection_points)
2814  {
2815  throw OomphLibError(error_message.str(),
2816  OOMPH_CURRENT_FUNCTION,
2817  OOMPH_EXCEPTION_LOCATION);
2818  }
2819 
2820 #endif // PARANOID
2821 
2822  // Intrinsic coordinate along GeomObjects
2823  Vector<double> zeta(1);
2824 
2825  // Position vector to point on GeomObject
2826  Vector<double> posn(2);
2827 
2828  //How many segments do we want on this polyline?
2829  unsigned n_seg = boundary_pt->nsegment();
2830 
2831  // How many connection vertices have we already created
2832  unsigned i_connection = 0;
2833  Vector<double> zeta_connection(1);
2834 
2835  // If we have more connection points than the generated
2836  // by the number of segments then we have to change the
2837  // number of segments and create all the vertices
2838  // according to the connection points list
2839  if (n_connections >= n_seg - 1)
2840  {
2841  std::ostringstream warning_message;
2842  std::string output_string="UnstructuredTwoDMeshGeometryBase::";
2843  output_string+="create_vertex_coordinates_for_polyline_connections()";
2844 
2845  warning_message
2846  << "The number of segments specified for the curviline with\n"
2847  << "boundary id (" << boundary_pt->boundary_id() << ") is less "
2848  << "(or equal) than the ones that will be\ngenerated by using "
2849  << "the specified number of connection points.\n"
2850  << "You specified (" << n_seg << ") segments but ("
2851  << n_connections + 1 << ") segments\nwill be generated."
2852  << std::endl;
2853  OomphLibWarning(warning_message.str(),
2854  output_string,
2855  OOMPH_EXCEPTION_LOCATION);
2856 
2857  // We have to explicitly change the number of segments
2858  boundary_pt->nsegment() = n_connections + 1;
2859  n_seg = boundary_pt->nsegment();
2860  vertex_coord.resize(n_seg+1);
2861 
2862  // Initial coordinate and initial values
2863  zeta[0]= zeta_initial;
2864  boundary_pt->geom_object_pt()->position(zeta, posn);
2865  vertex_coord[0]=posn;
2866 
2867  polygonal_vertex_arclength_info.resize(n_seg+1);
2868  polygonal_vertex_arclength_info[0].first=0.0;
2869  polygonal_vertex_arclength_info[0].second=zeta_initial;
2870 
2871  //Loop over the n_connections points bounding the segments
2872  for(i_connection = 0; i_connection < n_connections; i_connection++)
2873  {
2874 
2875  // Get the coordinates
2876  zeta[0]= (*connection_points_pt)[i_connection];
2877  boundary_pt->geom_object_pt()->position(zeta, posn);
2878  vertex_coord[i_connection + 1] = posn;
2879 
2880  // Bump up the polygonal arclength
2881  polygonal_vertex_arclength_info[i_connection + 1].first=
2882  polygonal_vertex_arclength_info[i_connection].first+
2883  sqrt(pow(vertex_coord[i_connection + 1][0]-
2884  vertex_coord[i_connection][0],2)+
2885  pow(vertex_coord[i_connection + 1][1]-
2886  vertex_coord[i_connection][1],2));
2887  polygonal_vertex_arclength_info[i_connection + 1].second=zeta[0];
2888 
2889  }
2890 
2891  // Final coordinate and final values
2892  zeta[0] = zeta_final;
2893  boundary_pt->geom_object_pt()->position(zeta, posn);
2894  vertex_coord[n_seg]=posn;
2895 
2896  polygonal_vertex_arclength_info[n_seg].first=
2897  polygonal_vertex_arclength_info[n_seg-1].first+
2898  sqrt(pow(vertex_coord[n_seg][0]-vertex_coord[n_seg-1][0],2)+
2899  pow(vertex_coord[n_seg][1]-vertex_coord[n_seg-1][1],2));
2900  polygonal_vertex_arclength_info[n_seg].second = zeta_final;
2901 
2902  }
2903  else
2904  {
2905  // Total number of vertices
2906  unsigned n_t_vertices = n_seg+1;
2907 
2908  // Number of vertices left for creation
2909  unsigned l_vertices = n_t_vertices;
2910 
2911  // Total number of already created vertices
2912  unsigned n_assigned_vertices = 0;
2913 
2914  // Stores the distance between current vertices in the list
2915  // Edge vertices + Connection points - 1
2916  Vector<double> delta_z(2 + n_connections - 1);
2917 
2918  std::list<double> zeta_values_pt;
2919  zeta_values_pt.push_back(zeta_initial);
2920  for (unsigned s = 0; s < n_connections; s++)
2921  {
2922  zeta_values_pt.push_back((*connection_points_pt)[s]);
2923  }
2924  zeta_values_pt.push_back(zeta_final);
2925 
2926  l_vertices-= 2; // Edge vertices
2927  l_vertices-= n_connections; // Connection points
2928  n_assigned_vertices+= 2; // Edge vertices
2929  n_assigned_vertices+= n_connections; // Connection points
2930 
2931  // Vertices placed in equal zeta increments
2932  if (!(boundary_pt->space_vertices_evenly_in_arclength()))
2933  {
2934  double local_zeta_initial;
2935  double local_zeta_final;
2936  double local_zeta_increment;
2937  double local_zeta_insert;
2938 
2939  // How many vertices for each section
2940  unsigned local_n_vertices;
2941 
2942  std::list<double>::iterator l_it = zeta_values_pt.begin();
2943  std::list<double>::iterator r_it = zeta_values_pt.begin();
2944  r_it++;
2945 
2946  for (unsigned h = 0; r_it!=zeta_values_pt.end(); l_it++,r_it++,h++)
2947  {
2948  delta_z[h] = *r_it-*l_it;
2949  }
2950 
2951  l_it = r_it = zeta_values_pt.begin();
2952  r_it++;
2953 
2954  for (unsigned h = 0; r_it!=zeta_values_pt.end(); h++)
2955  {
2956  local_n_vertices =
2957  static_cast<unsigned>(((double)n_t_vertices * delta_z[h]) /
2958  std::fabs(zeta_final - zeta_initial));
2959 
2960  local_zeta_initial = *l_it;
2961  local_zeta_final = *r_it;
2962  local_zeta_increment =
2963  (local_zeta_final - local_zeta_initial) /
2964  (double)(local_n_vertices + 1);
2965 
2966  for (unsigned s=0; s<local_n_vertices;s++)
2967  {
2968  local_zeta_insert =
2969  local_zeta_initial + local_zeta_increment*double(s+1);
2970  zeta_values_pt.insert(r_it, local_zeta_insert);
2971  n_assigned_vertices++;
2972  }
2973  // Moving to the next segment
2974  l_it = r_it;
2975  r_it++;
2976 
2977  }
2978 
2979  // Finishing it ...!!!
2980 #ifdef PARANOID
2981  // Counting the vertices number and the total of
2982  // assigned vertices values
2983  unsigned s = zeta_values_pt.size();
2984 
2985  if (s!=n_assigned_vertices)
2986  {
2987  error_message
2988  << "The total number of assigned vertices is different from\n"
2989  << "the number of elements in the z_values list. The number"
2990  << "of\nelements in the z_values list is (" << s << ") but "
2991  << "the number\n"
2992  << "of assigned vertices is (" << n_assigned_vertices << ")."
2993  << std::endl << std::endl;
2994  throw OomphLibError(error_message.str(),
2995  OOMPH_CURRENT_FUNCTION,
2996  OOMPH_EXCEPTION_LOCATION);
2997  }
2998 #endif // PARANOID
2999 
3000  vertex_coord.resize(n_assigned_vertices);
3001  polygonal_vertex_arclength_info.resize(n_assigned_vertices);
3002  polygonal_vertex_arclength_info[0].first=0.0;
3003  polygonal_vertex_arclength_info[0].second=zeta_initial;
3004 
3005  // Creating the vertices with the corresponding z_values
3006  l_it = zeta_values_pt.begin();
3007  for (unsigned s = 0; l_it!=zeta_values_pt.end(); s++,l_it++)
3008  {
3009  // Get the coordinates
3010  zeta[0]= *l_it;
3011  boundary_pt->geom_object_pt()->position(zeta, posn);
3012  vertex_coord[s] = posn;
3013 
3014  // Bump up the polygonal arclength
3015  if (s>0)
3016  {
3017  polygonal_vertex_arclength_info[s].first=
3018  polygonal_vertex_arclength_info[s-1].first+
3019  sqrt(pow(vertex_coord[s][0]-vertex_coord[s-1][0],2)+
3020  pow(vertex_coord[s][1]-vertex_coord[s-1][1],2));
3021  }
3022  }
3023  }
3024  // Vertices placed in equal increments in (approximate) arclength
3025  else
3026  {
3027  // Compute the total arclength
3028  // Number of sampling points to compute arclength and
3029  // arclength increments
3030  unsigned nsample_per_segment=100;
3031  unsigned nsample=nsample_per_segment*n_seg;
3032 
3033  // Work out start and increment
3034  double zeta_increment=
3035  (zeta_final-zeta_initial)/(double(nsample));
3036 
3037  // Get coordinate of first point
3038  Vector<double> start_point(2);
3039  zeta[0]=zeta_initial;
3040  boundary_pt->geom_object_pt()->position(zeta,start_point);
3041 
3042  // Storage for coordinates of end point
3043  Vector<double> end_point(2);
3044 
3045  // Compute total arclength
3046  double total_arclength=0.0;
3047  for (unsigned i=1;i<nsample;i++)
3048  {
3049  // Next point
3050  zeta[0]+=zeta_increment;
3051 
3052  // Get coordinate of end point
3053  boundary_pt->geom_object_pt()->position(zeta,end_point);
3054 
3055  // Increment arclength
3056  total_arclength+=sqrt(pow(end_point[0]-start_point[0],2)+
3057  pow(end_point[1]-start_point[1],2));
3058 
3059  // Shift back
3060  start_point=end_point;
3061  }
3062 
3063  double local_zeta_initial;
3064  double local_zeta_final;
3065  double local_zeta_increment;
3066 
3067  // How many vertices per section
3068  unsigned local_n_vertices;
3069 
3070  std::list<double>::iterator l_it = zeta_values_pt.begin();
3071  std::list<double>::iterator r_it = zeta_values_pt.begin();
3072  r_it++;
3073 
3074  for (unsigned h = 0; r_it!=zeta_values_pt.end(); h++)
3075  {
3076  // There is no need to move the r_it iterator since it is
3077  // moved at the final of this loop
3078  local_zeta_initial = *l_it;
3079  local_zeta_final = *r_it;
3080  local_zeta_increment =
3081  (local_zeta_final - local_zeta_initial) /
3082  (double)(nsample);
3083 
3084  // Compute local arclength
3085  // Get coordinate of first point
3086  zeta[0]=local_zeta_initial;
3087  boundary_pt->geom_object_pt()->position(zeta, start_point);
3088 
3089  delta_z[h] = 0.0;
3090 
3091  for (unsigned i=1;i<nsample;i++)
3092  {
3093  // Next point
3094  zeta[0]+=local_zeta_increment;
3095 
3096  // Get coordinate of end point
3097  boundary_pt->geom_object_pt()->position(zeta, end_point);
3098 
3099  // Increment arclength
3100  delta_z[h]+=sqrt(pow(end_point[0]-start_point[0],2)+
3101  pow(end_point[1]-start_point[1],2));
3102 
3103  // Shift back
3104  start_point=end_point;
3105  }
3106 
3107  local_n_vertices =
3108  static_cast<unsigned>(((double)n_t_vertices * delta_z[h]) /
3109  (total_arclength));
3110 
3111  // Desired arclength increment
3112  double local_target_s_increment=
3113  delta_z[h]/double(local_n_vertices+1);
3114 
3115  // Get coordinate of first point again
3116  zeta[0]=local_zeta_initial;
3117  boundary_pt->geom_object_pt()->position(zeta, start_point);
3118 
3119  // Start sampling point
3120  unsigned i_lo=1;
3121 
3122  //Loop over the n_seg-1 internal points bounding the segments
3123  for(unsigned s=0;s<local_n_vertices;s++)
3124  {
3125  // Visit potentially all sample points until we've found
3126  // the one at which we exceed the target arclength increment
3127  double local_arclength_increment=0.0;
3128  for (unsigned i=i_lo;i<nsample;i++)
3129  //for (unsigned i=i_lo;i<nsample_per_segment;i++)
3130  {
3131  // Next point
3132  zeta[0]+=local_zeta_increment;
3133 
3134  // Get coordinate of end point
3135  boundary_pt->geom_object_pt()->position(zeta, end_point);
3136 
3137  // Increment arclength increment
3138  local_arclength_increment+=
3139  sqrt(pow(end_point[0]-start_point[0],2)+
3140  pow(end_point[1]-start_point[1],2));
3141 
3142  // Shift back
3143  start_point=end_point;
3144 
3145  // Are we there yet?
3146  if (local_arclength_increment>local_target_s_increment)
3147  {
3148  // Remember how far we've got
3149  i_lo=i;
3150 
3151  // And bail out
3152  break;
3153  }
3154  }
3155 
3156  zeta_values_pt.insert(r_it, zeta[0]);
3157  n_assigned_vertices++;
3158 
3159  }
3160  // Moving to the next segments
3161  l_it = r_it;
3162  r_it++;
3163  }
3164 
3165  // Finishing it ... !!!
3166 #ifdef PARANOID
3167  // Counting the vertices number and the total of
3168  // assigned vertices values
3169  unsigned h = zeta_values_pt.size();
3170 
3171  if (h!=n_assigned_vertices)
3172  {
3173  error_message
3174  << "The total number of assigned vertices is different from\n"
3175  << "the number of elements in the z_values list. The number of\n"
3176  << "elements in the z_values list is (" << h << ") but the number\n"
3177  << "of assigned vertices is (" << n_assigned_vertices << ")."
3178  << std::endl << std::endl;
3179  throw OomphLibError(error_message.str(),
3180  OOMPH_CURRENT_FUNCTION,
3181  OOMPH_EXCEPTION_LOCATION);
3182  }
3183 #endif // PARANOID
3184 
3185  vertex_coord.resize(n_assigned_vertices);
3186  polygonal_vertex_arclength_info.resize(n_assigned_vertices);
3187  polygonal_vertex_arclength_info[0].first=0.0;
3188  polygonal_vertex_arclength_info[0].second=zeta_initial;
3189 
3190  // Creating the vertices with the corresponding z_values
3191  l_it = zeta_values_pt.begin();
3192  for (unsigned s = 0; l_it!=zeta_values_pt.end(); s++,l_it++)
3193  {
3194  // Get the coordinates
3195  zeta[0]= *l_it;
3196  boundary_pt->geom_object_pt()->position(zeta, posn);
3197  vertex_coord[s] = posn;
3198 
3199  // Bump up the polygonal arclength
3200  if (s>0)
3201  {
3202  polygonal_vertex_arclength_info[s].first=
3203  polygonal_vertex_arclength_info[s-1].first+
3204  sqrt(pow(vertex_coord[s][0]-vertex_coord[s-1][0],2)+
3205  pow(vertex_coord[s][1]-vertex_coord[s-1][1],2));
3206  polygonal_vertex_arclength_info[s].second=zeta[0];
3207  }
3208  }
3209  } // Arclength uniformly spaced
3210  } // Less number of insertion points than vertices
3211  }
3212 
3213  /// \short Helper function that returns a polygon representation for
3214  /// the given closed curve, it also computes the maximum boundary id of
3215  /// the constituent curves.
3216  /// If the TriangleMeshClosedCurve is already a TriangleMeshPolygon
3217  /// we simply return a pointer to it. Otherwise a new TrilangleMeshPolygon
3218  /// is created -- this is deleted automatically when the TriangleMesh
3219  /// destructor is called, so no external book-keeping is required.
3221  TriangleMeshClosedCurve* closed_curve_pt,unsigned &max_bnd_id_local)
3222  {
3223  // How many separate boundaries do we have
3224  unsigned nb = closed_curve_pt->ncurve_section();
3225 
3226 #ifdef PARANOID
3227  if (nb<2)
3228  {
3229  std::ostringstream error_message;
3230  error_message
3231  << "TriangleMeshClosedCurve that defines outer boundary\n"
3232  << "must be made up of at least two "
3233  << "TriangleMeshCurveSections\n"
3234  << "to allow the automatic set up boundary coordinates.\n"
3235  << "Yours only has (" << nb << ")" << std::endl;
3236  throw OomphLibError(error_message.str(),
3237  OOMPH_CURRENT_FUNCTION,
3238  OOMPH_EXCEPTION_LOCATION);
3239  }
3240 #endif
3241 
3242  // Provide storage for accompanying polylines
3243  Vector<TriangleMeshCurveSection*> my_boundary_polyline_pt(nb);
3244 
3245  // Store refinement tolerance
3246  Vector<double> refinement_tolerance(nb);
3247 
3248  // Store unrefinement tolerance
3249  Vector<double> unrefinement_tolerance(nb);
3250 
3251  // Store max. length
3252  Vector<double> max_length(nb);
3253 
3254  // Loop over boundaries that make up this boundary
3255  for (unsigned b=0;b<nb;b++)
3256  {
3257  // Get pointer to the curve segment boundary that makes up
3258  // this part of the boundary
3259  TriangleMeshCurviLine *curviline_pt =
3260  dynamic_cast<TriangleMeshCurviLine*>(
3261  closed_curve_pt->curve_section_pt(b));
3262 
3263  TriangleMeshPolyLine *polyline_pt =
3264  dynamic_cast<TriangleMeshPolyLine*>(
3265  closed_curve_pt->curve_section_pt(b));
3266 
3267  if (curviline_pt != 0)
3268  {
3269  // Boundary id
3270  unsigned bnd_id = curviline_pt->boundary_id();
3271 
3272  // Build associated polyline
3273  my_boundary_polyline_pt[b]=curviline_to_polyline(curviline_pt,bnd_id);
3274 
3275  // Copy the unrefinement tolerance
3276  unrefinement_tolerance[b] = curviline_pt->unrefinement_tolerance();
3277 
3278  // Copy the refinement tolerance
3279  refinement_tolerance[b] = curviline_pt->refinement_tolerance();
3280 
3281  // Copy the maximum length
3282  max_length[b] = curviline_pt->maximum_length();
3283 
3284  // Updates bnd_id<--->curve section map
3285  Boundary_curve_section_pt[bnd_id] = my_boundary_polyline_pt[b];
3286 
3287  // Keep track of curve sections that need to be deleted!!!
3288  Free_curve_section_pt.insert(my_boundary_polyline_pt[b]);
3289 
3290  // Keep track...
3291  if (bnd_id>max_bnd_id_local)
3292  {
3293  max_bnd_id_local=bnd_id;
3294  }
3295 
3296  }
3297  else if (polyline_pt != 0)
3298  {
3299  // Boundary id
3300  unsigned bnd_id=polyline_pt->boundary_id();
3301 
3302  // Pass the pointer of the already existing polyline
3303  my_boundary_polyline_pt[b] = polyline_pt;
3304 
3305  // Copy the unrefinement tolerance
3306  unrefinement_tolerance[b] = polyline_pt->unrefinement_tolerance();
3307 
3308  // Copy the refinement tolerance
3309  refinement_tolerance[b] = polyline_pt->refinement_tolerance();
3310 
3311  // Copy the maximum length
3312  max_length[b] = polyline_pt->maximum_length();
3313 
3314  // Updates bnd_id<--->curve section map
3315  Boundary_curve_section_pt[bnd_id] = my_boundary_polyline_pt[b];
3316 
3317  // Keep track...
3318  if (bnd_id>max_bnd_id_local)
3319  {
3320  max_bnd_id_local=bnd_id;
3321  }
3322  }
3323  else
3324  {
3325  std::ostringstream error_stream;
3326  error_stream
3327  << "The 'curve_segment' is not a curviline neither a\n "
3328  << "polyline: What is it?\n"
3329  << std::endl;
3330  throw OomphLibError(
3331  error_stream.str(),
3332  OOMPH_CURRENT_FUNCTION,
3333  OOMPH_EXCEPTION_LOCATION);
3334  }
3335 
3336  } //end of loop over boundaries
3337 
3338  // Create a new polygon by using the new created polylines
3339  TriangleMeshPolygon *output_polygon_pt=
3340  new TriangleMeshPolygon(my_boundary_polyline_pt,
3341  closed_curve_pt->internal_point(),closed_curve_pt->is_internal_point_fixed());
3342 
3343  // Keep track of new created polygons that need to be deleted!!!
3344  Free_polygon_pt.insert(output_polygon_pt);
3345 
3346  // Pass on refinement information
3347  output_polygon_pt->set_polyline_refinement_tolerance(
3348  closed_curve_pt->polyline_refinement_tolerance());
3349  output_polygon_pt->set_polyline_unrefinement_tolerance(
3350  closed_curve_pt->polyline_unrefinement_tolerance());
3351 
3352  // Loop over boundaries that make up this boundary and copy
3353  // refinement, unrefinement and max length information
3354  for (unsigned b=0;b<nb;b++)
3355  {
3356  // Set the unrefinement and refinement information
3357  my_boundary_polyline_pt[b]->
3358  set_unrefinement_tolerance(unrefinement_tolerance[b]);
3359 
3360  my_boundary_polyline_pt[b]->
3361  set_refinement_tolerance(refinement_tolerance[b]);
3362 
3363  // Copy the maximum length constraint
3364  my_boundary_polyline_pt[b]->set_maximum_length(max_length[b]);
3365  }
3366  return output_polygon_pt;
3367  }
3368 
3369  /// \short Helper function that creates and returns an open curve with
3370  /// the polyline representation of its constituent curve sections. The
3371  /// new created open curve is deleted when the TriangleMesh destructor
3372  /// is called
3374  TriangleMeshOpenCurve* open_curve_pt,unsigned &max_bnd_id_local)
3375  {
3376  unsigned nb=open_curve_pt->ncurve_section();
3377 
3378  // Provide storage for accompanying polylines
3379  Vector<TriangleMeshCurveSection*> my_boundary_polyline_pt(nb);
3380 
3381  // Store refinement tolerance
3382  Vector<double> refinement_tolerance(nb);
3383 
3384  // Store unrefinement tolerance
3385  Vector<double> unrefinement_tolerance(nb);
3386 
3387  // Store max. length
3388  Vector<double> max_length(nb);
3389 
3390  //Loop over the number of curve sections on the open curve
3391  for (unsigned i = 0; i < nb; i++)
3392  {
3393  // Get pointer to the curve segment boundary that makes up
3394  // this part of the boundary
3395  TriangleMeshCurviLine *curviline_pt =
3396  dynamic_cast<TriangleMeshCurviLine*>(
3397  open_curve_pt->curve_section_pt(i));
3398  TriangleMeshPolyLine *polyline_pt =
3399  dynamic_cast<TriangleMeshPolyLine*>(
3400  open_curve_pt->curve_section_pt(i));
3401 
3402  if (curviline_pt != 0)
3403  {
3404  // Boundary id
3405  unsigned bnd_id = curviline_pt->boundary_id();
3406 
3407  // Build associated polyline
3408  my_boundary_polyline_pt[i]=curviline_to_polyline(curviline_pt,bnd_id);
3409 
3410  // Copy the unrefinement tolerance
3411  unrefinement_tolerance[i] = curviline_pt->unrefinement_tolerance();
3412 
3413  // Copy the refinement tolerance
3414  refinement_tolerance[i] = curviline_pt->refinement_tolerance();
3415 
3416  // Copy the maximum length
3417  max_length[i] = curviline_pt->maximum_length();
3418 
3419  // Pass the connection information to the polyline representation
3420  copy_connection_information(curviline_pt,my_boundary_polyline_pt[i]);
3421 
3422  // Updates bnd_id<--->curve section map
3423  Boundary_curve_section_pt[bnd_id] = my_boundary_polyline_pt[i];
3424 
3425  // Keep track of curve sections that need to be deleted!!!
3426  Free_curve_section_pt.insert(my_boundary_polyline_pt[i]);
3427 
3428  // Keep track...
3429  if (bnd_id>max_bnd_id_local)
3430  {
3431  max_bnd_id_local=bnd_id;
3432  }
3433  }
3434  else if (polyline_pt != 0)
3435  {
3436  // Boundary id
3437  unsigned bnd_id=polyline_pt->boundary_id();
3438 
3439  // Storage pointer
3440  my_boundary_polyline_pt[i] = polyline_pt;
3441 
3442  // Copy the unrefinement tolerance
3443  unrefinement_tolerance[i] = polyline_pt->unrefinement_tolerance();
3444 
3445  // Copy the refinement tolerance
3446  refinement_tolerance[i] = polyline_pt->refinement_tolerance();
3447 
3448  // Copy the maximum length
3449  max_length[i] = polyline_pt->maximum_length();
3450 
3451  // Pass the connection information to the polyline representation
3452  copy_connection_information(polyline_pt, my_boundary_polyline_pt[i]);
3453 
3454  // Updates bnd_id<--->curve section map
3455  Boundary_curve_section_pt[bnd_id] = my_boundary_polyline_pt[i];
3456 
3457  // Keep track...
3458  if (bnd_id>max_bnd_id_local)
3459  {
3460  max_bnd_id_local=bnd_id;
3461  }
3462  }
3463  else
3464  {
3465  std::ostringstream error_stream;
3466  error_stream
3467  << "The 'curve_segment' (open) is not a curviline neither a\n "
3468  << "polyline: What is it?\n"
3469  << std::endl;
3470  throw OomphLibError(error_stream.str(),
3471  OOMPH_CURRENT_FUNCTION,
3472  OOMPH_EXCEPTION_LOCATION);
3473  }
3474  } // end of loop over boundaries
3475 
3476  // Create open curve with polylines boundaries
3477  TriangleMeshOpenCurve *output_open_polyline_pt =
3478  new TriangleMeshOpenCurve(my_boundary_polyline_pt);
3479 
3480  // Keep track of open polylines that need to be deleted!!!
3481  Free_open_curve_pt.insert(output_open_polyline_pt);
3482 
3483  // Pass on refinement information
3484  output_open_polyline_pt->set_polyline_refinement_tolerance(
3485  open_curve_pt->polyline_refinement_tolerance());
3486  output_open_polyline_pt->set_polyline_unrefinement_tolerance(
3487  open_curve_pt->polyline_unrefinement_tolerance());
3488 
3489  // Loop over boundaries that make up this boundary and copy
3490  // refinement, unrefinement and max length information
3491  for (unsigned b=0;b<nb;b++)
3492  {
3493  // Set the unrefinement and refinement information
3494  my_boundary_polyline_pt[b]->
3495  set_unrefinement_tolerance(unrefinement_tolerance[b]);
3496 
3497  my_boundary_polyline_pt[b]->
3498  set_refinement_tolerance(refinement_tolerance[b]);
3499 
3500  // Copy the maximum length constraint
3501  my_boundary_polyline_pt[b]->set_maximum_length(max_length[b]);
3502  }
3503  return output_open_polyline_pt;
3504  }
3505 
3506  /// \short Stores the geometric objects associated to the
3507  /// curve sections that compound the closed curve. It also
3508  /// stores the limits defined by these geometric objects
3510  TriangleMeshClosedCurve* input_closed_curve_pt)
3511  {
3512  unsigned nb=input_closed_curve_pt->ncurve_section();
3513 
3514 #ifdef PARANOID
3515 
3516  if (nb<2)
3517  {
3518  std::ostringstream error_message;
3519  error_message
3520  << "TriangleMeshCurve that defines closed boundary\n"
3521  << "must be made up of at least two "
3522  << "TriangleMeshCurveSection\n"
3523  << "to allow the automatic set up boundary coordinates.\n"
3524  << "Yours only has " << nb << std::endl;
3525  throw OomphLibError(error_message.str(),
3526  OOMPH_CURRENT_FUNCTION,
3527  OOMPH_EXCEPTION_LOCATION);
3528  }
3529 
3530 #endif
3531 
3532  // TODO: Used for the ImmersedRigidBodyTriangleMeshPolygon objects only
3533  //ImmersedRigidBodyTriangleMeshPolygon* bound_geom_obj_pt
3534  //= dynamic_cast<ImmersedRigidBodyTriangleMeshPolygon*>
3535  // (input_closed_curve_pt);
3536  GeomObject* bound_geom_obj_pt=
3537  dynamic_cast<GeomObject*>(input_closed_curve_pt);
3538 
3539  // If cast successful set up the coordinates
3540  if (bound_geom_obj_pt!=0)
3541  {
3542  unsigned n_poly=input_closed_curve_pt->ncurve_section();
3543  for(unsigned p=0;p<n_poly;p++)
3544  {
3545  // Read out the index of the boundary from the polyline
3546  unsigned b_index=input_closed_curve_pt->curve_section_pt(p)->boundary_id();
3547 
3548  // Set the geometric object
3549  Boundary_geom_object_pt[b_index] = bound_geom_obj_pt;
3550 
3551  // The coordinates along each polyline boundary are scaled to
3552  // of unit length so the total coordinate limits are simply
3553  // (p,p+1)
3554  Boundary_coordinate_limits[b_index].resize(2);
3555  Boundary_coordinate_limits[b_index][0] = p;
3556  Boundary_coordinate_limits[b_index][1] = p + 1.0;
3557  }
3558 
3559 #ifdef PARANOID
3560  // If we are using parallel mesh adaptation and load balancing,
3561  // we are going to need to check for the use of this type of
3562  // Polygon at this stage, so switch on the flag
3563  Immersed_rigid_body_triangle_mesh_polygon_used = true;
3564 #endif
3565  }
3566  else
3567  {
3568  // Loop over curve sections that make up this boundary
3569  for (unsigned b=0;b<nb;b++)
3570  {
3571  TriangleMeshCurviLine* curviline_pt =
3572  dynamic_cast<TriangleMeshCurviLine*>
3573  (input_closed_curve_pt->curve_section_pt(b));
3574 
3575  if (curviline_pt != 0)
3576  {
3577  // Read the values of the limiting coordinates
3578  Vector<double> zeta_bound(2);
3579  zeta_bound[0] = curviline_pt->zeta_start();
3580  zeta_bound[1] = curviline_pt->zeta_end();
3581 
3582  // Boundary id
3583  unsigned bnd_id=curviline_pt->boundary_id();
3584 
3585  // Set the boundary geometric object and limits
3586  Boundary_geom_object_pt[bnd_id] =
3587  curviline_pt->geom_object_pt();
3588  Boundary_coordinate_limits[bnd_id] = zeta_bound;
3589  }
3590  } // for
3591  } // else
3592  } // function
3593 
3594  /// \short Stores the geometric objects associated to the
3595  /// curve sections that compound the open curve. It also
3596  /// stores the limits defined by these geometric objects
3598  TriangleMeshOpenCurve* input_open_curve_pt)
3599  {
3600  unsigned nb=input_open_curve_pt->ncurve_section();
3601 
3602  // Loop over curve sections that make up this boundary
3603  for (unsigned b=0;b<nb;b++)
3604  {
3605  TriangleMeshCurviLine* curviline_pt =
3606  dynamic_cast<TriangleMeshCurviLine*>
3607  (input_open_curve_pt->curve_section_pt(b));
3608 
3609  if (curviline_pt != 0)
3610  {
3611  // ead the values of the limiting coordinates
3612  Vector<double> zeta_bound(2);
3613  zeta_bound[0] = curviline_pt->zeta_start();
3614  zeta_bound[1] = curviline_pt->zeta_end();
3615 
3616  // Boundary id
3617  unsigned bnd_id=curviline_pt->boundary_id();
3618 
3619  // Set the boundary geometric object and limits
3620  Boundary_geom_object_pt[bnd_id] =
3621  curviline_pt->geom_object_pt();
3622  Boundary_coordinate_limits[bnd_id] = zeta_bound;
3623  }
3624  } // for
3625  } // function
3626 
3627 #endif // OOMPH_HAS_TRIANGLE_LIB
3628 
3629  private:
3630 
3631 #ifdef OOMPH_HAS_TRIANGLE_LIB
3632 
3633  /// \short Helper function that creates the associated polyline
3634  /// representation for curvilines
3636  TriangleMeshCurviLine* &curviline_pt,unsigned &bnd_id)
3637  {
3638  // Create vertex coordinates for polygonal representation
3639  Vector<Vector<double> > bound;
3640  Vector<std::pair<double,double> > polygonal_vertex_arclength;
3641 
3642  if (curviline_pt->are_there_connection_points())
3643  {
3644  this->create_vertex_coordinates_for_polyline_connections(
3645  curviline_pt,bound,polygonal_vertex_arclength);
3646  }
3647  else
3648  {
3649  this->create_vertex_coordinates_for_polyline_no_connections(
3650  curviline_pt,bound,polygonal_vertex_arclength);
3651  }
3652 
3653  // Store the vertex-arclength information
3654  Polygonal_vertex_arclength_info[bnd_id]=polygonal_vertex_arclength;
3655 
3656  // Build associated polyline
3657  return new TriangleMeshPolyLine(bound, bnd_id);
3658  }
3659 
3660  /// \short Get the associated vertex to the given s value by looking to
3661  /// the list of s values created when changing from curviline to polyline
3662  unsigned get_associated_vertex_to_svalue(double &target_s_value,
3663  unsigned &bnd_id)
3664  {
3665  double s_tolerance=1.0e-14;
3666  return get_associated_vertex_to_svalue(target_s_value,bnd_id,s_tolerance);
3667  }
3668 
3669  /// \short Get the associated vertex to the given s value by looking to
3670  /// the list of s values created when changing from curviline to polyline
3671  unsigned get_associated_vertex_to_svalue(double &target_s_value,
3672  unsigned &bnd_id,
3673  double &s_tolerance)
3674  {
3675  // Create a pointer to the list of s coordinates and arclength values
3676  // associated with a vertex
3677  Vector<std::pair<double,double> >* vertex_info=
3678  &Polygonal_vertex_arclength_info[bnd_id];
3679 
3680  // Total vertex number
3681  unsigned vector_size = vertex_info->size();
3682 
3683  // Counter for current vertex number
3684  unsigned n_vertex = 0;
3685 
3686  // Find the associated value to the given s value
3687  do
3688  {
3689  // Store the current zeta value
3690  double s = (*vertex_info)[n_vertex].second;
3691 
3692  // When find it save the vertex number and return it
3693  if (std::fabs(s - target_s_value) < s_tolerance)
3694  {
3695  break;
3696  }
3697 
3698  // Increment n_vertex
3699  n_vertex++;
3700  }
3701  while(n_vertex < vector_size);
3702 
3703 #ifdef PARANOID
3704 
3705  if (n_vertex >= vector_size)
3706  {
3707  std::ostringstream error_message;
3708  error_message
3709  << "Could not find the associated vertex number in\n"
3710  << "boundary " << bnd_id << " with the given s\n"
3711  << "connection value (" << target_s_value << ") using\n"
3712  << "this tolerance: " << s_tolerance << std::endl;
3713  throw OomphLibError(error_message.str(),
3714  OOMPH_CURRENT_FUNCTION,
3715  OOMPH_EXCEPTION_LOCATION);
3716  }
3717 
3718 #endif
3719  return n_vertex;
3720  }
3721 
3722 #endif // OOMPH_HAS_TRIANGLE_LIB
3723 
3724  };
3725 
3726 
3727  //======================================================================
3728  /// Setup boundary coordinate on boundary b. Doc Faces
3729  /// in outfile. Boundary coordinate increases continously along
3730  /// polygonal boundary. It's zero at the lexicographically
3731  /// smallest node on the boundary.
3732  //======================================================================
3733  template<class ELEMENT>
3735  setup_boundary_coordinates(const unsigned& b,std::ofstream& outfile)
3736  {
3737  // Temporary storage for face elements
3738  Vector<FiniteElement*> face_el_pt;
3739 
3740  // Temporary storage for number of elements adjacent to the boundary
3741  unsigned nel = 0;
3742 
3743  // =================================================================
3744  // BEGIN: Get face elements from boundary elements
3745  // =================================================================
3746 
3747  // Temporary storage for elements adjacent to the boundary that have
3748  // an common edge (related with internal boundaries)
3749  unsigned n_repeated_ele = 0;
3750 
3751  unsigned n_regions = this->nregion();
3752 
3753 #ifdef OOMPH_HAS_MPI
3754  // map to associate the face element to the bulk element, this info.
3755  // is only necessary for the setup of boundary coordinates in a
3756  // distributed mesh when we need to extract the halo/haloed info.
3757  std::map<FiniteElement*,FiniteElement*> face_to_bulk_element_pt;
3758 #endif
3759 
3760  // Temporary storage for already done nodes
3761  Vector < std::pair<Node*, Node *> > done_nodes_pt;
3762 
3763  //If there is more than one region then only use boundary
3764  //coordinates from the bulk side (region 0)
3765  if (n_regions > 1)
3766  {
3767  for (unsigned rr = 0 ; rr < n_regions; rr++)
3768  {
3769  unsigned region_id = static_cast<unsigned>(this->Region_attribute[rr]);
3770 
3771 #ifdef PARANOID
3772  double diff=fabs(Region_attribute[rr]-
3773  static_cast<double>(static_cast<unsigned>(
3774  this->Region_attribute[rr])));
3775  if (diff>0.0)
3776  {
3777  std::ostringstream error_message;
3778  error_message
3779  << "Region attributes should be unsigneds because we \n"
3780  << "only use them to set region ids\n";
3781  throw OomphLibError(error_message.str(),
3782  OOMPH_CURRENT_FUNCTION,
3783  OOMPH_EXCEPTION_LOCATION);
3784  }
3785 #endif
3786 
3787  // Loop over all elements on boundaries in region rr
3788  unsigned nel_in_region =
3789  this->nboundary_element_in_region(b,region_id);
3790  unsigned nel_repeated_in_region = 0;
3791 
3792 #ifdef PARANOID
3793  if (!Suppress_warning_about_regions_and_boundaries)
3794  {
3795  if (nel_in_region==0)
3796  {
3797  std::ostringstream warning_message;
3798  std::string output_string=
3799  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
3800  warning_message
3801  << "There are no elements associated with boundary (" << b << ")\n"
3802  << "in region (" << region_id << "). This could happen because:\n"
3803  << "1) You did not specify boundaries with this boundary id.\n"
3804  << "---- Review carefully the indexing of your boundaries.\n"
3805  << "2) The boundary (" << b << ") is not associated with region ("
3806  << region_id << ").\n"
3807  << "---- The boundary does not touch the region.\n"
3808  << "You can suppress this warning by setting the static public bool\n\n"
3809  << " UnstructuredTwoDMeshGeometryBase::Suppress_warning_about_regions_and_boundaries\n\n"
3810  << "to true.\n";
3811  OomphLibWarning(warning_message.str(),
3812  output_string,
3813  OOMPH_EXCEPTION_LOCATION);
3814  }
3815  }
3816 #endif
3817 
3818  // Only bother to do anything else, if there are elements
3819  // associated with the boundary and the current region
3820  if (nel_in_region > 0)
3821  {
3822  // Flag that activates when a repeated face element is found,
3823  // possibly because we are dealing with an internal boundary
3824  bool repeated = false;
3825 
3826  // Loop over the bulk elements adjacent to boundary b
3827  for (unsigned e = 0; e < nel_in_region; e++)
3828  {
3829  // Get pointer to the bulk element that is adjacent to boundary b
3830  FiniteElement* bulk_elem_pt =
3831  this->boundary_element_in_region_pt(b, region_id, e);
3832 
3833 #ifdef OOMPH_HAS_MPI
3834  // In a distributed mesh only work with nonhalo elements
3835  if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
3836  {
3837  // Increase the number of repeated elements
3838  n_repeated_ele++;
3839  // Skip this element and go for the next one
3840  continue;
3841  }
3842 #endif
3843 
3844  //Find the index of the face of element e along boundary b
3845  int face_index =
3846  this->face_index_at_boundary_in_region(b, region_id, e);
3847 
3848  // Before adding the new element we need to be sure that
3849  // the edge that this element represent has not been
3850  // already added
3851  FiniteElement* tmp_ele_pt = new DummyFaceElement<ELEMENT> (
3852  bulk_elem_pt, face_index);
3853 
3854  const unsigned n_nodes = tmp_ele_pt->nnode();
3855 
3856  std::pair<Node*, Node*> tmp_pair =
3857  std::make_pair(tmp_ele_pt->node_pt(0),
3858  tmp_ele_pt->node_pt(n_nodes - 1));
3859 
3860  std::pair<Node*, Node*> tmp_pair_inverse =
3861  std::make_pair(tmp_ele_pt->node_pt(n_nodes - 1),
3862  tmp_ele_pt->node_pt(0));
3863 
3864  // Search for repeated nodes
3865  const unsigned n_done_nodes = done_nodes_pt.size();
3866  for (unsigned l = 0; l < n_done_nodes; l++)
3867  {
3868  if (tmp_pair == done_nodes_pt[l] || tmp_pair_inverse
3869  == done_nodes_pt[l])
3870  {
3871  nel_repeated_in_region++;
3872  repeated = true;
3873  break;
3874  }
3875  }
3876 
3877  // Create new face element?
3878  if (!repeated)
3879  {
3880  // Add the pair of nodes (edge) to the node dones
3881  done_nodes_pt.push_back(tmp_pair);
3882 #ifdef OOMPH_HAS_MPI
3883  // If the mesh is distributed then create a map from the
3884  // temporary face element to the bulk element, further
3885  // info. will be extracted from the bulk element for setup
3886  // of boundary coordinates in a distributed mesh
3887  if (this->is_mesh_distributed())
3888  {
3889  face_to_bulk_element_pt[tmp_ele_pt] = bulk_elem_pt;
3890  }
3891 #endif
3892  // Add the face element to the storage
3893  face_el_pt.push_back(tmp_ele_pt);
3894  }
3895  else
3896  {
3897  // Clean up
3898  delete tmp_ele_pt;
3899  tmp_ele_pt = 0;
3900  }
3901 
3902  // Re-start
3903  repeated = false;
3904 
3905  // Output faces?
3906  if (outfile.is_open())
3907  {
3908  face_el_pt[face_el_pt.size() - 1]->output(outfile);
3909  }
3910  } // for nel
3911 
3912  nel += nel_in_region;
3913 
3914  n_repeated_ele += nel_repeated_in_region;
3915 
3916  } // if (nel_in_region > 0)
3917 
3918  } // for (rr < n_regions)
3919 
3920  } // if (n_regions > 1)
3921  //Otherwise it's just the normal boundary functions
3922  else
3923  {
3924  // Loop over all elements on boundaries
3925  nel = this->nboundary_element(b);
3926 
3927 #ifdef PARANOID
3928  if (!Suppress_warning_about_regions_and_boundaries)
3929  {
3930  if (nel==0)
3931  {
3932  std::ostringstream warning_message;
3933  std::string output_string=
3934  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
3935  warning_message
3936  << "There are no elements associated with boundary (" << b << ").\n"
3937  << "This could happen because you did not specify boundaries with\n"
3938  << "this boundary id. Review carefully the indexing of your\n"
3939  << "boundaries.";
3940  OomphLibWarning(warning_message.str(),
3941  output_string,
3942  OOMPH_EXCEPTION_LOCATION);
3943  }
3944  }
3945 #endif
3946 
3947  //Only bother to do anything else, if there are elements
3948  if (nel > 0)
3949  {
3950  // Flag that activates when a repeated face element is found,
3951  // possibly because we are dealing with an internal boundary
3952  bool repeated = false;
3953 
3954  // Loop over the bulk elements adjacent to boundary b
3955  for (unsigned e = 0; e < nel; e++)
3956  {
3957  // Get pointer to the bulk element that is adjacent to boundary b
3958  FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
3959 
3960 #ifdef OOMPH_HAS_MPI
3961 
3962  // In a distributed mesh only work with nonhalo elements
3963  if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
3964  {
3965  // Increase the number of repeated elements
3966  n_repeated_ele++;
3967  // Skip this element and go for the next one
3968  continue;
3969  }
3970 
3971 #endif
3972 
3973  // Find the index of the face of element e along boundary b
3974  int face_index = this->face_index_at_boundary(b, e);
3975 
3976  // Before adding the new element we need to be sure that the
3977  // edge that this element represent has not been already added
3978  FiniteElement* tmp_ele_pt = new DummyFaceElement<ELEMENT> (
3979  bulk_elem_pt, face_index);
3980 
3981  const unsigned n_nodes = tmp_ele_pt->nnode();
3982 
3983  std::pair<Node*, Node*> tmp_pair =
3984  std::make_pair(tmp_ele_pt->node_pt(0),
3985  tmp_ele_pt->node_pt(n_nodes - 1));
3986 
3987  std::pair<Node*, Node*> tmp_pair_inverse =
3988  std::make_pair(tmp_ele_pt->node_pt(n_nodes - 1),
3989  tmp_ele_pt->node_pt(0));
3990 
3991  // Search for repeated nodes
3992  const unsigned n_done_nodes = done_nodes_pt.size();
3993  for (unsigned l = 0; l < n_done_nodes; l++)
3994  {
3995  if (tmp_pair == done_nodes_pt[l] || tmp_pair_inverse
3996  == done_nodes_pt[l])
3997  {
3998  n_repeated_ele++;
3999  repeated = true;
4000  break;
4001  }
4002  }
4003 
4004  // Create new face element
4005  if (!repeated)
4006  {
4007  // Add the pair of nodes (edge) to the node dones
4008  done_nodes_pt.push_back(tmp_pair);
4009 #ifdef OOMPH_HAS_MPI
4010  // Create a map from the temporary face element to the bulk
4011  // element, further info. will be extracted from the bulk
4012  // element for setup of boundary coordinates in a
4013  // distributed mesh
4014  if (this->is_mesh_distributed())
4015  {
4016  face_to_bulk_element_pt[tmp_ele_pt] = bulk_elem_pt;
4017  }
4018 #endif
4019  face_el_pt.push_back(tmp_ele_pt);
4020  }
4021  else
4022  {
4023  // Free the repeated bulk element!!
4024  delete tmp_ele_pt;
4025  tmp_ele_pt = 0;
4026  }
4027 
4028  // Re-start
4029  repeated = false;
4030 
4031  // Output faces?
4032  if (outfile.is_open())
4033  {
4034  face_el_pt[face_el_pt.size() - 1]->output(outfile);
4035  }
4036 
4037  } // for (e < nel)
4038 
4039  } // if (nel > 0)
4040 
4041  } // else if (n_regions > 1)
4042 
4043  // Do not consider the repeated elements
4044  nel -= n_repeated_ele;
4045 
4046 #ifdef PARANOID
4047  if (nel!=face_el_pt.size())
4048  {
4049  std::ostringstream error_message;
4050  error_message
4051  << "The independent counting of face elements (" << nel << ") for "
4052  << "boundary (" << b << ") is different\n"
4053  << "from the real number of face elements in the container ("
4054  << face_el_pt.size() << ")\n";
4055  throw OomphLibError(error_message.str(),
4056  OOMPH_CURRENT_FUNCTION,
4057  OOMPH_EXCEPTION_LOCATION);
4058  }
4059 #endif
4060 
4061  // =================================================================
4062  // END: Get face elements from boundary elements
4063  // =================================================================
4064 
4065  //Only bother to do anything else, if there are elements
4066  if (nel > 0)
4067  {
4068  // A flag vector to mark those face elements that are considered
4069  // as halo in the current processor
4070  std::vector<bool> is_halo_face_element(nel,false);
4071 
4072  // Count the total number of non halo face elements
4073  unsigned nnon_halo_face_elements = 0;
4074 
4075 #ifdef OOMPH_HAS_MPI
4076  // Only mark the face elements as halo if the mesh is marked as
4077  // distributed
4078  if (this->is_mesh_distributed())
4079  {
4080  for (unsigned ie = 0; ie < nel; ie++)
4081  {
4082  FiniteElement* face_ele_pt = face_el_pt[ie];
4083  // Get the bulk element
4084  FiniteElement* tmp_bulk_ele_pt =
4085  face_to_bulk_element_pt[face_ele_pt];
4086  // Check if the bulk element is halo
4087  if (!tmp_bulk_ele_pt->is_halo())
4088  {
4089  // Mark the face element as nonhalo
4090  is_halo_face_element[ie] = false;
4091  // Increase the counter for nonhalo elements
4092  nnon_halo_face_elements++;
4093  }
4094  else
4095  {
4096  // Mark the face element as halo
4097  is_halo_face_element[ie] = true;
4098  }
4099  } // for (ie < nel)
4100  } // if (this->is_mesh_distributed())
4101  else
4102  {
4103 #endif // OOMPH_HAS_MPI
4104 
4105  // If the mesh is not distributed then the number of non halo
4106  // elements is the same as the number of elements
4107  nnon_halo_face_elements = nel;
4108 
4109 #ifdef OOMPH_HAS_MPI
4110  } // else if (this->is_mesh_distributed())
4111 #endif
4112 
4113 #ifdef PARANOID
4114  // Get the total number of halo face elements
4115  const unsigned nhalo_face_element = nel - nnon_halo_face_elements;
4116 
4117  if (nhalo_face_element > 0)
4118  {
4119  std::ostringstream error_message;
4120  error_message
4121  << "There should not be halo face elements since they were not\n"
4122  << "considered when computing the face elements.\n"
4123  << "The number of found halo face elements is: ("
4124  << nhalo_face_element << ").\n\n";
4125  throw OomphLibError(error_message.str(),
4126  OOMPH_CURRENT_FUNCTION,
4127  OOMPH_EXCEPTION_LOCATION);
4128  }
4129 #endif
4130 
4131  // =================================================================
4132  // BEGIN: Sort face elements
4133  // =================================================================
4134 
4135  // The vector of lists to store the "segments" that compound the
4136  // boundaries (segments may appear only in a distributed mesh
4137  // because the boundary may have been split across multiple
4138  // processors)
4139  Vector<std::list<FiniteElement*> > segment_sorted_ele_pt;
4140 
4141  // Number of already sorted face elements (only nonhalo face
4142  // elements for a distributed mesh)
4143  unsigned nsorted_face_elements = 0;
4144 
4145  // Keep track of who's done (in a distributed mesh this apply to
4146  // nonhalo only)
4147  std::map<FiniteElement*, bool> done_el;
4148 
4149  // Keep track of which element is inverted (in distributed mesh
4150  // the elements may be inverted with respect to the segment they
4151  // belong)
4152  std::map<FiniteElement*, bool> is_inverted;
4153 
4154  // Iterate until all possible segments have been created. In a
4155  // non distributed mesh there is only one segment which defines
4156  // the complete boundary
4157  while(nsorted_face_elements < nnon_halo_face_elements)
4158  {
4159  // The sorted list of face elements (in a distributed mesh a
4160  // collection of continuous face elements define a segment)
4161  std::list<FiniteElement*> sorted_el_pt;
4162 
4163  FiniteElement* ele_face_pt = 0;
4164 
4165 #ifdef PARANOID
4166  // Select an initial element for the segment
4167  bool found_initial_face_element = false;
4168 #endif
4169 
4170  // Store the index of the initial face element
4171  unsigned iface = 0;
4172 #ifdef OOMPH_HAS_MPI
4173  if (this->is_mesh_distributed())
4174  {
4175  for (iface = 0; iface < nel; iface++)
4176  {
4177  // Only work with nonhalo face elements
4178  if (!is_halo_face_element[iface])
4179  {
4180  ele_face_pt = face_el_pt[iface];
4181  // If not done then take it as initial face element
4182  if (!done_el[ele_face_pt])
4183  {
4184 #ifdef PARANOID
4185  // Set the flag to indicate the initial element was
4186  // found
4187  found_initial_face_element = true;
4188 #endif
4189  // Increase the number of sorted face elements
4190  nsorted_face_elements++;
4191  // Set the index to the next face element
4192  iface++;
4193  // Add the face element in the container
4194  sorted_el_pt.push_back(ele_face_pt);
4195  // Mark as done
4196  done_el[ele_face_pt] = true;
4197  break;
4198  } // if (!done_el[ele_face_pt])
4199  } // if (!is_halo_face_element[iface])
4200  } // for (iface < nel)
4201  } // if (this->is_mesh_distributed())
4202  else
4203  {
4204 #endif // #ifdef OOMPH_HAS_MPI
4205 
4206  // When the mesh is not distributed just take the first
4207  // element and put it in the sorted list
4208  ele_face_pt = face_el_pt[0];
4209 #ifdef PARANOID
4210  // Set the flag to indicate the initial element was found
4211  found_initial_face_element = true;
4212 #endif
4213  // Increase the number of sorted face elements
4214  nsorted_face_elements++;
4215  // Set the index to the next face element
4216  iface = 1;
4217  // Add the face element in the container
4218  sorted_el_pt.push_back(ele_face_pt);
4219  // Mark as done
4220  done_el[ele_face_pt] = true;
4221 
4222 #ifdef OOMPH_HAS_MPI
4223  } // else if (this->is_mesh_distributed())
4224 #endif
4225 
4226 #ifdef PARANOID
4227  if (!found_initial_face_element)
4228  {
4229  std::ostringstream error_message;
4230  error_message
4231  <<"Could not find an initial face element for the current segment\n";
4232  throw OomphLibError(error_message.str(),
4233  OOMPH_CURRENT_FUNCTION,
4234  OOMPH_EXCEPTION_LOCATION);
4235  }
4236 #endif
4237 
4238  // Number of nodes of the initial face element
4239  const unsigned nnod = ele_face_pt->nnode();
4240 
4241  // Left and rightmost nodes (the left and right nodes of the
4242  // current face element)
4243  Node* left_node_pt = ele_face_pt->node_pt(0);
4244  Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
4245 
4246  // Continue iterating if a new face element has been added to
4247  // the list
4248  bool face_element_added = false;
4249 
4250  // While a new face element has been added to the set of sorted
4251  // face elements continue iterating
4252  do
4253  {
4254  // Start from the next face element since we have already
4255  // added the previous one as the initial face element (any
4256  // previous face element had to be added on previous
4257  // iterations)
4258  for (unsigned iiface=iface;iiface<nel;iiface++)
4259  {
4260  // Re-start flag
4261  face_element_added = false;
4262 
4263  // Get the candidate element
4264  ele_face_pt = face_el_pt[iiface];
4265 
4266  // Check that the candidate element has not been done and
4267  // is not a halo element
4268  if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
4269  {
4270  // Get the left and right nodes of the current element
4271  Node* local_left_node_pt = ele_face_pt->node_pt(0);
4272  Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
4273 
4274  // New element fits at the left of segment and is not inverted
4275  if (left_node_pt == local_right_node_pt)
4276  {
4277  left_node_pt = local_left_node_pt;
4278  sorted_el_pt.push_front(ele_face_pt);
4279  is_inverted[ele_face_pt] = false;
4280  face_element_added = true;
4281  }
4282  // New element fits at the left of segment and is inverted
4283  else if (left_node_pt == local_left_node_pt)
4284  {
4285  left_node_pt = local_right_node_pt;
4286  sorted_el_pt.push_front(ele_face_pt);
4287  is_inverted[ele_face_pt] = true;
4288  face_element_added = true;
4289  }
4290  // New element fits on the right of segment and is not inverted
4291  else if (right_node_pt == local_left_node_pt)
4292  {
4293  right_node_pt = local_right_node_pt;
4294  sorted_el_pt.push_back(ele_face_pt);
4295  is_inverted[ele_face_pt] = false;
4296  face_element_added = true;
4297  }
4298  // New element fits on the right of segment and is inverted
4299  else if (right_node_pt == local_right_node_pt)
4300  {
4301  right_node_pt = local_left_node_pt;
4302  sorted_el_pt.push_back(ele_face_pt);
4303  is_inverted[ele_face_pt] = true;
4304  face_element_added = true;
4305  }
4306 
4307  if (face_element_added)
4308  {
4309  done_el[ele_face_pt] = true;
4310  nsorted_face_elements++;
4311  break;
4312  }
4313 
4314  } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
4315  } // for (iiface<nnon_halo_face_element)
4316  }while(face_element_added &&
4317  (nsorted_face_elements < nnon_halo_face_elements));
4318 
4319  // Store the created segment in the vector of segments
4320  segment_sorted_ele_pt.push_back(sorted_el_pt);
4321 
4322  } // while(nsorted_face_elements < nnon_halo_face_elements);
4323 
4324 #ifdef OOMPH_HAS_MPI
4325  if (!this->is_mesh_distributed())
4326  {
4327 #endif
4328  // Are we done?
4329  if (nsorted_face_elements!=nel||segment_sorted_ele_pt.size()!=1)
4330  {
4331  std::ostringstream error_message;
4332  error_message << "Was only able to setup boundary coordinate on "
4333  << "boundary " << b << "\nfor "<< nsorted_face_elements
4334  << " of "<< nel << " face elements. This usually means\n"
4335  << "that the boundary is not simply connected.\n\n"
4336  << "Re-run the setup_boundary_coordintes() function\n"
4337  << "with an output file specified "
4338  << "as the second argument.\n"
4339  << "This file will contain FaceElements that\n"
4340  << "oomph-lib believes to be located on the boundary.\n"
4341  << std::endl;
4342  throw OomphLibError(error_message.str(),
4343  OOMPH_CURRENT_FUNCTION,
4344  OOMPH_EXCEPTION_LOCATION);
4345  }
4346 #ifdef OOMPH_HAS_MPI
4347  } // if (!this->is_mesh_distributed())
4348 #endif
4349 
4350  // =================================================================
4351  // END: Sort face elements
4352  // =================================================================
4353 
4354  // ----------------------------------------------------------------
4355 
4356  // =================================================================
4357  // BEGIN: Assign global/local (non distributed mesh/distributed
4358  // mesh) boundary coordinates to nodes
4359  // =================================================================
4360 
4361  // Compute the (local) boundary coordinates of the nodes in the
4362  // segments. In a distributed mesh this info. will be used by a
4363  // root processor to compute the (global) boundary coordinates.
4364 
4365  // Vector of sets that stores the nodes of each segment based on
4366  // a lexicographically order starting from the bottom left node
4367  // of each segment
4368  Vector<std::set<Node*> > segment_all_nodes_pt;
4369 
4370  // The number of segments in this processor
4371  const unsigned nsegments = segment_sorted_ele_pt.size();
4372 
4373 #ifdef PARANOID
4374  if (nnon_halo_face_elements > 0 && nsegments == 0)
4375  {
4376  std::ostringstream error_message;
4377  error_message
4378  << "The number of segments is zero, but the number of nonhalo\n"
4379  << "elements is: (" << nnon_halo_face_elements << ")\n";
4380  throw OomphLibError(error_message.str(),
4381  OOMPH_CURRENT_FUNCTION,
4382  OOMPH_EXCEPTION_LOCATION);
4383  } // if (nnon_halo_face_elements > 0 && nsegments == 0)
4384 
4385 #endif
4386 
4387  // Store the arclength of each segment in the current processor
4388  Vector<double> segment_arclength(nsegments);
4389 
4390 #ifdef OOMPH_HAS_MPI
4391 
4392  // Clear the boundary segment nodes storage
4393  this->flush_boundary_segment_node(b);
4394 
4395  // Set the number of segments for the current boundary
4396  this->set_nboundary_segment_node(b,nsegments);
4397 
4398 #endif // #ifdef OOMPH_HAS_MPI
4399 
4400  // Go through all the segments and compute their (local) boundary
4401  // coordinates
4402  for (unsigned is = 0; is < nsegments; is++)
4403  {
4404 #ifdef PARANOID
4405 
4406  if (segment_sorted_ele_pt[is].size() == 0)
4407  {
4408  std::ostringstream error_message;
4409  std::string output_string=
4410  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4411  error_message
4412  << "The (" << is << ")-th segment has no elements\n";
4413  throw OomphLibError(error_message.str(),
4414  output_string,
4415  OOMPH_EXCEPTION_LOCATION);
4416  } // if (segment_sorted_ele_pt[is].size() == 0)
4417 
4418 #endif
4419 
4420  // Get access to the first element on the segment
4421  FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
4422 
4423  // Number of nodes
4424  unsigned nnod = first_ele_pt->nnode();
4425 
4426  // Get the first node of the current segment
4427  Node *first_node_pt = first_ele_pt->node_pt(0);
4428  if (is_inverted[first_ele_pt])
4429  {
4430  first_node_pt = first_ele_pt->node_pt(nnod-1);
4431  }
4432 
4433  // Coordinates of left node
4434  double x_left = first_node_pt->x(0);
4435  double y_left = first_node_pt->x(1);
4436 
4437  // Initialise boundary coordinate (local boundary coordinate
4438  // for boundaries with more than one segment)
4439  Vector<double> zeta(1, 0.0);
4440 
4441  // Set boundary coordinate
4442  first_node_pt->set_coordinates_on_boundary(b, zeta);
4443 
4444  // Lexicographically bottom left node
4445  std::set<Node*> local_nodes_pt;
4446  local_nodes_pt.insert(first_node_pt);
4447 
4448 #ifdef OOMPH_HAS_MPI
4449 
4450  // Insert the node in the look-up scheme for nodes in segments
4451  this->add_boundary_segment_node(b,is,first_node_pt);
4452 
4453 #endif // #ifdef OOMPH_HAS_MPI
4454 
4455  // Now loop over nodes in order
4456  for (std::list<FiniteElement*>::iterator it =
4457  segment_sorted_ele_pt[is].begin();
4458  it != segment_sorted_ele_pt[is].end(); it++)
4459  {
4460  // Get element
4461  FiniteElement* el_pt = *it;
4462 
4463  // Start node and increment
4464  unsigned k_nod = 1;
4465  int nod_diff = 1;
4466  if (is_inverted[el_pt])
4467  {
4468  k_nod = nnod - 2;
4469  nod_diff = -1;
4470  }
4471 
4472  // Loop over nodes
4473  for (unsigned j = 1; j < nnod; j++)
4474  {
4475  Node* nod_pt = el_pt->node_pt(k_nod);
4476  k_nod += nod_diff;
4477 
4478  // Coordinates of right node
4479  double x_right = nod_pt->x(0);
4480  double y_right = nod_pt->x(1);
4481 
4482  // Increment boundary coordinate
4483  zeta[0] += sqrt(
4484  (x_right - x_left) * (x_right - x_left) + (y_right - y_left)
4485  * (y_right - y_left));
4486 
4487  // Set boundary coordinate
4488  nod_pt->set_coordinates_on_boundary(b, zeta);
4489 
4490  // Increment reference coordinate
4491  x_left = x_right;
4492  y_left = y_right;
4493 
4494  // Get lexicographically bottom left node but only
4495  // use vertex nodes as candidates
4496  local_nodes_pt.insert(nod_pt);
4497 
4498 #ifdef OOMPH_HAS_MPI
4499 
4500  // Insert the node in the look-up scheme for nodes in segments
4501  this->add_boundary_segment_node(b, is, nod_pt);
4502 
4503 #endif // #ifdef OOMPH_HAS_MPI
4504 
4505  } // for (j < nnod)
4506  } // iterator over the elements in the segment
4507 
4508  // Assign the arclength of the current segment
4509  segment_arclength[is] = zeta[0];
4510 
4511  // Add the nodes for the corresponding segment in the container
4512  segment_all_nodes_pt.push_back(local_nodes_pt);
4513 
4514  } // for (is < nsegments)
4515 
4516  // =================================================================
4517  // END: Assign global/local (non distributed mesh/distributed
4518  // mesh) boundary coordinates to nodes
4519  // =================================================================
4520 
4521  // Store the initial arclength for each segment of boundary in
4522  // the current processor, initialise to zero in case we have a
4523  // non distributed mesh
4524  Vector<double> initial_segment_arclength(nsegments,0.0);
4525 
4526  // Store the final arclength for each segment of boundary in the
4527  // current processor, initalise to zero in case we have a non
4528  // distributed mesh
4529  Vector<double> final_segment_arclength(nsegments,0.0);
4530 
4531  // Store the initial zeta for each segment of boundary in the
4532  // current processor, initalise to zero in case we have a non
4533  // distributed mesh
4534  Vector<double> initial_segment_zeta(nsegments,0.0);
4535 
4536  // Store the final zeta for each segment of boundary in the
4537  // current processor, initalise to zero in case we have a non
4538  // distributed mesh
4539  Vector<double> final_segment_zeta(nsegments,0.0);
4540 
4541  // --------------------------------------------------------------
4542  // DISTRIBUTED MESH: BEGIN - Check orientation of boundaries and
4543  // assign boundary coordinates accordingly
4544  // --------------------------------------------------------------
4545 
4546 #ifdef OOMPH_HAS_MPI
4547 
4548  // Check that the mesh is distributed and that the initial zeta
4549  // values for the boundary segments have been already
4550  // assigned. When the method is called by the first time (when
4551  // the mesh is just created) the initial zeta values for the
4552  // boundary segments are not available
4553  if (this->is_mesh_distributed() && Assigned_segments_initial_zeta_values[b])
4554  {
4555  // Get the initial and final coordinates of each segment
4556 
4557  // For each segment in the processor check whether it was
4558  // inverted in the root processor, if that is the case then it
4559  // is necessary to re-sort the face elements and invert them
4560  for (unsigned is = 0; is < nsegments; is++)
4561  {
4562  // Check if we need/or not to invert the current zeta values
4563  // on the segment (only apply for boundaries with GeomObject
4564  // associated)
4565 
4566  // Does the boundary HAS a GeomObject associated?
4567  if (this->boundary_geom_object_pt(b)!=0)
4568  {
4569  // Get the first and last node of the current segment and
4570  // their zeta values
4571 
4572  // Get access to the first element on the segment
4573  FiniteElement* first_ele_pt=segment_sorted_ele_pt[is].front();
4574 
4575  // Number of nodes
4576  const unsigned nnod = first_ele_pt->nnode();
4577 
4578  // Get the first node of the current segment
4579  Node *first_node_pt = first_ele_pt->node_pt(0);
4580  if (is_inverted[first_ele_pt])
4581  {
4582  first_node_pt = first_ele_pt->node_pt(nnod-1);
4583  }
4584 
4585  // Get access to the last element on the segment
4586  FiniteElement* last_ele_pt=segment_sorted_ele_pt[is].back();
4587 
4588  // Get the last node of the current segment
4589  Node *last_node_pt = last_ele_pt->node_pt(nnod-1);
4590  if (is_inverted[last_ele_pt])
4591  {
4592  last_node_pt = last_ele_pt->node_pt(0);
4593  }
4594 
4595  // Get the zeta coordinates for the first and last node
4596  Vector<double> current_segment_initial_zeta(1);
4597  Vector<double> current_segment_final_zeta(1);
4598 
4599  first_node_pt->get_coordinates_on_boundary(b,current_segment_initial_zeta);
4600  last_node_pt->get_coordinates_on_boundary(b,current_segment_final_zeta);
4601 
4602 #ifdef PARANOID
4603  // Check whether the zeta values in the segment are in
4604  // increasing or decreasing order
4605  if (current_segment_initial_zeta[0] <
4606  current_segment_final_zeta[0])
4607  {
4608  // do nothing
4609  }
4610  else if (current_segment_initial_zeta[0] >
4611  current_segment_final_zeta[0])
4612  {
4613  std::stringstream error_message;
4614  std::string output_string=
4615  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4616  error_message
4617  << "The zeta values are in decreasing order, this is weird\n"
4618  << "since they have just been set-up in increasing order few\n"
4619  << "lines above\n"
4620  << "Boundary: (" << b << ")\n"
4621  << "Segment: (" << is << ")\n"
4622  << "Initial zeta value: ("
4623  << current_segment_initial_zeta[0] << ")\n"
4624  << "Initial coordinate: (" << first_node_pt->x(0) << ", "
4625  << first_node_pt->x(1) << ")\n"
4626  << "Final zeta value: ("
4627  << current_segment_final_zeta[0] << ")\n"
4628  << "Initial coordinate: (" << last_node_pt->x(0) << ", "
4629  << last_node_pt->x(1) << ")\n";
4630  throw OomphLibError(error_message.str(),
4631  output_string,
4632  OOMPH_EXCEPTION_LOCATION);
4633  }
4634  else
4635  {
4636  std::stringstream error_message;
4637  std::string output_string=
4638  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4639  error_message
4640  << "It was not possible to determine whether the zeta values on"
4641  << "the current segment\nof the boundary are in"
4642  << "increasing or decreasing order\n\n"
4643  << "Boundary: (" << b << ")\n"
4644  << "Segment: (" << is << ")\n"
4645  << "Arclength: (" << segment_arclength[is] << "\n"
4646  << "Initial zeta value: ("
4647  << current_segment_initial_zeta[0] << ")\n"
4648  << "Initial coordinate: (" << first_node_pt->x(0) << ", "
4649  << first_node_pt->x(1) << ")\n"
4650  << "Final zeta value: ("
4651  << current_segment_final_zeta[0] << ")\n"
4652  << "Initial coordinate: (" << last_node_pt->x(0) << ", "
4653  << last_node_pt->x(1) << ")\n";
4654  throw OomphLibError(error_message.str(),
4655  output_string,
4656  OOMPH_EXCEPTION_LOCATION);
4657  }
4658 #endif // #ifdef PARANOID
4659 
4660  // Now get the original zeta values and check if they are in
4661  // increasing or decreasing order
4662  const double original_segment_initial_zeta =
4663  boundary_segment_initial_zeta(b)[is];
4664  const double original_segment_final_zeta =
4665  boundary_segment_final_zeta(b)[is];
4666 
4667  // Now check if the zeta values go in increase or decrease
4668  // order
4669  if (original_segment_final_zeta > original_segment_initial_zeta)
4670  {
4671  // The original values go in increasing order, only need
4672  // to change the values if the original segment is marked
4673  // as inverted
4674  if (boundary_segment_inverted(b)[is])
4675  {
4676  // The original segment is inverted, then we need to
4677  // reverse the boundary coordinates. Go through all the
4678  // nodes and reverse its value
4679  std::set<Node*> all_nodes_pt = segment_all_nodes_pt[is];
4680  for (std::set<Node*>::iterator it = all_nodes_pt.begin();
4681  it != all_nodes_pt.end(); it++)
4682  {
4683  Vector<double> zeta(1);
4684  // Get the node
4685  Node* nod_pt = (*it);
4686  // Get the boundary coordinate associated to the node
4687  nod_pt->get_coordinates_on_boundary(b, zeta);
4688  // Compute its new value
4689  zeta[0] = segment_arclength[is] - zeta[0];
4690  // Set the new boundary coordinate value
4691  nod_pt->set_coordinates_on_boundary(b, zeta);
4692  } // Loop over the nodes in the segment
4693  } // if (boundary_segment_inverted(b)[is])
4694  else
4695  {
4696  // The boundary is not inverted, do nothing!!!
4697  }
4698 
4699  } // original zeta values in increasing order
4700  else if (original_segment_final_zeta < original_segment_initial_zeta)
4701  {
4702  // The original values go in decreasing order, only need
4703  // to change the values if the original segment is NOT
4704  // marked as inverted
4705  if (boundary_segment_inverted(b)[is])
4706  {
4707  // The boundary is inverted, do nothing!!!
4708  }
4709  else
4710  {
4711  // The original segment is NOT inverted, then we need
4712  // to reverse the boundary coordinates. Go through all
4713  // the nodes and reverse its value
4714  std::set<Node*> all_nodes_pt = segment_all_nodes_pt[is];
4715  for (std::set<Node*>::iterator it = all_nodes_pt.begin();
4716  it != all_nodes_pt.end(); it++)
4717  {
4718  Vector<double> zeta(1);
4719  // Get the node
4720  Node* nod_pt = (*it);
4721  // Get the boundary coordinate associated to the node
4722  nod_pt->get_coordinates_on_boundary(b, zeta);
4723  // Compute its new value
4724  zeta[0] = segment_arclength[is] - zeta[0];
4725  // Set the new boundary coordinate value
4726  nod_pt->set_coordinates_on_boundary(b, zeta);
4727  } // Loop over the nodes in the segment
4728  } // else if (boundary_segment_inverted(b)[is])
4729  } // original zeta values in decreasing order
4730 #ifdef PARANOID
4731  else
4732  {
4733  std::stringstream error_message;
4734  std::string output_string=
4735  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4736  error_message
4737  << "It was not possible to identify if the zeta values on the\n"
4738  << "current segment in the boundary should go in increasing\n"
4739  << "or decreasing order.\n\n"
4740  << "Boundary: (" << b << ")\n"
4741  << "Segment: (" << is << ")\n"
4742  << "Initial zeta value: ("
4743  << original_segment_initial_zeta << ")\n"
4744  << "Final zeta value: ("
4745  << original_segment_final_zeta << ")\n";
4746  throw OomphLibError(error_message.str(),
4747  output_string,
4748  OOMPH_EXCEPTION_LOCATION);
4749  }
4750 #endif
4751 
4752  } // if (this->boundary_geom_object_pt(b)!=0)
4753  else
4754  {
4755 
4756  // No GeomObject associated, do nothing!!!
4757 
4758  } // else if (this->boundary_geom_object_pt(b)!=0)
4759  } // for (is < nsegments)
4760  } // if (this->is_mesh_distributed() &&
4761  // Assigned_segments_initial_zeta_values[b])
4762 
4763 #endif // #ifdef OOMPH_HAS_MPI
4764 
4765  // Get the number of sets for nodes
4766 #ifdef PARANOID
4767  if (segment_all_nodes_pt.size() != nsegments)
4768  {
4769  std::ostringstream error_message;
4770  std::string output_string=
4771  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4772  error_message
4773  <<"The number of segments ("<<nsegments<<") and the number of "
4774  <<"sets of nodes ("<<segment_all_nodes_pt.size()<<") representing\n"
4775  <<"the\nsegments is different!!!\n\n";
4776  throw OomphLibError(error_message.str(),
4777  output_string,
4778  OOMPH_EXCEPTION_LOCATION);
4779  }
4780 #endif
4781 
4782  // --------------------------------------------------------------
4783  // DISTRIBUTED MESH: END - Check orientation of boundaries and
4784  // assign boundary coordinates accordingly
4785  // --------------------------------------------------------------
4786 
4787  // =================================================================
4788  // BEGIN: Get the lenght of the boundaries or segments of the
4789  // boundary
4790  // =================================================================
4791 
4792  // The nodes have been assigned arc-length coordinates from one
4793  // end or the other of the segments. If the mesh is distributed
4794  // the values are set so that they agree with the increasing or
4795  // decreasing order of the zeta values for the segments
4796 
4797  // Storage for the coordinates of the first and last nodes
4798  Vector<double> first_coordinate(2);
4799  Vector<double> last_coordinate(2);
4800  // Storage for the zeta coordinate of the first and last nodes
4801  Vector<double> first_node_zeta_coordinate(1,0.0);
4802  Vector<double> last_node_zeta_coordinate(1,0.0);
4803 
4804  // Store the accumulated arclength, used at the end to check the
4805  // max arclength of the boundary
4806  double boundary_arclength = 0.0;
4807 
4808  // If the mesh is marked as distributed and the initial zeta
4809  // values for the segments have been computed then get the
4810  // info. regarding the initial and final nodes coordinates, same
4811  // as the zeta boundary values for those nodes
4812 
4813 #ifdef OOMPH_HAS_MPI
4814  if (this->is_mesh_distributed() && Assigned_segments_initial_zeta_values[b])
4815  {
4816  // --------------------------------------------------------------
4817  // DISTRIBUTED MESH: BEGIN
4818  // --------------------------------------------------------------
4819 
4820  // Get the initial and final coordinates for the complete boundary
4821  first_coordinate = boundary_initial_coordinate(b);
4822  last_coordinate = boundary_final_coordinate(b);
4823  // Get the initial and final zeta values for the boundary
4824  // (arclength)
4825  first_node_zeta_coordinate = boundary_initial_zeta_coordinate(b);
4826  last_node_zeta_coordinate = boundary_final_zeta_coordinate(b);
4827 
4828  // The total arclength is the maximum between the initial and
4829  // final zeta coordinate
4830  boundary_arclength = std::max(first_node_zeta_coordinate[0],
4831  last_node_zeta_coordinate[0]);
4832 
4833 #ifdef PARANOID
4834  if (boundary_arclength == 0)
4835  {
4836  std::ostringstream error_message;
4837  std::string output_string=
4838  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4839  error_message << "The boundary arclength is zero for boundary ("
4840  << b << ")\n";
4841  throw OomphLibError(error_message.str(),
4842  output_string,
4843  OOMPH_EXCEPTION_LOCATION);
4844  }
4845 #endif
4846 
4847  // Check if there is a GeomObject associated to the boundary,
4848  // and assign the corresponding zeta (arclength) values
4849  if (this->boundary_geom_object_pt(b)!=0)
4850  {
4851  initial_segment_zeta = boundary_segment_initial_zeta(b);
4852  final_segment_zeta = boundary_segment_final_zeta(b);
4853  }
4854  else
4855  {
4856  initial_segment_arclength = boundary_segment_initial_arclength(b);
4857  final_segment_arclength = boundary_segment_final_arclength(b);
4858  }
4859 
4860  // --------------------------------------------------------------
4861  // DISTRIBUTED MESH: END
4862  // --------------------------------------------------------------
4863 
4864  } // if (this->is_mesh_distributed() &&
4865  // Assigned_segments_initial_zeta_values[b])
4866  else
4867  {
4868 #endif // #ifdef OOMPH_HAS_MPI
4869 
4870  // If the mesh is NOT distributed or the initial and final zeta
4871  // values of the segments have not been assigned then perform
4872  // as in the serial case
4873  if (this->boundary_geom_object_pt(b)!=0)
4874  {
4875  initial_segment_zeta[0] = this->boundary_coordinate_limits(b)[0];
4876  final_segment_zeta[0] = this->boundary_coordinate_limits(b)[1];
4877  }
4878  else
4879  {
4880  // When the mesh is or not distributed but the initial
4881  // segment's zeta values HAVE NOT been established then
4882  // initalize the initial segment to zero
4883  initial_segment_arclength[0] = 0.0;
4884  }
4885 #ifdef OOMPH_HAS_MPI
4886  } // The mesh is NOT distributed or the zeta values for the
4887  // segments have not been established
4888 
4889 #endif // #ifdef OOMPH_HAS_MPI
4890 
4891  // =================================================================
4892  // END: Get the lenght of the boundaries or segments of the
4893  // boundary
4894  // =================================================================
4895 
4896  // =================================================================
4897  // BEGIN: Scale the boundary coordinates based on whether the
4898  // boundary has an associated GeomObj or not
4899  // =================================================================
4900 
4901  // Go through all the segments and assign the scaled boundary
4902  // coordinates
4903  for (unsigned is = 0; is < nsegments; is++)
4904  {
4905 #ifdef PARANOID
4906  if (segment_sorted_ele_pt[is].size() == 0)
4907  {
4908  std::ostringstream error_message;
4909  std::string output_string=
4910  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
4911  error_message
4912  << "The (" << is << ")-th segment has no elements\n";
4913  throw OomphLibError(error_message.str(),
4914  output_string,
4915  OOMPH_EXCEPTION_LOCATION);
4916  } // if (segment_sorted_ele_pt[is].size() == 0)x
4917 #endif
4918 
4919  // Get the first and last nodes coordinates for the current
4920  // segment
4921 
4922 #ifdef OOMPH_HAS_MPI
4923  // Check if the initial zeta values of the segments have been
4924  // established, if they have not then get the first and last
4925  // coordinates from the current segments, same as the zeta
4926  // values
4927  if (!Assigned_segments_initial_zeta_values[b])
4928  {
4929 #endif // #ifdef OOMPH_HAS_MPI
4930 
4931  // Get access to the first element on the segment
4932  FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
4933 
4934  // Number of nodes
4935  const unsigned nnod = first_ele_pt->nnode();
4936 
4937  // Get the first node of the current segment
4938  Node *first_node_pt = first_ele_pt->node_pt(0);
4939  if (is_inverted[first_ele_pt])
4940  {
4941  first_node_pt = first_ele_pt->node_pt(nnod-1);
4942  }
4943 
4944  // Get access to the last element on the segment
4945  FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
4946 
4947  // Get the last node of the current segment
4948  Node *last_node_pt = last_ele_pt->node_pt(nnod-1);
4949  if (is_inverted[last_ele_pt])
4950  {
4951  last_node_pt = last_ele_pt->node_pt(0);
4952  }
4953 
4954  // Get the coordinates for the first and last node
4955  for (unsigned i = 0; i < 2; i++)
4956  {
4957  first_coordinate[i] = first_node_pt->x(i);
4958  last_coordinate[i] = last_node_pt->x(i);
4959  }
4960 
4961  // Get the zeta coordinates for the first and last node
4962  first_node_pt->get_coordinates_on_boundary(b,first_node_zeta_coordinate);
4963  last_node_pt->get_coordinates_on_boundary(b,last_node_zeta_coordinate);
4964 
4965 #ifdef OOMPH_HAS_MPI
4966  } // if (!Assigned_segments_initial_zeta_values[b])
4967 #endif // #ifdef OOMPH_HAS_MPI
4968 
4969  // Get the nodes of the current segment
4970  std::set<Node*> all_nodes_pt = segment_all_nodes_pt[is];
4971 
4972  // If the boundary has a geometric object representation then
4973  // scale the coordinates to match those of the geometric object
4974  GeomObject* const geom_object_pt = this->boundary_geom_object_pt(b);
4975  if (geom_object_pt != 0)
4976  {
4977  Vector<double> bound_coord_limits=this->boundary_coordinate_limits(b);
4978  //Get the position of the ends of the geometric object
4979  Vector<double> zeta(1);
4980  Vector<double> first_geom_object_location(2);
4981  Vector<double> last_geom_object_location(2);
4982 
4983  // Get the zeta value for the initial coordinates of the
4984  // GeomObject
4985  zeta[0] = bound_coord_limits[0];
4986  // zeta[0] = initial_segment_zeta[is];
4987 
4988  // Get the coordinates from the initial zeta value from the
4989  // GeomObject
4990  geom_object_pt->position(zeta, first_geom_object_location);
4991 
4992  // Get the zeta value for the final coordinates of the
4993  // GeomObject
4994  zeta[0] = bound_coord_limits[1];
4995  // zeta[0] = final_segment_zeta[is];
4996 
4997  // Get the coordinates from the final zeta value from the
4998  // GeomObject
4999  geom_object_pt->position(zeta, last_geom_object_location);
5000 
5001  // Calculate the errors in position between the first and
5002  // last nodes and the endpoints of the geometric object
5003  double error = 0.0;
5004  double tmp_error = 0.0;
5005  for (unsigned i = 0; i < 2; i++)
5006  {
5007  const double dist = first_geom_object_location[i]
5008  - first_coordinate[i];
5009  tmp_error += dist * dist;
5010  }
5011  error += sqrt(tmp_error);
5012  tmp_error = 0.0;
5013  for (unsigned i = 0; i < 2; i++)
5014  {
5015  const double dist =
5016  last_geom_object_location[i] - last_coordinate[i];
5017  tmp_error += dist * dist;
5018  }
5019  error += sqrt(tmp_error);
5020 
5021  // Calculate the errors in position between the first and
5022  // last nodes and the endpoints of the geometric object if
5023  // reversed
5024  double rev_error = 0.0;
5025  tmp_error = 0.0;
5026  for (unsigned i = 0; i < 2; i++)
5027  {
5028  const double dist =
5029  first_geom_object_location[i] - last_coordinate[i];
5030  tmp_error += dist * dist;
5031  }
5032  rev_error += sqrt(tmp_error);
5033  tmp_error = 0.0;
5034  for (unsigned i = 0; i < 2; i++)
5035  {
5036  const double dist =
5037  last_geom_object_location[i] - first_coordinate[i];
5038  tmp_error += dist * dist;
5039  }
5040  rev_error += sqrt(tmp_error);
5041 
5042  // Number of polyline vertices along this boundary
5043  const unsigned n_vertex =
5044  Polygonal_vertex_arclength_info[b].size();
5045 
5046  // Get polygonal vertex data
5047  Vector < std::pair<double, double> > polygonal_vertex_arclength
5048  = Polygonal_vertex_arclength_info[b];
5049 
5050  // If the (normal) error is small than reversed then we have
5051  // the coordinate direction correct. If not then we must
5052  // reverse it bool reversed = false;
5053  if (error < rev_error)
5054  {
5055  // Coordinates are aligned (btw: don't delete this block --
5056  // there's a final else below to catch errors!)
5057 
5058  // reversed = false;
5059  }
5060  else if (error > rev_error)
5061  {
5062  // reversed = true;
5063 
5064  // Reverse the limits of the boundary coordinates along the
5065  // geometric object
5066  double temp = bound_coord_limits[0];
5067  bound_coord_limits[0] = bound_coord_limits[1];
5068  bound_coord_limits[1] = temp;
5069 
5070 #ifdef OOMPH_HAS_MPI
5071  // If we are working with a NON distributed mesh then
5072  // re-assign the initial and final zeta values
5073  if (!this->is_mesh_distributed())
5074  {
5075 #endif
5076  temp = initial_segment_zeta[is];
5077  initial_segment_zeta[is] = final_segment_zeta[is];
5078  final_segment_zeta[is] = temp;
5079 #ifdef OOMPH_HAS_MPI
5080  }
5081 #endif
5082  // Reverse the vertices information
5083  for (unsigned v = 0; v < n_vertex; v++)
5084  {
5085  polygonal_vertex_arclength[v].first
5086  = Polygonal_vertex_arclength_info[b][v].first;
5087 
5088  polygonal_vertex_arclength[v].second
5089  = Polygonal_vertex_arclength_info[b][n_vertex - v - 1].second;
5090  } // for (v < n_vertex)
5091  }
5092  else
5093  {
5094  std::ostringstream error_stream;
5095  std::string output_string=
5096  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
5097  error_stream << "Something very strange has happened.\n"
5098  << "The error between the endpoints of the geometric object\n"
5099  << "and the first and last nodes on the boundary is the same\n"
5100  << "irrespective of the direction of the coordinate.\n"
5101  << "This probably means that things are way off.\n"
5102  << "The errors are " << error << " and " << rev_error << "\n";
5103  std::cout << error_stream.str();
5104  throw OomphLibError(error_stream.str(),
5105  output_string,
5106  OOMPH_EXCEPTION_LOCATION);
5107  }
5108 
5109  //Get the total arclength of the edge
5110  //last_node_pt->get_coordinates_on_boundary(b, zeta);
5111  //double zeta_old_range=zeta[0];
5112 
5113  // Store the arclength of the segment (not yet mapped to the
5114  // boundary coordinates defined by the GeomObject)
5115  const double zeta_old_range = segment_arclength[is];
5116 
5117  //double zeta_new_range=bound_coord_limits[1]-bound_coord_limits[0];
5118 
5119  // The range to map the zeta values
5120  double zeta_new_range = final_segment_zeta[is] - initial_segment_zeta[is];
5121  // The initial zeta value for the current segment
5122  double initial_local_segment_zeta = initial_segment_zeta[is];
5123 
5124 #ifdef OOMPH_HAS_MPI
5125 
5126  // If we are working with a distributed mes and the initial
5127  // and final zeta values for the segments have been
5128  // established then reset the range to map
5129  if (this->is_mesh_distributed() && Assigned_segments_initial_zeta_values[b])
5130  {
5131  // Re-set the range to map the zeta values
5132  zeta_new_range=std::fabs(final_segment_zeta[is]-initial_segment_zeta[is]);
5133 
5134  // Re-set the initial zeta value of the segment
5135  initial_local_segment_zeta=std::min(initial_segment_zeta[is],
5136  final_segment_zeta[is]);
5137  }
5138 
5139 #endif
5140 
5141  // Re-assign boundary coordinate for the case where boundary
5142  // is represented by polygon
5143  unsigned use_old = false;
5144  if (n_vertex == 0)
5145  use_old = true;
5146 
5147  // Now scale the coordinates accordingly
5148  for (std::set<Node*>::iterator it = all_nodes_pt.begin();
5149  it != all_nodes_pt.end(); it++)
5150  {
5151  // Get the node
5152  Node* nod_pt = (*it);
5153 
5154  // Get coordinate based on arclength along polygonal repesentation
5155  nod_pt->get_coordinates_on_boundary(b, zeta);
5156 
5157  if (use_old)
5158  {
5159  // Boundary is actually a polygon -- simply rescale
5160  zeta[0]=initial_local_segment_zeta+
5161  (zeta_new_range/zeta_old_range)*(zeta[0]);
5162  }
5163  else
5164  {
5165  // Scale such that vertex nodes stay where they were
5166  bool found = false;
5167 
5168  // Loop over vertex nodes
5169  for (unsigned v = 1; v < n_vertex; v++)
5170  {
5171  if ((zeta[0] >= polygonal_vertex_arclength[v - 1].first)
5172  && (zeta[0] <= polygonal_vertex_arclength[v].first))
5173  {
5174  // Increment in intrinsic coordinate along geom object
5175  double delta_zeta =
5176  (polygonal_vertex_arclength[v].second
5177  - polygonal_vertex_arclength[v - 1].second);
5178  // Increment in arclength along segment
5179  double delta_polyarc =
5180  (polygonal_vertex_arclength[v].first
5181  - polygonal_vertex_arclength[v - 1].first);
5182 
5183  // Mapped arclength coordinate
5184  double zeta_new = polygonal_vertex_arclength[v - 1].second
5185  + delta_zeta * (zeta[0] - polygonal_vertex_arclength[v - 1].first) /
5186  delta_polyarc;
5187  zeta[0] = zeta_new;
5188 
5189  // Success!
5190  found = true;
5191 
5192  // Bail out
5193  break;
5194  }
5195  } // for (v < n_vertex)
5196 
5197  // If we still haven't found it's probably the last point along
5198  if (!found)
5199  {
5200 
5201 #ifdef PARANOID
5202  double diff=std::fabs(zeta[0]-
5203  polygonal_vertex_arclength[n_vertex-1].first);
5205  {
5206  std::ostringstream error_stream;
5207  error_stream
5208  << "Wasn't able to locate the polygonal arclength exactly\n"
5209  << "during re-setup of boundary coordinates and have\n"
5210  << "assumed that we're dealing with the final point along\n"
5211  << "the curvilinear segment and encountered some roundoff\n"
5212  << "However,the difference in the polygonal zeta coordinates\n"
5213  << "between zeta[0] " << zeta[0]
5214  << " and the originallly stored value "
5215  << polygonal_vertex_arclength[n_vertex-1].first << "\n"
5216  << "is " << diff << " which exceeds the threshold specified\n"
5217  << "in the publically modifiable variable\n"
5218  << "ToleranceForVertexMismatchInPolygons::Tolerable_error\n"
5219  << "whose current value is: "
5221  << "\nPlease check your mesh carefully and increase the\n"
5222  << "threshold if you're sure this is appropriate\n";
5223  throw OomphLibError(error_stream.str(),
5224  OOMPH_CURRENT_FUNCTION,
5225  OOMPH_EXCEPTION_LOCATION);
5226  }
5227 #endif
5228  zeta[0] = polygonal_vertex_arclength[n_vertex - 1].second;
5229  }
5230  }
5231 
5232  // Assign updated coordinate
5233  nod_pt->set_coordinates_on_boundary(b,zeta);
5234  }
5235  } // if (geom_object_pt != 0)
5236  else
5237  {
5238  // Establish the initial zeta value for this segment
5239  double z_initial = initial_segment_arclength[is];
5240 
5241  // Only use end points of the whole boundary and pick the
5242  // bottom left node
5243 
5244  // Set the bottom left coordinate as the first coordinates
5245  Vector<double> bottom_left_coordinate(2);
5246  bottom_left_coordinate = first_coordinate;
5247 
5248  // ... do the same with the zeta value
5249  Vector<double> bottom_left_zeta_coordinate(1);
5250  bottom_left_zeta_coordinate = first_node_zeta_coordinate;
5251 
5252  // Is the last y-coordinate smaller than y-coordinate of the
5253  // current bottom left coordinate
5254  if (last_coordinate[1] < bottom_left_coordinate[1])
5255  {
5256  // Re-set the bottom-left coordinate as the last coordinate
5257  bottom_left_coordinate = last_coordinate;
5258 
5259  // ... do the same with the zeta value
5260  bottom_left_zeta_coordinate = last_node_zeta_coordinate;
5261  }
5262  // The y-coordinates are the same
5263  else if (last_coordinate[1] == bottom_left_coordinate[1])
5264  {
5265  // Then check for the x-coordinate, which is the most-left
5266  if (last_coordinate[0] < bottom_left_coordinate[0])
5267  {
5268  // Re-set the bottom-left coordinate as the last
5269  // coordinate
5270  bottom_left_coordinate = last_coordinate;
5271 
5272  // ... do the same with the zeta value
5273  bottom_left_zeta_coordinate = last_node_zeta_coordinate;
5274  }
5275  } // else (The y-coordinates are the same)
5276 
5277  Vector<double> zeta(1, 0.0);
5278 
5279  // Now adjust boundary coordinate so that the bottom left
5280  // node has a boundary coordinate of 0.0 and that zeta
5281  // increases away from that point
5282  zeta = bottom_left_zeta_coordinate;
5283  const double zeta_ref = zeta[0];
5284 
5285  // Also get the maximum zeta value
5286  double zeta_max = 0.0;
5287  for (std::set<Node*>::iterator it = all_nodes_pt.begin(); it
5288  != all_nodes_pt.end(); it++)
5289  {
5290  Node* nod_pt = (*it);
5291  nod_pt->get_coordinates_on_boundary(b, zeta);
5292 
5293 #ifdef OOMPH_HAS_MPI
5294 
5295  // If the mesh is distributed and the initial and final
5296  // zeta values for the segment have been assigned then
5297  // check if the segment is inverted, we need to consider
5298  // that to correctly assgin the zeta values for the segment
5299  if (this->is_mesh_distributed() &&
5300  Assigned_segments_initial_zeta_values[b])
5301  {
5302  // Is the segment inverted, if that is the case then
5303  // invert the zeta values
5304  if (boundary_segment_inverted(b)[is])
5305  {
5306  zeta[0] = segment_arclength[is] - zeta[0];
5307  } // if (boundary_segment_inverted(b)[is])
5308  }
5309 
5310 #endif // #ifdef OOMPH_HAS_MPI
5311 
5312  // Set the zeta value
5313  zeta[0]+= z_initial;
5314  // Adjust the value based on the bottom-left condition
5315  zeta[0]-= zeta_ref;
5316  //If direction is reversed, then take absolute value
5317  if (zeta[0] < 0.0)
5318  {
5319  zeta[0] = std::fabs(zeta[0]);
5320  }
5321  // Get the max zeta value (we will use it to scale the
5322  // values to [0,1])
5323  if (zeta[0] > zeta_max)
5324  {
5325  zeta_max = zeta[0];
5326  }
5327  nod_pt->set_coordinates_on_boundary(b, zeta);
5328  } // Loop through the nodes in the segment (boundary)
5329 
5330 #ifdef OOMPH_HAS_MPI
5331  // After assigning boundary coordinates, BUT before scaling,
5332  // copy the initial and final segment arclengths so that we
5333  // know if the values go in increasing or decreasing
5334  // order. This will be used to identify the correct direction
5335  // of the segments in the new meshes created in the
5336  // adaptation method.
5337  if (this->is_mesh_distributed() &&
5338  Assigned_segments_initial_zeta_values[b])
5339  {
5340  // Get the first face element
5341  FiniteElement* first_seg_ele_pt = segment_sorted_ele_pt[is].front();
5342 
5343 #ifdef PARANOID
5344  // Check if the face element is nonhalo, it shouldn't, but
5345  // better check
5346  if (first_seg_ele_pt->is_halo())
5347  {
5348  std::ostringstream error_message;
5349  std::string output_string=
5350  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
5351  error_message
5352  << "The first face element in the (" << is << ")-th segment"
5353  << " is halo\n";
5354  throw OomphLibError(error_message.str(),
5355  output_string,
5356  OOMPH_EXCEPTION_LOCATION);
5357  } // if (first_seg_ele_pt->is_halo())
5358 #endif
5359 
5360  // Number of nodes
5361  const unsigned nnod = first_seg_ele_pt->nnode();
5362 
5363  // Get the first node of the current segment
5364  Node *first_seg_node_pt = first_seg_ele_pt->node_pt(0);
5365  if (is_inverted[first_seg_ele_pt])
5366  {
5367  first_seg_node_pt = first_seg_ele_pt->node_pt(nnod-1);
5368  }
5369 
5370  // Get the last face element
5371  FiniteElement* last_seg_ele_pt = segment_sorted_ele_pt[is].back();
5372 
5373 #ifdef PARANOID
5374  // Check if the face element is nonhalo, it shouldn't, but
5375  // better check
5376  if (last_seg_ele_pt->is_halo())
5377  {
5378  std::ostringstream error_message;
5379  std::string output_string=
5380  "UnstructuredTwoDMeshGeometryBase::setup_boundary_coordinates()";
5381  error_message
5382  << "The last face element in the (" << is << ")-th segment"
5383  << " is halo\n";
5384  throw OomphLibError(error_message.str(),
5385  output_string,
5386  OOMPH_EXCEPTION_LOCATION);
5387  } // if (last_seg_ele_pt->is_halo())
5388 #endif
5389 
5390  // Get the last node of the current segment
5391  Node *last_seg_node_pt = last_seg_ele_pt->node_pt(nnod-1);
5392  if (is_inverted[last_seg_ele_pt])
5393  {
5394  last_seg_node_pt = last_seg_ele_pt->node_pt(0);
5395  }
5396 
5397  // Now get the first and last node boundary coordinate values
5398  Vector<double> first_seg_arclen(1);
5399  Vector<double> last_seg_arclen(1);
5400 
5401  first_seg_node_pt->get_coordinates_on_boundary(b,first_seg_arclen);
5402  last_seg_node_pt->get_coordinates_on_boundary(b,last_seg_arclen);
5403 
5404  // Update the initial and final segments arclength
5405  boundary_segment_initial_arclength(b)[is] = first_seg_arclen[0];
5406  boundary_segment_final_arclength(b)[is] = last_seg_arclen[0];
5407 
5408  // Update the initial and final coordinates
5409  Vector<double> updated_segment_initial_coord(2);
5410  Vector<double> updated_segment_final_coord(2);
5411  for (unsigned k = 0 ; k < 2; k++)
5412  {
5413  updated_segment_initial_coord[k] = first_seg_node_pt->x(k);
5414  updated_segment_final_coord[k] = last_seg_node_pt->x(k);
5415  }
5416 
5417  // Set the updated initial coordinate
5418  boundary_segment_initial_coordinate(b)[is] =
5419  updated_segment_initial_coord;
5420 
5421  // Set the updated final coordinate
5422  boundary_segment_final_coordinate(b)[is] =
5423  updated_segment_final_coord;
5424 
5425  } // if (this->is_mesh_distributed() &&
5426  // Assigned_segments_initial_zeta_values[b])
5427 #endif // #ifdef OOMPH_HAS_MPI
5428 
5429  // The max. value will be incorrect if we are working with
5430  // distributed meshes where the current boundary has been
5431  // split by the distribution process. Here correct the
5432  // maximum value
5433  if (zeta_max < boundary_arclength)
5434  {
5435  zeta_max = boundary_arclength;
5436  }
5437 
5438  //Scale all surface coordinates so that all the points be on
5439  //the range [0, 1]
5440  for (std::set<Node*>::iterator it = all_nodes_pt.begin(); it
5441  != all_nodes_pt.end(); it++)
5442  {
5443  // Get the node
5444  Node* nod_pt = (*it);
5445 
5446  // Get the boundary coordinate
5447  nod_pt->get_coordinates_on_boundary(b, zeta);
5448 
5449  // Scale the value of the current node
5450  zeta[0] /= zeta_max;
5451 
5452  // Set the new scaled value
5453  nod_pt->set_coordinates_on_boundary(b, zeta);
5454  } // Loop over the nodes
5455 
5456  } // else if (geom_object_pt != 0)
5457 
5458  } // for (is < nsegments)
5459 
5460  // =================================================================
5461  // END: Scale the boundary coordinates based on whether the
5462  // boundary has an associated GeomObj or not
5463  // =================================================================
5464 
5465  // Cleanup
5466  for (unsigned e = 0; e < nel; e++)
5467  {
5468  delete face_el_pt[e];
5469  face_el_pt[e] = 0;
5470  }
5471 
5472  } // if (nel > 0), from the beginning of the method
5473 
5474  // Indicate that boundary coordinate has been set up
5475  Boundary_coordinate_exists[b] = true;
5476  }
5477 
5478 
5479 //////////////////////////////////////////////////////////////////////////
5480 //////////////////////////////////////////////////////////////////////////
5481 //////////////////////////////////////////////////////////////////////////
5482 
5483 
5484  //=================================================
5485  /// Helper namespace for BCInfo object used
5486  /// in the identification of boundary elements.
5487  //=================================================
5488  namespace TriangleBoundaryHelper
5489  {
5490 
5491  /// Structure for Boundary Informations
5492  struct BCInfo
5493  {
5494 
5495  /// Face ID
5496  unsigned Face_id;
5497 
5498  /// Boundary ID
5499  unsigned Boundary;
5500 
5501  /// Pointer to bulk finite element
5503 
5504  };
5505 
5506  }
5507 
5508 }
5509 
5510 #endif
std::map< unsigned, Vector< unsigned > > Boundary_segment_inverted
Stores the info. to know if it is necessary to reverse the segment based on a previous mesh...
Vector< unsigned > polygon_boundary_id()
Return vector of boundary ids of associated polylines.
unsigned & initial_vertex_connected_n_chunk()
Sets the boundary chunk to which the initial end is connected.
unsigned nregion_attribute()
Return the number of attributes used in the mesh.
bool is_redistribution_of_segments_between_polylines_enabled()
Is re-distribution of polyline segments in the curve between different boundaries during adaptation e...
std::map< unsigned, Vector< double > > & boundary_segment_initial_zeta()
Return direct access to the initial zeta for the segments that are part of a boundary.
void set_geom_objects_and_coordinate_limits_for_close_curve(TriangleMeshClosedCurve *input_closed_curve_pt)
Stores the geometric objects associated to the curve sections that compound the closed curve...
virtual TriangleMeshCurveSection * curve_section_pt(const unsigned &i) const
Pointer to i-th constituent curve section.
void add_boundary_segment_node(const unsigned &b, const unsigned &s, Node *const &node_pt)
std::map< unsigned, Vector< Vector< double > > > & boundary_segment_initial_coordinate()
Return direct access to the initial coordinates for the segments that are part of a boundary...
unsigned nboundary_segment(const unsigned &b)
Return the number of segments associated with a boundary.
std::map< unsigned, Vector< double > > & boundary_final_coordinate()
Return direct access to the final coordinates of a boundary.
std::map< unsigned, Vector< FiniteElement *> > Region_element_pt
Vector of elements in each region differentiated by attribute (the key of the map is the attribute) ...
void broken_copy(const std::string &class_name)
Issue error message and terminate execution.
TriangleMeshCurviLine(GeomObject *geom_object_pt, const double &zeta_start, const double &zeta_end, const unsigned &nsegment, const unsigned &boundary_id, const bool &space_vertices_evenly_in_arclength=true, const unsigned &boundary_chunk=0)
Constructor: Specify GeomObject, the start and end coordinates of the relevant boundary in terms of t...
void set_geom_objects_and_coordinate_limits_for_open_curve(TriangleMeshOpenCurve *input_open_curve_pt)
Stores the geometric objects associated to the curve sections that compound the open curve...
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
unsigned nsegment() const
Number of (initially straight-line) segments that this part of the boundary is to be represented by...
double * pointlist
Pointer to list of points x coordinate followed by y coordinate.
void set_initial_vertex_connected()
Sets the initial vertex as connected.
unsigned nboundary_element_in_region(const unsigned &b, const unsigned &r) const
Return the number of elements adjacent to boundary b in region r.
double * pointattributelist
Pointer to list of point attributes.
void enable_polyline_unrefinement(const double &tolerance=0.04)
Enable unrefinement of polylines to avoid unnecessarily large numbers of elements on of curvilinear b...
double Zeta_end
End coordinate in terms of the GeomObject&#39;s intrinsic coordinate.
void final_vertex_coordinate(Vector< double > &vertex)
Get last vertex coordinates.
void set_polyline_unrefinement_tolerance(const double &tolerance)
Set tolerance for unrefinement of polylines to avoid unnecessarily large numbers of elements on of cu...
std::map< unsigned, std::set< Node * > > Nodes_on_boundary_pt
Stores a pointer to a set with all the nodes related with a boundary.
TriangleMeshPolygon * closed_curve_to_polygon_helper(TriangleMeshClosedCurve *closed_curve_pt, unsigned &max_bnd_id_local)
Helper function that returns a polygon representation for the given closed curve, it also computes th...
Vector< TriangleMeshOpenCurve * > Internal_open_curve_pt
Vector of open polylines that define internal curves.
unsigned max_boundary_id()
Return max boundary id of associated curves.
TriangleMeshPolyLine * polyline_pt(const unsigned &i)
Pointer to i-th constituent polyline.
unsigned Initial_vertex_connected_bnd_id
Stores the id to which the initial end is connected.
int * pointmarkerlist
Pointer to list of point markers.
void create_vertex_coordinates_for_polyline_no_connections(TriangleMeshCurviLine *boundary_pt, Vector< Vector< double > > &vertex_coord, Vector< std::pair< double, double > > &polygonal_vertex_arclength_info)
Helper function to create polyline vertex coordinates for curvilinear boundary specified by boundary_...
bool Final_vertex_connected_suspended
Indicates if the connection is suspended because the boundary to connect is no longer part of the dom...
void write_triangulateio_to_polyfile(TriangulateIO &triangle_io, std::ostream &poly_file)
Write the triangulateio data to disk as a poly file, mainly used for debugging.
TriangleMeshPolyLine * polyline_pt(const unsigned &i) const
Pointer to i-th constituent polyline.
unsigned Initial_vertex_connected_n_chunk
Stores the chunk number of the boundary to which is connected th initial end.
Data structure filled when the connection matrix is created, for each polyline, there are two vertex_...
bool Initial_vertex_connected_suspended
Indicates if the connection is suspended because the boundary to connect is no longer part of the dom...
void output(std::ostream &outfile, const unsigned &n_sample=50)
Output curvilinear boundary at n_sample (default: 50) points.
double final_s_connection_value() const
Gets the s value to which the final end is connected.
double zeta_end()
End coordinate in terms of the GeomObject&#39;s intrinsic coordinate.
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.
unsigned Initial_vertex_connected_n_vertex
Stores the vertex number used for connection with the initial end.
std::map< unsigned, TriangleMeshCurveSection * > Boundary_curve_section_pt
A map that stores the associated curve section of the specified boundary id.
cstr elem_len * i
Definition: cfortran.h:607
void set_initial_vertex_connected_to_curviline()
Sets the initial vertex as connected to a curviline.
void operator=(const UnstructuredTwoDMeshGeometryBase &)
Broken assignment operator.
unsigned nsegment() const
Number of segments.
void unset_initial_vertex_connected_to_curviline()
Sets the initial vertex as non connected to a curviline.
double Polyline_refinement_tolerance
Tolerance for refinement of polylines (neg if refinement is disabled)
TriangleMeshCurve(const Vector< TriangleMeshCurveSection *> &curve_section_pt)
Empty constructor.
std::map< unsigned, Vector< double > > & boundary_coordinate_limits()
Return access to the vector of boundary coordinates associated with each geometric object...
bool Final_vertex_connected
Used for stating if the final end is connected to another boundary.
TriangleMeshPolyLine * boundary_polyline_pt(const unsigned &b)
Return pointer to the current polyline that describes the b-th mesh boundary.
bool are_there_connection_points()
Does the vector for storing connections has elements?
void disable_polyline_refinement()
Disable refinement of polylines.
virtual TriangleMeshCurveSection *& curve_section_pt(const unsigned &i)
Pointer to i-th constituent curve section.
bool Initial_vertex_connected
Used for stating if the initial end is connected to another boundary.
Vector< double > & boundary_final_zeta_coordinate(const unsigned &b)
Return the final zeta coordinate for the boundary.
unsigned & nsegment()
Number of (initially straight-line) segments that this part of the boundary is to be represented by...
unsigned Final_vertex_connected_n_chunk
Stores the chunk number of the boundary to which is connected th initial end.
A general Finite Element class.
Definition: elements.h:1274
unsigned final_vertex_connected_n_chunk() const
Gets the boundary chunk to which the final end is connected.
virtual void reset_reference_configuration()
Virtual function that should be overloaded to update the polygons reference configuration.
unsigned nvertex() const
Number of vertices.
double zeta_start()
Start coordinate in terms of the GeomObject&#39;s intrinsic coordinate.
unsigned Final_vertex_connected_bnd_id
Stores the id to which the initial end is connected.
Vector< double > & boundary_initial_zeta_coordinate(const unsigned &b)
Return the initial zeta coordinate for the boundary.
Vector< TriangleMeshPolygon * > Internal_polygon_pt
Vector of polygons that define internal polygons.
std::map< unsigned, Vector< double > > & boundary_initial_coordinate()
Return direct access to the initial coordinates of a boundary.
Vector< Vector< double > > & boundary_segment_final_coordinate(const unsigned &b)
Return the final coordinates for the segments that are part of a boundary.
virtual ~TriangleMeshPolygon()
Empty virtual destructor.
Vector< double > internal_point() const
Coordinates of the internal point.
std::map< unsigned, Vector< double > > Boundary_segment_initial_zeta
Stores the initial zeta coordinate for the segments that appear when a boundary is splited among proc...
double refinement_tolerance()
Get tolerance for refinement of curve sections to create a better representation of curvilinear bound...
void enable_polyline_refinement(const double &tolerance=0.08)
Enable refinement of polylines to create a better representation of curvilinear boundaries (e...
bool is_halo() const
Is this element a halo?
Definition: elements.h:1141
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
TriangleMeshCurveSection()
Empty constructor. Initialises the curve section as non connected.
std::map< unsigned, Vector< unsigned > > & boundary_segment_inverted()
Return the info. to know if it is necessary to reverse the segment based on a previous mesh...
virtual void position(const Vector< double > &zeta, Vector< double > &r) const =0
Parametrised position on object at current time: r(zeta).
void set_final_vertex_connected_to_curviline()
Sets the final vertex as connected to a curviline.
TriangleMeshPolyLine * polyline_pt(const unsigned &i) const
Pointer to i-th constituent polyline.
std::map< unsigned, Vector< double > > & boundary_final_zeta_coordinate()
Return direct access to the final zeta coordinates of a boundary.
double Zeta_start
Start coordinate in terms of the GeomObject&#39;s intrinsic coordinate.
void disable_redistribution_of_segments_between_polylines()
Disable re-distribution of polyline segments in the curve between different boundaries during adaptat...
e
Definition: cfortran.h:575
std::map< unsigned, Vector< Vector< Node * > > > Boundary_segment_node_pt
Used to store the nodes associated to a boundary and to an specific segment (this only applies in dis...
void flush_boundary_segment_node(const unsigned &b)
Flush the boundary segment node storage.
void initial_vertex_coordinate(Vector< double > &vertex)
Get first vertex coordinates.
std::set< TriangleMeshCurveSection * > Free_curve_section_pt
A set that contains the curve sections created by this object therefore it is necessary to free their...
void set_fixed()
Set the polygon to be fixed.
bool is_initial_vertex_connected() const
Test whether initial vertex is connected or not.
unsigned nsegments() const
Total number of segments.
std::map< unsigned, GeomObject * > Boundary_geom_object_pt
Storage for the geometric objects associated with any boundaries.
std::set< TriangleMeshPolygon * > Free_polygon_pt
A set that contains the polygons created by this object therefore it is necessary to free their assoc...
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
double region_attribute(const unsigned &i)
Return the attribute associated with region i.
Vector< double > Region_attribute
Vector of attributes associated with the elements in each region.
Vector< double > * connection_points_pt()
Returns the connection points vector.
void disable_unrefinement_tolerance()
Disable unrefinement of curve sections.
unsigned get_associated_vertex_to_svalue(double &target_s_value, unsigned &bnd_id)
Get the associated vertex to the given s value by looking to the list of s values created when changi...
void set_nboundary_segment_node(const unsigned &b, const unsigned &s)
Set the number of segments associated with a boundary.
void set_refinement_tolerance(const double &tolerance)
Set tolerance for refinement of curve sections to create a better representation of curvilinear bound...
Vector< double > Connection_points_pt
Stores the information for connections received on the curviline. Used when converting to polyline...
double maximum_length()
Gets access to the maximum length variable.
double Final_s_connection_value
Stores the s value used for connecting the final end with a curviline.
Vector< std::map< unsigned, Vector< int > > > Face_index_region_at_boundary
Storage for the face index adjacent to a boundary in a particular region.
Data structure to store the base vertex info, initial or final vertex in the polylines have an associ...
double Tolerance_for_s_connection
Tolerance used for connecting the ends to a curviline.
std::map< unsigned, Vector< Vector< double > > > Boundary_segment_final_coordinate
Stores the final coordinates for the segments that appear when a boundary is splited among processors...
void create_triangulateio_from_polyfiles(const std::string &node_file_name, const std::string &element_file_name, const std::string &poly_file_name, TriangulateIO &triangle_io, bool &use_attributes)
Vector< std::map< unsigned, Vector< FiniteElement * > > > Boundary_region_element_pt
Storage for elements adjacent to a boundary in a particular region.
void disable_use_maximum_length()
Disables the use of the maximum length criteria on the unrefinement or refinement steps...
void enable_unrefinement_tolerance(const double &tolerance=0.04)
Enable unrefinement of curve sections to avoid unnecessarily large numbers of elements on of curvilin...
bool space_vertices_evenly_in_arclength() const
Boolean to indicate if vertices in polygonal representation of the Curvline are spaced (approximately...
bool is_final_vertex_connected_to_curviline() const
Test whether the final vertex is connected to a curviline.
void disable_polyline_unrefinement()
Disable unrefinement of polylines.
TriangleMeshOpenCurve * create_open_curve_with_polyline_helper(TriangleMeshOpenCurve *open_curve_pt, unsigned &max_bnd_id_local)
Helper function that creates and returns an open curve with the polyline representation of its consti...
Vector< double > & internal_point()
Coordinates of the internal point.
void output(std::ostream &outfile, const unsigned &n_sample=50)
Output each sub-boundary at n_sample (default: 50) points.
GeomObject * geom_object_pt()
Pointer to GeomObject that represents this part of the boundary.
void unset_initial_vertex_connected()
Sets the initial vertex as non connected.
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
Vector< double > Internal_point_pt
Vector of vertex coordinates.
unsigned & initial_vertex_connected_n_vertex()
Sets the vertex number to which the initial end is connected.
unsigned nvertex() const
Number of vertices.
TriangleMeshPolyLine(const Vector< Vector< double > > &vertex_coordinate, const unsigned &boundary_id, const unsigned &boundary_chunk=0)
Constructor: Takes vectors of vertex coordinates in order Also allows the optional specification of a...
void set_polyline_refinement_tolerance(const double &tolerance)
Set tolerance for refinement of polylines to create a better representation of curvilinear boundaries...
Class defining a polyline for use in Triangle Mesh generation.
bool Initial_vertex_connected_to_curviline
States if the initial vertex is connected to a curviline.
std::map< unsigned, Vector< Vector< double > > > & boundary_segment_final_coordinate()
Return direct access to the final coordinates for the segments that are part of a boundary...
unsigned initial_vertex_connected_n_vertex() const
Gets the vertex number to which the initial end is connected.
std::map< unsigned, Vector< double > > Boundary_initial_coordinate
Stores the initial coordinates for the boundary.
double polyline_refinement_tolerance()
Get tolerance for refinement of polylines to create a better representation of curvilinear boundaries...
FiniteElement * region_element_pt(const unsigned &i, const unsigned &e)
Return the e-th element in the i-th region.
Vector< Vector< double > > Extra_holes_coordinates
Storage for extra coordinates for holes.
void clear_triangulateio(TriangulateIO &triangulate_io, const bool &clear_hole_data)
Clear TriangulateIO structure.
void final_vertex_coordinate(Vector< double > &vertex)
Get last vertex coordinates.
double & final_s_connection_value()
Sets the s value to which the final end is connected.
std::map< unsigned, GeomObject * > & boundary_geom_object_pt()
Return direct access to the geometric object storage.
std::map< unsigned, Vector< double > > Boundary_segment_initial_arclength
Stores the initial arclength for the segments that appear when a boundary is splited among processors...
unsigned get_associated_vertex_to_svalue(double &target_s_value, unsigned &bnd_id, double &s_tolerance)
Get the associated vertex to the given s value by looking to the list of s values created when changi...
double Tolerable_error
Acceptable discrepancy for mismatch in vertex coordinates. In paranoid mode, the code will die if the...
void setup_boundary_coordinates(const unsigned &b)
Setup boundary coordinate on boundary b. Boundary coordinate increases continously along polygonal bo...
static char t char * s
Definition: cfortran.h:572
bool is_fixed() const
Test whether the polygon is fixed or not.
std::map< unsigned, Vector< double > > & boundary_initial_zeta_coordinate()
Return direct access to the initial zeta coordinate of a boundary.
bool Allow_automatic_creation_of_vertices_on_boundaries
Flag to indicate whether the automatic creation of vertices along the boundaries by Triangle is allow...
double & tolerance_for_s_connection()
Sets the tolerance value for connections among curvilines.
unsigned & final_vertex_connected_bnd_id()
Sets the id to which the final end is connected.
std::map< unsigned, Vector< std::pair< double, double > > > Polygonal_vertex_arclength_info
Storage for pairs of doubles representing: .first: the arclength along the polygonal representation o...
void initial_vertex_coordinate(Vector< double > &vertex)
Get first vertex coordinates.
bool is_final_vertex_connected() const
Test whether final vertex is connected or not.
virtual unsigned boundary_id() const =0
Boundary id.
bool Final_vertex_connected_to_curviline
States if the final vertex is connected to a curviline.
unsigned nregion()
Return the number of regions specified by attributes.
std::map< unsigned, Vector< double > > Boundary_coordinate_limits
std::map< unsigned, Vector< double > > & boundary_segment_final_zeta()
Return direct access to the final zeta for the segments that are part of a boundary.
void set_final_vertex_connected()
Sets the final vertex as connected.
void output(std::ostream &outfile, const unsigned &n_sample=50)
Output each sub-boundary at n_sample (default: 50) points.
double unrefinement_tolerance()
Get tolerance for unrefinement of curve section to create a better representation of curvilinear boun...
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
std::map< unsigned, Vector< Vector< double > > > Boundary_segment_initial_coordinate
Stores the initial coordinates for the segments that appear when a boundary is splited among processo...
Base class defining a closed curve for the Triangle mesh generation.
void output(std::ostream &outfile, const unsigned &n_sample=50)
Output the polyline – n_sample is ignored.
UnstructuredTwoDMeshGeometryBase(const UnstructuredTwoDMeshGeometryBase &dummy)
Broken copy constructor.
Vector< TriangleMeshCurveSection * > Curve_section_pt
Vector of curve sections.
bool Is_internal_point_fixed
Indicate whether the internal point should be updated automatically.
void output(std::ostream &outfile)
Output with default number of plot points.
void initialise_triangulateio(TriangulateIO &triangle_io)
Initialise TriangulateIO structure.
double polyline_unrefinement_tolerance()
Get tolerance for unrefinement of polylines to create a better representation of curvilinear boundari...
FiniteElement * FE_pt
Pointer to bulk finite element.
void set_maximum_length(const double &maximum_length)
Allows to specify the maximum distance between two vertices that define the associated polyline of th...
double Initial_s_connection_value
Stores the s value used for connecting the initial end with a curviline.
void set_unrefinement_tolerance(const double &tolerance)
Set tolerance for unrefinement of curve sections to avoid unnecessarily large numbers of elements on ...
double tolerance_for_s_connection() const
Gets the tolerance value for connections among curvilines.
unsigned & final_vertex_connected_n_vertex()
Gets the vertex number to which the final end is connected.
TriangulateIO deep_copy_of_triangulateio_representation(TriangulateIO &triangle_io, const bool &quiet)
Make (partial) deep copy of TriangulateIO object. We only copy those items we need within oomph-lib&#39;s...
TriangleMeshCurveSection * curviline_to_polyline(TriangleMeshCurviLine *&curviline_pt, unsigned &bnd_id)
Helper function that creates the associated polyline representation for curvilines.
unsigned long nboundary_segment_node(const unsigned &b, const unsigned &s)
std::map< unsigned, bool > Assigned_segments_initial_zeta_values
Flag used at the setup_boundary_coordinate function to know if initial zeta values for segments have ...
void enable_refinement_tolerance(const double &tolerance=0.08)
Enable refinement of curve section to create a better representation of curvilinear boundaries (e...
Vector< unsigned > & boundary_segment_inverted(const unsigned &b)
Return the info. to know if it is necessary to reverse the segment based on a previous mesh...
Vector< double > & vertex_coordinate(const unsigned &i)
Coordinate vector of i-th vertex.
Vector< double > & boundary_coordinate_limits(const unsigned &b)
Return access to the coordinate limits associated with the geometric object associated with boundary ...
bool Enable_redistribution_of_segments_between_polylines
Is re-distribution of polyline segments between different boundaries during adaptation enabled...
bool Can_update_configuration
Boolean flag to indicate whether the polygon can update its own reference configuration after it has ...
void broken_assign(const std::string &class_name)
Issue error message and terminate execution.
Vector< double > & boundary_segment_initial_zeta(const unsigned &b)
Return the initial zeta for the segments that are part of a boundary.
std::map< unsigned, std::set< Node * > > & nodes_on_boundary_pt()
Gets a pointer to a set with all the nodes related with a boundary.
void dump_triangulateio(TriangulateIO &triangle_io, std::ostream &dump_file)
Write all the triangulateio data to disk in a dump file that can then be used to restart simulations...
Vector< Vector< double > > Vertex_coordinate
Vector of Vector of vertex coordinates.
unsigned nsegments() const
Total number of segments.
std::set< TriangleMeshOpenCurve * > Free_open_curve_pt
A set that contains the open curves created by this object therefore it is necessary to free their as...
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
Vector< double > & boundary_segment_final_zeta(const unsigned &b)
Return the final zeta for the segments that are part of a boundary.
Vector< double > & boundary_initial_coordinate(const unsigned &b)
Return the initial coordinates for the boundary.
std::map< unsigned, Vector< double > > Boundary_final_zeta_coordinate
Stores the final zeta coordinate for the boundary.
std::map< unsigned, Vector< double > > Boundary_final_coordinate
Stores the final coordinates for the boundary.
bool is_internal_point_fixed() const
Test whether the internal point is fixed.
Vector< double > vertex_coordinate(const unsigned &i) const
Coordinate vector of i-th vertex (const version)
unsigned & initial_vertex_connected_bnd_id()
Sets the id to which the initial end is connected.
Vector< double > & boundary_final_coordinate(const unsigned &b)
Return the final coordinates for the boundary.
Vector< Vector< double > > & boundary_segment_initial_coordinate(const unsigned &b)
Return the initial coordinates for the segments that are part of a boundary.
void disable_refinement_tolerance()
Disable refinement of curve section.
std::map< unsigned, Vector< double > > Boundary_initial_zeta_coordinate
Stores the initial zeta coordinate for the boundary.
void enable_redistribution_of_segments_between_polylines()
Enable re-distribution of polyline segments in the curve between different boundaries during adaptati...
std::map< unsigned, Vector< double > > Regions_coordinates
double initial_s_connection_value() const
Gets the s value to which the initial end is connected.
std::map< unsigned, Vector< double > > & boundary_segment_initial_arclength()
Return direct access to the initial arclength for the segments that are part of a boundary...
const Vector< unsigned > boundary_segment_inverted(const unsigned &b) const
Return the info. to know if it is necessary to reverse the segment based on a previous mesh...
Vector< double > & boundary_segment_final_arclength(const unsigned &b)
Return the final arclength for the segments that are part of a boundary.
double & initial_s_connection_value()
Sets the s value to which the initial end is connected.
void create_vertex_coordinates_for_polyline_connections(TriangleMeshCurviLine *boundary_pt, Vector< Vector< double > > &vertex_coord, Vector< std::pair< double, double > > &polygonal_vertex_arclength_info)
Helper function to create polyline vertex coordinates for curvilinear boundary specified by boundary_...
unsigned nvertices() const
Number of vertices.
unsigned final_vertex_connected_bnd_id() const
Gets the id to which the final end is connected.
std::map< unsigned, Vector< double > > Boundary_segment_final_zeta
Stores the final zeta coordinate for the segments that appear when a boundary is splited among proces...
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.
unsigned initial_vertex_connected_n_chunk() const
Gets the boundary chunk to which the initial end is connected.
void read_triangulateio(std::istream &restart_file, TriangulateIO &triangle_io)
Read the triangulateio data from a dump file on disk, which can then be used to restart simulations...
void unset_final_vertex_connected_to_curviline()
Sets the final vertex as non connected to a curviline.
unsigned long nboundary_segment_node(const unsigned &b)
Return the number of segments associated with a boundary.
static bool Suppress_warning_about_regions_and_boundaries
Public static flag to suppress warning; defaults to false.
unsigned nvertices() const
Number of vertices.
unsigned nregion_element(const unsigned &i)
Return the number of elements in the i-th region.
unsigned & final_vertex_connected_n_chunk()
Sets the boundary chunk to which the final end is connected.
unsigned Final_vertex_connected_n_vertex
Stores the vertex number used for connection with the final end.
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
unsigned ncurve_section() const
Number of constituent curves.
unsigned final_vertex_connected_n_vertex() const
Sets the vertex number to which the final end is connected.
bool Polygon_fixed
Boolean flag to indicate whether the polygon can move (default false because if it doesn&#39;t move this ...
GeomObject * boundary_geom_object_pt(const unsigned &b)
Return the geometric object associated with the b-th boundary or null if the boundary has associated ...
Vector< double > & boundary_segment_initial_arclength(const unsigned &b)
Return the initial arclength for the segments that are part of a boundary.
void add_connection_point(const double &z_value, const double &tol=1.0e-12)
bool is_initial_vertex_connected_to_curviline() const
Test whether the initial vertex is connected to a curviline.
std::map< unsigned, Vector< double > > & boundary_segment_final_arclength()
Return direct access to the final arclength for the segments that are part of a boundary.
A general mesh class.
Definition: mesh.h:74
Vector< TriangleMeshPolygon * > Outer_boundary_pt
Polygon that defines outer boundaries.
std::map< unsigned, Vector< double > > Boundary_segment_final_arclength
Stores the final arclength for the segments that appear when a boundary is splited among processors...
bool can_update_reference_configuration() const
Test whether curve can update reference.
bool Space_vertices_evenly_in_arclength
Boolean to indicate if vertices in polygonal representation of the Curviline are spaced (approximatel...
virtual unsigned ncurve_section() const
Number of constituent curves.
TriangleMeshPolyLine * polyline_pt(const unsigned &i)
Pointer to i-th constituent polyline.
unsigned initial_vertex_connected_bnd_id() const
Gets the id to which the initial end is connected.
unsigned npolyline() const
Number of constituent polylines.
void set_unfixed()
Set the polygon to be allowed to move (default)
double Polyline_unrefinement_tolerance
Tolerance for unrefinement of polylines (neg if refinement is disabled)
void unset_final_vertex_connected()
Sets the final vertex as non connected.
GeomObject * Geom_object_pt
Pointer to GeomObject that represents this part of the boundary.
Class defining a closed polygon for the Triangle mesh generation.