navier_stokes_elements.cc
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 //Non-inline functions for NS elements
31 
32 #include "navier_stokes_elements.h"
33 
34 
35 namespace oomph
36 {
37 
38 
39 
40 
41 
42 
43 
44 /////////////////////////////////////////////////////////////////////////
45 /////////////////////////////////////////////////////////////////////////
46 /////////////////////////////////////////////////////////////////////////
47 
48 
49 /// Navier--Stokes equations static data
50 template<unsigned DIM>
51 Vector<double> NavierStokesEquations<DIM>::Gamma(DIM,1.0);
52 
53 //=================================================================
54 /// "Magic" negative number that indicates that the pressure is
55 /// not stored at a node. This cannot be -1 because that represents
56 /// the positional hanging scheme in the hanging_pt object of nodes
57 //=================================================================
58 template<unsigned DIM>
60 
61 /// Navier--Stokes equations static data
62 template<unsigned DIM>
64 
65 /// Navier--Stokes equations static data
66 template<unsigned DIM>
68 
69 /// Navier-Stokes equations default gravity vector
70 template<unsigned DIM>
72 
73 
74 
75 
76 //===================================================================
77  /// Compute the diagonal of the velocity/pressure mass matrices.
78  /// If which one=0, both are computed, otherwise only the pressure
79  /// (which_one=1) or the velocity mass matrix (which_one=2 -- the
80  /// LSC version of the preconditioner only needs that one)
81 //===================================================================
82  template<unsigned DIM>
85  Vector<double> &press_mass_diag, Vector<double> &veloc_mass_diag,
86  const unsigned& which_one)
87  {
88  // Resize and initialise
89  unsigned n_dof=ndof();
90 
91  if ((which_one==0)||(which_one==1)) press_mass_diag.assign(n_dof,0.0);
92  if ((which_one==0)||(which_one==2)) veloc_mass_diag.assign(n_dof,0.0);
93 
94  // find out how many nodes there are
95  unsigned n_node = nnode();
96 
97  // find number of spatial dimensions
98  unsigned n_dim = this->dim();
99 
100  // Local coordinates
101  Vector<double> s(n_dim);
102 
103  // find the indices at which the local velocities are stored
104  Vector<unsigned> u_nodal_index(n_dim);
105  for(unsigned i=0; i<n_dim; i++)
106  {
107  u_nodal_index[i] = this->u_index_nst(i);
108  }
109 
110  //Set up memory for veloc shape functions
111  Shape psi(n_node);
112 
113  //Find number of pressure dofs
114  unsigned n_pres = npres_nst();
115 
116  // Pressure shape function
117  Shape psi_p(n_pres);
118 
119  //Number of integration points
120  unsigned n_intpt = integral_pt()->nweight();
121 
122  //Integer to store the local equations no
123  int local_eqn=0;
124 
125  //Loop over the integration points
126  for(unsigned ipt=0; ipt<n_intpt; ipt++)
127  {
128 
129  //Get the integral weight
130  double w = integral_pt()->weight(ipt);
131 
132  //Get determinant of Jacobian of the mapping
133  double J = J_eulerian_at_knot(ipt);
134 
135  //Assign values of s
136  for(unsigned i=0;i<n_dim;i++)
137  {
138  s[i] = integral_pt()->knot(ipt,i);
139  }
140 
141  //Premultiply weights and Jacobian
142  double W = w*J;
143 
144 
145 
146  // Do we want the velocity one?
147  if ((which_one==0)||(which_one==2))
148  {
149 
150  //Get the velocity shape functions
151  shape_at_knot(ipt,psi);
152 
153  //Loop over the veclocity shape functions
154  for(unsigned l=0; l<n_node; l++)
155  {
156  //Loop over the velocity components
157  for(unsigned i=0; i<n_dim; i++)
158  {
159  local_eqn = nodal_local_eqn(l,u_nodal_index[i]);
160 
161  //If not a boundary condition
162  if(local_eqn >= 0)
163  {
164  //Add the contribution
165  veloc_mass_diag[local_eqn] += pow(psi[l],2) * W;
166  }
167  }
168  }
169  }
170 
171  // Do we want the pressure one?
172  if ((which_one==0)||(which_one==1))
173  {
174  //Get the pressure shape functions
175  pshape_nst(s,psi_p);
176 
177  //Loop over the veclocity shape functions
178  for(unsigned l=0; l<n_pres; l++)
179  {
180  // Get equation number
181  local_eqn = p_local_eqn(l);
182 
183  //If not a boundary condition
184  if(local_eqn >= 0)
185  {
186  //Add the contribution
187  press_mass_diag[local_eqn] += pow(psi_p[l],2) * W;
188  }
189  }
190  }
191 
192  }
193  }
194 
195 
196 
197 //=======================================================================
198 /// Compute norm of the solution
199 //=======================================================================
200 template <unsigned DIM>
202 {
203 
204  // Initialise
205  norm=0.0;
206 
207  //Vector of local coordinates
208  Vector<double> s(DIM);
209 
210  // Solution
211  double u=0.0;
212 
213  //Find out how many nodes there are in the element
214  unsigned n_node = this->nnode();
215 
216  Shape psi(n_node);
217 
218  //Set the value of n_intpt
219  unsigned n_intpt = this->integral_pt()->nweight();
220 
221  //Loop over the integration points
222  for(unsigned ipt=0;ipt<n_intpt;ipt++)
223  {
224 
225  //Assign values of s
226  for(unsigned i=0;i<DIM;i++)
227  {
228  s[i] = this->integral_pt()->knot(ipt,i);
229  }
230 
231  //Get the integral weight
232  double w = this->integral_pt()->weight(ipt);
233 
234  // Get jacobian of mapping
235  double J=this->J_eulerian(s);
236 
237  //Premultiply the weights and the Jacobian
238  double W = w*J;
239 
240  for (unsigned i=0;i<DIM;i++)
241  {
242  // Get FE function value
243  u=this->interpolated_u_nst(s,i);
244 
245  // Add to norm
246  norm+=u*u*W;
247  }
248 
249  }
250 }
251 
252 
253 //======================================================================
254 /// Validate against exact velocity solution at given time.
255 /// Solution is provided via function pointer.
256 /// Plot at a given number of plot points and compute L2 error
257 /// and L2 norm of velocity solution over element.
258 //=======================================================================
259 template<unsigned DIM>
261 compute_error(std::ostream &outfile,
263  const double& time,
264  double& error, double& norm)
265 {
266  error=0.0;
267  norm=0.0;
268 
269  //Vector of local coordinates
270  Vector<double> s(DIM);
271 
272  // Vector for coordintes
273  Vector<double> x(DIM);
274 
275  //Set the value of n_intpt
276  unsigned n_intpt = integral_pt()->nweight();
277 
278  outfile << "ZONE" << std::endl;
279 
280  // Exact solution Vector (u,v,[w],p)
281  Vector<double> exact_soln(DIM+1);
282 
283  //Loop over the integration points
284  for(unsigned ipt=0;ipt<n_intpt;ipt++)
285  {
286 
287  //Assign values of s
288  for(unsigned i=0;i<DIM;i++)
289  {
290  s[i] = integral_pt()->knot(ipt,i);
291  }
292 
293  //Get the integral weight
294  double w = integral_pt()->weight(ipt);
295 
296  // Get jacobian of mapping
297  double J=J_eulerian(s);
298 
299  //Premultiply the weights and the Jacobian
300  double W = w*J;
301 
302  // Get x position as Vector
303  interpolated_x(s,x);
304 
305  // Get exact solution at this point
306  (*exact_soln_pt)(time,x,exact_soln);
307 
308  // Velocity error
309  for(unsigned i=0;i<DIM;i++)
310  {
311  norm+=exact_soln[i]*exact_soln[i]*W;
312  error+=(exact_soln[i]-interpolated_u_nst(s,i))*
313  (exact_soln[i]-interpolated_u_nst(s,i))*W;
314  }
315 
316  //Output x,y,...,u_exact
317  for(unsigned i=0;i<DIM;i++)
318  {
319  outfile << x[i] << " ";
320  }
321 
322  //Output x,y,[z],u_error,v_error,[w_error]
323  for(unsigned i=0;i<DIM;i++)
324  {
325  outfile << exact_soln[i]-interpolated_u_nst(s,i) << " ";
326  }
327 
328  outfile << std::endl;
329 
330  }
331 }
332 
333 //======================================================================
334 /// Validate against exact velocity solution
335 /// Solution is provided via function pointer.
336 /// Plot at a given number of plot points and compute L2 error
337 /// and L2 norm of velocity solution over element.
338 //=======================================================================
339 template<unsigned DIM>
341  std::ostream &outfile,
343  double& error, double& norm)
344 {
345 
346  error=0.0;
347  norm=0.0;
348 
349  //Vector of local coordinates
350  Vector<double> s(DIM);
351 
352  // Vector for coordintes
353  Vector<double> x(DIM);
354 
355  //Set the value of n_intpt
356  unsigned n_intpt = integral_pt()->nweight();
357 
358 
359  outfile << "ZONE" << std::endl;
360 
361  // Exact solution Vector (u,v,[w],p)
362  Vector<double> exact_soln(DIM+1);
363 
364  //Loop over the integration points
365  for(unsigned ipt=0;ipt<n_intpt;ipt++)
366  {
367 
368  //Assign values of s
369  for(unsigned i=0;i<DIM;i++)
370  {
371  s[i] = integral_pt()->knot(ipt,i);
372  }
373 
374  //Get the integral weight
375  double w = integral_pt()->weight(ipt);
376 
377  // Get jacobian of mapping
378  double J=J_eulerian(s);
379 
380  //Premultiply the weights and the Jacobian
381  double W = w*J;
382 
383  // Get x position as Vector
384  interpolated_x(s,x);
385 
386  // Get exact solution at this point
387  (*exact_soln_pt)(x,exact_soln);
388 
389  // Velocity error
390  for(unsigned i=0;i<DIM;i++)
391  {
392  norm+=exact_soln[i]*exact_soln[i]*W;
393  error+=(exact_soln[i]-interpolated_u_nst(s,i))*
394  (exact_soln[i]-interpolated_u_nst(s,i))*W;
395  }
396 
397  //Output x,y,...,u_exact
398  for(unsigned i=0;i<DIM;i++)
399  {
400  outfile << x[i] << " ";
401  }
402 
403  //Output x,y,[z],u_error,v_error,[w_error]
404  for(unsigned i=0;i<DIM;i++)
405  {
406  outfile << exact_soln[i]-interpolated_u_nst(s,i) << " ";
407  }
408  outfile << std::endl;
409  }
410 }
411 
412 //======================================================================
413 /// Output "exact" solution
414 /// Solution is provided via function pointer.
415 /// Plot at a given number of plot points.
416 /// Function prints as many components as are returned in solution Vector.
417 //=======================================================================
418 template<unsigned DIM>
419 void NavierStokesEquations<DIM>::output_fct(std::ostream &outfile,
420  const unsigned &nplot,
422 {
423 
424  //Vector of local coordinates
425  Vector<double> s(DIM);
426 
427  // Vector for coordintes
428  Vector<double> x(DIM);
429 
430  // Tecplot header info
431  outfile << tecplot_zone_string(nplot);
432 
433  // Exact solution Vector
434  Vector<double> exact_soln;
435 
436  // Loop over plot points
437  unsigned num_plot_points=nplot_points(nplot);
438  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
439  {
440 
441  // Get local coordinates of plot point
442  get_s_plot(iplot,nplot,s);
443 
444  // Get x position as Vector
445  interpolated_x(s,x);
446 
447  // Get exact solution at this point
448  (*exact_soln_pt)(x,exact_soln);
449 
450  //Output x,y,...
451  for(unsigned i=0;i<DIM;i++)
452  {
453  outfile << x[i] << " ";
454  }
455 
456  //Output "exact solution"
457  for(unsigned i=0;i<exact_soln.size();i++)
458  {
459  outfile << exact_soln[i] << " ";
460  }
461 
462  outfile << std::endl;
463 
464  }
465 
466  // Write tecplot footer (e.g. FE connectivity lists)
467  write_tecplot_zone_footer(outfile,nplot);
468 
469 }
470 
471 //======================================================================
472 /// Output "exact" solution at a given time
473 /// Solution is provided via function pointer.
474 /// Plot at a given number of plot points.
475 /// Function prints as many components as are returned in solution Vector.
476 //=======================================================================
477 template<unsigned DIM>
478 void NavierStokesEquations<DIM>::output_fct(std::ostream &outfile,
479  const unsigned &nplot,
480  const double& time,
482 {
483 
484  //Vector of local coordinates
485  Vector<double> s(DIM);
486 
487  // Vector for coordintes
488  Vector<double> x(DIM);
489 
490  // Tecplot header info
491  outfile << tecplot_zone_string(nplot);
492 
493  // Exact solution Vector
494  Vector<double> exact_soln;
495 
496  // Loop over plot points
497  unsigned num_plot_points=nplot_points(nplot);
498  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
499  {
500 
501  // Get local coordinates of plot point
502  get_s_plot(iplot,nplot,s);
503 
504  // Get x position as Vector
505  interpolated_x(s,x);
506 
507  // Get exact solution at this point
508  (*exact_soln_pt)(time,x,exact_soln);
509 
510  //Output x,y,...
511  for(unsigned i=0;i<DIM;i++)
512  {
513  outfile << x[i] << " ";
514  }
515 
516  //Output "exact solution"
517  for(unsigned i=0;i<exact_soln.size();i++)
518  {
519  outfile << exact_soln[i] << " ";
520  }
521 
522  outfile << std::endl;
523  }
524 
525  // Write tecplot footer (e.g. FE connectivity lists)
526  write_tecplot_zone_footer(outfile,nplot);
527 
528 }
529 
530 //==============================================================
531 /// Output function: Velocities only
532 /// x,y,[z],u,v,[w]
533 /// in tecplot format at specified previous timestep (t=0: present;
534 /// t>0: previous timestep). Specified number of plot points in each
535 /// coordinate direction.
536 //==============================================================
537 template<unsigned DIM>
538 void NavierStokesEquations<DIM>::output_veloc(std::ostream& outfile,
539  const unsigned& nplot,
540  const unsigned& t)
541 {
542 
543  //Find number of nodes
544  unsigned n_node = nnode();
545 
546  //Local shape function
547  Shape psi(n_node);
548 
549  //Vectors of local coordinates and coords and velocities
550  Vector<double> s(DIM);
551  Vector<double> interpolated_x(DIM);
552  Vector<double> interpolated_u(DIM);
553 
554  // Tecplot header info
555  outfile << tecplot_zone_string(nplot);
556 
557  // Loop over plot points
558  unsigned num_plot_points=nplot_points(nplot);
559  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
560  {
561 
562  // Get local coordinates of plot point
563  get_s_plot(iplot,nplot,s);
564 
565  // Get shape functions
566  shape(s,psi);
567 
568  // Loop over directions
569  for(unsigned i=0;i<DIM;i++)
570  {
571  interpolated_x[i]=0.0;
572  interpolated_u[i]=0.0;
573  //Get the index at which velocity i is stored
574  unsigned u_nodal_index = u_index_nst(i);
575  //Loop over the local nodes and sum
576  for(unsigned l=0;l<n_node;l++)
577  {
578  interpolated_u[i] += nodal_value(t,l,u_nodal_index)*psi[l];
579  interpolated_x[i] += nodal_position(t,l,i)*psi[l];
580  }
581  }
582 
583  // Coordinates
584  for(unsigned i=0;i<DIM;i++)
585  {
586  outfile << interpolated_x[i] << " ";
587  }
588 
589  // Velocities
590  for(unsigned i=0;i<DIM;i++)
591  {
592  outfile << interpolated_u[i] << " ";
593  }
594 
595  outfile << std::endl;
596  }
597 
598  // Write tecplot footer (e.g. FE connectivity lists)
599  write_tecplot_zone_footer(outfile,nplot);
600 
601 }
602 
603 //==============================================================
604 /// Output function:
605 /// x,y,[z],u,v,[w],p
606 /// in tecplot format. Specified number of plot points in each
607 /// coordinate direction.
608 //==============================================================
609 template<unsigned DIM>
610 void NavierStokesEquations<DIM>::output(std::ostream &outfile,
611  const unsigned &nplot)
612 {
613 
614  //Vector of local coordinates
615  Vector<double> s(DIM);
616 
617  // Tecplot header info
618  outfile << tecplot_zone_string(nplot);
619 
620  // Loop over plot points
621  unsigned num_plot_points=nplot_points(nplot);
622  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
623  {
624 
625  // Get local coordinates of plot point
626  get_s_plot(iplot,nplot,s);
627 
628  // Coordinates
629  for(unsigned i=0;i<DIM;i++)
630  {
631  outfile << interpolated_x(s,i) << " ";
632  }
633 
634  // Velocities
635  for(unsigned i=0;i<DIM;i++)
636  {
637  outfile << interpolated_u_nst(s,i) << " ";
638  }
639 
640  // Pressure
641  outfile << interpolated_p_nst(s) << " ";
642 
643  outfile << std::endl;
644  }
645  outfile << std::endl;
646 
647  // Write tecplot footer (e.g. FE connectivity lists)
648  write_tecplot_zone_footer(outfile,nplot);
649 
650 }
651 
652 
653 //==============================================================
654 /// C-style output function:
655 /// x,y,[z],u,v,[w],p
656 /// in tecplot format. Specified number of plot points in each
657 /// coordinate direction.
658 //==============================================================
659 template<unsigned DIM>
661  const unsigned &nplot)
662 {
663 
664  //Vector of local coordinates
665  Vector<double> s(DIM);
666 
667  // Tecplot header info
668  fprintf(file_pt,"%s",tecplot_zone_string(nplot).c_str());
669 
670  // Loop over plot points
671  unsigned num_plot_points=nplot_points(nplot);
672  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
673  {
674 
675  // Get local coordinates of plot point
676  get_s_plot(iplot,nplot,s);
677 
678  // Coordinates
679  for(unsigned i=0;i<DIM;i++)
680  {
681  fprintf(file_pt,"%g ",interpolated_x(s,i));
682  }
683 
684  // Velocities
685  for(unsigned i=0;i<DIM;i++)
686  {
687  fprintf(file_pt,"%g ",interpolated_u_nst(s,i));
688  }
689 
690  // Pressure
691  fprintf(file_pt,"%g \n",interpolated_p_nst(s));
692  }
693  fprintf(file_pt,"\n");
694 
695  // Write tecplot footer (e.g. FE connectivity lists)
696  write_tecplot_zone_footer(file_pt,nplot);
697 
698 }
699 
700 
701 //==============================================================
702 /// Full output function:
703 /// x,y,[z],u,v,[w],p,du/dt,dv/dt,[dw/dt],dissipation
704 /// in tecplot format. Specified number of plot points in each
705 /// coordinate direction
706 //==============================================================
707 template<unsigned DIM>
708 void NavierStokesEquations<DIM>::full_output(std::ostream &outfile,
709  const unsigned &nplot)
710 {
711 
712  //Vector of local coordinates
713  Vector<double> s(DIM);
714 
715  // Acceleration
716  Vector<double> dudt(DIM);
717 
718  // Mesh elocity
719  Vector<double> mesh_veloc(DIM,0.0);
720 
721  // Tecplot header info
722  outfile << tecplot_zone_string(nplot);
723 
724  //Find out how many nodes there are
725  unsigned n_node = nnode();
726 
727  //Set up memory for the shape functions
728  Shape psif(n_node);
729  DShape dpsifdx(n_node,DIM);
730 
731  // Loop over plot points
732  unsigned num_plot_points=nplot_points(nplot);
733  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
734  {
735 
736  // Get local coordinates of plot point
737  get_s_plot(iplot,nplot,s);
738 
739  //Call the derivatives of the shape and test functions
740  dshape_eulerian(s,psif,dpsifdx);
741 
742  //Allocate storage
743  Vector<double> mesh_veloc(DIM);
744  Vector<double> dudt(DIM);
745  Vector<double> dudt_ALE(DIM);
746  DenseMatrix<double> interpolated_dudx(DIM,DIM);
747 
748  //Initialise everything to zero
749  for(unsigned i=0;i<DIM;i++)
750  {
751  mesh_veloc[i]=0.0;
752  dudt[i]=0.0;
753  dudt_ALE[i]=0.0;
754  for(unsigned j=0;j<DIM;j++)
755  {
756  interpolated_dudx(i,j) = 0.0;
757  }
758  }
759 
760  //Calculate velocities and derivatives
761 
762 
763  //Loop over directions
764  for(unsigned i=0;i<DIM;i++)
765  {
766  //Get the index at which velocity i is stored
767  unsigned u_nodal_index = u_index_nst(i);
768  // Loop over nodes
769  for(unsigned l=0;l<n_node;l++)
770  {
771  dudt[i]+=du_dt_nst(l,u_nodal_index)*psif[l];
772  mesh_veloc[i]+=dnodal_position_dt(l,i)*psif[l];
773 
774  //Loop over derivative directions for velocity gradients
775  for(unsigned j=0;j<DIM;j++)
776  {
777  interpolated_dudx(i,j) += nodal_value(l,u_nodal_index)*dpsifdx(l,j);
778  }
779  }
780  }
781 
782 
783  // Get dudt in ALE form (incl mesh veloc)
784  for(unsigned i=0;i<DIM;i++)
785  {
786  dudt_ALE[i]=dudt[i];
787  for (unsigned k=0;k<DIM;k++)
788  {
789  dudt_ALE[i]-=mesh_veloc[k]*interpolated_dudx(i,k);
790  }
791  }
792 
793 
794  // Coordinates
795  for(unsigned i=0;i<DIM;i++)
796  {
797  outfile << interpolated_x(s,i) << " ";
798  }
799 
800  // Velocities
801  for(unsigned i=0;i<DIM;i++)
802  {
803  outfile << interpolated_u_nst(s,i) << " ";
804  }
805 
806  // Pressure
807  outfile << interpolated_p_nst(s) << " ";
808 
809  // Accelerations
810  for(unsigned i=0;i<DIM;i++)
811  {
812  outfile << dudt_ALE[i] << " ";
813  }
814 
815 // // Mesh velocity
816 // for(unsigned i=0;i<DIM;i++)
817 // {
818 // outfile << mesh_veloc[i] << " ";
819 // }
820 
821  // Dissipation
822  outfile << dissipation(s) << " ";
823 
824 
825  outfile << std::endl;
826 
827  }
828 
829  // Write tecplot footer (e.g. FE connectivity lists)
830  write_tecplot_zone_footer(outfile,nplot);
831 }
832 
833 
834 //==============================================================
835 /// Output function for vorticity.
836 /// x,y,[z],[omega_x,omega_y,[and/or omega_z]]
837 /// in tecplot format. Specified number of plot points in each
838 /// coordinate direction.
839 //==============================================================
840 template<unsigned DIM>
842  const unsigned &nplot)
843 {
844 
845  //Vector of local coordinates
846  Vector<double> s(DIM);
847 
848  // Create vorticity vector of the required size
849  Vector<double> vorticity;
850  if (DIM==2)
851  {
852  vorticity.resize(1);
853  }
854  else if (DIM==3)
855  {
856  vorticity.resize(3);
857  }
858  else
859  {
860  std::string error_message =
861  "Can't output vorticity in 1D - in fact, what do you mean\n";
862  error_message += "by the 1D Navier-Stokes equations?\n";
863 
864  throw OomphLibError(error_message,
865  OOMPH_CURRENT_FUNCTION,
866  OOMPH_EXCEPTION_LOCATION);
867  }
868 
869  // Tecplot header info
870  outfile << tecplot_zone_string(nplot);
871 
872  // Loop over plot points
873  unsigned num_plot_points=nplot_points(nplot);
874  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
875  {
876 
877  // Get local coordinates of plot point
878  get_s_plot(iplot,nplot,s);
879 
880  // Coordinates
881  for(unsigned i=0;i<DIM;i++)
882  {
883  outfile << interpolated_x(s,i) << " ";
884  }
885 
886  // Get vorticity
887  get_vorticity(s,vorticity);
888 
889  if (DIM==2)
890  {
891  outfile << vorticity[0];
892  }
893  else
894  {
895  outfile << vorticity[0] << " "
896  << vorticity[1] << " "
897  << vorticity[2] << " ";
898  }
899 
900  outfile << std::endl;
901  }
902  outfile << std::endl;
903 
904  // Write tecplot footer (e.g. FE connectivity lists)
905  write_tecplot_zone_footer(outfile,nplot);
906 
907 }
908 
909 
910 
911 //==============================================================
912 /// Return integral of dissipation over element
913 //==============================================================
914 template<unsigned DIM>
916 {
917 
918  // Initialise
919  double diss=0.0;
920 
921  //Set the value of n_intpt
922  unsigned n_intpt = integral_pt()->nweight();
923 
924  //Set the Vector to hold local coordinates
925  Vector<double> s(DIM);
926 
927  //Loop over the integration points
928  for(unsigned ipt=0;ipt<n_intpt;ipt++)
929  {
930 
931  //Assign values of s
932  for(unsigned i=0;i<DIM;i++)
933  {
934  s[i] = integral_pt()->knot(ipt,i);
935  }
936 
937  //Get the integral weight
938  double w = integral_pt()->weight(ipt);
939 
940  // Get Jacobian of mapping
941  double J = J_eulerian(s);
942 
943  // Get strain rate matrix
944  DenseMatrix<double> strainrate(DIM,DIM);
945  strain_rate(s,strainrate);
946 
947  // Initialise
948  double local_diss=0.0;
949  for(unsigned i=0;i<DIM;i++)
950  {
951  for(unsigned j=0;j<DIM;j++)
952  {
953  local_diss+=2.0*strainrate(i,j)*strainrate(i,j);
954  }
955  }
956 
957  diss+=local_diss*w*J;
958  }
959 
960  return diss;
961 
962 }
963 
964 //==============================================================
965 /// Compute traction (on the viscous scale) exerted onto
966 /// the fluid at local coordinate s. N has to be outer unit normal
967 /// to the fluid.
968 //==============================================================
969 template<unsigned DIM>
971  const Vector<double>& N,
972  Vector<double>& traction)
973 {
974 
975  // Get velocity gradients
976  DenseMatrix<double> strainrate(DIM,DIM);
977  strain_rate(s,strainrate);
978 
979  // Get pressure
980  double press=interpolated_p_nst(s);
981 
982  // Loop over traction components
983  for (unsigned i=0;i<DIM;i++)
984  {
985  traction[i]=-press*N[i];
986  for (unsigned j=0;j<DIM;j++)
987  {
988  traction[i]+=2.0*strainrate(i,j)*N[j];
989  }
990  }
991 
992 }
993 
994 //==============================================================
995 /// Compute traction (on the viscous scale) exerted onto
996 /// the fluid at local coordinate s, decomposed into pressure and
997 /// normal and tangential viscous components.
998 /// N has to be outer unit normal to the fluid.
999 //==============================================================
1000 template<unsigned DIM>
1002  const Vector<double>& N,
1003  Vector<double>& traction_p,
1004  Vector<double>& traction_visc_n,
1005  Vector<double>& traction_visc_t)
1006 {
1007  Vector<double> traction_visc(DIM);
1008 
1009  // Get velocity gradients
1010  DenseMatrix<double> strainrate(DIM,DIM);
1011  strain_rate(s,strainrate);
1012 
1013  // Get pressure
1014  double press=interpolated_p_nst(s);
1015 
1016  // Loop over traction components
1017  for (unsigned i=0;i<DIM;i++)
1018  {
1019  // pressure
1020  traction_p[i]=-press*N[i];
1021  for (unsigned j=0;j<DIM;j++)
1022  {
1023  // viscous stress
1024  traction_visc[i]+=2.0*strainrate(i,j)*N[j];
1025  }
1026  // Decompose viscous stress into normal and tangential components
1027  traction_visc_n[i]=traction_visc[i]*N[i];
1028  traction_visc_t[i]=traction_visc[i]-traction_visc_n[i];
1029  }
1030 
1031 }
1032 
1033 //==============================================================
1034 /// Return dissipation at local coordinate s
1035 //==============================================================
1036 template<unsigned DIM>
1038 {
1039  // Get strain rate matrix
1040  DenseMatrix<double> strainrate(DIM,DIM);
1041  strain_rate(s,strainrate);
1042 
1043  // Initialise
1044  double local_diss=0.0;
1045  for(unsigned i=0;i<DIM;i++)
1046  {
1047  for(unsigned j=0;j<DIM;j++)
1048  {
1049  local_diss+=2.0*strainrate(i,j)*strainrate(i,j);
1050  }
1051  }
1052 
1053  return local_diss;
1054 }
1055 
1056 //==============================================================
1057 /// Get strain-rate tensor: 1/2 (du_i/dx_j + du_j/dx_i)
1058 //==============================================================
1059 template<unsigned DIM>
1061  DenseMatrix<double>& strainrate) const
1062  {
1063 
1064 #ifdef PARANOID
1065  if ((strainrate.ncol()!=DIM)||(strainrate.nrow()!=DIM))
1066  {
1067  std::ostringstream error_message;
1068  error_message << "The strain rate has incorrect dimensions "
1069  << strainrate.ncol() << " x "
1070  << strainrate.nrow() << " Not " << DIM << std::endl;
1071 
1072  throw OomphLibError(error_message.str(),
1073  OOMPH_CURRENT_FUNCTION,
1074  OOMPH_EXCEPTION_LOCATION);
1075  }
1076 #endif
1077 
1078  // Velocity gradient matrix
1079  DenseMatrix<double> dudx(DIM);
1080 
1081  //Find out how many nodes there are in the element
1082  unsigned n_node = nnode();
1083 
1084  //Set up memory for the shape and test functions
1085  Shape psi(n_node);
1086  DShape dpsidx(n_node,DIM);
1087 
1088  //Call the derivatives of the shape functions
1089  dshape_eulerian(s,psi,dpsidx);
1090 
1091  //Initialise to zero
1092  for(unsigned i=0;i<DIM;i++)
1093  {
1094  for(unsigned j=0;j<DIM;j++)
1095  {
1096  dudx(i,j) = 0.0;
1097  }
1098  }
1099 
1100  //Loop over veclocity components
1101  for(unsigned i=0;i<DIM;i++)
1102  {
1103  //Get the index at which the i-th velocity is stored
1104  unsigned u_nodal_index = u_index_nst(i);
1105  //Loop over derivative directions
1106  for(unsigned j=0;j<DIM;j++)
1107  {
1108  // Loop over nodes
1109  for(unsigned l=0;l<n_node;l++)
1110  {
1111  dudx(i,j) += nodal_value(l,u_nodal_index)*dpsidx(l,j);
1112  }
1113  }
1114  }
1115 
1116  //Loop over veclocity components
1117  for(unsigned i=0;i<DIM;i++)
1118  {
1119  //Loop over derivative directions
1120  for(unsigned j=0;j<DIM;j++)
1121  {
1122  strainrate(i,j)=0.5*(dudx(i,j)+dudx(j,i));
1123  }
1124  }
1125 
1126  }
1127 
1128 
1129 
1130 //==============================================================
1131 /// Compute 2D vorticity vector at local coordinate s (return in
1132 /// one and only component of vorticity vector
1133 //==============================================================
1134 template<>
1136  Vector<double>& vorticity) const
1137 {
1138 
1139 #ifdef PARANOID
1140  if (vorticity.size()!=1)
1141  {
1142  std::ostringstream error_message;
1143  error_message
1144  << "Computation of vorticity in 2D requires a 1D vector\n"
1145  << "which contains the only non-zero component of the\n"
1146  << "vorticity vector. You've passed a vector of size "
1147  << vorticity.size() << std::endl;
1148 
1149  throw OomphLibError(error_message.str(),
1150  OOMPH_CURRENT_FUNCTION,
1151  OOMPH_EXCEPTION_LOCATION);
1152  }
1153 #endif
1154 
1155  // Specify spatial dimension
1156  unsigned DIM=2;
1157 
1158  // Velocity gradient matrix
1159  DenseMatrix<double> dudx(DIM);
1160 
1161  //Find out how many nodes there are in the element
1162  unsigned n_node = nnode();
1163 
1164  //Set up memory for the shape and test functions
1165  Shape psi(n_node);
1166  DShape dpsidx(n_node,DIM);
1167 
1168  //Call the derivatives of the shape functions
1169  dshape_eulerian(s,psi,dpsidx);
1170 
1171  //Initialise to zero
1172  for(unsigned i=0;i<DIM;i++)
1173  {
1174  for(unsigned j=0;j<DIM;j++)
1175  {
1176  dudx(i,j) = 0.0;
1177  }
1178  }
1179 
1180  //Loop over veclocity components
1181  for(unsigned i=0;i<DIM;i++)
1182  {
1183  //Get the index at which the i-th velocity is stored
1184  unsigned u_nodal_index = u_index_nst(i);
1185  //Loop over derivative directions
1186  for(unsigned j=0;j<DIM;j++)
1187  {
1188  // Loop over nodes
1189  for(unsigned l=0;l<n_node;l++)
1190  {
1191  dudx(i,j) += nodal_value(l,u_nodal_index)*dpsidx(l,j);
1192  }
1193  }
1194  }
1195 
1196  // Z-component of vorticity
1197  vorticity[0]=dudx(1,0)-dudx(0,1);
1198 
1199 }
1200 
1201 
1202 
1203 
1204 //==============================================================
1205 /// Compute 3D vorticity vector at local coordinate s
1206 //==============================================================
1207 template<>
1209  Vector<double>& vorticity) const
1210 {
1211 
1212 #ifdef PARANOID
1213  if (vorticity.size()!=3)
1214  {
1215  std::ostringstream error_message;
1216  error_message
1217  << "Computation of vorticity in 3D requires a 3D vector\n"
1218  << "which contains the only non-zero component of the\n"
1219  << "vorticity vector. You've passed a vector of size "
1220  << vorticity.size() << std::endl;
1221 
1222  throw OomphLibError(error_message.str(),
1223  OOMPH_CURRENT_FUNCTION,
1224  OOMPH_EXCEPTION_LOCATION);
1225  }
1226 #endif
1227 
1228  // Specify spatial dimension
1229  unsigned DIM=3;
1230 
1231  // Velocity gradient matrix
1232  DenseMatrix<double> dudx(DIM);
1233 
1234  //Find out how many nodes there are in the element
1235  unsigned n_node = nnode();
1236 
1237  //Set up memory for the shape and test functions
1238  Shape psi(n_node);
1239  DShape dpsidx(n_node,DIM);
1240 
1241  //Call the derivatives of the shape functions
1242  dshape_eulerian(s,psi,dpsidx);
1243 
1244  //Initialise to zero
1245  for(unsigned i=0;i<DIM;i++)
1246  {
1247  for(unsigned j=0;j<DIM;j++)
1248  {
1249  dudx(i,j) = 0.0;
1250  }
1251  }
1252 
1253  //Loop over veclocity components
1254  for(unsigned i=0;i<DIM;i++)
1255  {
1256  //Get the index at which the i-th velocity component is stored
1257  unsigned u_nodal_index = u_index_nst(i);
1258  //Loop over derivative directions
1259  for(unsigned j=0;j<DIM;j++)
1260  {
1261  // Loop over nodes
1262  for(unsigned l=0;l<n_node;l++)
1263  {
1264  dudx(i,j) += nodal_value(l,u_nodal_index)*dpsidx(l,j);
1265  }
1266  }
1267  }
1268 
1269  vorticity[0]=dudx(2,1)-dudx(1,2);
1270  vorticity[1]=dudx(0,2)-dudx(2,0);
1271  vorticity[2]=dudx(1,0)-dudx(0,1);
1272 
1273 }
1274 
1275 
1276 //==============================================================
1277 /// \short Get integral of kinetic energy over element:
1278 /// Note that this is the "raw" kinetic energy in the sense
1279 /// that the density ratio has not been included. In problems
1280 /// with two or more fluids the user will have to remember
1281 /// to premultiply certain elements by the appropriate density
1282 /// ratio.
1283 //==============================================================
1284 template<unsigned DIM>
1286 {
1287 
1288  // Initialise
1289  double kin_en=0.0;
1290 
1291  //Set the value of n_intpt
1292  unsigned n_intpt = integral_pt()->nweight();
1293 
1294  //Set the Vector to hold local coordinates
1295  Vector<double> s(DIM);
1296 
1297  //Loop over the integration points
1298  for(unsigned ipt=0;ipt<n_intpt;ipt++)
1299  {
1300  //Assign values of s
1301  for(unsigned i=0;i<DIM;i++)
1302  {
1303  s[i] = integral_pt()->knot(ipt,i);
1304  }
1305 
1306  //Get the integral weight
1307  double w = integral_pt()->weight(ipt);
1308 
1309  //Get Jacobian of mapping
1310  double J = J_eulerian(s);
1311 
1312  // Loop over directions
1313  double veloc_squared=0.0;
1314  for(unsigned i=0;i<DIM;i++)
1315  {
1316  veloc_squared+=interpolated_u_nst(s,i)*interpolated_u_nst(s,i);
1317  }
1318 
1319  kin_en+=0.5*veloc_squared*w*J;
1320  }
1321 
1322  return kin_en;
1323 
1324 }
1325 
1326 
1327 //==========================================================================
1328 /// \short Get integral of time derivative of kinetic energy over element:
1329 //==========================================================================
1330 template<unsigned DIM>
1332 {
1333  // Initialise
1334  double d_kin_en_dt=0.0;
1335 
1336  //Set the value of n_intpt
1337  const unsigned n_intpt = integral_pt()->nweight();
1338 
1339  //Set the Vector to hold local coordinates
1340  Vector<double> s(DIM);
1341 
1342  //Get the number of nodes
1343  const unsigned n_node = this->nnode();
1344 
1345  //Storage for the shape function
1346  Shape psi(n_node);
1347  DShape dpsidx(n_node,DIM);
1348 
1349  //Get the value at which the velocities are stored
1350  unsigned u_index[DIM];
1351  for(unsigned i=0;i<DIM;i++) {u_index[i] = this->u_index_nst(i);}
1352 
1353  //Loop over the integration points
1354  for(unsigned ipt=0;ipt<n_intpt;ipt++)
1355  {
1356  //Get the jacobian of the mapping
1357  double J = dshape_eulerian_at_knot(ipt,psi,dpsidx);
1358 
1359  //Get the integral weight
1360  double w = integral_pt()->weight(ipt);
1361 
1362  //Now work out the velocity and the time derivative
1363  Vector<double> interpolated_u(DIM,0.0), interpolated_dudt(DIM,0.0);
1364 
1365  //Loop over the shape functions
1366  for(unsigned l=0;l<n_node;l++)
1367  {
1368  //Loop over the dimensions
1369  for(unsigned i=0;i<DIM;i++)
1370  {
1371  interpolated_u[i] += nodal_value(l,u_index[i])*psi(l);
1372  interpolated_dudt[i] += du_dt_nst(l,u_index[i])*psi(l);
1373  }
1374  }
1375 
1376  //Get mesh velocity bit
1377  if (!ALE_is_disabled)
1378  {
1379  Vector<double> mesh_velocity(DIM,0.0);
1380  DenseMatrix<double> interpolated_dudx(DIM,DIM,0.0);
1381 
1382  // Loop over nodes again
1383  for(unsigned l=0;l<n_node;l++)
1384  {
1385  for(unsigned i=0;i<DIM;i++)
1386  {
1387  mesh_velocity[i] += this->dnodal_position_dt(l,i)*psi(l);
1388 
1389  for(unsigned j=0;j<DIM;j++)
1390  {
1391  interpolated_dudx(i,j) +=
1392  this->nodal_value(l,u_index[i])*dpsidx(l,j);
1393  }
1394  }
1395  }
1396 
1397  //Subtract mesh velocity from du_dt
1398  for(unsigned i=0;i<DIM;i++)
1399  {
1400  for (unsigned k=0;k<DIM;k++)
1401  {
1402  interpolated_dudt[i] -= mesh_velocity[k]*interpolated_dudx(i,k);
1403  }
1404  }
1405  }
1406 
1407 
1408  // Loop over directions and add up u du/dt terms
1409  double sum=0.0;
1410  for(unsigned i=0;i<DIM;i++)
1411  { sum += interpolated_u[i]*interpolated_dudt[i];}
1412 
1413  d_kin_en_dt += sum*w*J;
1414  }
1415 
1416  return d_kin_en_dt;
1417 
1418 }
1419 
1420 
1421 //==============================================================
1422 /// Return pressure integrated over the element
1423 //==============================================================
1424 template<unsigned DIM>
1426 {
1427 
1428  // Initialise
1429  double press_int=0;
1430 
1431  //Set the value of n_intpt
1432  unsigned n_intpt = integral_pt()->nweight();
1433 
1434  //Set the Vector to hold local coordinates
1435  Vector<double> s(DIM);
1436 
1437  //Loop over the integration points
1438  for(unsigned ipt=0;ipt<n_intpt;ipt++)
1439  {
1440 
1441  //Assign values of s
1442  for(unsigned i=0;i<DIM;i++)
1443  {
1444  s[i] = integral_pt()->knot(ipt,i);
1445  }
1446 
1447  //Get the integral weight
1448  double w = integral_pt()->weight(ipt);
1449 
1450  //Get Jacobian of mapping
1451  double J = J_eulerian(s);
1452 
1453  //Premultiply the weights and the Jacobian
1454  double W = w*J;
1455 
1456  // Get pressure
1457  double press=interpolated_p_nst(s);
1458 
1459  // Add
1460  press_int+=press*W;
1461 
1462  }
1463 
1464  return press_int;
1465 
1466 }
1467 
1468 
1469 //==============================================================
1470 /// Compute the residuals for the associated pressure advection
1471 /// diffusion problem. Used by the Fp preconditioner.
1472 /// flag=1(or 0): do (or don't) compute the Jacobian as well.
1473 //==============================================================
1474 template<unsigned DIM>
1477  Vector<double> &residuals,
1478  DenseMatrix<double> &jacobian,
1479  unsigned flag)
1480 {
1481  // Return immediately if there are no dofs
1482  if (ndof()==0) return;
1483 
1484  //Find out how many nodes there are
1485  unsigned n_node = nnode();
1486 
1487  //Find out how many pressure dofs there are
1488  unsigned n_pres = npres_nst();
1489 
1490  //Find the indices at which the local velocities are stored
1491  unsigned u_nodal_index[DIM];
1492  for(unsigned i=0;i<DIM;i++) {u_nodal_index[i] = u_index_nst(i);}
1493 
1494  //Set up memory for the velocity shape fcts
1495  Shape psif(n_node);
1496  DShape dpsidx(n_node,DIM);
1497 
1498  //Set up memory for pressure shape and test functions
1499  Shape psip(n_pres), testp(n_pres);
1500  DShape dpsip(n_pres,DIM);
1501  DShape dtestp(n_pres,DIM);
1502 
1503  //Number of integration points
1504  unsigned n_intpt = integral_pt()->nweight();
1505 
1506  //Set the Vector to hold local coordinates
1507  Vector<double> s(DIM);
1508 
1509  //Get Physical Variables from Element
1510  //Reynolds number must be multiplied by the density ratio
1511  double scaled_re = re()*density_ratio();
1512 
1513  //Integers to store the local equations and unknowns
1514  int local_eqn=0, local_unknown=0;
1515 
1516  //Loop over the integration points
1517  for(unsigned ipt=0;ipt<n_intpt;ipt++)
1518  {
1519  //Assign values of s
1520  for(unsigned i=0;i<DIM;i++) s[i] = integral_pt()->knot(ipt,i);
1521 
1522  //Get the integral weight
1523  double w = integral_pt()->weight(ipt);
1524 
1525  // Call the derivatives of the veloc shape functions
1526  // (Derivs not needed but they are free)
1527  double J = this->dshape_eulerian_at_knot(ipt,psif,dpsidx);
1528 
1529  //Call the pressure shape and test functions
1530  this->dpshape_and_dptest_eulerian_nst(s,psip,dpsip,testp,dtestp);
1531 
1532  //Premultiply the weights and the Jacobian
1533  double W = w*J;
1534 
1535  //Calculate local values of the pressure and velocity components
1536  //Allocate
1537  Vector<double> interpolated_u(DIM,0.0);
1538  Vector<double> interpolated_x(DIM,0.0);
1539  Vector<double> interpolated_dpdx(DIM,0.0);
1540 
1541  //Calculate pressure gradient
1542  for(unsigned l=0;l<n_pres;l++)
1543  {
1544  for (unsigned i=0;i<DIM;i++)
1545  {
1546  interpolated_dpdx[i] += p_nst(l)*dpsip(l,i);
1547  }
1548  }
1549 
1550  //Calculate velocities
1551 
1552  // Loop over nodes
1553  for(unsigned l=0;l<n_node;l++)
1554  {
1555  //Loop over directions
1556  for(unsigned i=0;i<DIM;i++)
1557  {
1558  //Get the nodal value
1559  double u_value = raw_nodal_value(l,u_nodal_index[i]);
1560  interpolated_u[i] += u_value*psif[l];
1561  interpolated_x[i] += raw_nodal_position(l,i)*psif[l];
1562  }
1563  }
1564 
1565  // Source function (for validaton only)
1566  double source=0.0;
1567  if (Press_adv_diff_source_fct_pt!=0)
1568  {
1569  source=Press_adv_diff_source_fct_pt(interpolated_x);
1570  }
1571 
1572  //Loop over the shape functions
1573  for(unsigned l=0;l<n_pres;l++)
1574  {
1575  local_eqn = p_local_eqn(l);
1576 
1577  //If not a boundary conditions
1578  if(local_eqn >= 0)
1579  {
1580  residuals[local_eqn] -= source*testp[l]*W;
1581  for(unsigned k=0;k<DIM;k++)
1582  {
1583  residuals[local_eqn] += interpolated_dpdx[k]*
1584  (scaled_re*interpolated_u[k]*testp[l]+dtestp(l,k))*W;
1585  }
1586 
1587  // Jacobian too?
1588  if(flag)
1589  {
1590  //Loop over the shape functions
1591  for(unsigned l2=0;l2<n_pres;l2++)
1592  {
1593  local_unknown = p_local_eqn(l2);
1594 
1595  //If not a boundary conditions
1596  if(local_unknown >= 0)
1597  {
1598  if ((int(eqn_number(local_eqn))!=Pinned_fp_pressure_eqn)&&
1599  (int(eqn_number(local_unknown))!=Pinned_fp_pressure_eqn))
1600  {
1601  for(unsigned k=0;k<DIM;k++)
1602  {
1603  jacobian(local_eqn,local_unknown)+=dtestp(l2,k)*
1604  (scaled_re*interpolated_u[k]*testp[l]+dtestp(l,k))*W;
1605  }
1606  }
1607  else
1608  {
1609  if ((int(eqn_number(local_eqn))==Pinned_fp_pressure_eqn)&&
1610  (int(eqn_number(local_unknown))==Pinned_fp_pressure_eqn))
1611  {
1612  jacobian(local_eqn,local_unknown)=1.0;
1613  }
1614  }
1615  }
1616  }
1617  } /*End of Jacobian calculation*/
1618  } //End of if not boundary condition
1619  }//End of loop over l
1620  }
1621 
1622  // Now add boundary contributions from Robin BCs
1623  unsigned nrobin=Pressure_advection_diffusion_robin_element_pt.size();
1624  for (unsigned e=0;e<nrobin;e++)
1625  {
1626  Pressure_advection_diffusion_robin_element_pt[e]->
1627  fill_in_generic_residual_contribution_fp_press_adv_diff_robin_bc(
1628  residuals,jacobian,flag);
1629  }
1630 }
1631 
1632 
1633 //==============================================================
1634 /// Compute the residuals for the Navier--Stokes
1635 /// equations; flag=1(or 0): do (or don't) compute the
1636 /// Jacobian as well.
1637 //==============================================================
1638 template<unsigned DIM>
1641  DenseMatrix<double> &jacobian,
1642  DenseMatrix<double> &mass_matrix,
1643  unsigned flag)
1644 {
1645  // Return immediately if there are no dofs
1646  if (ndof()==0) return;
1647 
1648  //Find out how many nodes there are
1649  unsigned n_node = nnode();
1650 
1651  // Get continuous time from timestepper of first node
1652  double time=node_pt(0)->time_stepper_pt()->time_pt()->time();
1653 
1654  //Find out how many pressure dofs there are
1655  unsigned n_pres = npres_nst();
1656 
1657  //Find the indices at which the local velocities are stored
1658  unsigned u_nodal_index[DIM];
1659  for(unsigned i=0;i<DIM;i++) {u_nodal_index[i] = u_index_nst(i);}
1660 
1661  //Set up memory for the shape and test functions
1662  Shape psif(n_node), testf(n_node);
1663  DShape dpsifdx(n_node,DIM), dtestfdx(n_node,DIM);
1664 
1665  //Set up memory for pressure shape and test functions
1666  Shape psip(n_pres), testp(n_pres);
1667 
1668  //Number of integration points
1669  unsigned n_intpt = integral_pt()->nweight();
1670 
1671  //Set the Vector to hold local coordinates
1672  Vector<double> s(DIM);
1673 
1674  //Get Physical Variables from Element
1675  //Reynolds number must be multiplied by the density ratio
1676  double scaled_re = re()*density_ratio();
1677  double scaled_re_st = re_st()*density_ratio();
1678  double scaled_re_inv_fr = re_invfr()*density_ratio();
1679  double visc_ratio = viscosity_ratio();
1680  Vector<double> G = g();
1681 
1682  //Integers to store the local equations and unknowns
1683  int local_eqn=0, local_unknown=0;
1684 
1685  //Loop over the integration points
1686  for(unsigned ipt=0;ipt<n_intpt;ipt++)
1687  {
1688  //Assign values of s
1689  for(unsigned i=0;i<DIM;i++) s[i] = integral_pt()->knot(ipt,i);
1690  //Get the integral weight
1691  double w = integral_pt()->weight(ipt);
1692 
1693  //Call the derivatives of the shape and test functions
1694  double J =
1695  dshape_and_dtest_eulerian_at_knot_nst(ipt,psif,dpsifdx,testf,dtestfdx);
1696 
1697  //Call the pressure shape and test functions
1698  pshape_nst(s,psip,testp);
1699 
1700  //Premultiply the weights and the Jacobian
1701  double W = w*J;
1702 
1703  //Calculate local values of the pressure and velocity components
1704  //Allocate
1705  double interpolated_p=0.0;
1706  Vector<double> interpolated_u(DIM,0.0);
1707  Vector<double> interpolated_x(DIM,0.0);
1708  Vector<double> mesh_velocity(DIM,0.0);
1709  Vector<double> dudt(DIM,0.0);
1710  DenseMatrix<double> interpolated_dudx(DIM,DIM,0.0);
1711 
1712  //Calculate pressure
1713  for(unsigned l=0;l<n_pres;l++) interpolated_p += p_nst(l)*psip[l];
1714 
1715  //Calculate velocities and derivatives:
1716 
1717  // Loop over nodes
1718  for(unsigned l=0;l<n_node;l++)
1719  {
1720  //Loop over directions
1721  for(unsigned i=0;i<DIM;i++)
1722  {
1723  //Get the nodal value
1724  double u_value = raw_nodal_value(l,u_nodal_index[i]);
1725  interpolated_u[i] += u_value*psif[l];
1726  interpolated_x[i] += raw_nodal_position(l,i)*psif[l];
1727  dudt[i] += du_dt_nst(l,i)*psif[l];
1728 
1729  //Loop over derivative directions
1730  for(unsigned j=0;j<DIM;j++)
1731  {
1732  interpolated_dudx(i,j) += u_value*dpsifdx(l,j);
1733  }
1734  }
1735  }
1736 
1737  if (!ALE_is_disabled)
1738  {
1739  // Loop over nodes
1740  for(unsigned l=0;l<n_node;l++)
1741  {
1742  //Loop over directions
1743  for(unsigned i=0;i<DIM;i++)
1744  {
1745  mesh_velocity[i] += this->raw_dnodal_position_dt(l,i)*psif[l];
1746  }
1747  }
1748  }
1749 
1750  //Get the user-defined body force terms
1751  Vector<double> body_force(DIM);
1752  get_body_force_nst(time,ipt,s,interpolated_x,body_force);
1753 
1754  //Get the user-defined source function
1755  double source = get_source_nst(time,ipt,interpolated_x);
1756 
1757 
1758  //MOMENTUM EQUATIONS
1759  //------------------
1760 
1761  //Loop over the test functions
1762  for(unsigned l=0;l<n_node;l++)
1763  {
1764  //Loop over the velocity components
1765  for(unsigned i=0;i<DIM;i++)
1766  {
1767  /*IF it's not a boundary condition*/
1768  local_eqn = nodal_local_eqn(l,u_nodal_index[i]);
1769  if(local_eqn >= 0)
1770  {
1771  //Add the user-defined body force terms
1772  residuals[local_eqn] +=
1773  body_force[i]*testf[l]*W;
1774 
1775  //Add the gravitational body force term
1776  residuals[local_eqn] += scaled_re_inv_fr*testf[l]*G[i]*W;
1777 
1778  //Add the pressure gradient term
1779  residuals[local_eqn] += interpolated_p*dtestfdx(l,i)*W;
1780 
1781  //Add in the stress tensor terms
1782  //The viscosity ratio needs to go in here to ensure
1783  //continuity of normal stress is satisfied even in flows
1784  //with zero pressure gradient!
1785  for(unsigned k=0;k<DIM;k++)
1786  {
1787  residuals[local_eqn] -= visc_ratio*
1788  (interpolated_dudx(i,k) + Gamma[i]*interpolated_dudx(k,i))
1789  *dtestfdx(l,k)*W;
1790  }
1791 
1792  //Add in the inertial terms
1793  //du/dt term
1794  residuals[local_eqn] -= scaled_re_st*dudt[i]*testf[l]*W;
1795 
1796 
1797  //Convective terms, including mesh velocity
1798  for(unsigned k=0;k<DIM;k++)
1799  {
1800  double tmp=scaled_re*interpolated_u[k];
1801  if (!ALE_is_disabled) tmp-=scaled_re_st*mesh_velocity[k];
1802  residuals[local_eqn] -= tmp*interpolated_dudx(i,k)*testf[l]*W;
1803 
1804  }
1805 
1806  //CALCULATE THE JACOBIAN
1807  if(flag)
1808  {
1809  //Loop over the velocity shape functions again
1810  for(unsigned l2=0;l2<n_node;l2++)
1811  {
1812  //Loop over the velocity components again
1813  for(unsigned i2=0;i2<DIM;i2++)
1814  {
1815  //If at a non-zero degree of freedom add in the entry
1816  local_unknown = nodal_local_eqn(l2,u_nodal_index[i2]);
1817  if(local_unknown >= 0)
1818  {
1819  //Add contribution to Elemental Matrix
1820  jacobian(local_eqn,local_unknown)
1821  -= visc_ratio*Gamma[i]*dpsifdx(l2,i)*dtestfdx(l,i2)*W;
1822 
1823  //Extra component if i2 = i
1824  if(i2 == i)
1825  {
1826  /*Loop over velocity components*/
1827  for(unsigned k=0;k<DIM;k++)
1828  {
1829  jacobian(local_eqn,local_unknown)
1830  -= visc_ratio*dpsifdx(l2,k)*dtestfdx(l,k)*W;
1831  }
1832  }
1833 
1834  //Now add in the inertial terms
1835  jacobian(local_eqn,local_unknown)
1836  -= scaled_re*psif[l2]*interpolated_dudx(i,i2)*testf[l]*W;
1837 
1838  //Extra component if i2=i
1839  if(i2 == i)
1840  {
1841  //Add the mass matrix term (only diagonal entries)
1842  //Note that this is positive because the mass matrix
1843  //is taken to the other side of the equation when
1844  //formulating the generalised eigenproblem.
1845  if(flag==2)
1846  {
1847  mass_matrix(local_eqn,local_unknown) +=
1848  scaled_re_st*psif[l2]*testf[l]*W;
1849  }
1850 
1851  //du/dt term
1852  jacobian(local_eqn,local_unknown)
1853  -= scaled_re_st*
1854  node_pt(l2)->time_stepper_pt()->weight(1,0)*
1855  psif[l2]*testf[l]*W;
1856 
1857  //Loop over the velocity components
1858  for(unsigned k=0;k<DIM;k++)
1859  {
1860  double tmp=scaled_re*interpolated_u[k];
1861  if (!ALE_is_disabled) tmp-=scaled_re_st*mesh_velocity[k];
1862  jacobian(local_eqn,local_unknown) -=
1863  tmp*dpsifdx(l2,k)*testf[l]*W;
1864  }
1865  }
1866 
1867  }
1868  }
1869  }
1870 
1871  /*Now loop over pressure shape functions*/
1872  /*This is the contribution from pressure gradient*/
1873  for(unsigned l2=0;l2<n_pres;l2++)
1874  {
1875  /*If we are at a non-zero degree of freedom in the entry*/
1876  local_unknown = p_local_eqn(l2);
1877  if(local_unknown >= 0)
1878  {
1879  jacobian(local_eqn,local_unknown)
1880  += psip[l2]*dtestfdx(l,i)*W;
1881  }
1882  }
1883  } /*End of Jacobian calculation*/
1884 
1885  } //End of if not boundary condition statement
1886 
1887  } //End of loop over dimension
1888  } //End of loop over shape functions
1889 
1890 
1891 
1892  //CONTINUITY EQUATION
1893  //-------------------
1894 
1895  //Loop over the shape functions
1896  for(unsigned l=0;l<n_pres;l++)
1897  {
1898  local_eqn = p_local_eqn(l);
1899  //If not a boundary conditions
1900  if(local_eqn >= 0)
1901  {
1902 
1903  // Source term
1904  //residuals[local_eqn] -=source*testp[l]*W;
1905  double aux=-source;
1906 
1907  //Loop over velocity components
1908  for(unsigned k=0;k<DIM;k++)
1909  {
1910  //residuals[local_eqn] += interpolated_dudx(k,k)*testp[l]*W;
1911  aux += interpolated_dudx(k,k);
1912  }
1913 
1914  residuals[local_eqn]+=aux*testp[l]*W;
1915 
1916  /*CALCULATE THE JACOBIAN*/
1917  if(flag)
1918  {
1919  /*Loop over the velocity shape functions*/
1920  for(unsigned l2=0;l2<n_node;l2++)
1921  {
1922  /*Loop over velocity components*/
1923  for(unsigned i2=0;i2<DIM;i2++)
1924  {
1925  /*If we're at a non-zero degree of freedom add it in*/
1926  local_unknown = nodal_local_eqn(l2,u_nodal_index[i2]);
1927  if(local_unknown >= 0)
1928  {
1929  jacobian(local_eqn,local_unknown)
1930  += dpsifdx(l2,i2)*testp[l]*W;
1931  }
1932  } /*End of loop over i2*/
1933  } /*End of loop over l2*/
1934  } /*End of Jacobian calculation*/
1935 
1936  } //End of if not boundary condition
1937  } //End of loop over l
1938  }
1939 
1940 }
1941 
1942 //==============================================================
1943 /// Compute the derivatives of the residuals for the Navier--Stokes
1944 /// equations with respect to a parameter;
1945 /// flag=2 or 1 or 0: do (or don't) compute the
1946 /// Jacobian and mass matrix as well
1947 //==============================================================
1948 template<unsigned DIM>
1951  double* const &parameter_pt,
1952  Vector<double> &dres_dparam,
1953  DenseMatrix<double> &djac_dparam,
1954  DenseMatrix<double> &dmass_matrix_dparam,
1955  unsigned flag)
1956 {
1957  throw OomphLibError("Not yet implemented\n",
1958  OOMPH_CURRENT_FUNCTION,
1959  OOMPH_EXCEPTION_LOCATION);
1960 }
1961 
1962 //==================================================================
1963 /// \short Compute the hessian tensor vector products required to
1964 /// perform continuation of bifurcations analytically
1965 //================================================================
1966 template<unsigned DIM>
1969  Vector<double> const &Y,
1970  DenseMatrix<double> const &C,
1971  DenseMatrix<double> &product)
1972 {
1973  throw OomphLibError(
1974  "Not yet implemented\n",
1975  OOMPH_CURRENT_FUNCTION,
1976  OOMPH_EXCEPTION_LOCATION);
1977 }
1978 
1979 
1980 //======================================================================
1981 /// Compute derivatives of elemental residual vector with respect
1982 /// to nodal coordinates.
1983 /// dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}
1984 /// Overloads the FD-based version in the FE base class.
1985 //======================================================================
1986 template <unsigned DIM>
1989  dresidual_dnodal_coordinates)
1990 {
1991  // Return immediately if there are no dofs
1992  if(ndof()==0) { return; }
1993 
1994  // Determine number of nodes in element
1995  const unsigned n_node = nnode();
1996 
1997  // Get continuous time from timestepper of first node
1998  double time=node_pt(0)->time_stepper_pt()->time_pt()->time();
1999 
2000  // Determine number of pressure dofs in element
2001  const unsigned n_pres = npres_nst();
2002 
2003  // Find the indices at which the local velocities are stored
2004  unsigned u_nodal_index[DIM];
2005  for(unsigned i=0;i<DIM;i++) { u_nodal_index[i] = u_index_nst(i); }
2006 
2007  // Set up memory for the shape and test functions
2008  Shape psif(n_node), testf(n_node);
2009  DShape dpsifdx(n_node,DIM), dtestfdx(n_node,DIM);
2010 
2011  // Set up memory for pressure shape and test functions
2012  Shape psip(n_pres), testp(n_pres);
2013 
2014  // Deriatives of shape fct derivatives w.r.t. nodal coords
2015  RankFourTensor<double> d_dpsifdx_dX(DIM,n_node,n_node,DIM);
2016  RankFourTensor<double> d_dtestfdx_dX(DIM,n_node,n_node,DIM);
2017 
2018  // Derivative of Jacobian of mapping w.r.t. to nodal coords
2019  DenseMatrix<double> dJ_dX(DIM,n_node);
2020 
2021  // Derivatives of derivative of u w.r.t. nodal coords
2022  RankFourTensor<double> d_dudx_dX(DIM,n_node,DIM,DIM);
2023 
2024  // Derivatives of nodal velocities w.r.t. nodal coords:
2025  // Assumption: Interaction only local via no-slip so
2026  // X_ij only affects U_ij.
2027  DenseMatrix<double> d_U_dX(DIM,n_node,0.0);
2028 
2029  // Determine the number of integration points
2030  const unsigned n_intpt = integral_pt()->nweight();
2031 
2032  // Vector to hold local coordinates
2033  Vector<double> s(DIM);
2034 
2035  // Get physical variables from element
2036  // (Reynolds number must be multiplied by the density ratio)
2037  double scaled_re = re()*density_ratio();
2038  double scaled_re_st = re_st()*density_ratio();
2039  double scaled_re_inv_fr = re_invfr()*density_ratio();
2040  double visc_ratio = viscosity_ratio();
2041  Vector<double> G = g();
2042 
2043  // FD step
2045 
2046  // Pre-compute derivatives of nodal velocities w.r.t. nodal coords:
2047  // Assumption: Interaction only local via no-slip so
2048  // X_ij only affects U_ij.
2049  bool element_has_node_with_aux_node_update_fct=false;
2050  for (unsigned q=0;q<n_node;q++)
2051  {
2052  Node* nod_pt=node_pt(q);
2053 
2054  // Only compute if there's a node-update fct involved
2055  if (nod_pt->has_auxiliary_node_update_fct_pt())
2056  {
2057  element_has_node_with_aux_node_update_fct=true;
2058 
2059  // Current nodal velocity
2060  Vector<double> u_ref(DIM);
2061  for (unsigned i=0;i<DIM;i++)
2062  {
2063  u_ref[i]=*(nod_pt->value_pt(u_nodal_index[i]));
2064  }
2065 
2066  // FD
2067  for (unsigned p=0;p<DIM;p++)
2068  {
2069  // Make backup
2070  double backup=nod_pt->x(p);
2071 
2072  // Do FD step. No node update required as we're
2073  // attacking the coordinate directly...
2074  nod_pt->x(p)+=eps_fd;
2075 
2076  // Do auxiliary node update (to apply no slip)
2078 
2079  // Evaluate
2080  d_U_dX(p,q)=(*(nod_pt->value_pt(u_nodal_index[p]))-u_ref[p])/eps_fd;
2081 
2082  // Reset
2083  nod_pt->x(p)=backup;
2084 
2085  // Do auxiliary node update (to apply no slip)
2087  }
2088  }
2089  }
2090 
2091  // Integer to store the local equation number
2092  int local_eqn=0;
2093 
2094  // Loop over the integration points
2095  for(unsigned ipt=0;ipt<n_intpt;ipt++)
2096  {
2097  // Assign values of s
2098  for(unsigned i=0;i<DIM;i++) { s[i] = integral_pt()->knot(ipt,i); }
2099 
2100  // Get the integral weight
2101  const double w = integral_pt()->weight(ipt);
2102 
2103  // Call the derivatives of the shape and test functions
2104  const double J = dshape_and_dtest_eulerian_at_knot_nst(ipt,psif,dpsifdx,
2105  d_dpsifdx_dX,
2106  testf,dtestfdx,
2107  d_dtestfdx_dX,dJ_dX);
2108 
2109  // Call the pressure shape and test functions
2110  pshape_nst(s,psip,testp);
2111 
2112  // Calculate local values of the pressure and velocity components
2113  // Allocate
2114  double interpolated_p=0.0;
2115  Vector<double> interpolated_u(DIM,0.0);
2116  Vector<double> interpolated_x(DIM,0.0);
2117  Vector<double> mesh_velocity(DIM,0.0);
2118  Vector<double> dudt(DIM,0.0);
2119  DenseMatrix<double> interpolated_dudx(DIM,DIM,0.0);
2120 
2121  // Calculate pressure
2122  for(unsigned l=0;l<n_pres;l++) { interpolated_p += p_nst(l)*psip[l]; }
2123 
2124  // Calculate velocities and derivatives:
2125 
2126  // Loop over nodes
2127  for(unsigned l=0;l<n_node;l++)
2128  {
2129  // Loop over directions
2130  for(unsigned i=0;i<DIM;i++)
2131  {
2132  // Get the nodal value
2133  double u_value = raw_nodal_value(l,u_nodal_index[i]);
2134  interpolated_u[i] += u_value*psif[l];
2135  interpolated_x[i] += raw_nodal_position(l,i)*psif[l];
2136  dudt[i] += du_dt_nst(l,i)*psif[l];
2137 
2138  // Loop over derivative directions
2139  for(unsigned j=0;j<DIM;j++)
2140  {
2141  interpolated_dudx(i,j) += u_value*dpsifdx(l,j);
2142  }
2143  }
2144  }
2145 
2146  if(!ALE_is_disabled)
2147  {
2148  // Loop over nodes
2149  for(unsigned l=0;l<n_node;l++)
2150  {
2151  // Loop over directions
2152  for(unsigned i=0;i<DIM;i++)
2153  {
2154  mesh_velocity[i] += this->raw_dnodal_position_dt(l,i)*psif[l];
2155  }
2156  }
2157  }
2158 
2159  // Calculate derivative of du_i/dx_k w.r.t. nodal positions X_{pq}
2160  for(unsigned q=0;q<n_node;q++)
2161  {
2162  // Loop over coordinate directions
2163  for(unsigned p=0;p<DIM;p++)
2164  {
2165  for(unsigned i=0;i<DIM;i++)
2166  {
2167  for(unsigned k=0;k<DIM;k++)
2168  {
2169  double aux=0.0;
2170  for(unsigned j=0;j<n_node;j++)
2171  {
2172  aux +=
2173  raw_nodal_value(j,u_nodal_index[i])*d_dpsifdx_dX(p,q,j,k);
2174  }
2175  d_dudx_dX(p,q,i,k) = aux;
2176  }
2177  }
2178  }
2179  }
2180 
2181  // Get weight of actual nodal position/value in computation of mesh
2182  // velocity from positional/value time stepper
2183  const double pos_time_weight
2184  = node_pt(0)->position_time_stepper_pt()->weight(1,0);
2185  const double val_time_weight = node_pt(0)->time_stepper_pt()->weight(1,0);
2186 
2187  // Get the user-defined body force terms
2188  Vector<double> body_force(DIM);
2189  get_body_force_nst(time,ipt,s,interpolated_x,body_force);
2190 
2191  // Get the user-defined source function
2192  const double source = get_source_nst(time,ipt,interpolated_x);
2193 
2194  // Note: Can use raw values and avoid bypassing hanging information
2195  // because this is the non-refineable version!
2196 
2197  // Get gradient of body force function
2198  DenseMatrix<double> d_body_force_dx(DIM,DIM,0.0);
2199  get_body_force_gradient_nst(
2200  time,ipt,s,interpolated_x,d_body_force_dx);
2201 
2202  // Get gradient of source function
2203  Vector<double> source_gradient(DIM,0.0);
2204  get_source_gradient_nst(time,ipt,interpolated_x,source_gradient);
2205 
2206 
2207  // Assemble shape derivatives
2208  // --------------------------
2209 
2210  // MOMENTUM EQUATIONS
2211  // ------------------
2212 
2213  // Loop over the test functions
2214  for(unsigned l=0;l<n_node;l++)
2215  {
2216 
2217  // Loop over coordinate directions
2218  for(unsigned i=0;i<DIM;i++)
2219  {
2220  //Get the local equation
2221  local_eqn = nodal_local_eqn(l,u_nodal_index[i]);;
2222 
2223  // IF it's not a boundary condition
2224  if(local_eqn >= 0)
2225  {
2226  // Loop over coordinate directions
2227  for (unsigned p=0;p<DIM;p++)
2228  {
2229  // Loop over nodes
2230  for (unsigned q=0;q<n_node;q++)
2231  {
2232 
2233  // Residual x deriv of Jacobian
2234  //-----------------------------
2235 
2236  //Add the user-defined body force terms
2237  double sum = body_force[i]*testf[l];
2238 
2239  //Add the gravitational body force term
2240  sum += scaled_re_inv_fr*testf[l]*G[i];
2241 
2242  //Add the pressure gradient term
2243  sum += interpolated_p*dtestfdx(l,i);
2244 
2245  //Add in the stress tensor terms
2246  //The viscosity ratio needs to go in here to ensure
2247  //continuity of normal stress is satisfied even in flows
2248  //with zero pressure gradient!
2249  for(unsigned k=0;k<DIM;k++)
2250  {
2251  sum -= visc_ratio*
2252  (interpolated_dudx(i,k) + Gamma[i]*interpolated_dudx(k,i))
2253  *dtestfdx(l,k);
2254  }
2255 
2256  // Add in the inertial terms
2257 
2258  // du/dt term
2259  sum -= scaled_re_st*dudt[i]*testf[l];
2260 
2261  // Convective terms, including mesh velocity
2262  for(unsigned k=0;k<DIM;k++)
2263  {
2264  double tmp=scaled_re*interpolated_u[k];
2265  if (!ALE_is_disabled) { tmp-=scaled_re_st*mesh_velocity[k]; }
2266  sum -= tmp*interpolated_dudx(i,k)*testf[l];
2267  }
2268 
2269  // Multiply through by deriv of Jacobian and integration weight
2270  dresidual_dnodal_coordinates(local_eqn,p,q)+=sum*dJ_dX(p,q)*w;
2271 
2272  // Derivative of residual x Jacobian
2273  //----------------------------------
2274 
2275  // Body force
2276  sum=d_body_force_dx(i,p)*psif(q)*testf(l);
2277 
2278  // Pressure gradient term
2279  sum += interpolated_p*d_dtestfdx_dX(p,q,l,i);
2280 
2281  // Viscous term
2282  for (unsigned k=0;k<DIM;k++)
2283  {
2284  sum -= visc_ratio*(
2285  (interpolated_dudx(i,k) + Gamma[i]*interpolated_dudx(k,i))
2286  *d_dtestfdx_dX(p,q,l,k)+
2287  (d_dudx_dX(p,q,i,k) + Gamma[i]*d_dudx_dX(p,q,k,i))
2288  *dtestfdx(l,k));
2289  }
2290 
2291  // Convective terms, including mesh velocity
2292  for(unsigned k=0;k<DIM;k++)
2293  {
2294  double tmp=scaled_re*interpolated_u[k];
2295  if(!ALE_is_disabled) { tmp-=scaled_re_st*mesh_velocity[k]; }
2296  sum -= tmp*d_dudx_dX(p,q,i,k)*testf(l);
2297  }
2298  if(!ALE_is_disabled)
2299  {
2300  sum+=scaled_re_st*pos_time_weight*
2301  psif(q)*interpolated_dudx(i,p)*testf(l);
2302  }
2303 
2304  // Multiply through by Jacobian and integration weight
2305  dresidual_dnodal_coordinates(local_eqn,p,q)+=sum*J*w;
2306 
2307  // Derivs w.r.t. to nodal velocities
2308  // ---------------------------------
2309  if(element_has_node_with_aux_node_update_fct)
2310  {
2311  sum=-visc_ratio*Gamma[i]*dpsifdx(q,i)*dtestfdx(l,p)
2312  -scaled_re*psif(q)*interpolated_dudx(i,p)*testf(l);
2313  if (i==p)
2314  {
2315  sum-=scaled_re_st*val_time_weight*psif(q)*testf(l);
2316  for (unsigned k=0;k<DIM;k++)
2317  {
2318  sum-=visc_ratio*dpsifdx(q,k)*dtestfdx(l,k);
2319  double tmp=scaled_re*interpolated_u[k];
2320  if (!ALE_is_disabled) tmp-=scaled_re_st*mesh_velocity[k];
2321  sum-=tmp*dpsifdx(q,k)*testf(l);
2322  }
2323  }
2324  dresidual_dnodal_coordinates(local_eqn,p,q)+=
2325  sum*d_U_dX(p,q)*J*w;
2326  }
2327  }
2328  }
2329  }
2330  }
2331  } // End of loop over test functions
2332 
2333 
2334  // CONTINUITY EQUATION
2335  // -------------------
2336 
2337  //Loop over the shape functions
2338  for(unsigned l=0;l<n_pres;l++)
2339  {
2340  local_eqn = p_local_eqn(l);
2341 
2342  //If not a boundary conditions
2343  if(local_eqn >= 0)
2344  {
2345 
2346  // Loop over coordinate directions
2347  for (unsigned p=0;p<DIM;p++)
2348  {
2349  // Loop over nodes
2350  for (unsigned q=0;q<n_node;q++)
2351  {
2352 
2353  // Residual x deriv of Jacobian
2354  //-----------------------------
2355 
2356  // Source term
2357  double aux=-source;
2358 
2359  // Loop over velocity components
2360  for(unsigned k=0;k<DIM;k++)
2361  {
2362  aux += interpolated_dudx(k,k);
2363  }
2364 
2365  // Multiply through by deriv of Jacobian and integration weight
2366  dresidual_dnodal_coordinates(local_eqn,p,q)+=
2367  aux*dJ_dX(p,q)*testp[l]*w;
2368 
2369  // Derivative of residual x Jacobian
2370  //----------------------------------
2371 
2372  // Loop over velocity components
2373  aux=-source_gradient[p]*psif(q);
2374  for(unsigned k=0;k<DIM;k++)
2375  {
2376  aux += d_dudx_dX(p,q,k,k);
2377  }
2378  // Multiply through by Jacobian and integration weight
2379  dresidual_dnodal_coordinates(local_eqn,p,q)+=
2380  aux*testp[l]*J*w;
2381 
2382  // Derivs w.r.t. to nodal velocities
2383  //---------------------------------
2384  if(element_has_node_with_aux_node_update_fct)
2385  {
2386  aux=dpsifdx(q,p)*testp(l);
2387  dresidual_dnodal_coordinates(local_eqn,p,q)+=
2388  aux*d_U_dX(p,q)*J*w;
2389  }
2390  }
2391  }
2392  }
2393  }
2394  } // End of loop over integration points
2395 
2396 }
2397 
2398 
2399 
2400 
2401 
2402 
2403 
2404 
2405 //////////////////////////////////////////////////////////////////////
2406 //////////////////////////////////////////////////////////////////////
2407 //////////////////////////////////////////////////////////////////////
2408 
2409 ///2D Crouzeix-Raviart elements
2410 //Set the data for the number of Variables at each node
2411 template<>
2413 ={2,2,2,2,2,2,2,2,2};
2414 
2415 ///3D Crouzeix-Raviart elements
2416 //Set the data for the number of Variables at each node
2417 template<>
2419 ={3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
2420 
2421 
2422 //========================================================================
2423 /// Number of values (pinned or dofs) required at node n.
2424 //========================================================================
2425 template<unsigned DIM>
2426 unsigned QCrouzeixRaviartElement<DIM>::required_nvalue(const unsigned& n) const
2427  {return Initial_Nvalue[n];}
2428 
2429 
2430 //=========================================================================
2431 /// Add to the set \c paired_load_data pairs containing
2432 /// - the pointer to a Data object
2433 /// and
2434 /// - the index of the value in that Data object
2435 /// .
2436 /// for all values (pressures, velocities) that affect the
2437 /// load computed in the \c get_load(...) function.
2438 //=========================================================================
2439 template<unsigned DIM>
2441 identify_load_data(std::set<std::pair<Data*,unsigned> > &paired_load_data)
2442 {
2443  //Find the index at which the velocity is stored
2444  unsigned u_index[DIM];
2445  for(unsigned i=0;i<DIM;i++) {u_index[i] = this->u_index_nst(i);}
2446 
2447  //Loop over the nodes
2448  unsigned n_node = this->nnode();
2449  for(unsigned n=0;n<n_node;n++)
2450  {
2451  //Loop over the velocity components and add pointer to their data
2452  //and indices to the vectors
2453  for(unsigned i=0;i<DIM;i++)
2454  {
2455  paired_load_data.insert(std::make_pair(this->node_pt(n),u_index[i]));
2456  }
2457  }
2458 
2459  // Identify the pressure data
2460  identify_pressure_data(paired_load_data);
2461 
2462 }
2463 
2464 
2465 //=========================================================================
2466 /// Add to the set \c paired_pressue_data pairs containing
2467 /// - the pointer to a Data object
2468 /// and
2469 /// - the index of the value in that Data object
2470 /// .
2471 /// for all pressures values that affect the
2472 /// load computed in the \c get_load(...) function.
2473 //=========================================================================
2474 template<unsigned DIM>
2476 identify_pressure_data(std::set<std::pair<Data*,unsigned> > &paired_pressure_data)
2477 {
2478  //Loop over the internal data
2479  unsigned n_internal = this->ninternal_data();
2480  for(unsigned l=0;l<n_internal;l++)
2481  {
2482  unsigned nval=this->internal_data_pt(l)->nvalue();
2483  //Add internal data
2484  for (unsigned j=0;j<nval;j++)
2485  {
2486  paired_pressure_data.insert(std::make_pair(this->internal_data_pt(l),j));
2487  }
2488  }
2489 }
2490 
2491 
2492 //=============================================================================
2493 /// Create a list of pairs for all unknowns in this element,
2494 /// so that the first entry in each pair contains the global equation
2495 /// number of the unknown, while the second one contains the number
2496 /// of the DOF that this unknown is associated with.
2497 /// (Function can obviously only be called if the equation numbering
2498 /// scheme has been set up.)
2499 //=============================================================================
2500 template<unsigned DIM>
2502  std::list<std::pair<unsigned long,unsigned> >& dof_lookup_list) const
2503 {
2504  // number of nodes
2505  unsigned n_node = this->nnode();
2506 
2507  // number of pressure values
2508  unsigned n_press = this->npres_nst();
2509 
2510  // temporary pair (used to store dof lookup prior to being added to list)
2511  std::pair<unsigned,unsigned> dof_lookup;
2512 
2513  // pressure dof number
2514  unsigned pressure_dof_number = DIM;
2515 
2516  // loop over the pressure values
2517  for (unsigned n = 0; n < n_press; n++)
2518  {
2519  // determine local eqn number
2520  int local_eqn_number = this->p_local_eqn(n);
2521 
2522  // ignore pinned values - far away degrees of freedom resulting
2523  // from hanging nodes can be ignored since these are be dealt
2524  // with by the element containing their master nodes
2525  if (local_eqn_number >= 0)
2526  {
2527  // store dof lookup in temporary pair: First entry in pair
2528  // is global equation number; second entry is dof type
2529  dof_lookup.first = this->eqn_number(local_eqn_number);
2530  dof_lookup.second = pressure_dof_number;
2531 
2532  // add to list
2533  dof_lookup_list.push_front(dof_lookup);
2534  }
2535  }
2536 
2537  // loop over the nodes
2538  for (unsigned n = 0; n < n_node; n++)
2539  {
2540  // find the number of values at this node
2541  unsigned nv = this->node_pt(n)->nvalue();
2542 
2543  //loop over these values
2544  for (unsigned v = 0; v < nv; v++)
2545  {
2546  // determine local eqn number
2547  int local_eqn_number = this->nodal_local_eqn(n, v);
2548 
2549  // ignore pinned values
2550  if (local_eqn_number >= 0)
2551  {
2552  // store dof lookup in temporary pair: First entry in pair
2553  // is global equation number; second entry is dof type
2554  dof_lookup.first = this->eqn_number(local_eqn_number);
2555  dof_lookup.second = v;
2556 
2557  // add to list
2558  dof_lookup_list.push_front(dof_lookup);
2559 
2560  }
2561  }
2562  }
2563 }
2564 
2565 
2566 
2567 
2568 ///////////////////////////////////////////////////////////////////////////
2569 ///////////////////////////////////////////////////////////////////////////
2570 ///////////////////////////////////////////////////////////////////////////
2571 
2572 
2573 
2574 //2D Taylor--Hood
2575 //Set the data for the number of Variables at each node
2576 template<>
2577 const unsigned QTaylorHoodElement<2>::Initial_Nvalue[9]={3,2,3,2,2,2,3,2,3};
2578 
2579 //Set the data for the pressure conversion array
2580 template<>
2581 const unsigned QTaylorHoodElement<2>::Pconv[4]={0,2,6,8};
2582 
2583 //3D Taylor--Hood
2584 //Set the data for the number of Variables at each node
2585 template<>
2586 const unsigned QTaylorHoodElement<3>::Initial_Nvalue[27]={4,3,4,3,3,3,4,3,4,3,3,3,3,3,3,3,3,3,4,3,4,3,3,3,4,3,4};
2587 
2588 //Set the data for the pressure conversion array
2589 template<>
2590 const unsigned QTaylorHoodElement<3>::Pconv[8]={0,2,6,8,18,20,24,26};
2591 
2592 //=========================================================================
2593 /// Add to the set \c paired_load_data pairs containing
2594 /// - the pointer to a Data object
2595 /// and
2596 /// - the index of the value in that Data object
2597 /// .
2598 /// for all values (pressures, velocities) that affect the
2599 /// load computed in the \c get_load(...) function.
2600 //=========================================================================
2601 template<unsigned DIM>
2603 identify_load_data(std::set<std::pair<Data*,unsigned> > &paired_load_data)
2604 {
2605  //Find the index at which the velocity is stored
2606  unsigned u_index[DIM];
2607  for(unsigned i=0;i<DIM;i++) {u_index[i] = this->u_index_nst(i);}
2608 
2609  //Loop over the nodes
2610  unsigned n_node = this->nnode();
2611  for(unsigned n=0;n<n_node;n++)
2612  {
2613  //Loop over the velocity components and add pointer to their data
2614  //and indices to the vectors
2615  for(unsigned i=0;i<DIM;i++)
2616  {
2617  paired_load_data.insert(std::make_pair(this->node_pt(n),u_index[i]));
2618  }
2619  }
2620 
2621  //Identify the pressure data
2622  this->identify_pressure_data(paired_load_data);
2623 
2624 }
2625 
2626 //=========================================================================
2627 /// Add to the set \c paired_pressure_data pairs containing
2628 /// - the pointer to a Data object
2629 /// and
2630 /// - the index of the value in that Data object
2631 /// .
2632 /// for pressure values that affect the
2633 /// load computed in the \c get_load(...) function.,
2634 //=========================================================================
2635 template<unsigned DIM>
2637 identify_pressure_data(std::set<std::pair<Data*,unsigned> > &paired_pressure_data)
2638 {
2639  //Find the index at which the pressure is stored
2640  unsigned p_index = static_cast<unsigned>(this->p_nodal_index_nst());
2641 
2642  //Loop over the pressure data
2643  unsigned n_pres= npres_nst();
2644  for(unsigned l=0;l<n_pres;l++)
2645  {
2646  //The DIMth entry in each nodal data is the pressure, which
2647  //affects the traction
2648  paired_pressure_data.insert(std::make_pair(this->node_pt(Pconv[l]),p_index));
2649  }
2650 }
2651 
2652 
2653 //============================================================================
2654 /// Create a list of pairs for all unknowns in this element,
2655 /// so the first entry in each pair contains the global equation
2656 /// number of the unknown, while the second one contains the number
2657 /// of the "DOF type" that this unknown is associated with.
2658 /// (Function can obviously only be called if the equation numbering
2659 /// scheme has been set up.)
2660 //============================================================================
2661 template<unsigned DIM>
2663  std::list<std::pair<unsigned long,
2664  unsigned> >& dof_lookup_list) const
2665 {
2666  // number of nodes
2667  unsigned n_node = this->nnode();
2668 
2669  // local eqn no for pressure unknown
2670 // unsigned p_index = this->p_nodal_index_nst();
2671 
2672  // temporary pair (used to store dof lookup prior to being added to list)
2673  std::pair<unsigned,unsigned> dof_lookup;
2674 
2675  // loop over the nodes
2676  for (unsigned n = 0; n < n_node; n++)
2677  {
2678  // find the number of values at this node
2679  unsigned nv = this->required_nvalue(n);
2680 
2681  //loop over these values
2682  for (unsigned v = 0; v < nv; v++)
2683  {
2684  // determine local eqn number
2685  int local_eqn_number = this->nodal_local_eqn(n, v);
2686 
2687  // ignore pinned values - far away degrees of freedom resulting
2688  // from hanging nodes can be ignored since these are be dealt
2689  // with by the element containing their master nodes
2690  if (local_eqn_number >= 0)
2691  {
2692  // store dof lookup in temporary pair: Global equation number
2693  // is the first entry in pair
2694  dof_lookup.first = this->eqn_number(local_eqn_number);
2695 
2696  // set dof numbers: Dof number is the second entry in pair
2697  dof_lookup.second = v;
2698 
2699  // add to list
2700  dof_lookup_list.push_front(dof_lookup);
2701  }
2702  }
2703  }
2704  }
2705 
2706 
2707 //====================================================================
2708 //// Force build of templates
2709 //====================================================================
2710 template class NavierStokesEquations<2>;
2711 template class QCrouzeixRaviartElement<2>;
2712 template class QTaylorHoodElement<2>;
2713 
2714 template class NavierStokesEquations<3>;
2715 template class QCrouzeixRaviartElement<3>;
2716 template class QTaylorHoodElement<3>;
2717 
2718 }
bool ALE_is_disabled
Boolean flag to indicate if ALE formulation is disabled when time-derivatives are computed...
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:504
void full_output(std::ostream &outfile)
Full output function: x,y,[z],u,v,[w],p,du/dt,dv/dt,[dw/dt],dissipation in tecplot format...
void get_dof_numbers_for_unknowns(std::list< std::pair< unsigned long, unsigned > > &dof_lookup_list) const
Create a list of pairs for all unknowns in this element, so that the first entry in each pair contain...
void compute_error(std::ostream &outfile, FiniteElement::UnsteadyExactSolutionFctPt exact_soln_pt, const double &time, double &error, double &norm)
Validate against exact solution at given time Solution is provided via function pointer. Plot at a given number of plot points and compute L2 error and L2 norm of velocity solution over element.
void get_pressure_and_velocity_mass_matrix_diagonal(Vector< double > &press_mass_diag, Vector< double > &veloc_mass_diag, const unsigned &which_one=0)
Compute the diagonal of the velocity/pressure mass matrices. If which one=0, both are computed...
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:507
void output_vorticity(std::ostream &outfile, const unsigned &nplot)
Output function: x,y,[z], [omega_x,omega_y,[and/or omega_z]] in tecplot format. nplot points in each ...
cstr elem_len * i
Definition: cfortran.h:607
double * value_pt(const unsigned &i) const
Return the pointer to the i-the stored value. Typically this is required when direct access to the st...
Definition: nodes.h:322
void compute_norm(double &norm)
Compute norm of solution: square of the L2 norm of the velocities.
double kin_energy() const
Get integral of kinetic energy over element.
static Vector< double > Default_Gravity_vector
Static default value for the gravity vector.
void perform_auxiliary_node_update_fct()
Execute auxiliary update function (if any) – this can be used to update any nodal values following t...
Definition: nodes.h:1510
static double Default_Physical_Constant_Value
Static default value for the physical constants (all initialised to zero)
void identify_load_data(std::set< std::pair< Data *, unsigned > > &paired_load_data)
Add to the set paired_load_data pairs containing.
void(* UnsteadyExactSolutionFctPt)(const double &, const Vector< double > &, Vector< double > &)
Function pointer for function that computes Vector-valued time-dependent function as ...
Definition: elements.h:1729
char t
Definition: cfortran.h:572
virtual unsigned required_nvalue(const unsigned &n) const
Number of values (pinned or dofs) required at local node n.
static double Default_Physical_Ratio_Value
Static default value for the physical ratios (all are initialised to one)
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
void output_fct(std::ostream &outfile, const unsigned &nplot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
Output exact solution specified via function pointer at a given number of plot points. Function prints as many components as are returned in solution Vector.
void get_dof_numbers_for_unknowns(std::list< std::pair< unsigned long, unsigned > > &dof_lookup_list) const
Create a list of pairs for all unknowns in this element, so that the first entry in each pair contain...
e
Definition: cfortran.h:575
void get_traction(const Vector< double > &s, const Vector< double > &N, Vector< double > &traction)
Compute traction (on the viscous scale) exerted onto the fluid at local coordinate s...
A Rank 4 Tensor class.
Definition: matrices.h:1625
bool has_auxiliary_node_update_fct_pt()
Boolean to indicate if node has a pointer to and auxiliary update function.
Definition: nodes.h:1501
virtual void fill_in_generic_dresidual_contribution_nst(double *const &parameter_pt, Vector< double > &dres_dparam, DenseMatrix< double > &djac_dparam, DenseMatrix< double > &dmass_matrix_dparam, unsigned flag)
Compute the derivatives of the residuals for the Navier–Stokes equations with respect to a parameter...
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
static Vector< double > Gamma
Vector to decide whether the stress-divergence form is used or not.
void output_veloc(std::ostream &outfile, const unsigned &nplot, const unsigned &t)
Output function: x,y,[z],u,v,[w] in tecplot format. nplot points in each coordinate direction at time...
void(* SteadyExactSolutionFctPt)(const Vector< double > &, Vector< double > &)
Function pointer for function that computes vector-valued steady "exact solution" as ...
Definition: elements.h:1723
void fill_in_contribution_to_hessian_vector_products(Vector< double > const &Y, DenseMatrix< double > const &C, DenseMatrix< double > &product)
Compute the hessian tensor vector products required to perform continuation of bifurcations analytica...
virtual void get_dresidual_dnodal_coordinates(RankThreeTensor< double > &dresidual_dnodal_coordinates)
Compute derivatives of elemental residual vector with respect to nodal coordinates. Overwrites default implementation in FiniteElement base class. dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}.
A Rank 3 Tensor class.
Definition: matrices.h:1337
virtual void fill_in_generic_pressure_advection_diffusion_contribution_nst(Vector< double > &residuals, DenseMatrix< double > &jacobian, unsigned flag)
Compute the residuals for the associated pressure advection diffusion problem. Used by the Fp precond...
static char t char * s
Definition: cfortran.h:572
void identify_load_data(std::set< std::pair< Data *, unsigned > > &paired_load_data)
Add to the set paired_load_data pairs containing.
void identify_pressure_data(std::set< std::pair< Data *, unsigned > > &paired_pressure_data)
Add to the set paired_pressure_data pairs containing.
void shape(const double &s, double *Psi)
Definition for 1D Lagrange shape functions. The value of all the shape functions at the local coordin...
Definition: shape.h:549
void output(std::ostream &outfile)
Output function: x,y,[z],u,v,[w],p in tecplot format. Default number of plot points.
void identify_pressure_data(std::set< std::pair< Data *, unsigned > > &paired_pressure_data)
Add to the set paired_pressure_data pairs containing.
double dissipation() const
Return integral of dissipation over element.
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
void get_vorticity(const Vector< double > &s, Vector< double > &vorticity) const
Compute the vorticity vector at local coordinate s.
void strain_rate(const Vector< double > &s, DenseMatrix< double > &strain_rate) const
Strain-rate tensor: 1/2 (du_i/dx_j + du_j/dx_i)
double pressure_integral() const
Integral of pressure over element.
virtual void fill_in_generic_residual_contribution_nst(Vector< double > &residuals, DenseMatrix< double > &jacobian, DenseMatrix< double > &mass_matrix, unsigned flag)
Compute the residuals for the Navier–Stokes equations. Flag=1 (or 0): do (or don&#39;t) compute the Jaco...
static int Pressure_not_stored_at_node
Static "magic" number that indicates that the pressure is not stored at a node.
double d_kin_energy_dt() const
Get integral of time derivative of kinetic energy over element.
static double Default_fd_jacobian_step
Double used for the default finite difference step in elemental jacobian calculations.
Definition: elements.h:1164