action functions
|
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 if you wish to be informed of the library's "official" release. |
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
1.4.7