void LaplaceMeshSmoother::init() { switch (_mesh.mesh_dimension()) { // TODO:[BSK] Fix this to work for refined meshes... I think // the implementation was done quickly for Damien, who did not have // refined grids. Fix it here and in the original Mesh member. case 2: // Stolen directly from build_L_graph in mesh_base.C { // Initialize space in the graph. It is indexed by node id. // Each node may be connected to an arbitrary number of other // nodes via edges. _graph.resize(_mesh.max_node_id()); MeshBase::element_iterator el = _mesh.active_local_elements_begin(); const MeshBase::element_iterator end = _mesh.active_local_elements_end(); for (; el != end; ++el) { // Constant handle for the element const Elem * elem = *el; for (unsigned int s=0; s<elem->n_neighbors(); s++) { // Only operate on sides which are on the // boundary or for which the current element's // id is greater than its neighbor's. // Sides get only built once. if ((elem->neighbor_ptr(s) == libmesh_nullptr) || (elem->id() > elem->neighbor_ptr(s)->id())) { UniquePtr<const Elem> side(elem->build_side_ptr(s)); _graph[side->node_id(0)].push_back(side->node_id(1)); _graph[side->node_id(1)].push_back(side->node_id(0)); } } } _initialized = true; break; } // case 2 case 3: // Stolen blatantly from build_L_graph in mesh_base.C { // Initialize space in the graph. _graph.resize(_mesh.max_node_id()); MeshBase::element_iterator el = _mesh.active_local_elements_begin(); const MeshBase::element_iterator end = _mesh.active_local_elements_end(); for (; el != end; ++el) { // Shortcut notation for simplicity const Elem * elem = *el; for (unsigned int f=0; f<elem->n_neighbors(); f++) // Loop over faces if ((elem->neighbor_ptr(f) == libmesh_nullptr) || (elem->id() > elem->neighbor_ptr(f)->id())) { // We need a full (i.e. non-proxy) element for the face, since we will // be looking at its sides as well! UniquePtr<const Elem> face = elem->build_side_ptr(f, /*proxy=*/false); for (unsigned int s=0; s<face->n_neighbors(); s++) // Loop over face's edges { // Here we can use a proxy UniquePtr<const Elem> side = face->build_side_ptr(s); // At this point, we just insert the node numbers // again. At the end we'll call sort and unique // to make sure there are no duplicates _graph[side->node_id(0)].push_back(side->node_id(1)); _graph[side->node_id(1)].push_back(side->node_id(0)); } } } _initialized = true; break; } // case 3 default: libmesh_error_msg("At this time it is not possible to smooth a dimension " << _mesh.mesh_dimension() << "mesh. Aborting..."); } // Done building graph from local elements. Let's now allgather the // graph so that it is available on all processors for the actual // smoothing operation? this->allgather_graph(); // In 3D, it's possible for > 2 processor partitions to meet // at a single edge, while in 2D only 2 processor partitions // share an edge. Therefore the allgather'd graph in 3D may // now have duplicate entries and we need to remove them so // they don't foul up the averaging algorithm employed by the // Laplace smoother. for (unsigned i=0; i<_graph.size(); ++i) { // The std::unique algorithm removes duplicate *consecutive* elements from a range, // so it only makes sense to call it on a sorted range... std::sort(_graph[i].begin(), _graph[i].end()); _graph[i].erase(std::unique(_graph[i].begin(), _graph[i].end()), _graph[i].end()); } } // init()