Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 #include "moab/LloydSmoother.hpp" 00002 #include "moab/Skinner.hpp" 00003 #include "moab/CN.hpp" 00004 #include "moab/CartVect.hpp" 00005 #include "moab/BoundBox.hpp" 00006 00007 #ifdef MOAB_HAVE_MPI 00008 #include "moab/ParallelComm.hpp" 00009 #include "MBParallelConventions.h" 00010 #endif 00011 00012 #include <iostream> 00013 00014 namespace moab 00015 { 00016 00017 LloydSmoother::LloydSmoother( Interface* impl, ParallelComm* pc, Range& elms, Tag ctag, Tag ftag, double at, double rt ) 00018 : mbImpl( impl ), myPcomm( pc ), myElems( elms ), coordsTag( ctag ), fixedTag( ftag ), absTol( at ), relTol( rt ), 00019 reportIts( 0 ), numIts( 0 ), iCreatedTag( false ) 00020 { 00021 } 00022 00023 LloydSmoother::~LloydSmoother() 00024 { 00025 if( iCreatedTag && fixedTag ) 00026 { 00027 ErrorCode rval = mbImpl->tag_delete( fixedTag );MB_CHK_SET_ERR_RET( rval, "Failed to delete the fixed tag" ); 00028 } 00029 } 00030 00031 ErrorCode LloydSmoother::perform_smooth() 00032 { 00033 ErrorCode rval; 00034 00035 if( myElems.empty() ) 00036 { 00037 MB_SET_ERR( MB_FAILURE, "No elements specified to Lloyd smoother" ); 00038 } 00039 else if( mbImpl->dimension_from_handle( *myElems.begin() ) != mbImpl->dimension_from_handle( *myElems.rbegin() ) ) 00040 { 00041 MB_SET_ERR( MB_FAILURE, "Elements of unequal dimension specified to Lloyd smoother" ); 00042 } 00043 00044 int dim = mbImpl->dimension_from_handle( *myElems.begin() ); 00045 00046 // first figure out tolerance to use 00047 if( 0 > absTol ) 00048 { 00049 // no tolerance set - get one relative to bounding box around elements 00050 BoundBox bb; 00051 rval = bb.update( *mbImpl, myElems );MB_CHK_SET_ERR( rval, "Failed to compute bounding box around elements" ); 00052 absTol = relTol * bb.diagonal_length(); 00053 } 00054 00055 // initialize if we need to 00056 rval = initialize();MB_CHK_SET_ERR( rval, "Failed to initialize" ); 00057 00058 // get all vertices 00059 Range verts; 00060 rval = mbImpl->get_adjacencies( myElems, 0, false, verts, Interface::UNION );MB_CHK_SET_ERR( rval, "Failed to get all vertices" ); 00061 00062 // perform Lloyd relaxation: 00063 // 1. setup: set vertex centroids from vertex coords; filter to owned verts; get fixed tags 00064 00065 // get all verts coords into tag; don't need to worry about filtering out fixed verts, 00066 // we'll just be setting to their fixed coords 00067 std::vector< double > vcentroids( 3 * verts.size() ); 00068 if( !coordsTag ) 00069 { 00070 rval = mbImpl->get_coords( verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to get vert coords" ); 00071 } 00072 else 00073 { 00074 rval = mbImpl->tag_get_data( coordsTag, verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to get vert coords tag values" ); 00075 } 00076 00077 Tag centroid; 00078 rval = mbImpl->tag_get_handle( "", 3, MB_TYPE_DOUBLE, centroid, MB_TAG_CREAT | MB_TAG_DENSE );MB_CHK_SET_ERR( rval, "Couldn't get tag handle" ); 00079 rval = mbImpl->tag_set_data( centroid, verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed setting centroid tag" ); 00080 00081 Range owned_verts, shared_owned_verts; 00082 #ifdef MOAB_HAVE_MPI 00083 // filter verts down to owned ones and get fixed tag for them 00084 if( myPcomm && myPcomm->size() > 1 ) 00085 { 00086 rval = myPcomm->filter_pstatus( verts, PSTATUS_NOT_OWNED, PSTATUS_NOT, -1, &owned_verts );MB_CHK_SET_ERR( rval, "Failed to filter on pstatus" ); 00087 // get shared owned verts, for exchanging tags 00088 rval = myPcomm->filter_pstatus( owned_verts, PSTATUS_SHARED, PSTATUS_AND, -1, &shared_owned_verts );MB_CHK_SET_ERR( rval, "Failed to filter for shared owned" ); 00089 // workaround: if no shared owned verts, put a non-shared one in the list, to prevent 00090 // exchanging tags for all shared entities 00091 if( shared_owned_verts.empty() ) shared_owned_verts.insert( *verts.begin() ); 00092 } 00093 else 00094 owned_verts = verts; 00095 #else 00096 owned_verts = verts; 00097 #endif 00098 00099 std::vector< unsigned char > fix_tag( owned_verts.size() ); 00100 rval = mbImpl->tag_get_data( fixedTag, owned_verts, &fix_tag[0] );MB_CHK_SET_ERR( rval, "Failed to get fixed tag" ); 00101 00102 // now fill vcentroids array with positions of just owned vertices, since those are the ones 00103 // we're actually computing 00104 vcentroids.resize( 3 * owned_verts.size() ); 00105 rval = mbImpl->tag_get_data( centroid, owned_verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to get centroid tag" ); 00106 00107 // some declarations for later iterations 00108 std::vector< double > fcentroids( 3 * myElems.size() ); // fcentroids for element centroids 00109 std::vector< double > ctag( 00110 3 * CN::MAX_NODES_PER_ELEMENT ); // temporary coordinate storage for verts bounding an element 00111 const EntityHandle* conn; // const ptr & size to elem connectivity 00112 int nconn; 00113 Range::iterator eit, vit; // for iterating over elems, verts 00114 int e, v; // for indexing into centroid vectors 00115 std::vector< EntityHandle > adj_elems; // used in vertex iteration 00116 00117 // 2. while !converged 00118 double resid = DBL_MAX; 00119 numIts = 0; 00120 while( resid > absTol ) 00121 { 00122 numIts++; 00123 resid = 0.0; 00124 00125 // 2a. foreach elem: centroid = sum(vertex centroids)/num_verts_in_cell 00126 for( eit = myElems.begin(), e = 0; eit != myElems.end(); ++eit, e++ ) 00127 { 00128 // get verts for this elem 00129 rval = mbImpl->get_connectivity( *eit, conn, nconn );MB_CHK_SET_ERR( rval, "Failed to get connectivity" ); 00130 // get centroid tags for those verts 00131 rval = mbImpl->tag_get_data( centroid, conn, nconn, &ctag[0] );MB_CHK_SET_ERR( rval, "Failed to get centroid" ); 00132 fcentroids[3 * e + 0] = fcentroids[3 * e + 1] = fcentroids[3 * e + 2] = 0.0; 00133 for( v = 0; v < nconn; v++ ) 00134 { 00135 fcentroids[3 * e + 0] += ctag[3 * v + 0]; 00136 fcentroids[3 * e + 1] += ctag[3 * v + 1]; 00137 fcentroids[3 * e + 2] += ctag[3 * v + 2]; 00138 } 00139 for( v = 0; v < 3; v++ ) 00140 fcentroids[3 * e + v] /= nconn; 00141 } 00142 rval = mbImpl->tag_set_data( centroid, myElems, &fcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to set elem centroid" ); 00143 00144 // 2b. foreach owned vertex: 00145 for( vit = owned_verts.begin(), v = 0; vit != owned_verts.end(); ++vit, v++ ) 00146 { 00147 // if !fixed 00148 if( fix_tag[v] ) continue; 00149 // vertex centroid = sum(cell centroids)/ncells 00150 adj_elems.clear(); 00151 rval = mbImpl->get_adjacencies( &( *vit ), 1, dim, false, adj_elems );MB_CHK_SET_ERR( rval, "Failed getting adjs" ); 00152 rval = mbImpl->tag_get_data( centroid, &adj_elems[0], adj_elems.size(), &fcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to get elem centroid" ); 00153 double vnew[] = { 0.0, 0.0, 0.0 }; 00154 for( e = 0; e < (int)adj_elems.size(); e++ ) 00155 { 00156 vnew[0] += fcentroids[3 * e + 0]; 00157 vnew[1] += fcentroids[3 * e + 1]; 00158 vnew[2] += fcentroids[3 * e + 2]; 00159 } 00160 for( e = 0; e < 3; e++ ) 00161 vnew[e] /= adj_elems.size(); 00162 double delta = ( CartVect( vnew ) - CartVect( &vcentroids[3 * v] ) ).length(); 00163 resid = ( v ? std::max( resid, delta ) : delta ); 00164 for( e = 0; e < 3; e++ ) 00165 vcentroids[3 * v + e] = vnew[e]; 00166 } 00167 00168 // set the centroid tag; having them only in vcentroids array isn't enough, as vertex 00169 // centroids are accessed randomly in loop over faces 00170 rval = mbImpl->tag_set_data( centroid, owned_verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to set vertex centroid" ); 00171 00172 #ifdef MOAB_HAVE_MPI 00173 // 2c. exchange tags on owned verts 00174 if( myPcomm && myPcomm->size() > 1 ) 00175 { 00176 rval = myPcomm->exchange_tags( centroid, shared_owned_verts );MB_CHK_SET_ERR( rval, "Failed to exchange tags" ); 00177 } 00178 #endif 00179 00180 if( reportIts && !( numIts % reportIts ) ) 00181 { 00182 double global_max = resid; 00183 #ifdef MOAB_HAVE_MPI 00184 // global reduce for maximum delta, then report it 00185 if( myPcomm && myPcomm->size() > 1 ) 00186 MPI_Reduce( &resid, &global_max, 1, MPI_DOUBLE, MPI_MAX, 0, myPcomm->comm() ); 00187 if( !myPcomm || !myPcomm->rank() ) 00188 #endif 00189 std::cout << "Max residual = " << global_max << std::endl; 00190 } 00191 00192 } // end while 00193 00194 // write the tag back onto vertex coordinates 00195 if( !coordsTag ) 00196 { 00197 rval = mbImpl->set_coords( owned_verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to set vertex coords" ); 00198 } 00199 else 00200 { 00201 rval = mbImpl->tag_set_data( coordsTag, owned_verts, &vcentroids[0] );MB_CHK_SET_ERR( rval, "Failed to set vert coords tag values" ); 00202 } 00203 00204 return MB_SUCCESS; 00205 } 00206 00207 ErrorCode LloydSmoother::initialize() 00208 { 00209 ErrorCode rval = MB_SUCCESS; 00210 00211 // first, check for tag; if we don't have it, make one and mark skin as fixed 00212 if( !fixedTag ) 00213 { 00214 unsigned char fixed = 0x0; 00215 rval = mbImpl->tag_get_handle( "", 1, MB_TYPE_OPAQUE, fixedTag, MB_TAG_DENSE | MB_TAG_CREAT, &fixed );MB_CHK_SET_ERR( rval, "Trouble making fixed tag" ); 00216 iCreatedTag = true; 00217 00218 // get the skin; get facets, because we might need to filter on shared entities 00219 Skinner skinner( mbImpl ); 00220 Range skin, skin_verts; 00221 rval = skinner.find_skin( 0, myElems, false, skin );MB_CHK_SET_ERR( rval, "Unable to find skin" ); 00222 00223 #ifdef MOAB_HAVE_MPI 00224 // need to do a little extra if we're working in parallel 00225 if( myPcomm ) 00226 { 00227 // filter out ghost and interface facets 00228 rval = myPcomm->filter_pstatus( skin, PSTATUS_GHOST | PSTATUS_INTERFACE, PSTATUS_NOT );MB_CHK_SET_ERR( rval, "Failed to filter on shared status" ); 00229 } 00230 #endif 00231 // get the vertices from those entities 00232 rval = mbImpl->get_adjacencies( skin, 0, false, skin_verts, Interface::UNION );MB_CHK_SET_ERR( rval, "Trouble getting vertices" ); 00233 00234 // mark them fixed 00235 std::vector< unsigned char > marks( skin_verts.size(), 0x1 ); 00236 rval = mbImpl->tag_set_data( fixedTag, skin_verts, &marks[0] );MB_CHK_SET_ERR( rval, "Unable to set tag on skin" ); 00237 } 00238 00239 return MB_SUCCESS; 00240 } 00241 00242 } // namespace moab