$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 // C++ includes 00021 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 00022 #include <cmath> // for std::acos() 00023 #include <algorithm> 00024 #include <limits> 00025 #include <map> 00026 00027 // Local includes 00028 #include "libmesh/boundary_info.h" 00029 #include "libmesh/function_base.h" 00030 #include "libmesh/face_tri3.h" 00031 #include "libmesh/face_tri6.h" 00032 #include "libmesh/libmesh_logging.h" 00033 #include "libmesh/mesh_communication.h" 00034 #include "libmesh/mesh_modification.h" 00035 #include "libmesh/mesh_tools.h" 00036 #include "libmesh/parallel.h" 00037 #include "libmesh/remote_elem.h" 00038 #include "libmesh/string_to_enum.h" 00039 #include "libmesh/unstructured_mesh.h" 00040 00041 namespace libMesh 00042 { 00043 00044 00045 // ------------------------------------------------------------ 00046 // MeshTools::Modification functions for mesh modification 00047 void MeshTools::Modification::distort (MeshBase& mesh, 00048 const Real factor, 00049 const bool perturb_boundary) 00050 { 00051 libmesh_assert (mesh.n_nodes()); 00052 libmesh_assert (mesh.n_elem()); 00053 libmesh_assert ((factor >= 0.) && (factor <= 1.)); 00054 00055 START_LOG("distort()", "MeshTools::Modification"); 00056 00057 00058 00059 // First find nodes on the boundary and flag them 00060 // so that we don't move them 00061 // on_boundary holds false (not on boundary) and true (on boundary) 00062 std::vector<bool> on_boundary (mesh.n_nodes(), false); 00063 00064 if (!perturb_boundary) MeshTools::find_boundary_nodes (mesh, on_boundary); 00065 00066 // Now calculate the minimum distance to 00067 // neighboring nodes for each node. 00068 // hmin holds these distances. 00069 std::vector<float> hmin (mesh.n_nodes(), 00070 std::numeric_limits<float>::max()); 00071 00072 MeshBase::element_iterator el = mesh.active_elements_begin(); 00073 const MeshBase::element_iterator end = mesh.active_elements_end(); 00074 00075 for (; el!=end; ++el) 00076 for (unsigned int n=0; n<(*el)->n_nodes(); n++) 00077 hmin[(*el)->node(n)] = std::min(hmin[(*el)->node(n)], 00078 static_cast<float>((*el)->hmin())); 00079 00080 00081 // Now actually move the nodes 00082 { 00083 const unsigned int seed = 123456; 00084 00085 // seed the random number generator. 00086 // We'll loop from 1 to n_nodes on every processor, even those 00087 // that don't have a particular node, so that the pseudorandom 00088 // numbers will be the same everywhere. 00089 std::srand(seed); 00090 00091 // If the node is on the boundary or 00092 // the node is not used by any element (hmin[n]<1.e20) 00093 // then we should not move it. 00094 // [Note: Testing for (in)equality might be wrong 00095 // (different types, namely float and double)] 00096 for (unsigned int n=0; n<mesh.n_nodes(); n++) 00097 if (!on_boundary[n] && (hmin[n] < 1.e20) ) 00098 { 00099 // the direction, random but unit normalized 00100 00101 Point dir( static_cast<Real>(std::rand())/static_cast<Real>(RAND_MAX), 00102 (mesh.mesh_dimension() > 1) ? 00103 static_cast<Real>(std::rand())/static_cast<Real>(RAND_MAX) 00104 : 0., 00105 ((mesh.mesh_dimension() == 3) ? 00106 static_cast<Real>(std::rand())/static_cast<Real>(RAND_MAX) 00107 : 0.) 00108 ); 00109 00110 dir(0) = (dir(0)-.5)*2.; 00111 if (mesh.mesh_dimension() > 1) 00112 dir(1) = (dir(1)-.5)*2.; 00113 if (mesh.mesh_dimension() == 3) 00114 dir(2) = (dir(2)-.5)*2.; 00115 00116 dir = dir.unit(); 00117 00118 Node *node = mesh.node_ptr(n); 00119 if (!node) 00120 continue; 00121 00122 (*node)(0) += dir(0)*factor*hmin[n]; 00123 if (mesh.mesh_dimension() > 1) 00124 (*node)(1) += dir(1)*factor*hmin[n]; 00125 if (mesh.mesh_dimension() == 3) 00126 (*node)(2) += dir(2)*factor*hmin[n]; 00127 } 00128 } 00129 00130 00131 // All done 00132 STOP_LOG("distort()", "MeshTools::Modification"); 00133 } 00134 00135 00136 00137 void MeshTools::Modification::redistribute (MeshBase& mesh, 00138 const FunctionBase<Real> &mapfunc) 00139 { 00140 libmesh_assert (mesh.n_nodes()); 00141 libmesh_assert (mesh.n_elem()); 00142 00143 START_LOG("redistribute()", "MeshTools::Modification"); 00144 00145 DenseVector<Real> output_vec(LIBMESH_DIM); 00146 00147 // FIXME - we should thread this later. 00148 UniquePtr<FunctionBase<Real> > myfunc = mapfunc.clone(); 00149 00150 MeshBase::node_iterator it = mesh.nodes_begin(); 00151 const MeshBase::node_iterator end = mesh.nodes_end(); 00152 00153 for (; it != end; ++it) 00154 { 00155 Node *node = *it; 00156 00157 (*myfunc)(*node, output_vec); 00158 00159 (*node)(0) = output_vec(0); 00160 #if LIBMESH_DIM > 1 00161 (*node)(1) = output_vec(1); 00162 #endif 00163 #if LIBMESH_DIM > 2 00164 (*node)(2) = output_vec(2); 00165 #endif 00166 } 00167 00168 // All done 00169 STOP_LOG("redistribute()", "MeshTools::Modification"); 00170 } 00171 00172 00173 00174 void MeshTools::Modification::translate (MeshBase& mesh, 00175 const Real xt, 00176 const Real yt, 00177 const Real zt) 00178 { 00179 const Point p(xt, yt, zt); 00180 00181 const MeshBase::node_iterator nd_end = mesh.nodes_end(); 00182 00183 for (MeshBase::node_iterator nd = mesh.nodes_begin(); 00184 nd != nd_end; ++nd) 00185 **nd += p; 00186 } 00187 00188 00189 // void MeshTools::Modification::rotate2D (MeshBase& mesh, 00190 // const Real alpha) 00191 // { 00192 // libmesh_assert_not_equal_to (mesh.mesh_dimension(), 1); 00193 00194 // const Real pi = std::acos(-1); 00195 // const Real a = alpha/180.*pi; 00196 // for (unsigned int n=0; n<mesh.n_nodes(); n++) 00197 // { 00198 // const Point p = mesh.node(n); 00199 // const Real x = p(0); 00200 // const Real y = p(1); 00201 // const Real z = p(2); 00202 // mesh.node(n) = Point(std::cos(a)*x - std::sin(a)*y, 00203 // std::sin(a)*x + std::cos(a)*y, 00204 // z); 00205 // } 00206 00207 // } 00208 00209 00210 00211 void MeshTools::Modification::rotate (MeshBase& mesh, 00212 const Real phi, 00213 const Real theta, 00214 const Real psi) 00215 { 00216 #if LIBMESH_DIM == 3 00217 const Real p = -phi/180.*libMesh::pi; 00218 const Real t = -theta/180.*libMesh::pi; 00219 const Real s = -psi/180.*libMesh::pi; 00220 const Real sp = std::sin(p), cp = std::cos(p); 00221 const Real st = std::sin(t), ct = std::cos(t); 00222 const Real ss = std::sin(s), cs = std::cos(s); 00223 00224 // We follow the convention described at http://mathworld.wolfram.com/EulerAngles.html 00225 // (equations 6-14 give the entries of the composite transformation matrix). 00226 // The rotations are performed sequentially about the z, x, and z axes, in that order. 00227 // A positive angle yields a counter-clockwise rotation about the axis in question. 00228 const MeshBase::node_iterator nd_end = mesh.nodes_end(); 00229 00230 for (MeshBase::node_iterator nd = mesh.nodes_begin(); 00231 nd != nd_end; ++nd) 00232 { 00233 const Point pt = **nd; 00234 const Real x = pt(0); 00235 const Real y = pt(1); 00236 const Real z = pt(2); 00237 **nd = Point(( cp*cs-sp*ct*ss)*x + ( sp*cs+cp*ct*ss)*y + (st*ss)*z, 00238 (-cp*ss-sp*ct*cs)*x + (-sp*ss+cp*ct*cs)*y + (st*cs)*z, 00239 ( sp*st)*x + (-cp*st)*y + (ct)*z ); 00240 } 00241 #else 00242 libmesh_error_msg("MeshTools::Modification::rotate() requires libMesh to be compiled with LIBMESH_DIM==3"); 00243 #endif 00244 } 00245 00246 00247 void MeshTools::Modification::scale (MeshBase& mesh, 00248 const Real xs, 00249 const Real ys, 00250 const Real zs) 00251 { 00252 const Real x_scale = xs; 00253 Real y_scale = ys; 00254 Real z_scale = zs; 00255 00256 if (ys == 0.) 00257 { 00258 libmesh_assert_equal_to (zs, 0.); 00259 00260 y_scale = z_scale = x_scale; 00261 } 00262 00263 // Scale the x coordinate in all dimensions 00264 const MeshBase::node_iterator nd_end = mesh.nodes_end(); 00265 00266 for (MeshBase::node_iterator nd = mesh.nodes_begin(); 00267 nd != nd_end; ++nd) 00268 (**nd)(0) *= x_scale; 00269 00270 00271 // Only scale the y coordinate in 2 and 3D 00272 if (mesh.spatial_dimension() < 2) 00273 return; 00274 00275 for (MeshBase::node_iterator nd = mesh.nodes_begin(); 00276 nd != nd_end; ++nd) 00277 (**nd)(1) *= y_scale; 00278 00279 // Only scale the z coordinate in 3D 00280 if (mesh.spatial_dimension() < 3) 00281 return; 00282 00283 for (MeshBase::node_iterator nd = mesh.nodes_begin(); 00284 nd != nd_end; ++nd) 00285 (**nd)(2) *= z_scale; 00286 } 00287 00288 00289 00290 00291 // ------------------------------------------------------------ 00292 // UnstructuredMesh class member functions for mesh modification 00293 void UnstructuredMesh::all_first_order () 00294 { 00295 /* 00296 * when the mesh is not prepared, 00297 * at least renumber the nodes and 00298 * elements, so that the node ids 00299 * are correct 00300 */ 00301 if (!this->_is_prepared) 00302 this->renumber_nodes_and_elements (); 00303 00304 START_LOG("all_first_order()", "Mesh"); 00305 00309 std::vector<bool> node_touched_by_me(this->max_node_id(), false); 00310 00316 element_iterator endit = elements_end(); 00317 for (element_iterator it = elements_begin(); 00318 it != endit; ++it) 00319 { 00320 Elem* so_elem = *it; 00321 00322 libmesh_assert(so_elem); 00323 00324 /* 00325 * build the first-order equivalent, add to 00326 * the new_elements list. 00327 */ 00328 Elem* lo_elem = Elem::build 00329 (Elem::first_order_equivalent_type 00330 (so_elem->type()), so_elem->parent()).release(); 00331 00332 for (unsigned int s=0; s != so_elem->n_sides(); ++s) 00333 if (so_elem->neighbor(s) == remote_elem) 00334 lo_elem->set_neighbor(s, const_cast<RemoteElem*>(remote_elem)); 00335 00336 #ifdef LIBMESH_ENABLE_AMR 00337 /* 00338 * Reset the parent links of any child elements 00339 */ 00340 if (so_elem->has_children()) 00341 for (unsigned int c=0; c != so_elem->n_children(); ++c) 00342 { 00343 so_elem->child(c)->set_parent(lo_elem); 00344 lo_elem->add_child(so_elem->child(c), c); 00345 } 00346 00347 /* 00348 * Reset the child link of any parent element 00349 */ 00350 if (so_elem->parent()) 00351 { 00352 unsigned int c = 00353 so_elem->parent()->which_child_am_i(so_elem); 00354 lo_elem->parent()->replace_child(lo_elem, c); 00355 } 00356 00357 /* 00358 * Copy as much data to the new element as makes sense 00359 */ 00360 lo_elem->set_p_level(so_elem->p_level()); 00361 lo_elem->set_refinement_flag(so_elem->refinement_flag()); 00362 lo_elem->set_p_refinement_flag(so_elem->p_refinement_flag()); 00363 #endif 00364 00365 libmesh_assert_equal_to (lo_elem->n_vertices(), so_elem->n_vertices()); 00366 00367 /* 00368 * By definition the vertices of the linear and 00369 * second order element are identically numbered. 00370 * transfer these. 00371 */ 00372 for (unsigned int v=0; v < so_elem->n_vertices(); v++) 00373 { 00374 lo_elem->set_node(v) = so_elem->get_node(v); 00375 node_touched_by_me[lo_elem->node(v)] = true; 00376 } 00377 00384 libmesh_assert_equal_to (lo_elem->n_sides(), so_elem->n_sides()); 00385 00386 for (unsigned short s=0; s<so_elem->n_sides(); s++) 00387 { 00388 const std::vector<boundary_id_type> boundary_ids = 00389 this->get_boundary_info().raw_boundary_ids (so_elem, s); 00390 00391 this->get_boundary_info().add_side (lo_elem, s, boundary_ids); 00392 } 00393 00394 /* 00395 * The new first-order element is ready. 00396 * Inserting it into the mesh will replace and delete 00397 * the second-order element. 00398 */ 00399 lo_elem->set_id(so_elem->id()); 00400 lo_elem->processor_id() = so_elem->processor_id(); 00401 lo_elem->subdomain_id() = so_elem->subdomain_id(); 00402 this->insert_elem(lo_elem); 00403 } 00404 00405 const MeshBase::node_iterator nd_end = this->nodes_end(); 00406 MeshBase::node_iterator nd = this->nodes_begin(); 00407 while (nd != nd_end) 00408 { 00409 Node *the_node = *nd; 00410 ++nd; 00411 if (!node_touched_by_me[the_node->id()]) 00412 this->delete_node(the_node); 00413 } 00414 00415 STOP_LOG("all_first_order()", "Mesh"); 00416 00417 // On hanging nodes that used to also be second order nodes, we 00418 // might now have an invalid nodal processor_id() 00419 Partitioner::set_node_processor_ids(*this); 00420 00421 // delete or renumber nodes, etc 00422 this->prepare_for_use(/*skip_renumber =*/ false); 00423 } 00424 00425 00426 00427 void UnstructuredMesh::all_second_order (const bool full_ordered) 00428 { 00429 // This function must be run on all processors at once 00430 parallel_object_only(); 00431 00432 /* 00433 * when the mesh is not prepared, 00434 * at least renumber the nodes and 00435 * elements, so that the node ids 00436 * are correct 00437 */ 00438 if (!this->_is_prepared) 00439 this->renumber_nodes_and_elements (); 00440 00441 /* 00442 * If the mesh is empty 00443 * then we have nothing to do 00444 */ 00445 if (!this->n_elem()) 00446 return; 00447 00448 /* 00449 * If the mesh is already second order 00450 * then we have nothing to do. 00451 * We have to test for this in a round-about way to avoid 00452 * a bug on distributed parallel meshes with more processors 00453 * than elements. 00454 */ 00455 bool already_second_order = false; 00456 if (this->elements_begin() != this->elements_end() && 00457 (*(this->elements_begin()))->default_order() != FIRST) 00458 already_second_order = true; 00459 this->comm().max(already_second_order); 00460 if (already_second_order) 00461 return; 00462 00463 START_LOG("all_second_order()", "Mesh"); 00464 00465 /* 00466 * this map helps in identifying second order 00467 * nodes. Namely, a second-order node: 00468 * - edge node 00469 * - face node 00470 * - bubble node 00471 * is uniquely defined through a set of adjacent 00472 * vertices. This set of adjacent vertices is 00473 * used to identify already added higher-order 00474 * nodes. We are safe to use node id's since we 00475 * make sure that these are correctly numbered. 00476 */ 00477 std::map<std::vector<dof_id_type>, Node*> adj_vertices_to_so_nodes; 00478 00479 /* 00480 * for speed-up of the \p add_point() method, we 00481 * can reserve memory. Guess the number of additional 00482 * nodes for different dimensions 00483 */ 00484 switch (this->mesh_dimension()) 00485 { 00486 case 1: 00487 /* 00488 * in 1D, there can only be order-increase from Edge2 00489 * to Edge3. Something like 1/2 of n_nodes() have 00490 * to be added 00491 */ 00492 this->reserve_nodes(static_cast<unsigned int> 00493 (1.5*static_cast<double>(this->n_nodes()))); 00494 break; 00495 00496 case 2: 00497 /* 00498 * in 2D, either refine from Tri3 to Tri6 (double the nodes) 00499 * or from Quad4 to Quad8 (again, double) or Quad9 (2.25 that much) 00500 */ 00501 this->reserve_nodes(static_cast<unsigned int> 00502 (2*static_cast<double>(this->n_nodes()))); 00503 break; 00504 00505 00506 case 3: 00507 /* 00508 * in 3D, either refine from Tet4 to Tet10 (factor = 2.5) up to 00509 * Hex8 to Hex27 (something > 3). Since in 3D there _are_ already 00510 * quite some nodes, and since we do not want to overburden the memory by 00511 * a too conservative guess, use the lower bound 00512 */ 00513 this->reserve_nodes(static_cast<unsigned int> 00514 (2.5*static_cast<double>(this->n_nodes()))); 00515 break; 00516 00517 default: 00518 // Hm? 00519 libmesh_error_msg("Unknown mesh dimension " << this->mesh_dimension()); 00520 } 00521 00522 00523 00524 /* 00525 * form a vector that will hold the node id's of 00526 * the vertices that are adjacent to the son-th 00527 * second-order node. Pull this outside of the 00528 * loop so that silly compilers don't repeatedly 00529 * create and destroy the vector. 00530 */ 00531 std::vector<dof_id_type> adjacent_vertices_ids; 00532 00539 const_element_iterator endit = elements_end(); 00540 for (const_element_iterator it = elements_begin(); 00541 it != endit; ++it) 00542 { 00543 // the linear-order element 00544 const Elem* lo_elem = *it; 00545 00546 libmesh_assert(lo_elem); 00547 00548 // make sure it is linear order 00549 if (lo_elem->default_order() != FIRST) 00550 libmesh_error_msg("ERROR: This is not a linear element: type=" << lo_elem->type()); 00551 00552 // this does _not_ work for refined elements 00553 libmesh_assert_equal_to (lo_elem->level (), 0); 00554 00555 /* 00556 * build the second-order equivalent, add to 00557 * the new_elements list. Note that this here 00558 * is the only point where \p full_ordered 00559 * is necessary. The remaining code works well 00560 * for either type of seconrd-order equivalent, e.g. 00561 * Hex20 or Hex27, as equivalents for Hex8 00562 */ 00563 Elem* so_elem = 00564 Elem::build (Elem::second_order_equivalent_type(lo_elem->type(), 00565 full_ordered) ).release(); 00566 00567 libmesh_assert_equal_to (lo_elem->n_vertices(), so_elem->n_vertices()); 00568 00569 00570 /* 00571 * By definition the vertices of the linear and 00572 * second order element are identically numbered. 00573 * transfer these. 00574 */ 00575 for (unsigned int v=0; v < lo_elem->n_vertices(); v++) 00576 so_elem->set_node(v) = lo_elem->get_node(v); 00577 00578 /* 00579 * Now handle the additional mid-side nodes. This 00580 * is simply handled through a map that remembers 00581 * the already-added nodes. This map maps the global 00582 * ids of the vertices (that uniquely define this 00583 * higher-order node) to the new node. 00584 * Notation: son = second-order node 00585 */ 00586 const unsigned int son_begin = so_elem->n_vertices(); 00587 const unsigned int son_end = so_elem->n_nodes(); 00588 00589 00590 for (unsigned int son=son_begin; son<son_end; son++) 00591 { 00592 const unsigned int n_adjacent_vertices = 00593 so_elem->n_second_order_adjacent_vertices(son); 00594 00595 adjacent_vertices_ids.resize(n_adjacent_vertices); 00596 00597 for (unsigned int v=0; v<n_adjacent_vertices; v++) 00598 adjacent_vertices_ids[v] = 00599 so_elem->node( so_elem->second_order_adjacent_vertex(son,v) ); 00600 00601 /* 00602 * \p adjacent_vertices_ids is now in order of the current 00603 * side. sort it, so that comparisons with the 00604 * \p adjacent_vertices_ids created through other elements' 00605 * sides can match 00606 */ 00607 std::sort(adjacent_vertices_ids.begin(), 00608 adjacent_vertices_ids.end()); 00609 00610 00611 // does this set of vertices already has a mid-node added? 00612 std::pair<std::map<std::vector<dof_id_type>, Node*>::iterator, 00613 std::map<std::vector<dof_id_type>, Node*>::iterator> 00614 pos = adj_vertices_to_so_nodes.equal_range (adjacent_vertices_ids); 00615 00616 // no, not added yet 00617 if (pos.first == pos.second) 00618 { 00619 /* 00620 * for this set of vertices, there is no 00621 * second_order node yet. Add it. 00622 * 00623 * compute the location of the new node as 00624 * the average over the adjacent vertices. 00625 */ 00626 Point new_location = this->point(adjacent_vertices_ids[0]); 00627 for (unsigned int v=1; v<n_adjacent_vertices; v++) 00628 new_location += this->point(adjacent_vertices_ids[v]); 00629 00630 new_location /= static_cast<Real>(n_adjacent_vertices); 00631 00632 /* Add the new point to the mesh, giving it a globally 00633 * well-defined processor id. 00634 */ 00635 Node* so_node = this->add_point 00636 (new_location, DofObject::invalid_id, 00637 this->node(adjacent_vertices_ids[0]).processor_id()); 00638 00639 /* 00640 * insert the new node with its defining vertex 00641 * set into the map, and relocate pos to this 00642 * new entry, so that the so_elem can use 00643 * \p pos for inserting the node 00644 */ 00645 adj_vertices_to_so_nodes.insert(pos.first, 00646 std::make_pair(adjacent_vertices_ids, 00647 so_node)); 00648 00649 so_elem->set_node(son) = so_node; 00650 } 00651 // yes, already added. 00652 else 00653 { 00654 libmesh_assert(pos.first->second); 00655 00656 so_elem->set_node(son) = pos.first->second; 00657 } 00658 } 00659 00660 00672 libmesh_assert_equal_to (lo_elem->n_sides(), so_elem->n_sides()); 00673 00674 for (unsigned short s=0; s<lo_elem->n_sides(); s++) 00675 { 00676 const std::vector<boundary_id_type> boundary_ids = 00677 this->get_boundary_info().raw_boundary_ids (lo_elem, s); 00678 00679 this->get_boundary_info().add_side (so_elem, s, boundary_ids); 00680 00681 if (lo_elem->neighbor(s) == remote_elem) 00682 so_elem->set_neighbor(s, const_cast<RemoteElem*>(remote_elem)); 00683 } 00684 00685 /* 00686 * The new second-order element is ready. 00687 * Inserting it into the mesh will replace and delete 00688 * the first-order element. 00689 */ 00690 so_elem->set_id(lo_elem->id()); 00691 so_elem->processor_id() = lo_elem->processor_id(); 00692 so_elem->subdomain_id() = lo_elem->subdomain_id(); 00693 this->insert_elem(so_elem); 00694 } 00695 00696 // we can clear the map 00697 adj_vertices_to_so_nodes.clear(); 00698 00699 00700 STOP_LOG("all_second_order()", "Mesh"); 00701 00702 // In a ParallelMesh our ghost node processor ids may be bad and 00703 // the ids of nodes touching remote elements may be inconsistent. 00704 // Fix them. 00705 if (!this->is_serial()) 00706 MeshCommunication().make_nodes_parallel_consistent (*this); 00707 00708 // renumber nodes, elements etc 00709 this->prepare_for_use(/*skip_renumber =*/ false); 00710 } 00711 00712 00713 00714 00715 00716 00717 void MeshTools::Modification::all_tri (MeshBase& mesh) 00718 { 00719 // The number of elements in the original mesh before any additions 00720 // or deletions. 00721 const dof_id_type n_orig_elem = mesh.n_elem(); 00722 00723 // We store pointers to the newly created elements in a vector 00724 // until they are ready to be added to the mesh. This is because 00725 // adding new elements on the fly can cause reallocation and invalidation 00726 // of existing iterators. 00727 std::vector<Elem*> new_elements; 00728 new_elements.reserve (2*n_orig_elem); 00729 00730 // If the original mesh has boundary data, we carry that over 00731 // to the new mesh with triangular elements. 00732 const bool mesh_has_boundary_data = (mesh.get_boundary_info().n_boundary_ids() > 0); 00733 00734 // Temporary vectors to store the new boundary element pointers, side numbers, and boundary ids 00735 std::vector<Elem*> new_bndry_elements; 00736 std::vector<unsigned short int> new_bndry_sides; 00737 std::vector<boundary_id_type> new_bndry_ids; 00738 00739 // Iterate over the elements, splitting QUADS into 00740 // pairs of conforming triangles. 00741 // FIXME: This algorithm does not work on refined grids! 00742 { 00743 MeshBase::element_iterator el = mesh.elements_begin(); 00744 const MeshBase::element_iterator end = mesh.elements_end(); 00745 00746 for (; el!=end; ++el) 00747 { 00748 Elem* elem = *el; 00749 00750 const ElemType etype = elem->type(); 00751 00752 // all_tri currently only works on coarse meshes 00753 libmesh_assert (!elem->parent()); 00754 00755 // We split the quads using the shorter of the two diagonals 00756 // to maintain the best angle properties. 00757 bool edge_swap = false; 00758 00759 // True if we actually split the current element. 00760 bool split_elem = false; 00761 00762 // The two new triangular elements we will split the quad into. 00763 Elem* tri0 = NULL; 00764 Elem* tri1 = NULL; 00765 00766 00767 switch (etype) 00768 { 00769 case QUAD4: 00770 { 00771 split_elem = true; 00772 00773 tri0 = new Tri3; 00774 tri1 = new Tri3; 00775 00776 // Check for possible edge swap 00777 if ((elem->point(0) - elem->point(2)).size() < 00778 (elem->point(1) - elem->point(3)).size()) 00779 { 00780 tri0->set_node(0) = elem->get_node(0); 00781 tri0->set_node(1) = elem->get_node(1); 00782 tri0->set_node(2) = elem->get_node(2); 00783 00784 tri1->set_node(0) = elem->get_node(0); 00785 tri1->set_node(1) = elem->get_node(2); 00786 tri1->set_node(2) = elem->get_node(3); 00787 } 00788 00789 else 00790 { 00791 edge_swap=true; 00792 00793 tri0->set_node(0) = elem->get_node(0); 00794 tri0->set_node(1) = elem->get_node(1); 00795 tri0->set_node(2) = elem->get_node(3); 00796 00797 tri1->set_node(0) = elem->get_node(1); 00798 tri1->set_node(1) = elem->get_node(2); 00799 tri1->set_node(2) = elem->get_node(3); 00800 } 00801 00802 00803 break; 00804 } 00805 00806 case QUAD8: 00807 { 00808 split_elem = true; 00809 00810 tri0 = new Tri6; 00811 tri1 = new Tri6; 00812 00813 Node* new_node = mesh.add_point( (mesh.node(elem->node(0)) + 00814 mesh.node(elem->node(1)) + 00815 mesh.node(elem->node(2)) + 00816 mesh.node(elem->node(3)) / 4) 00817 ); 00818 00819 // Check for possible edge swap 00820 if ((elem->point(0) - elem->point(2)).size() < 00821 (elem->point(1) - elem->point(3)).size()) 00822 { 00823 tri0->set_node(0) = elem->get_node(0); 00824 tri0->set_node(1) = elem->get_node(1); 00825 tri0->set_node(2) = elem->get_node(2); 00826 tri0->set_node(3) = elem->get_node(4); 00827 tri0->set_node(4) = elem->get_node(5); 00828 tri0->set_node(5) = new_node; 00829 00830 tri1->set_node(0) = elem->get_node(0); 00831 tri1->set_node(1) = elem->get_node(2); 00832 tri1->set_node(2) = elem->get_node(3); 00833 tri1->set_node(3) = new_node; 00834 tri1->set_node(4) = elem->get_node(6); 00835 tri1->set_node(5) = elem->get_node(7); 00836 00837 } 00838 00839 else 00840 { 00841 edge_swap=true; 00842 00843 tri0->set_node(0) = elem->get_node(3); 00844 tri0->set_node(1) = elem->get_node(0); 00845 tri0->set_node(2) = elem->get_node(1); 00846 tri0->set_node(3) = elem->get_node(7); 00847 tri0->set_node(4) = elem->get_node(4); 00848 tri0->set_node(5) = new_node; 00849 00850 tri1->set_node(0) = elem->get_node(1); 00851 tri1->set_node(1) = elem->get_node(2); 00852 tri1->set_node(2) = elem->get_node(3); 00853 tri1->set_node(3) = elem->get_node(5); 00854 tri1->set_node(4) = elem->get_node(6); 00855 tri1->set_node(5) = new_node; 00856 } 00857 00858 break; 00859 } 00860 00861 case QUAD9: 00862 { 00863 split_elem = true; 00864 00865 tri0 = new Tri6; 00866 tri1 = new Tri6; 00867 00868 // Check for possible edge swap 00869 if ((elem->point(0) - elem->point(2)).size() < 00870 (elem->point(1) - elem->point(3)).size()) 00871 { 00872 tri0->set_node(0) = elem->get_node(0); 00873 tri0->set_node(1) = elem->get_node(1); 00874 tri0->set_node(2) = elem->get_node(2); 00875 tri0->set_node(3) = elem->get_node(4); 00876 tri0->set_node(4) = elem->get_node(5); 00877 tri0->set_node(5) = elem->get_node(8); 00878 00879 tri1->set_node(0) = elem->get_node(0); 00880 tri1->set_node(1) = elem->get_node(2); 00881 tri1->set_node(2) = elem->get_node(3); 00882 tri1->set_node(3) = elem->get_node(8); 00883 tri1->set_node(4) = elem->get_node(6); 00884 tri1->set_node(5) = elem->get_node(7); 00885 } 00886 00887 else 00888 { 00889 edge_swap=true; 00890 00891 tri0->set_node(0) = elem->get_node(0); 00892 tri0->set_node(1) = elem->get_node(1); 00893 tri0->set_node(2) = elem->get_node(3); 00894 tri0->set_node(3) = elem->get_node(4); 00895 tri0->set_node(4) = elem->get_node(8); 00896 tri0->set_node(5) = elem->get_node(7); 00897 00898 tri1->set_node(0) = elem->get_node(1); 00899 tri1->set_node(1) = elem->get_node(2); 00900 tri1->set_node(2) = elem->get_node(3); 00901 tri1->set_node(3) = elem->get_node(5); 00902 tri1->set_node(4) = elem->get_node(6); 00903 tri1->set_node(5) = elem->get_node(8); 00904 } 00905 00906 break; 00907 } 00908 // No need to split elements that are already triangles 00909 case TRI3: 00910 case TRI6: 00911 continue; 00912 // Try to ignore non-2D elements for now 00913 default: 00914 { 00915 libMesh::err << "Warning, encountered non-2D element " 00916 << Utility::enum_to_string<ElemType>(etype) 00917 << " in MeshTools::Modification::all_tri(), hope that's OK..." 00918 << std::endl; 00919 } 00920 } // end switch (etype) 00921 00922 00923 00924 if (split_elem) 00925 { 00926 // Be sure the correct ID's are also set for tri0 and 00927 // tri1. 00928 tri0->processor_id() = elem->processor_id(); 00929 tri0->subdomain_id() = elem->subdomain_id(); 00930 tri1->processor_id() = elem->processor_id(); 00931 tri1->subdomain_id() = elem->subdomain_id(); 00932 00933 if (mesh_has_boundary_data) 00934 { 00935 for (unsigned short sn=0; sn<elem->n_sides(); ++sn) 00936 { 00937 const std::vector<boundary_id_type>& bc_ids = 00938 mesh.get_boundary_info().boundary_ids(*el, sn); 00939 for (std::vector<boundary_id_type>::const_iterator id_it=bc_ids.begin(); id_it!=bc_ids.end(); ++id_it) 00940 { 00941 const boundary_id_type b_id = *id_it; 00942 00943 if (b_id != BoundaryInfo::invalid_id) 00944 { 00945 // Add the boundary ID to the list of new boundary ids 00946 new_bndry_ids.push_back(b_id); 00947 00948 // Convert the boundary side information of the old element to 00949 // boundary side information for the new element. 00950 if (!edge_swap) 00951 { 00952 switch (sn) 00953 { 00954 case 0: 00955 { 00956 // New boundary side is Tri 0, side 0 00957 new_bndry_elements.push_back(tri0); 00958 new_bndry_sides.push_back(0); 00959 break; 00960 } 00961 case 1: 00962 { 00963 // New boundary side is Tri 0, side 1 00964 new_bndry_elements.push_back(tri0); 00965 new_bndry_sides.push_back(1); 00966 break; 00967 } 00968 case 2: 00969 { 00970 // New boundary side is Tri 1, side 1 00971 new_bndry_elements.push_back(tri1); 00972 new_bndry_sides.push_back(1); 00973 break; 00974 } 00975 case 3: 00976 { 00977 // New boundary side is Tri 1, side 2 00978 new_bndry_elements.push_back(tri1); 00979 new_bndry_sides.push_back(2); 00980 break; 00981 } 00982 00983 default: 00984 libmesh_error_msg("Quad4/8/9 cannot have more than 4 sides."); 00985 } 00986 } 00987 00988 else // edge_swap==true 00989 { 00990 switch (sn) 00991 { 00992 case 0: 00993 { 00994 // New boundary side is Tri 0, side 0 00995 new_bndry_elements.push_back(tri0); 00996 new_bndry_sides.push_back(0); 00997 break; 00998 } 00999 case 1: 01000 { 01001 // New boundary side is Tri 1, side 0 01002 new_bndry_elements.push_back(tri1); 01003 new_bndry_sides.push_back(0); 01004 break; 01005 } 01006 case 2: 01007 { 01008 // New boundary side is Tri 1, side 1 01009 new_bndry_elements.push_back(tri1); 01010 new_bndry_sides.push_back(1); 01011 break; 01012 } 01013 case 3: 01014 { 01015 // New boundary side is Tri 0, side 2 01016 new_bndry_elements.push_back(tri0); 01017 new_bndry_sides.push_back(2); 01018 break; 01019 } 01020 01021 default: 01022 libmesh_error_msg("Quad4/8/9 cannot have more than 4 sides."); 01023 } 01024 } // end edge_swap==true 01025 } // end if (b_id != BoundaryInfo::invalid_id) 01026 } // end for loop over boundary IDs 01027 } // end for loop over sides 01028 01029 // Remove the original element from the BoundaryInfo structure. 01030 mesh.get_boundary_info().remove(elem); 01031 01032 } // end if (mesh_has_boundary_data) 01033 01034 01035 // On a distributed mesh, we need to preserve remote_elem 01036 // links, since prepare_for_use can't reconstruct them for 01037 // us. 01038 for (unsigned int sn=0; sn<elem->n_sides(); ++sn) 01039 { 01040 if (elem->neighbor(sn) == remote_elem) 01041 { 01042 // Create a remote_elem link on one of the new 01043 // elements corresponding to the link from the old 01044 // element. 01045 if (!edge_swap) 01046 { 01047 switch (sn) 01048 { 01049 case 0: 01050 { 01051 // New remote side is Tri 0, side 0 01052 tri0->set_neighbor(0, const_cast<RemoteElem*>(remote_elem)); 01053 break; 01054 } 01055 case 1: 01056 { 01057 // New remote side is Tri 0, side 1 01058 tri0->set_neighbor(1, const_cast<RemoteElem*>(remote_elem)); 01059 break; 01060 } 01061 case 2: 01062 { 01063 // New remote side is Tri 1, side 1 01064 tri1->set_neighbor(1, const_cast<RemoteElem*>(remote_elem)); 01065 break; 01066 } 01067 case 3: 01068 { 01069 // New remote side is Tri 1, side 2 01070 tri1->set_neighbor(2, const_cast<RemoteElem*>(remote_elem)); 01071 break; 01072 } 01073 01074 default: 01075 libmesh_error_msg("Quad4/8/9 cannot have more than 4 sides."); 01076 } 01077 } 01078 01079 else // edge_swap==true 01080 { 01081 switch (sn) 01082 { 01083 case 0: 01084 { 01085 // New remote side is Tri 0, side 0 01086 tri0->set_neighbor(0, const_cast<RemoteElem*>(remote_elem)); 01087 break; 01088 } 01089 case 1: 01090 { 01091 // New remote side is Tri 1, side 0 01092 tri1->set_neighbor(0, const_cast<RemoteElem*>(remote_elem)); 01093 break; 01094 } 01095 case 2: 01096 { 01097 // New remote side is Tri 1, side 1 01098 tri1->set_neighbor(1, const_cast<RemoteElem*>(remote_elem)); 01099 break; 01100 } 01101 case 3: 01102 { 01103 // New remote side is Tri 0, side 2 01104 tri0->set_neighbor(2, const_cast<RemoteElem*>(remote_elem)); 01105 break; 01106 } 01107 01108 default: 01109 libmesh_error_msg("Quad4/8/9 cannot have more than 4 sides."); 01110 } 01111 } // end edge_swap==true 01112 } // end if (elem->neighbor(sn) == remote_elem) 01113 } // end for loop over sides 01114 01115 // Determine new IDs for the split elements which will be 01116 // the same on all processors, therefore keeping the Mesh 01117 // in sync. Note: we offset the new IDs by n_orig_elem to 01118 // avoid overwriting any of the original IDs, this assumes 01119 // they were contiguously-numbered to begin with... 01120 tri0->set_id( n_orig_elem + 2*elem->id() + 0 ); 01121 tri1->set_id( n_orig_elem + 2*elem->id() + 1 ); 01122 01123 // Add the newly-created triangles to the temporary vector of new elements. 01124 new_elements.push_back(tri0); 01125 new_elements.push_back(tri1); 01126 01127 // Delete the original element 01128 mesh.delete_elem(elem); 01129 } // end if (split_elem) 01130 } // End for loop over elements 01131 } // end scope 01132 01133 01134 // Now, iterate over the new elements vector, and add them each to 01135 // the Mesh. 01136 { 01137 std::vector<Elem*>::iterator el = new_elements.begin(); 01138 const std::vector<Elem*>::iterator end = new_elements.end(); 01139 for (; el != end; ++el) 01140 mesh.add_elem(*el); 01141 } 01142 01143 if (mesh_has_boundary_data) 01144 { 01145 // By this time, we should have removed all of the original boundary sides 01146 // - except on a hybrid mesh, where we can't "start from a blank slate"! - RHS 01147 // libmesh_assert_equal_to (mesh.get_boundary_info().n_boundary_conds(), 0); 01148 01149 // Clear the boundary info, to be sure and start from a blank slate. 01150 // mesh.get_boundary_info().clear(); 01151 01152 // If the old mesh had boundary data, the new mesh better have some. 01153 libmesh_assert_greater (new_bndry_elements.size(), 0); 01154 01155 // We should also be sure that the lengths of the new boundary data vectors 01156 // are all the same. 01157 libmesh_assert_equal_to (new_bndry_elements.size(), new_bndry_sides.size()); 01158 libmesh_assert_equal_to (new_bndry_sides.size(), new_bndry_ids.size()); 01159 01160 // Add the new boundary info to the mesh 01161 for (unsigned int s=0; s<new_bndry_elements.size(); ++s) 01162 mesh.get_boundary_info().add_side(new_bndry_elements[s], 01163 new_bndry_sides[s], 01164 new_bndry_ids[s]); 01165 } 01166 01167 01168 // Prepare the newly created mesh for use. 01169 mesh.prepare_for_use(/*skip_renumber =*/ false); 01170 01171 // Let the new_elements and new_bndry_elements vectors go out of scope. 01172 } 01173 01174 01175 void MeshTools::Modification::smooth (MeshBase& mesh, 01176 const unsigned int n_iterations, 01177 const Real power) 01178 { 01182 libmesh_assert_equal_to (mesh.mesh_dimension(), 2); 01183 01184 /* 01185 * find the boundary nodes 01186 */ 01187 std::vector<bool> on_boundary; 01188 MeshTools::find_boundary_nodes(mesh, on_boundary); 01189 01190 for (unsigned int iter=0; iter<n_iterations; iter++) 01191 01192 { 01193 /* 01194 * loop over the mesh refinement level 01195 */ 01196 unsigned int n_levels = MeshTools::n_levels(mesh); 01197 for (unsigned int refinement_level=0; refinement_level != n_levels; 01198 refinement_level++) 01199 { 01200 // initialize the storage (have to do it on every level to get empty vectors 01201 std::vector<Point> new_positions; 01202 std::vector<Real> weight; 01203 new_positions.resize(mesh.n_nodes()); 01204 weight.resize(mesh.n_nodes()); 01205 01206 { 01207 /* 01208 * Loop over the elements to calculate new node positions 01209 */ 01210 MeshBase::element_iterator el = mesh.level_elements_begin(refinement_level); 01211 const MeshBase::element_iterator end = mesh.level_elements_end(refinement_level); 01212 01213 for (; el != end; ++el) 01214 { 01215 /* 01216 * Constant handle for the element 01217 */ 01218 const Elem* elem = *el; 01219 01220 /* 01221 * We relax all nodes on level 0 first 01222 * If the element is refined (level > 0), we interpolate the 01223 * parents nodes with help of the embedding matrix 01224 */ 01225 if (refinement_level == 0) 01226 { 01227 for (unsigned int s=0; s<elem->n_neighbors(); s++) 01228 { 01229 /* 01230 * Only operate on sides which are on the 01231 * boundary or for which the current element's 01232 * id is greater than its neighbor's. 01233 * Sides get only built once. 01234 */ 01235 if ((elem->neighbor(s) != NULL) && 01236 (elem->id() > elem->neighbor(s)->id()) ) 01237 { 01238 UniquePtr<Elem> side(elem->build_side(s)); 01239 01240 Node* node0 = side->get_node(0); 01241 Node* node1 = side->get_node(1); 01242 01243 Real node_weight = 1.; 01244 // calculate the weight of the nodes 01245 if (power > 0) 01246 { 01247 Point diff = (*node0)-(*node1); 01248 node_weight = std::pow( diff.size(), power ); 01249 } 01250 01251 const dof_id_type id0 = node0->id(), id1 = node1->id(); 01252 new_positions[id0].add_scaled( *node1, node_weight ); 01253 new_positions[id1].add_scaled( *node0, node_weight ); 01254 weight[id0] += node_weight; 01255 weight[id1] += node_weight; 01256 } 01257 } // element neighbor loop 01258 } 01259 #ifdef LIBMESH_ENABLE_AMR 01260 else // refinement_level > 0 01261 { 01262 /* 01263 * Find the positions of the hanging nodes of refined elements. 01264 * We do this by calculating their position based on the parent 01265 * (one level less refined) element, and the embedding matrix 01266 */ 01267 01268 const Elem* parent = elem->parent(); 01269 01270 /* 01271 * find out which child I am 01272 */ 01273 for (unsigned int c=0; c < parent->n_children(); c++) 01274 { 01275 if (parent->child(c) == elem) 01276 { 01277 /* 01278 *loop over the childs (that is, the current elements) nodes 01279 */ 01280 for (unsigned int nc=0; nc < elem->n_nodes(); nc++) 01281 { 01282 /* 01283 * the new position of the node 01284 */ 01285 Point point; 01286 for (unsigned int n=0; n<parent->n_nodes(); n++) 01287 { 01288 /* 01289 * The value from the embedding matrix 01290 */ 01291 const float em_val = parent->embedding_matrix(c,nc,n); 01292 01293 if (em_val != 0.) 01294 point.add_scaled (parent->point(n), em_val); 01295 } 01296 01297 const dof_id_type id = elem->get_node(nc)->id(); 01298 new_positions[id] = point; 01299 weight[id] = 1.; 01300 } 01301 01302 } // if parent->child == elem 01303 } // for parent->n_children 01304 } // if element refinement_level 01305 #endif // #ifdef LIBMESH_ENABLE_AMR 01306 01307 } // element loop 01308 01309 /* 01310 * finally reposition the vertex nodes 01311 */ 01312 for (unsigned int nid=0; nid<mesh.n_nodes(); ++nid) 01313 if (!on_boundary[nid] && weight[nid] > 0.) 01314 mesh.node(nid) = new_positions[nid]/weight[nid]; 01315 } 01316 01317 { 01318 /* 01319 * Now handle the additional second_order nodes by calculating 01320 * their position based on the vertex postitions 01321 * we do a second loop over the level elements 01322 */ 01323 MeshBase::element_iterator el = mesh.level_elements_begin(refinement_level); 01324 const MeshBase::element_iterator end = mesh.level_elements_end(refinement_level); 01325 01326 for (; el != end; ++el) 01327 { 01328 /* 01329 * Constant handle for the element 01330 */ 01331 const Elem* elem = *el; 01332 const unsigned int son_begin = elem->n_vertices(); 01333 const unsigned int son_end = elem->n_nodes(); 01334 for (unsigned int n=son_begin; n<son_end; n++) 01335 { 01336 const unsigned int n_adjacent_vertices = 01337 elem->n_second_order_adjacent_vertices(n); 01338 01339 Point point; 01340 for (unsigned int v=0; v<n_adjacent_vertices; v++) 01341 point.add(elem->point( elem->second_order_adjacent_vertex(n,v) )); 01342 01343 const dof_id_type id = elem->get_node(n)->id(); 01344 mesh.node(id) = point/n_adjacent_vertices; 01345 } 01346 } 01347 } 01348 01349 } // refinement_level loop 01350 01351 } // end iteration 01352 } 01353 01354 01355 01356 #ifdef LIBMESH_ENABLE_AMR 01357 void MeshTools::Modification::flatten(MeshBase& mesh) 01358 { 01359 // Algorithm: 01360 // .) For each active element in the mesh: construct a 01361 // copy which is the same in every way *except* it is 01362 // a level 0 element. Store the pointers to these in 01363 // a separate vector. Save any boundary information as well. 01364 // Delete the active element from the mesh. 01365 // .) Loop over all (remaining) elements in the mesh, delete them. 01366 // .) Add the level-0 copies back to the mesh 01367 01368 // Temporary storage for new element pointers 01369 std::vector<Elem*> new_elements; 01370 01371 // BoundaryInfo Storage for element ids, sides, and BC ids 01372 std::vector<Elem*> saved_boundary_elements; 01373 std::vector<boundary_id_type> saved_bc_ids; 01374 std::vector<unsigned short int> saved_bc_sides; 01375 01376 // Reserve a reasonable amt. of space for each 01377 new_elements.reserve(mesh.n_active_elem()); 01378 saved_boundary_elements.reserve(mesh.get_boundary_info().n_boundary_conds()); 01379 saved_bc_ids.reserve(mesh.get_boundary_info().n_boundary_conds()); 01380 saved_bc_sides.reserve(mesh.get_boundary_info().n_boundary_conds()); 01381 { 01382 MeshBase::element_iterator it = mesh.active_elements_begin(); 01383 const MeshBase::element_iterator end = mesh.active_elements_end(); 01384 01385 for (; it != end; ++it) 01386 { 01387 Elem* elem = *it; 01388 01389 // Make a new element of the same type 01390 Elem* copy = Elem::build(elem->type()).release(); 01391 01392 // Set node pointers (they still point to nodes in the original mesh) 01393 for(unsigned int n=0; n<elem->n_nodes(); n++) 01394 copy->set_node(n) = elem->get_node(n); 01395 01396 // Copy over ids 01397 copy->processor_id() = elem->processor_id(); 01398 copy->subdomain_id() = elem->subdomain_id(); 01399 01400 // Retain the original element's ID as well, otherwise ParallelMesh will 01401 // try to create one for you... 01402 copy->set_id( elem->id() ); 01403 01404 // This element could have boundary info or ParallelMesh 01405 // remote_elem links as well. We need to save the (elem, 01406 // side, bc_id) triples and those links 01407 for (unsigned short s=0; s<elem->n_sides(); s++) 01408 { 01409 if (elem->neighbor(s) == remote_elem) 01410 copy->set_neighbor(s, const_cast<RemoteElem*>(remote_elem)); 01411 01412 const std::vector<boundary_id_type>& bc_ids = 01413 mesh.get_boundary_info().boundary_ids(elem,s); 01414 for (std::vector<boundary_id_type>::const_iterator id_it=bc_ids.begin(); id_it!=bc_ids.end(); ++id_it) 01415 { 01416 const boundary_id_type bc_id = *id_it; 01417 01418 if (bc_id != BoundaryInfo::invalid_id) 01419 { 01420 saved_boundary_elements.push_back(copy); 01421 saved_bc_ids.push_back(bc_id); 01422 saved_bc_sides.push_back(s); 01423 } 01424 } 01425 } 01426 01427 01428 // We're done with this element 01429 mesh.delete_elem(elem); 01430 01431 // But save the copy 01432 new_elements.push_back(copy); 01433 } 01434 01435 // Make sure we saved the same number of boundary conditions 01436 // in each vector. 01437 libmesh_assert_equal_to (saved_boundary_elements.size(), saved_bc_ids.size()); 01438 libmesh_assert_equal_to (saved_bc_ids.size(), saved_bc_sides.size()); 01439 } 01440 01441 01442 // Loop again, delete any remaining elements 01443 { 01444 MeshBase::element_iterator it = mesh.elements_begin(); 01445 const MeshBase::element_iterator end = mesh.elements_end(); 01446 01447 for (; it != end; ++it) 01448 mesh.delete_elem( *it ); 01449 } 01450 01451 01452 // Add the copied (now level-0) elements back to the mesh 01453 { 01454 for (std::vector<Elem*>::iterator it = new_elements.begin(); 01455 it != new_elements.end(); 01456 ++it) 01457 { 01458 #ifndef NDEBUG 01459 dof_id_type orig_id = (*it)->id(); 01460 01461 // ugly mid-statement endif to avoid unused variable warnings 01462 Elem* added_elem = 01463 #endif 01464 mesh.add_elem(*it); 01465 01466 #ifndef NDEBUG 01467 dof_id_type added_id = added_elem->id(); 01468 #endif 01469 01470 // If the Elem, as it was re-added to the mesh, now has a 01471 // different ID (this is unlikely, so it's just an assert) 01472 // the boundary information will no longer be correct. 01473 libmesh_assert_equal_to (orig_id, added_id); 01474 } 01475 } 01476 01477 // Finally, also add back the saved boundary information 01478 for (unsigned int e=0; e<saved_boundary_elements.size(); ++e) 01479 mesh.get_boundary_info().add_side(saved_boundary_elements[e], 01480 saved_bc_sides[e], 01481 saved_bc_ids[e]); 01482 01483 // Trim unused and renumber nodes and elements 01484 mesh.prepare_for_use(/*skip_renumber =*/ false); 01485 } 01486 #endif // #ifdef LIBMESH_ENABLE_AMR 01487 01488 01489 01490 void MeshTools::Modification::change_boundary_id (MeshBase& mesh, 01491 const boundary_id_type old_id, 01492 const boundary_id_type new_id) 01493 { 01494 // Only level-0 elements store BCs. Loop over them. 01495 MeshBase::element_iterator el = mesh.level_elements_begin(0); 01496 const MeshBase::element_iterator end_el = mesh.level_elements_end(0); 01497 for (; el != end_el; ++el) 01498 { 01499 Elem *elem = *el; 01500 01501 unsigned int n_nodes = elem->n_nodes(); 01502 for (unsigned int n=0; n != n_nodes; ++n) 01503 { 01504 const std::vector<boundary_id_type>& old_ids = 01505 mesh.get_boundary_info().boundary_ids(elem->get_node(n)); 01506 if (std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end()) 01507 { 01508 std::vector<boundary_id_type> new_ids(old_ids); 01509 std::replace(new_ids.begin(), new_ids.end(), old_id, new_id); 01510 mesh.get_boundary_info().remove(elem->get_node(n)); 01511 mesh.get_boundary_info().add_node(elem->get_node(n), new_ids); 01512 } 01513 } 01514 01515 unsigned int n_edges = elem->n_edges(); 01516 for (unsigned short edge=0; edge != n_edges; ++edge) 01517 { 01518 const std::vector<boundary_id_type>& old_ids = 01519 mesh.get_boundary_info().edge_boundary_ids(elem, edge); 01520 if (std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end()) 01521 { 01522 std::vector<boundary_id_type> new_ids(old_ids); 01523 std::replace(new_ids.begin(), new_ids.end(), old_id, new_id); 01524 mesh.get_boundary_info().remove_edge(elem, edge); 01525 mesh.get_boundary_info().add_edge(elem, edge, new_ids); 01526 } 01527 } 01528 01529 unsigned int n_sides = elem->n_sides(); 01530 for (unsigned short s=0; s != n_sides; ++s) 01531 { 01532 const std::vector<boundary_id_type>& old_ids = 01533 mesh.get_boundary_info().boundary_ids(elem, s); 01534 if (std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end()) 01535 { 01536 std::vector<boundary_id_type> new_ids(old_ids); 01537 std::replace(new_ids.begin(), new_ids.end(), old_id, new_id); 01538 mesh.get_boundary_info().remove_side(elem, s); 01539 mesh.get_boundary_info().add_side(elem, s, new_ids); 01540 } 01541 } 01542 } 01543 } 01544 01545 01546 01547 void MeshTools::Modification::change_subdomain_id (MeshBase& mesh, 01548 const subdomain_id_type old_id, 01549 const subdomain_id_type new_id) 01550 { 01551 MeshBase::element_iterator el = mesh.elements_begin(); 01552 const MeshBase::element_iterator end_el = mesh.elements_end(); 01553 01554 for (; el != end_el; ++el) 01555 { 01556 Elem *elem = *el; 01557 01558 if (elem->subdomain_id() == old_id) 01559 elem->subdomain_id() = new_id; 01560 } 01561 } 01562 01563 01564 } // namespace libMesh