action functions
|
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 if you wish to be informed of the library's "official" release. |
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 }
1.4.7