refineable_polar_navier_stokes_elements.h
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 //Header file for refineable 2D Polar Navier Stokes elements
31 // Created (approx 12/03/08) by copying and combining oomph-lib's existing:
32 // - refineable_navier_stokes_elements.h
33 // - refineable_navier_stokes_elements.cc
34 
35 // 13/06/08 - Weakform adjusted to "correct" traction version
36 
37 #ifndef OOMPH_REFINEABLE_POLAR_NAVIER_STOKES_HEADER
38 #define OOMPH_REFINEABLE_POLAR_NAVIER_STOKES_HEADER
39 
40 // Config header generated by autoconfig
41 #ifdef HAVE_CONFIG_H
42 #include <oomph-lib-config.h>
43 #endif
44 
45 //Oomph-lib headers
46 //Should already be looking in build/include/ for generic.h
47 #include "../generic/refineable_quad_element.h"
48 #include "../generic/error_estimator.h"
50 
51 namespace oomph
52 {
53 
54 //======================================================================
55 /// Refineable version of my Polar Navier--Stokes equations
56 ///
57 ///
58 //======================================================================
60 public virtual PolarNavierStokesEquations,
61 public virtual RefineableElement,
62 public virtual ElementWithZ2ErrorEstimator
63 {
64  protected:
65 
66  /// \short Pointer to n_p-th pressure node (Default: NULL,
67  /// indicating that pressure is not based on nodal interpolation).
68  virtual Node* pressure_node_pt(const unsigned& n_p) {return NULL;}
69 
70  /// \short Unpin all pressure dofs in the element
71  virtual void unpin_elemental_pressure_dofs()=0;
72 
73 /// \short Pin unused nodal pressure dofs (empty by default, because
74  /// by default pressure dofs are not associated with nodes)
76 
77  public:
78 
79  /// \short Constructor
84 
85 
86  /// \short Loop over all elements in Vector (which typically contains
87  /// all the elements in a fluid mesh) and pin the nodal pressure degrees
88  /// of freedom that are not being used. Function uses
89  /// the member function
90  /// - \c RefineablePolarNavierStokesEquations::
91  /// pin_elemental_redundant_nodal_pressure_dofs()
92  /// .
93  /// which is empty by default and should be implemented for
94  /// elements with nodal pressure degrees of freedom
95  /// (e.g. for refineable Taylor-Hood.)
97  element_pt)
98  {
99  // Loop over all elements and call the function that pins their
100  // unused nodal pressure data
101  unsigned n_element = element_pt.size();
102  for(unsigned e=0;e<n_element;e++)
103  {
104  dynamic_cast<RefineablePolarNavierStokesEquations*>(element_pt[e])->
106  }
107  }
108 
109  /// \short Unpin all pressure dofs in elements listed in vector.
111  element_pt)
112  {
113  // Loop over all elements to brutally unpin all nodal pressure degrees of
114  // freedom and internal pressure degrees of freedom
115  unsigned n_element = element_pt.size();
116  for(unsigned e=0;e<n_element;e++)
117  {
118  dynamic_cast<RefineablePolarNavierStokesEquations*>(element_pt[e])->
120  }
121  }
122 
123 
124  /// Number of 'flux' terms for Z2 error estimation
125  unsigned num_Z2_flux_terms()
126  {
127  // DIM diagonal strain rates, DIM(DIM -1) /2 off diagonal rates
128  return 3;
129  }
130 
131  /// \short Get 'flux' for Z2 error recovery: Upper triangular entries
132  /// in strain rate tensor.
134  {
135 #ifdef PARANOID
136  unsigned num_entries=3;
137  if (flux.size()!=num_entries)
138  {
139  std::ostringstream error_message;
140  error_message << "The flux vector has the wrong number of entries, "
141  << flux.size() << ", whereas it should be "
142  << num_entries << std::endl;
143  throw OomphLibError(error_message.str(),
144  OOMPH_CURRENT_FUNCTION,
145  OOMPH_EXCEPTION_LOCATION);
146  }
147 #endif
148 
149  // Get strain rate matrix
150  DenseMatrix<double> strainrate(2);
151  //this->strain_rate(s,strainrate);
152  this->strain_rate_by_r(s,strainrate);
153 
154  // Pack into flux Vector
155  unsigned icount=0;
156 
157  // Start with diagonal terms
158  for(unsigned i=0;i<2;i++)
159  {
160  flux[icount]=strainrate(i,i);
161  icount++;
162  }
163 
164  //Off diagonals row by row
165  for(unsigned i=0;i<2;i++)
166  {
167  for(unsigned j=i+1;j<2;j++)
168  {
169  flux[icount]=strainrate(i,j);
170  icount++;
171  }
172  }
173  }
174 
175  /// Further build, pass the pointers down to the sons
177  {
178  //Find the father element
179  RefineablePolarNavierStokesEquations* cast_father_element_pt
180  = dynamic_cast<RefineablePolarNavierStokesEquations*>
181  (this->father_element_pt());
182 
183  //Set the viscosity ratio pointer
184  this->Viscosity_Ratio_pt = cast_father_element_pt->viscosity_ratio_pt();
185  //Set the density ratio pointer
186  this->Density_Ratio_pt = cast_father_element_pt->density_ratio_pt();
187  //Set pointer to global Reynolds number
188  this->Re_pt = cast_father_element_pt->re_pt();
189  //Set pointer to global Reynolds number x Strouhal number (=Womersley)
190  this->ReSt_pt = cast_father_element_pt->re_st_pt();
191  //Set pointer to global Reynolds number x inverse Froude number
192  this->ReInvFr_pt = cast_father_element_pt->re_invfr_pt();
193  //Set pointer to global gravity Vector
194  this->G_pt = cast_father_element_pt->g_pt();
195  //Set pointer to alpha
196  this->Alpha_pt = cast_father_element_pt->alpha_pt();
197 
198  //Set pointer to body force function
199  this->Body_force_fct_pt = cast_father_element_pt->body_force_fct_pt();
200 
201  //Set pointer to volumetric source function
202  this->Source_fct_pt = cast_father_element_pt->source_fct_pt();
203  }
204 
205  protected:
206 
207 
208 /// \short Add element's contribution to elemental residual vector and/or
209 /// Jacobian matrix
210 /// flag=1: compute both
211 /// flag=0: compute only residual vector
212  virtual void fill_in_generic_residual_contribution(Vector<double> &residuals,
213  DenseMatrix<double> &jacobian,
214  DenseMatrix<double> &mass_matrix,
215  unsigned flag);
216 
217 };
218 
219 
220 //======================================================================
221 /// Refineable version of Polar Taylor Hood elements. These classes
222 /// can be written in total generality.
223 //======================================================================
227 public virtual RefineableQElement<2>
228 {
229  private:
230 
231  /// \short Pointer to n_p-th pressure node
232  Node* pressure_node_pt(const unsigned &n_p)
233  {return this->node_pt(this->Pconv[n_p]);}
234 
235  /// Unpin all pressure dofs
237  {
238  //find the index at which the pressure is stored
239  int p_index = this->p_nodal_index_pnst();
240  unsigned n_node = this->nnode();
241  // loop over nodes
242  for(unsigned n=0;n<n_node;n++)
243  {this->node_pt(n)->unpin(p_index);}
244  }
245 
246  /// Pin all nodal pressure dofs that are not required
248  {
249  //Find the pressure index
250  int p_index = this->p_nodal_index_pnst();
251  //Loop over all nodes
252  unsigned n_node = this->nnode();
253  // loop over all nodes and pin all the nodal pressures
254  for(unsigned n=0;n<n_node;n++) {this->node_pt(n)->pin(p_index);}
255 
256  // Loop over all actual pressure nodes and unpin if they're not hanging
257  unsigned n_pres = this->npres_pnst();
258  for(unsigned l=0;l<n_pres;l++)
259  {
260  Node* nod_pt = this->pressure_node_pt(l);
261  if (!nod_pt->is_hanging(p_index)) {nod_pt->unpin(p_index);}
262  }
263  }
264 
265  public:
266 
267  /// \short Constructor
271  RefineableQElement<2>(),
273 
274  /// \short Number of values required at local node n. In order to simplify
275  /// matters, we allocate storage for pressure variables at all the nodes
276  /// and then pin those that are not used.
277  unsigned required_nvalue(const unsigned &n) const {return 3;}
278 
279  /// Number of continuously interpolated values: (DIM velocities + 1 pressure)
280  unsigned ncont_interpolated_values() const {return 3;}
281 
282  /// Rebuild from sons: empty
283  void rebuild_from_sons(Mesh* &mesh_pt) {}
284 
285  /// \short Order of recovery shape functions for Z2 error estimation:
286  /// Same order as shape functions.
287  unsigned nrecovery_order() {return 2;}
288 
289  /// \short Number of vertex nodes in the element
290  unsigned nvertex_node() const
292 
293  /// \short Pointer to the j-th vertex node in the element
294  Node* vertex_node_pt(const unsigned& j) const
296 
297 /// \short Get the function value u in Vector.
298 /// Note: Given the generality of the interface (this function
299 /// is usually called from black-box documentation or interpolation routines),
300 /// the values Vector sets its own size in here.
302  {
303  // Set size of Vector: u,v,p and initialise to zero
304  values.resize(3,0.0);
305 
306  //Calculate velocities: values[0],...
307  for(unsigned i=0;i<2;i++) {values[i] = this->interpolated_u_pnst(s,i);}
308 
309  //Calculate pressure: values[DIM]
310  values[2] = this->interpolated_p_pnst(s);
311  }
312 
313 /// \short Get the function value u in Vector.
314 /// Note: Given the generality of the interface (this function
315 /// is usually called from black-box documentation or interpolation routines),
316 /// the values Vector sets its own size in here.
317  void get_interpolated_values(const unsigned& t,
318  const Vector<double>&s,
319  Vector<double>& values)
320  {
321 #ifdef PARANOID
322  //Find out the number of timesteps (present & previous)
323  // (the present element might not have been initialised yet but
324  // its root must know about the time integrator)
325  RefineableElement* root_el_pt = this->tree_pt()->root_pt()->object_pt();
326 
327  unsigned N_prev_time = root_el_pt->node_pt(0)->
329 
330  if (t>N_prev_time)
331  {
332  std::ostringstream error_message;
333  error_message
334  << "The value of t in get_interpolated_values(...), "
335  << t << std::endl
336  << "is greater than the number of previous stored timesteps";
337 
338  throw OomphLibError(
339  error_message.str(),
340  OOMPH_CURRENT_FUNCTION,
341  OOMPH_EXCEPTION_LOCATION);
342  }
343 #endif
344 
345  // Set size of Vector: u,v,p
346  values.resize(2+1);
347 
348  // Initialise
349  for(unsigned i=0;i<2+1;i++) {values[i]=0.0;}
350 
351  //Find out how many nodes there are
352  unsigned n_node = this->nnode();
353 
354  // Shape functions
355  Shape psif(n_node);
356  this->shape(s,psif);
357 
358  //Calculate velocities: values[0],...
359  for(unsigned i=0;i<2;i++)
360  {
361  //Get the index at which the i-th velocity is stored
362  unsigned u_nodal_index = this->u_index_pnst(i);
363  for(unsigned l=0;l<n_node;l++)
364  {
365  values[i] += this->nodal_value(t,l,u_nodal_index)*psif[l];
366  }
367  }
368 
369  //Calculate pressure: values[DIM]
370  //(no history is carried in the pressure)
371  values[2] = this->interpolated_p_pnst(s);
372  }
373 
374  /// \short Perform additional hanging node procedures for variables
375  /// that are not interpolated by all nodes. The pressures are stored
376  /// at the p_nodal_index_pnst-th location in each node
378  {
379  this->setup_hang_for_value(this->p_nodal_index_pnst());
380  }
381 
382  /// \short The velocities are isoparametric and so the "nodes" interpolating
383  /// the velocities are the geometric nodes. The pressure "nodes" are a
384  /// subset of the nodes, so when value_id==DIM, the n-th pressure
385  /// node is returned.
386  Node* interpolating_node_pt(const unsigned &n,
387  const int &value_id)
388 
389  {
390  //The only different nodes are the pressure nodes
391  if(value_id==2) {return this->pressure_node_pt(n);}
392  //The other variables are interpolated via the usual nodes
393  else {return this->node_pt(n);}
394  }
395 
396  /// \short The pressure nodes are the corner nodes, so when n_value==DIM,
397  /// the fraction is the same as the 1d node number, 0 or 1.
398  double local_one_d_fraction_of_interpolating_node(const unsigned &n1d,
399  const unsigned &i,
400  const int &value_id)
401  {
402  if(value_id==2)
403  {
404  //The pressure nodes are just located on the boundaries at 0 or 1
405  return double(n1d);
406  }
407  //Otherwise the velocity nodes are the same as the geometric ones
408  else
409  {
410  return this->local_one_d_fraction_of_node(n1d,i);
411  }
412  }
413 
414  /// \short The velocity nodes are the same as the geometric nodes. The
415  /// pressure nodes must be calculated by using the same methods as
416  /// the geometric nodes, but by recalling that there are only two pressure
417  /// nodes per edge.
419  const int &value_id)
420  {
421  //If we are calculating pressure nodes
422  if(value_id==2)
423  {
424  //Storage for the index of the pressure node
425  unsigned total_index=0;
426  //The number of nodes along each 1d edge is 2.
427  unsigned NNODE_1D = 2;
428  //Storage for the index along each boundary
429  Vector<int> index(2);
430  //Loop over the coordinates
431  for(unsigned i=0;i<2;i++)
432  {
433  //If we are at the lower limit, the index is zero
434  if(s[i]==-1.0)
435  {
436  index[i] = 0;
437  }
438  //If we are at the upper limit, the index is the number of nodes minus 1
439  else if(s[i] == 1.0)
440  {
441  index[i] = NNODE_1D-1;
442  }
443  //Otherwise, we have to calculate the index in general
444  else
445  {
446  //For uniformly spaced nodes the 0th node number would be
447  double float_index = 0.5*(1.0 + s[i])*(NNODE_1D-1);
448  index[i] = int(float_index);
449  //What is the excess. This should be safe because the
450  //taking the integer part rounds down
451  double excess = float_index - index[i];
452  //If the excess is bigger than our tolerance there is no node,
453  //return null
454  if((excess > FiniteElement::Node_location_tolerance) && (
455  (1.0 - excess) > FiniteElement::Node_location_tolerance))
456  {
457  return 0;
458  }
459  }
460  ///Construct the general pressure index from the components.
461  total_index += index[i]*
462  static_cast<unsigned>(pow(static_cast<float>(NNODE_1D),
463  static_cast<int>(i)));
464  }
465  //If we've got here we have a node, so let's return a pointer to it
466  return this->pressure_node_pt(total_index);
467  }
468  //Otherwise velocity nodes are the same as pressure nodes
469  else
470  {
471  return this->get_node_at_local_coordinate(s);
472  }
473  }
474 
475 
476  /// \short The number of 1d pressure nodes is 2, the number of 1d velocity
477  /// nodes is the same as the number of 1d geometric nodes.
478  unsigned ninterpolating_node_1d(const int &value_id)
479  {
480  if(value_id==2) {return 2;}
481  else {return this->nnode_1d();}
482  }
483 
484  /// \short The number of pressure nodes is 2^DIM. The number of
485  /// velocity nodes is the same as the number of geometric nodes.
486  unsigned ninterpolating_node(const int &value_id)
487  {
488  if(value_id==2)
489  {return static_cast<unsigned>(pow(2.0,static_cast<int>(2)));}
490  else {return this->nnode();}
491  }
492 
493  /// \short The basis interpolating the pressure is given by pshape().
494  //// The basis interpolating the velocity is shape().
496  Shape &psi,
497  const int &value_id) const
498  {
499  if(value_id==2) {return this->pshape_pnst(s,psi);}
500  else {return this->shape(s,psi);}
501  }
502 
503 
504  /// \short Add to the set \c paired_load_data pairs containing
505  /// - the pointer to a Data object
506  /// and
507  /// - the index of the value in that Data object
508  /// .
509  /// for all values (pressures, velocities) that affect the
510  /// load computed in the \c get_load(...) function.
511  /// (Overloads non-refineable version and takes hanging nodes
512  /// into account)
513  void insert_load_data(std::set<std::pair<Data*,unsigned> > &paired_load_data)
514  {
515  //Get the nodal indices at which the velocities are stored
516  unsigned u_index[2];
517  for(unsigned i=0;i<2;i++) {u_index[i] = this->u_index_pnst(i);}
518 
519  //Loop over the nodes
520  unsigned n_node = this->nnode();
521  for(unsigned n=0;n<n_node;n++)
522  {
523  // Pointer to current node
524  Node* nod_pt=this->node_pt(n);
525 
526  // Check if it's hanging:
527  if (nod_pt->is_hanging())
528  {
529  // It's hanging -- get number of master nodes
530  unsigned nmaster=nod_pt->hanging_pt()->nmaster();
531 
532  // Loop over masters
533  for (unsigned j=0;j<nmaster;j++)
534  {
535  Node* master_nod_pt=nod_pt->hanging_pt()->master_node_pt(j);
536 
537  //Loop over the velocity components and add pointer to their data
538  //and indices to the vectors
539  for(unsigned i=0;i<2;i++)
540  {
541  paired_load_data.insert(std::make_pair(master_nod_pt,u_index[i]));
542  }
543  }
544  }
545  //Not hanging
546  else
547  {
548  //Loop over the velocity components and add pointer to their data
549  //and indices to the vectors
550  for(unsigned i=0;i<2;i++)
551  {
552  paired_load_data.insert(std::make_pair(this->node_pt(n),u_index[i]));
553  }
554  }
555  }
556 
557  //Get the nodal index at which the pressure is stored
558  int p_index = this->p_nodal_index_pnst();
559 
560  //Loop over the pressure data
561  unsigned n_pres= this->npres_pnst();
562  for(unsigned l=0;l<n_pres;l++)
563  {
564  //Get the pointer to the nodal pressure
565  Node* pres_node_pt = this->pressure_node_pt(l);
566  // Check if the pressure dof is hanging
567  if(pres_node_pt->is_hanging(p_index))
568  {
569  //Get the pointer to the hang info object
570  // (pressure is stored as p_index--th nodal dof).
571  HangInfo* hang_info_pt = pres_node_pt->hanging_pt(p_index);
572 
573  // Get number of pressure master nodes (pressure is stored
574  unsigned nmaster = hang_info_pt->nmaster();
575 
576  // Loop over pressure master nodes
577  for(unsigned m=0;m<nmaster;m++)
578  {
579  //The p_index-th entry in each nodal data is the pressure, which
580  //affects the traction
581  paired_load_data.insert(
582  std::make_pair(hang_info_pt->master_node_pt(m),p_index));
583  }
584  }
585  // It's not hanging
586  else
587  {
588  //The p_index-th entry in each nodal data is the pressure, which
589  //affects the traction
590  paired_load_data.insert(std::make_pair(pres_node_pt,p_index));
591  }
592  }
593 
594  }
595 
596 };
597 
598 
599 //=======================================================================
600 /// \short Face geometry of the RefineablePolarTaylorHoodElements is the
601 /// same as the Face geometry of the PolarTaylorHoodElements.
602 //=======================================================================
603 template<>
605 public virtual FaceGeometry<PolarTaylorHoodElement >
606 {
607  public:
609 };
610 
611 
612 ///////////////////////////////////////////////////////////////////////////
613 ///////////////////////////////////////////////////////////////////////////
614 ///////////////////////////////////////////////////////////////////////////
615 
616 
617 //======================================================================
618 /// Refineable version of Crouzeix Raviart elements. Generic class definitions
619 //======================================================================
623 public virtual RefineableQElement<2>
624 {
625  private:
626 
627  /// Unpin all internal pressure dofs
629  {
630  unsigned n_pres = this->npres_pnst();
631  // loop over pressure dofs and unpin them
632  for(unsigned l=0;l<n_pres;l++)
633  {this->internal_data_pt(this->P_pnst_internal_index)->unpin(l);}
634  }
635 
636  public:
637 
638  /// \short Constructor
642  RefineableQElement<2>(),
644 
645  /// Number of continuously interpolated values: DIM (velocities)
646  unsigned ncont_interpolated_values() const {return 2;}
647 
648  /// \short Rebuild from sons: Reconstruct pressure from the (merged) sons
649  /// This must be specialised for each dimension.
650  inline void rebuild_from_sons(Mesh* &mesh_pt);
651 
652  /// \short Order of recovery shape functions for Z2 error estimation:
653  /// Same order as shape functions.
654  unsigned nrecovery_order() {return 2;}
655 
656  /// \short Number of vertex nodes in the element
657  unsigned nvertex_node() const
659 
660  /// \short Pointer to the j-th vertex node in the element
661  Node* vertex_node_pt(const unsigned& j) const
662  {
664  }
665 
666 /// \short Get the function value u in Vector.
667 /// Note: Given the generality of the interface (this function
668 /// is usually called from black-box documentation or interpolation routines),
669 /// the values Vector sets its own size in here.
671  {
672  // Set size of Vector: u,v,p and initialise to zero
673  values.resize(2,0.0);
674 
675  //Calculate velocities: values[0],...
676  for(unsigned i=0;i<2;i++) {values[i] = this->interpolated_u_pnst(s,i);}
677  }
678 
679  /// \short Get all function values [u,v..,p] at previous timestep t
680  /// (t=0: present; t>0: previous timestep).
681  /// \n
682  /// Note: Given the generality of the interface (this function
683  /// is usually called from black-box documentation or interpolation routines),
684  /// the values Vector sets its own size in here.
685  /// \n
686  /// Note: No pressure history is kept, so pressure is always
687  /// the current value.
688 void get_interpolated_values(const unsigned& t,
689  const Vector<double>&s,
690  Vector<double>& values)
691  {
692 #ifdef PARANOID
693 
694  //Find out the number of timesteps (present & previous)
695  // (the present element might not have been initialised yet but
696  // its root must know about the time integrator)
697  RefineableElement* root_el_pt = this->tree_pt()->root_pt()->object_pt();
698 
699  unsigned N_prev_time = root_el_pt->node_pt(0)->
701 
702  if (t>N_prev_time)
703  {
704  std::ostringstream error_message;
705  error_message
706  << "The value of t in get_interpolated_values(...), "
707  << t << std::endl
708  << "is greater than the number of previous stored timesteps";
709 
710  throw OomphLibError(
711  error_message.str(),
712  OOMPH_CURRENT_FUNCTION,
713  OOMPH_EXCEPTION_LOCATION);
714  }
715 #endif
716 
717  // Set size of Vector: u,v,p
718  values.resize(2);
719 
720  // Initialise
721  for(unsigned i=0;i<2;i++) {values[i]=0.0;}
722 
723  //Find out how many nodes there are
724  unsigned n_node = this->nnode();
725 
726  // Shape functions
727  Shape psif(n_node);
728  this->shape(s,psif);
729 
730  //Calculate velocities: values[0],...
731  for(unsigned i=0;i<2;i++)
732  {
733  //Get the nodal index at which the i-th velocity component is stored
734  unsigned u_nodal_index = this->u_index_pnst(i);
735  for(unsigned l=0;l<n_node;l++)
736  {
737  values[i] += this->nodal_value(t,l,u_nodal_index)*psif[l];
738  }
739  }
740  }
741 
742  /// \short Perform additional hanging node procedures for variables
743  /// that are not interpolated by all nodes. Empty
745 
746  /// Further build for Crouzeix_Raviart interpolates the internal
747  /// pressure dofs from father element: Make sure pressure values and
748  /// dp/ds agree between fathers and sons at the midpoints of the son
749  /// elements. This must be specialised for each dimension.
750  inline void further_build();
751 
752 
753 
754  /// \short Add to the set \c paired_load_data pairs containing
755  /// - the pointer to a Data object
756  /// and
757  /// - the index of the value in that Data object
758  /// .
759  /// for all values (pressures, velocities) that affect the
760  /// load computed in the \c get_load(...) function.
761  /// (Overloads non-refineable version and takes hanging nodes
762  /// into account)
763  void insert_load_data(std::set<std::pair<Data*,unsigned> > &paired_load_data)
764  {
765  //Get the nodal indices at which the velocities are stored
766  unsigned u_index[2];
767  for(unsigned i=0;i<2;i++) {u_index[i] = this->u_index_pnst(i);}
768 
769  //Loop over the nodes
770  unsigned n_node = this->nnode();
771  for(unsigned n=0;n<n_node;n++)
772  {
773  // Pointer to current node
774  Node* nod_pt=this->node_pt(n);
775 
776  // Check if it's hanging:
777  if (nod_pt->is_hanging())
778  {
779  // It's hanging -- get number of master nodes
780  unsigned nmaster=nod_pt->hanging_pt()->nmaster();
781 
782  // Loop over masters
783  for (unsigned j=0;j<nmaster;j++)
784  {
785  Node* master_nod_pt=nod_pt->hanging_pt()->master_node_pt(j);
786 
787  //Loop over the velocity components and add pointer to their data
788  //and indices to the vectors
789  for(unsigned i=0;i<2;i++)
790  {
791  paired_load_data.insert(std::make_pair(master_nod_pt,u_index[i]));
792  }
793  }
794  }
795  //Not hanging
796  else
797  {
798  //Loop over the velocity components and add pointer to their data
799  //and indices to the vectors
800  for(unsigned i=0;i<2;i++)
801  {
802  paired_load_data.insert(std::make_pair(this->node_pt(n),u_index[i]));
803  }
804  }
805  }
806 
807 
808  //Loop over the pressure data (can't be hanging!)
809  unsigned n_pres = this->npres_pnst();
810  for(unsigned l=0;l<n_pres;l++)
811  {
812  //The entries in the internal data at P_pnst_internal_index
813  //are the pressures, which affect the traction
814  paired_load_data.insert(
815  std::make_pair(this->internal_data_pt(this->P_pnst_internal_index),l));
816  }
817  }
818 
819 
820 };
821 
822 
823 //=======================================================================
824 /// Face geometry of the RefineableQuadQCrouzeixRaviartElements
825 //=======================================================================
826 template<>
828 public virtual FaceGeometry<PolarCrouzeixRaviartElement >
829 {
830  public:
832 };
833 
834 
835 //=====================================================================
836 /// 2D Rebuild from sons: Reconstruct pressure from the (merged) sons
837 //=====================================================================
840 {
841  using namespace QuadTreeNames;
842 
843  //Central pressure value:
844  //-----------------------
845 
846  // Use average of the sons central pressure values
847  // Other options: Take average of the four (discontinuous)
848  // pressure values at the father's midpoint]
849 
850  double av_press=0.0;
851 
852  // Loop over the sons
853  for (unsigned ison=0;ison<4;ison++)
854  {
855  // Add the sons midnode pressure
856  // Note that we can assume that the pressure is stored at the same
857  // location because these are EXACTLY the same type of elements
858  av_press += quadtree_pt()->son_pt(ison)->object_pt()->
859  internal_data_pt(this->P_pnst_internal_index)->value(0);
860  }
861 
862  // Use the average
863  internal_data_pt(this->P_pnst_internal_index)->set_value(0,0.25*av_press);
864 
865 
866  //Slope in s_0 direction
867  //----------------------
868 
869  // Use average of the 2 FD approximations based on the
870  // elements central pressure values
871  // [Other options: Take average of the four
872  // pressure derivatives]
873 
874  double slope1=
875  quadtree_pt()->son_pt(SE)->object_pt()->
876  internal_data_pt(this->P_pnst_internal_index)->value(0) -
877  quadtree_pt()->son_pt(SW)->object_pt()->
878  internal_data_pt(this->P_pnst_internal_index)->value(0);
879 
880  double slope2 =
881  quadtree_pt()->son_pt(NE)->object_pt()->
882  internal_data_pt(this->P_pnst_internal_index)->value(0) -
883  quadtree_pt()->son_pt(NW)->object_pt()->
884  internal_data_pt(this->P_pnst_internal_index)->value(0);
885 
886 
887  // Use the average
888  internal_data_pt(this->P_pnst_internal_index)->
889  set_value(1,0.5*(slope1+slope2));
890 
891 
892 
893  //Slope in s_1 direction
894  //----------------------
895 
896  // Use average of the 2 FD approximations based on the
897  // elements central pressure values
898  // [Other options: Take average of the four
899  // pressure derivatives]
900 
901  slope1 =
902  quadtree_pt()->son_pt(NE)->object_pt()
903  ->internal_data_pt(this->P_pnst_internal_index)->value(0) -
904  quadtree_pt()->son_pt(SE)->object_pt()
905  ->internal_data_pt(this->P_pnst_internal_index)->value(0);
906 
907  slope2=
908  quadtree_pt()->son_pt(NW)->object_pt()
909  ->internal_data_pt(this->P_pnst_internal_index)->value(0) -
910  quadtree_pt()->son_pt(SW)->object_pt()
911  ->internal_data_pt(this->P_pnst_internal_index)->value(0);
912 
913 
914  // Use the average
915  internal_data_pt(this->P_pnst_internal_index)->
916  set_value(2,0.5*(slope1+slope2));
917 }
918 
919 //======================================================================
920 /// 2D Further build for Crouzeix_Raviart interpolates the internal
921 /// pressure dofs from father element: Make sure pressure values and
922 /// dp/ds agree between fathers and sons at the midpoints of the son
923 /// elements.
924 //======================================================================
926 {
927  //Call the generic further build
929 
930  using namespace QuadTreeNames;
931 
932  // What type of son am I? Ask my quadtree representation...
933  int son_type=quadtree_pt()->son_type();
934 
935  // Pointer to my father (in element impersonation)
936  RefineableElement* father_el_pt= quadtree_pt()->father_pt()->object_pt();
937 
938  Vector<double> s_father(2);
939 
940  // Son midpoint is located at the following coordinates in father element:
941 
942  // South west son
943  if (son_type==SW)
944  {
945  s_father[0]=-0.5;
946  s_father[1]=-0.5;
947  }
948  // South east son
949  else if (son_type==SE)
950  {
951  s_father[0]= 0.5;
952  s_father[1]=-0.5;
953  }
954  // North east son
955  else if (son_type==NE)
956  {
957  s_father[0]=0.5;
958  s_father[1]=0.5;
959  }
960 
961  // North west son
962  else if (son_type==NW)
963  {
964  s_father[0]=-0.5;
965  s_father[1]= 0.5;
966  }
967 
968  // Pressure value in father element
969  RefineablePolarCrouzeixRaviartElement* cast_father_element_pt=
970  dynamic_cast<RefineablePolarCrouzeixRaviartElement*>(father_el_pt);
971 
972  double press=cast_father_element_pt->interpolated_p_pnst(s_father);
973 
974  // Pressure value gets copied straight into internal dof:
975  internal_data_pt(this->P_pnst_internal_index)->set_value(0,press);
976 
977  // The slopes get copied from father
978  for(unsigned i=1;i<3;i++)
979  {
980  double half_father_slope = 0.5*cast_father_element_pt->
981  internal_data_pt(this->P_pnst_internal_index)->value(i);
982  //Set the value in the son
983  internal_data_pt(this->P_pnst_internal_index)->
984  set_value(i,half_father_slope);
985  }
986 }
987 
988 }
989 #endif
double * Density_Ratio_pt
Pointer to the density ratio (relative to the density used in the definition of the Reynolds number) ...
void unpin(const unsigned &i)
Unpin the i-th stored variable.
Definition: nodes.h:386
double value(const unsigned &i) const
Return i-th stored value. This function is not virtual so that it can be inlined. This means that if ...
Definition: nodes.h:291
void get_interpolated_values(const Vector< double > &s, Vector< double > &values)
Get the function value u in Vector. Note: Given the generality of the interface (this function is usu...
void unpin_elemental_pressure_dofs()
Unpin all internal pressure dofs.
void insert_load_data(std::set< std::pair< Data *, unsigned > > &paired_load_data)
Add to the set paired_load_data pairs containing.
NavierStokesBodyForceFctPt Body_force_fct_pt
Pointer to body force function.
Base class for finite elements that can compute the quantities that are required for the Z2 error est...
double *& re_st_pt()
Pointer to product of Reynolds and Strouhal number (=Womersley number)
unsigned nrecovery_order()
Order of recovery shape functions for Z2 error estimation: Same order as shape functions.
virtual RefineableElement * father_element_pt() const
Return a pointer to the father element.
static void pin_redundant_nodal_pressures(const Vector< GeneralisedElement *> &element_pt)
Loop over all elements in Vector (which typically contains all the elements in a fluid mesh) and pin ...
virtual unsigned npres_pnst() const =0
Function to return number of pressure degrees of freedom.
virtual Node * get_node_at_local_coordinate(const Vector< double > &s) const
If there is a node at this local coordinate, return the pointer to the node.
Definition: elements.cc:3806
void rebuild_from_sons(Mesh *&mesh_pt)
Rebuild from sons: empty.
HangInfo *const & hanging_pt() const
Return pointer to hanging node data (this refers to the geometric hanging node status) (const version...
Definition: nodes.h:1148
void interpolated_u_pnst(const Vector< double > &s, Vector< double > &veloc) const
Compute vector of FE interpolated velocity u at local coordinate s.
cstr elem_len * i
Definition: cfortran.h:607
void insert_load_data(std::set< std::pair< Data *, unsigned > > &paired_load_data)
Add to the set paired_load_data pairs containing.
unsigned nrecovery_order()
Order of recovery shape functions for Z2 error estimation: Same order as shape functions.
void get_interpolated_values(const unsigned &t, const Vector< double > &s, Vector< double > &values)
Get all function values [u,v..,p] at previous timestep t (t=0: present; t>0: previous timestep)...
char t
Definition: cfortran.h:572
Refineable version of Crouzeix Raviart elements. Generic class definitions.
void rebuild_from_sons(Mesh *&mesh_pt)
Rebuild from sons: Reconstruct pressure from the (merged) sons This must be specialised for each dime...
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
unsigned nmaster() const
Return the number of master nodes.
Definition: nodes.h:733
virtual void pshape_pnst(const Vector< double > &s, Shape &psi) const =0
Compute the pressure shape functions at local coordinate s.
void further_setup_hanging_nodes()
Perform additional hanging node procedures for variables that are not interpolated by all nodes...
Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element.
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1207
virtual void fill_in_generic_residual_contribution(Vector< double > &residuals, DenseMatrix< double > &jacobian, DenseMatrix< double > &mass_matrix, unsigned flag)
Add element&#39;s contribution to elemental residual vector and/or Jacobian matrix flag=1: compute both f...
unsigned nvertex_node() const
Number of vertex nodes in the element.
e
Definition: cfortran.h:575
double * Viscosity_Ratio_pt
Pointer to the viscosity ratio (relative to the viscosity used in the definition of the Reynolds numb...
double * ReSt_pt
Pointer to global Reynolds number x Strouhal number (=Womersley)
double *& re_pt()
Pointer to Reynolds number.
void pin(const unsigned &i)
Pin the i-th stored variable.
Definition: nodes.h:383
NavierStokesSourceFctPt Source_fct_pt
Pointer to volumetric source function.
virtual void pin_elemental_redundant_nodal_pressure_dofs()
Pin unused nodal pressure dofs (empty by default, because.
void pin_elemental_redundant_nodal_pressure_dofs()
Pin all nodal pressure dofs that are not required.
TimeStepper *& time_stepper_pt()
Access function for pointer to time stepper: Null if object is not time-dependent.
Definition: geom_objects.h:197
double local_one_d_fraction_of_interpolating_node(const unsigned &n1d, const unsigned &i, const int &value_id)
The pressure nodes are the corner nodes, so when n_value==DIM, the fraction is the same as the 1d nod...
Node * get_interpolating_node_at_local_coordinate(const Vector< double > &s, const int &value_id)
The velocity nodes are the same as the geometric nodes. The pressure nodes must be calculated by usin...
double * Alpha_pt
Pointer to the angle alpha.
virtual int p_nodal_index_pnst()
Which nodal value represents the pressure? (Default: negative, indicating that pressure is not based ...
NavierStokesSourceFctPt & source_fct_pt()
Access function for the source-function pointer.
double * ReInvFr_pt
Pointer to global Reynolds number x inverse Froude number (= Bond number / Capillary number) ...
virtual unsigned nvertex_node() const
Definition: elements.h:2374
static char t char * s
Definition: cfortran.h:572
Tree * tree_pt()
Access function: Pointer to quadtree representation of this element.
void set_value(const unsigned &i, const double &value_)
Set the i-th stored data value to specified value. The only reason that we require an explicit set fu...
Definition: nodes.h:267
Data *& internal_data_pt(const unsigned &i)
Return a pointer to i-th internal data object.
Definition: elements.h:623
virtual unsigned u_index_pnst(const unsigned &i) const
Return the index at which the i-th unknown velocity component.
void further_setup_hanging_nodes()
Perform additional hanging node procedures for variables that are not interpolated by all nodes...
virtual void unpin_elemental_pressure_dofs()=0
Unpin all pressure dofs in the element.
unsigned required_nvalue(const unsigned &n) const
Number of values required at local node n. In order to simplify matters, we allocate storage for pres...
double * Re_pt
Pointer to global Reynolds number.
unsigned ncont_interpolated_values() const
Number of continuously interpolated values: (DIM velocities + 1 pressure)
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:736
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
Vector< double > * G_pt
Pointer to global gravity Vector.
static const double Node_location_tolerance
Default value for the tolerance to be used when locating nodes via local coordinates.
Definition: elements.h:1337
Vector< double > *& g_pt()
Pointer to Vector of gravitational components.
unsigned ninterpolating_node_1d(const int &value_id)
The number of 1d pressure nodes is 2, the number of 1d velocity nodes is the same as the number of 1d...
static void unpin_all_pressure_dofs(const Vector< GeneralisedElement *> &element_pt)
Unpin all pressure dofs in elements listed in vector.
double nodal_value(const unsigned &n, const unsigned &i) const
Return the i-th value stored at local node n. Produces suitably interpolated values for hanging nodes...
Definition: elements.h:2470
void strain_rate_by_r(const Vector< double > &s, DenseMatrix< double > &strain_rate) const
Function to return polar strain multiplied by r.
Class that contains data for hanging nodes.
Definition: nodes.h:684
virtual void rebuild_from_sons(Mesh *&mesh_pt)=0
Rebuild the element, e.g. set internal values in line with those of the sons that have now merged...
Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element.
Node * interpolating_node_pt(const unsigned &n, const int &value_id)
The velocities are isoparametric and so the "nodes" interpolating the velocities are the geometric no...
void get_Z2_flux(const Vector< double > &s, Vector< double > &flux)
Get &#39;flux&#39; for Z2 error recovery: Upper triangular entries in strain rate tensor. ...
virtual unsigned nnode_1d() const
Return the number of nodes along one edge of the element Default is to return zero — must be overloa...
Definition: elements.h:2151
virtual Node * pressure_node_pt(const unsigned &n_p)
Pointer to n_p-th pressure node (Default: NULL, indicating that pressure is not based on nodal interp...
unsigned num_Z2_flux_terms()
Number of &#39;flux&#39; terms for Z2 error estimation.
Node * pressure_node_pt(const unsigned &n_p)
Pointer to n_p-th pressure node.
virtual Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element. Broken virtual function in "pure" finite elements...
Definition: elements.h:2383
double *& re_invfr_pt()
Pointer to global inverse Froude number.
RefineableElement * object_pt() const
Return the pointer to the object (RefineableElement) represented by the tree.
Definition: tree.h:102
void interpolating_basis(const Vector< double > &s, Shape &psi, const int &value_id) const
The basis interpolating the pressure is given by pshape(). / The basis interpolating the velocity is ...
NavierStokesBodyForceFctPt & body_force_fct_pt()
Access function for the body-force pointer.
unsigned ncont_interpolated_values() const
Number of continuously interpolated values: DIM (velocities)
virtual double local_one_d_fraction_of_node(const unsigned &n1d, const unsigned &i)
Get the local fraction of any node in the n-th position in a one dimensional expansion along the i-th...
Definition: elements.h:1818
virtual unsigned nprev_values() const =0
Number of previous values available: 0 for static, 1 for BDF<1>,...
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
void get_interpolated_values(const Vector< double > &s, Vector< double > &values)
Get the function value u in Vector. Note: Given the generality of the interface (this function is usu...
double interpolated_p_pnst(const Vector< double > &s) const
Return FE interpolated pressure at local coordinate s.
double *& viscosity_ratio_pt()
Pointer to Viscosity Ratio.
virtual void shape(const Vector< double > &s, Shape &psi) const =0
Calculate the geometric shape functions at local coordinate s. This function must be overloaded for e...
A general mesh class.
Definition: mesh.h:74
void get_interpolated_values(const unsigned &t, const Vector< double > &s, Vector< double > &values)
Get the function value u in Vector. Note: Given the generality of the interface (this function is usu...
TreeRoot *& root_pt()
Return pointer to root of the tree.
Definition: tree.h:143
double *& density_ratio_pt()
Pointer to Density ratio.
unsigned nvertex_node() const
Number of vertex nodes in the element.
void further_build()
Further build, pass the pointers down to the sons.
unsigned ninterpolating_node(const int &value_id)
The number of pressure nodes is 2^DIM. The number of velocity nodes is the same as the number of geom...