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.

refineable_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 refineable 2D quad Navier Stokes elements
00029 
00030 #ifndef OOMPH_REFINEABLE_NAVIER_STOKES_ELEMENTS_HEADER
00031 #define OOMPH_REFINEABLE_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 //Oomph-lib headers
00039 #include "../generic/refineable_quad_element.h"
00040 #include "../generic/refineable_brick_element.h"
00041 #include "../generic/error_estimator.h"
00042 #include "navier_stokes_elements.h"
00043 
00044 namespace oomph
00045 {
00046 
00047 
00048 //======================================================================
00049 /// Refineable version of the Navier--Stokes equations
00050 ///
00051 ///
00052 //======================================================================
00053 template<unsigned DIM>
00054 class RefineableNavierStokesEquations : 
00055 public virtual NavierStokesEquations<DIM>,
00056 public virtual RefineableElement,
00057 public virtual ElementWithZ2ErrorEstimator
00058 {
00059   protected:
00060  
00061  /// \short Pointer to n_p-th pressure node (Default: NULL, 
00062  /// indicating that pressure is not based on nodal interpolation).
00063  virtual Node* pressure_node_pt(const unsigned& n_p) {return NULL;}
00064 
00065  /// \short Unpin all pressure dofs in the element 
00066  virtual void unpin_elemental_pressure_dofs()=0;
00067 
00068  /// \short Pin unused nodal pressure dofs (empty by default, because
00069  /// by default pressure dofs are not associated with nodes)
00070  virtual void pin_elemental_redundant_nodal_pressure_dofs(){}
00071    
00072   public:
00073  
00074  /// \short Constructor
00075  RefineableNavierStokesEquations() : 
00076   NavierStokesEquations<DIM>(),
00077   RefineableElement(),
00078   ElementWithZ2ErrorEstimator() {}
00079 
00080  
00081  /// \short  Loop over all elements in Vector (which typically contains
00082  /// all the elements in a fluid mesh) and pin the nodal pressure degrees
00083  /// of freedom that are not being used. Function uses 
00084  /// the member function
00085  /// - \c RefineableNavierStokesEquations::
00086  ///      pin_elemental_redundant_nodal_pressure_dofs()
00087  /// .
00088  /// which is empty by default and should be implemented for
00089  /// elements with nodal pressure degrees of freedom  
00090  /// (e.g. for refineable Taylor-Hood.)
00091  static void pin_redundant_nodal_pressures(const Vector<GeneralisedElement*>&
00092                                            element_pt)
00093   {
00094    // Loop over all elements and call the function that pins their
00095    // unused nodal pressure data
00096    unsigned n_element = element_pt.size();
00097    for(unsigned e=0;e<n_element;e++)
00098     {
00099      dynamic_cast<RefineableNavierStokesEquations<DIM>*>(element_pt[e])->
00100       pin_elemental_redundant_nodal_pressure_dofs();
00101     }
00102   }
00103 
00104  /// \short Unpin all pressure dofs in elements listed in vector.
00105  static void unpin_all_pressure_dofs(const Vector<GeneralisedElement*>&
00106                                      element_pt)
00107   {
00108    // Loop over all elements
00109    unsigned n_element = element_pt.size();
00110    for(unsigned e=0;e<n_element;e++)
00111     {
00112      dynamic_cast<RefineableNavierStokesEquations<DIM>*>(element_pt[e])->
00113       unpin_elemental_pressure_dofs();
00114     }
00115   }
00116 
00117  
00118  /// Number of 'flux' terms for Z2 error estimation 
00119  unsigned num_Z2_flux_terms()
00120   {
00121    // DIM diagonal strain rates, DIM(DIM -1) /2 off diagonal rates
00122    return DIM + (DIM*(DIM-1))/2;
00123   }
00124 
00125  /// \short Get 'flux' for Z2 error recovery:   Upper triangular entries
00126  /// in strain rate tensor.
00127  void get_Z2_flux(const Vector<double>& s, Vector<double>& flux)
00128   {
00129 #ifdef PARANOID
00130    unsigned num_entries=DIM+(DIM*(DIM-1))/2;
00131    if (flux.size() < num_entries)
00132     {
00133      std::ostringstream error_message;
00134      error_message << "The flux vector has the wrong number of entries, " 
00135                    << flux.size() << ", whereas it should be at least " 
00136                    << num_entries << std::endl;
00137      throw OomphLibError(error_message.str(),
00138                          "RefineableNavierStokesEquations::get_Z2_flux()",
00139                          OOMPH_EXCEPTION_LOCATION);
00140     }
00141 #endif
00142    
00143    // Get strain rate matrix
00144    DenseMatrix<double> strainrate(DIM);
00145    this->strain_rate(s,strainrate);
00146    
00147    // Pack into flux Vector
00148    unsigned icount=0;
00149    
00150    // Start with diagonal terms
00151    for(unsigned i=0;i<DIM;i++)
00152     {
00153      flux[icount]=strainrate(i,i);
00154      icount++;
00155     }
00156    
00157    //Off diagonals row by row
00158    for(unsigned i=0;i<DIM;i++)
00159     {
00160      for(unsigned j=i+1;j<DIM;j++)
00161       {
00162        flux[icount]=strainrate(i,j);
00163        icount++;
00164       }
00165     }
00166   }
00167 
00168  ///  Further build, pass the pointers down to the sons
00169  void further_build()
00170   {
00171    //Find the father element
00172    RefineableNavierStokesEquations<DIM>* cast_father_element_pt
00173     = dynamic_cast<RefineableNavierStokesEquations<DIM>*>
00174     (this->father_element_pt());
00175    
00176    //Set the viscosity ratio pointer
00177    this->Viscosity_Ratio_pt = cast_father_element_pt->viscosity_ratio_pt(); 
00178    //Set the density ratio pointer
00179    this->Density_Ratio_pt = cast_father_element_pt->density_ratio_pt();
00180    //Set pointer to global Reynolds number
00181    this->Re_pt = cast_father_element_pt->re_pt();
00182    //Set pointer to global Reynolds number x Strouhal number (=Womersley)
00183    this->ReSt_pt = cast_father_element_pt->re_st_pt();
00184    //Set pointer to global Reynolds number x inverse Froude number
00185    this->ReInvFr_pt = cast_father_element_pt->re_invfr_pt();
00186    //Set pointer to global gravity Vector
00187    this->G_pt = cast_father_element_pt->g_pt();
00188    
00189    //Set pointer to body force function
00190    this->Body_force_fct_pt = cast_father_element_pt->body_force_fct_pt();
00191  
00192    //Set pointer to volumetric source function
00193    this->Source_fct_pt = cast_father_element_pt->source_fct_pt();
00194 
00195    //Set the ALE flag
00196    this->ALE_is_disabled = cast_father_element_pt->ALE_is_disabled;
00197   }
00198 
00199 
00200  /// \short Compute the derivatives of the i-th component of 
00201  /// velocity at point s with respect
00202  /// to all data that can affect its value. In addition, return the global
00203  /// equation numbers corresponding to the data.
00204  /// Overload the non-refineable version to take account of hanging node
00205  /// information
00206  void dinterpolated_u_nst_ddata(const Vector<double> &s,
00207                                 const unsigned &i,
00208                                 Vector<double> &du_ddata,
00209                                 Vector<unsigned> &global_eqn_number)
00210   {
00211    //Find number of nodes
00212    unsigned n_node = this->nnode();
00213    //Local shape function
00214    Shape psi(n_node);
00215    //Find values of shape function at the given local coordinate
00216    this->shape(s,psi);
00217    
00218    //Find the index at which the velocity component is stored
00219    const unsigned u_nodal_index = this->u_index_nst(i);
00220    
00221    //Storage for hang info pointer
00222    HangInfo* hang_info_pt=0;
00223    //Storage for global equation
00224    int global_eqn = 0;
00225           
00226    //Find the number of dofs associated with interpolated u
00227    unsigned n_u_dof=0;
00228    for(unsigned l=0;l<n_node;l++)
00229     {
00230      unsigned n_master = 1;
00231      
00232      //Local bool (is the node hanging)
00233      bool is_node_hanging = this->node_pt(l)->is_hanging();
00234      
00235      //If the node is hanging, get the number of master nodes
00236      if(is_node_hanging)
00237       {
00238        hang_info_pt = this->node_pt(l)->hanging_pt();
00239        n_master = hang_info_pt->nmaster();
00240       }
00241      //Otherwise there is just one master node, the node itself
00242      else 
00243       {
00244        n_master = 1;
00245       }
00246      
00247      //Loop over the master nodes
00248      for(unsigned m=0;m<n_master;m++)
00249       {
00250        //Get the equation number
00251        if(is_node_hanging)
00252         {
00253          //Get the equation number from the master node
00254          global_eqn = hang_info_pt->master_node_pt(m)->
00255           eqn_number(u_nodal_index);
00256         }
00257        else
00258         {
00259          // Global equation number
00260          global_eqn = this->node_pt(l)->eqn_number(u_nodal_index);
00261         }
00262        
00263        //If it's positive add to the count
00264        if(global_eqn >= 0) {++n_u_dof;}
00265       }
00266     }
00267    
00268    //Now resize the storage schemes
00269    du_ddata.resize(n_u_dof,0.0);
00270    global_eqn_number.resize(n_u_dof,0);
00271    
00272    //Loop over th nodes again and set the derivatives
00273    unsigned count=0;
00274    //Loop over the local nodes and sum
00275    for(unsigned l=0;l<n_node;l++) 
00276     {
00277      unsigned n_master = 1;
00278      double hang_weight = 1.0;
00279      
00280      //Local bool (is the node hanging)
00281      bool is_node_hanging = this->node_pt(l)->is_hanging();
00282      
00283      //If the node is hanging, get the number of master nodes
00284      if(is_node_hanging)
00285       {
00286        hang_info_pt = this->node_pt(l)->hanging_pt();
00287        n_master = hang_info_pt->nmaster();
00288       }
00289      //Otherwise there is just one master node, the node itself
00290      else 
00291       {
00292        n_master = 1;
00293       }
00294      
00295      //Loop over the master nodes
00296      for(unsigned m=0;m<n_master;m++)
00297       {
00298        //If the node is hanging get weight from master node
00299        if(is_node_hanging)
00300         {
00301          //Get the hang weight from the master node
00302          hang_weight = hang_info_pt->master_weight(m);
00303         }
00304        else
00305         {
00306          // Node contributes with full weight
00307          hang_weight = 1.0;
00308         }
00309        
00310        //Get the equation number
00311        if(is_node_hanging)
00312         {
00313          //Get the equation number from the master node
00314          global_eqn = hang_info_pt->master_node_pt(m)->
00315           eqn_number(u_nodal_index);
00316         }
00317        else
00318         {
00319          // Local equation number
00320          global_eqn = this->node_pt(l)->eqn_number(u_nodal_index);
00321         }
00322        
00323        if (global_eqn >= 0)
00324         {
00325          //Set the global equation number
00326          global_eqn_number[count] = global_eqn;
00327          //Set the derivative with respect to the unknown
00328          du_ddata[count] = psi[l]*hang_weight;
00329          //Increase the counter
00330          ++count;
00331         }
00332       }
00333     }
00334   }
00335 
00336 
00337 
00338   protected:
00339 
00340 
00341 /// \short Add element's contribution to elemental residual vector and/or 
00342 /// Jacobian matrix 
00343 /// flag=1: compute both
00344 /// flag=0: compute only residual vector
00345  void fill_in_generic_residual_contribution_nst(
00346   Vector<double> &residuals, 
00347   DenseMatrix<double> &jacobian, 
00348   DenseMatrix<double> &mass_matrix,
00349   unsigned flag); 
00350 
00351  /// \short Compute derivatives of elemental residual vector with respect
00352  /// to nodal coordinates. Overwrites default implementation in 
00353  /// FiniteElement base class.
00354  /// dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}
00355  virtual void get_dresidual_dnodal_coordinates(RankThreeTensor<double>&
00356                                                dresidual_dnodal_coordinates);
00357   
00358 };
00359 
00360 
00361 //======================================================================
00362 /// Refineable version of Taylor Hood elements. These classes
00363 /// can be written in total generality.
00364 //======================================================================
00365 template<unsigned DIM>
00366 class RefineableQTaylorHoodElement : 
00367 public QTaylorHoodElement<DIM>,
00368 public virtual RefineableNavierStokesEquations<DIM>,
00369 public virtual RefineableQElement<DIM>
00370 {
00371   private:
00372   
00373  /// \short Pointer to n_p-th pressure node
00374  Node* pressure_node_pt(const unsigned &n_p)
00375   {return this->node_pt(this->Pconv[n_p]);}
00376 
00377  /// Unpin all pressure dofs
00378  void unpin_elemental_pressure_dofs()
00379   {
00380    //find the index at which the pressure is stored
00381    int p_index = this->p_nodal_index_nst();
00382    unsigned n_node = this->nnode();
00383    // loop over nodes
00384    for(unsigned n=0;n<n_node;n++) 
00385     {this->node_pt(n)->unpin(p_index);}
00386   }
00387  
00388  ///  Pin all nodal pressure dofs that are not required
00389  void pin_elemental_redundant_nodal_pressure_dofs()
00390   {
00391    //Find the pressure index
00392    int p_index = this->p_nodal_index_nst();
00393    //Loop over all nodes
00394    unsigned n_node = this->nnode();
00395    // loop over all nodes and pin all  the nodal pressures
00396    for(unsigned n=0;n<n_node;n++) {this->node_pt(n)->pin(p_index);}
00397    
00398    // Loop over all actual pressure nodes and unpin if they're not hanging
00399    unsigned n_pres = this->npres_nst();
00400    for(unsigned l=0;l<n_pres;l++)
00401     {
00402      Node* nod_pt = this->pressure_node_pt(l);
00403      if (!nod_pt->is_hanging(p_index)) {nod_pt->unpin(p_index);}
00404     }
00405   }
00406  
00407   public:
00408  
00409  /// \short Constructor
00410  RefineableQTaylorHoodElement() : 
00411   RefineableElement(),
00412   RefineableNavierStokesEquations<DIM>(),
00413   RefineableQElement<DIM>(), 
00414   QTaylorHoodElement<DIM>() {}
00415  
00416  /// \short Number of values required at local node n. In order to simplify
00417  /// matters, we allocate storage for pressure variables at all the nodes
00418  /// and then pin those that are not used.
00419  unsigned required_nvalue(const unsigned &n) const {return DIM+1;}
00420 
00421  /// Number of continuously interpolated values: (DIM velocities + 1 pressure)
00422  unsigned ncont_interpolated_values() const {return DIM+1;}
00423 
00424  /// Rebuild from sons: empty
00425  void rebuild_from_sons(Mesh* &mesh_pt) {}
00426 
00427  /// \short Order of recovery shape functions for Z2 error estimation:
00428  /// Same order as shape functions.
00429  unsigned nrecovery_order() {return 2;}
00430 
00431  /// \short Number of vertex nodes in the element
00432  unsigned nvertex_node() const
00433   {return QTaylorHoodElement<DIM>::nvertex_node();}
00434 
00435  /// \short Pointer to the j-th vertex node in the element
00436  Node* vertex_node_pt(const unsigned& j) const
00437   {return QTaylorHoodElement<DIM>::vertex_node_pt(j);}
00438 
00439 /// \short Get the function value u in Vector.
00440 /// Note: Given the generality of the interface (this function
00441 /// is usually called from black-box documentation or interpolation routines),
00442 /// the values Vector sets its own size in here.
00443  void get_interpolated_values(const Vector<double>&s,  Vector<double>& values)
00444   {
00445    // Set size of Vector: u,v,p and initialise to zero
00446    values.resize(DIM+1,0.0);
00447    
00448    //Calculate velocities: values[0],...
00449    for(unsigned i=0;i<DIM;i++) {values[i] = this->interpolated_u_nst(s,i);}
00450    
00451    //Calculate pressure: values[DIM]
00452    values[DIM] = this->interpolated_p_nst(s);
00453   }
00454  
00455 /// \short Get the function value u in Vector.
00456 /// Note: Given the generality of the interface (this function
00457 /// is usually called from black-box documentation or interpolation routines),
00458 /// the values Vector sets its own size in here.
00459  void get_interpolated_values(const unsigned& t, const Vector<double>&s, 
00460                               Vector<double>& values)
00461   {
00462    // Set size of Vector: u,v,p
00463    values.resize(DIM+1);
00464    
00465    // Initialise
00466    for(unsigned i=0;i<DIM+1;i++) {values[i]=0.0;}
00467    
00468    //Find out how many nodes there are
00469    unsigned n_node = this->nnode();
00470    
00471    // Shape functions
00472    Shape psif(n_node);
00473    this->shape(s,psif);
00474    
00475    //Calculate velocities: values[0],...
00476    for(unsigned i=0;i<DIM;i++) 
00477     {
00478      //Get the index at which the i-th velocity is stored
00479      unsigned u_nodal_index = this->u_index_nst(i);
00480      for(unsigned l=0;l<n_node;l++) 
00481       {
00482        values[i] += this->nodal_value(t,l,u_nodal_index)*psif[l];
00483       } 
00484     }
00485    
00486    //Calculate pressure: values[DIM] 
00487    //(no history is carried in the pressure)
00488    values[DIM] = this->interpolated_p_nst(s);
00489   }
00490 
00491   
00492  ///  \short Perform additional hanging node procedures for variables
00493  /// that are not interpolated by all nodes. The pressures are stored 
00494  /// at the p_nodal_index_nst-th location in each node
00495  void further_setup_hanging_nodes()
00496   {
00497    this->setup_hang_for_value(this->p_nodal_index_nst());
00498   }
00499 
00500  /// \short The velocities are isoparametric and so the "nodes" interpolating
00501  /// the velocities are the geometric nodes. The pressure "nodes" are a 
00502  /// subset of the nodes, so when value_id==DIM, the n-th pressure
00503  /// node is returned.
00504  Node* interpolating_node_pt(const unsigned &n,
00505                              const int &value_id) 
00506 
00507   {
00508    //The only different nodes are the pressure nodes
00509    if(value_id==DIM) {return this->pressure_node_pt(n);}
00510    //The other variables are interpolated via the usual nodes
00511    else {return this->node_pt(n);}
00512   }
00513 
00514  /// \short The pressure nodes are the corner nodes, so when n_value==DIM,
00515  /// the fraction is the same as the 1d node number, 0 or 1.
00516  double local_one_d_fraction_of_interpolating_node(const unsigned &n1d,
00517                                                    const unsigned &i, 
00518                                                    const int &value_id)
00519   {
00520    if(value_id==DIM) 
00521     {
00522      //The pressure nodes are just located on the boundaries at 0 or 1
00523      return double(n1d); 
00524     }
00525    //Otherwise the velocity nodes are the same as the geometric ones
00526    else
00527     {
00528      return this->local_one_d_fraction_of_node(n1d,i);
00529     }
00530   }
00531  
00532  /// \short The velocity nodes are the same as the geometric nodes. The
00533  /// pressure nodes must be calculated by using the same methods as
00534  /// the geometric nodes, but by recalling that there are only two pressure
00535  /// nodes per edge.
00536  Node* get_interpolating_node_at_local_coordinate(const Vector<double> &s,   
00537                                                   const int &value_id)
00538   {
00539    //If we are calculating pressure nodes
00540    if(value_id==DIM)
00541     {
00542      //Storage for the index of the pressure node
00543      unsigned total_index=0;
00544      //The number of nodes along each 1d edge is 2.
00545      unsigned NNODE_1D = 2;
00546      //Storage for the index along each boundary
00547      Vector<int> index(DIM);
00548      //Loop over the coordinates
00549      for(unsigned i=0;i<DIM;i++)
00550       {
00551        //If we are at the lower limit, the index is zero
00552        if(s[i]==-1.0)
00553         {
00554          index[i] = 0;
00555         }
00556        //If we are at the upper limit, the index is the number of nodes minus 1
00557        else if(s[i] == 1.0)
00558         {
00559          index[i] = NNODE_1D-1;
00560         }
00561        //Otherwise, we have to calculate the index in general
00562        else
00563         {
00564          //For uniformly spaced nodes the 0th node number would be
00565          double float_index = 0.5*(1.0 + s[i])*(NNODE_1D-1);
00566          index[i] = int(float_index);
00567          //What is the excess. This should be safe because the
00568          //taking the integer part rounds down
00569          double excess = float_index - index[i];
00570          //If the excess is bigger than our tolerance there is no node,
00571          //return null
00572          if((excess > FiniteElement::Node_location_tolerance) && (
00573              (1.0 - excess) > FiniteElement::Node_location_tolerance))
00574           {
00575            return 0;
00576           }
00577         }
00578        ///Construct the general pressure index from the components.
00579        total_index += index[i]*
00580         static_cast<unsigned>(pow(static_cast<float>(NNODE_1D),
00581                                   static_cast<int>(i)));
00582       }
00583      //If we've got here we have a node, so let's return a pointer to it
00584      return this->pressure_node_pt(total_index);
00585     }
00586    //Otherwise velocity nodes are the same as pressure nodes
00587    else
00588     {
00589      return this->get_node_at_local_coordinate(s);
00590     }
00591   }
00592 
00593 
00594  /// \short The number of 1d pressure nodes is 2, the number of 1d velocity
00595  /// nodes is the same as the number of 1d geometric nodes.
00596  unsigned ninterpolating_node_1d(const int &value_id)
00597   {
00598    if(value_id==DIM) {return 2;}
00599    else {return this->nnode_1d();}
00600   }
00601 
00602  /// \short The number of pressure nodes is 2^DIM. The number of 
00603  /// velocity nodes is the same as the number of geometric nodes.
00604  unsigned ninterpolating_node(const int &value_id)
00605   {
00606    if(value_id==DIM) 
00607     {return static_cast<unsigned>(pow(2.0,static_cast<int>(DIM)));}
00608    else {return this->nnode();}
00609   }
00610  
00611  /// \short The basis interpolating the pressure is given by pshape().
00612  //// The basis interpolating the velocity is shape().
00613  void interpolating_basis(const Vector<double> &s,
00614                           Shape &psi,
00615                           const int &value_id) const
00616   {
00617    if(value_id==DIM) {return this->pshape_nst(s,psi);}
00618    else {return this->shape(s,psi);}
00619   }
00620 
00621 
00622  /// \short Add to the set \c paired_load_data pairs containing
00623  /// - the pointer to a Data object
00624  /// and
00625  /// - the index of the value in that Data object
00626  /// .
00627  /// for all values (pressures, velocities) that affect the
00628  /// load computed in the \c get_load(...) function.
00629  /// (Overloads non-refineable version and takes hanging nodes
00630  /// into account)
00631  void identify_load_data(
00632   std::set<std::pair<Data*,unsigned> > &paired_load_data)
00633   {
00634    //Get the nodal indices at which the velocities are stored
00635    unsigned u_index[DIM];
00636    for(unsigned i=0;i<DIM;i++) {u_index[i] = this->u_index_nst(i);}
00637 
00638    //Loop over the nodes
00639    unsigned n_node = this->nnode();
00640    for(unsigned n=0;n<n_node;n++)
00641     {
00642      // Pointer to current node
00643      Node* nod_pt=this->node_pt(n);
00644      
00645      // Check if it's hanging:
00646      if (nod_pt->is_hanging())
00647       {
00648        // It's hanging -- get number of master nodes
00649        unsigned nmaster=nod_pt->hanging_pt()->nmaster();
00650        
00651        // Loop over masters
00652        for (unsigned j=0;j<nmaster;j++)
00653         {
00654          Node* master_nod_pt=nod_pt->hanging_pt()->master_node_pt(j);
00655          
00656          //Loop over the velocity components and add pointer to their data
00657          //and indices to the vectors
00658          for(unsigned i=0;i<DIM;i++)
00659           {
00660            paired_load_data.insert(std::make_pair(master_nod_pt,u_index[i]));
00661           }
00662         }
00663       }
00664      //Not hanging
00665      else
00666       {
00667        //Loop over the velocity components and add pointer to their data
00668        //and indices to the vectors
00669        for(unsigned i=0;i<DIM;i++)
00670         {
00671          paired_load_data.insert(std::make_pair(this->node_pt(n),u_index[i]));
00672         }
00673       }
00674     }
00675    
00676    //Get the nodal index at which the pressure is stored
00677    int p_index = this->p_nodal_index_nst();
00678    
00679    //Loop over the pressure data
00680    unsigned n_pres= this->npres_nst();
00681    for(unsigned l=0;l<n_pres;l++)
00682     {
00683      //Get the pointer to the nodal pressure
00684      Node* pres_node_pt = this->pressure_node_pt(l);
00685      // Check if the pressure dof is hanging
00686      if(pres_node_pt->is_hanging(p_index))
00687       {
00688        //Get the pointer to the hang info object
00689        // (pressure is stored as p_index--th nodal dof).
00690        HangInfo* hang_info_pt = pres_node_pt->hanging_pt(p_index);
00691 
00692        // Get number of pressure master nodes (pressure is stored  
00693        unsigned nmaster = hang_info_pt->nmaster();
00694        
00695        // Loop over pressure master nodes
00696        for(unsigned m=0;m<nmaster;m++)
00697         {
00698          //The p_index-th entry in each nodal data is the pressure, which
00699          //affects the traction
00700          paired_load_data.insert(
00701           std::make_pair(hang_info_pt->master_node_pt(m),p_index));
00702         }
00703       }
00704      // It's not hanging
00705      else
00706       {
00707        //The p_index-th entry in each nodal data is the pressure, which
00708        //affects the traction
00709        paired_load_data.insert(std::make_pair(pres_node_pt,p_index));
00710       }
00711     }
00712    
00713   }
00714 
00715 
00716 };
00717 
00718 
00719 //=======================================================================
00720 /// \short Face geometry of the RefineableQTaylorHoodElements is the
00721 /// same as the Face geometry of the QTaylorHoodElements.
00722 //=======================================================================
00723 template<unsigned DIM>
00724 class FaceGeometry<RefineableQTaylorHoodElement<DIM> >: 
00725 public virtual FaceGeometry<QTaylorHoodElement<DIM> >
00726 {
00727   public:
00728  FaceGeometry() : FaceGeometry<QTaylorHoodElement<DIM> >() {}
00729 };
00730 
00731 
00732 //=======================================================================
00733 /// \short Face geometry of the face geometry of 
00734 /// the RefineableQTaylorHoodElements is the
00735 /// same as the Face geometry of the Face geometry of QTaylorHoodElements.
00736 //=======================================================================
00737 template<unsigned DIM>
00738 class FaceGeometry<FaceGeometry<RefineableQTaylorHoodElement<DIM> > >: 
00739 public virtual FaceGeometry<FaceGeometry<QTaylorHoodElement<DIM> > >
00740 {
00741   public:
00742  FaceGeometry() : FaceGeometry<FaceGeometry<QTaylorHoodElement<DIM> > >() 
00743   {}
00744 };
00745 
00746 
00747 ///////////////////////////////////////////////////////////////////////////
00748 ///////////////////////////////////////////////////////////////////////////
00749 ///////////////////////////////////////////////////////////////////////////
00750 
00751 
00752 //======================================================================
00753 /// Refineable version of Crouzeix Raviart elements. Generic class definitions
00754 //======================================================================
00755 template<unsigned DIM>
00756 class RefineableQCrouzeixRaviartElement :
00757 public QCrouzeixRaviartElement<DIM>, 
00758 public virtual RefineableNavierStokesEquations<DIM>,
00759 public virtual RefineableQElement<DIM> 
00760 {
00761   private:
00762  
00763  /// Unpin all internal pressure dofs
00764  void unpin_elemental_pressure_dofs()
00765   {
00766    unsigned n_pres = this->npres_nst();
00767    // loop over pressure dofs and unpin them
00768    for(unsigned l=0;l<n_pres;l++) 
00769     {this->internal_data_pt(this->P_nst_internal_index)->unpin(l);}
00770   }
00771 
00772   public:
00773  
00774  /// \short Constructor
00775  RefineableQCrouzeixRaviartElement() : 
00776   RefineableElement(),
00777   RefineableNavierStokesEquations<DIM>(),
00778   RefineableQElement<DIM>(),  
00779   QCrouzeixRaviartElement<DIM>() {}
00780  
00781  /// Number of continuously interpolated values: DIM (velocities)
00782  unsigned ncont_interpolated_values() const {return DIM;}
00783  
00784  /// \short Rebuild from sons: Reconstruct pressure from the (merged) sons
00785  /// This must be specialised for each dimension.
00786  inline void rebuild_from_sons(Mesh* &mesh_pt);
00787 
00788  /// \short Order of recovery shape functions for Z2 error estimation:
00789  /// Same order as shape functions.
00790  unsigned nrecovery_order() {return 2;}
00791 
00792  /// \short Number of vertex nodes in the element
00793  unsigned nvertex_node() const
00794   {return QCrouzeixRaviartElement<DIM>::nvertex_node();}
00795 
00796  /// \short Pointer to the j-th vertex node in the element
00797  Node* vertex_node_pt(const unsigned& j) const
00798   {
00799    return QCrouzeixRaviartElement<DIM>::vertex_node_pt(j);
00800   }
00801 
00802 /// \short Get the function value u in Vector.
00803 /// Note: Given the generality of the interface (this function
00804 /// is usually called from black-box documentation or interpolation routines),
00805 /// the values Vector sets its own size in here.
00806 void get_interpolated_values(const Vector<double>&s,  Vector<double>& values)
00807  {
00808   // Set size of Vector: u,v,p and initialise to zero
00809   values.resize(DIM,0.0);
00810   
00811   //Calculate velocities: values[0],...
00812   for(unsigned i=0;i<DIM;i++) {values[i] = this->interpolated_u_nst(s,i);}
00813  }
00814 
00815  /// \short Get all function values [u,v..,p] at previous timestep t
00816  /// (t=0: present; t>0: previous timestep). 
00817  /// \n 
00818  /// Note: Given the generality of the interface (this function
00819  /// is usually called from black-box documentation or interpolation routines),
00820  /// the values Vector sets its own size in here.
00821  /// \n
00822  /// Note: No pressure history is kept, so pressure is always
00823  /// the current value.
00824  void get_interpolated_values(const unsigned& t, const Vector<double>&s, 
00825                               Vector<double>& values)
00826   {
00827    // Set size of Vector: u,v,p
00828    values.resize(DIM);
00829 
00830    // Initialise
00831    for(unsigned i=0;i<DIM;i++) {values[i]=0.0;}
00832    
00833    //Find out how many nodes there are
00834    unsigned n_node = this->nnode();
00835    
00836    // Shape functions
00837    Shape psif(n_node);
00838    this->shape(s,psif);
00839    
00840    //Calculate velocities: values[0],...
00841    for(unsigned i=0;i<DIM;i++) 
00842     {
00843      //Get the nodal index at which the i-th velocity component is stored
00844      unsigned u_nodal_index = this->u_index_nst(i);
00845      for(unsigned l=0;l<n_node;l++) 
00846       {
00847        values[i] += this->nodal_value(t,l,u_nodal_index)*psif[l];
00848       } 
00849     }
00850   }
00851  
00852  ///  \short Perform additional hanging node procedures for variables
00853  /// that are not interpolated by all nodes. Empty
00854  void further_setup_hanging_nodes() {}
00855 
00856  /// Further build for Crouzeix_Raviart interpolates the internal 
00857  /// pressure dofs from father element: Make sure pressure values and 
00858  /// dp/ds agree between fathers and sons at the midpoints of the son 
00859  /// elements. This must be specialised for each dimension.
00860  inline void further_build();
00861 
00862 
00863 
00864  /// \short Add to the set \c paired_load_data pairs containing
00865  /// - the pointer to a Data object
00866  /// and
00867  /// - the index of the value in that Data object
00868  /// .
00869  /// for all values (pressures, velocities) that affect the
00870  /// load computed in the \c get_load(...) function.
00871  /// (Overloads non-refineable version and takes hanging nodes
00872  /// into account)
00873  void identify_load_data(
00874   std::set<std::pair<Data*,unsigned> > &paired_load_data)
00875   {
00876    //Get the nodal indices at which the velocities are stored
00877    unsigned u_index[DIM];
00878    for(unsigned i=0;i<DIM;i++) {u_index[i] = this->u_index_nst(i);}
00879 
00880    //Loop over the nodes
00881    unsigned n_node = this->nnode();
00882    for(unsigned n=0;n<n_node;n++)
00883     {
00884      // Pointer to current node
00885      Node* nod_pt=this->node_pt(n);
00886      
00887      // Check if it's hanging:
00888      if (nod_pt->is_hanging())
00889       {
00890        // It's hanging -- get number of master nodes
00891        unsigned nmaster=nod_pt->hanging_pt()->nmaster();
00892        
00893        // Loop over masters
00894        for (unsigned j=0;j<nmaster;j++)
00895         {
00896          Node* master_nod_pt=nod_pt->hanging_pt()->master_node_pt(j);
00897          
00898          //Loop over the velocity components and add pointer to their data
00899          //and indices to the vectors
00900          for(unsigned i=0;i<DIM;i++)
00901           {
00902            paired_load_data.insert(std::make_pair(master_nod_pt,u_index[i]));
00903           }
00904         }
00905       }
00906      //Not hanging
00907      else
00908       {
00909        //Loop over the velocity components and add pointer to their data
00910        //and indices to the vectors
00911        for(unsigned i=0;i<DIM;i++)
00912         {
00913          paired_load_data.insert(std::make_pair(this->node_pt(n),u_index[i]));
00914         }
00915       }
00916     }
00917    
00918 
00919    //Loop over the pressure data (can't be hanging!)
00920    unsigned n_pres = this->npres_nst();
00921    for(unsigned l=0;l<n_pres;l++)
00922     {
00923      //The entries in the internal data at P_nst_internal_index
00924      //are the pressures, which affect the traction
00925      paired_load_data.insert(
00926       std::make_pair(this->internal_data_pt(this->P_nst_internal_index),l));
00927     }
00928   }
00929 
00930 
00931 };
00932 
00933 
00934 //=======================================================================
00935 /// Face geometry of the RefineableQuadQCrouzeixRaviartElements
00936 //=======================================================================
00937 template<unsigned DIM>
00938 class FaceGeometry<RefineableQCrouzeixRaviartElement<DIM> >: 
00939 public virtual FaceGeometry<QCrouzeixRaviartElement<DIM> >
00940 {
00941   public:
00942  FaceGeometry() : FaceGeometry<QCrouzeixRaviartElement<DIM> >() {}
00943 };
00944 
00945 //======================================================================
00946 /// \short Face geometry of the face geometry of 
00947 /// the RefineableQCrouzeixRaviartElements is the
00948 /// same as the Face geometry of the Face geometry of 
00949 /// QCrouzeixRaviartElements.
00950 //=======================================================================
00951 template<unsigned DIM>
00952 class FaceGeometry<FaceGeometry<RefineableQCrouzeixRaviartElement<DIM> > >: 
00953 public virtual FaceGeometry<FaceGeometry<QCrouzeixRaviartElement<DIM> > >
00954 {
00955   public:
00956  FaceGeometry() : FaceGeometry<FaceGeometry<QCrouzeixRaviartElement<DIM> > >() 
00957   {}
00958 };
00959 
00960 
00961 //=====================================================================
00962 /// 2D Rebuild from sons: Reconstruct pressure from the (merged) sons
00963 //=====================================================================
00964 template<>
00965 inline void RefineableQCrouzeixRaviartElement<2>::
00966 rebuild_from_sons(Mesh* &mesh_pt)
00967 {
00968  using namespace QuadTreeNames;
00969  
00970  //Central pressure value: 
00971  //-----------------------
00972  
00973  // Use average of the sons central pressure values
00974  // Other options: Take average of the four (discontinuous)
00975  // pressure values at the father's midpoint]
00976  
00977  double av_press=0.0;
00978  
00979  // Loop over the sons
00980  for (unsigned ison=0;ison<4;ison++)
00981   {
00982    // Add the sons midnode pressure
00983    // Note that we can assume that the pressure is stored at the same
00984    // location because these are EXACTLY the same type of elements
00985    av_press += quadtree_pt()->son_pt(ison)->object_pt()->
00986     internal_data_pt(this->P_nst_internal_index)->value(0);
00987   }
00988  
00989  // Use the average
00990  internal_data_pt(this->P_nst_internal_index)->set_value(0,0.25*av_press);
00991  
00992  
00993  //Slope in s_0 direction
00994  //----------------------
00995  
00996  // Use average of the 2 FD approximations based on the 
00997  // elements central pressure values
00998  // [Other options: Take average of the four 
00999  // pressure derivatives]
01000  
01001  double slope1= 
01002   quadtree_pt()->son_pt(SE)->object_pt()->
01003   internal_data_pt(this->P_nst_internal_index)->value(0) -
01004   quadtree_pt()->son_pt(SW)->object_pt()->
01005   internal_data_pt(this->P_nst_internal_index)->value(0);
01006  
01007  double slope2 = 
01008   quadtree_pt()->son_pt(NE)->object_pt()->
01009   internal_data_pt(this->P_nst_internal_index)->value(0) -
01010   quadtree_pt()->son_pt(NW)->object_pt()->
01011   internal_data_pt(this->P_nst_internal_index)->value(0);
01012  
01013  
01014  // Use the average
01015  internal_data_pt(this->P_nst_internal_index)->
01016   set_value(1,0.5*(slope1+slope2));
01017  
01018  
01019 
01020  //Slope in s_1 direction
01021  //----------------------
01022  
01023    // Use average of the 2 FD approximations based on the 
01024    // elements central pressure values
01025    // [Other options: Take average of the four 
01026    // pressure derivatives]
01027  
01028  slope1 = 
01029   quadtree_pt()->son_pt(NE)->object_pt()
01030   ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01031   quadtree_pt()->son_pt(SE)->object_pt()
01032   ->internal_data_pt(this->P_nst_internal_index)->value(0);
01033  
01034  slope2=
01035   quadtree_pt()->son_pt(NW)->object_pt()
01036   ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01037   quadtree_pt()->son_pt(SW)->object_pt()
01038   ->internal_data_pt(this->P_nst_internal_index)->value(0);
01039 
01040 
01041  // Use the average
01042  internal_data_pt(this->P_nst_internal_index)->
01043   set_value(2,0.5*(slope1+slope2));
01044 }
01045 
01046 
01047  
01048 //=================================================================
01049 /// 3D Rebuild from sons: Reconstruct pressure from the (merged) sons
01050 //=================================================================
01051 template<>
01052 inline void RefineableQCrouzeixRaviartElement<3>::
01053 rebuild_from_sons(Mesh* &mesh_pt)
01054 {
01055  using namespace OcTreeNames;
01056  
01057  //Central pressure value: 
01058  //-----------------------
01059  
01060  // Use average of the sons central pressure values
01061  // Other options: Take average of the four (discontinuous)
01062  // pressure values at the father's midpoint]
01063  
01064  double av_press=0.0;
01065  
01066  // Loop over the sons
01067  for (unsigned ison=0;ison<8;ison++)
01068   {
01069    // Add the sons midnode pressure
01070    av_press += octree_pt()->son_pt(ison)->object_pt()->
01071     internal_data_pt(this->P_nst_internal_index)->value(0);
01072   }
01073  
01074  // Use the average
01075  internal_data_pt(this->P_nst_internal_index)->
01076   set_value(0,0.125*av_press);
01077  
01078  
01079  //Slope in s_0 direction
01080  //----------------------
01081  
01082  // Use average of the 4 FD approximations based on the 
01083  // elements central pressure values
01084  // [Other options: Take average of the four 
01085  // pressure derivatives]
01086  
01087  double slope1 = 
01088   octree_pt()->son_pt(RDF)->object_pt()->
01089   internal_data_pt(this->P_nst_internal_index)->value(0) -
01090   octree_pt()->son_pt(LDF)->object_pt()->
01091   internal_data_pt(this->P_nst_internal_index)->value(0);
01092  
01093  double slope2 =
01094   octree_pt()->son_pt(RUF)->object_pt()->
01095   internal_data_pt(this->P_nst_internal_index)->value(0) -
01096   octree_pt()->son_pt(LUF)->object_pt()->
01097   internal_data_pt(this->P_nst_internal_index)->value(0);
01098  
01099  double slope3 =
01100   octree_pt()->son_pt(RDB)->object_pt()->
01101   internal_data_pt(this->P_nst_internal_index)->value(0) -
01102   octree_pt()->son_pt(LDB)->object_pt()->
01103   internal_data_pt(this->P_nst_internal_index)->value(0);
01104  
01105  double slope4 = 
01106   octree_pt()->son_pt(RUB)->object_pt()->
01107   internal_data_pt(this->P_nst_internal_index)->value(0) -
01108   octree_pt()->son_pt(LUB)->object_pt()->
01109   internal_data_pt(this->P_nst_internal_index)->value(0);
01110  
01111  
01112  // Use the average
01113  internal_data_pt(this->P_nst_internal_index)->
01114   set_value(1,0.25*(slope1+slope2+slope3+slope4));
01115  
01116  
01117    //Slope in s_1 direction
01118    //----------------------
01119  
01120    // Use average of the 4 FD approximations based on the 
01121    // elements central pressure values
01122    // [Other options: Take average of the four 
01123    // pressure derivatives]
01124  
01125  slope1 = 
01126   octree_pt()->son_pt(LUB)->object_pt()
01127   ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01128   octree_pt()->son_pt(LDB)->object_pt()
01129   ->internal_data_pt(this->P_nst_internal_index)->value(0);
01130  
01131  slope2 = 
01132   octree_pt()->son_pt(RUB)->object_pt()
01133   ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01134   octree_pt()->son_pt(RDB)->object_pt()
01135   ->internal_data_pt(this->P_nst_internal_index)->value(0);
01136    
01137  slope3 = 
01138   octree_pt()->son_pt(LUF)->object_pt()
01139   ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01140   octree_pt()->son_pt(LDF)->object_pt()
01141   ->internal_data_pt(this->P_nst_internal_index)->value(0);
01142 
01143  slope4 = 
01144   octree_pt()->son_pt(RUF)->object_pt()
01145   ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01146   octree_pt()->son_pt(RDF)->object_pt()
01147   ->internal_data_pt(this->P_nst_internal_index)->value(0);
01148 
01149 
01150    // Use the average
01151  internal_data_pt(this->P_nst_internal_index)->
01152   set_value(2,0.25*(slope1+slope2+slope3+slope4));
01153    
01154 
01155    //Slope in s_2 direction
01156    //----------------------
01157 
01158    // Use average of the 4 FD approximations based on the 
01159    // elements central pressure values
01160    // [Other options: Take average of the four 
01161    // pressure derivatives]
01162 
01163    slope1 =
01164     octree_pt()->son_pt(LUF)->object_pt()
01165     ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01166     octree_pt()->son_pt(LUB)->object_pt()
01167     ->internal_data_pt(this->P_nst_internal_index)->value(0);
01168 
01169    slope2 =
01170     octree_pt()->son_pt(RUF)->object_pt()
01171     ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01172     octree_pt()->son_pt(RUB)->object_pt()
01173     ->internal_data_pt(this->P_nst_internal_index)->value(0);
01174    
01175    slope3 =
01176     octree_pt()->son_pt(LDF)->object_pt()
01177     ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01178     octree_pt()->son_pt(LDB)->object_pt()
01179     ->internal_data_pt(this->P_nst_internal_index)->value(0);
01180    
01181    slope4 =
01182     octree_pt()->son_pt(RDF)->object_pt()
01183     ->internal_data_pt(this->P_nst_internal_index)->value(0) -
01184     octree_pt()->son_pt(RDB)->object_pt()
01185     ->internal_data_pt(this->P_nst_internal_index)->value(0);
01186 
01187    // Use the average
01188    internal_data_pt(this->P_nst_internal_index)->
01189     set_value(3,0.25*(slope1+slope2+slope3+slope4));
01190 
01191 }
01192 
01193 
01194 //======================================================================
01195 /// 2D Further build for Crouzeix_Raviart interpolates the internal 
01196 /// pressure dofs from father element: Make sure pressure values and 
01197 /// dp/ds agree between fathers and sons at the midpoints of the son 
01198 /// elements.
01199 //======================================================================
01200 template<>
01201 inline void RefineableQCrouzeixRaviartElement<2>::further_build()
01202 { 
01203  //Call the generic further build
01204  RefineableNavierStokesEquations<2>::further_build();
01205  
01206  using namespace QuadTreeNames;
01207  
01208  // What type of son am I? Ask my quadtree representation...
01209  int son_type=quadtree_pt()->son_type();
01210  
01211  // Pointer to my father (in element impersonation)
01212  RefineableElement* father_el_pt= quadtree_pt()->father_pt()->object_pt();
01213  
01214  Vector<double> s_father(2);
01215  
01216  // Son midpoint is located at the following coordinates in father element:
01217  
01218  // South west son
01219  if (son_type==SW)
01220   {
01221    s_father[0]=-0.5;
01222    s_father[1]=-0.5;
01223   }
01224  // South east son
01225  else if (son_type==SE)
01226   {
01227    s_father[0]= 0.5;
01228    s_father[1]=-0.5;
01229   }
01230  // North east son
01231  else if (son_type==NE)
01232   {
01233    s_father[0]=0.5;
01234    s_father[1]=0.5;
01235   }
01236  
01237  // North west son
01238  else if (son_type==NW)
01239   {
01240    s_father[0]=-0.5;
01241    s_father[1]= 0.5;
01242   }
01243  
01244  // Pressure value in father element
01245  RefineableQCrouzeixRaviartElement<2>* cast_father_element_pt=
01246   dynamic_cast<RefineableQCrouzeixRaviartElement<2>*>(father_el_pt);
01247  
01248  double press=cast_father_element_pt->interpolated_p_nst(s_father);
01249  
01250  // Pressure value gets copied straight into internal dof:
01251  internal_data_pt(this->P_nst_internal_index)->set_value(0,press);
01252   
01253  // The slopes get copied from father
01254  for(unsigned i=1;i<3;i++)
01255   {
01256    double half_father_slope = 0.5*cast_father_element_pt->
01257     internal_data_pt(this->P_nst_internal_index)->value(i);
01258    //Set the value in the son
01259    internal_data_pt(this->P_nst_internal_index)->
01260     set_value(i,half_father_slope);
01261   }
01262 }
01263 
01264 
01265 //=======================================================================
01266 /// 3D Further build for Crouzeix_Raviart interpolates the internal 
01267 /// pressure dofs from father element: Make sure pressure values and 
01268 /// dp/ds agree between fathers and sons at the midpoints of the son 
01269 /// elements.
01270 //=======================================================================
01271 template<>
01272 inline void RefineableQCrouzeixRaviartElement<3>::further_build()
01273 { 
01274  RefineableNavierStokesEquations<3>::further_build();
01275  
01276  using namespace OcTreeNames;
01277  
01278  // What type of son am I? Ask my octree representation...
01279  int son_type=octree_pt()->son_type();
01280  
01281  // Pointer to my father (in element impersonation)
01282  RefineableQElement<3>* father_el_pt=
01283   dynamic_cast<RefineableQElement<3>*>(
01284    octree_pt()->father_pt()->object_pt());
01285  
01286  Vector<double> s_father(3);
01287  
01288  // Son midpoint is located at the following coordinates in father element:
01289  for(unsigned i=0;i<3;i++)
01290   {
01291    s_father[i]=0.5*OcTree::Direction_to_vector[son_type][i];
01292   }
01293  
01294  // Pressure value in father element
01295  RefineableQCrouzeixRaviartElement<3>* cast_father_element_pt=
01296   dynamic_cast<RefineableQCrouzeixRaviartElement<3>*>(father_el_pt);
01297  
01298  double press=cast_father_element_pt->interpolated_p_nst(s_father);
01299  
01300  // Pressure value gets copied straight into internal dof:
01301  internal_data_pt(this->P_nst_internal_index)->set_value(0,press);
01302  
01303  // The slopes get copied from father
01304  for(unsigned i=1;i<4;i++)
01305   {
01306    double half_father_slope = 0.5*cast_father_element_pt
01307     ->internal_data_pt(this->P_nst_internal_index)->value(i);
01308    //Set the value
01309    internal_data_pt(this->P_nst_internal_index)
01310     ->set_value(i,half_father_slope);
01311   }
01312 }
01313 
01314 }
01315 
01316 #endif

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