MOAB: Mesh Oriented datABase
(version 5.4.1)
|
00001 /** @example LaplacianSmoother.cpp \n 00002 * \brief Perform Laplacian relaxation on a mesh and its dual \n 00003 * <b>To run</b>: mpiexec -np <np> LaplacianSmoother [filename]\n 00004 * 00005 * Briefly, Laplacian relaxation is a technique to smooth out a mesh. The centroid of each cell is 00006 * computed from its vertex positions, then vertices are placed at the average of their connected 00007 * cells' centroids. 00008 * 00009 * In the parallel algorithm, an extra ghost layer of cells is exchanged. This allows us to compute 00010 * the centroids for boundary cells on each processor where they appear; this eliminates the need 00011 * for one round of data exchange (for those centroids) between processors. New vertex positions 00012 * must be sent from owning processors to processors sharing those vertices. Convergence is 00013 * measured as the maximum distance moved by any vertex. 00014 * 00015 * In this implementation, a fixed number of iterations is performed. The final mesh is output to 00016 * 'laplacianfinal.h5m' in the current directory (H5M format must be used since the file is written 00017 * in parallel). 00018 * 00019 * Usage: mpiexec -n 2 valgrind ./LaplacianSmoother -f input/surfrandomtris-64part.h5m -r 2 -p 2 -n 00020 * 25 00021 */ 00022 00023 #include <iostream> 00024 #include <sstream> 00025 #include "moab/Core.hpp" 00026 #ifdef MOAB_HAVE_MPI 00027 #include "moab/ParallelComm.hpp" 00028 #include "MBParallelConventions.h" 00029 #endif 00030 #include "moab/Skinner.hpp" 00031 #include "moab/CN.hpp" 00032 #include "moab/ProgOptions.hpp" 00033 #include "moab/CartVect.hpp" 00034 #include "moab/NestedRefine.hpp" 00035 #include "moab/VerdictWrapper.hpp" 00036 #include "matrix.h" 00037 00038 using namespace moab; 00039 using namespace std; 00040 00041 #define WRITE_DEBUG_FILES 00042 00043 #ifdef MESH_DIR 00044 string test_file_name = string( MESH_DIR ) + string( "/surfrandomtris-4part.h5m" ); 00045 #else 00046 string test_file_name = string( "input/surfrandomtris-4part.h5m" ); 00047 #endif 00048 00049 #define RC MB_CHK_ERR( rval ) 00050 #define dbgprint( MSG ) \ 00051 do \ 00052 { \ 00053 if( !global_rank ) std::cerr << ( MSG ) << std::endl; \ 00054 } while( false ) 00055 00056 ErrorCode perform_laplacian_smoothing( Core* mb, 00057 Range& cells, 00058 Range& verts, 00059 int dim, 00060 Tag fixed, 00061 bool use_hc = false, 00062 bool use_acc = false, 00063 int acc_method = 1, 00064 int num_its = 10, 00065 double rel_eps = 1e-5, 00066 double alpha = 0.0, 00067 double beta = 0.5, 00068 int report_its = 1 ); 00069 00070 ErrorCode hcFilter( Core* mb, 00071 moab::ParallelComm* pcomm, 00072 moab::Range& verts, 00073 int dim, 00074 Tag fixed, 00075 std::vector< double >& verts_o, 00076 std::vector< double >& verts_n, 00077 double alpha, 00078 double beta ); 00079 00080 ErrorCode laplacianFilter( Core* mb, 00081 moab::ParallelComm* pcomm, 00082 moab::Range& verts, 00083 int dim, 00084 Tag fixed, 00085 std::vector< double >& verts_o, 00086 std::vector< double >& verts_n, 00087 bool use_updated = true ); 00088 00089 int main( int argc, char** argv ) 00090 { 00091 int num_its = 10; 00092 int num_ref = 0; 00093 int num_dim = 2; 00094 int report_its = 1; 00095 int num_degree = 2; 00096 bool use_hc = false; 00097 bool use_acc = false; 00098 int acc_method = 1; 00099 double alpha = 0.5, beta = 0.0; 00100 double rel_eps = 1e-5; 00101 const int nghostrings = 1; 00102 ProgOptions opts; 00103 ErrorCode rval; 00104 std::stringstream sstr; 00105 int global_rank; 00106 00107 MPI_Init( &argc, &argv ); 00108 MPI_Comm_rank( MPI_COMM_WORLD, &global_rank ); 00109 00110 // Decipher program options from user 00111 opts.addOpt< int >( std::string( "niter,n" ), 00112 std::string( "Number of Laplacian smoothing iterations (default=10)" ), &num_its ); 00113 opts.addOpt< double >( std::string( "eps,e" ), 00114 std::string( "Tolerance for the Laplacian smoothing error (default=1e-5)" ), &rel_eps ); 00115 opts.addOpt< double >( std::string( "alpha" ), 00116 std::string( "Tolerance for the Laplacian smoothing error (default=0.0)" ), &alpha ); 00117 opts.addOpt< double >( std::string( "beta" ), 00118 std::string( "Tolerance for the Laplacian smoothing error (default=0.5)" ), &beta ); 00119 opts.addOpt< int >( std::string( "dim,d" ), std::string( "Topological dimension of the mesh (default=2)" ), 00120 &num_dim ); 00121 opts.addOpt< std::string >( std::string( "file,f" ), 00122 std::string( "Input mesh file to smoothen (default=surfrandomtris-4part.h5m)" ), 00123 &test_file_name ); 00124 opts.addOpt< int >( 00125 std::string( "nrefine,r" ), 00126 std::string( "Number of uniform refinements to perform and apply smoothing cycles (default=1)" ), &num_ref ); 00127 opts.addOpt< int >( std::string( "ndegree,p" ), std::string( "Degree of uniform refinement (default=2)" ), 00128 &num_degree ); 00129 opts.addOpt< void >( std::string( "humphrey,c" ), 00130 std::string( "Use Humphrey’s Classes algorithm to reduce shrinkage of " 00131 "Laplacian smoother (default=false)" ), 00132 &use_hc ); 00133 opts.addOpt< void >( std::string( "aitken,d" ), 00134 std::string( "Use Aitken \\delta^2 acceleration to improve convergence of " 00135 "Lapalace smoothing algorithm (default=false)" ), 00136 &use_acc ); 00137 opts.addOpt< int >( std::string( "acc,a" ), 00138 std::string( "Type of vector Aitken process to use for acceleration (default=1)" ), 00139 &acc_method ); 00140 00141 opts.parseCommandLine( argc, argv ); 00142 00143 // get MOAB and ParallelComm instances 00144 Core* mb = new Core; 00145 if( NULL == mb ) return 1; 00146 int global_size = 1; 00147 00148 // get the ParallelComm instance 00149 ParallelComm* pcomm = new ParallelComm( mb, MPI_COMM_WORLD ); 00150 global_size = pcomm->size(); 00151 00152 string roptions, woptions; 00153 if( global_size > 1 ) 00154 { // if reading in parallel, need to tell it how 00155 sstr.str( "" ); 00156 sstr << "PARALLEL=READ_PART;PARTITION=PARALLEL_PARTITION;PARALLEL_RESOLVE_SHARED_ENTS;" 00157 "PARALLEL_GHOSTS=" 00158 << num_dim << ".0." << nghostrings << ";DEBUG_IO=0;DEBUG_PIO=0"; 00159 roptions = sstr.str(); 00160 woptions = "PARALLEL=WRITE_PART"; 00161 } 00162 00163 // read the file 00164 moab::EntityHandle fileset, currset; 00165 rval = mb->create_meshset( MESHSET_SET, fileset ); 00166 RC; 00167 currset = fileset; 00168 rval = mb->load_file( test_file_name.c_str(), &fileset, roptions.c_str() ); 00169 RC; 00170 00171 std::vector< EntityHandle > hsets( num_ref + 1, fileset ); 00172 if( num_ref ) 00173 { 00174 // Perform uniform refinement of the smoothed mesh 00175 #ifdef MOAB_HAVE_MPI 00176 NestedRefine* uref = new NestedRefine( mb, pcomm, currset ); 00177 #else 00178 NestedRefine* uref = new NestedRefine( mb, 0, currset ); 00179 #endif 00180 00181 std::vector< int > num_degrees( num_ref, num_degree ); 00182 rval = uref->generate_mesh_hierarchy( num_ref, &num_degrees[0], hsets ); 00183 RC; 00184 00185 // Now exchange 1 layer of ghost elements, using vertices as bridge 00186 // (we could have done this as part of reading process, using the PARALLEL_GHOSTS read 00187 // option) 00188 rval = uref->exchange_ghosts( hsets, nghostrings ); 00189 RC; 00190 00191 delete uref; 00192 } 00193 00194 for( int iref = 0; iref <= num_ref; ++iref ) 00195 { 00196 00197 // specify which set we are currently working on 00198 currset = hsets[iref]; 00199 00200 // make tag to specify fixed vertices, since it's input to the algorithm; use a default 00201 // value of non-fixed so we only need to set the fixed tag for skin vertices 00202 Tag fixed; 00203 int def_val = 0; 00204 rval = mb->tag_get_handle( "fixed", 1, MB_TYPE_INTEGER, fixed, MB_TAG_CREAT | MB_TAG_DENSE, &def_val ); 00205 RC; 00206 00207 // get all vertices and cells 00208 Range verts, cells, skin_verts; 00209 rval = mb->get_entities_by_type( currset, MBVERTEX, verts ); 00210 RC; 00211 rval = mb->get_entities_by_dimension( currset, num_dim, cells ); 00212 RC; 00213 dbgprint( "Found " << verts.size() << " vertices and " << cells.size() << " elements" ); 00214 00215 // get the skin vertices of those cells and mark them as fixed; we don't want to fix the 00216 // vertices on a part boundary, but since we exchanged a layer of ghost cells, those 00217 // vertices aren't on the skin locally ok to mark non-owned skin vertices too, I won't move 00218 // those anyway use MOAB's skinner class to find the skin 00219 Skinner skinner( mb ); 00220 rval = skinner.find_skin( currset, cells, true, skin_verts ); 00221 RC; // 'true' param indicates we want vertices back, not cells 00222 00223 std::vector< int > fix_tag( skin_verts.size(), 1 ); // initialized to 1 to indicate fixed 00224 rval = mb->tag_set_data( fixed, skin_verts, &fix_tag[0] ); 00225 RC; 00226 // exchange tags on owned verts for fixed points 00227 if( global_size > 1 ) 00228 { 00229 #ifdef MOAB_HAVE_MPI 00230 rval = pcomm->exchange_tags( fixed, skin_verts ); 00231 RC; 00232 #endif 00233 } 00234 00235 // now perform the Laplacian relaxation 00236 rval = perform_laplacian_smoothing( mb, cells, verts, num_dim, fixed, use_hc, use_acc, acc_method, num_its, 00237 rel_eps, alpha, beta, report_its ); 00238 RC; 00239 00240 // output file, using parallel write 00241 sstr.str( "" ); 00242 sstr << "LaplacianSmoother_" << iref << ".h5m"; 00243 rval = mb->write_file( sstr.str().c_str(), "H5M", woptions.c_str(), &currset, 1 ); 00244 RC; 00245 00246 // delete fixed tag, since we created it here 00247 rval = mb->tag_delete( fixed ); 00248 RC; 00249 } 00250 00251 delete pcomm; 00252 // delete MOAB instance 00253 delete mb; 00254 00255 MPI_Finalize(); 00256 return 0; 00257 } 00258 00259 ErrorCode perform_laplacian_smoothing( Core* mb, 00260 Range& cells, 00261 Range& verts, 00262 int dim, 00263 Tag fixed, 00264 bool use_hc, 00265 bool use_acc, 00266 int acc_method, 00267 int num_its, 00268 double rel_eps, 00269 double alpha, 00270 double beta, 00271 int report_its ) 00272 { 00273 ErrorCode rval; 00274 int global_rank = 0, global_size = 1; 00275 int nacc = 2; /* nacc_method: 1 = Method 2 from [1], 2 = Method 3 from [1] */ 00276 std::vector< double > verts_acc1, verts_acc2, verts_acc3; 00277 double rat_theta = rel_eps, rat_alpha = rel_eps, rat_alphaprev = rel_eps; 00278 #ifdef MOAB_HAVE_MPI 00279 const char* woptions = "PARALLEL=WRITE_PART"; 00280 #else 00281 const char* woptions = ""; 00282 #endif 00283 std::vector< int > fix_tag( verts.size() ); 00284 00285 rval = mb->tag_get_data( fixed, verts, &fix_tag[0] ); 00286 RC; 00287 00288 #ifdef MOAB_HAVE_MPI 00289 ParallelComm* pcomm = ParallelComm::get_pcomm( mb, 0 ); 00290 global_rank = pcomm->rank(); 00291 global_size = pcomm->size(); 00292 #endif 00293 00294 dbgprint( "-- Starting smoothing cycle --" ); 00295 // perform Laplacian relaxation: 00296 // 1. setup: set vertex centroids from vertex coords; filter to owned verts; get fixed tags 00297 00298 // get all verts coords into tag; don't need to worry about filtering out fixed verts, 00299 // we'll just be setting to their fixed coords 00300 std::vector< double > verts_o, verts_n; 00301 verts_o.resize( 3 * verts.size(), 0.0 ); 00302 verts_n.resize( 3 * verts.size(), 0.0 ); 00303 // std::vector<const void*> vdata(1); 00304 // vdata[0] = &verts_n[0]; 00305 // const int vdatasize = verts_n.size(); 00306 void* vdata = &verts_n[0]; 00307 rval = mb->get_coords( verts, &verts_o[0] ); 00308 RC; 00309 const int nbytes = sizeof( double ) * verts_o.size(); 00310 00311 Tag errt, vpost; 00312 double def_val[3] = { 0.0, 0.0, 0.0 }; 00313 rval = mb->tag_get_handle( "error", 1, MB_TYPE_DOUBLE, errt, MB_TAG_CREAT | MB_TAG_DENSE, def_val ); 00314 RC; 00315 rval = mb->tag_get_handle( "vpos", 3, MB_TYPE_DOUBLE, vpost, MB_TAG_CREAT | MB_TAG_DENSE, def_val ); 00316 RC; 00317 // rval = mb->tag_set_by_ptr(vpost, verts, vdata); RC; 00318 00319 if( use_acc ) 00320 { 00321 verts_acc1.resize( verts_o.size(), 0.0 ); 00322 verts_acc2.resize( verts_o.size(), 0.0 ); 00323 verts_acc3.resize( verts_o.size(), 0.0 ); 00324 memcpy( &verts_acc1[0], &verts_o[0], nbytes ); 00325 memcpy( &verts_acc2[0], &verts_o[0], nbytes ); 00326 memcpy( &verts_acc3[0], &verts_o[0], nbytes ); 00327 } 00328 00329 // Filter verts down to owned ones and get fixed tag for them 00330 Range owned_verts, shared_owned_verts; 00331 if( global_size > 1 ) 00332 { 00333 #ifdef MOAB_HAVE_MPI 00334 rval = pcomm->filter_pstatus( verts, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &owned_verts );MB_CHK_ERR( rval ); 00335 #endif 00336 } 00337 else 00338 owned_verts = verts; 00339 00340 #ifdef MOAB_HAVE_MPI 00341 // Get shared owned verts, for exchanging tags 00342 rval = pcomm->get_shared_entities( -1, shared_owned_verts, 0, false, true );MB_CHK_ERR( rval ); 00343 // Workaround: if no shared owned verts, put a non-shared one in the list, to prevent exchanging 00344 // tags for all shared entities 00345 if( shared_owned_verts.empty() ) shared_owned_verts.insert( *verts.begin() ); 00346 #endif 00347 00348 #ifdef WRITE_DEBUG_FILES 00349 { 00350 // output file, using parallel write 00351 std::stringstream sstr; 00352 sstr << "LaplacianSmootherIterate_0.h5m"; 00353 rval = mb->write_file( sstr.str().c_str(), NULL, woptions ); 00354 RC; 00355 } 00356 #endif 00357 00358 bool check_metrics = true; 00359 VerdictWrapper vw( mb ); 00360 vw.set_size( 1. ); // for relative size measures; maybe it should be user input 00361 00362 double mxdelta = 0.0, global_max = 0.0; 00363 // 2. for num_its iterations: 00364 for( int nit = 0; nit < num_its; nit++ ) 00365 { 00366 00367 mxdelta = 0.0; 00368 00369 // 2a. Apply Laplacian Smoothing Filter to Mesh 00370 if( use_hc ) 00371 { 00372 rval = hcFilter( mb, pcomm, verts, dim, fixed, verts_o, verts_n, alpha, beta ); 00373 RC; 00374 } 00375 else 00376 { 00377 rval = laplacianFilter( mb, pcomm, verts, dim, fixed, verts_o, verts_n ); 00378 RC; 00379 } 00380 00381 if( check_metrics ) 00382 { 00383 bool smooth_success = true; 00384 std::vector< double > coords; 00385 double jac = 0.0; 00386 int num_nodes = 0; 00387 for( unsigned ic = 0; ic < cells.size(); ++ic ) 00388 { 00389 EntityHandle ecell = cells[ic]; 00390 EntityType etype = mb->type_from_handle( ecell ); 00391 Range everts; 00392 rval = mb->get_connectivity( &ecell, 1, everts, num_nodes ); 00393 RC; 00394 coords.resize( num_nodes * 3, 0.0 ); 00395 for( int iv = 0; iv < num_nodes; ++iv ) 00396 { 00397 const int offset = mb->id_from_handle( everts[iv] ) * 3; 00398 for( int ii = 0; ii < 3; ++ii ) 00399 coords[iv * 3 + ii] = verts_n[offset + ii]; 00400 } 00401 rval = vw.quality_measure( ecell, MB_SHAPE, jac, num_nodes, etype, &coords[0] ); 00402 RC; 00403 if( jac < 1e-8 ) 00404 { 00405 dbgprint( "Inverted element " << ic << " with jacobian = " << jac << "." ); 00406 smooth_success = false; 00407 break; 00408 } 00409 } 00410 00411 if( !smooth_success ) 00412 { 00413 verts_o.swap( verts_n ); 00414 dbgprint( "Found element inversions during smoothing. Stopping iterations." ); 00415 break; 00416 } 00417 } 00418 00419 if( use_acc ) 00420 { 00421 00422 // if (acc_method < 5 || nit <= 3) { 00423 // memcpy( &verts_acc1[0], &verts_acc2[0], nbytes ); 00424 // memcpy( &verts_acc2[0], &verts_acc3[0], nbytes ); 00425 // memcpy( &verts_acc3[0], &verts_n[0], nbytes ); 00426 // } 00427 00428 rat_alphaprev = rat_alpha; 00429 for( unsigned i = 0; i < verts_n.size(); ++i ) 00430 { 00431 rat_alpha = 00432 std::max( rat_alpha, 00433 std::abs( ( verts_acc3[i] - verts_acc2[i] ) * ( verts_acc2[i] - verts_acc1[i] ) ) / 00434 ( ( verts_acc2[i] - verts_acc1[i] ) * ( verts_acc2[i] - verts_acc1[i] ) ) ); 00435 } 00436 rat_theta = std::abs( rat_alpha / rat_alphaprev - 1.0 ); 00437 00438 if( nit > 3 && ( nit % nacc ) && rat_theta < 1.0 ) 00439 { 00440 00441 if( acc_method == 1 ) 00442 { /* Method 2 from ACCELERATION OF VECTOR SEQUENCES: 00443 http://onlinelibrary.wiley.com/doi/10.1002/cnm.1630020409/pdf */ 00444 double vnorm = 0.0, den, acc_alpha = 0.0, acc_gamma = 0.0; 00445 for( unsigned i = 0; i < verts_n.size(); ++i ) 00446 { 00447 den = ( verts_acc3[i] - 2.0 * verts_acc2[i] + verts_acc1[i] ); 00448 vnorm += den * den; 00449 acc_alpha += ( verts_acc3[i] - verts_acc2[i] ) * ( verts_acc3[i] - verts_acc2[i] ); 00450 acc_gamma += ( verts_acc2[i] - verts_acc1[i] ) * ( verts_acc2[i] - verts_acc1[i] ); 00451 } 00452 for( unsigned i = 0; i < verts_n.size(); ++i ) 00453 { 00454 verts_n[i] = verts_acc2[i] + ( acc_gamma * ( verts_acc3[i] - verts_acc2[i] ) - 00455 acc_alpha * ( verts_acc2[i] - verts_acc1[i] ) ) / 00456 vnorm; 00457 } 00458 } 00459 else if( acc_method == 2 ) 00460 { /* Method 3 from ACCELERATION OF VECTOR SEQUENCES: 00461 http://onlinelibrary.wiley.com/doi/10.1002/cnm.1630020409/pdf */ 00462 double vnorm = 0.0, num = 0.0, den = 0.0, mu = 0.0; 00463 for( unsigned i = 0; i < verts_n.size(); ++i ) 00464 { 00465 num += 00466 ( verts_acc3[i] - verts_acc2[i] ) * ( verts_acc3[i] - 2.0 * verts_acc2[i] + verts_acc1[i] ); 00467 den = ( verts_acc3[i] - 2.0 * verts_acc2[i] + verts_acc1[i] ); 00468 vnorm += den * den; 00469 } 00470 mu = num / vnorm; 00471 for( unsigned i = 0; i < verts_n.size(); ++i ) 00472 { 00473 verts_n[i] = verts_acc3[i] + mu * ( verts_acc2[i] - verts_acc3[i] ); 00474 } 00475 } 00476 else if( acc_method == 3 ) 00477 { /* Method 5 from ACCELERATION OF VECTOR SEQUENCES: 00478 http://onlinelibrary.wiley.com/doi/10.1002/cnm.1630020409/pdf */ 00479 double num = 0.0, den = 0.0, mu = 0.0; 00480 for( unsigned i = 0; i < verts_n.size(); ++i ) 00481 { 00482 num += ( verts_acc3[i] - verts_acc2[i] ) * ( verts_acc2[i] - verts_acc1[i] ); 00483 den += 00484 ( verts_acc2[i] - verts_acc1[i] ) * ( verts_acc3[i] - 2.0 * verts_acc2[i] + verts_acc1[i] ); 00485 } 00486 mu = num / den; 00487 for( unsigned i = 0; i < verts_n.size(); ++i ) 00488 { 00489 verts_n[i] = verts_acc3[i] - mu * ( verts_acc3[i] - verts_acc2[i] ); 00490 } 00491 } 00492 else if( acc_method == 4 ) 00493 { /* Method 8 from ACCELERATION OF VECTOR SEQUENCES: 00494 http://onlinelibrary.wiley.com/doi/10.1002/cnm.1630020409/pdf */ 00495 double num = 0.0, den = 0.0, lambda = 0.0; 00496 for( unsigned i = 0; i < verts_n.size(); ++i ) 00497 { 00498 num += ( verts_acc3[i] - verts_acc2[i] ) * ( verts_acc3[i] - verts_acc2[i] ); 00499 den += ( verts_acc2[i] - verts_acc1[i] ) * ( verts_acc2[i] - verts_acc1[i] ); 00500 } 00501 lambda = std::sqrt( num / den ); 00502 for( unsigned i = 0; i < verts_n.size(); ++i ) 00503 { 00504 verts_n[i] = verts_acc3[i] - lambda / ( lambda - 1.0 ) * ( verts_acc3[i] - verts_acc2[i] ); 00505 } 00506 } 00507 else if( acc_method == 5 ) 00508 { /* Minimum polynomial extrapolation of vector sequenes : 00509 https://en.wikipedia.org/wiki/Minimum_polynomial_extrapolation */ 00510 /* Pseudo-code 00511 U=x(:,2:end-1)-x(:,1:end-2); 00512 c=-pinv(U)*(x(:,end)-x(:,end-1)); 00513 c(end+1,1)=1; 00514 s=(x(:,2:end)*c)/sum(c); 00515 */ 00516 Matrix U( verts_n.size(), 2 ); 00517 Vector res( verts_n.size() ); 00518 for( unsigned ir = 0; ir < verts_n.size(); ir++ ) 00519 { 00520 U( ir, 0 ) = verts_acc2[ir] - verts_acc1[ir]; 00521 U( ir, 1 ) = verts_acc3[ir] - verts_acc2[ir]; 00522 res[ir] = -( verts_n[ir] - verts_acc3[ir] ); 00523 } 00524 // U.print(); 00525 // Vector acc = QR(U).solve(res); 00526 Vector acc = solve( U, res ); 00527 double accsum = acc[0] + acc[1] + 1.0; 00528 for( unsigned ir = 0; ir < verts_n.size(); ir++ ) 00529 { 00530 verts_n[ir] = ( verts_acc1[ir] * acc[0] + verts_acc2[ir] * acc[1] + verts_acc3[ir] ) / accsum; 00531 } 00532 00533 memcpy( &verts_acc1[0], &verts_acc2[0], nbytes ); 00534 memcpy( &verts_acc2[0], &verts_acc3[0], nbytes ); 00535 memcpy( &verts_acc3[0], &verts_n[0], nbytes ); 00536 } 00537 else 00538 { 00539 int offset = 0; 00540 for( Range::const_iterator vit = verts.begin(); vit != verts.end(); ++vit, offset += 3 ) 00541 { 00542 // if !fixed 00543 if( fix_tag[offset / 3] ) continue; 00544 00545 CartVect num1 = ( CartVect( &verts_acc3[offset] ) - CartVect( &verts_acc2[offset] ) ); 00546 CartVect num2 = ( CartVect( &verts_acc3[offset] ) - 2.0 * CartVect( &verts_acc2[offset] ) + 00547 CartVect( &verts_acc1[offset] ) ); 00548 00549 num1.scale( num2 ); 00550 const double mu = num1.length_squared() / num2.length_squared(); 00551 00552 verts_n[offset + 0] = 00553 verts_acc3[offset + 0] + mu * ( verts_acc2[offset + 0] - verts_acc3[offset + 0] ); 00554 verts_n[offset + 1] = 00555 verts_acc3[offset + 1] + mu * ( verts_acc2[offset + 1] - verts_acc3[offset + 1] ); 00556 verts_n[offset + 2] = 00557 verts_acc3[offset + 2] + mu * ( verts_acc2[offset + 2] - verts_acc3[offset + 2] ); 00558 } 00559 } 00560 } 00561 memcpy( &verts_acc1[0], &verts_acc2[0], nbytes ); 00562 memcpy( &verts_acc2[0], &verts_acc3[0], nbytes ); 00563 memcpy( &verts_acc3[0], &verts_n[0], nbytes ); 00564 } 00565 00566 // 2b. foreach owned vertex: compute change in coordinate norm 00567 for( unsigned v = 0; v < verts.size(); ++v ) 00568 { 00569 double delta = ( CartVect( &verts_n[3 * v] ) - CartVect( &verts_o[3 * v] ) ).length(); 00570 mxdelta = std::max( delta, mxdelta ); 00571 EntityHandle vh = verts[v]; 00572 rval = mb->tag_set_data( errt, &vh, 1, &mxdelta ); 00573 RC; 00574 } 00575 00576 // global reduce for maximum delta, then report it 00577 global_max = mxdelta; 00578 #ifdef MOAB_HAVE_MPI 00579 if( global_size > 1 ) MPI_Allreduce( &mxdelta, &global_max, 1, MPI_DOUBLE, MPI_MAX, pcomm->comm() ); 00580 #endif 00581 00582 if( !( nit % report_its ) ) 00583 { 00584 dbgprint( "\tIterate " << nit << ": Global Max delta = " << global_max << "." ); 00585 } 00586 00587 #ifdef WRITE_DEBUG_FILES 00588 { 00589 // write the tag back onto vertex coordinates 00590 rval = mb->set_coords( verts, &verts_n[0] ); 00591 RC; 00592 // output VTK file for debugging purposes 00593 std::stringstream sstr; 00594 sstr << "LaplacianSmootherIterate_" << nit + 1 << ".h5m"; 00595 rval = mb->write_file( sstr.str().c_str(), NULL, woptions ); 00596 RC; 00597 } 00598 #endif 00599 00600 #ifdef MOAB_HAVE_MPI 00601 // 2c. exchange tags on owned verts 00602 if( global_size > 1 ) 00603 { 00604 rval = mb->tag_set_data( vpost, owned_verts, vdata ); 00605 RC; 00606 rval = pcomm->exchange_tags( vpost, shared_owned_verts ); 00607 RC; 00608 } 00609 #endif 00610 00611 if( global_max < rel_eps ) 00612 break; 00613 else 00614 { 00615 std::copy( verts_n.begin(), verts_n.end(), verts_o.begin() ); 00616 } 00617 } 00618 00619 // write the tag back onto vertex coordinates 00620 rval = mb->set_coords( verts, &verts_n[0] ); 00621 RC; 00622 00623 dbgprint( "-- Final iterate error = " << global_max << ".\n" ); 00624 return MB_SUCCESS; 00625 } 00626 00627 /* 00628 Standard Laplacian Smooth Filter 00629 00630 Additional references: http://www.doc.ic.ac.uk/~gr409/thesis-MSc.pdf 00631 */ 00632 ErrorCode laplacianFilter( Core* mb, 00633 moab::ParallelComm* pcomm, 00634 moab::Range& verts, 00635 int dim, 00636 Tag fixed, 00637 std::vector< double >& verts_o, 00638 std::vector< double >& verts_n, 00639 bool use_updated ) 00640 { 00641 ErrorCode rval; 00642 std::vector< int > fix_tag( verts.size() ); 00643 double* data; 00644 00645 memcpy( &verts_n[0], &verts_o[0], sizeof( double ) * verts_o.size() ); 00646 00647 if( use_updated ) 00648 data = &verts_n[0]; 00649 else 00650 data = &verts_o[0]; 00651 00652 // filter verts down to owned ones and get fixed tag for them 00653 Range owned_verts; 00654 if( pcomm->size() > 1 ) 00655 { 00656 #ifdef MOAB_HAVE_MPI 00657 rval = pcomm->filter_pstatus( verts, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &owned_verts ); 00658 if( rval != MB_SUCCESS ) return rval; 00659 #endif 00660 } 00661 else 00662 owned_verts = verts; 00663 00664 rval = mb->tag_get_data( fixed, owned_verts, &fix_tag[0] ); 00665 RC; 00666 00667 int vindex = 0; 00668 for( Range::const_iterator vit = owned_verts.begin(); vit != owned_verts.end(); ++vit, vindex++ ) 00669 { 00670 // if !fixed 00671 if( fix_tag[vindex] ) continue; 00672 00673 const int index = verts.index( *vit ) * 3; 00674 00675 moab::Range adjverts, adjelems; 00676 // Find the neighboring vertices (1-ring neighborhood) 00677 rval = mb->get_adjacencies( &( *vit ), 1, dim, false, adjelems ); 00678 RC; 00679 rval = mb->get_connectivity( adjelems, adjverts ); 00680 RC; 00681 adjverts.erase( *vit ); 00682 00683 const int nadjs = adjverts.size(); 00684 if( nadjs ) 00685 { 00686 double delta[3] = { 0.0, 0.0, 0.0 }; 00687 00688 // Add the vertices and divide by the number of vertices 00689 for( int j = 0; j < nadjs; ++j ) 00690 { 00691 const int joffset = verts.index( adjverts[j] ) * 3; 00692 delta[0] += data[joffset + 0]; 00693 delta[1] += data[joffset + 1]; 00694 delta[2] += data[joffset + 2]; 00695 } 00696 00697 verts_n[index + 0] = delta[0] / nadjs; 00698 verts_n[index + 1] = delta[1] / nadjs; 00699 verts_n[index + 2] = delta[2] / nadjs; 00700 } 00701 } 00702 00703 return MB_SUCCESS; 00704 } 00705 00706 /* 00707 HC (Humphrey’s Classes) Smooth Algorithm - Reduces Shrinkage of Laplacian Smoother 00708 00709 Link: 00710 http://informatikbuero.com/downloads/Improved_Laplacian_Smoothing_of_Noisy_Surface_Meshes.pdf 00711 00712 Where sv - original points 00713 pv - previous points, 00714 alpha [0..1] influences previous points pv, e.g. 0 00715 beta [0..1] e.g. > 0.5 00716 */ 00717 ErrorCode hcFilter( Core* mb, 00718 moab::ParallelComm* pcomm, 00719 moab::Range& verts, 00720 int dim, 00721 Tag fixed, 00722 std::vector< double >& verts_o, 00723 std::vector< double >& verts_n, 00724 double alpha, 00725 double beta ) 00726 { 00727 ErrorCode rval; 00728 std::vector< double > verts_hc( verts_o.size() ); 00729 std::vector< int > fix_tag( verts.size() ); 00730 00731 // Perform Laplacian Smooth 00732 rval = laplacianFilter( mb, pcomm, verts, dim, fixed, verts_o, verts_n ); 00733 RC; 00734 00735 // Compute Differences 00736 for( unsigned index = 0; index < verts_o.size(); ++index ) 00737 { 00738 verts_hc[index] = verts_n[index] - ( alpha * verts_n[index] + ( 1.0 - alpha ) * verts_o[index] ); 00739 } 00740 00741 // filter verts down to owned ones and get fixed tag for them 00742 Range owned_verts; 00743 #ifdef MOAB_HAVE_MPI 00744 if( pcomm->size() > 1 ) 00745 { 00746 rval = pcomm->filter_pstatus( verts, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &owned_verts ); 00747 if( rval != MB_SUCCESS ) return rval; 00748 } 00749 else 00750 #endif 00751 owned_verts = verts; 00752 00753 rval = mb->tag_get_data( fixed, owned_verts, &fix_tag[0] ); 00754 RC; 00755 00756 int vindex = 0; 00757 for( Range::const_iterator vit = owned_verts.begin(); vit != owned_verts.end(); ++vit, vindex++ ) 00758 { 00759 // if !fixed 00760 if( fix_tag[vindex] ) continue; 00761 00762 const int index = verts.index( *vit ) * 3; 00763 00764 moab::Range adjverts, adjelems; 00765 // Find the neighboring vertices (1-ring neighborhood) 00766 rval = mb->get_adjacencies( &( *vit ), 1, dim, false, adjelems ); 00767 RC; 00768 rval = mb->get_connectivity( adjelems, adjverts ); 00769 RC; 00770 adjverts.erase( *vit ); 00771 00772 const int nadjs = adjverts.size(); 00773 double delta[3] = { 0.0, 0.0, 0.0 }; 00774 00775 for( int j = 0; j < nadjs; ++j ) 00776 { 00777 const int joffset = verts.index( adjverts[j] ) * 3; 00778 delta[0] += verts_hc[joffset + 0]; 00779 delta[1] += verts_hc[joffset + 1]; 00780 delta[2] += verts_hc[joffset + 2]; 00781 } 00782 00783 verts_n[index + 0] -= beta * verts_hc[index + 0] + ( ( 1.0 - beta ) / nadjs ) * delta[0]; 00784 verts_n[index + 1] -= beta * verts_hc[index + 1] + ( ( 1.0 - beta ) / nadjs ) * delta[1]; 00785 verts_n[index + 2] -= beta * verts_hc[index + 2] + ( ( 1.0 - beta ) / nadjs ) * delta[2]; 00786 } 00787 00788 return MB_SUCCESS; 00789 }