mesh_smoother_laplace.C
Go to the documentation of this file.00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2012 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 { 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 } 00177 00178 00179 00180 00181 00182 void LaplaceMeshSmoother::init() 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() 00295 00296 00297 00298 00299 void LaplaceMeshSmoother::print_graph(std::ostream& out_stream) const 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 } 00310 00311 00312 00313 void LaplaceMeshSmoother::allgather_graph() 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() 00397 00398 } // namespace libMesh
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:47 UTC
Hosted By: