$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 // Local includes 00021 #include "libmesh/libmesh_config.h" 00022 #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS 00023 #include "libmesh/inf_fe.h" 00024 #include "libmesh/fe.h" 00025 #include "libmesh/elem.h" 00026 #include "libmesh/inf_fe_macro.h" 00027 #include "libmesh/libmesh_logging.h" 00028 00029 namespace libMesh 00030 { 00031 00032 00033 00034 // ------------------------------------------------------------ 00035 // InfFE static class members concerned with coordinate 00036 // mapping 00037 00038 00039 template <unsigned int Dim, FEFamily T_radial, InfMapType T_map> 00040 Point InfFE<Dim,T_radial,T_map>::map (const Elem* inf_elem, 00041 const Point& reference_point) 00042 { 00043 libmesh_assert(inf_elem); 00044 libmesh_assert_not_equal_to (Dim, 0); 00045 00046 UniquePtr<Elem> base_elem (Base::build_elem (inf_elem)); 00047 00048 const Order radial_mapping_order (Radial::mapping_order()); 00049 const Real v (reference_point(Dim-1)); 00050 00051 // map in the base face 00052 Point base_point; 00053 switch (Dim) 00054 { 00055 case 1: 00056 base_point = inf_elem->point(0); 00057 break; 00058 case 2: 00059 base_point = FE<1,LAGRANGE>::map (base_elem.get(), reference_point); 00060 break; 00061 case 3: 00062 base_point = FE<2,LAGRANGE>::map (base_elem.get(), reference_point); 00063 break; 00064 #ifdef DEBUG 00065 default: 00066 libmesh_error_msg("Unknown Dim = " << Dim); 00067 #endif 00068 } 00069 00070 00071 // map in the outer node face not necessary. Simply 00072 // compute the outer_point = base_point + (base_point-origin) 00073 const Point outer_point (base_point * 2. - inf_elem->origin()); 00074 00075 Point p; 00076 00077 // there are only two mapping shapes in radial direction 00078 p.add_scaled (base_point, InfFE<Dim,INFINITE_MAP,T_map>::eval (v, radial_mapping_order, 0)); 00079 p.add_scaled (outer_point, InfFE<Dim,INFINITE_MAP,T_map>::eval (v, radial_mapping_order, 1)); 00080 00081 return p; 00082 } 00083 00084 00085 00086 00087 00088 template <unsigned int Dim, FEFamily T_radial, InfMapType T_map> 00089 Point InfFE<Dim,T_radial,T_map>::inverse_map (const Elem* inf_elem, 00090 const Point& physical_point, 00091 const Real tolerance, 00092 const bool secure, 00093 const bool interpolated) 00094 { 00095 libmesh_assert(inf_elem); 00096 libmesh_assert_greater_equal (tolerance, 0.); 00097 00098 00099 // Start logging the map inversion. 00100 START_LOG("inverse_map()", "InfFE"); 00101 00102 00103 // 1.) 00104 // build a base element to do the map inversion in the base face 00105 UniquePtr<Elem> base_elem (Base::build_elem (inf_elem)); 00106 00107 const Order base_mapping_order (base_elem->default_order()); 00108 const ElemType base_mapping_elem_type (base_elem->type()); 00109 const unsigned int n_base_mapping_sf = Base::n_base_mapping_sf (base_mapping_elem_type, 00110 base_mapping_order); 00111 00112 const ElemType inf_elem_type = inf_elem->type(); 00113 if (inf_elem_type != INFHEX8 && 00114 inf_elem_type != INFPRISM6) 00115 libmesh_error_msg("ERROR: InfFE::inverse_map is currently implemented only for \ninfinite elments of type InfHex8 and InfPrism6."); 00116 00117 00118 // 2.) 00119 // just like in FE<Dim-1,LAGRANGE>::inverse_map(): compute 00120 // the local coordinates, but only in the base element. 00121 // The radial part can then be computed directly later on. 00122 00123 00124 // How much did the point on the reference 00125 // element change by in this Newton step? 00126 Real inverse_map_error = 0.; 00127 00128 00129 // The point on the reference element. This is 00130 // the "initial guess" for Newton's method. The 00131 // centroid seems like a good idea, but computing 00132 // it is a little more intensive than, say taking 00133 // the zero point. 00134 // 00135 // Convergence should be insensitive of this choice 00136 // for "good" elements. 00137 Point p; // the zero point. No computation required 00138 00139 00140 00141 // Now find the intersection of a plane represented by the base 00142 // element nodes and the line given by the origin of the infinite 00143 // element and the physical point. 00144 Point intersection; 00145 00146 // the origin of the infinite lement 00147 const Point o = inf_elem->origin(); 00148 00149 switch (Dim) 00150 { 00151 00152 // unnecessary for 1D 00153 case 1: 00154 { 00155 break; 00156 } 00157 00158 case 2: 00159 libmesh_error_msg("ERROR: InfFE::inverse_map is not yet implemented in 2d"); 00160 00161 00162 case 3: 00163 { 00164 // references to the nodal points of the base element 00165 const Point& p0 = base_elem->point(0); 00166 const Point& p1 = base_elem->point(1); 00167 const Point& p2 = base_elem->point(2); 00168 00169 // a reference to the physical point 00170 const Point& fp = physical_point; 00171 00172 // The intersection of the plane and the line is given by 00173 // can be computed solving a linear 3x3 system 00174 // a*({p1}-{p0})+b*({p2}-{p0})-c*({fp}-{o})={fp}-{p0}. 00175 const Real c_factor = -(p1(0)*fp(1)*p0(2)-p1(0)*fp(2)*p0(1) 00176 +fp(0)*p1(2)*p0(1)-p0(0)*fp(1)*p1(2) 00177 +p0(0)*fp(2)*p1(1)+p2(0)*fp(2)*p0(1) 00178 -p2(0)*fp(1)*p0(2)-fp(0)*p2(2)*p0(1) 00179 +fp(0)*p0(2)*p2(1)+p0(0)*fp(1)*p2(2) 00180 -p0(0)*fp(2)*p2(1)-fp(0)*p0(2)*p1(1) 00181 +p0(2)*p2(0)*p1(1)-p0(1)*p2(0)*p1(2) 00182 -fp(0)*p1(2)*p2(1)+p2(1)*p0(0)*p1(2) 00183 -p2(0)*fp(2)*p1(1)-p1(0)*fp(1)*p2(2) 00184 +p2(2)*p1(0)*p0(1)+p1(0)*fp(2)*p2(1) 00185 -p0(2)*p1(0)*p2(1)-p2(2)*p0(0)*p1(1) 00186 +fp(0)*p2(2)*p1(1)+p2(0)*fp(1)*p1(2))/ 00187 (fp(0)*p1(2)*p0(1)-p1(0)*fp(2)*p0(1) 00188 +p1(0)*fp(1)*p0(2)-p1(0)*o(1)*p0(2) 00189 +o(0)*p2(2)*p0(1)-p0(0)*fp(2)*p2(1) 00190 +p1(0)*o(1)*p2(2)+fp(0)*p0(2)*p2(1) 00191 -fp(0)*p1(2)*p2(1)-p0(0)*o(1)*p2(2) 00192 +p0(0)*fp(1)*p2(2)-o(0)*p0(2)*p2(1) 00193 +o(0)*p1(2)*p2(1)-p2(0)*fp(2)*p1(1) 00194 +fp(0)*p2(2)*p1(1)-p2(0)*fp(1)*p0(2) 00195 -o(2)*p0(0)*p1(1)-fp(0)*p0(2)*p1(1) 00196 +p0(0)*o(1)*p1(2)+p0(0)*fp(2)*p1(1) 00197 -p0(0)*fp(1)*p1(2)-o(0)*p1(2)*p0(1) 00198 -p2(0)*o(1)*p1(2)-o(2)*p2(0)*p0(1) 00199 -o(2)*p1(0)*p2(1)+o(2)*p0(0)*p2(1) 00200 -fp(0)*p2(2)*p0(1)+o(2)*p2(0)*p1(1) 00201 +p2(0)*o(1)*p0(2)+p2(0)*fp(1)*p1(2) 00202 +p2(0)*fp(2)*p0(1)-p1(0)*fp(1)*p2(2) 00203 +p1(0)*fp(2)*p2(1)-o(0)*p2(2)*p1(1) 00204 +o(2)*p1(0)*p0(1)+o(0)*p0(2)*p1(1)); 00205 00206 00207 // Compute the intersection with 00208 // {intersection} = {fp} + c*({fp}-{o}). 00209 intersection.add_scaled(fp,1.+c_factor); 00210 intersection.add_scaled(o,-c_factor); 00211 00212 break; 00213 } 00214 00215 default: 00216 libmesh_error_msg("Invalid dim = " << Dim); 00217 } 00218 00222 unsigned int cnt = 0; 00223 00224 00228 do 00229 { 00230 00231 // Increment in current iterate \p p, will be computed. 00232 // Automatically initialized to all zero. Note that 00233 // in 3D, actually only the first two entries are 00234 // filled by the inverse map, and in 2D only the first. 00235 Point dp; 00236 00237 // The form of the map and how we invert it depends 00238 // on the dimension that we are in. 00239 switch (Dim) 00240 { 00241 00242 //------------------------------------------------------------------ 00243 // 1D infinite element - no map inversion necessary 00244 case 1: 00245 { 00246 break; 00247 } 00248 00249 00250 //------------------------------------------------------------------ 00251 // 2D infinite element - 1D map inversion 00252 // 00253 // In this iteration scheme only search for the local coordinate 00254 // in xi direction. Once xi is determined, the radial coordinate eta is 00255 // uniquely determined, and there is no need to iterate in that direction. 00256 case 2: 00257 { 00258 00259 // Where our current iterate \p p maps to. 00260 const Point physical_guess = FE<1,LAGRANGE>::map (base_elem.get(), p); 00261 00262 00263 // How far our current iterate is from the actual point. 00264 const Point delta = physical_point - physical_guess; 00265 00266 00267 const Point dxi = FE<1,LAGRANGE>::map_xi (base_elem.get(), p); 00268 00269 00270 // For details on Newton's method see fe_map.C 00271 const Real G = dxi*dxi; 00272 00273 if (secure) 00274 libmesh_assert_greater (G, 0.); 00275 00276 const Real Ginv = 1./G; 00277 00278 const Real dxidelta = dxi*delta; 00279 00280 // compute only the first coordinate 00281 dp(0) = Ginv*dxidelta; 00282 00283 break; 00284 } 00285 00286 00287 00288 //------------------------------------------------------------------ 00289 // 3D infinite element - 2D map inversion 00290 // 00291 // In this iteration scheme only search for the local coordinates 00292 // in xi and eta direction. Once xi, eta are determined, the radial 00293 // coordinate zeta may directly computed. 00294 case 3: 00295 { 00296 00297 00298 // Where our current iterate \p p maps to. 00299 const Point physical_guess = FE<2,LAGRANGE>::map (base_elem.get(), p); 00300 00301 // How far our current iterate is from the actual point. 00302 // const Point delta = physical_point - physical_guess; 00303 const Point delta = intersection - physical_guess; 00304 00305 const Point dxi = FE<2,LAGRANGE>::map_xi (base_elem.get(), p); 00306 const Point deta = FE<2,LAGRANGE>::map_eta (base_elem.get(), p); 00307 00308 00309 // For details on Newton's method see fe_map.C 00310 const Real 00311 G11 = dxi*dxi, G12 = dxi*deta, 00312 G21 = dxi*deta, G22 = deta*deta; 00313 00314 00315 const Real det = (G11*G22 - G12*G21); 00316 00317 if (secure) 00318 { 00319 libmesh_assert_greater (det, 0.); 00320 libmesh_assert_greater (std::abs(det), 1.e-10); 00321 } 00322 00323 const Real inv_det = 1./det; 00324 00325 const Real 00326 Ginv11 = G22*inv_det, 00327 Ginv12 = -G12*inv_det, 00328 00329 Ginv21 = -G21*inv_det, 00330 Ginv22 = G11*inv_det; 00331 00332 00333 const Real dxidelta = dxi*delta; 00334 const Real detadelta = deta*delta; 00335 00336 // compute only the first two coordinates. 00337 dp(0) = (Ginv11*dxidelta + Ginv12*detadelta); 00338 dp(1) = (Ginv21*dxidelta + Ginv22*detadelta); 00339 00340 break; 00341 } 00342 00343 00344 00345 // Some other dimension? 00346 default: 00347 libmesh_error_msg("Unknown Dim = " << Dim); 00348 } // end switch(Dim), dp now computed 00349 00350 00351 00352 // determine the error in computing the local coordinates 00353 // in the base: ||P_n+1 - P_n|| 00354 inverse_map_error = dp.size(); 00355 00356 00357 // P_n+1 = P_n + dp 00358 p.add (dp); 00359 00360 00361 // Increment the iteration count. 00362 cnt++; 00363 00364 00365 // Watch for divergence of Newton's 00366 // method. 00367 if (cnt > 10) 00368 { 00369 if (secure || !secure) 00370 { 00371 libmesh_here(); 00372 { 00373 libMesh::err << "WARNING: Newton scheme has not converged in " 00374 << cnt << " iterations:\n" 00375 << " physical_point=" 00376 << physical_point 00377 << " dp=" 00378 << dp 00379 << " p=" 00380 << p 00381 << " error=" << inverse_map_error 00382 << std::endl; 00383 } 00384 } 00385 00386 if (cnt > 20) 00387 libmesh_error_msg("ERROR: Newton scheme FAILED to converge in " << cnt << " iterations!"); 00388 00389 // else 00390 // { 00391 // break; 00392 // } 00393 } 00394 } 00395 while (inverse_map_error > tolerance); 00396 00397 00398 00399 // 4. 00400 // 00401 // Now that we have the local coordinates in the base, 00402 // compute the interpolated radial distance a(s,t) \p a_interpolated 00403 if (interpolated) 00404 switch (Dim) 00405 { 00406 case 1: 00407 { 00408 Real a_interpolated = Point( inf_elem->point(0) 00409 - inf_elem->point(n_base_mapping_sf) ).size(); 00410 00411 p(0) = 1. - 2*a_interpolated/physical_point(0); 00412 00413 #ifdef DEBUG 00414 // the radial distance should always be >= -1. 00415 00416 if (p(0)+1 < tolerance) 00417 { 00418 libmesh_here(); 00419 libMesh::err << "WARNING: radial distance p(0) is " 00420 << p(0) 00421 << std::endl; 00422 } 00423 #endif 00424 00425 break; 00426 } 00427 00428 00429 case 2: 00430 { 00431 Real a_interpolated = 0.; 00432 00433 // the distance between the origin and the physical point 00434 const Real fp_o_dist = Point(o-physical_point).size(); 00435 00436 for (unsigned int i=0; i<n_base_mapping_sf; i++) 00437 { 00438 // the radial distance of the i-th base mapping point 00439 const Real dist_i = Point( inf_elem->point(i) 00440 - inf_elem->point(i+n_base_mapping_sf) ).size(); 00441 // weight with the corresponding shape function 00442 a_interpolated += dist_i * FE<1,LAGRANGE>::shape(base_mapping_elem_type, 00443 base_mapping_order, 00444 i, 00445 p); 00446 } 00447 00448 p(1) = 1. - 2*a_interpolated/fp_o_dist; 00449 00450 #ifdef DEBUG 00451 // the radial distance should always be >= -1. 00452 00453 // if (p(1)+1 < tolerance) 00454 // { 00455 // libmesh_here(); 00456 // libMesh::err << "WARNING: radial distance p(1) is " 00457 // << p(1) 00458 // << std::endl; 00459 // } 00460 #endif 00461 00462 break; 00463 } 00464 00465 00466 case 3: 00467 { 00468 Real a_interpolated = 0.; 00469 00470 00471 // the distance between the origin and the physical point 00472 const Real fp_o_dist = Point(o-physical_point).size(); 00473 00474 for (unsigned int i=0; i<n_base_mapping_sf; i++) 00475 { 00476 // the radial distance of the i-th base mapping point 00477 const Real dist_i = Point( inf_elem->point(i) 00478 - inf_elem->point(i+n_base_mapping_sf) ).size(); 00479 00480 // weight with the corresponding shape function 00481 a_interpolated += dist_i * FE<2,LAGRANGE>::shape(base_mapping_elem_type, 00482 base_mapping_order, 00483 i, 00484 p); 00485 00486 } 00487 00488 p(2) = 1. - 2*a_interpolated/fp_o_dist; 00489 00490 #ifdef DEBUG 00491 00492 00493 // the radial distance should always be >= -1. 00494 00495 // if (p(2)+1 < tolerance) 00496 // { 00497 // libmesh_here(); 00498 // libMesh::err << "WARNING: radial distance p(2) is " 00499 // << p(2) 00500 // << std::endl; 00501 // } 00502 #endif 00503 00504 break; 00505 } 00506 00507 default: 00508 libmesh_error_msg("Unknown Dim = " << Dim); 00509 } // end switch(Dim), p fully computed, including radial part 00510 00511 // if we do not want the interpolated distance, then 00512 // use newton iteration to get the actual distance 00513 else 00514 { 00515 // distance from the physical point to the ifem origin 00516 const Real fp_o_dist = Point(o-physical_point).size(); 00517 00518 // the distance from the intersection on the 00519 // base to the origin 00520 const Real a_dist = intersection.size(); 00521 00522 // element coordinate in radial direction 00523 // here our first guess is 0. 00524 Real v = 0.; 00525 00526 // the order of the radial mapping 00527 const Order radial_mapping_order (Radial::mapping_order()); 00528 00529 unsigned int cnt2 = 0; 00530 inverse_map_error = 0.; 00531 00532 // Newton iteration in 1-D 00533 do 00534 { 00535 // the mapping in radial direction 00536 // note that we only have two mapping functions in 00537 // radial direction 00538 const Real r = a_dist * InfFE<Dim,INFINITE_MAP,T_map>::eval (v, radial_mapping_order, 0) 00539 + 2. * a_dist * InfFE<Dim,INFINITE_MAP,T_map>::eval (v, radial_mapping_order, 1); 00540 00541 const Real dr = a_dist * InfFE<Dim,INFINITE_MAP,T_map>::eval_deriv (v, radial_mapping_order, 0) 00542 + 2. * a_dist * InfFE<Dim,INFINITE_MAP,T_map>::eval_deriv (v, radial_mapping_order, 1); 00543 00544 const Real G = dr*dr; 00545 const Real Ginv = 1./G; 00546 00547 const Real delta = fp_o_dist - r; 00548 const Real drdelta = dr*delta; 00549 00550 Real dp = Ginv*drdelta; 00551 00552 // update the radial coordinate 00553 v += dp; 00554 00555 // note that v should be smaller than 1, 00556 // since radial mapping function tends to infinity 00557 if (v >= 1.) 00558 v = .9999; 00559 00560 inverse_map_error = std::fabs(dp); 00561 00562 // increment iteration count 00563 cnt2 ++; 00564 if (cnt2 > 20) 00565 libmesh_error_msg("ERROR: 1D Newton scheme FAILED to converge"); 00566 00567 00568 } 00569 while (inverse_map_error > tolerance); 00570 00571 switch (Dim) 00572 { 00573 case 1: 00574 { 00575 p(0) = v; 00576 break; 00577 } 00578 case 2: 00579 { 00580 p(1) = v; 00581 break; 00582 } 00583 case 3: 00584 { 00585 p(2) = v; 00586 break; 00587 } 00588 default: 00589 libmesh_error_msg("Unknown Dim = " << Dim); 00590 } 00591 } 00592 00593 // If we are in debug mode do a sanity check. Make sure 00594 // the point \p p on the reference element actually does 00595 // map to the point \p physical_point within a tolerance. 00596 #ifdef DEBUG 00597 /* 00598 const Point check = InfFE<Dim,T_radial,T_map>::map (inf_elem, p); 00599 const Point diff = physical_point - check; 00600 00601 if (diff.size() > tolerance) 00602 { 00603 libmesh_here(); 00604 libMesh::err << "WARNING: diff is " 00605 << diff.size() 00606 << std::endl; 00607 } 00608 */ 00609 #endif 00610 00611 00612 00613 00614 // Stop logging the map inversion. 00615 STOP_LOG("inverse_map()", "InfFE"); 00616 00617 return p; 00618 } 00619 00620 template <unsigned int Dim, FEFamily T_radial, InfMapType T_map> 00621 void InfFE<Dim,T_radial,T_map>::inverse_map (const Elem* elem, 00622 const std::vector<Point>& physical_points, 00623 std::vector<Point>& reference_points, 00624 const Real tolerance, 00625 const bool secure) 00626 { 00627 // The number of points to find the 00628 // inverse map of 00629 const std::size_t n_points = physical_points.size(); 00630 00631 // Resize the vector to hold the points 00632 // on the reference element 00633 reference_points.resize(n_points); 00634 00635 // Find the coordinates on the reference 00636 // element of each point in physical space 00637 for (unsigned int p=0; p<n_points; p++) 00638 reference_points[p] = 00639 InfFE<Dim,T_radial,T_map>::inverse_map (elem, physical_points[p], tolerance, secure, false); 00640 } 00641 00642 00643 00644 00645 //-------------------------------------------------------------- 00646 // Explicit instantiations using the macro from inf_fe_macro.h 00647 //INSTANTIATE_INF_FE(1,CARTESIAN); 00648 00649 //INSTANTIATE_INF_FE(2,CARTESIAN); 00650 00651 //INSTANTIATE_INF_FE(3,CARTESIAN); 00652 00653 INSTANTIATE_INF_FE_MBRF(1,CARTESIAN,Point,inverse_map(const Elem*,const Point&,const Real,const bool,const bool)); 00654 INSTANTIATE_INF_FE_MBRF(2,CARTESIAN,Point,inverse_map(const Elem*,const Point&,const Real,const bool,const bool)); 00655 INSTANTIATE_INF_FE_MBRF(3,CARTESIAN,Point,inverse_map(const Elem*,const Point&,const Real,const bool,const bool)); 00656 00657 INSTANTIATE_INF_FE_MBRF(1,CARTESIAN,void,inverse_map(const Elem*,const std::vector<Point>&,std::vector<Point>&,const Real, const bool)); 00658 INSTANTIATE_INF_FE_MBRF(2,CARTESIAN,void,inverse_map(const Elem*,const std::vector<Point>&,std::vector<Point>&,const Real, const bool)); 00659 INSTANTIATE_INF_FE_MBRF(3,CARTESIAN,void,inverse_map(const Elem*,const std::vector<Point>&,std::vector<Point>&,const Real, const bool)); 00660 00661 00662 } // namespace libMesh 00663 00664 00665 #endif //ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS