MOAB: Mesh Oriented datABase
(version 5.4.1)
|
00001 /* ***************************************************************** 00002 MESQUITE -- The Mesh Quality Improvement Toolkit 00003 00004 Copyright 2006 Sandia National Laboratories. Developed at the 00005 University of Wisconsin--Madison under SNL contract number 00006 624796. The U.S. Government and the University of Wisconsin 00007 retian certain rights to this software. 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Lesser General Public 00011 License as published by the Free Software Foundation; either 00012 version 2.1 of the License, or (at your option) any later version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 Lesser General Public License for more details. 00018 00019 You should have received a copy of the GNU Lesser General Public License 00020 (lgpl.txt) along with this library; if not, write to the Free Software 00021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00022 00023 (2006) [email protected] 00024 00025 ***************************************************************** */ 00026 00027 #include "Mesquite.hpp" 00028 #include <iostream> 00029 using std::cerr; 00030 using std::cout; 00031 using std::endl; 00032 using std::ostream; 00033 #include <memory> 00034 using std::unique_ptr; 00035 #include <cctype> 00036 00037 #include "MeshImpl.hpp" 00038 #include "MsqError.hpp" 00039 #include "InstructionQueue.hpp" 00040 #include "TerminationCriterion.hpp" 00041 #include "QualityAssessor.hpp" 00042 #include "PMeanPTemplate.hpp" 00043 #include "PatchPowerMeanP.hpp" 00044 #include "ConjugateGradient.hpp" 00045 #include "PlanarDomain.hpp" 00046 #include "IdealShapeTarget.hpp" 00047 #include "ConditionNumberQualityMetric.hpp" 00048 #include "ReferenceMesh.hpp" 00049 #include "RefMeshTargetCalculator.hpp" 00050 #include "TestUtil.hpp" 00051 00052 #include "TQualityMetric.hpp" 00053 #include "ElementPMeanP.hpp" 00054 #include "VertexPMeanP.hpp" 00055 00056 #include "TSquared.hpp" 00057 #include "TShapeNB1.hpp" 00058 #include "TShapeB1.hpp" 00059 #include "TShapeOrientNB1.hpp" 00060 #include "TShapeOrientNB2.hpp" 00061 #include "TShapeOrientB1.hpp" 00062 #include "TShapeSize2DNB1.hpp" 00063 #include "TShapeSizeB1.hpp" 00064 #include "TShapeSize2DB2.hpp" 00065 #include "TShapeSizeB3.hpp" 00066 #include "TShapeSizeOrientNB1.hpp" 00067 #include "TShapeSizeOrientB1.hpp" 00068 #include "TShapeSizeOrientB2.hpp" 00069 00070 using namespace MBMesquite; 00071 00072 static const struct 00073 { 00074 TMetric* u; 00075 const char* n; 00076 } metrics[] = { { new TSquared, "TSquared" }, 00077 { new TShapeNB1, "Shape" }, 00078 { new TShapeB1, "ShapeBarrier" }, 00079 { new TShapeOrientNB1, "ShapeOrient1" }, 00080 { new TShapeOrientNB2, "ShapeOrient2" }, 00081 { new TShapeOrientB1, "ShapeOrientBarrier" }, 00082 { new TShapeSize2DNB1, "ShapeSize" }, 00083 { new TShapeSizeB1, "ShapeSizeBarrier1" }, 00084 { new TShapeSize2DB2, "ShapeSizeBarrier2" }, 00085 { new TShapeSizeB3, "ShapeSizeBarrier3" }, 00086 { new TShapeSizeOrientB1, "ShapeSizeOrient1" }, 00087 { new TShapeSizeOrientB1, "ShapeSizeOrientBarrier1" }, 00088 { new TShapeSizeOrientB2, "ShapeSizeOrientBarrier2" }, 00089 { 0, 0 } }; 00090 00091 enum AveragingScheme 00092 { 00093 NONE = 0, 00094 ELEMENT, 00095 VERTEX, 00096 PATCH 00097 }; 00098 const char* const averaging_names[] = { "none", "element", "vertex", "patch" }; 00099 00100 // default values 00101 const double DEFAULT_OF_POWER = 1.0; 00102 const unsigned DEFAULT_METRIC_IDX = 0; 00103 const AveragingScheme DEFAULT_AVG_SCHEME = NONE; 00104 std::string DEFAULT_INPUT_FILE = TestDir + "unittest/mesquite/2D/vtk/quads/untangled/quads_4by2_bad.vtk"; 00105 std::string DEFAULT_OUTPUT_FILE = "./out.vtk"; 00106 00107 static PlanarDomain make_domain( Mesh* mesh, MsqError& ); 00108 00109 static int do_smoother( const char* input_file, 00110 const char* output_file, 00111 const char* ref_mesh_file, 00112 double of_power, 00113 unsigned metric_idx, 00114 AveragingScheme avg_scheme ) 00115 { 00116 MsqPrintError err( cerr ); 00117 00118 TMetric* const target_metric = metrics[metric_idx].u; 00119 cout << "Input file: " << input_file << endl; 00120 cout << "Metric: "; 00121 if( avg_scheme != NONE ) cout << averaging_names[avg_scheme] << " average of "; 00122 cout << metrics[metric_idx].n << endl; 00123 cout << "Of Power: " << of_power << endl; 00124 00125 unique_ptr< TargetCalculator > tc; 00126 unique_ptr< MeshImpl > ref_mesh_impl; 00127 unique_ptr< ReferenceMesh > ref_mesh; 00128 if( ref_mesh_file ) 00129 { 00130 ref_mesh_impl.reset( new MeshImpl ); 00131 ref_mesh_impl->read_vtk( ref_mesh_file, err ); 00132 if( MSQ_CHKERR( err ) ) return 2; 00133 ref_mesh.reset( new ReferenceMesh( ref_mesh_impl.get() ) ); 00134 tc.reset( new RefMeshTargetCalculator( ref_mesh.get() ) ); 00135 } 00136 else 00137 { 00138 tc.reset( new IdealShapeTarget() ); 00139 } 00140 00141 TQualityMetric jacobian_metric( tc.get(), target_metric ); 00142 ElementPMeanP elem_avg( of_power, &jacobian_metric ); 00143 VertexPMeanP vtx_avg( of_power, &jacobian_metric ); 00144 QualityMetric* mmetrics[] = { &jacobian_metric, &elem_avg, &vtx_avg, &jacobian_metric }; 00145 QualityMetric* metric = mmetrics[avg_scheme]; 00146 00147 TerminationCriterion outer, inner; 00148 outer.add_iteration_limit( 1 ); 00149 inner.add_absolute_vertex_movement( 1e-4 ); 00150 inner.add_iteration_limit( 100 ); 00151 00152 PMeanPTemplate obj1( of_power, metric ); 00153 PatchPowerMeanP obj2( of_power, metric ); 00154 ObjectiveFunction& objective = *( ( avg_scheme == PATCH ) ? (ObjectiveFunction*)&obj2 : (ObjectiveFunction*)&obj1 ); 00155 00156 ConjugateGradient solver( &objective, err ); 00157 if( MSQ_CHKERR( err ) ) return 1; 00158 solver.set_inner_termination_criterion( &inner ); 00159 solver.set_outer_termination_criterion( &outer ); 00160 solver.use_global_patch(); 00161 00162 ConditionNumberQualityMetric qm_metric; 00163 QualityAssessor before_assessor; 00164 QualityAssessor after_assessor; 00165 before_assessor.add_quality_assessment( metric, 10 ); 00166 before_assessor.add_quality_assessment( &qm_metric ); 00167 after_assessor.add_quality_assessment( metric, 10 ); 00168 00169 InstructionQueue q; 00170 q.add_quality_assessor( &before_assessor, err ); 00171 q.set_master_quality_improver( &solver, err ); 00172 q.add_quality_assessor( &after_assessor, err ); 00173 00174 MeshImpl mesh; 00175 mesh.read_vtk( input_file, err ); 00176 if( MSQ_CHKERR( err ) ) return 2; 00177 PlanarDomain geom = make_domain( &mesh, err ); 00178 if( MSQ_CHKERR( err ) ) return 1; 00179 MeshDomainAssoc mesh_and_domain = MeshDomainAssoc( &mesh, &geom ); 00180 q.run_instructions( &mesh_and_domain, err ); 00181 if( MSQ_CHKERR( err ) ) return 3; 00182 mesh.write_vtk( output_file, err ); 00183 if( MSQ_CHKERR( err ) ) return 2; 00184 cout << "Wrote: " << output_file << endl; 00185 00186 before_assessor.scale_histograms( &after_assessor ); 00187 00188 return 0; 00189 } 00190 00191 static PlanarDomain make_domain( Mesh* mesh, MsqError& err ) 00192 { 00193 // calculate bounding box of mesh vertices 00194 Vector3D minimum( HUGE_VAL, HUGE_VAL, HUGE_VAL ); 00195 Vector3D maximum( -HUGE_VAL, -HUGE_VAL, -HUGE_VAL ); 00196 std::vector< Mesh::VertexHandle > vertices; 00197 mesh->get_all_vertices( vertices, err ); 00198 if( MSQ_CHKERR( err ) ) 00199 { 00200 return PlanarDomain( minimum, maximum ); 00201 } 00202 if( vertices.empty() ) 00203 { 00204 std::cerr << "Mesh contains no vertices" << std::endl; 00205 exit( 1 ); 00206 } 00207 std::vector< MsqVertex > coords( vertices.size() ); 00208 mesh->vertices_get_coordinates( arrptr( vertices ), arrptr( coords ), vertices.size(), err ); 00209 if( MSQ_CHKERR( err ) ) 00210 { 00211 return PlanarDomain( minimum, maximum ); 00212 } 00213 std::vector< MsqVertex >::const_iterator i; 00214 for( i = coords.begin(); i != coords.end(); ++i ) 00215 { 00216 const MsqVertex& v = *i; 00217 for( unsigned j = 0; j < 3; ++j ) 00218 { 00219 if( v[j] < minimum[j] ) minimum[j] = v[j]; 00220 if( v[j] > maximum[j] ) maximum[j] = v[j]; 00221 } 00222 } 00223 00224 // Look for a "zero" plane 00225 int k; 00226 maximum -= minimum; 00227 for( k = 2; k >= 0 && maximum[k] > 1e-6; --k ) 00228 ; 00229 if( k < 0 ) 00230 { 00231 MSQ_SETERR( err )( "Cannot determine plane of mesh.", MsqError::INVALID_STATE ); 00232 return PlanarDomain( minimum, maximum ); 00233 } 00234 00235 Vector3D point( 0.0, 0.0, 0.0 ), normal( 0.0, 0.0, 0.0 ); 00236 normal[k] = 1.0; 00237 point[k] = minimum[k]; 00238 return PlanarDomain( normal, point ); 00239 } 00240 00241 static void usage( const char* argv0, bool help = false ) 00242 { 00243 ostream& str = help ? cout : cerr; 00244 str << argv0 << " [-p <power>] [-m metric_name] [-a averaging] [-e]" 00245 << " -r [ref_mesh] [input_file] [output_file]" << endl 00246 << argv0 << " <-l|-h>" << std::endl; 00247 if( help ) 00248 { 00249 str << " -p : specify exponent value for p-mean^p OF template (default: " << DEFAULT_OF_POWER << ")" 00250 << endl 00251 << " -m : specify 2D target metric to use (default: " << metrics[DEFAULT_METRIC_IDX].n << ")" << endl 00252 << " -a : specify metric averaging scheme (default: " << averaging_names[DEFAULT_AVG_SCHEME] << ")" 00253 << endl 00254 << " (none,vertex,element,patch)" << endl 00255 << " -e : sample at mid-edge points (default is corners only)" << endl 00256 << " -r : use reference mesh instead of ideal elements for targets" << endl 00257 << " -l : list available metrics" << endl 00258 << " -h : this help output" << endl 00259 << " default input file: " << DEFAULT_INPUT_FILE << endl 00260 << " default output file: " << DEFAULT_OUTPUT_FILE << endl 00261 << endl; 00262 } 00263 exit( !help ); 00264 } 00265 00266 static void check_next_arg( int argc, char* argv[], int& i ) 00267 { 00268 if( i == argc ) 00269 { 00270 cerr << "Expected argument following \"" << argv[i] << '"' << endl; 00271 usage( argv[0] ); 00272 } 00273 ++i; 00274 } 00275 00276 static double parse_double( int argc, char* argv[], int& i ) 00277 { 00278 check_next_arg( argc, argv, i ); 00279 char* endptr; 00280 double result = strtod( argv[i], &endptr ); 00281 if( endptr == argv[i] || *endptr ) 00282 { 00283 cerr << "Expected real number following \"" << argv[i - 1] << '"' << endl; 00284 usage( argv[0] ); 00285 } 00286 return result; 00287 } 00288 00289 static int comp_string_start( const char* p, const char* s ) 00290 { 00291 int i; 00292 for( i = 0; p[i]; ++i ) 00293 if( tolower( p[i] ) != tolower( s[i] ) ) return 0; 00294 return s[i] ? -1 : 1; 00295 } 00296 00297 static AveragingScheme parse_averaging( int argc, char* argv[], int& i ) 00298 { 00299 check_next_arg( argc, argv, i ); 00300 for( unsigned j = 0; j < 4; ++j ) 00301 if( comp_string_start( argv[i], averaging_names[j] ) ) return (AveragingScheme)j; 00302 cerr << "Expected one of { "; 00303 for( unsigned j = 0; j < 4; ++j ) 00304 cerr << '"' << averaging_names[j] << "\" "; 00305 cerr << "} following \"" << argv[i - 1] << '"' << endl; 00306 usage( argv[0] ); 00307 return NONE; 00308 } 00309 00310 static unsigned parse_metric( int argc, char* argv[], int& i ) 00311 { 00312 check_next_arg( argc, argv, i ); 00313 unsigned part = 0, all = 0, count = 0, have_all = 0; 00314 for( unsigned j = 0; metrics[j].u; ++j ) 00315 { 00316 if( unsigned k = comp_string_start( argv[i], metrics[j].n ) ) 00317 { 00318 if( k > 0 ) 00319 { 00320 all = j; 00321 have_all = 1; 00322 } 00323 else 00324 { 00325 part = j; 00326 ++count; 00327 } 00328 } 00329 } 00330 00331 if( have_all ) return all; 00332 00333 if( count ) 00334 { 00335 if( count == 1 ) return part; 00336 cerr << "Ambiguous metric name: \"" << argv[i] << '"' << endl; 00337 usage( argv[0] ); 00338 } 00339 00340 cerr << "Invalid metric name following \"" << argv[i - 1] << "\" option" << endl; 00341 usage( argv[0] ); 00342 return (unsigned)-1; 00343 } 00344 00345 static void list_metrics() 00346 { 00347 cout << "Available metrics:" << endl; 00348 for( unsigned j = 0; metrics[j].u; ++j ) 00349 cout << "\t" << metrics[j].n << endl; 00350 exit( 0 ); 00351 } 00352 00353 int main( int argc, char* argv[] ) 00354 { 00355 MsqPrintError err( cout ); 00356 00357 // CL settings 00358 double of_power = DEFAULT_OF_POWER; 00359 unsigned metric_idx = DEFAULT_METRIC_IDX; 00360 AveragingScheme avg_scheme = DEFAULT_AVG_SCHEME; 00361 const char* input_file = 0; 00362 const char* output_file = 0; 00363 const char* ref_mesh_file = 0; 00364 00365 bool proc_opts = true; 00366 for( int i = 1; i < argc; ++i ) 00367 { 00368 if( !proc_opts || argv[i][0] != '-' ) 00369 { 00370 if( output_file ) 00371 { 00372 cerr << "Unexpected file name: \"" << argv[i] << '"' << endl; 00373 usage( argv[0] ); 00374 } 00375 else if( input_file ) 00376 output_file = argv[i]; 00377 else 00378 input_file = argv[i]; 00379 continue; 00380 } 00381 00382 if( !argv[i][1] || argv[i][2] ) 00383 { 00384 cerr << "Invalid option: \"" << argv[i] << '"' << endl; 00385 usage( argv[0] ); 00386 } 00387 00388 switch( argv[i][1] ) 00389 { 00390 case 'p': 00391 of_power = parse_double( argc, argv, i ); 00392 break; 00393 case 'm': 00394 metric_idx = parse_metric( argc, argv, i ); 00395 break; 00396 case 'a': 00397 avg_scheme = parse_averaging( argc, argv, i ); 00398 break; 00399 case 'r': 00400 check_next_arg( argc, argv, i ); 00401 ref_mesh_file = argv[i]; 00402 break; 00403 case '-': 00404 proc_opts = false; 00405 break; 00406 case 'h': 00407 usage( argv[0], true ); 00408 break; 00409 case 'l': 00410 list_metrics(); 00411 break; 00412 default: 00413 cerr << "Invalid option: \"" << argv[i] << '"' << endl; 00414 usage( argv[0] ); 00415 } 00416 } 00417 00418 if( !input_file ) input_file = DEFAULT_INPUT_FILE.c_str(); 00419 if( !output_file ) output_file = DEFAULT_OUTPUT_FILE.c_str(); 00420 00421 return do_smoother( input_file, output_file, ref_mesh_file, of_power, metric_idx, avg_scheme ); 00422 }