$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 // C++ includes 00021 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 00022 #include <cmath> // for std::sqrt, std::abs 00023 00024 00025 // Local includes 00026 #include "libmesh/fe.h" 00027 #include "libmesh/elem.h" 00028 #include "libmesh/libmesh_logging.h" 00029 #include "libmesh/fe_macro.h" 00030 #include "libmesh/fe_map.h" 00031 #include "libmesh/fe_xyz_map.h" 00032 #include "libmesh/mesh_subdivision_support.h" 00033 00034 namespace libMesh 00035 { 00036 00037 // Constructor (empty) 00038 FEMap::FEMap() {} 00039 00040 00041 00042 UniquePtr<FEMap> FEMap::build( FEType fe_type ) 00043 { 00044 switch( fe_type.family ) 00045 { 00046 case XYZ: 00047 return UniquePtr<FEMap>(new FEXYZMap); 00048 00049 default: 00050 return UniquePtr<FEMap>(new FEMap); 00051 } 00052 00053 libmesh_error_msg("We'll never get here!"); 00054 return UniquePtr<FEMap>(); 00055 } 00056 00057 00058 00059 template<unsigned int Dim> 00060 void FEMap::init_reference_to_physical_map( const std::vector<Point>& qp, 00061 const Elem* elem) 00062 { 00063 // Start logging the reference->physical map initialization 00064 START_LOG("init_reference_to_physical_map()", "FEMap"); 00065 00066 // The number of quadrature points. 00067 const std::size_t n_qp = qp.size(); 00068 00069 // The element type and order to use in 00070 // the map 00071 const Order mapping_order (elem->default_order()); 00072 const ElemType mapping_elem_type (elem->type()); 00073 00074 // Number of shape functions used to construt the map 00075 // (Lagrange shape functions are used for mapping) 00076 const unsigned int n_mapping_shape_functions = 00077 FE<Dim,LAGRANGE>::n_shape_functions (mapping_elem_type, 00078 mapping_order); 00079 00080 this->phi_map.resize (n_mapping_shape_functions); 00081 if (Dim > 0) 00082 { 00083 this->dphidxi_map.resize (n_mapping_shape_functions); 00084 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00085 this->d2phidxi2_map.resize (n_mapping_shape_functions); 00086 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00087 } 00088 00089 if (Dim > 1) 00090 { 00091 this->dphideta_map.resize (n_mapping_shape_functions); 00092 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00093 this->d2phidxideta_map.resize (n_mapping_shape_functions); 00094 this->d2phideta2_map.resize (n_mapping_shape_functions); 00095 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00096 } 00097 00098 if (Dim > 2) 00099 { 00100 this->dphidzeta_map.resize (n_mapping_shape_functions); 00101 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00102 this->d2phidxidzeta_map.resize (n_mapping_shape_functions); 00103 this->d2phidetadzeta_map.resize (n_mapping_shape_functions); 00104 this->d2phidzeta2_map.resize (n_mapping_shape_functions); 00105 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00106 } 00107 00108 00109 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00110 { 00111 this->phi_map[i].resize (n_qp); 00112 if (Dim > 0) 00113 { 00114 this->dphidxi_map[i].resize (n_qp); 00115 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00116 this->d2phidxi2_map[i].resize (n_qp); 00117 if (Dim > 1) 00118 { 00119 this->d2phidxideta_map[i].resize (n_qp); 00120 this->d2phideta2_map[i].resize (n_qp); 00121 } 00122 if (Dim > 2) 00123 { 00124 this->d2phidxidzeta_map[i].resize (n_qp); 00125 this->d2phidetadzeta_map[i].resize (n_qp); 00126 this->d2phidzeta2_map[i].resize (n_qp); 00127 } 00128 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00129 00130 if (Dim > 1) 00131 this->dphideta_map[i].resize (n_qp); 00132 00133 if (Dim > 2) 00134 this->dphidzeta_map[i].resize (n_qp); 00135 } 00136 } 00137 00138 // Optimize for the *linear* geometric elements case: 00139 bool is_linear = elem->is_linear(); 00140 00141 switch (Dim) 00142 { 00143 00144 //------------------------------------------------------------ 00145 // 0D 00146 case 0: 00147 { 00148 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00149 for (std::size_t p=0; p<n_qp; p++) 00150 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00151 00152 break; 00153 } 00154 00155 //------------------------------------------------------------ 00156 // 1D 00157 case 1: 00158 { 00159 // Compute the value of the mapping shape function i at quadrature point p 00160 // (Lagrange shape functions are used for mapping) 00161 if (is_linear) 00162 { 00163 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00164 { 00165 this->phi_map[i][0] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[0]); 00166 this->dphidxi_map[i][0] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 0, qp[0]); 00167 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00168 this->d2phidxi2_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 0, qp[0]); 00169 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00170 for (std::size_t p=1; p<n_qp; p++) 00171 { 00172 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00173 this->dphidxi_map[i][p] = this->dphidxi_map[i][0]; 00174 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00175 this->d2phidxi2_map[i][p] = this->d2phidxi2_map[i][0]; 00176 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00177 } 00178 } 00179 } 00180 else 00181 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00182 for (std::size_t p=0; p<n_qp; p++) 00183 { 00184 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00185 this->dphidxi_map[i][p] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 0, qp[p]); 00186 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00187 this->d2phidxi2_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 0, qp[p]); 00188 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00189 } 00190 00191 break; 00192 } 00193 //------------------------------------------------------------ 00194 // 2D 00195 case 2: 00196 { 00197 // Compute the value of the mapping shape function i at quadrature point p 00198 // (Lagrange shape functions are used for mapping) 00199 if (is_linear) 00200 { 00201 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00202 { 00203 this->phi_map[i][0] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[0]); 00204 this->dphidxi_map[i][0] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 0, qp[0]); 00205 this->dphideta_map[i][0] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 1, qp[0]); 00206 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00207 this->d2phidxi2_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 0, qp[0]); 00208 this->d2phidxideta_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 1, qp[0]); 00209 this->d2phideta2_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 2, qp[0]); 00210 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00211 for (std::size_t p=1; p<n_qp; p++) 00212 { 00213 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00214 this->dphidxi_map[i][p] = this->dphidxi_map[i][0]; 00215 this->dphideta_map[i][p] = this->dphideta_map[i][0]; 00216 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00217 this->d2phidxi2_map[i][p] = this->d2phidxi2_map[i][0]; 00218 this->d2phidxideta_map[i][p] = this->d2phidxideta_map[i][0]; 00219 this->d2phideta2_map[i][p] = this->d2phideta2_map[i][0]; 00220 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00221 } 00222 } 00223 } 00224 else 00225 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00226 for (std::size_t p=0; p<n_qp; p++) 00227 { 00228 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00229 this->dphidxi_map[i][p] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 0, qp[p]); 00230 this->dphideta_map[i][p] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 1, qp[p]); 00231 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00232 this->d2phidxi2_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 0, qp[p]); 00233 this->d2phidxideta_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 1, qp[p]); 00234 this->d2phideta2_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 2, qp[p]); 00235 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00236 } 00237 00238 break; 00239 } 00240 00241 //------------------------------------------------------------ 00242 // 3D 00243 case 3: 00244 { 00245 // Compute the value of the mapping shape function i at quadrature point p 00246 // (Lagrange shape functions are used for mapping) 00247 if (is_linear) 00248 { 00249 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00250 { 00251 this->phi_map[i][0] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[0]); 00252 this->dphidxi_map[i][0] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 0, qp[0]); 00253 this->dphideta_map[i][0] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 1, qp[0]); 00254 this->dphidzeta_map[i][0] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 2, qp[0]); 00255 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00256 this->d2phidxi2_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 0, qp[0]); 00257 this->d2phidxideta_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 1, qp[0]); 00258 this->d2phideta2_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 2, qp[0]); 00259 this->d2phidxidzeta_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 3, qp[0]); 00260 this->d2phidetadzeta_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 4, qp[0]); 00261 this->d2phidzeta2_map[i][0] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 5, qp[0]); 00262 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00263 for (std::size_t p=1; p<n_qp; p++) 00264 { 00265 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00266 this->dphidxi_map[i][p] = this->dphidxi_map[i][0]; 00267 this->dphideta_map[i][p] = this->dphideta_map[i][0]; 00268 this->dphidzeta_map[i][p] = this->dphidzeta_map[i][0]; 00269 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00270 this->d2phidxi2_map[i][p] = this->d2phidxi2_map[i][0]; 00271 this->d2phidxideta_map[i][p] = this->d2phidxideta_map[i][0]; 00272 this->d2phideta2_map[i][p] = this->d2phideta2_map[i][0]; 00273 this->d2phidxidzeta_map[i][p] = this->d2phidxidzeta_map[i][0]; 00274 this->d2phidetadzeta_map[i][p] = this->d2phidetadzeta_map[i][0]; 00275 this->d2phidzeta2_map[i][p] = this->d2phidzeta2_map[i][0]; 00276 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00277 } 00278 } 00279 } 00280 else 00281 for (unsigned int i=0; i<n_mapping_shape_functions; i++) 00282 for (std::size_t p=0; p<n_qp; p++) 00283 { 00284 this->phi_map[i][p] = FE<Dim,LAGRANGE>::shape (mapping_elem_type, mapping_order, i, qp[p]); 00285 this->dphidxi_map[i][p] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 0, qp[p]); 00286 this->dphideta_map[i][p] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 1, qp[p]); 00287 this->dphidzeta_map[i][p] = FE<Dim,LAGRANGE>::shape_deriv (mapping_elem_type, mapping_order, i, 2, qp[p]); 00288 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00289 this->d2phidxi2_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 0, qp[p]); 00290 this->d2phidxideta_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 1, qp[p]); 00291 this->d2phideta2_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 2, qp[p]); 00292 this->d2phidxidzeta_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 3, qp[p]); 00293 this->d2phidetadzeta_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 4, qp[p]); 00294 this->d2phidzeta2_map[i][p] = FE<Dim,LAGRANGE>::shape_second_deriv (mapping_elem_type, mapping_order, i, 5, qp[p]); 00295 #endif // ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00296 } 00297 00298 break; 00299 } 00300 00301 default: 00302 libmesh_error_msg("Invalid Dim = " << Dim); 00303 } 00304 00305 // Stop logging the reference->physical map initialization 00306 STOP_LOG("init_reference_to_physical_map()", "FEMap"); 00307 return; 00308 } 00309 00310 00311 00312 void FEMap::compute_single_point_map(const unsigned int dim, 00313 const std::vector<Real>& qw, 00314 const Elem* elem, 00315 unsigned int p, 00316 const std::vector<Node*>& elem_nodes) 00317 { 00318 libmesh_assert(elem); 00319 libmesh_assert_equal_to(phi_map.size(), elem_nodes.size()); 00320 00321 switch (dim) 00322 { 00323 //-------------------------------------------------------------------- 00324 // 0D 00325 case 0: 00326 { 00327 libmesh_assert(elem_nodes[0]); 00328 xyz[p] = *elem_nodes[0]; 00329 jac[p] = 1.0; 00330 JxW[p] = qw[p]; 00331 break; 00332 } 00333 00334 //-------------------------------------------------------------------- 00335 // 1D 00336 case 1: 00337 { 00338 // Clear the entities that will be summed 00339 xyz[p].zero(); 00340 dxyzdxi_map[p].zero(); 00341 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00342 d2xyzdxi2_map[p].zero(); 00343 #endif 00344 00345 // compute x, dx, d2x at the quadrature point 00346 for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes 00347 { 00348 // Reference to the point, helps eliminate 00349 // exessive temporaries in the inner loop 00350 libmesh_assert(elem_nodes[i]); 00351 const Point& elem_point = *elem_nodes[i]; 00352 00353 xyz[p].add_scaled (elem_point, phi_map[i][p] ); 00354 dxyzdxi_map[p].add_scaled (elem_point, dphidxi_map[i][p]); 00355 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00356 d2xyzdxi2_map[p].add_scaled(elem_point, d2phidxi2_map[i][p]); 00357 #endif 00358 } 00359 00360 // Compute the jacobian 00361 // 00362 // 1D elements can live in 2D or 3D space. 00363 // The transformation matrix from local->global 00364 // coordinates is 00365 // 00366 // T = | dx/dxi | 00367 // | dy/dxi | 00368 // | dz/dxi | 00369 // 00370 // The generalized determinant of T (from the 00371 // so-called "normal" eqns.) is 00372 // jac = "det(T)" = sqrt(det(T'T)) 00373 // 00374 // where T'= transpose of T, so 00375 // 00376 // jac = sqrt( (dx/dxi)^2 + (dy/dxi)^2 + (dz/dxi)^2 ) 00377 jac[p] = dxyzdxi_map[p].size(); 00378 00379 if (jac[p] <= 0.) 00380 libmesh_error_msg("ERROR: negative Jacobian: " << jac[p] << " in element " << elem->id()); 00381 00382 // The inverse Jacobian entries also come from the 00383 // generalized inverse of T (see also the 2D element 00384 // living in 3D code). 00385 const Real jacm2 = 1./jac[p]/jac[p]; 00386 dxidx_map[p] = jacm2*dxdxi_map(p); 00387 #if LIBMESH_DIM > 1 00388 dxidy_map[p] = jacm2*dydxi_map(p); 00389 #endif 00390 #if LIBMESH_DIM > 2 00391 dxidz_map[p] = jacm2*dzdxi_map(p); 00392 #endif 00393 00394 JxW[p] = jac[p]*qw[p]; 00395 00396 // done computing the map 00397 break; 00398 } 00399 00400 00401 //-------------------------------------------------------------------- 00402 // 2D 00403 case 2: 00404 { 00405 //------------------------------------------------------------------ 00406 // Compute the (x,y) values at the quadrature points, 00407 // the Jacobian at the quadrature points 00408 00409 xyz[p].zero(); 00410 00411 dxyzdxi_map[p].zero(); 00412 dxyzdeta_map[p].zero(); 00413 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00414 d2xyzdxi2_map[p].zero(); 00415 d2xyzdxideta_map[p].zero(); 00416 d2xyzdeta2_map[p].zero(); 00417 #endif 00418 00419 00420 // compute (x,y) at the quadrature points, derivatives once 00421 for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes 00422 { 00423 // Reference to the point, helps eliminate 00424 // exessive temporaries in the inner loop 00425 libmesh_assert(elem_nodes[i]); 00426 const Point& elem_point = *elem_nodes[i]; 00427 00428 xyz[p].add_scaled (elem_point, phi_map[i][p] ); 00429 00430 dxyzdxi_map[p].add_scaled (elem_point, dphidxi_map[i][p] ); 00431 dxyzdeta_map[p].add_scaled (elem_point, dphideta_map[i][p]); 00432 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00433 d2xyzdxi2_map[p].add_scaled (elem_point, d2phidxi2_map[i][p]); 00434 d2xyzdxideta_map[p].add_scaled (elem_point, d2phidxideta_map[i][p]); 00435 d2xyzdeta2_map[p].add_scaled (elem_point, d2phideta2_map[i][p]); 00436 #endif 00437 } 00438 00439 // compute the jacobian once 00440 const Real dx_dxi = dxdxi_map(p), 00441 dx_deta = dxdeta_map(p), 00442 dy_dxi = dydxi_map(p), 00443 dy_deta = dydeta_map(p); 00444 00445 #if LIBMESH_DIM == 2 00446 // Compute the Jacobian. This assumes the 2D face 00447 // lives in 2D space 00448 // 00449 // Symbolically, the matrix determinant is 00450 // 00451 // | dx/dxi dx/deta | 00452 // jac = | dy/dxi dy/deta | 00453 // 00454 // jac = dx/dxi*dy/deta - dx/deta*dy/dxi 00455 jac[p] = (dx_dxi*dy_deta - dx_deta*dy_dxi); 00456 00457 if (jac[p] <= 0.) 00458 libmesh_error_msg("ERROR: negative Jacobian: " << jac[p] << " in element " << elem->id()); 00459 00460 JxW[p] = jac[p]*qw[p]; 00461 00462 // Compute the shape function derivatives wrt x,y at the 00463 // quadrature points 00464 const Real inv_jac = 1./jac[p]; 00465 00466 dxidx_map[p] = dy_deta*inv_jac; //dxi/dx = (1/J)*dy/deta 00467 dxidy_map[p] = -dx_deta*inv_jac; //dxi/dy = -(1/J)*dx/deta 00468 detadx_map[p] = -dy_dxi* inv_jac; //deta/dx = -(1/J)*dy/dxi 00469 detady_map[p] = dx_dxi* inv_jac; //deta/dy = (1/J)*dx/dxi 00470 00471 dxidz_map[p] = detadz_map[p] = 0.; 00472 #else 00473 00474 const Real dz_dxi = dzdxi_map(p), 00475 dz_deta = dzdeta_map(p); 00476 00477 // Compute the Jacobian. This assumes a 2D face in 00478 // 3D space. 00479 // 00480 // The transformation matrix T from local to global 00481 // coordinates is 00482 // 00483 // | dx/dxi dx/deta | 00484 // T = | dy/dxi dy/deta | 00485 // | dz/dxi dz/deta | 00486 // note det(T' T) = det(T')det(T) = det(T)det(T) 00487 // so det(T) = std::sqrt(det(T' T)) 00488 // 00489 //---------------------------------------------- 00490 // Notes: 00491 // 00492 // dX = R dXi -> R'dX = R'R dXi 00493 // (R^-1)dX = dXi [(R'R)^-1 R']dX = dXi 00494 // 00495 // so R^-1 = (R'R)^-1 R' 00496 // 00497 // and R^-1 R = (R'R)^-1 R'R = I. 00498 // 00499 const Real g11 = (dx_dxi*dx_dxi + 00500 dy_dxi*dy_dxi + 00501 dz_dxi*dz_dxi); 00502 00503 const Real g12 = (dx_dxi*dx_deta + 00504 dy_dxi*dy_deta + 00505 dz_dxi*dz_deta); 00506 00507 const Real g21 = g12; 00508 00509 const Real g22 = (dx_deta*dx_deta + 00510 dy_deta*dy_deta + 00511 dz_deta*dz_deta); 00512 00513 const Real det = (g11*g22 - g12*g21); 00514 00515 if (det <= 0.) 00516 libmesh_error_msg("ERROR: negative Jacobian! " << " in element " << elem->id()); 00517 00518 const Real inv_det = 1./det; 00519 jac[p] = std::sqrt(det); 00520 00521 JxW[p] = jac[p]*qw[p]; 00522 00523 const Real g11inv = g22*inv_det; 00524 const Real g12inv = -g12*inv_det; 00525 const Real g21inv = -g21*inv_det; 00526 const Real g22inv = g11*inv_det; 00527 00528 dxidx_map[p] = g11inv*dx_dxi + g12inv*dx_deta; 00529 dxidy_map[p] = g11inv*dy_dxi + g12inv*dy_deta; 00530 dxidz_map[p] = g11inv*dz_dxi + g12inv*dz_deta; 00531 00532 detadx_map[p] = g21inv*dx_dxi + g22inv*dx_deta; 00533 detady_map[p] = g21inv*dy_dxi + g22inv*dy_deta; 00534 detadz_map[p] = g21inv*dz_dxi + g22inv*dz_deta; 00535 00536 #endif 00537 // done computing the map 00538 break; 00539 } 00540 00541 00542 00543 //-------------------------------------------------------------------- 00544 // 3D 00545 case 3: 00546 { 00547 //------------------------------------------------------------------ 00548 // Compute the (x,y,z) values at the quadrature points, 00549 // the Jacobian at the quadrature point 00550 00551 // Clear the entities that will be summed 00552 xyz[p].zero (); 00553 dxyzdxi_map[p].zero (); 00554 dxyzdeta_map[p].zero (); 00555 dxyzdzeta_map[p].zero (); 00556 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00557 d2xyzdxi2_map[p].zero(); 00558 d2xyzdxideta_map[p].zero(); 00559 d2xyzdxidzeta_map[p].zero(); 00560 d2xyzdeta2_map[p].zero(); 00561 d2xyzdetadzeta_map[p].zero(); 00562 d2xyzdzeta2_map[p].zero(); 00563 #endif 00564 00565 00566 // compute (x,y,z) at the quadrature points, 00567 // dxdxi, dydxi, dzdxi, 00568 // dxdeta, dydeta, dzdeta, 00569 // dxdzeta, dydzeta, dzdzeta all once 00570 for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes 00571 { 00572 // Reference to the point, helps eliminate 00573 // exessive temporaries in the inner loop 00574 libmesh_assert(elem_nodes[i]); 00575 const Point& elem_point = *elem_nodes[i]; 00576 00577 xyz[p].add_scaled (elem_point, phi_map[i][p] ); 00578 dxyzdxi_map[p].add_scaled (elem_point, dphidxi_map[i][p] ); 00579 dxyzdeta_map[p].add_scaled (elem_point, dphideta_map[i][p] ); 00580 dxyzdzeta_map[p].add_scaled (elem_point, dphidzeta_map[i][p]); 00581 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00582 d2xyzdxi2_map[p].add_scaled (elem_point, 00583 d2phidxi2_map[i][p]); 00584 d2xyzdxideta_map[p].add_scaled (elem_point, 00585 d2phidxideta_map[i][p]); 00586 d2xyzdxidzeta_map[p].add_scaled (elem_point, 00587 d2phidxidzeta_map[i][p]); 00588 d2xyzdeta2_map[p].add_scaled (elem_point, 00589 d2phideta2_map[i][p]); 00590 d2xyzdetadzeta_map[p].add_scaled (elem_point, 00591 d2phidetadzeta_map[i][p]); 00592 d2xyzdzeta2_map[p].add_scaled (elem_point, 00593 d2phidzeta2_map[i][p]); 00594 #endif 00595 } 00596 00597 // compute the jacobian 00598 const Real 00599 dx_dxi = dxdxi_map(p), dy_dxi = dydxi_map(p), dz_dxi = dzdxi_map(p), 00600 dx_deta = dxdeta_map(p), dy_deta = dydeta_map(p), dz_deta = dzdeta_map(p), 00601 dx_dzeta = dxdzeta_map(p), dy_dzeta = dydzeta_map(p), dz_dzeta = dzdzeta_map(p); 00602 00603 // Symbolically, the matrix determinant is 00604 // 00605 // | dx/dxi dy/dxi dz/dxi | 00606 // jac = | dx/deta dy/deta dz/deta | 00607 // | dx/dzeta dy/dzeta dz/dzeta | 00608 // 00609 // jac = dx/dxi*(dy/deta*dz/dzeta - dz/deta*dy/dzeta) + 00610 // dy/dxi*(dz/deta*dx/dzeta - dx/deta*dz/dzeta) + 00611 // dz/dxi*(dx/deta*dy/dzeta - dy/deta*dx/dzeta) 00612 00613 jac[p] = (dx_dxi*(dy_deta*dz_dzeta - dz_deta*dy_dzeta) + 00614 dy_dxi*(dz_deta*dx_dzeta - dx_deta*dz_dzeta) + 00615 dz_dxi*(dx_deta*dy_dzeta - dy_deta*dx_dzeta)); 00616 00617 if (jac[p] <= 0.) 00618 libmesh_error_msg("ERROR: negative Jacobian: " << jac[p] << " in element " << elem->id()); 00619 00620 JxW[p] = jac[p]*qw[p]; 00621 00622 // Compute the shape function derivatives wrt x,y at the 00623 // quadrature points 00624 const Real inv_jac = 1./jac[p]; 00625 00626 dxidx_map[p] = (dy_deta*dz_dzeta - dz_deta*dy_dzeta)*inv_jac; 00627 dxidy_map[p] = (dz_deta*dx_dzeta - dx_deta*dz_dzeta)*inv_jac; 00628 dxidz_map[p] = (dx_deta*dy_dzeta - dy_deta*dx_dzeta)*inv_jac; 00629 00630 detadx_map[p] = (dz_dxi*dy_dzeta - dy_dxi*dz_dzeta )*inv_jac; 00631 detady_map[p] = (dx_dxi*dz_dzeta - dz_dxi*dx_dzeta )*inv_jac; 00632 detadz_map[p] = (dy_dxi*dx_dzeta - dx_dxi*dy_dzeta )*inv_jac; 00633 00634 dzetadx_map[p] = (dy_dxi*dz_deta - dz_dxi*dy_deta )*inv_jac; 00635 dzetady_map[p] = (dz_dxi*dx_deta - dx_dxi*dz_deta )*inv_jac; 00636 dzetadz_map[p] = (dx_dxi*dy_deta - dy_dxi*dx_deta )*inv_jac; 00637 00638 // done computing the map 00639 break; 00640 } 00641 00642 default: 00643 libmesh_error_msg("Invalid dim = " << dim); 00644 } 00645 } 00646 00647 00648 00649 void FEMap::resize_quadrature_map_vectors(const unsigned int dim, unsigned int n_qp) 00650 { 00651 // Resize the vectors to hold data at the quadrature points 00652 xyz.resize(n_qp); 00653 dxyzdxi_map.resize(n_qp); 00654 dxidx_map.resize(n_qp); 00655 dxidy_map.resize(n_qp); // 1D element may live in 2D ... 00656 dxidz_map.resize(n_qp); // ... or 3D 00657 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00658 d2xyzdxi2_map.resize(n_qp); 00659 #endif 00660 if (dim > 1) 00661 { 00662 dxyzdeta_map.resize(n_qp); 00663 detadx_map.resize(n_qp); 00664 detady_map.resize(n_qp); 00665 detadz_map.resize(n_qp); 00666 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00667 d2xyzdxideta_map.resize(n_qp); 00668 d2xyzdeta2_map.resize(n_qp); 00669 #endif 00670 if (dim > 2) 00671 { 00672 dxyzdzeta_map.resize (n_qp); 00673 dzetadx_map.resize (n_qp); 00674 dzetady_map.resize (n_qp); 00675 dzetadz_map.resize (n_qp); 00676 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00677 d2xyzdxidzeta_map.resize(n_qp); 00678 d2xyzdetadzeta_map.resize(n_qp); 00679 d2xyzdzeta2_map.resize(n_qp); 00680 #endif 00681 } 00682 } 00683 00684 jac.resize(n_qp); 00685 JxW.resize(n_qp); 00686 } 00687 00688 00689 00690 void FEMap::compute_affine_map( const unsigned int dim, 00691 const std::vector<Real>& qw, 00692 const Elem* elem ) 00693 { 00694 // Start logging the map computation. 00695 START_LOG("compute_affine_map()", "FEMap"); 00696 00697 libmesh_assert(elem); 00698 00699 const unsigned int n_qp = cast_int<unsigned int>(qw.size()); 00700 00701 // Resize the vectors to hold data at the quadrature points 00702 this->resize_quadrature_map_vectors(dim, n_qp); 00703 00704 // Determine the nodes contributing to element elem 00705 std::vector<Node*> elem_nodes(elem->n_nodes(), NULL); 00706 for (unsigned int i=0; i<elem->n_nodes(); i++) 00707 elem_nodes[i] = elem->get_node(i); 00708 00709 // Compute map at quadrature point 0 00710 this->compute_single_point_map(dim, qw, elem, 0, elem_nodes); 00711 00712 // Compute xyz at all other quadrature points 00713 for (unsigned int p=1; p<n_qp; p++) 00714 { 00715 xyz[p].zero(); 00716 for (unsigned int i=0; i<phi_map.size(); i++) // sum over the nodes 00717 xyz[p].add_scaled (*elem_nodes[i], phi_map[i][p] ); 00718 } 00719 00720 // Copy other map data from quadrature point 0 00721 for (unsigned int p=1; p<n_qp; p++) // for each extra quadrature point 00722 { 00723 dxyzdxi_map[p] = dxyzdxi_map[0]; 00724 dxidx_map[p] = dxidx_map[0]; 00725 dxidy_map[p] = dxidy_map[0]; 00726 dxidz_map[p] = dxidz_map[0]; 00727 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00728 // The map should be affine, so second derivatives are zero 00729 d2xyzdxi2_map[p] = 0.; 00730 #endif 00731 if (dim > 1) 00732 { 00733 dxyzdeta_map[p] = dxyzdeta_map[0]; 00734 detadx_map[p] = detadx_map[0]; 00735 detady_map[p] = detady_map[0]; 00736 detadz_map[p] = detadz_map[0]; 00737 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00738 d2xyzdxideta_map[p] = 0.; 00739 d2xyzdeta2_map[p] = 0.; 00740 #endif 00741 if (dim > 2) 00742 { 00743 dxyzdzeta_map[p] = dxyzdzeta_map[0]; 00744 dzetadx_map[p] = dzetadx_map[0]; 00745 dzetady_map[p] = dzetady_map[0]; 00746 dzetadz_map[p] = dzetadz_map[0]; 00747 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00748 d2xyzdxidzeta_map[p] = 0.; 00749 d2xyzdetadzeta_map[p] = 0.; 00750 d2xyzdzeta2_map[p] = 0.; 00751 #endif 00752 } 00753 } 00754 jac[p] = jac[0]; 00755 JxW[p] = JxW[0] / qw[0] * qw[p]; 00756 } 00757 00758 STOP_LOG("compute_affine_map()", "FEMap"); 00759 } 00760 00761 00762 00763 void FEMap::compute_null_map( const unsigned int dim, 00764 const std::vector<Real>& qw) 00765 { 00766 // Start logging the map computation. 00767 START_LOG("compute_null_map()", "FEMap"); 00768 00769 const unsigned int n_qp = cast_int<unsigned int>(qw.size()); 00770 00771 // Resize the vectors to hold data at the quadrature points 00772 this->resize_quadrature_map_vectors(dim, n_qp); 00773 00774 // Compute "fake" xyz 00775 for (unsigned int p=1; p<n_qp; p++) 00776 { 00777 xyz[p].zero(); 00778 00779 dxyzdxi_map[p] = 0; 00780 dxidx_map[p] = 0; 00781 dxidy_map[p] = 0; 00782 dxidz_map[p] = 0; 00783 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00784 d2xyzdxi2_map[p] = 0; 00785 #endif 00786 if (dim > 1) 00787 { 00788 dxyzdeta_map[p] = 0; 00789 detadx_map[p] = 0; 00790 detady_map[p] = 0; 00791 detadz_map[p] = 0; 00792 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00793 d2xyzdxideta_map[p] = 0.; 00794 d2xyzdeta2_map[p] = 0.; 00795 #endif 00796 if (dim > 2) 00797 { 00798 dxyzdzeta_map[p] = 0; 00799 dzetadx_map[p] = 0; 00800 dzetady_map[p] = 0; 00801 dzetadz_map[p] = 0; 00802 #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES 00803 d2xyzdxidzeta_map[p] = 0; 00804 d2xyzdetadzeta_map[p] = 0; 00805 d2xyzdzeta2_map[p] = 0; 00806 #endif 00807 } 00808 } 00809 jac[p] = 1; 00810 JxW[p] = qw[p]; 00811 } 00812 00813 STOP_LOG("compute_null_map()", "FEMap"); 00814 } 00815 00816 00817 00818 void FEMap::compute_map(const unsigned int dim, 00819 const std::vector<Real>& qw, 00820 const Elem* elem) 00821 { 00822 if (!elem) 00823 { 00824 compute_null_map(dim, qw); 00825 return; 00826 } 00827 00828 if (elem->has_affine_map()) 00829 { 00830 compute_affine_map(dim, qw, elem); 00831 return; 00832 } 00833 00834 // Start logging the map computation. 00835 START_LOG("compute_map()", "FEMap"); 00836 00837 libmesh_assert(elem); 00838 00839 const unsigned int n_qp = cast_int<unsigned int>(qw.size()); 00840 00841 // Resize the vectors to hold data at the quadrature points 00842 this->resize_quadrature_map_vectors(dim, n_qp); 00843 00844 // Determine the nodes contributing to element elem 00845 std::vector<Node*> elem_nodes; 00846 if (elem->type() == TRI3SUBDIVISION) 00847 { 00848 // Subdivision surface FE require the 1-ring around elem 00849 libmesh_assert_equal_to (dim, 2); 00850 const Tri3Subdivision* sd_elem = static_cast<const Tri3Subdivision*>(elem); 00851 MeshTools::Subdivision::find_one_ring(sd_elem, elem_nodes); 00852 } 00853 else 00854 { 00855 // All other FE use only the nodes of elem itself 00856 elem_nodes.resize(elem->n_nodes(), NULL); 00857 for (unsigned int i=0; i<elem->n_nodes(); i++) 00858 elem_nodes[i] = elem->get_node(i); 00859 } 00860 00861 // Compute map at all quadrature points 00862 for (unsigned int p=0; p!=n_qp; p++) 00863 this->compute_single_point_map(dim, qw, elem, p, elem_nodes); 00864 00865 // Stop logging the map computation. 00866 STOP_LOG("compute_map()", "FEMap"); 00867 } 00868 00869 00870 00871 void FEMap::print_JxW(std::ostream& os) const 00872 { 00873 for (unsigned int i=0; i<JxW.size(); ++i) 00874 os << " [" << i << "]: " << JxW[i] << std::endl; 00875 } 00876 00877 00878 00879 void FEMap::print_xyz(std::ostream& os) const 00880 { 00881 for (unsigned int i=0; i<xyz.size(); ++i) 00882 os << " [" << i << "]: " << xyz[i]; 00883 } 00884 00885 00886 00887 // TODO: PB: We should consider moving this to the FEMap class 00888 template <unsigned int Dim, FEFamily T> 00889 Point FE<Dim,T>::inverse_map (const Elem* elem, 00890 const Point& physical_point, 00891 const Real tolerance, 00892 const bool secure) 00893 { 00894 libmesh_assert(elem); 00895 libmesh_assert_greater_equal (tolerance, 0.); 00896 00897 00898 // Start logging the map inversion. 00899 START_LOG("inverse_map()", "FE"); 00900 00901 // How much did the point on the reference 00902 // element change by in this Newton step? 00903 Real inverse_map_error = 0.; 00904 00905 // The point on the reference element. This is 00906 // the "initial guess" for Newton's method. The 00907 // centroid seems like a good idea, but computing 00908 // it is a little more intensive than, say taking 00909 // the zero point. 00910 // 00911 // Convergence should be insensitive of this choice 00912 // for "good" elements. 00913 Point p; // the zero point. No computation required 00914 00915 // The number of iterations in the map inversion process. 00916 unsigned int cnt = 0; 00917 00918 // The number of iterations after which we give up and declare 00919 // divergence 00920 const unsigned int max_cnt = 10; 00921 00922 // The distance (in master element space) beyond which we give up 00923 // and declare divergence. This is no longer used... 00924 // Real max_step_length = 4.; 00925 00926 00927 00928 // Newton iteration loop. 00929 do 00930 { 00931 // Where our current iterate \p p maps to. 00932 const Point physical_guess = FE<Dim,T>::map (elem, p); 00933 00934 // How far our current iterate is from the actual point. 00935 const Point delta = physical_point - physical_guess; 00936 00937 // Increment in current iterate \p p, will be computed. 00938 Point dp; 00939 00940 00941 // The form of the map and how we invert it depends 00942 // on the dimension that we are in. 00943 switch (Dim) 00944 { 00945 // ------------------------------------------------------------------ 00946 // 0D map inversion is trivial 00947 case 0: 00948 { 00949 break; 00950 } 00951 00952 // ------------------------------------------------------------------ 00953 // 1D map inversion 00954 // 00955 // Here we find the point on a 1D reference element that maps to 00956 // the point \p physical_point in the domain. This is a bit tricky 00957 // since we do not want to assume that the point \p physical_point 00958 // is also in a 1D domain. In particular, this method might get 00959 // called on the edge of a 3D element, in which case 00960 // \p physical_point actually lives in 3D. 00961 case 1: 00962 { 00963 const Point dxi = FE<Dim,T>::map_xi (elem, p); 00964 00965 // Newton's method in this case looks like 00966 // 00967 // {X} - {X_n} = [J]*dp 00968 // 00969 // Where {X}, {X_n} are 3x1 vectors, [J] is a 3x1 matrix 00970 // d(x,y,z)/dxi, and we seek dp, a scalar. Since the above 00971 // system is either overdetermined or rank-deficient, we will 00972 // solve the normal equations for this system 00973 // 00974 // [J]^T ({X} - {X_n}) = [J]^T [J] {dp} 00975 // 00976 // which involves the trivial inversion of the scalar 00977 // G = [J]^T [J] 00978 const Real G = dxi*dxi; 00979 00980 if (secure) 00981 libmesh_assert_greater (G, 0.); 00982 00983 const Real Ginv = 1./G; 00984 00985 const Real dxidelta = dxi*delta; 00986 00987 dp(0) = Ginv*dxidelta; 00988 00989 // No master elements have radius > 4, but sometimes we 00990 // can take a step that big while still converging 00991 // if (secure) 00992 // libmesh_assert_less (dp.size(), max_step_length); 00993 00994 break; 00995 } 00996 00997 00998 00999 // ------------------------------------------------------------------ 01000 // 2D map inversion 01001 // 01002 // Here we find the point on a 2D reference element that maps to 01003 // the point \p physical_point in the domain. This is a bit tricky 01004 // since we do not want to assume that the point \p physical_point 01005 // is also in a 2D domain. In particular, this method might get 01006 // called on the face of a 3D element, in which case 01007 // \p physical_point actually lives in 3D. 01008 case 2: 01009 { 01010 const Point dxi = FE<Dim,T>::map_xi (elem, p); 01011 const Point deta = FE<Dim,T>::map_eta (elem, p); 01012 01013 // Newton's method in this case looks like 01014 // 01015 // {X} - {X_n} = [J]*{dp} 01016 // 01017 // Where {X}, {X_n} are 3x1 vectors, [J] is a 3x2 matrix 01018 // d(x,y,z)/d(xi,eta), and we seek {dp}, a 2x1 vector. Since 01019 // the above system is either overdermined or rank-deficient, 01020 // we will solve the normal equations for this system 01021 // 01022 // [J]^T ({X} - {X_n}) = [J]^T [J] {dp} 01023 // 01024 // which involves the inversion of the 2x2 matrix 01025 // [G] = [J]^T [J] 01026 const Real 01027 G11 = dxi*dxi, G12 = dxi*deta, 01028 G21 = dxi*deta, G22 = deta*deta; 01029 01030 01031 const Real det = (G11*G22 - G12*G21); 01032 01033 if (secure) 01034 libmesh_assert_not_equal_to (det, 0.); 01035 01036 const Real inv_det = 1./det; 01037 01038 const Real 01039 Ginv11 = G22*inv_det, 01040 Ginv12 = -G12*inv_det, 01041 01042 Ginv21 = -G21*inv_det, 01043 Ginv22 = G11*inv_det; 01044 01045 01046 const Real dxidelta = dxi*delta; 01047 const Real detadelta = deta*delta; 01048 01049 dp(0) = (Ginv11*dxidelta + Ginv12*detadelta); 01050 dp(1) = (Ginv21*dxidelta + Ginv22*detadelta); 01051 01052 // No master elements have radius > 4, but sometimes we 01053 // can take a step that big while still converging 01054 // if (secure) 01055 // libmesh_assert_less (dp.size(), max_step_length); 01056 01057 break; 01058 } 01059 01060 01061 01062 // ------------------------------------------------------------------ 01063 // 3D map inversion 01064 // 01065 // Here we find the point in a 3D reference element that maps to 01066 // the point \p physical_point in a 3D domain. Nothing special 01067 // has to happen here, since (unless the map is singular because 01068 // you have a BAD element) the map will be invertable and we can 01069 // apply Newton's method directly. 01070 case 3: 01071 { 01072 const Point dxi = FE<Dim,T>::map_xi (elem, p); 01073 const Point deta = FE<Dim,T>::map_eta (elem, p); 01074 const Point dzeta = FE<Dim,T>::map_zeta (elem, p); 01075 01076 // Newton's method in this case looks like 01077 // 01078 // {X} = {X_n} + [J]*{dp} 01079 // 01080 // Where {X}, {X_n} are 3x1 vectors, [J] is a 3x3 matrix 01081 // d(x,y,z)/d(xi,eta,zeta), and we seek {dp}, a 3x1 vector. 01082 // Since the above system is nonsingular for invertable maps 01083 // we will solve 01084 // 01085 // {dp} = [J]^-1 ({X} - {X_n}) 01086 // 01087 // which involves the inversion of the 3x3 matrix [J] 01088 dp = RealTensorValue(dxi(0), deta(0), dzeta(0), 01089 dxi(1), deta(1), dzeta(1), 01090 dxi(2), deta(2), dzeta(2)).inverse() * delta; 01091 01092 // No master elements have radius > 4, but sometimes we 01093 // can take a step that big while still converging 01094 // if (secure) 01095 // libmesh_assert_less (dp.size(), max_step_length); 01096 01097 break; 01098 } 01099 01100 01101 // Some other dimension? 01102 default: 01103 libmesh_error_msg("Invalid Dim = " << Dim); 01104 } // end switch(Dim), dp now computed 01105 01106 01107 01108 // ||P_n+1 - P_n|| 01109 inverse_map_error = dp.size(); 01110 01111 // P_n+1 = P_n + dp 01112 p.add (dp); 01113 01114 // Increment the iteration count. 01115 cnt++; 01116 01117 // Watch for divergence of Newton's 01118 // method. Here's how it goes: 01119 // (1) For good elements, we expect convergence in 10 01120 // iterations, with no too-large steps. 01121 // - If called with (secure == true) and we have not yet converged 01122 // print out a warning message. 01123 // - If called with (secure == true) and we have not converged in 01124 // 20 iterations abort 01125 // (2) This method may be called in cases when the target point is not 01126 // inside the element and we have no business expecting convergence. 01127 // For these cases if we have not converged in 10 iterations forget 01128 // about it. 01129 if (cnt > max_cnt) 01130 { 01131 // Warn about divergence when secure is true - this 01132 // shouldn't happen 01133 if (secure) 01134 { 01135 // Print every time in devel/dbg modes 01136 #ifndef NDEBUG 01137 libmesh_here(); 01138 libMesh::err << "WARNING: Newton scheme has not converged in " 01139 << cnt << " iterations:" << std::endl 01140 << " physical_point=" 01141 << physical_point 01142 << " physical_guess=" 01143 << physical_guess 01144 << " dp=" 01145 << dp 01146 << " p=" 01147 << p 01148 << " error=" << inverse_map_error 01149 << " in element " << elem->id() 01150 << std::endl; 01151 01152 elem->print_info(libMesh::err); 01153 #else 01154 // In optimized mode, just print once that an inverse_map() call 01155 // had trouble converging its Newton iteration. 01156 libmesh_do_once(libMesh::err << "WARNING: At least one element took more than " 01157 << max_cnt 01158 << " iterations to converge in inverse_map()...\n" 01159 << "Rerun in devel/dbg mode for more details." 01160 << std::endl;); 01161 01162 #endif // NDEBUG 01163 01164 if (cnt > 2*max_cnt) 01165 { 01166 libMesh::err << "ERROR: Newton scheme FAILED to converge in " 01167 << cnt 01168 << " iterations!" 01169 << " in element " 01170 << elem->id() 01171 << std::endl; 01172 01173 elem->print_info(libMesh::err); 01174 01175 libmesh_error_msg("Exiting..."); 01176 } 01177 } 01178 // Return a far off point when secure is false - this 01179 // should only happen when we're trying to map a point 01180 // that's outside the element 01181 else 01182 { 01183 for (unsigned int i=0; i != Dim; ++i) 01184 p(i) = 1e6; 01185 01186 STOP_LOG("inverse_map()", "FE"); 01187 return p; 01188 } 01189 } 01190 } 01191 while (inverse_map_error > tolerance); 01192 01193 01194 01195 // If we are in debug mode do two sanity checks. 01196 #ifdef DEBUG 01197 01198 if (secure) 01199 { 01200 // Make sure the point \p p on the reference element actually 01201 // does map to the point \p physical_point within a tolerance. 01202 01203 const Point check = FE<Dim,T>::map (elem, p); 01204 const Point diff = physical_point - check; 01205 01206 if (diff.size() > tolerance) 01207 { 01208 libmesh_here(); 01209 libMesh::err << "WARNING: diff is " 01210 << diff.size() 01211 << std::endl 01212 << " point=" 01213 << physical_point; 01214 libMesh::err << " local=" << check; 01215 libMesh::err << " lref= " << p; 01216 01217 elem->print_info(libMesh::err); 01218 } 01219 01220 // Make sure the point \p p on the reference element actually 01221 // is 01222 01223 if (!FEAbstract::on_reference_element(p, elem->type(), 2*tolerance)) 01224 { 01225 libmesh_here(); 01226 libMesh::err << "WARNING: inverse_map of physical point " 01227 << physical_point 01228 << "is not on element." << '\n'; 01229 elem->print_info(libMesh::err); 01230 } 01231 } 01232 01233 #endif 01234 01235 01236 01237 // Stop logging the map inversion. 01238 STOP_LOG("inverse_map()", "FE"); 01239 01240 return p; 01241 } 01242 01243 01244 01245 // TODO: PB: We should consider moving this to the FEMap class 01246 template <unsigned int Dim, FEFamily T> 01247 void FE<Dim,T>::inverse_map (const Elem* elem, 01248 const std::vector<Point>& physical_points, 01249 std::vector<Point>& reference_points, 01250 const Real tolerance, 01251 const bool secure) 01252 { 01253 // The number of points to find the 01254 // inverse map of 01255 const std::size_t n_points = physical_points.size(); 01256 01257 // Resize the vector to hold the points 01258 // on the reference element 01259 reference_points.resize(n_points); 01260 01261 // Find the coordinates on the reference 01262 // element of each point in physical space 01263 for (std::size_t p=0; p<n_points; p++) 01264 reference_points[p] = 01265 FE<Dim,T>::inverse_map (elem, physical_points[p], tolerance, secure); 01266 } 01267 01268 01269 01270 // TODO: PB: We should consider moving this to the FEMap class 01271 template <unsigned int Dim, FEFamily T> 01272 Point FE<Dim,T>::map (const Elem* elem, 01273 const Point& reference_point) 01274 { 01275 libmesh_assert(elem); 01276 01277 Point p; 01278 01279 const ElemType type = elem->type(); 01280 const Order order = elem->default_order(); 01281 const unsigned int n_sf = FE<Dim,LAGRANGE>::n_shape_functions(type, order); 01282 01283 // Lagrange basis functions are used for mapping 01284 for (unsigned int i=0; i<n_sf; i++) 01285 p.add_scaled (elem->point(i), 01286 FE<Dim,LAGRANGE>::shape(type, 01287 order, 01288 i, 01289 reference_point) 01290 ); 01291 01292 return p; 01293 } 01294 01295 01296 01297 // TODO: PB: We should consider moving this to the FEMap class 01298 template <unsigned int Dim, FEFamily T> 01299 Point FE<Dim,T>::map_xi (const Elem* elem, 01300 const Point& reference_point) 01301 { 01302 libmesh_assert(elem); 01303 01304 Point p; 01305 01306 const ElemType type = elem->type(); 01307 const Order order = elem->default_order(); 01308 const unsigned int n_sf = FE<Dim,LAGRANGE>::n_shape_functions(type, order); 01309 01310 // Lagrange basis functions are used for mapping 01311 for (unsigned int i=0; i<n_sf; i++) 01312 p.add_scaled (elem->point(i), 01313 FE<Dim,LAGRANGE>::shape_deriv(type, 01314 order, 01315 i, 01316 0, 01317 reference_point) 01318 ); 01319 01320 return p; 01321 } 01322 01323 01324 01325 // TODO: PB: We should consider moving this to the FEMap class 01326 template <unsigned int Dim, FEFamily T> 01327 Point FE<Dim,T>::map_eta (const Elem* elem, 01328 const Point& reference_point) 01329 { 01330 libmesh_assert(elem); 01331 01332 Point p; 01333 01334 const ElemType type = elem->type(); 01335 const Order order = elem->default_order(); 01336 const unsigned int n_sf = FE<Dim,LAGRANGE>::n_shape_functions(type, order); 01337 01338 // Lagrange basis functions are used for mapping 01339 for (unsigned int i=0; i<n_sf; i++) 01340 p.add_scaled (elem->point(i), 01341 FE<Dim,LAGRANGE>::shape_deriv(type, 01342 order, 01343 i, 01344 1, 01345 reference_point) 01346 ); 01347 01348 return p; 01349 } 01350 01351 01352 01353 // TODO: PB: We should consider moving this to the FEMap class 01354 template <unsigned int Dim, FEFamily T> 01355 Point FE<Dim,T>::map_zeta (const Elem* elem, 01356 const Point& reference_point) 01357 { 01358 libmesh_assert(elem); 01359 01360 Point p; 01361 01362 const ElemType type = elem->type(); 01363 const Order order = elem->default_order(); 01364 const unsigned int n_sf = FE<Dim,LAGRANGE>::n_shape_functions(type, order); 01365 01366 // Lagrange basis functions are used for mapping 01367 for (unsigned int i=0; i<n_sf; i++) 01368 p.add_scaled (elem->point(i), 01369 FE<Dim,LAGRANGE>::shape_deriv(type, 01370 order, 01371 i, 01372 2, 01373 reference_point) 01374 ); 01375 01376 return p; 01377 } 01378 01379 01380 01381 // Explicit instantiation of FEMap member functions 01382 template void FEMap::init_reference_to_physical_map<0>( const std::vector<Point>&, const Elem*); 01383 template void FEMap::init_reference_to_physical_map<1>( const std::vector<Point>&, const Elem*); 01384 template void FEMap::init_reference_to_physical_map<2>( const std::vector<Point>&, const Elem*); 01385 template void FEMap::init_reference_to_physical_map<3>( const std::vector<Point>&, const Elem*); 01386 01387 //-------------------------------------------------------------- 01388 // Explicit instantiations using the macro from fe_macro.h 01389 INSTANTIATE_ALL_MAPS(0); 01390 INSTANTIATE_ALL_MAPS(1); 01391 INSTANTIATE_ALL_MAPS(2); 01392 INSTANTIATE_ALL_MAPS(3); 01393 01394 // subdivision elements are implemented only for 2D meshes & reimplement 01395 // the inverse_maps method separately 01396 INSTANTIATE_SUBDIVISION_MAPS; 01397 01398 } // namespace libMesh