MeshKit
1.0
|
00001 // ********************************* 00002 00003 // Patrick Shriwise 00004 // October, 2013 00005 00006 // functions needed to check models for watertightness 00007 00008 #include <iostream> 00009 #include <iomanip> // for setprecision 00010 #include <limits> // for min/max values 00011 #include <assert.h> 00012 #include <math.h> 00013 #include <time.h> 00014 #include <vector> 00015 #include <cmath> 00016 #include <cstdlib> 00017 #include <ctime> 00018 #include <set> 00019 #include <algorithm> 00020 #include "moab/Core.hpp" 00021 #include "MBTagConventions.hpp" 00022 #include "moab/Range.hpp" 00023 #include "moab/Skinner.hpp" 00024 00025 #include "moab/GeomTopoTool.hpp" 00026 #include "meshkit/cw_func.hpp" 00027 #include "meshkit/gen.hpp" 00028 #include "meshkit/zip.hpp" 00029 #include "moab/Skinner.hpp" 00030 00031 using namespace moab; 00032 namespace cw_func { 00033 00034 ErrorCode check_mesh_for_watertightness( EntityHandle input_set, double tol, bool &sealed, bool test, bool verbose, bool check_topology ) 00035 { 00036 00037 ErrorCode result; 00038 00039 // create tags on geometry 00040 Tag geom_tag, id_tag; 00041 result = MBI()->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, 00042 MB_TYPE_INTEGER, geom_tag, MB_TAG_DENSE|MB_TAG_CREAT ); 00043 if(gen::error(MB_SUCCESS != result, "could not get GEOM_DIMENSION_TAG_NAME handle")) return result; 00044 00045 result = MBI()->tag_get_handle( GLOBAL_ID_TAG_NAME, 1, 00046 MB_TYPE_INTEGER, id_tag, MB_TAG_DENSE|MB_TAG_CREAT ); 00047 if(gen::error(MB_SUCCESS != result, "could not get GLOBAL_ID_TAG_NAME handle")) return result; 00048 00049 00050 // get surface and volume sets 00051 Range surf_sets, vol_sets; // Range of set of surfaces and volumes 00052 // surface sets 00053 int dim = 2; 00054 void* input_dim[] = {&dim}; 00055 result = MBI()->get_entities_by_type_and_tag( input_set, MBENTITYSET, &geom_tag, 00056 input_dim, 1, surf_sets); 00057 if(MB_SUCCESS != result) 00058 { 00059 return result; 00060 } 00061 00062 // volume sets 00063 dim = 3; 00064 result = MBI()->get_entities_by_type_and_tag( input_set, MBENTITYSET, &geom_tag, 00065 input_dim, 1, vol_sets); 00066 if(MB_SUCCESS != result) 00067 { 00068 return result; 00069 } 00070 if(!test) 00071 { 00072 std::cout<< "number of surfaces=" << surf_sets.size() << std::endl; 00073 std::cout<< "number of volumes=" << vol_sets.size() << std::endl; 00074 } 00075 // ****************************************************************** 00076 // Watertightness is a property of volumes. Check each surface for 00077 // watertightness. 00078 // ****************************************************************** 00079 // counted leaky surfaces 00080 int total_counter = 0, unmatched_counter=0; 00081 std::set<int> leaky_surfs, leaky_vols; 00082 Skinner tool(MBI()); 00083 00084 // remove all edges for fast skinning 00085 Range edges; 00086 00087 result = MBI()->get_entities_by_type( 0, MBEDGE, edges ); // get all edges 00088 if(MB_SUCCESS != result) // failed to get edge data 00089 { 00090 return result; // failed 00091 } 00092 //10/11/13 00093 //removed as a result of the change from the gen::find_skin function to the Skinner:find_skin function 00094 /* 00095 result = MBI()->delete_entities( edges ); //otherwise delete all edge 00096 00097 if(MB_SUCCESS != result) // failed to delete edge data 00098 { 00099 return result; // failed 00100 } 00101 */ 00102 // loop over each volume meshset 00103 int vol_counter = 0; 00104 for(Range::iterator i=vol_sets.begin(); i!=vol_sets.end(); ++i) 00105 { 00106 ++vol_counter; 00107 int surf_counter=0; 00108 Range child_sets; 00109 00110 result = MBI()->get_child_meshsets( *i, child_sets ); // get child set 00111 if(MB_SUCCESS != result) 00112 { 00113 return result; // failed 00114 } 00115 00116 // get the volume id of the volume meshset to print a status message 00117 int vol_id=0; 00118 // i is the iterator, so &(*i) is a pointer to the first element of Range 00119 result = MBI()->tag_get_data( id_tag, &(*i), 1, &vol_id ); 00120 00121 if(MB_SUCCESS != result) 00122 { 00123 return result; 00124 } 00125 00126 if(verbose) 00127 { 00128 std::cout << "checking volume " << vol_counter << "/" << vol_sets.size() 00129 << " id=" << vol_id << std::endl; 00130 } 00131 00132 // determine how many skin edges are in each volume 00133 int n_tris = 0; 00134 00135 for(Range::iterator j=child_sets.begin(); j!=child_sets.end(); ++j) 00136 { 00137 result = MBI()->get_number_entities_by_type( *j, MBTRI, n_tris ); // for each child set get number of triangles 00138 if(MB_SUCCESS != result) 00139 { 00140 return result; 00141 } 00142 } 00143 00144 // save the edges in a vector that is large enough to avoid resizing 00145 // presumably some kind of efficiency thing?? ad ?? 00146 std::vector<coords_and_id> the_coords_and_id; 00147 the_coords_and_id.reserve(n_tris); 00148 00149 // loop over the surface meshsets of each volume meshset 00150 for(Range::iterator j=child_sets.begin(); j!=child_sets.end(); ++j) 00151 { 00152 00153 // get the surface id of the surface meshset 00154 surf_counter++; 00155 int surf_id=0; 00156 result = MBI()->tag_get_data( id_tag, &(*j), 1, &surf_id ); 00157 if(MB_SUCCESS != result) 00158 { 00159 return result; 00160 } 00161 00162 // get the range of facets of the surface meshset 00163 Range facets; 00164 result = MBI()->get_entities_by_type( *j, MBTRI, facets ); 00165 if(MB_SUCCESS != result) 00166 { 00167 return result; 00168 } 00169 00170 // get the range of skin edges from the range of facets 00171 // Fiasco: Jason wrote an optimized function (find_skin_vertices) that performed 00172 // almost as well as my specialized version (gen::find_skin). When he made then 00173 // generalized find_skin_vertices for MOAB it killed performance. As it stands, 00174 // gen::find_skin is ~7x faster (January 29, 2010). 00175 Range skin_edges; 00176 Skinner sk(MBI()); 00177 if(!facets.empty()) 00178 { 00179 result = sk.find_skin( 0 , facets, 1, skin_edges, false ); 00180 if(MB_SUCCESS != result) 00181 { 00182 return result; 00183 } 00184 } 00185 00186 // count the number of skin edges in the range 00187 if(verbose) 00188 { 00189 std::cout << "surface " << surf_counter << "/" << child_sets.size() 00190 << " id=" << surf_id << " contains " << facets.size() 00191 << " facets and " << skin_edges.size() << " skin edges" << std::endl; 00192 } 00193 00194 for(Range::const_iterator k=skin_edges.begin(); k!=skin_edges.end(); ++k) { 00195 // get the endpoint vertices of the facet edge 00196 Range verts; 00197 result = MBI()->get_adjacencies( &(*k), 1, 0, false, verts ); 00198 if(MB_SUCCESS != result) return result; 00199 if(2 != verts.size()) { 00200 std::cout << " WARNING: verts.size()=" << verts.size() << std::endl; 00201 continue; 00202 } 00203 00204 // Save the range of verts to an array of verts that can store duplicates. 00205 coords_and_id temp; 00206 if(check_topology) { 00207 temp.vert1 = verts[0]; 00208 temp.vert2 = verts[1]; 00209 } else { 00210 // get the coordinates of endpoint vertices 00211 double coords0[3], coords1[3]; 00212 result = MBI()->get_coords( &(verts.front()), 1, coords0 ); 00213 if(MB_SUCCESS != result) return result; 00214 result = MBI()->get_coords( &(verts.back()), 1, coords1 ); 00215 if(MB_SUCCESS != result) return result; 00216 // orient the edge by endpoint coords 00217 if((!check_topology) && 00218 (coords1[0]< coords0[0] || 00219 (coords1[0]==coords0[0] && coords1[1]< coords0[1]) || 00220 (coords1[0]==coords0[0] && coords1[1]==coords0[1] && coords1[2]< coords0[2]))) { 00221 temp.x1 = coords1[0]; 00222 temp.y1 = coords1[1]; 00223 temp.z1 = coords1[2]; 00224 temp.x2 = coords0[0]; 00225 temp.y2 = coords0[1]; 00226 temp.z2 = coords0[2]; 00227 temp.vert1 = verts[1]; 00228 temp.vert2 = verts[0]; 00229 } else { 00230 temp.x1 = coords0[0]; 00231 temp.y1 = coords0[1]; 00232 temp.z1 = coords0[2]; 00233 temp.x2 = coords1[0]; 00234 temp.y2 = coords1[1]; 00235 temp.z2 = coords1[2]; 00236 temp.vert1 = verts[0]; 00237 temp.vert2 = verts[1]; 00238 } 00239 } 00240 temp.surf_id = surf_id; 00241 temp.matched = false; 00242 the_coords_and_id.push_back(temp); 00243 } 00244 //10/10/13 00245 // Removed the following to avoid altering the data set at all 00246 // -No need to delete skin_edges with the moab:Skinner find_skin function 00247 // -skin_edges size will be reset to zero upon new Range skin_edges; call 00248 // clean up the edges for the next find_skin call 00249 //result = MBI()->delete_entities( skin_edges ); 00250 //if(MB_SUCCESS != result) return result; 00251 00252 //10/10/13 00253 // - No ned to ensure edges aren't in the meshset with Skinner find_skin function 00254 //int n_edges; 00255 //result = MBI()->get_number_entities_by_type(0, MBEDGE, n_edges ); 00256 //if(MB_SUCCESS != result) return result; 00257 //if(gen::error(0 != n_edges, "n_edges not equal to zero")) return MB_MULTIPLE_ENTITIES_FOUND; 00258 } 00259 00260 // sort the edges by the first vert. The first vert has a lower handle than the second. 00261 int n_edges = the_coords_and_id.size(); 00262 total_counter += n_edges; 00263 if(check_topology) { 00264 qsort( &the_coords_and_id[0], n_edges, sizeof(struct coords_and_id), compare_by_handle); 00265 } else { 00266 qsort( &the_coords_and_id[0], n_edges, sizeof(struct coords_and_id), compare_by_coords); 00267 } 00268 00269 // ****************************************************************** 00270 // Iterate through each facet edge, looking for its match. If a match 00271 // is found set the edge's flag to 'matched' so that we do not check 00272 // it again. 00273 // WARNING: The logic is different for checking by topology vs. proximity. 00274 // ****************************************************************** 00275 // loop over each facet edge in the volume 00276 for(int j=0; j!=n_edges; ++j) { 00277 00278 // if the edge has already been matched, skip it 00279 if (the_coords_and_id[j].matched) continue; 00280 00281 // try to match the edge with another facet edge: 00282 for(int k=j+1; k!=n_edges+1; ++k) { 00283 00284 // look for a match 00285 if(check_topology) { 00286 if( the_coords_and_id[j].vert1==the_coords_and_id[k].vert1 && 00287 the_coords_and_id[j].vert2==the_coords_and_id[k].vert2 ) { 00288 the_coords_and_id[j].matched = true; 00289 the_coords_and_id[k].matched = true; 00290 //std::cout<< "matched by handle" << std::endl; 00291 break; 00292 } 00293 } else { 00294 // When matching by proximity, it is possible that the k edge has already 00295 // been matched. If so, skip it. 00296 if (the_coords_and_id[k].matched) continue; 00297 00298 // see if the edge matches 00299 CartVect diff0(the_coords_and_id[j].x1-the_coords_and_id[k].x1, 00300 the_coords_and_id[j].y1-the_coords_and_id[k].y1, 00301 the_coords_and_id[j].z1-the_coords_and_id[k].z1); 00302 CartVect diff1(the_coords_and_id[j].x2-the_coords_and_id[k].x2, 00303 the_coords_and_id[j].y2-the_coords_and_id[k].y2, 00304 the_coords_and_id[j].z2-the_coords_and_id[k].z2); 00305 double d0 = diff0.length_squared(); 00306 double d1 = diff1.length_squared(); 00307 if( d0<tol*tol && d1<tol*tol ) { 00308 the_coords_and_id[j].matched = true; 00309 the_coords_and_id[k].matched = true; 00310 //std::cout<< "matched by proximity" << std::endl; 00311 break; 00312 } 00313 // Due to the sort, once the x-coords are out of tolerance, a match 00314 // cannot exist. 00315 if( the_coords_and_id[k].x1 - the_coords_and_id[j].x1 <= tol) continue; 00316 } 00317 00318 // If no break or continue has been hit, the edge is unmatched. 00319 // if we have a new leaky surface, save it 00320 std::set<int>::iterator found; 00321 found = leaky_surfs.find( the_coords_and_id[j].surf_id ); 00322 if(found == leaky_surfs.end()) { 00323 leaky_surfs.insert( the_coords_and_id[j].surf_id ); 00324 } 00325 found = leaky_vols.find( vol_id ); 00326 if(found == leaky_vols.end()) { 00327 leaky_vols.insert( vol_id ); 00328 } 00329 // print info for unmatched edge 00330 if(verbose) { 00331 // get the coordinates if we don't already have them 00332 if(check_topology) { 00333 double endpt_coords[3]; 00334 result = MBI()->get_coords( &the_coords_and_id[j].vert1, 1, endpt_coords ); 00335 if(MB_SUCCESS != result) return result; 00336 the_coords_and_id[j].x1 = endpt_coords[0]; 00337 the_coords_and_id[j].y1 = endpt_coords[1]; 00338 the_coords_and_id[j].z1 = endpt_coords[2]; 00339 result = MBI()->get_coords( &the_coords_and_id[j].vert2, 1, endpt_coords ); 00340 if(MB_SUCCESS != result) return result; 00341 the_coords_and_id[j].x2 = endpt_coords[0]; 00342 the_coords_and_id[j].y2 = endpt_coords[1]; 00343 the_coords_and_id[j].z2 = endpt_coords[2]; 00344 } 00345 std::cout << " edge of surf " << the_coords_and_id[j].surf_id 00346 << " unmatched: " << " (" 00347 << the_coords_and_id[j].x1 << "," 00348 << the_coords_and_id[j].y1 << "," 00349 << the_coords_and_id[j].z1 << ") (" 00350 << the_coords_and_id[j].x2 << "," 00351 << the_coords_and_id[j].y2 << "," 00352 << the_coords_and_id[j].z2 << ")" 00353 << " v0=" << the_coords_and_id[j].vert1 00354 << " v1=" << the_coords_and_id[j].vert2 00355 << " j=" << j << " k=" << k <<std::endl; 00356 } 00357 unmatched_counter++; 00358 break; 00359 } // k loop 00360 } // j loop 00361 } // volume loop 00362 00363 00364 00365 00366 00367 00368 if(!test) 00369 { 00370 // print time and summary 00371 00372 std::cout << std::endl << unmatched_counter << "/" << total_counter << " (" 00373 << double(100.0*unmatched_counter)/total_counter 00374 << "%) unmatched edges" << std::endl; 00375 std::cout << leaky_surfs.size() << "/" << surf_sets.size() << " (" 00376 << double(100.0*leaky_surfs.size())/surf_sets.size() 00377 << "%) unsealed surfaces" << std::endl; 00378 std::cout << leaky_vols.size() << "/" << vol_sets.size() << " (" 00379 << double(100.0*leaky_vols.size())/vol_sets.size() 00380 << "%) unsealed volumes" << std::endl; 00381 00382 00383 // list the leaky surface and volume ids 00384 std::cout << "leaky surface ids="; 00385 for( std::set<int>::iterator i=leaky_surfs.begin(); i!=leaky_surfs.end(); i++) { 00386 std::cout << *i << " "; 00387 } 00388 std::cout << std::endl; 00389 std::cout << "leaky volume ids="; 00390 for( std::set<int>::iterator i=leaky_vols.begin(); i!=leaky_vols.end(); i++) { 00391 std::cout << *i << " "; 00392 } 00393 std::cout << std::endl; 00394 } 00395 00396 00397 if(unmatched_counter == 0 && leaky_vols.size() == 0 && leaky_surfs.size() == 0 ) 00398 { 00399 sealed=true; 00400 } 00401 else 00402 { 00403 sealed=false; 00404 } 00405 return MB_SUCCESS; 00406 } 00407 00408 /* qsort struct comparision function */ 00409 int compare_by_handle(const void *a, const void *b) 00410 { 00411 struct coords_and_id *ia = (struct coords_and_id *)a; 00412 struct coords_and_id *ib = (struct coords_and_id *)b; 00413 if(ia->vert1 == ib->vert1) 00414 { 00415 return (int)(ia->vert2 - ib->vert2); 00416 } 00417 else 00418 { 00419 return (int)(ia->vert1 - ib->vert1); 00420 } 00421 /* float comparison: returns negative if b > a 00422 and positive if a > b. We multiplied result by 100.0 00423 to preserve decimal fraction */ 00424 } 00425 00426 /* qsort struct comparision function */ 00427 // This is tricky because doubles always get rounded down to ints. 00428 int compare_by_coords(const void *a, const void *b) 00429 { 00430 struct coords_and_id *ia = (struct coords_and_id *)a; 00431 struct coords_and_id *ib = (struct coords_and_id *)b; 00432 if(ia->x1 == ib->x1) { 00433 if(ia->y1 == ib->y1) { 00434 if(ia->z1 == ib->z1) { 00435 if(ia->x2 == ib->x2) { 00436 if(ia->y2 == ib->y2) { 00437 if(ia->z2 == ib->z2) { 00438 return ia->surf_id - ib->surf_id; 00439 } else { 00440 return (ia->z2 > ib->z2) - (ia->z2 < ib->z2); 00441 } 00442 } else { 00443 return (ia->y2 > ib->y2) - (ia->y2 < ib->y2); 00444 } 00445 } else { 00446 return (ia->x2 > ib->x2) - (ia->x2 < ib->x2); 00447 } 00448 } else { 00449 return (ia->z1 > ib->z1) - (ia->z1 < ib->z1); 00450 } 00451 } else { 00452 return (ia->y1 > ib->y1) - (ia->y1 < ib->y1);; 00453 } 00454 } else { 00455 return (ia->x1 > ib->x1) - (ia->x1 < ib->x1); 00456 } 00457 /* float comparison: returns negative if b > a 00458 and positive if a > b. We multiplied result by 100.0 00459 to preserve decimal fraction */ 00460 } 00461 00462 } 00463