mesh.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 //Non-inline member functions for general mesh classes
31 
32 #ifdef OOMPH_HAS_MPI
33 #include "mpi.h"
34 #endif
35 
36 #include<algorithm>
37 #include<limits.h>
38 #include <typeinfo>
39 
40 
41 //oomph-lib headers
42 #include "oomph_utilities.h"
43 #include "mesh.h"
44 #include "problem.h"
45 #include "elastic_problems.h"
46 #include "refineable_mesh.h"
47 #include "triangle_mesh.h"
48 #include "shape.h"
49 
50 namespace oomph
51 {
52 
53 //======================================================
54 /// The Steady Timestepper
55 //======================================================
57 
58 
59 //=======================================================================
60 /// Static boolean flag to control warning about mesh level timesteppers
61 //=======================================================================
63 
64 //=======================================================================
65 /// Merge meshes.
66 /// Note: This simply merges the meshes' elements and nodes (ignoring
67 /// duplicates; no boundary information etc. is created).
68 //=======================================================================
69  void Mesh::merge_meshes(const Vector<Mesh*>& sub_mesh_pt)
70  {
71  // No boundary lookup scheme is set up for the combined mesh
73 
74  //Number of submeshes
75  unsigned nsub_mesh=sub_mesh_pt.size();
76 
77  // Initialise element, node and boundary counters for global mesh
78  unsigned long n_element=0;
79  unsigned long n_node=0;
80  unsigned n_bound=0;
81 
82  // Loop over submeshes and get total number of elements, nodes and
83  // boundaries
84  for(unsigned imesh=0;imesh<nsub_mesh;imesh++)
85  {
86  n_element += sub_mesh_pt[imesh]->nelement();
87  n_node += sub_mesh_pt[imesh]->nnode();
88  n_bound += sub_mesh_pt[imesh]->nboundary();
89  }
90 
91  // Reserve storage for element and node pointers
92  Element_pt.clear();
93  Element_pt.reserve(n_element);
94  Node_pt.clear();
95  Node_pt.reserve(n_node);
96 
97  //Resize vector of vectors of nodes
98  Boundary_node_pt.clear();
99  Boundary_node_pt.resize(n_bound);
100 
101  // Sets of pointers to elements and nodes (to exlude duplicates -- they
102  // shouldn't occur anyway but if they do, they must only be added
103  // once in the global mesh to avoid trouble in the timestepping)
104  std::set<GeneralisedElement*> element_set_pt;
105  std::set<Node*> node_set_pt;
106 
107  //Counter for total number of boundaries in all the submeshes
108  unsigned ibound_global=0;
109  //Loop over the number of submeshes
110  for(unsigned imesh=0;imesh<nsub_mesh;imesh++)
111  {
112  //Loop over the elements of the submesh and add to vector
113  //duplicates are ignored
114  unsigned nel_before=0;
115  unsigned long n_element=sub_mesh_pt[imesh]->nelement();
116  for (unsigned long e=0;e<n_element;e++)
117  {
118  GeneralisedElement* el_pt=sub_mesh_pt[imesh]->element_pt(e);
119  element_set_pt.insert(el_pt);
120  // Was it a duplicate?
121  unsigned nel_now=element_set_pt.size();
122  if (nel_now==nel_before)
123  {
124  std::ostringstream warning_stream;
125  warning_stream <<"WARNING: " << std::endl
126  <<"Element " << e << " in submesh " << imesh
127  <<" is a duplicate \n and was ignored when assembling"
128  <<" combined mesh." << std::endl;
129  OomphLibWarning(warning_stream.str(),
130  "Mesh::Mesh(const Vector<Mesh*>&)",
131  OOMPH_EXCEPTION_LOCATION);
132  }
133  else
134  {
135  Element_pt.push_back(el_pt);
136  }
137  nel_before=nel_now;
138  }
139 
140  //Loop over the nodes of the submesh and add to vector
141  //duplicates are ignored
142  unsigned nnod_before=0;
143  unsigned long n_node=sub_mesh_pt[imesh]->nnode();
144  for (unsigned long n=0;n<n_node;n++)
145  {
146  Node* nod_pt=sub_mesh_pt[imesh]->node_pt(n);
147  node_set_pt.insert(nod_pt);
148  // Was it a duplicate?
149  unsigned nnod_now=node_set_pt.size();
150  if (nnod_now==nnod_before)
151  {
152  std::ostringstream warning_stream;
153  warning_stream<<"WARNING: " << std::endl
154  <<"Node " << n << " in submesh " << imesh
155  <<" is a duplicate \n and was ignored when assembling "
156  << "combined mesh." << std::endl;
157  OomphLibWarning(warning_stream.str(),
158  "Mesh::Mesh(const Vector<Mesh*>&)",
159  OOMPH_EXCEPTION_LOCATION);
160  }
161  else
162  {
163  Node_pt.push_back(nod_pt);
164  }
165  nnod_before=nnod_now;
166  }
167 
168  //Loop over the boundaries of the submesh
169  unsigned n_bound=sub_mesh_pt[imesh]->nboundary();
170  for (unsigned ibound=0;ibound<n_bound;ibound++)
171  {
172  //Loop over the number of nodes on the boundary and add to the
173  //global vector
174  unsigned long n_bound_node=sub_mesh_pt[imesh]->nboundary_node(ibound);
175  for (unsigned long n=0;n<n_bound_node;n++)
176  {
177  Boundary_node_pt[ibound_global].push_back(
178  sub_mesh_pt[imesh]->boundary_node_pt(ibound,n));
179  }
180  //Increase the number of the global boundary counter
181  ibound_global++;
182  }
183  } //End of loop over submeshes
184 
185  }
186 
187 
188 //========================================================
189 /// Remove the information about nodes stored on the
190 /// b-th boundary of the mesh
191 //========================================================
192 void Mesh::remove_boundary_nodes(const unsigned &b)
193 {
194  //Loop over all the nodes on the boundary and call
195  //their remove_from_boundary function
196  unsigned n_boundary_node = Boundary_node_pt[b].size();
197  for(unsigned n=0;n<n_boundary_node;n++)
198  {
200  }
201  //Clear the storage
202  Boundary_node_pt[b].clear();
203 }
204 
205 //=================================================================
206 /// Remove all information about mesh boundaries
207 //================================================================
209 {
210  //Loop over each boundary call remove_boundary_nodes
211  unsigned n_bound = Boundary_node_pt.size();
212  for(unsigned b=0;b<n_bound;b++) {remove_boundary_nodes(b);}
213  //Clear the storage
214  Boundary_node_pt.clear();
215 }
216 
217 //============================================================
218 /// Remove the node node_pt from the b-th boundary of the mesh
219 /// This function also removes the information from the Node
220 /// itself
221 //===========================================================
222 void Mesh::remove_boundary_node(const unsigned &b, Node* const &node_pt)
223 {
224  //Find the location of the node in the boundary
226  std::find(Boundary_node_pt[b].begin(),
227  Boundary_node_pt[b].end(),
228  node_pt);
229  //If the node is on this boundary
230  if(it!=Boundary_node_pt[b].end())
231  {
232  //Remove the node from the mesh's list of boundary nodes
233  Boundary_node_pt[b].erase(it);
234  //Now remove the node's boundary information
235  node_pt->remove_from_boundary(b);
236  }
237  //If not do nothing
238 }
239 
240 
241 //========================================================
242 /// Add the node node_pt to the b-th boundary of the mesh
243 /// This function also sets the boundary information in the
244 /// Node itself
245 //=========================================================
246 void Mesh::add_boundary_node(const unsigned &b, Node* const &node_pt)
247 {
248  //Tell the node that it's on boundary b.
249  //At this point, if the node is not a BoundaryNode, the function
250  //should throw an exception.
251  node_pt->add_to_boundary(b);
252 
253  //Get the size of the Boundary_node_pt vector
254  unsigned nbound_node=Boundary_node_pt[b].size();
255  bool node_already_on_this_boundary=false;
256  //Loop over the vector
257  for (unsigned n=0; n<nbound_node; n++)
258  {
259  // is the current node here already?
260  if (node_pt==Boundary_node_pt[b][n])
261  {
262  node_already_on_this_boundary=true;
263  }
264  }
265 
266  //Add the base node pointer to the vector if it's not there already
267  if (!node_already_on_this_boundary)
268  {
269  Boundary_node_pt[b].push_back(node_pt);
270  }
271 
272 }
273 
274 //=======================================================
275 /// Update nodal positions in response to changes in the domain shape.
276 /// Uses the FiniteElement::get_x(...) function for FiniteElements
277 /// and doesn't do anything for other element types.
278 /// If a MacroElement pointer has been set for a FiniteElement,
279 /// the MacroElement representation is used to update the
280 /// nodal positions; if not get_x(...) uses the FE interpolation
281 /// and thus leaves the nodal positions unchanged.
282 /// Virtual, so it can be overloaded by specific meshes,
283 /// such as AlgebraicMeshes or SpineMeshes.
284 /// Generally, this function updates the position of all nodes
285 /// in response to changes in the boundary position. For
286 /// SolidNodes it only applies the update to those SolidNodes
287 /// whose position is determined by the boundary position, unless
288 /// the bool flag is set to true.
289 //========================================================
290 void Mesh::node_update(const bool& update_all_solid_nodes)
291 {
292 #ifdef PARANOID
293 #ifdef OOMPH_HAS_MPI
294  //Paranoid check to throw an error if node update is called for elements
295  //with nonuniformly spaced nodes for which some masters are 'external'
296  for(unsigned long n=0;n<nnode();n++)
297  {
298  Node* nod_pt = Node_pt[n];
299  if (nod_pt->is_hanging())
300  {
301  //Loop over master nodes
302  unsigned nmaster=nod_pt->hanging_pt()->nmaster();
303  for (unsigned imaster=0;imaster<nmaster;imaster++)
304  {
305  //Get pointer to master node
306  Node* master_nod_pt = nod_pt->hanging_pt()->master_node_pt(imaster);
307 
308  //Get vector of all external halo nodes
310  get_external_halo_node_pt(external_halo_node_pt);
311 
312  //Search the external halo storage for this node
314  = std::find(external_halo_node_pt.begin(),
315  external_halo_node_pt.end(),
316  master_nod_pt);
317 
318  //Check if the node was found
319  if(it != external_halo_node_pt.end())
320  {
321  //Throw error becase node update won't work
322  //It's ok to throw an error here because this function is
323  //overloaded for Algebraic and MacroElementNodeUpdate
324  //Meshes. This is only a problem for meshes of ordinary
325  //nodes.
326  std::ostringstream err_stream;
327 
328  err_stream << "Calling node_update() for a mesh which contains"
329  << std::endl
330  << "master nodes which live in the external storage."
331  << std::endl
332  << "These nodes do not belong to elements on this"
333  << std::endl
334  << "processor and therefore cannot be updated locally."
335  << std::endl;
336 
337  throw OomphLibError(err_stream.str(),
338  OOMPH_CURRENT_FUNCTION,
339  OOMPH_EXCEPTION_LOCATION);
340  }
341  }
342  }
343  }
344  //If we get to here then none of the masters of any of the nodes in the
345  //mesh live in the external storage, so we'll be fine if we carry on.
346 #endif
347 #endif
348 
349  /// Local and global (Eulerian) coordinate
351  Vector<double> r;
352 
353  // NB: This repeats nodes a lot - surely it would be
354  // quicker to modify it so that it only does each node once,
355  // particularly in the update_all_solid_nodes=true case?
356 
357  // Loop over all elements
358  unsigned nel=nelement();
359  for (unsigned e=0;e<nel;e++)
360  {
361  // Try to cast to FiniteElement
362  FiniteElement* el_pt = dynamic_cast<FiniteElement*>(element_pt(e));
363 
364  // If it's a finite element we can proceed: FiniteElements have
365  // nodes and a get_x() function
366  if (el_pt!=0)
367  {
368  // Find out dimension of element = number of local coordinates
369  unsigned ndim_el=el_pt->dim();
370  s.resize(ndim_el);
371 
372  //Loop over nodal points
373  unsigned n_node=el_pt->nnode();
374  for (unsigned j=0;j<n_node;j++)
375  {
376 
377  // Get pointer to node
378  Node* nod_pt=el_pt->node_pt(j);
379 
380  // Get spatial dimension of node
381  unsigned ndim_node=nod_pt->ndim();
382  r.resize(ndim_node);
383 
384  // For non-hanging nodes
385  if (!(nod_pt->is_hanging()))
386  {
387  //Get the position of the node
388  el_pt->local_coordinate_of_node(j,s);
389 
390  // Get new position
391  el_pt->get_x(s,r);
392 
393  // Try to cast to SolidNode
394  SolidNode* solid_node_pt=dynamic_cast<SolidNode*>(nod_pt);
395 
396  // Loop over coordinate directions
397  for (unsigned i=0;i<ndim_node;i++)
398  {
399  // It's a SolidNode:
400  if (solid_node_pt!=0)
401  {
402  // only do it if explicitly requested!
403  if (update_all_solid_nodes)
404  {
405  solid_node_pt->x(i) = r[i];
406  }
407  }
408  // Not a SolidNode: Definitely update
409  else
410  {
411  nod_pt->x(i) = r[i];
412  }
413  }
414  }
415  }
416  }
417  }
418 
419  // Now update the external halo nodes before we adjust the positions of the
420  // hanging nodes incase any are masters of local nodes
421 #ifdef OOMPH_HAS_MPI
422  // Loop over all external halo nodes with other processors
423  // and update them
424  for (std::map<unsigned, Vector<Node*> >::iterator it=
425  External_halo_node_pt.begin();it!=External_halo_node_pt.end();it++)
426  {
427  // Get vector of external halo nodes
428  Vector<Node*> ext_halo_node_pt=(*it).second;
429  unsigned nnod=ext_halo_node_pt.size();
430  for (unsigned j=0;j<nnod;j++)
431  {
432  ext_halo_node_pt[j]->node_update();
433  }
434  }
435 #endif
436 
437  // Now loop over hanging nodes and adjust their position
438  // in line with their hanging node constraints
439  unsigned long n_node = nnode();
440  for(unsigned long n=0;n<n_node;n++)
441  {
442  Node* nod_pt = Node_pt[n];
443  if (nod_pt->is_hanging())
444  {
445  // Get spatial dimension of node
446  unsigned ndim_node=nod_pt->ndim();
447 
448  // Initialise
449  for (unsigned i=0;i<ndim_node;i++)
450  {
451  nod_pt->x(i)=0.0;
452  }
453 
454  //Loop over master nodes
455  unsigned nmaster=nod_pt->hanging_pt()->nmaster();
456  for (unsigned imaster=0;imaster<nmaster;imaster++)
457  {
458  // Loop over directions
459  for (unsigned i=0;i<ndim_node;i++)
460  {
461  nod_pt->x(i)+=nod_pt->hanging_pt()->
462  master_node_pt(imaster)->x(i)*
463  nod_pt->hanging_pt()->master_weight(imaster);
464  }
465  }
466  }
467  }
468 
469  // Loop over all nodes again and execute auxiliary node update
470  // function
471  for(unsigned long n=0;n<n_node;n++)
472  {
473  Node_pt[n]->perform_auxiliary_node_update_fct();
474  }
475 
476 }
477 
478 
479 //=======================================================
480 /// Reorder nodes in the order in which they are
481 /// encountered when stepping through the elements
482 //========================================================
483  void Mesh::reorder_nodes(const bool& use_old_ordering)
484  {
485  Vector<Node*> reordering;
486  get_node_reordering(reordering, use_old_ordering);
487 
488  unsigned n_node = nnode();
489  for(unsigned i=0; i<n_node; i++)
490  {
491  node_pt(i) = reordering[i];
492  }
493  }
494 
495 //=======================================================
496 /// Get a vector of the nodes in the order in which they are encountered
497 /// when stepping through the elements (similar to reorder_nodes() but
498 /// without changing the mesh's node vector).
499 //========================================================
501  const bool& use_old_ordering) const
502  {
503  if(use_old_ordering)
504  {
505  // Setup map to check if nodes have been done yet
506  std::map<Node*,bool> done;
507 
508  // Loop over all nodes
509  unsigned nnod=nnode();
510 
511  // Initialise the vector
512  reordering.assign(nnod,0);
513 
514  // Return immediately if there are no nodes: Note assumption:
515  // Either all the elements' nodes stored here or none.
516  // If only a subset is stored in the Node_pt vector we'll get
517  // a range checking error below (only if run with range, checking,
518  // of course).
519  if (nnod==0) return;
520  for (unsigned j=0;j<nnod;j++)
521  {
522  done[node_pt(j)]=false;
523  }
524 
525  // Initialise counter for number of nodes
526  unsigned long count=0;
527 
528  // Loop over all elements
529  unsigned nel=nelement();
530  for (unsigned e=0;e<nel;e++)
531  {
532  // Loop over nodes in element
534  unsigned nnod=el_pt->nnode();
535  for (unsigned j=0;j<nnod;j++)
536  {
537  Node* nod_pt=el_pt->node_pt(j);
538  // Has node been done yet?
539  if (!done[nod_pt])
540  {
541  // Insert into node vector. NOTE: If you get a seg fault/range checking
542  // error here then you probably haven't added all the elements' nodes
543  // to the Node_pt vector -- this is most likely to arise in the
544  // case of meshes of face elements (though they usually don't
545  // store the nodes at all so if you have any problems here there's
546  // something unusual/not quite right in any case... For this
547  // reason we don't range check here by default (not even under
548  // paranoia) but force you turn on proper (costly) range checking
549  // to track this down...
550  reordering[count]=nod_pt;
551  done[nod_pt]=true;
552  // Increase counter
553  count++;
554  }
555  }
556  }
557 
558  // Sanity check
559  if (count!=nnod)
560  {
561  throw OomphLibError(
562  "Trouble: Number of nodes hasn't stayed constant during reordering!\n",
563  OOMPH_CURRENT_FUNCTION,
564  OOMPH_EXCEPTION_LOCATION);
565  }
566 
567  }
568  else
569  {
570  // Copy node vector out
571  unsigned n_node = nnode();
572  reordering.resize(n_node);
573  for(unsigned i=0; i<n_node; i++)
574  {
575  reordering[i] = node_pt(i);
576  }
577 
578  // and sort it
579  std::sort(reordering.begin(), reordering.end(),
581  }
582  }
583 
584 
585 //========================================================
586 /// Virtual Destructor to clean up all memory
587 //========================================================
589 {
590  //Free the nodes first
591  //Loop over the nodes in reverse order
592  unsigned long Node_pt_range = Node_pt.size();
593  for(unsigned long i=Node_pt_range;i>0;i--)
594  {
595  delete Node_pt[i-1]; Node_pt[i-1] = 0;
596  }
597 
598  //Free the elements
599  //Loop over the elements in reverse order
600  unsigned long Element_pt_range = Element_pt.size();
601  for(unsigned long i=Element_pt_range;i>0;i--)
602  {
603  delete Element_pt[i-1]; Element_pt[i-1] = 0;
604  }
605 
606  // Wipe the storage for all externally-based elements and delete halos
608 
609 }
610 
611 //========================================================
612 /// Assign (global) equation numbers to the nodes
613 //========================================================
615 {
616  //Find out the current number of equations
617  unsigned long equation_number=Dof_pt.size();
618 
619  //Loop over the nodes and call their assigment functions
620  unsigned long nnod = Node_pt.size();
621 
622  for(unsigned long i=0;i<nnod;i++)
623  {
624  Node_pt[i]->assign_eqn_numbers(equation_number,Dof_pt);
625  }
626 
627  //Loop over the elements and number their internals
628  unsigned long nel = Element_pt.size();
629  for(unsigned long i=0;i<nel;i++)
630  {
631  Element_pt[i]->assign_internal_eqn_numbers(equation_number,Dof_pt);
632  }
633 
634  //Return the total number of equations
635  return(equation_number);
636 }
637 
638 //========================================================
639 /// \short Function to describe the dofs of the Mesh. The ostream
640 /// specifies the output stream to which the description
641 /// is written; the string stores the currently
642 /// assembled output that is ultimately written to the
643 /// output stream by Data::describe_dofs(...); it is typically
644 /// built up incrementally as we descend through the
645 /// call hierarchy of this function when called from
646 /// Problem::describe_dofs(...)
647 //========================================================
648 void Mesh::describe_dofs(std::ostream& out,
649  const std::string& current_string) const
650 {
651  //Loop over the nodes and call their classification functions
652  unsigned long nnod = Node_pt.size();
653  for(unsigned long i=0;i<nnod;i++)
654  {
655  std::stringstream conversion;
656  conversion << " of Node " << i << current_string;
657  std::string in(conversion.str());
658  Node_pt[i]->describe_dofs(out,in);
659  }
660 
661  //Loop over the elements and classify.
662  unsigned long nel = Element_pt.size();
663  for(unsigned long i=0;i<nel;i++)
664  {
665  std::stringstream conversion;
666  conversion <<" in Element "<<i<<" ["<<typeid(*Element_pt[i]).name()<<"] "
667  <<current_string;
668  std::string in(conversion.str());
669  Element_pt[i]->describe_dofs(out,in);
670  }
671 }
672 
673 //========================================================
674 /// \short Function to describe the local dofs of the elements. The ostream
675 /// specifies the output stream to which the description
676 /// is written; the string stores the currently
677 /// assembled output that is ultimately written to the
678 /// output stream by Data::describe_dofs(...); it is typically
679 /// built up incrementally as we descend through the
680 /// call hierarchy of this function when called from
681 /// Problem::describe_dofs(...)
682 //========================================================
683 void Mesh::describe_local_dofs(std::ostream& out,
684  const std::string& current_string) const
685 {
686  //Now loop over the elements and describe local dofs
687  unsigned long nel = Element_pt.size();
688  for(unsigned long i=0;i<nel;i++)
689  {
690  std::stringstream conversion;
691  conversion <<" in Element"<<i<<" ["<<typeid(*Element_pt[i]).name()<<"] "
692  << current_string;
693  std::string in(conversion.str());
694  Element_pt[i]->describe_local_dofs(out,in);
695  }
696 }
697 
698 
699 //========================================================
700 /// Assign local equation numbers in all elements
701 //========================================================
702 void Mesh::assign_local_eqn_numbers(const bool &store_local_dof_pt)
703 {
704  //Now loop over the elements and assign local equation numbers
705  unsigned long Element_pt_range = Element_pt.size();
706  for(unsigned long i=0;i<Element_pt_range;i++)
707  {
708  Element_pt[i]->assign_local_eqn_numbers(store_local_dof_pt);
709  }
710 }
711 
712 //========================================================
713 /// Self-test: Check elements and nodes. Return 0 for OK
714 //========================================================
715 unsigned Mesh::self_test()
716 {
717 
718  // Initialise
719  bool passed=true;
720 
721  // Check the mesh for repeated nodes (issues its own error message)
722  if (0!=check_for_repeated_nodes()) passed=false;
723 
724 // hierher -- re-enable once problem with Hermite elements has been resolved.
725 // // Check if there are any inverted elements
726 // bool mesh_has_inverted_elements=false;
727 // check_inverted_elements(mesh_has_inverted_elements);
728 // if (mesh_has_inverted_elements)
729 // {
730 // passed=false;
731 // oomph_info << "\n ERROR: Mesh has inverted elements\n"
732 // << " Run Mesh::check_inverted_elements(...) with"
733 // << " with output stream to find out which elements are"
734 // << " inverted.\n";
735 // }
736 
737  //Loop over the elements, check for duplicates and do self test
738  std::set<GeneralisedElement*> element_set_pt;
739  unsigned long Element_pt_range = Element_pt.size();
740  for(unsigned long i=0;i<Element_pt_range;i++)
741  {
742  if (Element_pt[i]->self_test()!=0)
743  {
744  passed=false;
745  oomph_info << "\n ERROR: Failed Element::self_test() for element i="
746  << i << std::endl;
747  }
748  // Add to set (which ignores duplicates):
749  element_set_pt.insert(Element_pt[i]);
750  }
751 
752  // Check for duplicates:
753  if (element_set_pt.size()!=Element_pt_range)
754  {
755  oomph_info << "ERROR: " << Element_pt_range-element_set_pt.size()
756  << " duplicate elements were encountered in mesh!" << std::endl;
757  passed=false;
758  }
759 
760 
761  //Loop over the nodes, check for duplicates and do self test
762  std::set<Node*> node_set_pt;
763  unsigned long Node_pt_range = Node_pt.size();
764  for(unsigned long i=0;i<Node_pt_range;i++)
765  {
766  if (Node_pt[i]->self_test()!=0)
767  {
768  passed=false;
769  oomph_info << "\n ERROR: Failed Node::self_test() for node i="
770  << i << std::endl;
771  }
772  // Add to set (which ignores duplicates):
773  node_set_pt.insert(Node_pt[i]);
774  }
775 
776  // Check for duplicates:
777  if (node_set_pt.size()!=Node_pt_range)
778  {
779  oomph_info << "ERROR: " << Node_pt_range-node_set_pt.size()
780  << " duplicate nodes were encountered in mesh!" << std::endl;
781  passed=false;
782  }
783 
784  // Return verdict
785  if (passed) {return 0;}
786  else {return 1;}
787 
788 }
789 
790 
791 
792 //========================================================
793 /// Check for inverted elements and report outcome
794  /// in boolean variable. This visits all elements at their
795  /// integration points and checks if the Jacobian of the
796  /// mapping between local and global coordinates is positive --
797  /// using the same test that would be carried out (but only in PARANOID
798  /// mode) during the assembly of the elements' Jacobian matrices.
799  /// Inverted elements are output in inverted_element_file (if the
800  /// stream is open).
801 //========================================================
802  void Mesh::check_inverted_elements(bool& mesh_has_inverted_elements,
803  std::ofstream& inverted_element_file)
804  {
805  // Initialise flag
806  mesh_has_inverted_elements=false;
807 
808  // Suppress output while checking for inverted elements
809  bool backup=
812 
813  // Loop over all elements
814  unsigned nelem=nelement();
815  for (unsigned e=0;e<nelem;e++)
816  {
818 
819  // Only check for finite elements
820  if (el_pt!=0)
821  {
822 
823  //Find out number of nodes and local coordinates in the element
824  unsigned n_node = el_pt->nnode();
825  unsigned n_dim=el_pt->dim();
826  unsigned ndim_node=el_pt->nodal_dimension();
827 
828  // Can't check Jacobian for elements in which nodal and elementa
829  // dimensions don't match
830  if (n_dim==ndim_node)
831  {
832 
833  //Set up memory for the shape and test function and local coord
834  Shape psi(n_node);
835  DShape dpsidx(n_node,n_dim);
836  Vector<double> s(n_dim);
837 
838  // Initialise element-level test
839  bool is_inverted=false;
840 
841  unsigned n_intpt=el_pt->integral_pt()->nweight();
842  for(unsigned ipt=0;ipt<n_intpt;ipt++)
843  {
844  // Local coordinates
845  for(unsigned i=0;i<n_dim;i++)
846  {
847  s[i] = el_pt->integral_pt()->knot(ipt,i);
848  }
849 
850  double J=0;
851  // Dummy assignment to keep gcc from complaining about
852  // "set but unused".
853  J+=0.0;
854  try
855  {
856  //Call the derivatives of the shape functions and Jacobian
857  J=el_pt->dshape_eulerian(s,psi,dpsidx);
858 
859  // If code is compiled without PARANOID setting, the
860  // above call will simply return the negative Jacobian
861  // without failing, so we need to check manually
862 #ifndef PARANOID
863  try
864  {
865  el_pt->check_jacobian(J);
866  }
867  catch(OomphLibQuietException& error)
868  {
869  is_inverted=true;
870  }
871 #endif
872  }
873  catch(OomphLibQuietException& error)
874  {
875  is_inverted=true;
876  }
877  }
878  if (is_inverted)
879  {
880  mesh_has_inverted_elements=true;
881  if (inverted_element_file.is_open())
882  {
883  el_pt->output(inverted_element_file);
884  }
885  }
886  }
887  }
888  }
889  // Reset
891  }
892 
893 
894 
895 //========================================================
896 /// Nodes that have been marked as obsolete are removed
897 /// from the mesh and the its boundaries. Returns vector
898 /// of pointers to deleted nodes.
899 //========================================================
901 {
902 
903  // Only copy the 'live' nodes across to new mesh
904  //----------------------------------------------
905 
906  // New Vector of pointers to nodes
907  Vector<Node *> new_node_pt;
908  Vector<Node *> deleted_node_pt;
909 
910  // Loop over all nodes in mesh
911  unsigned long n_node = nnode();
912  for(unsigned long n=0;n<n_node;n++)
913  {
914  // If the node still exists: Copy across
915  if(!(Node_pt[n]->is_obsolete()))
916  {
917  new_node_pt.push_back(Node_pt[n]);
918  }
919  // Otherwise the Node is gone:
920  // Delete it for good if it does not lie on a boundary
921  // (if it lives on a boundary we have to remove it from
922  // the boundary lookup schemes below)
923  else
924  {
925  if(!(Node_pt[n]->is_on_boundary()))
926  {
927  deleted_node_pt.push_back(Node_pt[n]);
928  delete Node_pt[n];
929  Node_pt[n]=0;
930  }
931  }
932  }
933 
934  // Now update old vector by setting it equal to the new vector
935  Node_pt = new_node_pt;
936 
937 
938  // Boundaries
939  //-----------
940 
941  // Only copy the 'live' nodes into new boundary node arrays
942  //---------------------------------------------------------
943  //Loop over the boundaries
944  unsigned num_bound = nboundary();
945  for(unsigned ibound=0;ibound<num_bound;ibound++)
946  {
947  // New Vector of pointers to existent boundary nodes
948  Vector<Node *> new_boundary_node_pt;
949 
950  //Loop over the boundary nodes
951  unsigned long Nboundary_node = Boundary_node_pt[ibound].size();
952 
953  // Reserve contiguous memory for new vector of pointers
954  // Must be equal in size to the number of nodes or less
955  new_boundary_node_pt.reserve(Nboundary_node);
956 
957  for(unsigned long n=0;n<Nboundary_node;n++)
958  {
959  // If node still exists: Copy across
960  if (!(Boundary_node_pt[ibound][n]->is_obsolete()))
961  {
962  new_boundary_node_pt.push_back(Boundary_node_pt[ibound][n]);
963  }
964  // Otherwise Node is gone: Delete it for good
965  else
966  {
967  //The node may lie on multiple boundaries, so remove the node
968  //from the current boundary
969  Boundary_node_pt[ibound][n]->remove_from_boundary(ibound);
970 
971  //Now if the node is no longer on any boundaries, delete it
972  if(!Boundary_node_pt[ibound][n]->is_on_boundary())
973  {
974  deleted_node_pt.push_back(
975  dynamic_cast<Node*>(Boundary_node_pt[ibound][n]));
976 
977  delete Boundary_node_pt[ibound][n];
978  }
979  }
980  }
981 
982  //Update the old vector by setting it equal to the new vector
983  Boundary_node_pt[ibound] = new_boundary_node_pt;
984 
985  } //End of loop over boundaries
986 
987  // Tell us who you deleted
988  return deleted_node_pt;
989 }
990 
991 
992 
993 
994 //========================================================
995 /// Output function for the mesh boundaries
996 ///
997 /// Loop over all boundaries and dump out the coordinates
998 /// of the points on the boundary (in individual tecplot
999 /// zones)
1000 //========================================================
1001 void Mesh::output_boundaries(std::ostream &outfile)
1002 {
1003  //Loop over the boundaries
1004  unsigned num_bound = nboundary();
1005  for(unsigned long ibound=0;ibound<num_bound;ibound++)
1006  {
1007  unsigned nnod=Boundary_node_pt[ibound].size();
1008  if (nnod>0)
1009  {
1010  outfile << "ZONE T=\"boundary" << ibound << "\"\n";
1011 
1012  for (unsigned inod=0;inod<nnod;inod++)
1013  {
1014  Boundary_node_pt[ibound][inod]->output(outfile);
1015  }
1016  }
1017  }
1018 }
1019 
1020 
1021 
1022 
1023 //===================================================================
1024 /// Dump function for the mesh class.
1025 /// Loop over all nodes and elements and dump them
1026 //===================================================================
1027  void Mesh::dump(std::ofstream &dump_file, const bool& use_old_ordering) const
1028 {
1029 
1030  // Get a reordering of the nodes so that the dump file is in a standard
1031  // ordering regardless of the sequence of mesh refinements etc.
1032  Vector<Node*> reordering;
1033  this->get_node_reordering(reordering, use_old_ordering);
1034 
1035  // Find number of nodes
1036  unsigned long Node_pt_range = this->nnode();
1037 
1038  // Doc # of nodes
1039  dump_file << Node_pt_range << " # number of nodes " << std::endl;
1040 
1041  //Loop over all the nodes and dump their data
1042  for(unsigned nd=0; nd<Node_pt_range; nd++)
1043  {
1044  reordering[nd]->dump(dump_file);
1045  }
1046 
1047  // Loop over elements and deal with internal data
1048  unsigned n_element = this->nelement();
1049  for(unsigned e=0;e<n_element;e++)
1050  {
1051  GeneralisedElement* el_pt = this->element_pt(e);
1052  unsigned n_internal = el_pt->ninternal_data();
1053  if(n_internal > 0)
1054  {
1055  dump_file << n_internal
1056  << " # number of internal Data items in element "
1057  << e << std::endl;
1058  for(unsigned i=0;i<n_internal;i++)
1059  {
1060  el_pt->internal_data_pt(i)->dump(dump_file);
1061  }
1062  }
1063  }
1064 }
1065 
1066 
1067 //=======================================================
1068 /// Read solution from restart file
1069 //=======================================================
1070 void Mesh::read(std::ifstream &restart_file)
1071 {
1072  std::string input_string;
1073 
1074  // Reorder the nodes within the mesh's node vector
1075  // to establish a standard ordering regardless of the sequence
1076  // of mesh refinements etc
1077  this->reorder_nodes();
1078 
1079  //Read nodes
1080 
1081  // Find number of nodes
1082  unsigned long n_node = this->nnode();
1083 
1084  // Read line up to termination sign
1085  getline(restart_file,input_string,'#');
1086 
1087  // Ignore rest of line
1088  restart_file.ignore(80,'\n');
1089 
1090  // Check # of nodes:
1091  unsigned long check_n_node=atoi(input_string.c_str());
1092  if (check_n_node!=n_node)
1093  {
1094  std::ostringstream error_stream;
1095  error_stream << "The number of nodes allocated " << n_node
1096  << " is not the same as specified in the restart file "
1097  << check_n_node << std::endl;
1098 
1099  throw OomphLibError(error_stream.str(),
1100  OOMPH_CURRENT_FUNCTION,
1101  OOMPH_EXCEPTION_LOCATION);
1102  }
1103 
1104  //Loop over the nodes
1105  for(unsigned long n=0;n<n_node;n++)
1106  {
1107  /// Try to cast to elastic node
1108  SolidNode* el_node_pt=dynamic_cast<SolidNode*>(
1109  this->node_pt(n));
1110  if (el_node_pt!=0)
1111  {
1112  el_node_pt->read(restart_file);
1113  }
1114  else
1115  {
1116  this->node_pt(n)->read(restart_file);
1117  }
1118  }
1119 
1120  // Read internal data of elements:
1121  //--------------------------------
1122  // Loop over elements and deal with internal data
1123  unsigned n_element = this->nelement();
1124  for (unsigned e=0;e<n_element;e++)
1125  {
1126  GeneralisedElement* el_pt = this->element_pt(e);
1127  unsigned n_internal=el_pt->ninternal_data();
1128  if (n_internal>0)
1129  {
1130  // Read line up to termination sign
1131  getline(restart_file,input_string,'#');
1132 
1133  // Ignore rest of line
1134  restart_file.ignore(80,'\n');
1135 
1136  // Check # of internals :
1137  unsigned long check_n_internal=atoi(input_string.c_str());
1138  if (check_n_internal!=n_internal)
1139  {
1140  std::ostringstream error_stream;
1141  error_stream << "The number of internal data " << n_internal
1142  << " is not the same as specified in the restart file "
1143  << check_n_internal << std::endl;
1144 
1145  throw OomphLibError(error_stream.str(),
1146  OOMPH_CURRENT_FUNCTION,
1147  OOMPH_EXCEPTION_LOCATION);
1148  }
1149 
1150  for (unsigned i=0;i<n_internal;i++)
1151  {
1152  el_pt->internal_data_pt(i)->read(restart_file);
1153  }
1154  }
1155  }
1156 }
1157 
1158 
1159 //========================================================
1160 /// Output in paraview format into specified file.
1161 ///
1162 /// Breaks up each element into sub-elements for plotting
1163 /// purposes. We assume that all elements are of the same
1164 /// type (fct will break (in paranoid mode) if paraview
1165 /// output fcts of the elements are inconsistent).
1166 //========================================================
1167 void Mesh::output_paraview(std::ofstream &file_out,
1168  const unsigned &nplot) const
1169 {
1170 
1171  // Change the scientific format so that E is used rather than e
1172  file_out.setf(std::ios_base::uppercase);
1173 
1174  // Decide how many elements there are to be plotted
1175  unsigned long number_of_elements=this->Element_pt.size();
1176 
1177  // Cast to finite element and return if cast fails.
1178  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(0));
1179 
1180 #ifdef PARANOID
1181  if (fe_pt==0)
1182  {
1183  throw OomphLibError(
1184  "Recast for FiniteElement failed for element 0!\n",
1185  OOMPH_CURRENT_FUNCTION,
1186  OOMPH_EXCEPTION_LOCATION);
1187  }
1188 #endif
1189 
1190 
1191 #ifdef PARANOID
1192  // Check if all elements have the same number of degrees of freedom,
1193  // if they don't, paraview will break
1194  unsigned el_zero_ndof=fe_pt->nscalar_paraview();
1195  for(unsigned i=1;i<number_of_elements;i++)
1196  {
1197  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1198  unsigned el_i_ndof=fe_pt->nscalar_paraview();
1199  if(el_zero_ndof!=el_i_ndof)
1200  {
1201  std::stringstream error_stream;
1202  error_stream
1203  << "Element " << i << " has different number of degrees of freedom\n"
1204  << "than from previous elements, Paraview cannot handle this.\n"
1205  << "We suggest that the problem is broken up into submeshes instead."
1206  << std::endl;
1207  throw OomphLibError(
1208  error_stream.str(),
1209  OOMPH_CURRENT_FUNCTION,
1210  OOMPH_EXCEPTION_LOCATION);
1211  }
1212  }
1213 #endif
1214 
1215  // Make variables to hold the number of nodes and elements
1216  unsigned long number_of_nodes=0;
1217  unsigned long total_number_of_elements=0;
1218 
1219  // Loop over all the elements to find total number of plot points
1220  for(unsigned i=0;i<number_of_elements;i++)
1221  {
1222  // Cast to FiniteElement and (in paranoid mode) check
1223  // if cast has failed.
1224  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1225 
1226 #ifdef PARANOID
1227  if (fe_pt==0)
1228  {
1229  std::stringstream error_stream;
1230  error_stream
1231  << "Recast for element " << i << " failed" << std::endl;
1232  throw OomphLibError(
1233  error_stream.str(),
1234  OOMPH_CURRENT_FUNCTION,
1235  OOMPH_EXCEPTION_LOCATION);
1236  }
1237 #endif
1238 
1239  number_of_nodes+=fe_pt->nplot_points_paraview(nplot);
1240  total_number_of_elements+=fe_pt->nsub_elements_paraview(nplot);
1241 
1242  }
1243 
1244 
1245  // File Declaration
1246  //------------------
1247 
1248  // Insert the necessary lines plus header of file, and
1249  // number of nodes and elements
1250  file_out
1251  << "<?xml version=\"1.0\"?>\n"
1252  << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" "
1253  << "byte_order=\"LittleEndian\">\n"
1254  << "<UnstructuredGrid>\n"
1255  << "<Piece NumberOfPoints=\""
1256  << number_of_nodes
1257  << "\" NumberOfCells=\""
1258  << total_number_of_elements
1259  <<"\">\n";
1260 
1261 
1262  // Point Data
1263  //-----------
1264 
1265  // Check the number of degrees of freedom
1266  unsigned ndof = fe_pt->nscalar_paraview();
1267 
1268  // Point data is going in here
1269  file_out << "<PointData ";
1270 
1271  // Insert just the first scalar name, since paraview reads everything
1272  // else after that as being of the same type. Get information from
1273  // first element.
1274  file_out << "Scalars=\""
1275  << fe_pt->scalar_name_paraview(0)
1276  << "\">\n";
1277 
1278  // Loop over i scalar fields and j number of elements
1279  for(unsigned i=0;i<ndof;i++)
1280  {
1281  file_out << "<DataArray type=\"Float32\" "
1282  << "Name=\""
1283  << fe_pt->scalar_name_paraview(i)
1284  << "\" "
1285  << "format=\"ascii\""
1286  << ">\n";
1287 
1288  for(unsigned j=0;j<number_of_elements;j++)
1289  {
1290  // Cast to FiniteElement and (in paranoid mode) check
1291  // if cast has failed.
1292  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(j));
1293 
1294 #ifdef PARANOID
1295  if (fe_pt==0)
1296  {
1297  std::stringstream error_stream;
1298  error_stream
1299  << "Recast for element " << j << " failed" << std::endl;
1300  throw OomphLibError(
1301  error_stream.str(),
1302  OOMPH_CURRENT_FUNCTION,
1303  OOMPH_EXCEPTION_LOCATION);
1304  }
1305 #endif
1306 
1307  fe_pt->scalar_value_paraview(file_out,i,nplot);
1308  }
1309 
1310  // Close of the DataArray
1311  file_out << "</DataArray>\n";
1312  }
1313 
1314  // Close off the PointData set
1315  file_out << "</PointData>\n";
1316 
1317 
1318  // Geometric Points
1319  //------------------
1320 
1321  file_out
1322  << "<Points>\n"
1323  << "<DataArray type=\"Float32\""
1324  << " NumberOfComponents=\""
1325  // This always has to be 3 for an unstructured grid
1326  << 3 << "\" "
1327  << "format=\"ascii\">\n";
1328 
1329  // Loop over all the elements to print their plot points
1330  for(unsigned i=0;i<number_of_elements;i++)
1331  {
1332  // Cast to FiniteElement and (in paranoid mode) check
1333  // if cast has failed.
1334  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1335 
1336 #ifdef PARANOID
1337  if (fe_pt==0)
1338  {
1339  std::stringstream error_stream;
1340  error_stream
1341  << "Recast for element " << i << " faild" << std::endl;
1342  throw OomphLibError(
1343  error_stream.str(),
1344  OOMPH_CURRENT_FUNCTION,
1345  OOMPH_EXCEPTION_LOCATION);
1346  }
1347 #endif
1348 
1349  fe_pt->output_paraview(file_out,nplot);
1350  }
1351 
1352  file_out
1353  << "</DataArray>\n"
1354  << "</Points>\n";
1355 
1356 
1357  // Cells
1358  //-------
1359 
1360  file_out
1361  << "<Cells>\n"
1362  << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\"ascii\">\n";
1363 
1364  // Make counter for keeping track of all the local elements,
1365  // because Paraview requires global coordinates
1366  unsigned counter=0;
1367 
1368  // Write connectivity with the local elements
1369  for(unsigned i=0;i<number_of_elements;i++)
1370  {
1371  // Cast to FiniteElement and (in paranoid mode) check
1372  // if cast has failed.
1373  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1374 
1375 #ifdef PARANOID
1376  if (fe_pt==0)
1377  {
1378  std::stringstream error_stream;
1379  error_stream
1380  << "Recast for element " << i << " faild" << std::endl;
1381  throw OomphLibError(
1382  error_stream.str(),
1383  OOMPH_CURRENT_FUNCTION,
1384  OOMPH_EXCEPTION_LOCATION);
1385  }
1386 #endif
1387  fe_pt->write_paraview_output_offset_information(file_out,nplot,counter);
1388  }
1389 
1390  file_out << "</DataArray>\n"
1391  << "<DataArray type=\"Int32\" "
1392  << "Name=\"offsets\" format=\"ascii\">\n";
1393 
1394  // Make variable that holds the current offset number
1395  unsigned offset_sum=0;
1396 
1397  // Write the offset for the specific elements
1398  for(unsigned i=0;i<number_of_elements;i++)
1399  {
1400  // Cast to FiniteElement and (in paranoid mode) check
1401  // if cast has failed.
1402  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1403 
1404 #ifdef PARANOID
1405  if (fe_pt==0)
1406  {
1407  std::stringstream error_stream;
1408  error_stream
1409  << "Recast for element " << i << " failed" << std::endl;
1410  throw OomphLibError(
1411  error_stream.str(),
1412  OOMPH_CURRENT_FUNCTION,
1413  OOMPH_EXCEPTION_LOCATION);
1414  }
1415 #endif
1416  fe_pt->write_paraview_offsets(file_out,nplot,offset_sum);
1417  }
1418 
1419  file_out <<"</DataArray>\n"
1420  <<"<DataArray type=\"UInt8\" Name=\"types\">\n";
1421 
1422  // Loop over all elements to get the type that they have
1423  for(unsigned i=0;i<number_of_elements;i++)
1424  {
1425  // Cast to FiniteElement and (in paranoid mode) check
1426  // if cast has failed.
1427  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1428 
1429 #ifdef PARANOID
1430  if (fe_pt==0)
1431  {
1432  std::stringstream error_stream;
1433  error_stream
1434  << "Recast for element " << i << " failed" << std::endl;
1435  throw OomphLibError(
1436  error_stream.str(),
1437  OOMPH_CURRENT_FUNCTION,
1438  OOMPH_EXCEPTION_LOCATION);
1439  }
1440 #endif
1441 
1442  fe_pt->write_paraview_type(file_out,nplot);
1443  }
1444 
1445  file_out <<"</DataArray>\n"
1446  <<"</Cells>\n";
1447 
1448 
1449  // File Closure
1450  //-------------
1451  file_out <<"</Piece>\n"
1452  <<"</UnstructuredGrid>\n"
1453  <<"</VTKFile>";
1454 }
1455 
1456 
1457 //========================================================
1458 /// Output in paraview format into specified file.
1459 ///
1460 /// Breaks up each element into sub-elements for plotting
1461 /// purposes. We assume that all elements are of the same
1462 /// type (fct will break (in paranoid mode) if paraview
1463 /// output fcts of the elements are inconsistent).
1464 //========================================================
1465  void Mesh::output_fct_paraview(std::ofstream &file_out,
1466  const unsigned &nplot,
1467  FiniteElement::SteadyExactSolutionFctPt exact_soln_pt) const
1468 {
1469 
1470  // Change the scientific format so that E is used rather than e
1471  file_out.setf(std::ios_base::uppercase);
1472 
1473  // Decide how many elements there are to be plotted
1474  unsigned long number_of_elements=this->Element_pt.size();
1475 
1476  // Cast to finite element and return if cast fails.
1477  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(0));
1478 
1479 #ifdef PARANOID
1480  if (fe_pt==0)
1481  {
1482  throw OomphLibError(
1483  "Recast for FiniteElement failed for element 0!\n",
1484  OOMPH_CURRENT_FUNCTION,
1485  OOMPH_EXCEPTION_LOCATION);
1486  }
1487 #endif
1488 
1489 
1490 #ifdef PARANOID
1491  // Check if all elements have the same number of degrees of freedom,
1492  // if they don't, paraview will break
1493  unsigned el_zero_ndof=fe_pt->nscalar_paraview();
1494  for(unsigned i=1;i<number_of_elements;i++)
1495  {
1496  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1497  unsigned el_i_ndof=fe_pt->nscalar_paraview();
1498  if(el_zero_ndof!=el_i_ndof)
1499  {
1500  std::stringstream error_stream;
1501  error_stream
1502  << "Element " << i << " has different number of degrees of freedom\n"
1503  << "than from previous elements, Paraview cannot handle this.\n"
1504  << "We suggest that the problem is broken up into submeshes instead."
1505  << std::endl;
1506  throw OomphLibError(
1507  error_stream.str(),
1508  OOMPH_CURRENT_FUNCTION,
1509  OOMPH_EXCEPTION_LOCATION);
1510  }
1511  }
1512 #endif
1513 
1514  // Make variables to hold the number of nodes and elements
1515  unsigned long number_of_nodes=0;
1516  unsigned long total_number_of_elements=0;
1517 
1518  // Loop over all the elements to find total number of plot points
1519  for(unsigned i=0;i<number_of_elements;i++)
1520  {
1521  // Cast to FiniteElement and (in paranoid mode) check
1522  // if cast has failed.
1523  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1524 
1525 #ifdef PARANOID
1526  if (fe_pt==0)
1527  {
1528  std::stringstream error_stream;
1529  error_stream
1530  << "Recast for element " << i << " failed" << std::endl;
1531  throw OomphLibError(
1532  error_stream.str(),
1533  OOMPH_CURRENT_FUNCTION,
1534  OOMPH_EXCEPTION_LOCATION);
1535  }
1536 #endif
1537 
1538  number_of_nodes+=fe_pt->nplot_points_paraview(nplot);
1539  total_number_of_elements+=fe_pt->nsub_elements_paraview(nplot);
1540 
1541  }
1542 
1543 
1544  // File Declaration
1545  //------------------
1546 
1547  // Insert the necessary lines plus header of file, and
1548  // number of nodes and elements
1549  file_out
1550  << "<?xml version=\"1.0\"?>\n"
1551  << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" "
1552  << "byte_order=\"LittleEndian\">\n"
1553  << "<UnstructuredGrid>\n"
1554  << "<Piece NumberOfPoints=\""
1555  << number_of_nodes
1556  << "\" NumberOfCells=\""
1557  << total_number_of_elements
1558  <<"\">\n";
1559 
1560 
1561  // Point Data
1562  //-----------
1563 
1564  // Check the number of degrees of freedom
1565  unsigned ndof = fe_pt->nscalar_paraview();
1566 
1567  // Point data is going in here
1568  file_out << "<PointData ";
1569 
1570  // Insert just the first scalar name, since paraview reads everything
1571  // else after that as being of the same type. Get information from
1572  // first element.
1573  file_out << "Scalars=\""
1574  << fe_pt->scalar_name_paraview(0)
1575  << "\">\n";
1576 
1577  // Loop over i scalar fields and j number of elements
1578  for(unsigned i=0;i<ndof;i++)
1579  {
1580  file_out << "<DataArray type=\"Float32\" "
1581  << "Name=\""
1582  << fe_pt->scalar_name_paraview(i)
1583  << "\" "
1584  << "format=\"ascii\""
1585  << ">\n";
1586 
1587  for(unsigned j=0;j<number_of_elements;j++)
1588  {
1589  // Cast to FiniteElement and (in paranoid mode) check
1590  // if cast has failed.
1591  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(j));
1592 
1593 #ifdef PARANOID
1594  if (fe_pt==0)
1595  {
1596  std::stringstream error_stream;
1597  error_stream
1598  << "Recast for element " << j << " failed" << std::endl;
1599  throw OomphLibError(
1600  error_stream.str(),
1601  OOMPH_CURRENT_FUNCTION,
1602  OOMPH_EXCEPTION_LOCATION);
1603  }
1604 #endif
1605 
1606  fe_pt->scalar_value_fct_paraview(file_out,i,nplot,exact_soln_pt);
1607  }
1608 
1609  // Close of the DataArray
1610  file_out << "</DataArray>\n";
1611  }
1612 
1613  // Close off the PointData set
1614  file_out << "</PointData>\n";
1615 
1616 
1617  // Geometric Points
1618  //------------------
1619 
1620  file_out
1621  << "<Points>\n"
1622  << "<DataArray type=\"Float32\""
1623  << " NumberOfComponents=\""
1624  // This always has to be 3 for an unstructured grid
1625  << 3 << "\" "
1626  << "format=\"ascii\">\n";
1627 
1628  // Loop over all the elements to print their plot points
1629  for(unsigned i=0;i<number_of_elements;i++)
1630  {
1631  // Cast to FiniteElement and (in paranoid mode) check
1632  // if cast has failed.
1633  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1634 
1635 #ifdef PARANOID
1636  if (fe_pt==0)
1637  {
1638  std::stringstream error_stream;
1639  error_stream
1640  << "Recast for element " << i << " faild" << std::endl;
1641  throw OomphLibError(
1642  error_stream.str(),
1643  OOMPH_CURRENT_FUNCTION,
1644  OOMPH_EXCEPTION_LOCATION);
1645  }
1646 #endif
1647 
1648  fe_pt->output_paraview(file_out,nplot);
1649  }
1650 
1651  file_out
1652  << "</DataArray>\n"
1653  << "</Points>\n";
1654 
1655 
1656  // Cells
1657  //-------
1658 
1659  file_out
1660  << "<Cells>\n"
1661  << "<DataArray type=\"Int32\" Name=\"connectivity\" format=\"ascii\">\n";
1662 
1663  // Make counter for keeping track of all the local elements,
1664  // because Paraview requires global coordinates
1665  unsigned counter=0;
1666 
1667  // Write connectivity with the local elements
1668  for(unsigned i=0;i<number_of_elements;i++)
1669  {
1670  // Cast to FiniteElement and (in paranoid mode) check
1671  // if cast has failed.
1672  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1673 
1674 #ifdef PARANOID
1675  if (fe_pt==0)
1676  {
1677  std::stringstream error_stream;
1678  error_stream
1679  << "Recast for element " << i << " faild" << std::endl;
1680  throw OomphLibError(
1681  error_stream.str(),
1682  OOMPH_CURRENT_FUNCTION,
1683  OOMPH_EXCEPTION_LOCATION);
1684  }
1685 #endif
1686  fe_pt->write_paraview_output_offset_information(file_out,nplot,counter);
1687  }
1688 
1689  file_out << "</DataArray>\n"
1690  << "<DataArray type=\"Int32\" "
1691  << "Name=\"offsets\" format=\"ascii\">\n";
1692 
1693  // Make variable that holds the current offset number
1694  unsigned offset_sum=0;
1695 
1696  // Write the offset for the specific elements
1697  for(unsigned i=0;i<number_of_elements;i++)
1698  {
1699  // Cast to FiniteElement and (in paranoid mode) check
1700  // if cast has failed.
1701  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1702 
1703 #ifdef PARANOID
1704  if (fe_pt==0)
1705  {
1706  std::stringstream error_stream;
1707  error_stream
1708  << "Recast for element " << i << " failed" << std::endl;
1709  throw OomphLibError(
1710  error_stream.str(),
1711  OOMPH_CURRENT_FUNCTION,
1712  OOMPH_EXCEPTION_LOCATION);
1713  }
1714 #endif
1715  fe_pt->write_paraview_offsets(file_out,nplot,offset_sum);
1716  }
1717 
1718  file_out <<"</DataArray>\n"
1719  <<"<DataArray type=\"UInt8\" Name=\"types\">\n";
1720 
1721  // Loop over all elements to get the type that they have
1722  for(unsigned i=0;i<number_of_elements;i++)
1723  {
1724  // Cast to FiniteElement and (in paranoid mode) check
1725  // if cast has failed.
1726  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(element_pt(i));
1727 
1728 #ifdef PARANOID
1729  if (fe_pt==0)
1730  {
1731  std::stringstream error_stream;
1732  error_stream
1733  << "Recast for element " << i << " failed" << std::endl;
1734  throw OomphLibError(
1735  error_stream.str(),
1736  OOMPH_CURRENT_FUNCTION,
1737  OOMPH_EXCEPTION_LOCATION);
1738  }
1739 #endif
1740 
1741  fe_pt->write_paraview_type(file_out,nplot);
1742  }
1743 
1744  file_out <<"</DataArray>\n"
1745  <<"</Cells>\n";
1746 
1747 
1748  // File Closure
1749  //-------------
1750  file_out <<"</Piece>\n"
1751  <<"</UnstructuredGrid>\n"
1752  <<"</VTKFile>";
1753 }
1754 
1755 //========================================================
1756 /// Output function for the mesh class
1757 ///
1758 /// Loop over all elements and plot (i.e. execute
1759 /// the element's own output() function)
1760 //========================================================
1761 void Mesh::output(std::ostream &outfile)
1762 {
1763  //Loop over the elements and call their output functions
1764  //Assign Element_pt_range
1765  unsigned long Element_pt_range = Element_pt.size();
1766  for(unsigned long e=0;e<Element_pt_range;e++)
1767  {
1768  // Try to cast to FiniteElement
1769  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
1770  if (el_pt==0)
1771  {
1772  oomph_info << "Can't execute output(...) for non FiniteElements"
1773  << std::endl;
1774  }
1775  else
1776  {
1777 #ifdef OOMPH_HAS_MPI
1779 #endif
1780  {
1781  el_pt->output(outfile);
1782  }
1783 #ifdef OOMPH_HAS_MPI
1784  else
1785  {
1786  if (!el_pt->is_halo())
1787  {
1788  el_pt->output(outfile);
1789  }
1790  }
1791 #endif
1792  }
1793  }
1794 }
1795 
1796 //========================================================
1797 /// Output function for the mesh class
1798 ///
1799 /// Loop over all elements and plot (i.e. execute
1800 /// the element's own output() function). Use
1801 /// n_plot plot points in each coordinate direction.
1802 //========================================================
1803 void Mesh::output(std::ostream &outfile, const unsigned &n_plot)
1804 {
1805  //Loop over the elements and call their output functions
1806  //Assign Element_pt_range
1807  unsigned long Element_pt_range = Element_pt.size();
1808 
1809  for(unsigned long e=0;e<Element_pt_range;e++)
1810  {
1811  // Try to cast to FiniteElement
1812  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
1813  if (el_pt==0)
1814  {
1815  oomph_info << "Can't execute output(...) for non FiniteElements"
1816  << std::endl;
1817  }
1818  else
1819  {
1820 #ifdef OOMPH_HAS_MPI
1822 #endif
1823  {
1824  el_pt->output(outfile,n_plot);
1825  }
1826 #ifdef OOMPH_HAS_MPI
1827  else
1828  {
1829  if (!el_pt->is_halo())
1830  {
1831  el_pt->output(outfile,n_plot);
1832  }
1833  }
1834 #endif
1835  }
1836  }
1837 }
1838 
1839 
1840 
1841 
1842 
1843 //========================================================
1844 /// Output function for the mesh class
1845 ///
1846 /// Loop over all elements and plot (i.e. execute
1847 /// the element's own output() function)
1848 /// (C style output)
1849 //========================================================
1850 void Mesh::output(FILE* file_pt)
1851 {
1852  //Loop over the elements and call their output functions
1853  //Assign Element_pt_range
1854  unsigned long Element_pt_range = Element_pt.size();
1855  for(unsigned long e=0;e<Element_pt_range;e++)
1856  {
1857  // Try to cast to FiniteElement
1858  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
1859  if (el_pt==0)
1860  {
1861  oomph_info << "Can't execute output(...) for non FiniteElements"
1862  << std::endl;
1863  }
1864  else
1865  {
1866 #ifdef OOMPH_HAS_MPI
1868 #endif
1869  {
1870  el_pt->output(file_pt);
1871  }
1872 #ifdef OOMPH_HAS_MPI
1873  else
1874  {
1875  if (!el_pt->is_halo())
1876  {
1877  el_pt->output(file_pt);
1878  }
1879  }
1880 #endif
1881  }
1882  }
1883 }
1884 
1885 //========================================================
1886 /// Output function for the mesh class
1887 ///
1888 /// Loop over all elements and plot (i.e. execute
1889 /// the element's own output() function). Use
1890 /// n_plot plot points in each coordinate direction.
1891 /// (C style output)
1892 //========================================================
1893 void Mesh::output(FILE* file_pt, const unsigned &n_plot)
1894 {
1895  //Loop over the elements and call their output functions
1896  //Assign Element_pt_range
1897  unsigned long Element_pt_range = Element_pt.size();
1898  for(unsigned long e=0;e<Element_pt_range;e++)
1899  {
1900  // Try to cast to FiniteElement
1901  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
1902  if (el_pt==0)
1903  {
1904  oomph_info << "Can't execute output(...) for non FiniteElements"
1905  << std::endl;
1906  }
1907  else
1908  {
1909 #ifdef OOMPH_HAS_MPI
1911 #endif
1912  {
1913  el_pt->output(file_pt,n_plot);
1914  }
1915 #ifdef OOMPH_HAS_MPI
1916  else
1917  {
1918  if (!el_pt->is_halo())
1919  {
1920  el_pt->output(file_pt,n_plot);
1921  }
1922  }
1923 #endif
1924  }
1925  }
1926 }
1927 
1928 
1929 //========================================================
1930 /// Output function for the mesh class
1931 ///
1932 /// Loop over all elements and plot (i.e. execute
1933 /// the element's own output() function). Use
1934 /// n_plot plot points in each coordinate direction.
1935 //========================================================
1936 void Mesh::output_fct(std::ostream &outfile, const unsigned &n_plot,
1938 {
1939  //Loop over the elements and call their output functions
1940  //Assign Element_pt_range
1941  unsigned long Element_pt_range = Element_pt.size();
1942  for(unsigned long e=0;e<Element_pt_range;e++)
1943  {
1944  // Try to cast to FiniteElement
1945  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
1946  if (el_pt==0)
1947  {
1948  oomph_info << "Can't execute output_fct(...) for non FiniteElements"
1949  << std::endl;
1950  }
1951  else
1952  {
1953 #ifdef OOMPH_HAS_MPI
1955 #endif
1956  {
1957  el_pt->output_fct(outfile,n_plot,exact_soln_pt);
1958  }
1959 #ifdef OOMPH_HAS_MPI
1960  else
1961  {
1962  if (!el_pt->is_halo())
1963  {
1964  el_pt->output_fct(outfile,n_plot,exact_soln_pt);
1965  }
1966  }
1967 #endif
1968  }
1969  }
1970 }
1971 
1972 //========================================================
1973 /// Output function for the mesh class
1974 ///
1975 /// Loop over all elements and plot (i.e. execute
1976 /// the element's own output() function) at time t. Use
1977 /// n_plot plot points in each coordinate direction.
1978 //========================================================
1979 void Mesh::output_fct(std::ostream &outfile, const unsigned &n_plot,
1980  const double& time,
1982 {
1983  //Loop over the elements and call their output functions
1984  //Assign Element_pt_range
1985  unsigned long Element_pt_range = Element_pt.size();
1986  for(unsigned long e=0;e<Element_pt_range;e++)
1987  {
1988  // Try to cast to FiniteElement
1989  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
1990  if (el_pt==0)
1991  {
1992  oomph_info << "Can't execute output_fct(...) for non FiniteElements"
1993  << std::endl;
1994  }
1995  else
1996  {
1997 #ifdef OOMPH_HAS_MPI
1999 #endif
2000  {
2001  el_pt->output_fct(outfile,n_plot,time,exact_soln_pt);
2002  }
2003 #ifdef OOMPH_HAS_MPI
2004  else
2005  {
2006  if (!el_pt->is_halo())
2007  {
2008  el_pt->output_fct(outfile,n_plot,time,exact_soln_pt);
2009  }
2010  }
2011 #endif
2012  }
2013  }
2014 }
2015 
2016 //==================================================================
2017 /// Assign the initial values for an impulsive start, which is
2018 /// acheived by looping over all data in the mesh (internal element
2019 /// data and data stored at nodes) and setting the calling the
2020 /// assign_initial_values_impulsive() function for each data's
2021 /// timestepper
2022 //=================================================================
2024 {
2025  //Loop over the elements
2026  unsigned long Nelement=nelement();
2027  for(unsigned long e=0;e<Nelement;e++)
2028  {
2029  //Find the number of internal dofs
2030  unsigned Ninternal = element_pt(e)->ninternal_data();
2031  //Loop over internal dofs and shift the time values
2032  //using the internals data's timestepper
2033  for(unsigned j=0;j<Ninternal;j++)
2034  {
2035  element_pt(e)->internal_data_pt(j)->time_stepper_pt()->
2036  assign_initial_values_impulsive(element_pt(e)->internal_data_pt(j));
2037  }
2038  }
2039 
2040  //Loop over the nodes
2041  unsigned long n_node=nnode();
2042  for (unsigned long n=0;n<n_node;n++)
2043  {
2044  // Assign initial values using the Node's timestepper
2045  Node_pt[n]->time_stepper_pt()->
2047  // Assign initial positions using the Node's timestepper
2048  Node_pt[n]->position_time_stepper_pt()->
2049  assign_initial_positions_impulsive(Node_pt[n]);
2050  }
2051 
2052 }
2053 
2054 //===============================================================
2055 /// Shift time-dependent data along for next timestep:
2056 /// Again this is achieved by looping over all data and calling
2057 /// the functions defined in each data object's timestepper.
2058 //==============================================================
2060 {
2061  // Loop over the elements which shift their internal data
2062  // via their own timesteppers
2063  const unsigned long Nelement=nelement();
2064  for (unsigned long e=0;e<Nelement;e++)
2065  {
2066  //Find the number of internal dofs
2067  const unsigned Ninternal = element_pt(e)->ninternal_data();
2068  //Loop over internal dofs and shift the time values
2069  //using the internals data's timestepper
2070  for(unsigned j=0;j<Ninternal;j++)
2071  {
2072  element_pt(e)->internal_data_pt(j)->time_stepper_pt()->
2073  shift_time_values(element_pt(e)->internal_data_pt(j));
2074  }
2075  }
2076 
2077  //Loop over the nodes
2078  const unsigned long n_node=nnode();
2079  for (unsigned long n=0;n<n_node;n++)
2080  {
2081  // Shift the Data associated with the nodes with the Node's own
2082  // timestepper
2083  Node_pt[n]->time_stepper_pt()->shift_time_values(Node_pt[n]);
2084  // Push history of nodal positions back
2085  Node_pt[n]->position_time_stepper_pt()->shift_time_positions(Node_pt[n]);
2086  }
2087 
2088 }
2089 
2090 //=========================================================================
2091 /// Calculate predictions for all Data and positions associated
2092 /// with the mesh. This is usually only used for adaptive time-stepping
2093 /// when the comparison between a predicted value and the actual value
2094 /// is usually used to determine the change in step size. Again the
2095 /// loop is over all data in the mesh and individual timestepper functions
2096 /// for each data value are called.
2097 //=========================================================================
2099 {
2100  // Loop over the elements which shift their internal data
2101  // via their own timesteppers
2102  const unsigned long Nelement=nelement();
2103  for (unsigned long e=0;e<Nelement;e++)
2104  {
2105  //Find the number of internal dofs
2106  const unsigned Ninternal = element_pt(e)->ninternal_data();
2107  //Loop over internal dofs and calculate predicted positions
2108  //using the internals data's timestepper
2109  for(unsigned j=0;j<Ninternal;j++)
2110  {
2111  element_pt(e)->internal_data_pt(j)->time_stepper_pt()->
2112  calculate_predicted_values(element_pt(e)->internal_data_pt(j));
2113  }
2114  }
2115 
2116  //Loop over the nodes
2117  const unsigned long n_node=nnode();
2118  for (unsigned long n=0;n<n_node;n++)
2119  {
2120  // Calculate the predicted values at the nodes
2121  Node_pt[n]->time_stepper_pt()->calculate_predicted_values(Node_pt[n]);
2122  //Calculate the predicted positions
2123  Node_pt[n]->position_time_stepper_pt()->
2124  calculate_predicted_positions(Node_pt[n]);
2125  }
2126 }
2127 
2128 //===============================================================
2129 /// Virtual function that should be overloaded if the mesh
2130 /// has any mesh level storage of the timestepper
2131 //==================================================================
2132 void Mesh::set_mesh_level_time_stepper(TimeStepper* const &time_stepper_pt,
2133  const bool &preserve_existing_data)
2134 {
2135 #ifdef PARANOID
2137  {
2138  std::ostringstream warning_stream;
2139  warning_stream <<
2140  "Empty set_mesh_level_time_stepper() has been called.\n"
2141  <<
2142  "This function needs to be overloaded to reset any (pointers to) \n"
2143  <<
2144  "timesteppers for meshes that store timesteppers in locations other\n"
2145  << "than the Nodes or Elements;\n"
2146  << "e.g. SpineMeshes have SpineData with timesteppers,\n"
2147  <<
2148  "Triangle and TetMeshes store the timestepper for use in adaptivity.\n\n\n";
2149  warning_stream << "If you are solving a continuation or bifurcation detecion\n"
2150  << "problem and strange things are happening, then check that\n"
2151  << "you don't need to overload this function for your mesh."
2152  << "\n This warning can be suppressed by setting:\n"
2153  <<
2154  "Mesh::Suppress_warning_about_empty_mesh_level_time_stepper_function=true"
2155  << std::endl;
2156  OomphLibWarning(warning_stream.str(),
2157  OOMPH_CURRENT_FUNCTION,
2158  OOMPH_EXCEPTION_LOCATION);
2159  }
2160 #endif
2161 }
2162 
2163 //===============================================================
2164 /// Set the values of auxilliary data used in continuation problems
2165 /// when the data is pinned.
2166 //==================================================================
2168  ContinuationStorageScheme* const &continuation_storage_pt)
2169 {
2170  //Loop over the nodes
2171  const unsigned long n_node=this->nnode();
2172  for(unsigned long n=0;n<n_node;n++)
2173  {
2174  continuation_storage_pt->set_consistent_pinned_values(this->Node_pt[n]);
2175  continuation_storage_pt->set_consistent_pinned_positions(this->Node_pt[n]);
2176  }
2177 
2178  // Loop over the elements
2179  const unsigned long n_element=this->nelement();
2180  for (unsigned long e=0;e<n_element;e++)
2181  {
2182  //Cache pointer to the elemnet
2183  GeneralisedElement* const elem_pt = this->element_pt(e);
2184  //Find the number of internal dofs
2185  const unsigned n_internal = elem_pt->ninternal_data();
2186 
2187  //Loop over internal dofs and test the data
2188  for(unsigned j=0;j<n_internal;j++)
2189  {
2190  continuation_storage_pt->
2191  set_consistent_pinned_values(elem_pt->internal_data_pt(j));
2192  }
2193  }
2194 }
2195 
2196 
2197 //===============================================================
2198 /// Return true if the pointer corresponds to any data stored in
2199 /// the mesh and false if not
2200 //==================================================================
2201 bool Mesh::does_pointer_correspond_to_mesh_data(double* const &parameter_pt)
2202 {
2203  //Loop over the nodes
2204  const unsigned long n_node=this->nnode();
2205  for(unsigned long n=0;n<n_node;n++)
2206  {
2207  //Check the values and positional data associated with each node
2208  if(
2209  (this->Node_pt[n]->does_pointer_correspond_to_value(parameter_pt))
2210  ||
2211  (this->Node_pt[n]->does_pointer_correspond_to_position_data(parameter_pt)))
2212  {return true;}
2213  }
2214 
2215  // Loop over the elements
2216  const unsigned long n_element=this->nelement();
2217  for (unsigned long e=0;e<n_element;e++)
2218  {
2219  //Cache pointer to the elemnet
2220  GeneralisedElement* const elem_pt = this->element_pt(e);
2221 
2222  //Find the number of internal dofs
2223  const unsigned n_internal = elem_pt->ninternal_data();
2224 
2225  //Loop over internal dofs and test the data
2226  for(unsigned j=0;j<n_internal;j++)
2227  {
2228  if(elem_pt->internal_data_pt(j)
2229  ->does_pointer_correspond_to_value(parameter_pt))
2230  {return true;}
2231  }
2232  }
2233 
2234  //If we get here we haven't found the data, so return false
2235  return false;
2236 }
2237 
2238 
2239 
2240 //===============================================================
2241 /// Set the time stepper associated with all the nodal data
2242 /// in the problem
2243 //==============================================================
2244 void Mesh::set_nodal_time_stepper(TimeStepper* const &time_stepper_pt,
2245  const bool &preserve_existing_data)
2246 {
2247  //Loop over the nodes
2248  const unsigned long n_node=this->nnode();
2249  for(unsigned long n=0;n<n_node;n++)
2250  {
2251  //Set the timestepper associated with each node
2252  this->Node_pt[n]->set_time_stepper(time_stepper_pt,preserve_existing_data);
2253  this->Node_pt[n]->set_position_time_stepper(time_stepper_pt,
2254  preserve_existing_data);
2255  }
2256 }
2257 
2258 //===============================================================
2259 /// Set the time stepper associated with all internal data stored
2260 /// in the elements in the mesh
2261 //===============================================================
2263  TimeStepper* const &time_stepper_pt, const bool &preserve_existing_data)
2264 {
2265  // Loop over the elements
2266  const unsigned long n_element=this->nelement();
2267  for (unsigned long e=0;e<n_element;e++)
2268  {
2269  //Find the number of internal dofs
2270  const unsigned n_internal = this->element_pt(e)->ninternal_data();
2271 
2272  //Loop over internal dofs and set the timestepper
2273  for(unsigned j=0;j<n_internal;j++)
2274  {
2275  this->element_pt(e)->internal_data_pt(j)
2276  ->set_time_stepper(time_stepper_pt,preserve_existing_data);
2277  }
2278  }
2279 }
2280 
2281 //========================================================================
2282 /// A function that upgrades an ordinary node to a boundary node.
2283 /// All pointers to the node from the mesh's elements are found.
2284 /// and replaced by pointers to the new boundary node. If the node
2285 /// is present in the mesh's list of nodes, that pointer is also
2286 /// replaced. Finally, the pointer argument node_pt addresses the new
2287 /// node on return from the function.
2288 /// We shouldn't ever really use this, but it does make life that
2289 /// bit easier for the lazy mesh writer.
2290 //=======================================================================
2292 {
2293  // Cache a list of FiniteElement pointers for use in this function.
2294  Vector<FiniteElement*> fe_pt(nelement(),0);
2295  for(unsigned e=0, ne=nelement(); e < ne; e++)
2296  {
2297  // Some elements may not have been build yet, just store a null pointer
2298  // for these cases.
2299  if(Element_pt[e] == 0) fe_pt[e] = 0;
2300  else fe_pt[e] = finite_element_pt(e);
2301  }
2302 
2303  // Now call the real function
2304  convert_to_boundary_node(node_pt, fe_pt);
2305 }
2306 
2307 // ============================================================
2308 /// As convert_to_boundary_node but with a vector of pre-"dynamic cast"ed
2309 /// pointers passed in. If this function is being called often then
2310 /// creating this vector and passing it in explicitly each time can give a
2311 /// large speed up.
2312 // Note: the real reason that this function is so slow in the first place
2313 // is because it has to loop over all elements. So if you use this function
2314 // O(N) times your boundary node creation complexity is O(N^2).
2315 // ============================================================
2318 {
2319 
2320  //If the node is already a boundary node, then return straight away,
2321  //we don't need to do anything
2322  if (dynamic_cast<BoundaryNodeBase*>(node_pt)!=0)
2323  {
2324  return;
2325  }
2326 
2327  //Loop over all the elements in the mesh and find all those in which
2328  //the present node is referenced and the corresponding local node number
2329  //in those elements.
2330 
2331  //Storage for elements and local node number
2332  std::list<std::pair<unsigned long, int> >
2333  list_of_elements_and_local_node_numbers;
2334 
2335  //Loop over all elements
2336  unsigned long n_element=this->nelement();
2337  for(unsigned long e=0;e<n_element;e++)
2338  {
2339  //Buffer the case when we have not yet filled up the element array
2340  //Unfortunately, we should not assume that the array has been filled
2341  //in a linear order, so we can't break out early.
2342  if(Element_pt[e]!=0)
2343  {
2344  //Find the local node number of the passed node
2345  int node_number = finite_element_pt[e]->get_node_number(node_pt);
2346  //If the node is present in the element, add it to our list and
2347  //NULL out the local element entries
2348  if(node_number!=-1)
2349  {
2350  list_of_elements_and_local_node_numbers.insert(
2351  list_of_elements_and_local_node_numbers.end(),
2352  std::make_pair(e,node_number));
2353  //Null it out
2354  finite_element_pt[e]->node_pt(node_number)=0;
2355  }
2356  }
2357  } //End of loop over elements
2358 
2359  //If there are no entries in the list we are in real trouble
2360  if(list_of_elements_and_local_node_numbers.empty())
2361  {
2362  std::ostringstream error_stream;
2363  error_stream << "Node " << node_pt
2364  << " is not contained in any elements in the Mesh."
2365  << std::endl
2366  << "How was it created then?" << std::endl;
2367 
2368  throw OomphLibError(error_stream.str(),
2369  OOMPH_CURRENT_FUNCTION,
2370  OOMPH_EXCEPTION_LOCATION);
2371  }
2372 
2373 
2374  //Create temporary storage for a pointer to the old node.
2375  //This is required because if we have passed a reference to the
2376  //first element that we find, constructing the new node
2377  //will over-write our pointer and we'll get segmentation faults.
2378  Node* old_node_pt = node_pt;
2379 
2380  //We now create the new node by using the first element in the list
2381  std::list<std::pair<unsigned long,int> >::iterator list_it
2382  = list_of_elements_and_local_node_numbers.begin();
2383 
2384  //Create a new boundary node, using the timestepper from the
2385  //original node
2386  Node* new_node_pt = finite_element_pt[list_it->first]
2387  ->construct_boundary_node(list_it->second,node_pt->time_stepper_pt());
2388 
2389  //Now copy all the information accross from the old node
2390 
2391  //Can we cast the node to a solid node
2392  SolidNode* solid_node_pt = dynamic_cast<SolidNode*>(new_node_pt);
2393  //If it's a solid node, do the casting
2394  if(solid_node_pt!=0)
2395  {
2396  solid_node_pt->copy(dynamic_cast<SolidNode*>(old_node_pt));
2397  }
2398  else
2399  {
2400  new_node_pt->copy(old_node_pt);
2401  }
2402 
2403  //Loop over all other elements in the list and set their pointers
2404  //to the new node
2405  for(++list_it; //Increment the iterator
2406  list_it!=list_of_elements_and_local_node_numbers.end();
2407  ++list_it)
2408  {
2409  finite_element_pt[list_it->first]->node_pt(list_it->second)
2410  = new_node_pt;
2411  }
2412 
2413  //Finally, find the position of the node in the global mesh
2415  std::find(Node_pt.begin(),Node_pt.end(),old_node_pt);
2416 
2417  //If it is in the mesh, update the pointer
2418  if(it!=Node_pt.end()) {*it = new_node_pt;}
2419 
2420  //Can now delete the old node
2421  delete old_node_pt;
2422 
2423  //Replace the passed pointer by a pointer to the new node
2424  //Note that in most cases, this will be wasted work because the node
2425  //pointer will either the pointer in the mesh's or an element's
2426  //node_pt vector. Still assignment is quicker than an if to check this.
2427  node_pt = new_node_pt;
2428 
2429 }
2430 
2431 #ifdef OOMPH_HAS_MPI
2432 
2433 //========================================================================
2434 /// Setup shared node scheme: Shared node lookup scheme contains
2435 /// a unique correspondence between all nodes on the halo/haloed
2436 /// elements between two processors.
2437 //========================================================================
2439 {
2440 
2441  // Determine the shared nodes lookup scheme - all nodes located on the
2442  // halo(ed) elements between two domains. This scheme is necessary in order
2443  // to identify master nodes that may not be present in the halo-haloed
2444  // element lookup scheme between two processors (for example, if the node
2445  // is on an element which is in a lookup scheme between two higher-numbered
2446  // processors)
2447 
2448  double t_start = 0.0;
2450  {
2451  t_start=TimingHelpers::timer();
2452  }
2453 
2454  // Store number of processors and current process
2455  int n_proc=Comm_pt->nproc();
2456  int my_rank=Comm_pt->my_rank();
2457 
2458  // Need to clear the shared node scheme first
2459  Shared_node_pt.clear();
2460 
2461  for (int d=0;d<n_proc;d++)
2462  {
2463 
2464  // map of bools for whether the node has been shared,
2465  // initialised to 0 (false) for each domain d
2466  std::map<Node*,bool> node_shared;
2467 
2468  // For all domains lower than the current domain: Do halos first
2469  // then haloed, to ensure correct order in lookup scheme from
2470  // the other side
2471  if (d<my_rank)
2472  {
2473  // Get the nodes from the halo elements first
2474  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(d));
2475  unsigned nhalo_elem=halo_elem_pt.size();
2476 
2477  for (unsigned e=0;e<nhalo_elem;e++)
2478  {
2479  // Get finite element
2480  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
2481  if (el_pt!=0)
2482  {
2483  // Loop over nodes
2484  unsigned nnod=el_pt->nnode();
2485  for (unsigned j=0;j<nnod;j++)
2486  {
2487  Node* nod_pt=el_pt->node_pt(j);
2488 
2489  // Add it as a shared node from current domain
2490  if (!node_shared[nod_pt])
2491  {
2492  this->add_shared_node_pt(d,nod_pt);
2493  node_shared[nod_pt]=true;
2494  }
2495 
2496  } // end loop over nodes
2497  }
2498 
2499  } // end loop over elements
2500 
2501  // Now get any left over nodes on the haloed elements
2502  Vector<GeneralisedElement*> haloed_elem_pt(this->haloed_element_pt(d));
2503  unsigned nhaloed_elem=haloed_elem_pt.size();
2504 
2505  for (unsigned e=0;e<nhaloed_elem;e++)
2506  {
2507  // Get element
2508  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(haloed_elem_pt[e]);
2509  if (el_pt!=0)
2510  {
2511  // Loop over the nodes
2512  unsigned nnod=el_pt->nnode();
2513  for (unsigned j=0;j<nnod;j++)
2514  {
2515  Node* nod_pt=el_pt->node_pt(j);
2516 
2517  // Add it as a shared node from current domain
2518  if (!node_shared[nod_pt])
2519  {
2520  this->add_shared_node_pt(d,nod_pt);
2521  node_shared[nod_pt]=true;
2522  }
2523 
2524  } // end loop over nodes
2525  }
2526  } // end loop over elements
2527 
2528  }
2529 
2530  // If the domain is bigger than the current rank: Do haloed first
2531  // then halo, to ensure correct order in lookup scheme from
2532  // the other side
2533  if (d>my_rank)
2534  {
2535  // Get the nodes from the haloed elements first
2536  Vector<GeneralisedElement*> haloed_elem_pt(this->haloed_element_pt(d));
2537  unsigned nhaloed_elem=haloed_elem_pt.size();
2538 
2539  for (unsigned e=0;e<nhaloed_elem;e++)
2540  {
2541  // Get element
2542  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(haloed_elem_pt[e]);
2543  if (el_pt!=0)
2544  {
2545  // Loop over nodes
2546  unsigned nnod=el_pt->nnode();
2547  for (unsigned j=0;j<nnod;j++)
2548  {
2549  Node* nod_pt=el_pt->node_pt(j);
2550 
2551  // Add it as a shared node from current domain
2552  if (!node_shared[nod_pt])
2553  {
2554  this->add_shared_node_pt(d,nod_pt);
2555  node_shared[nod_pt]=true;
2556  }
2557 
2558  } // end loop over nodes
2559  }
2560  } // end loop over elements
2561 
2562  // Now get the nodes from any halo elements left over
2563  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(d));
2564  unsigned nhalo_elem=halo_elem_pt.size();
2565 
2566  for (unsigned e=0;e<nhalo_elem;e++)
2567  {
2568  // Get element
2569  FiniteElement* el_pt=dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
2570  if (el_pt!=0)
2571  {
2572  // Loop over nodes
2573  unsigned nnod=el_pt->nnode();
2574  for (unsigned j=0;j<nnod;j++)
2575  {
2576  Node* nod_pt=el_pt->node_pt(j);
2577 
2578  // Add it as a shared node from current domain
2579  if (!node_shared[nod_pt])
2580  {
2581  this->add_shared_node_pt(d,nod_pt);
2582  node_shared[nod_pt]=true;
2583  }
2584 
2585  } // end loop over nodes
2586  }
2587  } // end loop over elements
2588 
2589  } // end if (d ...)
2590 
2591  } // end loop over processes
2592 
2593 
2594  double t_end=0.0;
2596  {
2597  t_end = TimingHelpers::timer();
2598  oomph_info << "Time for identification of shared nodes: "
2599  << t_end-t_start << std::endl;
2600  oomph_info.stream_pt()->flush();
2601  }
2602 
2603 }
2604 
2605 //========================================================================
2606 /// \short Synchronise shared node lookup schemes to cater for the
2607 /// the case where:
2608 /// (1) a certain node on the current processor is halo with proc p
2609 /// (i.e. its non-halo counterpart lives on processor p)
2610 /// (2) that node also exists (also as a halo) on another processor
2611 /// (q, say) where its non-halo counter part is also known to be
2612 /// on processor p.
2613 /// However, without calling this function the current processor does not
2614 /// necessarily know that it shares a node with processor q. This
2615 /// information can be required, e.g. when synchronising hanging node
2616 /// schemes over all processors.
2617 //========================================================================
2618 void Mesh::synchronise_shared_nodes(const bool& report_stats)
2619 {
2620 
2621  double t_start=0.0;
2623  {
2624  t_start=TimingHelpers::timer();
2625  }
2626 
2627  double tt_start=0.0;
2628  double tt_end=0.0;
2630  {
2631  tt_start=TimingHelpers::timer();
2632  }
2633 
2634  // Storage for current processor and number of processors
2635  int n_proc=Comm_pt->nproc();
2636  int my_rank=Comm_pt->my_rank();
2637 
2638 
2639 #ifdef PARANOID
2640  // Has some twit filled in shared nodes with own process?!
2641  // Check at start of function
2642  if (Shared_node_pt[my_rank].size()!=0)
2643  {
2644  throw OomphLibError(
2645  "Processor has shared nodes with itself! Something's gone wrong!",
2646  OOMPH_CURRENT_FUNCTION,
2647  OOMPH_EXCEPTION_LOCATION);
2648  }
2649 #endif
2650 
2651 
2652  // Stage 1: Populate the set of of processor IDs that
2653  // each haloed node on current processor is haloed by.
2654  std::map<Node*,std::set<int> > shared_domain_set;
2655 
2656  // Associate unique number with any haloed nodes on this processor
2657  std::map<Node*,unsigned> global_haloed_node_number_plus_one;
2658  unsigned global_haloed_count=0;
2659 
2660  // Loop over domains
2661  for (int d=0;d<n_proc;d++)
2662  {
2663  // Don't talk to yourself
2664  if (d!=my_rank)
2665  {
2666  // Loop over haloed nodes
2667  unsigned nnod_haloed=this->nhaloed_node(d);
2668  for (unsigned j=0;j<nnod_haloed;j++)
2669  {
2670  Node* nod_pt=this->haloed_node_pt(d,j);
2671  shared_domain_set[nod_pt].insert(d);
2672  if (global_haloed_node_number_plus_one[nod_pt]==0)
2673  {
2674  global_haloed_node_number_plus_one[nod_pt]=global_haloed_count+1;
2675  global_haloed_count++;
2676  }
2677  }
2678  }
2679  }
2680 
2682  {
2683  tt_end = TimingHelpers::timer();
2684  oomph_info
2685  << "Time for initial classification in Mesh::synchronise_shared_nodes(): "
2686  << tt_end-tt_start << std::endl;
2687  tt_start = TimingHelpers::timer();
2688  }
2689 
2690 
2691  // Stage 2: All-to-all communication to inform all processors that
2692  // hold halo nodes with current processor about all domains that the current
2693  // processor shares nodes with [This will allow the other processors to add
2694  // these nodes to their shared node lookup schemes with processors
2695  // that they currently "can't see"].
2696 
2697  // Data to be sent to each processor
2698  Vector<int> send_n(n_proc,0);
2699 
2700  // Storage for all values to be sent to all processors
2701  Vector<unsigned> send_data;
2702 
2703  // Start location within send_data for data to be sent to each processor
2704  Vector<int> send_displacement(n_proc,0);
2705 
2706 
2707  // Loop over haloed nodes with other domains
2708  for (int domain=0;domain<n_proc;domain++)
2709  {
2710  //Set the offset for the current processor
2711  send_displacement[domain] = send_data.size();
2712 
2713  // Every processor works on haloed nodes with proc "domain" and
2714  // sends associations across to that domain. No need to talk to yourself...
2715  if(domain!=my_rank)
2716  {
2717  // Send total number of global haloed nodes
2718  // send_data.push_back(global_haloed_count);
2719 
2720  // Loop over haloed nodes
2721  unsigned nnod_haloed=this->nhaloed_node(domain);
2722  for (unsigned j=0;j<nnod_haloed;j++)
2723  {
2724  Node* nod_pt=this->haloed_node_pt(domain,j);
2725 
2726  // Send global ID of haloed node
2727  send_data.push_back(global_haloed_node_number_plus_one[nod_pt]-1);
2728 
2729  // Get the set of domains that halo this node
2730  std::set<int> tmp_shared_domain_set=shared_domain_set[nod_pt];
2731 
2732  // Insert number of shared domains into send data
2733  unsigned n_shared_domains= tmp_shared_domain_set.size();
2734  send_data.push_back(n_shared_domains);
2735 
2736  // Add shared domains
2737  for (std::set<int>::iterator it=tmp_shared_domain_set.begin();
2738  it!=tmp_shared_domain_set.end();it++)
2739  {
2740  send_data.push_back(*it);
2741  }
2742  }
2743  }
2744 
2745  //Find the number of data added to the vector
2746  send_n[domain] = send_data.size() - send_displacement[domain];
2747 
2748  }
2749 
2750 
2751 
2752  //Storage for the number of data to be received from each processor
2753  Vector<int> receive_n(n_proc,0);
2754 
2755  //Now send numbers of data to be sent between all processors
2756  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
2757  Comm_pt->mpi_comm());
2758 
2759 
2760  //We now prepare the data to be received
2761  //by working out the displacements from the received data
2762  Vector<int> receive_displacement(n_proc,0);
2763  int receive_data_count=0;
2764  for(int rank=0;rank<n_proc;++rank)
2765  {
2766  //Displacement is number of data received so far
2767  receive_displacement[rank] = receive_data_count;
2768  receive_data_count += receive_n[rank];
2769  }
2770 
2771  //Now resize the receive buffer for all data from all processors
2772  //Make sure that it has a size of at least one
2773  if(receive_data_count==0) {++receive_data_count;}
2774  Vector<unsigned> receive_data(receive_data_count);
2775 
2776  //Make sure that the send buffer has size at least one
2777  //so that we don't get a segmentation fault
2778  if(send_data.size()==0) {send_data.resize(1);}
2779 
2780  //Now send the data between all the processors
2781  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
2782  MPI_UNSIGNED,
2783  &receive_data[0],&receive_n[0],
2784  &receive_displacement[0],
2785  MPI_UNSIGNED,
2786  Comm_pt->mpi_comm());
2787 
2788 
2790  {
2791  tt_end = TimingHelpers::timer();
2792  oomph_info
2793  << "Time for alltoall in Mesh::synchronise_shared_nodes(): "
2794  << tt_end-tt_start << std::endl;
2795  tt_start = TimingHelpers::timer();
2796  }
2797 
2798 
2800  {
2801  oomph_info
2802  << "Starting vector to set conversion in "
2803  << "Mesh::synchronise_shared_nodes() for a total of "
2804  << nshared_node() << " nodes\n";
2805  tt_start = TimingHelpers::timer();
2806  }
2807 
2808 
2809  // Copy vector-based representation of shared nodes into
2810  // sets for faster search
2811  Vector<std::set<Node*> > shared_node_set(n_proc);
2812  for (int d=0;d<n_proc;d++)
2813  {
2814  unsigned n_vector=Shared_node_pt[d].size();
2815  for (unsigned i=0;i<n_vector;i++)
2816  {
2817  shared_node_set[d].insert(Shared_node_pt[d][i]);
2818  }
2819  }
2820 
2821 
2823  {
2824  tt_end = TimingHelpers::timer();
2825  oomph_info
2826  << "Time for vector to set in Mesh::synchronise_shared_nodes(): "
2827  << tt_end-tt_start << std::endl;
2828  tt_start = TimingHelpers::timer();
2829  }
2830 
2831 
2832  //Now use the received data
2833  for (int send_rank=0;send_rank<n_proc;send_rank++)
2834  {
2835  //Don't bother to do anything for the processor corresponding to the
2836  //current processor or if no data were received from this processor
2837  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
2838  {
2839  //Counter for the data within the large array
2840  unsigned count=receive_displacement[send_rank];
2841 
2842  // Read total number of global haloed nodes
2843  //unsigned n_global_haloed_nodes_on_send_proc=receive_data[count++];
2844 
2845  // Storage for nodes and associated domains:
2846  // domains_map[global_haloed_node_number].first = node
2847  // domains_map[global_haloed_node_number].second = set of domains
2848  // this node is associated
2849  // with.
2850  std::map<unsigned,std::pair<Node*,std::set<unsigned> > > domains_map;
2851 
2852  // Loop over halo nodes with sending processor
2853  unsigned nnod_halo=this->nhalo_node(send_rank);
2854  for (unsigned j=0;j<nnod_halo;j++)
2855  {
2856  Node* nod_pt=this->halo_node_pt(send_rank,j);
2857 
2858  // Read unique ID of haloed node on send proc
2859  unsigned haloed_node_id_on_send_proc=receive_data[count++];
2860 
2861  // Read out number of shared domains into send data
2862  unsigned n_shared_domains=receive_data[count++];
2863 
2864  // Prepare set of domains
2865  std::set<unsigned> domain_set;
2866 
2867  // Read 'em
2868  for (unsigned i=0;i<n_shared_domains;i++)
2869  {
2870  int shared_domain=receive_data[count++];
2871 
2872  // Record in set
2873  domain_set.insert(shared_domain);
2874  }
2875 
2876  // Add entry:
2877  domains_map[haloed_node_id_on_send_proc]=std::make_pair(nod_pt,
2878  domain_set);
2879 
2880  } // end of loop over halo nodes
2881 
2882 
2883 
2884  // Now add new shared nodes in order
2885 #ifdef PARANOID
2886  int previous_one=-1;
2887 #endif
2888  for (std::map<unsigned,std::pair<Node*,std::set<unsigned> > >::iterator
2889  it=domains_map.begin();it!=domains_map.end();it++)
2890  {
2891 
2892  // Super-paranoid: Check that the map actually sorted entries
2893  // by key (as it should)
2894 #ifdef PARANOID
2895  if (int((*it).first)<previous_one)
2896  {
2897  std::ostringstream error_stream;
2898  error_stream << "Map did not store entries in order of key\n "
2899  << "Current key: " << (*it).first
2900  << "; previous one: " << previous_one << "\n"
2901  << "Need to rewrite this...\n";
2902  throw OomphLibError(error_stream.str(),
2903  OOMPH_CURRENT_FUNCTION,
2904  OOMPH_EXCEPTION_LOCATION);
2905  }
2906  previous_one=(*it).first;
2907 #endif
2908 
2909  // Extract node
2910  Node* nod_pt=(*it).second.first;
2911 
2912  // Extract set of domains
2913  std::set<unsigned> domain_set((*it).second.second);
2914 
2915  // Read 'em
2916  for (std::set<unsigned>::iterator itt=domain_set.begin();
2917  itt!=domain_set.end();itt++)
2918  {
2919  int shared_domain=(*itt);
2920 
2921  // No need to add shared nodes with oneself!
2922  if (shared_domain!=my_rank)
2923  {
2924  // Is node already listed in shared node scheme? Find it
2925  // and get iterator to entry
2926  std::set<Node*>::iterator ittt=
2927  shared_node_set[shared_domain].find(nod_pt);
2928 
2929  // If it's not in there already iterator points to end of
2930  // set
2931  if (ittt==shared_node_set[shared_domain].end())
2932  {
2933  // Now add it
2934  add_shared_node_pt(shared_domain,nod_pt);
2935 
2936  // Update set
2937  shared_node_set[shared_domain].insert(nod_pt);
2938  }
2939  }
2940  }
2941  }
2942 
2943  } // end of any data is received and ignore own domain
2944 
2945  } // end of loop over send ranks
2946 
2947 
2948 
2949 #ifdef PARANOID
2950  // Has some twit filled in shared nodes with own process?!
2951  // Check at end pf function.
2952  if (Shared_node_pt[my_rank].size()!=0)
2953  {
2954  throw OomphLibError(
2955  "Processor has shared nodes with itself! Something's gone wrong!",
2956  OOMPH_CURRENT_FUNCTION,
2957  OOMPH_EXCEPTION_LOCATION);
2958  }
2959 #endif
2960 
2961 
2963  {
2964  tt_end = TimingHelpers::timer();
2965  oomph_info
2966  << "Time for final processing in Mesh::synchronise_shared_nodes(): "
2967  << tt_end-tt_start << std::endl;
2968  tt_start = TimingHelpers::timer();
2969  }
2970 
2972  {
2973  double t_end = TimingHelpers::timer();
2974  oomph_info << "Total time for Mesh::synchronise_shared_nodes(): "
2975  << t_end-t_start << std::endl;
2976  }
2977 }
2978 
2979 
2980 
2981 //========================================================================
2982 /// Classify all halo and haloed information in the mesh
2983 //========================================================================
2985  const bool& report_stats)
2986 {
2987 
2988 
2989 // MemoryUsage::doc_memory_usage(
2990 // "at beginning of Mesh::classify_halo_and_haloed_nodes");
2991 
2992  double t_start = 0.0;
2994  {
2995  t_start=TimingHelpers::timer();
2996  }
2997 
2998  double tt_start = 0.0;
3000  {
3001  tt_start=TimingHelpers::timer();
3002  }
3003 
3004  // Set up shared nodes scheme
3006 
3007  //MemoryUsage::doc_memory_usage("after setup shared node scheme");
3008 
3009  double tt_end=0.0;
3011  {
3012  tt_end=TimingHelpers::timer();
3013  oomph_info
3014  << "Time for Mesh::setup_shared_node_scheme() "
3015  << " Mesh::classify_halo_and_haloed_nodes(): "
3016  << tt_end-tt_start << std::endl;
3017  oomph_info.stream_pt()->flush();
3018  tt_start = TimingHelpers::timer();
3019  }
3020 
3021 
3022  //Wipe existing storage schemes for halo(ed) nodes
3023  Halo_node_pt.clear();
3024  Haloed_node_pt.clear();
3025 
3026  // Storage for number of processors and current processor
3027  int n_proc=Comm_pt->nproc();
3028  int my_rank=Comm_pt->my_rank();
3029  MPI_Status status;
3030 
3031  // Determine which processors the nodes are associated with
3032  // and hence who's in charge
3033  std::map<Data*,std::set<unsigned> > processors_associated_with_data;
3034  std::map<Data*,unsigned> processor_in_charge;
3035 
3036  // Loop over all processors and associate any nodes on the halo
3037  // elements involved with that processor
3038  for (int domain=0;domain<n_proc;domain++)
3039  {
3040  // Get vector of halo elements by copy operation
3041  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(domain));
3042 
3043  // Loop over halo elements associated with this adjacent domain
3044  unsigned nelem=halo_elem_pt.size();
3045  for (unsigned e=0;e<nelem;e++)
3046  {
3047  // Get element only have nodes if a finite element
3048  FiniteElement* finite_el_pt=dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
3049  if(finite_el_pt!=0)
3050  {
3051  //Loop over nodes
3052  unsigned nnod=finite_el_pt->nnode();
3053  for (unsigned j=0;j<nnod;j++)
3054  {
3055  Node* nod_pt=finite_el_pt->node_pt(j);
3056  // Associate node with this domain
3057  processors_associated_with_data[nod_pt].insert(domain);
3058 
3059  // Do the same if the node is solid
3060  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
3061  if (solid_nod_pt!=0)
3062  {
3063  processors_associated_with_data[
3064  solid_nod_pt->variable_position_pt()].insert(domain);
3065  }
3066  }
3067  }
3068  }
3069  }
3070 
3071 
3072  // Loop over all [non-halo] elements and associate their nodes
3073  // with current procesor
3074  unsigned nelem=this->nelement();
3075  for (unsigned e=0;e<nelem;e++)
3076  {
3077  FiniteElement* finite_el_pt=
3078  dynamic_cast<FiniteElement*>(this->element_pt(e));
3079 
3080  // Only visit non-halos and finite elements
3081  if((finite_el_pt!=0) && (!finite_el_pt->is_halo()))
3082  {
3083  // Loop over nodes
3084  unsigned nnod=finite_el_pt->nnode();
3085  for (unsigned j=0;j<nnod;j++)
3086  {
3087  Node* nod_pt=finite_el_pt->node_pt(j);
3088 
3089  // Associate this node with current processor
3090  processors_associated_with_data[nod_pt].insert(my_rank);
3091 
3092  // do the same if we have a SolidNode
3093  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
3094  if (solid_nod_pt!=0)
3095  {
3096  processors_associated_with_data
3097  [solid_nod_pt->variable_position_pt()].insert(my_rank);
3098  }
3099  }
3100  }
3101  }
3102 
3103 
3105  {
3106  tt_end = TimingHelpers::timer();
3107  oomph_info
3108  << "Time for setup loops in Mesh::classify_halo_and_haloed_nodes: "
3109  << tt_end-tt_start << std::endl;
3110  oomph_info.stream_pt()->flush();
3111  tt_start = TimingHelpers::timer();
3112  }
3113 
3114  //MemoryUsage::doc_memory_usage("after setup loops");
3115 
3116  // At this point we need to "synchronise" the nodes on halo(ed) elements
3117  // so that the processors_associated_with_data agrees for the same node
3118  // on all processors. Strategy: All local nodes have just had their
3119  // association recorded. Now loop over all haloed elements
3120  // and send the association of their nodes to the corresponding
3121  // halo processors where they update/augment the association of the
3122  // nodes of the corresponding halo elements.
3123 
3124  // Loop over all domains
3125  for (int d=0;d<n_proc;d++)
3126  {
3127  // Prepare vector to send/receive
3128  Vector<unsigned> processors_associated_with_data_on_other_proc;
3129 
3130  if (d!=my_rank)
3131  {
3132  // Communicate the processors associated with data on haloed elements
3133 
3134  // Get haloed elements
3135  Vector<GeneralisedElement*> haloed_elem_pt(this->haloed_element_pt(d));
3136 
3137  // Initialise counter for this haloed layer
3138  unsigned count_data=0;
3139 
3140  // Loop over haloed elements
3141  unsigned n_haloed_elem=haloed_elem_pt.size();
3142  for (unsigned e=0;e<n_haloed_elem;e++)
3143  {
3144  //Only nodes in finite elements
3145  FiniteElement* haloed_el_pt =
3146  dynamic_cast<FiniteElement*>(haloed_elem_pt[e]);
3147  if(haloed_el_pt!=0)
3148  {
3149  // Loop over nodes
3150  unsigned n_node=haloed_el_pt->nnode();
3151  for (unsigned j=0;j<n_node;j++)
3152  {
3153  Node* nod_pt=haloed_el_pt->node_pt(j);
3154 
3155  // Number of processors associated with this node
3156  unsigned n_assoc=processors_associated_with_data[nod_pt].size();
3157 
3158  // This number needs to be sent
3159  processors_associated_with_data_on_other_proc.push_back(n_assoc);
3160  count_data++;
3161 
3162  // Now add the process IDs associated to the vector to be sent
3163  std::set<unsigned> procs_set=
3164  processors_associated_with_data[nod_pt];
3165  for (std::set<unsigned>::iterator it=procs_set.begin();
3166  it!=procs_set.end();it++)
3167  {
3168  processors_associated_with_data_on_other_proc.push_back(*it);
3169  count_data++;
3170  }
3171  }
3172  }
3173  }
3174 
3175 
3176 
3177  // Send the information
3178  MPI_Send(&count_data,1,MPI_UNSIGNED,d,0,Comm_pt->mpi_comm());
3179  if (count_data!=0)
3180  {
3181  MPI_Send(&processors_associated_with_data_on_other_proc[0],count_data,
3182  MPI_UNSIGNED,d,1,Comm_pt->mpi_comm());
3183  }
3184  }
3185  else
3186  {
3187  // Receive the processors associated with data onto halo elements
3188  for (int dd=0;dd<n_proc;dd++)
3189  {
3190  if (dd!=my_rank) // (my_rank=d)
3191  {
3192  // We will be looping over the halo elements with process dd
3193  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(dd));
3194  unsigned n_halo_elem=halo_elem_pt.size();
3195  unsigned count_data=0;
3196  MPI_Recv(&count_data,1,MPI_UNSIGNED,dd,0,Comm_pt->mpi_comm(),&status);
3197  if (count_data!=0)
3198  {
3199  processors_associated_with_data_on_other_proc.resize(count_data);
3200  MPI_Recv(&processors_associated_with_data_on_other_proc[0],
3201  count_data,MPI_UNSIGNED,dd,1,Comm_pt->mpi_comm(),&status);
3202 
3203  // Reset counter and loop through nodes on halo elements
3204  count_data=0;
3205  for (unsigned e=0;e<n_halo_elem;e++)
3206  {
3207  FiniteElement* halo_el_pt=
3208  dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
3209  if(halo_el_pt!=0)
3210  {
3211  unsigned n_node=halo_el_pt->nnode();
3212  for (unsigned j=0;j<n_node;j++)
3213  {
3214  Node* nod_pt=halo_el_pt->node_pt(j);
3215 
3216  // Get number of processors associated with data that was sent
3217  unsigned n_assoc=
3218  processors_associated_with_data_on_other_proc[count_data];
3219  count_data++;
3220 
3221  for (unsigned i_assoc=0;i_assoc<n_assoc;i_assoc++)
3222  {
3223  // Get the process ID
3224  unsigned sent_domain=
3225  processors_associated_with_data_on_other_proc[count_data];
3226  count_data++;
3227 
3228  // Add it to this processor's list of IDs
3229  processors_associated_with_data[nod_pt].insert(sent_domain);
3230 
3231  // If the node is solid then add the ID to the solid data
3232  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
3233  if (solid_nod_pt!=0)
3234  {
3235  processors_associated_with_data
3236  [solid_nod_pt->variable_position_pt()].
3237  insert(sent_domain);
3238  }
3239  }
3240  }
3241  }
3242  }
3243  }
3244  }
3245  }
3246  }
3247  }
3248 
3250  {
3251  tt_end = TimingHelpers::timer();
3252  oomph_info
3253  << "Time for pt2pt send/recv in Mesh::classify_halo_and_haloed_nodes: "
3254  << tt_end-tt_start << std::endl;
3255  oomph_info.stream_pt()->flush();
3256  tt_start = TimingHelpers::timer();
3257  }
3258 
3259 
3260  //MemoryUsage::doc_memory_usage("after pt2pt send/recv");
3261 
3262  // Loop over all nodes on the present processor and put the highest-numbered
3263  // processor associated with each node "in charge" of the node
3264  unsigned nnod=this->nnode();
3265  for (unsigned j=0;j<nnod;j++)
3266  {
3267  Node* nod_pt=this->node_pt(j);
3268 
3269  // Reset halo status of node to false
3270  nod_pt->set_nonhalo();
3271 
3272  // If it's a SolidNode then the halo status of the data
3273  // associated with that must also be reset to false
3274  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
3275  if (solid_nod_pt!=0)
3276  {
3277  solid_nod_pt->variable_position_pt()->set_nonhalo();
3278  }
3279 
3280  // Now put the highest-numbered one in charge
3281  unsigned proc_max=0;
3282  std::set<unsigned> procs_set=processors_associated_with_data[nod_pt];
3283  for (std::set<unsigned>::iterator it=procs_set.begin();
3284  it!=procs_set.end();it++)
3285  {
3286  if (*it>proc_max) proc_max=*it;
3287  }
3288  processor_in_charge[nod_pt]=proc_max;
3289 
3290  // Do the same if we have a SolidNode
3291  if (solid_nod_pt!=0)
3292  {
3293  // Now put the highest-numbered one in charge
3294  unsigned proc_max_solid=0;
3295  std::set<unsigned> procs_set_solid=processors_associated_with_data
3296  [solid_nod_pt->variable_position_pt()];
3297  for (std::set<unsigned>::iterator it=procs_set_solid.begin();
3298  it!=procs_set_solid.end();it++)
3299  {
3300  if (*it>proc_max_solid) proc_max_solid=*it;
3301  }
3302  processor_in_charge[solid_nod_pt->variable_position_pt()]=proc_max_solid;
3303  }
3304  }
3305 
3306 
3307  // First stab at determining halo nodes. They are located on the halo
3308  // elements and the processor in charge differs from the
3309  // current processor
3310 
3311  // Only count nodes once (map is initialised to 0 = false)
3312  std::map<Node*,bool> done;
3313 
3314  // Loop over all processors
3315  for (int domain=0;domain<n_proc;domain++)
3316  {
3317  // Get vector of halo elements by copy operation
3318  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(domain));
3319 
3320  // Loop over halo elements associated with this adjacent domain
3321  unsigned nelem=halo_elem_pt.size();
3322 
3323  for (unsigned e=0;e<nelem;e++)
3324  {
3325  // Get element
3326  GeneralisedElement* el_pt=halo_elem_pt[e];
3327 
3328  //Can if be cast to a finite element
3329  FiniteElement* finite_el_pt=dynamic_cast<FiniteElement*>(el_pt);
3330  if(finite_el_pt!=0)
3331  {
3332  //Loop over nodes
3333  unsigned nnod=finite_el_pt->nnode();
3334  for (unsigned j=0;j<nnod;j++)
3335  {
3336  Node* nod_pt=finite_el_pt->node_pt(j);
3337 
3338  // Have we done this node already?
3339  if (!done[nod_pt])
3340  {
3341  // Is the other processor/domain in charge of this node?
3342  int proc_in_charge=processor_in_charge[nod_pt];
3343 
3344  if (proc_in_charge!=my_rank)
3345  {
3346 
3347  // To keep the order of the nodes consistent with that
3348  // in the haloed node lookup scheme, only
3349  // allow it to be added when the current domain is in charge
3350  if (proc_in_charge==int(domain))
3351  {
3352  // Add it as being halo node whose non-halo counterpart
3353  // is located on processor proc_in_charge
3354  this->add_halo_node_pt(proc_in_charge,nod_pt);
3355 
3356  // The node itself needs to know it is a halo
3357  nod_pt->set_halo(proc_in_charge);
3358 
3359  // If it's a SolidNode then the data associated with that
3360  // must also be halo
3361  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
3362  if (solid_nod_pt!=0)
3363  {
3364  solid_nod_pt->variable_position_pt()->
3365  set_halo(proc_in_charge);
3366  }
3367 
3368  // We're done with this node
3369  done[nod_pt]=true;
3370  }
3371  }
3372  }
3373  }
3374 
3375  } //End of finite element case
3376 
3377  // Now make sure internal data on halo elements is also halo
3378  unsigned nintern_data = el_pt->ninternal_data();
3379  for (unsigned iintern=0;iintern<nintern_data;iintern++)
3380  {
3381  el_pt->internal_data_pt(iintern)->set_halo(domain);
3382  }
3383  }
3384  }
3385 
3386 
3387  // First stab at determining haloed nodes. They are located on the haloed
3388  // elements and the processor in charge is the current processor
3389 
3390  // Loop over processors that share haloes with the current one
3391  for (int domain=0;domain<n_proc;domain++)
3392  {
3393  // Only count nodes once (map is initialised to 0 = false)
3394  std::map<Node*,bool> node_done;
3395 
3396  // Get vector of haloed elements by copy operation
3397  Vector<GeneralisedElement*> haloed_elem_pt(this->haloed_element_pt(domain));
3398 
3399  // Loop over haloed elements associated with this adjacent domain
3400  unsigned nelem=haloed_elem_pt.size();
3401 
3402  for (unsigned e=0;e<nelem;e++)
3403  {
3404  // Get element
3405  GeneralisedElement* el_pt=haloed_elem_pt[e];
3406 
3407  //Can it be cast to a finite element
3408  FiniteElement* finite_el_pt=dynamic_cast<FiniteElement*>(el_pt);
3409  if(finite_el_pt!=0)
3410  {
3411  //Loop over nodes
3412  unsigned nnod=finite_el_pt->nnode();
3413  for (unsigned j=0;j<nnod;j++)
3414  {
3415  Node* nod_pt=finite_el_pt->node_pt(j);
3416 
3417  // Have we done this node already?
3418  if (!node_done[nod_pt])
3419  {
3420  // Is the current processor/domain in charge of this node?
3421  int proc_in_charge=processor_in_charge[nod_pt];
3422 
3423  if (proc_in_charge==my_rank)
3424  {
3425  // Add it as being haloed from specified domain
3426  this->add_haloed_node_pt(domain,nod_pt);
3427 
3428  // We're done with this node
3429  node_done[nod_pt]=true;
3430  }
3431  }
3432 
3433  }
3434  }
3435  }
3436  }
3437 
3438 
3440  {
3441  tt_end = TimingHelpers::timer();
3442  oomph_info
3443  << "Time for first classific in Mesh::classify_halo_and_haloed_nodes: "
3444  << tt_end-tt_start << std::endl;
3445  oomph_info.stream_pt()->flush();
3446  tt_start = TimingHelpers::timer();
3447  }
3448 
3449  //MemoryUsage::doc_memory_usage("after first classific");
3450 
3451 
3452  // Find any overlooked halo nodes: These are any nodes on the halo/haloed
3453  // elements (i.e. precisely the nodes currently contained in the shared
3454  // node scheme) that have not been classified as haloes (yet) though they
3455  // should have been because another processor is in charge of them.
3456  // This arises when the "overlooked halo node" is not part of the
3457  // halo/haloed element lookup scheme between the current processor
3458  // and the processor that holds the non-halo counterpart. E.g. we're
3459  // on proc 3. A node at the very edge of its halo layer also exists
3460  // on procs 0 and 1 with 1 being "in charge". However, the node in
3461  // question is not part of the halo/haloed element lookup scheme between
3462  // processor 1 and 3 so in the classification performed above, we never
3463  // visit it so it's overlooked. The code below rectifies this by going
3464  // through the intermediate processor (here proc 0) that contains the node in
3465  // lookup schemes with the halo processor (here proc 3, this one) and the one
3466  // that contains the non-halo counterpart (here proc 1).
3467 
3468 
3469  // Counter for number of overlooked halos (if there aren't any we don't
3470  // need any comms below)
3471  unsigned n_overlooked_halo=0;
3472 
3473  // Record previously overlooked halo nodes so they can be
3474  // added to the shared node lookup scheme (in a consistent order) below
3475  Vector<Vector<Node*> > over_looked_halo_node_pt(n_proc);
3476 
3477  // Record previously overlooked haloed nodes so they can be
3478  // added to the shared node lookup scheme (in a consistent order) below
3479  Vector<Vector<Node*> > over_looked_haloed_node_pt(n_proc);
3480 
3481  // Data to be sent to each processor
3482  Vector<int> send_n(n_proc,0);
3483 
3484  // Storage for all values to be sent to all processors
3485  Vector<int> send_data;
3486 
3487  // Start location within send_data for data to be sent to each processor
3488  Vector<int> send_displacement(n_proc,0);
3489 
3490  // Check missing ones
3491  for (int domain=0;domain<n_proc;domain++)
3492  {
3493  //Set the offset for the current processor
3494  send_displacement[domain] = send_data.size();
3495 
3496  //Don't bother to do anything if the processor in the loop is the
3497  //current processor
3498  if(domain!=my_rank)
3499  {
3500  unsigned nnod=nshared_node(domain);
3501  for (unsigned j=0;j<nnod;j++)
3502  {
3503  Node* nod_pt=shared_node_pt(domain,j);
3504 
3505  // Is a different-numbered processor/domain in charge of this node?
3506  int proc_in_charge=processor_in_charge[nod_pt];
3507  if ((proc_in_charge!=my_rank)&&!(nod_pt->is_halo()))
3508  {
3509  // Add it as being halo node whose non-halo counterpart
3510  // is located on processor proc_in_charge
3511  this->add_halo_node_pt(proc_in_charge,nod_pt);
3512 
3513  // We have another one...
3514  n_overlooked_halo++;
3515  over_looked_halo_node_pt[proc_in_charge].push_back(nod_pt);
3516 
3517  // The node itself needs to know it is a halo
3518  nod_pt->set_halo(proc_in_charge);
3519 
3520  // If it's a SolidNode then the data associated with that
3521  // must also be halo
3522  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
3523  if (solid_nod_pt!=0)
3524  {
3525  solid_nod_pt->variable_position_pt()->set_halo(proc_in_charge);
3526  }
3527 
3528  // Send shared node ID and processor in charge info to
3529  // "intermediate" processor (i.e. the processor that has
3530  // the lookup schemes to talk to both
3531  send_data.push_back(j);
3532  send_data.push_back(proc_in_charge);
3533  }
3534  }
3535  }
3536 
3537  // End of data
3538  send_data.push_back(-1);
3539 
3540  //Find the number of data added to the vector
3541  send_n[domain] = send_data.size() - send_displacement[domain];
3542  }
3543 
3544 
3545  // Check if any processor has stumbled across overlooked halos
3546  // (if not we can omit the comms below)
3547  unsigned global_max_n_overlooked_halo=0;
3548  MPI_Allreduce(&n_overlooked_halo,&global_max_n_overlooked_halo,1,
3549  MPI_UNSIGNED,MPI_MAX,
3550  Comm_pt->mpi_comm());
3551 
3552 
3553  oomph_info << "Global max number of overlooked haloes: "
3554  << global_max_n_overlooked_halo << std::endl;
3555 
3557  {
3558  tt_end = TimingHelpers::timer();
3559  oomph_info
3560  <<"Time for setup 1st alltoalls in Mesh::classify_halo_and_haloed_nodes: "
3561  << tt_end-tt_start << std::endl;
3562  oomph_info.stream_pt()->flush();
3563  tt_start = TimingHelpers::timer();
3564  }
3565 
3566  //MemoryUsage::doc_memory_usage("after setup 1st alltoalls");
3567 
3568  // Any comms needed?
3569  if (global_max_n_overlooked_halo>0)
3570  {
3571 
3572 
3573 
3574  //Storage for the number of data to be received from each processor
3575  Vector<int> receive_n(n_proc,0);
3576 
3577  //Now send numbers of data to be sent between all processors
3578  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
3579  Comm_pt->mpi_comm());
3580 
3581 
3582 
3584  {
3585  tt_end = TimingHelpers::timer();
3586  oomph_info
3587  << "Time for 1st alltoall in Mesh::classify_halo_and_haloed_nodes: "
3588  << tt_end-tt_start << std::endl;
3589  oomph_info.stream_pt()->flush();
3590  tt_start = TimingHelpers::timer();
3591  }
3592 
3593  //MemoryUsage::doc_memory_usage("after 1st alltoall");
3594 
3595 
3596  //We now prepare the data to be received
3597  //by working out the displacements from the received data
3598  Vector<int> receive_displacement(n_proc,0);
3599  int receive_data_count=0;
3600  for(int rank=0;rank<n_proc;++rank)
3601  {
3602  //Displacement is number of data received so far
3603  receive_displacement[rank] = receive_data_count;
3604  receive_data_count += receive_n[rank];
3605  }
3606 
3607  //Now resize the receive buffer for all data from all processors
3608  //Make sure that it has a size of at least one
3609  if(receive_data_count==0) {++receive_data_count;}
3610  Vector<int> receive_data(receive_data_count);
3611 
3612  //Make sure that the send buffer has size at least one
3613  //so that we don't get a segmentation fault
3614  if(send_data.size()==0) {send_data.resize(1);}
3615 
3616  //Now send the data between all the processors
3617  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
3618  MPI_INT,
3619  &receive_data[0],&receive_n[0],
3620  &receive_displacement[0],
3621  MPI_INT,
3622  Comm_pt->mpi_comm());
3623 
3624 
3625 
3627  {
3628  tt_end = TimingHelpers::timer();
3629  oomph_info
3630  << "Time for 2nd alltoall in Mesh::classify_halo_and_haloed_nodes: "
3631  << tt_end-tt_start << std::endl;
3632  oomph_info.stream_pt()->flush();
3633  tt_start = TimingHelpers::timer();
3634  }
3635 
3636  //MemoryUsage::doc_memory_usage("after 2nd alltoall");
3637 
3638  // Provide storage for data to be sent to processor that used to be
3639  // in charge
3640  Vector<Vector<int> > send_data_for_proc_in_charge(n_proc);
3641 
3642  //Now use the received data
3643  for (int send_rank=0;send_rank<n_proc;send_rank++)
3644  {
3645  //Don't bother to do anything for the processor corresponding to the
3646  //current processor or if no data were received from this processor
3647  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
3648  {
3649  //Counter for the data within the large array
3650  unsigned count=receive_displacement[send_rank];
3651 
3652  // Unpack until we reach "end of data" indicator (-1)
3653  while(true)
3654  {
3655 
3656  //Read next entry
3657  int next_one=receive_data[count++];
3658 
3659  if (next_one==-1)
3660  {
3661  break;
3662  }
3663  else
3664  {
3665  // Shared halo node number in lookup scheme between intermediate
3666  // (i.e. this) processor and the one that has the overlooked halo
3667  unsigned j=unsigned(next_one);
3668 
3669  // Processor in charge:
3670  unsigned proc_in_charge=unsigned(receive_data[count++]);
3671 
3672  // Find actual node from shared node lookup scheme
3673  Node* nod_pt=shared_node_pt(send_rank,j);
3674 
3675 
3676  // Note: This search seems relatively cheap
3677  // and in the tests done, did not benefit
3678  // from conversion to map-based search
3679  // as in
3680  // TreeBasedRefineableMeshBase::synchronise_hanging_nodes()
3681 
3682 
3683  // Locate its index in lookup scheme with proc in charge
3684  bool found=false;
3685  unsigned nnod=nshared_node(proc_in_charge);
3686  for (unsigned jj=0;jj<nnod;jj++)
3687  {
3688  if (nod_pt==shared_node_pt(proc_in_charge,jj))
3689  {
3690  found=true;
3691 
3692  // Shared node ID in lookup scheme with intermediate (i.e. this)
3693  // processor
3694  send_data_for_proc_in_charge[proc_in_charge].push_back(jj);
3695 
3696  // Processor that holds the overlooked halo node
3697  send_data_for_proc_in_charge[proc_in_charge].push_back(
3698  send_rank);
3699 
3700  break;
3701  }
3702  }
3703  if (!found)
3704  {
3705 
3706  std::ostringstream error_stream;
3707  error_stream << "Failed to find node that is shared node " << j
3708  << " (with processor " << send_rank
3709  << ") \n in shared node lookup scheme with processor "
3710  << proc_in_charge << " which is in charge.\n";
3711  throw OomphLibError(error_stream.str(),
3712  OOMPH_CURRENT_FUNCTION,
3713  OOMPH_EXCEPTION_LOCATION);
3714  }
3715  }
3716  }
3717 
3718  }
3719  } //End of data is received
3720 
3721 
3723  {
3724  tt_end = TimingHelpers::timer();
3725  oomph_info
3726  << "Time for 1st setup 3rd alltoall in Mesh::classify_halo_and_haloed_nodes: "
3727  << tt_end-tt_start << std::endl;
3728  oomph_info.stream_pt()->flush();
3729  tt_start = TimingHelpers::timer();
3730  }
3731 
3732  //MemoryUsage::doc_memory_usage("after 1st setup for 3rd alltoall");
3733 
3734  // Data to be sent to each processor
3735  Vector<int> all_send_n(n_proc,0);
3736 
3737  // Storage for all values to be sent to all processors
3738  Vector<int> all_send_data;
3739 
3740  // Start location within send_data for data to be sent to each processor
3741  Vector<int> all_send_displacement(n_proc,0);
3742 
3743  // Collate data
3744  for (int domain=0;domain<n_proc;domain++)
3745  {
3746  //Set the offset for the current processor
3747  all_send_displacement[domain] = all_send_data.size();
3748 
3749  //Don't bother to do anything if the processor in the loop is the
3750  //current processor
3751  if(domain!=my_rank)
3752  {
3753  unsigned n=send_data_for_proc_in_charge[domain].size();
3754  for (unsigned j=0;j<n;j++)
3755  {
3756  all_send_data.push_back(
3757  send_data_for_proc_in_charge[domain][j]);
3758  }
3759  }
3760 
3761  // End of data
3762  all_send_data.push_back(-1);
3763 
3764  //Find the number of data added to the vector
3765  all_send_n[domain]=all_send_data.size()-all_send_displacement[domain];
3766  }
3767 
3768 
3770  {
3771  tt_end = TimingHelpers::timer();
3772  oomph_info
3773  << "Time for 2nd setup 3rd alltoall in Mesh::classify_halo_and_haloed_nodes: "
3774  << tt_end-tt_start << std::endl;
3775  oomph_info.stream_pt()->flush();
3776  tt_start = TimingHelpers::timer();
3777  }
3778 
3779  //MemoryUsage::doc_memory_usage("after 2nd setup 3rd alltoall");
3780 
3781  //Storage for the number of data to be received from each processor
3782  Vector<int> all_receive_n(n_proc,0);
3783 
3784  //Now send numbers of data to be sent between all processors
3785  MPI_Alltoall(&all_send_n[0],1,MPI_INT,&all_receive_n[0],1,MPI_INT,
3786  Comm_pt->mpi_comm());
3787 
3788 
3789 
3791  {
3792  tt_end = TimingHelpers::timer();
3793  oomph_info
3794  << "Time for 3rd alltoall in Mesh::classify_halo_and_haloed_nodes: "
3795  << tt_end-tt_start << std::endl;
3796  oomph_info.stream_pt()->flush();
3797  tt_start = TimingHelpers::timer();
3798  }
3799 
3800  //MemoryUsage::doc_memory_usage("after 3rd alltoall");
3801 
3802  //We now prepare the data to be received
3803  //by working out the displacements from the received data
3804  Vector<int> all_receive_displacement(n_proc,0);
3805  int all_receive_data_count=0;
3806 
3807  for(int rank=0;rank<n_proc;++rank)
3808  {
3809  //Displacement is number of data received so far
3810  all_receive_displacement[rank] = all_receive_data_count;
3811  all_receive_data_count += all_receive_n[rank];
3812  }
3813 
3814  //Now resize the receive buffer for all data from all processors
3815  //Make sure that it has a size of at least one
3816  if (all_receive_data_count==0) {++all_receive_data_count;}
3817  Vector<int> all_receive_data(all_receive_data_count);
3818 
3819  //Make sure that the send buffer has size at least one
3820  //so that we don't get a segmentation fault
3821  if(all_send_data.size()==0) {all_send_data.resize(1);}
3822 
3823  //Now send the data between all the processors
3824  MPI_Alltoallv(&all_send_data[0],&all_send_n[0],&all_send_displacement[0],
3825  MPI_INT,
3826  &all_receive_data[0],&all_receive_n[0],
3827  &all_receive_displacement[0],
3828  MPI_INT,
3829  Comm_pt->mpi_comm());
3830 
3831 
3833  {
3834  tt_end = TimingHelpers::timer();
3835  oomph_info
3836  << "Time for 4th alltoall in Mesh::classify_halo_and_haloed_nodes: "
3837  << tt_end-tt_start << std::endl;
3838  oomph_info.stream_pt()->flush();
3839  tt_start = TimingHelpers::timer();
3840  }
3841 
3842  //MemoryUsage::doc_memory_usage("after 4th alltoall");
3843 
3844 
3845  //Now use the received data
3846  for (int send_rank=0;send_rank<n_proc;send_rank++)
3847  {
3848  //Don't bother to do anything for the processor corresponding to the
3849  //current processor or if no data were received from this processor
3850  if((send_rank != my_rank) && (all_receive_n[send_rank] != 0))
3851  {
3852  //Counter for the data within the large array
3853  unsigned count=all_receive_displacement[send_rank];
3854 
3855  // Unpack until we reach "end of data" indicator (-1)
3856  while(true)
3857  {
3858 
3859  //Read next entry
3860  int next_one=all_receive_data[count++];
3861 
3862  if (next_one==-1)
3863  {
3864  break;
3865  }
3866  else
3867  {
3868  // Shared node ID in lookup scheme with intermediate (sending)
3869  // processor
3870  unsigned j=unsigned(next_one);
3871 
3872  // Get pointer to previously overlooked halo
3873  Node* nod_pt=shared_node_pt(send_rank,j);
3874 
3875  // Proc where overlooked halo is
3876  unsigned proc_with_overlooked_halo=all_receive_data[count++];
3877 
3878  // Add it as being haloed from specified domain
3879  this->add_haloed_node_pt(proc_with_overlooked_halo,nod_pt);
3880 
3881  // Record new haloed node so it an be added to the shared
3882  // node lookup scheme (in a consistent order) below.
3883  over_looked_haloed_node_pt[proc_with_overlooked_halo].
3884  push_back(nod_pt);
3885  }
3886  }
3887  }
3888  } //End of data is received
3889 
3891  {
3892  tt_end = TimingHelpers::timer();
3893  oomph_info
3894  << "Time for postproc 4th alltoall in Mesh::classify_halo_and_haloed_nodes: "
3895  << tt_end-tt_start << std::endl;
3896  oomph_info.stream_pt()->flush();
3897  tt_start = TimingHelpers::timer();
3898  }
3899 
3900  //MemoryUsage::doc_memory_usage("after postprocess 4th alltoall");
3901 
3902  // Now add previously overlooked halo/haloed nodes to shared node
3903  // lookup scheme in consistent order
3904  for (int d=0;d<n_proc;d++)
3905  {
3906  // For all domains lower than the current domain: Do halos first
3907  // then haloed, to ensure correct order in lookup scheme from
3908  // the other side
3909  if (d<my_rank)
3910  {
3911  unsigned nnod=over_looked_halo_node_pt[d].size();
3912  for (unsigned j=0;j<nnod;j++)
3913  {
3914  this->add_shared_node_pt(d,over_looked_halo_node_pt[d][j]);
3915  }
3916  nnod=over_looked_haloed_node_pt[d].size();
3917  for (unsigned j=0;j<nnod;j++)
3918  {
3919  this->add_shared_node_pt(d,over_looked_haloed_node_pt[d][j]);
3920  }
3921  }
3922  else if (d>my_rank)
3923  {
3924 
3925  unsigned nnod=over_looked_haloed_node_pt[d].size();
3926  for (unsigned j=0;j<nnod;j++)
3927  {
3928  this->add_shared_node_pt(d,over_looked_haloed_node_pt[d][j]);
3929  }
3930  nnod=over_looked_halo_node_pt[d].size();
3931  for (unsigned j=0;j<nnod;j++)
3932  {
3933  this->add_shared_node_pt(d,over_looked_halo_node_pt[d][j]);
3934  }
3935  }
3936  }
3937 
3938 
3939  // Doc stats
3940  if (report_stats)
3941  {
3942  // Report total number of halo(ed) and shared nodes for this process
3943  oomph_info << "BEFORE SYNCHRONISE SHARED NODES Processor " << my_rank
3944  << " holds " << this->nnode()
3945  << " nodes of which " << this->nhalo_node()
3946  << " are halo nodes \n while " << this->nhaloed_node()
3947  << " are haloed nodes, and " << this->nshared_node()
3948  << " are shared nodes." << std::endl;
3949 
3950  // Report number of halo(ed) and shared nodes with each domain
3951  // from the current process
3952  for (int iproc=0;iproc<n_proc;iproc++)
3953  {
3954  // Get vector of halo elements by copy operation
3955  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(iproc));
3956  Vector<GeneralisedElement*> haloed_elem_pt(
3957  this->haloed_element_pt(iproc));
3958  oomph_info << "With process " << iproc << ", there are "
3959  << this->nhalo_node(iproc) << " halo nodes, and " << std::endl
3960  << this->nhaloed_node(iproc) << " haloed nodes, and "
3961  << this->nshared_node(iproc) << " shared nodes" << std::endl
3962  << halo_elem_pt.size() << " halo elements and "
3963  << haloed_elem_pt.size() << " haloed elements\n";
3964  }
3965  }
3966 
3967 // // Doc stats
3968 // if (report_stats)
3969 // {
3970 // // Report total number of halo(ed) and shared nodes for this process
3971 // oomph_info << "BEFORE SYNCHRONISE SHARED NODES Processor " << my_rank
3972 // << " holds " << this->nnode()
3973 // << " nodes of which " << this->nhalo_node()
3974 // << " are halo nodes \n while " << this->nhaloed_node()
3975 // << " are haloed nodes, and " << this->nshared_node()
3976 // << " are shared nodes." << std::endl;
3977 
3978 // // Report number of halo(ed) and shared nodes with each domain
3979 // // from the current process
3980 // for (int iproc=0;iproc<n_proc;iproc++)
3981 // {
3982 // // Get vector of halo elements by copy operation
3983 // Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(iproc));
3984 // Vector<GeneralisedElement*> haloed_elem_pt(
3985 // this->haloed_element_pt(iproc));
3986 // oomph_info << "With process " << iproc << ", there are "
3987 // << this->nhalo_node(iproc) << " halo nodes, and " << std::endl
3988 // << this->nhaloed_node(iproc) << " haloed nodes, and "
3989 // << this->nshared_node(iproc) << " shared nodes" << std::endl
3990 // << halo_elem_pt.size() << " halo elements and "
3991 // << haloed_elem_pt.size() << " haloed elements\n";
3992 // }
3993 // }
3994 
3995  } // end if comms reqd because we encountered overlooked halo elements
3996 
3997 
3998  //MemoryUsage::doc_memory_usage("before sync halo nodes");
3999 
4000  // Synchronise shared nodes
4001  synchronise_shared_nodes(report_stats);
4002 
4003  //MemoryUsage::doc_memory_usage("after sync halo nodes");
4004 
4005 #ifdef PARANOID
4006  // Has some twit filled in haloed nodes with own process?!
4007  if (Haloed_node_pt[my_rank].size()!=0)
4008  {
4009  throw OomphLibError(
4010  "Processor has haloed nodes with itself! Something's gone wrong!",
4011  OOMPH_CURRENT_FUNCTION,
4012  OOMPH_EXCEPTION_LOCATION);
4013  }
4014  // Has some twit filled in halo nodes with own process?!
4015  if (Halo_node_pt[my_rank].size()!=0)
4016  {
4017  throw OomphLibError(
4018  "Processor has halo nodes with itself! Something's gone wrong!",
4019  OOMPH_CURRENT_FUNCTION,
4020  OOMPH_EXCEPTION_LOCATION);
4021  }
4022  // Has some twit filled in root haloed elements with own process?!
4023  if (Root_haloed_element_pt[my_rank].size()!=0)
4024  {
4025  throw OomphLibError(
4026  "Processor has root haloed elements with itself! Something's gone wrong!",
4027  OOMPH_CURRENT_FUNCTION,
4028  OOMPH_EXCEPTION_LOCATION);
4029  }
4030  // Has some twit filled in root halo elements with own process?!
4031  if (Root_halo_element_pt[my_rank].size()!=0)
4032  {
4033  throw OomphLibError(
4034  "Processor has root halo elements with itself! Something's gone wrong!",
4035  OOMPH_CURRENT_FUNCTION,
4036  OOMPH_EXCEPTION_LOCATION);
4037  }
4038 #endif
4039 
4040  // Doc stats
4041  if (report_stats)
4042  {
4043  // Report total number of halo(ed) and shared nodes for this process
4044  oomph_info << "Processor " << my_rank
4045  << " holds " << this->nnode()
4046  << " nodes of which " << this->nhalo_node()
4047  << " are halo nodes \n while " << this->nhaloed_node()
4048  << " are haloed nodes, and " << this->nshared_node()
4049  << " are shared nodes." << std::endl;
4050 
4051  // Report number of halo(ed) and shared nodes with each domain
4052  // from the current process
4053  for (int iproc=0;iproc<n_proc;iproc++)
4054  {
4055  // Get vector of halo elements by copy operation
4056  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(iproc));
4057  Vector<GeneralisedElement*> haloed_elem_pt(
4058  this->haloed_element_pt(iproc));
4059  oomph_info << "With process " << iproc << ", there are "
4060  << this->nhalo_node(iproc) << " halo nodes, and " << std::endl
4061  << this->nhaloed_node(iproc) << " haloed nodes, and "
4062  << this->nshared_node(iproc) << " shared nodes" << std::endl
4063  << halo_elem_pt.size() << " halo elements and "
4064  << haloed_elem_pt.size() << " haloed elements\n";
4065  }
4066  }
4067 
4068 
4069  //MemoryUsage::doc_memory_usage("before resize halo nodes");
4070 
4071  // Now resize halo nodes if required (can be over-ruled from the outside
4072  // either by user (for (risky!) efficienty saving) or from overloaded
4073  // version of classify_... in refineable version of that function
4074  // where resize_halo_nodes() is called after synchronising hanging nodes.
4076  {
4078  }
4079 
4080  //MemoryUsage::doc_memory_usage("after resize halo nodes");
4081 
4083  {
4084  double t_end = TimingHelpers::timer();
4085  oomph_info << "Total time for Mesh::classify_halo_and_halo_nodes(): "
4086  << t_end-t_start << std::endl;
4087  oomph_info.stream_pt()->flush();
4088  }
4089 
4090 // MemoryUsage::doc_memory_usage(
4091 // "at end of Mesh::classify_halo_and_halo_nodes()");
4092 
4093 }
4094 
4095 
4096 //========================================================================
4097 /// Helper function that resizes halo nodes to the same
4098 /// size as their non-halo counterparts if required. (A discrepancy
4099 /// can arise if a FaceElement that introduces additional unknowns
4100 /// are attached to a bulk element that shares a node with a haloed element.
4101 /// In that case the joint node between haloed and non-haloed element
4102 /// is resized on that processor but not on the one that holds the
4103 /// halo counterpart (because no FaceElement is attached to the halo
4104 /// element)
4105 //=========================================================================
4107 {
4108 
4109 
4110  double t_start = 0.0;
4112  {
4113  t_start=TimingHelpers::timer();
4114  }
4115 
4116  MPI_Status status;
4117 
4118  // Nuffink needs to be done if mesh isn't distributed
4119  if (is_mesh_distributed())
4120  {
4121 
4122  // Storage for current processor and number of processors
4123  int n_proc=Comm_pt->nproc();
4124  int my_rank=Comm_pt->my_rank();
4125 
4126  // Loop over domains on which non-halo counter parts of my halo nodes live
4127  for (int d=0;d<n_proc;d++)
4128  {
4129  // On current processor: Receive data for my halo nodes with proc d.
4130  // Elsewhere: Send haloed data with proc d.
4131  if (d==my_rank)
4132  {
4133  // Loop over domains that hold non-halo counterparts of my halo nodes
4134  for (int dd=0;dd<n_proc;dd++)
4135  {
4136  // Don't talk to yourself
4137  if (dd!=d)
4138  {
4139  // How many of my nodes are halo nodes whose non-halo
4140  // counterpart is located on processor dd?
4141  int nnod_halo=this->nhalo_node(dd);
4142  int nnod_ext_halo=this->nexternal_halo_node(dd);
4143  if ((nnod_halo+nnod_ext_halo)!=0)
4144  {
4145  // Receive from processor dd number of haloed nodes (entry 0),
4146  // external haloed nodes (entry 1) and total number of
4147  // unsigneds to be sent below (entry 2)
4148  Vector<int> tmp(3);
4149  MPI_Recv(&tmp[0],3,MPI_INT,dd,0,Comm_pt->mpi_comm(),&status);
4150 
4151 #ifdef PARANOID
4152  // Check that number of halo/haloed nodes match
4153  int nnod_haloed=tmp[0];
4154  if (nnod_haloed!=nnod_halo)
4155  {
4156  std::ostringstream error_message;
4157  error_message
4158  << "Clash in numbers of halo and haloed nodes "
4159  << std::endl;
4160  error_message
4161  << " between procs "
4162  << dd << " and " << d << ": "
4163  << nnod_haloed << " " << nnod_halo << std::endl;
4164  throw OomphLibError(error_message.str(),
4165  OOMPH_CURRENT_FUNCTION,
4166  OOMPH_EXCEPTION_LOCATION);
4167  }
4168 
4169  // Check that number of external halo/haloed nodes match
4170  int nnod_ext_haloed=tmp[1];
4171  if (nnod_ext_haloed!=nnod_ext_halo)
4172  {
4173  std::ostringstream error_message;
4174  error_message
4175  << "Clash in numbers of external halo and haloed nodes "
4176  << std::endl;
4177  error_message
4178  << " between procs "
4179  << dd << " and " << d << ": "
4180  << nnod_ext_haloed << " " << nnod_ext_halo << std::endl;
4181  throw OomphLibError(error_message.str(),
4182  OOMPH_CURRENT_FUNCTION,
4183  OOMPH_EXCEPTION_LOCATION);
4184  }
4185 #endif
4186 
4187  // How many unsigneds are we about to receive
4188  unsigned n_rec=tmp[2];
4189 
4190  // Get strung-together data from other proc
4191  Vector<unsigned> unsigned_rec_data(n_rec);
4192  MPI_Recv(&unsigned_rec_data[0],n_rec,MPI_UNSIGNED,dd,
4193  0,Comm_pt->mpi_comm(),&status);
4194 
4195  // Step through the flat-packed unsigneds
4196  unsigned count=0;
4197 
4198  // Normal and external halo nodes
4199  for (unsigned loop=0;loop<2;loop++)
4200  {
4201  unsigned hi_nod=nnod_halo;
4202  if (loop==1)
4203  {
4204  hi_nod=nnod_ext_halo;
4205  }
4206  for (int j=0;j<int(hi_nod);j++)
4207  {
4208  // Which node are we dealing with
4209  Node* nod_pt=0;
4210  if (loop==0)
4211  {
4212  nod_pt=this->halo_node_pt(dd,j);
4213  }
4214  else
4215  {
4216  nod_pt=this->external_halo_node_pt(dd,j);
4217  }
4218 
4219  // How many values do we have locally?
4220  unsigned nval_local=nod_pt->nvalue();
4221 
4222  // Read number of values on other side
4223  unsigned nval_other=unsigned_rec_data[count++];
4224 
4225  if (nval_local!=nval_other)
4226  {
4227  nod_pt->resize(nval_other);
4228  }
4229 
4230  // Read number of entries in resize map
4231  unsigned nentry=unsigned_rec_data[count++];
4232  if (nentry!=0)
4233  {
4234  // Is current node a boundary node?
4235  BoundaryNodeBase* bnod_pt=
4236  dynamic_cast<BoundaryNodeBase*>(nod_pt);
4237 #ifdef PARANOID
4238  if (bnod_pt==0)
4239  {
4240  throw OomphLibError(
4241  "Failed to cast node to boundary node even though we've received data for boundary node",
4242  OOMPH_CURRENT_FUNCTION,
4243  OOMPH_EXCEPTION_LOCATION);
4244  }
4245 #endif
4246 
4247  // Create storage for map if it doesn't already exist
4248  bool already_existed=true;
4249  if(bnod_pt->
4250  index_of_first_value_assigned_by_face_element_pt()==0)
4251  {
4252  bnod_pt->
4253  index_of_first_value_assigned_by_face_element_pt()=
4254  new std::map<unsigned, unsigned>;
4255  already_existed=false;
4256  }
4257 
4258  // Get pointer to the map of indices associated with
4259  // additional values created by face elements
4260  std::map<unsigned, unsigned>* map_pt=
4262 
4263  // Loop over number of entries in map (as received)
4264  for (unsigned i=0;i<nentry;i++)
4265  {
4266  // Read out pairs...
4267  unsigned first_received=unsigned_rec_data[count++];
4268  unsigned second_received=unsigned_rec_data[count++];
4269 
4270  // If it exists check that values are consistent:
4271  if (already_existed)
4272  {
4273 #ifdef PARANOID
4274  if ((*map_pt)[first_received]!=second_received)
4275  {
4276  std::ostringstream error_message;
4277  error_message
4278  << "Existing map entry for map entry "
4279  << i << " for node located at ";
4280  unsigned n=nod_pt->ndim();
4281  for (unsigned ii=0;ii<n;ii++)
4282  {
4283  error_message << nod_pt->position(ii) << " ";
4284  }
4285  error_message
4286  << "Key: " << first_received << " "
4287  << "Local value: " << (*map_pt)[first_received] << " "
4288  << "Received value: " << second_received << std::endl;
4289  throw OomphLibError(error_message.str(),
4290  OOMPH_CURRENT_FUNCTION,
4291  OOMPH_EXCEPTION_LOCATION);
4292  }
4293 #endif
4294  }
4295  // Else assign
4296  else
4297  {
4298  (*map_pt)[first_received]=second_received;
4299  }
4300  }
4301  }
4302  }
4303  }
4304  }
4305  }
4306  }
4307  }
4308  // Send my haloed nodes whose halo counterparts are located on processor d
4309  else
4310  {
4311  // Storage for number of haloed nodes (entry 0), number of external
4312  // haloed nodes (entry 1) and total number of unsigneds to be sent below
4313  // (entry 2)
4314  Vector<int> tmp(3);
4315  int nnod_haloed=this->nhaloed_node(d);
4316  tmp[0]=nnod_haloed;
4317  int nnod_ext_haloed=this->nexternal_haloed_node(d);
4318  tmp[1]=nnod_ext_haloed;
4319  if ((nnod_haloed+nnod_ext_haloed)!=0)
4320  {
4321  // Now string together the data
4322  Vector<unsigned> unsigned_send_data;
4323 
4324  // Normal and external haloed nodes
4325  for (unsigned loop=0;loop<2;loop++)
4326  {
4327  unsigned hi_nod=nnod_haloed;
4328  if (loop==1)
4329  {
4330  hi_nod=nnod_ext_haloed;
4331  }
4332  for (int j=0;j<int(hi_nod);j++)
4333  {
4334  // Which node are we dealing with?
4335  Node* nod_pt=0;
4336  if (loop==0)
4337  {
4338  nod_pt=this->haloed_node_pt(d,j);
4339  }
4340  else
4341  {
4342  nod_pt=this->external_haloed_node_pt(d,j);
4343  }
4344 
4345  // Add number of values of node
4346  unsigned_send_data.push_back(nod_pt->nvalue());
4347 
4348  // Get pointer to the map of indices associated with
4349  // additional values created by face elements
4350 
4351  // Is it a boundary node?
4352  BoundaryNodeBase* bnod_pt=dynamic_cast<BoundaryNodeBase*>(nod_pt);
4353  if (bnod_pt==0)
4354  {
4355  // Not a boundary node -- there are zero map entries to follow
4356  unsigned_send_data.push_back(0);
4357  }
4358  else
4359  {
4360  // It's a boundary node: Check if it's been resized
4361  std::map<unsigned, unsigned>* map_pt=
4363 
4364  // No additional values created -- there are zero map entries
4365  // to follow
4366  if (map_pt==0)
4367  {
4368  unsigned_send_data.push_back(0);
4369  }
4370  // Created additional values
4371  else
4372  {
4373  // How many map entries were there
4374  unsigned_send_data.push_back(map_pt->size());
4375 
4376  // Loop over entries in map and add to send data
4377  for (std::map<unsigned, unsigned>::iterator p=
4378  map_pt->begin();
4379  p!=map_pt->end();p++)
4380  {
4381  unsigned_send_data.push_back((*p).first);
4382  unsigned_send_data.push_back((*p).second);
4383  }
4384  }
4385  }
4386  }
4387  }
4388 
4389  // How many values are there in total?
4390  int n_send=unsigned_send_data.size();
4391  tmp[2]=n_send;
4392 
4393  // Send the counts across to the other processor
4394  MPI_Send(&tmp[0],3,MPI_INT,d,0,Comm_pt->mpi_comm());
4395 
4396  // Send it across to the processor
4397  MPI_Send(&unsigned_send_data[0],n_send,MPI_UNSIGNED,d,0,
4398  Comm_pt->mpi_comm());
4399  }
4400  }
4401  }
4402  }
4403 
4405  {
4406  double t_end = TimingHelpers::timer();
4407  oomph_info << "Total time for Mesh::resize_halo_nodes(): "
4408  << t_end-t_start << std::endl;
4409  }
4410 
4411 }
4412 
4413 
4414 //========================================================================
4415 /// Get all the halo data stored in the mesh and add pointers to
4416 /// the data to the map, indexed by global equation number
4417 //=========================================================================
4418 void Mesh::get_all_halo_data(std::map<unsigned,double*> &map_of_halo_data)
4419 {
4420 
4421  //Loop over the map of Halo_nodes
4422  for(std::map<unsigned,Vector<Node*> >::iterator it = Halo_node_pt.begin();
4423  it!=Halo_node_pt.end();++it)
4424  {
4425  //Find the number of nodes
4426  unsigned n_node = (it->second).size();
4427  //Loop over them all
4428  for(unsigned n=0;n<n_node;n++)
4429  {
4430  //Add the Node's values (including any solid values) to
4431  //the map
4432  (it->second)[n]->add_value_pt_to_map(map_of_halo_data);
4433  }
4434  } //End of loop over halo nodes
4435 
4436  //Now loop over all the halo elements and add their internal data
4437  //Loop over the map of Halo_nodes
4438  for(std::map<unsigned,Vector<GeneralisedElement*> >::
4439  iterator it = Root_halo_element_pt.begin();
4440  it!=Root_halo_element_pt.end();++it)
4441  {
4442  //Find the number of root elements
4443  unsigned n_element = (it->second).size();
4444  for(unsigned e=0;e<n_element;e++)
4445  {
4446  GeneralisedElement* el_pt = (it->second)[e];
4447 
4448  // Is it a refineable element?
4449  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(el_pt);
4450  if (ref_el_pt!=0)
4451  {
4452  // Vector of pointers to leaves in tree emanating from
4453  // current root halo element
4454  Vector<Tree*> leaf_pt;
4455  ref_el_pt->tree_pt()->stick_leaves_into_vector(leaf_pt);
4456 
4457  // Loop over leaves and add their objects (the finite elements)
4458  // to vector
4459  unsigned nleaf=leaf_pt.size();
4460  for (unsigned l=0;l<nleaf;l++)
4461  {
4462  leaf_pt[l]->object_pt()->
4463  add_internal_value_pt_to_map(map_of_halo_data);
4464  }
4465  }
4466  else
4467  {
4468  el_pt->add_internal_value_pt_to_map(map_of_halo_data);
4469  }
4470  }
4471  }
4472 
4473  ///Repeat for the external data
4474  for(std::map<unsigned,Vector<Node*> >::iterator it =
4475  External_halo_node_pt.begin();
4476  it!=External_halo_node_pt.end();++it)
4477  {
4478  //Find the number of nodes
4479  unsigned n_node = (it->second).size();
4480  //Loop over them all
4481  for(unsigned n=0;n<n_node;n++)
4482  {
4483  //Add the Node's values (including any solid values) to
4484  //the map
4485  (it->second)[n]->add_value_pt_to_map(map_of_halo_data);
4486  }
4487  } //End of loop over halo nodes
4488 
4489  //Now loop over all the halo elements and add their internal data
4490  //Loop over the map of Halo_nodes
4491  for(std::map<unsigned,Vector<GeneralisedElement*> >::
4492  iterator it = External_halo_element_pt.begin();
4493  it!=External_halo_element_pt.end();++it)
4494  {
4495  //Find the number of root elements
4496  unsigned n_element = (it->second).size();
4497  for(unsigned e=0;e<n_element;e++)
4498  {
4499  (it->second)[e]->add_internal_value_pt_to_map(map_of_halo_data);
4500  }
4501  }
4502 
4503 }
4504 
4505 
4506 
4507 
4508 //========================================================================
4509 /// Get halo node stats for this distributed mesh:
4510 /// Average/max/min number of halo nodes over all processors.
4511 /// \b Careful: Involves MPI Broadcasts and must therefore
4512 /// be called on all processors!
4513 //========================================================================
4514 void Mesh::get_halo_node_stats(double& av_number,
4515  unsigned& max_number,
4516  unsigned& min_number)
4517 {
4518  // Storage for number of processors and current processor
4519  int n_proc=Comm_pt->nproc();
4520  int my_rank=Comm_pt->my_rank();
4521 
4522  // Create vector to hold number of halo nodes
4523  Vector<int> nhalo_nodes(n_proc);
4524 
4525  // Stick own number of halo nodes into appropriate entry
4526  int nhalo_node_local=nhalo_node();
4527 
4528  // Gather information on root processor: First argument group
4529  // specifies what is to be sent (one int from each procssor, indicating
4530  // the number of dofs on it), the second group indicates where
4531  // the results are to be gathered (in rank order) on root processor.
4532  MPI_Gather(&nhalo_node_local,1,MPI_INT,
4533  &nhalo_nodes[0],1, MPI_INT,
4534  0,Comm_pt->mpi_comm());
4535 
4536  // Initialise stats
4537  av_number=0.0;
4538  int max=-1;
4539  int min=1000000000;
4540 
4541  if (my_rank==0)
4542  {
4543  for (int i=0;i<n_proc;i++)
4544  {
4545  av_number+=double(nhalo_nodes[i]);
4546  if (int(nhalo_nodes[i])>max) max=nhalo_nodes[i];
4547  if (int(nhalo_nodes[i])<min) min=nhalo_nodes[i];
4548 
4549  }
4550  av_number/=double(n_proc);
4551  }
4552 
4553  // Now broadcast the result back out
4554  MPI_Bcast(&max,1,MPI_INT,0,Comm_pt->mpi_comm());
4555  MPI_Bcast(&min,1,MPI_INT,0,Comm_pt->mpi_comm());
4556  MPI_Bcast(&av_number,1,MPI_DOUBLE,0,Comm_pt->mpi_comm());
4557 
4558  max_number=max;
4559  min_number=min;
4560 }
4561 
4562 
4563 //========================================================================
4564 /// Get haloed node stats for this distributed mesh:
4565 /// Average/max/min number of haloed nodes over all processors.
4566 /// \b Careful: Involves MPI Broadcasts and must therefore
4567 /// be called on all processors!
4568 //========================================================================
4569  void Mesh::get_haloed_node_stats(double& av_number,
4570  unsigned& max_number,
4571  unsigned& min_number)
4572 {
4573  // Storage for number of processors and current processor
4574  int n_proc=Comm_pt->nproc();
4575  int my_rank=Comm_pt->my_rank();
4576 
4577  // Create vector to hold number of haloed nodes
4578  Vector<int> nhaloed_nodes(n_proc);
4579 
4580  // Stick own number of haloed nodes into appropriate entry
4581  int nhaloed_node_local=nhaloed_node();
4582 
4583  // Gather information on root processor: First argument group
4584  // specifies what is to be sent (one int from each procssor, indicating
4585  // the number of dofs on it), the second group indicates where
4586  // the results are to be gathered (in rank order) on root processor.
4587  MPI_Gather(&nhaloed_node_local,1,MPI_INT,
4588  &nhaloed_nodes[0],1, MPI_INT,
4589  0,Comm_pt->mpi_comm());
4590 
4591  // Initialise stats
4592  av_number=0.0;
4593  int max=-1;
4594  int min=1000000000;
4595 
4596  if (my_rank==0)
4597  {
4598  for (int i=0;i<n_proc;i++)
4599  {
4600  av_number+=double(nhaloed_nodes[i]);
4601  if (int(nhaloed_nodes[i])>max) max=nhaloed_nodes[i];
4602  if (int(nhaloed_nodes[i])<min) min=nhaloed_nodes[i];
4603 
4604  }
4605  av_number/=double(n_proc);
4606  }
4607 
4608  // Now broadcast the result back out
4609  MPI_Bcast(&max,1,MPI_INT,0,Comm_pt->mpi_comm());
4610  MPI_Bcast(&min,1,MPI_INT,0,Comm_pt->mpi_comm());
4611  MPI_Bcast(&av_number,1,MPI_DOUBLE,0,Comm_pt->mpi_comm());
4612 
4613  max_number=max;
4614  min_number=min;
4615 }
4616 
4617 //========================================================================
4618 /// Distribute the mesh. Add to vector of deleted elements.
4619 //========================================================================
4621  const Vector<unsigned>& element_domain,
4622  Vector<GeneralisedElement*>& deleted_element_pt,
4623  DocInfo& doc_info,
4624  const bool& report_stats,
4625  const bool& overrule_keep_as_halo_element_status)
4626  {
4627 
4628  // Store communicator
4629  Comm_pt=comm_pt;
4630 
4631  // Storage for number of processors and current processor
4632  int n_proc=comm_pt->nproc();
4633  int my_rank=comm_pt->my_rank();
4634 
4635  // Storage for number of elements and number of nodes on this mesh
4636  unsigned nelem=this->nelement();
4637  unsigned nnod=this->nnode();
4638 
4639  std::ostringstream filename;
4640 
4641  // Doc the partitioning (only on processor 0)
4642  //-------------------------------------------
4643  if (doc_info.is_doc_enabled())
4644  {
4645  if (my_rank==0)
4646  {
4647  // Open files for doc of element partitioning
4648  Vector<std::ofstream*> domain_file(n_proc);
4649  for (int d=0;d<n_proc;d++)
4650  {
4651  // Note: doc_info.number() was set in Problem::distribute(...) to
4652  // reflect the submesh number
4653  //Clear the filename
4654  filename.str("");
4655  filename << doc_info.directory() << "/domain"
4656  << d << "-" << doc_info.number() << ".dat";
4657  domain_file[d]=new std::ofstream(filename.str().c_str());
4658  }
4659 
4660  // Doc
4661  for (unsigned e=0;e<nelem;e++)
4662  {
4663  //If we can't cast to a finite element, we can't output because
4664  //there is no output function
4665  FiniteElement* f_el_pt =
4666  dynamic_cast<FiniteElement*>(this->element_pt(e));
4667  if(f_el_pt!=0)
4668  {
4669  f_el_pt->output(*domain_file[element_domain[e]],5);
4670  }
4671  }
4672 
4673  for (int d=0;d<n_proc;d++)
4674  {
4675  domain_file[d]->close();
4676  delete domain_file[d];
4677  domain_file[d]=0;
4678  }
4679  }
4680  }
4681 
4682  // Loop over all elements, associate all
4683  //--------------------------------------
4684  // nodes with the highest-numbered processor and record all
4685  //---------------------------------------------------------
4686  // processors the node is associated with
4687  //---------------------------------------
4688 
4689  // Storage for processors in charge and processors associated with data
4690  std::map<Data*,std::set<unsigned> > processors_associated_with_data;
4691  std::map<Data*,unsigned> processor_in_charge;
4692 
4693  // For all nodes set the processor in charge to zero
4694  for (unsigned j=0;j<nnod;j++)
4695  {
4696  Node* nod_pt=this->node_pt(j);
4697  processor_in_charge[nod_pt]=0;
4698  }
4699 
4700  // Loop over elements
4701  for (unsigned e=0;e<nelem;e++)
4702  {
4703  // Get an element and its domain
4704  FiniteElement* el_pt = dynamic_cast<FiniteElement*>(this->element_pt(e));
4705  //Element will only have nodes if it is a finite element
4706  if(el_pt!=0)
4707  {
4708  unsigned el_domain=element_domain[e];
4709 
4710  // Associate nodes with highest numbered processor
4711  unsigned nnod=el_pt->nnode();
4712  for (unsigned j=0;j<nnod;j++)
4713  {
4714  Node* nod_pt=el_pt->node_pt(j);
4715 
4716  // processor in charge was initialised to 0 above
4717  if (el_domain>processor_in_charge[nod_pt])
4718  {
4719  processor_in_charge[nod_pt]=el_domain;
4720  }
4721  processors_associated_with_data[nod_pt].insert(el_domain);
4722  }
4723  }
4724  }
4725 
4726  // Doc the partitioning (only on processor 0)
4727  //-------------------------------------------
4728  if (doc_info.is_doc_enabled())
4729  {
4730  if (my_rank==0)
4731  {
4732  // Open files for doc of node partitioning
4733  Vector<std::ofstream*> node_file(n_proc);
4734  for (int d=0;d<n_proc;d++)
4735  {
4736  // Note: doc_info.number() was set in Problem::distribute(...) to
4737  // reflect the submesh number...
4738  //Clear the filename
4739  filename.str("");
4740  filename << doc_info.directory() << "/node"
4741  << d << "-" << doc_info.number() << ".dat";
4742  node_file[d]=new std::ofstream(filename.str().c_str());
4743  }
4744 
4745  // Doc
4746  for (unsigned j=0;j<nnod;j++)
4747  {
4748  Node* nod_pt=this->node_pt(j);
4749  const unsigned n_dim = nod_pt->ndim();
4750  for(unsigned i=0;i<n_dim;i++)
4751  {
4752  *node_file[processor_in_charge[nod_pt]] << nod_pt->x(i) << " ";
4753  }
4754  *node_file[processor_in_charge[nod_pt]] << "\n";
4755  }
4756  for (int d=0;d<n_proc;d++)
4757  {
4758  node_file[d]->close();
4759  delete node_file[d];
4760  node_file[d]=0;
4761  }
4762  }
4763  }
4764 
4765  // Declare all nodes as obsolete. We'll
4766  // change this setting for all nodes that must be retained
4767  // further down
4768  for (unsigned j=0;j<nnod;j++)
4769  {
4770  this->node_pt(j)->set_obsolete();
4771  }
4772 
4773 
4774  // Backup old mesh data and flush mesh
4775  //-------------------------------------
4776 
4777  // Backup pointers to elements in this mesh
4778  Vector<GeneralisedElement*> backed_up_el_pt(nelem);
4779  for (unsigned e=0;e<nelem;e++)
4780  {
4781  backed_up_el_pt[e]=this->element_pt(e);
4782  }
4783 
4784  // Info. used to re-generate the boundary element scheme after the
4785  // deletion of elements not belonging to the current processor)
4786 
4787  // Get the number of boundary elements before the deletion of non
4788  // retained elements
4789  const unsigned tmp_nboundary = this->nboundary();
4790  Vector<unsigned> ntmp_boundary_elements(tmp_nboundary);
4791  // In case that there are regions, also get the number of boundary
4792  // elements in each region
4793  Vector<Vector<unsigned> > ntmp_boundary_elements_in_region(tmp_nboundary);
4794  // Also get the finite element version of the elements and back them
4795  // up
4796  Vector<FiniteElement*> backed_up_f_el_pt(nelem);
4797 
4798  // Only do this if the mesh is a TriangleMeshBase
4799  TriangleMeshBase* triangle_mesh_pt = dynamic_cast<TriangleMeshBase*>(this);
4800  bool is_a_triangle_mesh_base_mesh = false;
4801  if (triangle_mesh_pt!=0)
4802  {
4803  // Set the flag to indicate we are working with a TriangleMeshBase
4804  // mesh
4805  is_a_triangle_mesh_base_mesh = true;
4806 
4807  // Are there regions?
4808  const unsigned n_regions = triangle_mesh_pt->nregion();
4809 
4810  // Loop over the boundaries
4811  for (unsigned ib = 0; ib < tmp_nboundary; ib++)
4812  {
4813  // Get the number of boundary elements
4814  ntmp_boundary_elements[ib] = this->nboundary_element(ib);
4815 
4816  // Resize the container
4817  ntmp_boundary_elements_in_region[ib].resize(n_regions);
4818 
4819  // Loop over the regions
4820  for (unsigned rr = 0 ; rr < n_regions; rr++)
4821  {
4822  // Get the region id
4823  const unsigned region_id =
4824  static_cast<unsigned>(triangle_mesh_pt->region_attribute(rr));
4825 
4826  // Store the number of element in the region (notice we are
4827  // using the region index not the region id to refer to the
4828  // region)
4829  ntmp_boundary_elements_in_region[ib][rr] =
4830  triangle_mesh_pt->nboundary_element_in_region(ib, region_id);
4831 
4832  } // for (rr < n_regions)
4833 
4834  } // for (ib < tmp_nboundary)
4835 
4836  for (unsigned e=0;e<nelem;e++)
4837  {
4838  // Get the finite element version of the elements and back them
4839  // up
4840  backed_up_f_el_pt[e] = this->finite_element_pt(e);
4841  }
4842 
4843  } // if (triangle_mesh_pt!=0)
4844 
4845  // Flush the mesh storage
4846  this->flush_element_storage();
4847 
4848  // Delete any storage of external elements and nodes
4850 
4851  // Boolean to indicate which element is to be retained
4852  std::vector<bool> element_retained(nelem,false);
4853 
4854  // Storage for element numbers of root halo elements that will be
4855  // retained on current processor: root_halo_element[p][j]
4856  // stores the element number (in the order in which the elements are stored
4857  // in backed_up_el_pt) of the j-th root halo element with processor
4858  // p.
4859  Vector<Vector<int> > root_halo_element(n_proc);
4860 
4861  // Dummy entry to make sure we always have something to send
4862  for (int p=0;p<n_proc;p++)
4863  {
4864  root_halo_element[p].push_back(-1);
4865  }
4866 
4867  // Determine which elements are going to end up on which processor
4868  //----------------------------------------------------------------
4869  unsigned number_of_retained_elements=0;
4870 
4871  // Loop over all backed up elements
4872  nelem=backed_up_el_pt.size();
4873  for (unsigned e=0;e<nelem;e++)
4874  {
4875  // Get element and its domain
4876  GeneralisedElement* el_pt=backed_up_el_pt[e];
4877  unsigned el_domain=element_domain[e];
4878 
4879  // If element is located on current processor add it back to the mesh
4880  if (el_domain==unsigned(my_rank))
4881  {
4882  // Add element to current processor
4883  element_retained[e]=true;
4884  number_of_retained_elements++;
4885  }
4886  // Otherwise we may still need it if it's a halo element:
4887  else
4888  {
4889  // If this current mesh has been told to keep all elements as halos,
4890  // OR the element itself knows that it must be kept then
4891  // keep it
4892  if ((this->Keep_all_elements_as_halos) ||
4893  (el_pt->must_be_kept_as_halo()))
4894  {
4895  if (!overrule_keep_as_halo_element_status)
4896  {
4897  // Add as root halo element whose non-halo counterpart is
4898  // located on processor el_domain
4899  if (!element_retained[e])
4900  {
4901  root_halo_element[el_domain].push_back(e);
4902  element_retained[e]=true;
4903  number_of_retained_elements++;
4904  }
4905  }
4906  }
4907  //Otherwise: Is one of the nodes associated with the current processor?
4908  else
4909  {
4910  //Can only have nodes if this is a finite element
4911  FiniteElement* finite_el_pt = dynamic_cast<FiniteElement*>(el_pt);
4912  if(finite_el_pt!=0)
4913  {
4914  unsigned n_node = finite_el_pt->nnode();
4915  for (unsigned n=0;n<n_node;n++)
4916  {
4917  Node* nod_pt=finite_el_pt->node_pt(n);
4918 
4919  // Keep element? (use stl find?)
4920  unsigned keep_it=false;
4921  for (std::set<unsigned>::iterator
4922  it=processors_associated_with_data[nod_pt].begin();
4923  it!=processors_associated_with_data[nod_pt].end();
4924  it++)
4925  {
4926  if (*it==unsigned(my_rank))
4927  {
4928  keep_it=true;
4929  //Break out of the loop over processors
4930  break;
4931  }
4932  }
4933 
4934  // Add a root halo element either if keep_it=true
4935  if (keep_it)
4936  {
4937  // Add as root halo element whose non-halo counterpart is
4938  // located on processor el_domain
4939  if (!element_retained[e])
4940  {
4941  root_halo_element[el_domain].push_back(e);
4942  element_retained[e]=true;
4943  number_of_retained_elements++;
4944  }
4945  //Now break out of loop over nodes
4946  break;
4947  }
4948  }
4949  }
4950  } //End of testing for halo by virtue of shared nodes
4951  }//End of halo element conditions
4952  } //end of loop over elements
4953 
4954  // First check that the number of elements is greater than zero, when
4955  // working with submeshes it may be possible that some of them have
4956  // no elements (face element meshes) since those may have been
4957  // deleted in "Problem::actions_before_distribute()"
4958  if (nelem > 0)
4959  {
4960  // Check that we are working with a TriangleMeshBase mesh, if
4961  // that is the case then we need to create shared boundaries
4962  if (is_a_triangle_mesh_base_mesh)
4963  {
4964  // Creation of shared boundaries
4965  // ------------------------------
4966  // All processors share the same boundary id for the created
4967  // shared boundary. We need all the elements on all processors,
4968  // that is why this step is performed before the deletion of the
4969  // elements not associated to the current processor.
4970  // N.B.: This applies only to unstructured meshes
4971  this->create_shared_boundaries(comm_pt, element_domain,
4972  backed_up_el_pt,
4973  backed_up_f_el_pt,
4974  processors_associated_with_data,
4975  overrule_keep_as_halo_element_status);
4976  } // if (is_a_triangle_mesh_base_mesh)
4977  } // if (nelem > 0)
4978 
4979  // NOTE: No need to add additional layer of halo elements.
4980  // Procedure for removing "overlooked" halo nodes in
4981  // deals classify_halo_and_haloed_nodes() deals
4982  // with the problem addressed here. [code that dealt with this
4983  // problem at distribution stage has been removed]
4984 
4985  // Store the finite element pointer version of the elements that are
4986  // about to be deleted, used to reset the boundary elements info
4987  Vector<FiniteElement*> deleted_f_element_pt;
4988 
4989  // Copy the elements associated with the actual
4990  // current processor into its own permanent storage.
4991  // Do it in the order in which the elements appeared originally
4992  nelem=backed_up_el_pt.size();
4993  for (unsigned e=0;e<nelem;e++)
4994  {
4995  GeneralisedElement* el_pt=backed_up_el_pt[e];
4996  if (element_retained[e])
4997  {
4998  this->add_element_pt(el_pt);
4999  }
5000  else
5001  {
5002  // Flush the object attached to the tree for this element?
5003  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(el_pt);
5004  if (ref_el_pt!=0)
5005  {
5006  ref_el_pt->tree_pt()->flush_object();
5007  }
5008 
5009 
5010  // Store pointer to the element that's about to be deleted.
5011 
5012  // Only for structured meshes since this "deleted_element_pt"
5013  // vector is used in the "problem" class to set null pointer to
5014  // the deleted elements in the Base_mesh_element_pt structure
5015  if (!is_a_triangle_mesh_base_mesh)
5016  {
5017  deleted_element_pt.push_back(el_pt);
5018  } // if (!is_a_triangle_mesh_base_mesh)
5019 
5020  if (is_a_triangle_mesh_base_mesh)
5021  {
5022  // Store pointer to the finite element that's about to be deleted
5023  deleted_f_element_pt.push_back(backed_up_f_el_pt[e]);
5024  }
5025 
5026  // Delete the element
5027  delete el_pt;
5028  }
5029  }
5030 
5031  // Copy the root halo elements associated with the actual
5032  // current processor into its own permanent storage; the order
5033  // here is somewhat random but we compensate for that by
5034  // ensuring that the corresponding haloed elements are
5035  // added in the same order below
5036 #ifdef PARANOID
5037  std::map<unsigned,bool> done;
5038 #endif
5039  for (int d=0;d<n_proc;d++)
5040  {
5041  nelem=root_halo_element[d].size();
5042  for (unsigned e=0;e<nelem;e++)
5043  {
5044  int number=root_halo_element[d][e];
5045  if (number>=0)
5046  {
5047 #ifdef PARANOID
5048  if (done[number])
5049  {
5050  std::ostringstream error_message;
5051  error_message
5052  << "Have already added element " << number
5053  << " as root halo element\n"
5054  << std::endl;
5055  throw OomphLibError(error_message.str(),
5056  OOMPH_CURRENT_FUNCTION,
5057  OOMPH_EXCEPTION_LOCATION);
5058  }
5059  done[number]=true;
5060 #endif
5061  this->add_root_halo_element_pt(d,backed_up_el_pt[number]);
5062  }
5063  }
5064  }
5065 
5066 
5067  // Now get root haloed elements: root_haloed_element[p][j] stores
5068  // the element number (in the order in which the elements are stored
5069  // in backedup_el_pt) of the j-th rooted halo element with processor
5070  // p. On proc my_proc this the same as root_haloed_element[my_proc][j]
5071  // on processor p, so get the information by gatherv operations.
5072  Vector<Vector<unsigned> > root_haloed_element(n_proc);
5073 
5074  // Find out number of root halo elements with each other proc
5075  Vector<int> nhalo(n_proc,0);
5076  Vector<int> nhaloed(n_proc,0);
5077  for (int p=0;p<n_proc;p++)
5078  {
5079  nhalo[p]=root_halo_element[p].size();
5080  }
5081 
5082  // Each processor sends number of halo elements it has with processor
5083  // p to processor p where this information is stored in nhaloed[...]
5084  for (int p=0;p<n_proc;p++)
5085  {
5086  // Gather the p-th entries in nhalo from every processor on
5087  // processor p and store them in nhaloed consecutively
5088  // starting at beginning
5089  MPI_Gather(&nhalo[p], 1, MPI_INT,
5090  &nhaloed[0], 1, MPI_INT,
5091  p,comm_pt->mpi_comm());
5092  }
5093 
5094  // In the big sequence of concatenated root halo elements (enumerated
5095  // individually on the various processors) where do the root halo
5096  // elements from a given processor start? Also figure out how many
5097  // root haloed elements there are in total by summing up their numbers
5098  Vector<int> start_index(n_proc,0);
5099  unsigned total_number_of_root_haloed_elements=0;
5100  for (int i_proc=0; i_proc<n_proc; i_proc++)
5101  {
5102  total_number_of_root_haloed_elements+=nhaloed[i_proc];
5103  if (i_proc!=0)
5104  {
5105  start_index[i_proc]=total_number_of_root_haloed_elements-
5106  nhaloed[i_proc];
5107  }
5108  else
5109  {
5110  start_index[0]=0;
5111  }
5112  }
5113 
5114  // Storage for all root haloed elements from the various processors, one
5115  // after the other, with some padding from negative entries to avoid
5116  // zero length vectors
5117  Vector<int> all_root_haloed_element(total_number_of_root_haloed_elements,
5118  0);
5119 
5120  // Now send the ids of the relevant elements via gatherv
5121  for (int p=0;p<n_proc;p++)
5122  {
5123  // Gather the p-th entries in nhalo from every processor on
5124  // processor p and store them in nhaloed consecutively
5125  // starting at beginning
5126  MPI_Gatherv(&root_halo_element[p][0], // pointer to first entry in vector
5127  // to be gathered on processor p
5128  nhalo[p], // Number of entries to be sent
5129  MPI_INT,
5130  &all_root_haloed_element[0], // Target -- this will store
5131  // the element numbers of
5132  // all root haloed elements
5133  // received from other processors
5134  // in order
5135  &nhaloed[0], // Pointer to the vector containing the lengths
5136  // of the vectors received from elsewhere
5137  &start_index[0], // "offset" for storage of vector received
5138  // from various processors in the global
5139  // concatenated vector
5140  MPI_INT,
5141  p, // processor that gathers the information
5142  comm_pt->mpi_comm());
5143  }
5144 
5145 
5146  // Determine root haloed elements
5147  //-------------------------------
5148 
5149  // Loop over all other processors
5150  unsigned count=0;
5151  for (int d=0;d<n_proc;d++)
5152  {
5153 
5154 #ifdef PARANOID
5155  std::map<unsigned,bool> done;
5156 #endif
5157 
5158  // Loop over root haloed elements with specified processor
5159  unsigned n=nhaloed[d];
5160  for (unsigned e=0;e<n;e++)
5161  {
5162 
5163  int number=all_root_haloed_element[count];
5164  count++;
5165 
5166  // Ignore padded -1s that were only inserted to avoid
5167  // zero sized vectors
5168  if (number>=0)
5169  {
5170  // Get pointer to element
5171  GeneralisedElement* el_pt=backed_up_el_pt[number];
5172 
5173  // Halo elements can't be haloed themselves
5174  if (!el_pt->is_halo())
5175  {
5176 
5177 #ifdef PARANOID
5178  if (done[number])
5179  {
5180  std::ostringstream error_message;
5181  error_message
5182  << "Have already added element " << number
5183  << " as root haloed element\n"
5184  << std::endl;
5185  throw OomphLibError(error_message.str(),
5186  OOMPH_CURRENT_FUNCTION,
5187  OOMPH_EXCEPTION_LOCATION);
5188  }
5189  done[number]=true;
5190 #endif
5191 
5192  // Current element is haloed by other processor
5193  this->add_root_haloed_element_pt(d,el_pt);
5194  }
5195  }
5196  }
5197  }
5198 
5199 
5200  // Doc stats
5201  if (report_stats)
5202  {
5203  oomph_info << "Processor " << my_rank
5204  << " holds " << this->nelement()
5205  << " elements of which " << this->nroot_halo_element()
5206  << " are root halo elements \n while "
5207  << this->nroot_haloed_element()
5208  << " are root haloed elements" << std::endl;
5209  }
5210 
5211 
5212  // Loop over all retained elements and mark their nodes
5213  //-----------------------------------------------------
5214  // as to be retained too (some double counting going on here)
5215  //-----------------------------------------------------------
5216  nelem=this->nelement();
5217  for (unsigned e=0;e<nelem;e++)
5218  {
5219  FiniteElement* f_el_pt= dynamic_cast<FiniteElement*>(this->element_pt(e));
5220 
5221  //If we have a finite element
5222  if(f_el_pt!=0)
5223  {
5224  // Loop over nodes
5225  unsigned nnod=f_el_pt->nnode();
5226  for (unsigned j=0;j<nnod;j++)
5227  {
5228  Node* nod_pt=f_el_pt->node_pt(j);
5229  nod_pt->set_non_obsolete();
5230  }
5231  }
5232  }
5233 
5234 
5235  // Now remove the pruned nodes
5236  this->prune_dead_nodes();
5237 
5238 
5239 #ifdef OOMPH_HAS_TRIANGLE_LIB
5240  if (is_a_triangle_mesh_base_mesh)
5241  {
5242  triangle_mesh_pt->
5243  reset_boundary_element_info(ntmp_boundary_elements,
5244  ntmp_boundary_elements_in_region,
5245  deleted_f_element_pt);
5246  } // if (tri_mesh_pt!=0)
5247  else
5248  {
5249 #endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
5250 
5251  // Temporarly set the mesh as distributed
5253 
5254 #ifdef OOMPH_HAS_TRIANGLE_LIB
5255  }
5256 #endif
5257 
5258  // Re-setup tree forest. (Call this every time even if
5259  // a (distributed) mesh has no elements on this processor.
5260  // We still need to participate in communication.)
5261  TreeBasedRefineableMeshBase* ref_mesh_pt=
5262  dynamic_cast<TreeBasedRefineableMeshBase*>(this);
5263  if (ref_mesh_pt!=0)
5264  {
5265  ref_mesh_pt->setup_tree_forest();
5266  }
5267 
5268  // Classify nodes
5269  classify_halo_and_haloed_nodes(doc_info,report_stats);
5270 
5271  // Doc?
5272  //-----
5273  if (doc_info.is_doc_enabled())
5274  {
5275  doc_mesh_distribution(doc_info);
5276  }
5277 
5278 }
5279 
5280 
5281 //========================================================================
5282 /// (Irreversibly) prune halo(ed) elements and nodes, usually
5283 /// after another round of refinement, to get rid of
5284 /// excessively wide halo layers. Note that the current
5285 /// mesh will be now regarded as the base mesh and no unrefinement
5286 /// relative to it will be possible once this function
5287 /// has been called.
5288 //========================================================================
5290  deleted_element_pt,
5291  DocInfo& doc_info,
5292  const bool& report_stats)
5293 {
5294 
5295  //MemoryUsage::doc_memory_usage("at start of mesh-level prunes");
5296 
5297 
5298 #ifdef OOMPH_HAS_MPI
5299  // Delete any external element storage before performing the redistribution
5300  // (in particular, external halo nodes that are on mesh boundaries)
5302 #endif
5303 
5304  TreeBasedRefineableMeshBase* ref_mesh_pt=
5305  dynamic_cast<TreeBasedRefineableMeshBase*>(this);
5306  if (ref_mesh_pt!=0)
5307  {
5308 
5309  // Storage for number of processors and current processor
5310  int n_proc=Comm_pt->nproc();
5311  int my_rank=Comm_pt->my_rank();
5312 
5313  // Doc stats
5314  if (report_stats)
5315  {
5316  oomph_info << "\n----------------------------------------------------\n";
5317  oomph_info << "Before pruning: Processor " << my_rank
5318  << " holds " << this->nelement()
5319  << " elements of which " << this->nroot_halo_element()
5320  << " are root halo elements \n while "
5321  << this->nroot_haloed_element()
5322  << " are root haloed elements" << std::endl;
5323 
5324  // Report total number of halo(ed) and shared nodes for this process
5325  oomph_info << "Before pruning: Processor " << my_rank
5326  << " holds " << this->nnode()
5327  << " nodes of which " << this->nhalo_node()
5328  << " are halo nodes \n while " << this->nhaloed_node()
5329  << " are haloed nodes, and " << this->nshared_node()
5330  << " are shared nodes." << std::endl;
5331 
5332  // Report number of halo(ed) and shared nodes with each domain
5333  // from the current process
5334  for (int iproc=0;iproc<n_proc;iproc++)
5335  {
5336  oomph_info << "Before pruning: With process " << iproc << ", there are "
5337  << this->nhalo_node(iproc) << " halo nodes, and " << std::endl
5338  << this->nhaloed_node(iproc) << " haloed nodes, and "
5339  << this->nshared_node(iproc) << " shared nodes" << std::endl;
5340  }
5341  oomph_info << "----------------------------------------------------\n\n";
5342  }
5343 
5344 
5345  double t_start = 0.0;
5347  {
5348  t_start=TimingHelpers::timer();
5349  }
5350 
5351  // Declare all nodes as obsolete. We'll
5352  // change this setting for all nodes that must be retained
5353  // further down
5354  unsigned nnod=this->nnode();
5355  for (unsigned j=0;j<nnod;j++)
5356  {
5357  this->node_pt(j)->set_obsolete();
5358  }
5359 
5360  // Backup pointers to elements in this mesh (they must be finite elements
5361  // beacuse it's a refineable mesh)
5362  unsigned nelem=this->nelement();
5363  Vector<FiniteElement*> backed_up_el_pt(nelem);
5364  std::map<RefineableElement*,bool> keep_element;
5365  for (unsigned e=0;e<nelem;e++)
5366  {
5367  backed_up_el_pt[e]=this->finite_element_pt(e);
5368  }
5369 
5370  //MemoryUsage::doc_memory_usage("after backed up elements");
5371 
5372  // Get the min and max refinement level, and current refinement pattern
5373  unsigned min_ref=0;
5374  unsigned max_ref=0;
5375 
5376  // Get min and max refinement level
5377  unsigned local_min_ref=0;
5378  unsigned local_max_ref=0;
5379  ref_mesh_pt->get_refinement_levels(local_min_ref,local_max_ref);
5380 
5381  // Reconcile between processors: If (e.g. following distribution/pruning)
5382  // the mesh has no elements on this processor) then ignore its
5383  // contribution to the poll of max/min refinement levels
5384  int int_local_min_ref=local_min_ref;
5385  int int_local_max_ref=local_max_ref;
5386 
5387  if (nelem==0)
5388  {
5389  int_local_min_ref=INT_MAX;
5390  int_local_max_ref=INT_MIN;
5391  }
5392 
5393  int int_min_ref=0;
5394  int int_max_ref=0;
5395 
5396  MPI_Allreduce(&int_local_min_ref,&int_min_ref,1,
5397  MPI_INT,MPI_MIN,
5398  Comm_pt->mpi_comm());
5399  MPI_Allreduce(&int_local_max_ref,&int_max_ref,1,
5400  MPI_INT,MPI_MAX,
5401  Comm_pt->mpi_comm());
5402 
5403  min_ref=unsigned(int_min_ref);
5404  max_ref=unsigned(int_max_ref);
5405 
5406  // Get refinement pattern
5407  Vector<Vector<unsigned> > current_refined;
5408  ref_mesh_pt->get_refinement_pattern(current_refined);
5409 
5410  // get_refinement_pattern refers to the elements at each level
5411  // that were refined when proceeding to the next level
5412  int local_n_ref=current_refined.size();
5413 
5414  // Bypass if no elements
5415  if (nelem==0)
5416  {
5417  local_n_ref=INT_MIN;
5418  }
5419 
5420  // Reconcile between processors
5421  int int_n_ref=0;
5422  MPI_Allreduce(&local_n_ref,&int_n_ref,1,
5423  MPI_INT,MPI_MAX,
5424  Comm_pt->mpi_comm());
5425  unsigned n_ref=int(int_n_ref);
5426 
5427 
5428  double t_end = 0.0;
5430  {
5431  t_end=TimingHelpers::timer();
5432  oomph_info
5433  << "Time for establishing refinement levels in "
5434  << " Mesh::prune_halo_elements_and_nodes() [includes comms]: "
5435  << t_end-t_start << std::endl;
5436  t_start = TimingHelpers::timer();
5437  }
5438 
5439  //MemoryUsage::doc_memory_usage("after establishing refinement levels");
5440 
5441  // Bypass everything until next comms if no elements
5442  if (nelem>0)
5443  {
5444  // Loop over all elements; keep those on the min refinement level
5445  // Need to go back to the level indicated by min_ref
5446  unsigned base_level=n_ref-(max_ref-min_ref);
5447 
5448  // This is the new minimum uniform refinement level
5449  // relative to total unrefined base mesh
5450  ref_mesh_pt->uniform_refinement_level_when_pruned()=min_ref;
5451 
5452  // Get the elements at the specified "base" refinement level
5453  Vector<RefineableElement*> base_level_elements_pt;
5454  ref_mesh_pt->get_elements_at_refinement_level(base_level,
5455  base_level_elements_pt);
5456 
5457  // Loop over the elements at this level
5458  unsigned n_base_el=base_level_elements_pt.size();
5459  for (unsigned e=0;e<n_base_el;e++)
5460  {
5461  // Extract correct element...
5462  RefineableElement* ref_el_pt=base_level_elements_pt[e];
5463 
5464 
5465  // Check it exists
5466  if (ref_el_pt!=0)
5467  {
5468  // Keep all non-halo elements, remove excess halos
5469  if ((!ref_el_pt->is_halo())||(ref_el_pt->must_be_kept_as_halo()))
5470  {
5471  keep_element[ref_el_pt]=true;
5472 
5473  // Loop over this non-halo element's nodes and retain them too
5474  unsigned nnod=ref_el_pt->nnode();
5475  for (unsigned j=0;j<nnod;j++)
5476  {
5477  ref_el_pt->node_pt(j)->set_non_obsolete();
5478  }
5479  }
5480  }
5481 
5482  } // end loop over base level elements
5483  }
5484 
5485  // Synchronise refinement level when pruned over all meshes even if they
5486  // were empty (in which case the uniform refinement level is still zero
5487  // so go for max
5488  unsigned n_unif=0;
5489  unsigned n_unif_local=ref_mesh_pt->uniform_refinement_level_when_pruned();
5490  MPI_Allreduce(&n_unif_local,&n_unif,1,
5491  MPI_UNSIGNED,MPI_MAX,
5492  Comm_pt->mpi_comm());
5493  ref_mesh_pt->uniform_refinement_level_when_pruned()=n_unif;
5494 
5495 
5496  t_end = 0.0;
5498  {
5499  t_end=TimingHelpers::timer();
5500  oomph_info
5501  << "Time for synchronising refinement levels in "
5502  << " Mesh::prune_halo_elements_and_nodes() [includes comms]: "
5503  << t_end-t_start << std::endl;
5504  t_start = TimingHelpers::timer();
5505  }
5506 
5507 
5508 
5509  //MemoryUsage::doc_memory_usage("after synchronising refinement levels");
5510 
5511 
5512  // Now work on which "root" halo elements to keep at this level
5513  // Can't use the current set directly; however,
5514  // we know the refinement level of the current halo, so
5515  // it is possible to go from that backwards to find the "father
5516  // halo element" necessary to complete this step
5517 
5518  // Temp map of vectors holding the pointers to the root halo elements
5519  std::map<unsigned, Vector<FiniteElement*> > tmp_root_halo_element_pt;
5520 
5521  // Temp map of vectors holding the pointers to the root haloed elements
5522  std::map<unsigned, Vector<FiniteElement*> > tmp_root_haloed_element_pt;
5523 
5524  // Make sure we only push back each element once
5525  std::map<unsigned,std::map<FiniteElement*,bool> >
5526  tmp_root_halo_element_is_retained;
5527 
5528  // Map to store if a halo element survives
5529  std::map<FiniteElement*,bool> halo_element_is_retained;
5530 
5531  for (int domain=0;domain<n_proc;domain++)
5532  {
5533  // Get vector of halo elements with processor domain by copy operation
5534  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(domain));
5535 
5536  // Loop over halo elements associated with this adjacent domain
5537  unsigned nelem=halo_elem_pt.size();
5538  for (unsigned e=0;e<nelem;e++)
5539  {
5540  // Get element
5541  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
5542  (halo_elem_pt[e]);
5543 
5544  // An element should only be kept if its refinement
5545  // level is the same as the minimum refinement level
5546  unsigned halo_el_level=ref_el_pt->refinement_level();
5547 
5548  RefineableElement* el_pt=0;
5549  if (halo_el_level==min_ref)
5550  {
5551  // Already at the correct level
5552  el_pt=ref_el_pt;
5553  }
5554  else
5555  {
5556  // Need to go up the tree to the father element at min_ref
5557  RefineableElement* father_el_pt;
5558  ref_el_pt->get_father_at_refinement_level(min_ref,father_el_pt);
5559  el_pt=father_el_pt;
5560  }
5561 
5562  // Now loop over nodes in the halo element and retain it as
5563  // halo element if any of it's nodes are shared with one of the
5564  // non-halo-elements that we've already identified earlier -- these
5565  // were set to non-obsolete above.
5566  unsigned nnod=el_pt->nnode();
5567  for (unsigned j=0;j<nnod;j++)
5568  {
5569  // Node has been reclaimed by one of the non-halo elements above
5570  Node* nod_pt=el_pt->node_pt(j);
5571  if (!nod_pt->is_obsolete())
5572  {
5573  // Keep element and add it to preliminary storage for
5574  // halo elements associated with current neighbouring domain
5575  if (!tmp_root_halo_element_is_retained[domain][el_pt])
5576  {
5577  tmp_root_halo_element_pt[domain].push_back(el_pt);
5578  tmp_root_halo_element_is_retained[domain][el_pt]=true;
5579  }
5580  keep_element[el_pt]=true;
5581  halo_element_is_retained[el_pt]=true;
5582  break;
5583  }
5584  }
5585  }
5586  }
5587 
5589  {
5590  t_end=TimingHelpers::timer();
5591  oomph_info
5592  << "Time for setup of retention pattern in "
5593  << " Mesh::prune_halo_elements_and_nodes(): "
5594  << t_end-t_start << std::endl;
5595  t_start = TimingHelpers::timer();
5596  }
5597 
5598  //MemoryUsage::doc_memory_usage("after setup of retention pattern");
5599 
5600  // Make sure everybody finishes this part
5601  MPI_Barrier(Comm_pt->mpi_comm());
5602 
5603  // Now all processors have decided (independently) which of their
5604  // (to-be root) halo elements they wish to retain. Now we need to figure out
5605  // which of their elements are haloed and add them in the appropriate
5606  // order into the haloed element scheme. For this we exploit that
5607  // the halo and haloed elements are accessed in the same order on
5608  // all processors!
5609 
5610  // Identify haloed elements on domain d
5611  for (int d=0;d<n_proc;d++)
5612  {
5613  // Loop over domains that halo this domain
5614  for (int dd=0;dd<n_proc;dd++)
5615  {
5616  // Dont't talk to yourself
5617  if (d!=dd)
5618  {
5619  // If we're identifying my haloed elements:
5620  if (d==my_rank)
5621  {
5622  // Get vector all elements that are currently haloed by domain dd
5624  haloed_elem_pt(this->haloed_element_pt(dd));
5625 
5626  // Create a vector of ints to indicate if the halo element
5627  // on processor dd processor was kept
5628  unsigned nelem=haloed_elem_pt.size();
5629  Vector<int> halo_kept(nelem);
5630 
5631  // Receive this vector from processor dd
5632  if (nelem!=0)
5633  {
5634  MPI_Status status;
5635  MPI_Recv(&halo_kept[0],nelem,MPI_INT,dd,0,Comm_pt->mpi_comm(),
5636  &status);
5637 
5638  // Classify haloed element accordingly
5639  for (unsigned e=0;e<nelem;e++)
5640  {
5641  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
5642  (haloed_elem_pt[e]);
5643 
5644  // An element should only be kept if its refinement
5645  // level is the same as the minimum refinement level
5646  unsigned haloed_el_level=ref_el_pt->refinement_level();
5647 
5648  // Go up the tree to the correct level
5649  RefineableElement* el_pt;
5650 
5651  if (haloed_el_level==min_ref)
5652  {
5653  // Already at the correct level
5654  el_pt=ref_el_pt;
5655  }
5656  else
5657  {
5658  // Need to go up the tree to the father element at min_ref
5659  RefineableElement* father_el_pt;
5661  (min_ref,father_el_pt);
5662  el_pt=father_el_pt;
5663  }
5664 
5665  if (halo_kept[e]==1)
5666  {
5667  // I am being haloed by processor dd
5668  // Only keep it if it's not already in the storage
5669  bool already_root_haloed=false;
5670  unsigned n_root_haloed=tmp_root_haloed_element_pt[dd].size();
5671  for (unsigned e_root=0;e_root<n_root_haloed;e_root++)
5672  {
5673  if (el_pt==tmp_root_haloed_element_pt[dd][e_root])
5674  {
5675  already_root_haloed=true;
5676  break;
5677  }
5678  }
5679  if (!already_root_haloed)
5680  {
5681  tmp_root_haloed_element_pt[dd].push_back(el_pt);
5682  }
5683  }
5684  }
5685  }
5686  }
5687  else
5688  {
5689  // If we're dealing with my halo elements:
5690  if (dd==my_rank)
5691  {
5692  // Find (current) halo elements on processor dd whose non-halo is
5693  // on processor d
5695  halo_elem_pt(this->halo_element_pt(d));
5696 
5697  // Create a vector of ints to indicate if the halo
5698  // element was kept
5699  unsigned nelem=halo_elem_pt.size();
5700  Vector<int> halo_kept(nelem,0);
5701  for (unsigned e=0;e<nelem;e++)
5702  {
5703  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
5704  (halo_elem_pt[e]);
5705 
5706  // An element should only be kept if its refinement
5707  // level is the same as the minimum refinement level
5708  unsigned halo_el_level=ref_el_pt->refinement_level();
5709 
5710  // Go up the tree to the correct level
5711  RefineableElement* el_pt;
5712  if (halo_el_level==min_ref)
5713  {
5714  // Already at the correct level
5715  el_pt=ref_el_pt;
5716  }
5717  else
5718  {
5719  // Need to go up the tree to the father element at min_ref
5720  RefineableElement* father_el_pt;
5722  (min_ref,father_el_pt);
5723  el_pt=father_el_pt;
5724  }
5725 
5726  if (halo_element_is_retained[el_pt])
5727  {
5728  halo_kept[e]=1;
5729  }
5730  }
5731 
5732  // Now send this vector to processor d to tell it which of
5733  // the haloed elements (which are listed in the same order)
5734  // are to be retained as haloed elements.
5735  if (nelem!=0)
5736  {
5737  MPI_Send(&halo_kept[0],nelem,MPI_INT,d,0,Comm_pt->mpi_comm());
5738  }
5739  }
5740  }
5741  }
5742  }
5743  }
5744 
5746  {
5747  t_end=TimingHelpers::timer();
5748  oomph_info
5749  << "Time for pt2pt comms of retention pattern in "
5750  << " Mesh::prune_halo_elements_and_nodes(): "
5751  << t_end-t_start << std::endl;
5752  t_start = TimingHelpers::timer();
5753  }
5754 
5755  //MemoryUsage::doc_memory_usage("after pt2pt comms of retention pattern");
5756 
5757 
5758  // Backup pointers to nodes in this mesh
5759  nnod=this->nnode();
5760  Vector<Node*> backed_up_nod_pt(nnod);
5761  for (unsigned j=0;j<nnod;j++)
5762  {
5763  backed_up_nod_pt[j]=this->node_pt(j);
5764  }
5765 
5766  // Flush the mesh storage
5768 
5769  // Storage for non-active trees/elements that are to be deleted
5770  std::set<Tree*> trees_to_be_deleted_pt;
5771 
5772  // Loop over all backed-up elements
5773  nelem=backed_up_el_pt.size();
5774  for (unsigned e=0;e<nelem;e++)
5775  {
5776  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
5777  (backed_up_el_pt[e]);
5778 
5779  // Get refinement level
5780  unsigned level=ref_el_pt->refinement_level();
5781 
5782  // Go up the tree to the correct level
5783  RefineableElement* el_pt;
5784 
5785  if (level==min_ref)
5786  {
5787  // Already at the correct level
5788  el_pt=ref_el_pt;
5789  }
5790  else
5791  {
5792  // Need to go up the tree to the father element at min_ref
5793  RefineableElement* father_el_pt;
5795  (min_ref,father_el_pt);
5796  el_pt=father_el_pt;
5797  }
5798 
5799  // If the base element is going to be kept, then add the current element
5800  // to the "new" mesh
5801  if (keep_element[el_pt])
5802  {
5803  this->add_element_pt(ref_el_pt);
5804  }
5805  else
5806  {
5807  // Flush the object attached to the tree for this element?
5808  RefineableElement* my_el_pt=dynamic_cast<RefineableElement*>(ref_el_pt);
5809  if (my_el_pt!=0)
5810  {
5811  my_el_pt->tree_pt()->flush_object();
5812  }
5813 
5814  // Get associated tree root
5815  Tree* tmp_tree_root_pt=my_el_pt->tree_pt()->root_pt();
5816 
5817  // Get all the tree nodes
5818  Vector<Tree*> tmp_all_tree_nodes_pt;
5819  tmp_tree_root_pt->stick_all_tree_nodes_into_vector(
5820  tmp_all_tree_nodes_pt);
5821 
5822  // Loop over all of them and delete associated object/
5823  // and flush any record of it in tree
5824  unsigned n_tree=tmp_all_tree_nodes_pt.size();
5825  for (unsigned j=0;j<n_tree;j++)
5826  {
5827  if (tmp_all_tree_nodes_pt[j]->object_pt()!=0)
5828  {
5829  unsigned lev=tmp_all_tree_nodes_pt[j]->object_pt()->refinement_level();
5830  if (lev<=min_ref)
5831  {
5832  if (!keep_element[tmp_all_tree_nodes_pt[j]->object_pt()])
5833  {
5834  trees_to_be_deleted_pt.insert(tmp_all_tree_nodes_pt[j]);
5835  }
5836  }
5837  }
5838  }
5839 
5840  // Delete the element
5841  deleted_element_pt.push_back(ref_el_pt);
5842  delete ref_el_pt;
5843  }
5844  }
5845 
5846  //MemoryUsage::doc_memory_usage("before deleting superfluous elements");
5847 
5848  // Delete superfluous elements
5849  for (std::set<Tree*>::iterator it=trees_to_be_deleted_pt.begin();
5850  it!=trees_to_be_deleted_pt.end();it++)
5851  {
5852  Tree* tree_pt=(*it);
5853  if (tree_pt->object_pt()!=0)
5854  {
5855  deleted_element_pt.push_back(tree_pt->object_pt());
5856  delete tree_pt->object_pt();
5857  tree_pt->flush_object();
5858  }
5859  }
5860 
5861  //MemoryUsage::doc_memory_usage("after deleting superfluous elements");
5862 
5863 
5864  // Wipe the storage scheme for (root) halo(ed) elements and then re-assign
5865  Root_haloed_element_pt.clear();
5866  Root_halo_element_pt.clear();
5867  for (int domain=0;domain<n_proc;domain++)
5868  {
5869  unsigned nelem=tmp_root_halo_element_pt[domain].size();
5870  for (unsigned e=0;e<nelem;e++)
5871  {
5872  Root_halo_element_pt[domain].push_back(
5873  tmp_root_halo_element_pt[domain][e]);
5874  }
5875 
5876  nelem=tmp_root_haloed_element_pt[domain].size();
5877  for (unsigned e=0;e<nelem;e++)
5878  {
5879  Root_haloed_element_pt[domain].push_back(
5880  tmp_root_haloed_element_pt[domain][e]);
5881  }
5882  }
5883 
5884 // MemoryUsage::doc_memory_usage(
5885 // "after wiping storage scheme for root halo/ed els");
5886 
5887  // Loop over all retained elements at this level and mark their nodes
5888  //-------------------------------------------------------------------
5889  // as to be retained too (some double counting going on here)
5890  //-----------------------------------------------------------
5891  nelem=this->nelement();
5892  for (unsigned e=0;e<nelem;e++)
5893  {
5894  FiniteElement* el_pt=this->finite_element_pt(e);
5895 
5896  // Loop over nodes
5897  unsigned nnod=el_pt->nnode();
5898  for (unsigned j=0;j<nnod;j++)
5899  {
5900  Node* nod_pt=el_pt->node_pt(j);
5901  nod_pt->set_non_obsolete();
5902  }
5903  }
5904 
5905  // Complete rebuild of mesh by adding retained nodes
5906  // Note that they are added in the order in which they
5907  // occured in the original mesh as this guarantees the
5908  // synchronisity between the serialised access to halo
5909  // and haloed nodes from different processors.
5910  nnod=backed_up_nod_pt.size();
5911  for (unsigned j=0;j<nnod;j++)
5912  {
5913  Node* nod_pt=backed_up_nod_pt[j];
5914  if(!nod_pt->is_obsolete())
5915  {
5916  // Not obsolete so add it back to the mesh
5917  this->add_node_pt(nod_pt);
5918  }
5919  }
5920 
5921  // MemoryUsage::doc_memory_usage("after adding nodes back in");
5922 
5923  // Prune and rebuild mesh
5924  //-----------------------
5925 
5926  // Now remove the pruned nodes from the boundary lookup scheme
5927  this->prune_dead_nodes();
5928 
5929  // MemoryUsage::doc_memory_usage("after prune dead nodes");
5930 
5931  // And finally re-setup the boundary lookup scheme for elements
5933 
5934  //MemoryUsage::doc_memory_usage("after setup boundary info");
5935 
5936  // Re-setup tree forest if needed. (Call this every time even if
5937  // a (distributed) mesh has no elements on this processor.
5938  // We still need to participate in communication.)
5939  TreeBasedRefineableMeshBase* ref_mesh_pt=
5940  dynamic_cast<TreeBasedRefineableMeshBase*>(this);
5941  if (ref_mesh_pt!=0)
5942  {
5943  ref_mesh_pt->setup_tree_forest();
5944  }
5945 
5946  //MemoryUsage::doc_memory_usage("after setup tree forest");
5947 
5949  {
5950  t_end=TimingHelpers::timer();
5951  oomph_info
5952  << "Time for local rebuild of mesh from retained elements in "
5953  << " Mesh::prune_halo_elements_and_nodes(): "
5954  << t_end-t_start << std::endl;
5955  t_start = TimingHelpers::timer();
5956  }
5957 
5958  // Classify nodes
5959  classify_halo_and_haloed_nodes(doc_info,report_stats);
5960 
5962  {
5963  t_end=TimingHelpers::timer();
5964  oomph_info
5965  << "Time for Mesh::classify_halo_and_haloed_nodes() "
5966  << " Mesh::prune_halo_elements_and_nodes(): "
5967  << t_end-t_start << std::endl;
5968  t_start = TimingHelpers::timer();
5969  }
5970 
5971  //MemoryUsage::doc_memory_usage("after classify_halo_and_haloed_nodes()");
5972 
5973  // Doc?
5974  //-----
5975  if (doc_info.is_doc_enabled())
5976  {
5977  oomph_info << "Outputting distribution in "
5978  << doc_info.directory() << " "
5979  << doc_info.number() << std::endl;
5980  doc_mesh_distribution(doc_info);
5981  }
5982 
5983  // Finally: Reorder the nodes within the mesh's node vector
5984  // to establish a standard ordering regardless of the sequence
5985  // of mesh refinements -- this is required to allow dump/restart
5986  // on refined meshes
5987  this->reorder_nodes();
5988 
5989 
5991  {
5992  t_end=TimingHelpers::timer();
5993  oomph_info
5994  << "Time for Mesh::reorder_nodes() "
5995  << " Mesh::prune_halo_elements_and_nodes(): "
5996  << t_end-t_start << std::endl;
5997  t_start = TimingHelpers::timer();
5998  }
5999 
6000  //MemoryUsage::doc_memory_usage("after reorder nodes");
6001 
6002  // Doc stats
6003  if (report_stats)
6004  {
6005  oomph_info << "\n----------------------------------------------------\n";
6006  oomph_info << "After pruning: Processor " << my_rank
6007  << " holds " << this->nelement()
6008  << " elements of which " << this->nroot_halo_element()
6009  << " are root halo elements \n while "
6010  << this->nroot_haloed_element()
6011  << " are root haloed elements" << std::endl;
6012 
6013  // Report total number of halo(ed) and shared nodes for this process
6014  oomph_info << "After pruning: Processor " << my_rank
6015  << " holds " << this->nnode()
6016  << " nodes of which " << this->nhalo_node()
6017  << " are halo nodes \n while " << this->nhaloed_node()
6018  << " are haloed nodes, and " << this->nshared_node()
6019  << " are shared nodes." << std::endl;
6020 
6021  // Report number of halo(ed) and shared nodes with each domain
6022  // from the current process
6023  for (int iproc=0;iproc<n_proc;iproc++)
6024  {
6025  oomph_info << "After pruning: With process " << iproc << ", there are "
6026  << this->nhalo_node(iproc) << " halo nodes, and " << std::endl
6027  << this->nhaloed_node(iproc) << " haloed nodes, and "
6028  << this->nshared_node(iproc) << " shared nodes" << std::endl;
6029  }
6030  oomph_info << "----------------------------------------------------\n\n";
6031  }
6032 
6033  }
6034 
6035  //MemoryUsage::doc_memory_usage("end of mesh level prune");
6036 
6037 }
6038 
6039 
6040 
6041 
6042 
6043 
6044 //========================================================================
6045 /// Get efficiency of mesh distribution: In an ideal distribution
6046 /// without halo overhead, each processor would only hold its own
6047 /// elements. Efficieny per processor = (number of non-halo elements)/
6048 /// (total number of elements).
6049 //========================================================================
6051  double& max_efficiency,
6052  double& min_efficiency)
6053 {
6054  // Storage for number of processors and current processor
6055  int n_proc=Comm_pt->nproc();
6056  int my_rank=Comm_pt->my_rank();
6057 
6058  // Create vector to hold number of elements and halo elements
6059  Vector<int> nhalo_elements(n_proc);
6060  Vector<int> n_elements(n_proc);
6061 
6062  // Count total number of halo elements
6063  unsigned count=0;
6064  for (int d=0;d<n_proc;d++)
6065  {
6067  count+=halo_elem_pt.size();
6068  }
6069 
6070  // Stick own number into appropriate entry
6071  int nhalo_element_local=count;
6072  int n_element_local=nelement();
6073 
6074  // Gather information on root processor: First argument group
6075  // specifies what is to be sent (one int from each procssor, indicating
6076  // the number of elements on it), the second group indicates where
6077  // the results are to be gathered (in rank order) on root processor.
6078  MPI_Gather(&nhalo_element_local,1,MPI_INT,
6079  &nhalo_elements[0],1, MPI_INT,
6080  0,Comm_pt->mpi_comm());
6081  MPI_Gather(&n_element_local,1,MPI_INT,
6082  &n_elements[0],1, MPI_INT,
6083  0,Comm_pt->mpi_comm());
6084 
6085  // Initialise stats
6086  av_efficiency=0.0;
6087  double max=-1.0;
6088  double min=1000000000.0;
6089 
6090  if (my_rank==0)
6091  {
6092  for (int i=0;i<n_proc;i++)
6093  {
6094  unsigned nel=n_elements[i];
6095  double eff=0.0;
6096  if (nel!=0)
6097  {
6098  eff=double(n_elements[i]-nhalo_elements[i])/double(nel);
6099  }
6100  av_efficiency+=eff;
6101  if (eff>max) max=eff;
6102  if (eff<min) min=eff;
6103 
6104  }
6105  av_efficiency/=double(n_proc);
6106  }
6107 
6108  // Now broadcast the result back out
6109  MPI_Bcast(&max,1,MPI_DOUBLE,0,Comm_pt->mpi_comm());
6110  MPI_Bcast(&min,1,MPI_DOUBLE,0,Comm_pt->mpi_comm());
6111  MPI_Bcast(&av_efficiency,1,MPI_DOUBLE,0,Comm_pt->mpi_comm());
6112 
6113  max_efficiency=max;
6114  min_efficiency=min;
6115 
6116 }
6117 
6118 
6119 
6120 //========================================================================
6121 /// Doc the mesh distribution
6122 //========================================================================
6124 {
6125  // Storage for current processor and number of processors
6126  int my_rank=Comm_pt->my_rank();
6127  int n_proc=Comm_pt->nproc();
6128 
6129  std::ostringstream filename;
6130  std::ostringstream filename2;
6131  std::ofstream some_file;
6132  std::ofstream some_file2;
6133 
6134  // Doc elements on this processor
6135  filename << doc_info.directory() << "/"<< doc_info.label()<< "elements_on_proc"
6136  << my_rank << "_" << doc_info.number() << ".dat";
6137  some_file.open(filename.str().c_str());
6138  this->output(some_file,5);
6139  some_file.close();
6140 
6141  // Doc non-halo elements on this processor
6142  filename.str("");
6143  filename << doc_info.directory() << "/"
6144  << doc_info.label()<< "non_halo_elements_on_proc"
6145  << my_rank << "_" << doc_info.number() << ".dat";
6146  some_file.open(filename.str().c_str());
6147 
6148  // Get to elements on processor
6149  unsigned nelem=this->nelement();
6150  for (unsigned e=0;e<nelem;e++)
6151  {
6152  //Get the element
6153  GeneralisedElement* el_pt = this->element_pt(e);
6154  //Only output if not a halo element
6155  if(!el_pt->is_halo())
6156  {
6157  FiniteElement* f_el_pt=dynamic_cast<FiniteElement*>(el_pt);
6158  //Indicate a generalised element
6159  if(f_el_pt==0)
6160  {
6161  some_file << "Generalised Element " << e << "\n";
6162  }
6163  else
6164  // output if non-halo and a finite element
6165  {
6166  f_el_pt->output(some_file,5);
6167  }
6168  }
6169  }
6170 
6171  some_file.close();
6172 
6173 
6174  // Doc halo elements on this processor
6175  filename.str("");
6176  filename << doc_info.directory() << "/"<< doc_info.label()
6177  << "halo_elements_on_proc"
6178  << my_rank << "_" << doc_info.number() << ".dat";
6179  some_file.open(filename.str().c_str());
6180  for (int domain=0; domain<n_proc; domain++)
6181  {
6182  filename2.str("");
6183  filename2 << doc_info.directory() << "/"<< doc_info.label()
6184  << "halo_elements_with_proc" << domain << "_on_proc"
6185  << my_rank << "_" << doc_info.number() << ".dat";
6186  some_file2.open(filename2.str().c_str());
6187 
6188  // Get vector of halo elements by copy operation
6189  Vector<GeneralisedElement*> halo_elem_pt(this->halo_element_pt(domain));
6190  unsigned nelem=halo_elem_pt.size();
6191  for (unsigned e=0;e<nelem;e++)
6192  {
6193  FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
6194  if(f_el_pt!=0)
6195  {
6196 
6197 #ifdef PARANOID
6198  // Check if it's refineable and if so if it's a leaf.
6199  // If not something must have gone wrong.
6200  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(
6201  f_el_pt);
6202  if (ref_el_pt!=0)
6203  {
6204  if (!(ref_el_pt->tree_pt()->is_leaf()))
6205  {
6206  std::ostringstream error_message;
6207  error_message
6208  << "Haloed element is not a leaf element. This shouldn't happen"
6209  << std::endl;
6210  error_message
6211  << "Here are the nodal positions: " << std::endl;
6212  unsigned nnod=ref_el_pt->nnode();
6213  for (unsigned j=0;j<nnod;j++)
6214  {
6215  Node* nod_pt=ref_el_pt->node_pt(j);
6216  unsigned n_dim=nod_pt->ndim();
6217  for (unsigned i=0;i<n_dim;i++)
6218  {
6219  error_message << nod_pt->x(i) << " ";
6220  }
6221  error_message << "\n";
6222  throw OomphLibError(error_message.str(),
6223  OOMPH_CURRENT_FUNCTION,
6224  OOMPH_EXCEPTION_LOCATION);
6225  }
6226  }
6227  }
6228 #endif
6229 
6230  f_el_pt->output(some_file,5);
6231  f_el_pt->output(some_file2,5);
6232  }
6233  //Indicate a generalised element
6234  else
6235  {
6236  some_file << "Generalised Element " << e << "\n";
6237  some_file2 << "Generalised Element " << e << "\n";
6238  }
6239  }
6240  some_file2.close();
6241  }
6242  some_file.close();
6243 
6244  // Doc haloed elements on this processor
6245  filename.str("");
6246  filename << doc_info.directory() << "/"<< doc_info.label()
6247  << "haloed_elements_on_proc"
6248  << my_rank << "_" << doc_info.number() << ".dat";
6249  some_file.open(filename.str().c_str());
6250  for (int domain=0; domain<n_proc; domain++)
6251  {
6252  filename2.str("");
6253  filename2 << doc_info.directory() << "/"<< doc_info.label()
6254  << "haloed_elements_with_proc" << domain << "_on_proc"
6255  << my_rank << "_" << doc_info.number() << ".dat";
6256  some_file2.open(filename2.str().c_str());
6257 
6258  // Get vector of haloed elements by copy operation
6259  Vector<GeneralisedElement*> haloed_elem_pt(this->haloed_element_pt(domain));
6260  unsigned nelem=haloed_elem_pt.size();
6261  for (unsigned e=0;e<nelem;e++)
6262  {
6263  //Is it a finite element
6264  FiniteElement *finite_el_pt = dynamic_cast<FiniteElement*>
6265  (haloed_elem_pt[e]);
6266  if(finite_el_pt!=0)
6267  {
6268 
6269 #ifdef PARANOID
6270  // Check if it's refineable and if so if it's a leaf.
6271  // If not something must have gone wrong.
6272  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(
6273  finite_el_pt);
6274  if (ref_el_pt!=0)
6275  {
6276  if (!(ref_el_pt->tree_pt()->is_leaf()))
6277  {
6278  std::ostringstream error_message;
6279  error_message
6280  << "Haloed element is not a leaf element. This shouldn't happen"
6281  << std::endl;
6282  error_message
6283  << "Here are the nodal positions: " << std::endl;
6284  unsigned nnod=ref_el_pt->nnode();
6285  for (unsigned j=0;j<nnod;j++)
6286  {
6287  Node* nod_pt=ref_el_pt->node_pt(j);
6288  unsigned n_dim=nod_pt->ndim();
6289  for (unsigned i=0;i<n_dim;i++)
6290  {
6291  error_message << nod_pt->x(i) << " ";
6292  }
6293  error_message << "\n";
6294  throw OomphLibError(error_message.str(),
6295  OOMPH_CURRENT_FUNCTION,
6296  OOMPH_EXCEPTION_LOCATION);
6297  }
6298  }
6299  }
6300 #endif
6301 
6302  finite_el_pt->output(some_file,5);
6303  finite_el_pt->output(some_file2,5);
6304  }
6305  //Indicate a generalised element
6306  else
6307  {
6308  some_file << "Generalised Element " << e << "\n";
6309  some_file2 << "Generalised Element " << e << "\n";
6310  }
6311  }
6312  some_file2.close();
6313  }
6314  some_file.close();
6315 
6316 
6317  // Doc non-halo nodes on this processor
6318  filename.str("");
6319  filename << doc_info.directory() << "/"<< doc_info.label()
6320  << "non_halo_nodes_on_proc"
6321  << my_rank << "_" << doc_info.number() << ".dat";
6322  some_file.open(filename.str().c_str());
6323  unsigned nnod=this->nnode();
6324  for (unsigned j=0;j<nnod;j++)
6325  {
6326  Node* nod_pt=this->node_pt(j);
6327  if (!nod_pt->is_halo())
6328  {
6329  unsigned ndim=nod_pt->ndim();
6330  for (unsigned i=0;i<ndim;i++)
6331  {
6332  some_file << nod_pt->x(i) << " " ;
6333  }
6334  some_file << nod_pt->nvalue() << " "
6335  << nod_pt->non_halo_proc_ID() << " "
6336  << nod_pt->is_halo() << " "
6337  << nod_pt->hang_code() << std::endl;
6338  }
6339  }
6340  some_file.close();
6341 
6342 
6343  // Doc nodes on this processor
6344  filename.str("");
6345  filename << doc_info.directory() << "/"<< doc_info.label()
6346  << "nodes_on_proc"
6347  << my_rank << "_" << doc_info.number() << ".dat";
6348  some_file.open(filename.str().c_str());
6349  for (unsigned j=0;j<nnod;j++)
6350  {
6351  Node* nod_pt=this->node_pt(j);
6352  unsigned ndim=nod_pt->ndim();
6353  for (unsigned i=0;i<ndim;i++)
6354  {
6355  some_file << nod_pt->x(i) << " " ;
6356  }
6357  some_file << nod_pt->nvalue() << " "
6358  << nod_pt->non_halo_proc_ID() << " "
6359  << nod_pt->is_halo() << " "
6360  << nod_pt->hang_code() << std::endl;
6361  }
6362  some_file.close();
6363 
6364  // Doc solid nodes on this processor
6365  filename.str("");
6366  filename << doc_info.directory() << "/"
6367  << doc_info.label()<< "solid_nodes_on_proc"
6368  << my_rank << "_" << doc_info.number() << ".dat";
6369  some_file.open(filename.str().c_str());
6370  unsigned nsnod=this->nnode();
6371  for (unsigned j=0;j<nsnod;j++)
6372  {
6373  Node* nod_pt=this->node_pt(j);
6374  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
6375  if (solid_nod_pt!=0)
6376  {
6377  unsigned ndim=solid_nod_pt->ndim();
6378  for (unsigned i=0;i<ndim;i++)
6379  {
6380  some_file << nod_pt->x(i) << " " ;
6381  }
6382  some_file << nod_pt->nvalue() << " "
6383  << nod_pt->non_halo_proc_ID() << " "
6384  << nod_pt->is_halo() << " "
6385  << nod_pt->hang_code() << "\n";
6386  }
6387  }
6388  some_file.close();
6389 
6390 
6391  // Doc halo nodes on this processor
6392  filename.str("");
6393  filename << doc_info.directory() << "/"<< doc_info.label()
6394  << "halo_nodes_on_proc"
6395  << my_rank << "_" << doc_info.number() << ".dat";
6396  some_file.open(filename.str().c_str());
6397  for (int domain=0; domain<n_proc; domain++)
6398  {
6399  filename2.str("");
6400  filename2 << doc_info.directory() << "/"<< doc_info.label()
6401  << "halo_nodes_with_proc" << domain << "_on_proc"
6402  << my_rank << "_" << doc_info.number() << ".dat";
6403  some_file2.open(filename2.str().c_str());
6404  unsigned nnod=this->nhalo_node(domain);
6405  for (unsigned j=0;j<nnod;j++)
6406  {
6407  Node* nod_pt=this->halo_node_pt(domain,j);
6408  unsigned ndim=nod_pt->ndim();
6409  for (unsigned i=0;i<ndim;i++)
6410  {
6411  some_file << nod_pt->x(i) << " " ;
6412  some_file2 << nod_pt->x(i) << " " ;
6413  }
6414  some_file << nod_pt->nvalue() << " "
6415  << nod_pt->non_halo_proc_ID() << " "
6416  << nod_pt->is_halo() << " "
6417  << nod_pt->hang_code() << "\n";
6418  some_file2 << nod_pt->nvalue() << " "
6419  << nod_pt->non_halo_proc_ID() << " "
6420  << nod_pt->is_halo() << " "
6421  << nod_pt->hang_code() << "\n";
6422  }
6423  some_file2.close();
6424  }
6425  some_file.close();
6426 
6427 
6428 
6429  // Doc haloed nodes on this processor
6430  filename.str("");
6431  filename << doc_info.directory() << "/"<< doc_info.label()
6432  << "haloed_nodes_on_proc"
6433  << my_rank << "_" << doc_info.number() << ".dat";
6434  some_file.open(filename.str().c_str());
6435  for (int domain=0; domain<n_proc; domain++)
6436  {
6437  filename2.str("");
6438  filename2 << doc_info.directory() << "/"<< doc_info.label()
6439  << "haloed_nodes_with_proc" << domain << "_on_proc"
6440  << my_rank << "_" << doc_info.number() << ".dat";
6441  some_file2.open(filename2.str().c_str());
6442  unsigned nnod=this->nhaloed_node(domain);
6443  for (unsigned j=0;j<nnod;j++)
6444  {
6445  Node* nod_pt=this->haloed_node_pt(domain,j);
6446  unsigned ndim=nod_pt->ndim();
6447  for (unsigned i=0;i<ndim;i++)
6448  {
6449  some_file << nod_pt->x(i) << " " ;
6450  some_file2 << nod_pt->x(i) << " " ;
6451  }
6452  some_file << nod_pt->nvalue() << " "
6453  << nod_pt->non_halo_proc_ID() << " "
6454  << nod_pt->is_halo() << " "
6455  << nod_pt->hang_code() << "\n";
6456  some_file2 << nod_pt->nvalue() << " "
6457  << nod_pt->non_halo_proc_ID() << " "
6458  << nod_pt->is_halo() << " "
6459  << nod_pt->hang_code() << "\n";
6460  }
6461  some_file2.close();
6462  }
6463  some_file.close();
6464 
6465 
6466 
6467  // Doc shared nodes on this processor
6468  filename.str("");
6469  filename << doc_info.directory() << "/"<< doc_info.label()
6470  << "shared_nodes_on_proc"
6471  << my_rank << "_" << doc_info.number() << ".dat";
6472  some_file.open(filename.str().c_str());
6473  for (int domain=0; domain<n_proc; domain++)
6474  {
6475  filename2.str("");
6476  filename2 << doc_info.directory() << "/"<< doc_info.label()
6477  << "shared_nodes_with_proc" << domain << "_on_proc"
6478  << my_rank << "_" << doc_info.number() << ".dat";
6479  some_file2.open(filename2.str().c_str());
6480  unsigned nnod=this->nshared_node(domain);
6481  for (unsigned j=0;j<nnod;j++)
6482  {
6483  Node* nod_pt=this->shared_node_pt(domain,j);
6484  unsigned ndim=nod_pt->ndim();
6485  for (unsigned i=0;i<ndim;i++)
6486  {
6487  some_file << nod_pt->x(i) << " " ;
6488  some_file2 << nod_pt->x(i) << " " ;
6489  }
6490  some_file << nod_pt->nvalue() << " "
6491  << nod_pt->non_halo_proc_ID() << " "
6492  << nod_pt->is_halo() << "\n";
6493  some_file2 << nod_pt->nvalue() << " "
6494  << nod_pt->non_halo_proc_ID() << " "
6495  << nod_pt->is_halo() << " "
6496  << nod_pt->hang_code() << "\n";
6497  }
6498  some_file2.close();
6499  }
6500  some_file.close();
6501 
6502 
6503  // Doc mesh
6504  filename.str("");
6505  filename << doc_info.directory() << "/"<< doc_info.label()
6506  << "mesh"
6507  << my_rank << "_" << doc_info.number() << ".dat";
6508  some_file.open(filename.str().c_str());
6509  this->output(some_file,5);
6510  some_file.close();
6511 
6512 
6513  // Doc boundary scheme
6514  filename.str("");
6515  filename << doc_info.directory() << "/"<< doc_info.label()
6516  << "boundaries"
6517  << my_rank << "_" << doc_info.number() << ".dat";
6518  some_file.open(filename.str().c_str());
6519  this->output_boundaries(some_file);
6520  some_file.close();
6521 
6522 
6523  // Doc elements next to boundaries scheme
6524  // if set up
6526  {
6527  // How many finite elements are adjacent to boundary b?
6528  unsigned nbound=this->nboundary();
6529  for (unsigned b=0;b<nbound;b++)
6530  {
6531  filename.str("");
6532  filename << doc_info.directory() << "/"<< doc_info.label()
6533  << "boundary_elements"
6534  << my_rank << "_" << b << "_" << doc_info.number() << ".dat";
6535  some_file.open(filename.str().c_str());
6536  unsigned nelem=this->nboundary_element(b);
6537  for (unsigned e=0;e<nelem;e++)
6538  {
6539  this->boundary_element_pt(b,e)->output(some_file,5);
6540  }
6541  some_file.close();
6542  }
6543  }
6544 
6545 }
6546 
6547 
6548 //========================================================================
6549 /// Check the halo/haloed/shared node/element schemes on the Mesh
6550 //========================================================================
6552  double& max_permitted_error_for_halo_check)
6553 {
6554 
6555  oomph_info << "Doing check_halo_schemes for mesh: "
6556  << typeid(*this).name() << std::endl;
6557 
6558  // Moved this from the Problem class so that it would work better
6559  // in multiple mesh problems; there remains a simple "wrapper"
6560  // function in the Problem class that calls this for each (sub)mesh.
6561 
6562  MPI_Status status;
6563  std::ostringstream filename;
6564  std::ofstream shared_file;
6565  std::ofstream halo_file;
6566  std::ofstream haloed_file;
6567  std::ofstream ext_halo_file;
6568  std::ofstream ext_haloed_file;
6569 
6570  // Storage for current processor and number of processors
6571  int n_proc=Comm_pt->nproc();
6572  int my_rank=Comm_pt->my_rank();
6573 
6574 
6575  // Check the shared node scheme first: if this is incorrect then
6576  // the halo(ed) node scheme is likely to be wrong too
6577 
6578  // Doc shared nodes lookup schemes
6579  //-------------------------------------
6580  if (doc_info.is_doc_enabled())
6581  {
6582  // Loop over domains for shared nodes
6583  for (int dd=0;dd<n_proc;dd++)
6584  {
6585  filename.str("");
6586  filename << doc_info.directory()
6587  << "/shared_node_check" << doc_info.label() << "_on_proc"
6588  << my_rank << "_with_proc" << dd << "_"
6589  << doc_info.number() << ".dat";
6590  shared_file.open(filename.str().c_str());
6591  shared_file << "ZONE " << std::endl;
6592 
6593  unsigned nnod=nshared_node(dd);
6594  for (unsigned j=0;j<nnod;j++)
6595  {
6596  Node* nod_pt=shared_node_pt(dd,j);
6597  unsigned ndim=nod_pt->ndim();
6598  for (unsigned i=0;i<ndim;i++)
6599  {
6600  shared_file << nod_pt->position(i) << " ";
6601  }
6602  shared_file << j << " " << nod_pt << std::endl;
6603  }
6604  // Dummy output for processor that doesn't share nodes
6605  // (needed for tecplot)
6606  if ((nnod==0) && (nelement()!=0))
6607  {
6608  FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(element_pt(0));
6609  //If it's a generalised element mesh dummy output
6610  if(f_el_pt==0)
6611  {
6612  shared_file << "0.0" << std::endl;
6613  }
6614  else
6615  {
6616  unsigned ndim=f_el_pt->node_pt(0)->ndim();
6617  if (ndim==2)
6618  {
6619  shared_file << " 1.0 1.1 " << std::endl;
6620  }
6621  else
6622  {
6623  shared_file << " 1.0 1.1 1.1" << std::endl;
6624  }
6625  }
6626  }
6627  shared_file.close();
6628  }
6629 
6630  }
6631 
6632 
6633 
6634  // Check for duplicates in shared node scheme
6635  for (int d=0;d<n_proc;d++)
6636  {
6637  unsigned n_vector=Shared_node_pt[d].size();
6638  std::set<Node*> shared_node_set;
6639  for (unsigned i=0;i<n_vector;i++)
6640  {
6641  unsigned old_size=shared_node_set.size();
6642  shared_node_set.insert(Shared_node_pt[d][i]);
6643  unsigned new_size=shared_node_set.size();
6644  if (old_size==new_size)
6645  {
6646  Node* nod_pt=Shared_node_pt[d][i];
6647  oomph_info << "Repeated node in shared node lookup scheme: "
6648  << i << "-th node with proc " << d << " : "
6649  << nod_pt << " at: ";
6650  unsigned n=nod_pt->ndim();
6651  for (unsigned ii=0;ii<n;ii++)
6652  {
6653  oomph_info << nod_pt->x(ii) << " ";
6654  }
6655  oomph_info << std::endl;
6656  }
6657  }
6658  unsigned n_set=shared_node_set.size();
6659  if (n_vector!=n_set)
6660  {
6661  std::ostringstream warning_stream;
6662  warning_stream
6663  <<"WARNING: " << std::endl
6664  <<"There seem to be repeated entries in shared node scheme "
6665  << "with proc " << d << ".\n"
6666  << "n_vector=" << n_vector
6667  << "; n_set=" << n_set << std::endl;
6668  if (doc_info.is_doc_enabled())
6669  {
6670  warning_stream
6671  << "Shared node scheme has been output in files like\n"
6672  << filename.str() << std::endl;
6673  }
6674  else
6675  {
6676  warning_stream
6677  << "Re-run with doc_info enabled to see where the shared nodes are.\n";
6678  }
6679  OomphLibWarning(warning_stream.str(),
6680  "Mesh::check_halo_schemes",
6681  OOMPH_EXCEPTION_LOCATION);
6682  }
6683  }
6684 
6685 
6686 
6687  // Check shared nodes lookup schemes
6688  //----------------------------------
6689  double max_error=0.0;
6690 
6691  // Loop over domains for shared nodes
6692  for (int d=0;d<n_proc;d++)
6693  {
6694  // Are my shared nodes being checked?
6695  if (d==my_rank)
6696  {
6697  // Loop over domains for shared nodes
6698  for (int dd=0;dd<n_proc;dd++)
6699  {
6700  // Don't talk to yourself
6701  if (dd!=d)
6702  {
6703  // How many of my nodes are shared nodes with processor dd?
6704  int nnod_shared=nshared_node(dd);
6705 
6706  if (nnod_shared!=0)
6707  {
6708  // Receive from processor dd how many of his nodes are shared
6709  // with this processor
6710  int nnod_share=0;
6711  MPI_Recv(&nnod_share,1, MPI_INT,dd,0,Comm_pt->mpi_comm(),&status);
6712 
6713  if (nnod_shared!=nnod_share)
6714  {
6715  std::ostringstream error_message;
6716 
6717  error_message
6718  << "Clash in numbers of shared nodes! "
6719  << std::endl;
6720  error_message
6721  << "# of shared nodes on proc "
6722  << dd << ": " << nnod_shared << std::endl;
6723  error_message
6724  << "# of shared nodes on proc "
6725  << d << ": " << nnod_share << std::endl;
6726  error_message
6727  << "(Re-)run Problem::check_halo_schemes() with DocInfo object"
6728  << std::endl;
6729  error_message
6730  << "to identify the problem" << std::endl;
6731  throw OomphLibError(error_message.str(),
6732  OOMPH_CURRENT_FUNCTION,
6733  OOMPH_EXCEPTION_LOCATION);
6734  }
6735 
6736 
6737  unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
6738 
6739  // Get strung-together nodal positions from other processor
6740  Vector<double> other_nodal_positions(nod_dim*nnod_share);
6741  MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_share,MPI_DOUBLE,dd,
6742  0,Comm_pt->mpi_comm(),&status);
6743 
6744  // Check
6745  unsigned count=0;
6746  for (int j=0;j<nnod_share;j++)
6747  {
6748  Vector<double> x_shared(nod_dim);
6749  for(unsigned i=0;i<nod_dim;i++)
6750  {
6751  x_shared[i] = shared_node_pt(dd,j)->position(i);
6752  }
6753  Vector<double> x_share(nod_dim);
6754  for(unsigned i=0;i<nod_dim;i++)
6755  {
6756  x_share[i] = other_nodal_positions[count];
6757  count++;
6758  }
6759  double error=0.0;
6760  for(unsigned i=0;i<nod_dim;i++)
6761  {error += (x_shared[i] - x_share[i])*(x_shared[i] - x_share[i]);}
6762  error = sqrt(error);
6763 
6764  // Doc if relevant
6765  if (error>max_permitted_error_for_halo_check)
6766  {
6767  oomph_info << "Error in shared nodes: ";
6768  for(unsigned i=0;i<nod_dim;i++)
6769  {
6770  oomph_info << x_shared[i] << " ";
6771  }
6772  for(unsigned i=0;i<nod_dim;i++)
6773  {
6774  oomph_info << x_share[i] << " ";
6775  }
6776  oomph_info << error << std::endl;
6777  oomph_info << "shared node: " << shared_node_pt(dd,j) << std::endl;
6778  if(shared_node_pt(dd,j)->is_hanging())
6779  {
6780  oomph_info << "shared node: " << shared_node_pt(dd,j) << " is hanging with masters" << std::endl;
6781  for(unsigned m=0; m<shared_node_pt(dd,j)->hanging_pt()->nmaster(); m++)
6782  {
6783  oomph_info << "master: " << shared_node_pt(dd,j)->hanging_pt()->master_node_pt(m) << std::endl;
6784  }
6785  }
6786  }
6787 
6788  // Keep tracking max
6789  if (error>max_error)
6790  {
6791  max_error=error;
6792  }
6793  }
6794  }
6795  }
6796  }
6797  }
6798  // My shared nodes are not being checked: Send my shared nodes
6799  // to the other processor
6800  else
6801  {
6802  int nnod_share=nshared_node(d);
6803 
6804  if (nnod_share!=0)
6805  {
6806  // Send it across to the processor whose shared nodes are being checked
6807  MPI_Send(&nnod_share,1,MPI_INT,d,0,Comm_pt->mpi_comm());
6808 
6809  unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
6810 
6811  // Now string together the nodal positions of all shared nodes
6812  Vector<double> nodal_positions(nod_dim*nnod_share);
6813  unsigned count=0;
6814  for (int j=0;j<nnod_share;j++)
6815  {
6816  for(unsigned i=0;i<nod_dim;i++)
6817  {
6818  nodal_positions[count]=shared_node_pt(d,j)->position(i);
6819  count++;
6820  }
6821  }
6822  // Send it across to the processor whose shared nodes are being checked
6823  MPI_Send(&nodal_positions[0],nod_dim*nnod_share,MPI_DOUBLE,d,0,
6824  Comm_pt->mpi_comm());
6825  }
6826  }
6827  }
6828 
6829  oomph_info << "Max. error for shared nodes " << max_error
6830  << std::endl;
6831  if (max_error>max_permitted_error_for_halo_check)
6832  {
6833  std::ostringstream error_message;
6834  error_message
6835  << "This is bigger than the permitted threshold "
6836  << max_permitted_error_for_halo_check << std::endl;
6837  error_message
6838  << "If you believe this to be acceptable for your problem\n"
6839  << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
6840  throw OomphLibError(error_message.str(),
6841  OOMPH_CURRENT_FUNCTION,
6842  OOMPH_EXCEPTION_LOCATION);
6843  }
6844 
6845  // Now check the halo/haloed element lookup scheme
6846 
6847  // Doc halo/haoloed element lookup schemes
6848  //-----------------------------------------
6849  if (doc_info.is_doc_enabled())
6850  {
6851  // Loop over domains for halo elements
6852  for (int dd=0;dd<n_proc;dd++)
6853  {
6854  filename.str("");
6855  filename << doc_info.directory() << "/halo_element_check"
6856  << doc_info.label() << "_on_proc"
6857  << my_rank << "_with_proc" << dd << "_"
6858  << doc_info.number()
6859  << ".dat";
6860  halo_file.open(filename.str().c_str());
6861 
6862  // Get vectors of halo/haloed elements by copy operation
6863  Vector<GeneralisedElement*> halo_elem_pt(halo_element_pt(dd));
6864 
6865  unsigned nelem=halo_elem_pt.size();
6866 
6867  for (unsigned e=0;e<nelem;e++)
6868  {
6869  halo_file << "ZONE " << std::endl;
6870  //Can I cast to a finite element
6871  FiniteElement* finite_el_pt =
6872  dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
6873  if(finite_el_pt!=0)
6874  {
6875  unsigned nnod=finite_el_pt->nnode();
6876  for (unsigned j=0;j<nnod;j++)
6877  {
6878  Node* nod_pt=finite_el_pt->node_pt(j);
6879  unsigned ndim=nod_pt->ndim();
6880  for (unsigned i=0;i<ndim;i++)
6881  {
6882  halo_file << nod_pt->position(i) << " ";
6883  }
6884  halo_file << std::endl;
6885  }
6886  }
6887  }
6888  halo_file.close();
6889  }
6890 
6891  // Loop over domains for halo elements
6892  for (int d=0;d<n_proc;d++)
6893  {
6894  filename.str("");
6895  filename << doc_info.directory() << "/haloed_element_check"
6896  << doc_info.label() << "_on_proc"
6897  << my_rank << "_with_proc" << d << "_"
6898  << doc_info.number()
6899  << ".dat";
6900  haloed_file.open(filename.str().c_str());
6901 
6902  // Get vectors of halo/haloed elements by copy operation
6904  haloed_elem_pt(haloed_element_pt(d));
6905 
6906  unsigned nelem2=haloed_elem_pt.size();
6907  for (unsigned e=0;e<nelem2;e++)
6908  {
6909  haloed_file << "ZONE " << std::endl;
6910  //Is it a finite element
6911  FiniteElement* finite_el_pt =
6912  dynamic_cast<FiniteElement*>(haloed_elem_pt[e]);
6913  if(finite_el_pt!=0)
6914  {
6915 
6916  // Check if it's refineable and if so if it's a leaf.
6917  // If not something must have gone wrong.
6918  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(
6919  finite_el_pt);
6920  if (ref_el_pt!=0)
6921  {
6922  if (!(ref_el_pt->tree_pt()->is_leaf()))
6923  {
6924  std::ostringstream error_message;
6925  error_message
6926  << "Haloed element is not a leaf element. This shouldn't happen"
6927  << std::endl;
6928  error_message
6929  << "Here are the nodal positions: " << std::endl;
6930  unsigned nnod=ref_el_pt->nnode();
6931  for (unsigned j=0;j<nnod;j++)
6932  {
6933  Node* nod_pt=ref_el_pt->node_pt(j);
6934  unsigned n_dim=nod_pt->ndim();
6935  for (unsigned i=0;i<n_dim;i++)
6936  {
6937  error_message << nod_pt->x(i) << " ";
6938  }
6939  error_message << "\n";
6940  throw OomphLibError(error_message.str(),
6941  OOMPH_CURRENT_FUNCTION,
6942  OOMPH_EXCEPTION_LOCATION);
6943  }
6944  }
6945  }
6946  unsigned nnod2=finite_el_pt->nnode();
6947  for (unsigned j=0;j<nnod2;j++)
6948  {
6949  Node* nod_pt=finite_el_pt->node_pt(j);
6950  unsigned ndim=nod_pt->ndim();
6951  for (unsigned i=0;i<ndim;i++)
6952  {
6953  haloed_file << nod_pt->position(i) << " ";
6954  }
6955  haloed_file << std::endl;
6956  }
6957  }
6958  }
6959  haloed_file.close();
6960  }
6961  } //end of if doc flag
6962 
6963  // Check halo/haloed element lookup schemes
6964  //-----------------------------------------
6965  max_error=0.0;
6966  bool shout=false;
6967  bool shout_and_terminate=false;
6968 
6969  // Loop over domains for haloed elements
6970  for (int d=0;d<n_proc;d++)
6971  {
6972  // Are my haloed elements being checked?
6973  if (d==my_rank)
6974  {
6975  // Loop over domains for halo elements
6976  for (int dd=0;dd<n_proc;dd++)
6977  {
6978  // Don't talk to yourself
6979  if (dd!=d)
6980  {
6981  // Get vectors of haloed elements by copy operation
6983  haloed_elem_pt(haloed_element_pt(dd));
6984 
6985  // How many of my elements are haloed elements whose halo
6986  // counterpart is located on processor dd?
6987  int nelem_haloed=haloed_elem_pt.size();
6988 
6989  if (nelem_haloed!=0)
6990  {
6991  // Receive from processor dd how many of his elements are halo
6992  // nodes whose non-halo counterparts are located here
6993  int nelem_halo=0;
6994  MPI_Recv(&nelem_halo,1,MPI_INT,dd,0,Comm_pt->mpi_comm(),&status);
6995  if (nelem_halo!=nelem_haloed)
6996  {
6997  std::ostringstream error_message;
6998  error_message
6999  << "Clash in numbers of halo and haloed elements! "
7000  << std::endl;
7001  error_message
7002  << "# of haloed elements whose halo counterpart lives on proc "
7003  << dd << ": " << nelem_haloed << std::endl;
7004  error_message
7005  << "# of halo elements whose non-halo counterpart lives on proc "
7006  << d << ": " << nelem_halo << std::endl;
7007  error_message
7008  << "(Re-)run Problem::check_halo_schemes() with DocInfo object"
7009  << std::endl;
7010  error_message
7011  << "to identify the problem" << std::endl;
7012  throw OomphLibError(error_message.str(),
7013  OOMPH_CURRENT_FUNCTION,
7014  OOMPH_EXCEPTION_LOCATION);
7015  }
7016 
7017 
7018  //We can only check nodal stuff for meshes of finite elements
7019  if(dynamic_cast<FiniteElement*>(this->element_pt(0)))
7020  {
7021  // Get strung-together elemental nodal positions
7022  // from other processor
7023  //unsigned nnod_per_el=finite_element_pt(0)->nnode();
7024  unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
7025  //Vector<double> other_nodal_positions
7026  // (nod_dim*nnod_per_el*nelem_halo);
7027  //MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_per_el*nelem_halo,
7028  // MPI_DOUBLE,dd,0,comm_pt->mpi_comm(),&status);
7029  unsigned n_nodal_positions=0;
7030  MPI_Recv(&n_nodal_positions,1,
7031  MPI_UNSIGNED,dd,0,Comm_pt->mpi_comm(),&status);
7032  Vector<double> other_nodal_positions(n_nodal_positions);
7033  if (n_nodal_positions>0)
7034  {
7035  MPI_Recv(&other_nodal_positions[0],n_nodal_positions,
7036  MPI_DOUBLE,dd,0,Comm_pt->mpi_comm(),&status);
7037  }
7038 
7039 
7040  // Receive hanging info to be checked
7041  Vector<int> other_nodal_hangings;
7042  unsigned n_other_nodal_hangings=0;
7043  MPI_Recv(&n_other_nodal_hangings,1,
7044  MPI_UNSIGNED,dd,
7045  1,Comm_pt->mpi_comm(),&status);
7046  if (n_other_nodal_hangings>0)
7047  {
7048  other_nodal_hangings.resize(n_other_nodal_hangings);
7049  MPI_Recv(&other_nodal_hangings[0],n_other_nodal_hangings,
7050  MPI_INT,dd,
7051  1,Comm_pt->mpi_comm(),&status);
7052  }
7053 
7054  //If documenting, open the output files
7055  if(doc_info.is_doc_enabled())
7056  {
7057  filename.str("");
7058  filename << doc_info.directory() << "/error_haloed_check"
7059  << doc_info.label() << "_on_proc"
7060  << my_rank << "_with_proc" << dd << "_"
7061  << doc_info.number()
7062  << ".dat";
7063  haloed_file.open(filename.str().c_str());
7064  filename.str("");
7065  filename << doc_info.directory() << "/error_halo_check"
7066  << doc_info.label() << "_on_proc"
7067  << my_rank << "_with_proc" << dd << "_"
7068  << doc_info.number()
7069  << ".dat";
7070  halo_file.open(filename.str().c_str());
7071  }
7072 
7073  unsigned count=0;
7074  unsigned count_hanging=0;
7075  for (int e=0;e<nelem_haloed;e++)
7076  {
7077  FiniteElement* finite_el_pt =
7078  dynamic_cast<FiniteElement*>(haloed_elem_pt[e]);
7079 
7080  if(finite_el_pt!=0)
7081  {
7082  unsigned nnod_this_el = finite_el_pt->nnode();
7083  for (unsigned j=0;j<nnod_this_el;j++)
7084  {
7085  Node* nod_pt=finite_el_pt->node_pt(j);
7086  //unsigned nod_dim = nod_pt->ndim();
7087 
7088  // Testing POSITIONS, not x location
7089  // (cf hanging nodes, nodes.h)
7090  Vector<double> x_haloed(nod_dim);
7091  for(unsigned i=0;i<nod_dim;i++)
7092  {
7093  x_haloed[i] = nod_pt->position(i);
7094  }
7095 
7096  Vector<double> x_halo(nod_dim);
7097  for(unsigned i=0;i<nod_dim;i++)
7098  {
7099  x_halo[i] = other_nodal_positions[count];
7100  ++count;
7101  }
7102 
7103  double error =0.0;
7104  shout=false;
7105  for(unsigned i=0;i<nod_dim;i++)
7106  {
7107  error += (x_haloed[i] - x_halo[i])*
7108  (x_haloed[i] - x_halo[i]);
7109  }
7110  error = sqrt(error);
7111 
7112  if (error>max_error) {max_error=error;}
7113  double tol=1.0e-12;
7114  if (error>tol)
7115  {
7116  oomph_info
7117  << "Discrepancy between nodal coordinates of halo(ed)"
7118  << "element larger than tolerance (" << tol
7119  <<")\n Error: " << error << "\n";
7120  shout=true;
7121  }
7122 
7123  unsigned nval=nod_pt->nvalue();
7124  int nval_other=other_nodal_hangings[count_hanging];
7125  count_hanging++;
7126  if (int(nval)!=nval_other)
7127  {
7128  oomph_info
7129  << "Number of values of node, " << nval
7130  << ", does not match number of values on other proc, "
7131  << nval_other << std::endl;
7132  shout=true;
7133  shout_and_terminate=true;
7134  }
7135 
7136  // Is other node geometrically hanging?
7137  int other_geom_hanging=0;
7138 
7139  // Check hangingness/number of master nodes
7140  for (int i=-1;i<int(nval);i++)
7141  {
7142  int nmaster_other=other_nodal_hangings[count_hanging];
7143  count_hanging++;
7144 
7145  // Record geom hang status of other node
7146  if (i==-1) other_geom_hanging=nmaster_other;
7147 
7148  // Value is hanging on local proc: Does it have the same
7149  // number of masters as its counterpart on other proc?
7150  if (nod_pt->is_hanging(i))
7151  {
7152  unsigned nmaster=nod_pt->hanging_pt(i)->nmaster();
7153  if (int(nmaster)!=nmaster_other)
7154  {
7155  oomph_info
7156  << "Number of master nodes for hanging value " << i
7157  << " of node, " << nmaster
7158  << ", does not match number of master "
7159  << "nodes on other proc, "
7160  << nmaster_other << std::endl;
7161  shout=true;
7162  shout_and_terminate=true;
7163  }
7164  }
7165  // Value is not hanging on local proc: It had better
7166  // not have any masters (i.e. be hanging) on the other
7167  // proc
7168  else
7169  {
7170  if (nmaster_other!=0)
7171  {
7172  oomph_info
7173  << "Value " << i
7174  << " of node is not hanging whereas "
7175  << " node on other proc has "
7176  << nmaster_other
7177  << " masters and therefore is hanging. \n";
7178  shout=true;
7179  shout_and_terminate=true;
7180  }
7181  }
7182  }
7183 
7184  if (shout)
7185  {
7186  // Report error. NOTE: ERROR IS THROWN BELOW ONCE
7187  // ALL THIS HAS BEEN PROCESSED.
7188 
7189  oomph_info
7190  << "Error(s) displayed above are for "
7191  << "domain with non-halo (i.e. haloed) elem: "
7192  << dd << "\n";
7193  oomph_info
7194  << "Domain with halo elem: " << d
7195  << "\n";
7196  switch(nod_dim)
7197  {
7198  case 1:
7199  oomph_info
7200  << "Current processor is " << my_rank
7201  << "\n"
7202  << "Nodal positions: " << x_halo[0] << "\n"
7203  << "and haloed: " << x_haloed[0] << "\n"
7204  //<< "Node pointer: " << finite_el_pt->node_pt(j)
7205  << "\n";
7206  break;
7207  case 2:
7208  oomph_info
7209  << "Current processor is " << my_rank
7210  << "\n"
7211  << "Nodal positions: "
7212  << x_halo[0] << " " << x_halo[1]
7213  << "\n"
7214  << "and haloed: " << x_haloed[0] << " "
7215  << x_haloed[1] << std::endl
7216  //<< "Node pointer: " << finite_el_pt->node_pt(j)
7217  << "\n";
7218  break;
7219  case 3:
7220  oomph_info
7221  << "Current processor is " << my_rank
7222  << "\n"
7223  << "Nodal positions: "
7224  << x_halo[0] << " " << x_halo[1] << " " << x_halo[2]
7225  << "\n"
7226  << "and haloed: " << x_haloed[0] << " "
7227  << x_haloed[1] << " " << x_haloed[2] << std::endl
7228  //<< "Node pointer: " << finite_el_pt->node_pt(j)
7229  << "\n";
7230  break;
7231  default:
7232  throw OomphLibError(
7233  "Nodal dimension not equal to 1, 2 or 3\n",
7234  OOMPH_CURRENT_FUNCTION,
7235  OOMPH_EXCEPTION_LOCATION);
7236  }
7237 
7238 
7239 
7240  //If documenting, write to output files
7241  if(doc_info.is_doc_enabled())
7242  {
7243  for(unsigned i=0;i<nod_dim;i++)
7244  {
7245  haloed_file << x_haloed[i] << " ";
7246  halo_file << x_halo[i] << " ";
7247  }
7248  haloed_file << error << " " << my_rank << " "
7249  << dd << " "
7250  << finite_el_pt->node_pt(j)->is_hanging()
7251  << std::endl;
7252  halo_file << error << " " << my_rank << " "
7253  << dd << " "
7254  << other_geom_hanging << std::endl;
7255  }
7256  }
7257  } // j<nnod_per_el
7258  }
7259  } // e<nelem_haloed
7260 
7261  //If documenting, close output files
7262  if(doc_info.is_doc_enabled())
7263  {
7264  haloed_file.close();
7265  halo_file.close();
7266  }
7267  }
7268  }
7269  }
7270  }
7271  }
7272  // My haloed elements are not being checked: Send my halo elements
7273  // whose non-halo counterparts are located on processor d
7274  else
7275  {
7276 
7277  // Get vectors of halo elements by copy operation
7279  halo_elem_pt(halo_element_pt(d));
7280 
7281  // How many of my elements are halo elements whose non-halo
7282  // counterpart is located on processor d?
7283  unsigned nelem_halo=halo_elem_pt.size();
7284 
7285  if (nelem_halo!=0)
7286  {
7287  // Send it across to the processor whose haloed nodes are being checked
7288  MPI_Send(&nelem_halo,1,MPI_UNSIGNED,d,0,Comm_pt->mpi_comm());
7289 
7290  //Only bother if the mesh consists of finite elements
7291  if(dynamic_cast<FiniteElement*>(this->element_pt(0)))
7292  {
7293  // Now string together the nodal positions of all halo nodes
7294  // Use this only to work out roughly how much space to
7295  // reserve for the vector. Then we can push data cheaply
7296  // while not assuming all elements have the same number
7297  // of nodes.
7298  unsigned nnod_first_el=finite_element_pt(0)->nnode();
7299  unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
7300  Vector<double> nodal_positions;
7301  nodal_positions.reserve(nod_dim*nnod_first_el*nelem_halo);
7302 
7303  // Storage for hang information
7304  Vector<int> nodal_hangings;
7305 
7306  //unsigned count=0;
7307  for (unsigned e=0;e<nelem_halo;e++)
7308  {
7309  FiniteElement* finite_el_pt =
7310  dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
7311  if(finite_el_pt!=0)
7312  {
7313  unsigned nnod_this_el = finite_el_pt->nnode();
7314  for (unsigned j=0;j<nnod_this_el;j++)
7315  {
7316  Node* nod_pt=finite_el_pt->node_pt(j);
7317  //unsigned nod_dim = nod_pt->ndim();
7318 
7319  //Throw error if node doesn't exist
7320  if(nod_pt==0)
7321  {
7322  //Print our nodes in element
7323  oomph_info << "element: " << finite_el_pt << std::endl;
7324  for(unsigned i=0; i<finite_el_pt->nnode(); i++)
7325  {
7326  oomph_info << finite_el_pt->node_pt(i) << std::endl;
7327  }
7328 
7329  //Throw error
7330  throw OomphLibError("Node doesn't exist!",
7331  OOMPH_CURRENT_FUNCTION,
7332  OOMPH_EXCEPTION_LOCATION);
7333  }
7334 
7335  // Testing POSITIONS, not x location (cf hanging nodes, nodes.h)
7336  for(unsigned i=0;i<nod_dim;i++)
7337  {
7338  //nodal_positions[count]=nod_pt->position(i);
7339  //count++;
7340  nodal_positions.push_back(nod_pt->position(i));
7341  }
7342 
7343  unsigned nval=nod_pt->nvalue();
7344  nodal_hangings.push_back(nval);
7345  for (int i=-1;i<int(nval);i++)
7346  {
7347  if (nod_pt->is_hanging(i))
7348  {
7349  unsigned nmaster=nod_pt->hanging_pt(i)->nmaster();
7350  nodal_hangings.push_back(nmaster);
7351  }
7352  else
7353  {
7354  nodal_hangings.push_back(0);
7355  }
7356  }
7357  }
7358  }
7359  }
7360 
7361  // Total number of nodal positions to be checked
7362  unsigned n_nodal_positions=nodal_positions.size();
7363 
7364  // Total number of nodal hang information to be checked
7365  unsigned n_nodal_hangings=nodal_hangings.size();
7366 
7367  // Send it across to the processor whose haloed elements are being
7368  // checked
7369  //MPI_Send(&nodal_positions[0],nod_dim*nnod_per_el*nelem_halo,
7370  // MPI_DOUBLE,d,0,comm_pt->mpi_comm());
7371  MPI_Send(&n_nodal_positions,1,
7372  MPI_UNSIGNED,d,0,Comm_pt->mpi_comm());
7373  if (n_nodal_positions>0)
7374  {
7375  MPI_Send(&nodal_positions[0],n_nodal_positions,
7376  MPI_DOUBLE,d,0,Comm_pt->mpi_comm());
7377  }
7378  MPI_Send(&n_nodal_hangings,1,
7379  MPI_UNSIGNED,d,1,Comm_pt->mpi_comm());
7380  if (n_nodal_hangings>0)
7381  {
7382  MPI_Send(&nodal_hangings[0], n_nodal_hangings,
7383  MPI_INT,d,1,Comm_pt->mpi_comm());
7384  }
7385  }
7386  }
7387  }
7388  }
7389 
7390  oomph_info << "Max. error for halo/haloed elements " << max_error
7391  << std::endl;
7392  if (max_error>max_permitted_error_for_halo_check)
7393  {
7394  shout_and_terminate=true;
7395  oomph_info
7396  << "This is bigger than the permitted threshold "
7397  << max_permitted_error_for_halo_check << std::endl;
7398  oomph_info
7399  << "If you believe this to be acceptable for your problem\n"
7400  << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
7401  }
7402 
7403  if (shout_and_terminate)
7404  {
7405  throw OomphLibError("Error in halo checking",
7406  OOMPH_CURRENT_FUNCTION,
7407  OOMPH_EXCEPTION_LOCATION);
7408  }
7409 
7410  // Now check the halo/haloed nodes lookup schemes
7411 
7412  // Doc halo/haloed nodes lookup schemes
7413  //-------------------------------------
7414  if (doc_info.is_doc_enabled())
7415  {
7416  // Loop over domains for halo nodes
7417  for (int dd=0;dd<n_proc;dd++)
7418  {
7419  filename.str("");
7420  filename << doc_info.directory() << "/halo_node_check"
7421  << doc_info.label() << "_on_proc"
7422  << my_rank << "_with_proc" << dd << "_"
7423  << doc_info.number()
7424  << ".dat";
7425  halo_file.open(filename.str().c_str());
7426  halo_file << "ZONE " << std::endl;
7427 
7428  unsigned nnod=nhalo_node(dd);
7429  for (unsigned j=0;j<nnod;j++)
7430  {
7431  Node* nod_pt=halo_node_pt(dd,j);
7432  unsigned ndim=nod_pt->ndim();
7433  for (unsigned i=0;i<ndim;i++)
7434  {
7435  halo_file << nod_pt->position(i) << " ";
7436  }
7437  halo_file << nod_pt->is_hanging() << std::endl;
7438  }
7439  // Dummy output for processor that doesn't share halo nodes
7440  // (needed for tecplot)
7441  // (This will only work if there are elements on this processor...)
7442  if ((nnod==0) && (nelement()!=0))
7443  {
7444  FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(element_pt(0));
7445  //If it's a generalised element mesh dummy output
7446  if(f_el_pt==0)
7447  {
7448  halo_file << "0.0" << std::endl;
7449  }
7450  else
7451  {
7452  unsigned ndim=f_el_pt->node_pt(0)->ndim();
7453  if (ndim==2)
7454  {
7455  halo_file << " 1.0 1.1 " << std::endl;
7456  }
7457  else
7458  {
7459  halo_file << " 1.0 1.1 1.1" << std::endl;
7460  }
7461  }
7462  }
7463  halo_file.close();
7464  }
7465 
7466 
7467  // Loop over domains for haloed nodes
7468  for (int d=0;d<n_proc;d++)
7469  {
7470  filename.str("");
7471  filename << doc_info.directory() << "/haloed_node_check"
7472  << doc_info.label() << "_on_proc"
7473  << my_rank << "_with_proc" << d << "_"
7474  << doc_info.number()
7475  << ".dat";
7476  haloed_file.open(filename.str().c_str());
7477  haloed_file << "ZONE " << std::endl;
7478 
7479  unsigned nnod=nhaloed_node(d);
7480  for (unsigned j=0;j<nnod;j++)
7481  {
7482  Node* nod_pt=haloed_node_pt(d,j);
7483  unsigned ndim=nod_pt->ndim();
7484  for (unsigned i=0;i<ndim;i++)
7485  {
7486  haloed_file << nod_pt->position(i) << " ";
7487  }
7488  haloed_file << nod_pt->is_hanging() << std::endl;
7489  }
7490  // Dummy output for processor that doesn't share halo nodes
7491  // (needed for tecplot)
7492  if ((nnod==0) && (nelement()!=0))
7493  {
7494  FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(element_pt(0));
7495  //If it's a generalised element mesh dummy output
7496  if(f_el_pt==0)
7497  {
7498  haloed_file << "0.0" << std::endl;
7499  }
7500  else
7501  {
7502  unsigned ndim=f_el_pt->node_pt(0)->ndim();
7503  if (ndim==2)
7504  {
7505  haloed_file << " 1.0 1.1 " << std::endl;
7506  }
7507  else
7508  {
7509  haloed_file << " 1.0 1.1 1.1" << std::endl;
7510  }
7511  }
7512  }
7513  haloed_file.close();
7514  }
7515  }
7516 
7517  // Check halo/haloed nodes lookup schemes
7518  //---------------------------------------
7519  max_error=0.0;
7520 
7521  // Loop over domains for haloed nodes
7522  for (int d=0;d<n_proc;d++)
7523  {
7524  // Are my haloed nodes being checked?
7525  if (d==my_rank)
7526  {
7527  // Loop over domains for halo nodes
7528  for (int dd=0;dd<n_proc;dd++)
7529  {
7530  // Don't talk to yourself
7531  if (dd!=d)
7532  {
7533  // How many of my nodes are haloed nodes whose halo
7534  // counterpart is located on processor dd?
7535  int nnod_haloed=nhaloed_node(dd);
7536 
7537  if (nnod_haloed!=0)
7538  {
7539  // Receive from processor dd how many of his nodes are halo
7540  // nodes whose non-halo counterparts are located here
7541  int nnod_halo=0;
7542  MPI_Recv(&nnod_halo,1,MPI_INT,dd,0,Comm_pt->mpi_comm(),&status);
7543 
7544  if (nnod_haloed!=nnod_halo)
7545  {
7546  std::ostringstream error_message;
7547 
7548  error_message
7549  << "Clash in numbers of halo and haloed nodes! "
7550  << std::endl;
7551  error_message
7552  << "# of haloed nodes whose halo counterpart lives on proc "
7553  << dd << ": " << nnod_haloed << std::endl;
7554  error_message
7555  << "# of halo nodes whose non-halo counterpart lives on proc "
7556  << d << ": " << nnod_halo << std::endl;
7557  error_message
7558  << "(Re-)run Mesh::check_halo_schemes() with DocInfo object"
7559  << std::endl;
7560  error_message
7561  << "to identify the problem" << std::endl;
7562  throw OomphLibError(error_message.str(),
7563  OOMPH_CURRENT_FUNCTION,
7564  OOMPH_EXCEPTION_LOCATION);
7565  }
7566 
7567 
7568  unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
7569 
7570  // Get strung-together nodal positions from other processor
7571  Vector<double> other_nodal_positions(nod_dim*nnod_halo);
7572  MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_halo,MPI_DOUBLE,dd,
7573  0,Comm_pt->mpi_comm(),&status);
7574 
7575  // Check
7576  unsigned count=0;
7577  for (int j=0;j<nnod_halo;j++)
7578  {
7579  Vector<double> x_haloed(nod_dim);
7580  for(unsigned i=0;i<nod_dim;i++)
7581  {
7582  x_haloed[i] = haloed_node_pt(dd,j)->position(i);
7583  }
7584  Vector<double> x_halo(nod_dim);
7585  for(unsigned i=0;i<nod_dim;i++)
7586  {
7587  x_halo[i]=other_nodal_positions[count];
7588  count++;
7589  }
7590  double error=0.0;
7591  for(unsigned i=0;i<nod_dim;i++)
7592  {
7593  error += (x_haloed[i] - x_halo[i])*(x_haloed[i] - x_halo[i]);
7594  }
7595  error = sqrt(error);
7596  if (error>max_error)
7597  {
7598  max_error=error;
7599  }
7600  }
7601  }
7602  }
7603  }
7604  }
7605  // My haloed nodes are not being checked: Send my halo nodes
7606  // whose non-halo counterparts are located on processor d
7607  else
7608  {
7609  int nnod_halo=nhalo_node(d);
7610 
7611  if (nnod_halo!=0)
7612  {
7613  // Send it across to the processor whose haloed nodes are being checked
7614  MPI_Send(&nnod_halo,1,MPI_INT,d,0,Comm_pt->mpi_comm());
7615 
7616  unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
7617 
7618  // Now string together the nodal positions of all halo nodes
7619  Vector<double> nodal_positions(nod_dim*nnod_halo);
7620  unsigned count=0;
7621  for (int j=0;j<nnod_halo;j++)
7622  {
7623  for(unsigned i=0;i<nod_dim;i++)
7624  {
7625  nodal_positions[count]=halo_node_pt(d,j)->position(i);
7626  count++;
7627  }
7628  }
7629  // Send it across to the processor whose haloed nodes are being checked
7630  MPI_Send(&nodal_positions[0],nod_dim*nnod_halo,MPI_DOUBLE,d,0,
7631  Comm_pt->mpi_comm());
7632  }
7633  }
7634  }
7635 
7636  oomph_info << "Max. error for halo/haloed nodes " << max_error
7637  << std::endl;
7638 
7639  if (max_error>max_permitted_error_for_halo_check)
7640  {
7641  std::ostringstream error_message;
7642  error_message
7643  << "This is bigger than the permitted threshold "
7644  << max_permitted_error_for_halo_check << std::endl;
7645  error_message
7646  << "If you believe this to be acceptable for your problem\n"
7647  << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
7648  throw OomphLibError(error_message.str(),
7649  OOMPH_CURRENT_FUNCTION,
7650  OOMPH_EXCEPTION_LOCATION);
7651  }
7652 
7653 
7654 
7655 
7656 
7657 
7658 
7659  // Now check the external halo/haloed element lookup scheme
7660 
7661  // Doc external halo/haoloed element lookup schemes
7662  //--------------------------------------------------
7663  if (doc_info.is_doc_enabled())
7664  {
7665  // Loop over domains for external halo elements
7666  for (int dd=0;dd<n_proc;dd++)
7667  {
7668 
7669  filename.str("");
7670  filename << doc_info.directory() << "/ext_halo_element_check"
7671  << doc_info.label() << "_on_proc"
7672  << my_rank << "_with_proc" << dd << "_"
7673  << doc_info.number()
7674  << ".dat";
7675  ext_halo_file.open(filename.str().c_str());
7676  output_external_halo_elements(dd,ext_halo_file);
7677  ext_halo_file.close();
7678 
7679 
7680  filename.str("");
7681  filename << doc_info.directory() << "/ext_halo_node_check"
7682  << doc_info.label() << "_on_proc"
7683  << my_rank << "_with_proc" << dd << "_"
7684  << doc_info.number()
7685  << ".dat";
7686  ext_halo_file.open(filename.str().c_str());
7687 
7688  // Get vectors of external halo/haloed elements by copy operation
7690 
7691  unsigned nelem=ext_halo_elem_pt.size();
7692 
7693  for (unsigned e=0;e<nelem;e++)
7694  {
7695  ext_halo_file << "ZONE " << std::endl;
7696  //Can I cast to a finite element
7697  FiniteElement* finite_el_pt =
7698  dynamic_cast<FiniteElement*>(ext_halo_elem_pt[e]);
7699  if(finite_el_pt!=0)
7700  {
7701  unsigned nnod=finite_el_pt->nnode();
7702  for (unsigned j=0;j<nnod;j++)
7703  {
7704  Node* nod_pt=finite_el_pt->node_pt(j);
7705  unsigned ndim=nod_pt->ndim();
7706  for (unsigned i=0;i<ndim;i++)
7707  {
7708  ext_halo_file << nod_pt->position(i) << " ";
7709  }
7710  ext_halo_file << std::endl;
7711  }
7712  }
7713  }
7714  ext_halo_file.close();
7715  }
7716 
7717  // Loop over domains for external halo elements
7718  for (int d=0;d<n_proc;d++)
7719  {
7720 
7721 
7722  filename.str("");
7723  filename << doc_info.directory() << "/ext_haloed_element_check"
7724  << doc_info.label() << "_on_proc"
7725  << my_rank << "_with_proc" << d << "_"
7726  << doc_info.number()
7727  << ".dat";
7728  ext_haloed_file.open(filename.str().c_str());
7729  output_external_haloed_elements(d,ext_haloed_file);
7730  ext_haloed_file.close();
7731 
7732 
7733 
7734  filename.str("");
7735  filename << doc_info.directory() << "/ext_haloed_node_check"
7736  << doc_info.label() << "_on_proc"
7737  << my_rank << "_with_proc" << d << "_"
7738  << doc_info.number()
7739  << ".dat";
7740  ext_haloed_file.open(filename.str().c_str());
7741 
7742  // Get vectors of external halo/haloed elements by copy operation
7744  ext_haloed_elem_pt(External_haloed_element_pt[d]);
7745 
7746  unsigned nelem2=ext_haloed_elem_pt.size();
7747  for (unsigned e=0;e<nelem2;e++)
7748  {
7749  ext_haloed_file << "ZONE " << std::endl;
7750  //Is it a finite element
7751  FiniteElement* finite_el_pt =
7752  dynamic_cast<FiniteElement*>(ext_haloed_elem_pt[e]);
7753  if(finite_el_pt!=0)
7754  {
7755  unsigned nnod2=finite_el_pt->nnode();
7756  for (unsigned j=0;j<nnod2;j++)
7757  {
7758  Node* nod_pt=finite_el_pt->node_pt(j);
7759  unsigned ndim=nod_pt->ndim();
7760  for (unsigned i=0;i<ndim;i++)
7761  {
7762  ext_haloed_file << nod_pt->position(i) << " ";
7763  }
7764  ext_haloed_file << std::endl;
7765  }
7766  }
7767  }
7768  ext_haloed_file.close();
7769  }
7770  } //end of if doc flag
7771 
7772  // Check external halo/haloed element lookup schemes
7773  //--------------------------------------------------
7774  max_error=0.0;
7775  shout=false;
7776  shout_and_terminate=false;
7777 
7778  // Loop over domains for external haloed elements
7779  for (int d=0;d<n_proc;d++)
7780  {
7781  // Are my external haloed elements being checked?
7782  if (d==my_rank)
7783  {
7784  // Loop over domains for external halo elements
7785  for (int dd=0;dd<n_proc;dd++)
7786  {
7787  // Don't talk to yourself
7788  if (dd!=d)
7789  {
7790  // Get vectors of external haloed elements by copy operation
7792  ext_haloed_elem_pt(External_haloed_element_pt[dd]);
7793 
7794  // How many of my elements are external haloed elements whose halo
7795  // counterpart is located on processor dd?
7796  int nelem_haloed=ext_haloed_elem_pt.size();
7797 
7798  if (nelem_haloed!=0)
7799  {
7800  // Receive from processor dd how many of his elements are halo
7801  // nodes whose non-halo counterparts are located here
7802  int nelem_halo=0;
7803  MPI_Recv(&nelem_halo,1,MPI_INT,dd,0,Comm_pt->mpi_comm(),&status);
7804  if (nelem_halo!=nelem_haloed)
7805  {
7806  std::ostringstream error_message;
7807  error_message
7808  << "Clash in numbers of external halo and haloed elements! "
7809  << std::endl;
7810  error_message
7811  << "# of external haloed elements whose halo counterpart lives on proc "
7812  << dd << ": " << nelem_haloed << std::endl;
7813  error_message
7814  << "# of external halo elements whose non-halo counterpart lives on proc "
7815  << d << ": " << nelem_halo << std::endl;
7816  error_message
7817  << "(Re-)run Problem::check_halo_schemes() with DocInfo object"
7818  << std::endl;
7819  error_message
7820  << "to identify the problem" << std::endl;
7821  throw OomphLibError(error_message.str(),
7822  OOMPH_CURRENT_FUNCTION,
7823  OOMPH_EXCEPTION_LOCATION);
7824  }
7825 
7826 
7827  //We can only check nodal stuff for meshes of finite elements
7828  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(ext_haloed_elem_pt[0]);
7829  if(fe_pt!=0)
7830  {
7831  // Get strung-together elemental nodal positions
7832  // from other processor
7833  //unsigned nnod_per_el=fe_pt->nnode();
7834  unsigned nod_dim=fe_pt->node_pt(0)->ndim();
7835  //Vector<double> other_nodal_positions
7836  // (nod_dim*nnod_per_el*nelem_halo);
7837  //MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_per_el*nelem_halo,
7838  // MPI_DOUBLE,dd,0,comm_pt->mpi_comm(),&status);
7839  unsigned n_nodal_positions=0;
7840  MPI_Recv(&n_nodal_positions,1,
7841  MPI_UNSIGNED,dd,0,Comm_pt->mpi_comm(),&status);
7842  Vector<double> other_nodal_positions(n_nodal_positions);
7843  if (n_nodal_positions>0)
7844  {
7845  MPI_Recv(&other_nodal_positions[0],n_nodal_positions,
7846  MPI_DOUBLE,dd,0,Comm_pt->mpi_comm(),&status);
7847  }
7848 
7849 
7850  // Receive hanging info to be checked
7851  Vector<int> other_nodal_hangings;
7852  unsigned n_other_nodal_hangings=0;
7853  MPI_Recv(&n_other_nodal_hangings,1,
7854  MPI_UNSIGNED,dd,
7855  1,Comm_pt->mpi_comm(),&status);
7856  if (n_other_nodal_hangings>0)
7857  {
7858  other_nodal_hangings.resize(n_other_nodal_hangings);
7859  MPI_Recv(&other_nodal_hangings[0],n_other_nodal_hangings,
7860  MPI_INT,dd,
7861  1,Comm_pt->mpi_comm(),&status);
7862  }
7863 
7864  //If documenting, open the output files
7865  if(doc_info.is_doc_enabled())
7866  {
7867  filename.str("");
7868  filename << doc_info.directory() << "/error_ext_haloed_check"
7869  << doc_info.label() << "_on_proc"
7870  << my_rank << "_with_proc" << dd << "_"
7871  << doc_info.number()
7872  << ".dat";
7873  ext_haloed_file.open(filename.str().c_str());
7874  filename.str("");
7875  filename << doc_info.directory() << "/error_ext_halo_check"
7876  << doc_info.label() << "_on_proc"
7877  << my_rank << "_with_proc" << dd << "_"
7878  << doc_info.number()
7879  << ".dat";
7880  ext_halo_file.open(filename.str().c_str());
7881  }
7882 
7883  unsigned count=0;
7884  unsigned count_hanging=0;
7885  for (int e=0;e<nelem_haloed;e++)
7886  {
7887  FiniteElement* finite_el_pt =
7888  dynamic_cast<FiniteElement*>(ext_haloed_elem_pt[e]);
7889 
7890  if(finite_el_pt!=0)
7891  {
7892  unsigned nnod_this_el = finite_el_pt->nnode();
7893  for (unsigned j=0;j<nnod_this_el;j++)
7894  {
7895  Node* nod_pt=finite_el_pt->node_pt(j);
7896  //unsigned nod_dim = mod_pt->ndim();
7897 
7898  // Testing POSITIONS, not x location
7899  // (cf hanging nodes, nodes.h)
7900  Vector<double> x_haloed(nod_dim);
7901  for(unsigned i=0;i<nod_dim;i++)
7902  {
7903  x_haloed[i] = nod_pt->position(i);
7904  }
7905 
7906  Vector<double> x_halo(nod_dim);
7907  for(unsigned i=0;i<nod_dim;i++)
7908  {
7909  x_halo[i] = other_nodal_positions[count];
7910  ++count;
7911  }
7912 
7913  double error =0.0;
7914  shout=false;
7915  for(unsigned i=0;i<nod_dim;i++)
7916  {
7917  error += (x_haloed[i] - x_halo[i])*
7918  (x_haloed[i] - x_halo[i]);
7919  }
7920  error = sqrt(error);
7921 
7922  if (error>max_error) {max_error=error;}
7923  double tol=1.0e-12;
7924  if (error>tol)
7925  {
7926  oomph_info
7927  << "Discrepancy between nodal coordinates of external halo(ed)"
7928  << "element larger than tolerance (" << tol
7929  <<")\n Error: " << error << "\n";
7930  shout=true;
7931  }
7932 
7933  unsigned nval=nod_pt->nvalue();
7934  int nval_other=other_nodal_hangings[count_hanging];
7935  count_hanging++;
7936  if (int(nval)!=nval_other)
7937  {
7938  oomph_info
7939  << "Number of values of node, " << nval
7940  << ", does not match number of values on other proc, "
7941  << nval_other << std::endl;
7942  shout=true;
7943  shout_and_terminate=true;
7944  }
7945 
7946  // Is other node geometrically hanging?
7947  int other_geom_hanging=0;
7948 
7949  // Check hangingness/number of master nodes
7950  for (int i=-1;i<int(nval);i++)
7951  {
7952  int nmaster_other=other_nodal_hangings[count_hanging];
7953  count_hanging++;
7954 
7955  // Record geom hang status of other node
7956  if (i==-1) other_geom_hanging=nmaster_other;
7957 
7958  // Value is hanging on local proc: Does it have the same
7959  // number of masters as its counterpart on other proc?
7960  if (nod_pt->is_hanging(i))
7961  {
7962  unsigned nmaster=nod_pt->hanging_pt(i)->nmaster();
7963  if (int(nmaster)!=nmaster_other)
7964  {
7965  oomph_info
7966  << "Number of master nodes for hanging value " << i
7967  << " of node, " << nmaster
7968  << ", does not match number of master "
7969  << "nodes on other proc, "
7970  << nmaster_other << std::endl;
7971  shout=true;
7972  shout_and_terminate=true;
7973  }
7974  }
7975  // Value is not hanging on local proc: It had better
7976  // not have any masters (i.e. be hanging) on the other
7977  // proc
7978  else
7979  {
7980  if (nmaster_other!=0)
7981  {
7982  oomph_info
7983  << "Value " << i
7984  << " of node is not hanging whereas "
7985  << " node on other proc has "
7986  << nmaster_other
7987  << " masters and therefore is hanging. \n";
7988  shout=true;
7989  shout_and_terminate=true;
7990  }
7991  }
7992  }
7993 
7994  if (shout)
7995  {
7996  // Report error. NOTE: ERROR IS THROWN BELOW ONCE
7997  // ALL THIS HAS BEEN PROCESSED.
7998 
7999  oomph_info
8000  << "Error(s) displayed above are for "
8001  << "domain with external non-halo (i.e. haloed) elem: "
8002  << dd << "\n";
8003  oomph_info
8004  << "Domain with halo elem: " << d
8005  << "\n";
8006  switch(nod_dim)
8007  {
8008  case 1:
8009  oomph_info
8010  << "Current processor is " << my_rank
8011  << "\n"
8012  << "Nodal positions: " << x_halo[0] << "\n"
8013  << "and haloed: " << x_haloed[0] << "\n"
8014  //<< "Node pointer: " << finite_el_pt->node_pt(j)
8015  << "\n";
8016  break;
8017  case 2:
8018  oomph_info
8019  << "Current processor is " << my_rank
8020  << "\n"
8021  << "Nodal positions: "
8022  << x_halo[0] << " " << x_halo[1]
8023  << "\n"
8024  << "and haloed: " << x_haloed[0] << " "
8025  << x_haloed[1] << std::endl
8026  //<< "Node pointer: " << finite_el_pt->node_pt(j)
8027  << "\n";
8028  break;
8029  case 3:
8030  oomph_info
8031  << "Current processor is " << my_rank
8032  << "\n"
8033  << "Nodal positions: "
8034  << x_halo[0] << " " << x_halo[1] << " " << x_halo[2]
8035  << "\n"
8036  << "and haloed: " << x_haloed[0] << " "
8037  << x_haloed[1] << " " << x_haloed[2] << std::endl
8038  //<< "Node pointer: " << finite_el_pt->node_pt(j)
8039  << "\n";
8040  break;
8041  default:
8042  throw OomphLibError(
8043  "Nodal dimension not equal to 1, 2 or 3\n",
8044  OOMPH_CURRENT_FUNCTION,
8045  OOMPH_EXCEPTION_LOCATION);
8046  }
8047 
8048 
8049 
8050  //If documenting, write to output files
8051  if(doc_info.is_doc_enabled())
8052  {
8053  for(unsigned i=0;i<nod_dim;i++)
8054  {
8055  ext_haloed_file << x_haloed[i] << " ";
8056  ext_halo_file << x_halo[i] << " ";
8057  }
8058  ext_haloed_file << error << " " << my_rank << " "
8059  << dd << " "
8060  << finite_el_pt->node_pt(j)->is_hanging()
8061  << std::endl;
8062  ext_halo_file << error << " " << my_rank << " "
8063  << dd << " "
8064  << other_geom_hanging << std::endl;
8065  }
8066  }
8067  } // j<nnod_per_el
8068  }
8069  } // e<nelem_haloed
8070 
8071  //If documenting, close output files
8072  if(doc_info.is_doc_enabled())
8073  {
8074  ext_haloed_file.close();
8075  ext_halo_file.close();
8076  }
8077  }
8078  }
8079  }
8080  }
8081  }
8082  // My external haloed elements are not being checked: Send my halo elements
8083  // whose non-halo counterparts are located on processor d
8084  else
8085  {
8086 
8087  // Get vectors of external halo elements by copy operation
8089  ext_halo_elem_pt(External_halo_element_pt[d]);
8090 
8091  // How many of my elements are external halo elements whose non-halo
8092  // counterpart is located on processor d?
8093  unsigned nelem_halo=ext_halo_elem_pt.size();
8094 
8095  if (nelem_halo!=0)
8096  {
8097  // Send it across to the processor whose external haloed nodes
8098  // are being checked
8099  MPI_Send(&nelem_halo,1,MPI_UNSIGNED,d,0,Comm_pt->mpi_comm());
8100 
8101  //Only bother if the mesh consists of finite elements
8102  FiniteElement* fe_pt=dynamic_cast<FiniteElement*>(ext_halo_elem_pt[0]);
8103  if(fe_pt!=0)
8104  {
8105  // Now string together the nodal positions of all halo nodes
8106  unsigned nnod_first_el=fe_pt->nnode();
8107  unsigned nod_dim=fe_pt->node_pt(0)->ndim();
8108  Vector<double> nodal_positions;
8109  nodal_positions.reserve(nod_dim*nnod_first_el*nelem_halo);
8110 
8111  // Storage for hang information
8112  Vector<int> nodal_hangings;
8113 
8114  unsigned count=0;
8115  for (unsigned e=0;e<nelem_halo;e++)
8116  {
8117  FiniteElement* finite_el_pt =
8118  dynamic_cast<FiniteElement*>(ext_halo_elem_pt[e]);
8119  if(finite_el_pt!=0)
8120  {
8121  unsigned nnod_this_el = finite_el_pt->nnode();
8122  for (unsigned j=0;j<nnod_this_el;j++)
8123  {
8124  Node* nod_pt=finite_el_pt->node_pt(j);
8125 
8126  // Testing POSITIONS, not x location (cf hanging nodes, nodes.h)
8127  for(unsigned i=0;i<nod_dim;i++)
8128  {
8129  nodal_positions.push_back(nod_pt->position(i));
8130  count++;
8131  }
8132 
8133  unsigned nval=nod_pt->nvalue();
8134  nodal_hangings.push_back(nval);
8135  for (int i=-1;i<int(nval);i++)
8136  {
8137  if (nod_pt->is_hanging(i))
8138  {
8139  unsigned nmaster=nod_pt->hanging_pt(i)->nmaster();
8140  nodal_hangings.push_back(nmaster);
8141  }
8142  else
8143  {
8144  nodal_hangings.push_back(0);
8145  }
8146  }
8147  }
8148  }
8149  }
8150 
8151  // Total number of nodal positions to be checked
8152  unsigned n_nodal_positions=nodal_positions.size();
8153 
8154  // Total number of nodal hang information to be checked
8155  unsigned n_nodal_hangings=nodal_hangings.size();
8156 
8157  // Send it across to the processor whose external haloed
8158  // elements are being checked
8159  //MPI_Send(&nodal_positions[0],nod_dim*nnod_per_el*nelem_halo,
8160  // MPI_DOUBLE,d,0,comm_pt->mpi_comm());
8161  MPI_Send(&n_nodal_positions,1,
8162  MPI_UNSIGNED,d,0,Comm_pt->mpi_comm());
8163  if (n_nodal_positions>0)
8164  {
8165  MPI_Send(&nodal_positions[0], n_nodal_positions,
8166  MPI_DOUBLE,d,0,Comm_pt->mpi_comm());
8167  }
8168  MPI_Send(&n_nodal_hangings,1,
8169  MPI_UNSIGNED,d,1,Comm_pt->mpi_comm());
8170  if (n_nodal_hangings>0)
8171  {
8172  MPI_Send(&nodal_hangings[0], n_nodal_hangings,
8173  MPI_INT,d,1,Comm_pt->mpi_comm());
8174  }
8175  }
8176  }
8177  }
8178  }
8179 
8180  oomph_info << "Max. error for external halo/haloed elements " << max_error
8181  << std::endl;
8182  if (max_error>max_permitted_error_for_halo_check)
8183  {
8184  shout_and_terminate=true;
8185  oomph_info
8186  << "This is bigger than the permitted threshold "
8187  << max_permitted_error_for_halo_check << std::endl;
8188  oomph_info
8189  << "If you believe this to be acceptable for your problem\n"
8190  << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
8191  }
8192 
8193  if (shout_and_terminate)
8194  {
8195  throw OomphLibError("Error in halo checking",
8196 OOMPH_CURRENT_FUNCTION,
8197  OOMPH_EXCEPTION_LOCATION);
8198  }
8199 }
8200 
8201 
8202 
8203 //========================================================================
8204  /// Null out specified external halo node (used when deleting duplicates)
8205 //========================================================================
8206 void Mesh::null_external_halo_node(const unsigned& p, Node* nod_pt)
8207 {
8208  // Loop over all external halo nodes with specified processor
8209  Vector<Node*> ext_halo_node_pt=External_halo_node_pt[p];
8210  unsigned n=ext_halo_node_pt.size();
8211  for (unsigned j=0;j<n;j++)
8212  {
8213  if (ext_halo_node_pt[j]==nod_pt)
8214  {
8215  External_halo_node_pt[p][j]=0;
8216  break;
8217  }
8218  }
8219 }
8220 
8221 
8222 //========================================================================
8223  /// Consolidate external halo node storage by removing nulled out
8224  /// pointes in external halo and haloed schemes
8225 //========================================================================
8227 {
8228 
8229  // Storage for number of processors and current processor
8230  int n_proc=Comm_pt->nproc();
8231  int my_rank=Comm_pt->my_rank();
8232 
8233  // Loop over all (other) processors and store index of any nulled-out
8234  // external halo nodes in storage scheme.
8235 
8236  // Data to be sent to each processor
8237  Vector<int> send_n(n_proc,0);
8238 
8239  // Storage for all values to be sent to all processors
8240  Vector<int> send_data;
8241 
8242  // Start location within send_data for data to be sent to each processor
8243  Vector<int> send_displacement(n_proc,0);
8244 
8245  // Check missing ones
8246  for (int domain=0;domain<n_proc;domain++)
8247  {
8248  //Set the offset for the current processor
8249  send_displacement[domain] = send_data.size();
8250 
8251  //Don't bother to do anything if the processor in the loop is the
8252  //current processor
8253  if(domain!=my_rank)
8254  {
8255  // Make backup of external halo node pointers with this domain
8256  Vector<Node*> backup_pt=External_halo_node_pt[domain];
8257 
8258  // Wipe
8259  External_halo_node_pt[domain].clear();
8260 
8261  // How many do we have currently?
8262  unsigned nnod=backup_pt.size();
8263  External_halo_node_pt[domain].reserve(nnod);
8264 
8265  // Loop over external halo nodes with this domain
8266  for (unsigned j=0;j<nnod;j++)
8267  {
8268  // Get pointer to node
8269  Node* nod_pt=backup_pt[j];
8270 
8271  // Has it been nulled out?
8272  if (nod_pt==0)
8273  {
8274  // Save index of nulled out one
8275  send_data.push_back(j);
8276  }
8277  else
8278  {
8279  // Still alive: Copy across
8280  External_halo_node_pt[domain].push_back(nod_pt);
8281  }
8282  }
8283  }
8284 
8285  // End of data
8286  send_data.push_back(-1);
8287 
8288  //Find the number of data added to the vector
8289  send_n[domain] = send_data.size() - send_displacement[domain];
8290  }
8291 
8292 
8293 
8294  //Storage for the number of data to be received from each processor
8295  Vector<int> receive_n(n_proc,0);
8296 
8297  //Now send numbers of data to be sent between all processors
8298  MPI_Alltoall(&send_n[0],1,MPI_INT,&receive_n[0],1,MPI_INT,
8299  Comm_pt->mpi_comm());
8300 
8301 
8302  //We now prepare the data to be received
8303  //by working out the displacements from the received data
8304  Vector<int> receive_displacement(n_proc,0);
8305  int receive_data_count=0;
8306  for(int rank=0;rank<n_proc;++rank)
8307  {
8308  //Displacement is number of data received so far
8309  receive_displacement[rank] = receive_data_count;
8310  receive_data_count += receive_n[rank];
8311  }
8312 
8313  //Now resize the receive buffer for all data from all processors
8314  //Make sure that it has a size of at least one
8315  if(receive_data_count==0) {++receive_data_count;}
8316  Vector<int> receive_data(receive_data_count);
8317 
8318  //Make sure that the send buffer has size at least one
8319  //so that we don't get a segmentation fault
8320  if(send_data.size()==0) {send_data.resize(1);}
8321 
8322  //Now send the data between all the processors
8323  MPI_Alltoallv(&send_data[0],&send_n[0],&send_displacement[0],
8324  MPI_INT,
8325  &receive_data[0],&receive_n[0],
8326  &receive_displacement[0],
8327  MPI_INT,
8328  Comm_pt->mpi_comm());
8329 
8330  //Now use the received data
8331  for (int send_rank=0;send_rank<n_proc;send_rank++)
8332  {
8333  //Don't bother to do anything for the processor corresponding to the
8334  //current processor or if no data were received from this processor
8335  if((send_rank != my_rank) && (receive_n[send_rank] != 0))
8336  {
8337  //Counter for the data within the large array
8338  unsigned count=receive_displacement[send_rank];
8339 
8340  // Unpack until we reach "end of data" indicator (-1)
8341  while(true)
8342  {
8343 
8344  //Read next entry
8345  int next_one=receive_data[count++];
8346 
8347  if (next_one==-1)
8348  {
8349  break;
8350  }
8351  else
8352  {
8353  // Null out the entry
8354  External_haloed_node_pt[send_rank][next_one]=0;
8355  }
8356  }
8357 
8358  // Make backup of external haloed node pointers with this domain
8359  Vector<Node*> backup_pt=External_haloed_node_pt[send_rank];
8360 
8361  // Wipe
8362  External_haloed_node_pt[send_rank].clear();
8363 
8364  // How many do we have currently?
8365  unsigned nnod=backup_pt.size();
8366  External_haloed_node_pt[send_rank].reserve(nnod);
8367 
8368  // Loop over external haloed nodes with this domain
8369  for (unsigned j=0;j<nnod;j++)
8370  {
8371  // Get pointer to node
8372  Node* nod_pt=backup_pt[j];
8373 
8374  // Has it been nulled out?
8375  if (nod_pt!=0)
8376  {
8377  // Still alive: Copy across
8378  External_haloed_node_pt[send_rank].push_back(nod_pt);
8379  }
8380  }
8381  }
8382 
8383  } //End of data is received
8384 }
8385 
8386 #endif
8387 
8388 
8389 // =================================================================
8390 /// Get the number of dof types in the mesh from the first element of the
8391 /// mesh. If MPI is on then also do some consistency checks between
8392 /// processors. \b Careful: Involves MPI Broadcasts and must therefore be
8393 /// called on all processors!
8394 // =================================================================
8395 unsigned Mesh::ndof_types() const
8396 {
8397  // Remains -1 if we don't have any elements on this processor.
8398  int int_ndof_types = -1;
8399  unsigned nel=nelement();
8400  if (nel > 0)
8401  {
8402  int_ndof_types = element_pt(0)->ndof_types();
8403 #ifdef PARANOID
8404  // Check that every element in this mesh has the same number of
8405  // types of DOF.
8406  for (unsigned i = 1; i < nel; i++)
8407  {
8408  if (int_ndof_types != int(element_pt(i)->ndof_types()) )
8409  {
8410  std::ostringstream error_message;
8411  error_message
8412  << "Every element in the mesh must have the same number of "
8413  << "types of DOF for ndof_types() to work\n"
8414  << "Element 0 has " << int_ndof_types << " DOF types\n"
8415  << "Element " << i << " [out of a total of " << nel << " ] has "
8416  << element_pt(i)->ndof_types() << " DOF types"
8417  << "Element types are: Element 0:" << typeid(*element_pt(0)).name()
8418  << "\n"
8419  << " Current Element :" << typeid(*element_pt(i)).name()
8420  << "\n";
8421  throw OomphLibError(error_message.str(),
8422  OOMPH_CURRENT_FUNCTION,
8423  OOMPH_EXCEPTION_LOCATION);
8424  }
8425  }
8426 #endif
8427  }
8428 
8429 #ifdef OOMPH_HAS_MPI
8430 
8431  // If mesh is distributed
8432  if (is_mesh_distributed())
8433  {
8434  // if more than one processor then
8435  // + ensure number of DOFs is consistent on each processor (PARANOID)
8436  // + ensure processors with no elements in this mesh have the
8437  // correct number of DOF types
8438  if (Comm_pt->nproc() > 1)
8439  {
8440  unsigned nproc = Comm_pt->nproc();
8441  unsigned my_rank = Comm_pt->my_rank();
8442 
8443  // Collect on root the number of dofs types determined independently
8444  // on all processors (-1 indicates that the processor didn't have
8445  // any elements and therefore doesn't know!)
8446  int* ndof_types_recv = 0;
8447  if (my_rank == 0)
8448  {
8449  ndof_types_recv = new int[nproc];
8450  }
8451 
8452  MPI_Gather(&int_ndof_types,1,MPI_INT,
8453  ndof_types_recv,1,MPI_INT,0,
8454  Comm_pt->mpi_comm());
8455 
8456  // Root: Update own number of dof types, check consistency amongst
8457  // all processors (in paranoid mode) and send out the actual
8458  // number of dof types to those processors who couldn't figure this
8459  // out themselves
8460  if (my_rank == 0)
8461  {
8462  // Check number of types of all non-root processors
8463  for (unsigned p = 1; p < nproc; p++)
8464  {
8465  if (ndof_types_recv[p] != -1)
8466  {
8467  // Processor p was able to figure out how many
8468  // dof types there are, so I root can update
8469  // its own (if required)
8470  if (int_ndof_types == -1)
8471  {
8472  int_ndof_types = ndof_types_recv[p];
8473  }
8474 #ifdef PARANOID
8475  // Check consistency
8476  else if (int_ndof_types != ndof_types_recv[p])
8477  {
8478  std::ostringstream error_message;
8479  error_message
8480  << "The elements in this mesh must have the same number "
8481  << "of types of DOF on each processor";
8482  for (unsigned p = 0; p < nproc; p++)
8483  {
8484  if (ndof_types_recv[p] != -1)
8485  {
8486  error_message << "Processor " << p << " : "
8487  << ndof_types_recv[p] << "\n";
8488  }
8489  else
8490  {
8491  error_message << "Processor " << p << " : (no elements)\n";
8492  }
8493  }
8494  throw OomphLibError(error_message.str(),
8495 OOMPH_CURRENT_FUNCTION,
8496  OOMPH_EXCEPTION_LOCATION);
8497  }
8498 #endif
8499  }
8500  }
8501 
8502  // Now send ndof types to non-root processors that don't have it
8503  for (unsigned p = 1; p < nproc; p++)
8504  {
8505  if (ndof_types_recv[p] == -1)
8506  {
8507  MPI_Send(&int_ndof_types,1,MPI_INT,p,0,
8508  Comm_pt->mpi_comm());
8509  }
8510  }
8511  // clean up
8512  delete[] ndof_types_recv;
8513  }
8514  // "else if": "else" for non-root; "if" for checking if current
8515  // (non-root) processor does not know ndof type and is therefore
8516  // about to receive it from root.
8517  else if (int_ndof_types == -1)
8518  {
8519  MPI_Recv(&int_ndof_types,1,MPI_INT,0,0,
8520  Comm_pt->mpi_comm(),MPI_STATUS_IGNORE);
8521  }
8522  }
8523  }
8524 #endif
8525 
8526  // If int_ndof_types if still -1 then no elements were found for this mesh, so it
8527  // has no dofs.
8528  if (int_ndof_types == -1) int_ndof_types = 0;
8529 
8530  return unsigned(int_ndof_types);
8531 }
8532 
8533 // =================================================================
8534 /// Get the number of elemental dimension in the mesh from the first
8535 /// element of the mesh. If MPI is on then also do some consistency
8536 /// checks between processors. \b Careful: Involves MPI Broadcasts
8537 /// and must therefore be called on all processors!
8538 // =================================================================
8540 {
8541  // Remains -1 if we don't have any elements on this processor.
8542  int int_dim = -1;
8543  if (nelement() > 0)
8544  {
8545  int_dim = finite_element_pt(0)->dim();
8546 #ifdef PARANOID
8547  // Check that every element in this mesh has the same number of
8548  // types of elemental dimension.
8549  for (unsigned i = 1; i < nelement(); i++)
8550  {
8551  if (int_dim != int(finite_element_pt(i)->dim()) )
8552  {
8553  std::ostringstream error_message;
8554  error_message
8555  << "Every element in the mesh must have the same number of "
8556  << "elemental dimension for elemental_dimension() to work.\n"
8557  << "Element 0 has elemental dimension " << int_dim << "\n"
8558  << "Element " << i << " has elemental dimension "
8559  << finite_element_pt(i)->dim() << ".";
8560  throw OomphLibError(error_message.str(),
8561  OOMPH_CURRENT_FUNCTION,
8562  OOMPH_EXCEPTION_LOCATION);
8563  }
8564  }
8565 #endif
8566  }
8567 
8568 #ifdef OOMPH_HAS_MPI
8569 
8570  // If mesh is distributed
8571  if (Comm_pt != 0)
8572  {
8573  // if more than one processor then
8574  // + ensure dimension number is consistent on each processor (PARANOID)
8575  // + ensure processors with no elements in this mesh have the
8576  // correct dimension number.
8577  if (Comm_pt->nproc() > 1)
8578  {
8579  unsigned nproc = Comm_pt->nproc();
8580  unsigned my_rank = Comm_pt->my_rank();
8581 
8582  // Collect on root the dimension number determined independently
8583  // on all processors (-1 indicates that the processor didn't have
8584  // any elements and therefore doesn't know!)
8585  int* dim_recv = 0;
8586  if (my_rank == 0)
8587  {
8588  dim_recv = new int[nproc];
8589  }
8590 
8591  MPI_Gather(&int_dim,1,MPI_INT,
8592  dim_recv,1,MPI_INT,0,
8593  Comm_pt->mpi_comm());
8594 
8595  // Root: Update own dimension, check consistency amongst
8596  // all processors (in paranoid mode) and send out the actual
8597  // dimension number to those processors who couldn't figure this
8598  // out themselves
8599  if (my_rank == 0)
8600  {
8601  // Check number of types of all non-root processors
8602  for (unsigned p = 1; p < nproc; p++)
8603  {
8604  if (dim_recv[p] != -1)
8605  {
8606  // Processor p was able to figure out the elemental
8607  // dimension, so I root can update
8608  // its own (if required)
8609  if (int_dim == -1)
8610  {
8611  int_dim = dim_recv[p];
8612  }
8613 #ifdef PARANOID
8614  // Check consistency
8615  else if (int_dim != dim_recv[p])
8616  {
8617  std::ostringstream error_message;
8618  error_message
8619  << "The elements in this mesh must have the same elemental "
8620  << "dimension number on each processor";
8621  for (unsigned p = 0; p < nproc; p++)
8622  {
8623  if (dim_recv[p] != -1)
8624  {
8625  error_message << "Processor " << p << " : "
8626  << dim_recv[p] << "\n";
8627  }
8628  else
8629  {
8630  error_message << "Processor " << p << " : (no elements)\n";
8631  }
8632  }
8633  throw OomphLibError(error_message.str(),
8634 OOMPH_CURRENT_FUNCTION,
8635  OOMPH_EXCEPTION_LOCATION);
8636  }
8637 #endif
8638  }
8639  }
8640 
8641  // Now send the elemental dimension to non-root processors that
8642  // don't have it
8643  for (unsigned p = 1; p < nproc; p++)
8644  {
8645  if (dim_recv[p] == -1)
8646  {
8647  MPI_Send(&int_dim,1,MPI_INT,p,0,
8648  Comm_pt->mpi_comm());
8649  }
8650  }
8651  // clean up
8652  delete[] dim_recv;
8653  }
8654  // "else if": "else" for non-root; "if" for checking if current
8655  // (non-root) processor does not know elemental dimension and is therefore
8656  // about to receive it from root.
8657  else if (int_dim == -1)
8658  {
8659  MPI_Recv(&int_dim,1,MPI_INT,0,0,
8660  Comm_pt->mpi_comm(),MPI_STATUS_IGNORE);
8661  }
8662  }
8663  }
8664 #endif
8665 
8666  // If int_dim if still -1 then no elements were found for this mesh, so it
8667  // has no elemental dimension.
8668  if (int_dim == -1) int_dim = 0;
8669 
8670  return unsigned(int_dim);
8671 }
8672 
8673 // =================================================================
8674 /// Get the number of nodal dimension in the mesh from the first
8675 /// element of the mesh. If MPI is on then also do some consistency
8676 /// checks between processors. \b Careful: Involves MPI Broadcasts
8677 /// and must therefore be called on all processors!
8678 // =================================================================
8679 unsigned Mesh::nodal_dimension() const
8680 {
8681  // Remains -1 if we don't have any elements on this processor.
8682  int int_dim = -1;
8683  if (nelement() > 0)
8684  {
8685  int_dim = finite_element_pt(0)->nodal_dimension();
8686 #ifdef PARANOID
8687  // Check that every element in this mesh has the same number of
8688  // types of nodal dimension.
8689  for (unsigned i = 1; i < nelement(); i++)
8690  {
8691  if (int_dim != int(finite_element_pt(i)->nodal_dimension()) )
8692  {
8693  std::ostringstream error_message;
8694  error_message
8695  << "Every element in the mesh must have the same number of "
8696  << "nodal dimension for nodal_dimension() to work.\n"
8697  << "Element 0 has nodal dimension " << int_dim << "\n"
8698  << "Element " << i << " has nodal dimension "
8699  << finite_element_pt(i)->nodal_dimension() << ".";
8700  throw OomphLibError(error_message.str(),
8701  OOMPH_CURRENT_FUNCTION,
8702  OOMPH_EXCEPTION_LOCATION);
8703  }
8704  }
8705 #endif
8706  }
8707 
8708 #ifdef OOMPH_HAS_MPI
8709 
8710  // If mesh is distributed
8711  if (Comm_pt != 0)
8712  {
8713  // if more than one processor then
8714  // + ensure dimension number is consistent on each processor (PARANOID)
8715  // + ensure processors with no elements in this mesh have the
8716  // correct dimension number.
8717  if (Comm_pt->nproc() > 1)
8718  {
8719  unsigned nproc = Comm_pt->nproc();
8720  unsigned my_rank = Comm_pt->my_rank();
8721 
8722  // Collect on root the dimension number determined independently
8723  // on all processors (-1 indicates that the processor didn't have
8724  // any elements and therefore doesn't know!)
8725  int* dim_recv = 0;
8726  if (my_rank == 0)
8727  {
8728  dim_recv = new int[nproc];
8729  }
8730 
8731  MPI_Gather(&int_dim,1,MPI_INT,
8732  dim_recv,1,MPI_INT,0,
8733  Comm_pt->mpi_comm());
8734 
8735  // Root: Update own dimension, check consistency amongst
8736  // all processors (in paranoid mode) and send out the actual
8737  // dimension number to those processors who couldn't figure this
8738  // out themselves
8739  if (my_rank == 0)
8740  {
8741  // Check number of types of all non-root processors
8742  for (unsigned p = 1; p < nproc; p++)
8743  {
8744  if (dim_recv[p] != -1)
8745  {
8746  // Processor p was able to figure out the nodal
8747  // dimension, so I root can update
8748  // its own (if required)
8749  if (int_dim == -1)
8750  {
8751  int_dim = dim_recv[p];
8752  }
8753 #ifdef PARANOID
8754  // Check consistency
8755  else if (int_dim != dim_recv[p])
8756  {
8757  std::ostringstream error_message;
8758  error_message
8759  << "The elements in this mesh must have the same nodal "
8760  << "dimension number on each processor";
8761  for (unsigned p = 0; p < nproc; p++)
8762  {
8763  if (dim_recv[p] != -1)
8764  {
8765  error_message << "Processor " << p << " : "
8766  << dim_recv[p] << "\n";
8767  }
8768  else
8769  {
8770  error_message << "Processor " << p << " : (no elements)\n";
8771  }
8772  }
8773  throw OomphLibError(error_message.str(),
8774  OOMPH_CURRENT_FUNCTION,
8775  OOMPH_EXCEPTION_LOCATION);
8776  }
8777 #endif
8778  }
8779  }
8780 
8781  // Now send the nodal dimension to non-root processors that
8782  // don't have it
8783  for (unsigned p = 1; p < nproc; p++)
8784  {
8785  if (dim_recv[p] == -1)
8786  {
8787  MPI_Send(&int_dim,1,MPI_INT,p,0,
8788  Comm_pt->mpi_comm());
8789  }
8790  }
8791  // clean up
8792  delete[] dim_recv;
8793  }
8794  // "else if": "else" for non-root; "if" for checking if current
8795  // (non-root) processor does not know nodal dimension and is therefore
8796  // about to receive it from root.
8797  else if (int_dim == -1)
8798  {
8799  MPI_Recv(&int_dim,1,MPI_INT,0,0,
8800  Comm_pt->mpi_comm(),MPI_STATUS_IGNORE);
8801  }
8802  }
8803  }
8804 #endif
8805 
8806  // If int_dim if still -1 then no elements were found for this mesh, so it
8807  // has no nodal dimension.
8808  if (int_dim == -1) int_dim = 0;
8809 
8810  return unsigned(int_dim);
8811 }
8812 
8813 //========================================================================
8814 /// Wipe the storage for all externally-based elements and delete halos
8815 //========================================================================
8817 {
8818 
8819 #ifdef OOMPH_HAS_MPI
8820 
8821  //Only do for distributed meshes
8822  if(is_mesh_distributed())
8823  {
8824  //Some of the external halo/haloed nodes are masters of nodes
8825  //in this mesh. We must set to be non-hanging any nodes whose
8826  //masters we are about to delete, to remove any dependencies.
8827 
8828  // Loop over all the mesh nodes and check their masters
8829  for(unsigned i=0; i<nnode(); i++)
8830  {
8831  //Get pointer to the node
8832  Node* nod_pt = node_pt(i);
8833 
8834  //Check if the node exists
8835  if(nod_pt != 0)
8836  {
8837  //Check if the node is hanging
8838  if(nod_pt->is_hanging())
8839  {
8840  //Get pointer to the hang info
8841  HangInfo* hang_pt = nod_pt->hanging_pt();
8842 
8843  //Check if any master is in the external halo storage
8844  //External haloed nodes don't get deleted, so we don't need to
8845  //(and shouldn't) un-hang their slaves
8846  bool found_a_master_in_external_halo_storage = false;
8847  for(unsigned m=0; m<hang_pt->nmaster(); m++)
8848  {
8849  //Iterator for vector of nodes
8851 
8852  //Loop over external halo storage with all processors
8853  bool found_this_master_in_external_halo_storage = false;
8854  for(int d=0; d<Comm_pt->nproc(); d++)
8855  {
8856  //Find master in map of external halo nodes
8857  it = std::find(External_halo_node_pt[d].begin(),
8858  External_halo_node_pt[d].end(),
8859  hang_pt->master_node_pt(m));
8860 
8861  //Check if it was found
8862  if(it != External_halo_node_pt[d].end())
8863  {
8864  //Mark as found
8865  found_this_master_in_external_halo_storage = true;
8866  //Don't need to search remaining processors
8867  break;
8868  }
8869  }
8870 
8871  //Check if any have been found
8872  if(found_this_master_in_external_halo_storage)
8873  {
8874  //Mark as found
8875  found_a_master_in_external_halo_storage = true;
8876  //Don't need to search remaining masters
8877  break;
8878  }
8879  }
8880 
8881  //If it was found...
8882  if(found_a_master_in_external_halo_storage)
8883  {
8884  //Master is in external halo storage and is about to be deleted,
8885  //so we'd better make this node non-hanging. In case the node
8886  //does not become hanging again, we must get all the required
8887  //information from its masters to make it a 'proper' node again.
8888 
8889  // Reconstruct the nodal values/position from the node's
8890  // hanging node representation
8891  unsigned nt=nod_pt->ntstorage();
8892  unsigned n_value=nod_pt->nvalue();
8893  Vector<double> values(n_value);
8894  unsigned n_dim=nod_pt->ndim();
8895  Vector<double> position(n_dim);
8896  // Loop over all history values
8897  for(unsigned t=0;t<nt;t++)
8898  {
8899  nod_pt->value(t,values);
8900  for(unsigned i=0;i<n_value;i++) {nod_pt->set_value(t,i,values[i]);}
8901  nod_pt->position(t,position);
8902  for(unsigned i=0;i<n_dim;i++) {nod_pt->x(t,i)=position[i];}
8903  }
8904 
8905  // If it's an algebraic node: Update its previous nodal positions too
8906  AlgebraicNode* alg_node_pt=dynamic_cast<AlgebraicNode*>(nod_pt);
8907  if (alg_node_pt!=0)
8908  {
8909  bool update_all_time_levels=true;
8910  alg_node_pt->node_update(update_all_time_levels);
8911  }
8912 
8913 
8914  //If it's a Solid node, update Lagrangian coordinates
8915  // from its hanging node representation
8916  SolidNode* solid_node_pt = dynamic_cast<SolidNode*>(nod_pt);
8917  if(solid_node_pt!=0)
8918  {
8919  unsigned n_lagrangian = solid_node_pt->nlagrangian();
8920  for(unsigned i=0;i<n_lagrangian;i++)
8921  {
8922  solid_node_pt->xi(i) = solid_node_pt->lagrangian_position(i);
8923  }
8924  }
8925 
8926  //No need to worry about geometrically hanging nodes
8927  //on boundaries (as in (p_)adapt_mesh())
8928  ////Now store geometrically hanging nodes on boundaries that
8929  ////may need updating after refinement.
8930  ////There will only be a problem if we have 3 spatial dimensions
8931  //if((mesh_dim > 2) && (nod_pt->is_hanging()))
8932  // {
8933  // //If the node is on a boundary then add a pointer to the node
8934  // //to our lookup scheme
8935  // if(nod_pt->is_on_boundary())
8936  // {
8937  // //Storage for the boundaries on which the Node is located
8938  // std::set<unsigned>* boundaries_pt;
8939  // nod_pt->get_boundaries_pt(boundaries_pt);
8940  // if(boundaries_pt!=0)
8941  // {
8942  // //Loop over the boundaries and add a pointer to the node
8943  // //to the appropriate storage scheme
8944  // for(std::set<unsigned>::iterator it=boundaries_pt->begin();
8945  // it!=boundaries_pt->end();++it)
8946  // {
8947  // hanging_nodes_on_boundary_pt[*it].insert(nod_pt);
8948  // }
8949  // }
8950  // }
8951  // }
8952 
8953  //Finally set nonhanging
8954  nod_pt->set_nonhanging();
8955  }
8956  }
8957  }
8958  else
8959  {
8960  //Node doesn't exist!
8961  }
8962  }
8963  }
8964 
8965  // Careful: some of the external halo nodes are also in boundary
8966  // node storage and should be removed from this first
8967  for (std::map<unsigned,Vector<Node*> >::iterator it=
8968  External_halo_node_pt.begin();it!=External_halo_node_pt.end();it++)
8969  {
8970  // Processor ID
8971  int d=(*it).first;
8972 
8973  // How many external haloes with this process?
8974  unsigned n_ext_halo_nod=nexternal_halo_node(d);
8975  for (unsigned j=0;j<n_ext_halo_nod;j++)
8976  {
8977  Node* ext_halo_nod_pt=external_halo_node_pt(d,j);
8978  unsigned n_bnd=nboundary();
8979  for (unsigned i_bnd=0;i_bnd<n_bnd;i_bnd++)
8980  {
8981  // Call this for all boundaries; it will do nothing
8982  // if the node is not on the current boundary
8983  remove_boundary_node(i_bnd,ext_halo_nod_pt);
8984  }
8985  }
8986  }
8987 
8988  // A loop to delete external halo nodes
8989  for (std::map<unsigned,Vector<Node*> >::iterator it=
8990  External_halo_node_pt.begin();it!=External_halo_node_pt.end();it++)
8991  {
8992  // Processor ID
8993  int d=(*it).first;
8994 
8995  unsigned n_ext_halo_nod=nexternal_halo_node(d);
8996  for (unsigned j=0;j<n_ext_halo_nod;j++)
8997  {
8998  // Only delete if it's not a node stored in the current mesh
8999  bool is_a_mesh_node=false;
9000  unsigned n_node=nnode();
9001  for (unsigned jj=0;jj<n_node;jj++)
9002  {
9003  if (Node_pt[jj]==External_halo_node_pt[d][j])
9004  {
9005  is_a_mesh_node=true;
9006  }
9007  }
9008 
9009  // There will also be duplications between multiple processors,
9010  // so make sure that we don't try to delete these twice
9011  if (!is_a_mesh_node)
9012  {
9013  // Loop over all other higher-numbered processors and check
9014  // for duplicated external halo nodes
9015  // (The highest numbered processor should delete all its ext halos)
9016  for (std::map<unsigned,Vector<Node*> >::iterator itt=
9017  External_halo_node_pt.begin();itt!=External_halo_node_pt.end();
9018  itt++)
9019  {
9020  // Processor ID
9021  int dd=(*itt).first;
9022 
9023  if (dd>d)
9024  {
9025  unsigned n_ext_halo=nexternal_halo_node(dd);
9026  for (unsigned jjj=0;jjj<n_ext_halo;jjj++)
9027  {
9028  if (External_halo_node_pt[dd][jjj]==External_halo_node_pt[d][j])
9029  {
9030  is_a_mesh_node=true;
9031  }
9032  }
9033  }
9034  }
9035  }
9036 
9037  // Only now if no duplicates exist can the node be safely deleted
9038  if (!is_a_mesh_node)
9039  {
9040  delete External_halo_node_pt[d][j];
9041  }
9042  }
9043  }
9044 
9045  // Another loop to delete external halo elements (which are distinct)
9046  for (std::map<unsigned,Vector<GeneralisedElement*> >::iterator it=
9047  External_halo_element_pt.begin();it!=External_halo_element_pt.end();it++)
9048  {
9049  // Processor ID
9050  int d=(*it).first;
9051 
9052  unsigned n_ext_halo_el=nexternal_halo_element(d);
9053  for (unsigned e=0;e<n_ext_halo_el;e++)
9054  {
9055  delete External_halo_element_pt[d][e];
9056  }
9057  }
9058 
9059  // Now we are okay to clear the external halo node storage
9060  External_halo_node_pt.clear();
9061  External_halo_element_pt.clear();
9062 
9063  // External haloed nodes and elements are actual members
9064  // of the external mesh and should not be deleted
9065  External_haloed_node_pt.clear();
9067 #endif
9068 
9069 }
9070 
9071 
9072 #ifdef OOMPH_HAS_MPI
9073 
9074 // NOTE: the add_external_haloed_node_pt and add_external_haloed_element_pt
9075 // functions need to check whether the Node/FiniteElement argument
9076 // has been added to the storage already; this is not the case
9077 // for the add_external_halo_node_pt and add_external_halo_element_pt
9078 // functions as these are newly-created elements that are created and
9079 // added to the storage based on the knowledge of when their haloed
9080 // counterparts were created and whether they were newly added
9081 
9082 //========================================================================
9083 /// \short Add external haloed element whose non-halo counterpart is held
9084 /// on processor p to the storage scheme for external haloed elements.
9085 /// If the element is already in the storage scheme then return its index
9086 //========================================================================
9087 unsigned Mesh::add_external_haloed_element_pt(const unsigned& p,
9088  GeneralisedElement*& el_pt)
9089 {
9090  // Loop over current storage
9091  unsigned n_extern_haloed=nexternal_haloed_element(p);
9092 
9093  // Is this already an external haloed element?
9094  bool already_external_haloed_element=false;
9095  unsigned external_haloed_el_index=0;
9096  for (unsigned eh=0;eh<n_extern_haloed;eh++)
9097  {
9098  if (el_pt==External_haloed_element_pt[p][eh])
9099  {
9100  // It's already there, so...
9101  already_external_haloed_element=true;
9102  // ...set the index of this element
9103  external_haloed_el_index=eh;
9104  break;
9105  }
9106  }
9107 
9108  // Has it been found?
9109  if (!already_external_haloed_element)
9110  {
9111  // Not found, so add it:
9112  External_haloed_element_pt[p].push_back(el_pt);
9113  // Return the index where it's just been added
9114  return n_extern_haloed;
9115  }
9116  else
9117  {
9118  // Return the index where it was found
9119  return external_haloed_el_index;
9120  }
9121 }
9122 
9123 //========================================================================
9124 /// \short Add external haloed node whose halo (external) counterpart
9125 /// is held on processor p to the storage scheme for external haloed nodes.
9126 /// If the node is already in the storage scheme then return its index
9127 //========================================================================
9128 unsigned Mesh::add_external_haloed_node_pt(const unsigned& p, Node*& nod_pt)
9129 {
9130  // Loop over current storage
9131  unsigned n_ext_haloed_nod=nexternal_haloed_node(p);
9132 
9133  // Is this already an external haloed node?
9134  bool is_an_external_haloed_node=false;
9135  unsigned external_haloed_node_index=0;
9136  for (unsigned k=0;k<n_ext_haloed_nod;k++)
9137  {
9138  if (nod_pt==External_haloed_node_pt[p][k])
9139  {
9140  is_an_external_haloed_node=true;
9141  external_haloed_node_index=k;
9142  break;
9143  }
9144  }
9145 
9146  // Has it been found?
9147  if (!is_an_external_haloed_node)
9148  {
9149  // Not found, so add it
9150  External_haloed_node_pt[p].push_back(nod_pt);
9151  // Return the index where it's just been added
9152  return n_ext_haloed_nod;
9153  }
9154  else
9155  {
9156  // Return the index where it was found
9157  return external_haloed_node_index;
9158  }
9159 }
9160 
9161 #endif
9162 
9163 
9164 //////////////////////////////////////////////////////////////////////
9165 //////////////////////////////////////////////////////////////////////
9166 // Functions for solid meshes
9167 //////////////////////////////////////////////////////////////////////
9168 //////////////////////////////////////////////////////////////////////
9169 
9170 
9171 //========================================================================
9172 /// Make the current configuration the undeformed one by
9173 /// setting the nodal Lagrangian coordinates to their current
9174 /// Eulerian ones
9175 //========================================================================
9177 {
9178 
9179  //Find out how many nodes there are
9180  unsigned long n_node = nnode();
9181 
9182  //Loop over all the nodes
9183  for(unsigned n=0;n<n_node;n++)
9184  {
9185  //Cast node to solid node (can safely be done because
9186  // SolidMeshes consist of SolidNodes
9187  SolidNode* node_pt = static_cast<SolidNode*>(Node_pt[n]);
9188 
9189  // Number of Lagrangian coordinates
9190  unsigned n_lagrangian = node_pt->nlagrangian();
9191 
9192  // Number of generalised Lagrangian coordinates
9193  unsigned n_lagrangian_type = node_pt->nlagrangian_type();
9194 
9195  //The assumption here is that there must be fewer lagrangian coordinates
9196  //than eulerian (which must be true?)
9197  // Set (generalised) Lagrangian coords = (generalised) Eulerian coords
9198  for(unsigned k=0;k<n_lagrangian_type;k++)
9199  {
9200  // Loop over lagrangian coordinates and set their values
9201  for(unsigned j=0;j<n_lagrangian;j++)
9202  {
9203  node_pt->xi_gen(k,j)=node_pt->x_gen(k,j);
9204  }
9205  }
9206  }
9207 
9208 }
9209 
9210 
9211 //=======================================================================
9212 /// Static problem that can be used to assign initial conditions
9213 /// on a given mesh.
9214 //=======================================================================
9216 
9217 
9218 ///////////////////////////////////////////////////////////////////////
9219 ///////////////////////////////////////////////////////////////////////
9220 ///////////////////////////////////////////////////////////////////////
9221 
9222 
9223 //=================================================================
9224 /// Namespace for paraview-style output helper functions
9225 //=================================================================
9226 namespace ParaviewHelper
9227 {
9228 
9229  /// Write the pvd file header
9230  void write_pvd_header(std::ofstream &pvd_file)
9231  {
9232  pvd_file
9233  << "<?xml version=\"1.0\"?>" << std::endl
9234  << "<VTKFile type=\"Collection\" version=\"0.1\">" << std::endl
9235  << "<Collection>"<< std::endl;
9236  }
9237 
9238  /// \short Add name of output file and associated continuous time
9239  /// to pvd file.
9240  void write_pvd_information(std::ofstream &pvd_file,
9241  const std::string& output_filename,
9242  const double& time)
9243  {
9244  // Output the actual time values
9245  pvd_file
9246  << "<DataSet timestep=\""
9247  << time
9248  << "\" ";
9249 
9250  // Apparently this has to go in
9251  pvd_file << "part=\"0\" ";
9252 
9253  // Add the name of the file, so that the pvd file knows what it is called
9254  pvd_file
9255  << "file=\""
9256  << output_filename
9257  <<"\"/>" << std::endl;
9258  }
9259 
9260  /// Write the pvd file footer
9261  void write_pvd_footer(std::ofstream &pvd_file)
9262  {
9263  pvd_file
9264  << "</Collection>" << std::endl
9265  << "</VTKFile>";
9266  }
9267 
9268 }
9269 
9270 ////////////////////////////////////////////////////////////////
9271 ////////////////////////////////////////////////////////////////
9272 ////////////////////////////////////////////////////////////////
9273 
9274 
9275 
9276 
9277 
9278 
9279 }
unsigned nshared_node()
Total number of shared nodes in this Mesh.
Definition: mesh.h:1717
static bool Suppress_warning_about_empty_mesh_level_time_stepper_function
Boolean used to control warning about empty mesh level timestepper function.
Definition: mesh.h:246
void node_update(const bool &update_all_time_levels_for_new_node=false)
Broken assignment operator.
void assign_local_eqn_numbers(const bool &store_local_dof_pt)
Assign the local equation numbers in all elements If the boolean argument is true then also store poi...
Definition: mesh.cc:702
unsigned nboundary_element(const unsigned &b) const
Return number of finite elements that are adjacent to boundary b.
Definition: mesh.h:852
A Generalised Element class.
Definition: elements.h:76
virtual void distribute(OomphCommunicator *comm_pt, const Vector< unsigned > &element_domain, Vector< GeneralisedElement *> &deleted_element_pt, DocInfo &doc_info, const bool &report_stats, const bool &overrule_keep_as_halo_element_status)
Distribute the problem and doc; make this virtual to allow overloading for particular meshes where fu...
Definition: mesh.cc:4620
Node * haloed_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th haloed node in this Mesh whose halo counterpart is held on processor p...
Definition: mesh.h:1673
void add_shared_node_pt(const unsigned &p, Node *&nod_pt)
Add shared node whose counterpart is held on processor p to the storage scheme for shared nodes...
Definition: mesh.h:1789
virtual void reset_boundary_element_info(Vector< unsigned > &ntmp_boundary_elements, Vector< Vector< unsigned > > &ntmp_boundary_elements_in_region, Vector< FiniteElement *> &deleted_elements)
Virtual function to perform the reset boundary elements info rutines.
Definition: mesh.h:297
void get_x(const Vector< double > &s, Vector< double > &x) const
Global coordinates as function of local coordinates. Either via FE representation or via macro-elemen...
Definition: elements.h:1841
virtual unsigned nscalar_paraview() const
Number of scalars/fields output by this element. Broken virtual. Needs to be implemented for each new...
Definition: elements.h:2838
virtual void local_coordinate_of_node(const unsigned &j, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size (broken virtual) ...
Definition: elements.h:1803
unsigned ndof_types() const
Return number of dof types in mesh.
Definition: mesh.cc:8395
std::map< unsigned, Vector< Node * > > Shared_node_pt
Definition: mesh.h:127
void write_pvd_footer(std::ofstream &pvd_file)
Write the pvd file footer.
Definition: mesh.cc:9261
Vector< GeneralisedElement * > halo_element_pt(const unsigned &p)
Return vector of halo elements in this Mesh whose non-halo counterpart is held on processor p...
Definition: mesh.h:1406
unsigned nboundary_element_in_region(const unsigned &b, const unsigned &r) const
Return the number of elements adjacent to boundary b in region r.
Vector< Node * > Node_pt
Vector of pointers to nodes.
Definition: mesh.h:194
std::map< unsigned, Vector< GeneralisedElement * > > Root_halo_element_pt
Map of vectors holding the pointers to the root halo elements.
Definition: mesh.h:111
Node *& boundary_node_pt(const unsigned &b, const unsigned &n)
Return pointer to node n on boundary b.
Definition: mesh.h:497
unsigned nexternal_halo_element()
Total number of external halo elements in this Mesh.
Definition: mesh.h:1874
std::map< unsigned, Vector< Node * > > Haloed_node_pt
Map of vectors holding the pointers to the haloed nodes.
Definition: mesh.h:120
void convert_to_boundary_node(Node *&node_pt, const Vector< FiniteElement *> &finite_element_pt)
A function that upgrades an ordinary node to a boundary node We shouldn&#39;t ever really use this...
Definition: mesh.cc:2317
std::map< unsigned, Vector< GeneralisedElement * > > External_halo_element_pt
Map of vectors holding the pointers to the external halo elements.
Definition: mesh.h:137
void add_boundary_node(const unsigned &b, Node *const &node_pt)
Add a (pointer to) a node to the b-th boundary.
Definition: mesh.cc:246
bool is_doc_enabled() const
Are we documenting?
void resize_halo_nodes()
Helper function that resizes halo nodes to the same size as their non-halo counterparts if required...
Definition: mesh.cc:4106
bool does_pointer_correspond_to_value(double *const &parameter_pt)
Check whether the pointer parameter_pt addresses internal data values.
Definition: nodes.cc:533
static Steady< 0 > Default_TimeStepper
Default Steady Timestepper, to be used in default arguments to Mesh constructors. ...
Definition: mesh.h:85
virtual void output(std::ostream &outfile)
Output the element data — typically the values at the nodes in a format suitable for post-processing...
Definition: elements.h:2884
void output_fct(std::ostream &outfile, const unsigned &n_plot, FiniteElement::SteadyExactSolutionFctPt)
Output a given Vector function at f(n_plot) points in each element.
Definition: mesh.cc:1936
HangInfo *const & hanging_pt() const
Return pointer to hanging node data (this refers to the geometric hanging node status) (const version...
Definition: nodes.h:1148
unsigned add_external_haloed_element_pt(const unsigned &p, GeneralisedElement *&el_pt)
Add external haloed element whose non-halo counterpart is held on processor p to the storage scheme f...
Definition: mesh.cc:9087
virtual ~Mesh()
Virtual Destructor to clean up all memory.
Definition: mesh.cc:588
void set_nonhanging()
Label node as non-hanging node by removing all hanging node data.
Definition: nodes.cc:2204
Information for documentation of results: Directory and file number to enable output in the form RESL...
virtual unsigned nplot_points_paraview(const unsigned &nplot) const
Return the number of actual plot points for paraview plot with parameter nplot. Broken virtual; can b...
Definition: elements.h:2712
void get_external_halo_node_pt(Vector< Node *> &external_halo_node_pt)
Get vector of pointers to all external halo nodes.
Definition: mesh.h:1971
std::string directory() const
Output directory.
cstr elem_len * i
Definition: cfortran.h:607
void add_root_haloed_element_pt(const unsigned &p, GeneralisedElement *&el_pt)
Add root haloed element whose non-halo counterpart is held on processor p to the storage scheme for h...
Definition: mesh.h:1638
void add_halo_node_pt(const unsigned &p, Node *&nod_pt)
Add halo node whose non-halo counterpart is held on processor p to the storage scheme for halo nodes...
Definition: mesh.h:1569
void shift_time_values()
Shift time-dependent data along for next timestep: Deal with nodal Data/positions and the element&#39;s i...
Definition: mesh.cc:2059
void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the elements. The ostream specifies the output stream to which...
Definition: mesh.cc:683
virtual void get_refinement_levels(unsigned &min_refinement_level, unsigned &max_refinement_level)
Get max/min refinement levels in mesh.
std::map< unsigned, Vector< Node * > > External_haloed_node_pt
Map of vectors holding the pointers to the external haloed nodes.
Definition: mesh.h:149
void add_root_halo_element_pt(const unsigned &p, GeneralisedElement *&el_pt)
Add root halo element whose non-halo counterpart is held on processor p to this Mesh.
Definition: mesh.h:1535
unsigned nexternal_halo_node()
Total number of external halo nodes in this Mesh.
Definition: mesh.h:1958
std::string & label()
String used (e.g.) for labeling output files.
bool Output_halo_elements
Bool for output of halo elements.
Definition: mesh.h:1686
A general Finite Element class.
Definition: elements.h:1274
virtual void scalar_value_paraview(std::ofstream &file_out, const unsigned &i, const unsigned &nplot) const
Write values of the i-th scalar field at the plot points. Broken virtual. Needs to be implemented for...
Definition: elements.h:2851
void(* UnsteadyExactSolutionFctPt)(const double &, const Vector< double > &, Vector< double > &)
Function pointer for function that computes Vector-valued time-dependent function as ...
Definition: elements.h:1729
void get_halo_node_stats(double &av_number, unsigned &max_number, unsigned &min_number)
Get halo node stats for this distributed mesh: Average/max/min number of halo nodes over all processo...
Definition: mesh.cc:4514
virtual void set_mesh_level_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Function that can be used to set any additional timestepper data stored at the Mesh (as opposed to no...
Definition: mesh.cc:2132
char t
Definition: cfortran.h:572
int non_halo_proc_ID()
ID of processor ID that holds non-halo counterpart of halo node; negative if not a halo...
Definition: nodes.h:494
double const & master_weight(const unsigned &i) const
Return weight for dofs on i-th master node.
Definition: nodes.h:753
FiniteElement * boundary_element_pt(const unsigned &b, const unsigned &e) const
Return pointer to e-th finite element on boundary b.
Definition: mesh.h:814
Target checked_dynamic_cast(Source *x)
Runtime checked dynamic cast. This is the safe but slightly slower cast. Use it in any of these cases...
bool is_halo() const
Is this element a halo?
Definition: elements.h:1141
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
unsigned nmaster() const
Return the number of master nodes.
Definition: nodes.h:733
unsigned self_test()
Self-test: Check elements and nodes. Return 0 for OK.
Definition: mesh.cc:715
unsigned nodal_dimension() const
Return number of nodal dimension in mesh.
Definition: mesh.cc:8679
Vector< GeneralisedElement * > haloed_element_pt(const unsigned &p)
Return vector of haloed elements in this Mesh whose haloing counterpart is held on processor p...
Definition: mesh.h:1445
void output_paraview(std::ofstream &file_out, const unsigned &nplot) const
Paraview output – this outputs the coordinates at the plot points (for parameter nplot) to specified...
Definition: elements.h:2739
OomphInfo oomph_info
virtual void dump(std::ofstream &dump_file, const bool &use_old_ordering=true) const
Dump the data in the mesh into a file for restart.
Definition: mesh.cc:1027
void set_elemental_internal_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Set the timestepper associated with the internal data stored within elements in the meah...
Definition: mesh.cc:2262
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1207
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:448
unsigned nroot_haloed_element()
Total number of root haloed elements in this Mesh.
Definition: mesh.h:1584
void output_external_haloed_elements(std::ostream &outfile, const unsigned &n_plot=5)
Output all external haloed elements.
Definition: mesh.h:1844
e
Definition: cfortran.h:575
bool Lookup_for_elements_next_boundary_is_setup
Definition: mesh.h:98
bool Doc_comprehensive_timings
Global boolean to switch on comprehensive timing – can probably be declared const false when develop...
void get_efficiency_of_mesh_distribution(double &av_efficiency, double &max_efficiency, double &min_efficiency)
Get efficiency of mesh distribution: In an ideal distribution without halo overhead, each processor would only hold its own elements. Efficieny per processor = (number of non-halo elements)/ (total number of elements).
Definition: mesh.cc:6050
double size() const
Definition: elements.cc:4207
void get_haloed_node_stats(double &av_number, unsigned &max_number, unsigned &min_number)
Get haloed node stats for this distributed mesh: Average/max/min number of haloed nodes over all proc...
Definition: mesh.cc:4569
double region_attribute(const unsigned &i)
Return the attribute associated with region i.
void read(std::ifstream &restart_file)
Read nodal positions (variable and fixed) and associated data from file for restart.
Definition: nodes.cc:3390
unsigned & number()
Number used (e.g.) for labeling output files.
void write_pvd_information(std::ofstream &pvd_file, const std::string &output_filename, const double &time)
Add name of output file and associated continuous time to pvd file.
Definition: mesh.cc:9240
void remove_boundary_node(const unsigned &b, Node *const &node_pt)
Definition: mesh.cc:222
void set_obsolete()
Mark node as obsolete.
Definition: nodes.h:1348
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:587
void output(std::ostream &outfile)
Output for all elements.
Definition: mesh.cc:1761
void assign_initial_values_impulsive()
Assign initial values for an impulsive start.
Definition: mesh.cc:2023
void add_node_pt(Node *const &node_pt)
Add a (pointer to a) node to the mesh.
Definition: mesh.h:602
void output_fct_paraview(std::ofstream &file_out, const unsigned &nplot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt) const
Output in paraview format into specified file. Breaks up each element into sub-elements for plotting ...
Definition: mesh.cc:1465
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:992
virtual void remove_from_boundary(const unsigned &b)
Broken interface for removing the node from the mesh boundary b Here to provide error reporting...
Definition: nodes.cc:2272
virtual void write_paraview_type(std::ofstream &file_out, const unsigned &nplot) const
Return the paraview element type. Broken virtual. Needs to be implemented for each new geometric elem...
Definition: elements.h:2814
virtual void write_paraview_offsets(std::ofstream &file_out, const unsigned &nplot, unsigned &offset_sum) const
Return the offsets for the paraview sub-elements. Broken virtual. Needs to be implemented for each ne...
Definition: elements.h:2826
bool must_be_kept_as_halo() const
Test whether the element must be kept as a halo element.
Definition: elements.h:1158
void set_consistent_pinned_positions(Node *const &node_pt)
Set consistent values of the derivatives and current value when the Nodes position is pinned...
void flush_element_storage()
Flush storage for elements (only) by emptying the vectors that store the pointers to them...
Definition: mesh.h:443
unsigned nodal_dimension() const
Return the required Eulerian dimension of the nodes in this element.
Definition: elements.h:2370
void calculate_predictions()
Calculate predictions for all Data and positions associated with the mesh, usually used in adaptive t...
Definition: mesh.cc:2098
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
double & xi(const unsigned &i)
Reference to i-th Lagrangian position.
Definition: nodes.h:1741
unsigned add_external_haloed_node_pt(const unsigned &p, Node *&nod_pt)
Add external haloed node whose halo (external) counterpart is held on processor p to the storage sche...
Definition: mesh.cc:9128
A class that contains the information required by Nodes that are located on Mesh boundaries. A BoundaryNode of a particular type is obtained by combining a given Node with this class. By differentiating between Nodes and BoundaryNodes we avoid a lot of un-necessary storage in the bulk Nodes.
Definition: nodes.h:1855
bool Keep_all_elements_as_halos
bool to indicate whether to keep all elements in a mesh as halos or not
Definition: mesh.h:152
void(* SteadyExactSolutionFctPt)(const Vector< double > &, Vector< double > &)
Function pointer for function that computes vector-valued steady "exact solution" as ...
Definition: elements.h:1723
double & xi_gen(const unsigned &k, const unsigned &i)
Reference to the generalised Lagrangian position. `Type&#39;: k; &#39;Coordinate direction: i...
Definition: nodes.h:1760
void check_inverted_elements(bool &mesh_has_inverted_elements, std::ofstream &inverted_element_file)
Check for inverted elements and report outcome in boolean variable. This visits all elements at their...
Definition: mesh.cc:802
void null_external_halo_node(const unsigned &p, Node *nod_pt)
Null out specified external halo node (used when deleting duplicates)
Definition: mesh.cc:8206
unsigned nhalo_node()
Total number of halo nodes in this Mesh.
Definition: mesh.h:1542
void read(std::ifstream &restart_file)
Read nodal position and associated data from file for restart.
Definition: nodes.cc:1919
std::map< unsigned, Vector< GeneralisedElement * > > Root_haloed_element_pt
Map of vectors holding the pointers to the root haloed elements.
Definition: mesh.h:114
std::map< unsigned, Vector< GeneralisedElement * > > External_haloed_element_pt
Map of vectors holding the pointers to the external haloed elements.
Definition: mesh.h:140
void set_nonhalo()
Label the node as not being a halo.
Definition: nodes.h:487
std::ostream *& stream_pt()
Access function for the stream pointer.
double dshape_eulerian(const Vector< double > &s, Shape &psi, DShape &dpsidx) const
Compute the geometric shape functions and also first derivatives w.r.t. global coordinates at local c...
Definition: elements.cc:3227
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition: elements.h:1908
bool is_mesh_distributed() const
Boolean to indicate if Mesh has been distributed.
Definition: mesh.h:1266
void copy(SolidNode *orig_node_pt)
Copy nodal positions and associated data from specified node object.
Definition: nodes.cc:3330
Base class for tree-based refineable meshes.
Node *& external_halo_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th external halo node in this Mesh whose non-halo external counterpart is held on...
Definition: mesh.h:2025
unsigned nexternal_haloed_element()
Total number of external haloed elements in this Mesh.
Definition: mesh.h:1918
virtual unsigned nsub_elements_paraview(const unsigned &nplot) const
Return the number of local sub-elements for paraview plot with parameter nplot. Broken virtual; can b...
Definition: elements.h:2726
virtual void create_shared_boundaries(OomphCommunicator *comm_pt, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement *> &backed_up_el_pt, const Vector< FiniteElement *> &backed_up_f_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status)
Creates the shared boundaries, only used in unstructured meshes In this case with the "TriangleMesh" ...
Definition: mesh.h:2134
unsigned nboundary() const
Return number of boundaries.
Definition: mesh.h:806
static char t char * s
Definition: cfortran.h:572
virtual void classify_halo_and_haloed_nodes(DocInfo &doc_info, const bool &report_stats)
Classify the halo and haloed nodes in the mesh. Virtual so it can be overloaded to perform additional...
Definition: mesh.cc:2984
unsigned nexternal_haloed_node()
Total number of external haloed nodes in this Mesh.
Definition: mesh.h:2060
static bool Suppress_output_while_checking_for_inverted_elements
Static boolean to suppress output while checking for inverted elements.
Definition: elements.h:1743
virtual double knot(const unsigned &i, const unsigned &j) const =0
Return local coordinate s[j] of i-th integration point.
Tree * tree_pt()
Access function: Pointer to quadtree representation of this element.
void set_value(const unsigned &i, const double &value_)
Set the i-th stored data value to specified value. The only reason that we require an explicit set fu...
Definition: nodes.h:267
Vector< Node * > prune_dead_nodes()
Prune nodes. Nodes that have been marked as obsolete are removed from the mesh (and its boundary-node...
Definition: mesh.cc:900
Data *& internal_data_pt(const unsigned &i)
Return a pointer to i-th internal data object.
Definition: elements.h:623
virtual void setup_tree_forest()=0
Set up the tree forest associated with the Mesh (if any)
Node * shared_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th shared node in this Mesh who has a counterpart on processor p...
Definition: mesh.h:1766
void position(Vector< double > &pos) const
Compute Vector of nodal positions either directly or via hanging node representation.
Definition: nodes.cc:2413
void check_jacobian(const double &jacobian) const
Helper function used to check for singular or negative Jacobians in the transform from local to globa...
Definition: elements.cc:1724
void output_boundaries(std::ostream &outfile)
Output the nodes on the boundaries (into separate tecplot zones)
Definition: mesh.cc:1001
void set_nodal_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Set the timestepper associated with the nodal data in the mesh.
Definition: mesh.cc:2244
virtual void write_paraview_output_offset_information(std::ofstream &file_out, const unsigned &nplot, unsigned &counter) const
Fill in the offset information for paraview plot. Broken virtual. Needs to be implemented for each ne...
Definition: elements.h:2801
void add_haloed_node_pt(const unsigned &p, Node *&nod_pt)
Add haloed node whose halo counterpart is held on processor p to the storage scheme for haloed nodes...
Definition: mesh.h:1680
void remove_null_pointers_from_external_halo_node_storage()
Consolidate external halo node storage by removing nulled out pointes in external halo and haloed sch...
Definition: mesh.cc:8226
unsigned nregion()
Return the number of regions specified by attributes.
void prune_halo_elements_and_nodes(Vector< GeneralisedElement *> &deleted_element_pt, const bool &report_stats=false)
(Irreversibly) prune halo(ed) elements and nodes, usually after another round of refinement, to get rid of excessively wide halo layers. Note that the current mesh will be now regarded as the base mesh and no unrefinement relative to it will be possible once this function has been called.
Definition: mesh.h:1333
virtual void get_node_reordering(Vector< Node *> &reordering, const bool &use_old_ordering=true) const
Get a reordering of the nodes in the order in which they appear in elements – can be overloaded for ...
Definition: mesh.cc:500
virtual std::string scalar_name_paraview(const unsigned &i) const
Name of the i-th scalar field. Default implementation returns V1 for the first one, V2 for the second etc. Can (should!) be overloaded with more meaningful names in specific elements.
Definition: elements.h:2877
Vector< GeneralisedElement * > Element_pt
Vector of pointers to generalised elements.
Definition: mesh.h:197
double & x_gen(const unsigned &k, const unsigned &i)
Reference to the generalised position x(k,i).
Definition: nodes.h:1055
Data *const & variable_position_pt() const
Pointer to variable_position data (const version)
Definition: nodes.h:1655
Node *& external_haloed_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th external haloed node in this Mesh whose halo external counterpart is held on p...
Definition: mesh.h:2087
void get_father_at_refinement_level(unsigned &refinement_level, RefineableElement *&father_at_reflevel_pt)
Return a pointer to the "father" element at the specified refinement level.
virtual void output_fct(std::ostream &outfile, const unsigned &n_plot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
Output an exact solution over the element.
Definition: elements.h:2938
void read(std::ifstream &restart_file)
Read data object from a file.
Definition: nodes.cc:635
unsigned refinement_level() const
Return the Refinement level.
double lagrangian_position(const unsigned &i) const
Return lagrangian coordinate either directly or via hanging node representation.
Definition: nodes.cc:3487
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:736
void check_halo_schemes(DocInfo &doc_info, double &max_permitted_error_for_halo_check)
Check halo and shared schemes on the mesh.
Definition: mesh.cc:6551
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
double timer()
returns the time in seconds after some point in past
unsigned nlagrangian_type() const
Number of types of Lagrangian coordinates used to interpolate the Lagrangian coordinates within the e...
Definition: nodes.h:1738
void dump(std::ostream &dump_file) const
Dump the data object to a file.
Definition: nodes.cc:608
unsigned nhaloed_node()
Total number of haloed nodes in this Mesh.
Definition: mesh.h:1645
std::map< unsigned, Vector< Node * > > Halo_node_pt
Map of vectors holding the pointers to the halo nodes.
Definition: mesh.h:117
Class that contains data for hanging nodes.
Definition: nodes.h:684
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition: mesh.h:456
GeneralisedTimestepper used to store the arclength derivatives and pervious solutions required in con...
void doc_mesh_distribution(DocInfo &doc_info)
Doc the mesh distribution, to be processed with tecplot macros.
Definition: mesh.cc:6123
bool Resize_halo_nodes_not_required
Set this to true to suppress resizing of halo nodes (at your own risk!)
Definition: mesh.h:155
unsigned long nnode() const
Return number of nodes in the mesh.
Definition: mesh.h:590
unsigned ntstorage() const
Return total number of doubles stored per value to record time history of each value (one for steady ...
Definition: nodes.cc:847
std::map< unsigned, Vector< Node * > > External_halo_node_pt
Map of vectors holding the pointers to the external halo nodes.
Definition: mesh.h:146
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:477
void merge_meshes(const Vector< Mesh *> &sub_mesh_pt)
Merge meshes. Note: This simply merges the meshes&#39; elements and nodes (ignoring duplicates; no bounda...
Definition: mesh.cc:69
void copy(Node *orig_node_pt)
Copy all nodal data from specified Node object.
Definition: nodes.cc:1834
void set_halo(const unsigned &non_halo_proc_ID)
Label the node as halo and specify processor that holds non-halo counterpart.
Definition: nodes.h:481
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition: elements.h:2482
virtual void node_update(const bool &update_all_solid_nodes=false)
Update nodal positions in response to changes in the domain shape. Uses the FiniteElement::get_x(...) function for FiniteElements and doesn&#39;t do anything for other element types. If a MacroElement pointer has been set for a FiniteElement, the MacroElement representation is used to update the nodal positions; if not get_x(...) uses the FE interpolation and thus leaves the nodal positions unchanged. Virtual, so it can be overloaded by specific meshes, such as AlgebraicMeshes or SpineMeshes. Generally, this function updates the position of all nodes in response to changes in the boundary position. However, we ignore all SolidNodes since their position is computed as part of the solution – unless the bool flag is set to true. Such calls are typically made when the initial mesh is created and/or after a mesh has been refined repeatedly before the start of the computation.
Definition: mesh.cc:290
unsigned ninternal_data() const
Return the number of internal data objects.
Definition: elements.h:828
A Class for nodes that deform elastically (i.e. position is an unknown in the problem). The idea is that the Eulerian positions are stored in a Data object and the Lagrangian coordinates are stored in addition. The pointer that addresses the Eulerian positions is set to the pointer to Value in the Data object. Hence, SolidNode uses knowledge of the internal structure of Data and must be a friend of the Data class. In order to allow a mesh to deform via an elastic-style equation in deforming-domain problems, the positions are stored separately from the values, so that elastic problems may be combined with any other type of problem.
Definition: nodes.h:1569
unsigned nroot_halo_element()
Total number of root halo elements in this Mesh.
Definition: mesh.h:1496
void stick_all_tree_nodes_into_vector(Vector< Tree * > &)
Traverse and stick pointers to all "nodes" into Vector.
Definition: tree.cc:276
Vector< Vector< Node * > > Boundary_node_pt
Vector of Vector of pointers to nodes on the boundaries: Boundary_node_pt(b,n). Note that this is pri...
Definition: mesh.h:94
void synchronise_shared_nodes(const bool &report_stats)
Synchronise shared node lookup schemes to cater for the the case where: (1) a certain node on the cur...
Definition: mesh.cc:2618
void resize(const unsigned &n_value)
Resize the number of equations.
Definition: nodes.cc:2089
virtual unsigned nweight() const =0
Return the number of integration points of the scheme.
void set_consistent_pinned_values_for_continuation(ContinuationStorageScheme *const &continuation_stepper_pt)
Set consistent values for pinned data in continuation.
Definition: mesh.cc:2167
void output_external_halo_elements(std::ostream &outfile, const unsigned &n_plot=5)
Output all external halo elements.
Definition: mesh.h:1814
virtual void get_elements_at_refinement_level(unsigned &refinement_level, Vector< RefineableElement *> &level_elements)
Extract the elements at a particular refinement level in the refinement pattern - used in Mesh::redis...
TimeStepper *& time_stepper_pt()
Return the pointer to the timestepper.
Definition: nodes.h:246
void set_consistent_pinned_values(Data *const &data_pt)
Set consistent values of the derivatives and current value when the data is pinned. This must be done by the "timestepper" because only it knows the local storage scheme.
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
unsigned check_for_repeated_nodes(const double &epsilon=1.0e-12)
Check for repeated nodes within a given spatial tolerance. Return (0/1) for (pass/fail).
Definition: mesh.h:737
bool does_pointer_correspond_to_mesh_data(double *const &parameter_pt)
Does the double pointer correspond to any mesh data.
Definition: mesh.cc:2201
void get_all_halo_data(std::map< unsigned, double *> &map_of_halo_data)
Get all the halo data stored in the mesh and add pointers to the data to the map, indexed by global e...
Definition: mesh.cc:4418
virtual void setup_boundary_element_info()
Interface for function that is used to setup the boundary information (Empty virtual function – impl...
Definition: mesh.h:288
static SolidICProblem Solid_IC_problem
Static problem that can be used to assign initial conditions on a given solid mesh (need to define th...
Definition: mesh.h:2345
void add_element_pt(GeneralisedElement *const &element_pt)
Add a (pointer to) an element to the mesh.
Definition: mesh.h:605
virtual void reorder_nodes(const bool &use_old_ordering=true)
Re-order nodes in the order in which they appear in elements – can be overloaded for more efficient ...
Definition: mesh.cc:483
bool node_global_position_comparison(Node *nd1_pt, Node *nd2_pt)
Definition: mesh.h:2599
bool is_halo() const
Is this Data a halo?
Definition: nodes.h:490
unsigned elemental_dimension() const
Return number of elemental dimension in mesh.
Definition: mesh.cc:8539
virtual void scalar_value_fct_paraview(std::ofstream &file_out, const unsigned &i, const unsigned &nplot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt) const
Write values of the i-th scalar field at the plot points. Broken virtual. Needs to be implemented for...
Definition: elements.h:2863
const Vector< GeneralisedElement * > & element_pt() const
Return reference to the Vector of elements.
Definition: mesh.h:470
void describe_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the dofs of the Mesh. The ostream specifies the output stream to which the descr...
Definition: mesh.cc:648
RefineableElement * object_pt() const
Return the pointer to the object (RefineableElement) represented by the tree.
Definition: tree.h:102
unsigned long assign_global_eqn_numbers(Vector< double *> &Dof_pt)
Assign the global equation numbers in the Data stored at the nodes and also internal element Data...
Definition: mesh.cc:614
void add_internal_value_pt_to_map(std::map< unsigned, double *> &map_of_value_pt)
Add pointers to the internal data values to map indexed by the global equation number.
Definition: elements.cc:583
void flush_element_and_node_storage()
Flush storage for elements and nodes by emptying the vectors that store the pointers to them...
Definition: mesh.h:427
unsigned nlagrangian() const
Return number of lagrangian coordinates.
Definition: nodes.h:1734
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:2257
std::map< unsigned, unsigned > *& index_of_first_value_assigned_by_face_element_pt()
Return pointer to the map giving the index of the first face element value.
Definition: nodes.h:1910
void set_non_obsolete()
Mark node as non-obsolete.
Definition: nodes.h:1351
void stick_leaves_into_vector(Vector< Tree * > &)
Traverse tree and stick pointers to leaf "nodes" (only) into Vector.
Definition: tree.cc:255
bool is_leaf()
Return true if the tree is a leaf node.
Definition: tree.h:212
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
unsigned hang_code()
Code that encapsulates the hanging status of the node (incl. the geometric hanging status) as ...
Definition: nodes.h:1133
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
Definition: timesteppers.h:219
void delete_all_external_storage()
Wipe the storage for all externally-based elements.
Definition: mesh.cc:8816
OomphCommunicator * Comm_pt
Pointer to communicator – set to NULL if mesh is not distributed.
Definition: mesh.h:130
void set_lagrangian_nodal_coordinates()
Make the current configuration the undeformed one by setting the nodal Lagrangian coordinates to thei...
Definition: mesh.cc:9176
virtual void get_refinement_pattern(Vector< Vector< unsigned > > &to_be_refined)
Extract refinement pattern: Consider the hypothetical mesh obtained by truncating the refinement of t...
bool is_obsolete()
Test whether node is obsolete.
Definition: nodes.h:1354
unsigned uniform_refinement_level_when_pruned() const
Level to which the mesh was uniformly refined when it was pruned (const version)
double value(const unsigned &i) const
Return i-th value (dofs or pinned) at this node either directly or via hanging node representation...
Definition: nodes.cc:2328
IC problem for an elastic body discretised on a given (sub)-mesh. We switch the elements&#39; residuals a...
TreeRoot *& root_pt()
Return pointer to root of the tree.
Definition: tree.h:143
void remove_boundary_nodes()
Clear all pointers to boundary nodes.
Definition: mesh.cc:208
Node * halo_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th halo node in this Mesh whose non-halo counterpart is held on processor p...
Definition: mesh.h:1577
void output_paraview(std::ofstream &file_out, const unsigned &nplot) const
Output in paraview format into specified file. Breaks up each element into sub-elements for plotting ...
Definition: mesh.cc:1167
void setup_shared_node_scheme()
Setup shared node scheme.
Definition: mesh.cc:2438
void flush_object()
Flush the object represented by the tree.
Definition: tree.h:105
virtual void read(std::ifstream &restart_file)
Read solution from restart file.
Definition: mesh.cc:1070
void write_pvd_header(std::ofstream &pvd_file)
Write the pvd file header.
Definition: mesh.cc:9230
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
Definition: communicator.h:57