refineable_mixed_order_petrov_galerkin_space_time_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 space-time Navier-Stokes elements
28
29/////////////////////////////////////////////////////////////////////////
30/////////////////////////////////////////////////////////////////////////
31/////////////////////////////////////////////////////////////////////////
32
33namespace oomph
34{
35 //===================================================================
36 /// Compute the diagonal of the velocity/pressure mass matrices.
37 /// If which one=0, both are computed, otherwise only the pressure
38 /// (which_one=1) or the velocity mass matrix (which_one=2 -- the
39 /// LSC version of the preconditioner only needs that one)
40 //===================================================================
41 template<unsigned DIM>
46 const unsigned& which_one)
47 {
48 // Resize and initialise
49 unsigned n_dof = ndof();
50
51 // If the pressure mass matrix needs to be constructed
52 if ((which_one == 0) || (which_one == 1))
53 {
54 // Assign the appropriate amount of space
55 press_mass_diag.assign(n_dof, 0.0);
56 }
57
58 // If the velocity mass matrix needs to be constructed
59 if ((which_one == 0) || (which_one == 2))
60 {
61 // Assign the appropriate amount of space
62 veloc_mass_diag.assign(n_dof, 0.0);
63 }
64
65 // Pointer to hang info object
67
68 // Number of master nodes
69 unsigned n_master = 1;
70
71 // Hang weight for shape functions
72 double hang_weight = 1.0;
73
74 // Find out how many nodes there are in the element
75 unsigned n_node = nnode();
76
77 // Local coordinates
78 Vector<double> s(DIM + 1, 0.0);
79
80 // Storage for the local velocity indices
82
83 // Find the indices at which the local velocities are stored
84 for (unsigned i = 0; i < DIM; i++)
85 {
86 // Calculate the i-th local velocity component
87 u_nodal_index[i] = this->u_index_nst(i);
88 }
89
90 // Set up memory for velocity shape functions
92
93 // Find number of pressure dofs
94 unsigned n_pres = this->npres_nst();
95
96 // Pressure shape function
98
99 // Which nodal value represents the pressure? (Negative if pressure
100 // is not based on nodal interpolation).
101 int p_index = this->p_nodal_index_nst();
102
103 // Local array of booleans that are true if the l-th pressure value is
104 // hanging (avoid repeated virtual function calls)
106
107 // If the pressure is stored at a node
108 if (p_index >= 0)
109 {
110 // Loop over the pressure nodes
111 for (unsigned l = 0; l < n_pres; l++)
112 {
113 // Read out the hang status of the pressure node
115 this->pressure_node_pt(l)->is_hanging(p_index);
116 }
117 }
118 // Otherwise the pressure is not stored at a node and so cannot hang
119 else
120 {
121 // Loop over the pressures nodes
122 for (unsigned l = 0; l < n_pres; l++)
123 {
124 // Indicate that the pressure node is not hanging
125 pressure_dof_is_hanging[l] = false;
126 }
127 } // if (p_index>=0)
128
129 // Number of integration points
130 unsigned n_intpt = integral_pt()->nweight();
131
132 // Integer to store the local equations no
133 int local_eqn = 0;
134
135 // Loop over the integration points
136 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
137 {
138 // Get the integral weight
139 double w = integral_pt()->weight(ipt);
140
141 // Get determinant of Jacobian of the mapping
142 double J = J_eulerian_at_knot(ipt);
143
144 // Assign values of s
145 for (unsigned i = 0; i < DIM + 1; i++)
146 {
147 // Calculate the i-th local coordinate at the ipt-th integration point
148 s[i] = integral_pt()->knot(ipt, i);
149 }
150
151 // Premultiply weights and Jacobian
152 double W = w * J;
153
154 // Do we want the velocity one?
155 if ((which_one == 0) || (which_one == 2))
156 {
157 // Get the velocity shape functions
159
160 // Loop over the velocity shape functions
161 for (unsigned l = 0; l < n_node; l++)
162 {
163 // Local boolean to indicate whether or not the node is hanging
165
166 // If the node is hanging
167 if (is_node_hanging)
168 {
169 // Get the HangInfo pointer from the node
171
172 // Read out number of master nodes from hanging data
173 n_master = hang_info_pt->nmaster();
174 }
175 // Otherwise the node is its own master
176 else
177 {
178 // There is only one node to consider; the node itself
179 n_master = 1;
180 }
181
182 // Loop over the master nodes
183 for (unsigned m = 0; m < n_master; m++)
184 {
185 // Loop over velocity components for equations
186 for (unsigned i = 0; i < DIM; i++)
187 {
188 // If the node is hanging
189 if (is_node_hanging)
190 {
191 // Get the equation number from the master node
192 local_eqn = this->local_hang_eqn(
193 hang_info_pt->master_node_pt(m), u_nodal_index[i]);
194
195 // Get the hang weight from the master node
196 hang_weight = hang_info_pt->master_weight(m);
197 }
198 // If the node is not hanging
199 else
200 {
201 // Local equation number
203
204 // Node contributes with full weight
205 hang_weight = 1.0;
206 }
207
208 // If it's not a boundary condition
209 if (local_eqn >= 0)
210 {
211 // Add the contribution
213 }
214 } // for (unsigned i=0;i<DIM;i++)
215 } // for (unsigned m=0;m<n_master;m++)
216 } // if ((which_one==0)||(which_one==2))
217 } // for (unsigned ipt=0;ipt<n_intpt;ipt++)
218
219 // Do we want the pressure one?
220 if ((which_one == 0) || (which_one == 1))
221 {
222 // Get the pressure shape functions
223 this->pshape_nst(s, psi_p);
224
225 // Loop over the pressure shape functions
226 for (unsigned l = 0; l < n_pres; l++)
227 {
228 // If the pressure dof is hanging
230 {
231 // Get the HangInfo object
232 hang_info_pt = this->pressure_node_pt(l)->hanging_pt(p_index);
233
234 // Get the number of master nodes from the pressure node
235 n_master = hang_info_pt->nmaster();
236 }
237 // Otherwise the node is its own master
238 else
239 {
240 // There is only one node to consider; the node itself
241 n_master = 1;
242 }
243
244 // Loop over the master nodes
245 for (unsigned m = 0; m < n_master; m++)
246 {
247 // If the pressure dof is hanging
249 {
250 // Get the local equation from the master node
251 local_eqn =
252 this->local_hang_eqn(hang_info_pt->master_node_pt(m), p_index);
253
254 // Get the weight for the node
255 hang_weight = hang_info_pt->master_weight(m);
256 }
257 // If the pressure dof is not hanging
258 else
259 {
260 // Get the local equation number of this pressure dof
261 local_eqn = this->p_local_eqn(l);
262
263 // Get the hang weight associated with this pressure node
264 hang_weight = 1.0;
265 }
266
267 // If the equation is not pinned
268 if (local_eqn >= 0)
269 {
270 // Add the contribution
272 }
273 } // for (unsigned m=0;m<n_master;m++)
274 } // for (unsigned l=0;l<n_pres;l++)
275 } // if ((which_one==0)||(which_one==1))
276 } // for (unsigned ipt=0;ipt<n_intpt;ipt++)
277 } // End of get_pressure_and_velocity_mass_matrix_diagonal
278
279
280 //==============================================================
281 /// Compute the residuals for the associated pressure advection
282 /// diffusion problem. Used by the Fp preconditioner.
283 /// flag=1(or 0): do (or don't) compute the Jacobian as well.
284 //==============================================================
285 template<unsigned DIM>
289 DenseMatrix<double>& jacobian,
290 const unsigned& flag)
291 {
292 OomphLibWarning("I'm not sure this is correct yet...",
295
296 // Return immediately if there are no dofs
297 if (ndof() == 0) return;
298
299 // Find out how many nodes there are
300 unsigned n_node = nnode();
301
302 // Find out how many pressure dofs there are
303 unsigned n_pres = this->npres_nst();
304
305 // Find the indices at which the local velocities are stored
306 unsigned u_nodal_index[DIM];
307 for (unsigned i = 0; i < DIM; i++)
308 {
309 u_nodal_index[i] = this->u_index_nst(i);
310 }
311
312
313 // Which nodal value represents the pressure? (Negative if pressure
314 // is not based on nodal interpolation).
315 int p_index = this->p_nodal_index_nst();
316
317 // Local array of booleans that are true if the l-th pressure value is
318 // hanging (avoid repeated virtual function calls)
320 // If the pressure is stored at a node
321 if (p_index >= 0)
322 {
323 // Read out whether the pressure is hanging
324 for (unsigned l = 0; l < n_pres; ++l)
325 {
327 this->pressure_node_pt(l)->is_hanging(p_index);
328 }
329 }
330 // Otherwise the pressure is not stored at a node and so cannot hang
331 else
332 {
333 // pressure advection diffusion doesn't work for this one!
334 throw OomphLibError(
335 "Pressure advection diffusion does not work in this case\n",
338
339 for (unsigned l = 0; l < n_pres; ++l)
340 {
341 pressure_dof_is_hanging[l] = false;
342 }
343 }
344
345 // Set up memory for the velocity shape fcts
348
349 // Set up memory for pressure shape and test functions
353
354 // Number of integration points
355 unsigned n_intpt = integral_pt()->nweight();
356
357 // Set the Vector to hold local coordinates
359
360 // Get Physical Variables from Element
361 // Reynolds number must be multiplied by the density ratio
362 double scaled_re = this->re() * this->density_ratio();
363
364 // Integers to store the local equations and unknowns
365 int local_eqn = 0, local_unknown = 0;
366
367 // Pointers to hang info objects
369
370 // Loop over the integration points
371 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
372 {
373 // Assign values of s
374 for (unsigned i = 0; i < DIM; i++) s[i] = integral_pt()->knot(ipt, i);
375
376 // Get the integral weight
377 double w = integral_pt()->weight(ipt);
378
379 // Call the derivatives of the veloc shape functions
380 // (Derivs not needed but they are free)
381 double J = this->dshape_eulerian_at_knot(ipt, psif, dpsidx);
382
383 // Call the pressure shape and test functions
384 this->dpshape_and_dptest_eulerian_nst(s, psip, dpsip, testp, dtestp);
385
386 // Premultiply the weights and the Jacobian
387 double W = w * J;
388
389 // Calculate local values of the pressure and velocity components
390 // Allocate
391 Vector<double> interpolated_u(DIM, 0.0);
394
395 // Calculate pressure gradient
396 for (unsigned l = 0; l < n_pres; l++)
397 {
398 for (unsigned i = 0; i < DIM; i++)
399 {
400 interpolated_dpdx[i] += this->p_nst(l) * dpsip(l, i);
401 }
402 }
403
404 // Calculate velocities
405
406 // Loop over nodes
407 for (unsigned l = 0; l < n_node; l++)
408 {
409 // Loop over directions
410 for (unsigned i = 0; i < DIM; i++)
411 {
412 // Get the nodal value
413 double u_value = nodal_value(l, u_nodal_index[i]);
414 interpolated_u[i] += u_value * psif[l];
416 }
417 }
418
419 // Source function (for validaton only)
420 double source = 0.0;
421 if (this->Press_adv_diff_source_fct_pt != 0)
422 {
423 source = this->Press_adv_diff_source_fct_pt(interpolated_x);
424 }
425
426
427 // Number of master nodes and storage for the weight of the shape function
428 unsigned n_master = 1;
429 double hang_weight = 1.0;
430
431
432 // Loop over the pressure shape functions
433 for (unsigned l = 0; l < n_pres; l++)
434 {
435 // If the pressure dof is hanging
437 {
438 // Pressure dof is hanging so it must be nodal-based
439 // Get the hang info object
440 hang_info_pt = this->pressure_node_pt(l)->hanging_pt(p_index);
441
442 // Get the number of master nodes from the pressure node
443 n_master = hang_info_pt->nmaster();
444 }
445 // Otherwise the node is its own master
446 else
447 {
448 n_master = 1;
449 }
450
451 // Loop over the master nodes
452 for (unsigned m = 0; m < n_master; m++)
453 {
454 // Get the number of the unknown
455 // If the pressure dof is hanging
457 {
458 // Get the local equation from the master node
459 local_eqn =
460 this->local_hang_eqn(hang_info_pt->master_node_pt(m), p_index);
461 // Get the weight for the node
462 hang_weight = hang_info_pt->master_weight(m);
463 }
464 else
465 {
466 local_eqn = this->p_local_eqn(l);
467 hang_weight = 1.0;
468 }
469
470 // If the equation is not pinned
471 if (local_eqn >= 0)
472 {
473 residuals[local_eqn] -= source * testp[l] * W * hang_weight;
474 for (unsigned k = 0; k < DIM; k++)
475 {
478 (scaled_re * interpolated_u[k] * testp[l] + dtestp(l, k)) * W *
480 }
481
482 // Jacobian too?
483 if (flag)
484 {
485 // Number of master nodes and weights
486 unsigned n_master2 = 1;
487 double hang_weight2 = 1.0;
488
489 // Loop over the pressure shape functions
490 for (unsigned l2 = 0; l2 < n_pres; l2++)
491 {
492 // If the pressure dof is hanging
494 {
496 this->pressure_node_pt(l2)->hanging_pt(p_index);
497 // Pressure dof is hanging so it must be nodal-based
498 // Get the number of master nodes from the pressure node
499 n_master2 = hang_info2_pt->nmaster();
500 }
501 // Otherwise the node is its own master
502 else
503 {
504 n_master2 = 1;
505 }
506
507 // Loop over the master nodes
508 for (unsigned m2 = 0; m2 < n_master2; m2++)
509 {
510 // Get the number of the unknown
511 // If the pressure dof is hanging
513 {
514 // Get the unknown from the master node
515 local_unknown = this->local_hang_eqn(
516 hang_info2_pt->master_node_pt(m2), p_index);
517 // Get the weight from the hanging object
518 hang_weight2 = hang_info2_pt->master_weight(m2);
519 }
520 else
521 {
522 local_unknown = this->p_local_eqn(l2);
523 hang_weight2 = 1.0;
524 }
525
526 // If the unknown is not pinned
527 if (local_unknown >= 0)
528 {
529 if ((int(eqn_number(local_eqn)) !=
530 this->Pinned_fp_pressure_eqn) &&
531 (int(eqn_number(local_unknown)) !=
532 this->Pinned_fp_pressure_eqn))
533 {
534 for (unsigned k = 0; k < DIM; k++)
535 {
536 jacobian(local_eqn, local_unknown) +=
537 dtestp(l2, k) *
538 (scaled_re * interpolated_u[k] * testp[l] +
539 dtestp(l, k)) *
541 }
542 }
543 else
544 {
545 if ((int(eqn_number(local_eqn)) ==
546 this->Pinned_fp_pressure_eqn) &&
548 this->Pinned_fp_pressure_eqn))
549 {
550 jacobian(local_eqn, local_unknown) = 1.0;
551 }
552 }
553 }
554 }
555 }
556 } /*End of Jacobian calculation*/
557 } // End of if not boundary condition
558 } // End of loop over master nodes
559 } // End of loop over l
560 } // end of integration loop
561
562 // Now add boundary contributions from Robin BCs
563 unsigned nrobin =
564 this->Pressure_advection_diffusion_robin_element_pt.size();
565 for (unsigned e = 0; e < nrobin; e++)
566 {
567 this->Pressure_advection_diffusion_robin_element_pt[e]
568 ->fill_in_generic_residual_contribution_fp_press_adv_diff_robin_bc(
569 residuals, jacobian, flag);
570 }
571 }
572
573
574 //========================================================================
575 /// Add element's contribution to the elemental
576 /// residual vector and/or Jacobian matrix.
577 /// flag=1: compute both
578 /// flag=0: compute only residual vector
579 //========================================================================
580 template<unsigned DIM>
583 DenseMatrix<double>& jacobian,
585 const unsigned& flag)
586 {
587 // Return immediately if there are no dofs
588 if (ndof() == 0) return;
589
590 // Find out how many nodes there are
591 unsigned n_node = nnode();
592
593 // Find out how many pressure dofs there are
594 unsigned n_pres = this->npres_nst();
595
596 // Allocate storage for the indices of the velocity components
597 unsigned u_nodal_index[DIM];
598
599 // Loop over the velocity components
600 for (unsigned i = 0; i < DIM; i++)
601 {
602 // Find the index at which the i-th local velocity is stored
603 u_nodal_index[i] = this->u_index_nst(i);
604 }
605
606 // Which nodal value represents the pressure? (Negative if pressure
607 // is not based on nodal interpolation).
608 int p_index = this->p_nodal_index_nst();
609
610 // Local array of booleans that are true if the l-th pressure value is
611 // hanging (avoid repeated virtual function calls)
613
614 // If the pressure is stored at a node
615 if (p_index >= 0)
616 {
617 // Loop over the pressure nodes
618 for (unsigned l = 0; l < n_pres; ++l)
619 {
620 // Get the hang status of the l-th pressure node
622 this->pressure_node_pt(l)->is_hanging(p_index);
623 }
624 }
625 // Otherwise the pressure is not stored at a node and so cannot hang
626 else
627 {
628 // Loop over the pressure nodes
629 for (unsigned l = 0; l < n_pres; ++l)
630 {
631 // Indicate that the l-th pressure node is not hangings
632 pressure_dof_is_hanging[l] = false;
633 }
634 } // if (p_index>=0)
635
636 // Set up memory for the shape functions
638
639 // Set up memory for the test functions
641
642 // Allocate space for the derivatives of the shape functions
643 DShape dpsifdx(n_node, DIM + 1);
644
645 // Allocate space for the derivatives of the test functions
647
648 // Set up memory for pressure shape functions
650
651 // Set up memory for pressure test functions
653
654 // Number of integration points
655 unsigned n_intpt = integral_pt()->nweight();
656
657 // Set the Vector to hold local coordinates
658 Vector<double> s(DIM + 1, 0.0);
659
660 //-------------------------------------
661 // Get physical variables from element:
662 //-------------------------------------
663 // Reynolds number must be multiplied by the density ratio
664 double scaled_re = this->re() * this->density_ratio();
665
666 // Get the scaled Womersley value
667 double scaled_re_st = this->re_st() * this->density_ratio();
668
669 // Get the scaled Womersley value differentiated w.r.t. the Strouhal number
670 double scaled_dre_st_dre_st = this->density_ratio();
671
672 // Get the scaled Reynolds / Froude number
673 double scaled_re_inv_fr = this->re_invfr() * this->density_ratio();
674
675 // Get the viscosity ratio
676 double visc_ratio = this->viscosity_ratio();
677
678 // Get the gravity vector
679 Vector<double> G = this->g();
680
681 // Integer to store the local equation number
682 int local_eqn = 0;
683
684 // Integer to store the local unknown number
685 int local_unknown = 0;
686
687 // Pointers to first hang info object
689
690 // Pointers to second hang info object
692
693 // Loop over the integration points
694 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
695 {
696 // Assign values of s
697 for (unsigned i = 0; i < DIM + 1; i++)
698 {
699 // Calculate the i-th local coordinate
700 s[i] = integral_pt()->knot(ipt, i);
701 }
702
703 // Get the integral weight
704 double w = integral_pt()->weight(ipt);
705
706 // Call the derivatives of the shape and test functions
707 double J = this->dshape_and_dtest_eulerian_at_knot_nst(
709
710 // Call the pressure shape and test functions
711 this->pshape_nst(s, psip, testp);
712
713 // Pre-multiply the weights and the Jacobian
714 double W = w * J;
715
716 // Storage for the interpolated time value
717 double interpolated_t = 0.0;
718
719 // Storage for the interpolated pressure value
720 double interpolated_p = 0.0;
721
722 // Storage for the spatial coordinates
724
725 // Storage for the interpolated velocity components
726 Vector<double> interpolated_u(DIM, 0.0);
727
728 // Storage for the interpolated time-derivative of the velocities
730
731 // Storage for the mesh velocity
733
734 // Storage for the spatial derivatives of the velocity components
735 DenseMatrix<double> interpolated_dudx(DIM, DIM, 0.0);
736
737 // Calculate pressure
738 for (unsigned l = 0; l < n_pres; l++)
739 {
740 // Update the current approximation to the interpolated pressure
741 interpolated_p += this->p_nst(l) * psip[l];
742 }
743
744 //-------------------------------------------------------------------
745 // Calculate velocities, derivatives, source function and body force:
746 //-------------------------------------------------------------------
747 // Loop over nodes
748 for (unsigned l = 0; l < n_node; l++)
749 {
750 // Update the interpolated time value
751 interpolated_t += this->nodal_position(l, DIM) * psif(l);
752
753 // Loop over coordinate directions
754 for (unsigned i = 0; i < DIM; i++)
755 {
756 // Get the nodal value
757 double u_value = this->nodal_value(l, u_nodal_index[i]);
758
759 // Update the i-th interpolated velocity component
760 interpolated_u[i] += u_value * psif[l];
761
762 // Update the i-th interpolated coordinate value
763 interpolated_x[i] += this->nodal_position(l, i) * psif[l];
764
765 // Update the interpolated du_i/dt value
767
768 // Loop over derivative directions
769 for (unsigned j = 0; j < DIM; j++)
770 {
771 // Update the interpolated du_i/dx_j value
772 interpolated_dudx(i, j) += u_value * dpsifdx(l, j);
773 }
774 } // for (unsigned i=0;i<DIM;i++)
775 } // for (unsigned l=0;l<n_node;l++)
776
777 // If ALE is enabled
778 if (!(this->ALE_is_disabled))
779 {
780 // Loop over nodes
781 for (unsigned l = 0; l < n_node; l++)
782 {
783 // Loop over directions
784 for (unsigned i = 0; i < DIM; i++)
785 {
786 // Update the i-th mesh velocity component
787 mesh_velocity[i] += this->nodal_position(l, i) * dpsifdx(l, DIM);
788 }
789 } // for (unsigned l=0;l<n_node;l++)
790 } // if (!(this->ALE_is_disabled))
791
792 // Allocate space for the body force
793 Vector<double> body_force(DIM, 0.0);
794
795 // Get the user-defined body force term
796 this->get_body_force_nst(
797 interpolated_t, ipt, s, interpolated_x, body_force);
798
799 // Get the user-defined source function
800 double source = this->get_source_nst(interpolated_t, ipt, interpolated_x);
801
802 //---------------------------------
803 // Assemble residuals and Jacobian:
804 //---------------------------------
805 //--------------------
806 // Momentum equations:
807 //--------------------
808 // Number of master nodes
809 unsigned n_master = 1;
810
811 // Storage for the weight of the shape function
812 double hang_weight = 1.0;
813
814 // Loop over the test functions
815 for (unsigned l = 0; l < n_node; l++)
816 {
817 // Local boolean to indicate whether the node is hanging
819
820 // If the node is hanging
821 if (is_node_hanging)
822 {
823 // Get the HangInfo object associated with the l-th node
825
826 // Read out number of master nodes from hanging data
827 n_master = hang_info_pt->nmaster();
828 }
829 // Otherwise the node is its own master
830 else
831 {
832 // There is only one node to consider; the node itself
833 n_master = 1;
834 }
835
836 // Loop over the master nodes
837 for (unsigned m = 0; m < n_master; m++)
838 {
839 // Loop over velocity components
840 for (unsigned i = 0; i < DIM; i++)
841 {
842 // Check if the node is hanging
843 if (is_node_hanging)
844 {
845 // Get the equation number from the master node
846 local_eqn = this->local_hang_eqn(hang_info_pt->master_node_pt(m),
848
849 // Get the hang weight from the master node
850 hang_weight = hang_info_pt->master_weight(m);
851 }
852 // If the node is not hanging
853 else
854 {
855 // Local equation number
857
858 // Node contributes with full weight
859 hang_weight = 1.0;
860 }
861
862 // If it's not a boundary condition
863 if (local_eqn >= 0)
864 {
865 // Add the user-defined body force terms
867 body_force[i] * testf[l] * W * hang_weight;
868
869 // Add the gravitational body force term
872
873 // Add the pressure gradient term
875 interpolated_p * dtestfdx(l, i) * W * hang_weight;
876
877 // Add in the contribution from the time derivative
879 testf[l] * W * hang_weight);
880
881 // If ALE is enabled
882 if (!(this->ALE_is_disabled))
883 {
884 // Loop over the coordinate directions
885 for (unsigned k = 0; k < DIM; k++)
886 {
887 // Add in the mesh velocity contribution
889 (scaled_re_st * mesh_velocity[k] * interpolated_dudx(i, k) *
890 testf[l] * W * hang_weight);
891 }
892 } // if (!(this->ALE_is_disabled))
893
894 // Loop over the coordinate directions
895 for (unsigned k = 0; k < DIM; k++)
896 {
897 // Add in the convective term contribution
899 (scaled_re * interpolated_u[k] * interpolated_dudx(i, k) *
900 testf[l] * W * hang_weight);
901 }
902
903 // Loop over the coordinate directions
904 for (unsigned k = 0; k < DIM; k++)
905 {
906 // Add in the stress tensor terms:
907 // NOTE: The viscosity ratio needs to go in here to ensure
908 // continuity of normal stress is satisfied even in flows
909 // with zero pressure gradient!
911 ((interpolated_dudx(i, k) +
912 this->Gamma[i] * interpolated_dudx(k, i)) *
913 visc_ratio * dtestfdx(l, k) * W * hang_weight);
914 }
915
916 //------------------------
917 // Calculate the Jacobian:
918 //------------------------
919 // If we also need to construct the Jacobian
920 if (flag)
921 {
922 // Number of master nodes
923 unsigned n_master2 = 1;
924
925 // Storage for the weight of the shape function
926 double hang_weight2 = 1.0;
927
928 // Loop over the velocity nodes for columns
929 for (unsigned l2 = 0; l2 < n_node; l2++)
930 {
931 // Local boolean to indicate whether the node is hanging
933
934 // If the node is hanging
936 {
937 // Get the HangInfo object associated with the l-th node
939
940 // Read out number of master nodes from hanging data
941 n_master2 = hang_info2_pt->nmaster();
942 }
943 // Otherwise the node is its own master
944 else
945 {
946 // There is only one node to consider; the node itself
947 n_master2 = 1;
948 }
949
950 // Loop over the master nodes
951 for (unsigned m2 = 0; m2 < n_master2; m2++)
952 {
953 // Loop over the velocity components
954 for (unsigned i2 = 0; i2 < DIM; i2++)
955 {
956 // Check if the node is hanging
958 {
959 // Get the equation number from the master node
960 local_unknown = this->local_hang_eqn(
961 hang_info2_pt->master_node_pt(m2), u_nodal_index[i2]);
962
963 // Get the hang weight from the master node
964 hang_weight2 = hang_info2_pt->master_weight(m2);
965 }
966 // If the node is not hanging
967 else
968 {
969 // Get the local equation number
972
973 // Node contributes with full weight
974 hang_weight2 = 1.0;
975 }
976
977 // If the unknown is non-zero
978 if (local_unknown >= 0)
979 {
980 // Add contribution to elemental matrix
981 jacobian(local_eqn, local_unknown) -=
982 (visc_ratio * this->Gamma[i] * dpsifdx(l2, i) *
984
985 // Now add in the inertial terms
986 jacobian(local_eqn, local_unknown) -=
987 (scaled_re * psif[l2] * interpolated_dudx(i, i2) *
989
990 // Extra component if i2=i
991 if (i2 == i)
992 {
993 // If we also need to construct the mass matrix (only
994 // diagonal entries)
995 if (flag == 2)
996 {
997 // NOTE: This is positive because the mass matrix is
998 // taken to the other side of the equation when
999 // formulating the generalised eigenproblem.
1001 (scaled_re_st * psif[l2] * testf[l] * W *
1003 }
1004
1005 // Add in the time-derivative contribution
1006 jacobian(local_eqn, local_unknown) -=
1007 (scaled_re_st * dpsifdx(l2, DIM) * testf[l] * W *
1009
1010 // Loop over the velocity components
1011 for (unsigned k = 0; k < DIM; k++)
1012 {
1013 // Add in the convective term contribution
1014 jacobian(local_eqn, local_unknown) -=
1015 (scaled_re * interpolated_u[k] * dpsifdx(l2, k) *
1016 testf[l] * W * hang_weight * hang_weight2);
1017 }
1018
1019 // If ALE is enabled
1020 if (!(this->ALE_is_disabled))
1021 {
1022 // Loop over the velocity components
1023 for (unsigned k = 0; k < DIM; k++)
1024 {
1025 // Add in the mesh velocity contribution
1026 jacobian(local_eqn, local_unknown) +=
1028 dpsifdx(l2, k) * testf[l] * W * hang_weight *
1029 hang_weight2);
1030 }
1031 } // if (!(this->ALE_is_disabled))
1032
1033 // Loop over velocity components
1034 for (unsigned k = 0; k < DIM; k++)
1035 {
1036 // Add in the velocity gradient terms
1037 jacobian(local_eqn, local_unknown) -=
1038 (visc_ratio * dpsifdx(l2, k) * dtestfdx(l, k) *
1040 }
1041 } // if (i2==i)
1042 } // if (local_unknown>=0)
1043 } // for (unsigned i2=0;i2<DIM;i2++)
1044 } // for (unsigned m2=0;m2<n_master2;m2++)
1045 } // for (unsigned l2=0;l2<n_node;l2++)
1046
1047 // Loop over the pressure shape functions
1048 for (unsigned l2 = 0; l2 < n_pres; l2++)
1049 {
1050 // If the pressure dof is hanging
1052 {
1053 // Get the HangInfo object associated with the l2-th
1054 // pressure node
1056 this->pressure_node_pt(l2)->hanging_pt(p_index);
1057
1058 // Get the number of master nodes from the pressure node
1059 n_master2 = hang_info2_pt->nmaster();
1060 }
1061 // Otherwise the node is its own master
1062 else
1063 {
1064 // There is only one node to consider; the node itself
1065 n_master2 = 1;
1066 }
1067
1068 // Loop over the master nodes
1069 for (unsigned m2 = 0; m2 < n_master2; m2++)
1070 {
1071 // If the pressure dof is hanging
1073 {
1074 // Get the unknown from the master node
1075 local_unknown = this->local_hang_eqn(
1076 hang_info2_pt->master_node_pt(m2), p_index);
1077
1078 // Get the weight from the HangInfo object
1079 hang_weight2 = hang_info2_pt->master_weight(m2);
1080 }
1081 // If the node is not hanging
1082 else
1083 {
1084 // Get the local equation number
1085 local_unknown = this->p_local_eqn(l2);
1086
1087 // Node contributes with full weight
1088 hang_weight2 = 1.0;
1089 }
1090
1091 // If the unknown is not pinned
1092 if (local_unknown >= 0)
1093 {
1094 jacobian(local_eqn, local_unknown) +=
1095 (psip[l2] * dtestfdx(l, i) * W * hang_weight *
1096 hang_weight2);
1097 }
1098 } // for (unsigned m2=0;m2<n_master2;m2++)
1099 } // for (unsigned l2=0;l2<n_pres;l2++)
1100
1101 //------------------------------------
1102 // Calculate external data information
1103 //------------------------------------
1104 // If we're storing the Strouhal number as external data then we
1105 // need to calculate dr/d(St) in the elemental Jacobian
1106 if (this->ReynoldsStrouhal_is_stored_as_external_data)
1107 {
1108 // The index of the external data (there's only one!)
1109 unsigned data_index = 0;
1110
1111 // The index of the unknown value stored in the external data
1112 unsigned value_index = 0;
1113
1114 // Get the local equation number associated with the extra
1115 // unknown
1117 this->external_local_eqn(data_index, value_index);
1118
1119 // If we're at a non-zero degree of freedom add in the entry
1120 if (local_unknown >= 0)
1121 {
1122 // Add in the contribution from the time derivative
1123 jacobian(local_eqn, local_unknown) -=
1125 W * hang_weight);
1126
1127 // If ALE is enabled
1128 if (!this->ALE_is_disabled)
1129 {
1130 // Loop over the coordinate directions
1131 for (unsigned k = 0; k < DIM; k++)
1132 {
1133 // Add in the mesh velocity contribution
1134 jacobian(local_eqn, local_unknown) +=
1136 interpolated_dudx(i, k) * testf[l] * W *
1137 hang_weight);
1138 }
1139 } // if (!ALE_is_disabled)
1140 } // if (local_unknown>=0)
1141 } // if (Strouhal_is_stored_as_external_data)
1142 } // if (flag)
1143 } // if (local_eqn>=0)
1144 } // for (unsigned i=0;i<DIM;i++)
1145 } // for (unsigned m=0;m<n_master;m++)
1146 } // for (unsigned l=0;l<n_node;l++)
1147
1148 //---------------------
1149 // Continuity equation:
1150 //---------------------
1151 // Loop over the pressure shape functions
1152 for (unsigned l = 0; l < n_pres; l++)
1153 {
1154 // If the pressure dof is hanging
1156 {
1157 // Get the HangInfo object associated with the l2-th pressure node
1158 hang_info_pt = this->pressure_node_pt(l)->hanging_pt(p_index);
1159
1160 // Get the number of master nodes from the pressure node
1161 n_master = hang_info_pt->nmaster();
1162 }
1163 // Otherwise the node is its own master
1164 else
1165 {
1166 // There is only one node to consider; the node itself
1167 n_master = 1;
1168 }
1169
1170 // Loop over the master nodes
1171 for (unsigned m = 0; m < n_master; m++)
1172 {
1173 // If the pressure dof is hanging
1175 {
1176 // Get the unknown from the master node
1177 local_eqn =
1178 this->local_hang_eqn(hang_info_pt->master_node_pt(m), p_index);
1179
1180 // Get the weight from the HangInfo object
1181 hang_weight = hang_info_pt->master_weight(m);
1182 }
1183 // If the node is not hanging
1184 else
1185 {
1186 // Get the local equation number
1187 local_eqn = this->p_local_eqn(l);
1188
1189 // Node contributes with full weight
1190 hang_weight = 1.0;
1191 }
1192
1193 // If the equation is not pinned
1194 if (local_eqn >= 0)
1195 {
1196 // Add in the source term contribution
1197 residuals[local_eqn] -= source * testp[l] * W * hang_weight;
1198
1199 // Loop over velocity components
1200 for (unsigned k = 0; k < DIM; k++)
1201 {
1202 // Add in the velocity gradient terms
1204 interpolated_dudx(k, k) * testp[l] * W * hang_weight;
1205 }
1206
1207 //------------------------
1208 // Calculate the Jacobian:
1209 //------------------------
1210 // If we also need to construct the Jacobian
1211 if (flag)
1212 {
1213 // Number of master nodes
1214 unsigned n_master2 = 1;
1215
1216 // Storage for the weight of the shape function
1217 double hang_weight2 = 1.0;
1218
1219 // Loop over the velocity nodes for columns
1220 for (unsigned l2 = 0; l2 < n_node; l2++)
1221 {
1222 // Local boolean to indicate whether the node is hanging
1224
1225 // If the node is hanging
1226 if (is_node2_hanging)
1227 {
1228 // Get the HangInfo object associated with the l2-th node
1230
1231 // Read out number of master nodes from hanging data
1232 n_master2 = hang_info2_pt->nmaster();
1233 }
1234 // Otherwise the node is its own master
1235 else
1236 {
1237 // There is only one node to consider; the node itself
1238 n_master2 = 1;
1239 }
1240
1241 // Loop over the master nodes
1242 for (unsigned m2 = 0; m2 < n_master2; m2++)
1243 {
1244 // Loop over the velocity components
1245 for (unsigned i2 = 0; i2 < DIM; i2++)
1246 {
1247 // If the node is hanging
1248 if (is_node2_hanging)
1249 {
1250 // Get the equation number from the master node
1251 local_unknown = this->local_hang_eqn(
1252 hang_info2_pt->master_node_pt(m2), u_nodal_index[i2]);
1253
1254 // Get the weight from the HangInfo object
1255 hang_weight2 = hang_info2_pt->master_weight(m2);
1256 }
1257 // If the node is not hanging
1258 else
1259 {
1260 // Get the local equation number
1263
1264 // Node contributes with full weight
1265 hang_weight2 = 1.0;
1266 }
1267
1268 // If the unknown is not pinned
1269 if (local_unknown >= 0)
1270 {
1271 // Add in the velocity gradient contribution
1272 jacobian(local_eqn, local_unknown) +=
1273 (dpsifdx(l2, i2) * testp[l] * W * hang_weight *
1274 hang_weight2);
1275 }
1276 } // for (unsigned i2=0;i2<DIM;i2++)s
1277 } // for (unsigned m2=0;m2<n_master2;m2++)
1278 } // for (unsigned l2=0;l2<n_node;l2++)
1279 } // if (flag)
1280 } // if (local_eqn>=0)
1281 } // for (unsigned m=0;m<n_master;m++)
1282 } // for (unsigned l=0;l<n_pres;l++)
1283 } // for (unsigned ipt=0;ipt<n_intpt;ipt++)
1284 } // End of fill_in_generic_residual_contribution_nst
1285
1286
1287 //======================================================================
1288 /// Compute derivatives of elemental residual vector with respect
1289 /// to nodal coordinates.
1290 /// dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}
1291 /// Overloads the FD-based version in the FE base class.
1292 //======================================================================
1293 template<unsigned DIM>
1295 DIM>::get_dresidual_dnodal_coordinates(RankThreeTensor<double>&
1297 {
1298 // Throw an error
1299 throw OomphLibError("Space-time version has not been implemented yet!",
1302 } // End of
1303
1304
1305 //====================================================================
1306 /// Force build of templates
1307 //====================================================================
1310} // namespace oomph
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
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
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 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 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 nnode() const
Return the number of nodes.
Definition elements.h:2214
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
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
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
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
int external_local_eqn(const unsigned &i, const unsigned &j)
Return the local equation number corresponding to the j-th value stored at the i-th external data.
Definition elements.h:311
Class that contains data for hanging nodes.
Definition nodes.h:742
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.
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition nodes.h:1285
HangInfo *const & hanging_pt() const
Return pointer to hanging node data (this refers to the geometric hanging node status) (const version...
Definition nodes.h:1228
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
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 fill_in_generic_residual_contribution_nst(Vector< double > &residuals, DenseMatrix< double > &jacobian, DenseMatrix< double > &mass_matrix, const unsigned &compute_jacobian_flag)
Add the elements contribution to elemental residual vector and/or Jacobian matrix....
void fill_in_generic_pressure_advection_diffusion_contribution_nst(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &compute_jacobian_flag)
Compute the residuals for the associated pressure advection diffusion problem. Used by the Fp precond...
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...
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).