$extrastylesheet
inf_fe_map.C
Go to the documentation of this file.
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