$extrastylesheet
inf_elem_builder.C
Go to the documentation of this file.
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