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