Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
LloydRelaxation.cpp File Reference

Example demonstrating Lloyd relaxation for mesh smoothing. More...

#include "moab/Core.hpp"
#include "moab/Skinner.hpp"
#include "moab/CN.hpp"
#include "moab/CartVect.hpp"
#include <iostream>
#include <sstream>
+ Include dependency graph for LloydRelaxation.cpp:

Go to the source code of this file.

Functions

ErrorCode perform_lloyd_relaxation (Interface *mb, Range &verts, Range &cells, Tag fixed, int num_its, int report_its)
 
int main (int argc, char **argv)
 

Variables

string test_file_name = string( MESH_DIR ) + string( "/surfrandomtris-4part.h5m" )
 

Detailed Description

Example demonstrating Lloyd relaxation for mesh smoothing.

This example shows how to:

  • Load a parallel mesh with ghost layers
  • Perform Lloyd relaxation iterations for mesh smoothing
  • Handle fixed vertices (e.g., boundary vertices)
  • Exchange ghost data between processors
  • Measure convergence based on vertex movement
  • Write the smoothed mesh to a parallel file

Lloyd relaxation is a technique to smooth out a mesh by iteratively moving vertices to the centroids of their connected cells.

Author
MOAB Development Team
Date
2024

Perform Lloyd relaxation on a mesh and its dual
To run: mpiexec -np np LloydRelaxation [filename]
Briefly, Lloyd relaxation is a technique to smooth out a mesh. The centroid of each cell is computed from its vertex positions, then vertices are placed at the average of their connected cells' centroids.

In the parallel algorithm, an extra ghost layer of cells is exchanged. This allows us to compute the centroids for boundary cells on each processor where they appear; this eliminates the need for one round of data exchange (for those centroids) between processors. New vertex positions must be sent from owning processors to processors sharing those vertices. Convergence is measured as the maximum distance moved by any vertex.

In this implementation, a fixed number of iterations is performed. The final mesh is output to 'lloydfinal.h5m' in the current directory (H5M format must be used since the file is written in parallel).

Parameters
argcNumber of command line arguments
argvCommand line arguments array
Returns
0 on success, 1 on failure

Definition in file LloydRelaxation.cpp.

Function Documentation

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 60 of file LloydRelaxation.cpp.

61 {
62  int num_its = 10;
63  int report_its = 1;
64 
65 #ifdef MOAB_HAVE_MPI
66  MPI_Init( &argc, &argv );
67 #endif
68 
69  // Need option handling here for input filename
70  if( argc > 1 )
71  {
72  // User has input a mesh file
73  test_file_name = argv[1];
74  }
75 
76  // Get MOAB instance
77  Interface* mb = new( std::nothrow ) Core;
78  if( NULL == mb ) return 1;
79 
80 #ifdef MOAB_HAVE_MPI
81  // Get the ParallelComm instance
82  ParallelComm* pcomm = new ParallelComm( mb, MPI_COMM_WORLD );
83  int nprocs = pcomm->size();
84 #else
85  int nprocs = 1;
86 #endif
87  string options;
88  if( nprocs > 1 ) // If reading in parallel, need to tell it how
89  options = "PARALLEL=READ_PART;PARTITION=PARALLEL_PARTITION;PARALLEL_RESOLVE_SHARED_ENTS;"
90  "PARALLEL_GHOSTS=2.0.1;DEBUG_IO=0;DEBUG_PIO=0";
91 
92  // Load the test file with specified options
93  MB_CHK_ERR( mb->load_file( test_file_name.c_str(), 0, options.c_str() ) );
94 
95  // Make tag to specify fixed vertices, since it's input to the algorithm; use a default value of
96  // non-fixed so we only need to set the fixed tag for skin vertices
97  Tag fixed;
98  int def_val = 0;
99  MB_CHK_ERR( mb->tag_get_handle( "fixed", 1, MB_TYPE_INTEGER, fixed, MB_TAG_CREAT | MB_TAG_DENSE, &def_val ) );
100 
101  // Get all vertices and faces
102  Range verts, faces, skin_verts;
103  MB_CHK_ERR( mb->get_entities_by_type( 0, MBVERTEX, verts ) );
104  MB_CHK_ERR( mb->get_entities_by_dimension( 0, 2, faces ) );
105 
106  // Get the skin vertices of those faces and mark them as fixed; we don't want to fix the
107  // vertices on a part boundary, but since we exchanged a layer of ghost faces, those vertices
108  // aren't on the skin locally ok to mark non-owned skin vertices too, I won't move those anyway
109  // use MOAB's skinner class to find the skin
110  Skinner skinner( mb );
111  MB_CHK_ERR(
112  skinner.find_skin( 0, faces, true, skin_verts ) ); // 'true' param indicates we want vertices back, not faces
113 
114  vector< int > fix_tag( skin_verts.size(), 1 ); // Initialized to 1 to indicate fixed
115  MB_CHK_ERR( mb->tag_set_data( fixed, skin_verts, &fix_tag[0] ) );
116 
117  // Now perform the Lloyd relaxation
118  MB_CHK_ERR( perform_lloyd_relaxation( mb, verts, faces, fixed, num_its, report_its ) );
119 
120  // Delete fixed tag, since we created it here
121  MB_CHK_ERR( mb->tag_delete( fixed ) );
122 
123  // Output file, using parallel write
124 
125 #ifdef MOAB_HAVE_MPI
126  options = "PARALLEL=WRITE_PART";
127 #endif
128 
129  MB_CHK_ERR( mb->write_file( "lloydfinal.h5m", NULL, options.c_str() ) );
130 
131  // Delete MOAB instance
132  delete mb;
133 
134 #ifdef MOAB_HAVE_MPI
135  MPI_Finalize();
136 #endif
137 
138  return 0;
139 }

References moab::Skinner::find_skin(), moab::Core::get_entities_by_dimension(), moab::Core::get_entities_by_type(), moab::Core::load_file(), mb, MB_CHK_ERR, MB_TAG_CREAT, MB_TAG_DENSE, MB_TYPE_INTEGER, MBVERTEX, perform_lloyd_relaxation(), moab::Range::size(), moab::ParallelComm::size(), moab::Core::tag_delete(), moab::Core::tag_get_handle(), moab::Core::tag_set_data(), test_file_name, and moab::Core::write_file().

◆ perform_lloyd_relaxation()

ErrorCode perform_lloyd_relaxation ( Interface mb,
Range verts,
Range cells,
Tag  fixed,
int  num_its,
int  report_its 
)

Definition at line 141 of file LloydRelaxation.cpp.

142 {
143  ErrorCode rval;
144 
145 #ifdef MOAB_HAVE_MPI
146  ParallelComm* pcomm = ParallelComm::get_pcomm( mb, 0 );
147  int nprocs = pcomm->size();
148 #else
149  int nprocs = 1;
150 #endif
151 
152  // Perform Lloyd relaxation:
153  // 1. Setup: set vertex centroids from vertex coords; filter to owned verts; get fixed tags
154 
155  // Get all verts coords into tag; don't need to worry about filtering out fixed verts,
156  // we'll just be setting to their fixed coords
157  vector< double > vcentroids( 3 * verts.size() );
158  MB_CHK_ERR( mb->get_coords( verts, &vcentroids[0] ) );
159 
160  Tag centroid;
161  MB_CHK_ERR( mb->tag_get_handle( "centroid", 3, MB_TYPE_DOUBLE, centroid, MB_TAG_CREAT | MB_TAG_DENSE ) );
162  MB_CHK_ERR( mb->tag_set_data( centroid, verts, &vcentroids[0] ) );
163 
164  // Filter verts down to owned ones and get fixed tag for them
165  Range owned_verts, shared_owned_verts;
166  if( nprocs > 1 )
167  {
168 #ifdef MOAB_HAVE_MPI
169  MB_CHK_ERR( pcomm->filter_pstatus( verts, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &owned_verts ) );
170 #endif
171  }
172  else
173  owned_verts = verts;
174  vector< int > fix_tag( owned_verts.size() );
175  MB_CHK_ERR( mb->tag_get_data( fixed, owned_verts, &fix_tag[0] ) );
176 
177  // Now fill vcentroids array with positions of just owned vertices, since those are the ones
178  // we're actually computing
179  vcentroids.resize( 3 * owned_verts.size() );
180  MB_CHK_ERR( mb->tag_get_data( centroid, owned_verts, &vcentroids[0] ) );
181 
182 #ifdef MOAB_HAVE_MPI
183  // Get shared owned verts, for exchanging tags
184  MB_CHK_ERR( pcomm->get_shared_entities( -1, shared_owned_verts, 0, false, true ) );
185  // Workaround: if no shared owned verts, put a non-shared one in the list, to prevent exchanging
186  // tags for all shared entities
187  if( shared_owned_verts.empty() ) shared_owned_verts.insert( *verts.begin() );
188 #endif
189 
190  // Some declarations for later iterations
191  vector< double > fcentroids( 3 * faces.size() ); // fcentroids for face centroids
192  vector< double > ctag( 3 * CN::MAX_NODES_PER_ELEMENT ); // Temporary coordinate storage for verts bounding a face
193  const EntityHandle* conn; // const ptr & size to face connectivity
194  int nconn;
195  Range::iterator fit, vit; // For iterating over faces, verts
196  int f, v; // For indexing into centroid vectors
197  vector< EntityHandle > adj_faces; // Used in vertex iteration
198 
199  // 2. For num_its iterations:
200  for( int nit = 0; nit < num_its; nit++ )
201  {
202  double mxdelta = 0.0;
203 
204  // 2a. For each face: centroid = sum(vertex centroids)/num_verts_in_cell
205  for( fit = faces.begin(), f = 0; fit != faces.end(); ++fit, f++ )
206  {
207  // Get verts for this face
208  MB_CHK_ERR( mb->get_connectivity( *fit, conn, nconn ) );
209  // Get centroid tags for those verts
210  MB_CHK_ERR( mb->tag_get_data( centroid, conn, nconn, &ctag[0] ) );
211  fcentroids[3 * f + 0] = fcentroids[3 * f + 1] = fcentroids[3 * f + 2] = 0.0;
212  for( v = 0; v < nconn; v++ )
213  {
214  fcentroids[3 * f + 0] += ctag[3 * v + 0];
215  fcentroids[3 * f + 1] += ctag[3 * v + 1];
216  fcentroids[3 * f + 2] += ctag[3 * v + 2];
217  }
218  for( v = 0; v < 3; v++ )
219  fcentroids[3 * f + v] /= nconn;
220  }
221  MB_CHK_ERR( mb->tag_set_data( centroid, faces, &fcentroids[0] ) );
222 
223  // 2b. For each owned vertex:
224  for( vit = owned_verts.begin(), v = 0; vit != owned_verts.end(); ++vit, v++ )
225  {
226  // if !fixed
227  if( fix_tag[v] ) continue;
228  // vertex centroid = sum(cell centroids)/ncells
229  adj_faces.clear();
230  MB_CHK_ERR( mb->get_adjacencies( &( *vit ), 1, 2, false, adj_faces ) );
231  MB_CHK_ERR( mb->tag_get_data( centroid, &adj_faces[0], adj_faces.size(), &fcentroids[0] ) );
232  double vnew[] = { 0.0, 0.0, 0.0 };
233  for( f = 0; f < (int)adj_faces.size(); f++ )
234  {
235  vnew[0] += fcentroids[3 * f + 0];
236  vnew[1] += fcentroids[3 * f + 1];
237  vnew[2] += fcentroids[3 * f + 2];
238  }
239  for( f = 0; f < 3; f++ )
240  vnew[f] /= adj_faces.size();
241  double delta = ( CartVect( vnew ) - CartVect( &vcentroids[3 * v] ) ).length();
242  mxdelta = std::max( delta, mxdelta );
243  for( f = 0; f < 3; f++ )
244  vcentroids[3 * v + f] = vnew[f];
245  }
246 
247  // Set the centroid tag; having them only in vcentroids array isn't enough, as vertex
248  // centroids are accessed randomly in loop over faces
249  MB_CHK_ERR( mb->tag_set_data( centroid, owned_verts, &vcentroids[0] ) );
250 
251  // 2c. Exchange tags on owned verts
252  if( nprocs > 1 )
253  {
254 #ifdef MOAB_HAVE_MPI
255  MB_CHK_ERR( pcomm->exchange_tags( centroid, shared_owned_verts ) );
256 #endif
257  }
258 
259  if( !( nit % report_its ) )
260  {
261  // Global reduce for maximum delta, then report it
262  double global_max = mxdelta;
263  int myrank = 0;
264 #ifdef MOAB_HAVE_MPI
265  if( nprocs > 1 ) MPI_Reduce( &mxdelta, &global_max, 1, MPI_DOUBLE, MPI_MAX, 0, pcomm->comm() );
266  myrank = pcomm->rank();
267 #endif
268  if( 1 == nprocs || 0 == myrank ) cout << "Max delta = " << global_max << endl;
269  }
270  }
271 
272  // Write the tag back onto vertex coordinates
273  MB_CHK_ERR( mb->set_coords( owned_verts, &vcentroids[0] ) );
274 
275  // Delete the centroid tag, since we don't need it anymore
276  MB_CHK_ERR( mb->tag_delete( centroid ) );
277 
278  return MB_SUCCESS;
279 }

References moab::Range::begin(), moab::ParallelComm::comm(), moab::Range::empty(), moab::Range::end(), ErrorCode, moab::ParallelComm::exchange_tags(), moab::ParallelComm::filter_pstatus(), moab::Core::get_adjacencies(), moab::Core::get_connectivity(), moab::Core::get_coords(), moab::ParallelComm::get_pcomm(), moab::ParallelComm::get_shared_entities(), moab::Range::insert(), length(), moab::CN::MAX_NODES_PER_ELEMENT, mb, MB_CHK_ERR, MB_SUCCESS, MB_TAG_CREAT, MB_TAG_DENSE, MB_TYPE_DOUBLE, PSTATUS_NOT, PSTATUS_NOT_OWNED, moab::ParallelComm::rank(), moab::Core::set_coords(), moab::Range::size(), moab::ParallelComm::size(), moab::Core::tag_delete(), moab::Core::tag_get_data(), moab::Core::tag_get_handle(), and moab::Core::tag_set_data().

Referenced by main().

Variable Documentation

◆ test_file_name

string test_file_name = string( MESH_DIR ) + string( "/surfrandomtris-4part.h5m" )

Definition at line 56 of file LloydRelaxation.cpp.

Referenced by main().