Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 #include <iostream> 00002 #include <limits> 00003 #include <cassert> 00004 00005 #include "ElemUtil.hpp" 00006 #include "moab/BoundBox.hpp" 00007 00008 namespace moab 00009 { 00010 namespace ElemUtil 00011 { 00012 00013 bool debug = false; 00014 00015 /**\brief Class representing a 3-D mapping function (e.g. shape function for volume element) */ 00016 class VolMap 00017 { 00018 public: 00019 /**\brief Return \f$\vec \xi\f$ corresponding to logical center of element */ 00020 virtual CartVect center_xi() const = 0; 00021 /**\brief Evaluate mapping function (calculate \f$\vec x = F($\vec \xi)\f$ )*/ 00022 virtual CartVect evaluate( const CartVect& xi ) const = 0; 00023 /**\brief Evaluate Jacobian of mapping function */ 00024 virtual Matrix3 jacobian( const CartVect& xi ) const = 0; 00025 /**\brief Evaluate inverse of mapping function (calculate \f$\vec \xi = F^-1($\vec x)\f$ )*/ 00026 bool solve_inverse( const CartVect& x, CartVect& xi, double tol ) const; 00027 }; 00028 00029 bool VolMap::solve_inverse( const CartVect& x, CartVect& xi, double tol ) const 00030 { 00031 const double error_tol_sqr = tol * tol; 00032 double det; 00033 xi = center_xi(); 00034 CartVect delta = evaluate( xi ) - x; 00035 Matrix3 J; 00036 while( delta % delta > error_tol_sqr ) 00037 { 00038 J = jacobian( xi ); 00039 det = J.determinant(); 00040 if( det < std::numeric_limits< double >::epsilon() ) return false; 00041 xi -= J.inverse() * delta; 00042 delta = evaluate( xi ) - x; 00043 } 00044 return true; 00045 } 00046 00047 /**\brief Shape function for trilinear hexahedron */ 00048 class LinearHexMap : public VolMap 00049 { 00050 public: 00051 LinearHexMap( const CartVect* corner_coords ) : corners( corner_coords ) {} 00052 virtual CartVect center_xi() const; 00053 virtual CartVect evaluate( const CartVect& xi ) const; 00054 virtual double evaluate_scalar_field( const CartVect& xi, const double* f_vals ) const; 00055 virtual Matrix3 jacobian( const CartVect& xi ) const; 00056 00057 private: 00058 const CartVect* corners; 00059 static const double corner_xi[8][3]; 00060 }; 00061 00062 const double LinearHexMap::corner_xi[8][3] = { { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 }, 00063 { -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 } }; 00064 CartVect LinearHexMap::center_xi() const 00065 { 00066 return CartVect( 0.0 ); 00067 } 00068 00069 CartVect LinearHexMap::evaluate( const CartVect& xi ) const 00070 { 00071 CartVect x( 0.0 ); 00072 for( unsigned i = 0; i < 8; ++i ) 00073 { 00074 const double N_i = 00075 ( 1 + xi[0] * corner_xi[i][0] ) * ( 1 + xi[1] * corner_xi[i][1] ) * ( 1 + xi[2] * corner_xi[i][2] ); 00076 x += N_i * corners[i]; 00077 } 00078 x *= 0.125; 00079 return x; 00080 } 00081 00082 double LinearHexMap::evaluate_scalar_field( const CartVect& xi, const double* f_vals ) const 00083 { 00084 double f( 0.0 ); 00085 for( unsigned i = 0; i < 8; ++i ) 00086 { 00087 const double N_i = 00088 ( 1 + xi[0] * corner_xi[i][0] ) * ( 1 + xi[1] * corner_xi[i][1] ) * ( 1 + xi[2] * corner_xi[i][2] ); 00089 f += N_i * f_vals[i]; 00090 } 00091 f *= 0.125; 00092 return f; 00093 } 00094 00095 Matrix3 LinearHexMap::jacobian( const CartVect& xi ) const 00096 { 00097 Matrix3 J( 0.0 ); 00098 for( unsigned i = 0; i < 8; ++i ) 00099 { 00100 const double xi_p = 1 + xi[0] * corner_xi[i][0]; 00101 const double eta_p = 1 + xi[1] * corner_xi[i][1]; 00102 const double zeta_p = 1 + xi[2] * corner_xi[i][2]; 00103 const double dNi_dxi = corner_xi[i][0] * eta_p * zeta_p; 00104 const double dNi_deta = corner_xi[i][1] * xi_p * zeta_p; 00105 const double dNi_dzeta = corner_xi[i][2] * xi_p * eta_p; 00106 J( 0, 0 ) += dNi_dxi * corners[i][0]; 00107 J( 1, 0 ) += dNi_dxi * corners[i][1]; 00108 J( 2, 0 ) += dNi_dxi * corners[i][2]; 00109 J( 0, 1 ) += dNi_deta * corners[i][0]; 00110 J( 1, 1 ) += dNi_deta * corners[i][1]; 00111 J( 2, 1 ) += dNi_deta * corners[i][2]; 00112 J( 0, 2 ) += dNi_dzeta * corners[i][0]; 00113 J( 1, 2 ) += dNi_dzeta * corners[i][1]; 00114 J( 2, 2 ) += dNi_dzeta * corners[i][2]; 00115 } 00116 return J *= 0.125; 00117 } 00118 00119 bool nat_coords_trilinear_hex( const CartVect* corner_coords, const CartVect& x, CartVect& xi, double tol ) 00120 { 00121 return LinearHexMap( corner_coords ).solve_inverse( x, xi, tol ); 00122 } 00123 // 00124 // nat_coords_trilinear_hex2 00125 // Duplicate functionality of nat_coords_trilinear_hex using hex_findpt 00126 // 00127 void nat_coords_trilinear_hex2( const CartVect hex[8], const CartVect& xyz, CartVect& ncoords, double etol ) 00128 00129 { 00130 const int ndim = 3; 00131 const int nverts = 8; 00132 const int vertMap[nverts] = { 0, 1, 3, 2, 4, 5, 7, 6 }; // Map from nat to lex ordering 00133 00134 const int n = 2; // linear 00135 realType coords[ndim * nverts]; // buffer 00136 00137 realType* xm[ndim]; 00138 for( int i = 0; i < ndim; i++ ) 00139 xm[i] = coords + i * nverts; 00140 00141 // stuff hex into coords 00142 for( int i = 0; i < nverts; i++ ) 00143 { 00144 realType vcoord[ndim]; 00145 hex[i].get( vcoord ); 00146 00147 for( int d = 0; d < ndim; d++ ) 00148 coords[d * nverts + vertMap[i]] = vcoord[d]; 00149 } 00150 00151 double dist = 0.0; 00152 ElemUtil::hex_findpt( xm, n, xyz, ncoords, dist ); 00153 if( 3 * MOAB_POLY_EPS < dist ) 00154 { 00155 // outside element, set extremal values to something outside range 00156 for( int j = 0; j < 3; j++ ) 00157 { 00158 if( ncoords[j] < ( -1.0 - etol ) || ncoords[j] > ( 1.0 + etol ) ) ncoords[j] *= 10; 00159 } 00160 } 00161 } 00162 bool point_in_trilinear_hex( const CartVect* hex, const CartVect& xyz, double etol ) 00163 { 00164 CartVect xi; 00165 return nat_coords_trilinear_hex( hex, xyz, xi, etol ) && ( fabs( xi[0] - 1.0 ) < etol ) && 00166 ( fabs( xi[1] - 1.0 ) < etol ) && ( fabs( xi[2] - 1.0 ) < etol ); 00167 } 00168 00169 bool point_in_trilinear_hex( const CartVect* hex, 00170 const CartVect& xyz, 00171 const CartVect& box_min, 00172 const CartVect& box_max, 00173 double etol ) 00174 { 00175 // all values scaled by 2 (eliminates 3 flops) 00176 const CartVect mid = box_max + box_min; 00177 const CartVect dim = box_max - box_min; 00178 const CartVect pt = 2 * xyz - mid; 00179 return ( fabs( pt[0] - dim[0] ) < etol ) && ( fabs( pt[1] - dim[1] ) < etol ) && 00180 ( fabs( pt[2] - dim[2] ) < etol ) && point_in_trilinear_hex( hex, xyz, etol ); 00181 } 00182 00183 // Wrapper to James Lottes' findpt routines 00184 // hex_findpt 00185 // Find the parametric coordinates of an xyz point inside 00186 // a 3d hex spectral element with n nodes per dimension 00187 // xm: coordinates fields, value of x,y,z for each of then n*n*n gauss-lobatto nodes. Nodes are 00188 // in lexicographical order (x is fastest-changing) n: number of nodes per dimension -- n=2 for 00189 // a linear element xyz: input, point to find rst: output: parametric coords of xyz inside the 00190 // element. If xyz is outside the element, rst will be the coords of the closest point dist: 00191 // output: distance between xyz and the point with parametric coords rst 00192 /*extern "C"{ 00193 #include "types.h" 00194 #include "poly.h" 00195 #include "tensor.h" 00196 #include "findpt.h" 00197 #include "extrafindpt.h" 00198 #include "errmem.h" 00199 }*/ 00200 00201 void hex_findpt( realType* xm[3], int n, CartVect xyz, CartVect& rst, double& dist ) 00202 { 00203 00204 // compute stuff that only depends on the order -- could be cached 00205 realType* z[3]; 00206 lagrange_data ld[3]; 00207 opt_data_3 data; 00208 00209 // triplicates 00210 for( int d = 0; d < 3; d++ ) 00211 { 00212 z[d] = tmalloc( realType, n ); 00213 lobatto_nodes( z[d], n ); 00214 lagrange_setup( &ld[d], z[d], n ); 00215 } 00216 00217 opt_alloc_3( &data, ld ); 00218 00219 // find nearest point 00220 realType x_star[3]; 00221 xyz.get( x_star ); 00222 00223 realType r[3] = { 0, 0, 0 }; // initial guess for parametric coords 00224 unsigned c = opt_no_constraints_3; 00225 dist = opt_findpt_3( &data, (const realType**)xm, x_star, r, &c ); 00226 // c tells us if we landed inside the element or exactly on a face, edge, or node 00227 00228 // copy parametric coords back 00229 rst = r; 00230 00231 // Clean-up (move to destructor if we decide to cache) 00232 opt_free_3( &data ); 00233 for( int d = 0; d < 3; ++d ) 00234 lagrange_free( &ld[d] ); 00235 for( int d = 0; d < 3; ++d ) 00236 free( z[d] ); 00237 } 00238 00239 // hex_eval 00240 // Evaluate a field in a 3d hex spectral element with n nodes per dimension, at some given 00241 // parametric coordinates field: field values for each of then n*n*n gauss-lobatto nodes. Nodes 00242 // are in lexicographical order (x is fastest-changing) n: number of nodes per dimension -- n=2 00243 // for a linear element rst: input: parametric coords of the point where we want to evaluate the 00244 // field value: output: value of field at rst 00245 00246 void hex_eval( realType* field, int n, CartVect rstCartVec, double& value ) 00247 { 00248 int d; 00249 realType rst[3]; 00250 rstCartVec.get( rst ); 00251 00252 // can cache stuff below 00253 lagrange_data ld[3]; 00254 realType* z[3]; 00255 for( d = 0; d < 3; ++d ) 00256 { 00257 z[d] = tmalloc( realType, n ); 00258 lobatto_nodes( z[d], n ); 00259 lagrange_setup( &ld[d], z[d], n ); 00260 } 00261 00262 // cut and paste -- see findpt.c 00263 const unsigned nf = n * n, ne = n, nw = 2 * n * n + 3 * n; 00264 realType* od_work = tmalloc( realType, 6 * nf + 9 * ne + nw ); 00265 00266 // piece that we shouldn't want to cache 00267 for( d = 0; d < 3; d++ ) 00268 { 00269 lagrange_0( &ld[d], rst[d] ); 00270 } 00271 00272 value = tensor_i3( ld[0].J, ld[0].n, ld[1].J, ld[1].n, ld[2].J, ld[2].n, field, od_work ); 00273 00274 // all this could be cached 00275 for( d = 0; d < 3; d++ ) 00276 { 00277 free( z[d] ); 00278 lagrange_free( &ld[d] ); 00279 } 00280 free( od_work ); 00281 } 00282 00283 // Gaussian quadrature points for a trilinear hex element. 00284 // Five 2d arrays are defined. 00285 // One for the single gaussian point solution, 2 point solution, 00286 // 3 point solution, 4 point solution and 5 point solution. 00287 // There are 2 columns, one for Weights and the other for Locations 00288 // Weight Location 00289 00290 const double gauss_1[1][2] = { { 2.0, 0.0 } }; 00291 00292 const double gauss_2[2][2] = { { 1.0, -0.5773502691 }, { 1.0, 0.5773502691 } }; 00293 00294 const double gauss_3[3][2] = { { 0.5555555555, -0.7745966692 }, 00295 { 0.8888888888, 0.0 }, 00296 { 0.5555555555, 0.7745966692 } }; 00297 00298 const double gauss_4[4][2] = { { 0.3478548451, -0.8611363116 }, 00299 { 0.6521451549, -0.3399810436 }, 00300 { 0.6521451549, 0.3399810436 }, 00301 { 0.3478548451, 0.8611363116 } }; 00302 00303 const double gauss_5[5][2] = { { 0.2369268851, -0.9061798459 }, 00304 { 0.4786286705, -0.5384693101 }, 00305 { 0.5688888889, 0.0 }, 00306 { 0.4786286705, 0.5384693101 }, 00307 { 0.2369268851, 0.9061798459 } }; 00308 00309 // Function to integrate the field defined by field_fn function 00310 // over the volume of the trilinear hex defined by the hex_corners 00311 00312 bool integrate_trilinear_hex( const CartVect* hex_corners, double* corner_fields, double& field_val, int num_pts ) 00313 { 00314 // Create the LinearHexMap object using the hex_corners array of CartVects 00315 LinearHexMap hex( hex_corners ); 00316 00317 // Use the correct table of points and locations based on the num_pts parameter 00318 const double( *g_pts )[2] = 0; 00319 switch( num_pts ) 00320 { 00321 case 1: 00322 g_pts = gauss_1; 00323 break; 00324 00325 case 2: 00326 g_pts = gauss_2; 00327 break; 00328 00329 case 3: 00330 g_pts = gauss_3; 00331 break; 00332 00333 case 4: 00334 g_pts = gauss_4; 00335 break; 00336 00337 case 5: 00338 g_pts = gauss_5; 00339 break; 00340 00341 default: // value out of range 00342 return false; 00343 } 00344 00345 // Test code - print Gaussian Quadrature data 00346 if( debug ) 00347 { 00348 for( int r = 0; r < num_pts; r++ ) 00349 for( int c = 0; c < 2; c++ ) 00350 std::cout << "g_pts[" << r << "][" << c << "]=" << g_pts[r][c] << std::endl; 00351 } 00352 // End Test code 00353 00354 double soln = 0.0; 00355 00356 for( int i = 0; i < num_pts; i++ ) 00357 { // Loop for xi direction 00358 double w_i = g_pts[i][0]; 00359 double xi_i = g_pts[i][1]; 00360 for( int j = 0; j < num_pts; j++ ) 00361 { // Loop for eta direction 00362 double w_j = g_pts[j][0]; 00363 double eta_j = g_pts[j][1]; 00364 for( int k = 0; k < num_pts; k++ ) 00365 { // Loop for zeta direction 00366 double w_k = g_pts[k][0]; 00367 double zeta_k = g_pts[k][1]; 00368 00369 // Calculate the "realType" space point given the "normal" point 00370 CartVect normal_pt( xi_i, eta_j, zeta_k ); 00371 00372 // Calculate the value of F(x(xi,eta,zeta),y(xi,eta,zeta),z(xi,eta,zeta) 00373 double field = hex.evaluate_scalar_field( normal_pt, corner_fields ); 00374 00375 // Calculate the Jacobian for this "normal" point and its determinant 00376 Matrix3 J = hex.jacobian( normal_pt ); 00377 double det = J.determinant(); 00378 00379 // Calculate integral and update the solution 00380 soln = soln + ( w_i * w_j * w_k * field * det ); 00381 } 00382 } 00383 } 00384 00385 // Set the output parameter 00386 field_val = soln; 00387 00388 return true; 00389 } 00390 00391 } // namespace ElemUtil 00392 00393 namespace Element 00394 { 00395 00396 Map::~Map() {} 00397 00398 inline const std::vector< CartVect >& Map::get_vertices() 00399 { 00400 return this->vertex; 00401 } 00402 // 00403 void Map::set_vertices( const std::vector< CartVect >& v ) 00404 { 00405 if( v.size() != this->vertex.size() ) 00406 { 00407 throw ArgError(); 00408 } 00409 this->vertex = v; 00410 } 00411 00412 bool Map::inside_box( const CartVect& xi, double& tol ) const 00413 { 00414 // bail out early, before doing an expensive NR iteration 00415 // compute box 00416 BoundBox box( this->vertex ); 00417 return box.contains_point( xi.array(), tol ); 00418 } 00419 00420 // 00421 CartVect Map::ievaluate( const CartVect& x, double tol, const CartVect& x0 ) const 00422 { 00423 // TODO: should differentiate between epsilons used for 00424 // Newton Raphson iteration, and epsilons used for curved boundary geometry errors 00425 // right now, fix the tolerance used for NR 00426 tol = 1.0e-10; 00427 const double error_tol_sqr = tol * tol; 00428 double det; 00429 CartVect xi = x0; 00430 CartVect delta = evaluate( xi ) - x; 00431 Matrix3 J; 00432 00433 int iters = 0; 00434 while( delta % delta > error_tol_sqr ) 00435 { 00436 if( ++iters > 10 ) throw Map::EvaluationError( x, vertex ); 00437 00438 J = jacobian( xi ); 00439 det = J.determinant(); 00440 if( det < std::numeric_limits< double >::epsilon() ) throw Map::EvaluationError( x, vertex ); 00441 xi -= J.inverse() * delta; 00442 delta = evaluate( xi ) - x; 00443 } 00444 return xi; 00445 } // Map::ievaluate() 00446 00447 SphericalQuad::SphericalQuad( const std::vector< CartVect >& vertices ) : LinearQuad( vertices ) 00448 { 00449 // project the vertices to the plane tangent at first vertex 00450 v1 = vertex[0]; // member data 00451 double v1v1 = v1 % v1; // this is 1, in general, for unit sphere meshes 00452 for( int j = 1; j < 4; j++ ) 00453 { 00454 // first, bring all vertices in the gnomonic plane 00455 // the new vertex will intersect the plane at vnew 00456 // so that (vnew-v1)%v1 is 0 ( vnew is in the tangent plane, i.e. normal to v1 ) 00457 // pos is the old position of the vertex, and it is in general on the sphere 00458 // vnew = alfa*pos; (alfa*pos-v1)%v1 = 0 <=> alfa*(pos%v1)=v1%v1 <=> alfa = 00459 // v1v1/(pos%v1) 00460 // <=> vnew = ( v1v1/(pos%v1) )*pos 00461 CartVect vnew = v1v1 / ( vertex[j] % v1 ) * vertex[j]; 00462 vertex[j] = vnew; 00463 } 00464 // will compute a transf matrix, such that a new point will be transformed with 00465 // newpos = transf * (vnew-v1), and it will be a point in the 2d plane 00466 // the transformation matrix will be oriented in such a way that orientation will be 00467 // positive 00468 CartVect vx = vertex[1] - v1; // this will become Ox axis 00469 // z axis will be along v1, in such a way that orientation of the quad is positive 00470 // look at the first 2 edges 00471 CartVect vz = vx * ( vertex[2] - vertex[1] ); 00472 vz = vz / vz.length(); 00473 00474 vx = vx / vx.length(); 00475 00476 CartVect vy = vz * vx; 00477 transf = Matrix3( vx[0], vx[1], vx[2], vy[0], vy[1], vy[2], vz[0], vz[1], vz[2] ); 00478 vertex[0] = CartVect( 0. ); 00479 for( int j = 1; j < 4; j++ ) 00480 vertex[j] = transf * ( vertex[j] - v1 ); 00481 } 00482 00483 CartVect SphericalQuad::ievaluate( const CartVect& x, double tol, const CartVect& x0 ) const 00484 { 00485 // project to the plane tangent at first vertex (gnomonic projection) 00486 double v1v1 = v1 % v1; 00487 CartVect vnew = v1v1 / ( x % v1 ) * x; // so that (vnew-v1)%v1 is 0 00488 vnew = transf * ( vnew - v1 ); 00489 // det will be positive now 00490 return Map::ievaluate( vnew, tol, x0 ); 00491 } 00492 00493 bool SphericalQuad::inside_box( const CartVect& pos, double& tol ) const 00494 { 00495 // project to the plane tangent at first vertex 00496 // CartVect v1=vertex[0]; 00497 double v1v1 = v1 % v1; 00498 CartVect vnew = v1v1 / ( pos % v1 ) * pos; // so that (x-v1)%v1 is 0 00499 vnew = transf * ( vnew - v1 ); 00500 return Map::inside_box( vnew, tol ); 00501 } 00502 00503 const double LinearTri::corner[3][3] = { { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 } }; 00504 00505 LinearTri::LinearTri() : Map( 0 ), det_T( 0.0 ), det_T_inverse( 0.0 ) {} // LinearTri::LinearTri() 00506 00507 LinearTri::~LinearTri() {} 00508 00509 void LinearTri::set_vertices( const std::vector< CartVect >& v ) 00510 { 00511 this->Map::set_vertices( v ); 00512 this->T = Matrix3( v[1][0] - v[0][0], v[2][0] - v[0][0], 0, v[1][1] - v[0][1], v[2][1] - v[0][1], 0, 00513 v[1][2] - v[0][2], v[2][2] - v[0][2], 1 ); 00514 this->T_inverse = this->T.inverse(); 00515 this->det_T = this->T.determinant(); 00516 this->det_T_inverse = ( this->det_T < 1e-12 ? std::numeric_limits< double >::max() : 1.0 / this->det_T ); 00517 } // LinearTri::set_vertices() 00518 00519 bool LinearTri::inside_nat_space( const CartVect& xi, double& tol ) const 00520 { 00521 // linear tri space is a triangle with vertices (0,0,0), (1,0,0), (0,1,0) 00522 // first check if outside bigger box, then below the line x+y=1 00523 return ( xi[0] >= -tol ) && ( xi[1] >= -tol ) && ( xi[2] >= -tol ) && ( xi[2] <= tol ) && 00524 ( xi[0] + xi[1] < 1.0 + tol ); 00525 } 00526 CartVect LinearTri::ievaluate( const CartVect& x, double /*tol*/, const CartVect& /*x0*/ ) const 00527 { 00528 return this->T_inverse * ( x - this->vertex[0] ); 00529 } // LinearTri::ievaluate 00530 00531 double LinearTri::evaluate_scalar_field( const CartVect& xi, const double* field_vertex_value ) const 00532 { 00533 double f0 = field_vertex_value[0]; 00534 double f = f0; 00535 for( unsigned i = 1; i < 3; ++i ) 00536 { 00537 f += ( field_vertex_value[i] - f0 ) * xi[i - 1]; 00538 } 00539 return f; 00540 } // LinearTri::evaluate_scalar_field() 00541 00542 double LinearTri::integrate_scalar_field( const double* field_vertex_values ) const 00543 { 00544 double I( 0.0 ); 00545 for( unsigned int i = 0; i < 3; ++i ) 00546 { 00547 I += field_vertex_values[i]; 00548 } 00549 I *= this->det_T / 6.0; // TODO 00550 return I; 00551 } // LinearTri::integrate_scalar_field() 00552 00553 SphericalTri::SphericalTri( const std::vector< CartVect >& vertices ) 00554 { 00555 vertex.resize( vertices.size() ); 00556 vertex = vertices; 00557 // project the vertices to the plane tangent at first vertex 00558 v1 = vertex[0]; // member data 00559 double v1v1 = v1 % v1; // this is 1, in general, for unit sphere meshes 00560 for( int j = 1; j < 3; j++ ) 00561 { 00562 // first, bring all vertices in the gnomonic plane 00563 // the new vertex will intersect the plane at vnew 00564 // so that (vnew-v1)%v1 is 0 ( vnew is in the tangent plane, i.e. normal to v1 ) 00565 // pos is the old position of the vertex, and it is in general on the sphere 00566 // vnew = alfa*pos; (alfa*pos-v1)%v1 = 0 <=> alfa*(pos%v1)=v1%v1 <=> alfa = 00567 // v1v1/(pos%v1) 00568 // <=> vnew = ( v1v1/(pos%v1) )*pos 00569 CartVect vnew = v1v1 / ( vertex[j] % v1 ) * vertex[j]; 00570 vertex[j] = vnew; 00571 } 00572 // will compute a transf matrix, such that a new point will be transformed with 00573 // newpos = transf * (vnew-v1), and it will be a point in the 2d plane 00574 // the transformation matrix will be oriented in such a way that orientation will be 00575 // positive 00576 CartVect vx = vertex[1] - v1; // this will become Ox axis 00577 // z axis will be along v1, in such a way that orientation of the quad is positive 00578 // look at the first 2 edges 00579 CartVect vz = vx * ( vertex[2] - vertex[1] ); 00580 vz = vz / vz.length(); 00581 00582 vx = vx / vx.length(); 00583 00584 CartVect vy = vz * vx; 00585 transf = Matrix3( vx[0], vx[1], vx[2], vy[0], vy[1], vy[2], vz[0], vz[1], vz[2] ); 00586 vertex[0] = CartVect( 0. ); 00587 for( int j = 1; j < 3; j++ ) 00588 vertex[j] = transf * ( vertex[j] - v1 ); 00589 00590 LinearTri::set_vertices( vertex ); 00591 } 00592 00593 CartVect SphericalTri::ievaluate( const CartVect& x, double /*tol*/, const CartVect& /*x0*/ ) const 00594 { 00595 // project to the plane tangent at first vertex (gnomonic projection) 00596 double v1v1 = v1 % v1; 00597 CartVect vnew = v1v1 / ( x % v1 ) * x; // so that (vnew-v1)%v1 is 0 00598 vnew = transf * ( vnew - v1 ); 00599 // det will be positive now 00600 return LinearTri::ievaluate( vnew ); 00601 } 00602 00603 bool SphericalTri::inside_box( const CartVect& pos, double& tol ) const 00604 { 00605 // project to the plane tangent at first vertex 00606 // CartVect v1=vertex[0]; 00607 double v1v1 = v1 % v1; 00608 CartVect vnew = v1v1 / ( pos % v1 ) * pos; // so that (x-v1)%v1 is 0 00609 vnew = transf * ( vnew - v1 ); 00610 return Map::inside_box( vnew, tol ); 00611 } 00612 00613 // filescope for static member data that is cached 00614 const double LinearEdge::corner[2][3] = { { -1, 0, 0 }, { 1, 0, 0 } }; 00615 00616 LinearEdge::LinearEdge() : Map( 0 ) {} // LinearEdge::LinearEdge() 00617 00618 /* For each point, its weight and location are stored as an array. 00619 Hence, the inner dimension is 2, the outer dimension is gauss_count. 00620 We use a one-point Gaussian quadrature, since it integrates linear functions exactly. 00621 */ 00622 const double LinearEdge::gauss[1][2] = { { 2.0, 0.0 } }; 00623 00624 CartVect LinearEdge::evaluate( const CartVect& xi ) const 00625 { 00626 CartVect x( 0.0 ); 00627 for( unsigned i = 0; i < LinearEdge::corner_count; ++i ) 00628 { 00629 const double N_i = ( 1.0 + xi[0] * corner[i][0] ); 00630 x += N_i * this->vertex[i]; 00631 } 00632 x /= LinearEdge::corner_count; 00633 return x; 00634 } // LinearEdge::evaluate 00635 00636 Matrix3 LinearEdge::jacobian( const CartVect& xi ) const 00637 { 00638 Matrix3 J( 0.0 ); 00639 for( unsigned i = 0; i < LinearEdge::corner_count; ++i ) 00640 { 00641 const double xi_p = 1.0 + xi[0] * corner[i][0]; 00642 const double dNi_dxi = corner[i][0] * xi_p; 00643 J( 0, 0 ) += dNi_dxi * vertex[i][0]; 00644 } 00645 J( 1, 1 ) = 1.0; /* to make sure the Jacobian determinant is non-zero */ 00646 J( 2, 2 ) = 1.0; /* to make sure the Jacobian determinant is non-zero */ 00647 J /= LinearEdge::corner_count; 00648 return J; 00649 } // LinearEdge::jacobian() 00650 00651 double LinearEdge::evaluate_scalar_field( const CartVect& xi, const double* field_vertex_value ) const 00652 { 00653 double f( 0.0 ); 00654 for( unsigned i = 0; i < LinearEdge::corner_count; ++i ) 00655 { 00656 const double N_i = ( 1 + xi[0] * corner[i][0] ) * ( 1.0 + xi[1] * corner[i][1] ); 00657 f += N_i * field_vertex_value[i]; 00658 } 00659 f /= LinearEdge::corner_count; 00660 return f; 00661 } // LinearEdge::evaluate_scalar_field() 00662 00663 double LinearEdge::integrate_scalar_field( const double* field_vertex_values ) const 00664 { 00665 double I( 0.0 ); 00666 for( unsigned int j1 = 0; j1 < this->gauss_count; ++j1 ) 00667 { 00668 double x1 = this->gauss[j1][1]; 00669 double w1 = this->gauss[j1][0]; 00670 CartVect x( x1, 0.0, 0.0 ); 00671 I += this->evaluate_scalar_field( x, field_vertex_values ) * w1 * this->det_jacobian( x ); 00672 } 00673 return I; 00674 } // LinearEdge::integrate_scalar_field() 00675 00676 bool LinearEdge::inside_nat_space( const CartVect& xi, double& tol ) const 00677 { 00678 // just look at the box+tol here 00679 return ( xi[0] >= -1. - tol ) && ( xi[0] <= 1. + tol ); 00680 } 00681 00682 const double LinearHex::corner[8][3] = { { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 }, 00683 { -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 } }; 00684 00685 LinearHex::LinearHex() : Map( 0 ) {} // LinearHex::LinearHex() 00686 00687 LinearHex::~LinearHex() {} 00688 /* For each point, its weight and location are stored as an array. 00689 Hence, the inner dimension is 2, the outer dimension is gauss_count. 00690 We use a one-point Gaussian quadrature, since it integrates linear functions exactly. 00691 */ 00692 // const double LinearHex::gauss[1][2] = { { 2.0, 0.0 } }; 00693 const double LinearHex::gauss[2][2] = { { 1.0, -0.5773502691 }, { 1.0, 0.5773502691 } }; 00694 // const double LinearHex::gauss[4][2] = { { 0.3478548451, -0.8611363116 }, 00695 // { 0.6521451549, -0.3399810436 }, 00696 // { 0.6521451549, 0.3399810436 }, 00697 // { 0.3478548451, 0.8611363116 } }; 00698 00699 CartVect LinearHex::evaluate( const CartVect& xi ) const 00700 { 00701 CartVect x( 0.0 ); 00702 for( unsigned i = 0; i < 8; ++i ) 00703 { 00704 const double N_i = 00705 ( 1 + xi[0] * corner[i][0] ) * ( 1 + xi[1] * corner[i][1] ) * ( 1 + xi[2] * corner[i][2] ); 00706 x += N_i * this->vertex[i]; 00707 } 00708 x *= 0.125; 00709 return x; 00710 } // LinearHex::evaluate 00711 00712 Matrix3 LinearHex::jacobian( const CartVect& xi ) const 00713 { 00714 Matrix3 J( 0.0 ); 00715 for( unsigned i = 0; i < 8; ++i ) 00716 { 00717 const double xi_p = 1 + xi[0] * corner[i][0]; 00718 const double eta_p = 1 + xi[1] * corner[i][1]; 00719 const double zeta_p = 1 + xi[2] * corner[i][2]; 00720 const double dNi_dxi = corner[i][0] * eta_p * zeta_p; 00721 const double dNi_deta = corner[i][1] * xi_p * zeta_p; 00722 const double dNi_dzeta = corner[i][2] * xi_p * eta_p; 00723 J( 0, 0 ) += dNi_dxi * vertex[i][0]; 00724 J( 1, 0 ) += dNi_dxi * vertex[i][1]; 00725 J( 2, 0 ) += dNi_dxi * vertex[i][2]; 00726 J( 0, 1 ) += dNi_deta * vertex[i][0]; 00727 J( 1, 1 ) += dNi_deta * vertex[i][1]; 00728 J( 2, 1 ) += dNi_deta * vertex[i][2]; 00729 J( 0, 2 ) += dNi_dzeta * vertex[i][0]; 00730 J( 1, 2 ) += dNi_dzeta * vertex[i][1]; 00731 J( 2, 2 ) += dNi_dzeta * vertex[i][2]; 00732 } 00733 return J *= 0.125; 00734 } // LinearHex::jacobian() 00735 00736 double LinearHex::evaluate_scalar_field( const CartVect& xi, const double* field_vertex_value ) const 00737 { 00738 double f( 0.0 ); 00739 for( unsigned i = 0; i < 8; ++i ) 00740 { 00741 const double N_i = 00742 ( 1 + xi[0] * corner[i][0] ) * ( 1 + xi[1] * corner[i][1] ) * ( 1 + xi[2] * corner[i][2] ); 00743 f += N_i * field_vertex_value[i]; 00744 } 00745 f *= 0.125; 00746 return f; 00747 } // LinearHex::evaluate_scalar_field() 00748 00749 double LinearHex::integrate_scalar_field( const double* field_vertex_values ) const 00750 { 00751 double I( 0.0 ); 00752 for( unsigned int j1 = 0; j1 < this->gauss_count; ++j1 ) 00753 { 00754 double x1 = this->gauss[j1][1]; 00755 double w1 = this->gauss[j1][0]; 00756 for( unsigned int j2 = 0; j2 < this->gauss_count; ++j2 ) 00757 { 00758 double x2 = this->gauss[j2][1]; 00759 double w2 = this->gauss[j2][0]; 00760 for( unsigned int j3 = 0; j3 < this->gauss_count; ++j3 ) 00761 { 00762 double x3 = this->gauss[j3][1]; 00763 double w3 = this->gauss[j3][0]; 00764 CartVect x( x1, x2, x3 ); 00765 I += this->evaluate_scalar_field( x, field_vertex_values ) * w1 * w2 * w3 * this->det_jacobian( x ); 00766 } 00767 } 00768 } 00769 return I; 00770 } // LinearHex::integrate_scalar_field() 00771 00772 bool LinearHex::inside_nat_space( const CartVect& xi, double& tol ) const 00773 { 00774 // just look at the box+tol here 00775 return ( xi[0] >= -1. - tol ) && ( xi[0] <= 1. + tol ) && ( xi[1] >= -1. - tol ) && ( xi[1] <= 1. + tol ) && 00776 ( xi[2] >= -1. - tol ) && ( xi[2] <= 1. + tol ); 00777 } 00778 00779 // those are not just the corners, but for simplicity, keep this name 00780 // 00781 const int QuadraticHex::corner[27][3] = { 00782 { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, // corner nodes: 0-7 00783 { -1, 1, -1 }, // mid-edge nodes: 8-19 00784 { -1, -1, 1 }, // center-face nodes 20-25 center node 26 00785 { 1, -1, 1 }, // 00786 { 1, 1, 1 }, { -1, 1, 1 }, // 4 ----- 19 ----- 7 00787 { 0, -1, -1 }, // . | . | 00788 { 1, 0, -1 }, // 16 25 18 | 00789 { 0, 1, -1 }, // . | . | 00790 { -1, 0, -1 }, // 5 ----- 17 ----- 6 | 00791 { -1, -1, 0 }, // | 12 | 23 15 00792 { 1, -1, 0 }, // | | | 00793 { 1, 1, 0 }, // | 20 | 26 | 22 | 00794 { -1, 1, 0 }, // | | | 00795 { 0, -1, 1 }, // 13 21 | 14 | 00796 { 1, 0, 1 }, // | 0 ----- 11 ----- 3 00797 { 0, 1, 1 }, // | . | . 00798 { -1, 0, 1 }, // | 8 24 | 10 00799 { 0, -1, 0 }, // | . | . 00800 { 1, 0, 0 }, // 1 ----- 9 ----- 2 00801 { 0, 1, 0 }, // 00802 { -1, 0, 0 }, { 0, 0, -1 }, { 0, 0, 1 }, { 0, 0, 0 } }; 00803 // QuadraticHex::QuadraticHex(const std::vector<CartVect>& vertices) : Map(vertices){}; 00804 QuadraticHex::QuadraticHex() : Map( 0 ) {} 00805 00806 QuadraticHex::~QuadraticHex() {} 00807 double SH( const int i, const double xi ) 00808 { 00809 switch( i ) 00810 { 00811 case -1: 00812 return ( xi * xi - xi ) / 2; 00813 case 0: 00814 return 1 - xi * xi; 00815 case 1: 00816 return ( xi * xi + xi ) / 2; 00817 default: 00818 return 0.; 00819 } 00820 } 00821 double DSH( const int i, const double xi ) 00822 { 00823 switch( i ) 00824 { 00825 case -1: 00826 return xi - 0.5; 00827 case 0: 00828 return -2 * xi; 00829 case 1: 00830 return xi + 0.5; 00831 default: 00832 return 0.; 00833 } 00834 } 00835 00836 CartVect QuadraticHex::evaluate( const CartVect& xi ) const 00837 { 00838 00839 CartVect x( 0.0 ); 00840 for( int i = 0; i < 27; i++ ) 00841 { 00842 const double sh = SH( corner[i][0], xi[0] ) * SH( corner[i][1], xi[1] ) * SH( corner[i][2], xi[2] ); 00843 x += sh * vertex[i]; 00844 } 00845 00846 return x; 00847 } 00848 00849 bool QuadraticHex::inside_nat_space( const CartVect& xi, double& tol ) const 00850 { // just look at the box+tol here 00851 return ( xi[0] >= -1. - tol ) && ( xi[0] <= 1. + tol ) && ( xi[1] >= -1. - tol ) && ( xi[1] <= 1. + tol ) && 00852 ( xi[2] >= -1. - tol ) && ( xi[2] <= 1. + tol ); 00853 } 00854 00855 Matrix3 QuadraticHex::jacobian( const CartVect& xi ) const 00856 { 00857 Matrix3 J( 0.0 ); 00858 for( int i = 0; i < 27; i++ ) 00859 { 00860 const double sh[3] = { SH( corner[i][0], xi[0] ), SH( corner[i][1], xi[1] ), SH( corner[i][2], xi[2] ) }; 00861 const double dsh[3] = { DSH( corner[i][0], xi[0] ), DSH( corner[i][1], xi[1] ), 00862 DSH( corner[i][2], xi[2] ) }; 00863 00864 for( int j = 0; j < 3; j++ ) 00865 { 00866 J( j, 0 ) += dsh[0] * sh[1] * sh[2] * vertex[i][j]; // dxj/dr first column 00867 J( j, 1 ) += sh[0] * dsh[1] * sh[2] * vertex[i][j]; // dxj/ds 00868 J( j, 2 ) += sh[0] * sh[1] * dsh[2] * vertex[i][j]; // dxj/dt 00869 } 00870 } 00871 00872 return J; 00873 } 00874 double QuadraticHex::evaluate_scalar_field( const CartVect& xi, const double* field_vertex_values ) const 00875 { 00876 double x = 0.0; 00877 for( int i = 0; i < 27; i++ ) 00878 { 00879 const double sh = SH( corner[i][0], xi[0] ) * SH( corner[i][1], xi[1] ) * SH( corner[i][2], xi[2] ); 00880 x += sh * field_vertex_values[i]; 00881 } 00882 00883 return x; 00884 } 00885 double QuadraticHex::integrate_scalar_field( const double* /*field_vertex_values*/ ) const 00886 { 00887 return 0.; // TODO: gaussian integration , probably 2x2x2 00888 } 00889 00890 const double LinearTet::corner[4][3] = { { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }; 00891 00892 LinearTet::LinearTet() : Map( 0 ), det_T( 0.0 ), det_T_inverse( 0.0 ) {} // LinearTet::LinearTet() 00893 00894 LinearTet::~LinearTet() {} 00895 00896 void LinearTet::set_vertices( const std::vector< CartVect >& v ) 00897 { 00898 this->Map::set_vertices( v ); 00899 this->T = 00900 Matrix3( v[1][0] - v[0][0], v[2][0] - v[0][0], v[3][0] - v[0][0], v[1][1] - v[0][1], v[2][1] - v[0][1], 00901 v[3][1] - v[0][1], v[1][2] - v[0][2], v[2][2] - v[0][2], v[3][2] - v[0][2] ); 00902 this->T_inverse = this->T.inverse(); 00903 this->det_T = this->T.determinant(); 00904 this->det_T_inverse = ( this->det_T < 1e-12 ? std::numeric_limits< double >::max() : 1.0 / this->det_T ); 00905 } // LinearTet::set_vertices() 00906 00907 double LinearTet::evaluate_scalar_field( const CartVect& xi, const double* field_vertex_value ) const 00908 { 00909 double f0 = field_vertex_value[0]; 00910 double f = f0; 00911 for( unsigned i = 1; i < 4; ++i ) 00912 { 00913 f += ( field_vertex_value[i] - f0 ) * xi[i - 1]; 00914 } 00915 return f; 00916 } // LinearTet::evaluate_scalar_field() 00917 00918 CartVect LinearTet::ievaluate( const CartVect& x, double /*tol*/, const CartVect& /*x0*/ ) const 00919 { 00920 return this->T_inverse * ( x - this->vertex[0] ); 00921 } // LinearTet::ievaluate 00922 00923 double LinearTet::integrate_scalar_field( const double* field_vertex_values ) const 00924 { 00925 double I( 0.0 ); 00926 for( unsigned int i = 0; i < 4; ++i ) 00927 { 00928 I += field_vertex_values[i]; 00929 } 00930 I *= this->det_T / 24.0; 00931 return I; 00932 } // LinearTet::integrate_scalar_field() 00933 bool LinearTet::inside_nat_space( const CartVect& xi, double& tol ) const 00934 { 00935 // linear tet space is a tetra with vertices (0,0,0), (1,0,0), (0,1,0), (0, 0, 1) 00936 // first check if outside bigger box, then below the plane x+y+z=1 00937 return ( xi[0] >= -tol ) && ( xi[1] >= -tol ) && ( xi[2] >= -tol ) && ( xi[0] + xi[1] + xi[2] < 1.0 + tol ); 00938 } 00939 // SpectralHex 00940 00941 // filescope for static member data that is cached 00942 int SpectralHex::_n; 00943 realType* SpectralHex::_z[3]; 00944 lagrange_data SpectralHex::_ld[3]; 00945 opt_data_3 SpectralHex::_data; 00946 realType* SpectralHex::_odwork; 00947 00948 bool SpectralHex::_init = false; 00949 00950 SpectralHex::SpectralHex() : Map( 0 ) 00951 { 00952 _xyz[0] = _xyz[1] = _xyz[2] = NULL; 00953 } 00954 // the preferred constructor takes pointers to GL blocked positions 00955 SpectralHex::SpectralHex( int order, double* x, double* y, double* z ) : Map( 0 ) 00956 { 00957 Init( order ); 00958 _xyz[0] = x; 00959 _xyz[1] = y; 00960 _xyz[2] = z; 00961 } 00962 SpectralHex::SpectralHex( int order ) : Map( 0 ) 00963 { 00964 Init( order ); 00965 _xyz[0] = _xyz[1] = _xyz[2] = NULL; 00966 } 00967 SpectralHex::~SpectralHex() 00968 { 00969 if( _init ) freedata(); 00970 _init = false; 00971 } 00972 void SpectralHex::Init( int order ) 00973 { 00974 if( _init && _n == order ) return; 00975 if( _init && _n != order ) 00976 { 00977 // TODO: free data cached 00978 freedata(); 00979 } 00980 // compute stuff that depends only on order 00981 _init = true; 00982 _n = order; 00983 // triplicates! n is the same in all directions !!! 00984 for( int d = 0; d < 3; d++ ) 00985 { 00986 _z[d] = tmalloc( realType, _n ); 00987 lobatto_nodes( _z[d], _n ); 00988 lagrange_setup( &_ld[d], _z[d], _n ); 00989 } 00990 opt_alloc_3( &_data, _ld ); 00991 00992 unsigned int nf = _n * _n, ne = _n, nw = 2 * _n * _n + 3 * _n; 00993 _odwork = tmalloc( realType, 6 * nf + 9 * ne + nw ); 00994 } 00995 void SpectralHex::freedata() 00996 { 00997 for( int d = 0; d < 3; d++ ) 00998 { 00999 free( _z[d] ); 01000 lagrange_free( &_ld[d] ); 01001 } 01002 opt_free_3( &_data ); 01003 free( _odwork ); 01004 } 01005 01006 void SpectralHex::set_gl_points( double* x, double* y, double* z ) 01007 { 01008 _xyz[0] = x; 01009 _xyz[1] = y; 01010 _xyz[2] = z; 01011 } 01012 CartVect SpectralHex::evaluate( const CartVect& xi ) const 01013 { 01014 // piece that we shouldn't want to cache 01015 int d = 0; 01016 for( d = 0; d < 3; d++ ) 01017 { 01018 lagrange_0( &_ld[d], xi[d] ); 01019 } 01020 CartVect result; 01021 for( d = 0; d < 3; d++ ) 01022 { 01023 result[d] = tensor_i3( _ld[0].J, _ld[0].n, _ld[1].J, _ld[1].n, _ld[2].J, _ld[2].n, 01024 _xyz[d], // this is the "field" 01025 _odwork ); 01026 } 01027 return result; 01028 } 01029 // replicate the functionality of hex_findpt 01030 CartVect SpectralHex::ievaluate( CartVect const& xyz, double tol, const CartVect& x0 ) const 01031 { 01032 // find nearest point 01033 realType x_star[3]; 01034 xyz.get( x_star ); 01035 01036 realType r[3] = { 0, 0, 0 }; // initial guess for parametric coords 01037 x0.get( r ); 01038 unsigned c = opt_no_constraints_3; 01039 realType dist = opt_findpt_3( &_data, (const realType**)_xyz, x_star, r, &c ); 01040 // if it did not converge, get out with throw... 01041 if( dist > 10 * tol ) // outside the element 01042 { 01043 std::vector< CartVect > dummy; 01044 throw Map::EvaluationError( xyz, dummy ); 01045 } 01046 // c tells us if we landed inside the element or exactly on a face, edge, or node 01047 // also, dist shows the distance to the computed point. 01048 // copy parametric coords back 01049 return CartVect( r ); 01050 } 01051 Matrix3 SpectralHex::jacobian( const CartVect& xi ) const 01052 { 01053 realType x_i[3]; 01054 xi.get( x_i ); 01055 // set the positions of GL nodes, before evaluations 01056 _data.elx[0] = _xyz[0]; 01057 _data.elx[1] = _xyz[1]; 01058 _data.elx[2] = _xyz[2]; 01059 opt_vol_set_intp_3( &_data, x_i ); 01060 Matrix3 J( 0. ); 01061 // it is organized differently 01062 J( 0, 0 ) = _data.jac[0]; // dx/dr 01063 J( 0, 1 ) = _data.jac[1]; // dx/ds 01064 J( 0, 2 ) = _data.jac[2]; // dx/dt 01065 J( 1, 0 ) = _data.jac[3]; // dy/dr 01066 J( 1, 1 ) = _data.jac[4]; // dy/ds 01067 J( 1, 2 ) = _data.jac[5]; // dy/dt 01068 J( 2, 0 ) = _data.jac[6]; // dz/dr 01069 J( 2, 1 ) = _data.jac[7]; // dz/ds 01070 J( 2, 2 ) = _data.jac[8]; // dz/dt 01071 return J; 01072 } 01073 double SpectralHex::evaluate_scalar_field( const CartVect& xi, const double* field ) const 01074 { 01075 // piece that we shouldn't want to cache 01076 int d; 01077 for( d = 0; d < 3; d++ ) 01078 { 01079 lagrange_0( &_ld[d], xi[d] ); 01080 } 01081 01082 double value = tensor_i3( _ld[0].J, _ld[0].n, _ld[1].J, _ld[1].n, _ld[2].J, _ld[2].n, field, _odwork ); 01083 return value; 01084 } 01085 double SpectralHex::integrate_scalar_field( const double* field_vertex_values ) const 01086 { 01087 // set the position of GL points 01088 // set the positions of GL nodes, before evaluations 01089 _data.elx[0] = _xyz[0]; 01090 _data.elx[1] = _xyz[1]; 01091 _data.elx[2] = _xyz[2]; 01092 double xi[3]; 01093 // triple loop; the most inner loop is in r direction, then s, then t 01094 double integral = 0.; 01095 // double volume = 0; 01096 int index = 0; // used fr the inner loop 01097 for( int k = 0; k < _n; k++ ) 01098 { 01099 xi[2] = _ld[2].z[k]; 01100 // double wk= _ld[2].w[k]; 01101 for( int j = 0; j < _n; j++ ) 01102 { 01103 xi[1] = _ld[1].z[j]; 01104 // double wj= _ld[1].w[j]; 01105 for( int i = 0; i < _n; i++ ) 01106 { 01107 xi[0] = _ld[0].z[i]; 01108 // double wi= _ld[0].w[i]; 01109 opt_vol_set_intp_3( &_data, xi ); 01110 double wk = _ld[2].J[k]; 01111 double wj = _ld[1].J[j]; 01112 double wi = _ld[0].J[i]; 01113 Matrix3 J( 0. ); 01114 // it is organized differently 01115 J( 0, 0 ) = _data.jac[0]; // dx/dr 01116 J( 0, 1 ) = _data.jac[1]; // dx/ds 01117 J( 0, 2 ) = _data.jac[2]; // dx/dt 01118 J( 1, 0 ) = _data.jac[3]; // dy/dr 01119 J( 1, 1 ) = _data.jac[4]; // dy/ds 01120 J( 1, 2 ) = _data.jac[5]; // dy/dt 01121 J( 2, 0 ) = _data.jac[6]; // dz/dr 01122 J( 2, 1 ) = _data.jac[7]; // dz/ds 01123 J( 2, 2 ) = _data.jac[8]; // dz/dt 01124 double bm = wk * wj * wi * J.determinant(); 01125 integral += bm * field_vertex_values[index++]; 01126 // volume +=bm; 01127 } 01128 } 01129 } 01130 // std::cout << "volume: " << volume << "\n"; 01131 return integral; 01132 } 01133 // this is the same as a linear hex, although we should not need it 01134 bool SpectralHex::inside_nat_space( const CartVect& xi, double& tol ) const 01135 { 01136 // just look at the box+tol here 01137 return ( xi[0] >= -1. - tol ) && ( xi[0] <= 1. + tol ) && ( xi[1] >= -1. - tol ) && ( xi[1] <= 1. + tol ) && 01138 ( xi[2] >= -1. - tol ) && ( xi[2] <= 1. + tol ); 01139 } 01140 01141 // SpectralHex 01142 01143 // filescope for static member data that is cached 01144 const double LinearQuad::corner[4][3] = { { -1, -1, 0 }, { 1, -1, 0 }, { 1, 1, 0 }, { -1, 1, 0 } }; 01145 01146 LinearQuad::LinearQuad() : Map( 0 ) {} // LinearQuad::LinearQuad() 01147 01148 LinearQuad::~LinearQuad() {} 01149 01150 /* For each point, its weight and location are stored as an array. 01151 Hence, the inner dimension is 2, the outer dimension is gauss_count. 01152 We use a one-point Gaussian quadrature, since it integrates linear functions exactly. 01153 */ 01154 const double LinearQuad::gauss[1][2] = { { 2.0, 0.0 } }; 01155 01156 CartVect LinearQuad::evaluate( const CartVect& xi ) const 01157 { 01158 CartVect x( 0.0 ); 01159 for( unsigned i = 0; i < LinearQuad::corner_count; ++i ) 01160 { 01161 const double N_i = ( 1 + xi[0] * corner[i][0] ) * ( 1 + xi[1] * corner[i][1] ); 01162 x += N_i * this->vertex[i]; 01163 } 01164 x /= LinearQuad::corner_count; 01165 return x; 01166 } // LinearQuad::evaluate 01167 01168 Matrix3 LinearQuad::jacobian( const CartVect& xi ) const 01169 { 01170 // this basically ignores the z component: xi[2] or vertex[][2] 01171 Matrix3 J( 0.0 ); 01172 for( unsigned i = 0; i < LinearQuad::corner_count; ++i ) 01173 { 01174 const double xi_p = 1 + xi[0] * corner[i][0]; 01175 const double eta_p = 1 + xi[1] * corner[i][1]; 01176 const double dNi_dxi = corner[i][0] * eta_p; 01177 const double dNi_deta = corner[i][1] * xi_p; 01178 J( 0, 0 ) += dNi_dxi * vertex[i][0]; 01179 J( 1, 0 ) += dNi_dxi * vertex[i][1]; 01180 J( 0, 1 ) += dNi_deta * vertex[i][0]; 01181 J( 1, 1 ) += dNi_deta * vertex[i][1]; 01182 } 01183 J( 2, 2 ) = 1.0; /* to make sure the Jacobian determinant is non-zero */ 01184 J /= LinearQuad::corner_count; 01185 return J; 01186 } // LinearQuad::jacobian() 01187 01188 double LinearQuad::evaluate_scalar_field( const CartVect& xi, const double* field_vertex_value ) const 01189 { 01190 double f( 0.0 ); 01191 for( unsigned i = 0; i < LinearQuad::corner_count; ++i ) 01192 { 01193 const double N_i = ( 1 + xi[0] * corner[i][0] ) * ( 1 + xi[1] * corner[i][1] ); 01194 f += N_i * field_vertex_value[i]; 01195 } 01196 f /= LinearQuad::corner_count; 01197 return f; 01198 } // LinearQuad::evaluate_scalar_field() 01199 01200 double LinearQuad::integrate_scalar_field( const double* field_vertex_values ) const 01201 { 01202 double I( 0.0 ); 01203 for( unsigned int j1 = 0; j1 < this->gauss_count; ++j1 ) 01204 { 01205 double x1 = this->gauss[j1][1]; 01206 double w1 = this->gauss[j1][0]; 01207 for( unsigned int j2 = 0; j2 < this->gauss_count; ++j2 ) 01208 { 01209 double x2 = this->gauss[j2][1]; 01210 double w2 = this->gauss[j2][0]; 01211 CartVect x( x1, x2, 0.0 ); 01212 I += this->evaluate_scalar_field( x, field_vertex_values ) * w1 * w2 * this->det_jacobian( x ); 01213 } 01214 } 01215 return I; 01216 } // LinearQuad::integrate_scalar_field() 01217 01218 bool LinearQuad::inside_nat_space( const CartVect& xi, double& tol ) const 01219 { 01220 // just look at the box+tol here 01221 return ( xi[0] >= -1. - tol ) && ( xi[0] <= 1. + tol ) && ( xi[1] >= -1. - tol ) && ( xi[1] <= 1. + tol ); 01222 } 01223 01224 // filescope for static member data that is cached 01225 int SpectralQuad::_n; 01226 realType* SpectralQuad::_z[2]; 01227 lagrange_data SpectralQuad::_ld[2]; 01228 opt_data_2 SpectralQuad::_data; 01229 realType* SpectralQuad::_odwork; 01230 realType* SpectralQuad::_glpoints; 01231 bool SpectralQuad::_init = false; 01232 01233 SpectralQuad::SpectralQuad() : Map( 0 ) 01234 { 01235 _xyz[0] = _xyz[1] = _xyz[2] = NULL; 01236 } 01237 // the preferred constructor takes pointers to GL blocked positions 01238 SpectralQuad::SpectralQuad( int order, double* x, double* y, double* z ) : Map( 0 ) 01239 { 01240 Init( order ); 01241 _xyz[0] = x; 01242 _xyz[1] = y; 01243 _xyz[2] = z; 01244 } 01245 SpectralQuad::SpectralQuad( int order ) : Map( 4 ) 01246 { 01247 Init( order ); 01248 _xyz[0] = _xyz[1] = _xyz[2] = NULL; 01249 } 01250 SpectralQuad::~SpectralQuad() 01251 { 01252 if( _init ) freedata(); 01253 _init = false; 01254 } 01255 void SpectralQuad::Init( int order ) 01256 { 01257 if( _init && _n == order ) return; 01258 if( _init && _n != order ) 01259 { 01260 // TODO: free data cached 01261 freedata(); 01262 } 01263 // compute stuff that depends only on order 01264 _init = true; 01265 _n = order; 01266 // duplicates! n is the same in all directions !!! 01267 for( int d = 0; d < 2; d++ ) 01268 { 01269 _z[d] = tmalloc( realType, _n ); 01270 lobatto_nodes( _z[d], _n ); 01271 lagrange_setup( &_ld[d], _z[d], _n ); 01272 } 01273 opt_alloc_2( &_data, _ld ); 01274 01275 unsigned int nf = _n * _n, ne = _n, nw = 2 * _n * _n + 3 * _n; 01276 _odwork = tmalloc( realType, 6 * nf + 9 * ne + nw ); 01277 _glpoints = tmalloc( realType, 3 * nf ); 01278 } 01279 01280 void SpectralQuad::freedata() 01281 { 01282 for( int d = 0; d < 2; d++ ) 01283 { 01284 free( _z[d] ); 01285 lagrange_free( &_ld[d] ); 01286 } 01287 opt_free_2( &_data ); 01288 free( _odwork ); 01289 free( _glpoints ); 01290 } 01291 01292 void SpectralQuad::set_gl_points( double* x, double* y, double* z ) 01293 { 01294 _xyz[0] = x; 01295 _xyz[1] = y; 01296 _xyz[2] = z; 01297 } 01298 CartVect SpectralQuad::evaluate( const CartVect& xi ) const 01299 { 01300 // piece that we shouldn't want to cache 01301 int d = 0; 01302 for( d = 0; d < 2; d++ ) 01303 { 01304 lagrange_0( &_ld[d], xi[d] ); 01305 } 01306 CartVect result; 01307 for( d = 0; d < 3; d++ ) 01308 { 01309 result[d] = tensor_i2( _ld[0].J, _ld[0].n, _ld[1].J, _ld[1].n, _xyz[d], _odwork ); 01310 } 01311 return result; 01312 } 01313 // replicate the functionality of hex_findpt 01314 CartVect SpectralQuad::ievaluate( CartVect const& xyz, double /*tol*/, const CartVect& /*x0*/ ) const 01315 { 01316 // find nearest point 01317 realType x_star[3]; 01318 xyz.get( x_star ); 01319 01320 realType r[2] = { 0, 0 }; // initial guess for parametric coords 01321 unsigned c = opt_no_constraints_3; 01322 realType dist = opt_findpt_2( &_data, (const realType**)_xyz, x_star, r, &c ); 01323 // if it did not converge, get out with throw... 01324 if( dist > 0.9e+30 ) 01325 { 01326 std::vector< CartVect > dummy; 01327 throw Map::EvaluationError( xyz, dummy ); 01328 } 01329 01330 // c tells us if we landed inside the element or exactly on a face, edge, or node 01331 // also, dist shows the distance to the computed point. 01332 // copy parametric coords back 01333 return CartVect( r[0], r[1], 0. ); 01334 } 01335 01336 Matrix3 SpectralQuad::jacobian( const CartVect& /*xi*/ ) const 01337 { 01338 // not implemented 01339 Matrix3 J( 0. ); 01340 return J; 01341 } 01342 01343 double SpectralQuad::evaluate_scalar_field( const CartVect& xi, const double* field ) const 01344 { 01345 // piece that we shouldn't want to cache 01346 int d; 01347 for( d = 0; d < 2; d++ ) 01348 { 01349 lagrange_0( &_ld[d], xi[d] ); 01350 } 01351 01352 double value = tensor_i2( _ld[0].J, _ld[0].n, _ld[1].J, _ld[1].n, field, _odwork ); 01353 return value; 01354 } 01355 double SpectralQuad::integrate_scalar_field( const double* /*field_vertex_values*/ ) const 01356 { 01357 return 0.; // not implemented 01358 } 01359 // this is the same as a linear hex, although we should not need it 01360 bool SpectralQuad::inside_nat_space( const CartVect& xi, double& tol ) const 01361 { 01362 // just look at the box+tol here 01363 return ( xi[0] >= -1. - tol ) && ( xi[0] <= 1. + tol ) && ( xi[1] >= -1. - tol ) && ( xi[1] <= 1. + tol ); 01364 } 01365 // something we don't do for spectral hex; we do it here because 01366 // we do not store the position of gl points in a tag yet 01367 void SpectralQuad::compute_gl_positions() 01368 { 01369 // will need to use shape functions on a simple linear quad to compute gl points 01370 // so we know the position of gl points in parametric space, we will just compute those 01371 // from the 3d vertex position (corner nodes of the quad), using simple mapping 01372 assert( this->vertex.size() == 4 ); 01373 static double corner_xi[4][2] = { { -1., -1. }, { 1., -1. }, { 1., 1. }, { -1., 1. } }; 01374 // we will use the cached lobatto nodes in parametric space _z[d] (the same in both 01375 // directions) 01376 int indexGL = 0; 01377 int n2 = _n * _n; 01378 for( int i = 0; i < _n; i++ ) 01379 { 01380 double eta = _z[0][i]; 01381 for( int j = 0; j < _n; j++ ) 01382 { 01383 double csi = _z[1][j]; // we could really use the same _z[0] array of lobatto nodes 01384 CartVect pos( 0.0 ); 01385 for( int k = 0; k < 4; k++ ) 01386 { 01387 const double N_k = ( 1 + csi * corner_xi[k][0] ) * ( 1 + eta * corner_xi[k][1] ); 01388 pos += N_k * vertex[k]; 01389 } 01390 pos *= 0.25; // these are x, y, z of gl points; reorder them 01391 _glpoints[indexGL] = pos[0]; // x 01392 _glpoints[indexGL + n2] = pos[1]; // y 01393 _glpoints[indexGL + 2 * n2] = pos[2]; // z 01394 indexGL++; 01395 } 01396 } 01397 // now, we can set the _xyz pointers to internal memory allocated to these! 01398 _xyz[0] = &( _glpoints[0] ); 01399 _xyz[1] = &( _glpoints[n2] ); 01400 _xyz[2] = &( _glpoints[2 * n2] ); 01401 } 01402 void SpectralQuad::get_gl_points( double*& x, double*& y, double*& z, int& psize ) 01403 { 01404 x = (double*)_xyz[0]; 01405 y = (double*)_xyz[1]; 01406 z = (double*)_xyz[2]; 01407 psize = _n * _n; 01408 } 01409 } // namespace Element 01410 01411 } // namespace moab