libMesh
Loading...
Searching...
No Matches
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, const unsigned int n_iterations)
 Constructor.
 
 LaplaceMeshSmoother (UnstructuredMesh &mesh)
 Constructor.
 
virtual ~LaplaceMeshSmoother ()=default
 Destructor.
 
virtual void smooth () override
 Redefinition of the smooth function from the base class.
 
void smooth (unsigned int n_iterations)
 The actual smoothing function, gets called whenever the user specifies an actual number of smoothing iterations.
 
void init ()
 Initialization for the Laplace smoothing routine is basically identical to building an "L-graph" which is expensive.
 
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.
 

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.
 

Private Attributes

bool _initialized
 True if the L-graph has been created, false otherwise.
 
std::vector< std::vector< dof_id_type > > _graph
 Data structure for holding the L-graph.
 
unsigned int _n_iterations
 Number of smoothing iterations to perform.
 

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() [1/2]

libMesh::LaplaceMeshSmoother::LaplaceMeshSmoother ( UnstructuredMesh mesh,
const unsigned int  n_iterations 
)
explicit

Constructor.

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

Parameters
n_iterationsThe number of smoothing iterations to be performed.

Definition at line 38 of file mesh_smoother_laplace.C.

40 : MeshSmoother(mesh), _initialized(false), _n_iterations(n_iterations) {}
bool _initialized
True if the L-graph has been created, false otherwise.
unsigned int _n_iterations
Number of smoothing iterations to perform.
MeshSmoother(UnstructuredMesh &mesh)
Constructor.
MeshBase & mesh

◆ LaplaceMeshSmoother() [2/2]

libMesh::LaplaceMeshSmoother::LaplaceMeshSmoother ( UnstructuredMesh mesh)
inlineexplicit

Constructor.

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

Deprecated:
This constructor has been deprecated in favor of the (UnstructuredMesh, const unsigned int) constructor. By specifying the number of smoothing iterations in the constructor, there is no need to pass this parameter to the smooth method. As such, the smooth method's signature will match that of the parent class and the sister VariationalMeshSmoother class.

Definition at line 67 of file mesh_smoother_laplace.h.

69 libmesh_deprecated();
70 }
LaplaceMeshSmoother(UnstructuredMesh &mesh, const unsigned int n_iterations)
Constructor.

◆ ~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 293 of file mesh_smoother_laplace.C.

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

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

Referenced by init().

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

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

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().

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

280{
281 for (auto i : index_range(_graph))
282 {
283 out_stream << i << ": ";
284 std::copy(_graph[i].begin(),
285 _graph[i].end(),
286 std::ostream_iterator<unsigned>(out_stream, " "));
287 out_stream << std::endl;
288 }
289}
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:153

References _graph, and libMesh::index_range().

◆ smooth() [1/2]

void libMesh::LaplaceMeshSmoother::smooth ( )
overridevirtual

Redefinition of the smooth function from the base class.

Implements libMesh::MeshSmoother.

Definition at line 42 of file mesh_smoother_laplace.C.

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

References _graph, _initialized, libMesh::MeshSmoother::_mesh, _n_iterations, 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().

Referenced by libMesh::TetGenMeshInterface::pointset_convexhull(), smooth(), libMesh::TriangleInterface::triangulate(), libMesh::Poly2TriTriangulator::triangulate(), libMesh::TetGenMeshInterface::triangulate_conformingDelaunayMesh_carvehole(), and libMesh::TetGenMeshInterface::triangulate_pointset().

◆ 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.

Deprecated:
The number of iterations should be set in the class constructor. The parameterless smooth() override should be used to smooth.

Definition at line 154 of file mesh_smoother_laplace.C.

155{
156 libmesh_deprecated();
157 _n_iterations = n_iterations;
158 this->smooth();
159}
virtual void smooth() override
Redefinition of the smooth function from the base class.

References _n_iterations, and smooth().

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

Referenced by init(), and smooth().

◆ _mesh

UnstructuredMesh& libMesh::MeshSmoother::_mesh
protectedinherited

◆ _n_iterations

unsigned int libMesh::LaplaceMeshSmoother::_n_iterations
private

Number of smoothing iterations to perform.

Definition at line 131 of file mesh_smoother_laplace.h.

Referenced by smooth(), and smooth().


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