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