Example codes
and tutorials
The (Not-so) Quick Guide
List of tutorials/demo codes
Single-Physics Problems
Poisson
Adaptivity illustrated for Poisson
Advection-Diffusion
Unsteady heat equation
Linear wave equation
The Young-Laplace equation
Navier-Stokes
Free-surface Navier-Stokes
Axisymmetric Navier-Stokes
Solid mechanics
Beam structures
Shell structures
Multi-physics Problems
Fluid-structure interaction
Boussinesq convection
Steady thermoelasticity
Methods-based example codes and tutorials
Mesh generation
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
How to write a new element
How to write a new refineable element
Default nonlinear solvers -- the sequence of action functions
...
Documentation
FE theory and top-down discussion of the data structure
The (Not-so) Quick Guide
Comprehensive bottom-up discussion of the data structure
List of available structured and unstructured meshes
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
Coding conventions and C++ style
Creating documentation
Optimisation - robustness vs. "raw speed"
Linear vs. nonlinear problems
Storing shape functions
Changing the default "full" integration scheme
Disabling the ALE formulation of unsteady equations
C vs. C++ output
Different sparse assembly techniques and the STL memory pool
Publications
Publications
Talks
Journal publications
Theses
Picture show
Download
Copyright
Download/installation instructions
Download page
FAQ & Contact
FAQ
Change log
Bugs and other known problems
Completeness of the library & our "To-Do List"
Contact the developers
Get involved

 


Beta release!

Please note that the library has not been "officially" released. While we continue to work on the documentation, these web pages are likely to contain broken links and documents in draft form. Please send an email to

oomph-lib AT maths DOT man DOT ac DOT uk

if you wish to be informed of the library's "official" release.

shape.h

Go to the documentation of this file.
00001 //LIC// ====================================================================
00002 //LIC// This file forms part of oomph-lib, the object-oriented, 
00003 //LIC// multi-physics finite-element library, available 
00004 //LIC// at http://www.oomph-lib.org.
00005 //LIC// 
00006 //LIC//           Version 0.90. August 3, 2009.
00007 //LIC// 
00008 //LIC// Copyright (C) 2006-2009 Matthias Heil and Andrew Hazel
00009 //LIC// 
00010 //LIC// This library is free software; you can redistribute it and/or
00011 //LIC// modify it under the terms of the GNU Lesser General Public
00012 //LIC// License as published by the Free Software Foundation; either
00013 //LIC// version 2.1 of the License, or (at your option) any later version.
00014 //LIC// 
00015 //LIC// This library is distributed in the hope that it will be useful,
00016 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 //LIC// Lesser General Public License for more details.
00019 //LIC// 
00020 //LIC// You should have received a copy of the GNU Lesser General Public
00021 //LIC// License along with this library; if not, write to the Free Software
00022 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00023 //LIC// 02110-1301  USA.
00024 //LIC// 
00025 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
00026 //LIC// 
00027 //LIC//====================================================================
00028 //This header file includes generic shape function classes
00029 
00030 //Include guards to prevent multiple inclusions of the file
00031 #ifndef OOMPH_SHAPE_HEADER
00032 #define OOMPH_SHAPE_HEADER
00033 
00034 
00035 // Config header generated by autoconfig
00036 #ifdef HAVE_CONFIG_H
00037   #include <oomph-lib-config.h>
00038 #endif
00039 
00040 //oomph-lib includes
00041 #include "Vector.h"
00042 #include "matrices.h"
00043 #include "orthpoly.h"
00044 
00045 namespace oomph
00046 {
00047 
00048 
00049 //========================================================================
00050 /// A Class for shape functions. In simple cases, the shape functions 
00051 /// have only one index that can be thought of as corresponding to the
00052 /// nodal points. In general, however, when quantities and 
00053 /// their gradients are interpolated separately, the shape function have
00054 /// two indicies: one corresponding to the nodal points, and the other
00055 /// to the "type" of quantity being interpolated: function, derivative, &c
00056 /// The second index can also represent the vector coordinate for 
00057 /// vector-valued (Nedelec) shape functions.
00058 ///
00059 /// The implementation of Shape functions is designed to permit fast
00060 /// copying of entire sets of values by resetting the internal pointer
00061 /// to the data, Psi;
00062 /// functionality that is required, for example, 
00063 ///  when setting the test functions
00064 /// in Galerkin elements and when reading pre-computed values of the shape 
00065 /// functions.
00066 /// In general, we cannot know at construction time whether the pointer to 
00067 /// the values will be reset or not and, therefore,
00068 /// whether the storage for values should be allocated by the object.
00069 /// We choose to allocate storage on construction and store an
00070 /// additional pointer Allocated_data that \b always addresses the storage
00071 /// allocated by the object. If the Psi pointer is reset then this storage
00072 /// will be "wasted", but only for the lifetime of the object. The cost for
00073 /// non-copied Shape functions is one additional pointer.
00074 //=========================================================================
00075 class Shape
00076 {
00077   private:
00078 
00079  /// \short Pointer that addresses the storage that will be used to read and
00080  /// set the shape functions. The shape functions are packed into 
00081  /// a flat array of doubles.
00082  double *Psi;
00083 
00084  /// \short Pointer that addresses the storage allocated by the object on
00085  /// construction. This will be the same as Psi if the object is not
00086  /// copied.
00087  double *Allocated_storage;
00088 
00089  ///Size of the first index of the shape function
00090  unsigned Index1;
00091 
00092  ///Size of the second index of the shape function
00093  unsigned Index2;
00094 
00095  ///Private function that checks whether the index is in range
00096  void range_check(const unsigned &i, const unsigned &j) const
00097   {
00098    //If an index is out of range, throw an error
00099    if((i >= Index1) || (j >= Index2))
00100     {
00101      std::ostringstream error_stream;
00102      error_stream << "Range Error: ";
00103      if(i >= Index1)
00104       {
00105        error_stream << i  << " is not in the range (0," << Index1-1 << ")" 
00106                     << std::endl;
00107       }
00108      if(j >= Index2)
00109       {
00110        error_stream << j  << " is not in the range (0," << Index2-1 << ")" 
00111                     << std::endl;
00112       }
00113      throw OomphLibError(error_stream.str(),
00114                          "Shape::range_check()",
00115                          OOMPH_EXCEPTION_LOCATION);
00116     }
00117   }
00118 
00119 public:
00120 
00121  /// Constructor for a single-index set of shape functions.
00122  Shape(const unsigned &N) : Index1(N), Index2(1) 
00123   {Allocated_storage = new double[N]; Psi = Allocated_storage;}
00124 
00125  /// Constructor for a two-index set of shape functions.
00126  Shape(const unsigned &N, const unsigned &M) : Index1(N), Index2(M) 
00127   {Allocated_storage = new double[N*M]; Psi = Allocated_storage;}
00128 
00129  /// Broken copy constructor
00130  Shape(const Shape &shape) {BrokenCopy::broken_copy("Shape");}
00131 
00132  /// The assignment operator does a shallow copy 
00133  /// (resets the pointer to the data)
00134  void operator=(const Shape &shape)
00135   {
00136 #ifdef PARANOID
00137    //Check the dimensions
00138    if((shape.Index1 != Index1) || 
00139       (shape.Index2 != Index2))
00140     {
00141      std::ostringstream error_stream;
00142      error_stream << "Cannot assign Shape object:" << std::endl
00143                   << "Indices do not match " 
00144                   << "LHS: " << Index1 << " " << Index2 
00145                   << ", RHS: " << shape.Index1 << " " << shape.Index2
00146                   << std::endl;
00147      throw OomphLibError(error_stream.str(),
00148                          "Shape::operator=()",
00149                          OOMPH_EXCEPTION_LOCATION);
00150     }
00151 #endif
00152    Psi = shape.Psi;
00153   }
00154 
00155  /// The assignment operator does a shallow copy 
00156  /// (resets the pointer to the data)
00157  void operator=(Shape* const &shape_pt)
00158   {
00159 #ifdef PARANOID
00160    //Check the dimensions
00161    if((shape_pt->Index1 != Index1) || 
00162       (shape_pt->Index2 != Index2))
00163     {
00164      std::ostringstream error_stream;
00165      error_stream << "Cannot assign Shape object:" << std::endl
00166                   << "Indices do not match " 
00167                   << "LHS: " << Index1 << " " << Index2 
00168                   << ", RHS: " << shape_pt->Index1 << " " 
00169                   << shape_pt->Index2
00170                   << std::endl;
00171      throw OomphLibError(error_stream.str(),
00172                          "Shape::operator=()",
00173                          OOMPH_EXCEPTION_LOCATION);
00174     }
00175 #endif
00176    Psi = shape_pt->Psi;
00177   }
00178 
00179  /// Destructor, clear up the memory allocated by the object
00180  ~Shape() {delete[] Allocated_storage; Allocated_storage=0;}
00181 
00182  /// Overload the bracket operator to provide access to values.
00183  inline double & operator[](const unsigned &i) 
00184   {
00185 #ifdef RANGE_CHECKING
00186    range_check(i,0);
00187 #endif
00188    return Psi[i*Index2];
00189   }
00190 
00191  /// Overload the bracket operator (const version)
00192  inline const double & operator[](const unsigned &i) const 
00193   {
00194 #ifdef RANGE_CHECKING
00195    range_check(i,0);
00196 #endif
00197    return Psi[i*Index2];
00198   }
00199 
00200  /// Overload the round bracket operator to provide access to values.
00201  inline double &operator()(const unsigned &i) 
00202   {
00203 #ifdef RANGE_CHECKING
00204    range_check(i,0);
00205 #endif
00206    return Psi[i*Index2];
00207   }
00208 
00209  /// Overload the round bracket operator (const version)
00210  inline const double &operator()(const unsigned &i) const 
00211   {
00212 #ifdef RANGE_CHECKING
00213    range_check(i,0);
00214 #endif
00215    return Psi[i*Index2];
00216   }
00217  
00218  /// Overload the round bracket operator, allowing for two indicies
00219  inline double &operator()(const unsigned &i, const unsigned &j) 
00220   {
00221 #ifdef RANGE_CHECKING
00222    range_check(i,j);
00223 #endif
00224    return Psi[i*Index2 + j];
00225   }
00226  
00227  ///\short Overload the round bracket operator, allowing for two indices 
00228  /// (const version)
00229  inline const double &operator()(const unsigned &i, const unsigned &j)
00230   const{
00231 #ifdef RANGE_CHECKING
00232   range_check(i,j);
00233 #endif
00234   return Psi[i*Index2 + j];
00235  }
00236 
00237  /// Return the range of index 1 of the shape function object
00238  inline unsigned nindex1() const {return Index1;}
00239 
00240  /// Return the range of index 2 of the shape function object
00241  inline unsigned nindex2() const {return Index2;}
00242 
00243 };
00244 
00245 //================================================================
00246 /// A Class for the derivatives of shape functions
00247 /// The class design is essentially the same as Shape, but there is
00248 /// on additional index that is used to indicate the coordinate direction in 
00249 /// which the derivative is taken.
00250 //================================================================
00251 class DShape 
00252 {
00253   private:
00254 
00255  /// \short Pointer that addresses the storage that will be used to read and
00256  /// set the shape-function derivatives. The values are packed into 
00257  /// a flat array of doubles.
00258  double *DPsi;
00259 
00260  /// \short Pointer that addresses the storage allocated by the object on
00261  /// construction. This will be the same as DPsi if the object is not
00262  /// copied.
00263  double *Allocated_storage;
00264 
00265  ///Size of the first index of the shape function
00266  unsigned Index1;
00267 
00268  ///Size of the second index of the shape function
00269  unsigned Index2;
00270 
00271  ///Size of the third index of the shape function
00272  unsigned Index3;
00273  
00274  /// Private function that checks whether the indices are in range
00275  void range_check(const unsigned &i, const unsigned &j,
00276                   const unsigned &k) const
00277   {
00278    //Check the first index
00279    if((i >= Index1) || (j >= Index2) || (k >= Index3))
00280     {
00281      std::ostringstream error_stream;
00282      error_stream << "Range Error: ";
00283      if(i >= Index1)
00284       {
00285        error_stream << i 
00286                     << " is not in the range (0," << Index1-1 << ")" 
00287                     << std::endl;
00288       }
00289      if(j >= Index2)
00290       {
00291        error_stream << j 
00292                     << " is not in the range (0," << Index2-1 << ")" 
00293                     << std::endl;
00294       }
00295      if(k >= Index3)
00296       {
00297        error_stream << k 
00298                     << " is not in the range (0," << Index3-1 << ")" 
00299                     << std::endl;
00300       }
00301      throw OomphLibError(error_stream.str(),
00302                          "DShape::range_check()",
00303                          OOMPH_EXCEPTION_LOCATION);
00304     }
00305   }
00306 
00307  
00308   public:
00309 
00310  /// Constructor with two parameters: a single-index shape function
00311  DShape(const unsigned &N, const unsigned &P) : Index1(N), Index2(1),
00312   Index3(P)
00313   {Allocated_storage = new double[N*P]; DPsi = Allocated_storage;}
00314 
00315  /// Constructor with three paramters: a two-index shape function
00316  DShape(const unsigned &N, const unsigned &M, const unsigned &P) :
00317   Index1(N), Index2(M), Index3(P)
00318   {Allocated_storage = new double[N*M*P]; DPsi = Allocated_storage;}
00319 
00320  /// Broken copy constructor
00321  DShape(const DShape &dshape) {BrokenCopy::broken_copy("DShape");}
00322 
00323  /// The assignment operator does a shallow copy 
00324  /// (resets the pointer to the data)
00325  void operator=(const DShape &dshape)
00326   {
00327 #ifdef PARANOID
00328    //Check the dimensions
00329    if((dshape.Index1 != Index1) || 
00330       (dshape.Index2 != Index2) ||
00331       (dshape.Index3 != Index3))
00332     {
00333      std::ostringstream error_stream;
00334      error_stream << "Cannot assign DShape object:" << std::endl
00335                   << "Indices do not match " 
00336                   << "LHS: " << Index1 << " " << Index2 << " " 
00337                   << Index3
00338                   << ", RHS: " << dshape.Index1 << " " 
00339                   << dshape.Index2 << " " << dshape.Index3
00340                   << std::endl;
00341      throw OomphLibError(error_stream.str(),
00342                          "DShape::operator=()",
00343                          OOMPH_EXCEPTION_LOCATION);
00344     }
00345 #endif
00346    DPsi = dshape.DPsi;
00347   }
00348 
00349  /// The assignment operator does a shallow copy 
00350  /// (resets the pointer to the data)
00351  void operator=(DShape* const &dshape_pt)
00352   {
00353 #ifdef PARANOID
00354    //Check the dimensions
00355    if((dshape_pt->Index1 != Index1) || 
00356       (dshape_pt->Index2 != Index2) ||
00357       (dshape_pt->Index3 != Index3))
00358     {
00359      std::ostringstream error_stream;
00360      error_stream << "Cannot assign Shape object:" << std::endl
00361                   << "Indices do not match " 
00362                   << "LHS: " << Index1 << " " << Index2 
00363                   << " " << Index3
00364                   << ", RHS: " << dshape_pt->Index1 << " " 
00365                   << dshape_pt->Index2 << " "
00366                   << dshape_pt->Index3
00367                   << std::endl;
00368      throw OomphLibError(error_stream.str(),
00369                          "DShape::operator=()",
00370                          OOMPH_EXCEPTION_LOCATION);
00371     }
00372 #endif
00373    DPsi = dshape_pt->DPsi;
00374   }
00375 
00376 
00377 
00378  /// Destructor, clean up the memory allocated by this object
00379  ~DShape() {delete[] Allocated_storage; Allocated_storage=0;}
00380 
00381  /// Overload the round bracket operator for access to the data
00382  inline double &operator()(const unsigned &i, const unsigned &k)
00383   {
00384 #ifdef RANGE_CHECKING
00385    range_check(i,0,k);
00386 #endif
00387    return DPsi[i*Index2*Index3 + k];
00388   }
00389 
00390  /// Overload the round bracket operator (const version)
00391  inline const double &operator()(const unsigned &i, const unsigned &k) const
00392   {
00393 #ifdef RANGE_CHECKING
00394    range_check(i,0,k);
00395 #endif
00396    return DPsi[i*Index2*Index3 + k];
00397   }
00398 
00399  /// Overload the round bracket operator, with 3 indices
00400  inline double &operator()(const unsigned &i, const unsigned &j, 
00401                     const unsigned &k)
00402   {
00403 #ifdef RANGE_CHECKING
00404    range_check(i,j,k);
00405 #endif
00406    return DPsi[(i*Index2 + j)*Index3 + k];
00407   }
00408 
00409  /// Overload the round bracket operator (const version)
00410  inline const double &operator()(const unsigned &i, const unsigned &j,
00411                           const unsigned &k) const
00412   {
00413 #ifdef RANGE_CHECKING
00414    range_check(i,j,k);
00415 #endif
00416    return DPsi[(i*Index2 + j)*Index3 + k];
00417   }
00418 
00419  /// \short Direct access to internal storage of data in flat-packed C-style 
00420  /// column-major format. WARNING: Only for experienced users. Only
00421  /// use this if raw speed is of the essence, as in the solid mechanics 
00422  /// problems.
00423  inline double& raw_direct_access(const unsigned long &i)
00424   {return DPsi[i];}
00425 
00426  /// \short Direct access to internal storage of data in flat-packed C-style 
00427  /// column-major format. WARNING: Only for experienced users. Only
00428  /// use this if raw speed is of the essence, as in the solid mechanics 
00429  /// problems.
00430  inline const double& raw_direct_access(const unsigned long &i) const
00431   {return DPsi[i];}
00432 
00433  /// \short Caculate the offset in flat-packed C-style, column-major format,
00434  /// required for a given i,j. WARNING: Only for experienced users. Only
00435  /// use this if raw speed is of the essence, as in the solid mechanics 
00436  /// problems.
00437  unsigned offset(const unsigned long &i,
00438                  const unsigned long &j) const
00439   {return (i*Index2 + j)*Index3 + 0;}
00440 
00441 
00442 
00443  /// Return the range of index 1 of the derivatives of the shape functions
00444  inline unsigned long nindex1() const {return Index1;}
00445 
00446  /// Return the range of index 2 of the derivatives of the shape functions
00447  inline unsigned long nindex2() const {return Index2;}
00448 
00449  /// Return the range of index 3 of the derivatives of the shape functions
00450  inline unsigned long nindex3() const {return Index3;}
00451 
00452 };
00453 
00454 ////////////////////////////////////////////////////////////////////
00455 //
00456 // One dimensional shape functions and derivatives.
00457 // empty -- simply establishes the template parameters.
00458 //
00459 ////////////////////////////////////////////////////////////////////
00460 
00461 namespace OneDimLagrange
00462 {
00463  /// \short Definition for 1D Lagrange shape functions. The
00464  /// value of all the shape functions at the local coordinate s
00465  /// are returned in the array Psi.
00466  template<unsigned NNODE_1D>
00467   void shape(const double &s, double *Psi)
00468   {
00469    std::ostringstream error_stream;
00470    error_stream << "One dimensional Lagrange shape functions "
00471                 << "have not been defined "
00472                 << "for " << NNODE_1D << " nodes." << std::endl;
00473    throw OomphLibError(error_stream.str()<
00474                        "OneDimLagrange::shape()",
00475                        OOMPH_EXCEPTION_LOCATION);
00476   }
00477  
00478 
00479  /// \short Definition for derivatives of 1D Lagrange shape functions. The
00480  /// value of all the shape function derivatives at the local coordinate s
00481  /// are returned in the array DPsi.
00482  template<unsigned NNODE_1D>
00483   void dshape(const double &s, double *DPsi)
00484   {
00485    std::ostringstream error_stream;
00486    error_stream << "One dimensional Lagrange shape function derivatives "
00487                 << "have not been defined "
00488                 << "for " << NNODE_1D << " nodes." << std::endl;
00489    throw OomphLibError(error_stream.str()<
00490                        "OneDimLagrange::dshape()",
00491                        OOMPH_EXCEPTION_LOCATION);
00492   }
00493  
00494 
00495  /// \short Definition for second derivatives of 
00496  /// 1D Lagrange shape functions. The
00497  /// value of all the shape function derivatives at the local coordinate s
00498  /// are returned in the array DPsi.
00499  template<unsigned NNODE_1D>
00500   void d2shape(const double &s, double *DPsi)
00501   {
00502    std::ostringstream error_stream;
00503    error_stream << "One dimensional Lagrange shape function "
00504                 << "second derivatives "
00505                 << "have not been defined "
00506                 << "for " << NNODE_1D << " nodes." << std::endl;
00507    throw OomphLibError(error_stream.str()<
00508                        "OneDimLagrangeShape::d2shape()",
00509                        OOMPH_EXCEPTION_LOCATION);
00510   }
00511 
00512  /// \short 1D shape functions specialised to linear order (2 Nodes)
00513  // Note that the numbering is such that shape[0] is at s = -1.0.
00514  // and shape[1] is at s = 1.0
00515  template<>
00516   inline void shape<2>(const double &s, double *Psi)
00517   {
00518    Psi[0] = 0.5*(1.0-s);
00519    Psi[1] = 0.5*(1.0+s);
00520   }
00521 
00522  /// Derivatives of 1D shape functions specialised to linear order (2 Nodes)
00523  template<>
00524   inline void dshape<2>(const double &s, double *DPsi)
00525   {
00526    DPsi[0] = -0.5;
00527    DPsi[1] =  0.5;
00528   }
00529  
00530  /// \short Second Derivatives of 1D shape functions, 
00531  /// specialised to linear order  (2 Nodes)
00532  template<>
00533   inline void d2shape<2>(const double &s, double *DPsi)
00534   {
00535    DPsi[0] = 0.0;
00536    DPsi[1] = 0.0;
00537   }
00538  
00539  /// \short 1D shape functions specialised to quadratic order (3 Nodes)
00540  // Note that the numbering is such that shape[0] is at s = -1.0,
00541  // shape[1] is at s = 0.0 and shape[2] is at s = 1.0.
00542  template<> 
00543   inline void shape<3>(const double &s, double *Psi) 
00544   {
00545    Psi[0] = 0.5*s*(s-1.0);
00546    Psi[1] = 1.0 - s*s;
00547    Psi[2] = 0.5*s*(s+1.0);
00548   }
00549  
00550  /// Derivatives of 1D shape functions specialised to quadratic order (3 Nodes)
00551  template<>
00552   inline void dshape<3>(const double &s, double *DPsi)
00553   {
00554    DPsi[0] = s - 0.5;
00555    DPsi[1] = -2.0*s;
00556    DPsi[2] = s + 0.5;
00557   }
00558 
00559 
00560  /// Second Derivatives of 1D shape functions, specialised to quadratic order 
00561  /// (3 Nodes)
00562  template<>
00563   inline void d2shape<3>(const double &s, double *DPsi)
00564   {
00565    DPsi[0] =  1.0;
00566    DPsi[1] = -2.0;
00567    DPsi[2] =  1.0;
00568   }
00569  
00570  /// 1D shape functions specialised to cubic order (4 Nodes)
00571  template<>
00572   inline void shape<4>(const double &s, double *Psi) 
00573   {
00574    //Output from Maple
00575    double t1 = s*s;
00576    double t2 = t1*s;
00577    double t3 = 0.5625*t2;
00578    double t4 = 0.5625*t1;
00579    double t5 = 0.625E-1*s;
00580    double t7 = 0.16875E1*t2;
00581    double t8 = 0.16875E1*s;
00582    Psi[0] = -t3+t4+t5-0.625E-1;
00583    Psi[1] = t7-t4-t8+0.5625;
00584    Psi[2] = -t7-t4+t8+0.5625;
00585    Psi[3] = t3+t4-t5-0.625E-1;
00586   }
00587 
00588 
00589  /// Derivatives of 1D shape functions specialised to cubic order (4 Nodes)
00590  template<>
00591   inline void dshape<4>(const double &s, double *DPsi)
00592   {
00593    //Output from Maple
00594    double t1 = s*s;
00595    double t2 = 0.16875E1*t1;
00596    double t3 = 0.1125E1*s;
00597    double t5 = 0.50625E1*t1;
00598    DPsi[0] = -t2+t3+0.625E-1;
00599    DPsi[1] = t5-t3-0.16875E1;
00600    DPsi[2] = -t5-t3+0.16875E1;
00601    DPsi[3] = t2+t3-0.625E-1;
00602   }
00603 
00604  /// Second Derivatives of 1D shape functions specialised to cubic 
00605  /// order (4 Nodes)
00606  template<>
00607   inline void d2shape<4>(const double &s, double *DPsi)
00608   {
00609    //Output from Maple (modified by ALH, CHECK IT)
00610    double t1 = 2.0*s;
00611    double t2 = 0.16875E1*t1;
00612    double t5 = 0.50625E1*t1;
00613    DPsi[0] = -t2+0.1125E1;
00614    DPsi[1] = t5-0.1125E1;
00615    DPsi[2] = -t5-0.1125E1;
00616    DPsi[3] = t2+0.1125E1;
00617   }
00618  
00619 };
00620 
00621 //===============================================================
00622 /// One Dimensional Hermite shape functions
00623 //===============================================================
00624 namespace OneDimHermite
00625 {
00626  //Convention for polynomial numbering scheme
00627  //Type 0 is position, 1 is slope
00628  //Node 0 is at s=0 and 1 is s=1
00629 
00630  ///Constructor sets the values of the shape functions at the position s.
00631  inline void shape(const double &s, double Psi[2][2])
00632   {
00633    //Node 0
00634    Psi[0][0] = 0.25*(s*s*s - 3.0*s + 2.0);
00635    Psi[0][1] = 0.25*(s*s*s - s*s - s + 1.0);
00636    //Node 1
00637    Psi[1][0] = 0.25*(2.0 + 3.0*s - s*s*s);
00638    Psi[1][1] = 0.25*(s*s*s + s*s - s - 1.0);
00639   }
00640 
00641 
00642 /// Derivatives of 1D Hermite shape functions
00643  inline void dshape(const double &s, double DPsi[2][2]) 
00644   {
00645    //Node 0
00646    DPsi[0][0] = 0.75*(s*s - 1.0);
00647    DPsi[0][1] = 0.25*(3.0*s*s - 2.0*s - 1.0);
00648     //Node 1
00649    DPsi[1][0] = 0.75*(1.0 - s*s);
00650    DPsi[1][1] = 0.25*(3.0*s*s + 2.0*s - 1.0);
00651  }
00652 
00653 /// Second derivatives of the Hermite shape functions
00654  inline void d2shape(const double &s, double DPsi[2][2])
00655   {
00656    //Node 0
00657    DPsi[0][0] = 1.5*s;
00658    DPsi[0][1] = 0.5*(3.0*s - 1.0);
00659    //Node 1
00660    DPsi[1][0] = -1.5*s;
00661    DPsi[1][1] = 0.5*(3.0*s + 1.0);
00662 
00663   }
00664 
00665 };
00666 
00667 //=====================================================================
00668 /// Class that returns the shape functions associated with legendre
00669 //=====================================================================
00670 template<unsigned NNODE_1D>
00671 class OneDimensionalLegendreShape : public Shape
00672 {
00673  static bool Nodes_calculated;
00674 
00675   public:
00676  static Vector<double> z;
00677  
00678  /// Static function used to populate the stored positions
00679  static inline void calculate_nodal_positions()
00680   {
00681    if(!Nodes_calculated) 
00682     {
00683      Orthpoly::gll_nodes(NNODE_1D,z);
00684      Nodes_calculated=true;
00685     }
00686   }
00687 
00688  static inline double nodal_position(const unsigned &n)
00689   {return z[n];}
00690 
00691  /// Constructor
00692  OneDimensionalLegendreShape(const double &s) : Shape(NNODE_1D)
00693   {
00694    using namespace Orthpoly;
00695    
00696    unsigned p = NNODE_1D-1;
00697    //Now populate the shape function
00698    for(unsigned i=0;i<NNODE_1D;i++)
00699     {
00700      //If we're at one of the nodes, the value must be 1.0
00701      if(std::abs(s-z[i]) < Orthpoly::eps) {(*this)[i] = 1.0;}
00702      //Otherwise use the lagrangian interpolant
00703      else
00704       {
00705        (*this)[i] = (1.0 - s*s)*dlegendre(p,s)/
00706         (p*(p+1)*legendre(p,z[i])*(z[i] - s));
00707       }
00708     }
00709   }
00710 };
00711 
00712 template<unsigned NNODE_1D>
00713 Vector<double> OneDimensionalLegendreShape<NNODE_1D>::z;
00714 
00715 template<unsigned NNODE_1D>
00716 bool OneDimensionalLegendreShape<NNODE_1D>::Nodes_calculated=false;
00717 
00718 
00719 template <unsigned NNODE_1D>
00720 class OneDimensionalLegendreDShape : public Shape
00721 {
00722   public:
00723  // Constructor 
00724  OneDimensionalLegendreDShape(const double &s) : Shape(NNODE_1D)
00725   {
00726    unsigned p = NNODE_1D - 1;
00727    Vector <double> z = OneDimensionalLegendreShape<NNODE_1D>::z;
00728 
00729    
00730    bool root=false;
00731    
00732    for(unsigned i=0;i<NNODE_1D;i++)
00733     {
00734      unsigned rootnum=0;
00735      for(unsigned j=0;j<NNODE_1D;j++){     // Loop over roots to check if
00736       if(std::abs(s-z[j])<10*Orthpoly::eps){ // s happens to be a root.
00737        root=true;
00738        break;
00739       }
00740                     rootnum+=1;
00741      }
00742      if(root==true){
00743       if(i==rootnum && i==0){(*this)[i]=-(1.0+p)*p/4.0;}
00744       else if(i==rootnum && i==p){(*this)[i]=(1.0+p)*p/4.0;}
00745                     else if(i==rootnum){(*this)[i]=0.0;}
00746       else{(*this)[i]=Orthpoly::legendre(p,z[rootnum])
00747             /Orthpoly::legendre(p,z[i])/(z[rootnum]-z[i]);}
00748      }
00749      else{
00750                     (*this)[i]=((1+s*(s-2*z[i]))/(s-z[i])* Orthpoly::dlegendre(p,s)
00751                                 -(1-s*s)* Orthpoly::ddlegendre(p,s))
00752                      /p/(p+1.0)/Orthpoly::legendre(p,z[i])/(s-z[i]);
00753      }
00754      root = false;
00755     }
00756    
00757    
00758   }
00759 
00760 };
00761 
00762 }
00763 
00764 #endif

Generated on Mon Aug 10 11:23:50 2009 by  doxygen 1.4.7