MOAB: Mesh Oriented datABase  (version 5.4.1)
TMPQualityMetric.cpp
Go to the documentation of this file.
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     retain 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 /** \file TMPQualityMetric.cpp
00028  *  \brief
00029  *  \author Jason Kraftcheck
00030  */
00031 
00032 #undef PRINT_INFO
00033 
00034 #include "Mesquite.hpp"
00035 #include "TMPQualityMetric.hpp"
00036 #include "MsqMatrix.hpp"
00037 #include "ElementQM.hpp"
00038 #include "MsqError.hpp"
00039 #include "Vector3D.hpp"
00040 #include "PatchData.hpp"
00041 #include "MappingFunction.hpp"
00042 #include "WeightCalculator.hpp"
00043 #include "TargetCalculator.hpp"
00044 #include "TargetMetricUtil.hpp"
00045 
00046 #ifdef PRINT_INFO
00047 #include <iostream>
00048 #endif
00049 
00050 #include <functional>
00051 #include <algorithm>
00052 
00053 namespace MBMesquite
00054 {
00055 
00056 int TMPQualityMetric::get_negate_flag() const
00057 {
00058     return 1;
00059 }
00060 
00061 void TMPQualityMetric::get_evaluations( PatchData& pd, std::vector< size_t >& handles, bool free, MsqError& err )
00062 {
00063     get_sample_pt_evaluations( pd, handles, free, err );
00064 }
00065 
00066 void TMPQualityMetric::get_patch_evaluations( PatchData& pd, std::vector< size_t >& handles, bool free, MsqError& err )
00067 {
00068     get_sample_pt_evaluations( pd, handles, free, err );
00069 }
00070 
00071 void TMPQualityMetric::get_element_evaluations( PatchData& pd,
00072                                                 size_t p_elem,
00073                                                 std::vector< size_t >& handles,
00074                                                 MsqError& err )
00075 {
00076     get_elem_sample_points( pd, p_elem, handles, err );
00077 }
00078 
00079 bool TMPQualityMetric::evaluate( PatchData& pd, size_t p_handle, double& value, MsqError& err )
00080 {
00081     size_t num_idx;
00082     bool valid = evaluate_internal( pd, p_handle, value, mIndices, num_idx, err );
00083     if( MSQ_CHKERR( err ) || !valid ) return false;
00084 
00085     // apply target weight to value
00086     if( weightCalc )
00087     {
00088         const Sample s = ElemSampleQM::sample( p_handle );
00089         const size_t e = ElemSampleQM::elem( p_handle );
00090         double ck      = weightCalc->get_weight( pd, e, s, err );
00091         MSQ_ERRZERO( err );
00092         value *= ck;
00093     }
00094     return true;
00095 }
00096 
00097 bool TMPQualityMetric::evaluate_with_indices( PatchData& pd,
00098                                               size_t p_handle,
00099                                               double& value,
00100                                               std::vector< size_t >& indices,
00101                                               MsqError& err )
00102 {
00103     indices.resize( MAX_ELEM_NODES );
00104     size_t num_idx = 0;
00105     bool result    = evaluate_internal( pd, p_handle, value, arrptr( indices ), num_idx, err );
00106     if( MSQ_CHKERR( err ) || !result ) return false;
00107 
00108     indices.resize( num_idx );
00109 
00110     // apply target weight to value
00111     if( weightCalc )
00112     {
00113         const Sample s = ElemSampleQM::sample( p_handle );
00114         const size_t e = ElemSampleQM::elem( p_handle );
00115         double ck      = weightCalc->get_weight( pd, e, s, err );
00116         MSQ_ERRZERO( err );
00117         value *= ck;
00118     }
00119 
00120     return true;
00121 }
00122 
00123 static void get_u_perp( const MsqVector< 3 >& u, MsqVector< 3 >& u_perp )
00124 {
00125     double a = sqrt( u[0] * u[0] + u[1] * u[1] );
00126     if( a < 1e-10 )
00127     {
00128         u_perp[0] = 1.0;
00129         u_perp[1] = u_perp[2] = 0.0;
00130     }
00131     else
00132     {
00133         double b  = -u[2] / a;
00134         u_perp[0] = u[0] * b;
00135         u_perp[1] = u[1] * b;
00136         u_perp[2] = a;
00137     }
00138 }
00139 
00140 /* Do transform M_hat = S_a M_{3x2}, M_{2x2} Theta^-1 M_hat
00141  * where the plane into which we are projecting is orthogonal
00142  * to the passed u vector.
00143  */
00144 static inline bool project_to_perp_plane( MsqMatrix< 3, 2 > J,
00145                                           const MsqVector< 3 >& u,
00146                                           const MsqVector< 3 >& u_perp,
00147                                           MsqMatrix< 2, 2 >& A,
00148                                           MsqMatrix< 3, 2 >& S_a_transpose_Theta )
00149 {
00150     MsqVector< 3 > n_a = J.column( 0 ) * J.column( 1 );
00151     double sc, len = length( n_a );
00152     if( !divide( 1.0, len, sc ) ) return false;
00153     n_a *= sc;
00154     double ndot          = n_a % u;
00155     double sigma         = ( ndot < 0.0 ) ? -1 : 1;
00156     double cosphi        = sigma * ndot;
00157     MsqVector< 3 > cross = n_a * u;
00158     double sinphi        = length( cross );
00159 
00160     MsqMatrix< 3, 2 > Theta;
00161     Theta.set_column( 0, u_perp );
00162     Theta.set_column( 1, u * u_perp );
00163 
00164     // If columns of J are not in plane orthogonal to u, then
00165     // rotate J such that they are.
00166     if( sinphi > 1e-12 )
00167     {
00168         MsqVector< 3 > m = sigma * cross;
00169         MsqVector< 3 > n = ( 1 / sinphi ) * m;
00170         MsqVector< 3 > p = ( 1 - cosphi ) * n;
00171         double s_a[]     = { p[0] * n[0] + cosphi, p[0] * n[1] - m[2],   p[0] * n[2] + m[1],
00172                          p[1] * n[0] + m[2],   p[1] * n[1] + cosphi, p[1] * n[2] - m[0],
00173                          p[2] * n[0] - m[1],   p[2] * n[1] + m[0],   p[2] * n[2] + cosphi };
00174         MsqMatrix< 3, 3 > S_a( s_a );
00175         J                   = S_a * J;
00176         S_a_transpose_Theta = transpose( S_a ) * Theta;
00177     }
00178     else
00179     {
00180         S_a_transpose_Theta = Theta;
00181         //    J *= sigma;
00182     }
00183 
00184     // Project to get 2x2 A from A_hat (which might be equal to J)
00185     A = transpose( Theta ) * J;
00186     return true;
00187 }
00188 
00189 /* Do transform M_hat = S_a M_{3x2}, M_{2x2} Theta^-1 M_hat
00190  * where the plane into which we are projecting is the cross
00191  * product of the columns of M, such that S_a is I.  Use the
00192  * first column of M as u_perp.
00193  *
00194  * Also pass back the cross product of the columns of M as u,
00195  * and the first column of M as u_perp, both normalized.
00196  */
00197 static inline void project_to_matrix_plane( const MsqMatrix< 3, 2 >& M_in,
00198                                             MsqMatrix< 2, 2 >& M_out,
00199                                             MsqVector< 3 >& u,
00200                                             MsqVector< 3 >& u_perp )
00201 {
00202     u            = M_in.column( 0 ) * M_in.column( 1 );
00203     u_perp       = M_in.column( 0 );
00204     double len0  = length( u_perp );
00205     double u_len = length( u );
00206     double d_perp, d_u;
00207     if( !divide( 1.0, len0, d_perp ) )
00208     {
00209         // try the other column
00210         u_perp = M_in.column( 1 );
00211         len0   = length( u_perp );
00212         if( !divide( 1.0, len0, d_perp ) )
00213         {
00214             // matrix is all zeros
00215             u[0]      = 0;
00216             u[1]      = 0;
00217             u[2]      = 1;
00218             u_perp[0] = 1;
00219             u_perp[1] = 0;
00220             u_perp[2] = 0;
00221             M_out     = MsqMatrix< 2, 2 >( 0.0 );
00222         }
00223         else
00224         {
00225             MsqMatrix< 3, 2 > junk;
00226             get_u_perp( u_perp, u );
00227             project_to_perp_plane( M_in, u, u_perp, M_out, junk );
00228         }
00229     }
00230     else if( !divide( 1.0, u_len, d_u ) )
00231     {
00232         MsqMatrix< 3, 2 > junk;
00233         get_u_perp( u_perp, u );
00234         project_to_perp_plane( M_in, u, u_perp, M_out, junk );
00235     }
00236     else
00237     {  // the normal case (neither column is zero)
00238         u *= d_u;
00239         u_perp *= d_perp;
00240 
00241         // M_out = transpose(theta)*M_in
00242         M_out( 0, 0 ) = len0;
00243         M_out( 0, 1 ) = u_perp % M_in.column( 1 );
00244         M_out( 1, 0 ) = 0.0;
00245         M_out( 1, 1 ) = u_len / len0;
00246     }
00247 }
00248 
00249 bool TMPQualityMetric::evaluate_surface_common( PatchData& pd,
00250                                                 Sample s,
00251                                                 size_t e,
00252                                                 const NodeSet& bits,
00253                                                 size_t* indices,
00254                                                 size_t& num_indices,
00255                                                 MsqVector< 2 >* derivs,
00256                                                 MsqMatrix< 2, 2 >& W,
00257                                                 MsqMatrix< 2, 2 >& A,
00258                                                 MsqMatrix< 3, 2 >& S_a_transpose_Theta,
00259                                                 MsqError& err )
00260 {
00261     EntityTopology type = pd.element_by_index( e ).get_element_type();
00262 
00263     const MappingFunction2D* mf = pd.get_mapping_function_2D( type );
00264     if( !mf )
00265     {
00266         MSQ_SETERR( err )( "No mapping function for element type", MsqError::UNSUPPORTED_ELEMENT );
00267         return false;
00268     }
00269 
00270     MsqMatrix< 3, 2 > J;
00271     mf->jacobian( pd, e, bits, s, indices, derivs, num_indices, J, err );
00272 
00273     // If we have a 3x2 target matrix
00274     if( targetCalc->have_surface_orient() )
00275     {
00276         MsqVector< 3 > u, u_perp;
00277         MsqMatrix< 3, 2 > W_hat;
00278         targetCalc->get_surface_target( pd, e, s, W_hat, err );
00279         MSQ_ERRZERO( err );
00280         // Use the cross product of the columns of W as the normal of the
00281         // plane to work in (i.e. u.).  W should have been constructed such
00282         // that said cross product is in the direction of (n_s)_init.  And if
00283         // for some reason it as not, then using something other than said
00284         // cross product is likely to produce very wrong results.
00285         project_to_matrix_plane( W_hat, W, u, u_perp );
00286         // Do the transforms on A to align it with W and project into the plane.
00287         if( !project_to_perp_plane( J, u, u_perp, A, S_a_transpose_Theta ) ) return false;
00288     }
00289     // Otherwise if we have a 2x2 target matrix (i.e. the target does
00290     // not contain orientation information), project into the plane
00291     // tangent to J.
00292     else
00293     {
00294         MsqVector< 3 > u, u_perp;
00295         targetCalc->get_2D_target( pd, e, s, W, err );
00296         MSQ_ERRZERO( err );
00297         project_to_matrix_plane( J, A, u, u_perp );
00298         S_a_transpose_Theta.set_column( 0, u_perp );
00299         S_a_transpose_Theta.set_column( 1, u * u_perp );
00300         // If the domain is set, adjust the sign of things correctly
00301         // for the case where the element is inverted with respect
00302         // to the domain.
00303         if( pd.domain_set() )
00304         {
00305             Vector3D n;
00306             pd.get_domain_normal_at_sample( e, s, n, err );
00307             MSQ_ERRZERO( err );
00308             // if sigma == -1
00309             if( Vector3D( u.data() ) % n < 0.0 )
00310             {
00311                 // flip u
00312                 u = -u;
00313                 // S_a_transpose_Theta == Theta, because S_a == I here.
00314                 // u_perp is unaffected by flipping u, so only the second
00315                 // column of S_a_transpose_Theta and the second row of A
00316                 // are flipped because u x u_perp will be flipped.
00317                 S_a_transpose_Theta.set_column( 1, -S_a_transpose_Theta.column( 1 ) );
00318                 A.set_row( 1, -A.row( 1 ) );
00319             }
00320         }
00321     }
00322 
00323     return true;
00324 }
00325 
00326 void TMPQualityMetric::weight( PatchData& pd,
00327                                Sample p_sample,
00328                                size_t p_elem,
00329                                int num_idx,
00330                                double& value,
00331                                Vector3D* grad,
00332                                SymMatrix3D* diag,
00333                                Matrix3D* hess,
00334                                MsqError& err )
00335 {
00336     if( !weightCalc ) return;
00337 
00338     double ck = weightCalc->get_weight( pd, p_elem, p_sample, err );MSQ_ERRRTN( err );
00339     value *= ck;
00340     if( grad )
00341     {
00342         for( int i = 0; i < num_idx; ++i )
00343             grad[i] *= ck;
00344     }
00345     if( diag )
00346     {
00347         for( int i = 0; i < num_idx; ++i )
00348             diag[i] *= ck;
00349     }
00350     if( hess )
00351     {
00352         const int n = num_idx * ( num_idx + 1 ) / 2;
00353         for( int i = 0; i < n; ++i )
00354             hess[i] *= ck;
00355     }
00356 }
00357 
00358 void TMPQualityMetric::initialize_queue( MeshDomainAssoc* mesh_and_domain, const Settings* settings, MsqError& err )
00359 {
00360     targetCalc->initialize_queue( mesh_and_domain, settings, err );MSQ_ERRRTN( err );
00361     if( weightCalc )
00362     {
00363         weightCalc->initialize_queue( mesh_and_domain, settings, err );MSQ_ERRRTN( err );
00364     }
00365 }
00366 
00367 }  // namespace MBMesquite
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines