libMesh::LaplaceMeshSmoother Class Reference

#include <mesh_smoother_laplace.h>

Inheritance diagram for libMesh::LaplaceMeshSmoother:

List of all members.

Public Member Functions

 LaplaceMeshSmoother (UnstructuredMesh &mesh)
virtual ~LaplaceMeshSmoother ()
virtual void smooth ()
void smooth (unsigned int n_iterations)
void init ()
void print_graph (std::ostream &out=libMesh::out) const

Protected Attributes

UnstructuredMesh_mesh

Private Member Functions

void allgather_graph ()

Private Attributes

bool _initialized
std::vector< std::vector
< dof_id_type > > 
_graph

Detailed Description

This class defines the data structures necessary for Laplace smoothing. Note that this is a simple averaging smoother, which does NOT guarantee that points will be smoothed to valid locations, e.g. locations inside the boundary! This aspect could use work.

Author:
John W. Peterson
Date:
2002-2007

Definition at line 52 of file mesh_smoother_laplace.h.


Constructor & Destructor Documentation

libMesh::LaplaceMeshSmoother::LaplaceMeshSmoother ( UnstructuredMesh mesh  )  [explicit]

Constructor. Sets the constant mesh reference in the protected data section of the class.

Definition at line 36 of file mesh_smoother_laplace.C.

00037     : MeshSmoother(mesh),
00038       _initialized(false)
00039   {
00040   }

virtual libMesh::LaplaceMeshSmoother::~LaplaceMeshSmoother (  )  [inline, virtual]

Destructor.

Definition at line 65 of file mesh_smoother_laplace.h.

00065 {}


Member Function Documentation

void libMesh::LaplaceMeshSmoother::allgather_graph (  )  [private]

This function allgather's the (local) graph after it is computed on each processor by the init() function.

Definition at line 313 of file mesh_smoother_laplace.C.

References _graph, libMesh::MeshSmoother::_mesh, libMesh::Parallel::Communicator::allgather(), libMesh::CommWorld, libMesh::MeshBase::n_nodes(), and libMesh::n_processors().

Referenced by init().

00314   {
00315     // The graph data structure is not well-suited for parallel communication,
00316     // so copy the graph into a single vector defined by:
00317     // NA A_0 A_1 ... A_{NA} | NB B_0 B_1 ... B_{NB} | NC C_0 C_1 ... C_{NC}
00318     // where:
00319     // * NA is the number of graph connections for node A
00320     // * A_0, A_1, etc. are the IDs connected to node A
00321     std::vector<std::size_t> flat_graph;
00322 
00323     // Reserve at least enough space for each node to have zero entries
00324     flat_graph.reserve(_graph.size());
00325 
00326     for (std::size_t i=0; i<_graph.size(); ++i)
00327       {
00328         // First push back the number of entries for this node
00329         flat_graph.push_back (_graph[i].size());
00330 
00331         // Then push back all the IDs
00332         for (std::size_t j=0; j<_graph[i].size(); ++j)
00333           flat_graph.push_back(_graph[i][j]);
00334       }
00335 
00336     // // A copy of the flat graph (for printing only, delete me later)
00337     // std::vector<unsigned> copy_of_flat_graph(flat_graph);
00338 
00339     // Use the allgather routine to combine all the flat graphs on all processors
00340     CommWorld.allgather(flat_graph);
00341 
00342     // Now reconstruct _graph from the allgathered flat_graph.
00343 
00344     // // (Delete me later, the copy is just for printing purposes.)
00345     // std::vector<std::vector<unsigned > > copy_of_graph(_graph);
00346 
00347     // Make sure the old graph is cleared out
00348     _graph.clear();
00349     _graph.resize(_mesh.n_nodes());
00350 
00351     // Our current position in the allgather'd flat_graph
00352     std::size_t cursor=0;
00353 
00354     // There are n_nodes * n_processors vectors to read in total
00355     for (processor_id_type p=0; p<libMesh::n_processors(); ++p)
00356       for (dof_id_type node_ctr=0; node_ctr<_mesh.n_nodes(); ++node_ctr)
00357         {
00358           // Read the number of entries for this node, move cursor
00359           std::size_t n_entries = flat_graph[cursor++];
00360 
00361           // Reserve space for that many more entries, then push back
00362           _graph[node_ctr].reserve(_graph[node_ctr].size() + n_entries);
00363 
00364           // Read all graph connections for this node, move the cursor each time
00365           // Note: there might be zero entries but that's fine
00366           for (std::size_t i=0; i<n_entries; ++i)
00367             _graph[node_ctr].push_back(flat_graph[cursor++]);
00368         }
00369 
00370 
00371 //    // Print local graph to uniquely named file (debugging)
00372 //    {
00373 //      // Generate unique filename for this processor
00374 //      std::ostringstream oss;
00375 //      oss << "graph_filename_" << libMesh::processor_id() << ".txt";
00376 //      std::ofstream graph_stream(oss.str().c_str());
00377 //
00378 //      // Print the local non-flat graph
00379 //      std::swap(_graph, copy_of_graph);
00380 //      print_graph(graph_stream);
00381 //
00382 //      // Print the (local) flat graph for verification
00383 //      for (std::size_t i=0; i<copy_of_flat_graph.size(); ++i)
00384 //      graph_stream << copy_of_flat_graph[i] << " ";
00385 //      graph_stream << "\n";
00386 //
00387 //      // Print the allgather'd grap for verification
00388 //      for (std::size_t i=0; i<flat_graph.size(); ++i)
00389 //      graph_stream << flat_graph[i] << " ";
00390 //      graph_stream << "\n";
00391 //
00392 //      // Print the global non-flat graph
00393 //      std::swap(_graph, copy_of_graph);
00394 //      print_graph(graph_stream);
00395 //    }
00396   } // allgather_graph()

void libMesh::LaplaceMeshSmoother::init (  ) 

Initialization for the Laplace smoothing routine is basically identical to building an "L-graph" which is expensive. It's provided separately from the constructor since you may or may not want to build the L-graph on construction.

Definition at line 182 of file mesh_smoother_laplace.C.

References _graph, _initialized, libMesh::MeshSmoother::_mesh, libMesh::MeshBase::active_local_elements_begin(), libMesh::MeshBase::active_local_elements_end(), allgather_graph(), libMesh::Elem::build_side(), end, libMesh::err, libMesh::DofObject::id(), libMesh::MeshBase::mesh_dimension(), libMesh::Elem::n_neighbors(), libMesh::MeshBase::n_nodes(), libMesh::Elem::neighbor(), and side.

Referenced by smooth().

00183 {
00184   switch (_mesh.mesh_dimension())
00185     {
00186 
00187       // TODO:[BSK] Fix this to work for refined meshes...  I think
00188       // the implementation was done quickly for Damien, who did not have
00189       // refined grids.  Fix it here and in the original Mesh member.
00190 
00191     case 2: // Stolen directly from build_L_graph in mesh_base.C
00192       {
00193         // Initialize space in the graph.  It is n_nodes long.  Each
00194         // node may be connected to an arbitrary number of other nodes
00195         // via edges.
00196         _graph.resize(_mesh.n_nodes());
00197 
00198         MeshBase::element_iterator       el  = _mesh.active_local_elements_begin();
00199         const MeshBase::element_iterator end = _mesh.active_local_elements_end();
00200 
00201         for (; el != end; ++el)
00202           {
00203             // Constant handle for the element
00204             const Elem* elem = *el;
00205 
00206             for (unsigned int s=0; s<elem->n_neighbors(); s++)
00207               {
00208                 // Only operate on sides which are on the
00209                 // boundary or for which the current element's
00210                 // id is greater than its neighbor's.
00211                 // Sides get only built once.
00212                 if ((elem->neighbor(s) == NULL) ||
00213                     (elem->id() > elem->neighbor(s)->id()))
00214                   {
00215                     AutoPtr<Elem> side(elem->build_side(s));
00216                     _graph[side->node(0)].push_back(side->node(1));
00217                     _graph[side->node(1)].push_back(side->node(0));
00218                   }
00219               }
00220           }
00221         _initialized = true;
00222         break;
00223       } // case 2
00224 
00225     case 3: // Stolen blatantly from build_L_graph in mesh_base.C
00226       {
00227         // Initialize space in the graph.
00228         _graph.resize(_mesh.n_nodes());
00229 
00230         MeshBase::element_iterator       el  = _mesh.active_local_elements_begin();
00231         const MeshBase::element_iterator end = _mesh.active_local_elements_end();
00232 
00233         for (; el != end; ++el)
00234           {
00235             // Shortcut notation for simplicity
00236             const Elem* elem = *el;
00237 
00238             for (unsigned int f=0; f<elem->n_neighbors(); f++) // Loop over faces
00239               if ((elem->neighbor(f) == NULL) ||
00240                   (elem->id() > elem->neighbor(f)->id()))
00241                 {
00242                   // We need a full (i.e. non-proxy) element for the face, since we will
00243                   // be looking at its sides as well!
00244                   AutoPtr<Elem> face = elem->build_side(f, /*proxy=*/false);
00245 
00246                   for (unsigned int s=0; s<face->n_neighbors(); s++) // Loop over face's edges
00247                     {
00248                       // Here we can use a proxy
00249                       AutoPtr<Elem> side = face->build_side(s);
00250 
00251                       // At this point, we just insert the node numbers
00252                       // again.  At the end we'll call sort and unique
00253                       // to make sure there are no duplicates
00254                       _graph[side->node(0)].push_back(side->node(1));
00255                       _graph[side->node(1)].push_back(side->node(0));
00256                     }
00257                 }
00258           }
00259 
00260         _initialized = true;
00261         break;
00262       } // case 3
00263 
00264     default:
00265       {
00266         libMesh::err << "At this time it is not possible "
00267                       << "to smooth a dimension "
00268                       << _mesh.mesh_dimension()
00269                       << "mesh.  Aborting..."
00270                       << std::endl;
00271         libmesh_error();
00272       }
00273     }
00274 
00275   // Done building graph from local elements.  Let's now allgather the
00276   // graph so that it is available on all processors for the actual
00277   // smoothing operation?
00278   this->allgather_graph();
00279 
00280   // In 3D, it's possible for > 2 processor partitions to meet
00281   // at a single edge, while in 2D only 2 processor partitions
00282   // share an edge.  Therefore the allgather'd graph in 3D may
00283   // now have duplicate entries and we need to remove them so
00284   // they don't foul up the averaging algorithm employed by the
00285   // Laplace smoother.
00286   for (unsigned i=0; i<_graph.size(); ++i)
00287     {
00288       // The std::unique algorithm removes duplicate *consecutive* elements from a range,
00289       // so it only makes sense to call it on a sorted range...
00290       std::sort(_graph[i].begin(), _graph[i].end());
00291       _graph[i].erase(std::unique(_graph[i].begin(), _graph[i].end()), _graph[i].end());
00292     }
00293 
00294 } // init()

void libMesh::LaplaceMeshSmoother::print_graph ( std::ostream &  out = libMesh::out  )  const

Mainly for debugging, this function will print out the connectivity graph which has been created.

Definition at line 299 of file mesh_smoother_laplace.C.

References _graph, and end.

00300 {
00301   for (unsigned int i=0; i<_graph.size(); ++i)
00302     {
00303       out_stream << i << ": ";
00304       std::copy(_graph[i].begin(),
00305                 _graph[i].end(),
00306                 std::ostream_iterator<unsigned>(out_stream, " "));
00307       out_stream << std::endl;
00308     }
00309 }

void libMesh::LaplaceMeshSmoother::smooth ( unsigned int  n_iterations  ) 

The actual smoothing function, gets called whenever the user specifies an actual number of smoothing iterations.

Definition at line 45 of file mesh_smoother_laplace.C.

References _graph, _initialized, libMesh::MeshSmoother::_mesh, libMesh::MeshBase::active_elements_begin(), libMesh::MeshBase::active_elements_end(), libMesh::TypeVector< T >::add(), end, libMesh::err, libMesh::MeshTools::find_boundary_nodes(), libMesh::DofObject::id(), init(), libMesh::MeshBase::local_nodes_begin(), libMesh::MeshBase::local_nodes_end(), libMesh::Elem::n_nodes(), libMesh::MeshBase::n_nodes(), libMesh::Elem::n_second_order_adjacent_vertices(), libMesh::Elem::n_vertices(), libMesh::Elem::node(), libMesh::MeshBase::node(), libMesh::MeshBase::node_ptr(), libMesh::MeshBase::nodes_begin(), libMesh::MeshBase::nodes_end(), libMesh::MeshBase::point(), libMesh::processor_id(), libMesh::Real, libMesh::Elem::second_order_adjacent_vertex(), and libMesh::Parallel::sync_dofobject_data_by_id().

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     {
00059       libMesh::err << "MeshTools::find_boundary_nodes() returned incorrect length vector!" << std::endl;
00060       libmesh_error();
00061     }
00062 
00063   // We can only update the nodes after all new positions were
00064   // determined. We store the new positions here
00065   std::vector<Point> new_positions;
00066 
00067   for (unsigned int n=0; n<n_iterations; n++)
00068     {
00069       new_positions.resize(_mesh.n_nodes());
00070 
00071       {
00072         MeshBase::node_iterator       it     = _mesh.local_nodes_begin();
00073         const MeshBase::node_iterator it_end = _mesh.local_nodes_end();
00074         for (; it != it_end; ++it)
00075           {
00076             Node* node = *it;
00077 
00078             if (node == NULL)
00079               {
00080                 libMesh::err << "[" << libMesh::processor_id() << "]: Node iterator returned NULL pointer." << std::endl;
00081                 libmesh_error();
00082               }
00083 
00084             // leave the boundary intact
00085             // Only relocate the nodes which are vertices of an element
00086             // All other entries of _graph (the secondary nodes) are empty
00087             if (!on_boundary[node->id()] && (_graph[node->id()].size() > 0))
00088               {
00089                 Point avg_position(0.,0.,0.);
00090 
00091                 for (unsigned j=0; j<_graph[node->id()].size(); ++j)
00092                   {
00093                     // Will these nodal positions always be available
00094                     // or will they refer to remote nodes?  To be
00095                     // careful, we grab a pointer and test it against
00096                     // NULL.
00097                     Node* connected_node = _mesh.node_ptr(_graph[node->id()][j]);
00098 
00099                     if (connected_node == NULL)
00100                       {
00101                         libMesh::err << "Error! Libmesh returned NULL pointer for node " << _graph[connected_node->id()][j] << std::endl;
00102                         libmesh_error();
00103                       }
00104 
00105                     avg_position.add( *connected_node );
00106                   } // end for(j)
00107 
00108                 // Compute the average, store in the new_positions vector
00109                 new_positions[node->id()] = avg_position / static_cast<Real>(_graph[node->id()].size());
00110               } // end if
00111           } // end for
00112       } // end scope
00113 
00114 
00115       // now update the node positions (local node positions only)
00116       {
00117         MeshBase::node_iterator it           = _mesh.local_nodes_begin();
00118         const MeshBase::node_iterator it_end = _mesh.local_nodes_end();
00119         for (; it != it_end; ++it)
00120           {
00121             Node* node = *it;
00122 
00123             if (!on_boundary[node->id()] && (_graph[node->id()].size() > 0))
00124               {
00125                 // Should call Point::op=
00126                 // libMesh::out << "Setting node id " << node->id() << " to position " << new_positions[node->id()];
00127                 _mesh.node(node->id()) = new_positions[node->id()];
00128               }
00129           } // end for
00130       } // end scope
00131 
00132       // Now the nodes which are ghosts on this processor may have been moved on
00133       // the processors which own them.  So we need to synchronize with our neighbors
00134       // and get the most up-to-date positions for the ghosts.
00135       SyncNodalPositions sync_object(_mesh);
00136       Parallel::sync_dofobject_data_by_id(_mesh.nodes_begin(), _mesh.nodes_end(), sync_object);
00137 
00138     } // end for n_iterations
00139 
00140   // finally adjust the second order nodes (those located between vertices)
00141   // these nodes will be located between their adjacent nodes
00142   // do this element-wise
00143   MeshBase::element_iterator       el  = _mesh.active_elements_begin();
00144   const MeshBase::element_iterator end = _mesh.active_elements_end();
00145 
00146   for (; el != end; ++el)
00147     {
00148       // Constant handle for the element
00149       const Elem* elem = *el;
00150 
00151       // get the second order nodes (son)
00152       // their element indices start at n_vertices and go to n_nodes
00153       const unsigned int son_begin = elem->n_vertices();
00154       const unsigned int son_end   = elem->n_nodes();
00155 
00156       // loop over all second order nodes (son)
00157       for (unsigned int son=son_begin; son<son_end; son++)
00158         {
00159           // Don't smooth second-order nodes which are on the boundary
00160           if (!on_boundary[elem->node(son)])
00161             {
00162               const unsigned int n_adjacent_vertices =
00163                 elem->n_second_order_adjacent_vertices(son);
00164 
00165               // calculate the new position which is the average of the
00166               // position of the adjacent vertices
00167               Point avg_position(0,0,0);
00168               for (unsigned int v=0; v<n_adjacent_vertices; v++)
00169                 avg_position +=
00170                   _mesh.point( elem->node( elem->second_order_adjacent_vertex(son,v) ) );
00171 
00172               _mesh.node(elem->node(son)) = avg_position / n_adjacent_vertices;
00173             }
00174         }
00175     }
00176 }

virtual void libMesh::LaplaceMeshSmoother::smooth (  )  [inline, virtual]

Redefinition of the smooth function from the base class. All this does is call the smooth function in this class which takes an int, using a default value of 1.

Implements libMesh::MeshSmoother.

Definition at line 73 of file mesh_smoother_laplace.h.

References smooth().

Referenced by smooth().

00073 { this->smooth(1); }


Member Data Documentation

std::vector<std::vector<dof_id_type> > libMesh::LaplaceMeshSmoother::_graph [private]

Data structure for holding the L-graph

Definition at line 112 of file mesh_smoother_laplace.h.

Referenced by allgather_graph(), init(), print_graph(), and smooth().

True if the L-graph has been created, false otherwise.

Definition at line 107 of file mesh_smoother_laplace.h.

Referenced by init(), and smooth().


The documentation for this class was generated from the following files:

Site Created By: libMesh Developers
Last modified: February 05 2013 19:55:29 UTC

Hosted By:
SourceForge.net Logo