gmsh_tet_mesh.template.h
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision: 1174 $
7 //LIC//
8 //LIC// $LastChangedDate: 2016-05-11 10:03:56 +0100 (Wed, 11 May 2016) $
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 
31 #ifndef OOMPH_GMSH_TET_MESH_HEADER
32 #define OOMPH_GMSH_TET_MESH_HEADER
33 
34 // Config header generated by autoconfig
35 #ifdef HAVE_CONFIG_H
36  #include <oomph-lib-config.h>
37 #endif
38 
39 #include<algorithm>
40 #include <iterator>
41 
42 // OOMPH-LIB Headers
44 #include "../generic/sample_point_parameters.h"
45 #include "../generic/mesh_as_geometric_object.h"
46 #include "../generic/projection.h"
47 
48 namespace oomph
49 {
50 
51 
52 
53  //=========================================================================
54  /// Class to collate parameters for Gmsh mesh generation
55  //=========================================================================
57  {
58  public:
59 
60 
61  /// \short Specify outer boundary of domain to be meshed.
62  /// Other parameters get default values and can be set via
63  /// member functions
64  GmshParameters(TetMeshFacetedClosedSurface* const &outer_boundary_pt,
65  const std::string& gmsh_command_line_invocation) :
66  Outer_boundary_pt(outer_boundary_pt), Element_volume(1.0),
67  Target_size_file_name(".gmsh_target_size_for_adaptation.dat"),
68  Geo_and_msh_file_stem(".geo_file"),
69  Gmsh_command_line_invocation(gmsh_command_line_invocation),
79  {}
80 
81  /// Outer boundary
82  TetMeshFacetedClosedSurface*& outer_boundary_pt()
83  {
84  return Outer_boundary_pt;
85  }
86 
87  /// Internal boundaries
88  Vector<TetMeshFacetedSurface*>& internal_surface_pt()
89  {
90  return Internal_surface_pt;
91  }
92 
93  /// Uniform target element volume
94  double& element_volume()
95  {
96  return Element_volume;
97  }
98 
99  /// \short Filename for target volumes (for system-call based transfer to gmsh
100  /// during mesh adaptation). Default: .gmsh_target_size_for_adaptation.dat
101  std::string& target_size_file_name()
102  {
103  return Target_size_file_name;
104  }
105 
106  /// String to be issued via system command to activate gmsh
108  {
110  }
111 
112  /// \short Stem for geo and msh files (input/output to command-line gmsh
113  /// invocation)
114  std::string& geo_and_msh_file_stem()
115  {
116  return Geo_and_msh_file_stem;
117  }
118 
119 
120  /// Max. element size during refinement
122  {
123  return Max_element_size;
124  }
125 
126  /// Min. element size during refinement
128  {
129  return Min_element_size;
130  }
131 
132  /// Max. permitted edge ratio
134  {
136  }
137 
138  /// (Isotropic) grid spacing for target size transfer
140  {
142  }
143 
144  /// \short Target size is transferred onto regular grid (for gmsh) by
145  /// locate zeta. We try to find the exact point in the existing
146  /// mesh but if we fail to converge from the nearest specified number
147  /// of sample points we use the nearest of those.
149  {
151  }
152 
153  /// \short Stem for filename used to doc target element sizes on
154  /// gmsh grid. No doc if stem is equal to empty string (or counter
155  /// is negative)
157  {
159  }
160 
161  /// \short Counter for filename used to doc target element sizes on
162  /// gmsh grid. No doc if stem is equal to empty string (or counter
163  /// is negative)
165  {
167  }
168 
169  /// Is projection of old solution onto new mesh disabled?
171  {
172  return Projection_is_disabled;
173  }
174 
175  /// Disable projection of old solution onto new mesh
177  {
179  }
180 
181  /// Disable projection of old solution onto new mesh
183  {
185  }
186 
187  /// \short Output filename for gmsh on-screen output
189  {
191  }
192 
193  /// \short Counter for marker that indicates where we are
194  /// in gmsh on-screen output
196  {
198  }
199 
200  private:
201 
202  /// Pointer to outer boundary
203  TetMeshFacetedClosedSurface* Outer_boundary_pt;
204 
205  /// Internal boundaries
206  Vector<TetMeshFacetedSurface*> Internal_surface_pt;
207 
208  /// Uniform element volume
210 
211  /// \short Filename for target volume (for system-call based transfer
212  /// to gmsh during mesh adaptation)
214 
215  /// \short Stem for geo and msh files (input/output to command-line gmsh
216  /// invocation)
218 
219  /// Gmsh command line invocation string
221 
222  /// Max. element size during refinement
224 
225  /// Min. element size during refinement
227 
228  /// Max edge ratio before remesh gets triggered
230 
231  /// (Isotropic) grid spacing for target size transfer
233 
234  /// \short Target size is transferred onto regular grid (for gmsh) by
235  /// locate zeta. We try to find the exact point in the existing
236  /// mesh but if we fail to converge from the nearest specified number of
237  /// sample points we use the nearest of those.
239 
240  /// \short Stem for filename used to doc target element sizes on
241  /// gmsh grid. No doc if stem is equal to empty string (or counter
242  /// is negative)
244 
245  /// \short Counter for filename used to doc target element sizes on
246  /// gmsh grid. No doc if stem is equal to empty string (or counter
247  /// is negative)
249 
250  /// Is projection of old solution onto new mesh disabled?
252 
253  /// \short Output filename for gmsh on-screen output
255 
256  /// \short Counter for marker that indicates where we are
257  /// in gmsh on-screen output
259 
260 
261 
262 };
263 
264 /////////////////////////////////////////////////////////////////////
265 /////////////////////////////////////////////////////////////////////
266 /////////////////////////////////////////////////////////////////////
267 
268 
269  //=========================================================================
270  /// Helper class to keep track of edges in tet mesh generation
271  //=========================================================================
272  class TetEdge
273  {
274  public:
275 
276  /// \short Constructor: Pass two vertices, identified by their indices
277  /// Edge "direction" is from lower vertex to higher vertex id so
278  /// can compare if we're dealing with the same one...
279  TetEdge(const unsigned& vertex1, const unsigned& vertex2)
280  {
281  if (vertex1>vertex2)
282  {
283  Reversed=true;
284  Vertex_pair=std::make_pair(vertex2,vertex1);
285  }
286  else if (vertex1<vertex2)
287  {
288  Reversed=false;
289  Vertex_pair=std::make_pair(vertex1,vertex2);
290  }
291  else
292  {
293  throw OomphLibError(
294  "Muppet! You can't build an edge from one vertex to the same vertex!",
295  OOMPH_CURRENT_FUNCTION,
296  OOMPH_EXCEPTION_LOCATION);
297  }
298  }
299 
300 
301  /// First vertex id
302  unsigned first_vertex_id() const
303  {
304  return Vertex_pair.first;
305  }
306 
307  /// Second vertex id
308  unsigned second_vertex_id() const
309  {
310  return Vertex_pair.second;
311  }
312 
313 
314  /// \short Edge is reversed in the sense that vertex1 actually has a higher
315  /// id than vertex2 (when specified in the constructor)
316  bool is_reversed() const
317  {
318  return Reversed;
319  }
320 
321  /// \short Comparison operator: Edges are identical if their sorted
322  /// (and therefore possibly reversed) vertex ids agree
323  bool operator ==(const TetEdge& tet_edge) const
324  {
325  return ((tet_edge.first_vertex_id()==Vertex_pair.first)&&
326  (tet_edge.second_vertex_id()==Vertex_pair.second));
327  }
328 
329  /// \short Comparison operator. Lexicographic comparison based on
330  /// vertex ids
331  bool operator < (const TetEdge& tet_edge) const
332  {
333  if ((tet_edge.first_vertex_id()==Vertex_pair.first)&&
334  (tet_edge.second_vertex_id()==Vertex_pair.second))
335  {
336  return false;
337  }
338  else
339  {
340  if (tet_edge.first_vertex_id()==Vertex_pair.first)
341  {
342  return (tet_edge.second_vertex_id()<Vertex_pair.second);
343  }
344  else
345  {
346  return (tet_edge.first_vertex_id()<Vertex_pair.first);
347  }
348  }
349  }
350 
351 
352  private:
353 
354  /// The vertices (sorted by vertex ids)
355  std::pair<unsigned,unsigned> Vertex_pair;
356 
357  /// \short Is it reversed? I.e. is the first input vertex stored after
358  /// the second one?
359  bool Reversed;
360 
361  };
362 
363 
364 //////////////////////////////////////////////////////////////////////////
365 //////////////////////////////////////////////////////////////////////////
366 //////////////////////////////////////////////////////////////////////////
367 
368 
369  /// Forward declaration
370  template<class ELEMENT>
371  class GmshTetMesh;
372 
373 
374 //=========================================================================
375 // Gmsh-based Tet scaffold mesh
376 //=========================================================================
377  class GmshTetScaffoldMesh : public virtual TetMeshBase, public virtual Mesh
378  {
379 
380  public:
381 
382 
383  /// We're friends with the actual mesh
384  template<class ELEMENT>
385  friend class GmshTetMesh;
386 
387  /// \short Build mesh, based on specified parameters. If boolean is set
388  /// to true, the target element sizes are read from file (used during
389  /// adaptation; otherwise uniform target size is used).
390  GmshTetScaffoldMesh(GmshParameters* gmsh_parameters_pt,
391  const bool& use_mesh_grading_from_file) :
392  Gmsh_parameters_pt(gmsh_parameters_pt)
393  {
394 
395  double t_start=TimingHelpers::timer();
396 
397  // Create .geo file
398  write_geo_file(use_mesh_grading_from_file);
399 
400  oomph_info << "Time for writing geo file : "
401  << TimingHelpers::timer()-t_start
402  << " sec " << std::endl;
403  t_start=TimingHelpers::timer();
404 
405  // Execute gmsh on command line
406  std::string gmsh_command_line_string="";
407 
408  std::string gmsh_onscreen_output_file_name=
409  gmsh_parameters_pt->gmsh_onscreen_output_file_name();
410  std::ofstream gmsh_on_screen_output_file;
411  std::stringstream marker;
412  if (gmsh_onscreen_output_file_name!="")
413  {
414  marker
415  << "\n\n====================================================\n"
416  << " gmsh invocation: "
417  << gmsh_parameters_pt->gmsh_onscreen_output_counter() << std::endl
418  << "====================================================\n\n\n"
419  << std::endl;
420  gmsh_parameters_pt->gmsh_onscreen_output_counter()++;
421  oomph_info << marker.str();
422  gmsh_on_screen_output_file.open(gmsh_onscreen_output_file_name.c_str(),
423  std::ofstream::app);
424  gmsh_on_screen_output_file << marker.str();
425  gmsh_on_screen_output_file.close();
426  }
427 
428  gmsh_command_line_string +=
429  Gmsh_parameters_pt->gmsh_command_line_invocation() + " " +
430  Gmsh_parameters_pt->geo_and_msh_file_stem() + ".geo -3";
431  if (gmsh_onscreen_output_file_name!="")
432  {
433  gmsh_command_line_string +=" >> "+gmsh_onscreen_output_file_name;
434  }
435 
436  // Note return flag isn't particularly well defined but we report it
437  // anyway to aid detection of problems...
438  int return_flag=system(gmsh_command_line_string.c_str());
439  oomph_info << "fyi: return from system command: " << return_flag
440  << std::endl;
441  oomph_info << "Time for gmsh system call : "
442  << TimingHelpers::timer()-t_start
443  << " sec " << std::endl;
444  t_start=TimingHelpers::timer();
445 
446 
447  // Create the mesh
448  create_mesh_from_msh_file();
449 
450  oomph_info << "Time for creating mesh from msh file: "
451  << TimingHelpers::timer()-t_start
452  << " sec " << std::endl;
453  }
454 
455  private:
456 
457 
458 
459  /// Create mesh from msh file (created internally via disk-based operations)
461  {
462  // Create filename from stem
463  std::string mesh_file_name=Gmsh_parameters_pt->
464  geo_and_msh_file_stem() + ".msh";
465 
466 // Keep around if we ever write a version where name is passed in.
467 /* // Check extension */
468 /* #ifdef PARANOID */
469 /* std::string mesh_file_stem= */
470 /* mesh_file_name.substr(0,mesh_file_name.length()-4); */
471 /* std::string test=mesh_file_stem+".msh"; */
472 /* if (test!=mesh_file_name) */
473 /* { */
474 /* std::string error_msg("msh file has wrong extension: "); */
475 /* error_msg += " " + mesh_file_name; */
476 /* throw OomphLibError(error_msg, */
477 /* OOMPH_CURRENT_FUNCTION, */
478 /* OOMPH_EXCEPTION_LOCATION); */
479 /* } */
480 /* #endif */
481 
482  // Open wide...
483  std::ifstream mesh_file(mesh_file_name.c_str(),std::ios_base::in);
484 
485 // Check that the file actually opened correctly
486 #ifdef PARANOID
487  if(!mesh_file.is_open())
488  {
489  std::string error_msg("Failed to open mesh file: ");
490  error_msg += "\"" + mesh_file_name + "\".";
491  throw OomphLibError(error_msg,
492  OOMPH_CURRENT_FUNCTION,
493  OOMPH_EXCEPTION_LOCATION);
494  }
495 #endif
496 
497  // First line: Must be "$MeshFormat"
498  //----------------------------------
499  std::string line;
500  mesh_file >> line;
501  if (line!="$MeshFormat")
502  {
503  std::string error_msg
504  ("First line has to contain the string \"$MeshFormat\"; ");
505  error_msg += " yours contains: " + line;
506  throw OomphLibError(error_msg,
507  OOMPH_CURRENT_FUNCTION,
508  OOMPH_EXCEPTION_LOCATION);
509  }
510 
511  // Rest of line
512  mesh_file >> line;
513 
514 
515  // Nodes
516  //------
517 
518  // Now keep reading until we find the nodes
519  bool keep_going=true;
520  while (keep_going)
521  {
522  std::string line;
523  mesh_file >> line;
524  if (line=="$Nodes")
525  {
526  keep_going=false;
527  }
528  }
529  unsigned nnod=0;
530  mesh_file >> nnod;
531  std::map<unsigned,Vector<double> > node_coordinate;
532  for (unsigned j=0;j<nnod;j++)
533  {
534  unsigned node_number=0;
535  mesh_file >> node_number;
536 
537  // Read rest of line word by word
538  std::string s;
539  std::getline(mesh_file, s);
540  std::istringstream iss(s);
541  std::string sub;
542  while(iss >> sub)
543  {
544  node_coordinate[node_number].push_back(atof(sub.c_str()));
545  }
546  }
547 
548  mesh_file >> line;
549  if (line!="$EndNodes")
550  {
551  std::string error_msg("Line has to contain the string \"$EndNodes\"; ");
552  error_msg += " yours contains: " + line;
553  throw OomphLibError(error_msg,
554  OOMPH_CURRENT_FUNCTION,
555  OOMPH_EXCEPTION_LOCATION);
556  }
557 
558  // Rewind
559  mesh_file.clear();
560  mesh_file.seekg(0);
561 
562  mesh_file >> line;
563  if (line!="$MeshFormat")
564  {
565  std::string error_msg
566  ("First line has to contain the string \"$MeshFormat\"; ");
567  error_msg += " yours contains: " + line;
568  throw OomphLibError(error_msg,
569  OOMPH_CURRENT_FUNCTION,
570  OOMPH_EXCEPTION_LOCATION);
571  }
572 
573  // Rest of line
574  mesh_file >> line;
575 
576  // Storage for boundaries the nodes are on
577  std::map<unsigned,std::set<unsigned> > one_based_boundaries_of_node;
578 
579  // Elements
580  //---------
581 
582  // node number = boundary_node[one_based_bound_id][...]
583  std::map<unsigned,std::set<unsigned> > boundary_node;
584 
585  // element number = region_element[one_based_region_id][...]
586  std::map<unsigned,std::set<unsigned> > region_element;
587 
588  // Now keep reading until we find the nodes
589  keep_going=true;
590  while (keep_going)
591  {
592  std::string line;
593  mesh_file >> line;
594  if (line=="$Elements")
595  {
596  keep_going=false;
597  }
598  }
599 
600 
601  unsigned highest_one_based_boundary_id=0;
602  unsigned n_tet_el=0;
603 
604  unsigned nel=0;
605  mesh_file >> nel;
606  std::map<unsigned,Vector<unsigned> > element_node_index;
607  for (unsigned e=0;e<nel;e++)
608  {
609  // For each element the msh file provides:
610  //
611  // elm-number elm-type number-of-tags < tag > ... node-number-list
612  //
613  // By default, the first tag is the number of the physical entity to
614  // which the element belongs; the second is the number of the elementary
615  // geometrical entity to which the element belongs; the third is the
616  // number of mesh partitions to which the element belongs, followed by
617  // the partition ids (negative partition ids indicate ghost cells). A
618  // zero tag is equivalent to no tag. Gmsh and most codes using the MSH 2
619  // format require at least the first two tags (physical and elementary
620  // tags).
621  unsigned el_number=0;
622  mesh_file >> el_number;
623 
624 
625  unsigned el_type=0;
626  mesh_file >> el_type;
627 
628  switch(el_type)
629  {
630  case 1:
631  //oomph_info << "Line element" << std::endl;
632  break;
633 
634  case 2:
635  //oomph_info << "Triangle" << std::endl;
636  break;
637 
638  case 4:
639  n_tet_el++;
640  //oomph_info << "Tet" << std::endl;
641  break;
642 
643  case 15:
644  //oomph_info << "Point" << std::endl;
645  break;
646 
647  default:
648  std:: string error_msg("Can't handle element type: ");
649  error_msg += oomph::StringConversion::to_string(el_type);
650  throw OomphLibError(error_msg,
651  OOMPH_CURRENT_FUNCTION,
652  OOMPH_EXCEPTION_LOCATION);
653  }
654 
655  unsigned ntags;
656  mesh_file >> ntags;
657 
658  // Gmesh produces at least two flags:
659 
660  // Physical tag = what we use as boundary or region IDs.
661  int physical_tag=0;
662  mesh_file >> physical_tag;
663 
664  // Geometric tag; not something we use
665  int geom_tag=0;
666  mesh_file >> geom_tag;
667 
668  Vector<int> other_tags;
669  for (unsigned i=2;i<ntags;i++)
670  {
671  int tag=0;
672  mesh_file >> tag;
673  other_tags.push_back(tag);
674  }
675 
676 
677  // Now read the rest: node numbers
678  // https://stackoverflow.com/questions/16991002/getting-a-line-from-a-file-and-then-reading-word-by-word-c
679  std::string s;
680  std::getline(mesh_file,s);
681  std::istringstream iss(s);
682  std::string sub;
683  Vector<int> other_ints;
684  while(iss >> sub)
685  {
686  other_ints.push_back(atoi(sub.c_str()));
687  }
688  unsigned n_el_nod=other_ints.size();
689  for (unsigned j=0;j<n_el_nod;j++)
690  {
691  unsigned node_number=unsigned(other_ints[j]);
692  element_node_index[el_number].push_back(node_number);
693 
694  // If the element is a triangle, add node to boundary
695  // lookup scheme (if tag is not zero, i.e. hasn't been specified)
696  if (el_type==2)
697  {
698  if (physical_tag!=0)
699  {
700  boundary_node[unsigned(physical_tag)].insert(node_number);
701  one_based_boundaries_of_node[node_number].insert(
702  unsigned(physical_tag));
703  if (unsigned(physical_tag)>highest_one_based_boundary_id)
704  {
705  highest_one_based_boundary_id=physical_tag;
706  }
707  }
708  }
709  }
710  // If it's a bulk element (tet) and the physical tag (region id)
711  // is not equal to 0 add it to region list
712  if (el_type==4)
713  {
714  if (physical_tag!=0)
715  {
716  region_element[unsigned(physical_tag)].insert(n_tet_el);
717  }
718  }
719  }
720 
721  mesh_file >> line;
722  if (line!="$EndElements")
723  {
724  std::string error_msg
725  ("Line has to contain the string \"$EndElements\"; ");
726  error_msg += " yours contains: " + line;
727  throw OomphLibError(error_msg,
728  OOMPH_CURRENT_FUNCTION,
729  OOMPH_EXCEPTION_LOCATION);
730  }
731 
732  // Rewind
733  mesh_file.clear();
734  mesh_file.seekg(0);
735 
736  mesh_file >> line;
737  if (line!="$MeshFormat")
738  {
739  std::string error_msg
740  ("First line has to contain the string \"$MeshFormat\"; ");
741  error_msg += " yours contains: " + line;
742  throw OomphLibError(error_msg,
743  OOMPH_CURRENT_FUNCTION,
744  OOMPH_EXCEPTION_LOCATION);
745  }
746 
747  // Rest of line
748  mesh_file >> line;
749  mesh_file.close();
750 
751 
752 
753 
754  // Identify elements next to boundaries
755  //-------------------------------------
756 
757  // Now loop over tet elements and check if three their nodes are
758  // on given boundary
759  std::map<unsigned,std::set<unsigned> > element_next_to_boundary;
760  for (std::map<unsigned,Vector<unsigned> >::iterator it=
761  element_node_index.begin();it!=element_node_index.end();it++)
762  {
763  unsigned el_number=(*it).first;
764  unsigned nnod=((*it).second).size();
765  if (nnod==4)
766  {
767  std::map<unsigned,unsigned> boundary_node_count;
768  for (unsigned j=0;j<nnod;j++)
769  {
770  unsigned node_number=((*it).second)[j];
771  std::map<unsigned,std::set<unsigned> >::iterator itt=
772  one_based_boundaries_of_node.find(node_number);
773  if (itt!=one_based_boundaries_of_node.end())
774  {
775  for (std::set<unsigned>::iterator ittt=(itt->second).begin();
776  ittt!=(itt->second).end();ittt++)
777  {
778  unsigned one_based_boundary_id=(*ittt);
779  boundary_node_count[one_based_boundary_id]++;
780  }
781  }
782  }
783  for (std::map<unsigned,unsigned>::iterator
784  itt=boundary_node_count.begin();
785  itt!=boundary_node_count.end();itt++)
786  {
787  if ((*itt).second==3)
788  {
789  element_next_to_boundary[(*itt).first].insert(el_number);
790  }
791  }
792  }
793  }
794 
795  this->set_nboundary(highest_one_based_boundary_id);
796 
797  // Done reading/processing; now move across
798  // ----------------------------------------
799 
800  // Translate enumeration
801  Vector<unsigned> oomph_lib_node_number(4);
802  oomph_lib_node_number[0]=0;
803  oomph_lib_node_number[1]=3;
804  oomph_lib_node_number[2]=2;
805  oomph_lib_node_number[3]=1;
806 
807  // make space to accomodate all this in the actual mesh
808  Element_pt.resize(n_tet_el);
809  Node_pt.resize(nnod);
810 
811  // Existing nodes
812  Vector<Node*> existing_node_pt(nnod,0);
813 
814  // Now process the simplex tets only (they have four nodes!)
815  unsigned e=0;
816  for (std::map<unsigned,Vector<unsigned> >::iterator it=
817  element_node_index.begin();
818  it!=element_node_index.end();it++)
819  {
820  unsigned el_nnod=(*it).second.size();
821  if (el_nnod==4)
822  {
823  // Here comes the new element
824  TElement<3,2>* el_pt=new TElement<3,2>;
825 
826  // Store it
827  Element_pt[e]=el_pt;
828  e++;
829 
830  // Make/get nodes
831  for (unsigned j=0;j<el_nnod;j++)
832  {
833  // Get global, one-based node number
834  unsigned node_number=(*it).second[j];
835 
836  // Does it already exist?
837  if (existing_node_pt[node_number-1]!=0)
838  {
839  el_pt->node_pt(oomph_lib_node_number[j])=
840  existing_node_pt[node_number-1];
841  }
842  // Make new node
843  else
844  {
845  Node* nod_pt=0;
846 
847  // Is it on the boundary?
848  if (one_based_boundaries_of_node[node_number].size()==0)
849  {
850  // Make normal node
851  nod_pt=el_pt->construct_node(oomph_lib_node_number[j]);
852  Node_pt[node_number-1]=nod_pt;
853  existing_node_pt[node_number-1]=nod_pt;
854  }
855  // Make boundary node
856  else
857  {
858  nod_pt=el_pt->construct_boundary_node(oomph_lib_node_number[j]);
859  Node_pt[node_number-1]=nod_pt;
860  existing_node_pt[node_number-1]=nod_pt;
861 
862  // Add to boundary lookup scheme
863  for (std::set<unsigned>::iterator it=
864  one_based_boundaries_of_node[node_number].begin();
865  it!=one_based_boundaries_of_node[node_number].end();it++)
866  {
867  add_boundary_node((*it)-1,nod_pt);
868  }
869  }
870 
871  // Assign coordinates of new node
872  Vector<double> x(node_coordinate[node_number]);
873  for (unsigned i=0;i<3;i++)
874  {
875  nod_pt->x(i)=x[i];
876  }
877  }
878  }
879  } // end for tet element
880 
881  } // End of loop over all elements
882 
883 
884  // Setup region info. This is ugly because we're using
885  // a lookup scheme that was originally designed for tetgen...
886  unsigned n_region=region_element.size();
887  this->Region_element_pt.resize(n_region);
888  this->Region_attribute.resize(n_region);
889  unsigned region_count=0;
890  for (std::map<unsigned,std::set<unsigned> >::iterator it=
891  region_element.begin();it!=region_element.end();it++)
892  {
893  this->Region_element_pt[region_count].resize((*it).second.size());
894  unsigned one_based_region_id=(*it).first;
895  this->Region_attribute[region_count]=one_based_region_id-1;
896  unsigned count=0;
897  for (std::set<unsigned>::iterator itt=(*it).second.begin();
898  itt!=(*it).second.end();itt++)
899  {
900  unsigned one_based_el_number=(*itt);
901  this->Region_element_pt[region_count][count]=
902  dynamic_cast<FiniteElement*>(Element_pt[one_based_el_number-1]);
903  count++;
904  }
905  region_count++;
906  }
907 
908 
909 
910  // Keep alive for debugging
911  bool plot_for_debugging=false;
912  if (plot_for_debugging)
913  {
914  std::ofstream outfile;
915  std::string outfile_name;
916  std::string mesh_file_stem="shite";
917 
918  // Output element nodes
919  //----------------------
920  outfile_name=mesh_file_stem+"_element_nodes.dat";
921  outfile.open(outfile_name.c_str());
922  for (std::map<unsigned,Vector<unsigned> >::iterator it=
923  element_node_index.begin();it!=element_node_index.end();it++)
924  {
925  std::set<unsigned> shite_nodes;
926  unsigned el_id=(*it).first;
927  std::stringstream tmp_out;
928  for (Vector<unsigned>::iterator itt=(*it).second.begin();
929  itt!=(*it).second.end();itt++)
930  {
931  unsigned node_number=(*itt);
932  shite_nodes.insert(node_number);
933  Vector<double> x(node_coordinate[node_number]);
934  unsigned n=x.size();
935  for (unsigned i=0;i<n;i++)
936  {
937  tmp_out << x[i] << " ";
938  }
939  tmp_out << node_number << std::endl;
940  }
941  std::string prefix=", N=4, E=1, F=FEPOINT, ET=TETRAHEDRON";
942  std::string postfix="1 2 3 4";
943  if ((*it).second.size()==3)
944  {
945  prefix=", N=3, E=1, F=FEPOINT, ET=TRIANGLE";
946  postfix="1 2 3";
947  }
948  outfile << "ZONE T=\"one-based element id="
949  << el_id << "\"" << prefix << std::endl;
950  outfile << tmp_out.str();
951  outfile << postfix << std::endl;
952  }
953  outfile.close();
954 
955  // Output boundary nodes
956  //----------------------
957  outfile_name=mesh_file_stem+"_boundary_nodes.dat";
958  outfile.open(outfile_name.c_str());
959  for (std::map<unsigned,std::set<unsigned> >::iterator it=
960  boundary_node.begin();it!=boundary_node.end();it++)
961  {
962  unsigned one_based_boundary_id=(*it).first;
963  outfile << "ZONE T=\"one-based boundary id="
964  << one_based_boundary_id << "\"" << std::endl;
965  for (std::set<unsigned>::iterator itt=(*it).second.begin();
966  itt!=(*it).second.end();itt++)
967  {
968  unsigned node_number=(*itt);
969  Vector<double> x(node_coordinate[node_number]);
970  unsigned n=x.size();
971  for (unsigned i=0;i<n;i++)
972  {
973  outfile << x[i] << " ";
974  }
975  outfile << node_number << std::endl;
976  }
977  }
978  outfile.close();
979 
980 
981  // Output elements next to boundaries
982  //-----------------------------------
983  for (std::map<unsigned,std::set<unsigned> >::iterator it=
984  element_next_to_boundary.begin();
985  it!=element_next_to_boundary.end();it++)
986  {
987  unsigned one_based_boundary_id=(*it).first;
988  outfile_name=mesh_file_stem+"_elements_next_to_boundary_"
989  +oomph::StringConversion::to_string(one_based_boundary_id)+".dat";
990  outfile.open(outfile_name.c_str());
991  for (std::set<unsigned>::iterator itt=(*it).second.begin();
992  itt!=(*it).second.end();itt++)
993  {
994  outfile << "ZONE T=\"one-based boundary "
995  << one_based_boundary_id
996  << "\", N=4, E=1, F=FEPOINT, ET=TETRAHEDRON"
997  << std::endl;
998  unsigned el_number=(*itt);
999  unsigned nnod=element_node_index[el_number].size();
1000  for (unsigned j=0;j<nnod;j++)
1001  {
1002  unsigned node_number=element_node_index[el_number][j];
1003  Vector<double> x(node_coordinate[node_number]);
1004  unsigned n=x.size();
1005  for (unsigned i=0;i<n;i++)
1006  {
1007  outfile << x[i] << " ";
1008  }
1009  outfile << std::endl;
1010  }
1011  outfile << "1 2 3 4" << std::endl;
1012  }
1013  outfile.close();
1014  }
1015 
1016 
1017  // Compute/report total volume for sanity check
1018  {
1019  double vol=0.0;
1020  unsigned nel=this->nelement();
1021  for (unsigned e=0;e<nel;e++)
1022  {
1023  vol+=this->finite_element_pt(e)->size();
1024  }
1025  oomph_info << "Total volume of all elements in scaffold mesh: "
1026  << vol << std::endl;
1027  }
1028  }
1029  }
1030 
1031 
1032 
1033  /// Write geo file for gmsh
1034  void write_geo_file(const bool& use_mesh_grading_from_file)
1035  {
1036  // Transfer data from parameters
1037  TetMeshFacetedClosedSurface* outer_boundary_pt=
1038  Gmsh_parameters_pt->outer_boundary_pt();
1039 
1040  Vector<TetMeshFacetedSurface*> internal_surface_pt=
1041  Gmsh_parameters_pt->internal_surface_pt();
1042 
1043  double element_volume=Gmsh_parameters_pt->element_volume();
1044 
1045  std::string& target_size_file_name=
1046  Gmsh_parameters_pt->target_size_file_name();
1047 
1048  // Write gmsh geo file:
1049  std::string filename=Gmsh_parameters_pt->geo_and_msh_file_stem()+".geo";
1050  std::ofstream geo_file;
1051  geo_file.open(filename.c_str());
1052 
1053  geo_file << "// Uniform element size" << std::endl;
1054  geo_file << "//---------------------" << std::endl;
1055  geo_file << "lc=" << pow(element_volume,1.0/3.0) << ";" << std::endl;
1056  geo_file << std::endl;
1057 
1058  // Outer boundary
1059  //===============
1060 
1061  // Create vertices
1062  geo_file << "// Outer box" << std::endl;
1063  geo_file << "//==========" << std::endl;
1064  geo_file << std::endl;
1065  geo_file << "// Vertices" << std::endl;
1066  geo_file << "//---------" << std::endl;
1067  std::map<TetMeshVertex*,unsigned> vertex_number;
1068  unsigned nv=outer_boundary_pt->nvertex();
1069  for (unsigned j=0;j<nv;j++)
1070  {
1071  TetMeshVertex* vertex_pt=outer_boundary_pt->vertex_pt(j);
1072  vertex_number[vertex_pt]=j;
1073  geo_file << "Point(" << j+1 << ")={";
1074  for (unsigned i=0;i<3;i++)
1075  {
1076  geo_file << vertex_pt->x(i) << ",";
1077  }
1078  geo_file << "lc};" << std::endl;
1079  }
1080 
1081 
1082  // Determine unique edges
1083  std::set<TetEdge> tet_edge_set;
1084  unsigned nfacet=outer_boundary_pt->nfacet();
1085  for (unsigned f=0;f<nfacet;f++)
1086  {
1087  TetMeshFacet* facet_pt=outer_boundary_pt->facet_pt(f);
1088  unsigned nv=facet_pt->nvertex();
1089  for (unsigned j=0;j<nv-1;j++)
1090  {
1091  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(j);
1092  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(j+1);
1093  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1094  vertex_number[second_vertex_pt]+1);
1095  tet_edge_set.insert(my_tet_edge);
1096  }
1097  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(nv-1);
1098  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(0);
1099  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1100  vertex_number[second_vertex_pt]+1);
1101  tet_edge_set.insert(my_tet_edge);
1102  }
1103 
1104 
1105  geo_file <<std::endl;
1106  geo_file << "// Edge of outer box" << std::endl;
1107  geo_file << "//------------------" << std::endl;
1108  unsigned count=0;
1109  std::map<TetEdge,unsigned> tet_edge;
1110  for (std::set<TetEdge>::iterator it=tet_edge_set.begin();
1111  it!=tet_edge_set.end();it++)
1112  {
1113  tet_edge.insert(std::make_pair((*it),count));
1114  geo_file << "Line(" << count+1 << ")={"
1115  << (*it).first_vertex_id()
1116  << ","
1117  << (*it).second_vertex_id()
1118  << "};" << std::endl;
1119 
1120  count++;
1121  }
1122 
1123 
1124  geo_file <<std::endl;
1125  geo_file << "// Faces of outer box" << std::endl;
1126  geo_file << "//-------------------" << std::endl;
1127  for (unsigned f=0;f<nfacet;f++)
1128  {
1129  geo_file << "Line Loop(" << f+1 << ")={";
1130 
1131  TetMeshFacet* facet_pt=outer_boundary_pt->facet_pt(f);
1132  unsigned nv=facet_pt->nvertex();
1133  for (unsigned j=0;j<nv-1;j++)
1134  {
1135  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(j);
1136  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(j+1);
1137  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1138  vertex_number[second_vertex_pt]+1);
1139 
1140  std::map<TetEdge,unsigned>::iterator it=tet_edge.find(my_tet_edge);
1141  if (my_tet_edge.is_reversed())
1142  {
1143  geo_file << -int((it->second)+1) << ",";
1144  }
1145  else
1146  {
1147  geo_file << ((it->second)+1) << ",";
1148  }
1149 
1150 
1151  }
1152 
1153  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(nv-1);
1154  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(0);
1155  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1156  vertex_number[second_vertex_pt]+1);
1157 
1158  std::map<TetEdge,unsigned>::iterator it=tet_edge.find(my_tet_edge);
1159  if (my_tet_edge.is_reversed())
1160  {
1161  geo_file << -int((it->second)+1) << "};" << std::endl;
1162  }
1163  else
1164  {
1165  geo_file << ((it->second)+1) << "};" << std::endl;
1166  }
1167  geo_file << "Plane Surface(" << f+1 << ")={" << f+1 << "};"
1168  << std::endl;
1169 
1170  }
1171 
1172 
1173  geo_file << std::endl;
1174  geo_file << "// Define Plane Surfaces bounding the volume" << std::endl;
1175  geo_file << "//------------------------------------------" << std::endl;
1176  geo_file << "Surface Loop(1) = {";
1177  for (unsigned f=0;f<nfacet-1;f++)
1178  {
1179  geo_file << f+1 << ",";
1180  }
1181  geo_file << nfacet << "};" << std::endl;
1182 
1183 
1184  geo_file << std::endl;
1185  geo_file << "// Define one-based boundary IDs" << std::endl;
1186  geo_file << "//------------------------------" << std::endl;
1187  for (unsigned f=0;f<nfacet;f++)
1188  {
1189  unsigned one_based_boundary_id=
1190  outer_boundary_pt->one_based_facet_boundary_id(f);
1191  geo_file << "Physical Surface(" << one_based_boundary_id
1192  << ") = {" << f+1 << "};" << std::endl;
1193  }
1194 
1195 
1196 
1197 
1198  // Offsets before we start adding the various internal volumes/surfaces
1199  Vector<unsigned> volume_id_to_be_subtracted_off;
1200  unsigned nvertex_offset=outer_boundary_pt->nvertex();
1201  unsigned nfacet_offset=outer_boundary_pt->nfacet();
1202  unsigned nvolume_offset=1;
1203  unsigned nline_offset=tet_edge.size();
1204 
1205 
1206  // Storage for one-based ids of surfaces to be embedded in
1207  // main volume
1208  Vector<unsigned> surfaces_to_be_embedded_in_main_volume;
1209  std::map<unsigned,Vector<unsigned> >
1210  surfaces_to_be_embedded_in_specified_one_based_region;
1211 
1212  // Now deal with internal faceted objects
1213  //=======================================
1214  unsigned n_internal=internal_surface_pt.size();
1215  for (unsigned i_internal=0;i_internal<n_internal;i_internal++)
1216  {
1217 
1218  // Closed surface?
1219  TetMeshFacetedClosedSurface* closed_srf_pt=
1220  dynamic_cast<TetMeshFacetedClosedSurface*>(
1221  internal_surface_pt[i_internal]);
1222  bool inner_surface_is_closed=true;
1223  if (closed_srf_pt==0)
1224  {
1225  inner_surface_is_closed=false;
1226  }
1227 
1228  // What it says
1229  unsigned number_of_volumes_created_for_this_internal_object=0;
1230 
1231  // Create vertices
1232  geo_file <<std::endl;
1233  geo_file <<std::endl;
1234  geo_file << "// Inner faceted surface " << i_internal << std::endl;
1235  geo_file << "//==========================" << std::endl;
1236  geo_file << std::endl;
1237  geo_file << "// Vertices" << std::endl;
1238  geo_file << "//---------" << std::endl;
1239  std::map<TetMeshVertex*,unsigned> vertex_number;
1240  unsigned nv=internal_surface_pt[i_internal]->nvertex();
1241  for (unsigned j=0;j<nv;j++)
1242  {
1243  TetMeshVertex* vertex_pt=
1244  internal_surface_pt[i_internal]->vertex_pt(j);
1245  vertex_number[vertex_pt]=nvertex_offset+j;
1246  geo_file << "Point(" << nvertex_offset+j+1 << ")={";
1247  for (unsigned i=0;i<3;i++)
1248  {
1249  geo_file << vertex_pt->x(i) << ",";
1250  }
1251  geo_file << "lc};" << std::endl;
1252  }
1253 
1254  // Determine unique edges
1255  std::set<TetEdge> tet_edge_set;
1256  unsigned nfacet=internal_surface_pt[i_internal]->nfacet();
1257  for (unsigned f=0;f<nfacet;f++)
1258  {
1259  TetMeshFacet* facet_pt=internal_surface_pt[i_internal]->facet_pt(f);
1260  unsigned nv=facet_pt->nvertex();
1261  for (unsigned j=0;j<nv-1;j++)
1262  {
1263  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(j);
1264  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(j+1);
1265  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1266  vertex_number[second_vertex_pt]+1);
1267  tet_edge_set.insert(my_tet_edge);
1268  }
1269  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(nv-1);
1270  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(0);
1271  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1272  vertex_number[second_vertex_pt]+1);
1273  tet_edge_set.insert(my_tet_edge);
1274  }
1275 
1276  geo_file <<std::endl;
1277  geo_file << "// Edge of inner faceted surface" << std::endl;
1278  geo_file << "//------------------------------" << std::endl;
1279  unsigned count=0;
1280  std::map<TetEdge,unsigned> tet_edge;
1281  for (std::set<TetEdge>::iterator it=tet_edge_set.begin();
1282  it!=tet_edge_set.end();it++)
1283  {
1284  tet_edge.insert(std::make_pair((*it),nline_offset+count));
1285  geo_file << "Line(" << nline_offset+count+1 << ")={"
1286  << (*it).first_vertex_id()
1287  << ","
1288  << (*it).second_vertex_id()
1289  << "};" << std::endl;
1290 
1291  count++;
1292  }
1293 
1294 
1295  geo_file <<std::endl;
1296  geo_file << "// Faces of inner faceted surface" << std::endl;
1297  geo_file << "//-------------------------------" << std::endl;
1298  for (unsigned f=0;f<nfacet;f++)
1299  {
1300  geo_file << "Line Loop(" << nfacet_offset+f+1 << ")={";
1301 
1302  TetMeshFacet* facet_pt=internal_surface_pt[i_internal]->facet_pt(f);
1303  unsigned nv=facet_pt->nvertex();
1304  for (unsigned j=0;j<nv-1;j++)
1305  {
1306  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(j);
1307  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(j+1);
1308  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1309  vertex_number[second_vertex_pt]+1);
1310 
1311  std::map<TetEdge,unsigned>::iterator it=tet_edge.find(my_tet_edge);
1312  if (my_tet_edge.is_reversed())
1313  {
1314  geo_file << -int((it->second)+1) << ",";
1315  }
1316  else
1317  {
1318  geo_file << ((it->second)+1) << ",";
1319  }
1320 
1321 
1322  }
1323 
1324  TetMeshVertex* first_vertex_pt=facet_pt->vertex_pt(nv-1);
1325  TetMeshVertex* second_vertex_pt=facet_pt->vertex_pt(0);
1326  TetEdge my_tet_edge(vertex_number[first_vertex_pt]+1,
1327  vertex_number[second_vertex_pt]+1);
1328 
1329  std::map<TetEdge,unsigned>::iterator it=tet_edge.find(my_tet_edge);
1330  if (my_tet_edge.is_reversed())
1331  {
1332  geo_file << -int((it->second)+1) << "};" << std::endl;
1333  }
1334  else
1335  {
1336  geo_file << ((it->second)+1) << "};" << std::endl;
1337  }
1338  geo_file << "Plane Surface(" << nfacet_offset+f+1 << ")={"
1339  << nfacet_offset+f+1
1340  << "};" << std::endl;
1341 
1342  // Keep track of plane surfaces that we've created so we
1343  // can embed them in volume for non-closed surfaces
1344  bool facet_is_embedded_in_a_volume=facet_pt->
1345  facet_is_embedded_in_a_specified_region();
1346  if (facet_is_embedded_in_a_volume)
1347  {
1348  unsigned one_based_region_id=
1349  facet_pt->one_based_region_that_facet_is_embedded_in();
1350  if (one_based_region_id==1)
1351  {
1352  surfaces_to_be_embedded_in_main_volume.push_back(nfacet_offset+f+1);
1353  }
1354  else
1355  {
1356  surfaces_to_be_embedded_in_specified_one_based_region
1357  [one_based_region_id].push_back(nfacet_offset+f+1);
1358  }
1359  }
1360  else
1361  {
1362  // By default put all surfaces into main volume
1363  if (!inner_surface_is_closed)
1364  {
1365  surfaces_to_be_embedded_in_main_volume.push_back(nfacet_offset+f+1);
1366  }
1367  }
1368  }
1369 
1370  // Store all region IDs defined by bounding facets
1371  std::set<unsigned> all_regions_id;
1372 
1373  // region_bounding_facet[r][i] returns the facet id (in gmsh
1374  // counting) of i-th facet bounding region r
1375  std::map<unsigned,Vector<unsigned> > region_bounding_facet;
1376 
1377  // outer_bounding_facet[i] returns the facet id (in gmsh
1378  // counting) that encloses the actual regions and acts as a
1379  // hole in the main volume (volume 1)
1380  Vector<unsigned> outer_bounding_facet;
1381 
1382  // Loop pver all facets to figure out which regions are bounded
1383  // by them
1384  for (unsigned f=0;f<nfacet;f++)
1385  {
1386  TetMeshFacet* facet_pt=internal_surface_pt[i_internal]->facet_pt(f);
1387  std::set<unsigned> region_id(facet_pt->one_based_adjacent_region_id());
1388  unsigned nr=region_id.size();
1389  if (nr==1) outer_bounding_facet.push_back(nfacet_offset+f+1);
1390  if ((nr==0)&&inner_surface_is_closed)
1391  {
1392 
1393  // Add to list of plane surfaces that don't bound regions so we
1394  // can embed them in volume for non-closed surfaces
1395  surfaces_to_be_embedded_in_main_volume.push_back(nfacet_offset+f+1);
1396  }
1397  for (std::set<unsigned>::iterator it=region_id.begin();
1398  it!=region_id.end();it++)
1399  {
1400  all_regions_id.insert((*it));
1401  region_bounding_facet[(*it)].push_back(nfacet_offset+f+1);
1402  }
1403  }
1404 
1405  // Number of regions bounded by facets
1406  unsigned n_regions_bounded_by_facets=all_regions_id.size();
1407 
1408  // No bounded regions
1409  if (n_regions_bounded_by_facets==0)
1410  {
1411  if (inner_surface_is_closed)
1412  {
1413  std::ostringstream error_message;
1414  error_message
1415  << "Something fishy going on! "
1416  << "Internal faceted surface "
1417  << i_internal
1418  << " is closed but does not bound any regions!\n"
1419  << "Specify one-based region ID for all facets using\n\n"
1420  << " TetMeshFacet::set_one_based_adjacent_region_id(...)\n\n";
1421  throw OomphLibError(error_message.str(),
1422  OOMPH_CURRENT_FUNCTION,
1423  OOMPH_EXCEPTION_LOCATION);
1424  }
1425  }
1426  else
1427  {
1428  // Do we need to create a hole in the main volume that contains
1429  // the multiple sub-volumes/regions?
1430  unsigned offset_for_extra_hole=0;
1431  if (n_regions_bounded_by_facets>1)
1432  {
1433  geo_file << std::endl;
1434  geo_file
1435  << "// Define Plane Surfaces bounding compound volume"
1436  << std::endl
1437  << "//-----------------------------------------------"
1438  << std::endl;
1439  geo_file
1440  << "// that will have to be treated as hole in main volume"
1441  << std::endl
1442  << "//-----------------------------------------------"
1443  << std::endl;
1444  offset_for_extra_hole=1;
1445  geo_file << "Surface Loop(" << nvolume_offset+1
1446  << ") = {";
1447  unsigned n=outer_bounding_facet.size();
1448  for (unsigned f=0;f<n-1;f++)
1449  {
1450  geo_file << outer_bounding_facet[f] << ",";
1451  }
1452  geo_file << outer_bounding_facet[n-1] << "};" << std::endl;
1453 
1454  // Bump
1455  number_of_volumes_created_for_this_internal_object++;
1456  }
1457 
1458  // Need to remove the internal volume from the outer volume
1459  volume_id_to_be_subtracted_off.push_back(nvolume_offset+1);
1460 
1461  // Deal with actual regions that fill the hole
1462  unsigned count=0;
1463  for (std::map<unsigned,Vector<unsigned> >::iterator it=
1464  region_bounding_facet.begin();it!=region_bounding_facet.end();
1465  it++)
1466  {
1467  geo_file << std::endl;
1468  geo_file << "// Define Plane Surfaces bounding the region volume "
1469  << (*it).first << std::endl;
1470  geo_file
1471  << "//----------------------------------------------------"
1472  << std::endl;
1473  geo_file << "Surface Loop("
1474  << nvolume_offset+1+offset_for_extra_hole+count
1475  << ") = {";
1476  unsigned n=(*it).second.size();
1477  for (unsigned f=0;f<n-1;f++)
1478  {
1479  geo_file << ((*it).second)[f] << ",";
1480  }
1481  geo_file << ((*it).second)[n-1] << "};" << std::endl;
1482 
1483  geo_file << std::endl;
1484  geo_file << "// Define volume "
1485  << nvolume_offset+1+offset_for_extra_hole+count
1486  << " as the volume bounded by Surface Loop "
1487  << nvolume_offset+1+offset_for_extra_hole+count
1488  << std::endl;
1489  geo_file
1490  << "//--------------------------------------------------------"
1491  << std::endl;
1492  geo_file << "Volume("
1493  << nvolume_offset+1+offset_for_extra_hole+count << ")={"
1494  << nvolume_offset+1+offset_for_extra_hole+count
1495  << "};" << std::endl;
1496  geo_file << std::endl;
1497 
1498  // Bump
1499  number_of_volumes_created_for_this_internal_object++;
1500 
1501 
1502  // Add as physical (to be meshed) volume if it's not a volume
1503  bool mesh_the_volume=true;
1504  if (closed_srf_pt!=0)
1505  {
1506  if (closed_srf_pt->faceted_volume_represents_hole_for_gmsh())
1507  {
1508  mesh_the_volume=false;
1509  }
1510  }
1511  if (mesh_the_volume)
1512  {
1513  geo_file << "// Define one-based region IDs" << std::endl;
1514  geo_file << "//----------------------------" << std::endl;
1515  geo_file << "Physical Volume(" << (*it).first
1516  << ")={"
1517  << nvolume_offset+1+offset_for_extra_hole+count
1518  << "};" << std::endl;
1519 
1520  unsigned ns_embedded=
1521  surfaces_to_be_embedded_in_specified_one_based_region
1522  [(*it).first].size();
1523  if (ns_embedded>0)
1524  {
1525  geo_file << "// This region has " << ns_embedded
1526  << " embedded surfaces\n";
1527  geo_file << "Surface{";
1528  for (unsigned i=0;i<ns_embedded-1;i++)
1529  {
1530  geo_file << surfaces_to_be_embedded_in_specified_one_based_region
1531  [(*it).first][i] << ",";
1532  }
1533  geo_file << surfaces_to_be_embedded_in_specified_one_based_region
1534  [(*it).first][ns_embedded-1] << "}In Volume {"
1535  << nvolume_offset+1+offset_for_extra_hole+count
1536  << "};" << std::endl;
1537  }
1538  }
1539 
1540  count++;
1541  }
1542  }
1543 
1544  geo_file << std::endl;
1545  geo_file << "// Define one-based boundary IDs" << std::endl;
1546  geo_file << "//------------------------------" << std::endl;
1547  for (unsigned f=0;f<nfacet;f++)
1548  {
1549  unsigned one_based_boundary_id=
1550  internal_surface_pt[i_internal]->one_based_facet_boundary_id(f);
1551  geo_file << "Physical Surface(" << one_based_boundary_id
1552  << ") = {" << nfacet_offset+f+1
1553  << "};" << std::endl;
1554  }
1555 
1556  // Bump
1557  nvertex_offset+=internal_surface_pt[i_internal]->nvertex();
1558  nfacet_offset+=internal_surface_pt[i_internal]->nfacet();
1559  nvolume_offset+=number_of_volumes_created_for_this_internal_object;
1560  nline_offset+=tet_edge.size();
1561  }
1562 
1563 
1564 
1565  // Done with the internal regions; write the actual volume
1566  // and specify embedded surfaces
1567  geo_file << std::endl;
1568  geo_file << "// Define volume 1 as the volume bounded by Surface Loop 1"
1569  << std::endl;
1570  geo_file << "//--------------------------------------------------------"
1571  << std::endl;
1572  unsigned n=volume_id_to_be_subtracted_off.size();
1573  if (n>0)
1574  {
1575  geo_file << "// with volume[s]: ";
1576  for (unsigned i=0;i<n;i++)
1577  {
1578  geo_file << volume_id_to_be_subtracted_off[i] << " ";
1579  }
1580  geo_file << "removed." << std::endl;
1581  geo_file << "//--------------------------------------------------------"
1582  << std::endl;
1583  }
1584 
1585  // Add initial volume
1586  geo_file << "Volume(1)={1";
1587 
1588  // Remove volumes that has separate IDs
1589  for (unsigned i=0;i<n;i++)
1590  {
1591  geo_file << "," << volume_id_to_be_subtracted_off[i];
1592  }
1593  geo_file << "};" << std::endl;
1594  geo_file << std::endl;
1595 
1596 
1597 
1598  geo_file << "// Define one-based region IDs" << std::endl;
1599  geo_file << "//----------------------------" << std::endl;
1600  geo_file << "Physical Volume(1"
1601  << ")={1};" << std::endl;
1602 
1603 
1604  // Now embed any surfaces that don't bound volumes
1605  unsigned ns=surfaces_to_be_embedded_in_main_volume.size();
1606  if (ns>0)
1607  {
1608  geo_file << std::endl;
1609  geo_file << "// Embed Plane Surfaces in main volume (volume 1)"
1610  << std::endl;
1611  geo_file << "//-----------------------------------------------"
1612  << std::endl;
1613  geo_file << "Surface{";
1614  for (unsigned s=0;s<ns-1;s++)
1615  {
1616  geo_file << surfaces_to_be_embedded_in_main_volume[s] << ",";
1617  }
1618  geo_file << surfaces_to_be_embedded_in_main_volume[ns-1]
1619  << "} In Volume{1};" << std::endl;
1620  geo_file << std::endl;
1621  }
1622 
1623 
1624  // Mesh grading
1625  {
1626  if (use_mesh_grading_from_file)
1627  {
1628 
1629 #ifdef PARANOID
1630 
1631  // Open wide...
1632  std::ifstream file(target_size_file_name.c_str(),
1633  std::ios_base::in);
1634 
1635  // Check that the file actually opened correctly
1636  if(!file.is_open())
1637  {
1638  std::string error_msg("Failed to open target volume file: ");
1639  error_msg += "\"" + target_size_file_name;
1640  throw OomphLibError(error_msg,
1641  OOMPH_CURRENT_FUNCTION,
1642  OOMPH_EXCEPTION_LOCATION);
1643  }
1644 #endif
1645 
1646  geo_file << "Field[1]=Structured;" << std::endl;
1647  geo_file << "Field[1].FileName=\""
1648  << target_size_file_name << "\";" << std::endl;
1649  geo_file << "Field[1].TextFormat=1;" << std::endl;
1650  geo_file << "Background Field = 1;" << std::endl;
1651  }
1652 
1653  }
1654 
1655  // Mesh the bloody thing
1656  geo_file << std::endl;
1657  geo_file << "Mesh 3;" << std::endl;
1658 
1659 
1660  /* // hierher Christmas attempt at optimisation */
1661  /* geo_file << std::endl; */
1662  /* geo_file << "// Attempt at optimisation: http://onelab.info/pipermail/gmsh/2015/010126.html" << std::endl; */
1663  /* geo_file << "//----------------------------" << std::endl; */
1664  /* geo_file << "Mesh.Optimize=1;" << std::endl; */
1665  /* geo_file << "Mesh.OptimizeNetgen=1;" << std::endl; */
1666 
1667 
1668 
1669  geo_file.close();
1670 
1671  }
1672 
1673  /// Parameters
1675 
1676 
1677  };
1678 
1679 
1680 
1681 
1682 
1683 
1684 //////////////////////////////////////////////////////////////////////////
1685 //////////////////////////////////////////////////////////////////////////
1686 //////////////////////////////////////////////////////////////////////////
1687 
1688 
1689 
1690 //=========================================================================
1691 // Gmsh-based Tet Mesh
1692 //=========================================================================
1693  template<class ELEMENT>
1694  class GmshTetMesh : public virtual TetMeshBase, public virtual Mesh
1695  {
1696 
1697  public:
1698 
1699  /// \short Constructor
1700  GmshTetMesh(GmshParameters* gmsh_parameters_pt,
1701  TimeStepper* time_stepper_pt=&Mesh::Default_TimeStepper) :
1702  Gmsh_parameters_pt(gmsh_parameters_pt)
1703  {
1704  bool use_mesh_grading_from_file=false;
1705  build_it(time_stepper_pt,use_mesh_grading_from_file);
1706  }
1707 
1708  /// \short Constructor. If boolean is set
1709  /// to true, the target element sizes are read from file (used during
1710  /// adaptation; otherwise uniform target size is used).
1711  GmshTetMesh(GmshParameters* gmsh_parameters_pt,
1712  const bool& use_mesh_grading_from_file,
1713  TimeStepper* time_stepper_pt=&Mesh::Default_TimeStepper) :
1714  Gmsh_parameters_pt(gmsh_parameters_pt)
1715  {
1716  build_it(time_stepper_pt,use_mesh_grading_from_file);
1717  }
1718 
1719 
1720  private:
1721 
1722  // Build function
1723  void build_it(TimeStepper* time_stepper_pt,
1724  const bool& use_mesh_grading_from_file)
1725  {
1726  // Mesh can only be built with 3D Telements.
1727  MeshChecker::assert_geometric_element<TElementGeometricBase,ELEMENT>(3);
1728 
1729  // Transfer data from parameters
1730  TetMeshFacetedClosedSurface* outer_boundary_pt=
1731  Gmsh_parameters_pt->outer_boundary_pt();
1732 
1733  Vector<TetMeshFacetedSurface*> internal_surface_pt=
1734  Gmsh_parameters_pt->internal_surface_pt();
1735 
1736  // Remember timestepper
1737  Time_stepper_pt=time_stepper_pt;
1738 
1739  // Store the boundary
1741 
1742  // Setup reverse lookup scheme
1743  {
1744  unsigned n_facet=Outer_boundary_pt->nfacet();
1745  for (unsigned f=0;f<n_facet;f++)
1746  {
1747  unsigned b=Outer_boundary_pt->one_based_facet_boundary_id(f);
1748  if (b!=0)
1749  {
1750  Tet_mesh_faceted_surface_pt[b-1]=Outer_boundary_pt;
1751  Tet_mesh_facet_pt[b-1]=Outer_boundary_pt->facet_pt(f);
1752  }
1753  else
1754  {
1755  std::ostringstream error_message;
1756  error_message << "Boundary IDs have to be one-based. Yours is "
1757  << b << "\n";
1758  throw OomphLibError(error_message.str(),
1759  OOMPH_CURRENT_FUNCTION,
1760  OOMPH_EXCEPTION_LOCATION);
1761  }
1762  }
1763  }
1764 
1765 
1766  //Store the internal boundary
1768 
1769  // Setup reverse lookup scheme
1770  {
1771  unsigned n=Internal_surface_pt.size();
1772  for (unsigned i=0;i<n;i++)
1773  {
1774  unsigned n_facet=Internal_surface_pt[i]->nfacet();
1775  for (unsigned f=0;f<n_facet;f++)
1776  {
1777  unsigned b=Internal_surface_pt[i]->one_based_facet_boundary_id(f);
1778  if (b!=0)
1779  {
1780  Tet_mesh_faceted_surface_pt[b-1]=Internal_surface_pt[i];
1781  Tet_mesh_facet_pt[b-1]=
1782  Internal_surface_pt[i]->facet_pt(f);
1783  }
1784  else
1785  {
1786  std::ostringstream error_message;
1787  error_message << "Boundary IDs have to be one-based. Yours is "
1788  << b << "\n";
1789  throw OomphLibError(error_message.str(),
1790  OOMPH_CURRENT_FUNCTION,
1791  OOMPH_EXCEPTION_LOCATION);
1792  }
1793  }
1794  }
1795  }
1796 
1797  // Build scaffold
1798  GmshTetScaffoldMesh* tmp_scaffold_mesh_pt =
1799  new GmshTetScaffoldMesh(Gmsh_parameters_pt,
1800  use_mesh_grading_from_file);
1801 
1802  // Convert mesh from scaffold to actual mesh
1803  build_from_scaffold(tmp_scaffold_mesh_pt,time_stepper_pt);
1804 
1805  // Kill the scaffold
1806  delete tmp_scaffold_mesh_pt;
1807  tmp_scaffold_mesh_pt=0;
1808 
1809  // Setup boundary coordinates
1810  unsigned nb=nboundary();
1811  for (unsigned b=0;b<nb;b++)
1812  {
1813  bool switch_normal=false;
1814  setup_boundary_coordinates<ELEMENT>(b,switch_normal);
1815  }
1816 
1817  // Now snap onto geometric objects associated with triangular facets
1818  // (if any!)
1819  snap_nodes_onto_geometric_objects();
1820 
1821  }
1822 
1823 
1824  protected:
1825 
1826  /// Parameters
1828 
1829 
1830  private:
1831 
1832  /// Build unstructured tet gmesh mesh based on output from scaffold
1833  void build_from_scaffold(GmshTetScaffoldMesh* tmp_scaffold_mesh_pt,
1834  TimeStepper* time_stepper_pt)
1835  {
1836  // Mesh can only be built with 3D Telements.
1837  MeshChecker::assert_geometric_element<TElementGeometricBase,ELEMENT>(3);
1838 
1839  // Create space for elements
1840  unsigned nelem=tmp_scaffold_mesh_pt->nelement();
1841  Element_pt.resize(nelem);
1842 
1843  // Relation between elements pointers and numbers in old mesh
1844  std::map<FiniteElement*,unsigned> scaffold_mesh_element_number;
1845 
1846  // Create space for nodes
1847  unsigned nnode_scaffold=tmp_scaffold_mesh_pt->nnode();
1848  Node_pt.resize(nnode_scaffold);
1849 
1850  // Set number of boundaries
1851  unsigned nbound=tmp_scaffold_mesh_pt->nboundary();
1852  set_nboundary(nbound);
1853 
1854  // Resize boundary info stuff
1855  Boundary_element_pt.resize(nbound); // hierher shouldn't this be a map?
1856  Face_index_at_boundary.resize(nbound); // hierher shouldn't this be a map?
1857  Boundary_region_element_pt.resize(nbound);
1858  Face_index_region_at_boundary.resize(nbound);
1859 
1860  // Build elements
1861  for (unsigned e=0;e<nelem;e++)
1862  {
1863  Element_pt[e]=new ELEMENT;
1864  }
1865 
1866  // Number of nodes per element
1867  unsigned nnod_el=tmp_scaffold_mesh_pt->finite_element_pt(0)->nnode();
1868 
1869  // Setup map to check the (pseudo-)global node number
1870  // Nodes whose number is zero haven't been copied across
1871  // into the mesh yet.
1872  std::map<Node*,unsigned> global_number;
1873  unsigned global_count=0;
1874 
1875  // Loop over elements in scaffold mesh, visit their nodes
1876  for (unsigned e=0;e<nelem;e++)
1877  {
1878  // Setup reverse lookup scheme to decipher lookup schemes from
1879  // scaffold mesh
1880  scaffold_mesh_element_number[tmp_scaffold_mesh_pt->finite_element_pt(e)]=e;
1881 
1882  // Loop over all nodes in element
1883  for (unsigned j=0;j<nnod_el;j++)
1884  {
1885 
1886  // Pointer to node in the scaffold mesh
1887  Node* scaffold_node_pt=
1888  tmp_scaffold_mesh_pt->finite_element_pt(e)->node_pt(j);
1889 
1890  // Get the (pseudo-)global node number in scaffold mesh
1891  // (It's zero [=default] if not visited this one yet)
1892  unsigned j_global=global_number[scaffold_node_pt];
1893 
1894  // Haven't done this one yet
1895  if (j_global==0)
1896  {
1897  // Get pointer to set of mesh boundaries that this
1898  // scaffold node occupies; NULL if the node is not on any boundary
1899  std::set<unsigned>* boundaries_pt;
1900  scaffold_node_pt->get_boundaries_pt(boundaries_pt);
1901 
1902  // Is it on boundaries?
1903  if (boundaries_pt!=0)
1904  {
1905  // Create new boundary node
1906  Node* new_node_pt=finite_element_pt(e)->
1907  construct_boundary_node(j,time_stepper_pt);
1908 
1909  // Give it a number (not necessarily the global node
1910  // number in the scaffold mesh -- we just need something
1911  // to keep track...)
1912  global_count++;
1913  global_number[scaffold_node_pt]=global_count;
1914 
1915  // Add to boundaries
1916  for(std::set<unsigned>::iterator it=boundaries_pt->begin();
1917  it!=boundaries_pt->end();++it)
1918  {
1919  add_boundary_node(*it,new_node_pt);
1920  }
1921  }
1922  // Build normal node
1923  else
1924  {
1925  // Create new normal node
1926  finite_element_pt(e)->construct_node(j,time_stepper_pt);
1927 
1928  // Give it a number (not necessarily the global node
1929  // number in the scaffold mesh -- we just need something
1930  // to keep track...)
1931  global_count++;
1932  global_number[scaffold_node_pt]=global_count;
1933  }
1934 
1935  // Copy new node, created using the NEW element's construct_node
1936  // function into global storage, using the same global
1937  // node number that we've just associated with the
1938  // corresponding node in the scaffold mesh
1939  Node_pt[global_count-1]=finite_element_pt(e)->node_pt(j);
1940 
1941  // Assign coordinates
1942  Node_pt[global_count-1]->x(0)=scaffold_node_pt->x(0);
1943  Node_pt[global_count-1]->x(1)=scaffold_node_pt->x(1);
1944  Node_pt[global_count-1]->x(2)=scaffold_node_pt->x(2);
1945 
1946  }
1947  // This one has already been done: Copy across
1948  else
1949  {
1950  finite_element_pt(e)->node_pt(j)=Node_pt[j_global-1];
1951  }
1952  }
1953 
1954 
1955  // Now figure out which boundaries the faces are on
1956  FiniteElement* fe_pt=finite_element_pt(e);
1957  for (unsigned f=0;f<4;f++)
1958  {
1959  Node* face_node0_pt=0;
1960  Node* face_node1_pt=0;
1961  Node* face_node2_pt=0;
1962 
1963  switch(f)
1964  {
1965  case 0:
1966  face_node0_pt=fe_pt->node_pt(1);
1967  face_node1_pt=fe_pt->node_pt(2);
1968  face_node2_pt=fe_pt->node_pt(3);
1969  break;
1970 
1971  case 1:
1972  face_node0_pt=fe_pt->node_pt(0);
1973  face_node1_pt=fe_pt->node_pt(2);
1974  face_node2_pt=fe_pt->node_pt(3);
1975  break;
1976 
1977  case 2:
1978  face_node0_pt=fe_pt->node_pt(0);
1979  face_node1_pt=fe_pt->node_pt(1);
1980  face_node2_pt=fe_pt->node_pt(3);
1981  break;
1982 
1983  case 3:
1984  face_node0_pt=fe_pt->node_pt(0);
1985  face_node1_pt=fe_pt->node_pt(1);
1986  face_node2_pt=fe_pt->node_pt(2);
1987  break;
1988 
1989  default:
1990 
1991  std::ostringstream error_message;
1992  error_message << "Wrong face number " << f << std::endl;
1993  throw OomphLibError(error_message.str(),
1994  OOMPH_CURRENT_FUNCTION,
1995  OOMPH_EXCEPTION_LOCATION);
1996 
1997  }
1998 
1999  // If any of the boundary sets are empty we don't have
2000  // three nodes on the boundary...
2001  std::set<unsigned>* bc0_pt;
2002  face_node0_pt->get_boundaries_pt(bc0_pt);
2003  if (bc0_pt!=0)
2004  {
2005  std::set<unsigned>* bc1_pt;
2006  face_node1_pt->get_boundaries_pt(bc1_pt);
2007  if (bc1_pt!=0)
2008  {
2009  std::set<unsigned>* bc2_pt;
2010  face_node2_pt->get_boundaries_pt(bc2_pt);
2011  if (bc2_pt!=0)
2012  {
2013  std::set<unsigned> common_bound_0_and_1;
2014  std::set_intersection(bc0_pt->begin(),bc0_pt->end(),
2015  bc1_pt->begin(),bc1_pt->end(),
2016  std::inserter(common_bound_0_and_1,
2017  common_bound_0_and_1.begin()));
2018  std::set<unsigned> common_bound_0_and_1_and_2;
2019  std::set_intersection
2020  (common_bound_0_and_1.begin(),
2021  common_bound_0_and_1.end(),
2022  bc2_pt->begin(),bc2_pt->end(),
2023  std::inserter(common_bound_0_and_1_and_2,
2024  common_bound_0_and_1_and_2.begin()));
2025  for (std::set<unsigned>::iterator it=
2026  common_bound_0_and_1_and_2.begin();
2027  it!=common_bound_0_and_1_and_2.end();it++)
2028  {
2029  Boundary_element_pt[(*it)].push_back(fe_pt);
2030  Face_index_at_boundary[(*it)].push_back(f);
2031  }
2032  }
2033  }
2034  }
2035  }
2036  }
2037 
2038  // Copy across region information (scaffold mesh is a friend)
2039  unsigned nr=tmp_scaffold_mesh_pt->Region_element_pt.size();
2040  Region_attribute.resize(nr);
2041  Region_element_pt.resize(nr);
2042  for (unsigned i=0;i<nr;i++)
2043  { //--
2044  Region_attribute[i]=tmp_scaffold_mesh_pt->Region_attribute[i];
2045  unsigned nel=tmp_scaffold_mesh_pt->Region_element_pt[i].size();
2046  Region_element_pt[i].resize(nel);
2047  for (unsigned e=0;e<nel;e++)
2048  {
2049  FiniteElement* scaff_el_pt=tmp_scaffold_mesh_pt->Region_element_pt[i][e];
2050  unsigned scaff_el_number=scaffold_mesh_element_number[scaff_el_pt];
2051  Region_element_pt[i][e]=
2052  dynamic_cast<FiniteElement*>(Element_pt[scaff_el_number]);
2053 
2054 
2055 
2056  // Now figure out which boundaries the faces are on (again!)
2057  FiniteElement* fe_pt=Region_element_pt[i][e];
2058  for (unsigned f=0;f<4;f++)
2059  {
2060  Node* face_node0_pt=0;
2061  Node* face_node1_pt=0;
2062  Node* face_node2_pt=0;
2063 
2064  switch(f)
2065  {
2066  case 0:
2067  face_node0_pt=fe_pt->node_pt(1);
2068  face_node1_pt=fe_pt->node_pt(2);
2069  face_node2_pt=fe_pt->node_pt(3);
2070  break;
2071 
2072  case 1:
2073  face_node0_pt=fe_pt->node_pt(0);
2074  face_node1_pt=fe_pt->node_pt(2);
2075  face_node2_pt=fe_pt->node_pt(3);
2076  break;
2077 
2078  case 2:
2079  face_node0_pt=fe_pt->node_pt(0);
2080  face_node1_pt=fe_pt->node_pt(1);
2081  face_node2_pt=fe_pt->node_pt(3);
2082  break;
2083 
2084  case 3:
2085  face_node0_pt=fe_pt->node_pt(0);
2086  face_node1_pt=fe_pt->node_pt(1);
2087  face_node2_pt=fe_pt->node_pt(2);
2088  break;
2089 
2090  default:
2091  std::ostringstream error_message;
2092  error_message << "Wrong face number " << f << std::endl;
2093  throw OomphLibError(error_message.str(),
2094  OOMPH_CURRENT_FUNCTION,
2095  OOMPH_EXCEPTION_LOCATION);
2096  }
2097 
2098  // If any of the boundary sets are empty we don't have
2099  // three nodes on the boundary...
2100  std::set<unsigned>* bc0_pt;
2101  face_node0_pt->get_boundaries_pt(bc0_pt);
2102  if (bc0_pt!=0)
2103  {
2104  std::set<unsigned>* bc1_pt;
2105  face_node1_pt->get_boundaries_pt(bc1_pt);
2106  if (bc1_pt!=0)
2107  {
2108  std::set<unsigned>* bc2_pt;
2109  face_node2_pt->get_boundaries_pt(bc2_pt);
2110  if (bc2_pt!=0)
2111  {
2112  std::set<unsigned> common_bound_0_and_1;
2113  std::set_intersection
2114  (bc0_pt->begin(),bc0_pt->end(),
2115  bc1_pt->begin(),bc1_pt->end(),
2116  std::inserter(common_bound_0_and_1,
2117  common_bound_0_and_1.begin()));
2118  std::set<unsigned> common_bound_0_and_1_and_2;
2119  std::set_intersection
2120  (common_bound_0_and_1.begin(),
2121  common_bound_0_and_1.end(),
2122  bc2_pt->begin(),bc2_pt->end(),
2123  std::inserter(common_bound_0_and_1_and_2,
2124  common_bound_0_and_1_and_2.begin()));
2125  for (std::set<unsigned>::iterator it=
2126  common_bound_0_and_1_and_2.begin();
2127  it!=common_bound_0_and_1_and_2.end();it++)
2128  {
2129  Boundary_region_element_pt[(*it)][Region_attribute[i]].
2130  push_back(fe_pt);
2131  Face_index_region_at_boundary[(*it)][Region_attribute[i]].
2132  push_back(f);
2133  }
2134  }
2135  }
2136  }
2137  }
2138  }
2139  }
2140 
2141  //Lookup scheme has now been setup
2142  Lookup_for_elements_next_boundary_is_setup=true;
2143 
2144 
2145  // At this point we've created all the elements and
2146  // created their vertex nodes. Now we need to create
2147  // the additional (midside and internal) nodes!
2148 
2149  // Get number of nodes along element edge
2150  unsigned n_node_1d=finite_element_pt(0)->nnode_1d();
2151 
2152  // At the moment we're only able to deal with nnode_1d=2 or 3.
2153  if ((n_node_1d!=2)&&(n_node_1d!=3))
2154  {
2155  std::ostringstream error_message;
2156  error_message << "Mesh generation from gmsh currently only works\n";
2157  error_message << "for nnode_1d = 2 or 3. You're trying to use it\n";
2158  error_message << "for nnode_1d=" << n_node_1d << std::endl;
2159  throw OomphLibError(error_message.str(),
2160  OOMPH_CURRENT_FUNCTION,
2161  OOMPH_EXCEPTION_LOCATION);
2162  }
2163 
2164  // Storage for the local coordinate of the new node
2165  Vector<double> s(3);
2166 
2167  // Map associating edge with midside node
2168  std::map<std::pair<Node*,Node*>,Node*> midside_node_pt;
2169 
2170  // Loop over all elements
2171  for(unsigned e=0;e<nelem;e++)
2172  {
2173  //Cache pointers to the elements
2174  FiniteElement* const el_pt = this->finite_element_pt(e);
2175  FiniteElement* const simplex_el_pt =
2176  tmp_scaffold_mesh_pt->finite_element_pt(e);
2177 
2178  //Loop over the edges
2179  for(unsigned j=0;j<6;++j)
2180  {
2181  Node* nod0_pt=0;
2182  Node* nod1_pt=0;
2183  unsigned new_node_number=0;
2184  std::pair<Node*,Node*> edge;
2185  switch(j)
2186  {
2187  case 0:
2188  nod0_pt=el_pt->node_pt(0);
2189  nod1_pt=el_pt->node_pt(1);
2190  new_node_number=4;
2191  break;
2192 
2193  case 1:
2194  nod0_pt=el_pt->node_pt(0);
2195  nod1_pt=el_pt->node_pt(2);
2196  new_node_number=5;
2197  break;
2198 
2199  case 2:
2200  nod0_pt=el_pt->node_pt(0);
2201  nod1_pt=el_pt->node_pt(3);
2202  new_node_number=6;
2203  break;
2204 
2205  case 3:
2206  nod0_pt=el_pt->node_pt(1);
2207  nod1_pt=el_pt->node_pt(2);
2208  new_node_number=7;
2209  break;
2210 
2211  case 4:
2212  nod0_pt=el_pt->node_pt(2);
2213  nod1_pt=el_pt->node_pt(3);
2214  new_node_number=8;
2215  break;
2216 
2217  case 5:
2218  nod0_pt=el_pt->node_pt(1);
2219  nod1_pt=el_pt->node_pt(3);
2220  new_node_number=9;
2221  break;
2222 
2223  default:
2224  std::ostringstream error_message;
2225  error_message << "Wrong edge number " << j << std::endl;
2226  throw OomphLibError(error_message.str(),
2227  OOMPH_CURRENT_FUNCTION,
2228  OOMPH_EXCEPTION_LOCATION);
2229  }
2230 
2231  // Identify existence of node via edge
2232  edge=std::make_pair(std::min(nod0_pt,nod1_pt),
2233  std::max(nod0_pt,nod1_pt));
2234  Node* existing_node_pt=midside_node_pt[edge];
2235  if (existing_node_pt==0)
2236  {
2237  // If any of the boundary sets are empty we don't have
2238  // two edge nodes on the boundary...
2239  std::set<unsigned> common_bound_0_and_1;
2240  std::set<unsigned>* bc0_pt;
2241  nod0_pt->get_boundaries_pt(bc0_pt);
2242  if (bc0_pt!=0)
2243  {
2244  std::set<unsigned>* bc1_pt;
2245  nod1_pt->get_boundaries_pt(bc1_pt);
2246  if (bc1_pt!=0)
2247  {
2248  std::set_intersection(bc0_pt->begin(),bc0_pt->end(),
2249  bc1_pt->begin(),bc1_pt->end(),
2250  std::inserter(common_bound_0_and_1,
2251  common_bound_0_and_1.begin()));
2252  }
2253  }
2254  //Storage for the new node
2255  Node* new_node_pt = 0;
2256 
2257  // New non-boundary node:
2258  if (common_bound_0_and_1.size()==0)
2259  {
2260  new_node_pt=el_pt->construct_node(new_node_number,time_stepper_pt);
2261  }
2262  // New boundary node
2263  else
2264  {
2265  new_node_pt=el_pt->construct_boundary_node
2266  (new_node_number,time_stepper_pt);
2267  for (std::set<unsigned>::iterator it=common_bound_0_and_1.begin();
2268  it!=common_bound_0_and_1.end();it++)
2269  {
2270  this->add_boundary_node((*it),new_node_pt);
2271  }
2272  }
2273 
2274  // Find the local coordinates of the node
2275  el_pt->local_coordinate_of_node(new_node_number,s);
2276 
2277  // Find the coordinates of the new node from the existing
2278  // and fully-functional element in the scaffold mesh
2279  for(unsigned i=0;i<3;i++)
2280  {
2281  new_node_pt->x(i)=simplex_el_pt->interpolated_x(s,i);
2282  }
2283 
2284  // Associate node with edge
2285  midside_node_pt[edge]=new_node_pt;
2286 
2287  // Add node to mesh
2288  Node_pt.push_back(new_node_pt);
2289  }
2290  // Node already exists
2291  else
2292  {
2293  el_pt->node_pt(new_node_number)=existing_node_pt;
2294  }
2295  }
2296  }
2297 
2298  }
2299 
2300  };
2301 
2302 
2303 
2304 //////////////////////////////////////////////////////////////////////////
2305 //////////////////////////////////////////////////////////////////////////
2306 //////////////////////////////////////////////////////////////////////////
2307 
2308 
2309 
2310 //=========================================================================
2311 // Refineable Gmsh-based Tet Mesh
2312 //=========================================================================
2313  template<class ELEMENT>
2314  class RefineableGmshTetMesh : public virtual GmshTetMesh<ELEMENT>,
2315  public virtual RefineableTetMeshBase
2316  {
2317 
2318  public:
2319 
2320  /// \short Constructor. If boolean is set
2321  /// to true, the target element sizes are read from file (used during
2322  /// adaptation; otherwise uniform target size is used).
2324  const bool& use_mesh_grading_from_file,
2325  TimeStepper* time_stepper_pt=
2326  &Mesh::Default_TimeStepper) :
2327  GmshTetMesh<ELEMENT>(gmsh_parameters_pt,
2328  use_mesh_grading_from_file,
2329  time_stepper_pt)
2330  {
2331  initialise_adaptation_data();
2332  }
2333 
2334  /// \short Constructor
2336  (GmshParameters* gmsh_parameters_pt,
2337  TimeStepper* time_stepper_pt=&Mesh::Default_TimeStepper) :
2338  GmshTetMesh<ELEMENT>(gmsh_parameters_pt,
2339  false, // Don't read mesh size data from file unless
2340  // explicitly requested.
2341  time_stepper_pt)
2342  {
2343  initialise_adaptation_data();
2344  }
2345 
2346 
2347  /// Adapt mesh, based on elemental error provided
2348  void adapt(const Vector<double>& elem_error);
2349 
2350  /// Refine uniformly
2352  {
2353  // hierher do it!
2354  std::string error_msg("Not written yet...");
2355  throw OomphLibError(error_msg,
2356  OOMPH_CURRENT_FUNCTION,
2357  OOMPH_EXCEPTION_LOCATION);
2358  }
2359 
2360  /// Unrefine uniformly
2361  void refine_uniformly(DocInfo& doc_info)
2362  {
2363  // hierher do it!
2364  std::string error_msg("Not written yet...");
2365  throw OomphLibError(error_msg,
2366  OOMPH_CURRENT_FUNCTION,
2367  OOMPH_EXCEPTION_LOCATION);
2368  }
2369 
2370 
2371  protected:
2372 
2373  /// Helper function to initialise data associated with adaptation
2375  {
2376  // Set max and min targets for adaptation
2377  this->Max_element_size=this->Gmsh_parameters_pt->max_element_size();
2378  this->Min_element_size=this->Gmsh_parameters_pt->min_element_size();
2380  this->Gmsh_parameters_pt->max_permitted_edge_ratio();
2381  }
2382 
2383  };
2384 
2385 
2386 
2387 /////////////////////////////////////////////////////////////////////////////
2388 /////////////////////////////////////////////////////////////////////////////
2389 /////////////////////////////////////////////////////////////////////////////
2390 
2391 
2392 
2393 
2394 }
2395 
2396 #endif
void create_mesh_from_msh_file()
Create mesh from msh file (created internally via disk-based operations)
bool Reversed
Is it reversed? I.e. is the first input vertex stored after the second one?
std::string Stem_for_filename_gmsh_size_transfer
Stem for filename used to doc target element sizes on gmsh grid. No doc if stem is equal to empty str...
std::string & stem_for_filename_gmsh_size_transfer()
Stem for filename used to doc target element sizes on gmsh grid. No doc if stem is equal to empty str...
unsigned Gmsh_onscreen_output_counter
Counter for marker that indicates where we are in gmsh on-screen output.
int & counter_for_filename_gmsh_size_transfer()
Counter for filename used to doc target element sizes on gmsh grid. No doc if stem is equal to empty ...
bool is_reversed() const
Edge is reversed in the sense that vertex1 actually has a higher id than vertex2 (when specified in t...
void disable_projection()
Disable projection of old solution onto new mesh.
TetMeshFacetedClosedSurface * Outer_boundary_pt
Pointer to outer boundary.
bool projection_is_disabled()
Is projection of old solution onto new mesh disabled?
std::string Target_size_file_name
Filename for target volume (for system-call based transfer to gmsh during mesh adaptation) ...
Forward declaration.
double Max_element_size
Max. element size during refinement.
bool Projection_is_disabled
Is projection of old solution onto new mesh disabled?
GmshTetMesh(GmshParameters *gmsh_parameters_pt, const bool &use_mesh_grading_from_file, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor. If boolean is set to true, the target element sizes are read from file (used during adap...
GmshTetMesh(GmshParameters *gmsh_parameters_pt, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor.
void enable_projection()
Disable projection of old solution onto new mesh.
unsigned & gmsh_onscreen_output_counter()
Counter for marker that indicates where we are in gmsh on-screen output.
unsigned second_vertex_id() const
Second vertex id.
double Min_element_size
Min. element size during refinement.
double & min_element_size()
Min. element size during refinement.
std::string Gmsh_onscreen_output_file_name
Output filename for gmsh on-screen output.
void build_from_scaffold(GmshTetScaffoldMesh *tmp_scaffold_mesh_pt, TimeStepper *time_stepper_pt)
Build unstructured tet gmesh mesh based on output from scaffold.
GmshParameters * Gmsh_parameters_pt
Parameters.
unsigned & max_sample_points_for_limited_locate_zeta_during_target_size_transfer()
Target size is transferred onto regular grid (for gmsh) by locate zeta. We try to find the exact poin...
TetMeshFacetedClosedSurface *& outer_boundary_pt()
Outer boundary.
RefineableGmshTetMesh(GmshParameters *gmsh_parameters_pt, const bool &use_mesh_grading_from_file, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor. If boolean is set to true, the target element sizes are read from file (used during adap...
std::string & target_size_file_name()
Filename for target volumes (for system-call based transfer to gmsh during mesh adaptation). Default: .gmsh_target_size_for_adaptation.dat.
TetEdge(const unsigned &vertex1, const unsigned &vertex2)
Constructor: Pass two vertices, identified by their indices Edge "direction" is from lower vertex to ...
int Counter_for_filename_gmsh_size_transfer
Counter for filename used to doc target element sizes on gmsh grid. No doc if stem is equal to empty ...
void build_it(TimeStepper *time_stepper_pt, const bool &use_mesh_grading_from_file)
unsigned unrefine_uniformly()
Refine uniformly.
double & element_volume()
Uniform target element volume.
double & dx_for_target_size_transfer()
(Isotropic) grid spacing for target size transfer
double & max_element_size()
Max. element size during refinement.
unsigned first_vertex_id() const
First vertex id.
GmshParameters(TetMeshFacetedClosedSurface *const &outer_boundary_pt, const std::string &gmsh_command_line_invocation)
Specify outer boundary of domain to be meshed. Other parameters get default values and can be set via...
Vector< TetMeshFacetedSurface * > Internal_surface_pt
Internal boundaries.
double Element_volume
Uniform element volume.
GmshTetScaffoldMesh(GmshParameters *gmsh_parameters_pt, const bool &use_mesh_grading_from_file)
Build mesh, based on specified parameters. If boolean is set to true, the target element sizes are re...
unsigned Max_sample_points_for_limited_locate_zeta_during_target_size_transfer
Target size is transferred onto regular grid (for gmsh) by locate zeta. We try to find the exact poin...
std::pair< unsigned, unsigned > Vertex_pair
The vertices (sorted by vertex ids)
Class to collate parameters for Gmsh mesh generation.
void write_geo_file(const bool &use_mesh_grading_from_file)
Write geo file for gmsh.
Helper class to keep track of edges in tet mesh generation.
GmshParameters * Gmsh_parameters_pt
Parameters.
std::string & gmsh_onscreen_output_file_name()
Output filename for gmsh on-screen output.
double & max_permitted_edge_ratio()
Max. permitted edge ratio.
void initialise_adaptation_data()
Helper function to initialise data associated with adaptation.
std::string & geo_and_msh_file_stem()
Stem for geo and msh files (input/output to command-line gmsh invocation)
Vector< TetMeshFacetedSurface * > & internal_surface_pt()
Internal boundaries.
double Max_permitted_edge_ratio
Max edge ratio before remesh gets triggered.
std::string Gmsh_command_line_invocation
Gmsh command line invocation string.
std::string Geo_and_msh_file_stem
Stem for geo and msh files (input/output to command-line gmsh invocation)
double Dx_for_target_size_transfer
(Isotropic) grid spacing for target size transfer
void refine_uniformly(DocInfo &doc_info)
Unrefine uniformly.
std::string & gmsh_command_line_invocation()
String to be issued via system command to activate gmsh.