libMesh
Public Member Functions | Protected Attributes | Private Member Functions | Private Attributes | List of all members
libMesh::LaplaceMeshSmoother Class Reference

This class defines the data structures necessary for Laplace smoothing. More...

#include <mesh_smoother_laplace.h>

Inheritance diagram for libMesh::LaplaceMeshSmoother:
[legend]

Public Member Functions

 LaplaceMeshSmoother (UnstructuredMesh &mesh)
 Constructor. More...
 
virtual ~LaplaceMeshSmoother ()=default
 Destructor. More...
 
virtual void smooth () override
 Redefinition of the smooth function from the base class. More...
 
void smooth (unsigned int n_iterations)
 The actual smoothing function, gets called whenever the user specifies an actual number of smoothing iterations. More...
 
void init ()
 Initialization for the Laplace smoothing routine is basically identical to building an "L-graph" which is expensive. More...
 
void print_graph (std::ostream &out_stream=libMesh::out) const
 Mainly for debugging, this function will print out the connectivity graph which has been created. More...
 

Protected Attributes

UnstructuredMesh_mesh
 

Private Member Functions

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

Private Attributes

bool _initialized
 True if the L-graph has been created, false otherwise. More...
 
std::vector< std::vector< dof_id_type > > _graph
 Data structure for holding the L-graph. More...
 

Detailed Description

This class defines the data structures necessary for Laplace smoothing.

Note
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 44 of file mesh_smoother_laplace.h.

Constructor & Destructor Documentation

◆ LaplaceMeshSmoother()

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

Constructor.

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

Definition at line 37 of file mesh_smoother_laplace.C.

38  : MeshSmoother(mesh),
39  _initialized(false)
40 {
41 }
bool _initialized
True if the L-graph has been created, false otherwise.
MeshBase & mesh
MeshSmoother(UnstructuredMesh &mesh)
Constructor.
Definition: mesh_smoother.h:46

◆ ~LaplaceMeshSmoother()

virtual libMesh::LaplaceMeshSmoother::~LaplaceMeshSmoother ( )
virtualdefault

Destructor.

Member Function Documentation

◆ allgather_graph()

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 289 of file mesh_smoother_laplace.C.

References _graph, libMesh::MeshSmoother::_mesh, TIMPI::Communicator::allgather(), libMesh::ParallelObject::comm(), libMesh::make_range(), libMesh::MeshBase::max_node_id(), and libMesh::ParallelObject::n_processors().

Referenced by init().

290 {
291  // The graph data structure is not well-suited for parallel communication,
292  // so copy the graph into a single vector defined by:
293  // NA A_0 A_1 ... A_{NA} | NB B_0 B_1 ... B_{NB} | NC C_0 C_1 ... C_{NC}
294  // where:
295  // * NA is the number of graph connections for node A
296  // * A_0, A_1, etc. are the IDs connected to node A
297  std::vector<dof_id_type> flat_graph;
298 
299  // Reserve at least enough space for each node to have zero entries
300  flat_graph.reserve(_graph.size());
301 
302  for (const auto & id_vec : _graph)
303  {
304  // First push back the number of entries for this node
305  flat_graph.push_back (cast_int<dof_id_type>(id_vec.size()));
306 
307  // Then push back all the IDs
308  for (const auto & dof : id_vec)
309  flat_graph.push_back(dof);
310  }
311 
312  // // A copy of the flat graph (for printing only, delete me later)
313  // std::vector<unsigned> copy_of_flat_graph(flat_graph);
314 
315  // Use the allgather routine to combine all the flat graphs on all processors
316  _mesh.comm().allgather(flat_graph);
317 
318  // Now reconstruct _graph from the allgathered flat_graph.
319 
320  // // (Delete me later, the copy is just for printing purposes.)
321  // std::vector<std::vector<unsigned >> copy_of_graph(_graph);
322 
323  // Make sure the old graph is cleared out
324  _graph.clear();
325  const auto max_node_id = _mesh.max_node_id();
326  _graph.resize(max_node_id);
327 
328  // Our current position in the allgather'd flat_graph
329  std::size_t cursor=0;
330 
331  // There are max_node_id * n_processors entries to read in total
332  const auto n_procs = _mesh.n_processors();
333  for (processor_id_type p = 0; p != n_procs; ++p)
334  for (dof_id_type node_ctr : make_range(max_node_id))
335  {
336  // Read the number of entries for this node, move cursor
337  std::size_t n_entries = flat_graph[cursor++];
338 
339  // Reserve space for that many more entries, then push back
340  _graph[node_ctr].reserve(_graph[node_ctr].size() + n_entries);
341 
342  // Read all graph connections for this node, move the cursor each time
343  // Note: there might be zero entries but that's fine
344  for (std::size_t i=0; i<n_entries; ++i)
345  _graph[node_ctr].push_back(flat_graph[cursor++]);
346  }
347 
348  // // Print local graph to uniquely named file (debugging)
349  // {
350  // // Generate unique filename for this processor
351  // std::ostringstream oss;
352  // oss << "graph_filename_" << _mesh.processor_id() << ".txt";
353  // std::ofstream graph_stream(oss.str().c_str());
354  //
355  // // Print the local non-flat graph
356  // std::swap(_graph, copy_of_graph);
357  // print_graph(graph_stream);
358  //
359  // // Print the (local) flat graph for verification
360  // for (const auto & dof : copy_of_flat_graph)
361  // graph_stream << dof << " ";
362  // graph_stream << "\n";
363  //
364  // // Print the allgather'd grap for verification
365  // for (const auto & dof : flat_graph)
366  // graph_stream << dof << " ";
367  // graph_stream << "\n";
368  //
369  // // Print the global non-flat graph
370  // std::swap(_graph, copy_of_graph);
371  // print_graph(graph_stream);
372  // }
373 } // allgather_graph()
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
std::vector< std::vector< dof_id_type > > _graph
Data structure for holding the L-graph.
const Parallel::Communicator & comm() const
UnstructuredMesh & _mesh
Definition: mesh_smoother.h:61
uint8_t processor_id_type
processor_id_type n_processors() const
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:134
virtual dof_id_type max_node_id() const =0
uint8_t dof_id_type
Definition: id_types.h:67

◆ init()

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 159 of file mesh_smoother_laplace.C.

References _graph, _initialized, libMesh::MeshSmoother::_mesh, allgather_graph(), libMesh::MeshBase::max_node_id(), libMesh::MeshBase::mesh_dimension(), libMesh::Elem::node_id(), libMesh::ParallelObject::processor_id(), and libMesh::Elem::side_index_range().

Referenced by smooth().

160 {
161  // For avoiding extraneous element side construction
162  ElemSideBuilder side_builder;
163 
164  switch (_mesh.mesh_dimension())
165  {
166 
167  // TODO:[BSK] Fix this to work for refined meshes... I think
168  // the implementation was done quickly for Damien, who did not have
169  // refined grids. Fix it here and in the original Mesh member.
170 
171  case 2: // Stolen directly from build_L_graph in mesh_base.C
172  {
173  // Initialize space in the graph. It is indexed by node id.
174  // Each node may be connected to an arbitrary number of other
175  // nodes via edges.
176  _graph.resize(_mesh.max_node_id());
177 
178  auto elem_to_graph =
179  [this, &side_builder](const Elem & elem) {
180  for (auto s : elem.side_index_range())
181  {
182  // Only operate on sides which are on the
183  // boundary or for which the current element's
184  // id is greater than its neighbor's.
185  // Sides get only built once.
186  if ((elem.neighbor_ptr(s) == nullptr) ||
187  (elem.id() > elem.neighbor_ptr(s)->id()))
188  {
189  const Elem & side = side_builder(elem, s);
190  _graph[side.node_id(0)].push_back(side.node_id(1));
191  _graph[side.node_id(1)].push_back(side.node_id(0));
192  }
193  }
194  };
195 
196  for (auto & elem : _mesh.active_local_element_ptr_range())
197  elem_to_graph(*elem);
198 
199  if (!_mesh.processor_id())
200  for (auto & elem : _mesh.active_unpartitioned_element_ptr_range())
201  elem_to_graph(*elem);
202 
203  _initialized = true;
204  break;
205  } // case 2
206 
207  case 3: // Stolen blatantly from build_L_graph in mesh_base.C
208  {
209  // Extra builder for the face elements
210  ElemSideBuilder face_builder;
211 
212  // Initialize space in the graph.
213  _graph.resize(_mesh.max_node_id());
214 
215  auto elem_to_graph =
216  [this, &side_builder, &face_builder](const Elem & elem) {
217  for (auto f : elem.side_index_range()) // Loop over faces
218  if ((elem.neighbor_ptr(f) == nullptr) ||
219  (elem.id() > elem.neighbor_ptr(f)->id()))
220  {
221  const Elem & face = face_builder(elem, f);
222 
223  for (auto s : face.side_index_range()) // Loop over face's edges
224  {
225  const Elem & side = side_builder(face, s);
226 
227  // At this point, we just insert the node numbers
228  // again. At the end we'll call sort and unique
229  // to make sure there are no duplicates
230  _graph[side.node_id(0)].push_back(side.node_id(1));
231  _graph[side.node_id(1)].push_back(side.node_id(0));
232  }
233  }
234  };
235 
236  for (auto & elem : _mesh.active_local_element_ptr_range())
237  elem_to_graph(*elem);
238 
239  if (!_mesh.processor_id())
240  for (auto & elem : _mesh.active_unpartitioned_element_ptr_range())
241  elem_to_graph(*elem);
242 
243  _initialized = true;
244  break;
245  } // case 3
246 
247  default:
248  libmesh_error_msg("At this time it is not possible to smooth a dimension " << _mesh.mesh_dimension() << "mesh. Aborting...");
249  }
250 
251  // Done building graph from local and/or unpartitioned elements.
252  // Let's now allgather the graph so that it is available on all
253  // processors for the actual smoothing operation.
254  this->allgather_graph();
255 
256  // In 3D, it's possible for > 2 processor partitions to meet
257  // at a single edge, while in 2D only 2 processor partitions
258  // share an edge. Therefore the allgather'd graph in 3D may
259  // now have duplicate entries and we need to remove them so
260  // they don't foul up the averaging algorithm employed by the
261  // Laplace smoother.
262  for (auto & id_vec : _graph)
263  {
264  // The std::unique algorithm removes duplicate *consecutive* elements from a range,
265  // so it only makes sense to call it on a sorted range...
266  std::sort(id_vec.begin(), id_vec.end());
267  id_vec.erase(std::unique(id_vec.begin(), id_vec.end()), id_vec.end());
268  }
269 
270 } // init()
std::vector< std::vector< dof_id_type > > _graph
Data structure for holding the L-graph.
bool _initialized
True if the L-graph has been created, false otherwise.
UnstructuredMesh & _mesh
Definition: mesh_smoother.h:61
void allgather_graph()
This function allgather&#39;s the (local) graph after it is computed on each processor by the init() func...
unsigned int mesh_dimension() const
Definition: mesh_base.C:324
virtual dof_id_type max_node_id() const =0
processor_id_type processor_id() const

◆ print_graph()

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

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

Definition at line 275 of file mesh_smoother_laplace.C.

References _graph, and libMesh::index_range().

276 {
277  for (auto i : index_range(_graph))
278  {
279  out_stream << i << ": ";
280  std::copy(_graph[i].begin(),
281  _graph[i].end(),
282  std::ostream_iterator<unsigned>(out_stream, " "));
283  out_stream << std::endl;
284  }
285 }
std::vector< std::vector< dof_id_type > > _graph
Data structure for holding the L-graph.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:111

◆ smooth() [1/2]

virtual void libMesh::LaplaceMeshSmoother::smooth ( )
inlineoverridevirtual

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 65 of file mesh_smoother_laplace.h.

References smooth().

Referenced by smooth(), libMesh::TriangleInterface::triangulate(), and libMesh::Poly2TriTriangulator::triangulate().

65 { this->smooth(1); }
virtual void smooth() override
Redefinition of the smooth function from the base class.

◆ smooth() [2/2]

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 46 of file mesh_smoother_laplace.C.

References _graph, _initialized, libMesh::MeshSmoother::_mesh, libMesh::TypeVector< T >::add(), libMesh::as_range(), libMesh::ParallelObject::comm(), libMesh::MeshTools::find_block_boundary_nodes(), libMesh::MeshTools::find_boundary_nodes(), init(), libMesh::DofObject::invalid_processor_id, libMesh::MeshBase::max_node_id(), libMesh::MeshBase::node_ref(), libMesh::MeshBase::point(), libMesh::Real, and libMesh::Parallel::sync_dofobject_data_by_id().

47 {
48  if (!_initialized)
49  this->init();
50 
51  // Don't smooth the nodes on the boundary...
52  // this would change the mesh geometry which
53  // is probably not something we want!
54  auto on_boundary = MeshTools::find_boundary_nodes(_mesh);
55 
56  // Also: don't smooth block boundary nodes
57  auto on_block_boundary = MeshTools::find_block_boundary_nodes(_mesh);
58 
59  // Merge them
60  on_boundary.insert(on_block_boundary.begin(), on_block_boundary.end());
61 
62  // We can only update the nodes after all new positions were
63  // determined. We store the new positions here
64  std::vector<Point> new_positions;
65 
66  for (unsigned int n=0; n<n_iterations; n++)
67  {
68  new_positions.resize(_mesh.max_node_id());
69 
70  auto calculate_new_position = [this, &on_boundary, &new_positions](const Node * node) {
71  // leave the boundary intact
72  // Only relocate the nodes which are vertices of an element
73  // All other entries of _graph (the secondary nodes) are empty
74  if (!on_boundary.count(node->id()) && (_graph[node->id()].size() > 0))
75  {
76  Point avg_position(0.,0.,0.);
77 
78  for (const auto & connected_id : _graph[node->id()])
79  {
80  // Will these nodal positions always be available
81  // or will they refer to remote nodes? This will
82  // fail an assertion in the latter case, which
83  // shouldn't occur if DistributedMesh is working
84  // correctly.
85  const Point & connected_node = _mesh.point(connected_id);
86 
87  avg_position.add( connected_node );
88  } // end for (j)
89 
90  // Compute the average, store in the new_positions vector
91  new_positions[node->id()] = avg_position / static_cast<Real>(_graph[node->id()].size());
92  } // end if
93  };
94 
95  // calculate new node positions (local and unpartitioned nodes only)
96  for (auto & node : _mesh.local_node_ptr_range())
97  calculate_new_position(node);
98 
99  for (auto & node : as_range(_mesh.pid_nodes_begin(DofObject::invalid_processor_id),
100  _mesh.pid_nodes_end(DofObject::invalid_processor_id)))
101  calculate_new_position(node);
102 
103 
104  // now update the node positions (local and unpartitioned nodes only)
105  for (auto & node : _mesh.local_node_ptr_range())
106  if (!on_boundary.count(node->id()) && (_graph[node->id()].size() > 0))
107  *node = new_positions[node->id()];
108 
109  for (auto & node : as_range(_mesh.pid_nodes_begin(DofObject::invalid_processor_id),
110  _mesh.pid_nodes_end(DofObject::invalid_processor_id)))
111  if (!on_boundary.count(node->id()) && (_graph[node->id()].size() > 0))
112  *node = new_positions[node->id()];
113 
114  // Now the nodes which are ghosts on this processor may have been moved on
115  // the processors which own them. So we need to synchronize with our neighbors
116  // and get the most up-to-date positions for the ghosts.
117  SyncNodalPositions sync_object(_mesh);
119  (_mesh.comm(), _mesh.nodes_begin(), _mesh.nodes_end(), sync_object);
120 
121  } // end for n_iterations
122 
123  // finally adjust the second order nodes (those located between vertices)
124  // these nodes will be located between their adjacent nodes
125  // do this element-wise
126  for (auto & elem : _mesh.active_element_ptr_range())
127  {
128  // get the second order nodes (son)
129  // their element indices start at n_vertices and go to n_nodes
130  const unsigned int son_begin = elem->n_vertices();
131  const unsigned int son_end = elem->n_nodes();
132 
133  // loop over all second order nodes (son)
134  for (unsigned int son=son_begin; son<son_end; son++)
135  {
136  // Don't smooth second-order nodes which are on the boundary
137  if (!on_boundary.count(elem->node_id(son)))
138  {
139  const unsigned int n_adjacent_vertices =
140  elem->n_second_order_adjacent_vertices(son);
141 
142  // calculate the new position which is the average of the
143  // position of the adjacent vertices
144  Point avg_position(0,0,0);
145  for (unsigned int v=0; v<n_adjacent_vertices; v++)
146  avg_position +=
147  _mesh.point( elem->node_id( elem->second_order_adjacent_vertex(son,v) ) );
148 
149  _mesh.node_ref(elem->node_id(son)) = avg_position / n_adjacent_vertices;
150  }
151  }
152  }
153 }
std::vector< std::vector< dof_id_type > > _graph
Data structure for holding the L-graph.
bool _initialized
True if the L-graph has been created, false otherwise.
std::unordered_set< dof_id_type > find_block_boundary_nodes(const MeshBase &mesh)
Returns a std::set containing Node IDs for all of the block boundary nodes.
Definition: mesh_tools.C:536
const Parallel::Communicator & comm() const
void init()
Initialization for the Laplace smoothing routine is basically identical to building an "L-graph" whic...
UnstructuredMesh & _mesh
Definition: mesh_smoother.h:61
void add(const TypeVector< T2 > &)
Add to this vector without creating a temporary.
Definition: type_vector.h:627
std::unordered_set< dof_id_type > find_boundary_nodes(const MeshBase &mesh)
Calling this function on a 2D mesh will convert all the elements to triangles.
Definition: mesh_tools.C:516
static const processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition: dof_object.h:488
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:575
virtual const Point & point(const dof_id_type i) const =0
virtual dof_id_type max_node_id() const =0

Member Data Documentation

◆ _graph

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

Data structure for holding the L-graph.

Definition at line 104 of file mesh_smoother_laplace.h.

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

◆ _initialized

bool libMesh::LaplaceMeshSmoother::_initialized
private

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

Definition at line 99 of file mesh_smoother_laplace.h.

Referenced by init(), and smooth().

◆ _mesh

UnstructuredMesh& libMesh::MeshSmoother::_mesh
protectedinherited

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