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( ¢er_qm, ¢erOnly ); }
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( ¢er_qm, ¢erOnly ); }
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( ¢er_qm, ¢erOnly ); }
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