Example codes
and tutorials
The (Not-so) Quick Guide
List of tutorials/demo codes
Single-Physics Problems
Poisson
Adaptivity illustrated for Poisson
Advection-Diffusion
Unsteady heat equation
Linear wave equation
The Young-Laplace equation
Navier-Stokes
Free-surface Navier-Stokes
Axisymmetric Navier-Stokes
Solid mechanics
Beam structures
Shell structures
Multi-physics Problems
Fluid-structure interaction
Boussinesq convection
Steady thermoelasticity
Methods-based example codes and tutorials
Mesh generation
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
How to write a new element
How to write a new refineable element
Default nonlinear solvers -- the sequence of action functions
...
Documentation
FE theory and top-down discussion of the data structure
The (Not-so) Quick Guide
Comprehensive bottom-up discussion of the data structure
List of available structured and unstructured meshes
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
Coding conventions and C++ style
Creating documentation
Optimisation - robustness vs. "raw speed"
Linear vs. nonlinear problems
Storing shape functions
Changing the default "full" integration scheme
Disabling the ALE formulation of unsteady equations
C vs. C++ output
Different sparse assembly techniques and the STL memory pool
Publications
Publications
Talks
Journal publications
Theses
Picture show
Download
Copyright
Download/installation instructions
Download page
FAQ & Contact
FAQ
Change log
Bugs and other known problems
Completeness of the library & our "To-Do List"
Contact the developers
Get involved

 


Beta release!

Please note that the library has not been "officially" released. While we continue to work on the documentation, these web pages are likely to contain broken links and documents in draft form. Please send an email to

oomph-lib AT maths DOT man DOT ac DOT uk

if you wish to be informed of the library's "official" release.

mesh.cc

Go to the documentation of this file.
00001 //LIC// ====================================================================
00002 //LIC// This file forms part of oomph-lib, the object-oriented, 
00003 //LIC// multi-physics finite-element library, available 
00004 //LIC// at http://www.oomph-lib.org.
00005 //LIC// 
00006 //LIC//           Version 0.90. August 3, 2009.
00007 //LIC// 
00008 //LIC// Copyright (C) 2006-2009 Matthias Heil and Andrew Hazel
00009 //LIC// 
00010 //LIC// This library is free software; you can redistribute it and/or
00011 //LIC// modify it under the terms of the GNU Lesser General Public
00012 //LIC// License as published by the Free Software Foundation; either
00013 //LIC// version 2.1 of the License, or (at your option) any later version.
00014 //LIC// 
00015 //LIC// This library is distributed in the hope that it will be useful,
00016 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 //LIC// Lesser General Public License for more details.
00019 //LIC// 
00020 //LIC// You should have received a copy of the GNU Lesser General Public
00021 //LIC// License along with this library; if not, write to the Free Software
00022 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00023 //LIC// 02110-1301  USA.
00024 //LIC// 
00025 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
00026 //LIC// 
00027 //LIC//====================================================================
00028 //Non-inline member functions for general mesh classes
00029 #include<algorithm>
00030 
00031 #ifdef OOMPH_HAS_MPI
00032 #include "mpi.h"
00033 #endif
00034 
00035 //oomph-lib headers
00036 #include "oomph_utilities.h"
00037 #include "mesh.h"
00038 #include "problem.h"
00039 #include "elastic_problems.h"
00040 #include "refineable_mesh.h"
00041 
00042 namespace oomph
00043 {
00044 
00045 //======================================================
00046 /// The Steady Timestepper
00047 //======================================================
00048 Steady<0> Mesh::Default_TimeStepper;
00049 
00050 
00051 
00052 //=======================================================================
00053 /// Merge meshes.
00054 /// Note: This simply merges the meshes' elements and nodes (ignoring
00055 /// duplicates; no boundary information etc. is created). 
00056 //=======================================================================
00057  void Mesh::merge_meshes(const Vector<Mesh*>& sub_mesh_pt)
00058  {
00059   // No boundary lookup scheme is set up for the combined mesh
00060    Lookup_for_elements_next_boundary_is_setup=false;
00061 
00062    //Number of submeshes
00063    unsigned nsub_mesh=sub_mesh_pt.size();
00064    
00065    // Initialise element, node and boundary counters for global mesh
00066    unsigned long n_element=0;
00067    unsigned long n_node=0;
00068    unsigned n_bound=0;
00069    
00070    // Loop over submeshes and get total number of elements, nodes and
00071    // boundaries
00072    for(unsigned imesh=0;imesh<nsub_mesh;imesh++)
00073     {
00074      n_element += sub_mesh_pt[imesh]->nelement();
00075      n_node += sub_mesh_pt[imesh]->nnode();
00076      n_bound += sub_mesh_pt[imesh]->nboundary();
00077     }  
00078    
00079    // Reserve storage for element and node pointers 
00080    Element_pt.clear();
00081    Element_pt.reserve(n_element);
00082    Node_pt.clear();
00083    Node_pt.reserve(n_node);
00084 
00085    //Resize vector of vectors of nodes
00086    Boundary_node_pt.clear();
00087    Boundary_node_pt.resize(n_bound);
00088    
00089    // Sets of pointers to elements and nodes (to exlude duplicates -- they
00090    // shouldn't occur anyway but if they do, they must only be added
00091    // once in the global mesh to avoid trouble in the timestepping)
00092    std::set<GeneralisedElement*> element_set_pt;
00093    std::set<Node*> node_set_pt;
00094    
00095    //Counter for total number of boundaries in all the submeshes
00096    unsigned ibound_global=0;   
00097    //Loop over the number of submeshes 
00098    for(unsigned imesh=0;imesh<nsub_mesh;imesh++)
00099     {
00100      //Loop over the elements of the submesh and add to vector
00101      //duplicates are ignored
00102      unsigned nel_before=0;
00103      unsigned long n_element=sub_mesh_pt[imesh]->nelement();
00104      for (unsigned long e=0;e<n_element;e++)
00105       {
00106        GeneralisedElement* el_pt=sub_mesh_pt[imesh]->element_pt(e);
00107        element_set_pt.insert(el_pt);
00108        // Was it a duplicate?
00109        unsigned nel_now=element_set_pt.size();
00110        if (nel_now==nel_before)
00111         {
00112          std::ostringstream warning_stream;
00113          warning_stream <<"WARNING: " << std::endl
00114                         <<"Element " << e << " in submesh " << imesh 
00115                         <<" is a duplicate \n and was ignored when assembling" 
00116                         <<" combined mesh." << std::endl;
00117          OomphLibWarning(warning_stream.str(),
00118                          "Mesh::Mesh(const Vector<Mesh*>&)",
00119                          OOMPH_EXCEPTION_LOCATION);
00120         }
00121        else
00122         {
00123          Element_pt.push_back(el_pt);
00124         }
00125        nel_before=nel_now;
00126       }
00127      
00128      //Loop over the nodes of the submesh and add to vector
00129      //duplicates are ignored
00130      unsigned nnod_before=0;
00131      unsigned long n_node=sub_mesh_pt[imesh]->nnode();
00132      for (unsigned long n=0;n<n_node;n++)
00133       {
00134        Node* nod_pt=sub_mesh_pt[imesh]->node_pt(n);
00135        node_set_pt.insert(nod_pt);
00136        // Was it a duplicate?
00137        unsigned nnod_now=node_set_pt.size();
00138        if (nnod_now==nnod_before)
00139         {
00140          std::ostringstream warning_stream;
00141          warning_stream<<"WARNING: " << std::endl
00142                        <<"Node " << n << " in submesh " << imesh 
00143                        <<" is a duplicate \n and was ignored when assembling " 
00144                        << "combined mesh." << std::endl;
00145          OomphLibWarning(warning_stream.str(),
00146                          "Mesh::Mesh(const Vector<Mesh*>&)",
00147                          OOMPH_EXCEPTION_LOCATION);
00148         }
00149        else
00150         {
00151          Node_pt.push_back(nod_pt);
00152         }
00153        nnod_before=nnod_now;
00154       }
00155      
00156      //Loop over the boundaries of the submesh
00157      unsigned n_bound=sub_mesh_pt[imesh]->nboundary();
00158      for (unsigned ibound=0;ibound<n_bound;ibound++)
00159       {
00160        //Loop over the number of nodes on the boundary and add to the 
00161        //global vector
00162        unsigned long n_bound_node=sub_mesh_pt[imesh]->nboundary_node(ibound);
00163        for (unsigned long n=0;n<n_bound_node;n++)
00164         {
00165          Boundary_node_pt[ibound_global].push_back(
00166           sub_mesh_pt[imesh]->boundary_node_pt(ibound,n));
00167         }
00168        //Increase the number of the global boundary counter
00169        ibound_global++;
00170       }
00171     } //End of loop over submeshes
00172    
00173   }
00174  
00175 
00176 //========================================================
00177 /// Remove the information about nodes stored on the 
00178 /// b-th boundary of the mesh
00179 //========================================================
00180 void Mesh::remove_boundary_nodes(const unsigned &b)
00181 {
00182  //Loop over all the nodes on the boundary and call 
00183  //their remove_from_boundary function
00184  unsigned n_boundary_node = Boundary_node_pt[b].size();
00185  for(unsigned n=0;n<n_boundary_node;n++)
00186   {
00187    boundary_node_pt(b,n)->remove_from_boundary(b);
00188   }
00189  //Clear the storage
00190  Boundary_node_pt[b].clear();
00191 }
00192 
00193 //=================================================================
00194 /// Remove all information about mesh boundaries
00195 //================================================================
00196 void Mesh::remove_boundary_nodes()
00197 {
00198  //Loop over each boundary call remove_boundary_nodes
00199  unsigned n_bound = Boundary_node_pt.size();
00200  for(unsigned b=0;b<n_bound;b++) {remove_boundary_nodes(b);}
00201  //Clear the storage
00202  Boundary_node_pt.clear();
00203 }          
00204 
00205 //============================================================
00206 /// Remove the node node_pt from the b-th boundary of the mesh
00207 /// This function also removes the information from the Node
00208 /// itself
00209 //===========================================================
00210 void Mesh::remove_boundary_node(const unsigned &b, Node* const &node_pt)
00211 {
00212  //Find the location of the node in the boundary
00213  Vector<Node*>::iterator it =
00214   std::find(Boundary_node_pt[b].begin(),
00215             Boundary_node_pt[b].end(),
00216             node_pt);
00217  //If the node is on this boundary
00218  if(it!=Boundary_node_pt[b].end())
00219   {
00220    //Remove the node from the mesh's list of boundary nodes
00221    Boundary_node_pt[b].erase(it);
00222   //Now remove the node's boundary information
00223    node_pt->remove_from_boundary(b);
00224   }
00225  //If not do nothing
00226 }
00227 
00228 
00229 //========================================================
00230 /// Add the node node_pt to the b-th boundary of the mesh
00231 /// This function also sets the boundary information in the
00232 /// Node itself
00233 //=========================================================
00234 void Mesh::add_boundary_node(const unsigned &b, Node* const &node_pt)
00235 {
00236  //Tell the node that it's on boundary b.
00237  //At this point, if the node is not a BoundaryNode, the function
00238  //should throw an exception.
00239  node_pt->add_to_boundary(b);
00240  
00241  //Get the size of the Boundary_node_pt vector
00242  unsigned nbound_node=Boundary_node_pt[b].size();
00243  bool node_already_on_this_boundary=false;
00244  //Loop over the vector
00245  for (unsigned n=0; n<nbound_node; n++)
00246   {
00247    // is the current node here already?
00248    if (node_pt==Boundary_node_pt[b][n])
00249     { 
00250      node_already_on_this_boundary=true;
00251     }
00252   }
00253 
00254  //Add the base node pointer to the vector if it's not there already
00255  if (!node_already_on_this_boundary)
00256   {
00257    Boundary_node_pt[b].push_back(node_pt); 
00258   }
00259  
00260 }
00261 
00262 //=======================================================
00263 /// Update nodal positions in response to changes in the domain shape.
00264 /// Uses the FiniteElement::get_x(...) function for FiniteElements
00265 /// and doesn't do anything for other element types. \n\n 
00266 /// If a MacroElement pointer has been set for a FiniteElement,
00267 /// the MacroElement representation is used to update the
00268 /// nodal positions; if not get_x(...) uses the FE interpolation
00269 /// and thus leaves the nodal positions unchanged.
00270 /// Virtual, so it can be overloaded by specific meshes,
00271 /// such as AlgebraicMeshes or SpineMeshes. \n\n
00272 /// Generally, this function updates the position of all nodes
00273 /// in response to changes in the boundary position. For
00274 /// SolidNodes it only applies the update to those SolidNodes
00275 /// whose position is determined by the boundary position, unless
00276 /// the bool flag is set to true.
00277 //========================================================
00278 void Mesh::node_update(const bool& update_all_solid_nodes)
00279 {
00280  /// Local and global (Eulerian) coordinate
00281  Vector<double> s;
00282  Vector<double> r;
00283 
00284  // NB: This repeats nodes a lot - surely it would be 
00285  // quicker to modify it so that it only does each node once,
00286  // particularly in the update_all_solid_nodes=true case?
00287 
00288  // Loop over all elements 
00289  unsigned nel=nelement();
00290  for (unsigned e=0;e<nel;e++)
00291   {
00292    // Try to cast to FiniteElement
00293    FiniteElement* el_pt = dynamic_cast<FiniteElement*>(element_pt(e));
00294    
00295    // If it's a finite element we can proceed: FiniteElements have
00296    // nodes and a get_x() function
00297    if (el_pt!=0)
00298     {
00299      // Find out dimension of element = number of local coordinates
00300      unsigned ndim_el=el_pt->dim();
00301      s.resize(ndim_el);
00302 
00303      //Loop over nodal points
00304      unsigned n_node=el_pt->nnode();
00305      for (unsigned j=0;j<n_node;j++)
00306       {
00307 
00308        // Get pointer to node
00309        Node* nod_pt=el_pt->node_pt(j);
00310 
00311        // Get spatial dimension of node
00312        unsigned ndim_node=nod_pt->ndim();
00313        r.resize(ndim_node);
00314 
00315        // For non-hanging nodes
00316        if (!(nod_pt->is_hanging()))
00317         {
00318          //Get the position of the node
00319          el_pt->local_coordinate_of_node(j,s);
00320 
00321          // Get new position
00322          el_pt->get_x(s,r);
00323 
00324          // Try to cast to SolidNode
00325          SolidNode* solid_node_pt=dynamic_cast<SolidNode*>(nod_pt);
00326 
00327          // Loop over coordinate directions
00328          for (unsigned i=0;i<ndim_node;i++)
00329           {
00330            // It's a SolidNode:
00331            if (solid_node_pt!=0)
00332             {
00333              // Update nodal positon (only if it's pinned -- otherwise
00334              // the nodal position is determined by the equations of
00335              // solid mechanics)
00336              if (update_all_solid_nodes||
00337                  (solid_node_pt->position_is_pinned(i)&&
00338                   !solid_node_pt->is_hanging() ) )
00339               {
00340                solid_node_pt->x(i) = r[i];
00341               }
00342             }
00343            // Not a SolidNode: Update regardless
00344            else
00345             {
00346              nod_pt->x(i) = r[i];
00347             }
00348           }
00349         }
00350       }
00351     }
00352   }
00353 
00354  // Now loop over hanging nodes and adjust their position
00355  // in line with their hanging node constraints
00356  unsigned long n_node = nnode();
00357  for(unsigned long n=0;n<n_node;n++)
00358   {
00359    Node* nod_pt = Node_pt[n];
00360    if (nod_pt->is_hanging())
00361     {
00362      // Get spatial dimension of node
00363      unsigned ndim_node=nod_pt->ndim();
00364 
00365      // Initialise
00366      for (unsigned i=0;i<ndim_node;i++)
00367       {
00368        nod_pt->x(i)=0.0;
00369       }
00370      
00371      //Loop over master nodes
00372      unsigned nmaster=nod_pt->hanging_pt()->nmaster();
00373      for (unsigned imaster=0;imaster<nmaster;imaster++)
00374       {
00375        // Loop over directions
00376        for (unsigned i=0;i<ndim_node;i++)
00377         {
00378          nod_pt->x(i)+=nod_pt->hanging_pt()->
00379           master_node_pt(imaster)->x(i)*
00380           nod_pt->hanging_pt()->master_weight(imaster);
00381         }
00382       }
00383     }
00384   }
00385  
00386  // Loop over all nodes again and execute auxiliary node update
00387  // function
00388  for(unsigned long n=0;n<n_node;n++)
00389   {
00390    Node_pt[n]->perform_auxiliary_node_update_fct();
00391   }
00392 
00393 }
00394 
00395 
00396 //=======================================================
00397 /// Reorder nodes in the order in which they are
00398 /// encountered when stepping through the elements
00399 //========================================================
00400 void Mesh::reorder_nodes()
00401 {
00402 
00403  // Setup map to check if nodes have been done yet
00404  std::map<Node*,bool> done;
00405  
00406  // Loop over all nodes
00407  unsigned nnod=nnode();
00408  for (unsigned j=0;j<nnod;j++)
00409   {
00410    done[node_pt(j)]=false;
00411   }
00412 
00413  // Initialise counter for number of nodes
00414  unsigned long count=0;
00415 
00416  // Loop over all elements
00417  unsigned nel=nelement();
00418  for (unsigned e=0;e<nel;e++)
00419   {
00420    // Loop over nodes in element
00421    unsigned nnod=finite_element_pt(e)->nnode();
00422    for (unsigned j=0;j<nnod;j++)
00423     {
00424      Node* nod_pt=finite_element_pt(e)->node_pt(j);
00425      // Has node been done yet?
00426      if (!done[nod_pt])
00427       {
00428        // Insert into node vector
00429        Node_pt[count]=nod_pt;
00430        done[nod_pt]=true;
00431        // Increase counter
00432        count++;
00433       }
00434     }
00435   }
00436 
00437  // Sanity check
00438  if (count!=nnod)
00439   {
00440    throw OomphLibError(
00441     "Trouble: Number of nodes hasn't stayed constant during reordering!\n",
00442     "Mesh::reorder_nodes()",
00443     OOMPH_EXCEPTION_LOCATION);
00444   }
00445 
00446 }
00447 
00448 //========================================================
00449 /// Flush storage for elements and nodes by emptying the
00450 /// vectors that store the pointers to them. This is
00451 /// useful if a particular mesh is only built to generate
00452 /// a small part of a bigger mesh. Once the elements and
00453 /// nodes have been created, they are typically copied
00454 /// into the new mesh and the auxiliary mesh can be
00455 /// deleted. However, if we simply call the destructor
00456 /// of the auxiliary mesh, it will also wipe out
00457 /// the nodes and elements, because it still "thinks"
00458 /// it's in charge of these...
00459 //========================================================
00460 void Mesh::flush_element_and_node_storage()
00461 {
00462  //Clear vectors of pointers to the nodes and elements
00463  Node_pt.clear();
00464  Element_pt.clear();
00465 }
00466 
00467 //========================================================
00468 /// Virtual Destructor to clean up all memory
00469 //========================================================
00470 Mesh::~Mesh()
00471 {
00472  //Free the nodes first
00473  //Loop over the nodes in reverse order
00474  unsigned long Node_pt_range = Node_pt.size();
00475  for(unsigned long i=Node_pt_range;i>0;i--) 
00476   {
00477    delete Node_pt[i-1]; Node_pt[i-1] = 0;
00478   }
00479  //Free the elements
00480  //Loop over the elements in reverse order
00481  unsigned long Element_pt_range = Element_pt.size();
00482  for(unsigned long i=Element_pt_range;i>0;i--) 
00483   {
00484    delete Element_pt[i-1]; Element_pt[i-1] = 0;
00485   }
00486 }
00487 
00488 //========================================================
00489 /// Assign (global) equation numbers to the nodes
00490 //========================================================
00491 unsigned long Mesh::assign_global_eqn_numbers(Vector<double *> &Dof_pt)
00492 {
00493  //Find out the current number of equations
00494  unsigned long equation_number=Dof_pt.size();
00495 
00496  //Loop over the nodes and call their assigment functions
00497  unsigned long Node_pt_range = Node_pt.size();
00498  for(unsigned long i=0;i<Node_pt_range;i++)
00499   {
00500    Node_pt[i]->assign_eqn_numbers(equation_number,Dof_pt);
00501   }
00502 
00503  //Loop over the elements and number their internals
00504  unsigned long Element_pt_range = Element_pt.size();
00505  for(unsigned long i=0;i<Element_pt_range;i++)
00506   {  
00507    Element_pt[i]->assign_internal_eqn_numbers(equation_number,Dof_pt);
00508   }
00509 
00510  //Return the total number of equations
00511  return(equation_number);
00512 }
00513 
00514 //========================================================
00515 /// Assign local equation numbers in all elements
00516 //========================================================
00517 void Mesh::assign_local_eqn_numbers()
00518 { 
00519  //Now loop over the elements and assign local equation numbers
00520  unsigned long Element_pt_range = Element_pt.size();
00521  for(unsigned long i=0;i<Element_pt_range;i++)
00522   {
00523    Element_pt[i]->assign_local_eqn_numbers();
00524   }
00525 }
00526 
00527 //========================================================
00528 /// Self-test: Check elements and nodes. Return 0 for OK
00529 //========================================================
00530 unsigned Mesh::self_test()
00531 { 
00532 
00533  // Initialise
00534  bool passed=true;
00535 
00536  // Check the mesh for repeated nodes (issues its own error message)
00537  if (0!=check_for_repeated_nodes()) passed=false;
00538 
00539  //Loop over the elements, check for duplicates and do self test
00540  std::set<GeneralisedElement*> element_set_pt;
00541  unsigned long Element_pt_range = Element_pt.size();
00542  for(unsigned long i=0;i<Element_pt_range;i++)
00543   {  
00544    if (Element_pt[i]->self_test()!=0)
00545     {
00546      passed=false;
00547      oomph_info << "\n ERROR: Failed Element::self_test() for element i=" 
00548                << i << std::endl;
00549     }
00550    // Add to set (which ignores duplicates):
00551    element_set_pt.insert(Element_pt[i]);
00552   }
00553 
00554  // Check for duplicates:
00555  if (element_set_pt.size()!=Element_pt_range)
00556   {
00557    oomph_info << "ERROR:  " << Element_pt_range-element_set_pt.size() 
00558              << " duplicate elements were encountered in mesh!" << std::endl;
00559    passed=false;
00560   }
00561  
00562  
00563  //Loop over the nodes, check for duplicates and do self test
00564  std::set<Node*> node_set_pt;
00565  unsigned long Node_pt_range = Node_pt.size();
00566  for(unsigned long i=0;i<Node_pt_range;i++)
00567   {
00568    if (Node_pt[i]->self_test()!=0)
00569     {
00570      passed=false;
00571      oomph_info << "\n ERROR: Failed Node::self_test() for node i=" 
00572                << i << std::endl;
00573     }
00574    // Add to set (which ignores duplicates):
00575    node_set_pt.insert(Node_pt[i]);
00576   }
00577 
00578  // Check for duplicates:
00579  if (node_set_pt.size()!=Node_pt_range)
00580   {
00581    oomph_info << "ERROR:  " << Node_pt_range-node_set_pt.size() 
00582              << " duplicate nodes were encountered in mesh!" << std::endl;
00583    passed=false;
00584   }
00585 
00586  // Return verdict
00587  if (passed) {return 0;}
00588  else {return 1;}
00589 
00590 }
00591 
00592 
00593 //========================================================
00594 /// Nodes that have been marked as obsolete are removed
00595 /// from the mesh and the its boundaries
00596 //========================================================
00597 void Mesh::prune_dead_nodes()
00598 {
00599  // Only copy the 'live' nodes across to new mesh
00600  //----------------------------------------------
00601  // New Vector of pointers to nodes
00602  Vector<Node *> new_node_pt;
00603 
00604  // Loop over all nodes in mesh 
00605  unsigned long n_node = nnode();
00606  for(unsigned long n=0;n<n_node;n++)
00607   {
00608    // If the node still exists: Copy across
00609    if(!(Node_pt[n]->is_obsolete())) {new_node_pt.push_back(Node_pt[n]);}
00610    // Otherwise the Node is gone: 
00611    // Delete it for good if it does not lie on a boundary
00612    else {if(Node_pt[n]->is_on_boundary()==false) {delete Node_pt[n];}}
00613   }
00614  
00615  // Now update old vector by setting it equal to the new vector
00616  Node_pt = new_node_pt;
00617  
00618  //---BOUNDARIES
00619  Vector<double> bnd_coords;
00620  // Only copy the 'live' nodes into new boundary node arrays
00621  //---------------------------------------------------------
00622  //Loop over the boundaries
00623  unsigned num_bound = nboundary();
00624  for(unsigned ibound=0;ibound<num_bound;ibound++)
00625   {
00626    // New Vector of pointers to existent boundary nodes 
00627    Vector<Node *> new_boundary_node_pt;
00628 
00629    //Loop over the boundary nodes
00630    unsigned long Nboundary_node = Boundary_node_pt[ibound].size();
00631 
00632    // Reserve contiguous memory for new vector of pointers
00633    // Must be equal in size to the number of nodes or less
00634    new_boundary_node_pt.reserve(Nboundary_node);
00635 
00636    for(unsigned long n=0;n<Nboundary_node;n++)
00637     {
00638      // If node still exists: Copy across
00639      if (!(Boundary_node_pt[ibound][n]->is_obsolete()))
00640       {new_boundary_node_pt.push_back(Boundary_node_pt[ibound][n]);}
00641        // Otherwise Node is gone: Delete it for good
00642      else
00643       {
00644        //The node may lie on multiple boundaries, so remove the node 
00645        //from the current boundary
00646        //Remove the node from the bounadry
00647        Boundary_node_pt[ibound][n]->remove_from_boundary(ibound);
00648        //Now if the node is no longer on any boundaries, delete it
00649        if(!Boundary_node_pt[ibound][n]->is_on_boundary())
00650         {
00651          delete Boundary_node_pt[ibound][n];
00652         }
00653       }
00654     }
00655 
00656    //Update the old vector by setting it equal to the new vector
00657    Boundary_node_pt[ibound] = new_boundary_node_pt;
00658   
00659   } //End of loop over boundaries
00660 }
00661 
00662 
00663 
00664 
00665 //========================================================
00666 /// Output function for the mesh boundaries
00667 ///
00668 /// Loop over all boundaries and dump out the coordinates
00669 /// of the points on the boundary (in individual tecplot
00670 /// zones) 
00671 //========================================================
00672 void Mesh::output_boundaries(std::ostream &outfile)
00673 {
00674  //Loop over the boundaries
00675  unsigned num_bound = nboundary();
00676  for(unsigned long ibound=0;ibound<num_bound;ibound++)
00677   {
00678    unsigned nnod=Boundary_node_pt[ibound].size();
00679    if (nnod>0)
00680     {
00681      outfile << "ZONE T=\"boundary" << ibound << "\"\n";
00682      
00683      for (unsigned inod=0;inod<nnod;inod++)
00684       {
00685        Boundary_node_pt[ibound][inod]->output(outfile);
00686       }
00687     }
00688   }
00689 }
00690 
00691 
00692 
00693 //===================================================================
00694 /// Dump function for the mesh class.
00695 /// Loop over all nodes and elements and dump them
00696 //===================================================================
00697 void Mesh::dump(std::ofstream &dump_file)
00698 {
00699  // Find number of nodes
00700  unsigned long Node_pt_range = this->nnode(); 
00701    
00702  // Doc # of nodes
00703  dump_file << Node_pt_range << " # number of nodes " << std::endl;
00704    
00705  //Loop over all the nodes and dump their data
00706  for(unsigned long n=0;n<Node_pt_range;n++)
00707   {
00708    this->node_pt(n)->dump(dump_file);
00709   }
00710  
00711  // Loop over elements and deal with internal data
00712  unsigned n_element = this->nelement();
00713  for(unsigned e=0;e<n_element;e++)
00714   {
00715    GeneralisedElement* el_pt = this->element_pt(e);
00716    unsigned n_internal = el_pt->ninternal_data();
00717    if(n_internal > 0)
00718     {
00719      dump_file << n_internal 
00720                << " # number of internal Data items in element " 
00721                << e << std::endl;
00722      for(unsigned i=0;i<n_internal;i++)
00723       {
00724        el_pt->internal_data_pt(i)->dump(dump_file);
00725       }
00726     }
00727   }
00728 }
00729 
00730 
00731 //=======================================================
00732 /// Read solution from restart file
00733 //=======================================================
00734 void Mesh::read(std::ifstream &restart_file)
00735 {
00736  std::string input_string;
00737  
00738  //Read nodes
00739  
00740  // Find number of nodes
00741  unsigned long n_node = this->nnode(); 
00742  
00743  // Read line up to termination sign
00744  getline(restart_file,input_string,'#');
00745  
00746  // Ignore rest of line
00747  restart_file.ignore(80,'\n');
00748  
00749  // Check # of nodes:
00750  unsigned long check_n_node=atoi(input_string.c_str());
00751  if (check_n_node!=n_node)
00752   {
00753    std::ostringstream error_stream;
00754    error_stream << "The number of nodes allocated " << n_node 
00755                 << " is not the same as specified in the restart file "
00756                 << check_n_node << std::endl;
00757    
00758    throw OomphLibError(error_stream.str(),
00759                        "Mesh::read()",
00760                        OOMPH_EXCEPTION_LOCATION);
00761   }
00762  
00763  //Loop over the nodes
00764  for(unsigned long n=0;n<n_node;n++)
00765   {     
00766    /// Try to cast to elastic node 
00767    SolidNode* el_node_pt=dynamic_cast<SolidNode*>(
00768     this->node_pt(n));
00769    if (el_node_pt!=0)
00770     {
00771      el_node_pt->read(restart_file);
00772     }
00773    else
00774     {
00775      this->node_pt(n)->read(restart_file);
00776     }
00777   }
00778  
00779  // Read internal data of elements:
00780  //--------------------------------
00781  // Loop over elements and deal with internal data
00782  unsigned n_element = this->nelement();
00783  for (unsigned e=0;e<n_element;e++)
00784   {
00785    GeneralisedElement* el_pt = this->element_pt(e);
00786    unsigned n_internal=el_pt->ninternal_data();
00787    if (n_internal>0)
00788     {
00789      // Read line up to termination sign
00790      getline(restart_file,input_string,'#');
00791      
00792      // Ignore rest of line
00793      restart_file.ignore(80,'\n');
00794      
00795      // Check # of internals :
00796      unsigned long check_n_internal=atoi(input_string.c_str());
00797      if (check_n_internal!=n_internal)
00798       {
00799        std::ostringstream error_stream;
00800        error_stream << "The number of internal data  " << n_internal 
00801                     << " is not the same as specified in the restart file "
00802                     << check_n_internal << std::endl;
00803    
00804        throw OomphLibError(error_stream.str(),
00805                            "Mesh::read()",
00806                            OOMPH_EXCEPTION_LOCATION);
00807       }
00808      
00809      for (unsigned i=0;i<n_internal;i++)
00810       {
00811        el_pt->internal_data_pt(i)->read(restart_file);
00812       }
00813     }
00814   }
00815 }
00816 
00817 
00818 
00819 //========================================================
00820 /// Output function for the mesh class
00821 ///
00822 /// Loop over all elements and plot (i.e. execute
00823 /// the element's own output() function)
00824 //========================================================
00825 void Mesh::output(std::ostream &outfile)
00826 {
00827  //Loop over the elements and call their output functions
00828  //Assign Element_pt_range
00829  unsigned long Element_pt_range = Element_pt.size();
00830  for(unsigned long e=0;e<Element_pt_range;e++)
00831   {
00832    // Try to cast to FiniteElement
00833    FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
00834    if (el_pt==0)
00835     {
00836      oomph_info << "Can't execute output(...) for non FiniteElements" 
00837                << std::endl;
00838     }
00839    else
00840     {
00841 #ifdef OOMPH_HAS_MPI
00842      if (Output_halo_elements)
00843 #endif
00844       {
00845        el_pt->output(outfile);
00846       }
00847 #ifdef OOMPH_HAS_MPI
00848      else
00849       {
00850        if (!el_pt->is_halo())
00851         {
00852          el_pt->output(outfile);
00853         }
00854       }
00855 #endif
00856     }
00857   }
00858 }
00859 
00860 //========================================================
00861 /// Output function for the mesh class
00862 ///
00863 /// Loop over all elements and plot (i.e. execute
00864 /// the element's own output() function). Use
00865 /// n_plot plot points in each coordinate direction.
00866 //========================================================
00867 void Mesh::output(std::ostream &outfile, const unsigned &n_plot)
00868 {
00869  //Loop over the elements and call their output functions
00870  //Assign Element_pt_range
00871  unsigned long Element_pt_range = Element_pt.size();
00872 
00873  for(unsigned long e=0;e<Element_pt_range;e++)
00874   {
00875    // Try to cast to FiniteElement
00876    FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
00877    if (el_pt==0)
00878     {
00879      oomph_info << "Can't execute output(...) for non FiniteElements" 
00880                << std::endl;
00881     }
00882    else
00883     {
00884 #ifdef OOMPH_HAS_MPI
00885      if (Output_halo_elements)
00886 #endif
00887       {
00888        el_pt->output(outfile,n_plot);
00889       }
00890 #ifdef OOMPH_HAS_MPI
00891      else
00892       {
00893        if (!el_pt->is_halo())
00894         {
00895          el_pt->output(outfile,n_plot);
00896         }
00897       }
00898 #endif
00899     }
00900   }
00901 }
00902 
00903 
00904 
00905 
00906 
00907 //========================================================
00908 /// Output function for the mesh class
00909 ///
00910 /// Loop over all elements and plot (i.e. execute
00911 /// the element's own output() function)
00912 /// (C style output)
00913 //========================================================
00914 void Mesh::output(FILE* file_pt)
00915 {
00916  //Loop over the elements and call their output functions
00917  //Assign Element_pt_range
00918  unsigned long Element_pt_range = Element_pt.size();
00919  for(unsigned long e=0;e<Element_pt_range;e++)
00920   {
00921    // Try to cast to FiniteElement
00922    FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
00923    if (el_pt==0)
00924     {
00925      oomph_info << "Can't execute output(...) for non FiniteElements" 
00926                << std::endl;
00927     }
00928    else
00929     {
00930 #ifdef OOMPH_HAS_MPI
00931      if (Output_halo_elements)
00932 #endif
00933       {
00934        el_pt->output(file_pt);
00935       }
00936 #ifdef OOMPH_HAS_MPI
00937      else
00938       {
00939        if (!el_pt->is_halo())
00940         {
00941          el_pt->output(file_pt);
00942         }
00943       }
00944 #endif
00945     }
00946   }
00947 }
00948 
00949 //========================================================
00950 /// Output function for the mesh class
00951 ///
00952 /// Loop over all elements and plot (i.e. execute
00953 /// the element's own output() function). Use
00954 /// n_plot plot points in each coordinate direction.
00955 /// (C style output)
00956 //========================================================
00957 void Mesh::output(FILE* file_pt, const unsigned &n_plot)
00958 {
00959  //Loop over the elements and call their output functions
00960  //Assign Element_pt_range
00961  unsigned long Element_pt_range = Element_pt.size();
00962  for(unsigned long e=0;e<Element_pt_range;e++)
00963   {
00964    // Try to cast to FiniteElement
00965    FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
00966    if (el_pt==0)
00967     {
00968      oomph_info << "Can't execute output(...) for non FiniteElements" 
00969                << std::endl;
00970     }
00971    else
00972     {
00973 #ifdef OOMPH_HAS_MPI
00974      if (Output_halo_elements)
00975 #endif
00976       {
00977        el_pt->output(file_pt,n_plot);
00978       }
00979 #ifdef OOMPH_HAS_MPI
00980      else
00981       {
00982        if (!el_pt->is_halo())
00983         {
00984          el_pt->output(file_pt,n_plot);
00985         }
00986       }
00987 #endif
00988     }
00989   }
00990 }
00991 
00992 
00993 //========================================================
00994 /// Output function for the mesh class
00995 ///
00996 /// Loop over all elements and plot (i.e. execute
00997 /// the element's own output() function). Use
00998 /// n_plot plot points in each coordinate direction.
00999 //========================================================
01000 void Mesh::output_fct(std::ostream &outfile, const unsigned &n_plot, 
01001                       FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
01002 {
01003  //Loop over the elements and call their output functions
01004  //Assign Element_pt_range
01005  unsigned long Element_pt_range = Element_pt.size();
01006  for(unsigned long e=0;e<Element_pt_range;e++)
01007   {
01008    // Try to cast to FiniteElement
01009    FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
01010    if (el_pt==0)
01011     {
01012      oomph_info << "Can't execute output_fct(...) for non FiniteElements" 
01013                << std::endl;
01014     }
01015    else
01016     {
01017 #ifdef OOMPH_HAS_MPI
01018      if (Output_halo_elements)
01019 #endif
01020       {
01021        el_pt->output_fct(outfile,n_plot,exact_soln_pt);
01022       }
01023 #ifdef OOMPH_HAS_MPI
01024      else
01025       {
01026        if (!el_pt->is_halo())
01027         {
01028          el_pt->output_fct(outfile,n_plot,exact_soln_pt);
01029         }
01030       }
01031 #endif
01032     }
01033   }
01034 }
01035 
01036 //========================================================
01037 /// Output function for the mesh class
01038 ///
01039 /// Loop over all elements and plot (i.e. execute
01040 /// the element's own output() function) at time t. Use
01041 /// n_plot plot points in each coordinate direction.
01042 //========================================================
01043 void Mesh::output_fct(std::ostream &outfile, const unsigned &n_plot, 
01044                       const double& time, 
01045                       FiniteElement::UnsteadyExactSolutionFctPt exact_soln_pt)
01046 {
01047  //Loop over the elements and call their output functions
01048  //Assign Element_pt_range
01049  unsigned long Element_pt_range = Element_pt.size();
01050  for(unsigned long e=0;e<Element_pt_range;e++)
01051   {
01052    // Try to cast to FiniteElement
01053    FiniteElement* el_pt=dynamic_cast<FiniteElement*>(Element_pt[e]);
01054    if (el_pt==0)
01055     {
01056      oomph_info << "Can't execute output_fct(...) for non FiniteElements" 
01057                << std::endl;
01058     }
01059    else
01060     {
01061 #ifdef OOMPH_HAS_MPI
01062      if (Output_halo_elements)
01063 #endif
01064       {
01065        el_pt->output_fct(outfile,n_plot,time,exact_soln_pt);
01066       }
01067 #ifdef OOMPH_HAS_MPI
01068      else
01069       {
01070        if (!el_pt->is_halo())
01071         {
01072          el_pt->output_fct(outfile,n_plot,time,exact_soln_pt);
01073         }
01074       }
01075 #endif
01076     }
01077   }
01078 }
01079 
01080 //==================================================================
01081 /// Assign the initial values for an impulsive start, which is 
01082 /// acheived by looping over all data in the mesh (internal element
01083 /// data and data stored at nodes) and setting the calling the 
01084 /// assign_initial_values_impulsive() function for each data's 
01085 /// timestepper
01086 //=================================================================
01087 void Mesh::assign_initial_values_impulsive()
01088 {
01089  //Loop over the elements
01090  unsigned long Nelement=nelement();
01091  for(unsigned long e=0;e<Nelement;e++)
01092   {
01093    //Find the number of internal dofs
01094    unsigned Ninternal = element_pt(e)->ninternal_data();
01095    //Loop over internal dofs and shift the time values
01096    //using the internals data's timestepper
01097    for(unsigned j=0;j<Ninternal;j++)
01098     {
01099      element_pt(e)->internal_data_pt(j)->time_stepper_pt()->
01100       assign_initial_values_impulsive(element_pt(e)->internal_data_pt(j));
01101     }
01102   }
01103  
01104  //Loop over the nodes
01105  unsigned long n_node=nnode();
01106  for (unsigned long n=0;n<n_node;n++)
01107   {
01108    // Assign initial values using the Node's timestepper
01109    Node_pt[n]->time_stepper_pt()->
01110     assign_initial_values_impulsive(Node_pt[n]);
01111    // Assign initial positions using the Node's timestepper
01112    Node_pt[n]->position_time_stepper_pt()->
01113     assign_initial_positions_impulsive(Node_pt[n]);
01114   }
01115 
01116 }
01117 
01118 //===============================================================
01119 /// Shift time-dependent data along for next timestep:
01120 /// Again this is achieved by looping over all data and calling
01121 /// the functions defined in each data object's timestepper.
01122 //==============================================================
01123 void Mesh::shift_time_values()
01124 {
01125  // Loop over the elements which shift their internal data
01126  // via their own timesteppers
01127  unsigned long Nelement=nelement();
01128  for (unsigned long e=0;e<Nelement;e++)
01129   {
01130    //Find the number of internal dofs
01131    unsigned Ninternal = element_pt(e)->ninternal_data();
01132    //Loop over internal dofs and shift the time values
01133    //using the internals data's timestepper
01134    for(unsigned j=0;j<Ninternal;j++)
01135     {
01136      element_pt(e)->internal_data_pt(j)->time_stepper_pt()->
01137       shift_time_values(element_pt(e)->internal_data_pt(j));
01138     }
01139   }
01140  
01141  //Loop over the nodes
01142  unsigned long n_node=nnode();
01143  for (unsigned long n=0;n<n_node;n++)
01144   {
01145    // Shift the Data associated with the nodes with the Node's own 
01146    // timestepper
01147    Node_pt[n]->time_stepper_pt()->shift_time_values(Node_pt[n]);
01148    // Push history of nodal positions back
01149    Node_pt[n]->position_time_stepper_pt()->shift_time_positions(Node_pt[n]);
01150   }
01151 
01152 }
01153 
01154 //=========================================================================
01155 /// Calculate predictions for all Data and positions associated 
01156 /// with the mesh. This is usually only used for adaptive time-stepping
01157 /// when the comparison between a predicted value and the actual value
01158 /// is usually used to determine the change in step size. Again the
01159 /// loop is over all data in the mesh and individual timestepper functions
01160 /// for each data value are called.
01161 //=========================================================================
01162 void Mesh::calculate_predictions()
01163 {
01164  // Loop over the elements which shift their internal data
01165  // via their own timesteppers
01166  unsigned long Nelement=nelement();
01167  for (unsigned long e=0;e<Nelement;e++)
01168   {
01169    //Find the number of internal dofs
01170    unsigned Ninternal = element_pt(e)->ninternal_data();
01171    //Loop over internal dofs and calculate predicted positions
01172    //using the internals data's timestepper
01173    for(unsigned j=0;j<Ninternal;j++)
01174     {
01175      element_pt(e)->internal_data_pt(j)->time_stepper_pt()->
01176       calculate_predicted_values(element_pt(e)->internal_data_pt(j));
01177     }
01178   }
01179  
01180  //Loop over the nodes
01181  unsigned long n_node=nnode();
01182  for (unsigned long n=0;n<n_node;n++)
01183   {
01184    // Calculate the predicted values at the nodes
01185    Node_pt[n]->time_stepper_pt()->calculate_predicted_values(Node_pt[n]);
01186    //Calculate the predicted positions
01187    Node_pt[n]->position_time_stepper_pt()->
01188     calculate_predicted_positions(Node_pt[n]);
01189   }
01190 }
01191 
01192 //========================================================================
01193 /// A function that upgrades an ordinary node to a boundary node.
01194 /// All pointers to the node from the mesh's elements are found.
01195 /// and replaced by pointers to the new boundary node. If the node
01196 /// is present in the mesh's list of nodes, that pointer is also
01197 /// replaced. Finally, the pointer argument node_pt addresses the new
01198 /// node on return from the function.
01199 /// We shouldn't ever really use this, but it does make life that
01200 /// bit easier for the lazy mesh writer.
01201 //=======================================================================
01202 void Mesh::convert_to_boundary_node(Node* &node_pt)
01203 {
01204 
01205  //If the node is already a boundary node, then return straight away,
01206  //we don't need to do anything
01207  if (dynamic_cast<BoundaryNodeBase*>(node_pt)!=0)
01208   {
01209    return;
01210   }
01211 
01212  //Loop over all the elements in the mesh and find all those in which
01213  //the present node is referenced and the corresponding local node number
01214  //in those elements.
01215  
01216  //Storage for elements and local node number
01217  std::list<std::pair<unsigned long, int> > 
01218   list_of_elements_and_local_node_numbers;
01219  
01220  //Loop over all elements
01221  unsigned long n_element=this->nelement();
01222  for(unsigned long e=0;e<n_element;e++)
01223   {
01224    //Buffer the case when we have not yet filled up the element array
01225    //Unfortunately, we should not assume that the array has been filled
01226    //in a linear order, so we can't break out early.
01227    if(Element_pt[e]!=0)
01228     {
01229      //Find the local node number of the passed node
01230      int node_number = finite_element_pt(e)->get_node_number(node_pt);
01231      //If the node is present in the element, add it to our list and 
01232      //NULL out the local element entries
01233      if(node_number!=-1)
01234       {
01235        list_of_elements_and_local_node_numbers.insert(
01236         list_of_elements_and_local_node_numbers.end(),
01237         std::make_pair(e,node_number));
01238        //Null it out
01239        finite_element_pt(e)->node_pt(node_number)=0;
01240       }
01241     }
01242   } //End of loop over elements
01243 
01244  //If there are no entries in the list we are in real trouble
01245  if(list_of_elements_and_local_node_numbers.empty())
01246   {
01247    std::ostringstream error_stream;
01248    error_stream << "Node " << node_pt 
01249                 << " is not contained in any elements in the Mesh."
01250                 << std::endl
01251                 << "How was it created then?" << std::endl;
01252    
01253    throw OomphLibError(error_stream.str(),
01254                        "Mesh::convert_to_boundary_node()",
01255                        OOMPH_EXCEPTION_LOCATION);
01256   }
01257  
01258  
01259  //Create temporary storage for a pointer to the old node.
01260  //This is required because if we have passed a reference to the
01261  //first element that we find, constructing the new node
01262  //will over-write our pointer and we'll get segmentation faults.
01263  Node* old_node_pt = node_pt;
01264  
01265  //We now create the new node by using the first element in the list
01266  std::list<std::pair<unsigned long,int> >::iterator list_it
01267   = list_of_elements_and_local_node_numbers.begin();
01268  
01269  //Create a new boundary node, using the timestepper from the
01270  //original node
01271  Node* new_node_pt = finite_element_pt(list_it->first)
01272   ->construct_boundary_node(list_it->second,node_pt->time_stepper_pt());
01273 
01274   //Now copy all the information accross from the old node
01275  
01276  //Can we cast the node to a solid node
01277  SolidNode* solid_node_pt = dynamic_cast<SolidNode*>(new_node_pt);
01278  //If it's a solid node, do the casting
01279  if(solid_node_pt!=0)
01280   {
01281    solid_node_pt->copy(dynamic_cast<SolidNode*>(old_node_pt));
01282   }
01283  else
01284   {
01285    new_node_pt->copy(old_node_pt);
01286   }
01287 
01288  //Loop over all other elements in the list and set their pointers
01289  //to the new node
01290  for(++list_it; //Increment the iterator
01291      list_it!=list_of_elements_and_local_node_numbers.end();
01292      ++list_it)
01293   {
01294    finite_element_pt(list_it->first)->node_pt(list_it->second)
01295     = new_node_pt;
01296   }
01297  
01298  //Finally, find the position of the node in the global mesh
01299  Vector<Node*>::iterator it=
01300   std::find(Node_pt.begin(),Node_pt.end(),old_node_pt);
01301  
01302  //If it is in the mesh, update the pointer
01303  if(it!=Node_pt.end()) {*it = new_node_pt;}
01304    
01305  //Can now delete the old node
01306  delete old_node_pt;
01307   
01308  //Replace the passed pointer by a pointer to the new node
01309  //Note that in most cases, this will be wasted work because the node
01310  //pointer will either the pointer in the mesh's or an element's 
01311  //node_pt vector. Still assignment is quicker than an if to check this.
01312  node_pt = new_node_pt;
01313 
01314 }
01315 
01316 
01317 
01318 #ifdef OOMPH_HAS_MPI
01319 
01320 
01321 //========================================================================
01322 /// Classify all halo and haloed information in the mesh
01323 //========================================================================
01324 void Mesh::classify_halo_and_haloed_nodes(OomphCommunicator* comm_pt,
01325                                           DocInfo& doc_info,
01326                                           const bool& report_stats)
01327 {
01328  //Wipe existing storage schemes for halo(ed) nodes
01329  Halo_node_pt.clear();
01330  Haloed_node_pt.clear();
01331 
01332  // Storage for number of processors and current processor
01333  int n_proc=comm_pt->nproc();
01334  int my_rank=comm_pt->my_rank();
01335  MPI_Status status;
01336  
01337  // Determine which processors the nodes are associated with
01338  // and hence who's in charge
01339  std::map<Data*,std::set<unsigned> > processors_associated_with_data;
01340  std::map<Data*,unsigned> processor_in_charge;
01341  
01342  // Loop over all processors and associate any nodes on the halo
01343  // elements involved with that processor
01344  for (int domain=0;domain<n_proc;domain++) 
01345   {   
01346    // Get vector of halo elements by copy operation
01347    Vector<FiniteElement*> halo_elem_pt(this->halo_element_pt(domain));
01348    
01349    // Loop over halo elements associated with this adjacent domain
01350    unsigned nelem=halo_elem_pt.size();
01351    for (unsigned e=0;e<nelem;e++)
01352     {
01353      // Get element
01354      FiniteElement* el_pt=halo_elem_pt[e];
01355      
01356      //Loop over nodes
01357      unsigned nnod=el_pt->nnode();
01358      for (unsigned j=0;j<nnod;j++)
01359       {
01360        Node* nod_pt=el_pt->node_pt(j);
01361        // Associate node with this domain
01362        processors_associated_with_data[nod_pt].insert(domain);
01363 
01364        // Do the same if the node is solid
01365        SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
01366        if (solid_nod_pt!=0)
01367         {
01368          processors_associated_with_data[solid_nod_pt->variable_position_pt()].
01369           insert(domain);
01370         }
01371       }
01372     }
01373   }
01374  
01375  
01376  // Loop over all [non-halo] elements and associate their nodes
01377  // with current procesor
01378  unsigned nelem=this->nelement();
01379  for (unsigned e=0;e<nelem;e++)
01380   {
01381    FiniteElement* el_pt=this->finite_element_pt(e);
01382    
01383    // Only visit non-halos
01384    if (!el_pt->is_halo())
01385     {
01386      // Loop over nodes
01387      unsigned nnod=el_pt->nnode();
01388      for (unsigned j=0;j<nnod;j++)
01389       {
01390        Node* nod_pt=el_pt->node_pt(j);
01391 
01392        // Associate this node with current processor
01393        processors_associated_with_data[nod_pt].insert(my_rank);
01394 
01395        // do the same if we have a SolidNode
01396        SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
01397        if (solid_nod_pt!=0)
01398         {
01399          processors_associated_with_data
01400           [solid_nod_pt->variable_position_pt()].insert(my_rank);
01401         }
01402       }
01403     }
01404   }
01405 
01406  // At this point we need to "syncrhonise" the nodes on halo(ed) elements
01407  // so that the processors_associated_with_data agrees for the same node
01408  // on all processors [this is only a problem in 3D, where hanging nodes
01409  // on an edge which is at a junction between at least 3 processors do not
01410  // necessarily know that they should be associated with all of the processors
01411  // at the junction, on all of the processors]
01412  
01413  // Loop over all domains
01414  for (int d=0;d<n_proc;d++)
01415   {
01416    // Prepare vector to send/receive
01417    Vector<unsigned> processors_associated_with_data_on_other_proc;
01418 
01419    if (d!=my_rank)
01420     {
01421      // Communicate the processors associated with data on haloed elements
01422 
01423      // Get haloed elements
01424      Vector<FiniteElement*> haloed_elem_pt(this->haloed_element_pt(d));
01425 
01426      // Initialise counter for this haloed layer
01427      unsigned count_data=0;
01428 
01429      // Loop over haloed elements
01430      unsigned n_haloed_elem=haloed_elem_pt.size();
01431      for (unsigned e=0;e<n_haloed_elem;e++)
01432       {
01433        FiniteElement* haloed_el_pt=haloed_elem_pt[e];
01434        // Loop over nodes
01435        unsigned n_node=haloed_el_pt->nnode();
01436        for (unsigned j=0;j<n_node;j++)
01437         {
01438          Node* nod_pt=haloed_el_pt->node_pt(j);
01439 
01440          // Number of processors associated with this node
01441          unsigned n_assoc=processors_associated_with_data[nod_pt].size();
01442        
01443          // This number needs to be sent
01444          processors_associated_with_data_on_other_proc.push_back(n_assoc);
01445          count_data++;
01446 
01447          // Now add the process IDs associated to the vector to be sent
01448          std::set<unsigned> procs_set=processors_associated_with_data[nod_pt];
01449          for (std::set<unsigned>::iterator it=procs_set.begin();
01450               it!=procs_set.end();it++)
01451           {
01452            processors_associated_with_data_on_other_proc.push_back(*it);
01453            count_data++;
01454           }
01455         }
01456       }
01457 
01458      // Send the information
01459      MPI_Send(&count_data,1,MPI_INT,d,0,comm_pt->mpi_comm());
01460      if (count_data!=0)
01461       {
01462        MPI_Send(&processors_associated_with_data_on_other_proc[0],count_data,
01463                 MPI_INT,d,1,comm_pt->mpi_comm());
01464       }
01465     }
01466    else
01467     {
01468      // Receive the processors associated with data onto halo elements
01469      for (int dd=0;dd<n_proc;dd++)
01470       {
01471        if (dd!=my_rank) // (my_rank=d)
01472         {
01473          // We will be looping over the halo elements with process dd
01474          Vector<FiniteElement*> halo_elem_pt(this->halo_element_pt(dd));
01475          unsigned n_halo_elem=halo_elem_pt.size();
01476 
01477          unsigned count_data=0;
01478          MPI_Recv(&count_data,1,MPI_INT,dd,0,comm_pt->mpi_comm(),&status);
01479 
01480          if (count_data!=0)
01481           {
01482            processors_associated_with_data_on_other_proc.resize(count_data);
01483 
01484            MPI_Recv(&processors_associated_with_data_on_other_proc[0],
01485                     count_data,MPI_INT,dd,1,comm_pt->mpi_comm(),&status);
01486 
01487            // Reset counter and loop through nodes on halo elements
01488            count_data=0;
01489            for (unsigned e=0;e<n_halo_elem;e++)
01490             {
01491              FiniteElement* halo_el_pt=halo_elem_pt[e];
01492              unsigned n_node=halo_el_pt->nnode();
01493              for (unsigned j=0;j<n_node;j++)
01494               {
01495                Node* nod_pt=halo_el_pt->node_pt(j);
01496 
01497                // Get number of processors associated with data that was sent
01498                unsigned n_assoc=
01499                 processors_associated_with_data_on_other_proc[count_data];
01500                count_data++;
01501 
01502                for (unsigned i_assoc=0;i_assoc<n_assoc;i_assoc++)
01503                 {
01504                  // Get the process ID
01505                  unsigned sent_domain=
01506                   processors_associated_with_data_on_other_proc[count_data];
01507                  count_data++;
01508 
01509                  // Add it to this processor's list of IDs
01510                  processors_associated_with_data[nod_pt].insert(sent_domain);
01511 
01512                  // If the node is solid then add the ID to the solid data
01513                  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
01514                  if (solid_nod_pt!=0)
01515                   {
01516                    processors_associated_with_data
01517                     [solid_nod_pt->variable_position_pt()].insert(sent_domain);
01518                   }
01519                 }
01520               }
01521             }
01522           }
01523         }
01524       }
01525     }
01526   }
01527 
01528 
01529  // Loop over all nodes on the present processor and put the highest-numbered
01530  // processor associated with each node "in charge" of the node
01531  unsigned nnod=this->nnode();
01532  for (unsigned j=0;j<nnod;j++)
01533   {
01534    Node* nod_pt=this->node_pt(j);
01535 
01536    // Reset halo status of node to false
01537    nod_pt->is_halo()=false;
01538 
01539    // If it's a SolidNode then the halo status of the data 
01540    // associated with that must also be reset to false
01541    SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
01542    if (solid_nod_pt!=0)
01543     {
01544      solid_nod_pt->variable_position_pt()->is_halo()=false;
01545     }
01546 
01547    // Now put the highest-numbered one in charge
01548    unsigned proc_max=0;
01549    std::set<unsigned> procs_set=processors_associated_with_data[nod_pt];
01550    for (std::set<unsigned>::iterator it=procs_set.begin();
01551         it!=procs_set.end();it++)
01552     {
01553      if (*it>proc_max) proc_max=*it;
01554     }
01555    processor_in_charge[nod_pt]=proc_max;
01556 
01557    // Do the same if we have a SolidNode
01558    if (solid_nod_pt!=0)
01559     {
01560      // Now put the highest-numbered one in charge
01561      unsigned proc_max_solid=0;
01562      std::set<unsigned> procs_set_solid=processors_associated_with_data
01563       [solid_nod_pt->variable_position_pt()];
01564      for (std::set<unsigned>::iterator it=procs_set_solid.begin();
01565           it!=procs_set_solid.end();it++)
01566       {
01567        if (*it>proc_max_solid) proc_max_solid=*it;
01568       }
01569      processor_in_charge[solid_nod_pt->variable_position_pt()]=proc_max_solid;
01570     }
01571   }
01572 
01573  // Determine halo nodes. They are located on the halo
01574  // elements and the processor in charge differs from the
01575  // current processor
01576 
01577  // Only count nodes once (map is initialised to 0 = false)
01578  std::map<Node*,bool> done;
01579 
01580  // Loop over all processors
01581  for (int domain=0;domain<n_proc;domain++) 
01582   {
01583    // Get vector of halo elements by copy operation
01584    Vector<FiniteElement*> halo_elem_pt(this->halo_element_pt(domain));
01585 
01586    // Loop over halo elements associated with this adjacent domain
01587    unsigned nelem=halo_elem_pt.size();
01588 
01589    for (unsigned e=0;e<nelem;e++)
01590     {
01591      // Get element
01592      FiniteElement* el_pt=halo_elem_pt[e];
01593 
01594      //Loop over nodes
01595      unsigned nnod=el_pt->nnode();
01596      for (unsigned j=0;j<nnod;j++)
01597       {
01598        Node* nod_pt=el_pt->node_pt(j);
01599        
01600        // Have we done this node already?
01601        if (!done[nod_pt])
01602         {
01603          // Is the other processor/domain in charge of this node?
01604          int proc_in_charge=processor_in_charge[nod_pt];
01605 
01606          // To keep the order of the nodes consistent with that
01607          // in the haloed node lookup scheme, only 
01608          // allow it to be added when the current domain is in charge
01609          if (proc_in_charge==int(domain))
01610           {
01611            if (proc_in_charge!=my_rank)
01612             {
01613              // Add it as being halo node whose non-halo counterpart
01614              // is located on processor domain
01615              this->add_halo_node_pt(proc_in_charge,nod_pt);
01616 
01617              // The node itself needs to know it is a halo
01618              nod_pt->is_halo()=true;
01619 
01620              // If it's a SolidNode then the data associated with that
01621              // must also be halo
01622              SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
01623              if (solid_nod_pt!=0)
01624               {
01625                solid_nod_pt->variable_position_pt()->is_halo()=true;
01626               }
01627 
01628              // We're done with this node
01629              done[nod_pt]=true;
01630             }
01631           }
01632 
01633         }
01634 
01635       }
01636      // Now make sure internal data on halo elements is also halo
01637      unsigned nintern_data = el_pt->ninternal_data();
01638      for (unsigned iintern=0;iintern<nintern_data;iintern++)
01639       {
01640        el_pt->internal_data_pt(iintern)->is_halo()=true;
01641       }
01642     }
01643   }
01644 
01645  // Determine haloed nodes. They are located on the haloed
01646  // elements and the processor in charge is the current processor
01647  
01648  // Loop over processors that share haloes with the current one
01649  for (int domain=0;domain<n_proc;domain++) 
01650   {
01651    // Only count nodes once (map is initialised to 0 = false)
01652    std::map<Node*,bool> node_done;
01653   
01654    // Get vector of haloed elements by copy operation
01655    Vector<FiniteElement*> haloed_elem_pt(this->haloed_element_pt(domain));
01656 
01657    // Loop over haloed elements associated with this adjacent domain
01658    unsigned nelem=haloed_elem_pt.size();
01659 
01660    for (unsigned e=0;e<nelem;e++)
01661     {
01662      // Get element
01663      FiniteElement* el_pt=haloed_elem_pt[e];
01664 
01665      //Loop over nodes
01666      unsigned nnod=el_pt->nnode();
01667      for (unsigned j=0;j<nnod;j++)
01668       {
01669        Node* nod_pt=el_pt->node_pt(j);
01670        
01671        // Have we done this node already?
01672        if (!node_done[nod_pt])
01673         {
01674          // Is the current processor/domain in charge of this node?
01675          int proc_in_charge=processor_in_charge[nod_pt];
01676 
01677          if (proc_in_charge==my_rank)
01678           {
01679            // Add it as being haloed from specified domain
01680            this->add_haloed_node_pt(domain,nod_pt);
01681            // We're done with this node
01682            node_done[nod_pt]=true;
01683           }
01684         }
01685 
01686       }
01687     }
01688   }
01689 
01690  // Doc stats
01691  if (report_stats)
01692   {
01693    // Report total number of halo(ed) and shared nodes for this process
01694    oomph_info << "Processor " << my_rank 
01695               << " holds " << this->nnode() 
01696               << " nodes of which " << this->nhalo_node()
01697               << " are halo nodes \n while " << this->nhaloed_node()
01698               << " are haloed nodes, and " << this->nshared_node()
01699               << " are shared nodes." << std::endl;   
01700 
01701    // Report number of halo(ed) and shared nodes with each domain 
01702    // from the current process
01703    for (int iproc=0;iproc<n_proc;iproc++)
01704     {
01705      oomph_info << "With process " << iproc << ", there are " 
01706                 << this->nhalo_node(iproc) << " halo nodes, and " << std::endl
01707                 << this->nhaloed_node(iproc) << " haloed nodes, and " 
01708                 << this->nshared_node(iproc) << " shared nodes" << std::endl; 
01709     }
01710   }
01711 
01712 }
01713 
01714 
01715 
01716 
01717 
01718 //========================================================================
01719 /// Get halo node stats for this distributed mesh:
01720 /// Average/max/min number of halo nodes over all processors.
01721 /// \b Careful: Involves MPI Broadcasts and must therefore
01722 /// be called on all processors!
01723 //========================================================================
01724 void Mesh::get_halo_node_stats(OomphCommunicator* comm_pt,
01725                                double& av_number,
01726                                unsigned& max_number,
01727                                unsigned& min_number)
01728 {
01729  // Storage for number of processors and current processor
01730  int n_proc=comm_pt->nproc();
01731  int my_rank=comm_pt->my_rank();
01732 
01733  // Create vector to hold number of halo nodes
01734  Vector<int> nhalo_nodes(n_proc);
01735  
01736  // Stick own number of halo nodes into appropriate entry 
01737  nhalo_nodes[my_rank]=nhalo_node();
01738 
01739  // Gather information on root processor: First argument group
01740  // specifies what is to be sent (one int from each procssor, indicating
01741  // the number of dofs on it), the second group indicates where
01742  // the results are to be gathered (in rank order) on root processor.
01743  MPI_Gather(&nhalo_nodes[my_rank],1,MPI_INT,
01744             &nhalo_nodes[0],1, MPI_INT,
01745             0,comm_pt->mpi_comm());
01746 
01747  // Initialise stats
01748  av_number=0.0;
01749  int max=-1;
01750  int min=1000000000;
01751 
01752  if (my_rank==0)
01753   {
01754    for (int i=0;i<n_proc;i++)
01755     {
01756      av_number+=double(nhalo_nodes[i]);
01757      if (int(nhalo_nodes[i])>max) max=nhalo_nodes[i];
01758      if (int(nhalo_nodes[i])<min) min=nhalo_nodes[i];
01759 
01760     }  
01761    av_number/=double(n_proc); 
01762   }
01763 
01764  // Now broadcast the result back out
01765  MPI_Bcast(&max,1,MPI_INT,0,comm_pt->mpi_comm());
01766  MPI_Bcast(&min,1,MPI_INT,0,comm_pt->mpi_comm());
01767  MPI_Bcast(&av_number,1,MPI_DOUBLE,0,comm_pt->mpi_comm());
01768  
01769  max_number=max;
01770  min_number=min;  
01771 }
01772 
01773 
01774 //========================================================================
01775 /// Get haloed node stats for this distributed mesh:
01776 /// Average/max/min number of haloed nodes over all processors.
01777 /// \b Careful: Involves MPI Broadcasts and must therefore
01778 /// be called on all processors!
01779 //========================================================================
01780  void  Mesh::get_haloed_node_stats(OomphCommunicator* comm_pt,
01781                                    double& av_number,
01782                                    unsigned& max_number,
01783                                    unsigned& min_number)
01784 {
01785  // Storage for number of processors and current processor
01786  int n_proc=comm_pt->nproc();
01787  int my_rank=comm_pt->my_rank();
01788 
01789  // Create vector to hold number of haloed nodes
01790  Vector<int> nhaloed_nodes(n_proc);
01791  
01792  // Stick own number of haloed nodes into appropriate entry
01793  nhaloed_nodes[my_rank]=nhaloed_node();
01794 
01795  // Gather information on root processor: First argument group
01796  // specifies what is to be sent (one int from each procssor, indicating
01797  // the number of dofs on it), the second group indicates where
01798  // the results are to be gathered (in rank order) on root processor.
01799  MPI_Gather(&nhaloed_nodes[my_rank],1,MPI_INT,
01800             &nhaloed_nodes[0],1, MPI_INT,
01801             0,comm_pt->mpi_comm());
01802 
01803  // Initialise stats
01804  av_number=0.0;
01805  int max=-1;
01806  int min=1000000000;
01807 
01808  if (my_rank==0)
01809   {
01810    for (int i=0;i<n_proc;i++)
01811     {
01812      av_number+=double(nhaloed_nodes[i]);
01813      if (int(nhaloed_nodes[i])>max) max=nhaloed_nodes[i];
01814      if (int(nhaloed_nodes[i])<min) min=nhaloed_nodes[i];
01815 
01816     }  
01817    av_number/=double(n_proc); 
01818   }
01819 
01820  // Now broadcast the result back out
01821  MPI_Bcast(&max,1,MPI_INT,0,comm_pt->mpi_comm());
01822  MPI_Bcast(&min,1,MPI_INT,0,comm_pt->mpi_comm());
01823  MPI_Bcast(&av_number,1,MPI_DOUBLE,0,comm_pt->mpi_comm());
01824  
01825  max_number=max;
01826  min_number=min;  
01827 }
01828 
01829 //========================================================================
01830 /// Distribute the mesh
01831 //========================================================================
01832 void Mesh::distribute(OomphCommunicator* comm_pt,
01833                       const Vector<unsigned>& element_domain,
01834                       DocInfo& doc_info,
01835                       const bool& report_stats)
01836 { 
01837  // Storage for number of processors and current processor
01838  int n_proc=comm_pt->nproc();
01839  int my_rank=comm_pt->my_rank();
01840 
01841  // Storage for number of elements and number of nodes on this mesh
01842  unsigned nelem=this->nelement();
01843  unsigned nnod=this->nnode();
01844 
01845  char filename[100];
01846 
01847  // Doc the partitioning (only on processor 0) 
01848  //-------------------------------------------
01849  if (doc_info.doc_flag())
01850   {
01851    if (my_rank==0)
01852     {
01853      // Open files for doc of element partitioning
01854      Vector<std::ofstream*> domain_file(n_proc);
01855      for (int d=0;d<n_proc;d++)
01856       {
01857        // Note: doc_info.number() was set in Problem::distribute(...) to
01858        // reflect the submesh number
01859        sprintf(filename,"%s/domain%i-%i.dat",doc_info.directory().c_str(),
01860                d,doc_info.number());
01861        domain_file[d]=new std::ofstream(filename);
01862       }
01863      
01864      // Doc
01865      for (unsigned e=0;e<nelem;e++)
01866       {
01867        this->finite_element_pt(e)->
01868         output(*domain_file[element_domain[e]],5);
01869       }
01870      
01871      for (int d=0;d<n_proc;d++)
01872       {
01873        domain_file[d]->close();
01874       }
01875     }
01876   }
01877 
01878  // Loop over all elements, associate all 
01879  //--------------------------------------
01880  // nodes with the highest-numbered processor and record all
01881  //---------------------------------------------------------
01882  // processors the node is associated with
01883  //---------------------------------------
01884 
01885  // Storage for processors in charge and processors associated with data
01886  std::map<Data*,std::set<unsigned> > processors_associated_with_data;
01887  std::map<Data*,unsigned> processor_in_charge;
01888 
01889  // For all nodes set the processor in charge to zero
01890  for (unsigned j=0;j<nnod;j++)
01891   {
01892    Node* nod_pt=this->node_pt(j);
01893    processor_in_charge[nod_pt]=0;
01894   }
01895 
01896  // Loop over elements
01897  for (unsigned e=0;e<nelem;e++)
01898   {
01899    // Get element and its domain
01900    FiniteElement* el_pt=this->finite_element_pt(e);
01901    unsigned el_domain=element_domain[e];
01902 
01903    // Associate nodes with highest numbered processor
01904    unsigned nnod=el_pt->nnode();
01905    for (unsigned j=0;j<nnod;j++)
01906     {
01907      Node* nod_pt=el_pt->node_pt(j); 
01908 
01909      // processor in charge was initialised to 0 above
01910      if (el_domain>processor_in_charge[nod_pt])
01911       {
01912        processor_in_charge[nod_pt]=el_domain;
01913       }
01914      processors_associated_with_data[nod_pt].insert(el_domain);
01915     }
01916   }
01917 
01918  // Doc the partitioning (only on processor 0) 
01919  //-------------------------------------------
01920  if (doc_info.doc_flag())
01921   {
01922    if (my_rank==0)
01923     {
01924      // Open files for doc of node partitioning
01925      Vector<std::ofstream*> node_file(n_proc);
01926      for (int d=0;d<n_proc;d++)
01927       {
01928        // Note: doc_info.number() was set in Problem::distribute(...) to
01929        // reflect the submesh number...
01930        sprintf(filename,"%s/node%i-%i.dat",doc_info.directory().c_str(),
01931                d,doc_info.number());
01932        node_file[d]=new std::ofstream(filename);
01933       }
01934      
01935      // Doc
01936      for (unsigned j=0;j<nnod;j++)
01937       {
01938        Node* nod_pt=this->node_pt(j);
01939        *node_file[processor_in_charge[nod_pt]]
01940         << nod_pt->x(0) << " " 
01941         << nod_pt->x(1) << std::endl;
01942       }
01943      for (int d=0;d<n_proc;d++)
01944       {
01945        node_file[d]->close();
01946       }
01947     }
01948   }
01949 
01950  // Declare all nodes as obsolete. We'll
01951  // change this setting for all nodes that must be retained
01952  // further down
01953  for (unsigned j=0;j<nnod;j++)
01954   {
01955    this->node_pt(j)->set_obsolete();
01956   }
01957 
01958 
01959  // Backup old mesh data and flush mesh
01960  //-------------------------------------
01961 
01962  // Backup pointers to elements in this mesh
01963  Vector<FiniteElement*> backed_up_el_pt(nelem);
01964  for (unsigned e=0;e<nelem;e++)
01965   {
01966    backed_up_el_pt[e]=this->finite_element_pt(e);
01967   }
01968 
01969  // Backup pointers to nodes in this mesh
01970  Vector<Node*> backed_up_nod_pt(nnod);
01971  for (unsigned j=0;j<nnod;j++)
01972   {
01973    backed_up_nod_pt[j]=this->node_pt(j);
01974   }
01975 
01976  // Flush the mesh storage
01977  this->flush_element_and_node_storage();
01978 
01979  // Flush any storage of external elements and nodes
01980  this->flush_all_external_storage();
01981 
01982  // Now loop over the (backed up) elements and identify the ones
01983  //--------------------------------------------------------------
01984  // that must be retained. 
01985  //-----------------------
01986 
01987  // Boolean to indicate which element is to be retained on 
01988  // which processor. This is needed because elements have
01989  // to be added into the various halo/haloed lookup schemes in the
01990  // same order and we base this on the order in which
01991  // they were in the original mesh.
01992  Vector<std::vector<bool> > tmp_element_retained;
01993  tmp_element_retained.resize(n_proc);
01994  nelem=backed_up_el_pt.size();
01995  for (int i=0;i<n_proc;i++)
01996   {
01997    tmp_element_retained[i].resize(nelem,false);
01998   }
01999  
02000  // Temporary storage for root halo elements on the various
02001  // processors. Needed to figure out haloed lookup schemes:
02002  // When setting these up on any given processor we have to know
02003  // which elements will (have) become halo elements on other processors.
02004  Vector<Vector<Vector<FiniteElement*> > > tmp_root_halo_element_pt;
02005  tmp_root_halo_element_pt.resize(n_proc);
02006  for (int i=0;i<n_proc;i++)
02007   {
02008    tmp_root_halo_element_pt[i].resize(n_proc);
02009   }
02010 
02011  // Determine which elements are going to end up on which processor
02012  //----------------------------------------------------------------
02013 
02014  // This procedure needs to be repeated to catch elements which may
02015  // be missed the first time round but which contain nodes from this process
02016 
02017  unsigned elements_retained=true;
02018  int myi=1;
02019  while (elements_retained) 
02020   {
02021    Vector<unsigned> number_of_retained_elements(n_proc,0);
02022    int number_of_retained_halo_elements=0; // not dependent on dummy_my_rank
02023 
02024    for (int dummy_my_rank=0;dummy_my_rank<n_proc;dummy_my_rank++)
02025     {
02026      // Loop over all backed up elements
02027      nelem=backed_up_el_pt.size();
02028      for (unsigned e=0;e<nelem;e++)
02029       {
02030        // Get element and its domain
02031        FiniteElement* el_pt=backed_up_el_pt[e];
02032        unsigned el_domain=element_domain[e];
02033      
02034        // If element is located on current processor add it back to the mesh
02035        if (el_domain==unsigned(dummy_my_rank))
02036         {
02037          // Add element to current processor 
02038          tmp_element_retained[dummy_my_rank][e]=true;
02039          number_of_retained_elements[dummy_my_rank]++;
02040         }
02041        // Otherwise we may still need it if it's a halo element:
02042        else
02043         {       
02044          // Is one of the nodes associated with the current processor?
02045          unsigned nnod=el_pt->nnode();
02046          for (unsigned j=0;j<nnod;j++)
02047           {
02048            Node* nod_pt=el_pt->node_pt(j); 
02049 
02050            // Keep element?
02051            unsigned keep_it=false;
02052            for (std::set<unsigned>::iterator 
02053                  it=processors_associated_with_data[nod_pt].begin();
02054                 it!=processors_associated_with_data[nod_pt].end();
02055                 it++)
02056             {
02057              if (*it==unsigned(dummy_my_rank))
02058               {
02059                keep_it=true;
02060                break;
02061               }
02062             }
02063          
02064            // Add a root halo element either if keep_it=true OR this 
02065            // current mesh has been told to keep all elements as halos,
02066            // OR the element itself knows that it must be kept
02067            if ((keep_it) || (keep_all_elements_as_halos())
02068                || (el_pt->must_be_kept_as_halo()))
02069             {
02070              // Add as root halo element whose non-halo counterpart is
02071              // located on processor el_domain
02072              tmp_root_halo_element_pt[dummy_my_rank][el_domain].
02073               push_back(el_pt);
02074              tmp_element_retained[dummy_my_rank][e]=true;
02075              number_of_retained_elements[dummy_my_rank]++;
02076              break;
02077             }
02078           }
02079         }
02080       }
02081 
02082 
02083      // Now loop over all halo elements to check if any of their
02084      // nodes are located on a higher-numbered processor.
02085      // The elements associated with these must be added as halo elements, too
02086      std::map<Node*,bool> higher_numbered_node;
02087          
02088      // Loop over all domains
02089      for (int d=0;d<n_proc;d++)
02090       {  
02091        // Loop over root halo elements 
02092        unsigned nelem=tmp_root_halo_element_pt[dummy_my_rank][d].size();     
02093        for (unsigned e=0;e<nelem;e++)
02094         {
02095          FiniteElement* el_pt=tmp_root_halo_element_pt[dummy_my_rank][d][e];
02096        
02097          // Loop over its nodes
02098          unsigned nnod=el_pt->nnode();
02099          for (unsigned j=0;j<nnod;j++)
02100           {
02101            Node* nod_pt=el_pt->node_pt(j);
02102            int proc_in_charge=processor_in_charge[nod_pt];
02103            if (proc_in_charge>d) // too many halos if > changed to >=
02104             {
02105              higher_numbered_node[nod_pt]=true;
02106             }
02107            else
02108             {
02109              higher_numbered_node[nod_pt]=false;
02110             }
02111           }
02112         }
02113       }
02114 
02115      // Now loop over all the original elements again
02116      nelem=backed_up_el_pt.size();
02117      for (unsigned e=0;e<nelem;e++)
02118       {
02119        // Get element and its domain
02120        FiniteElement* el_pt=backed_up_el_pt[e];
02121        unsigned el_domain=element_domain[e];
02122      
02123        // By default, don't keep it
02124        bool keep_it=false;
02125      
02126        // Check if it's already retained
02127        if (!tmp_element_retained[dummy_my_rank][e])
02128         {
02129          // Loop over its nodes
02130          unsigned nnod=el_pt->nnode();
02131          for (unsigned j=0;j<nnod;j++)
02132           {
02133            Node* nod_pt=el_pt->node_pt(j);
02134            if (higher_numbered_node[nod_pt]&&
02135                (element_domain[e]==processor_in_charge[nod_pt]))
02136             {
02137              keep_it=true; 
02138              break; // doesn't help if the break is removed
02139             }
02140           }
02141          if (keep_it)
02142           {
02143            tmp_root_halo_element_pt[dummy_my_rank][el_domain].push_back(el_pt);
02144            tmp_element_retained[dummy_my_rank][e]=true;
02145            number_of_retained_elements[dummy_my_rank]++;
02146            number_of_retained_halo_elements++; // need to count these
02147           }
02148         }
02149 
02150       }
02151 
02152      if (report_stats)
02153       {
02154        // Check number of retained halo elements on this process
02155        if (number_of_retained_elements[dummy_my_rank]!=0)
02156         {
02157          oomph_info << "Percentage of extra halo elements retained: "
02158                     << 100.0*double(number_of_retained_halo_elements)/
02159           double(number_of_retained_elements[dummy_my_rank])
02160                     << " on process " << dummy_my_rank 
02161                     << " in loop number " << myi << std::endl;
02162         }
02163        else // Dummy output in case a process has no retained elements
02164             // (relevant in some multi-mesh problems)
02165         {
02166          oomph_info << "Percentage of extra halo elements retained: "
02167                     << 0.0 << " on process " << dummy_my_rank
02168                     << " in loop number " << myi << std::endl;
02169         }
02170       }
02171 
02172     } // end of loop over all "processors"; we've now established the
02173    // elements and the root halo elements for all processors
02174 
02175    int total_number_of_retained_halo_elements=0;
02176    
02177    // Sum values over all processes 
02178    // - must be zero retained in order to continue
02179    MPI_Allreduce(&number_of_retained_halo_elements, 
02180                  &total_number_of_retained_halo_elements,1,MPI_INT,
02181                  MPI_SUM,comm_pt->mpi_comm());
02182 
02183    if (report_stats)
02184     {
02185      oomph_info << "Total number of extra halo elements retained: " 
02186                 << total_number_of_retained_halo_elements
02187                 << " in loop: " << myi << std::endl;
02188     }
02189 
02190    if (total_number_of_retained_halo_elements==0) {elements_retained=false;} 
02191    
02192    myi++;
02193 
02194   } // end of while(elements_retained)
02195 
02196  // Copy the elements associated with the actual
02197  // current processor into its own permanent storage.
02198  // Do it in the order in which the elements appeared originally
02199  nelem=backed_up_el_pt.size();
02200  for (unsigned e=0;e<nelem;e++)
02201   {
02202    FiniteElement* el_pt=backed_up_el_pt[e];
02203    if (tmp_element_retained[my_rank][e])
02204     {
02205      this->add_element_pt(el_pt);
02206     }
02207    else
02208     {
02209      // Flush the object attached to the tree for this element?
02210      RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(el_pt);
02211      if (ref_el_pt!=0)
02212       {
02213        ref_el_pt->tree_pt()->flush_object();
02214       }
02215      // Delete the element
02216      delete el_pt;
02217     }
02218   }
02219  
02220  // Copy the root halo elements associated with the actual
02221  // current processor into its own permanent storage; the order
02222  // here is somewhat random but we compensate for that by
02223  // ensuring that the corresponding haloed elements are 
02224  // added in the same order below
02225  for (int d=0;d<n_proc;d++)
02226   {  
02227    nelem=tmp_root_halo_element_pt[my_rank][d].size();
02228    for (unsigned e=0;e<nelem;e++)
02229     {
02230      this->add_root_halo_element_pt(d,
02231       tmp_root_halo_element_pt[my_rank][d][e]);
02232     }
02233   }
02234   
02235 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02236 //  oomph_info << "....done " << elapsed_time << std::endl;
02237 
02238 //  oomph_info << "Determine root haloed elements....";
02239 
02240  // Determine root haloed elements
02241  //-------------------------------
02242 
02243  // Loop over all other processors
02244  for (int d=0;d<n_proc;d++)
02245   {
02246    if (d!=my_rank)
02247     {
02248      // Loop over root halo elements that are held on that processor
02249      unsigned nelem_other=tmp_root_halo_element_pt[d][my_rank].size();
02250      for (unsigned e2=0;e2<nelem_other;e2++)
02251       {
02252        // Loop over all elements on current processor.
02253        // We loop over these in the inner loop) to ensure that they are 
02254        // added to the haloed lookup scheme in the same
02255        // order in which elements were added to the
02256        // corresponding halo lookup scheme.
02257        nelem=this->nelement();
02258        for (unsigned e=0;e<nelem;e++)
02259         {
02260          // Get pointer to element
02261          FiniteElement* el_pt=this->finite_element_pt(e);
02262          
02263          // Halo elements can't be haloed themselves
02264          if (!el_pt->is_halo())
02265           {
02266            if (el_pt==tmp_root_halo_element_pt[d][my_rank][e2])
02267             {
02268              // Current element is haloed by other processor
02269              this->add_root_haloed_element_pt(d,el_pt);
02270              break;
02271             }
02272           }  
02273         }
02274       }
02275     }
02276   }
02277 
02278 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02279 //  oomph_info << "....done " << elapsed_time << std::endl;
02280 
02281 
02282  // Doc stats
02283  if (report_stats)
02284   {
02285    oomph_info << "Processor " << my_rank 
02286               << " holds " << this->nelement() 
02287               << " elements of which " << this->nroot_halo_element()
02288               << " are root halo elements \n while " 
02289               << this->nroot_haloed_element()
02290               << " are root haloed elements" << std::endl;
02291   }
02292 
02293 //  oomph_info << "Retain nodes....";
02294  
02295  // Loop over all retained elements and mark their nodes
02296  //-----------------------------------------------------
02297  // as to be retained too (some double counting going on here)
02298  //-----------------------------------------------------------
02299  nelem=this->nelement();
02300  for (unsigned e=0;e<nelem;e++)
02301   {
02302    FiniteElement* el_pt=this->finite_element_pt(e);
02303 
02304    // Loop over nodes
02305    unsigned nnod=el_pt->nnode();
02306    for (unsigned j=0;j<nnod;j++)
02307     {
02308      Node* nod_pt=el_pt->node_pt(j);
02309      nod_pt->set_non_obsolete();
02310     }
02311   }
02312 
02313 
02314  // Complete rebuild of mesh by adding retained nodes
02315  // Note that they are added in the order in which they 
02316  // occured in the original mesh as this guarantees the
02317  // synchronisity between the serialised access to halo
02318  // and haloed nodes from different processors.
02319  nnod=backed_up_nod_pt.size();
02320  for (unsigned j=0;j<nnod;j++)
02321   {
02322    Node* nod_pt=backed_up_nod_pt[j];
02323    if(!nod_pt->is_obsolete())
02324     {
02325      // Not obsolete so add it back to the mesh
02326      this->add_node_pt(nod_pt);
02327     }
02328   }
02329 
02330 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02331 //  oomph_info << "....done " << elapsed_time << std::endl;
02332 
02333 
02334  // Prune and rebuild mesh
02335  //-----------------------
02336 
02337 //  oomph_info << "Pruning dead nodes...";
02338 
02339  // Now remove the pruned nodes from the boundary lookup scheme
02340  this->prune_dead_nodes();
02341 
02342 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02343 //  oomph_info << "....done " << elapsed_time << std::endl;
02344 
02345 //  oomph_info << "Setup boundary info....";
02346 
02347  // And finally re-setup the boundary lookup scheme for elements
02348  this->setup_boundary_element_info();
02349 
02350 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02351 //  oomph_info << "....done " << elapsed_time << std::endl;
02352 
02353 //  oomph_info << "Recreate forest....";
02354 
02355  // Re-setup tree forest if needed
02356  if (this->nelement()>0)
02357   {
02358    RefineableMeshBase* ref_mesh_pt=dynamic_cast<RefineableMeshBase*>(this);
02359    if (ref_mesh_pt!=0)
02360     {
02361      ref_mesh_pt->setup_tree_forest();
02362     }
02363   }
02364 
02365 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02366 //  oomph_info << "....done " << elapsed_time << std::endl;
02367 
02368 //  oomph_info << "Classify nodes....";
02369 
02370  // Classify nodes 
02371  classify_halo_and_haloed_nodes(comm_pt,doc_info,report_stats);
02372 
02373 //  elapsed_time=double(clock()-t_start)/CLOCKS_PER_SEC; t_start=clock();
02374 //  oomph_info << "....done " << elapsed_time << std::endl;
02375 
02376  // Mesh has now been distributed 
02377  // (required for Z2ErrorEstimator::get_element_errors)
02378  Mesh_has_been_distributed=true;
02379 
02380  // Doc?
02381  //-----
02382  if (doc_info.doc_flag())
02383   {
02384    doc_mesh_distribution(comm_pt,doc_info);
02385   }
02386 
02387 
02388 }
02389 
02390 
02391 //========================================================================
02392 /// (Irreversibly) prune halo(ed) elements and nodes, usually
02393 /// after another round of refinement, to get rid of
02394 /// excessively wide halo layers. Note that the current
02395 /// mesh will be now regarded as the base mesh and no unrefinement
02396 /// relative to it will be possible once this function 
02397 /// has been called.
02398 //========================================================================
02399 void Mesh::prune_halo_elements_and_nodes(OomphCommunicator* comm_pt,
02400                                          DocInfo& doc_info,
02401                                          const bool& report_stats)
02402 {
02403 
02404  RefineableMeshBase* ref_mesh_pt=dynamic_cast<RefineableMeshBase*>(this);
02405  if (ref_mesh_pt!=0)
02406   {
02407 
02408 #ifdef OOMPH_HAS_MPI
02409    // Flush any external element storage before performing the redistribution
02410    // (in particular, external halo nodes that are on mesh boundaries)
02411    this->flush_all_external_storage();
02412 #endif
02413    
02414    // Storage for number of processors and current processor
02415    int n_proc=comm_pt->nproc();
02416    int my_rank=comm_pt->my_rank();
02417    
02418    // Doc stats
02419    if (report_stats)
02420     {
02421      oomph_info << "Before pruning: Processor " << my_rank 
02422                 << " holds " << this->nelement() 
02423                 << " elements of which " << this->nroot_halo_element()
02424                 << " are root halo elements \n while " 
02425                 << this->nroot_haloed_element()
02426                 << " are root haloed elements" << std::endl;
02427     }
02428    
02429    // Declare all nodes as obsolete. We'll
02430    // change this setting for all nodes that must be retained
02431    // further down
02432    unsigned nnod=this->nnode();
02433    for (unsigned j=0;j<nnod;j++)
02434     {
02435      this->node_pt(j)->set_obsolete();
02436     }
02437    
02438    // Backup pointers to elements in this mesh
02439    unsigned nelem=this->nelement();
02440    Vector<FiniteElement*> backed_up_el_pt(nelem);
02441    std::map<FiniteElement*,bool> keep_element;
02442    for (unsigned e=0;e<nelem;e++)
02443     {
02444      FiniteElement* el_pt=this->finite_element_pt(e);
02445      backed_up_el_pt[e]=el_pt;
02446     }
02447    
02448    // Get the min and max refinement level, and current refinement pattern
02449    unsigned min_ref=0;
02450    unsigned max_ref=0;
02451    
02452    // Skip this first bit if you have no elements 
02453    if (nelem>0)
02454     {
02455      // Get min and max refinement level
02456      ref_mesh_pt->get_refinement_levels(min_ref,max_ref);
02457 
02458      // Get refinement pattern
02459      Vector<Vector<unsigned> > current_refined;
02460      ref_mesh_pt->get_refinement_pattern(current_refined);
02461 
02462      // get_refinement_pattern refers to the elements at each level
02463      // that were refined when proceeding to the next level
02464      unsigned n_ref=current_refined.size();
02465 
02466      // Loop over all elements; keep those on the min refinement level
02467      // Need to go back to the level indicated by min_ref
02468      unsigned base_level=n_ref-(max_ref-min_ref);
02469 
02470 #ifdef PARANOID
02471      if (base_level<0)
02472       {
02473        std::ostringstream error_stream;
02474        error_stream  << "Error: the base level of refinement " 
02475                      << "is negative; this cannot be the case" << std::endl;
02476        throw OomphLibError(error_stream.str(),
02477                            "Mesh::prune_halo_elements_and_nodes(...)",
02478                            OOMPH_EXCEPTION_LOCATION);
02479       }
02480 #endif
02481 
02482      // Get the elements at the specified "base" refinement level
02483      Vector<RefineableElement*> base_level_elements_pt;
02484      ref_mesh_pt->get_elements_at_refinement_level(base_level,
02485                                                    base_level_elements_pt);
02486      unsigned n_base_el=base_level_elements_pt.size();
02487 
02488      // Loop over the elements at this level
02489      for (unsigned e=0;e<n_base_el;e++)
02490       {
02491        // Extract correct element...
02492        RefineableElement* ref_el_pt=base_level_elements_pt[e];
02493 
02494        // Check it exists
02495        if (ref_el_pt!=0)
02496         {
02497          // Keep all non-halo elements, remove excess halos
02498          if (!ref_el_pt->is_halo())
02499           {
02500            keep_element[ref_el_pt]=true;
02501 
02502            // Loop over this non-halo element's nodes and retain them too
02503            unsigned nnod=ref_el_pt->nnode();
02504            for (unsigned j=0;j<nnod;j++)
02505             {
02506              ref_el_pt->node_pt(j)->set_non_obsolete();
02507             }
02508           }
02509         }
02510       } // end loop over base level elements
02511     }
02512 
02513    // Now work on which "root" halo elements to keep at this level
02514    // Can't use the current set directly; however, 
02515    // we know the refinement level of the current halo, so
02516    // it is possible to go from that backwards to find the "father
02517    // halo element" necessary to complete this step
02518 
02519    // Temp map of vectors holding the pointers to the root halo elements
02520    std::map<unsigned, Vector<FiniteElement*> > tmp_root_halo_element_pt;
02521 
02522    // Temp map of vectors holding the pointers to the root haloed elements
02523    std::map<unsigned, Vector<FiniteElement*> > tmp_root_haloed_element_pt;
02524  
02525    // Map to store if a halo element survives
02526    std::map<FiniteElement*,bool> halo_element_is_retained;
02527 
02528    for (int domain=0;domain<n_proc;domain++)
02529     {
02530      // Get vector of halo elements with processor domain by copy operation
02531      Vector<FiniteElement*> halo_elem_pt(this->halo_element_pt(domain));
02532      
02533      // Loop over halo elements associated with this adjacent domain
02534      unsigned nelem=halo_elem_pt.size();
02535      for (unsigned e=0;e<nelem;e++)
02536       {
02537        // Get element
02538        RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
02539         (halo_elem_pt[e]);
02540        
02541        // An element should only be kept if its refinement
02542        // level is the same as the minimum refinement level
02543        unsigned halo_el_level=ref_el_pt->refinement_level();
02544 
02545        RefineableElement* el_pt;
02546        if (halo_el_level==min_ref)
02547         {
02548          // Already at the correct level
02549          el_pt=ref_el_pt;
02550         }
02551        else
02552         {
02553          // Need to go up the tree to the father element at min_ref
02554          RefineableElement* father_el_pt;
02555          ref_el_pt->get_father_at_refinement_level(min_ref,father_el_pt);
02556          el_pt=father_el_pt;
02557         }
02558 
02559        //Loop over nodes
02560        unsigned nnod=el_pt->nnode();
02561        for (unsigned j=0;j<nnod;j++)
02562         {
02563          Node* nod_pt=el_pt->node_pt(j);
02564          if (!nod_pt->is_obsolete())
02565           {
02566            // Keep element and add it to preliminary storage for
02567            // halo elements associated with current neighbouring domain
02568            if (!halo_element_is_retained[el_pt])
02569             {
02570              keep_element[el_pt]=true;
02571              tmp_root_halo_element_pt[domain].push_back(el_pt);
02572              halo_element_is_retained[el_pt]=true;
02573              break;
02574             }
02575           }
02576         }       
02577       }
02578 
02579     }
02580 
02581    // Make sure everybody finishes this part
02582    MPI_Barrier(comm_pt->mpi_comm());
02583 
02584    // Now all processors have decided (independently) which of their
02585    // (to-be root) halo elements they wish to retain. Now we need to figure out
02586    // which of their elements are haloed and add them in the appropriate
02587    // order into the haloed element scheme. For this we exploit that
02588    // the halo and haloed elements are accessed in the same order on
02589    // all processors!
02590    
02591    // Identify haloed elements on domain d
02592    for (int d=0;d<n_proc;d++)
02593     {
02594      // Loop over domains that halo this domain
02595      for (int dd=0;dd<n_proc;dd++)
02596       {       
02597        // Dont't talk to yourself
02598        if (d!=dd)
02599         {
02600          // If we're identifying my haloed elements:
02601          if (d==my_rank)
02602           {
02603            // Get vector all elements that are currently haloed by domain dd
02604            Vector<FiniteElement*> haloed_elem_pt(this->haloed_element_pt(dd));
02605            // Create a vector of ints to indicate if the halo element
02606            // on processor dd processor was kept
02607            unsigned nelem=haloed_elem_pt.size();
02608            Vector<int> halo_kept(nelem);
02609            
02610            // Receive this vector from processor dd 
02611            if (nelem!=0)
02612             {
02613              MPI_Status status;
02614              MPI_Recv(&halo_kept[0],nelem,MPI_INT,dd,0,comm_pt->mpi_comm(),
02615                       &status);
02616            
02617              // Classify haloed element accordingly
02618              for (unsigned e=0;e<nelem;e++)
02619               {
02620                RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
02621                 (haloed_elem_pt[e]);
02622 
02623                // An element should only be kept if its refinement
02624                // level is the same as the minimum refinement level
02625                unsigned haloed_el_level=ref_el_pt->refinement_level();
02626 
02627                // Go up the tree to the correct level
02628                RefineableElement* el_pt;
02629 
02630                if (haloed_el_level==min_ref)
02631                 {
02632                  // Already at the correct level
02633                  el_pt=ref_el_pt;
02634                 }
02635                else
02636                 {
02637                  // Need to go up the tree to the father element at min_ref
02638                  RefineableElement* father_el_pt;
02639                  ref_el_pt->get_father_at_refinement_level
02640                   (min_ref,father_el_pt);
02641                  el_pt=father_el_pt;
02642                 }
02643 
02644                if (halo_kept[e]==1)
02645                 {
02646                  // I am being haloed by processor dd
02647                  // Only keep it if it's not already in the storage
02648                  unsigned n_root_haloed=tmp_root_haloed_element_pt[dd].size();
02649                  bool already_root_haloed=false;
02650                  for (unsigned e_root=0;e_root<n_root_haloed;e_root++)
02651                   {
02652                    if (el_pt==tmp_root_haloed_element_pt[dd][e_root])
02653                     {
02654                      already_root_haloed=true;
02655                      break;
02656                     }
02657                   }
02658                  if (!already_root_haloed)
02659                   {
02660                    tmp_root_haloed_element_pt[dd].push_back(el_pt);
02661                   }
02662                 }
02663               }
02664             }
02665           }
02666          else
02667           {
02668            // If we're dealing with my halo elements:
02669            if (dd==my_rank)
02670             {
02671              // Find (current) halo elements on processor dd whose non-halo is 
02672              // on processor d
02673              Vector<FiniteElement*> halo_elem_pt(this->halo_element_pt(d));
02674              
02675              // Create a vector of ints to indicate if the halo 
02676              // element was kept
02677              unsigned nelem=halo_elem_pt.size();
02678              Vector<int> halo_kept(nelem,0);
02679              for (unsigned e=0;e<nelem;e++)
02680               {
02681                RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
02682                 (halo_elem_pt[e]);
02683 
02684                // An element should only be kept if its refinement
02685                // level is the same as the minimum refinement level
02686                unsigned halo_el_level=ref_el_pt->refinement_level();
02687 
02688                // Go up the tree to the correct level
02689                RefineableElement* el_pt;
02690                if (halo_el_level==min_ref)
02691                 {
02692                  // Already at the correct level
02693                  el_pt=ref_el_pt;
02694                 }
02695                else
02696                 {
02697                  // Need to go up the tree to the father element at min_ref
02698                  RefineableElement* father_el_pt;
02699                  ref_el_pt->get_father_at_refinement_level
02700                   (min_ref,father_el_pt);
02701                  el_pt=father_el_pt;
02702                 }
02703 
02704                if (halo_element_is_retained[el_pt])
02705                 {
02706                  halo_kept[e]=1;
02707                 }
02708               }
02709              
02710              // Now send this vector to processor d to tell it which of
02711              // the haloed elements (which are listed in the same order)
02712              // are to be retained as haloed elements.
02713              if (nelem!=0)
02714               {
02715                MPI_Send(&halo_kept[0],nelem,MPI_INT,d,0,comm_pt->mpi_comm());
02716               }
02717             }
02718           }
02719         }
02720       }
02721     }
02722  
02723    // Backup pointers to nodes in this mesh
02724    nnod=this->nnode();
02725    Vector<Node*> backed_up_nod_pt(nnod);
02726    for (unsigned j=0;j<nnod;j++)
02727     {
02728      backed_up_nod_pt[j]=this->node_pt(j);
02729     }
02730    
02731    // Flush the mesh storage
02732    this->flush_element_and_node_storage();
02733 
02734    // Loop over all backed-up elements
02735    nelem=backed_up_el_pt.size();
02736    for (unsigned e=0;e<nelem;e++)
02737     {
02738      RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>
02739       (backed_up_el_pt[e]);
02740 
02741      // Get refinement level
02742      unsigned level=ref_el_pt->refinement_level();
02743 
02744      // Go up the tree to the correct level
02745      RefineableElement* el_pt;
02746 
02747      if (level==min_ref)
02748       {
02749        // Already at the correct level
02750        el_pt=ref_el_pt;
02751       }
02752      else
02753       {
02754        // Need to go up the tree to the father element at min_ref
02755        RefineableElement* father_el_pt;
02756        ref_el_pt->get_father_at_refinement_level
02757         (min_ref,father_el_pt);
02758        el_pt=father_el_pt;
02759       }     
02760 
02761      // If the base element is going to be kept, then add the current element
02762      // to the "new" mesh
02763      if (keep_element[el_pt])
02764       {
02765        this->add_element_pt(ref_el_pt);
02766       }
02767      else
02768       {
02769        // Flush the object attached to the tree for this element?
02770        RefineableElement* my_el_pt=dynamic_cast<RefineableElement*>(ref_el_pt);
02771        if (my_el_pt!=0)
02772         {
02773          my_el_pt->tree_pt()->flush_object();
02774         }
02775 
02776        // Delete the element
02777        delete ref_el_pt;
02778       }
02779     }
02780 
02781    // Wipe the storage scheme for (root) halo(ed) elements and then re-assign
02782    Root_haloed_element_pt.clear();
02783    Root_halo_element_pt.clear();     
02784    for (int domain=0;domain<n_proc;domain++)
02785     {
02786      
02787      unsigned nelem=tmp_root_halo_element_pt[domain].size();
02788      for (unsigned e=0;e<nelem;e++)
02789       {
02790        Root_halo_element_pt[domain].push_back(
02791         tmp_root_halo_element_pt[domain][e]);
02792       }
02793     
02794      nelem=tmp_root_haloed_element_pt[domain].size();
02795      for (unsigned e=0;e<nelem;e++)
02796       {
02797        Root_haloed_element_pt[domain].push_back(
02798         tmp_root_haloed_element_pt[domain][e]);
02799       }
02800     }
02801    
02802    // Doc stats
02803    if (report_stats)
02804     {
02805      oomph_info << "AFTER pruning:  Processor " << my_rank 
02806                 << " holds " << this->nelement() 
02807                 << " elements of which " << this->nroot_halo_element()
02808                 << " are root halo elements \n while " 
02809                 << this->nroot_haloed_element()
02810                 << " are root haloed elements" << std::endl;
02811     }
02812 
02813    // Loop over all retained elements at this level and mark their nodes
02814    //-------------------------------------------------------------------
02815    // as to be retained too (some double counting going on here)
02816    //-----------------------------------------------------------
02817    nelem=this->nelement();
02818    for (unsigned e=0;e<nelem;e++)
02819     {
02820      FiniteElement* el_pt=this->finite_element_pt(e);
02821      
02822      // Loop over nodes
02823      unsigned nnod=el_pt->nnode();
02824      for (unsigned j=0;j<nnod;j++)
02825       {
02826        Node* nod_pt=el_pt->node_pt(j);
02827        nod_pt->set_non_obsolete();
02828       }
02829     }
02830    
02831    // Complete rebuild of mesh by adding retained nodes
02832    // Note that they are added in the order in which they 
02833    // occured in the original mesh as this guarantees the
02834    // synchronisity between the serialised access to halo
02835    // and haloed nodes from different processors.
02836    nnod=backed_up_nod_pt.size();
02837    for (unsigned j=0;j<nnod;j++)
02838     {
02839      Node* nod_pt=backed_up_nod_pt[j];
02840      if(!nod_pt->is_obsolete())
02841       {
02842        // Not obsolete so add it back to the mesh
02843        this->add_node_pt(nod_pt);
02844       }
02845     }
02846    
02847    // Prune and rebuild mesh
02848    //-----------------------
02849    
02850    // Now remove the pruned nodes from the boundary lookup scheme
02851    this->prune_dead_nodes();
02852     
02853    // And finally re-setup the boundary lookup scheme for elements
02854    this->setup_boundary_element_info();
02855 
02856    // Re-setup tree forest if needed
02857    if (this->nelement()>0)
02858     {
02859      RefineableMeshBase* ref_mesh_pt=dynamic_cast<RefineableMeshBase*>(this);
02860      if (ref_mesh_pt!=0)
02861       {
02862        ref_mesh_pt->setup_tree_forest();
02863       }
02864     }
02865 
02866    // Classify nodes 
02867    classify_halo_and_haloed_nodes(comm_pt,doc_info,report_stats);
02868    
02869    // Doc?
02870    //-----
02871    if (doc_info.doc_flag())
02872     {
02873      doc_mesh_distribution(comm_pt,doc_info);
02874     }
02875 
02876   }
02877 
02878 }
02879 
02880 
02881 
02882 
02883 
02884 
02885 //========================================================================
02886 ///  Get efficiency of mesh distribution: In an ideal distribution
02887 /// without halo overhead, each processor would only hold its own
02888 /// elements. Efficieny per processor =  (number of non-halo elements)/
02889 /// (total number of elements). 
02890 //========================================================================
02891 void Mesh::get_efficiency_of_mesh_distribution(OomphCommunicator* comm_pt,
02892                                                double& av_efficiency,
02893                                                double& max_efficiency,
02894                                                double& min_efficiency)
02895 {
02896  // Storage for number of processors and current processor
02897  int n_proc=comm_pt->nproc();
02898  int my_rank=comm_pt->my_rank();
02899 
02900  // Create vector to hold number of elements and halo elements
02901  Vector<int> nhalo_elements(n_proc);
02902  Vector<int> n_elements(n_proc);
02903 
02904  // Count total number of halo elements
02905  unsigned count=0;
02906  for (int d=0;d<n_proc;d++)
02907   {
02908    Vector<FiniteElement*> halo_elem_pt(halo_element_pt(d));
02909    count+=halo_elem_pt.size();
02910   }
02911 
02912  // Stick own number into appropriate entry
02913  nhalo_elements[my_rank]=count;
02914  n_elements[my_rank]=nelement();
02915 
02916  // Gather information on root processor: First argument group
02917  // specifies what is to be sent (one int from each procssor, indicating
02918  // the number of elements on it), the second group indicates where
02919  // the results are to be gathered (in rank order) on root processor.
02920  MPI_Gather(&nhalo_elements[my_rank],1,MPI_INT,
02921             &nhalo_elements[0],1, MPI_INT,
02922             0,comm_pt->mpi_comm());
02923  MPI_Gather(&n_elements[my_rank],1,MPI_INT,
02924             &n_elements[0],1, MPI_INT,
02925             0,comm_pt->mpi_comm());
02926 
02927  // Initialise stats
02928  av_efficiency=0.0;
02929  double max=-1.0;
02930  double min=1000000000.0;
02931 
02932  if (my_rank==0)
02933   {
02934    for (int i=0;i<n_proc;i++)
02935     {
02936      double eff=double(n_elements[i]-nhalo_elements[i])/double(n_elements[i]);
02937      av_efficiency+=eff;
02938      if (eff>max) max=eff;
02939      if (eff<min) min=eff;
02940        
02941     }
02942    av_efficiency/=double(n_proc);
02943   }
02944 
02945  // Now broadcast the result back out
02946  MPI_Bcast(&max,1,MPI_DOUBLE,0,comm_pt->mpi_comm());
02947  MPI_Bcast(&min,1,MPI_DOUBLE,0,comm_pt->mpi_comm());
02948  MPI_Bcast(&av_efficiency,1,MPI_DOUBLE,0,comm_pt->mpi_comm());
02949 
02950  max_efficiency=max;
02951  min_efficiency=min;
02952 
02953 }
02954 
02955 
02956 
02957 //========================================================================
02958 /// Doc the mesh distribution
02959 //========================================================================
02960 void Mesh::doc_mesh_distribution(OomphCommunicator* comm_pt,DocInfo& doc_info)
02961 { 
02962  // Storage for current processor and number of processors
02963  int my_rank=comm_pt->my_rank();
02964  int n_proc=comm_pt->nproc();
02965 
02966  char filename[100];
02967  std::ofstream some_file;
02968 
02969  // Doc elements on this processor
02970  sprintf(filename,"%s/elements_on_proc%i_%i.dat",
02971          doc_info.directory().c_str(),
02972          my_rank,doc_info.number());
02973  some_file.open(filename);
02974  this->output(some_file,5);
02975  some_file.close();
02976 
02977  // Doc non-halo elements on this processor
02978  sprintf(filename,"%s/non_halo_elements_on_proc%i_%i.dat",
02979          doc_info.directory().c_str(),
02980          my_rank,doc_info.number());
02981  some_file.open(filename);
02982 
02983  // Get to elements on processor
02984  unsigned nelem=this->nelement();
02985  for (unsigned e=0;e<nelem;e++)
02986   {
02987    FiniteElement* el_pt=this->finite_element_pt(e);
02988 
02989    if (!el_pt->is_halo()) // output if non-halo
02990     {
02991      el_pt->output(some_file,5);
02992     }
02993   }
02994 
02995  some_file.close();
02996  
02997  // Doc halo elements on this processor
02998  sprintf(filename,"%s/halo_elements_on_proc%i_%i.dat",
02999          doc_info.directory().c_str(),
03000          my_rank,doc_info.number());
03001  some_file.open(filename);
03002  for (int domain=0; domain<n_proc; domain++)
03003   {
03004    // Get vector of halo elements by copy operation
03005    Vector<FiniteElement*> halo_elem_pt(this->halo_element_pt(domain));
03006    unsigned nelem=halo_elem_pt.size();
03007 //   oomph_info
03008 //    << "Processor " << my_rank << " holds " << nelem 
03009 //    << " halo elem whose non-halo counterparts are located on domain "
03010 //    << domain << std::endl;
03011    for (unsigned e=0;e<nelem;e++)
03012     {
03013      halo_elem_pt[e]->output(some_file,5);
03014     }
03015   }
03016  some_file.close();
03017  
03018  
03019  // Doc haloed elements on this processor
03020  sprintf(filename,"%s/haloed_elements_on_proc%i_%i.dat",
03021          doc_info.directory().c_str(),
03022          my_rank,doc_info.number());
03023  some_file.open(filename);
03024  for (int domain=0; domain<n_proc; domain++)
03025   {
03026    // Get vector of haloed elements by copy operation
03027    Vector<FiniteElement*> haloed_elem_pt(this->haloed_element_pt(domain));
03028    unsigned nelem=haloed_elem_pt.size();
03029 //   oomph_info
03030 //    << "Processor " << my_rank << " holds " << nelem
03031 //    << " haloed elem whose halo counterparts are located on domain "
03032 //    << domain << std::endl;
03033    for (unsigned e=0;e<nelem;e++)
03034     {
03035      haloed_elem_pt[e]->output(some_file,5);
03036     }
03037   }
03038  some_file.close();
03039  
03040  
03041  // Doc nodes on this processor
03042  sprintf(filename,"%s/nodes_on_proc%i_%i.dat",doc_info.directory().c_str(),
03043          my_rank,doc_info.number());
03044  some_file.open(filename);
03045  unsigned nnod=this->nnode();
03046  for (unsigned j=0;j<nnod;j++)
03047   {
03048    Node* nod_pt=this->node_pt(j);
03049    SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
03050    if (solid_nod_pt==0) // not a SolidNode (see comment below)
03051     {
03052      unsigned ndim=nod_pt->ndim();
03053      for (unsigned i=0;i<ndim;i++)
03054       {
03055        some_file << nod_pt->x(i) << " " ;
03056       }
03057      some_file
03058 //      << nod_pt->processor_in_charge() << " " 
03059       << nod_pt->is_halo() << " " 
03060       << nod_pt->eqn_number(0) << " "   // these two won't work for SolidNodes
03061       << nod_pt->is_pinned(0) << " "    // with eqn numbers for position only
03062       << std::endl;
03063     }
03064   }
03065  some_file.close();
03066  
03067  // Doc solid nodes on this processor
03068  sprintf(filename,"%s/solid_nodes_on_proc%i_%i.dat",
03069          doc_info.directory().c_str(),my_rank,doc_info.number());
03070  some_file.open(filename);
03071  unsigned nsnod=this->nnode();
03072  for (unsigned j=0;j<nsnod;j++)
03073   {
03074    Node* nod_pt=this->node_pt(j);
03075    SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
03076    if (solid_nod_pt!=0)
03077     {
03078      unsigned ndim=solid_nod_pt->ndim();
03079      for (unsigned i=0;i<ndim;i++)
03080       {
03081        some_file << nod_pt->x(i) << " " ;
03082       }
03083      some_file
03084 //      << solid_nod_pt->processor_in_charge() << " " 
03085       << solid_nod_pt->is_halo() << " ";
03086      unsigned nval=solid_nod_pt->variable_position_pt()->nvalue();
03087      for (unsigned ival=0;ival<nval;ival++)
03088       {
03089        some_file
03090         << solid_nod_pt->variable_position_pt()->eqn_number(ival) << " "
03091         << solid_nod_pt->variable_position_pt()->is_pinned(ival) << " ";
03092       }
03093      some_file << std::endl;
03094     }
03095   }
03096  some_file.close();
03097  
03098  
03099  // Doc halo nodes on this processor
03100  sprintf(filename,"%s/halo_nodes_on_proc%i_%i.dat",
03101          doc_info.directory().c_str(),
03102          my_rank,doc_info.number());
03103  some_file.open(filename);
03104  for (int domain=0; domain<n_proc; domain++)
03105   {
03106    unsigned nnod=this->nhalo_node(domain);
03107 //   oomph_info << "Processor " << my_rank 
03108 //             << " holds " << nnod << " halo nodes assoc with domain "
03109 //              << domain << std::endl;
03110    for (unsigned j=0;j<nnod;j++)
03111     {
03112      Node* nod_pt=this->halo_node_pt(domain,j);
03113      unsigned ndim=nod_pt->ndim();
03114      for (unsigned i=0;i<ndim;i++)
03115       {
03116        some_file << nod_pt->x(i) << " " ;
03117       }
03118      some_file << domain << std::endl;
03119     }
03120   }
03121  some_file.close();
03122  
03123  
03124  
03125  // Doc haloed nodes on this processor
03126  sprintf(filename,"%s/haloed_nodes_on_proc%i_%i.dat",
03127          doc_info.directory().c_str(),
03128          my_rank,doc_info.number());
03129  some_file.open(filename);
03130  for (int domain=0; domain<n_proc; domain++)
03131   {
03132    unsigned nnod=this->nhaloed_node(domain);
03133 //   oomph_info << "Processor " << my_rank 
03134 //              << " holds " << nnod << " haloed nodes assoc with domain "
03135 //              << domain << std::endl;
03136    for (unsigned j=0;j<nnod;j++)
03137     {
03138      Node* nod_pt=this->haloed_node_pt(domain,j);
03139      unsigned ndim=nod_pt->ndim();
03140      for (unsigned i=0;i<ndim;i++)
03141       {
03142        some_file << nod_pt->x(i) << " " ;
03143       }
03144      some_file << domain << std::endl;
03145     }
03146   }
03147  some_file.close();
03148  
03149  
03150  // Doc mesh
03151  sprintf(filename,"%s/mesh%i_%i.dat",
03152          doc_info.directory().c_str(),
03153          my_rank,doc_info.number());
03154  some_file.open(filename);
03155  this->output(some_file,5);
03156  some_file.close();
03157  
03158  
03159  // Doc boundary scheme
03160  sprintf(filename,"%s/boundaries%i_%i.dat",
03161          doc_info.directory().c_str(),
03162          my_rank,doc_info.number());
03163  some_file.open(filename);
03164  this->output_boundaries(some_file);
03165  some_file.close();
03166  
03167  
03168  // Doc elements next to boundaries scheme
03169  
03170  // How many finite elements are adjacent to boundary b?
03171  unsigned nbound=this->nboundary();
03172  for (unsigned b=0;b<nbound;b++)
03173   {
03174    sprintf(filename,"%s/boundary_elements%i_%i_%i.dat",
03175            doc_info.directory().c_str(),
03176            my_rank,b,doc_info.number());
03177    some_file.open(filename);
03178    unsigned nelem=this->nboundary_element(b);
03179    for (unsigned e=0;e<nelem;e++)
03180     {
03181      this->boundary_element_pt(b,e)->output(some_file,5);
03182     }
03183    some_file.close();
03184   }
03185  
03186 }
03187 
03188 
03189 //========================================================================
03190 /// Check the halo/haloed/shared node/element schemes on the Mesh
03191 //========================================================================
03192 void Mesh::check_halo_schemes(OomphCommunicator* comm_pt, DocInfo& doc_info,
03193                               double& max_permitted_error_for_halo_check)
03194 {
03195  // Moved this from the Problem class so that it would work better
03196  // in multiple mesh problems; there remains a simple "wrapper"
03197  // function in the Problem class that calls this for each (sub)mesh.
03198 
03199  MPI_Status status;
03200  char filename[100];
03201  std::ofstream shared_file;
03202  std::ofstream halo_file;
03203  std::ofstream haloed_file;
03204 
03205  // Storage for current processor and number of processors
03206  int n_proc=comm_pt->nproc();
03207  int my_rank=comm_pt->my_rank();
03208 
03209  // Check the shared node scheme first: if this is incorrect then
03210  // the halo(ed) node scheme is likely to be wrong too
03211 
03212  // Doc shared nodes lookup schemes
03213  //-------------------------------------
03214  if (doc_info.doc_flag())
03215   {
03216    // Loop over domains for shared nodes
03217    for (int dd=0;dd<n_proc;dd++)
03218     {   
03219      sprintf(filename,"%s/shared_node_check%i_%i.dat",
03220              doc_info.directory().c_str(),my_rank,dd);
03221      shared_file.open(filename);
03222      shared_file << "ZONE " << std::endl;
03223      
03224      unsigned nnod=nshared_node(dd);
03225      for (unsigned j=0;j<nnod;j++)
03226       {
03227        Node* nod_pt=shared_node_pt(dd,j);
03228        unsigned ndim=nod_pt->ndim();
03229        for (unsigned i=0;i<ndim;i++)
03230         {
03231          shared_file << nod_pt->position(i) << " ";
03232         }
03233        shared_file << std::endl;
03234       }
03235      // Dummy output for processor that doesn't share nodes
03236      // (needed for tecplot)
03237      if ((nnod==0) && (nelement()!=0))
03238       {
03239        unsigned ndim=finite_element_pt(0)->node_pt(0)->ndim();
03240        if (ndim==2)
03241         {
03242          shared_file   << " 1.0 1.1 " << std::endl;
03243         }
03244        else
03245         {
03246          shared_file   << " 1.0 1.1 1.1" << std::endl;
03247         }
03248       }
03249      shared_file.close(); 
03250     }
03251 
03252   }
03253 
03254  // Check shared nodes lookup schemes
03255  //---------------------------------------
03256  double max_error=0.0;
03257 
03258  // Loop over domains for shared nodes
03259  for (int d=0;d<n_proc;d++)
03260   {
03261    // Are my shared nodes being checked?
03262    if (d==my_rank)
03263     {
03264      // Loop over domains for shared nodes
03265      for (int dd=0;dd<n_proc;dd++)
03266       {
03267        // Don't talk to yourself
03268        if (dd!=d)
03269         {
03270          // How many of my nodes are shared nodes with processor dd?
03271          int nnod_shared=nshared_node(dd);
03272 
03273          if (nnod_shared!=0)
03274           {         
03275            // Receive from processor dd how many of his nodes are shared
03276            // with this processor
03277            int nnod_share=0;
03278            MPI_Recv(&nnod_share,1, MPI_INT,dd,0,comm_pt->mpi_comm(),&status);
03279          
03280            if (nnod_shared!=nnod_share)
03281             {
03282              std::ostringstream error_message;
03283            
03284              error_message
03285               << "Clash in numbers of shared nodes! " 
03286               << std::endl;
03287              error_message 
03288               << "# of shared nodes on proc "
03289               << dd << ": " << nnod_shared << std::endl;
03290              error_message
03291               << "# of shared nodes on proc "
03292               << d << ": " << nnod_share << std::endl;
03293              error_message 
03294               << "(Re-)run Problem::check_halo_schemes() with DocInfo object"
03295               << std::endl;
03296              error_message 
03297               << "to identify the problem" << std::endl;
03298              throw OomphLibError(error_message.str(),
03299                                  "Mesh::check_halo_schemes()",
03300                                  OOMPH_EXCEPTION_LOCATION);
03301             }
03302 
03303 
03304            unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
03305          
03306            // Get strung-together nodal positions from other processor
03307            Vector<double> other_nodal_positions(nod_dim*nnod_share);
03308            MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_share,MPI_DOUBLE,dd,
03309                     0,comm_pt->mpi_comm(),&status);
03310 
03311            // Check
03312            unsigned count=0;
03313            for (int j=0;j<nnod_share;j++)
03314             {
03315              double x_shared=shared_node_pt(dd,j)->position(0);
03316              double y_shared=shared_node_pt(dd,j)->position(1);
03317              double z_shared=0.0;
03318              if (nod_dim==3)
03319               {
03320                z_shared=shared_node_pt(dd,j)->position(2);
03321               }
03322              double x_share=other_nodal_positions[count];
03323              count++;
03324              double y_share=other_nodal_positions[count];
03325              count++;
03326              double z_share=0.0;
03327              if (nod_dim==3)
03328               {
03329                z_share=other_nodal_positions[count];
03330                count++;
03331               }
03332              double error=sqrt( pow(x_shared-x_share,2)+
03333                                 pow(y_shared-y_share,2)+
03334                                 pow(z_shared-z_share,2));
03335              if (fabs(error)>max_error)
03336               {
03337 //              oomph_info << "ZONE" << std::endl;
03338 //              oomph_info << x_halo << " " 
03339 //                        << y_halo << " " 
03340 //                        << y_halo << " " 
03341 //                        << d << " " << dd 
03342 //                        << std::endl;
03343 //              oomph_info << x_haloed << " " 
03344 //                        << y_haloed << " " 
03345 //                        << y_haloed << " "
03346 //                        << d << " " << dd  
03347 //                        << std::endl;
03348 //              oomph_info << std::endl;
03349                max_error=fabs(error);       
03350               }
03351             }
03352           }
03353         }
03354       }
03355     }
03356    // My shared nodes are not being checked: Send my shared nodes
03357    // to the other processor
03358    else
03359     {
03360      int nnod_share=nshared_node(d);
03361      
03362      if (nnod_share!=0)
03363       {
03364        // Send it across to the processor whose shared nodes are being checked
03365        MPI_Send(&nnod_share,1,MPI_INT,d,0,comm_pt->mpi_comm());
03366 
03367        unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
03368          
03369        // Now string together the nodal positions of all shared nodes
03370        Vector<double> nodal_positions(nod_dim*nnod_share);
03371        unsigned count=0;
03372        for (int j=0;j<nnod_share;j++)
03373         {
03374          nodal_positions[count]=shared_node_pt(d,j)->position(0);
03375          count++;
03376          nodal_positions[count]=shared_node_pt(d,j)->position(1);
03377          count++;
03378          if (nod_dim==3)
03379           {
03380            nodal_positions[count]=shared_node_pt(d,j)->position(2);
03381            count++;
03382           }
03383         }
03384        // Send it across to the processor whose shared nodes are being checked
03385        MPI_Send(&nodal_positions[0],nod_dim*nnod_share,MPI_DOUBLE,d,0,
03386                 comm_pt->mpi_comm());
03387       }
03388     }
03389   }
03390 
03391  oomph_info << "Max. error for shared nodes " << max_error
03392             << std::endl;
03393 
03394  if (max_error>max_permitted_error_for_halo_check)
03395   {         
03396    std::ostringstream error_message;
03397    error_message
03398     << "This is bigger than the permitted threshold "
03399     << max_permitted_error_for_halo_check << std::endl;
03400    error_message
03401     << "If you believe this to be acceptable for your problem\n"
03402     << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
03403    throw OomphLibError(error_message.str(),
03404                        "Mesh::check_halo_schemes()",
03405                        OOMPH_EXCEPTION_LOCATION);
03406   }
03407 
03408  // Now check the halo/haloed element lookup scheme
03409 
03410  // Doc halo/haoloed element lookup schemes
03411  //-----------------------------------------
03412  if (doc_info.doc_flag())
03413   {
03414    // Loop over domains for halo elements
03415    for (int dd=0;dd<n_proc;dd++)
03416     {
03417      sprintf(filename,"%s/halo_element_check%i_%i_mesh_%i.dat",
03418              doc_info.directory().c_str(),my_rank,dd,doc_info.number());
03419      halo_file.open(filename);
03420      
03421      // Get vectors of halo/haloed elements by copy operation
03422      Vector<FiniteElement*> 
03423       halo_elem_pt(halo_element_pt(dd));
03424      
03425      unsigned nelem=halo_elem_pt.size();
03426 
03427      for (unsigned e=0;e<nelem;e++)
03428       {
03429        halo_file << "ZONE " << std::endl;
03430        unsigned nnod=halo_elem_pt[e]->nnode();
03431        for (unsigned j=0;j<nnod;j++)
03432         {
03433          Node* nod_pt=halo_elem_pt[e]->node_pt(j);
03434          unsigned ndim=nod_pt->ndim();
03435          for (unsigned i=0;i<ndim;i++)
03436           {
03437            halo_file << nod_pt->position(i) << " ";
03438           }
03439          halo_file << std::endl;
03440         }
03441       }
03442      halo_file.close(); 
03443     }
03444       
03445    // Loop over domains for halo elements
03446    for (int d=0;d<n_proc;d++)
03447     {
03448      sprintf(filename,"%s/haloed_element_check%i_%i_mesh_%i.dat",
03449              doc_info.directory().c_str(),d,my_rank,doc_info.number());
03450      haloed_file.open(filename);
03451      
03452      // Get vectors of halo/haloed elements by copy operation
03453      Vector<FiniteElement*> 
03454       haloed_elem_pt(haloed_element_pt(d));
03455      
03456      unsigned nelem2=haloed_elem_pt.size(); 
03457      for (unsigned e=0;e<nelem2;e++)
03458       {
03459        haloed_file << "ZONE " << std::endl;
03460        unsigned nnod2=haloed_elem_pt[e]->nnode();
03461        for (unsigned j=0;j<nnod2;j++)
03462         {
03463          Node* nod_pt=haloed_elem_pt[e]->node_pt(j);
03464          unsigned ndim=nod_pt->ndim();
03465          for (unsigned i=0;i<ndim;i++)
03466           {
03467            haloed_file << nod_pt->position(i) << " ";
03468           }
03469          haloed_file << std::endl;
03470         }
03471       }
03472      haloed_file.close(); 
03473     }
03474   }
03475 
03476  // Check halo/haloed element lookup schemes
03477  //-----------------------------------------
03478  max_error=0.0;
03479 
03480  // Loop over domains for haloed elements
03481  for (int d=0;d<n_proc;d++)
03482   {
03483    // Are my haloed elements being checked?
03484    if (d==my_rank)
03485     {
03486      // Loop over domains for halo elements
03487      for (int dd=0;dd<n_proc;dd++)
03488       {
03489        // Don't talk to yourself
03490        if (dd!=d)
03491         {
03492          // Get vectors of haloed elements by copy operation
03493          Vector<FiniteElement*> 
03494           haloed_elem_pt(haloed_element_pt(dd));
03495          
03496          // How many of my elements are haloed elements whose halo
03497          // counterpart is located on processor dd?
03498          int nelem_haloed=haloed_elem_pt.size();
03499 
03500          if (nelem_haloed!=0)
03501           {
03502            // Receive from processor dd how many of his elements are halo
03503            // nodes whose non-halo counterparts are located here
03504            int nelem_halo=0;
03505            MPI_Recv(&nelem_halo,1,MPI_INT,dd,0,comm_pt->mpi_comm(),&status);
03506            if (nelem_halo!=nelem_haloed)
03507             {
03508              std::ostringstream error_message;
03509              error_message 
03510               << "Clash in numbers of halo and haloed elements! " 
03511               << std::endl;           
03512              error_message 
03513               << "# of haloed elements whose halo counterpart lives on proc "
03514               << dd << ": " << nelem_haloed << std::endl;
03515              error_message
03516               << "# of halo elements whose non-halo counterpart lives on proc "
03517               << d << ": " << nelem_halo << std::endl;
03518              error_message 
03519               << "(Re-)run Problem::check_halo_schemes() with DocInfo object"
03520               << std::endl;
03521              error_message 
03522               << "to identify the problem" << std::endl;
03523              throw OomphLibError(error_message.str(),
03524                                  "Mesh::check_halo_schemes()",
03525                                  OOMPH_EXCEPTION_LOCATION);
03526             }
03527 
03528 
03529            // Get strung-together elemental nodal positions 
03530            // from other processor
03531            unsigned nnod_per_el=finite_element_pt(0)->nnode();
03532            unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
03533            Vector<double> other_nodal_positions
03534             (nod_dim*nnod_per_el*nelem_halo);
03535            Vector<int> other_nodal_hangings(nnod_per_el*nelem_halo);
03536            MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_per_el*nelem_halo,
03537                     MPI_DOUBLE,dd,0,comm_pt->mpi_comm(),&status);
03538            MPI_Recv(&other_nodal_hangings[0],nnod_per_el*nelem_halo,MPI_INT,dd,
03539                     1,comm_pt->mpi_comm(),&status);
03540 
03541 //         oomph_info << "Received from process " << dd 
03542 //            << ", with size=" << nod_dim*nnod_per_el*nelem_halo << std::endl;
03543          
03544            sprintf(filename,"%s/error_haloed_check%i_%i.dat",
03545                    doc_info.directory().c_str(),dd,my_rank);
03546            haloed_file.open(filename);
03547            sprintf(filename,"%s/error_halo_check%i_%i.dat",
03548                    doc_info.directory().c_str(),dd,my_rank);
03549            halo_file.open(filename);
03550      
03551            unsigned count=0;         
03552            unsigned count_hanging=0;
03553            for (int e=0;e<nelem_haloed;e++)
03554             {   
03555              for (unsigned j=0;j<nnod_per_el;j++)
03556               {
03557                // Testing POSITIONS, not x location 
03558                // (cf hanging nodes, nodes.h)
03559                double x_haloed=haloed_elem_pt[e]->node_pt(j)->position(0);
03560                double y_haloed=haloed_elem_pt[e]->node_pt(j)->position(1);
03561                double z_haloed=0.0;
03562                if (nod_dim==3)
03563                 {
03564                  z_haloed=haloed_elem_pt[e]->node_pt(j)->position(2);
03565                 }
03566                double x_halo=other_nodal_positions[count];
03567                count++;
03568                double y_halo=other_nodal_positions[count];
03569                count++;
03570                int other_hanging=other_nodal_hangings[count_hanging];
03571                count_hanging++;
03572                double z_halo=0.0;
03573                if (nod_dim==3)
03574                 {
03575                  z_halo=other_nodal_positions[count];
03576                  count++;
03577                 }
03578                double error=sqrt( pow(x_haloed-x_halo,2)+ 
03579                                   pow(y_haloed-y_halo,2)+
03580                                   pow(z_haloed-z_halo,2));
03581                if (fabs(error)>max_error) max_error=fabs(error);
03582                if (fabs(error)>0.0)
03583                 {
03584                  // Report error. NOTE: ERROR IS THROWN BELOW ONCE 
03585                  // ALL THIS HAS BEEN PROCESSED.
03586                  oomph_info
03587                   << "Discrepancy between nodal coordinates of halo(ed)"
03588                   << "element.  Error: " << error << std::endl;
03589                  oomph_info
03590                   << "Domain with non-halo (i.e. haloed) elem: " 
03591                   << dd << std::endl;
03592                  oomph_info
03593                   << "Domain with    halo                elem: " << d
03594                   << std::endl;
03595                  oomph_info
03596                   << "Current processor is " << my_rank 
03597                   << std::endl
03598                   << "Nodal positions: " << x_halo << " " << y_halo 
03599                   << std::endl
03600                   << "and haloed: " << x_haloed << " " << y_haloed << std::endl
03601                   << "Node pointer: " << haloed_elem_pt[e]->node_pt(j)
03602                   << std::endl;
03603 //               oomph_info << "Haloed: " << x_haloed << " " << y_haloed << " "
03604 //                          << error << " " << my_rank << " "
03605 //                          << dd << std::endl;
03606 //               oomph_info << "Halo: " << x_halo << " " << y_halo << " "
03607 //                          << error << " " << my_rank << " "
03608 //                          << dd << std::endl;
03609                  haloed_file << x_haloed << " " << y_haloed << " "
03610                              << error << " " << my_rank << " " 
03611                              << dd << " "
03612                              << haloed_elem_pt[e]->node_pt(j)->is_hanging() 
03613                              << std::endl;
03614                  halo_file << x_halo << " " << y_halo << " "
03615                            << error << " " << my_rank << " " 
03616                            << dd << " " 
03617                            << other_hanging << std::endl; 
03618                  // (communicated is_hanging value)
03619                 }
03620               } // j<nnod_per_el
03621             } // e<nelem_haloed
03622 //         oomph_info << "Check count (receive)... " << count << std::endl;
03623            haloed_file.close();
03624            halo_file.close();  
03625           }
03626         }
03627       }
03628     }
03629    // My haloed elements are not being checked: Send my halo elements
03630    // whose non-halo counterparts are located on processor d
03631    else
03632     {
03633 
03634      // Get vectors of halo elements by copy operation
03635      Vector<FiniteElement*> 
03636       halo_elem_pt(halo_element_pt(d));
03637      
03638      // How many of my elements are halo elements whose non-halo
03639      // counterpart is located on processor d?
03640      unsigned nelem_halo=halo_elem_pt.size();
03641 
03642      if (nelem_halo!=0)
03643       {          
03644        // Send it across to the processor whose haloed nodes are being checked
03645        MPI_Send(&nelem_halo,1,MPI_INT,d,0,comm_pt->mpi_comm());
03646 
03647 
03648        // Now string together the nodal positions of all halo nodes
03649        unsigned nnod_per_el=finite_element_pt(0)->nnode();
03650        unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
03651        Vector<double> nodal_positions(nod_dim*nnod_per_el*nelem_halo); 
03652        Vector<int> nodal_hangings(nnod_per_el*nelem_halo);
03653        unsigned count=0;
03654        unsigned count_hanging=0;
03655        for (unsigned e=0;e<nelem_halo;e++)
03656         {
03657          FiniteElement* el_pt= halo_elem_pt[e];
03658          for (unsigned j=0;j<nnod_per_el;j++)
03659           {
03660            // Testing POSITIONS, not x location (cf hanging nodes, nodes.h)
03661            nodal_positions[count]=el_pt->node_pt(j)->position(0);
03662            count++;
03663            nodal_positions[count]=el_pt->node_pt(j)->position(1);
03664            count++;
03665            if (el_pt->node_pt(j)->is_hanging())
03666             {
03667              nodal_hangings[count_hanging]=1;
03668             }
03669            else
03670             {
03671              nodal_hangings[count_hanging]=0;
03672             }
03673            count_hanging++;
03674            if (nod_dim==3)
03675             {
03676              nodal_positions[count]=el_pt->node_pt(j)->position(2);
03677              count++;
03678             }
03679           }
03680         }
03681        // Send it across to the processor whose haloed elements are being 
03682        // checked
03683 
03684        MPI_Send(&nodal_positions[0],nod_dim*nnod_per_el*nelem_halo,
03685                 MPI_DOUBLE,d,0,comm_pt->mpi_comm());
03686        MPI_Send(&nodal_hangings[0],nnod_per_el*nelem_halo,
03687                 MPI_INT,d,1,comm_pt->mpi_comm());
03688       }
03689     }
03690   }
03691 
03692  oomph_info << "Max. error for halo/haloed elements " << max_error
03693             << std::endl;
03694 
03695  if (max_error>max_permitted_error_for_halo_check)
03696   {         
03697    std::ostringstream error_message;
03698    error_message
03699     << "This is bigger than the permitted threshold "
03700     << max_permitted_error_for_halo_check << std::endl;
03701    error_message
03702     << "If you believe this to be acceptable for your problem\n"
03703     << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
03704    throw OomphLibError(error_message.str(),
03705                        "Mesh::check_halo_schemes()",
03706                        OOMPH_EXCEPTION_LOCATION);
03707   }
03708 
03709  // Now check the halo/haloed nodes lookup schemes
03710  
03711  // Doc halo/haloed nodes lookup schemes
03712  //-------------------------------------
03713  if (doc_info.doc_flag())
03714   {
03715    // Loop over domains for halo nodes
03716    for (int dd=0;dd<n_proc;dd++)
03717     {   
03718      sprintf(filename,"%s/halo_node_check%i_%i_mesh_%i.dat",
03719              doc_info.directory().c_str(),my_rank,dd,doc_info.number());
03720      halo_file.open(filename);
03721      halo_file << "ZONE " << std::endl;
03722      
03723      unsigned nnod=nhalo_node(dd);
03724      for (unsigned j=0;j<nnod;j++)
03725       {
03726        Node* nod_pt=halo_node_pt(dd,j);
03727        unsigned ndim=nod_pt->ndim();
03728        for (unsigned i=0;i<ndim;i++)
03729         {
03730          halo_file << nod_pt->position(i) << " ";
03731         }
03732        halo_file << nod_pt->is_hanging() << std::endl;
03733       }
03734      // Dummy output for processor that doesn't share halo nodes
03735      // (needed for tecplot)
03736      // (This will only work if there are elements on this processor...)
03737      if ((nnod==0) && (nelement()!=0))
03738       {
03739        unsigned ndim=finite_element_pt(0)->node_pt(0)->ndim();
03740        if (ndim==2)
03741         {
03742          halo_file   << " 1.0 1.1 " << std::endl;
03743         }
03744        else
03745         {
03746          halo_file   << " 1.0 1.1 1.1" << std::endl;
03747         }
03748       }
03749      halo_file.close(); 
03750     }
03751    
03752    
03753    // Loop over domains for haloed nodes
03754    for (int d=0;d<n_proc;d++)
03755     {
03756      sprintf(filename,"%s/haloed_node_check%i_%i_mesh_%i.dat",
03757              doc_info.directory().c_str(),d,my_rank,doc_info.number());
03758      haloed_file.open(filename);
03759      haloed_file << "ZONE " << std::endl;
03760      
03761      unsigned nnod=nhaloed_node(d);
03762      for (unsigned j=0;j<nnod;j++)
03763       {
03764        Node* nod_pt=haloed_node_pt(d,j);
03765        unsigned ndim=nod_pt->ndim();
03766        for (unsigned i=0;i<ndim;i++)
03767         {
03768          haloed_file << nod_pt->position(i) << " ";
03769         }
03770        haloed_file << nod_pt->is_hanging() << std::endl;
03771       }
03772      // Dummy output for processor that doesn't share halo nodes
03773      // (needed for tecplot)
03774      if ((nnod==0) && (nelement()!=0))
03775       {
03776        unsigned ndim=finite_element_pt(0)->node_pt(0)->ndim();
03777        if (ndim==2)
03778         {
03779          halo_file   << " 1.0 1.1 " << std::endl;
03780         }
03781        else
03782         {
03783          halo_file   << " 1.0 1.1 1.1" << std::endl;
03784         }
03785       }
03786      haloed_file.close(); 
03787     }
03788   }
03789 
03790  // Check halo/haloed nodes lookup schemes
03791  //---------------------------------------
03792  max_error=0.0;
03793 
03794  // Loop over domains for haloed nodes
03795  for (int d=0;d<n_proc;d++)
03796   {
03797    // Are my haloed nodes being checked?
03798    if (d==my_rank)
03799     {
03800      // Loop over domains for halo nodes
03801      for (int dd=0;dd<n_proc;dd++)
03802       {
03803        // Don't talk to yourself
03804        if (dd!=d)
03805         {
03806          // How many of my nodes are haloed nodes whose halo
03807          // counterpart is located on processor dd?
03808          int nnod_haloed=nhaloed_node(dd);
03809 
03810          if (nnod_haloed!=0)
03811           {         
03812            // Receive from processor dd how many of his nodes are halo
03813            // nodes whose non-halo counterparts are located here
03814            int nnod_halo=0;
03815            MPI_Recv(&nnod_halo,1,MPI_INT,dd,0,comm_pt->mpi_comm(),&status);
03816          
03817            if (nnod_haloed!=nnod_halo)
03818             {
03819              std::ostringstream error_message;
03820            
03821              error_message
03822               << "Clash in numbers of halo and haloed nodes! " 
03823               << std::endl;
03824              error_message 
03825               << "# of haloed nodes whose halo counterpart lives on proc "
03826               << dd << ": " << nnod_haloed << std::endl;
03827              error_message
03828               << "# of halo nodes whose non-halo counterpart lives on proc "
03829               << d << ": " << nnod_halo << std::endl;
03830              error_message 
03831               << "(Re-)run Mesh::check_halo_schemes() with DocInfo object"
03832               << std::endl;
03833              error_message 
03834               << "to identify the problem" << std::endl;
03835              throw OomphLibError(error_message.str(),
03836                                  "Mesh::check_halo_schemes()",
03837                                  OOMPH_EXCEPTION_LOCATION);
03838             }
03839 
03840 
03841            unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
03842          
03843            // Get strung-together nodal positions from other processor
03844            Vector<double> other_nodal_positions(nod_dim*nnod_halo);
03845            MPI_Recv(&other_nodal_positions[0],nod_dim*nnod_halo,MPI_DOUBLE,dd,
03846                     0,comm_pt->mpi_comm(),&status);
03847 
03848            // Check
03849            unsigned count=0;
03850            for (int j=0;j<nnod_halo;j++)
03851             {
03852              double x_haloed=haloed_node_pt(dd,j)->position(0);
03853              double y_haloed=haloed_node_pt(dd,j)->position(1);
03854              double z_haloed=0.0;
03855              if (nod_dim==3)
03856               {
03857                z_haloed=haloed_node_pt(dd,j)->position(2);
03858               }
03859              double x_halo=other_nodal_positions[count];
03860              count++;
03861              double y_halo=other_nodal_positions[count];
03862              count++;
03863              double z_halo=0.0;
03864              if (nod_dim==3)
03865               {
03866                z_halo=other_nodal_positions[count];
03867                count++;
03868               }
03869              double error=sqrt( pow(x_haloed-x_halo,2)+
03870                                 pow(y_haloed-y_halo,2)+
03871                                 pow(z_haloed-z_halo,2));
03872              if (fabs(error)>max_error)
03873               {
03874 //              oomph_info << "ZONE" << std::endl;
03875 //              oomph_info << x_halo << " " 
03876 //                        << y_halo << " " 
03877 //                        << y_halo << " " 
03878 //                        << d << " " << dd 
03879 //                        << std::endl;
03880 //              oomph_info << x_haloed << " " 
03881 //                        << y_haloed << " " 
03882 //                        << y_haloed << " "
03883 //                        << d << " " << dd  
03884 //                        << std::endl;
03885 //              oomph_info << std::endl;
03886                max_error=fabs(error);       
03887               }
03888             }
03889           }
03890         }
03891       }
03892     }
03893    // My haloed nodes are not being checked: Send my halo nodes
03894    // whose non-halo counterparts are located on processor d
03895    else
03896     {
03897      int nnod_halo=nhalo_node(d);
03898 
03899      if (nnod_halo!=0)
03900       {     
03901        // Send it across to the processor whose haloed nodes are being checked
03902        MPI_Send(&nnod_halo,1,MPI_INT,d,0,comm_pt->mpi_comm());
03903 
03904        unsigned nod_dim=finite_element_pt(0)->node_pt(0)->ndim();
03905          
03906        // Now string together the nodal positions of all halo nodes
03907        Vector<double> nodal_positions(nod_dim*nnod_halo);
03908        unsigned count=0;
03909        for (int j=0;j<nnod_halo;j++)
03910         {
03911          nodal_positions[count]=halo_node_pt(d,j)->position(0);
03912          count++;
03913          nodal_positions[count]=halo_node_pt(d,j)->position(1);
03914          count++;
03915          if (nod_dim==3)
03916           {
03917            nodal_positions[count]=halo_node_pt(d,j)->position(2);
03918            count++;
03919           }
03920         }
03921        // Send it across to the processor whose haloed nodes are being checked
03922        MPI_Send(&nodal_positions[0],nod_dim*nnod_halo,MPI_DOUBLE,d,0,
03923                 comm_pt->mpi_comm());
03924       }
03925     }
03926   }
03927 
03928  oomph_info << "Max. error for halo/haloed nodes " << max_error
03929             << std::endl;
03930 
03931  if (max_error>max_permitted_error_for_halo_check)
03932   {         
03933    std::ostringstream error_message;
03934    error_message
03935     << "This is bigger than the permitted threshold "
03936     << max_permitted_error_for_halo_check << std::endl;
03937    error_message
03938     << "If you believe this to be acceptable for your problem\n"
03939     << "increase Problem::Max_permitted_error_for_halo_check and re-run \n";
03940    throw OomphLibError(error_message.str(),
03941                        "Mesh::check_halo_schemes()",
03942                        OOMPH_EXCEPTION_LOCATION);
03943   }
03944 
03945 }
03946 
03947 
03948 #endif
03949 
03950 //========================================================================
03951 /// Wipe the storage for all externally-based elements and delete halos
03952 //========================================================================
03953 void Mesh::flush_all_external_storage()
03954 {
03955  // Clear the local storage for elements and nodes
03956  External_element_pt.clear();
03957  External_node_pt.clear();
03958 
03959 #ifdef OOMPH_HAS_MPI
03960  // Storage for number of processors - use size of external halo node array
03961  int n_proc=External_halo_node_pt.size();
03962 
03963  // Careful: some of the external halo nodes are also in boundary
03964  //          node storage and should be removed from this first
03965  for (int d=0;d<n_proc;d++)
03966   {
03967    // How many external haloes with this process?
03968    unsigned n_ext_halo_nod=nexternal_halo_node(d);
03969    for (unsigned j=0;j<n_ext_halo_nod;j++)
03970     {
03971      Node* ext_halo_nod_pt=external_halo_node_pt(d,j);
03972      unsigned n_bnd=nboundary();
03973      for (unsigned i_bnd=0;i_bnd<n_bnd;i_bnd++)
03974       {
03975        // Call this for all boundaries; it will do nothing
03976        // if the node is not on the current boundary
03977        remove_boundary_node(i_bnd,ext_halo_nod_pt);
03978       }
03979     }
03980   }
03981 
03982  // A loop to delete external halo nodes
03983  for (int d=0;d<n_proc;d++)
03984   {
03985    unsigned n_ext_halo_nod=nexternal_halo_node(d);
03986    for (unsigned j=0;j<n_ext_halo_nod;j++)
03987     {
03988      // Only delete if it's not a node stored in the current mesh
03989      bool is_a_mesh_node=false;
03990      unsigned n_node=nnode();
03991      for (unsigned jj=0;jj<n_node;jj++)
03992       {
03993        if (Node_pt[jj]==External_halo_node_pt[d][j])
03994         {
03995          is_a_mesh_node=true;
03996         }
03997       }
03998 
03999      // There will also be duplications between multiple processors,
04000      // so make sure that we don't try to delete these twice
04001      if (!is_a_mesh_node)
04002       {
04003        // Loop over all other higher-numbered processors and check
04004        // for duplicated external halo nodes
04005        // (The highest numbered processor should delete all its ext halos)
04006        for (int dd=d+1;dd<n_proc;dd++)
04007         {
04008          unsigned n_ext_halo=nexternal_halo_node(dd);
04009          for (unsigned jjj=0;jjj<n_ext_halo;jjj++)
04010           {
04011            if (External_halo_node_pt[dd][jjj]==External_halo_node_pt[d][j])
04012             {
04013              is_a_mesh_node=true;
04014             }
04015           }
04016         }
04017       }
04018 
04019      // Only now if no duplicates exist can the node be safely deleted
04020      if (!is_a_mesh_node)
04021       {
04022        delete External_halo_node_pt[d][j];
04023 //       External_halo_node_pt[d][j]=0;
04024       }
04025     }
04026   }
04027 
04028  // Another loop to delete external halo elements (which are distinct)
04029  for (int d=0;d<n_proc;d++)
04030   {
04031    unsigned n_ext_halo_el=nexternal_halo_element(d);
04032    for (unsigned e=0;e<n_ext_halo_el;e++)
04033     {
04034      delete External_halo_element_pt[d][e];
04035 //     External_halo_element_pt[d][e]=0;
04036     }
04037   }
04038 
04039  // Now we are okay to clear the external halo node storage
04040  External_halo_node_pt.clear();
04041  External_halo_element_pt.clear();
04042 
04043  // External haloed nodes and elements are actual members
04044  // of the external mesh and should not be deleted
04045  External_haloed_node_pt.clear();
04046  External_haloed_element_pt.clear();
04047 #endif
04048 }
04049 
04050 
04051 //========================================================================
04052 /// \short Add external element to this Mesh.
04053 //========================================================================
04054 bool Mesh::add_external_element_pt(FiniteElement*& el_pt)
04055 {
04056  // Only add the element to the external element storage if
04057  // it's not already there
04058  bool already_external_element=false;
04059  unsigned n_ext_el=nexternal_element();
04060  for (unsigned e_ext=0;e_ext<n_ext_el;e_ext++)
04061   {
04062    if (el_pt==External_element_pt[e_ext])
04063     {
04064      already_external_element=true;
04065      break;
04066     }
04067   }
04068  if (!already_external_element)
04069   {
04070    External_element_pt.push_back(el_pt);
04071    return true;
04072   }
04073  else
04074   {
04075    return false;
04076   }
04077 }
04078 
04079 //========================================================================
04080 /// \short Add external node to this Mesh.
04081 //========================================================================
04082 bool Mesh::add_external_node_pt(Node*& nod_pt)
04083 {
04084  // Only add the node if it's not already there
04085  bool node_is_external=false;
04086  unsigned n_ext_nod=nexternal_node();
04087  for (unsigned j_ext=0;j_ext<n_ext_nod;j_ext++)
04088   {
04089    if (nod_pt==External_node_pt[j_ext])
04090     {
04091      node_is_external=true;
04092      break;
04093     }
04094   }
04095  if (!node_is_external)
04096   {
04097    External_node_pt.push_back(nod_pt);
04098    return true;
04099   }
04100  else
04101   {
04102    return false;
04103   }
04104 }
04105 
04106 #ifdef OOMPH_HAS_MPI
04107 
04108 // NOTE: the add_external_haloed_node_pt and add_external_haloed_element_pt
04109 //       functions need to check whether the Node/FiniteElement argument
04110 //       has been added to the storage already; this is not the case
04111 //       for the add_external_halo_node_pt and add_external_halo_element_pt
04112 //       functions as these are newly-created elements that are created and
04113 //       added to the storage based on the knowledge of when their haloed 
04114 //       counterparts were created and whether they were newly added
04115 
04116 //========================================================================
04117 /// \short Add external haloed element whose non-halo counterpart is held 
04118 /// on processor p to the storage scheme for external haloed elements.
04119 /// If the element is already in the storage scheme then return its index
04120 //========================================================================
04121 unsigned Mesh::add_external_haloed_element_pt(const unsigned& p, 
04122                                               FiniteElement*& el_pt)
04123 {
04124  // Loop over current storage
04125  unsigned n_extern_haloed=nexternal_haloed_element(p);
04126 
04127  // Is this already an external haloed element?
04128  bool already_external_haloed_element=false;
04129  unsigned external_haloed_el_index=0;
04130  for (unsigned eh=0;eh<n_extern_haloed;eh++)
04131   {
04132    if (el_pt==External_haloed_element_pt[p][eh])
04133     {
04134      // It's already there, so...
04135      already_external_haloed_element=true;
04136      // ...set the index of this element
04137      external_haloed_el_index=eh;
04138      break;
04139     }
04140   }
04141 
04142  // Has it been found?
04143  if (!already_external_haloed_element)
04144   {
04145    // Not found, so add it:
04146    External_haloed_element_pt[p].push_back(el_pt);
04147    // Return the index where it's just been added
04148    return n_extern_haloed;
04149   }
04150  else
04151   {
04152    // Return the index where it was found
04153    return external_haloed_el_index;
04154   }
04155 }
04156 
04157 //========================================================================
04158 /// \short Add external haloed node whose halo (external) counterpart
04159 /// is held on processor p to the storage scheme for external haloed nodes.
04160 /// If the node is already in the storage scheme then return its index
04161 //========================================================================
04162 unsigned Mesh::add_external_haloed_node_pt(const unsigned& p, Node*& nod_pt)
04163 {
04164  // Loop over current storage
04165  unsigned n_ext_haloed_nod=nexternal_haloed_node(p);
04166 
04167  // Is this already an external haloed node?
04168  bool is_an_external_haloed_node=false;
04169  unsigned external_haloed_node_index=0;
04170  for (unsigned k=0;k<n_ext_haloed_nod;k++)
04171   {
04172    if (nod_pt==External_haloed_node_pt[p][k])
04173     {
04174      is_an_external_haloed_node=true;
04175      external_haloed_node_index=k;
04176      break;
04177     }
04178   }
04179 
04180  // Has it been found?
04181  if (!is_an_external_haloed_node)
04182   {
04183    // Not found, so add it
04184    External_haloed_node_pt[p].push_back(nod_pt);
04185    // Return the index where it's just been added
04186    return n_ext_haloed_nod;
04187   }
04188  else
04189   {
04190    // Return the index where it was found
04191    return external_haloed_node_index;
04192   }
04193 }
04194 
04195 #endif
04196 
04197 
04198 //////////////////////////////////////////////////////////////////////
04199 //////////////////////////////////////////////////////////////////////
04200 // Functions for solid meshes
04201 //////////////////////////////////////////////////////////////////////
04202 //////////////////////////////////////////////////////////////////////
04203 
04204 
04205 //========================================================================
04206 /// Make the current configuration the undeformed one by
04207 /// setting the nodal Lagrangian coordinates to their current
04208 /// Eulerian ones
04209 //========================================================================
04210 void SolidMesh::set_lagrangian_nodal_coordinates()
04211 { 
04212 
04213  //Find out how many nodes there are
04214  unsigned long n_node = nnode();
04215  
04216  //Loop over all the nodes
04217  for(unsigned n=0;n<n_node;n++)
04218   {
04219    //Cast node to solid node (can safely be done because
04220    // SolidMeshes consist of SolidNodes
04221    SolidNode* node_pt = static_cast<SolidNode*>(Node_pt[n]);
04222    
04223    // Number of Lagrangian coordinates
04224    unsigned n_lagrangian = node_pt->nlagrangian();
04225 
04226    // Number of generalised Lagrangian coordinates
04227    unsigned n_lagrangian_type = node_pt->nlagrangian_type();
04228 
04229    //The assumption here is that there must be fewer lagrangian coordinates
04230    //than eulerian (which must be true?)
04231 
04232    // Set (generalised) Lagrangian coords = (generalised) Eulerian coords
04233    for(unsigned k=0;k<n_lagrangian_type;k++)
04234     {
04235      // Loop over lagrangian coordinates and set their values
04236      for(unsigned j=0;j<n_lagrangian;j++)
04237       {
04238        node_pt->xi_gen(k,j)=node_pt->x_gen(k,j);
04239       }
04240     }
04241   }
04242 }
04243 
04244 
04245 //=======================================================================
04246 /// Static problem that can be used to assign initial conditions
04247 /// on a given mesh.
04248 //=======================================================================
04249 SolidICProblem SolidMesh::Solid_IC_problem;
04250 
04251 
04252 }

Generated on Mon Aug 10 11:23:47 2009 by  doxygen 1.4.7