$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 #include "libmesh/libmesh_config.h" 00019 00020 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS 00021 00022 // C++ includes 00023 00024 // Local includes 00025 #include "libmesh/inf_elem_builder.h" 00026 #include "libmesh/libmesh_logging.h" 00027 #include "libmesh/mesh_tools.h" 00028 #include "libmesh/face_inf_quad4.h" 00029 #include "libmesh/face_inf_quad6.h" 00030 #include "libmesh/cell_inf_prism6.h" 00031 #include "libmesh/cell_inf_prism12.h" 00032 #include "libmesh/cell_inf_hex8.h" 00033 #include "libmesh/cell_inf_hex16.h" 00034 #include "libmesh/cell_inf_hex18.h" 00035 #include "libmesh/mesh_base.h" 00036 00037 #ifdef DEBUG 00038 #include "libmesh/parallel_mesh.h" 00039 #endif 00040 00041 namespace libMesh 00042 { 00043 00044 const Point InfElemBuilder::build_inf_elem(bool be_verbose) 00045 { 00046 // determine origin automatically, 00047 // works only if the mesh has no symmetry planes. 00048 const MeshTools::BoundingBox b_box = MeshTools::bounding_box(_mesh); 00049 Point origin = (b_box.first + b_box.second) / 2; 00050 00051 if (be_verbose && _mesh.processor_id() == 0) 00052 { 00053 #ifdef DEBUG 00054 libMesh::out << " Determined origin for Infinite Elements:" 00055 << std::endl 00056 << " "; 00057 origin.write_unformatted(libMesh::out); 00058 libMesh::out << std::endl; 00059 #endif 00060 } 00061 00062 // Call the protected implementation function with the 00063 // automatically determined origin. 00064 this->build_inf_elem(origin, false, false, false, be_verbose); 00065 00066 // when finished with building the Ifems, 00067 // it remains to prepare the mesh for use: 00068 // find neighbors (again), partition (if needed)... 00069 this->_mesh.prepare_for_use (/*skip_renumber =*/ false); 00070 00071 return origin; 00072 } 00073 00074 00075 00076 00077 00078 00079 00080 00081 00082 00083 00084 00085 const Point InfElemBuilder::build_inf_elem (const InfElemOriginValue& origin_x, 00086 const InfElemOriginValue& origin_y, 00087 const InfElemOriginValue& origin_z, 00088 const bool x_sym, 00089 const bool y_sym, 00090 const bool z_sym, 00091 const bool be_verbose, 00092 std::vector<const Node*>* inner_boundary_nodes) 00093 { 00094 START_LOG("build_inf_elem()", "InfElemBuilder"); 00095 00096 // first determine the origin of the 00097 // infinite elements. For this, the 00098 // origin defaults to the given values, 00099 // and may be overridden when the user 00100 // provided values 00101 Point origin(origin_x.second, origin_y.second, origin_z.second); 00102 00103 // when only _one_ of the origin coordinates is _not_ 00104 // given, we have to determine it on our own 00105 if ( !origin_x.first || !origin_y.first || !origin_z.first) 00106 { 00107 // determine origin 00108 const MeshTools::BoundingBox b_box = MeshTools::bounding_box(_mesh); 00109 const Point auto_origin = (b_box.first+b_box.second)/2; 00110 00111 // override default values, if necessary 00112 if (!origin_x.first) 00113 origin(0) = auto_origin(0); 00114 if (!origin_y.first) 00115 origin(1) = auto_origin(1); 00116 if (!origin_z.first) 00117 origin(2) = auto_origin(2); 00118 00119 if (be_verbose) 00120 { 00121 libMesh::out << " Origin for Infinite Elements:" << std::endl; 00122 00123 if (!origin_x.first) 00124 libMesh::out << " determined x-coordinate" << std::endl; 00125 if (!origin_y.first) 00126 libMesh::out << " determined y-coordinate" << std::endl; 00127 if (!origin_z.first) 00128 libMesh::out << " determined z-coordinate" << std::endl; 00129 00130 libMesh::out << " coordinates: "; 00131 origin.write_unformatted(libMesh::out); 00132 libMesh::out << std::endl; 00133 } 00134 } 00135 00136 else if (be_verbose) 00137 00138 { 00139 libMesh::out << " Origin for Infinite Elements:" << std::endl; 00140 libMesh::out << " coordinates: "; 00141 origin.write_unformatted(libMesh::out); 00142 libMesh::out << std::endl; 00143 } 00144 00145 00146 00147 // Now that we have the origin, check if the user provided an \p 00148 // inner_boundary_nodes. If so, we pass a std::set to the actual 00149 // implementation of the build_inf_elem(), so that we can convert 00150 // this to the Node* vector 00151 if (inner_boundary_nodes != NULL) 00152 { 00153 // note that the std::set that we will get 00154 // from build_inf_elem() uses the index of 00155 // the element in this->_elements vector, 00156 // and the second entry is the side index 00157 // for this element. Therefore, we do _not_ 00158 // need to renumber nodes and elements 00159 // prior to building the infinite elements. 00160 // 00161 // However, note that this method here uses 00162 // node id's... Do we need to renumber? 00163 00164 00165 // Form the list of faces of elements which finally 00166 // will tell us which nodes should receive boundary 00167 // conditions (to form the std::vector<const Node*>) 00168 std::set< std::pair<dof_id_type, 00169 unsigned int> > inner_faces; 00170 00171 00172 // build infinite elements 00173 this->build_inf_elem(origin, 00174 x_sym, y_sym, z_sym, 00175 be_verbose, 00176 &inner_faces); 00177 00178 if (be_verbose) 00179 { 00180 this->_mesh.print_info(); 00181 libMesh::out << "Data pre-processing:" << std::endl 00182 << " convert the <int,int> list to a Node* list..." 00183 << std::endl; 00184 } 00185 00186 // First use a std::vector<dof_id_type> that holds 00187 // the global node numbers. Then sort this vector, 00188 // so that it can be made unique (no multiple occurence 00189 // of a node), and then finally insert the Node* in 00190 // the vector inner_boundary_nodes. 00191 // 00192 // Reserve memory for the vector<> with 00193 // 4 times the size of the number of elements in the 00194 // std::set. This is a good bet for Quad4 face elements. 00195 // For higher-order elements, this probably _has_ to lead 00196 // to additional allocations... 00197 // Practice has to show how this affects performance. 00198 std::vector<dof_id_type> inner_boundary_node_numbers; 00199 inner_boundary_node_numbers.reserve(4*inner_faces.size()); 00200 00201 // Now transform the set of pairs to a list of (possibly 00202 // duplicate) global node numbers. 00203 std::set< std::pair<dof_id_type,unsigned int> >::iterator face_it = inner_faces.begin(); 00204 const std::set< std::pair<dof_id_type,unsigned int> >::iterator face_end = inner_faces.end(); 00205 for(; face_it!=face_end; ++face_it) 00206 { 00207 std::pair<dof_id_type,unsigned int> p = *face_it; 00208 00209 // build a full-ordered side element to get _all_ the base nodes 00210 UniquePtr<Elem> side( this->_mesh.elem(p.first)->build_side(p.second) ); 00211 00212 // insert all the node numbers in inner_boundary_node_numbers 00213 for (unsigned int n=0; n< side->n_nodes(); n++) 00214 inner_boundary_node_numbers.push_back(side->node(n)); 00215 } 00216 00217 00218 // inner_boundary_node_numbers now still holds multiple entries of 00219 // node numbers. So first sort, then unique the vector. 00220 // Note that \p std::unique only puts the new ones in 00221 // front, while to leftovers are @e not deleted. Instead, 00222 // it returns a pointer to the end of the unique range. 00223 //TODO:[BSK] int_ibn_size_before is not the same type as unique_size! 00224 #ifndef NDEBUG 00225 const std::size_t ibn_size_before = inner_boundary_node_numbers.size(); 00226 #endif 00227 std::sort (inner_boundary_node_numbers.begin(), inner_boundary_node_numbers.end()); 00228 std::vector<dof_id_type>::iterator unique_end = 00229 std::unique (inner_boundary_node_numbers.begin(), inner_boundary_node_numbers.end()); 00230 00231 std::size_t unique_size = std::distance(inner_boundary_node_numbers.begin(), unique_end); 00232 libmesh_assert_less_equal (unique_size, ibn_size_before); 00233 00234 // Finally, create const Node* in the inner_boundary_nodes 00235 // vector. Reserve, not resize (otherwise, the push_back 00236 // would append the interesting nodes, while NULL-nodes 00237 // live in the resize'd area... 00238 inner_boundary_nodes->reserve (unique_size); 00239 inner_boundary_nodes->clear(); 00240 00241 00242 std::vector<dof_id_type>::iterator pos_it = inner_boundary_node_numbers.begin(); 00243 for (; pos_it != unique_end; ++pos_it) 00244 { 00245 const Node& node = this->_mesh.node(*pos_it); 00246 inner_boundary_nodes->push_back(&node); 00247 } 00248 00249 if (be_verbose) 00250 libMesh::out << " finished identifying " << unique_size 00251 << " target nodes." << std::endl; 00252 } 00253 00254 else 00255 00256 { 00257 // There are no inner boundary nodes, so simply build the infinite elements 00258 this->build_inf_elem(origin, x_sym, y_sym, z_sym, be_verbose); 00259 } 00260 00261 00262 STOP_LOG("build_inf_elem()", "InfElemBuilder"); 00263 00264 // when finished with building the Ifems, 00265 // it remains to prepare the mesh for use: 00266 // find neighbors again, partition (if needed)... 00267 this->_mesh.prepare_for_use (/*skip_renumber =*/ false); 00268 00269 return origin; 00270 } 00271 00272 00273 00274 00275 00276 00277 00278 00279 00280 // The actual implementation of building elements. 00281 void InfElemBuilder::build_inf_elem(const Point& origin, 00282 const bool x_sym, 00283 const bool y_sym, 00284 const bool z_sym, 00285 const bool be_verbose, 00286 std::set< std::pair<dof_id_type, 00287 unsigned int> >* inner_faces) 00288 { 00289 if (be_verbose) 00290 { 00291 #ifdef DEBUG 00292 libMesh::out << " Building Infinite Elements:" << std::endl; 00293 libMesh::out << " updating element neighbor tables..." << std::endl; 00294 #else 00295 libMesh::out << " Verbose mode disabled in non-debug mode." << std::endl; 00296 #endif 00297 } 00298 00299 00300 // update element neighbors 00301 this->_mesh.find_neighbors(); 00302 00303 START_LOG("build_inf_elem()", "InfElemBuilder"); 00304 00305 // A set for storing element number, side number pairs. 00306 // pair.first == element number, pair.second == side number 00307 std::set< std::pair<dof_id_type,unsigned int> > faces; 00308 std::set< std::pair<dof_id_type,unsigned int> > ofaces; 00309 00310 // A set for storing node numbers on the outer faces. 00311 std::set<dof_id_type> onodes; 00312 00313 // The distance to the farthest point in the mesh from the origin 00314 Real max_r=0.; 00315 00316 // The index of the farthest point in the mesh from the origin 00317 int max_r_node = -1; 00318 00319 #ifdef DEBUG 00320 if (be_verbose) 00321 { 00322 libMesh::out << " collecting boundary sides"; 00323 if (x_sym || y_sym || z_sym) 00324 libMesh::out << ", skipping sides in symmetry planes..." << std::endl; 00325 else 00326 libMesh::out << "..." << std::endl; 00327 } 00328 #endif 00329 00330 // Iterate through all elements and sides, collect indices of all active 00331 // boundary sides in the faces set. Skip sides which lie in symmetry planes. 00332 // Later, sides of the inner boundary will be sorted out. 00333 { 00334 MeshBase::element_iterator it = this->_mesh.active_elements_begin(); 00335 const MeshBase::element_iterator end = this->_mesh.active_elements_end(); 00336 00337 for(; it != end; ++it) 00338 { 00339 Elem* elem = *it; 00340 00341 for (unsigned int s=0; s<elem->n_neighbors(); s++) 00342 { 00343 // check if elem(e) is on the boundary 00344 if (elem->neighbor(s) == NULL) 00345 { 00346 // note that it is safe to use the Elem::side() method, 00347 // which gives a non-full-ordered element 00348 UniquePtr<Elem> side(elem->build_side(s)); 00349 00350 // bool flags for symmetry detection 00351 bool sym_side=false; 00352 bool on_x_sym=true; 00353 bool on_y_sym=true; 00354 bool on_z_sym=true; 00355 00356 00357 // Loop over the nodes to check whether they are on the symmetry planes, 00358 // and therefore sufficient to use a non-full-ordered side element 00359 for(unsigned int n=0; n<side->n_nodes(); n++) 00360 { 00361 const Point dist_from_origin = this->_mesh.point(side->node(n)) - origin; 00362 00363 if(x_sym) 00364 if( std::abs(dist_from_origin(0)) > 1.e-3 ) 00365 on_x_sym=false; 00366 00367 if(y_sym) 00368 if( std::abs(dist_from_origin(1)) > 1.e-3 ) 00369 on_y_sym=false; 00370 00371 if(z_sym) 00372 if( std::abs(dist_from_origin(2)) > 1.e-3 ) 00373 on_z_sym=false; 00374 00375 // if(x_sym) 00376 // if( std::abs(dist_from_origin(0)) > 1.e-6 ) 00377 // on_x_sym=false; 00378 00379 // if(y_sym) 00380 // if( std::abs(dist_from_origin(1)) > 1.e-6 ) 00381 // on_y_sym=false; 00382 00383 // if(z_sym) 00384 // if( std::abs(dist_from_origin(2)) > 1.e-6 ) 00385 // on_z_sym=false; 00386 00387 //find the node most distant from origin 00388 00389 Real r = dist_from_origin.size(); 00390 if (r > max_r) 00391 { 00392 max_r = r; 00393 max_r_node=side->node(n); 00394 } 00395 00396 } 00397 00398 sym_side = (x_sym && on_x_sym) || (y_sym && on_y_sym) || (z_sym && on_z_sym); 00399 00400 if (!sym_side) 00401 faces.insert( std::make_pair(elem->id(), s) ); 00402 00403 } // neighbor(s) == NULL 00404 } // sides 00405 } // elems 00406 } 00407 00408 00409 00410 00411 00412 00413 // If a boundary side has one node on the outer boundary, 00414 // all points of this side are on the outer boundary. 00415 // Start with the node most distant from origin, which has 00416 // to be on the outer boundary, then recursively find all 00417 // sides and nodes connected to it. Found sides are moved 00418 // from faces to ofaces, nodes are collected in onodes. 00419 // Here, the search is done iteratively, because, depending on 00420 // the mesh, a very high level of recursion might be necessary. 00421 if (max_r_node > 0) 00422 onodes.insert(max_r_node); 00423 00424 00425 { 00426 std::set< std::pair<dof_id_type,unsigned int> >::iterator face_it = faces.begin(); 00427 unsigned int facesfound=0; 00428 while (face_it != faces.end()) { 00429 00430 std::pair<dof_id_type, unsigned int> p; 00431 p = *face_it; 00432 00433 // This has to be a full-ordered side element, 00434 // since we need the correct n_nodes, 00435 UniquePtr<Elem> side(this->_mesh.elem(p.first)->build_side(p.second)); 00436 00437 bool found=false; 00438 for(unsigned int sn=0; sn<side->n_nodes(); sn++) 00439 if(onodes.count(side->node(sn))) 00440 { 00441 found=true; 00442 break; 00443 } 00444 00445 00446 // If a new oface is found, include its nodes in onodes 00447 if(found) 00448 { 00449 for(unsigned int sn=0; sn<side->n_nodes(); sn++) 00450 onodes.insert(side->node(sn)); 00451 00452 ofaces.insert(p); 00453 ++face_it; // iteration is done here 00454 faces.erase(p); 00455 00456 facesfound++; 00457 } 00458 00459 else 00460 ++face_it; // iteration is done here 00461 00462 // If at least one new oface was found in this cycle, 00463 // do another search cycle. 00464 if(facesfound>0 && face_it == faces.end()) 00465 { 00466 facesfound = 0; 00467 face_it = faces.begin(); 00468 } 00469 00470 } 00471 } 00472 00473 00474 #ifdef DEBUG 00475 if (be_verbose) 00476 libMesh::out << " found " 00477 << faces.size() 00478 << " inner and " 00479 << ofaces.size() 00480 << " outer boundary faces" 00481 << std::endl; 00482 #endif 00483 00484 // When the user provided a non-null pointer to 00485 // inner_faces, that implies he wants to have 00486 // this std::set. For now, simply copy the data. 00487 if (inner_faces != NULL) 00488 *inner_faces = faces; 00489 00490 // free memory, clear our local variable, no need 00491 // for it any more. 00492 faces.clear(); 00493 00494 00495 // outer_nodes maps onodes to their duplicates 00496 std::map<dof_id_type, Node *> outer_nodes; 00497 00498 // We may need to pick our own object ids in parallel 00499 dof_id_type old_max_node_id = _mesh.max_node_id(); 00500 dof_id_type old_max_elem_id = _mesh.max_elem_id(); 00501 00502 // for each boundary node, add an outer_node with 00503 // double distance from origin. 00504 std::set<dof_id_type>::iterator on_it = onodes.begin(); 00505 for( ; on_it != onodes.end(); ++on_it) 00506 { 00507 Point p = (Point(this->_mesh.point(*on_it)) * 2) - origin; 00508 if (_mesh.is_serial()) 00509 { 00510 // Add with a default id in serial 00511 outer_nodes[*on_it]=this->_mesh.add_point(p); 00512 } 00513 else 00514 { 00515 // Pick a unique id in parallel 00516 Node &bnode = _mesh.node(*on_it); 00517 dof_id_type new_id = bnode.id() + old_max_node_id; 00518 outer_nodes[*on_it] = 00519 this->_mesh.add_point(p, new_id, 00520 bnode.processor_id()); 00521 } 00522 } 00523 00524 00525 #ifdef DEBUG 00526 // for verbose, remember n_elem 00527 dof_id_type n_conventional_elem = this->_mesh.n_elem(); 00528 #endif 00529 00530 00531 // build Elems based on boundary side type 00532 std::set< std::pair<dof_id_type,unsigned int> >::iterator face_it = ofaces.begin(); 00533 for( ; face_it != ofaces.end(); ++face_it) 00534 { 00535 // Shortcut to the pair being iterated over 00536 std::pair<dof_id_type,unsigned int> p = *face_it; 00537 00538 // build a full-ordered side element to get the base nodes 00539 UniquePtr<Elem> side(this->_mesh.elem(p.first)->build_side(p.second)); 00540 00541 // create cell depending on side type, assign nodes, 00542 // use braces to force scope. 00543 bool is_higher_order_elem = false; 00544 00545 Elem* el; 00546 switch(side->type()) 00547 { 00548 // 3D infinite elements 00549 // TRIs 00550 case TRI3: 00551 el=new InfPrism6; 00552 break; 00553 00554 case TRI6: 00555 el=new InfPrism12; 00556 is_higher_order_elem = true; 00557 break; 00558 00559 // QUADs 00560 case QUAD4: 00561 el=new InfHex8; 00562 break; 00563 00564 case QUAD8: 00565 el=new InfHex16; 00566 is_higher_order_elem = true; 00567 break; 00568 00569 case QUAD9: 00570 el=new InfHex18; 00571 00572 // the method of assigning nodes (which follows below) 00573 // omits in the case of QUAD9 the bubble node; therefore 00574 // we assign these first by hand here. 00575 el->set_node(16) = side->get_node(8); 00576 el->set_node(17) = outer_nodes[side->node(8)]; 00577 is_higher_order_elem=true; 00578 break; 00579 00580 // 2D infinite elements 00581 case EDGE2: 00582 el=new InfQuad4; 00583 break; 00584 00585 case EDGE3: 00586 el=new InfQuad6; 00587 el->set_node(4) = side->get_node(2); 00588 break; 00589 00590 // 1D infinite elements not supported 00591 default: 00592 libMesh::out << "InfElemBuilder::build_inf_elem(Point, bool, bool, bool, bool): " 00593 << "invalid face element " 00594 << std::endl; 00595 continue; 00596 } 00597 00598 // In parallel, assign unique ids to the new element 00599 if (!_mesh.is_serial()) 00600 { 00601 Elem *belem = _mesh.elem(p.first); 00602 el->processor_id() = belem->processor_id(); 00603 // We'd better not have elements with more than 6 sides 00604 el->set_id (belem->id() * 6 + p.second + old_max_elem_id); 00605 } 00606 00607 // assign vertices to the new infinite element 00608 const unsigned int n_base_vertices = side->n_vertices(); 00609 for(unsigned int i=0; i<n_base_vertices; i++) 00610 { 00611 el->set_node(i ) = side->get_node(i); 00612 el->set_node(i+n_base_vertices) = outer_nodes[side->node(i)]; 00613 } 00614 00615 00616 // when this is a higher order element, 00617 // assign also the nodes in between 00618 if (is_higher_order_elem) 00619 { 00620 // n_safe_base_nodes is the number of nodes in \p side 00621 // that may be safely assigned using below for loop. 00622 // Actually, n_safe_base_nodes is _identical_ with el->n_vertices(), 00623 // since for QUAD9, the 9th node was already assigned above 00624 const unsigned int n_safe_base_nodes = el->n_vertices(); 00625 00626 for(unsigned int i=n_base_vertices; i<n_safe_base_nodes; i++) 00627 { 00628 el->set_node(i+n_base_vertices) = side->get_node(i); 00629 el->set_node(i+n_safe_base_nodes) = outer_nodes[side->node(i)]; 00630 } 00631 } 00632 00633 00634 // add infinite element to mesh 00635 this->_mesh.add_elem(el); 00636 } // for 00637 00638 00639 #ifdef DEBUG 00640 _mesh.libmesh_assert_valid_parallel_ids(); 00641 00642 if (be_verbose) 00643 libMesh::out << " added " 00644 << this->_mesh.n_elem() - n_conventional_elem 00645 << " infinite elements and " 00646 << onodes.size() 00647 << " nodes to the mesh" 00648 << std::endl 00649 << std::endl; 00650 #endif 00651 00652 STOP_LOG("build_inf_elem()", "InfElemBuilder"); 00653 } 00654 00655 } // namespace libMesh 00656 00657 00658 00659 00660 00661 #endif // LIBMESH_ENABLE_INFINITE_ELEMENTS