1 /* *****************************************************************
2     MESQUITE -- The Mesh Quality Improvement Toolkit
3 
4     Copyright 2006 Sandia National Laboratories.  Developed at the
5     University of Wisconsin--Madison under SNL contract number
6     624796.  The U.S. Government and the University of Wisconsin
7     retian certain rights to this software.
8 
9     This library is free software; you can redistribute it and/or
10     modify it under the terms of the GNU Lesser General Public
11     License as published by the Free Software Foundation; either
12     version 2.1 of the License, or (at your option) any later version.
13 
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     Lesser General Public License for more details.
18 
19     You should have received a copy of the GNU Lesser General Public License
20     (lgpl.txt) along with this library; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23     (2006) kraftche@cae.wisc.edu
24     (2010) kraftche@cae.wisc.edu
25 
26   ***************************************************************** */
27 
28 /*! \file TMPQualityMetricTest.cpp
29 
30 Unit testing for the TMPQualityMetric class
31 \author Jasno Kraftcheck
32 */
33 
34 
35 #include "IdealShapeTarget.hpp"
36 #include "MsqMatrix.hpp"
37 #include "QualityMetricTester.hpp"
38 #include "Settings.hpp"
39 #include "UnitUtil.hpp"
40 #include "PlanarDomain.hpp"
41 #include "PatchData.hpp"
42 #include "WeightCalculator.hpp"
43 #include "ElementPMeanP.hpp"
44 #include "ElemSampleQM.hpp"
45 
46 #include <iostream>
47 
48 using namespace MBMesquite;
49 using std::cout;
50 using std::cerr;
51 using std::endl;
52 
53 
54 /** Target metric (templatized by dimension) for use in misc. tests.
55  *  'evaluate' method records input values and returns a constant.
56  */
57 template <typename B>
58 class FauxMetric : public B
59 {
60 public:
61   int count;
62   double value;
63   bool rval;
64   MsqMatrix<2,2> last_A_2D;
65   MsqMatrix<3,3> last_A_3D;
66 
FauxMetric(double v)67   FauxMetric(double v) : count(0), value(v), rval(true) {}
68 
get_name() const69   std::string get_name() const { return "Faux"; }
70 
evaluate(const MsqMatrix<2,2> & A,const MsqMatrix<2,2> & W,double & result,MsqError &)71   bool evaluate( const MsqMatrix<2,2>& A,
72                  const MsqMatrix<2,2>& W,
73                  double& result, MsqError&  )
74   {
75     last_A_2D = A;
76     result = value;
77     ++count;
78     return rval;
79   }
80 
evaluate(const MsqMatrix<3,3> & A,const MsqMatrix<3,3> & W,double & result,MsqError &)81   bool evaluate( const MsqMatrix<3,3>& A,
82                  const MsqMatrix<3,3>& W,
83                  double& result, MsqError&  )
84   {
85     last_A_3D = A;
86     result = value;
87     ++count;
88     return rval;
89   }
90 
evaluate(const MsqMatrix<2,2> & T,double & result,MsqError &)91   bool evaluate( const MsqMatrix<2,2>& T,
92                  double& result, MsqError&  )
93   {
94     last_A_2D = T;
95     result = value;
96     ++count;
97     return rval;
98   }
99 
evaluate(const MsqMatrix<3,3> & T,double & result,MsqError &)100   bool evaluate( const MsqMatrix<3,3>& T,
101                  double& result, MsqError&  )
102   {
103     last_A_3D = T;
104     result = value;
105     ++count;
106     return rval;
107   }
108 };
109 
110 /** Weight calculator used for testing.  Returns constant weight. */
111 class ScaleWeight : public WeightCalculator
112 {
113   public:
ScaleWeight(double s)114     ScaleWeight( double s ) : value(s) {}
get_weight(PatchData &,size_t,Sample,MsqError &)115     double get_weight( PatchData&, size_t, Sample, MsqError& )
116       { return value; }
117     double value;
118 };
119 
120 /** wrapper class to force numeric approximation of derivatives */
121 template <class Base>
122 class NumericalMetric : public Base
123 {
124 public:
125 
NumericalMetric(Base * real_metric)126   NumericalMetric( Base* real_metric ) : mMetric(real_metric) {}
127 
~NumericalMetric()128   ~NumericalMetric() {}
129 
get_name() const130   std::string get_name() const
131     { return "Numerical " + mMetric->get_name(); }
132 
evaluate(const MsqMatrix<2,2> & A,const MsqMatrix<2,2> & W,double & result,MsqError & err)133   bool evaluate( const MsqMatrix<2,2>& A,
134                  const MsqMatrix<2,2>& W,
135                  double& result,
136                  MsqError& err )
137   { return mMetric->evaluate( A, W, result, err ); }
138 
evaluate(const MsqMatrix<3,3> & A,const MsqMatrix<3,3> & W,double & result,MsqError & err)139   bool evaluate( const MsqMatrix<3,3>& A,
140                  const MsqMatrix<3,3>& W,
141                  double& result,
142                  MsqError& err )
143   { return mMetric->evaluate( A, W, result, err ); }
144 
evaluate(const MsqMatrix<2,2> & T,double & result,MsqError & err)145   bool evaluate( const MsqMatrix<2,2>& T,
146                  double& result,
147                  MsqError& err )
148   { return mMetric->evaluate( T, result, err ); }
149 
evaluate(const MsqMatrix<3,3> & T,double & result,MsqError & err)150   bool evaluate( const MsqMatrix<3,3>& T,
151                  double& result,
152                  MsqError& err )
153   { return mMetric->evaluate( T, result, err ); }
154 private:
155   Base* mMetric;
156 };
157 
158 
159 /** Simple target metric for testing first partial derivatives.
160  *  \f$\mu(A,W) = |A|^2\f$
161  *  \f$\frac{\partial\mu}{\partial \A} = 2 A \f$
162  */
163 template <class Base>
164 class TestGradTargetMetric : public Base
165 {
166   public:
167 
get_name() const168     std::string get_name() const { return "TestGrad"; }
169 
evaluate(const MsqMatrix<2,2> & T,double & result,MsqError & err)170     bool evaluate( const MsqMatrix<2,2>& T,
171                    double& result, MsqError& err )
172       { result = sqr_Frobenius(T); return true; }
173 
evaluate(const MsqMatrix<2,2> & A,const MsqMatrix<2,2> &,double & result,MsqError & err)174     bool evaluate( const MsqMatrix<2,2>& A,
175                    const MsqMatrix<2,2>&,
176                    double& result, MsqError& err )
177       { return evaluate( A, result, err ); }
178 
evaluate_with_grad(const MsqMatrix<2,2> & T,double & result,MsqMatrix<2,2> & d,MsqError & err)179     bool evaluate_with_grad( const MsqMatrix<2,2>& T,
180                              double& result,
181                              MsqMatrix<2,2>& d,
182                              MsqError& err )
183     {
184       result = sqr_Frobenius(T);
185       d = 2*T;
186       return true;
187     }
188 
evaluate_with_grad(const MsqMatrix<2,2> & A,const MsqMatrix<2,2> &,double & result,MsqMatrix<2,2> & d,MsqError & err)189     bool evaluate_with_grad( const MsqMatrix<2,2>& A,
190                              const MsqMatrix<2,2>&,
191                              double& result,
192                              MsqMatrix<2,2>& d,
193                              MsqError& err )
194     { return evaluate_with_grad( A, result, d, err ); }
195 
evaluate(const MsqMatrix<3,3> & T,double & result,MsqError & err)196     bool evaluate( const MsqMatrix<3,3>& T,
197                    double& result, MsqError& err )
198       { result = sqr_Frobenius(T); return true; }
199 
evaluate(const MsqMatrix<3,3> & A,const MsqMatrix<3,3> &,double & result,MsqError & err)200     bool evaluate( const MsqMatrix<3,3>& A,
201                    const MsqMatrix<3,3>&,
202                    double& result, MsqError& err )
203       { return evaluate( A, result, err ); }
204 
evaluate_with_grad(const MsqMatrix<3,3> & T,double & result,MsqMatrix<3,3> & d,MsqError & err)205     bool evaluate_with_grad( const MsqMatrix<3,3>& T,
206                              double& result,
207                              MsqMatrix<3,3>& d,
208                              MsqError& err )
209     {
210       result = sqr_Frobenius(T);
211       d = 2*T;
212       return true;
213     }
214 
evaluate_with_grad(const MsqMatrix<3,3> & A,const MsqMatrix<3,3> &,double & result,MsqMatrix<3,3> & d,MsqError & err)215     bool evaluate_with_grad( const MsqMatrix<3,3>& A,
216                              const MsqMatrix<3,3>&,
217                              double& result,
218                              MsqMatrix<3,3>& d,
219                              MsqError& err )
220     { return evaluate_with_grad( A, result, d, err ); }
221 };
222 
223 /* class to force evaluation of mapping function only at element center
224  * so that we can re-use tests from QualityMetricTester that work only
225  * for element-based metrics (TMP metric is "element based" if only one
226  * sample in each element.)
227  */
228 class CenterMF2D : public MappingFunction2D
229 {
230   public:
CenterMF2D(const MappingFunction2D * real_mf)231     CenterMF2D( const MappingFunction2D* real_mf ) : myFunc(real_mf) {};
element_topology() const232     EntityTopology element_topology() const { return myFunc->element_topology(); }
num_nodes() const233     int num_nodes() const { return myFunc->num_nodes(); }
sample_points(NodeSet) const234     NodeSet sample_points( NodeSet ) const { NodeSet s; s.set_mid_face_node(0); return s; }
coefficients(Sample l,NodeSet s,double * c,size_t * i,size_t & n,MsqError & e) const235     void coefficients( Sample l, NodeSet s, double* c, size_t* i, size_t& n, MsqError& e ) const
236       { myFunc->coefficients( l, s, c, i, n, e ); }
derivatives(Sample l,NodeSet s,size_t * i,MsqVector<2> * c,size_t & n,MsqError & e) const237     void derivatives( Sample l, NodeSet s, size_t* i, MsqVector<2>* c, size_t& n, MsqError& e ) const
238       { myFunc->derivatives( l, s, i, c, n, e ); }
239   private:
240     const MappingFunction2D* myFunc;
241 };
242 class CenterMF3D : public MappingFunction3D
243 {
244   public:
CenterMF3D(const MappingFunction3D * real_mf)245     CenterMF3D( const MappingFunction3D* real_mf ) : myFunc(real_mf) {};
element_topology() const246     EntityTopology element_topology() const { return myFunc->element_topology(); }
num_nodes() const247     int num_nodes() const { return myFunc->num_nodes(); }
sample_points(NodeSet) const248     NodeSet sample_points( NodeSet ) const { NodeSet s; s.set_mid_region_node(); return s; }
coefficients(Sample l,NodeSet s,double * c,size_t * i,size_t & n,MsqError & e) const249     void coefficients( Sample l, NodeSet s, double* c, size_t* i, size_t& n, MsqError& e ) const
250       { myFunc->coefficients( l, s, c, i, n, e ); }
derivatives(Sample l,NodeSet s,size_t * i,MsqVector<3> * c,size_t & n,MsqError & e) const251     void derivatives( Sample l, NodeSet s, size_t* i, MsqVector<3>* c, size_t& n, MsqError& e ) const
252       { myFunc->derivatives( l, s, i, c, n, e ); }
253   private:
254     const MappingFunction3D* myFunc;
255 };
256 
257 // Define a target calculator that returns targets for
258 // ideally shaped elements, but also includes orientation
259 // information (aligning surface elements to the xy plane
260 // with the first column of the jacobian in the x direction).
261 class IdealShapeXY : public IdealShapeTarget
262 {
have_surface_orient() const263   public: bool have_surface_orient() const { return true; }
get_surface_target(PatchData & pd,size_t element,Sample sample,MsqMatrix<3,2> & W_out,MsqError & err)264           bool get_surface_target( PatchData& pd,
265                                    size_t element,
266                                    Sample sample,
267                                    MsqMatrix<3,2>& W_out,
268                                    MsqError& err )
269           { MsqMatrix<2,2> W;
270             bool rval = get_2D_target( pd, element, sample, W, err );
271             W_out.set_row( 0, W.row(0) );
272             W_out.set_row( 1, W.row(1) );
273             W_out.set_row( 2, MsqMatrix<1,2>(0.0) );
274             return rval;
275           }
276 };
277 
278 #define REGISTER_TMP_TESTS \
279   \
280   CPPUNIT_TEST (test_negate_flag);\
281   CPPUNIT_TEST (test_supported_types);\
282   CPPUNIT_TEST (test_get_evaluations);\
283   CPPUNIT_TEST (test_get_element_evaluations);\
284   \
285   CPPUNIT_TEST (test_evaluate_2D);\
286   CPPUNIT_TEST (test_evaluate_surface);\
287   CPPUNIT_TEST (test_evaluate_3D);\
288   CPPUNIT_TEST (test_evaluate_2D_weight);\
289   CPPUNIT_TEST (test_evaluate_surface_weight);\
290   CPPUNIT_TEST (test_evaluate_3D_weight);\
291   CPPUNIT_TEST (test_2d_eval_ortho_quad);\
292   CPPUNIT_TEST (test_surf_eval_ortho_quad);\
293   CPPUNIT_TEST (test_3d_eval_ortho_hex);\
294   \
295   CPPUNIT_TEST (test_sample_indices);\
296   CPPUNIT_TEST (test_evaluate_with_indices);\
297   CPPUNIT_TEST (test_evaluate_fixed_indices);\
298   \
299   CPPUNIT_TEST (test_gradient_2D);\
300   CPPUNIT_TEST (test_gradient_surface);\
301   CPPUNIT_TEST (test_gradient_3D);\
302   CPPUNIT_TEST (compare_indices_and_gradient);\
303   CPPUNIT_TEST (test_ideal_element_gradient);\
304   CPPUNIT_TEST (compare_analytical_and_numerical_gradient);\
305   CPPUNIT_TEST (test_weighted_gradients);\
306   CPPUNIT_TEST (test_gradient_with_fixed_vertices);\
307   \
308   CPPUNIT_TEST (compare_indices_and_hessian);\
309   CPPUNIT_TEST (compare_gradient_and_hessian);\
310   CPPUNIT_TEST (compare_analytical_and_numerical_hessians);\
311   CPPUNIT_TEST (test_symmetric_hessian_diagonal);\
312   CPPUNIT_TEST (test_weighted_hessians);\
313   CPPUNIT_TEST (test_hessian_with_fixed_vertices);\
314   \
315   CPPUNIT_TEST (compare_indices_and_diagonal);\
316   CPPUNIT_TEST (compare_gradient_and_diagonal);\
317   CPPUNIT_TEST (compare_analytical_and_numerical_diagonals);\
318   CPPUNIT_TEST (test_weighted_diagonals);\
319   CPPUNIT_TEST (test_diagonal_with_fixed_vertices);
320 
col_dot_prod(MsqMatrix<2,2> & m)321 static double col_dot_prod( MsqMatrix<2,2>& m )
322   { return m(0,0) * m(0,1) + m(1,0) * m(1,1); }
323 
324 template <class QMType> class TMPTypes {
325 };
326 
327 template <class QMType>
328 class TMPQualityMetricTest : public CppUnit::TestFixture
329 {
330 protected:
331   QualityMetricTester tester;
332 
333   Settings settings;
334   IdealShapeTarget ideal;
335   IdealShapeXY surf_target;
336   ScaleWeight e_weight;
337 
338   FauxMetric< typename TMPTypes<QMType>::MetricType > faux_pi, faux_zero, faux_two;
339   typename TMPTypes<QMType>::TestType test_metric;
340   NumericalMetric< typename QMType::MetricType > num_metric;
341   QMType test_qm, test_qm_surf, zero_qm, weight_qm, center_qm;
342   Settings centerOnly;
343   CenterMF2D triCenter, quadCenter;
344   CenterMF3D tetCenter, pyrCenter, priCenter, hexCenter;
345 
346 public:
TMPQualityMetricTest()347   TMPQualityMetricTest() :
348     tester( QualityMetricTester::ALL_FE_EXCEPT_SEPTAHEDRON, &settings ),
349     e_weight( 2.7182818284590451 ),
350     faux_pi(3.14159), faux_zero(0.0), faux_two(2.0),
351     num_metric( &test_metric ),
352     test_qm( &ideal, &num_metric ),
353     test_qm_surf( &surf_target, &num_metric ),
354     zero_qm( &ideal, &faux_zero ),
355     weight_qm( &ideal, &e_weight, &test_metric ),
356     center_qm( &ideal, &test_metric ),
357     triCenter( centerOnly.get_mapping_function_2D(TRIANGLE) ),
358     quadCenter( centerOnly.get_mapping_function_2D(QUADRILATERAL) ),
359     tetCenter( centerOnly.get_mapping_function_3D(TETRAHEDRON) ),
360     pyrCenter( centerOnly.get_mapping_function_3D(PYRAMID) ),
361     priCenter( centerOnly.get_mapping_function_3D(PRISM) ),
362     hexCenter( centerOnly.get_mapping_function_3D(HEXAHEDRON) )
363   {
364     centerOnly.set_mapping_function( &triCenter );
365     centerOnly.set_mapping_function( &quadCenter );
366     centerOnly.set_mapping_function( &tetCenter );
367     centerOnly.set_mapping_function( &pyrCenter );
368     centerOnly.set_mapping_function( &priCenter );
369     centerOnly.set_mapping_function( &hexCenter );
370     tester.ideal_pyramid_base_equals_height( true );
371   }
372 
test_negate_flag()373   void test_negate_flag()
374     { CPPUNIT_ASSERT_EQUAL( 1, zero_qm.get_negate_flag() ); }
test_supported_types()375   void test_supported_types()
376     { tester.test_supported_element_types( &zero_qm ); }
test_get_evaluations()377   void test_get_evaluations()
378     {
379       QMType edge_metric( &ideal, &faux_zero );
380       tester.test_get_sample_evaluations( &zero_qm );
381       tester.test_get_sample_evaluations( &edge_metric );
382     }
test_get_element_evaluations()383   void test_get_element_evaluations()
384     {
385       QMType edge_metric( &ideal, &faux_zero );
386       tester.test_get_in_element_evaluations( &zero_qm );
387       tester.test_get_in_element_evaluations( &edge_metric );
388     }
389 
390   void test_evaluate_2D();
391       void test_evaluate_surface();
392   void test_evaluate_3D();
393 
test_evaluate_2D_weight()394   void test_evaluate_2D_weight()
395     {
396       MsqPrintError err(cout);
397       PatchData pd;
398       bool rval;
399       double value;
400 
401       QMType m( &ideal, &e_weight, &faux_pi );
402       tester.get_ideal_element( TRIANGLE, true, pd );
403       rval = m.evaluate( pd, 0, value, err );
404       CPPUNIT_ASSERT(!MSQ_CHKERR(err));
405       CPPUNIT_ASSERT(rval);
406       CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value*e_weight.value, value, DBL_EPSILON );
407     }
408 
test_evaluate_surface_weight()409   void test_evaluate_surface_weight()
410     {
411       MsqPrintError err(cout);
412       PatchData pd;
413       bool rval;
414       double value;
415 
416       QMType m( &surf_target, &e_weight, &faux_pi );
417 
418       tester.get_ideal_element( TRIANGLE, true, pd );
419       rval = m.evaluate( pd, 0, value, err );
420       CPPUNIT_ASSERT(!MSQ_CHKERR(err));
421       CPPUNIT_ASSERT(rval);
422       CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value*e_weight.value, value, DBL_EPSILON );
423     }
424 
test_evaluate_3D_weight()425   void test_evaluate_3D_weight()
426     {
427       MsqPrintError err(cout);
428       PatchData pd;
429       bool rval;
430       double value;
431 
432       QMType m( &ideal, &e_weight, &faux_two );
433 
434       tester.get_ideal_element( PRISM, true, pd );
435       rval = m.evaluate( pd, 0, value, err );
436       CPPUNIT_ASSERT(!MSQ_CHKERR(err));
437       CPPUNIT_ASSERT(rval);
438       CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_two.value*e_weight.value, value, DBL_EPSILON );
439     }
440 
test_2d_eval_ortho_quad()441   void test_2d_eval_ortho_quad()
442     {
443       MsqPrintError err(cout);
444       PatchData pd;
445       bool rval;
446       double value;
447 
448       QMType m( &ideal, &faux_zero );
449       faux_zero.count = 0;
450 
451       tester.get_ideal_element( QUADRILATERAL, true, pd );
452       rval = m.evaluate( pd, 0, value, err );
453       CPPUNIT_ASSERT(!MSQ_CHKERR(err));
454       CPPUNIT_ASSERT(rval);
455       CPPUNIT_ASSERT_EQUAL( 1, faux_zero.count );
456       CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, col_dot_prod(faux_zero.last_A_2D), DBL_EPSILON );
457     }
458 
test_surf_eval_ortho_quad()459   void test_surf_eval_ortho_quad()
460     {
461       MsqPrintError err(cout);
462       PatchData pd;
463       bool rval;
464       double value;
465 
466       QMType m( &surf_target, &faux_zero );
467       faux_zero.count = 0;
468 
469       tester.get_ideal_element( QUADRILATERAL, true, pd );
470       rval = m.evaluate( pd, 0, value, err );
471       CPPUNIT_ASSERT(!MSQ_CHKERR(err));
472       CPPUNIT_ASSERT(rval);
473       CPPUNIT_ASSERT_EQUAL( 1, faux_zero.count );
474       CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, col_dot_prod(faux_zero.last_A_2D), DBL_EPSILON );
475     }
476 
test_3d_eval_ortho_hex()477   void test_3d_eval_ortho_hex()
478     {
479       MsqPrintError err(cout);
480       PatchData pd;
481       bool rval;
482       double value;
483 
484       QMType m( &ideal, &faux_zero );
485       faux_zero.count = 0;
486 
487       tester.get_ideal_element( HEXAHEDRON, true, pd );
488       rval = m.evaluate( pd, 0, value, err );
489       CPPUNIT_ASSERT(!MSQ_CHKERR(err));
490       CPPUNIT_ASSERT(rval);
491       CPPUNIT_ASSERT_EQUAL( 1, faux_zero.count );
492 
493         // test that columns are orthogonal for ideal hex element
494       MsqMatrix<3,3> A = faux_zero.last_A_3D;
495       CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(1), 1e-6 );
496       CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(2), 1e-6 );
497       CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(1) % A.column(2), 1e-6 );
498     }
499 
500   void test_gradient_common(TargetCalculator* tc);
test_gradient_2D()501   void test_gradient_2D() { test_gradient_common( &ideal ); }
test_gradient_surface()502   void test_gradient_surface() { test_gradient_common( &surf_target ); }
503   void test_gradient_3D();
504 
test_sample_indices()505   void test_sample_indices()
506     { tester.test_get_sample_indices( &zero_qm ); }
test_evaluate_with_indices()507   void test_evaluate_with_indices()
508     { tester.compare_eval_and_eval_with_indices( &zero_qm ); }
test_evaluate_fixed_indices()509   void test_evaluate_fixed_indices()
510     { tester.test_get_indices_fixed( &zero_qm ); }
511 
compare_indices_and_gradient()512   void compare_indices_and_gradient()
513     { tester.compare_eval_with_indices_and_eval_with_gradient( &test_qm );
514       tester.compare_eval_with_indices_and_eval_with_gradient( &test_qm_surf ); }
test_ideal_element_gradient()515   void test_ideal_element_gradient()
516     { tester.test_ideal_element_zero_gradient( &test_qm, false );
517       tester.test_ideal_element_zero_gradient( &test_qm_surf, false ); }
compare_analytical_and_numerical_gradient()518   void compare_analytical_and_numerical_gradient()
519     { compare_analytical_and_numerical_gradients( &test_qm );
520       compare_analytical_and_numerical_gradients( &test_qm_surf ); }
test_weighted_gradients()521   void test_weighted_gradients()
522     { compare_analytical_and_numerical_gradients( &weight_qm ); }
test_gradient_with_fixed_vertices()523   void test_gradient_with_fixed_vertices()
524     { tester.test_gradient_with_fixed_vertex( &center_qm, &centerOnly ); }
525 
compare_indices_and_hessian()526   void compare_indices_and_hessian()
527     { tester.compare_eval_with_indices_and_eval_with_hessian( &test_qm );
528       tester.compare_eval_with_indices_and_eval_with_hessian( &test_qm_surf ); }
compare_gradient_and_hessian()529   void compare_gradient_and_hessian()
530     { tester.compare_eval_with_grad_and_eval_with_hessian( &test_qm );
531       tester.compare_eval_with_grad_and_eval_with_hessian( &test_qm_surf ); }
compare_analytical_and_numerical_hessians()532   void compare_analytical_and_numerical_hessians()
533     { compare_analytical_and_numerical_hessians( &test_qm );
534       compare_analytical_and_numerical_hessians( &test_qm_surf ); }
test_symmetric_hessian_diagonal()535   void test_symmetric_hessian_diagonal()
536     { tester.test_symmetric_Hessian_diagonal_blocks( &test_qm );
537       tester.test_symmetric_Hessian_diagonal_blocks( &test_qm_surf ); }
test_weighted_hessians()538   void test_weighted_hessians()
539     { compare_analytical_and_numerical_hessians( &weight_qm ); }
test_hessian_with_fixed_vertices()540   void test_hessian_with_fixed_vertices()
541     { tester.test_hessian_with_fixed_vertex( &center_qm, &centerOnly ); }
542 
compare_indices_and_diagonal()543   void compare_indices_and_diagonal()
544     { tester.compare_eval_with_indices_and_eval_with_diagonal( &test_qm );
545       tester.compare_eval_with_indices_and_eval_with_diagonal( &test_qm_surf ); }
compare_gradient_and_diagonal()546   void compare_gradient_and_diagonal()
547     { tester.compare_eval_with_grad_and_eval_with_diagonal( &test_qm );
548       tester.compare_eval_with_grad_and_eval_with_diagonal( &test_qm_surf ); }
compare_analytical_and_numerical_diagonals()549   void compare_analytical_and_numerical_diagonals()
550     { compare_analytical_and_numerical_diagonals( &test_qm );
551       compare_analytical_and_numerical_diagonals( &test_qm_surf ); }
test_weighted_diagonals()552   void test_weighted_diagonals()
553     { compare_analytical_and_numerical_diagonals( &weight_qm ); }
test_diagonal_with_fixed_vertices()554   void test_diagonal_with_fixed_vertices()
555     { tester.test_diagonal_with_fixed_vertex( &center_qm, &centerOnly ); }
556 
557     // Delcare specialized versions of the functions from
558     // QualityMetricTester because we surface elements must
559     // be handled differently.  For a surface element in the XY plane,
560     // the finite difference approximations of the derivatives will
561     // have non-zero values for derivatives wrt Z coordinates while the
562     // analytical derivative calculations will return all derivatives
563     // wrt Z coordiantes as zero.
564 
get_nonideal_element(EntityTopology type,PatchData & pd)565   void get_nonideal_element( EntityTopology type, PatchData& pd )
566     {
567       tester.get_nonideal_element( type, pd, true );
568         // Callers assume surface elements are in XY plane.
569         // Verify this assumption.
570       if (TopologyInfo::dimension(type) == 2) {
571         for (size_t i = 0; i < pd.num_nodes(); ++i) {
572           CPPUNIT_ASSERT_DOUBLES_EQUAL( pd.vertex_by_index(i)[2], 0.0, 1e-6 );
573         }
574       }
575     }
576 
compare_analytical_and_numerical_gradients(QualityMetric * qm)577   void compare_analytical_and_numerical_gradients( QualityMetric* qm )
578     {
579       PatchData pd;
580       const EntityTopology types[] = { TRIANGLE,
581                                        QUADRILATERAL,
582                                        TETRAHEDRON,
583                                        PYRAMID,
584                                        PRISM,
585                                        HEXAHEDRON };
586       const int num_types = sizeof(types)/sizeof(types[0]);
587       for (int i = 0; i < num_types; ++i) {
588         get_nonideal_element( types[i], pd );
589         compare_analytical_and_numerical_gradients( qm, pd, TopologyInfo::dimension(types[i]) );
590       }
591     }
592 
593   void compare_analytical_and_numerical_hessians( QualityMetric* qm );
594   void compare_analytical_and_numerical_diagonals( QualityMetric* qm );
595   void compare_analytical_and_numerical_gradients( QualityMetric* qm, PatchData&, int dim );
596 };
597 
598 //CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TMPQualityMetricTest, "TMPQualityMetricTest");
599 //CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(TMPQualityMetricTest, "Unit");
600 
601 template <class QMType> inline
test_evaluate_2D()602 void TMPQualityMetricTest<QMType>::test_evaluate_2D()
603 {
604   MsqPrintError err(cout);
605   PatchData pd;
606   bool rval;
607   double value;
608 
609   QMType m( &ideal, &faux_pi );
610 
611     // test with aligned elements
612   faux_pi.count = 0;
613   tester.get_ideal_element( QUADRILATERAL, true, pd );
614   rval = m.evaluate( pd, 0, value, err );
615   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
616   CPPUNIT_ASSERT(rval);
617   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value, value, DBL_EPSILON );
618   CPPUNIT_ASSERT_EQUAL( 1, faux_pi.count );
619 
620     // test that columns are orthogonal for ideal quad element
621   CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, col_dot_prod(faux_pi.last_A_2D), 1e-6 );
622 
623     // test with an element rotated about X-axis
624   faux_pi.count = 0;
625   tester.get_ideal_element( QUADRILATERAL, true, pd );
626   // rotate by 90 degrees about X axis
627   for (size_t i = 0; i < pd.num_nodes(); ++i) {
628     Vector3D orig = pd.vertex_by_index(i);
629     Vector3D newp( orig[0], -orig[2], orig[1] );
630     pd.set_vertex_coordinates( newp, i, err );
631   }
632   rval = m.evaluate( pd, 0, value, err );
633   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
634   CPPUNIT_ASSERT(rval);
635   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value, value, DBL_EPSILON );
636   CPPUNIT_ASSERT_EQUAL( 1, faux_pi.count );
637 
638     // test with an element rotated about Y-axis
639   faux_pi.count = 0;
640   tester.get_ideal_element( TRIANGLE, true, pd );
641   // rotate by -90 degrees about Y axis
642   for (size_t i = 0; i < pd.num_nodes(); ++i) {
643     Vector3D orig = pd.vertex_by_index(i);
644     Vector3D newp( orig[2], orig[1], -orig[0] );
645     pd.set_vertex_coordinates( newp, i, err );
646   }
647   rval = m.evaluate( pd, 0, value, err );
648   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
649   CPPUNIT_ASSERT(rval);
650   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value, value, DBL_EPSILON );
651   CPPUNIT_ASSERT_EQUAL( 1, faux_pi.count );
652 }
653 
654 template <class QMType> inline
test_evaluate_surface()655 void TMPQualityMetricTest<QMType>::test_evaluate_surface()
656 {
657   MsqPrintError err(cout);
658   PatchData pd;
659   bool rval;
660   double value;
661 
662   QMType m( &surf_target, &faux_pi );
663 
664     // test with aligned elements
665   faux_pi.count = 0;
666   tester.get_ideal_element( QUADRILATERAL, true, pd );
667   rval = m.evaluate( pd, 0, value, err );
668   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
669   CPPUNIT_ASSERT(rval);
670   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value, value, DBL_EPSILON );
671   CPPUNIT_ASSERT_EQUAL( 1, faux_pi.count );
672 
673     // test that columns are orthogonal for ideal quad element
674   CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, col_dot_prod(faux_pi.last_A_2D), 1e-6 );
675 
676     // test with an element rotated about X-axis
677   faux_pi.count = 0;
678   tester.get_ideal_element( QUADRILATERAL, true, pd );
679   // rotate by 90 degrees about X axis
680   for (size_t i = 0; i < pd.num_nodes(); ++i) {
681     Vector3D orig = pd.vertex_by_index(i);
682     Vector3D newp( orig[0], -orig[2], orig[1] );
683     pd.set_vertex_coordinates( newp, i, err );
684   }
685   rval = m.evaluate( pd, 0, value, err );
686   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
687   CPPUNIT_ASSERT(rval);
688   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value, value, DBL_EPSILON );
689   CPPUNIT_ASSERT_EQUAL( 1, faux_pi.count );
690 
691     // test with an element rotated about Y-axis
692   faux_pi.count = 0;
693   tester.get_ideal_element( TRIANGLE, true, pd );
694   // rotate by -90 degrees about Y axis
695   for (size_t i = 0; i < pd.num_nodes(); ++i) {
696     Vector3D orig = pd.vertex_by_index(i);
697     Vector3D newp( orig[2], orig[1], -orig[0] );
698     pd.set_vertex_coordinates( newp, i, err );
699   }
700   rval = m.evaluate( pd, 0, value, err );
701   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
702   CPPUNIT_ASSERT(rval);
703   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_pi.value, value, DBL_EPSILON );
704   CPPUNIT_ASSERT_EQUAL( 1, faux_pi.count );
705 }
706 
707 
708 template <class QMType> inline
test_evaluate_3D()709 void TMPQualityMetricTest<QMType>::test_evaluate_3D()
710 {
711   MsqPrintError err(cout);
712   PatchData pd;
713   bool rval;
714   double value;
715 
716   QMType m( &ideal, &faux_two );
717 
718     // test with aligned elements
719   faux_two.count = 0;
720   tester.get_ideal_element( HEXAHEDRON, true, pd );
721   rval = m.evaluate( pd, 0, value, err );
722   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
723   CPPUNIT_ASSERT(rval);
724   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_two.value, value, DBL_EPSILON );
725   CPPUNIT_ASSERT_EQUAL( 1, faux_two.count );
726 
727     // test that columns are orthogonal for ideal hex element
728   MsqMatrix<3,3> A = faux_two.last_A_3D;
729   CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(1), 1e-6 );
730   CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(2), 1e-6 );
731   CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(1) % A.column(2), 1e-6 );
732 
733     // test with rotated element
734   faux_two.count = 0;
735   tester.get_ideal_element( TETRAHEDRON, true, pd );
736   // rotate by 90-degrees about X axis
737   for (size_t i = 0; i < pd.num_nodes(); ++i) {
738     Vector3D orig = pd.vertex_by_index(i);
739     Vector3D newp( orig[0], -orig[2], orig[1] );
740     pd.set_vertex_coordinates( newp, i, err );
741   }
742   rval = m.evaluate( pd, 0, value, err );
743   CPPUNIT_ASSERT(!MSQ_CHKERR(err));
744   CPPUNIT_ASSERT(rval);
745   CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_two.value, value, DBL_EPSILON );
746   CPPUNIT_ASSERT_EQUAL( 1, faux_two.count );
747 }
748 
749 
750 template <class QMType> inline
test_gradient_common(TargetCalculator * tc)751 void TMPQualityMetricTest<QMType>::test_gradient_common(TargetCalculator* tc)
752 {
753   MsqPrintError err(std::cout);
754 
755     // check for expected value at center of flattened hex
756 
757     // construct flattened hex
758   const double y = 0.5;
759   const double vertices[] = { 0.0, 0.0, 0.0,
760                               1.0, 0.0, 0.0,
761                               1.0, y  , 0.0,
762                               0.0, y  , 0.0 };
763   size_t conn[8] = { 0, 1, 2, 3 };
764   PatchData pd;
765   pd.fill( 4, vertices, 1, QUADRILATERAL, conn, 0, err );
766   ASSERT_NO_ERROR(err);
767 
768     // calculate Jacobian matrix at element center
769 
770     // derivatives of bilinear map at quad center
771   const double deriv_vals[] = { -0.5, -0.5,
772                                  0.5, -0.5,
773                                  0.5,  0.5,
774                                 -0.5,  0.5 } ;
775   MsqMatrix<4,2> coeff_derivs(deriv_vals);
776   MsqMatrix<4,3> coords( vertices );
777   MsqMatrix<3,2> J = transpose(coords) * coeff_derivs;
778     // calculate expected metric value
779   const double expt_val = sqr_Frobenius( J );
780     // calculate derivative for each element vertex
781   MsqVector<3> expt_grad[4];
782   for (int v = 0; v < 4; ++v)
783     expt_grad[v] = 2 * J * transpose( coeff_derivs.row(v) );
784 
785 
786     // construct metric
787   pd.attach_settings( &settings );
788   TestGradTargetMetric< typename TMPTypes<QMType>::MetricType > tm;
789   //IdealShapeTarget tc;
790   QMType m( tc, &tm );
791   PlanarDomain plane( PlanarDomain::XY );
792   pd.set_domain( &plane );
793 
794     // evaluate metric
795   double act_val;
796   std::vector<size_t> indices;
797   std::vector<Vector3D> act_grad;
798   size_t h = ElemSampleQM::handle( Sample(2, 0), 0 );
799   m.evaluate_with_gradient( pd, h, act_val, indices, act_grad, err );
800   ASSERT_NO_ERROR(err);
801 
802     // compare values
803   CPPUNIT_ASSERT_DOUBLES_EQUAL( expt_val, act_val, 1e-10 );
804   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[0]].data()), act_grad[0], 1e-10 );
805   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[1]].data()), act_grad[1], 1e-10 );
806   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[2]].data()), act_grad[2], 1e-10 );
807   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[3]].data()), act_grad[3], 1e-10 );
808 
809     // check numerical approx of gradient
810   m.QualityMetric::evaluate_with_gradient( pd, h, act_val, indices, act_grad, err );
811   ASSERT_NO_ERROR(err);
812   CPPUNIT_ASSERT_DOUBLES_EQUAL( expt_val, act_val, 1e-10 );
813   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[0]].data()), act_grad[0], 1e-5 );
814   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[1]].data()), act_grad[1], 1e-5 );
815   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[2]].data()), act_grad[2], 1e-5 );
816   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[3]].data()), act_grad[3], 1e-5 );
817 }
818 
819 
820 template <class QMType> inline
test_gradient_3D()821 void TMPQualityMetricTest<QMType>::test_gradient_3D()
822 {
823   MsqPrintError err(std::cout);
824 
825     // check for expected value at center of flattened hex
826 
827     // construct flattened hex
828   const double z = 0.5;
829   const double vertices[] = { 0.0, 0.0, 0.0,
830                               1.0, 0.0, 0.0,
831                               1.0, 1.0, 0.0,
832                               0.0, 1.0, 0.0,
833                               0.0, 0.0, z,
834                               1.0, 0.0, z,
835                               1.0, 1.0, z,
836                               0.0, 1.0, z };
837   size_t conn[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
838   PatchData pd;
839   pd.fill( 8, vertices, 1, HEXAHEDRON, conn, 0, err );
840   ASSERT_NO_ERROR(err);
841 
842     // calculate Jacobian matrix at element center
843 
844     // derivatives of trilinear map at hex center
845   const double deriv_vals[8][3] = { { -0.25, -0.25, -0.25 },
846                                     {  0.25, -0.25, -0.25 },
847                                     {  0.25,  0.25, -0.25 },
848                                     { -0.25,  0.25, -0.25 },
849                                     { -0.25, -0.25,  0.25 },
850                                     {  0.25, -0.25,  0.25 },
851                                     {  0.25,  0.25,  0.25 },
852                                     { -0.25,  0.25,  0.25 } };
853   MsqMatrix<8,3> coeff_derivs(deriv_vals);
854   MsqMatrix<8,3> coords( vertices );
855   MsqMatrix<3,3> J = transpose(coords) * coeff_derivs;
856     // calculate expected metric value
857   const double expt_val = sqr_Frobenius( J );
858     // calculate derivative for each element vertex
859   MsqVector<3> expt_grad[8];
860   for (int v = 0; v < 8; ++v)
861     expt_grad[v] = 2 * J * transpose( coeff_derivs.row(v) );
862 
863 
864     // construct metric
865   pd.attach_settings( &settings );
866   TestGradTargetMetric< typename TMPTypes<QMType>::MetricType > tm;
867   IdealShapeTarget tc;
868   QMType m( &tc, 0, &tm );
869 
870     // evaluate metric
871   double act_val;
872   std::vector<size_t> indices;
873   std::vector<Vector3D> act_grad;
874   size_t h = ElemSampleQM::handle( Sample(3, 0), 0 );
875   m.evaluate_with_gradient( pd, h, act_val, indices, act_grad, err );
876   ASSERT_NO_ERROR(err);
877 
878     // compare values
879   CPPUNIT_ASSERT_DOUBLES_EQUAL( expt_val, act_val, 1e-10 );
880   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[0]].data()), act_grad[0], 1e-10 );
881   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[1]].data()), act_grad[1], 1e-10 );
882   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[2]].data()), act_grad[2], 1e-10 );
883   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[3]].data()), act_grad[3], 1e-10 );
884   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[4]].data()), act_grad[4], 1e-10 );
885   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[5]].data()), act_grad[5], 1e-10 );
886   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[6]].data()), act_grad[6], 1e-10 );
887   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[7]].data()), act_grad[7], 1e-10 );
888 
889     // check numerical approx of gradient
890   m.QualityMetric::evaluate_with_gradient( pd, h, act_val, indices, act_grad, err );
891   ASSERT_NO_ERROR(err);
892   CPPUNIT_ASSERT_DOUBLES_EQUAL( expt_val, act_val, 1e-10 );
893   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[0]].data()), act_grad[0], 1e-5 );
894   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[1]].data()), act_grad[1], 1e-5 );
895   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[2]].data()), act_grad[2], 1e-5 );
896   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[3]].data()), act_grad[3], 1e-5 );
897   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[4]].data()), act_grad[4], 1e-5 );
898   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[5]].data()), act_grad[5], 1e-5 );
899   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[6]].data()), act_grad[6], 1e-5 );
900   CPPUNIT_ASSERT_VECTORS_EQUAL( Vector3D(expt_grad[indices[7]].data()), act_grad[7], 1e-5 );
901 }
902 
903 template <class QMType> inline
compare_analytical_and_numerical_gradients(QualityMetric * qm,PatchData & pd,int dim)904 void TMPQualityMetricTest<QMType>::compare_analytical_and_numerical_gradients(
905                                                       QualityMetric* qm,
906                                                       PatchData& pd,
907                                                       int dim )
908 {
909   MsqPrintError err( std::cout );
910 
911   std::vector<size_t> handles, indices1, indices2;
912   std::vector<Vector3D> grad1, grad2;
913   double qm_val1, qm_val2;
914   bool rval;
915 
916   qm->get_evaluations( pd, handles, false, err );
917   CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
918   CPPUNIT_ASSERT( !handles.empty() );
919   for (size_t j = 0; j < handles.size(); ++j) {
920     rval = qm->QualityMetric::evaluate_with_gradient( pd, handles[j], qm_val1, indices1, grad1, err );
921     CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
922     CPPUNIT_ASSERT( rval );
923 
924       // For analytical gradient of a 2D element in the XY plane,
925       // we expect all Z terms to be zero.
926     if (dim == 2)
927       for (size_t k = 0; k < grad1.size(); ++k)
928         grad1[k][2] = 0.0;
929 
930     rval = qm->evaluate_with_gradient( pd, handles[j], qm_val2, indices2, grad2, err );
931     CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
932     CPPUNIT_ASSERT( rval );
933 
934     CPPUNIT_ASSERT_DOUBLES_EQUAL( qm_val1, qm_val2, 1e-6 );
935     CPPUNIT_ASSERT_EQUAL( indices1.size(), indices2.size() );
936     CPPUNIT_ASSERT( !indices1.empty() );
937 
938     std::vector<size_t>::iterator it1, it2;
939     for (it1 = indices1.begin(); it1 != indices1.end(); ++it1) {
940       it2 = std::find( indices2.begin(), indices2.end(), *it1 );
941       CPPUNIT_ASSERT( it2 != indices2.end() );
942 
943       size_t idx1 = it1 - indices1.begin();
944       size_t idx2 = it2 - indices2.begin();
945       CPPUNIT_ASSERT_VECTORS_EQUAL( grad1[idx1], grad2[idx2], 0.01 );
946     }
947   }
948 }
949 
950 
951     // Delcare specialized versions of the functions from
952     // QualityMetricTester because we surface elements must
953     // be handled differently.  For a surface element in the XY plane,
954     // the finite difference approximations of the derivatives will
955     // have non-zero values for derivatives wrt Z coordinates while the
956     // analytical derivative calculations will return all derivatives
957     // wrt Z coordiantes as zero.
958 template <class QMType> inline
compare_analytical_and_numerical_hessians(QualityMetric * qm)959 void TMPQualityMetricTest<QMType>::compare_analytical_and_numerical_hessians( QualityMetric* qm )
960 {
961   MsqPrintError err( std::cout );
962   PatchData pd;
963   const EntityTopology types[] = { TRIANGLE,
964                                    QUADRILATERAL,
965                                    TETRAHEDRON,
966                                    PYRAMID,
967                                    PRISM,
968                                    HEXAHEDRON };
969   const int num_types = sizeof(types)/sizeof(types[0]);
970   for (int i = 0; i < num_types; ++i) {
971     get_nonideal_element( types[i], pd );
972 
973     std::vector<size_t> handles, indices1, indices2;
974     std::vector<Vector3D> grad1, grad2;
975     std::vector<Matrix3D> Hess1, Hess2;
976     double qm_val1, qm_val2;
977     bool rval;
978 
979     qm->get_evaluations( pd, handles, false, err );
980     CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
981     CPPUNIT_ASSERT( !handles.empty() );
982     for (size_t j = 0; j < handles.size(); ++j) {
983       rval = qm->QualityMetric::evaluate_with_Hessian( pd, handles[j], qm_val1, indices1, grad1, Hess1, err );
984       CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
985       CPPUNIT_ASSERT( rval );
986 
987         // For analytical gradient of a 2D element in the XY plane,
988         // we expect all Z terms to be zero.
989 #ifdef PLANAR_HESSIAN
990       if (TopologyInfo::dimension(types[i]) == 2)
991         for (size_t k = 0; k < Hess1.size(); ++k)
992           Hess1[k](0,2) = Hess1[k](1,2) = Hess1[k](2,0)
993             = Hess1[k](2,1) = Hess1[k](2,2) = 0.0;
994 #endif
995 
996       rval = qm->evaluate_with_Hessian( pd, handles[j], qm_val2, indices2, grad2, Hess2, err );
997       CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
998       CPPUNIT_ASSERT( rval );
999 
1000       CPPUNIT_ASSERT_DOUBLES_EQUAL( qm_val1, qm_val2, 1e-6 );
1001       CPPUNIT_ASSERT_EQUAL( indices1.size(), indices2.size() );
1002       CPPUNIT_ASSERT( !indices1.empty() );
1003 
1004       std::vector<size_t>::iterator it;
1005       unsigned h = 0;
1006       for (unsigned r = 0; r < indices1.size(); ++r) {
1007         it = std::find( indices2.begin(), indices2.end(), indices1[r] );
1008         CPPUNIT_ASSERT( it != indices2.end() );
1009         unsigned r2 = it - indices2.begin();
1010 
1011         for (unsigned c = r; c < indices1.size(); ++c, ++h) {
1012           it = std::find( indices2.begin(), indices2.end(), indices1[c] );
1013           CPPUNIT_ASSERT( it != indices2.end() );
1014           unsigned c2 = it - indices2.begin();
1015 
1016           unsigned h2;
1017           if (r2 <= c2)
1018             h2 = indices2.size()*r - r*(r+1)/2 + c;
1019           else
1020             h2 = indices2.size()*c - c*(c+1)/2 + r;
1021 
1022           //if (!utest_mat_equal(Hess1[h],Hess2[h2],0.001))
1023           //  assert(false);
1024           CPPUNIT_ASSERT_MATRICES_EQUAL( Hess1[h], Hess2[h2], 0.05 );
1025         }
1026       }
1027     }
1028   }
1029 }
1030 
1031     // Delcare specialized versions of the functions from
1032     // QualityMetricTester because we surface elements must
1033     // be handled differently.  For a surface element in the XY plane,
1034     // the finite difference approximations of the derivatives will
1035     // have non-zero values for derivatives wrt Z coordinates while the
1036     // analytical derivative calculations will return all derivatives
1037     // wrt Z coordiantes as zero.
1038 template <class QMType> inline
compare_analytical_and_numerical_diagonals(QualityMetric * qm)1039 void TMPQualityMetricTest<QMType>::compare_analytical_and_numerical_diagonals( QualityMetric* qm )
1040 {
1041   MsqPrintError err( std::cout );
1042   PatchData pd;
1043   const EntityTopology types[] = { TRIANGLE,
1044                                    QUADRILATERAL,
1045                                    TETRAHEDRON,
1046                                    PYRAMID,
1047                                    PRISM,
1048                                    HEXAHEDRON };
1049   const int num_types = sizeof(types)/sizeof(types[0]);
1050   for (int i = 0; i < num_types; ++i) {
1051     get_nonideal_element( types[i], pd );
1052 
1053     std::vector<size_t> handles, indices1, indices2;
1054     std::vector<Vector3D> grad1, grad2;
1055     std::vector<Matrix3D> Hess1;
1056     std::vector<SymMatrix3D> Hess2;
1057     double qm_val1, qm_val2;
1058     bool rval;
1059 
1060     qm->get_evaluations( pd, handles, false, err );
1061     CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
1062     CPPUNIT_ASSERT( !handles.empty() );
1063     for (size_t j = 0; j < handles.size(); ++j) {
1064       rval = qm->QualityMetric::evaluate_with_Hessian( pd, handles[j], qm_val1, indices1, grad1, Hess1, err );
1065       CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
1066       CPPUNIT_ASSERT( rval );
1067 
1068         // For analytical gradient of a 2D element in the XY plane,
1069         // we expect all Z terms to be zero.
1070 #ifdef PLANAR_HESSIAN
1071       if (TopologyInfo::dimension(types[i]) == 2)
1072         for (size_t k = 0; k < Hess1.size(); ++k)
1073           Hess1[k](0,2) = Hess1[k](1,2) = Hess1[k](2,0)
1074             = Hess1[k](2,1) = Hess1[k](2,2) = 0.0;
1075 #endif
1076 
1077       rval = qm->evaluate_with_Hessian_diagonal( pd, handles[j], qm_val2, indices2, grad2, Hess2, err );
1078       CPPUNIT_ASSERT( !MSQ_CHKERR(err) );
1079       CPPUNIT_ASSERT( rval );
1080 
1081       CPPUNIT_ASSERT_DOUBLES_EQUAL( qm_val1, qm_val2, 1e-6 );
1082       CPPUNIT_ASSERT_EQUAL( indices1.size(), indices2.size() );
1083       CPPUNIT_ASSERT( !indices1.empty() );
1084       CPPUNIT_ASSERT_EQUAL( indices1.size() * (indices1.size()+1) / 2, Hess1.size() );
1085       CPPUNIT_ASSERT_EQUAL( indices2.size(), Hess2.size() );
1086 
1087       size_t h = 0;
1088       std::vector<size_t>::iterator it;
1089       for (unsigned r = 0; r < indices1.size(); ++r) {
1090         it = std::find( indices2.begin(), indices2.end(), indices1[r] );
1091         CPPUNIT_ASSERT( it != indices2.end() );
1092         unsigned r2 = it - indices2.begin();
1093         //if (!utest_mat_equal(Hess1[h],Hess2[r2],0.001))
1094         //  assert(false);
1095         CPPUNIT_ASSERT_MATRICES_EQUAL( Hess1[h], Hess2[r2], 0.05 );
1096         h += indices1.size() - r;
1097       }
1098     }
1099   }
1100 }
1101