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 (2010) kraftche@cae.wisc.edu
24
25 ***************************************************************** */
26
27
28 /** \file HexLagrangeShapeTest.cpp
29 * \brief
30 * \author Jason Kraftcheck
31 */
32
33 #include "Mesquite.hpp"
34 #include "HexLagrangeShape.hpp"
35 #include "TopologyInfo.hpp"
36 #include "MsqError.hpp"
37 #include "IdealElements.hpp"
38 #include "JacobianCalculator.hpp"
39
40 #include "UnitUtil.hpp"
41
42 #include <vector>
43 #include <algorithm>
44 #include <iostream>
45 #include <sstream>
46
47 using namespace MBMesquite;
48 using namespace std;
49 const double epsilon = 1e-6;
50 #define ASSERT_VALUES_EQUAL( v1, v2, location ) \
51 ASSERT_MESSAGE( value_message( (location), (v1), (v2) ), test_value((v1), (v2)) )
52
test_value(double v1,double v2)53 static bool test_value( double v1, double v2 )
54 { return fabs(v1 - v2) < epsilon; }
55
value_message(unsigned location,double v1,double v2)56 static inline CppUnit::Message value_message( unsigned location, double v1, double v2 )
57 {
58 CppUnit::Message m( "equality assertion failed" );
59
60 std::ostringstream buffer1;
61 buffer1 << "Expected : " << v1;
62 m.addDetail( buffer1.str() );
63
64 std::ostringstream buffer2;
65 buffer2 << "Actual : " << v2;
66 m.addDetail( buffer2.str() );
67
68 std::ostringstream buffer3;
69 buffer3 << "Location : ";
70 if (location < 9)
71 buffer3 << "Corner " << location;
72 else if (location < 20)
73 buffer3 << "Edge " << location-8;
74 else if (location < 26)
75 buffer3 << "Face " << location-20;
76 else if (location == 26)
77 buffer3 << "Mid-element";
78 else
79 buffer3 << "INVALID!!";
80 m.addDetail( buffer3.str() );
81 return m;
82 }
83
test_value(MsqVector<3> v1,MsqVector<3> v2)84 static bool test_value( MsqVector<3> v1, MsqVector<3> v2 )
85 { return length(v1 - v2) < epsilon; }
86
value_message(unsigned location,MsqVector<3> v1,MsqVector<3> v2)87 static inline CppUnit::Message value_message( unsigned location,
88 MsqVector<3> v1,
89 MsqVector<3> v2 )
90 {
91 CppUnit::Message m( "equality assertion failed" );
92
93 std::ostringstream buffer1;
94 buffer1 << "Expected : [" << v1[0] << ", " << v1[1] << ", " << v1[2] << "]";
95 m.addDetail( buffer1.str() );
96
97 std::ostringstream buffer2;
98 buffer2 << "Actual : [" << v2[0] << ", " << v2[1] << ", " << v2[2] << "]";
99 m.addDetail( buffer2.str() );
100
101 std::ostringstream buffer3;
102 buffer3 << "Location : ";
103 if (location < 9)
104 buffer3 << "Corner " << location;
105 else if (location < 20)
106 buffer3 << "Edge " << location-8;
107 else if (location < 26)
108 buffer3 << "Face " << location-20;
109 else if (location == 26)
110 buffer3 << "Mid-element";
111 else
112 buffer3 << "INVALID!!";
113 m.addDetail( buffer3.str() );
114 return m;
115 }
116
117 class HexLagrangeShapeTest : public CppUnit::TestFixture
118 {
119 private:
120 CPPUNIT_TEST_SUITE(HexLagrangeShapeTest);
121
122 CPPUNIT_TEST(test_corners_coeff);
123 CPPUNIT_TEST(test_edges_coeff);
124 CPPUNIT_TEST(test_faces_coeff);
125 CPPUNIT_TEST(test_mid_coeff);
126
127 CPPUNIT_TEST(test_corners_derivs);
128 CPPUNIT_TEST(test_edges_derivs);
129 CPPUNIT_TEST(test_faces_derivs);
130 CPPUNIT_TEST(test_mid_derivs);
131
132 CPPUNIT_TEST(test_ideal_jacobian);
133
134 CPPUNIT_TEST_SUITE_END();
135
136 HexLagrangeShape sf;
137
138 public:
139
140 void test_corner_coeff( int corner );
141 void test_edge_coeff( int edge );
142 void test_face_coeff( int face );
143 void test_mid_coeff();
144
145 void test_corner_derivs( int corner );
146 void test_edge_derivs( int edge );
147 void test_face_derivs( int face );
148 void test_mid_derivs();
149
150 NodeSet allNodes;
setUp()151 void setUp() { allNodes.set_all_nodes( HEXAHEDRON ); }
152
153 void test_corners_coeff();
154 void test_edges_coeff();
155 void test_faces_coeff();
156
157 void test_corners_derivs();
158 void test_edges_derivs();
159 void test_faces_derivs();
160
161 void test_ideal_jacobian();
162 };
163
164
165
166 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(HexLagrangeShapeTest, "HexLagrangeShapeTest");
167 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(HexLagrangeShapeTest, "Unit");
168
169 // Indices
170 enum { XI = 0, ETA = 1, ZETA = 2 };
171 // Parameter range
172 const double min_xi = 0;
173 const double max_xi = 1;
174 const double mid_xi = 0.5*(min_xi + max_xi);
175
176 // Some lagrange polynomial terms
l21(double xi)177 static double l21( double xi ) { return (2 * xi - 1) * (xi - 1); }
l22(double xi)178 static double l22( double xi ) { return 4 * xi * (1 - xi); }
l23(double xi)179 static double l23( double xi ) { return xi * (2 * xi - 1); }
180 // First derivatives of lagrange polynomial terms
dl21(double xi)181 static double dl21( double xi ) { return 4 * xi - 3; }
dl22(double xi)182 static double dl22( double xi ) { return 4 - 8 * xi; }
dl23(double xi)183 static double dl23( double xi ) { return 4 * xi - 1; }
184 // Indexible polynomail terms
185 typedef double (*f_t)(double);
186 f_t l[4] = { 0, &l21, &l22, &l23 };
187 f_t dl[4] = { 0, &dl21, &dl22, &dl23 };
188 const int C[][3] = {
189 { 1, 1, 1 }, // 0
190 { 3, 1, 1 }, // 1
191 { 3, 3, 1 }, // 2
192 { 1, 3, 1 }, // 3
193
194 { 1, 1, 3 }, // 4
195 { 3, 1, 3 }, // 5
196 { 3, 3, 3 }, // 6
197 { 1, 3, 3 }, // 7
198
199 { 2, 1, 1 }, // 8
200 { 3, 2, 1 }, // 9
201 { 2, 3, 1 }, // 10
202 { 1, 2, 1 }, // 11
203
204 { 1, 1, 2 }, // 12
205 { 3, 1, 2 }, // 13
206 { 3, 3, 2 }, // 14
207 { 1, 3, 2 }, // 15
208
209 { 2, 1, 3 }, // 16
210 { 3, 2, 3 }, // 17
211 { 2, 3, 3 }, // 18
212 { 1, 2, 3 }, // 19
213
214 { 2, 1, 2 }, // 20
215 { 3, 2, 2 }, // 21
216 { 2, 3, 2 }, // 22
217 { 1, 2, 2 }, // 23
218
219 { 2, 2, 1 }, // 24
220 { 2, 2, 3 }, // 25
221
222 { 2, 2, 2 } // 26
223 };
224
225 const double corners[8][3] = {
226 { min_xi, min_xi, min_xi },
227 { max_xi, min_xi, min_xi },
228 { max_xi, max_xi, min_xi },
229 { min_xi, max_xi, min_xi },
230 { min_xi, min_xi, max_xi },
231 { max_xi, min_xi, max_xi },
232 { max_xi, max_xi, max_xi },
233 { min_xi, max_xi, max_xi } };
234
XI_corner(int i)235 static MsqVector<3> XI_corner( int i )
236 { return MsqVector<3>(corners[i]); }
237
XI_edge(int i)238 static MsqVector<3> XI_edge( int i )
239 {
240 const unsigned* idx = TopologyInfo::edge_vertices( HEXAHEDRON, i );
241 return 0.5 * (XI_corner(idx[0]) + XI_corner(idx[1]));
242 }
243
XI_face(int i)244 static MsqVector<3> XI_face( int i )
245 {
246 unsigned n;
247 const unsigned* idx = TopologyInfo::face_vertices( HEXAHEDRON, i, n );
248 MsqVector<3> result = XI_corner(idx[0]);
249 for (unsigned j = 1; j < n; ++j)
250 result += XI_corner(idx[j]);
251 result *= 1.0/n;
252 return result;
253 }
254
XI_elem()255 static MsqVector<3> XI_elem()
256 {
257 const double vals[] = { mid_xi, mid_xi, mid_xi };
258 return MsqVector<3>(vals);
259 }
260
N(int i,MsqVector<3> xi)261 static double N( int i, MsqVector<3> xi )
262 {
263 const int* c = C[i];
264 return l[c[XI]](xi[XI]) * l[c[ETA]](xi[ETA]) * l[c[ZETA]](xi[ZETA]);
265 }
266
dN(int i,MsqVector<3> xi)267 static MsqVector<3> dN( int i, MsqVector<3> xi )
268 {
269 MsqVector<3> result;
270 const int* c = C[i];
271 result[XI ] = dl[c[XI]](xi[XI]) * l[c[ETA]](xi[ETA]) * l[c[ZETA]](xi[ZETA]);
272 result[ETA ] = l[c[XI]](xi[XI]) * dl[c[ETA]](xi[ETA]) * l[c[ZETA]](xi[ZETA]);
273 result[ZETA] = l[c[XI]](xi[XI]) * l[c[ETA]](xi[ETA]) * dl[c[ZETA]](xi[ZETA]);
274 return result;
275 }
276
get_coeffs(MsqVector<3> xi,double coeffs_out[27])277 static void get_coeffs( MsqVector<3> xi, double coeffs_out[27] )
278 {
279 for (int i = 0; i < 27; ++i)
280 coeffs_out[i] = N(i, xi);
281 }
282
get_derivs(MsqVector<3> xi,MsqVector<3> derivs_out[27])283 static void get_derivs( MsqVector<3> xi, MsqVector<3> derivs_out[27] )
284 {
285 for (int i = 0; i < 27; ++i)
286 derivs_out[i] = dN(i, xi);
287 }
288
289
290
check_valid_indices(const size_t * vertices,size_t num_vtx)291 static void check_valid_indices( const size_t* vertices, size_t num_vtx )
292 {
293 // check valid size of list (at least fout, at most all nodes)
294 CPPUNIT_ASSERT( num_vtx <= 27 );
295 CPPUNIT_ASSERT( num_vtx >= 4 );
296 // make sure vertex indices are valid (in [0,26])
297 size_t vertcopy[27];
298 std::copy( vertices, vertices+num_vtx, vertcopy );
299 std::sort( vertcopy, vertcopy+num_vtx );
300 CPPUNIT_ASSERT( vertcopy[num_vtx-1] <= 26 ); // max value less than 27
301 // make sure there are no duplicates in the list
302 const size_t* iter = std::unique( vertcopy, vertcopy+num_vtx );
303 CPPUNIT_ASSERT( iter == vertcopy+num_vtx );
304 }
305
check_no_zeros(const MsqVector<3> * derivs,size_t num_vtx)306 static void check_no_zeros( const MsqVector<3>* derivs, size_t num_vtx )
307 {
308 for (unsigned i = 0; i < num_vtx; ++i) {
309 CPPUNIT_ASSERT( length(derivs[i]) > 1e-6 );
310 }
311 }
312
compare_coefficients(const double * coeffs,const size_t * indices,size_t num_coeff,const double * expected_coeffs,unsigned loc)313 static void compare_coefficients( const double* coeffs,
314 const size_t* indices,
315 size_t num_coeff,
316 const double* expected_coeffs,
317 unsigned loc )
318 {
319 // find the location in the returned list for each node
320 size_t revidx[27];
321 double test_vals[27];
322 for (size_t i = 0; i < 27; ++i) {
323 revidx[i] = std::find( indices, indices+num_coeff, i ) - indices;
324 test_vals[i] = (revidx[i] == num_coeff) ? 0.0 : coeffs[revidx[i]];
325 }
326
327 // compare expected and actual coefficient values
328 ASSERT_VALUES_EQUAL( expected_coeffs[0], test_vals[0], loc );
329 ASSERT_VALUES_EQUAL( expected_coeffs[1], test_vals[1], loc );
330 ASSERT_VALUES_EQUAL( expected_coeffs[2], test_vals[2], loc );
331 ASSERT_VALUES_EQUAL( expected_coeffs[3], test_vals[3], loc );
332 ASSERT_VALUES_EQUAL( expected_coeffs[4], test_vals[4], loc );
333 ASSERT_VALUES_EQUAL( expected_coeffs[5], test_vals[5], loc );
334 ASSERT_VALUES_EQUAL( expected_coeffs[6], test_vals[6], loc );
335 ASSERT_VALUES_EQUAL( expected_coeffs[7], test_vals[7], loc );
336 ASSERT_VALUES_EQUAL( expected_coeffs[8], test_vals[8], loc );
337 ASSERT_VALUES_EQUAL( expected_coeffs[9], test_vals[9], loc );
338 ASSERT_VALUES_EQUAL( expected_coeffs[10], test_vals[10], loc );
339 ASSERT_VALUES_EQUAL( expected_coeffs[11], test_vals[11], loc );
340 ASSERT_VALUES_EQUAL( expected_coeffs[12], test_vals[12], loc );
341 ASSERT_VALUES_EQUAL( expected_coeffs[13], test_vals[13], loc );
342 ASSERT_VALUES_EQUAL( expected_coeffs[14], test_vals[14], loc );
343 ASSERT_VALUES_EQUAL( expected_coeffs[15], test_vals[15], loc );
344 ASSERT_VALUES_EQUAL( expected_coeffs[16], test_vals[16], loc );
345 ASSERT_VALUES_EQUAL( expected_coeffs[17], test_vals[17], loc );
346 ASSERT_VALUES_EQUAL( expected_coeffs[18], test_vals[18], loc );
347 ASSERT_VALUES_EQUAL( expected_coeffs[19], test_vals[19], loc );
348 ASSERT_VALUES_EQUAL( expected_coeffs[20], test_vals[20], loc );
349 ASSERT_VALUES_EQUAL( expected_coeffs[21], test_vals[21], loc );
350 ASSERT_VALUES_EQUAL( expected_coeffs[22], test_vals[22], loc );
351 ASSERT_VALUES_EQUAL( expected_coeffs[23], test_vals[23], loc );
352 ASSERT_VALUES_EQUAL( expected_coeffs[24], test_vals[24], loc );
353 ASSERT_VALUES_EQUAL( expected_coeffs[25], test_vals[25], loc );
354 ASSERT_VALUES_EQUAL( expected_coeffs[26], test_vals[26], loc );
355 }
356
compare_derivatives(const size_t * vertices,size_t num_vtx,const MsqVector<3> * actual,const MsqVector<3> * expected,unsigned loc)357 static void compare_derivatives( const size_t* vertices,
358 size_t num_vtx,
359 const MsqVector<3>* actual,
360 const MsqVector<3>* expected,
361 unsigned loc )
362 {
363 check_valid_indices( vertices, num_vtx );
364 check_no_zeros( actual, num_vtx );
365
366 // Input has values in dxi & deta & dzeta only for nodes in 'vertices'
367 // Convert to values for every possible node, with zero's for
368 // nodes that are not present.
369 CPPUNIT_ASSERT(num_vtx <= 9);
370 MsqVector<3> expanded[27];
371 std::fill( expanded, expanded+27,MsqVector<3>(0.0) );
372 for (unsigned i = 0; i < num_vtx; ++i) {
373 CPPUNIT_ASSERT(vertices[i] <= 26);
374 expanded[vertices[i]] = actual[i];
375 }
376
377 ASSERT_VALUES_EQUAL( expected[0], expanded[0], loc );
378 ASSERT_VALUES_EQUAL( expected[1], expanded[1], loc );
379 ASSERT_VALUES_EQUAL( expected[2], expanded[2], loc );
380 ASSERT_VALUES_EQUAL( expected[3], expanded[3], loc );
381 ASSERT_VALUES_EQUAL( expected[4], expanded[4], loc );
382 ASSERT_VALUES_EQUAL( expected[5], expanded[5], loc );
383 ASSERT_VALUES_EQUAL( expected[6], expanded[6], loc );
384 ASSERT_VALUES_EQUAL( expected[7], expanded[7], loc );
385 ASSERT_VALUES_EQUAL( expected[8], expanded[8], loc );
386 ASSERT_VALUES_EQUAL( expected[9], expanded[9], loc );
387 ASSERT_VALUES_EQUAL( expected[10], expanded[10], loc );
388 ASSERT_VALUES_EQUAL( expected[11], expanded[11], loc );
389 ASSERT_VALUES_EQUAL( expected[12], expanded[12], loc );
390 ASSERT_VALUES_EQUAL( expected[13], expanded[13], loc );
391 ASSERT_VALUES_EQUAL( expected[14], expanded[14], loc );
392 ASSERT_VALUES_EQUAL( expected[15], expanded[15], loc );
393 ASSERT_VALUES_EQUAL( expected[16], expanded[16], loc );
394 ASSERT_VALUES_EQUAL( expected[17], expanded[17], loc );
395 ASSERT_VALUES_EQUAL( expected[18], expanded[18], loc );
396 ASSERT_VALUES_EQUAL( expected[19], expanded[19], loc );
397 ASSERT_VALUES_EQUAL( expected[20], expanded[20], loc );
398 ASSERT_VALUES_EQUAL( expected[21], expanded[21], loc );
399 ASSERT_VALUES_EQUAL( expected[22], expanded[22], loc );
400 ASSERT_VALUES_EQUAL( expected[23], expanded[23], loc );
401 ASSERT_VALUES_EQUAL( expected[24], expanded[24], loc );
402 ASSERT_VALUES_EQUAL( expected[25], expanded[25], loc );
403 ASSERT_VALUES_EQUAL( expected[26], expanded[26], loc );
404 }
405
test_corner_coeff(int corner)406 void HexLagrangeShapeTest::test_corner_coeff( int corner )
407 {
408 MsqPrintError err(std::cout);
409
410 double expected[27];
411 get_coeffs( XI_corner(corner), expected );
412
413 double coeff[100];
414 size_t num_coeff = 11, indices[100];
415 sf.coefficients( Sample(0, corner), allNodes, coeff, indices, num_coeff, err );
416 CPPUNIT_ASSERT( !err );
417
418 compare_coefficients( coeff, indices, num_coeff, expected, corner );
419 }
420
test_edge_coeff(int edge)421 void HexLagrangeShapeTest::test_edge_coeff( int edge )
422 {
423 MsqPrintError err(std::cout);
424
425 double expected[27];
426 get_coeffs( XI_edge(edge), expected );
427
428 double coeff[100];
429 size_t num_coeff = 11, indices[100];
430 sf.coefficients( Sample(1, edge), allNodes, coeff, indices, num_coeff, err );
431 CPPUNIT_ASSERT( !err );
432
433 compare_coefficients( coeff, indices, num_coeff, expected, edge+8 );
434 }
435
test_face_coeff(int face)436 void HexLagrangeShapeTest::test_face_coeff( int face )
437 {
438 MsqPrintError err(std::cout);
439
440 double expected[27];
441 get_coeffs( XI_face(face), expected );
442
443 double coeff[100];
444 size_t num_coeff = 11, indices[100];
445 sf.coefficients( Sample(2, face), allNodes, coeff, indices, num_coeff, err );
446 CPPUNIT_ASSERT( !err );
447
448 compare_coefficients( coeff, indices, num_coeff, expected, face+20 );
449 }
450
test_mid_coeff()451 void HexLagrangeShapeTest::test_mid_coeff()
452 {
453 MsqPrintError err(std::cout);
454
455 double expected[27];
456 get_coeffs( XI_elem(), expected );
457
458 double coeff[100];
459 size_t num_coeff = 11, indices[100];
460 sf.coefficients( Sample(3, 0), allNodes, coeff, indices, num_coeff, err );
461 CPPUNIT_ASSERT( !err );
462
463 compare_coefficients( coeff, indices, num_coeff, expected, 26 );
464 }
465
test_corner_derivs(int corner)466 void HexLagrangeShapeTest::test_corner_derivs( int corner )
467 {
468 MsqPrintError err(std::cout);
469
470 MsqVector<3> expected[27];
471 get_derivs( XI_corner(corner), expected );
472
473 size_t vertices[100], num_vtx = 23;
474 MsqVector<3> derivs[100];
475 sf.derivatives( Sample(0, corner), allNodes, vertices, derivs, num_vtx, err );
476 CPPUNIT_ASSERT( !err );
477
478 compare_derivatives( vertices, num_vtx, derivs, expected, corner );
479 }
480
test_edge_derivs(int edge)481 void HexLagrangeShapeTest::test_edge_derivs( int edge )
482 {
483 MsqPrintError err(std::cout);
484
485 MsqVector<3> expected[27];
486 get_derivs( XI_edge(edge), expected );
487
488 size_t vertices[100], num_vtx = 23;
489 MsqVector<3> derivs[100];
490 sf.derivatives( Sample(1, edge), allNodes, vertices, derivs, num_vtx, err );
491 CPPUNIT_ASSERT( !err );
492
493 compare_derivatives( vertices, num_vtx, derivs, expected, edge+8 );
494 }
495
test_face_derivs(int face)496 void HexLagrangeShapeTest::test_face_derivs( int face )
497 {
498 MsqPrintError err(std::cout);
499
500 MsqVector<3> expected[27];
501 get_derivs( XI_face(face), expected );
502
503 size_t vertices[100], num_vtx = 23;
504 MsqVector<3> derivs[100];
505 sf.derivatives( Sample(2, face), allNodes, vertices, derivs, num_vtx, err );
506 CPPUNIT_ASSERT( !err );
507
508 compare_derivatives( vertices, num_vtx, derivs, expected, face+20 );
509 }
510
test_mid_derivs()511 void HexLagrangeShapeTest::test_mid_derivs( )
512 {
513 MsqPrintError err(std::cout);
514
515 MsqVector<3> expected[27];
516 get_derivs( XI_elem(), expected );
517
518 size_t vertices[100], num_vtx = 23;
519 MsqVector<3> derivs[100];
520 sf.derivatives( Sample(3, 0), allNodes, vertices, derivs, num_vtx, err );
521 CPPUNIT_ASSERT( !err );
522
523 compare_derivatives( vertices, num_vtx, derivs, expected, 26 );
524 }
525
test_corners_coeff()526 void HexLagrangeShapeTest::test_corners_coeff()
527 {
528 for (unsigned i = 0; i < 8; ++i)
529 test_corner_coeff( i );
530 }
531
test_edges_coeff()532 void HexLagrangeShapeTest::test_edges_coeff()
533 {
534 for (unsigned i = 0; i < 12; ++i)
535 test_edge_coeff( i );
536 }
537
test_faces_coeff()538 void HexLagrangeShapeTest::test_faces_coeff()
539 {
540 for (unsigned i = 0; i < 6; ++i)
541 test_face_coeff( i );
542 }
543
test_corners_derivs()544 void HexLagrangeShapeTest::test_corners_derivs()
545 {
546 for (unsigned i = 0; i < 8; ++i)
547 test_corner_derivs( i );
548 }
549
test_edges_derivs()550 void HexLagrangeShapeTest::test_edges_derivs()
551 {
552 for (unsigned i = 0; i < 12; ++i)
553 test_edge_derivs( i );
554 }
555
test_faces_derivs()556 void HexLagrangeShapeTest::test_faces_derivs()
557 {
558 for (unsigned i = 0; i < 6; ++i)
559 test_face_derivs( i );
560 }
561
test_ideal_jacobian()562 void HexLagrangeShapeTest::test_ideal_jacobian()
563 {
564 MsqError err;
565 MsqMatrix<3,3> J_act, J_exp(1.0);
566 sf.ideal( Sample(3,0), J_act, err );
567 ASSERT_NO_ERROR(err);
568
569 // Matrices should be a rotation of each other.
570 // First, calculate tentative rotation matrix
571 MsqMatrix<3,3> R = inverse(J_exp) * J_act;
572 // next check that it is a rotation
573 CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.0, det(R), 1e-6 ); // no scaling
574 ASSERT_MATRICES_EQUAL( transpose(R), inverse(R), 1e-6 ); // orthogonal
575 }
576