$extrastylesheet
elem_cutter.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2013 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 #include "libmesh/libmesh_config.h"
00020 #if defined(LIBMESH_HAVE_TRIANGLE) && defined(LIBMESH_HAVE_TETGEN)
00021 
00022 // Local includes
00023 #include "libmesh/elem_cutter.h"
00024 #include "libmesh/elem.h"
00025 #include "libmesh/serial_mesh.h"
00026 #include "libmesh/mesh_triangle_interface.h"
00027 #include "libmesh/mesh_tetgen_interface.h"
00028 
00029 // C++ includes
00030 
00031 namespace
00032 {
00033 unsigned int cut_cntr;
00034 }
00035 
00036 namespace libMesh
00037 {
00038 
00039 // ------------------------------------------------------------
00040 // ElemCutter implementation
00041 ElemCutter::ElemCutter()
00042 {
00043   _inside_mesh_2D.reset  (new SerialMesh(_comm_self,2));  _triangle_inside.reset  (new TriangleInterface (*_inside_mesh_2D));
00044   _outside_mesh_2D.reset (new SerialMesh(_comm_self,2));  _triangle_outside.reset (new TriangleInterface (*_outside_mesh_2D));
00045 
00046   _inside_mesh_3D.reset  (new SerialMesh(_comm_self,3));  _tetgen_inside.reset  (new TetGenMeshInterface (*_inside_mesh_3D));
00047   _outside_mesh_3D.reset (new SerialMesh(_comm_self,3));  _tetgen_outside.reset (new TetGenMeshInterface (*_outside_mesh_3D));
00048 
00049   cut_cntr = 0;
00050 }
00051 
00052 
00053 
00054 ElemCutter::~ElemCutter()
00055 {}
00056 
00057 
00058 
00059 bool ElemCutter::is_inside (const Elem &elem,
00060                             const std::vector<Real> &vertex_distance_func) const
00061 {
00062   libmesh_assert_equal_to (elem.n_vertices(), vertex_distance_func.size());
00063 
00064   for (std::vector<Real>::const_iterator it=vertex_distance_func.begin();
00065        it!=vertex_distance_func.end(); ++it)
00066     if (*it > 0.) return false;
00067 
00068   // if the distance function is nonpositive, we are outside
00069   return true;
00070 }
00071 
00072 
00073 
00074 bool ElemCutter::is_outside (const Elem &elem,
00075                              const std::vector<Real> &vertex_distance_func) const
00076 {
00077   libmesh_assert_equal_to (elem.n_vertices(), vertex_distance_func.size());
00078 
00079   for (std::vector<Real>::const_iterator it=vertex_distance_func.begin();
00080        it!=vertex_distance_func.end(); ++it)
00081     if (*it < 0.) return false;
00082 
00083   // if the distance function is nonnegative, we are outside
00084   return true;
00085 }
00086 
00087 
00088 
00089 bool ElemCutter::is_cut (const Elem &elem,
00090                          const std::vector<Real> &vertex_distance_func) const
00091 {
00092   libmesh_assert_equal_to (elem.n_vertices(), vertex_distance_func.size());
00093 
00094   Real
00095     vmin = vertex_distance_func.front(),
00096     vmax = vmin;
00097 
00098   for (std::vector<Real>::const_iterator it=vertex_distance_func.begin();
00099        it!=vertex_distance_func.end(); ++it)
00100     {
00101       vmin = std::min (vmin, *it);
00102       vmax = std::max (vmax, *it);
00103     }
00104 
00105   // if the distance function changes sign, we're cut.
00106   return (vmin*vmax < 0.);
00107 }
00108 
00109 
00110 
00111 void ElemCutter::operator()(const Elem &elem,
00112                             const std::vector<Real> &vertex_distance_func)
00113 
00114 {
00115   libmesh_assert_equal_to (vertex_distance_func.size(), elem.n_vertices());
00116 
00117   _inside_elem.clear();
00118   _outside_elem.clear();
00119 
00120   // check for quick return?
00121   {
00122     // completely outside?
00123     if (this->is_outside(elem, vertex_distance_func))
00124       {
00125         //std::cout << "element completely outside\n";
00126         _outside_elem.push_back(&elem);
00127         return;
00128       }
00129 
00130     // completely inside?
00131     else if (this->is_inside(elem, vertex_distance_func))
00132       {
00133         //std::cout << "element completely inside\n";
00134         _inside_elem.push_back(&elem);
00135         return;
00136       }
00137 
00138     libmesh_assert (this->is_cut (elem, vertex_distance_func));
00139   }
00140 
00141   // we now know we are in a cut element, find the intersecting points.
00142   this->find_intersection_points (elem, vertex_distance_func);
00143 
00144   // and then dispatch the proper method
00145   switch (elem.dim())
00146     {
00147     case 1: this->cut_1D(elem, vertex_distance_func); break;
00148     case 2: this->cut_2D(elem, vertex_distance_func); break;
00149     case 3: this->cut_3D(elem, vertex_distance_func); break;
00150     default: libmesh_error_msg("Invalid element dimension: " << elem.dim());
00151     }
00152 }
00153 
00154 
00155 
00156 void ElemCutter::find_intersection_points(const Elem &elem,
00157                                           const std::vector<Real> &vertex_distance_func)
00158 {
00159   _intersection_pts.clear();
00160 
00161   for (unsigned int e=0; e<elem.n_edges(); e++)
00162     {
00163       UniquePtr<Elem> edge (elem.build_edge(e));
00164 
00165       // find the element nodes el0, el1 that map
00166       unsigned int
00167         el0 = elem.get_node_index(edge->get_node(0)),
00168         el1 = elem.get_node_index(edge->get_node(1));
00169 
00170       libmesh_assert (elem.is_vertex(el0));
00171       libmesh_assert (elem.is_vertex(el1));
00172       libmesh_assert_less (el0, vertex_distance_func.size());
00173       libmesh_assert_less (el1, vertex_distance_func.size());
00174 
00175       const Real
00176         d0 = vertex_distance_func[el0],
00177         d1 = vertex_distance_func[el1];
00178 
00179       // if this egde has a 0 crossing
00180       if (d0*d1 < 0.)
00181         {
00182           libmesh_assert_not_equal_to (d0, d1);
00183 
00184           // then find d_star in [0,1], the
00185           // distance from el0 to el1 where the 0 lives.
00186           const Real d_star = d0 / (d0 - d1);
00187 
00188 
00189           // Prevent adding nodes trivially close to existing
00190           // nodes.
00191           const Real endpoint_tol = 0.01;
00192 
00193           if ( (d_star > endpoint_tol) &&
00194                (d_star < (1.-endpoint_tol)) )
00195             {
00196               const Point x_star = (edge->point(0)*(1.-d_star) +
00197                                     edge->point(1)*d_star);
00198 
00199               std::cout << "adding cut point (d_star, x_star) = "
00200                         << d_star << " , " << x_star << std::endl;
00201 
00202               _intersection_pts.push_back (x_star);
00203             }
00204         }
00205     }
00206 }
00207 
00208 
00209 
00210 
00211 void ElemCutter::cut_1D (const Elem & /*elem*/,
00212                          const std::vector<Real> &/*vertex_distance_func*/)
00213 {
00214   libmesh_not_implemented();
00215 }
00216 
00217 
00218 
00219 void ElemCutter::cut_2D (const Elem &elem,
00220                          const std::vector<Real> &vertex_distance_func)
00221 {
00222 #ifndef LIBMESH_HAVE_TRIANGLE
00223 
00224   // current implementation requires triangle!
00225   libMesh::err << "ERROR: current libMesh ElemCutter 2D implementation requires\n"
00226                << "       the \"triangle\" library!\n"
00227                << std::endl;
00228   libmesh_not_implemented();
00229 
00230 #else // OK, LIBMESH_HAVE_TRIANGLE
00231 
00232   std::cout << "Inside cut face element!\n";
00233 
00234   libmesh_assert (_inside_mesh_2D.get()  != NULL);
00235   libmesh_assert (_outside_mesh_2D.get() != NULL);
00236 
00237   _inside_mesh_2D->clear();
00238   _outside_mesh_2D->clear();
00239 
00240   for (unsigned int v=0; v<elem.n_vertices(); v++)
00241     {
00242       if (vertex_distance_func[v] >= 0.)
00243         _outside_mesh_2D->add_point (elem.point(v));
00244 
00245       if (vertex_distance_func[v] <= 0.)
00246         _inside_mesh_2D->add_point (elem.point(v));
00247     }
00248 
00249   for (std::vector<Point>::const_iterator it=_intersection_pts.begin();
00250        it != _intersection_pts.end(); ++it)
00251     {
00252       _inside_mesh_2D->add_point(*it);
00253       _outside_mesh_2D->add_point(*it);
00254     }
00255 
00256 
00257   // Customize the variables for the triangulation
00258   // we will be cutting reference cell, and want as few triangles
00259   // as possible, so jack this up larger than the area we will be
00260   // triangulating so we are governed only by accurately defining
00261   // the boundaries.
00262   _triangle_inside->desired_area()  = 100.;
00263   _triangle_outside->desired_area() = 100.;
00264 
00265   // allow for small angles
00266   _triangle_inside->minimum_angle()  = 5.;
00267   _triangle_outside->minimum_angle() = 5.;
00268 
00269   // Turn off Laplacian mesh smoothing after generation.
00270   _triangle_inside->smooth_after_generating()  = false;
00271   _triangle_outside->smooth_after_generating() = false;
00272 
00273   // Triangulate!
00274   _triangle_inside->triangulate();
00275   _triangle_outside->triangulate();
00276 
00277   // std::ostringstream name;
00278 
00279   // name << "cut_face_"
00280   //  << cut_cntr++
00281   //  << ".dat";
00282   // _inside_mesh_2D->write  ("in_"  + name.str());
00283   // _outside_mesh_2D->write ("out_" + name.str());
00284 
00285   // finally, add the elements to our lists.
00286   {
00287     _inside_elem.clear();  _outside_elem.clear();
00288 
00289     MeshBase::const_element_iterator
00290       it  = _inside_mesh_2D->elements_begin(),
00291       end = _inside_mesh_2D->elements_end();
00292 
00293     for (; it!=end; ++it)
00294       _inside_elem.push_back (*it);
00295 
00296     it  = _outside_mesh_2D->elements_begin();
00297     end = _outside_mesh_2D->elements_end();
00298 
00299     for (; it!=end; ++it)
00300       _outside_elem.push_back (*it);
00301   }
00302 
00303 #endif
00304 }
00305 
00306 
00307 
00308 void ElemCutter::cut_3D (const Elem &elem,
00309                          const std::vector<Real> &vertex_distance_func)
00310 {
00311 #ifndef LIBMESH_HAVE_TETGEN
00312 
00313   // current implementation requires tetgen!
00314   libMesh::err << "ERROR: current libMesh ElemCutter 3D implementation requires\n"
00315                << "       the \"tetgen\" library!\n"
00316                << std::endl;
00317   libmesh_not_implemented();
00318 
00319 #else // OK, LIBMESH_HAVE_TETGEN
00320 
00321   std::cout << "Inside cut cell element!\n";
00322 
00323   libmesh_assert (_inside_mesh_3D.get()  != NULL);
00324   libmesh_assert (_outside_mesh_3D.get() != NULL);
00325 
00326   _inside_mesh_3D->clear();
00327   _outside_mesh_3D->clear();
00328 
00329   for (unsigned int v=0; v<elem.n_vertices(); v++)
00330     {
00331       if (vertex_distance_func[v] >= 0.)
00332         _outside_mesh_3D->add_point (elem.point(v));
00333 
00334       if (vertex_distance_func[v] <= 0.)
00335         _inside_mesh_3D->add_point (elem.point(v));
00336     }
00337 
00338   for (std::vector<Point>::const_iterator it=_intersection_pts.begin();
00339        it != _intersection_pts.end(); ++it)
00340     {
00341       _inside_mesh_3D->add_point(*it);
00342       _outside_mesh_3D->add_point(*it);
00343     }
00344 
00345 
00346   // Triangulate!
00347   _tetgen_inside->triangulate_pointset();
00348   //_inside_mesh_3D->print_info();
00349   _tetgen_outside->triangulate_pointset();
00350   //_outside_mesh_3D->print_info();
00351 
00352 
00353   // (below generates some horribly expensive meshes,
00354   //  but seems immune to the 0 volume problem).
00355   // _tetgen_inside->pointset_convexhull();
00356   // _inside_mesh_3D->find_neighbors();
00357   // _inside_mesh_3D->print_info();
00358   // _tetgen_inside->triangulate_conformingDelaunayMesh (1.e3, 100.);
00359   // _inside_mesh_3D->print_info();
00360 
00361   // _tetgen_outside->pointset_convexhull();
00362   // _outside_mesh_3D->find_neighbors();
00363   // _outside_mesh_3D->print_info();
00364   // _tetgen_outside->triangulate_conformingDelaunayMesh (1.e3, 100.);
00365   // _outside_mesh_3D->print_info();
00366 
00367   std::ostringstream name;
00368 
00369   name << "cut_cell_"
00370        << cut_cntr++
00371        << ".dat";
00372   _inside_mesh_3D->write  ("in_"  + name.str());
00373   _outside_mesh_3D->write ("out_" + name.str());
00374 
00375   // finally, add the elements to our lists.
00376   {
00377     _inside_elem.clear();  _outside_elem.clear();
00378 
00379     MeshBase::const_element_iterator
00380       it  = _inside_mesh_3D->elements_begin(),
00381       end = _inside_mesh_3D->elements_end();
00382 
00383     for (; it!=end; ++it)
00384       if ((*it)->volume() > std::numeric_limits<Real>::epsilon())
00385         _inside_elem.push_back (*it);
00386 
00387     it  = _outside_mesh_3D->elements_begin();
00388     end = _outside_mesh_3D->elements_end();
00389 
00390     for (; it!=end; ++it)
00391       if ((*it)->volume() > std::numeric_limits<Real>::epsilon())
00392         _outside_elem.push_back (*it);
00393   }
00394 
00395 #endif
00396 }
00397 
00398 
00399 
00400 } // namespace libMesh
00401 
00402 #endif // LIBMESH_HAVE_TRIANGLE && LIBMESH_HAVE_TETGEN