$extrastylesheet
mesh_smoother_laplace.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 
00019 
00020 // C++ includes
00021 #include <algorithm> // for std::copy, std::sort
00022 
00023 
00024 // Local includes
00025 #include "libmesh/mesh_smoother_laplace.h"
00026 #include "libmesh/mesh_tools.h"
00027 #include "libmesh/elem.h"
00028 #include "libmesh/unstructured_mesh.h"
00029 #include "libmesh/parallel.h"
00030 #include "libmesh/parallel_ghost_sync.h" // sync_dofobject_data_by_id()
00031 #include "libmesh/parallel_algebra.h" // StandardType<Point>
00032 
00033 namespace libMesh
00034 {
00035 // LaplaceMeshSmoother member functions
00036 LaplaceMeshSmoother::LaplaceMeshSmoother(UnstructuredMesh& mesh)
00037   : MeshSmoother(mesh),
00038     _initialized(false)
00039 {
00040 }
00041 
00042 
00043 
00044 
00045 void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
00046 {
00047   if (!_initialized)
00048     this->init();
00049 
00050   // Don't smooth the nodes on the boundary...
00051   // this would change the mesh geometry which
00052   // is probably not something we want!
00053   std::vector<bool> on_boundary;
00054   MeshTools::find_boundary_nodes(_mesh, on_boundary);
00055 
00056   // Ensure that the find_boundary_nodes() function returned a properly-sized vector
00057   if (on_boundary.size() != _mesh.n_nodes())
00058     libmesh_error_msg("MeshTools::find_boundary_nodes() returned incorrect length vector!");
00059 
00060   // We can only update the nodes after all new positions were
00061   // determined. We store the new positions here
00062   std::vector<Point> new_positions;
00063 
00064   for (unsigned int n=0; n<n_iterations; n++)
00065     {
00066       new_positions.resize(_mesh.n_nodes());
00067 
00068       {
00069         MeshBase::node_iterator       it     = _mesh.local_nodes_begin();
00070         const MeshBase::node_iterator it_end = _mesh.local_nodes_end();
00071         for (; it != it_end; ++it)
00072           {
00073             Node* node = *it;
00074 
00075             if (node == NULL)
00076               libmesh_error_msg("[" << _mesh.processor_id() << "]: Node iterator returned NULL pointer.");
00077 
00078             // leave the boundary intact
00079             // Only relocate the nodes which are vertices of an element
00080             // All other entries of _graph (the secondary nodes) are empty
00081             if (!on_boundary[node->id()] && (_graph[node->id()].size() > 0))
00082               {
00083                 Point avg_position(0.,0.,0.);
00084 
00085                 for (unsigned j=0; j<_graph[node->id()].size(); ++j)
00086                   {
00087                     // Will these nodal positions always be available
00088                     // or will they refer to remote nodes?  To be
00089                     // careful, we grab a pointer and test it against
00090                     // NULL.
00091                     Node* connected_node = _mesh.node_ptr(_graph[node->id()][j]);
00092 
00093                     if (connected_node == NULL)
00094                       libmesh_error_msg("Error! Libmesh returned NULL pointer for node " << _graph[connected_node->id()][j]);
00095 
00096                     avg_position.add( *connected_node );
00097                   } // end for(j)
00098 
00099                 // Compute the average, store in the new_positions vector
00100                 new_positions[node->id()] = avg_position / static_cast<Real>(_graph[node->id()].size());
00101               } // end if
00102           } // end for
00103       } // end scope
00104 
00105 
00106       // now update the node positions (local node positions only)
00107       {
00108         MeshBase::node_iterator it           = _mesh.local_nodes_begin();
00109         const MeshBase::node_iterator it_end = _mesh.local_nodes_end();
00110         for (; it != it_end; ++it)
00111           {
00112             Node* node = *it;
00113 
00114             if (!on_boundary[node->id()] && (_graph[node->id()].size() > 0))
00115               {
00116                 // Should call Point::op=
00117                 // libMesh::out << "Setting node id " << node->id() << " to position " << new_positions[node->id()];
00118                 _mesh.node(node->id()) = new_positions[node->id()];
00119               }
00120           } // end for
00121       } // end scope
00122 
00123       // Now the nodes which are ghosts on this processor may have been moved on
00124       // the processors which own them.  So we need to synchronize with our neighbors
00125       // and get the most up-to-date positions for the ghosts.
00126       SyncNodalPositions sync_object(_mesh);
00127       Parallel::sync_dofobject_data_by_id
00128         (_mesh.comm(), _mesh.nodes_begin(), _mesh.nodes_end(), sync_object);
00129 
00130     } // end for n_iterations
00131 
00132   // finally adjust the second order nodes (those located between vertices)
00133   // these nodes will be located between their adjacent nodes
00134   // do this element-wise
00135   MeshBase::element_iterator       el  = _mesh.active_elements_begin();
00136   const MeshBase::element_iterator end = _mesh.active_elements_end();
00137 
00138   for (; el != end; ++el)
00139     {
00140       // Constant handle for the element
00141       const Elem* elem = *el;
00142 
00143       // get the second order nodes (son)
00144       // their element indices start at n_vertices and go to n_nodes
00145       const unsigned int son_begin = elem->n_vertices();
00146       const unsigned int son_end   = elem->n_nodes();
00147 
00148       // loop over all second order nodes (son)
00149       for (unsigned int son=son_begin; son<son_end; son++)
00150         {
00151           // Don't smooth second-order nodes which are on the boundary
00152           if (!on_boundary[elem->node(son)])
00153             {
00154               const unsigned int n_adjacent_vertices =
00155                 elem->n_second_order_adjacent_vertices(son);
00156 
00157               // calculate the new position which is the average of the
00158               // position of the adjacent vertices
00159               Point avg_position(0,0,0);
00160               for (unsigned int v=0; v<n_adjacent_vertices; v++)
00161                 avg_position +=
00162                   _mesh.point( elem->node( elem->second_order_adjacent_vertex(son,v) ) );
00163 
00164               _mesh.node(elem->node(son)) = avg_position / n_adjacent_vertices;
00165             }
00166         }
00167     }
00168 }
00169 
00170 
00171 
00172 
00173 
00174 void LaplaceMeshSmoother::init()
00175 {
00176   switch (_mesh.mesh_dimension())
00177     {
00178 
00179       // TODO:[BSK] Fix this to work for refined meshes...  I think
00180       // the implementation was done quickly for Damien, who did not have
00181       // refined grids.  Fix it here and in the original Mesh member.
00182 
00183     case 2: // Stolen directly from build_L_graph in mesh_base.C
00184       {
00185         // Initialize space in the graph.  It is n_nodes long.  Each
00186         // node may be connected to an arbitrary number of other nodes
00187         // via edges.
00188         _graph.resize(_mesh.n_nodes());
00189 
00190         MeshBase::element_iterator       el  = _mesh.active_local_elements_begin();
00191         const MeshBase::element_iterator end = _mesh.active_local_elements_end();
00192 
00193         for (; el != end; ++el)
00194           {
00195             // Constant handle for the element
00196             const Elem* elem = *el;
00197 
00198             for (unsigned int s=0; s<elem->n_neighbors(); s++)
00199               {
00200                 // Only operate on sides which are on the
00201                 // boundary or for which the current element's
00202                 // id is greater than its neighbor's.
00203                 // Sides get only built once.
00204                 if ((elem->neighbor(s) == NULL) ||
00205                     (elem->id() > elem->neighbor(s)->id()))
00206                   {
00207                     UniquePtr<Elem> side(elem->build_side(s));
00208                     _graph[side->node(0)].push_back(side->node(1));
00209                     _graph[side->node(1)].push_back(side->node(0));
00210                   }
00211               }
00212           }
00213         _initialized = true;
00214         break;
00215       } // case 2
00216 
00217     case 3: // Stolen blatantly from build_L_graph in mesh_base.C
00218       {
00219         // Initialize space in the graph.
00220         _graph.resize(_mesh.n_nodes());
00221 
00222         MeshBase::element_iterator       el  = _mesh.active_local_elements_begin();
00223         const MeshBase::element_iterator end = _mesh.active_local_elements_end();
00224 
00225         for (; el != end; ++el)
00226           {
00227             // Shortcut notation for simplicity
00228             const Elem* elem = *el;
00229 
00230             for (unsigned int f=0; f<elem->n_neighbors(); f++) // Loop over faces
00231               if ((elem->neighbor(f) == NULL) ||
00232                   (elem->id() > elem->neighbor(f)->id()))
00233                 {
00234                   // We need a full (i.e. non-proxy) element for the face, since we will
00235                   // be looking at its sides as well!
00236                   UniquePtr<Elem> face = elem->build_side(f, /*proxy=*/false);
00237 
00238                   for (unsigned int s=0; s<face->n_neighbors(); s++) // Loop over face's edges
00239                     {
00240                       // Here we can use a proxy
00241                       UniquePtr<Elem> side = face->build_side(s);
00242 
00243                       // At this point, we just insert the node numbers
00244                       // again.  At the end we'll call sort and unique
00245                       // to make sure there are no duplicates
00246                       _graph[side->node(0)].push_back(side->node(1));
00247                       _graph[side->node(1)].push_back(side->node(0));
00248                     }
00249                 }
00250           }
00251 
00252         _initialized = true;
00253         break;
00254       } // case 3
00255 
00256     default:
00257       libmesh_error_msg("At this time it is not possible to smooth a dimension " << _mesh.mesh_dimension() << "mesh.  Aborting...");
00258     }
00259 
00260   // Done building graph from local elements.  Let's now allgather the
00261   // graph so that it is available on all processors for the actual
00262   // smoothing operation?
00263   this->allgather_graph();
00264 
00265   // In 3D, it's possible for > 2 processor partitions to meet
00266   // at a single edge, while in 2D only 2 processor partitions
00267   // share an edge.  Therefore the allgather'd graph in 3D may
00268   // now have duplicate entries and we need to remove them so
00269   // they don't foul up the averaging algorithm employed by the
00270   // Laplace smoother.
00271   for (unsigned i=0; i<_graph.size(); ++i)
00272     {
00273       // The std::unique algorithm removes duplicate *consecutive* elements from a range,
00274       // so it only makes sense to call it on a sorted range...
00275       std::sort(_graph[i].begin(), _graph[i].end());
00276       _graph[i].erase(std::unique(_graph[i].begin(), _graph[i].end()), _graph[i].end());
00277     }
00278 
00279 } // init()
00280 
00281 
00282 
00283 
00284 void LaplaceMeshSmoother::print_graph(std::ostream& out_stream) const
00285 {
00286   for (unsigned int i=0; i<_graph.size(); ++i)
00287     {
00288       out_stream << i << ": ";
00289       std::copy(_graph[i].begin(),
00290                 _graph[i].end(),
00291                 std::ostream_iterator<unsigned>(out_stream, " "));
00292       out_stream << std::endl;
00293     }
00294 }
00295 
00296 
00297 
00298 void LaplaceMeshSmoother::allgather_graph()
00299 {
00300   // The graph data structure is not well-suited for parallel communication,
00301   // so copy the graph into a single vector defined by:
00302   // NA A_0 A_1 ... A_{NA} | NB B_0 B_1 ... B_{NB} | NC C_0 C_1 ... C_{NC}
00303   // where:
00304   // * NA is the number of graph connections for node A
00305   // * A_0, A_1, etc. are the IDs connected to node A
00306   std::vector<dof_id_type> flat_graph;
00307 
00308   // Reserve at least enough space for each node to have zero entries
00309   flat_graph.reserve(_graph.size());
00310 
00311   for (std::size_t i=0; i<_graph.size(); ++i)
00312     {
00313       // First push back the number of entries for this node
00314       flat_graph.push_back (cast_int<dof_id_type>(_graph[i].size()));
00315 
00316       // Then push back all the IDs
00317       for (std::size_t j=0; j<_graph[i].size(); ++j)
00318         flat_graph.push_back(_graph[i][j]);
00319     }
00320 
00321   // // A copy of the flat graph (for printing only, delete me later)
00322   // std::vector<unsigned> copy_of_flat_graph(flat_graph);
00323 
00324   // Use the allgather routine to combine all the flat graphs on all processors
00325   _mesh.comm().allgather(flat_graph);
00326 
00327   // Now reconstruct _graph from the allgathered flat_graph.
00328 
00329   // // (Delete me later, the copy is just for printing purposes.)
00330   // std::vector<std::vector<unsigned > > copy_of_graph(_graph);
00331 
00332   // Make sure the old graph is cleared out
00333   _graph.clear();
00334   _graph.resize(_mesh.n_nodes());
00335 
00336   // Our current position in the allgather'd flat_graph
00337   std::size_t cursor=0;
00338 
00339   // There are n_nodes * n_processors vectors to read in total
00340   for (processor_id_type p=0; p<_mesh.n_processors(); ++p)
00341     for (dof_id_type node_ctr=0; node_ctr<_mesh.n_nodes(); ++node_ctr)
00342       {
00343         // Read the number of entries for this node, move cursor
00344         std::size_t n_entries = flat_graph[cursor++];
00345 
00346         // Reserve space for that many more entries, then push back
00347         _graph[node_ctr].reserve(_graph[node_ctr].size() + n_entries);
00348 
00349         // Read all graph connections for this node, move the cursor each time
00350         // Note: there might be zero entries but that's fine
00351         for (std::size_t i=0; i<n_entries; ++i)
00352           _graph[node_ctr].push_back(flat_graph[cursor++]);
00353       }
00354 
00355 
00356   //    // Print local graph to uniquely named file (debugging)
00357   //    {
00358   //      // Generate unique filename for this processor
00359   //      std::ostringstream oss;
00360   //      oss << "graph_filename_" << _mesh.processor_id() << ".txt";
00361   //      std::ofstream graph_stream(oss.str().c_str());
00362   //
00363   //      // Print the local non-flat graph
00364   //      std::swap(_graph, copy_of_graph);
00365   //      print_graph(graph_stream);
00366   //
00367   //      // Print the (local) flat graph for verification
00368   //      for (std::size_t i=0; i<copy_of_flat_graph.size(); ++i)
00369   //graph_stream << copy_of_flat_graph[i] << " ";
00370   //      graph_stream << "\n";
00371   //
00372   //      // Print the allgather'd grap for verification
00373   //      for (std::size_t i=0; i<flat_graph.size(); ++i)
00374   //graph_stream << flat_graph[i] << " ";
00375   //      graph_stream << "\n";
00376   //
00377   //      // Print the global non-flat graph
00378   //      std::swap(_graph, copy_of_graph);
00379   //      print_graph(graph_stream);
00380   //    }
00381 } // allgather_graph()
00382 
00383 } // namespace libMesh