MOAB: Mesh Oriented datABase
(version 5.4.1)
|
00001 /* ***************************************************************** 00002 MESQUITE -- The Mesh Quality Improvement Toolkit 00003 00004 Copyright 2004 Sandia Corporation and Argonne National 00005 Laboratory. Under the terms of Contract DE-AC04-94AL85000 00006 with Sandia Corporation, the U.S. Government retains certain 00007 rights in 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 [email protected], [email protected], [email protected], 00024 [email protected], [email protected], [email protected] 00025 00026 ***************************************************************** */ 00027 /*! 00028 \file LPtoPTemplate.cpp 00029 \brief 00030 00031 This Objective Function is evaluated using an L P norm to the pth power. 00032 total=(sum (x_i)^pVal) 00033 \author Michael Brewer 00034 \author Thomas Leurent 00035 \date 2002-01-23 00036 */ 00037 #include <cmath> 00038 #include "LPtoPTemplate.hpp" 00039 #include "MsqFreeVertexIndexIterator.hpp" 00040 #include "MsqTimer.hpp" 00041 #include "MsqHessian.hpp" 00042 #include "MsqDebug.hpp" 00043 #include "QualityMetric.hpp" 00044 00045 using namespace MBMesquite; 00046 00047 LPtoPTemplate::LPtoPTemplate( QualityMetric* qualitymetric, short Pinput, MsqError& err ) 00048 : ObjectiveFunctionTemplate( qualitymetric ) 00049 { 00050 pVal = Pinput; 00051 if( pVal < 1 ) 00052 { 00053 MSQ_SETERR( err )( "P_VALUE must be greater than 0.", MsqError::INVALID_ARG ); 00054 return; 00055 } 00056 00057 dividingByN = false; 00058 00059 clear(); 00060 } 00061 00062 LPtoPTemplate::LPtoPTemplate( short P, QualityMetric* qm ) 00063 : ObjectiveFunctionTemplate( qm ), pVal( P ), dividingByN( false ) 00064 { 00065 clear(); 00066 } 00067 00068 void LPtoPTemplate::clear() 00069 { 00070 mCount = 0; 00071 mPowSum = 0; 00072 saveCount = 0; 00073 savePowSum = 0; 00074 } 00075 00076 // Michael: need to clean up here 00077 LPtoPTemplate::~LPtoPTemplate() {} 00078 00079 ObjectiveFunction* LPtoPTemplate::clone() const 00080 { 00081 return new LPtoPTemplate( *this ); 00082 } 00083 00084 double LPtoPTemplate::get_value( double power_sum, 00085 size_t count, 00086 EvalType type, 00087 size_t& global_count, 00088 MsqError& /*err*/ ) 00089 { 00090 double result = 0; 00091 switch( type ) 00092 { 00093 default: 00094 case CALCULATE: 00095 result = power_sum; 00096 global_count = count; 00097 break; 00098 00099 case ACCUMULATE: 00100 mPowSum += power_sum; 00101 mCount += count; 00102 result = mPowSum; 00103 global_count = mCount; 00104 break; 00105 00106 case SAVE: 00107 savePowSum = power_sum; 00108 saveCount = count; 00109 result = mPowSum; 00110 global_count = mCount; 00111 break; 00112 00113 case UPDATE: 00114 mPowSum -= savePowSum; 00115 mCount -= saveCount; 00116 savePowSum = power_sum; 00117 saveCount = count; 00118 mPowSum += savePowSum; 00119 mCount += saveCount; 00120 result = mPowSum; 00121 global_count = mCount; 00122 break; 00123 00124 case TEMPORARY: 00125 result = mPowSum - savePowSum + power_sum; 00126 global_count = mCount + count - saveCount; 00127 break; 00128 } 00129 00130 // if (!global_count) 00131 // { 00132 // MSQ_SETERR(err)(" global_count is zero, possibly due to an invalid mesh.", 00133 // MsqError::INVALID_MESH); return -1; // result is invalid 00134 // } 00135 if( dividingByN && global_count ) result /= global_count; 00136 return result; 00137 } 00138 00139 bool LPtoPTemplate::evaluate( EvalType type, PatchData& pd, double& value_out, bool free, MsqError& err ) 00140 { 00141 QualityMetric* qm = get_quality_metric(); 00142 if( type == ObjectiveFunction::ACCUMULATE ) 00143 qm->get_single_pass( pd, qmHandles, free, err ); 00144 else 00145 qm->get_evaluations( pd, qmHandles, free, err ); 00146 MSQ_ERRFALSE( err ); 00147 00148 // calculate OF value for just the patch 00149 std::vector< size_t >::const_iterator i; 00150 double value, working_sum = 0.0; 00151 for( i = qmHandles.begin(); i != qmHandles.end(); ++i ) 00152 { 00153 bool result = qm->evaluate( pd, *i, value, err ); 00154 if( MSQ_CHKERR( err ) || !result ) return false; 00155 00156 double tmp_val = value; 00157 for( short j = 1; j < pVal; ++j ) 00158 tmp_val *= value; 00159 working_sum += fabs( tmp_val ); 00160 } 00161 00162 // get overall OF value, update member data, etc. 00163 size_t global_count; 00164 value_out = qm->get_negate_flag() * get_value( working_sum, qmHandles.size(), type, global_count, err ); 00165 // if (!global_count) 00166 // return false; // invalid mesh 00167 // else 00168 return true; 00169 } 00170 00171 bool LPtoPTemplate::evaluate_with_gradient( EvalType type, 00172 PatchData& pd, 00173 double& OF_val, 00174 std::vector< Vector3D >& grad_out, 00175 MsqError& err ) 00176 { 00177 QualityMetric* qm = get_quality_metric(); 00178 qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); 00179 MSQ_ERRFALSE( err ); 00180 00181 // zero gradient 00182 grad_out.clear(); 00183 grad_out.resize( pd.num_free_vertices(), Vector3D( 0.0, 0.0, 0.0 ) ); 00184 bool qm_bool = true; 00185 double QM_val; 00186 OF_val = 0.; 00187 int p1; 00188 00189 // calculate OF value and gradient for just the patch 00190 std::vector< size_t >::const_iterator i; 00191 for( i = qmHandles.begin(); i != qmHandles.end(); ++i ) 00192 { 00193 qm_bool = qm->evaluate_with_gradient( pd, *i, QM_val, mIndices, mGradient, err ); 00194 if( MSQ_CHKERR( err ) || !qm_bool ) return false; 00195 00196 QM_val = fabs( QM_val ); 00197 double QM_pow = 1.0; 00198 double factor = qm->get_negate_flag(); 00199 if( pVal == 1 ) 00200 QM_pow = 1.0; 00201 else 00202 { 00203 QM_pow = QM_val; 00204 for( p1 = 2; p1 < pVal; ++p1 ) 00205 QM_pow *= QM_val; 00206 factor *= QM_pow * pVal; 00207 } 00208 00209 OF_val += QM_pow * QM_val; 00210 for( size_t j = 0; j < mIndices.size(); ++j ) 00211 { 00212 mGradient[j] *= factor; 00213 grad_out[mIndices[j]] += mGradient[j]; 00214 } 00215 } 00216 00217 // get overall OF value, update member data, etc. 00218 size_t global_count; 00219 OF_val = qm->get_negate_flag() * get_value( OF_val, qmHandles.size(), type, global_count, err ); 00220 // if (!global_count) 00221 // return false; // invalid mesh 00222 00223 if( dividingByN && global_count ) 00224 { 00225 const double inv_n = 1.0 / global_count; 00226 std::vector< Vector3D >::iterator g; 00227 for( g = grad_out.begin(); g != grad_out.end(); ++g ) 00228 *g *= inv_n; 00229 } 00230 00231 return true; 00232 } 00233 00234 bool LPtoPTemplate::evaluate_with_Hessian_diagonal( EvalType type, 00235 PatchData& pd, 00236 double& OF_val, 00237 std::vector< Vector3D >& grad, 00238 std::vector< SymMatrix3D >& hess_diag, 00239 MsqError& err ) 00240 { 00241 QualityMetric* qm = get_quality_metric(); 00242 qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); 00243 MSQ_ERRFALSE( err ); 00244 00245 // zero gradient and hessian 00246 grad.clear(); 00247 grad.resize( pd.num_free_vertices(), 0.0 ); 00248 hess_diag.clear(); 00249 hess_diag.resize( pd.num_free_vertices(), 0.0 ); 00250 00251 double QM_val, QM_pow = 1.0; 00252 double fac1, fac2; 00253 const double negate_flag = qm->get_negate_flag(); 00254 bool qm_bool; 00255 size_t i; 00256 short p; 00257 00258 // Loops over all elements in the patch. 00259 OF_val = 0.0; 00260 std::vector< size_t >::const_iterator k; 00261 for( k = qmHandles.begin(); k != qmHandles.end(); ++k ) 00262 { 00263 // Computes \nabla^2 Q(e). Only the free vertices will have non-zero entries. 00264 qm_bool = qm->evaluate_with_Hessian_diagonal( pd, *k, QM_val, mIndices, mGradient, mDiag, err ); 00265 if( MSQ_CHKERR( err ) || !qm_bool ) return false; 00266 QM_val = fabs( QM_val ); 00267 00268 // **** Computes Hessian **** 00269 const size_t nve = mIndices.size(); 00270 if( pVal == 1 ) 00271 { 00272 QM_pow = 1.0; 00273 for( i = 0; i < nve; ++i ) 00274 { 00275 mDiag[i] *= negate_flag; 00276 hess_diag[mIndices[i]] += mDiag[i]; 00277 } 00278 fac1 = 1; 00279 } 00280 else if( pVal >= 2 ) 00281 { 00282 // Computes the coefficients: 00283 QM_pow = 1.0; 00284 for( p = 0; p < pVal - 2; ++p ) 00285 QM_pow *= QM_val; 00286 // 1 - computes p(p-1)Q(e)^{p-2} 00287 fac2 = pVal * ( pVal - 1 ) * QM_pow; 00288 // 2 - computes pQ(e)^{p-1} 00289 QM_pow *= QM_val; 00290 fac1 = pVal * QM_pow; 00291 00292 // fac1 *= qm->get_negate_flag(); 00293 // fac2 *= qm->get_negate_flag(); 00294 00295 for( i = 0; i < nve; ++i ) 00296 { 00297 SymMatrix3D op( mGradient[i] ); 00298 op *= fac2; 00299 mDiag[i] *= fac1; 00300 op += mDiag[i]; 00301 op *= negate_flag; 00302 hess_diag[mIndices[i]] += op; 00303 } 00304 } 00305 else 00306 { 00307 MSQ_SETERR( err )( " invalid P value.", MsqError::INVALID_STATE ); 00308 return false; 00309 } 00310 00311 // **** Computes Gradient **** 00312 00313 // For each vertex in the element ... 00314 for( i = 0; i < nve; ++i ) 00315 { 00316 // ... computes p*q^{p-1}*grad(q) ... 00317 mGradient[i] *= fac1 * negate_flag; 00318 // ... and accumulates it in the objective function gradient. 00319 // also scale the gradient by the scaling factor 00320 assert( mIndices[i] < pd.num_free_vertices() ); 00321 grad[mIndices[i]] += mGradient[i]; 00322 } 00323 00324 // **** computes Objective Function value \sum_{i=1}^{N_e} |q_i|^P **** 00325 OF_val += QM_pow * QM_val; 00326 } 00327 00328 size_t global_count; 00329 OF_val = negate_flag * get_value( OF_val, qmHandles.size(), type, global_count, err ); 00330 // if (!global_count) 00331 // return false; // invalid mesh 00332 00333 if( dividingByN && global_count ) 00334 { 00335 const double inv_n = 1.0 / global_count; 00336 for( i = 0; i < pd.num_free_vertices(); ++i ) 00337 { 00338 grad[i] *= inv_n; 00339 hess_diag[i] *= inv_n; 00340 } 00341 } 00342 00343 return true; 00344 } 00345 00346 /*\ For each element, each entry to be accumulated in the Hessian for 00347 this objective function (\f$ \sum_{e \in E} Q(e)^p \f$ where \f$ E \f$ 00348 is the set of all elements in the patch) has the form: 00349 \f$ pQ(e)^{p-1} \nabla^2 Q(e) + p(p-1)Q(e)^{p-2} \nabla Q(e) [\nabla Q(e)]^T \f$. 00350 00351 For \f$ p=2 \f$, this simplifies to 00352 \f$ 2Q(e) \nabla^2 Q(e) + 2 \nabla Q(e) [\nabla Q(e)]^T \f$. 00353 00354 For \f$ p=1 \f$, this simplifies to \f$ \nabla^2 Q(e) \f$. 00355 00356 The \f$ p=1 \f$ simplified version is implemented directly 00357 to speed up computation. 00358 00359 This function does not support vertex-based metrics. 00360 00361 \param pd The PatchData object for which the objective function 00362 hessian is computed. 00363 \param hessian this object must have been previously initialized. 00364 */ 00365 bool LPtoPTemplate::evaluate_with_Hessian( EvalType type, 00366 PatchData& pd, 00367 double& OF_val, 00368 std::vector< Vector3D >& grad, 00369 MsqHessian& hessian, 00370 MsqError& err ) 00371 { 00372 QualityMetric* qm = get_quality_metric(); 00373 qm->get_evaluations( pd, qmHandles, OF_FREE_EVALS_ONLY, err ); 00374 MSQ_ERRFALSE( err ); 00375 double negate_flag = qm->get_negate_flag(); 00376 00377 // zero gradient and hessian 00378 grad.clear(); 00379 grad.resize( pd.num_free_vertices(), 0.0 ); 00380 hessian.zero_out(); 00381 00382 double QM_val, QM_pow = 1.0; 00383 double fac1, fac2; 00384 Matrix3D elem_outer_product; 00385 bool qm_bool; 00386 size_t i, j, n; 00387 short p; 00388 00389 // Loops over all elements in the patch. 00390 OF_val = 0.0; 00391 std::vector< size_t >::const_iterator k; 00392 for( k = qmHandles.begin(); k != qmHandles.end(); ++k ) 00393 { 00394 // Computes \nabla^2 Q(e). Only the free vertices will have non-zero entries. 00395 qm_bool = qm->evaluate_with_Hessian( pd, *k, QM_val, mIndices, mGradient, mHessian, err ); 00396 if( MSQ_CHKERR( err ) || !qm_bool ) return false; 00397 QM_val = fabs( QM_val ); 00398 00399 // **** Computes Hessian **** 00400 const size_t nve = mIndices.size(); 00401 if( pVal == 1 ) 00402 { 00403 QM_pow = 1.0; 00404 n = 0; 00405 for( i = 0; i < nve; ++i ) 00406 { 00407 for( j = i; j < nve; ++j ) 00408 { 00409 // negate if necessary 00410 mHessian[n] *= negate_flag; 00411 hessian.add( mIndices[i], mIndices[j], mHessian[n], err ); 00412 MSQ_ERRFALSE( err ); 00413 ++n; 00414 } 00415 } 00416 fac1 = 1; 00417 } 00418 else if( pVal >= 2 ) 00419 { 00420 // Computes the coefficients: 00421 QM_pow = 1.0; 00422 for( p = 0; p < pVal - 2; ++p ) 00423 QM_pow *= QM_val; 00424 // 1 - computes p(p-1)Q(e)^{p-2} 00425 fac2 = pVal * ( pVal - 1 ) * QM_pow; 00426 // 2 - computes pQ(e)^{p-1} 00427 QM_pow *= QM_val; 00428 fac1 = pVal * QM_pow; 00429 00430 // fac1 *= qm->get_negate_flag(); 00431 // fac2 *= qm->get_negate_flag(); 00432 00433 n = 0; 00434 for( i = 0; i < nve; ++i ) 00435 { 00436 for( j = i; j < nve; ++j ) 00437 { 00438 elem_outer_product.outer_product( mGradient[i], mGradient[j] ); 00439 elem_outer_product *= fac2; 00440 mHessian[n] *= fac1; 00441 mHessian[n] += elem_outer_product; 00442 mHessian[n] *= negate_flag; 00443 hessian.add( mIndices[i], mIndices[j], mHessian[n], err ); 00444 MSQ_ERRFALSE( err ); 00445 ++n; 00446 } 00447 } 00448 } 00449 else 00450 { 00451 MSQ_SETERR( err )( " invalid P value.", MsqError::INVALID_STATE ); 00452 return false; 00453 } 00454 00455 // **** Computes Gradient **** 00456 00457 // For each vertex in the element ... 00458 for( i = 0; i < nve; ++i ) 00459 { 00460 // ... computes p*q^{p-1}*grad(q) ... 00461 mGradient[i] *= fac1 * negate_flag; 00462 // ... and accumulates it in the objective function gradient. 00463 // also scale the gradient by the scaling factor 00464 assert( mIndices[i] < pd.num_free_vertices() ); 00465 grad[mIndices[i]] += mGradient[i]; 00466 } 00467 00468 // **** computes Objective Function value \sum_{i=1}^{N_e} |q_i|^P **** 00469 OF_val += QM_pow * QM_val; 00470 } 00471 00472 size_t global_count; 00473 OF_val = negate_flag * get_value( OF_val, qmHandles.size(), type, global_count, err ); 00474 // if (!global_count) 00475 // return false; // invalid mesh 00476 00477 if( dividingByN && global_count ) 00478 { 00479 const double inv_n = 1.0 / global_count; 00480 std::vector< Vector3D >::iterator g; 00481 for( g = grad.begin(); g != grad.end(); ++g ) 00482 *g *= inv_n; 00483 hessian.scale( inv_n ); 00484 } 00485 00486 return true; 00487 }