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.

navier_stokes_elements.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 //Header file for Navier Stokes elements
00029 
00030 #ifndef OOMPH_NAVIER_STOKES_ELEMENTS_HEADER
00031 #define OOMPH_NAVIER_STOKES_ELEMENTS_HEADER
00032 
00033 // Config header generated by autoconfig
00034 #ifdef HAVE_CONFIG_H
00035 #include <oomph-lib-config.h>
00036 #endif
00037 
00038 
00039 //OOMPH-LIB headers 
00040 #include "../generic/Qelements.h"
00041 #include "../generic/fsi.h"
00042 
00043 namespace oomph
00044 {
00045 
00046 
00047 //======================================================================
00048 /// A class for elements that solve the cartesian Navier--Stokes equations,
00049 /// templated by the dimension DIM.
00050 /// This contains the generic maths -- any concrete implementation must 
00051 /// be derived from this.
00052 ///
00053 /// We're solving:
00054 ///
00055 ///  \f$ { Re \left( St \frac{\partial u_i}{\partial t} + 
00056 ///              (u_j - u_j^{M}) \frac{\partial u_i}{\partial x_j} \right) =
00057 ///      - \frac{\partial p}{\partial x_i}  - R_\rho B_i(x_j) -
00058 ///       \frac{Re}{Fr} G_i +
00059 ///        \frac{\partial }{\partial x_j} \left[  R_\mu \left(
00060 ///        \frac{\partial u_i}{\partial x_j} +  
00061 ///         \frac{\partial u_j}{\partial x_i} \right) \right] } \f$
00062 ///
00063 ///  and 
00064 ///
00065 ///  \f$ {\Large \frac{\partial u_i}{\partial x_i} = Q } \f$
00066 ///
00067 /// We also provide all functions required to use this element
00068 /// in FSI problems, by deriving it from the FSIFluidElement base
00069 /// class. 
00070 //======================================================================
00071 template <unsigned DIM>
00072 class NavierStokesEquations : public virtual FSIFluidElement
00073 {
00074 
00075   public:
00076 
00077  /// \short Function pointer to body force function fct(t,x,f(x))
00078  /// x is a Vector!
00079  typedef void (*NavierStokesBodyForceFctPt)(const double& time,
00080                                             const Vector<double>& x,
00081                                             Vector<double>& body_force);
00082 
00083  /// \short Function pointer to source function fct(t,x)
00084  /// x is a Vector!
00085  typedef double (*NavierStokesSourceFctPt)(const double& time,
00086                                            const Vector<double>& x);
00087 
00088   private:
00089 
00090  /// \short Static "magic" number that indicates that the pressure is
00091  /// not stored at a node
00092  static int Pressure_not_stored_at_node;
00093 
00094  /// Static default value for the physical constants (all initialised to zero)
00095  static double Default_Physical_Constant_Value;
00096 
00097  /// Static default value for the physical ratios (all are initialised to one)
00098  static double Default_Physical_Ratio_Value;
00099 
00100  /// Static default value for the gravity vector
00101  static Vector<double> Default_Gravity_vector; 
00102 
00103   protected:
00104 
00105  //Physical constants
00106 
00107  /// \short Pointer to the viscosity ratio (relative to the 
00108  /// viscosity used in the definition of the Reynolds number)
00109  double *Viscosity_Ratio_pt;
00110  
00111  /// \short Pointer to the density ratio (relative to the density used in the 
00112  /// definition of the Reynolds number)
00113  double *Density_Ratio_pt;
00114  
00115  // Pointers to global physical constants
00116 
00117  /// Pointer to global Reynolds number
00118  double *Re_pt;
00119  
00120  /// Pointer to global Reynolds number x Strouhal number (=Womersley)
00121  double *ReSt_pt;
00122  
00123  /// \short Pointer to global Reynolds number x inverse Froude number
00124  /// (= Bond number / Capillary number) 
00125  double *ReInvFr_pt;
00126  
00127  /// Pointer to global gravity Vector
00128  Vector<double> *G_pt;
00129  
00130  /// Pointer to body force function
00131  NavierStokesBodyForceFctPt Body_force_fct_pt;
00132  
00133  /// Pointer to volumetric source function
00134  NavierStokesSourceFctPt Source_fct_pt;
00135 
00136  /// \short Boolean flag to indicate if ALE formulation is disabled when
00137  /// time-derivatives are computed. Only set to true if you're sure
00138  /// that the mesh is stationary.
00139  bool ALE_is_disabled;
00140  
00141  /// \short Access function for the local equation number information for
00142  /// the pressure.
00143  /// p_local_eqn[n] = local equation number or < 0 if pinned
00144  virtual int p_local_eqn(const unsigned &n)=0;
00145 
00146  /// \short Compute the shape functions and derivatives 
00147  /// w.r.t. global coords at local coordinate s.
00148  /// Return Jacobian of mapping between local and global coordinates.
00149  virtual double dshape_and_dtest_eulerian_nst(const Vector<double> &s, 
00150                                               Shape &psi, 
00151                                               DShape &dpsidx, Shape &test, 
00152                                               DShape &dtestdx) const=0;
00153 
00154  /// \short Compute the shape functions and derivatives 
00155  /// w.r.t. global coords at ipt-th integration point
00156  /// Return Jacobian of mapping between local and global coordinates.
00157  virtual double dshape_and_dtest_eulerian_at_knot_nst(const unsigned &ipt, 
00158                                                       Shape &psi, 
00159                                                       DShape &dpsidx, 
00160                                                       Shape &test, 
00161                                                       DShape &dtestdx) const=0;
00162 
00163  /// Compute the pressure shape functions at local coordinate s
00164  virtual void pshape_nst(const Vector<double> &s, Shape &psi) const=0;
00165 
00166  /// \short Compute the pressure shape and test functions 
00167  /// at local coordinate s
00168  virtual void pshape_nst(const Vector<double> &s, Shape &psi, 
00169                          Shape &test) const=0;
00170 
00171 
00172  /// \short Calculate the body force at a given time and local and/or 
00173  /// Eulerian position. This function is virtual so that it can be 
00174  /// overloaded in multi-physics elements where the body force might
00175  /// depend on another variable.
00176  virtual void get_body_force_nst(const double& time,
00177                                  const unsigned& ipt,
00178                                  const Vector<double> &s,
00179                                  const Vector<double> &x, 
00180                                  Vector<double> &result)
00181   {
00182    //If the function pointer is zero return zero
00183    if(Body_force_fct_pt == 0)
00184     {
00185      //Loop over dimensions and set body forces to zero
00186      for(unsigned i=0;i<DIM;i++) {result[i] = 0.0;}
00187     }
00188    //Otherwise call the function
00189    else
00190     {
00191      (*Body_force_fct_pt)(time,x,result);
00192     }
00193   }
00194 
00195  /// Get gradient of body force term at (Eulerian) position x. This function is
00196  /// virtual to allow overloading in multi-physics problems where
00197  /// the strength of the source function might be determined by
00198  /// another system of equations. Computed via function pointer 
00199  /// (if set) or by finite differencing (default)
00200  inline virtual void get_body_force_gradient_nst(
00201   const double& time,
00202   const unsigned& ipt,
00203   const Vector<double>& s,
00204   const Vector<double>& x, 
00205   DenseMatrix<double>& d_body_force_dx)
00206   {
00207 // hierher: Implement function pointer version
00208 /*    //If no gradient function has been set, FD it */
00209 /*    if(Body_force_fct_gradient_pt==0) */
00210     {
00211      // Reference value
00212      Vector<double> body_force(DIM,0.0);
00213      get_body_force_nst(time,ipt,s,x,body_force);
00214 
00215      // FD it
00216      double eps_fd=GeneralisedElement::Default_fd_jacobian_step;
00217      Vector<double> body_force_pls(DIM,0.0);
00218      Vector<double> x_pls(x);
00219      for (unsigned i=0;i<DIM;i++)
00220       {
00221        x_pls[i]+=eps_fd;
00222        get_body_force_nst(time,ipt,s,x_pls,body_force_pls);
00223        for (unsigned j=0;j<DIM;j++)
00224         {
00225          d_body_force_dx(j,i)=(body_force_pls[j]-body_force[j])/eps_fd;
00226         }
00227        x_pls[i]=x[i];
00228       }
00229     }
00230 /*    else */
00231 /*     { */
00232 /*      // Get gradient */
00233 /*      (*Source_fct_gradient_pt)(time,x,gradient); */
00234 /*     } */
00235   }
00236 
00237 
00238 
00239  /// \short Calculate the source fct at given time and
00240  /// Eulerian position 
00241  virtual double get_source_nst(const double& time, const unsigned& ipt,
00242                                const Vector<double> &x)
00243   {
00244    //If the function pointer is zero return zero
00245    if (Source_fct_pt == 0) {return 0;}
00246    //Otherwise call the function
00247    else {return (*Source_fct_pt)(time,x);}
00248   }
00249  
00250 
00251  /// Get gradient of source term at (Eulerian) position x. This function is
00252  /// virtual to allow overloading in multi-physics problems where
00253  /// the strength of the source function might be determined by
00254  /// another system of equations. Computed via function pointer 
00255  /// (if set) or by finite differencing (default)
00256  inline virtual void get_source_gradient_nst(
00257   const double& time,
00258   const unsigned& ipt,
00259   const Vector<double>& x, 
00260   Vector<double>& gradient)
00261   {
00262 // hierher: Implement function pointer version
00263 /*    //If no gradient function has been set, FD it */
00264 /*    if(Source_fct_gradient_pt==0) */
00265     {
00266      // Reference value
00267      double source=get_source_nst(time,ipt,x);
00268 
00269      // FD it
00270      double eps_fd=GeneralisedElement::Default_fd_jacobian_step;
00271      double source_pls=0.0;
00272      Vector<double> x_pls(x);
00273      for (unsigned i=0;i<DIM;i++)
00274       {
00275        x_pls[i]+=eps_fd;
00276        source_pls=get_source_nst(time,ipt,x_pls);
00277        gradient[i]=(source_pls-source)/eps_fd;
00278        x_pls[i]=x[i];
00279       }
00280     }
00281 /*    else */
00282 /*     { */
00283 /*      // Get gradient */
00284 /*      (*Source_fct_gradient_pt)(time,x,gradient); */
00285 /*     } */
00286   }
00287 
00288 
00289 
00290 
00291 
00292 
00293 
00294 
00295  ///\short Compute the residuals for the Navier--Stokes equations; 
00296  /// flag=1(or 0): do (or don't) compute the Jacobian as well. 
00297  virtual void fill_in_generic_residual_contribution_nst(
00298   Vector<double> &residuals, DenseMatrix<double> &jacobian, 
00299   DenseMatrix<double> &mass_matrix, unsigned flag);
00300 
00301     
00302 public:
00303 
00304  /// \short Constructor: NULL the body force and source function
00305  /// and make sure the ALE terms are included by default.
00306  NavierStokesEquations() : Body_force_fct_pt(0), Source_fct_pt(0),
00307   ALE_is_disabled(false) 
00308   {
00309    //Set all the Physical parameter pointers to the default value zero
00310    Re_pt = &Default_Physical_Constant_Value;
00311    ReSt_pt = &Default_Physical_Constant_Value;
00312    ReInvFr_pt = &Default_Physical_Constant_Value;
00313    G_pt = &Default_Gravity_vector;
00314    //Set the Physical ratios to the default value of 1
00315    Viscosity_Ratio_pt = &Default_Physical_Ratio_Value;
00316    Density_Ratio_pt = &Default_Physical_Ratio_Value;
00317   }
00318 
00319  /// Vector to decide whether the stress-divergence form is used or not
00320  // N.B. This needs to be public so that the intel compiler gets things correct
00321  // somehow the access function messes things up when going to refineable
00322  // navier--stokes
00323  static Vector<double> Gamma;
00324 
00325  //Access functions for the physical constants
00326 
00327  /// Reynolds number
00328  const double &re() const {return *Re_pt;}
00329 
00330  /// Product of Reynolds and Strouhal number (=Womersley number)
00331  const double &re_st() const {return *ReSt_pt;}
00332 
00333  /// Pointer to Reynolds number
00334  double* &re_pt() {return Re_pt;}
00335 
00336  /// Pointer to product of Reynolds and Strouhal number (=Womersley number)
00337  double* &re_st_pt() {return ReSt_pt;}
00338 
00339  /// \short Viscosity ratio for element: Element's viscosity relative to the 
00340  /// viscosity used in the definition of the Reynolds number
00341  const double &viscosity_ratio() const {return *Viscosity_Ratio_pt;}
00342 
00343  /// Pointer to Viscosity Ratio
00344  double* &viscosity_ratio_pt() {return Viscosity_Ratio_pt;}
00345 
00346  /// \short Density ratio for element: Element's density relative to the 
00347  ///  viscosity used in the definition of the Reynolds number
00348  const double &density_ratio() const {return *Density_Ratio_pt;}
00349 
00350  /// Pointer to Density ratio
00351  double* &density_ratio_pt() {return Density_Ratio_pt;}
00352 
00353  /// Global inverse Froude number
00354  const double &re_invfr() const {return *ReInvFr_pt;}
00355 
00356  /// Pointer to global inverse Froude number
00357  double* &re_invfr_pt() {return ReInvFr_pt;}
00358  
00359  /// Vector of gravitational components
00360  const Vector<double> &g() const {return *G_pt;}
00361 
00362  /// Pointer to Vector of gravitational components
00363  Vector<double>* &g_pt() {return G_pt;}
00364 
00365  /// Access function for the body-force pointer
00366  NavierStokesBodyForceFctPt& body_force_fct_pt() 
00367   {return Body_force_fct_pt;}
00368 
00369  /// Access function for the body-force pointer. Const version
00370  NavierStokesBodyForceFctPt body_force_fct_pt() const
00371   {return Body_force_fct_pt;}
00372  
00373  ///Access function for the source-function pointer
00374  NavierStokesSourceFctPt& source_fct_pt() {return Source_fct_pt;}
00375 
00376  ///Access function for the source-function pointer. Const version
00377  NavierStokesSourceFctPt source_fct_pt() const {return Source_fct_pt;}
00378  
00379  ///Function to return number of pressure degrees of freedom
00380  virtual unsigned npres_nst() const=0;
00381  
00382  /// \short Velocity i at local node n. Uses suitably interpolated value 
00383  /// for hanging nodes. The use of u_index_nst() permits the use of this
00384  /// element as the basis for multi-physics elements. The default
00385  /// is to assume that the i-th velocity component is stored at the
00386  /// i-th location of the node
00387  double u_nst(const unsigned &n, const unsigned &i) const
00388   {return nodal_value(n,u_index_nst(i));}
00389 
00390  /// \short Velocity i at local node n at timestep t (t=0: present; 
00391  /// t>0: previous). Uses suitably interpolated value for hanging nodes.
00392  double u_nst(const unsigned &t, const unsigned &n, 
00393               const unsigned &i) const
00394   {return nodal_value(t,n,u_index_nst(i));}
00395  
00396   /// \short Return the index at which the i-th unknown velocity component
00397  /// is stored. The default value, i, is appropriate for single-physics
00398  /// problems.
00399  /// In derived multi-physics elements, this function should be overloaded
00400  /// to reflect the chosen storage scheme. Note that these equations require
00401  /// that the unknowns are always stored at the same indices at each node.
00402  virtual inline unsigned u_index_nst(const unsigned &i) const {return i;}
00403 
00404 
00405  /// \short i-th component of du/dt at local node n. 
00406  /// Uses suitably interpolated value for hanging nodes.
00407  double du_dt_nst(const unsigned &n, const unsigned &i) const
00408   {
00409    // Get the data's timestepper
00410    TimeStepper* time_stepper_pt = this->node_pt(n)->time_stepper_pt();
00411 
00412    //Initialise dudt
00413    double dudt=0.0;
00414 
00415    //Loop over the timesteps, if there is a non Steady timestepper
00416    if (!time_stepper_pt->is_steady())
00417     {
00418      //Find the index at which the dof is stored
00419      const unsigned u_nodal_index = this->u_index_nst(i);
00420      
00421      // Number of timsteps (past & present)
00422      const unsigned n_time = time_stepper_pt->ntstorage();
00423      // Loop over the timesteps
00424      for(unsigned t=0;t<n_time;t++)
00425       {
00426        dudt+=time_stepper_pt->weight(1,t)*nodal_value(t,n,u_nodal_index);
00427       }
00428     }
00429    
00430    return dudt;
00431   }
00432 
00433  /// \short Disable ALE, i.e. assert the mesh is not moving -- you do this
00434  /// at your own risk!
00435  void disable_ALE()
00436   {
00437    ALE_is_disabled=true;
00438   }
00439 
00440  /// \short (Re-)enable ALE, i.e. take possible mesh motion into account
00441  /// when evaluating the time-derivative. Note: By default, ALE is
00442  /// enabled, at the expense of possibly creating unnecessary work
00443  /// in problems where the mesh is, in fact, stationary.
00444  void enable_ALE()
00445   {
00446    ALE_is_disabled=false;
00447   }
00448 
00449  /// \short Pressure at local pressure "node" n_p
00450  /// Uses suitably interpolated value for hanging nodes.
00451  virtual double p_nst(const unsigned &n_p)const=0; 
00452 
00453  /// Pin p_dof-th pressure dof and set it to value specified by p_value.
00454  virtual void fix_pressure(const unsigned &p_dof, const double &p_value)=0;
00455 
00456  /// \short Return the index at which the pressure is stored if it is
00457  /// stored at the nodes. If not stored at the nodes this will return 
00458  /// a negative number.
00459  virtual int p_nodal_index_nst() const {return Pressure_not_stored_at_node;}
00460  
00461  /// Integral of pressure over element
00462  double pressure_integral() const;
00463 
00464  /// \short Return integral of dissipation over element
00465  double dissipation() const;
00466  
00467  /// \short Return dissipation at local coordinate s
00468  double dissipation(const Vector<double>& s) const;
00469 
00470  /// \short Compute the vorticity vector at local coordinate s
00471  void get_vorticity(const Vector<double>& s, Vector<double>& vorticity) const;
00472 
00473  /// \short Get integral of kinetic energy over element
00474  double kin_energy() const;
00475 
00476  /// \short Get integral of time derivative of kinetic energy over element
00477  double d_kin_energy_dt() const;
00478 
00479  /// Strain-rate tensor: 1/2 (du_i/dx_j + du_j/dx_i)
00480  void strain_rate(const Vector<double>& s, 
00481                   DenseMatrix<double>& strain_rate) const;
00482  
00483  /// \short Compute traction (on the viscous scale) exerted onto 
00484  /// the fluid at local coordinate s. N has to be outer unit normal
00485  /// to the fluid. 
00486  void get_traction(const Vector<double>& s, const Vector<double>& N, 
00487                    Vector<double>& traction);
00488 
00489  /// \short This implements a pure virtual function defined
00490  /// in the FSIFluidElement class. The function computes
00491  /// the traction (on the viscous scale), at the element's local 
00492  /// coordinate s, that the fluid element exerts onto an adjacent
00493  /// solid element. The number of arguments is imposed by
00494  /// the interface defined in the FSIFluidElement -- only the 
00495  /// unit normal N (pointing into the fluid!) is actually used
00496  /// in the computation. 
00497  void get_load(const Vector<double> &s, 
00498                const Vector<double> &N,
00499                Vector<double> &load)
00500   {
00501    // Note: get_traction() computes the traction onto the fluid
00502    // if N is the outer unit normal onto the fluid; here we're
00503    // exepcting N to point into the fluid so we're getting the
00504    // traction onto the adjacent wall instead!
00505    get_traction(s,N,load);
00506   }
00507  
00508  /// Compute the diagonal of the velocity mass matrix
00509  void get_velocity_mass_matrix_diagonal(Vector<double> &mass_diag);
00510 
00511  /// \short Output function: x,y,[z],u,v,[w],p
00512  /// in tecplot format. Default number of plot points
00513  void output(std::ostream &outfile)
00514   {
00515    unsigned nplot=5;
00516    output(outfile,nplot);
00517   }
00518 
00519  /// \short Output function: x,y,[z],u,v,[w],p
00520  /// in tecplot format. nplot points in each coordinate direction
00521  void output(std::ostream &outfile, const unsigned &nplot);
00522 
00523  /// \short C-style output function: x,y,[z],u,v,[w],p
00524  /// in tecplot format. Default number of plot points
00525  void output(FILE* file_pt)
00526   {
00527    unsigned nplot=5;
00528    output(file_pt,nplot);
00529   }
00530 
00531  /// \short C-style output function: x,y,[z],u,v,[w],p
00532  /// in tecplot format. nplot points in each coordinate direction
00533  void output(FILE* file_pt, const unsigned &nplot);
00534 
00535  /// \short Full output function: 
00536  /// x,y,[z],u,v,[w],p,du/dt,dv/dt,[dw/dt],dissipation
00537  /// in tecplot format. Default number of plot points
00538  void full_output(std::ostream &outfile) 
00539   {
00540    unsigned nplot=5;
00541    full_output(outfile,nplot);
00542   }
00543 
00544  /// \short Full output function: 
00545  /// x,y,[z],u,v,[w],p,du/dt,dv/dt,[dw/dt],dissipation
00546  /// in tecplot format. nplot points in each coordinate direction
00547  void full_output(std::ostream &outfile, const unsigned &nplot);
00548 
00549 
00550  /// \short Output function: x,y,[z],u,v,[w] in tecplot format.
00551  /// nplot points in each coordinate direction at timestep t
00552  /// (t=0: present; t>0: previous timestep)
00553  void output_veloc(std::ostream &outfile, const unsigned &nplot, 
00554                    const unsigned& t);
00555 
00556 
00557  /// \short Output function: x,y,[z], [omega_x,omega_y,[and/or omega_z]] 
00558  /// in tecplot format. nplot points in each coordinate direction
00559  void output_vorticity(std::ostream &outfile, 
00560                        const unsigned &nplot);
00561 
00562  /// \short Output exact solution specified via function pointer
00563  /// at a given number of plot points. Function prints as
00564  /// many components as are returned in solution Vector
00565  void output_fct(std::ostream &outfile, const unsigned &nplot, 
00566                  FiniteElement::SteadyExactSolutionFctPt exact_soln_pt);
00567 
00568  /// \short Output exact solution specified via function pointer
00569  /// at a given time and at a given number of plot points.
00570  /// Function prints as many components as are returned in solution Vector.
00571  void output_fct(std::ostream &outfile, const unsigned &nplot, 
00572                  const double& time,
00573                  FiniteElement::UnsteadyExactSolutionFctPt exact_soln_pt);
00574 
00575  /// \short Validate against exact solution at given time
00576  /// Solution is provided via function pointer.
00577  /// Plot at a given number of plot points and compute L2 error
00578  /// and L2 norm of velocity solution over element
00579  void compute_error(std::ostream &outfile,
00580                     FiniteElement::UnsteadyExactSolutionFctPt exact_soln_pt,
00581                     const double& time,
00582                     double& error, double& norm);
00583 
00584  /// \short Validate against exact solution.
00585  /// Solution is provided via function pointer.
00586  /// Plot at a given number of plot points and compute L2 error
00587  /// and L2 norm of velocity solution over element
00588  void compute_error(std::ostream &outfile,
00589                     FiniteElement::SteadyExactSolutionFctPt exact_soln_pt,
00590                     double& error, double& norm);
00591 
00592  /// Compute the element's residual Vector
00593  void fill_in_contribution_to_residuals(Vector<double> &residuals)
00594   {
00595    //Call the generic residuals function with flag set to 0
00596    //and using a dummy matrix argument
00597    fill_in_generic_residual_contribution_nst(
00598     residuals,GeneralisedElement::Dummy_matrix,
00599     GeneralisedElement::Dummy_matrix,0);
00600   }
00601 
00602  ///\short Compute the element's residual Vector and the jacobian matrix
00603  /// Virtual function can be overloaded by hanging-node version
00604  void fill_in_contribution_to_jacobian(Vector<double> &residuals,
00605                                    DenseMatrix<double> &jacobian)
00606   {
00607    //Call the generic routine with the flag set to 1
00608    fill_in_generic_residual_contribution_nst(residuals,jacobian,
00609                                          GeneralisedElement::Dummy_matrix,1);
00610   }
00611 
00612  /// Add the element's contribution to its residuals vector,
00613  /// jacobian matrix and mass matrix
00614  void fill_in_contribution_to_jacobian_and_mass_matrix(
00615   Vector<double> &residuals, DenseMatrix<double> &jacobian, 
00616   DenseMatrix<double> &mass_matrix)
00617   {
00618    //Call the generic routine with the flag set to 2
00619    fill_in_generic_residual_contribution_nst(residuals,jacobian,mass_matrix,2);
00620   }
00621 
00622 
00623  /// \short Compute derivatives of elemental residual vector with respect
00624  /// to nodal coordinates. Overwrites default implementation in 
00625  /// FiniteElement base class.
00626  /// dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}
00627  virtual void get_dresidual_dnodal_coordinates(RankThreeTensor<double>&
00628                                                dresidual_dnodal_coordinates);
00629   
00630  /// Compute vector of FE interpolated velocity u at local coordinate s
00631  void interpolated_u_nst(const Vector<double> &s, Vector<double>& veloc) const
00632   {
00633    //Find number of nodes
00634    unsigned n_node = nnode();
00635    //Local shape function
00636    Shape psi(n_node);
00637    //Find values of shape function
00638    shape(s,psi);
00639    
00640    for (unsigned i=0;i<DIM;i++)
00641     {
00642      //Index at which the nodal value is stored
00643      unsigned u_nodal_index = u_index_nst(i);
00644      //Initialise value of u
00645      veloc[i] = 0.0;
00646      //Loop over the local nodes and sum
00647      for(unsigned l=0;l<n_node;l++) 
00648       {
00649        veloc[i] += nodal_value(l,u_nodal_index)*psi[l];
00650       }
00651     }
00652   }
00653 
00654  /// Return FE interpolated velocity u[i] at local coordinate s
00655  double interpolated_u_nst(const Vector<double> &s, const unsigned &i) const
00656   {
00657    //Find number of nodes
00658    unsigned n_node = nnode();
00659    //Local shape function
00660    Shape psi(n_node);
00661    //Find values of shape function
00662    shape(s,psi);
00663    
00664    //Get nodal index at which i-th velocity is stored
00665    unsigned u_nodal_index = u_index_nst(i);
00666 
00667    //Initialise value of u
00668    double interpolated_u = 0.0;
00669    //Loop over the local nodes and sum
00670    for(unsigned l=0;l<n_node;l++) 
00671     {
00672      interpolated_u += nodal_value(l,u_nodal_index)*psi[l];
00673     }
00674    
00675    return(interpolated_u);
00676   }
00677 
00678  /// \short Compute the derivatives of the i-th component of 
00679  /// velocity at point s with respect
00680  /// to all data that can affect its value. In addition, return the global
00681  /// equation numbers corresponding to the data. The function is virtual 
00682  /// so that it can be overloaded in the refineable version
00683  virtual void dinterpolated_u_nst_ddata(const Vector<double> &s,
00684                                         const unsigned &i,
00685                                         Vector<double> &du_ddata,
00686                                         Vector<unsigned> &global_eqn_number)
00687   {
00688    //Find number of nodes
00689    unsigned n_node = nnode();
00690    //Local shape function
00691    Shape psi(n_node);
00692    //Find values of shape function
00693    shape(s,psi);
00694 
00695    //Find the index at which the velocity component is stored
00696    const unsigned u_nodal_index = u_index_nst(i);
00697    
00698    //Find the number of dofs associated with interpolated u
00699    unsigned n_u_dof=0;
00700    for(unsigned l=0;l<n_node;l++)
00701     {
00702      int global_eqn = this->node_pt(l)->eqn_number(u_nodal_index);
00703      //If it's positive add to the count
00704      if(global_eqn >= 0) {++n_u_dof;}
00705     }
00706    
00707    //Now resize the storage schemes
00708    du_ddata.resize(n_u_dof,0.0);
00709    global_eqn_number.resize(n_u_dof,0);
00710    
00711    //Loop over th nodes again and set the derivatives
00712    unsigned count=0;
00713    //Loop over the local nodes and sum
00714    for(unsigned l=0;l<n_node;l++) 
00715     {
00716      //Get the global equation number
00717      int global_eqn = this->node_pt(l)->eqn_number(u_nodal_index);
00718      if (global_eqn >= 0)
00719       {
00720        //Set the global equation number
00721        global_eqn_number[count] = global_eqn;
00722        //Set the derivative with respect to the unknown
00723        du_ddata[count] = psi[l];
00724        //Increase the counter
00725        ++count;
00726       }
00727     }
00728   }
00729 
00730 
00731  /// Return FE interpolated pressure at local coordinate s
00732  double interpolated_p_nst(const Vector<double> &s) const
00733   {
00734    //Find number of nodes
00735    unsigned n_pres = npres_nst();
00736    //Local shape function
00737    Shape psi(n_pres);
00738    //Find values of shape function
00739    pshape_nst(s,psi);
00740    
00741    //Initialise value of p
00742    double interpolated_p = 0.0;
00743    //Loop over the local nodes and sum
00744    for(unsigned l=0;l<n_pres;l++) 
00745     {
00746      interpolated_p += p_nst(l)*psi[l];
00747     }
00748    
00749    return(interpolated_p);
00750   }
00751 
00752 }; 
00753 
00754 //////////////////////////////////////////////////////////////////////////////
00755 //////////////////////////////////////////////////////////////////////////////
00756 //////////////////////////////////////////////////////////////////////////////
00757 
00758 
00759 //==========================================================================
00760 /// Crouzeix_Raviart elements are Navier--Stokes elements with quadratic
00761 /// interpolation for velocities and positions, but a discontinuous linear
00762 /// pressure interpolation. They can be used within oomph-lib's
00763 /// block preconditioning framework.
00764 //==========================================================================
00765 template <unsigned DIM>
00766 class QCrouzeixRaviartElement : public virtual QElement<DIM,3>, 
00767  public virtual NavierStokesEquations<DIM>
00768 {
00769   private:
00770 
00771  /// Static array of ints to hold required number of variables at nodes
00772  static const unsigned Initial_Nvalue[];
00773  
00774   protected:
00775 
00776  /// Internal index that indicates at which internal data the pressure
00777  /// is stored
00778  unsigned P_nst_internal_index;
00779  
00780  
00781  /// \short Velocity shape and test functions and their derivs 
00782  /// w.r.t. to global coords  at local coordinate s (taken from geometry)
00783  ///Return Jacobian of mapping between local and global coordinates.
00784  inline double dshape_and_dtest_eulerian_nst(const Vector<double> &s, 
00785                                              Shape &psi, 
00786                                              DShape &dpsidx, Shape &test, 
00787                                              DShape &dtestdx) const;
00788 
00789  /// \short Velocity shape and test functions and their derivs 
00790  /// w.r.t. to global coords at ipt-th integation point (taken from geometry)
00791  ///Return Jacobian of mapping between local and global coordinates.
00792  inline double dshape_and_dtest_eulerian_at_knot_nst(const unsigned &ipt, 
00793                                                      Shape &psi, 
00794                                                      DShape &dpsidx, 
00795                                                      Shape &test, 
00796                                                      DShape &dtestdx) const;
00797 
00798  /// Pressure shape functions at local coordinate s
00799  inline void pshape_nst(const Vector<double> &s, Shape &psi) const;
00800  
00801  /// Pressure shape and test functions at local coordinte s
00802  inline void pshape_nst(const Vector<double> &s, Shape &psi, 
00803                         Shape &test) const;
00804  
00805  /// Return the local equation numbers for the pressure values.
00806  inline int p_local_eqn(const unsigned &n)
00807   {return this->internal_local_eqn(P_nst_internal_index,n);}
00808 
00809 public:
00810 
00811  /// Constructor, there are DIM+1 internal values (for the pressure)
00812  QCrouzeixRaviartElement() : QElement<DIM,3>(), NavierStokesEquations<DIM>()
00813   {
00814    //Allocate and add one Internal data object that stored DIM+1 pressure
00815    //values;
00816    P_nst_internal_index = this->add_internal_data(new Data(DIM+1));
00817   }
00818  
00819  /// \short Number of values (pinned or dofs) required at local node n. 
00820  virtual unsigned required_nvalue(const unsigned &n) const;
00821 
00822  
00823  /// \short Return the i-th pressure value
00824  /// (Discontinous pressure interpolation -- no need to cater for hanging 
00825  /// nodes). 
00826  double p_nst(const unsigned &i) const
00827   {return this->internal_data_pt(P_nst_internal_index)->value(i);}
00828 
00829  /// Return number of pressure values
00830  unsigned npres_nst() const {return DIM+1;} 
00831  
00832  /// Pin p_dof-th pressure dof and set it to value specified by p_value.
00833  void fix_pressure(const unsigned &p_dof, const double &p_value)
00834   {
00835    this->internal_data_pt(P_nst_internal_index)->pin(p_dof);
00836    this->internal_data_pt(P_nst_internal_index)->set_value(p_dof,p_value);
00837   }
00838 
00839  /// \short  Add to the set \c paired_load_data pairs containing
00840  /// - the pointer to a Data object
00841  /// and
00842  /// - the index of the value in that Data object
00843  /// .
00844  /// for all values (pressures, velocities) that affect the
00845  /// load computed in the \c get_load(...) function.
00846  void identify_load_data(
00847   std::set<std::pair<Data*,unsigned> > &paired_load_data);
00848 
00849  /// \short  Add to the set \c paired_pressure_data pairs 
00850  /// containing
00851  /// - the pointer to a Data object
00852  /// and
00853  /// - the index of the value in that Data object
00854  /// .
00855  /// for all pressure values that affect the
00856  /// load computed in the \c get_load(...) function.
00857  void identify_pressure_data(
00858   std::set<std::pair<Data*,unsigned> > &paired_pressure_data);
00859 
00860  /// Redirect output to NavierStokesEquations output
00861  void output(std::ostream &outfile)
00862   {NavierStokesEquations<DIM>::output(outfile);}
00863 
00864  /// Redirect output to NavierStokesEquations output
00865  void output(std::ostream &outfile, const unsigned &nplot)
00866   {NavierStokesEquations<DIM>::output(outfile,nplot);}
00867 
00868 
00869  /// Redirect output to NavierStokesEquations output
00870  void output(FILE* file_pt) {NavierStokesEquations<DIM>::output(file_pt);}
00871 
00872  /// Redirect output to NavierStokesEquations output
00873  void output(FILE* file_pt, const unsigned &nplot)
00874   {NavierStokesEquations<DIM>::output(file_pt,nplot);}
00875 
00876 
00877  /// \short Full output function: 
00878  /// x,y,[z],u,v,[w],p,du/dt,dv/dt,[dw/dt],dissipation
00879  /// in tecplot format. Default number of plot points
00880  void full_output(std::ostream &outfile)
00881   {NavierStokesEquations<DIM>::full_output(outfile);}
00882 
00883  /// \short Full output function: 
00884  /// x,y,[z],u,v,[w],p,du/dt,dv/dt,[dw/dt],dissipation
00885  /// in tecplot format. nplot points in each coordinate direction
00886  void full_output(std::ostream &outfile, const unsigned &nplot)
00887   {NavierStokesEquations<DIM>::full_output(outfile,nplot);}
00888 
00889 
00890  /// \short The number of "blocks" that degrees of freedom in this element
00891  /// are sub-divided into: Velocity and pressure.
00892  unsigned ndof_types()
00893   {
00894    return DIM+1;
00895   }
00896  
00897  /// \short Create a list of pairs for all unknowns in this element,
00898  /// so that the first entry in each pair contains the global equation
00899  /// number of the unknown, while the second one contains the number
00900  /// of the "block" that this unknown is associated with.
00901  /// (Function can obviously only be called if the equation numbering
00902  /// scheme has been set up.) Velocity=0; Pressure=1
00903  void get_dof_numbers_for_unknowns(
00904   std::list<std::pair<unsigned long,unsigned> >& dof_lookup_list);
00905 
00906 };
00907 
00908 //Inline functions
00909 
00910 //=======================================================================
00911 /// 2D
00912 /// Derivatives of the shape functions and test functions w.r.t. to global
00913 /// (Eulerian) coordinates. Return Jacobian of mapping between
00914 /// local and global coordinates.
00915 //=======================================================================
00916 template<>
00917 inline double QCrouzeixRaviartElement<2>::dshape_and_dtest_eulerian_nst(
00918  const Vector<double> &s, Shape &psi, 
00919  DShape &dpsidx, Shape &test, 
00920  DShape &dtestdx) const
00921 {
00922  //Call the geometrical shape functions and derivatives  
00923  double J = this->dshape_eulerian(s,psi,dpsidx);
00924  //Loop over the test functions and derivatives and set them equal to the
00925  //shape functions
00926  for(unsigned i=0;i<9;i++)
00927   {
00928    test[i] = psi[i]; 
00929    dtestdx(i,0) = dpsidx(i,0);
00930    dtestdx(i,1) = dpsidx(i,1);
00931   }
00932  //Return the jacobian
00933  return J;
00934 }
00935 
00936 
00937 //=======================================================================
00938 /// 3D
00939 /// Derivatives of the shape functions and test functions w.r.t. to global
00940 /// (Eulerian) coordinates. Return Jacobian of mapping between
00941 /// local and global coordinates.
00942 //=======================================================================
00943 template<>
00944 inline double QCrouzeixRaviartElement<3>::dshape_and_dtest_eulerian_nst(
00945                                   const Vector<double> &s, Shape &psi, 
00946                                   DShape &dpsidx, Shape &test, 
00947                                   DShape &dtestdx) const
00948 {
00949  //Call the geometrical shape functions and derivatives  
00950  double J = this->dshape_eulerian(s,psi,dpsidx);
00951  //Loop over the test functions and derivatives and set them equal to the
00952  //shape functions
00953  for(unsigned i=0;i<27;i++)
00954   {
00955    test[i] = psi[i]; 
00956    dtestdx(i,0) = dpsidx(i,0);
00957    dtestdx(i,1) = dpsidx(i,1);
00958    dtestdx(i,2) = dpsidx(i,2);
00959   }
00960  //Return the jacobian
00961  return J;
00962 }
00963 
00964 
00965 
00966 //=======================================================================
00967 /// 2D
00968 /// Derivatives of the shape functions and test functions w.r.t. to global
00969 /// (Eulerian) coordinates. Return Jacobian of mapping between
00970 /// local and global coordinates.
00971 //=======================================================================
00972 template<>
00973 inline double QCrouzeixRaviartElement<2>::dshape_and_dtest_eulerian_at_knot_nst(
00974  const unsigned &ipt, Shape &psi, 
00975  DShape &dpsidx, Shape &test, 
00976  DShape &dtestdx) const
00977 {
00978  //Call the geometrical shape functions and derivatives  
00979  double J = this->dshape_eulerian_at_knot(ipt,psi,dpsidx);
00980  //Loop over the test functions and derivatives and set them equal to the
00981  //shape functions
00982  for(unsigned i=0;i<9;i++)
00983   {
00984    test[i] = psi[i]; 
00985    dtestdx(i,0) = dpsidx(i,0);
00986    dtestdx(i,1) = dpsidx(i,1);
00987   }
00988  //Return the jacobian
00989  return J;
00990 }
00991 
00992 
00993 //=======================================================================
00994 /// 3D
00995 /// Derivatives of the shape functions and test functions w.r.t. to global
00996 /// (Eulerian) coordinates. Return Jacobian of mapping between
00997 /// local and global coordinates.
00998 //=======================================================================
00999 template<>
01000 inline double QCrouzeixRaviartElement<3>::
01001 dshape_and_dtest_eulerian_at_knot_nst(
01002  const unsigned &ipt, Shape &psi, 
01003  DShape &dpsidx, Shape &test, 
01004  DShape &dtestdx) const
01005 {
01006  //Call the geometrical shape functions and derivatives  
01007  double J = this->dshape_eulerian_at_knot(ipt,psi,dpsidx);
01008  //Loop over the test functions and derivatives and set them equal to the
01009  //shape functions
01010  for(unsigned i=0;i<27;i++)
01011   {
01012    test[i] = psi[i]; 
01013    dtestdx(i,0) = dpsidx(i,0);
01014    dtestdx(i,1) = dpsidx(i,1);
01015    dtestdx(i,2) = dpsidx(i,2);
01016   }
01017  //Return the jacobian
01018  return J;
01019 }
01020 
01021 
01022 
01023 //=======================================================================
01024 /// 2D :
01025 /// Pressure shape functions
01026 //=======================================================================
01027 template<>
01028 inline void QCrouzeixRaviartElement<2>::pshape_nst(const Vector<double> &s, 
01029                                                   Shape &psi)
01030 const
01031 {
01032  psi[0] = 1.0;
01033  psi[1] = s[0];
01034  psi[2] = s[1];
01035 }
01036 
01037 ///Define the pressure shape and test functions
01038 template<>
01039 inline void QCrouzeixRaviartElement<2>::pshape_nst(const Vector<double> &s, 
01040                                                   Shape &psi, 
01041 Shape &test) const
01042 {
01043  //Call the pressure shape functions
01044  pshape_nst(s,psi);
01045  //Loop over the test functions and set them equal to the shape functions
01046  for(unsigned i=0;i<3;i++) test[i] = psi[i];
01047 }
01048 
01049 
01050 //=======================================================================
01051 /// 3D :
01052 /// Pressure shape functions
01053 //=======================================================================
01054 template<>
01055 inline void QCrouzeixRaviartElement<3>::pshape_nst(const Vector<double> &s, 
01056                                                   Shape &psi)
01057 const
01058 {
01059  psi[0] = 1.0;
01060  psi[1] = s[0];
01061  psi[2] = s[1];
01062  psi[3] = s[2];
01063 }
01064 
01065 ///Define the pressure shape and test functions
01066 template<>
01067 inline void QCrouzeixRaviartElement<3>::pshape_nst(const Vector<double> &s, 
01068                                                   Shape &psi, 
01069 Shape &test) const
01070 {
01071  //Call the pressure shape functions
01072  pshape_nst(s,psi);
01073  //Loop over the test functions and set them equal to the shape functions
01074  for(unsigned i=0;i<4;i++) test[i] = psi[i];
01075 }
01076 
01077 
01078 //=======================================================================
01079 /// Face geometry of the 2D Crouzeix_Raviart elements
01080 //=======================================================================
01081 template<>
01082 class FaceGeometry<QCrouzeixRaviartElement<2> >: public virtual QElement<1,3>
01083 {
01084   public:
01085  FaceGeometry() : QElement<1,3>() {}
01086 };
01087 
01088 //=======================================================================
01089 /// Face geometry of the 3D Crouzeix_Raviart elements
01090 //=======================================================================
01091 template<>
01092 class FaceGeometry<QCrouzeixRaviartElement<3> >: public virtual QElement<2,3>
01093 {
01094  
01095   public:
01096  FaceGeometry() : QElement<2,3>() {}
01097 };
01098 
01099 //=======================================================================
01100 /// Face geometry of the FaceGeometry of the 2D Crouzeix_Raviart elements
01101 //=======================================================================
01102 template<>
01103 class FaceGeometry<FaceGeometry<QCrouzeixRaviartElement<2> > >: 
01104 public virtual PointElement
01105 {
01106   public:
01107  FaceGeometry() : PointElement() {}
01108 };
01109 
01110 
01111 //=======================================================================
01112 /// Face geometry of the FaceGeometry of the 3D Crouzeix_Raviart elements
01113 //=======================================================================
01114 template<>
01115 class FaceGeometry<FaceGeometry<QCrouzeixRaviartElement<3> > >: 
01116 public virtual QElement<1,3>
01117 {
01118   public:
01119  FaceGeometry() : QElement<1,3>() {}
01120 };
01121 
01122 
01123 
01124 ////////////////////////////////////////////////////////////////////////////
01125 ////////////////////////////////////////////////////////////////////////////
01126 
01127 
01128 //=======================================================================
01129 /// Taylor--Hood elements are Navier--Stokes elements 
01130 /// with quadratic interpolation for velocities and positions and 
01131 /// continous linear pressure interpolation. They can be used
01132 /// within oomph-lib's block-preconditioning framework.
01133 //=======================================================================
01134 template <unsigned DIM>
01135 class QTaylorHoodElement : public virtual QElement<DIM,3>, 
01136  public virtual NavierStokesEquations<DIM>
01137 {
01138   private:
01139  
01140  /// Static array of ints to hold number of variables at node
01141  static const unsigned Initial_Nvalue[];
01142  
01143   protected:
01144 
01145  /// \short Static array of ints to hold conversion from pressure 
01146  /// node numbers to actual node numbers
01147  static const unsigned Pconv[];
01148  
01149  /// \short Velocity shape and test functions and their derivs 
01150  /// w.r.t. to global coords  at local coordinate s (taken from geometry)
01151  /// Return Jacobian of mapping between local and global coordinates.
01152  inline double dshape_and_dtest_eulerian_nst(const Vector<double> &s, 
01153                                              Shape &psi, 
01154                                              DShape &dpsidx, Shape &test, 
01155                                              DShape &dtestdx) const;
01156 
01157  /// \short Velocity shape and test functions and their derivs 
01158  /// w.r.t. to global coords  at local coordinate s (taken from geometry)
01159  /// Return Jacobian of mapping between local and global coordinates.
01160  inline double dshape_and_dtest_eulerian_at_knot_nst(const unsigned &ipt, 
01161                                                      Shape &psi, 
01162                                                      DShape &dpsidx, 
01163                                                      Shape &test, 
01164                                                      DShape &dtestdx) const;
01165 
01166  /// Pressure shape functions at local coordinate s
01167  inline void pshape_nst(const Vector<double> &s, Shape &psi) const;
01168 
01169  /// Pressure shape and test functions at local coordinte s
01170  inline void pshape_nst(const Vector<double> &s, Shape &psi, 
01171                         Shape &test) const;
01172  
01173  /// Return the local equation numbers for the pressure values.
01174  inline int p_local_eqn(const unsigned &n)
01175   {return this->nodal_local_eqn(Pconv[n],p_nodal_index_nst());}
01176 
01177 public:
01178 
01179  /// Constructor, no internal data points
01180  QTaylorHoodElement() : QElement<DIM,3>(),  NavierStokesEquations<DIM>() {}
01181  
01182  /// \short Number of values (pinned or dofs) required at node n. Can
01183  /// be overwritten for hanging node version
01184  inline virtual unsigned required_nvalue(const unsigned &n) const 
01185   {return Initial_Nvalue[n];}
01186 
01187  /// \short Set the value at which the pressure is stored in the nodes
01188  int p_nodal_index_nst() const {return static_cast<int>(DIM);}
01189 
01190  /// \short Access function for the pressure values at local pressure 
01191  /// node n_p (const version)
01192  double p_nst(const unsigned &n_p) const
01193   {return this->nodal_value(Pconv[n_p],this->p_nodal_index_nst());}
01194  
01195  /// Return number of pressure values
01196  unsigned npres_nst() const 
01197   {return static_cast<unsigned>(pow(2.0,static_cast<int>(DIM)));}
01198 
01199  /// Pin p_dof-th pressure dof and set it to value specified by p_value.
01200  void fix_pressure(const unsigned &p_dof, const double &p_value)
01201   {
01202    this->node_pt(Pconv[p_dof])->pin(this->p_nodal_index_nst());
01203    this->node_pt(Pconv[p_dof])->set_value(this->p_nodal_index_nst(),p_value);
01204   }
01205 
01206 
01207  /// \short  Add to the set \c paired_load_data pairs containing
01208  /// - the pointer to a Data object
01209  /// and
01210  /// - the index of the value in that Data object
01211  /// .
01212  /// for all values (pressures, velocities) that affect the
01213  /// load computed in the \c get_load(...) function.
01214  void identify_load_data(
01215   std::set<std::pair<Data*,unsigned> > &paired_load_data);
01216 
01217 
01218  /// \short  Add to the set \c paired_pressure_data pairs 
01219  /// containing
01220  /// - the pointer to a Data object
01221  /// and
01222  /// - the index of the value in that Data object
01223  /// .
01224  /// for all pressure values that affect the
01225  /// load computed in the \c get_load(...) function.
01226  void identify_pressure_data(
01227   std::set<std::pair<Data*,unsigned> > &paired_pressure_data);
01228 
01229 
01230  /// Redirect output to NavierStokesEquations output
01231  void output(std::ostream &outfile) 
01232   {NavierStokesEquations<DIM>::output(outfile);}
01233 
01234  /// Redirect output to NavierStokesEquations output
01235  void output(std::ostream &outfile, const unsigned &nplot)
01236   {NavierStokesEquations<DIM>::output(outfile,nplot);}
01237 
01238  /// Redirect output to NavierStokesEquations output
01239  void output(FILE* file_pt) {NavierStokesEquations<DIM>::output(file_pt);}
01240 
01241  /// Redirect output to NavierStokesEquations output
01242  void output(FILE* file_pt, const unsigned &nplot)
01243   {NavierStokesEquations<DIM>::output(file_pt,nplot);}
01244 
01245  
01246  /// \short Returns the number of "blocks" that degrees of freedom
01247  /// in this element are sub-divided into: Velocity and pressure.
01248  unsigned ndof_types()
01249   {
01250    return DIM+1;
01251   }
01252  
01253  /// \short Create a list of pairs for all unknowns in this element,
01254  /// so that the first entry in each pair contains the global equation
01255  /// number of the unknown, while the second one contains the number
01256  /// of the "block" that this unknown is associated with.
01257  /// (Function can obviously only be called if the equation numbering
01258  /// scheme has been set up.) Velocity=0; Pressure=1
01259  void get_dof_numbers_for_unknowns(
01260   std::list<std::pair<unsigned long, unsigned> >& block_lookup_list);
01261  
01262  
01263 };
01264 
01265 //Inline functions
01266 
01267 //==========================================================================
01268 /// 2D : 
01269 /// Derivatives of the shape functions and test functions w.r.t to
01270 /// global (Eulerian) coordinates. Return Jacobian of mapping between
01271 /// local and global coordinates.
01272 //==========================================================================
01273 template<>
01274 inline double QTaylorHoodElement<2>::dshape_and_dtest_eulerian_nst(
01275                                               const Vector<double> &s,
01276                                               Shape &psi, 
01277                                               DShape &dpsidx, Shape &test, 
01278                                               DShape &dtestdx) const
01279 {
01280  //Call the geometrical shape functions and derivatives  
01281  double J = this->dshape_eulerian(s,psi,dpsidx);
01282  //Loop over the test functions and derivatives and set them equal to the
01283  //shape functions
01284  for(unsigned i=0;i<9;i++)
01285   {
01286    test[i] = psi[i]; 
01287    dtestdx(i,0) = dpsidx(i,0);
01288    dtestdx(i,1) = dpsidx(i,1);
01289   }
01290  //Return the jacobian
01291  return J;
01292 }
01293 
01294 
01295 //==========================================================================
01296 /// 3D : 
01297 /// Derivatives of the shape functions and test functions w.r.t to
01298 /// global (Eulerian) coordinates. Return Jacobian of mapping between
01299 /// local and global coordinates.
01300 //==========================================================================
01301 template<>
01302 inline double QTaylorHoodElement<3>::dshape_and_dtest_eulerian_nst(
01303                                               const Vector<double> &s,
01304                                               Shape &psi, 
01305                                               DShape &dpsidx, Shape &test, 
01306                                               DShape &dtestdx) const
01307 {
01308  //Call the geometrical shape functions and derivatives  
01309  double J = this->dshape_eulerian(s,psi,dpsidx);
01310  //Loop over the test functions and derivatives and set them equal to the
01311  //shape functions
01312  for(unsigned i=0;i<27;i++)
01313   {
01314    test[i] = psi[i]; 
01315    dtestdx(i,0) = dpsidx(i,0);
01316    dtestdx(i,1) = dpsidx(i,1);
01317    dtestdx(i,2) = dpsidx(i,2);
01318   }
01319  //Return the jacobian
01320  return J;
01321 }
01322 
01323 
01324 //==========================================================================
01325 /// 2D : 
01326 /// Derivatives of the shape functions and test functions w.r.t to
01327 /// global (Eulerian) coordinates. Return Jacobian of mapping between
01328 /// local and global coordinates.
01329 //==========================================================================
01330 template<>
01331 inline double QTaylorHoodElement<2>::dshape_and_dtest_eulerian_at_knot_nst(
01332  const unsigned &ipt,Shape &psi, DShape &dpsidx, Shape &test, 
01333  DShape &dtestdx) const
01334 {
01335  //Call the geometrical shape functions and derivatives  
01336  double J = this->dshape_eulerian_at_knot(ipt,psi,dpsidx);
01337  //Loop over the test functions and derivatives and set them equal to the
01338  //shape functions
01339  for(unsigned i=0;i<9;i++)
01340   {
01341    test[i] = psi[i]; 
01342    dtestdx(i,0) = dpsidx(i,0);
01343    dtestdx(i,1) = dpsidx(i,1);
01344   }
01345  //Return the jacobian
01346  return J;
01347 }
01348 
01349 
01350 //==========================================================================
01351 /// 3D : 
01352 /// Derivatives of the shape functions and test functions w.r.t to
01353 /// global (Eulerian) coordinates. Return Jacobian of mapping between
01354 /// local and global coordinates.
01355 //==========================================================================
01356 template<>
01357 inline double QTaylorHoodElement<3>::dshape_and_dtest_eulerian_at_knot_nst(
01358  const unsigned &ipt,Shape &psi, DShape &dpsidx, Shape &test, 
01359  DShape &dtestdx) const
01360 {
01361  //Call the geometrical shape functions and derivatives  
01362  double J = this->dshape_eulerian_at_knot(ipt,psi,dpsidx);
01363  //Loop over the test functions and derivatives and set them equal to the
01364  //shape functions
01365  for(unsigned i=0;i<27;i++)
01366   {
01367    test[i] = psi[i]; 
01368    dtestdx(i,0) = dpsidx(i,0);
01369    dtestdx(i,1) = dpsidx(i,1);
01370    dtestdx(i,2) = dpsidx(i,2);
01371   }
01372  //Return the jacobian
01373  return J;
01374 }
01375 
01376 
01377 //==========================================================================
01378 /// 2D :
01379 /// Pressure shape functions
01380 //==========================================================================
01381 template<>
01382 inline void QTaylorHoodElement<2>::pshape_nst(const Vector<double> &s, 
01383                                               Shape &psi)
01384 const
01385 {
01386  //Local storage
01387  double psi1[2], psi2[2];
01388  //Call the OneDimensional Shape functions
01389  OneDimLagrange::shape<2>(s[0],psi1);
01390  OneDimLagrange::shape<2>(s[1],psi2);
01391 
01392  //Now let's loop over the nodal points in the element
01393  //s1 is the "x" coordinate, s2 the "y" 
01394  for(unsigned i=0;i<2;i++)
01395   {
01396    for(unsigned j=0;j<2;j++)
01397     {
01398      /*Multiply the two 1D functions together to get the 2D function*/
01399      psi[2*i + j] = psi2[i]*psi1[j];
01400     }
01401   }
01402 }
01403 
01404 //==========================================================================
01405 /// 3D :
01406 /// Pressure shape functions
01407 //==========================================================================
01408 template<>
01409 inline void QTaylorHoodElement<3>::pshape_nst(const Vector<double> &s, 
01410                                               Shape &psi)
01411 const
01412 {
01413  //Local storage
01414  double psi1[2], psi2[2], psi3[2];
01415  //Call the OneDimensional Shape functions
01416  OneDimLagrange::shape<2>(s[0],psi1);
01417  OneDimLagrange::shape<2>(s[1],psi2);
01418  OneDimLagrange::shape<2>(s[2],psi3);
01419 
01420  //Now let's loop over the nodal points in the element
01421  //s0 is the "x" coordinate, s1 the "y", s2 is the "z"  
01422  for(unsigned i=0;i<2;i++)
01423   {
01424    for(unsigned j=0;j<2;j++)
01425     {
01426      for(unsigned k=0;k<2;k++)
01427       {
01428        /*Multiply the three 1D functions together to get the 3D function*/
01429        psi[4*i + 2*j + k] = psi3[i]*psi2[j]*psi1[k];
01430       }
01431     }
01432   }
01433 }
01434 
01435 
01436 //==========================================================================
01437 /// 2D :
01438 /// Pressure shape and test functions
01439 //==========================================================================
01440 template<>
01441 inline void QTaylorHoodElement<2>::pshape_nst(const Vector<double> &s, 
01442                                               Shape &psi, 
01443                                               Shape &test) const
01444 {
01445  //Call the pressure shape functions
01446  pshape_nst(s,psi);
01447  //Loop over the test functions and set them equal to the shape functions
01448  for(unsigned i=0;i<4;i++) test[i] = psi[i];
01449 }
01450 
01451 
01452 //==========================================================================
01453 /// 3D :
01454 /// Pressure shape and test functions
01455 //==========================================================================
01456 template<>
01457 inline void QTaylorHoodElement<3>::pshape_nst(const Vector<double> &s, 
01458                                               Shape &psi, 
01459                                               Shape &test) const
01460 {
01461  //Call the pressure shape functions
01462  pshape_nst(s,psi);
01463  //Loop over the test functions and set them equal to the shape functions
01464  for(unsigned i=0;i<8;i++) test[i] = psi[i];
01465 }
01466 
01467 
01468 
01469 //=======================================================================
01470 /// Face geometry of the 2D Taylor_Hood elements
01471 //=======================================================================
01472 template<>
01473 class FaceGeometry<QTaylorHoodElement<2> >: public virtual QElement<1,3>
01474 {
01475   public:
01476  FaceGeometry() : QElement<1,3>() {}
01477 };
01478 
01479 //=======================================================================
01480 /// Face geometry of the 3D Taylor_Hood elements
01481 //=======================================================================
01482 template<>
01483 class FaceGeometry<QTaylorHoodElement<3> >: public virtual QElement<2,3>
01484 {
01485  
01486   public:
01487  FaceGeometry() : QElement<2,3>() {}
01488 };
01489 
01490 
01491 //=======================================================================
01492 /// Face geometry of the FaceGeometry of the 2D Taylor Hoodelements
01493 //=======================================================================
01494 template<>
01495 class FaceGeometry<FaceGeometry<QTaylorHoodElement<2> > >: 
01496 public virtual PointElement
01497 {
01498   public:
01499  FaceGeometry() : PointElement() {}
01500 };
01501 
01502 
01503 //=======================================================================
01504 /// Face geometry of the FaceGeometry of the 3D Taylor_Hood elements
01505 //=======================================================================
01506 template<>
01507 class FaceGeometry<FaceGeometry<QTaylorHoodElement<3> > >: 
01508 public virtual QElement<1,3>
01509 {
01510   public:
01511  FaceGeometry() : QElement<1,3>() {}
01512 };
01513 
01514 }
01515 
01516 #endif

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