womersley_elements.h
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 
31 
32 //Header file for Womersley elements
33 #ifndef OOMPH_WOMERSLEY_ELEMENTS_HEADER
34 #define OOMPH_WOMERSLEY_ELEMENTS_HEADER
35 
36 // Config header generated by autoconfig
37 #ifdef HAVE_CONFIG_H
38  #include <oomph-lib-config.h>
39 #endif
40 
41 
42 //OOMPH-LIB headers
43 #include "../generic/nodes.h"
44 #include "../generic/Qelements.h"
45 #include "../generic/mesh.h"
46 #include "../generic/problem.h"
47 #include "../generic/oomph_utilities.h"
48 #include "../navier_stokes/navier_stokes_flux_control_elements.h"
49 
50 
51 namespace oomph
52 {
53 
54 
55 /////////////////////////////////////////////////////////////////////////
56 /////////////////////////////////////////////////////////////////////////
57 /////////////////////////////////////////////////////////////////////////
58 
59 
60 //===============================================================
61 /// Template-free base class for Impedance Tube -- to faciliate
62 /// interactions between the Womersley elements and the
63 /// Navier Stokes impedance traction elements.
64 //===============================================================
66 {
67 
68  public:
69 
70  /// Empty constructor
72 
73  /// Empty virtual destructor
75 
76  /// \short Empty virtual dummy member function -- every base class
77  /// needs at least one virtual member function if it's
78  /// to be used as a base class for a polymorphic object.
79 // virtual void dummy(){};
80 
81  /// \short Pure virtual function to compute inlet pressure, p_in,
82  /// required to achieve the currently imposed, instantaneous
83  /// volume flux q prescribed by total_volume_flux_into_impedance_tube(),
84  /// and its derivative, dp_in/dq.
85  virtual void get_response(double& p_in,
86  double& dp_in_dq)=0;
87 
88  /// Zero!
89  static double Zero;
90 
91 };
92 
93 
94 
95 /////////////////////////////////////////////////////////////////////////
96 /////////////////////////////////////////////////////////////////////////
97 /////////////////////////////////////////////////////////////////////////
98 
99 
100 
101 
102 //======================================================================
103 /// A base class for elements that allow the imposition of an impedance
104 /// type boundary condition to the Navier--Stokes equations. Establishes
105 /// the template-free common functionality, that they must have to be able
106 /// to compute the volume flux that passes through them, etc.
107 //======================================================================
109 {
110 
111 public:
112 
113  //Empty constructor
115 
116  ///Empty vitual destructor
118 
119  /// \short Pure virtual function that must be implemented to compute
120  /// the volume flux that passes through this element
121  virtual double get_volume_flux()=0;
122 
123  /// \short Add the element's contribution to the auxiliary integral
124  /// used in the element's Jacobian. The aux_integral contains
125  /// the derivative of the total volume flux through the
126  /// outflow boundary of the (higher-dimensional) Navier-Stokes mesh w.r.t.
127  /// to the discrete (global) (velocity) degrees of freedom.
128  virtual void add_element_contribution_to_aux_integral(
129  std::map<unsigned,double>* aux_integral_pt)=0;
130 
131  /// \short Pass the pointer to the pre-computed auxiliary integral
132  /// to the element so it can be accessed when computing the
133  /// elemental Jacobian.
134  virtual void set_aux_integral_pt(std::map<unsigned,double>*
135  aux_integral_pt)=0;
136 
137  /// \short Pass the pointer to the "impedance tube" that computes
138  /// the flow resistance via the solution of Womersley's equations
139  /// to the element.
140  virtual void set_impedance_tube_pt(TemplateFreeWomersleyImpedanceTubeBase*
141  impedance_tube_pt)=0;
142 
143 
144  /// \short Pass the pointer to the mesh containing all
145  /// NavierStokesImpedanceTractionElements that contribute
146  /// to the volume flux into the downstream "impedance tube"
147  /// to the element and classify all nodes in that mesh
148  /// as external Data for this element (unless the nodes
149  /// are also the element's own nodes, of course).
150  virtual void set_external_data_from_navier_stokes_outflow_mesh(
151  Mesh* navier_stokes_outflow_mesh_pt_mesh_pt)=0;
152 
153  protected:
154 
155  ///\short Pointer to mesh containing the NavierStokesImpedanceTractionElements
156  /// that contribute to the volume flux into the "downstream tube" that
157  /// provides the flow resistance
159 
160 
161 };
162 
163 
164 
165 
166 /////////////////////////////////////////////////////////////////////////
167 /////////////////////////////////////////////////////////////////////////
168 /////////////////////////////////////////////////////////////////////////
169 
170 
171 //=============================================================
172 /// A class for all isoparametric elements that solve the
173 /// Womersley (parallel flow) equations.
174 /// \f[
175 /// Re St \frac{\partial u}{\partial t} = - g +
176 /// \frac{\partial^2 u}{\partial x_i^2}
177 /// \f]
178 /// which may be derived from the full Navier-Stokes equations
179 /// (with a viscous scaling of the pressure) under the assumption of
180 /// parallel flow in the z direction. u then represents the axial
181 /// velocity and g is the (spatially constant) axial component of
182 /// the pressure gradient.
183 ///
184 /// This class contains the generic maths. Shape functions, geometric
185 /// mapping etc. must get implemented in derived class.
186 /// Note that this class assumes an isoparametric formulation, i.e. that
187 /// the scalar unknown is interpolated using the same shape functions
188 /// as the position.
189 ///
190 /// Generally, the instantaneous value of the pressure gradient, g,
191 /// is prescribed (and specified via a pointer to a single-valued
192 /// Data object whose current (pinned) value contains the pressure.
193 ///
194 /// It is also possible to prescribe the flow rate through
195 /// a mesh of Womersley elements and to determine the pressure
196 /// gradient required to achieve this flow rate as an unknown.
197 /// In that case the external pressure is treated as
198 /// an external Data object that an associated
199 /// ImposeFluxForWomersleyElement is in charge of. Note that only
200 /// the ImposeFluxForWomersleyElement can set the pressure gradient
201 /// Data object as external Data. This is because (counter to
202 /// general practice) the WomersleyEquations make contributions
203 /// to the residuals of the ImposeFluxForWomersleyElements in order
204 /// to keep the elemental Jacobians as small as possible.
205 //=============================================================
206 template <unsigned DIM>
207 class WomersleyEquations : public virtual FiniteElement
208 {
209 
210 public:
211 
212  /// \short Constructor: Initialises the Pressure_gradient_data_pt to null
213  WomersleyEquations() : Pressure_gradient_data_pt(0)
214  {
215  ReSt_pt = &Default_ReSt_value;
216  }
217 
218 
219  /// Broken copy constructor
221  {
222  BrokenCopy::broken_copy("WomersleyEquations");
223  }
224 
225 
226  /// Broken assignment operator
228  {
229  BrokenCopy::broken_assign("WomersleyEquations");
230  }
231 
232 
233  /// Set pointer to pressure gradient (single-valued Data)
234  void set_pressure_gradient_pt(Data*& pressure_gradient_data_pt)
235  {
236 #ifdef PARANOID
237  if (pressure_gradient_data_pt->nvalue()!=1)
238  {
239  throw OomphLibError(
240  "Pressure gradient Data must only contain a single value!\n",
241  OOMPH_CURRENT_FUNCTION,
242  OOMPH_EXCEPTION_LOCATION);
243  }
244 #endif
245  Pressure_gradient_data_pt=pressure_gradient_data_pt;
246  }
247 
248 
249 
250  /// Read-only access to pointer to pressure gradient
252  {
253  return Pressure_gradient_data_pt;
254  }
255 
256 
257  /// Product of Reynolds and Strouhal number (=Womersley number)
258  const double &re_st() const {return *ReSt_pt;}
259 
260 
261  /// Pointer to product of Reynolds and Strouhal number (=Womersley number)
262  double* &re_st_pt() {return ReSt_pt;}
263 
264 
265  /// \short Return the index at which the unknown value
266  /// is stored. The default value, 0, is appropriate for single-physics
267  /// problems, when there is only one variable, the value that satisfies the
268  /// Womersley equation.
269  /// In derived multi-physics elements, this function should be overloaded
270  /// to reflect the chosen storage scheme. Note that these equations require
271  /// that the unknown is always stored at the same index at each node.
272  virtual inline unsigned u_index_womersley() const {return 0;}
273 
274 
275  /// \short du/dt at local node n.
276  /// Uses suitably interpolated value for hanging nodes.
277  double du_dt_womersley(const unsigned &n) const
278  {
279  // Get the data's timestepper
280  TimeStepper* time_stepper_pt= this->node_pt(n)->time_stepper_pt();
281 
282  //Initialise dudt
283  double dudt=0.0;
284 
285  //Loop over the timesteps, if there is a non Steady timestepper
286  if (!time_stepper_pt->is_steady())
287  {
288  //Find the index at which the variable is stored
289  const unsigned u_nodal_index = u_index_womersley();
290 
291  // Number of timsteps (past & present)
292  const unsigned n_time = time_stepper_pt->ntstorage();
293 
294  //Add the contributions to the time derivative
295  for(unsigned t=0;t<n_time;t++)
296  {
297  dudt += time_stepper_pt->weight(1,t)*nodal_value(t,n,u_nodal_index);
298  }
299  }
300  return dudt;
301  }
302 
303 
304  /// Output with default number of plot points
305  void output(std::ostream &outfile)
306  {
307  unsigned nplot=5;
308  output(outfile,nplot);
309  }
310 
311  /// \short Output function: x,y,z_out,0,0,u,0 to allow comparison
312  /// against full Navier Stokes at n_nplot x n_plot points (2D)
313  void output_3d(std::ostream &outfile,
314  const unsigned &n_plot,
315  const double& z_out);
316 
317  /// \short Output FE representation of soln: x,y,u or x,y,z,u at
318  /// n_plot^DIM plot points
319  void output(std::ostream &outfile, const unsigned &nplot);
320 
321 
322  /// C_style output with default number of plot points
323  void output(FILE* file_pt)
324  {
325  unsigned n_plot=5;
326  output(file_pt,n_plot);
327  }
328 
329 
330  /// \short C-style output FE representation of soln: x,y,u or x,y,z,u at
331  /// n_plot^DIM plot points
332  void output(FILE* file_pt, const unsigned &n_plot);
333 
334 
335  /// Output exact soln: x,y,u_exact or x,y,z,u_exact at nplot^DIM plot points
336  void output_fct(std::ostream &outfile, const unsigned &nplot,
338  exact_soln_pt);
339 
340 
341  /// \short Output exact soln: x,y,u_exact or x,y,z,u_exact at
342  /// nplot^DIM plot points (time-dependent version)
343  virtual
344  void output_fct(std::ostream &outfile, const unsigned &nplot,
345  const double& time,
347  exact_soln_pt);
348 
349 
350  /// Get error against and norm of exact solution
351  void compute_error(std::ostream &outfile,
353  exact_soln_pt,
354  double& error, double& norm);
355 
356 
357  /// Get error against and norm of exact solution
358  void compute_error(std::ostream &outfile,
360  exact_soln_pt,
361  const double& time, double& error, double& norm);
362 
363  /// Get flux: flux[i] = du/dx_i
364  void get_flux(const Vector<double>& s, Vector<double>& flux) const
365  {
366  //Find out how many nodes there are in the element
367  unsigned n_node = nnode();
368 
369  //Find the index at which the variable is stored
370  unsigned u_nodal_index = u_index_womersley();
371 
372  //Set up memory for the shape and test functions
373  Shape psi(n_node);
374  DShape dpsidx(n_node,DIM);
375 
376  //Call the derivatives of the shape and test functions
377  dshape_eulerian(s,psi,dpsidx);
378 
379  //Initialise to zero
380  for(unsigned j=0;j<DIM;j++) {flux[j] = 0.0;}
381 
382  // Loop over nodes
383  for(unsigned l=0;l<n_node;l++)
384  {
385  //Loop over derivative directions
386  for(unsigned j=0;j<DIM;j++)
387  {
388  flux[j] += nodal_value(l,u_nodal_index)*dpsidx(l,j);
389  }
390  }
391  }
392 
393 
394  /// Compute element residual Vector (wrapper)
396  {
397  //Call the generic residuals function with flag set to 0
398  //using a dummy matrix argument
399  fill_in_generic_residual_contribution_womersley(
401  }
402 
403 
404  /// Compute element residual Vector and element Jacobian matrix (wrapper)
406  DenseMatrix<double> &jacobian)
407  {
408  //Call the generic routine with the flag set to 1
409  fill_in_generic_residual_contribution_womersley(residuals,jacobian,1);
410  }
411 
412 
413  /// Return FE representation of function value u(s) at local coordinate s
414  inline double interpolated_u_womersley(const Vector<double> &s) const
415  {
416  //Find number of nodes
417  unsigned n_node = nnode();
418 
419  //Find the index at which the variable is stored
420  unsigned u_nodal_index = u_index_womersley();
421 
422  //Local shape function
423  Shape psi(n_node);
424 
425  //Find values of shape function
426  shape(s,psi);
427 
428  //Initialise value of u
429  double interpolated_u = 0.0;
430 
431  //Loop over the local nodes and sum
432  for(unsigned l=0;l<n_node;l++)
433  {
434  interpolated_u += nodal_value(l,u_nodal_index)*psi[l];
435  }
436 
437  return(interpolated_u);
438  }
439 
440  /// \short Self-test: Return 0 for OK
441  unsigned self_test();
442 
443  /// Compute total volume flux through element
444  double get_volume_flux();
445 
446 protected:
447 
448  /// \short Shape/test functions and derivs w.r.t. to global coords at
449  /// local coord. s; return Jacobian of mapping
450  virtual double dshape_and_dtest_eulerian_womersley(const Vector<double> &s,
451  Shape &psi,
452  DShape &dpsidx,
453  Shape &test,
454  DShape &dtestdx) const=0;
455 
456 
457  /// \short Shape/test functions and derivs w.r.t. to global coords at
458  /// integration point ipt; return Jacobian of mapping
459  virtual double dshape_and_dtest_eulerian_at_knot_womersley(
460  const unsigned &ipt,
461  Shape &psi,
462  DShape &dpsidx,
463  Shape &test,
464  DShape &dtestdx)
465  const=0;
466 
467  /// \short Compute element residual Vector only (if flag=and/or element
468  /// Jacobian matrix
469  virtual void fill_in_generic_residual_contribution_womersley(
470  Vector<double> &residuals, DenseMatrix<double> &jacobian,
471  unsigned flag);
472 
473  /// Pointer to pressure gradient Data (single value Data item)
475 
476  /// Pointer to global Reynolds number x Strouhal number (=Womersley)
477  double *ReSt_pt;
478 
479  /// Static default value for the Womersley number
480  static double Default_ReSt_value;
481 
482 private:
483 
484  template<unsigned DIMM>
486 
487  /// \short Set pointer to pressure gradient (single-valued Data) and
488  /// treat it as external data -- this can only be called
489  /// by the friend class ImposeFluxForWomersleyElement which
490  /// imposes a volume flux constraint and trades it
491  /// for the now unknown pressure gradient Data that is
492  /// treated as external Data for this element.
493  /// This slightly convoluted (private/friend) construction
494  /// is necessary because, counter to practice, the current
495  /// element adds contributions to the equation that determines
496  /// the external data. This obviously requires that the
497  /// ImposeFluxForWomersleyElement doesn't do the same. We know
498  /// that it doesn't and therefore we make it a friend that's allowed
499  /// to collaborate with this element...
501  Data* pressure_gradient_data_pt)
502  {
503  Pressure_gradient_data_pt=pressure_gradient_data_pt;
504  add_external_data(pressure_gradient_data_pt);
505  }
506 
507 };
508 
509 
510 ///////////////////////////////////////////////////////////////////////////
511 ///////////////////////////////////////////////////////////////////////////
512 ///////////////////////////////////////////////////////////////////////////
513 
514 
515 
516 //========================================================================
517 /// \short Element to impose volume flux through collection of Womersley
518 /// elements, in exchange for treating the pressure gradient
519 /// as an unknown. The pressure gradient is created (as a single-valued
520 /// Data item) in the constructor for this element which also
521 /// takes a pointer to the Mesh containing the Womersley elements whose
522 /// total flux is being controlled. While doing this we tell them
523 /// that their pressure gradient is now an unknown and must be
524 /// treated as external Data.
525 //========================================================================
526 template<unsigned DIM>
528 {
529 
530 public:
531 
532  /// \short Constructor: Pass pointer to mesh that contains the
533  /// Womersley elements whose volume flux is controlled, and pointer to
534  /// double that contains the instantaneous value of the
535  /// prescribed flux
536  ImposeFluxForWomersleyElement(Mesh* womersley_mesh_pt,
537  double* prescribed_flux_pt) :
538  Prescribed_flux_pt(prescribed_flux_pt)
539  {
540  // Store the mesh of the flux-controlled Womerersley elements
541  Womersley_mesh_pt=womersley_mesh_pt;
542 
543  // Create the pressure gradient Data
544  Pressure_gradient_data_pt=new Data(1);
545  Pressure_gradient_data_pt->set_value(0,0.0);
546 
547  // Pressure gradient is internal data of this element
548  add_internal_data(Pressure_gradient_data_pt);
549 
550  // Find number of elements in the mesh of Womersley elements
551  // whose total flux is controlled
552  unsigned n_element = womersley_mesh_pt->nelement();
553 
554  // Loop over the elements to tell them that the pressure
555  // gradient is given by the newly created Data object
556  // which is to be treated as external Data.
557  for(unsigned e=0;e<n_element;e++)
558  {
559  // Upcast from FiniteElement to the present element
560  WomersleyEquations<DIM>* el_pt =
561  dynamic_cast<WomersleyEquations<DIM>*>(womersley_mesh_pt->element_pt(e));
562 
563  //Set the pressure gradient function pointer
565  Pressure_gradient_data_pt);
566  }
567 
568  }
569 
570  /// \short Read-only access to the single-valued Data item that
571  /// stores the pressure gradient (to be determined via the
572  /// flux control)
574  {
575  return Pressure_gradient_data_pt;
576  }
577 
578 
579  /// Get volume flux through all Womersley elements
581  {
582  // Initialise
583  double flux=0.0;
584 
585  // Assemble contributions from elements
586  unsigned nelem=Womersley_mesh_pt->nelement();
587  for (unsigned e=0;e<nelem;e++)
588  {
590  dynamic_cast<WomersleyEquations<DIM>*>(Womersley_mesh_pt->element_pt(e));
591  if (el_pt!=0) flux+=el_pt->get_volume_flux();
592  }
593 
594  // Return total volume flux
595  return flux;
596  }
597 
598 
599  /// \short Compute residual vector: the volume flux constraint
600  /// determines this element's one-and-only internal Data which represents
601  /// the pressure gradient
602  void get_residuals(Vector<double> &residuals)
603  {
604  // Local equation number of volume flux constraint -- associated
605  // with the internal data (the unknown pressure gradient)
606  int local_eqn=internal_local_eqn(0,0);
607  if (local_eqn>=0)
608  {
609  residuals[local_eqn]+=total_volume_flux()-(*Prescribed_flux_pt);
610  }
611  }
612 
613 
614  /// \short Compute element residual Vector and element Jacobian matrix
615  /// Note: Jacobian is zero because the derivatives w.r.t. to
616  /// velocity dofs are added by the Womersley elements; the current
617  /// element's internal Data (the pressure gradient) does not feature
618  /// in the volume constraint.
619  void get_jacobian(Vector<double> &residuals,
620  DenseMatrix<double> &jacobian)
621  {
622  // Initialise Jacobian
623  unsigned n_dof=ndof();
624  for (unsigned i=0;i<n_dof;i++)
625  {
626  for (unsigned j=0;j<n_dof;j++)
627  {
628  jacobian(i,j)=0.0;
629  }
630  }
631  // Get residuals
632  get_residuals(residuals);
633  }
634 
635 private:
636 
637  /// Pointer to mesh that contains the Womersley elements
639 
640  /// \short Data item whose one and only value contains the pressure
641  /// gradient
643 
644  /// \short Pointer to current value of prescribed flux
646 
647 };
648 
649 
650 
651 
652 ///////////////////////////////////////////////////////////////////////////
653 ///////////////////////////////////////////////////////////////////////////
654 ///////////////////////////////////////////////////////////////////////////
655 
656 
657 
658 //======================================================================
659 /// QWomersleyElement elements are linear/quadrilateral/brick-shaped
660 /// Womersley elements with isoparametric interpolation for the function.
661 //======================================================================
662 template <unsigned DIM, unsigned NNODE_1D>
663  class QWomersleyElement : public virtual QElement<DIM,NNODE_1D>,
664  public virtual WomersleyEquations<DIM>
665 {
666  private:
667 
668  /// \short Static array of ints to hold number of variables at
669  /// nodes: Initial_Nvalue[n]
670  static const unsigned Initial_Nvalue;
671 
672  public:
673 
674  ///\short Constructor: Call constructors for QElement and
675  /// Womersley equations
676  QWomersleyElement() : QElement<DIM,NNODE_1D>(),
677  WomersleyEquations<DIM>()
678  { }
679 
680  /// Broken copy constructor
682  {
683  BrokenCopy::broken_copy("QWomersleyElement");
684  }
685 
686  /// Broken assignment operator
688  {
689  BrokenCopy::broken_assign("QWomersleyElement");
690  }
691 
692  /// \short Required # of `values' (pinned or dofs)
693  /// at node n
694  inline unsigned required_nvalue(const unsigned &n) const
695  {return Initial_Nvalue;}
696 
697  /// \short Output function:
698  /// x,y,u or x,y,z,u
699  void output(std::ostream &outfile)
701 
702 
703  /// \short Output function:
704  /// x,y,u or x,y,z,u at n_plot^DIM plot points
705  void output(std::ostream &outfile, const unsigned &n_plot)
706  {WomersleyEquations<DIM>::output(outfile,n_plot);}
707 
708 
709 
710  /// \short C-style output function:
711  /// x,y,u or x,y,z,u
712  void output(FILE* file_pt)
714 
715 
716  /// \short C-style output function:
717  /// x,y,u or x,y,z,u at n_plot^DIM plot points
718  void output(FILE* file_pt, const unsigned &n_plot)
719  {WomersleyEquations<DIM>::output(file_pt,n_plot);}
720 
721 
722  /// \short Output function for an exact solution:
723  /// x,y,u_exact or x,y,z,u_exact at n_plot^DIM plot points
724  void output_fct(std::ostream &outfile, const unsigned &n_plot,
726  exact_soln_pt)
727  {WomersleyEquations<DIM>::output_fct(outfile,n_plot,exact_soln_pt);}
728 
729 
730 
731  /// \short Output function for a time-dependent exact solution.
732  /// x,y,u_exact or x,y,z,u_exact at n_plot^DIM plot points
733  /// (Calls the steady version)
734  void output_fct(std::ostream &outfile, const unsigned &n_plot,
735  const double& time,
737  {WomersleyEquations<DIM>::output_fct(outfile,n_plot,time,exact_soln_pt);}
738 
739 
740 
741 protected:
742 
743  /// Shape, test functions & derivs. w.r.t. to global coords. Return Jacobian.
744  inline double dshape_and_dtest_eulerian_womersley(const Vector<double> &s,
745  Shape &psi,
746  DShape &dpsidx,
747  Shape &test,
748  DShape &dtestdx) const;
749 
750 
751  /// \short Shape/test functions and derivs w.r.t. to global coords at
752  /// integration point ipt; return Jacobian of mapping
753  inline double dshape_and_dtest_eulerian_at_knot_womersley(const unsigned &ipt,
754  Shape &psi,
755  DShape &dpsidx,
756  Shape &test,
757  DShape &dtestdx)
758  const;
759 
760 };
761 
762 
763 ////////////////////////////////////////////////////////////////////////
764 ////////////////////////////////////////////////////////////////////////
765 ////////////////////////////////////////////////////////////////////////
766 
767 
768 //=====start_of_problem_class=========================================
769 /// Womersley problem
770 //====================================================================
771 template<class ELEMENT, unsigned DIM>
772 class WomersleyProblem : public Problem
773 {
774 
775 public:
776 
777  /// \short Function pointer to fct that prescribes pressure gradient
778  /// g=fct(t)
779  typedef double (*PrescribedPressureGradientFctPt)(const double& time);
780 
781 
782  /// \short Constructor: Pass pointer to Womersley number, pointer to the
783  /// double that stores the currently imposed flow rate, the pointer
784  /// to the mesh of WomersleyElements (of the type specified by the template
785  /// argument) and the TimeStepper used in these
786  /// elements. (Note that the mesh must be created, and boundary
787  /// conditions applied BEFORE creating the Womersley problem.
788  /// This is to facilitate the re-use of this class
789  /// for different geometries.
790  WomersleyProblem(double* re_st_pt,
791  double* prescribed_volume_flux_pt,
792  TimeStepper* time_stepper_pt,
793  Mesh* womersley_mesh_pt);
794 
795 
796  /// \short Constructor: Pass pointer to Womersley number, pointer to the
797  /// function that returns the imposed pressure gradient, the pointer
798  /// to the mesh of WomersleyElements and the TimeStepper used in these
799  /// elements. (Note that the mesh must be created, and boundary
800  /// conditions applied BEFORE creating the Womersley problem.
801  /// This is to allow the facilitate the re-use of this class
802  /// for different geometries.)
803  WomersleyProblem(double* re_st_pt,
804  PrescribedPressureGradientFctPt pressure_gradient_fct_pt,
805  TimeStepper* time_stepper_pt,
806  Mesh* womersley_mesh_pt);
807 
808  /// Destructor to clean up memory
809  ~WomersleyProblem();
810 
811  /// Update the problem specs after solve (empty)
813 
814  /// \short Update the problem specs before solve (empty)
816 
817 
818  /// \short Update the problem specs before next timestep:
819  /// Update time-varying pressure gradient (if prescribed)
821  {
822  /// Assign current prescribed pressure gradient to Data
823  if (Prescribed_pressure_gradient_fct_pt!=0)
824  {
825  Pressure_gradient_data_pt->set_value(
826  0,Prescribed_pressure_gradient_fct_pt(time_pt()->time()));
827  }
828  }
829 
830  /// \short Doc the solution incl. trace file for various quantities of
831  /// interest (to some...)
832  void doc_solution(DocInfo& doc_info,
833  std::ofstream& trace_file,
834  const double& z_out=0.0);
835 
836  /// Doc the solution
837  void doc_solution(DocInfo& doc_info,
838  const double& z_out=0.0)
839  {
840  std::ofstream trace_file;
841  doc_solution(doc_info,trace_file,z_out);
842  }
843 
844  /// \short Access function to the single-valued Data object that
845  /// contains the unknown pressure gradient (used if flux is prescribed)
847  {
848  return Pressure_gradient_data_pt;
849  }
850 
851 
852 private:
853 
854  /// Pointer to currently prescribed volume flux
856 
857  /// \short Pointer to element that imposes the flux through the collection
858  /// of Womersley elements
860 
861  /// Fct pointer to fct that prescribes pressure gradient
862  PrescribedPressureGradientFctPt Prescribed_pressure_gradient_fct_pt;
863 
864  /// Pointer to single-valued Data item that stores pressure gradient
866 
867 }; // end of problem class
868 
869 
870 
871 
872 
873 //========start_of_constructor============================================
874 /// Constructor: Pass pointer to Womersley number, fct pointer to the
875 /// function that returns the prescribed pressure gradient, the pointer
876 /// to the mesh of WomersleyElements (of the type specified by the
877 /// template argument), and the TimeStepper used in these
878 /// elements. (Note that the mesh must be created, and boundary
879 /// conditions applied BEFORE creating the Womersley problem.
880 /// This is to facilitate the re-use of this class
881 /// for different geometries.)
882 //========================================================================
883 template<class ELEMENT, unsigned DIM>
885  double* re_st_pt,
886  PrescribedPressureGradientFctPt pressure_gradient_fct_pt,
887  TimeStepper* time_stepper_pt,
888  Mesh* womersley_mesh_pt) :
889  Prescribed_volume_flux_pt(0),
890  Flux_el_pt(0),
891  Prescribed_pressure_gradient_fct_pt(pressure_gradient_fct_pt)
892 {
893 
894  // Problem is linear: Skip convergence check in Newton solver
895  Problem_is_nonlinear=false;
896 
897  // Set the timestepper
898  add_time_stepper_pt(time_stepper_pt);
899 
900  // Set the mesh (bcs have already been allocated!)
901  mesh_pt() = womersley_mesh_pt;
902 
903  // Complete the build of all elements so they are fully functional
904  //----------------------------------------------------------------
905 
906  // Find number of elements in mesh
907  unsigned n_element = mesh_pt()->nelement();
908 
909  // Loop over the elements to set up element-specific
910  // things that cannot be handled by constructor
911  for(unsigned i=0;i<n_element;i++)
912  {
913  // Upcast from FiniteElement to the present element
914  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
915 
916  // Set pointer to Womersley number
917  el_pt->re_st_pt()=re_st_pt;
918  }
919 
920  // Create pressure gradient as pinned, single-valued Data item
921  Pressure_gradient_data_pt=new Data(1);
922  Pressure_gradient_data_pt->pin(0);
923 
924  // Pass pointer to pressure gradient Data to elements
925  unsigned nelem=mesh_pt()->nelement();
926  for (unsigned e=0;e<nelem;e++)
927  {
928  ELEMENT* el_pt=dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
929  if (el_pt!=0)
930  {
931  el_pt->set_pressure_gradient_pt(Pressure_gradient_data_pt);
932  }
933  }
934 
935 
936  // Do equation numbering
937  oomph_info <<"Number of equations in WomersleyProblem: "
938  << assign_eqn_numbers() << std::endl;
939 
940 } // end of constructor
941 
942 
943 
944 
945 
946 //========start_of_constructor============================================
947 /// Constructor: Pass pointer to Womersley number, pointer to the
948 /// double that stores the currently imposed flow rate, the pointer
949 /// to the mesh of WomersleyElements and the TimeStepper used in these
950 /// elements. (Note that the mesh must be created, and boundary
951 /// conditions applied BEFORE creating the Womersley problem.
952 /// This is to facilitate the re-use of this class
953 /// for different geometries.)
954 //========================================================================
955 template<class ELEMENT, unsigned DIM>
957  double* re_st_pt,
958  double* prescribed_volume_flux_pt,
959  TimeStepper* time_stepper_pt,
960  Mesh* womersley_mesh_pt) :
961  Prescribed_volume_flux_pt(prescribed_volume_flux_pt),
962  Flux_el_pt(0),
963  Prescribed_pressure_gradient_fct_pt(0)
964 {
965  // Problem is linear: Skip convergence check in Newton solver
966  Problem_is_nonlinear=false;
967 
968  // Set the timestepper
969  add_time_stepper_pt(time_stepper_pt);
970 
971  // Set the mesh (bcs have already been allocated!)
972  mesh_pt() = womersley_mesh_pt;
973 
974 
975  // Complete the build of all elements so they are fully functional
976  //----------------------------------------------------------------
977 
978  // Find number of elements in mesh
979  unsigned n_element = mesh_pt()->nelement();
980 
981  // Loop over the elements to set up element-specific
982  // things that cannot be handled by constructor
983  for(unsigned i=0;i<n_element;i++)
984  {
985  // Upcast from FiniteElement to the present element
986  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
987 
988  // Set pointer to Womersley number
989  el_pt->re_st_pt()=re_st_pt;
990  }
991 
992  // Create element that imposes the flux -- this element creates
993  // the single-valued Data object that represents the (unknown)
994  // pressure gradient internally. It also passes the pointer to
995  // this Data object to the Womersley elements contained in the
996  // mesh. The Womersley elements treat this Data as external Data.
997  Flux_el_pt=new ImposeFluxForWomersleyElement<DIM>(
998  mesh_pt(),Prescribed_volume_flux_pt);
999 
1000  // Add the ImposeFluxForWomersleyElement to the mesh
1001  mesh_pt()->add_element_pt(Flux_el_pt);
1002 
1003  // Store pressure gradient data that was
1004  // created in the ImposeFluxForWomersleyElement
1005  Pressure_gradient_data_pt=Flux_el_pt->pressure_gradient_data_pt();
1006 
1007  // Do equation numbering
1008  oomph_info <<"Number of equations in WomersleyProblem: "
1009  << assign_eqn_numbers() << std::endl;
1010 
1011 } // end of constructor
1012 
1013 
1014 //======start_of_destructor===============================================
1015 /// Destructor for Womersley problem
1016 //========================================================================
1017 template<class ELEMENT, unsigned DIM>
1019 {
1020  // Timestepper gets killed in general problem destructor
1021 
1022  // Mesh gets killed in general problem destructor
1023 
1024 } // end of destructor
1025 
1026 
1027 
1028 //=======start_of_doc_solution============================================
1029 /// Doc the solution
1030 //========================================================================
1031 template<class ELEMENT, unsigned DIM>
1034  std::ofstream& trace_file,
1035  const double& z_out)
1036 {
1037 
1038  std::ofstream some_file;
1039  std::ofstream some_file1;
1040  std::ostringstream filename;
1041 
1042  // Number of plot points
1043  unsigned npts;
1044  npts=5;
1045 
1046 
1047  // Compute total volume flux directly
1048  double flux=0.0;
1049 
1050  // Output solution
1051  //-----------------
1052  filename << doc_info.directory() << "/womersley_soln"
1053  << doc_info.number() << ".dat";
1054  some_file1.open(filename.str().c_str());
1055 
1056  filename.str("");
1057  filename << doc_info.directory() << "/womersley_soln_3d_"
1058  << doc_info.number() << ".dat";
1059  some_file.open(filename.str().c_str());
1060 
1061  // Assemble contributions from elements and output 3D solution
1062  unsigned nelem=mesh_pt()->nelement();
1063  for (unsigned e=0;e<nelem;e++)
1064  {
1065  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
1066  if (el_pt!=0)
1067  {
1068  flux+=el_pt->get_volume_flux();
1069  el_pt->output_3d(some_file,npts,z_out);
1070  el_pt->output(some_file1,npts);
1071  }
1072  }
1073  some_file.close();
1074  some_file1.close();
1075 
1076  double prescribed_g=0.0;
1077  if (Prescribed_pressure_gradient_fct_pt!=0)
1078  {
1079  prescribed_g=Prescribed_pressure_gradient_fct_pt(time_pt()->time());
1080  }
1081 
1082 
1083  double prescribed_q=0.0;
1084  if (Prescribed_volume_flux_pt!=0)
1085  {
1086  prescribed_q=*Prescribed_volume_flux_pt;
1087  }
1088 
1089  if (trace_file.is_open())
1090  {
1091  trace_file
1092  << time_pt()->time() << " "
1093  << pressure_gradient_data_pt()->value(0) << " "
1094  << flux << " "
1095  << prescribed_g << " "
1096  << prescribed_q << " "
1097  << std::endl;
1098  }
1099 
1100 } // end of doc_solution
1101 
1102 
1103 
1104 
1105 ////////////////////////////////////////////////////////////////////////
1106 ////////////////////////////////////////////////////////////////////////
1107 ////////////////////////////////////////////////////////////////////////
1108 
1109 
1110 
1111 //====================================================================
1112 /// Base class for Womersley impedance tube. Allows the computation
1113 /// of the inlet pressure p_in into a uniform tube of specified length
1114 /// that is assumed to convey fully-developed, but time-dependent flow with a
1115 /// presribed instantaneous flow rate, q. Also computes the derivative
1116 /// dp_in/dq required when this is used to determine impedance-type
1117 /// outlet boundary conditions in a Navier-Stokes computation.
1118 //====================================================================
1119 template<class ELEMENT, unsigned DIM>
1122 {
1123 
1124 public:
1125 
1126  /// \short Function pointer to fct that prescribes volume flux
1127  /// q=fct(t) -- mainly used for validation purposes.
1128  typedef double (*PrescribedVolumeFluxFctPt)(const double& time);
1129 
1130 
1131  /// \short Constructor: Specify length of tube and pointer to function that
1132  /// specifies the prescribed volume flux. Outlet pressure is set to zero.
1134  const double& length,
1135  PrescribedVolumeFluxFctPt prescribed_volume_flux_fct_pt) :
1136  Length(length),
1137  P_out(0.0),
1138  Prescribed_volume_flux_fct_pt(prescribed_volume_flux_fct_pt),
1139  Navier_stokes_outflow_mesh_pt(0)
1140  {
1141  // Initialise currently prescribed flux
1142  Current_volume_flux_pt= new double(0.0);
1143 
1144  // Auxiliary integral isn't used if flux isn't prescribed
1145  // via outflow through NavierStokesImpedanceTractionElements
1146  Aux_integral_pt=0;
1147  }
1148 
1149 
1150  /// \short Constructor: Specify length of tube and the pointer to the
1151  /// mesh of either NavierStokesImpedanceTractionElements or
1152  /// NavierStokesFluxControlElements that are attached
1153  /// to the outflow cross-section of a (higher-dimensional)
1154  /// Navier Stokes mesh and provide the inflow into the ImpedanceTube.
1155  /// Outlet pressure is set to zero.
1157  const double& length,
1158  Mesh* navier_stokes_outflow_mesh_pt) :
1159  Length(length),
1160  P_out(0.0),
1161  Prescribed_volume_flux_fct_pt(0),
1162  Navier_stokes_outflow_mesh_pt(navier_stokes_outflow_mesh_pt)
1163  {
1164  // Initialise currently prescribed flux
1165  Current_volume_flux_pt= new double(0.0);
1166 
1167  // Initialise flag to record if NavierStokesFluxControlElement
1168  // or NavierStokesImpedanceTractionElement elements are being used
1169  Using_flux_control_elements=true;
1170 
1171  // Attempt to cast 1st element to NavierStokesImpedanceTractionElementBase
1172  if (dynamic_cast<NavierStokesImpedanceTractionElementBase*>(
1173  navier_stokes_outflow_mesh_pt->element_pt(0)))
1174  {
1175  Using_flux_control_elements=false;
1176 
1177  // Create map used to store the non-zero entries of the
1178  // auxiliary integral, containing the derivative of the total
1179  // volume flux through the outflow boundary of the (higher-dimensional)
1180  // Navier-Stokes mesh w.r.t. to the discrete (global) (velocity)
1181  // degrees of freedom.
1182  Aux_integral_pt=new std::map<unsigned,double>;
1183 
1184  // Pass pointer to Navier_stokes_outflow_mesh_pt to the Navier
1185  // Stokes traction elements
1186  unsigned nelem=navier_stokes_outflow_mesh_pt->nelement();
1187  for (unsigned e=0;e<nelem;e++)
1188  {
1191  navier_stokes_outflow_mesh_pt->element_pt(e));
1192 
1193  // Pass the mesh of all NavierStokesImpedanceTractionElements to
1194  // each NavierStokesImpedanceTractionElements in that mesh
1195  // and treat nodes in that mesh that are not part of the element
1196  // itself as external data (since they affect the total volume
1197  // flux and therefore the traction onto the element).
1199  navier_stokes_outflow_mesh_pt);
1200  }
1201  }
1202 #ifdef PARANOID
1203  // Test to make sure the elements in the mesh are valid
1204  else
1205  {
1206  if (!dynamic_cast<TemplateFreeNavierStokesFluxControlElementBase*>(
1207  navier_stokes_outflow_mesh_pt->element_pt(0)))
1208  {
1209  std::ostringstream error_message;
1210  error_message << "WomersleyImpedanceTubeBase requires a Navier-Stokes\n"
1211  << "outflow mesh of elements which inherit from either\n"
1212  << "TemplateFreeNavierStokesFluxControlElementBase or\n"
1213  << "NavierStokesImpedanceTractionElementBase.\n";
1214  throw OomphLibError(
1215  error_message.str(),
1216  OOMPH_CURRENT_FUNCTION,
1217  OOMPH_EXCEPTION_LOCATION);
1218  }
1219  }
1220 #endif
1221  }
1222 
1223  /// Access fct to outlet pressure
1224  double& p_out(){return P_out;}
1225 
1226  /// \short Pure virtual function in which the user of a derived class
1227  /// must create the mesh of WomersleyElements (of the type specified
1228  /// by the class's template argument) and apply the boundary conditions.
1229  /// The Womersley elements use the timestepper specified as the
1230  /// input argument.
1231  virtual Mesh* build_mesh_and_apply_boundary_conditions(TimeStepper*
1232  time_stepper_pt)=0;
1233 
1234 
1235  /// \short Set up the Womersley tubes so that a subsequent call
1236  /// to get_response(...) computes the inlet pressure for the currently
1237  /// prescribed instantaneous flow rate. Steady version!
1238  void setup()
1239  {
1240  // Dummy parameters
1241  double* re_st_pt=&Zero;
1242  double dt=0.0;
1243  double q_initial=0;
1244  TimeStepper* time_stepper_pt=&Mesh::Default_TimeStepper;
1245  setup(re_st_pt,dt,q_initial,time_stepper_pt);
1246  }
1247 
1248 
1249  /// \short Set up the Womersley tubes so that a subsequent call
1250  /// to get_response(...) computes the inlet pressure for the currently
1251  /// prescribed instantaneous flow rate, assuming that at all previous times
1252  /// the tube conveyed steady, fully-developed flow with flowrate q_initial.
1253  /// dt specifies the timestep for the subsequent time integration.
1254  /// Specify: Womersley number, (constant) timestep, the initial volume
1255  /// flux (from which the subsequent impulsive start is performed) and,
1256  /// optionally the pointer to the timestepper to be used in the Womersley
1257  /// elements (defaults to BDF<2>).
1258  void setup(double* re_st_pt,
1259  const double& dt,
1260  const double& q_initial,
1261  TimeStepper* time_stepper_pt=0)
1262  {
1263  // Create timestepper if none specified so far
1264  if (time_stepper_pt==0)
1265  {
1266  time_stepper_pt=new BDF<2>;
1267  }
1268 
1269  //Build mesh and apply bcs
1270  Mesh* my_mesh_pt=build_mesh_and_apply_boundary_conditions(time_stepper_pt);
1271 
1272  // Build problem
1273  Womersley_problem_pt=
1274  new WomersleyProblem<ELEMENT,DIM>(re_st_pt,Current_volume_flux_pt,
1275  time_stepper_pt,my_mesh_pt);
1276 
1277  /// By default, we do want to suppress the output from the
1278  /// Newton solver
1279  Womersley_problem_pt->disable_info_in_newton_solve();
1280  oomph_info
1281  << "NOTE: We're suppressing timings etc from \n"
1282  << " Newton solver in WomersleyImpedanceTubeBase. "
1283  << std::endl;
1284 
1285  // Precompute the auxiliary integrals for the Navier-Stokes
1286  // impedance traction elements (if they're used to specify the inflow
1287  if ((!Using_flux_control_elements) && (Navier_stokes_outflow_mesh_pt!=0))
1288  {
1289  precompute_aux_integrals();
1290  }
1291 
1292  // Initialise timestep -- also sets the weights for all timesteppers
1293  // in the problem.
1294  Womersley_problem_pt->initialise_dt(dt);
1295 
1296  // Set currently imposed flux
1297  *Current_volume_flux_pt=q_initial;
1298 
1299  // Assign steady initial solution for this flux
1300  Womersley_problem_pt->steady_newton_solve();
1301 
1302  // Allow for resolve
1303  Womersley_problem_pt->linear_solver_pt()->enable_resolve();
1304 
1305  // Re-use Jacobian
1306  Womersley_problem_pt->enable_jacobian_reuse();
1307 
1308  // Shut up
1309  Womersley_problem_pt->linear_solver_pt()->disable_doc_time();
1310 
1311  // Do a dummy solve with time-dependent terms switched on
1312  // to generate (and store) the Jacobian. (We're not using
1313  // a Newton solve because the initial residual may be zero
1314  // in which case the Jacobian would never be computed!)
1315  unsigned n_dof=Womersley_problem_pt->ndof();
1316 
1317  // Local scope to make sure dx goes out of scope
1318  {
1319  DoubleVector dx;
1320  Womersley_problem_pt->linear_solver_pt()->solve(Womersley_problem_pt,dx);
1321  }
1322 
1323 
1324  // Pre-compute derivative of p_in w.r.t. q
1325 
1326  // Setup vector of derivatives of residuals & unknowns w.r.t. Q
1327  LinearAlgebraDistribution dist(Womersley_problem_pt->communicator_pt(),
1328  n_dof,false);
1329  DoubleVector drdq(&dist,0.0);
1330  DoubleVector dxdq(&dist,0.0);
1331 
1332  // What's the global equation number of the equation that
1333  // determines the pressure gradient
1334  unsigned g_eqn=Womersley_problem_pt->
1335  pressure_gradient_data_pt()->eqn_number(0);
1336 
1337  // Derivative of volume constraint residual w.r.t. prescribed
1338  // instantaenous volume flux (in ImposeFluxForWomersleyElement)
1339  drdq[g_eqn]=-1.0;
1340 
1341  // Solve for derivatives of unknowns in Womersley problem, w.r.t.
1342  // instantaenous volume flux (in ImposeFluxForWomersleyElement)
1343  Womersley_problem_pt->linear_solver_pt()->resolve(drdq,dxdq);
1344 
1345  // Rate of change of inflow pressure w.r.t to instantaneous
1346  // volume flux
1347  Dp_in_dq=dxdq[g_eqn]*Length;
1348 
1349 
1350  }
1351 
1352 
1353  /// Access to underlying Womersley problem
1355  {
1356  return Womersley_problem_pt;
1357  }
1358 
1359 
1360 
1361  /// \short Shift history values to allow coputation of next timestep.
1362  /// Note: When used with a full Navier-Stokes problem this function
1363  /// must be called in actions_before_implicit_timestep()
1364  void shift_time_values(const double& dt)
1365  {
1366  // Shift the history values in the Womersley problem
1367  Womersley_problem_pt->shift_time_values();
1368 
1369  // Advance global time and set current value of dt
1370  Womersley_problem_pt->time_pt()->time()+=dt;
1371  Womersley_problem_pt->time_pt()->dt()=dt;
1372 
1373  //Find out how many timesteppers there are
1374  unsigned n_time_steppers = Womersley_problem_pt->ntime_stepper();
1375 
1376  //Loop over them all and set the weights
1377  for(unsigned i=0;i<n_time_steppers;i++)
1378  {
1379  Womersley_problem_pt->time_stepper_pt(i)->set_weights();
1380  }
1381  }
1382 
1383 
1384 
1385  /// \short Compute total current volume flux into the "impedance tube" that
1386  /// provides the flow resistance (flux is either obtained from
1387  /// the function that specifies it externally or by by adding up the flux
1388  /// through all NavierStokesImpedanceTractionElements in
1389  /// the mesh pointed to by the Navier_stokes_outflow_mesh_pt.
1391  {
1392  if (Prescribed_volume_flux_fct_pt!=0)
1393  {
1394  return Prescribed_volume_flux_fct_pt(
1395  Womersley_problem_pt->time_pt()->time());
1396  }
1397  else
1398  {
1399  unsigned nelem=Navier_stokes_outflow_mesh_pt->nelement();
1400  double flux=0.0;
1401  if (Using_flux_control_elements)
1402  {
1403  for (unsigned e=0;e<nelem;e++)
1404  {
1406  Navier_stokes_outflow_mesh_pt->element_pt(e))->get_volume_flux();
1407  }
1408  }
1409  else
1410  {
1411  for (unsigned e=0;e<nelem;e++)
1412  {
1413  flux+=dynamic_cast<NavierStokesImpedanceTractionElementBase*>(
1414  Navier_stokes_outflow_mesh_pt->element_pt(e))->get_volume_flux();
1415  }
1416  }
1417  return flux;
1418  }
1419  }
1420 
1421 
1422 
1423 
1424  /// \short Compute inlet pressure, p_in, required to achieve the currently
1425  /// imposed, instantaneous volume flux q prescribed by
1426  /// total_volume_flux_into_impedance_tube(), and its
1427  /// derivative, dp_in/dq.
1428  void get_response(double& p_in,
1429  double& dp_in_dq)
1430  {
1431  // Set currently imposed flux
1432  *Current_volume_flux_pt=total_volume_flux_into_impedance_tube();
1433 
1434  // Do a Newton solve to compute the pressure gradient
1435  // required to achieve the imposed instantaneous flow rate
1436  Womersley_problem_pt->newton_solve();
1437 
1438  // Compute inflow pressure based on computed pressure gradient,
1439  // the length of tube, and the outlet pressure
1440  p_in=-Womersley_problem_pt->pressure_gradient_data_pt()->value(0)*Length+
1441  P_out;
1442 
1443  // Return pre-computed value for dp_in/dq
1444  dp_in_dq=Dp_in_dq;
1445  }
1446 
1447 
1448 protected:
1449 
1450  /// \short Precompute auxiliary integrals required for the computation of the
1451  /// Jacobian in the NavierStokesImpedanceTractionElement. Also pass the
1452  /// pointer to the pre-computed integrals to the elements in the
1453  /// Navier_stokes_outflow_mesh_pt so they can refer to it.
1455  {
1456  // Loop over all elements
1457  unsigned nelem=Navier_stokes_outflow_mesh_pt->nelement();
1458  for (unsigned e=0;e<nelem;e++)
1459  {
1462  Navier_stokes_outflow_mesh_pt->element_pt(e));
1463 
1464  // Add the element's contribution
1465  el_pt->add_element_contribution_to_aux_integral(Aux_integral_pt);
1466 
1467  // Tell the elements who's setting their flow resistance
1468  el_pt->set_impedance_tube_pt(this);
1469 
1470  }
1471 
1472  // Pass pointer to Aux_integral to the elements so they can
1473  // use it in the computation of the Jacobian
1474  for (unsigned e=0;e<nelem;e++)
1475  {
1477  =dynamic_cast<
1479  Navier_stokes_outflow_mesh_pt->element_pt(e));
1480 
1481  // Pass pointer to elements
1482  el_pt->set_aux_integral_pt(Aux_integral_pt);
1483 
1484  }
1485 
1486  }
1487 
1488  /// Length of the tube
1489  double Length;
1490 
1491  /// \short Derivative of inflow pressure w.r.t. instantaenous volume flux
1492  /// (Note: Can be pre-computed)
1493  double Dp_in_dq;
1494 
1495  /// \short Pointer to double that specifies the currently imposed
1496  /// instantaneous volume flux into the impedance tube. This is
1497  /// used to communicate with the Womersley elements which require
1498  /// access to the flux via a pointer to a double.
1500 
1501  /// \short Pointer to Womersley problem that determines the
1502  /// pressure gradient along the tube
1504 
1505  /// Outlet pressure
1506  double P_out;
1507 
1508  /// Pointer to function that specifies the prescribed volume flux
1509  PrescribedVolumeFluxFctPt Prescribed_volume_flux_fct_pt;
1510 
1511  /// \short Pointer to the mesh of NavierStokesImpedanceTractionElements
1512  /// that are attached to the outflow cross-section of the higher-dimensional
1513  /// Navier Stokes mesh and provide the inflow into the Impedance tube.
1515 
1516  /// \short Pointer to auxiliary integral, containing
1517  /// the derivative of the total volume flux through the
1518  /// outflow boundary of the (higher-dimensional) Navier-Stokes mesh w.r.t.
1519  /// to the discrete (global) (velocity) degrees of freedom.
1520  std::map<unsigned,double>* Aux_integral_pt;
1521 
1522  private:
1523 
1524  // \short Flag to record if NavierStokesFluxControlElement
1525  // or NavierStokesImpedanceTractionElement elements are being used
1527 
1528 };
1529 
1530 ////////////////////////////////////////////////////////////////////////
1531 ////////////////////////////////////////////////////////////////////////
1532 ////////////////////////////////////////////////////////////////////////
1533 
1534 //Inline functions:
1535 
1536 
1537 //======================================================================
1538 /// Define the shape functions and test functions and derivatives
1539 /// w.r.t. global coordinates and return Jacobian of mapping.
1540 ///
1541 /// Galerkin: Test functions = shape functions
1542 //======================================================================
1543 template<unsigned DIM, unsigned NNODE_1D>
1546  Shape &psi,
1547  DShape &dpsidx,
1548  Shape &test,
1549  DShape &dtestdx) const
1550 {
1551  //Call the geometrical shape functions and derivatives
1552  double J = this->dshape_eulerian(s,psi,dpsidx);
1553 
1554  //Loop over the test functions and derivatives and set them equal to the
1555  //shape functions
1556  for(unsigned i=0;i<NNODE_1D;i++)
1557  {
1558  test[i] = psi[i];
1559  for(unsigned j=0;j<DIM;j++)
1560  {
1561  dtestdx(i,j) = dpsidx(i,j);
1562  }
1563  }
1564 
1565  //Return the jacobian
1566  return J;
1567 }
1568 
1569 
1570 //======================================================================
1571 /// Define the shape functions and test functions and derivatives
1572 /// w.r.t. global coordinates and return Jacobian of mapping.
1573 ///
1574 /// Galerkin: Test functions = shape functions
1575 //======================================================================
1576 template<unsigned DIM,unsigned NNODE_1D>
1579  const unsigned &ipt,
1580  Shape &psi,
1581  DShape &dpsidx,
1582  Shape &test,
1583  DShape &dtestdx) const
1584 {
1585  //Call the geometrical shape functions and derivatives
1586  double J = this->dshape_eulerian_at_knot(ipt,psi,dpsidx);
1587 
1588  //Set the test functions equal to the shape functions
1589  //(sets internal pointers)
1590  test = psi;
1591  dtestdx = dpsidx;
1592 
1593  //Return the jacobian
1594  return J;
1595 }
1596 
1597 
1598 ////////////////////////////////////////////////////////////////////////
1599 ////////////////////////////////////////////////////////////////////////
1600 
1601 
1602 
1603 //=======================================================================
1604 /// Face geometry for the QWomersleyElement elements: The spatial
1605 /// dimension of the face elements is one lower than that of the
1606 /// bulk element but they have the same number of points
1607 /// along their 1D edges.
1608 //=======================================================================
1609 template<unsigned DIM, unsigned NNODE_1D>
1610 class FaceGeometry<QWomersleyElement<DIM,NNODE_1D> >:
1611 public virtual QElement<DIM-1,NNODE_1D>
1612 {
1613 
1614  public:
1615 
1616  /// \short Constructor: Call the constructor for the
1617  /// appropriate lower-dimensional QElement
1618  FaceGeometry() : QElement<DIM-1,NNODE_1D>() {}
1619 
1620 };
1621 
1622 
1623 ////////////////////////////////////////////////////////////////////////
1624 ////////////////////////////////////////////////////////////////////////
1625 ////////////////////////////////////////////////////////////////////////
1626 
1627 
1628 //=======================================================================
1629 /// Face geometry for the 1D QWomersleyElement elements: Point elements
1630 //=======================================================================
1631 template<unsigned NNODE_1D>
1632 class FaceGeometry<QWomersleyElement<1,NNODE_1D> >:
1633  public virtual PointElement
1634 {
1635 
1636  public:
1637 
1638  /// \short Constructor: Call the constructor for the
1639  /// appropriate lower-dimensional QElement
1641 
1642 };
1643 
1644 
1645 ////////////////////////////////////////////////////////////////////////
1646 ////////////////////////////////////////////////////////////////////////
1647 ////////////////////////////////////////////////////////////////////////
1648 
1649 
1650 //====================================================================
1651 /// Template-free base class
1652 //====================================================================
1654 {
1655 
1656 public:
1657 
1658  /// Static bool to suppress warning
1660 
1661 };
1662 
1663 
1664 ////////////////////////////////////////////////////////////////////////
1665 ////////////////////////////////////////////////////////////////////////
1666 ////////////////////////////////////////////////////////////////////////
1667 
1668 
1669 //====================================================================
1670 /// Mesh of Womersley elements whose topology, nodal position etc.
1671 /// matches that of a given mesh of face elements in the outflow
1672 /// cross-section of a full Navier-Stokes mesh.
1673 //====================================================================
1674 template <class WOMERSLEY_ELEMENT>
1675  class WomersleyMesh : public virtual Mesh,
1676  public virtual TemplateFreeWomersleyMeshBase
1677 {
1678 
1679 public:
1680 
1681 
1682  /// \short Constructor: Pass pointer to mesh of face elements in the outflow
1683  /// cross-section of a full Navier-Stokes mesh, the timestepper
1684  /// to be used for the Womersley elements, the coordinate (in the
1685  /// higher-dimensional Navier-Stokes mesh) that is constant
1686  /// in the outflow cross-section and the velocity component
1687  /// (in terms of the nodal index) that represents the outflow
1688  /// component -- the latter is used to automatically apply
1689  /// the boundary conditions for the Womersley problem.
1690  WomersleyMesh(Mesh* n_st_outflow_mesh_pt,
1691  TimeStepper* time_stepper_pt,
1692  const unsigned& fixed_coordinate,
1693  const unsigned& w_index)
1694  {
1695  /// How many elements and nodes are there in the original mesh?
1696  unsigned nelem=n_st_outflow_mesh_pt->nelement();
1697 
1698  // Navier-Stokes outflow mesh may not have any nodes stored (it usually
1699  // just acts as a container for traction elements) -->
1700  // Count number of distinct Navier-Stokes nodes by adding
1701  // the elements' nodes to a set
1702  std::set<Node*> n_st_nodes;
1703  for (unsigned e=0;e<nelem;e++)
1704  {
1705  FiniteElement* el_pt=n_st_outflow_mesh_pt->finite_element_pt(e);
1706  unsigned nnod_el=el_pt->nnode();
1707  for (unsigned j=0;j<nnod_el;j++)
1708  {
1709  n_st_nodes.insert(el_pt->node_pt(j));
1710 
1711  // Careful: It there are hanging nodes this won't work!
1712  if (el_pt->node_pt(j)->is_hanging())
1713  {
1714  throw OomphLibError(
1715  "Cannot build WomersleyMesh from mesh with hanging nodes!",
1716  OOMPH_CURRENT_FUNCTION,
1717  OOMPH_EXCEPTION_LOCATION);
1718  }
1719  }
1720  }
1721 
1722  // Extract size then wipe
1723  unsigned nnode_n_st= n_st_nodes.size();
1724  n_st_nodes.clear();
1725 
1726  // Create enough storage
1727  Node_pt.resize(nnode_n_st);
1728 
1729  /// Create new elements
1730  for (unsigned e=0;e<nelem;e++)
1731  {
1732  add_element_pt(new WOMERSLEY_ELEMENT);
1733 #ifdef PARANOID
1734  if (finite_element_pt(e)->nnode()!=
1735  n_st_outflow_mesh_pt->finite_element_pt(e)->nnode())
1736  {
1737  throw OomphLibError(
1738  "Number of nodes in existing and new elements don't match",
1739  OOMPH_CURRENT_FUNCTION,
1740  OOMPH_EXCEPTION_LOCATION);
1741  }
1742 #endif
1743  }
1744 
1745  // Map to record which Navier-Stokes nodes have been visited (default
1746  // return is false)
1747  std::map<Node*,bool> n_st_node_done;
1748 
1749  // Map to store the Womersley node that corresponds to a
1750  // Navier Stokes node
1751  std::map<Node*,Node*> equivalent_womersley_node_pt;
1752 
1753  // Initialise count of newly created nodes
1754  unsigned node_count=0;
1755 
1756 
1757  // This is awkward do diagnose: We're assuming that
1758  // the boundary conditions have been applied for the
1759  // underlying Navier-Stokes problem before calling
1760  // this function (otherwise it's really tricky to
1761  // apply the right boundary conditions here), but it's
1762  // hard to police. Issue definite (but suppressable)
1763  // warning if nothing has been pinned at all.
1764  unsigned n_pinned_nodes=0;
1765 
1766  // Loop over nst and womersley elements in tandem to sort out
1767  // which new nodes are required
1768  for (unsigned e=0;e<nelem;e++)
1769  {
1770  FiniteElement* n_st_el_pt=n_st_outflow_mesh_pt->finite_element_pt(e);
1771  unsigned nnod_el=n_st_el_pt->nnode();
1772  for (unsigned j=0;j<nnod_el;j++)
1773  {
1774  // Has the Navier Stokes node been done yet?
1775  Node* n_st_node_pt=n_st_el_pt->node_pt(j);
1776 
1777  // Hasn't been done: Create new node in Womersley element
1778  if (!n_st_node_done[n_st_node_pt])
1779  {
1780  // Create a new node in the Womersley element
1781  Node* new_node_pt=
1782  finite_element_pt(e)->construct_node(j,time_stepper_pt);
1783 
1784  // Add newly created node
1785  Node_pt[node_count]=new_node_pt;
1786  node_count++;
1787 
1788 
1789  // Set coordinates
1790  unsigned dim=n_st_node_pt->ndim();
1791  unsigned icount=0;
1792  for (unsigned i=0;i<dim;i++)
1793  {
1794  if (i!=fixed_coordinate)
1795  {
1796  new_node_pt->x(icount)=n_st_node_pt->x(i);
1797  icount++;
1798  }
1799  }
1800 
1801  // Set pin status
1802  if (n_st_node_pt->is_pinned(w_index))
1803  {
1804  new_node_pt->pin(0);
1805  n_pinned_nodes++;
1806  }
1807  else
1808  {
1809  // shouldn't need this, but...
1810  new_node_pt->unpin(0);
1811  }
1812 
1813  // Record which Womersley node the
1814  // Navier Stokes node is associated with
1815  equivalent_womersley_node_pt[n_st_node_pt]=new_node_pt;
1816 
1817  // Now the Navier-Stokes node has been done
1818  n_st_node_done[n_st_node_pt]=true;
1819  }
1820  // The node has already been done -- set pointer to existing
1821  // Womersley node
1822  else
1823  {
1824  finite_element_pt(e)->node_pt(j)=
1825  equivalent_womersley_node_pt[n_st_node_pt];
1826  }
1827  }
1828 
1829  bool passed=true;
1830  finite_element_pt(e)->check_J_eulerian_at_knots(passed);
1831  if (!passed)
1832  {
1833  //Reverse the nodes
1834  unsigned nnod=finite_element_pt(e)->nnode();
1835  Vector<Node*> orig_nod_pt(nnod);
1836  for (unsigned j=0;j<nnod;j++)
1837  {
1838  orig_nod_pt[j]=finite_element_pt(e)->node_pt(j);
1839  }
1840  for (unsigned j=0;j<nnod;j++)
1841  {
1842  finite_element_pt(e)->node_pt(j)=orig_nod_pt[nnod-j-1];
1843  }
1844  bool passed=true;
1845  finite_element_pt(e)->check_J_eulerian_at_knots(passed);
1846  if (!passed)
1847  {
1848  throw OomphLibError(
1849  "Element remains inverted even after reversing the local node numbers",
1850  OOMPH_CURRENT_FUNCTION,
1851  OOMPH_EXCEPTION_LOCATION);
1852  }
1853  }
1854  }
1855 
1856 
1857 #ifdef PARANOID
1858  if (!Suppress_warning_about_unpinned_nst_dofs)
1859  {
1860  if (n_pinned_nodes==0)
1861  {
1862  std::stringstream bla;
1863  bla << "Boundary conditions must be applied in Navier-Stokes\n"
1864  << "problem before attaching impedance elements.\n"
1865  << "Note: This warning can be suppressed by setting the\n"
1866  << "global static boolean\n\n"
1867  << " TemplateFreeWomersleyMeshBase::Suppress_warning_about_unpinned_nst_dofs\n\n"
1868  << "to true\n";
1869  OomphLibWarning(bla.str(),
1870  OOMPH_CURRENT_FUNCTION,
1871  OOMPH_EXCEPTION_LOCATION);
1872  }
1873  }
1874 #endif
1875 
1876 #ifdef PARANOID
1877  if (nnode()!=nnode_n_st)
1878  {
1879  throw OomphLibError(
1880  "Number of nodes in the new mesh don't match that in the old one",
1881  OOMPH_CURRENT_FUNCTION,
1882  OOMPH_EXCEPTION_LOCATION);
1883  }
1884 #endif
1885 
1886  }
1887 
1888 };
1889 
1890 
1891 ////////////////////////////////////////////////////////////////////////
1892 /////////////////////////////////////////////////////////////////////////
1893 /////////////////////////////////////////////////////////////////////////
1894 
1895 
1896 
1897 //====================================================================
1898 /// WomersleyImpedanceTube that attaches itself to the outflow
1899 /// of a Navier-Stokes mesh.
1900 //====================================================================
1901 template<class ELEMENT, unsigned DIM>
1903 public WomersleyImpedanceTubeBase<ELEMENT,DIM>
1904 {
1905 
1906 public:
1907 
1908  /// \short Constructor: Pass length and mesh of face elements that
1909  /// are attached to the outflow cross-section of the Navier Stokes mesh
1910  /// to constructor of underlying base class. Also specify
1911  /// the coordinate (in the higher-dimensional
1912  /// Navier-Stokes mesh) that is constant
1913  /// in the outflow cross-section and the velocity component
1914  /// (in terms of the nodal index) that represents the outflow
1915  /// component -- the latter is used to automatically apply
1916  /// the boundary conditions for the Womersley problem.
1918  const double& length,
1919  Mesh* navier_stokes_outflow_mesh_pt,
1920  const unsigned& fixed_coordinate,
1921  const unsigned& w_index) :
1922  WomersleyImpedanceTubeBase<ELEMENT,DIM>(length,
1923  navier_stokes_outflow_mesh_pt),
1924  Fixed_coordinate(fixed_coordinate), W_index(w_index)
1925  {}
1926 
1927  /// \short Implement pure virtual fct (defined in the base class
1928  /// WomersleyImpedanceTubeBase) that builds the mesh of Womersley elements
1929  /// (of the type specified by the template argument), using the
1930  /// specified timestepper. Also applies the boundary condition.
1932  {
1933  // Build mesh and automatically apply the same boundary
1934  // conditions as those that are applied to the W_index-th
1935  // value of the nodes in the Navier-Stokes mesh.
1936  WomersleyMesh<ELEMENT>* womersley_mesh_pt=
1938  this->Navier_stokes_outflow_mesh_pt,
1939  time_stepper_pt,
1940  Fixed_coordinate,
1941  W_index);
1942 
1943  return womersley_mesh_pt;
1944 
1945  }
1946 
1947  private:
1948 
1949  /// \short The coordinate (in the higher-dimensional Navier-Stokes
1950  /// mesh) that is constant in the outflow cross-section
1952 
1953  /// \short The velocity component
1954  /// (in terms of the nodal index) that represents the outflow
1955  /// component -- the latter is used to automatically apply
1956  /// the boundary conditions for the Womersley problem.
1957  unsigned W_index;
1958 
1959 
1960 };
1961 
1962 ////////////////////////////////////////////////////////////////////////
1963 /////////////////////////////////////////////////////////////////////////
1964 /////////////////////////////////////////////////////////////////////////
1965 
1966 
1967 
1968 /* //==================================================================== */
1969 /* /// WomersleyImpedanceTube that attaches itself to the outflow */
1970 /* /// of a Navier-Stokes mesh for use with . */
1971 /* //==================================================================== */
1972 /* template<class ELEMENT, unsigned DIM> */
1973 /* class WomersleyOutflowImpedanceTubeForNavierStokesBlockPreconditioner : */
1974 /* public WomersleyImpedanceTubeBaseForNavierStokesBlockPreconditioner */
1975 /* <ELEMENT,DIM> */
1976 /* { */
1977 
1978 /* public: */
1979 
1980 /* /// \short Constructor: Pass length and mesh of face elements that */
1981 /* /// are attached to the outflow cross-section of the Navier Stokes mesh */
1982 /* /// to constructor of underlying base class. Also specify */
1983 /* /// the coordinate (in the higher-dimensional */
1984 /* /// Navier-Stokes mesh) that is constant */
1985 /* /// in the outflow cross-section and the velocity component */
1986 /* /// (in terms of the nodal index) that represents the outflow */
1987 /* /// component -- the latter is used to automatically apply */
1988 /* /// the boundary conditions for the Womersley problem. */
1989 /* WomersleyOutflowImpedanceTubeForNavierStokesBlockPreconditioner( */
1990 /* const double& length, */
1991 /* Mesh* navier_stokes_outflow_mesh_pt, */
1992 /* const unsigned& fixed_coordinate, */
1993 /* const unsigned& w_index) : */
1994 /* WomersleyImpedanceTubeBaseForNavierStokesBlockPreconditioner<ELEMENT,DIM> */
1995 /* (length, */
1996 /* navier_stokes_outflow_mesh_pt), */
1997 /* Fixed_coordinate(fixed_coordinate), W_index(w_index) */
1998 /* {} */
1999 
2000 /* /// \short Implement pure virtual fct (defined in the base class */
2001 /* /// WomersleyImpedanceTubeBase) that builds the mesh of Womersley elements */
2002 /* /// (of the type specified by the template argument), using the */
2003 /* /// specified timestepper. Also applies the boundary condition. */
2004 /* Mesh* build_mesh_and_apply_boundary_conditions(TimeStepper* time_stepper_pt) */
2005 /* { */
2006 /* // Build mesh and automatically apply the same boundary */
2007 /* // conditions as those that are applied to the W_index-th */
2008 /* // value of the nodes in the Navier-Stokes mesh. */
2009 /* WomersleyMesh<ELEMENT>* womersley_mesh_pt= // CHANGED TO USE ELEMENT */
2010 /* new WomersleyMesh<ELEMENT>( */
2011 /* this->Navier_stokes_outflow_mesh_pt, */
2012 /* time_stepper_pt, */
2013 /* Fixed_coordinate, */
2014 /* W_index); */
2015 
2016 /* return womersley_mesh_pt; */
2017 
2018 /* } */
2019 
2020 /* private: */
2021 
2022 /* /// \short The coordinate (in the higher-dimensional Navier-Stokes */
2023 /* /// mesh) that is constant in the outflow cross-section */
2024 /* unsigned Fixed_coordinate; */
2025 
2026 /* /// \short The velocity component */
2027 /* /// (in terms of the nodal index) that represents the outflow */
2028 /* /// component -- the latter is used to automatically apply */
2029 /* /// the boundary conditions for the Womersley problem. */
2030 /* unsigned W_index; */
2031 
2032 
2033 /* }; */
2034 
2035 
2036 
2037 /////////////////////////////////////////////////////////////////////////
2038 /////////////////////////////////////////////////////////////////////////
2039 /////////////////////////////////////////////////////////////////////////
2040 
2041 
2042 
2043 //======================================================================
2044 /// A class for elements that allow the imposition of an impedance type
2045 /// traction boundary condition to the Navier--Stokes equations
2046 /// The geometrical information can be read from the FaceGeometery<ELEMENT>
2047 /// class and and thus, we can be generic enough without the need to have
2048 /// a separate equations class. Template arguments specify the
2049 /// type of the bulk Navier Stokes elements that the elements are
2050 /// attached to, and the type of the Womersley element used
2051 /// to compute the flow resistance in the downstream "impedance tube".
2052 //======================================================================
2053 template <class BULK_NAVIER_STOKES_ELEMENT,
2054  class WOMERSLEY_ELEMENT,
2055  unsigned DIM>
2057  public virtual FaceGeometry<BULK_NAVIER_STOKES_ELEMENT>,
2058  public virtual FaceElement,
2060 {
2061 
2062  private:
2063 
2064  /// \short Pointer to auxiliary integral, containing
2065  /// the derivative of the total volume flux through the
2066  /// outflow boundary of the (higher-dimensional) Navier-Stokes mesh w.r.t.
2067  /// to the discrete (global) (velocity) degrees of freedom.
2068  std::map<unsigned,double>* Aux_integral_pt;
2069 
2070  /// Pointer to ImpedanceTubeProblem that computes the flow resistance
2072 
2073 
2074  protected:
2075 
2076  /// \short Access function that returns the local equation numbers
2077  /// for velocity components.
2078  /// u_local_eqn(n,i) = local equation number or < 0 if pinned.
2079  /// The default is to asssume that n is the local node number
2080  /// and the i-th velocity component is the i-th unknown stored at the node.
2081  virtual inline int u_local_eqn(const unsigned &n, const unsigned &i)
2082  {return nodal_local_eqn(n,i);}
2083 
2084  ///\short Function to compute the shape and test functions and to return
2085  ///the Jacobian of mapping
2086  inline double shape_and_test_at_knot(const unsigned &ipt,
2087  Shape &psi, Shape &test)
2088  const
2089  {
2090  //Find number of nodes
2091  unsigned n_node = nnode();
2092 
2093  //Calculate the shape functions
2094  shape_at_knot(ipt,psi);
2095 
2096  //Set the test functions to be the same as the shape functions
2097  for(unsigned i=0;i<n_node;i++) {test[i] = psi[i];}
2098 
2099  //Return the value of the jacobian
2100  return J_eulerian_at_knot(ipt);
2101  }
2102 
2103 
2104  ///\short This function returns the residuals for the
2105  /// traction function.
2106  ///flag=1(or 0): do (or don't) compute the Jacobian as well.
2107  void fill_in_generic_residual_contribution_fluid_traction(
2108  Vector<double> &residuals,
2109  DenseMatrix<double> &jacobian,
2110  unsigned flag);
2111 
2112 
2113  public:
2114 
2115  ///Constructor, which takes a "bulk" element and the value of the index
2116  ///and its limit
2118  const int &face_index) :
2119  FaceGeometry<BULK_NAVIER_STOKES_ELEMENT>(), FaceElement()
2120  {
2121 
2122  //Attach the geometrical information to the element. N.B. This function
2123  //also assigns nbulk_value from the required_nvalue of the bulk element
2124  element_pt->build_face_element(face_index,this);
2125 
2126  // Initialise pointer to mesh containing the
2127  // NavierStokesImpedanceTractionElements
2128  // that contribute to the volume flux into the "downstream tube" that
2129  // provides the impedance
2130  Navier_stokes_outflow_mesh_pt=0;
2131 
2132  // Initialise pointer to impedance tube
2133  Impedance_tube_pt=0;
2134 
2135  // Initialise pointer to auxiliary integral
2136  Aux_integral_pt=0;
2137 
2138  //Set the dimension from the dimension of the first node
2139  //Dim = this->node_pt(0)->ndim();
2140 
2141 #ifdef PARANOID
2142  {
2143  //Check that the element is not a refineable 3d element
2144  BULK_NAVIER_STOKES_ELEMENT* elem_pt =
2145  dynamic_cast<BULK_NAVIER_STOKES_ELEMENT*>(element_pt);
2146  //If it's three-d
2147  if(elem_pt->dim()==3)
2148  {
2149  //Is it refineable
2150  RefineableElement* ref_el_pt=dynamic_cast<RefineableElement*>(elem_pt);
2151  if(ref_el_pt!=0)
2152  {
2153  if (this->has_hanging_nodes())
2154  {
2155  throw OomphLibError(
2156  "This flux element will not work correctly if nodes are hanging\n",
2157  OOMPH_CURRENT_FUNCTION,
2158  OOMPH_EXCEPTION_LOCATION);
2159  }
2160  }
2161  }
2162  }
2163 #endif
2164  }
2165 
2166 
2167  /// Destructor should not delete anything
2169 
2170  /// \short Access to mesh containing all NavierStokesImpedanceTractionElements
2171  /// that contribute to the volume flux into the "downstream tube" that
2172  /// provides the impedance
2174  {
2175  return Navier_stokes_outflow_mesh_pt;
2176  }
2177 
2178  /// \short Get integral of volume flux through element
2180  {
2181  // Initialise
2182  double volume_flux_integral=0.0;
2183 
2184  //Vector of local coordinates in face element
2185  Vector<double> s(DIM);
2186 
2187  // Vector for global Eulerian coordinates
2188  Vector<double> x(DIM+1);
2189 
2190  // Vector for local coordinates in bulk element
2191  Vector<double> s_bulk(DIM+1);
2192 
2193  //Set the value of n_intpt
2194  unsigned n_intpt = integral_pt()->nweight();
2195 
2196  // Get pointer to assocated bulk element
2197  BULK_NAVIER_STOKES_ELEMENT* bulk_el_pt=
2198  dynamic_cast<BULK_NAVIER_STOKES_ELEMENT*>(bulk_element_pt());
2199 
2200  //Loop over the integration points
2201  for (unsigned ipt=0;ipt<n_intpt;ipt++)
2202  {
2203 
2204  //Assign values of s in FaceElement and local coordinates in bulk element
2205  for(unsigned i=0;i<DIM;i++)
2206  {
2207  s[i] = integral_pt()->knot(ipt,i);
2208  }
2209 
2210  //Get the bulk coordinates
2211  this->get_local_coordinate_in_bulk(s,s_bulk);
2212 
2213  //Get the integral weight
2214  double w = integral_pt()->weight(ipt);
2215 
2216  // Get jacobian of mapping
2217  double J=J_eulerian(s);
2218 
2219  //Premultiply the weights and the Jacobian
2220  double W = w*J;
2221 
2222 
2223 #ifdef PARANOID
2224 
2225  // Get x position as Vector
2226  interpolated_x(s,x);
2227 
2228  // Get x position as Vector from bulk element
2229  Vector<double> x_bulk(DIM+1);
2230  bulk_el_pt->interpolated_x(s_bulk,x_bulk);
2231 
2232  double max_legal_error=1.0e-12;
2233  double error=0.0;
2234  for(unsigned i=0;i<DIM+1;i++)
2235  {
2236  error+=std::fabs(x[i]- x_bulk[i]);
2237  }
2238  if (error>max_legal_error)
2239  {
2240  std::ostringstream error_stream;
2241  error_stream << "difference in Eulerian posn from bulk and face: "
2242  << error << " exceeds threshold " << max_legal_error
2243  << std::endl;
2244  throw OomphLibError(
2245  error_stream.str(),
2246  OOMPH_CURRENT_FUNCTION,
2247  OOMPH_EXCEPTION_LOCATION);
2248  }
2249 #endif
2250 
2251  // Outer unit normal
2252  Vector<double> normal(DIM+1);
2253  outer_unit_normal(s,normal);
2254 
2255  // Get velocity from bulk element
2256  Vector<double> veloc(DIM+1);
2257  bulk_el_pt->interpolated_u_nst(s_bulk,veloc);
2258 
2259  // Volume flux
2260  double volume_flux=0.0;
2261  for (unsigned i=0;i<DIM+1;i++)
2262  {
2263  volume_flux+=normal[i]*veloc[i];
2264  }
2265 
2266  // Add to integral
2267  volume_flux_integral+=volume_flux*W;
2268 
2269  }
2270 
2271  return volume_flux_integral;
2272 
2273  }
2274 
2275 
2276  /// \short NavierStokesImpedanceTractionElements that contribute
2277  /// to the volume flux into the downstream "impedance tube"
2278  /// to the element and classify all nodes in that mesh
2279  /// as external Data for this element (unless the nodes
2280  /// are also the element's own nodes, of course).
2282  Mesh* navier_stokes_outflow_mesh_pt)
2283  {
2284 
2285  // Store pointer to mesh of NavierStokesImpedanceTractionElement
2286  // that contribute to the volume flux into the "impedance tube" that
2287  // provides the flow resistance
2288  Navier_stokes_outflow_mesh_pt=navier_stokes_outflow_mesh_pt;
2289 
2290  // Create a set the contains all nodal Data in the flux mesh
2291  std::set<Data*> external_data_set;
2292  unsigned nelem=Navier_stokes_outflow_mesh_pt->nelement();
2293  for (unsigned e=0;e<nelem;e++)
2294  {
2295  FiniteElement* el_pt=Navier_stokes_outflow_mesh_pt->finite_element_pt(e);
2296  unsigned nnod=el_pt->nnode();
2297  for (unsigned j=0;j<nnod;j++)
2298  {
2299  external_data_set.insert(el_pt->node_pt(j));
2300  }
2301  }
2302 
2303  // Remove the element's own nodes
2304  unsigned nnod=nnode();
2305  for (unsigned j=0;j<nnod;j++)
2306  {
2307  external_data_set.erase(node_pt(j));
2308  }
2309 
2310  // Copy across
2311  for (std::set<Data*>::iterator it=external_data_set.begin();
2312  it!=external_data_set.end();it++)
2313  {
2314  add_external_data(*it);
2315  }
2316  }
2317 
2318 
2319  /// \short Set pointer to the precomputed auxiliary integral that contains
2320  /// the derivative of the total volume flux through the
2321  /// outflow boundary of the (higher-dimensional) Navier-Stokes mesh w.r.t.
2322  /// to the discrete (global) (velocity) degrees of freedom.
2323  void set_aux_integral_pt(std::map<unsigned,double>* aux_integral_pt)
2324  {
2325  Aux_integral_pt=aux_integral_pt;
2326  }
2327 
2328 
2329  /// \short Compute total volume flux into the "downstream tube" that
2330  /// provides the impedance (computed by adding up the flux
2331  /// through all NavierStokesImpedanceTractionElements in
2332  /// the mesh specified by volume_flux_mesh_pt().
2334  {
2335 #ifdef PARANOID
2336  if (Navier_stokes_outflow_mesh_pt==0)
2337  {
2338  throw OomphLibError(
2339  "Navier_stokes_outflow_mesh_pt==0 -- set it with \n set_external_data_from_navier_stokes_outflow_mesh() before calling this function!\n",
2340  OOMPH_CURRENT_FUNCTION,
2341  OOMPH_EXCEPTION_LOCATION); }
2342 #endif
2343 
2344 
2345  double total_flux=0.0;
2346  unsigned nelem=Navier_stokes_outflow_mesh_pt->nelement();
2347  for (unsigned e=0;e<nelem;e++)
2348  {
2349  NavierStokesImpedanceTractionElement<BULK_NAVIER_STOKES_ELEMENT,
2350  WOMERSLEY_ELEMENT,DIM>* el_pt=
2351  dynamic_cast<
2352  NavierStokesImpedanceTractionElement<BULK_NAVIER_STOKES_ELEMENT,
2353  WOMERSLEY_ELEMENT,DIM>*>(
2354  Navier_stokes_outflow_mesh_pt->element_pt(e));
2355  total_flux+=el_pt->get_volume_flux();
2356  }
2357  return total_flux;
2358  }
2359 
2360 
2361  /// \short Set pointer to "impedance tube" that provides the flow
2362  /// resistance
2364  impedance_tube_pt)
2365  {
2366  Impedance_tube_pt=dynamic_cast<
2368  }
2369 
2370 
2371  /// Add the element's contribution to the auxiliary integral
2372  /// that contains the derivative of the total volume flux through the
2373  /// outflow boundary of the (higher-dimensional) Navier-Stokes mesh w.r.t.
2374  /// to the discrete (global) (velocity) degrees of freedom.
2375  void add_element_contribution_to_aux_integral(std::map<unsigned,double>*
2376  aux_integral_pt)
2377  {
2378  // Spatial dimension of element
2379  // unsigned ndim=dim();
2380 
2381  //Vector of local coordinates in face element
2382  Vector<double> s(DIM);
2383 
2384  // Create storage for shape functions
2385  unsigned nnod=nnode();
2386  Shape psi(nnod);
2387 
2388  //Set the value of n_intpt
2389  unsigned n_intpt = integral_pt()->nweight();
2390 
2391  //Loop over the integration points
2392  for (unsigned ipt=0;ipt<n_intpt;ipt++)
2393  {
2394 
2395  //Assign values of s in FaceElement and local coordinates in bulk element
2396  for(unsigned i=0;i<DIM;i++)
2397  {
2398  s[i] = integral_pt()->knot(ipt,i);
2399  }
2400 
2401  //Get the integral weight
2402  double w = integral_pt()->weight(ipt);
2403 
2404  // Get jacobian of mapping
2405  double J=J_eulerian(s);
2406 
2407  // Get shape functions
2408  shape(s,psi);
2409 
2410  //Premultiply the weights and the Jacobian
2411  double W = w*J;
2412 
2413  // Outer unit normal
2414  Vector<double> normal(DIM+1);
2415  outer_unit_normal(s,normal);
2416 
2417  // Loop over nodes
2418  for (unsigned j=0;j<nnod;j++)
2419  {
2420  // Get pointer to Node
2421  Node* nod_pt=node_pt(j);
2422 
2423  // Loop over directions
2424  for (unsigned i=0;i<(DIM+1);i++)
2425  {
2426  // Get global equation number
2427  int i_global=nod_pt->eqn_number(i);
2428 
2429  // Real dof or bc?
2430  if (i_global>=0)
2431  {
2432  (*aux_integral_pt)[i_global]+=psi[j]*normal[i]*W;
2433  }
2434  }
2435  }
2436  }
2437  }
2438 
2439 
2440 
2441  /// Fill in the element's contribution to the element's residual vector
2443  {
2444  //Call the generic residuals function with flag set to 0
2445  //using a dummy matrix argument
2446  fill_in_generic_residual_contribution_fluid_traction(
2447  residuals,GeneralisedElement::Dummy_matrix,0);
2448  }
2449 
2450 
2451  /// \short Fill in the element's contribution to the element's residual vector
2452  /// and Jacobian matrix
2454  DenseMatrix<double> &jacobian)
2455  {
2456  //Call the generic routine with the flag set to 1
2457  fill_in_generic_residual_contribution_fluid_traction(residuals,jacobian,1);
2458  }
2459 
2460 
2461  /// Specify the value of nodal zeta from the face geometry
2462  /// \short The "global" intrinsic coordinate of the element when
2463  /// viewed as part of a geometric object should be given by
2464  /// the FaceElement representation, by default (needed to break
2465  /// indeterminacy if bulk element is SolidElement)
2466  double zeta_nodal(const unsigned &n, const unsigned &k,
2467  const unsigned &i) const
2468  {return FaceElement::zeta_nodal(n,k,i);}
2469 
2470 
2471  ///Overload the output function
2472  void output(std::ostream &outfile) {FiniteElement::output(outfile);}
2473 
2474 ///Output function: x,y,[z],u,v,[w],p in tecplot format
2475  void output(std::ostream &outfile, const unsigned &nplot)
2476  {FiniteElement::output(outfile,nplot);}
2477 
2478 };
2479 
2480 
2481 
2482 ///////////////////////////////////////////////////////////////////////
2483 ///////////////////////////////////////////////////////////////////////
2484 ///////////////////////////////////////////////////////////////////////
2485 
2486 
2487 
2488 //============================================================================
2489 /// Function that returns the residuals for the imposed traction Navier_Stokes
2490 /// equations
2491 //============================================================================
2492 template <class BULK_NAVIER_STOKES_ELEMENT,
2493  class WOMERSLEY_ELEMENT,
2494  unsigned DIM>
2495 void NavierStokesImpedanceTractionElement<BULK_NAVIER_STOKES_ELEMENT,
2496  WOMERSLEY_ELEMENT,
2497  DIM>::
2498 fill_in_generic_residual_contribution_fluid_traction(
2499  Vector<double> &residuals,
2500  DenseMatrix<double> &jacobian,
2501  unsigned flag)
2502 {
2503  //Find out how many nodes there are
2504  unsigned n_node = nnode();
2505 
2506  //Set up memory for the shape and test functions
2507  Shape psif(n_node), testf(n_node);
2508 
2509  //Set the value of n_intpt
2510  unsigned n_intpt = integral_pt()->nweight();
2511 
2512  //Integers to store local equation numbers
2513  int local_eqn=0;
2514  int local_unknown=0;
2515 
2516  //Loop over the integration points
2517  for(unsigned ipt=0;ipt<n_intpt;ipt++)
2518  {
2519  //Get the integral weight
2520  double w = integral_pt()->weight(ipt);
2521 
2522  //Find the shape and test functions and return the Jacobian
2523  //of the mapping
2524  double J = shape_and_test_at_knot(ipt,psif,testf);
2525 
2526  //Premultiply the weights and the Jacobian
2527  double W = w*J;
2528 
2529  // Traction vector
2530  Vector<double> traction(DIM+1);
2531 
2532  // Initialise response
2533  double p_in=0.0;
2534  double dp_in_dq=0.0;
2535 
2536  // Traction= outer unit normal x pressure at upstream end of
2537  // impedance tube
2538  if (Navier_stokes_outflow_mesh_pt!=0)
2539  {
2540  // Get response of the impedance tube:
2541  Impedance_tube_pt->get_response(p_in, dp_in_dq);
2542  }
2543 
2544  // Get outer unit normal at current integration point
2545  Vector<double> unit_normal(DIM+1);
2546  outer_unit_normal(ipt, unit_normal);
2547 
2548  //Loop over the directions
2549  for(unsigned i=0;i<DIM+1;i++)
2550  {
2551  traction[i]=-unit_normal[i]*p_in;
2552  }
2553 
2554 
2555  //Loop over the test functions
2556  for(unsigned l=0;l<n_node;l++)
2557  {
2558 
2559  //Loop over the velocity components
2560  for(unsigned i=0;i<DIM+1;i++)
2561  {
2562  local_eqn = u_local_eqn(l,i);
2563  /*IF it's not a boundary condition*/
2564  if(local_eqn >= 0)
2565  {
2566  //Add the user-defined traction terms
2567  residuals[local_eqn] += traction[i]*testf[l]*W;
2568 
2569  // Compute the Jacobian too?
2570  if (flag&&(Navier_stokes_outflow_mesh_pt!=0))
2571  {
2572 
2573  //Loop over the nodes
2574  for(unsigned j=0;j<n_node;j++)
2575  {
2576 
2577  // Get pointer to Node
2578  Node* nod_pt=node_pt(j);
2579 
2580  //Loop over the velocity components
2581  for(unsigned ii=0;ii<DIM+1;ii++)
2582  {
2583  local_unknown = u_local_eqn(j,ii);
2584 
2585  /*IF it's not a boundary condition*/
2586  if(local_unknown >= 0)
2587  {
2588  // Get corresponding global unknown number
2589  unsigned global_unknown=nod_pt->eqn_number(ii);
2590 
2591  // Add contribution
2592  jacobian(local_eqn,local_unknown)-=
2593  (*Aux_integral_pt)[global_unknown]
2594  *psif[l]*unit_normal[i]*dp_in_dq*W;
2595  }
2596  }
2597  }
2598 
2599 
2600  // Loop over external dofs for unknowns
2601  unsigned n_ext=nexternal_data();
2602  for (unsigned j=0;j<n_ext;j++)
2603  {
2604  // Get pointer to external Data (=other nodes)
2605  Data* ext_data_pt=external_data_pt(j);
2606 
2607  // Loop over directions for equation
2608  for (unsigned ii=0;ii<DIM+1;ii++)
2609  {
2610  // Get local unknown number
2611  int local_unknown=external_local_eqn(j,ii);
2612 
2613  // Real dof or bc?
2614  if (local_unknown>=0)
2615  {
2616  // Get corresponding global unknown number
2617  unsigned global_unknown=ext_data_pt->eqn_number(ii);
2618 
2619  // Add contribution
2620  jacobian(local_eqn,local_unknown)-=
2621  (*Aux_integral_pt)[global_unknown]
2622  *psif[l]*unit_normal[i]*dp_in_dq*W;
2623  }
2624  }
2625  }
2626  } // end of computation of Jacobian terms
2627  }
2628  } //End of loop over dimension
2629  } //End of loop over shape functions
2630 
2631  }
2632 }
2633 
2634 //////////////////////////////////////////////////////////////////////////
2635 //////////////////////////////////////////////////////////////////////////
2636 //////////////////////////////////////////////////////////////////////////
2637 
2638 
2639 //======================================================================
2640 /// An element to impose a fluid pressure obtained from a Womersley
2641 /// impedance tube at a boundary. This element is used in conjunction with a
2642 /// NetFluxControlElementForWomersleyPressureControl element, and is
2643 /// passed to the NetFluxControlElementForWomersleyPressureControl element's
2644 /// constructor. The volume flux across the boundary is then an
2645 /// unknown of the problem. The constructor argument for this element
2646 /// is a suitable Womersley impedance tube to give the pressure via
2647 /// its get_response(...) function.
2648 ///
2649 /// Note: the NavierStokesWomersleyPressureControlElement element calculates
2650 /// Jacobian entries BOTH for itself AND for the
2651 /// NetFluxControlElementForWomersleyPressureControl with respect to
2652 /// the unknowns in this (NavierStokesWomersleyPressureControlElement)
2653 /// element.
2654 //======================================================================
2656 public virtual GeneralisedElement
2657 {
2658  public :
2659 
2660  /// \short Constructor takes a pointer to a suitable Womersley
2661  /// impedance tube which defines the pressure via get_response(...)
2663  TemplateFreeWomersleyImpedanceTubeBase* womersley_tube_pt)
2664  : Womersley_tube_pt(womersley_tube_pt)
2665  {
2666  // Create the new Data which contains the volume flux.
2667  Volume_flux_data_pt = new Data(1);
2668 
2669  // Add new Data to internal data
2670  Volume_flux_data_id=add_internal_data(Volume_flux_data_pt);
2671  }
2672 
2673  /// Destructor should not delete anything
2675 
2676  /// This function returns the residuals
2678  {
2679  //Call the generic residuals function using a dummy matrix argument
2680  fill_in_generic_residual_contribution_pressure_control(
2681  residuals,
2682  GeneralisedElement::Dummy_matrix,
2683  0);
2684  }
2685 
2686  /// \short This function returns the residuals and the Jacobian,
2687  /// plus the Jacobian contribution for the
2688  /// NetFluxControlElementForWomersleyPressureControl
2689  /// with respect to unknowns in this element
2691  DenseMatrix<double> &jacobian)
2692  {
2693  //Call the generic routine
2694  fill_in_generic_residual_contribution_pressure_control(residuals,
2695  jacobian,1);
2696  }
2697 
2698  /// \short Function to return a pointer to the Data object whose
2699  /// single value is the flux degree of freedom
2700  Data* volume_flux_data_pt() const {return Volume_flux_data_pt;}
2701 
2702  /// \short Function to add to external data the Data object whose
2703  /// single value is the pressure applied at the boundary
2704  void add_pressure_data(Data* pressure_data_pt)
2705  {
2706  Pressure_data_id = add_external_data(pressure_data_pt);
2707  }
2708 
2709  /// \short The number of "DOF types" that degrees of freedom in this element
2710  /// are sub-divided into - set to 1
2711  unsigned ndof_types() const
2712  {
2713  return 1;
2714  }
2715 
2716  /// \short Create a list of pairs for all unknowns in this element,
2717  /// so that the first entry in each pair contains the global equation
2718  /// number of the unknown, while the second one contains the number
2719  /// of the "DOF type" that this unknown is associated with.
2720  /// (Function can obviously only be called if the equation numbering
2721  /// scheme has been set up.)
2723  std::list<std::pair<unsigned long, unsigned> >& dof_lookup_list) const
2724  {
2725  // pair to store dof lookup prior to being added to list
2726  std::pair<unsigned,unsigned> dof_lookup;
2727 
2728  dof_lookup.first = this->eqn_number(0);
2729  dof_lookup.second = 0;
2730 
2731  // add to list
2732  dof_lookup_list.push_front(dof_lookup);
2733  }
2734 
2735  protected:
2736 
2737  /// \short This function returns the residuals.
2738  /// flag=1(or 0): do (or don't) compute the Jacobian as well.
2739  /// Note that this function also calculates the Jacobian contribution
2740  /// for the NetFluxControlElementForWomersleyPressureControl
2742  Vector<double> &residuals,
2743  DenseMatrix<double> &jacobian,
2744  const unsigned& flag)
2745  {
2746  // Get Womersley pressure and derivative with respect to the flux
2747  double womersley_pressure=0.0;
2748  double d_womersley_pressure_d_q=0.0;
2749 
2750  // Get response of impedance tube
2751  Womersley_tube_pt->get_response(womersley_pressure,
2752  d_womersley_pressure_d_q);
2753 
2754  // Get the current pressure
2755  double pressure = external_data_pt(Pressure_data_id)->value(0);
2756 
2757  // Get equation number of the volume flux unknown
2758  int local_eq = internal_local_eqn(Volume_flux_data_id,0);
2759 
2760  // Calculate residuals
2761  residuals[local_eq] += pressure - womersley_pressure;
2762 
2763  // Calculate Jacobian contributions if required
2764  if (flag)
2765  {
2766  //Get equation number of the pressure data unknown
2767  int local_unknown = external_local_eqn(Pressure_data_id,0);
2768 
2769  //Add the Jacobian contriburions
2770  jacobian(local_eq,local_eq) -= d_womersley_pressure_d_q;
2771  jacobian(local_eq,local_unknown) += 1.0;
2772  jacobian(local_unknown,local_eq) += 1.0;
2773  }
2774  }
2775 
2776  private:
2777 
2778  /// \short Data object whose single value is the volume flux
2779  /// applied by the elements in the Flux_control_mesh_pt
2781 
2782  /// Pointer to the Womersley impedance tube
2784 
2785  /// \short Id of external Data object whose single value is the pressure
2787 
2788  /// \short Id of internal Data object whose single value is the volume
2789  /// flux
2791 };
2792 
2793 
2794 //////////////////////////////////////////////////////////////////////////
2795 //////////////////////////////////////////////////////////////////////////
2796 //////////////////////////////////////////////////////////////////////////
2797 
2798 
2799 
2800 //======================================================================
2801 /// A class for an element to control net fluid flux across a boundary
2802 /// by imposing an applied pressure to the Navier-Stokes equations.
2803 /// This element is used with a mesh of NavierStokesFluxControlElements
2804 /// attached to the boundary. The flux imposed by this element is given
2805 /// by a NavierStokesWomersleyPressureControlElement.
2806 /// Note: fill_in_contribution_to_jacobian() does not calculate any
2807 /// Jacobian contributions for this element as they are calculated by
2808 /// NavierStokesFluxControlElement::fill_in_contribution_to_jacobian(...)
2809 /// and
2810 /// NavierStokesWomersleyPressureControlElement::
2811 /// fill_in_contribution_to_jacobian(...)
2812 //======================================================================
2814 public virtual NetFluxControlElement
2815 {
2816  public:
2817 
2818  /// \short Constructor takes the mesh of
2819  /// TemplateFreeNavierStokesFluxControlElementBase which impose
2820  /// the pressure to controls the flux, plus a pointer to
2821  /// the PressureControlElement whoes internal data is the prescribed
2822  /// flux.
2824  Mesh* flux_control_mesh_pt,
2825  NavierStokesWomersleyPressureControlElement* pressure_control_element_pt)
2827  flux_control_mesh_pt,
2828  pressure_control_element_pt->volume_flux_data_pt()->value_pt(0))
2829  {
2830  // There's no need to add external data to this element since
2831  // this element's Jacobian contributions are calculated by the
2832  // NavierStokesFluxControlElements and the P
2833  // NavierStokesWomersleyPressureControlElement
2834 
2835  // Add this elements Data to the external data of the
2836  // PressureControlElement
2837  pressure_control_element_pt->add_pressure_data(pressure_data_pt());
2838  }
2839 
2840  /// Empty Destructor - Data gets deleted automatically
2842 
2843  /// Broken copy constructor
2846  NetFluxControlElement(dummy)
2847  {
2849  "NetFluxControlElementForWomersleyPressureControl");
2850  }
2851 
2852 
2853  /// Broken assignment operator
2855  {
2857  "NetFluxControlElementForWomersleyPressureControl");
2858  }
2859 
2860 
2861  /// \short The number of "DOF types" that degrees of freedom in this element
2862  /// are sub-divided into - set to 1
2863  unsigned ndof_types() const
2864  {
2865  return 1;
2866  }
2867 
2868  /// \short Create a list of pairs for all unknowns in this element,
2869  /// so that the first entry in each pair contains the global equation
2870  /// number of the unknown, while the second one contains the number
2871  /// of the "DOF type" that this unknown is associated with.
2872  /// (Function can obviously only be called if the equation numbering
2873  /// scheme has been set up.)
2875  std::list<std::pair<unsigned long, unsigned> >& dof_lookup_list) const
2876  {
2877  // pair to store dof lookup prior to being added to list
2878  std::pair<unsigned,unsigned> dof_lookup;
2879 
2880  dof_lookup.first = this->eqn_number(0);
2881  dof_lookup.second = 0;
2882 
2883  // add to list
2884  dof_lookup_list.push_front(dof_lookup);
2885  }
2886 
2887 };
2888 
2889 /////////////////////////////////////////////////////////////////////
2890 /////////////////////////////////////////////////////////////////////
2891 /////////////////////////////////////////////////////////////////////
2892 
2893 }
2894 
2895 #endif
2896 
2897 
2898 
NavierStokesWomersleyPressureControlElement(TemplateFreeWomersleyImpedanceTubeBase *womersley_tube_pt)
Constructor takes a pointer to a suitable Womersley impedance tube which defines the pressure via get...
A Generalised Element class.
Definition: elements.h:76
static const unsigned Initial_Nvalue
Static array of ints to hold number of variables at nodes: Initial_Nvalue[n].
void steady_newton_solve(unsigned const &max_adapt=0)
Solve a steady problem using adaptive Newton&#39;s method, but in the context of an overall unstady probl...
Definition: problem.cc:9236
void unpin(const unsigned &i)
Unpin the i-th stored variable.
Definition: nodes.h:386
void broken_copy(const std::string &class_name)
Issue error message and terminate execution.
void set_impedance_tube_pt(TemplateFreeWomersleyImpedanceTubeBase *impedance_tube_pt)
Set pointer to "impedance tube" that provides the flow resistance.
FaceGeometry()
Constructor: Call the constructor for the appropriate lower-dimensional QElement. ...
void output_fct(std::ostream &outfile, const unsigned &n_plot, const double &time, FiniteElement::UnsteadyExactSolutionFctPt exact_soln_pt)
Output function for a time-dependent exact solution. x,y,u_exact or x,y,z,u_exact at n_plot^DIM plot ...
double *& re_st_pt()
Pointer to product of Reynolds and Strouhal number (=Womersley number)
void setup()
Set up the Womersley tubes so that a subsequent call to get_response(...) computes the inlet pressure...
NetFluxControlElementForWomersleyPressureControl(Mesh *flux_control_mesh_pt, NavierStokesWomersleyPressureControlElement *pressure_control_element_pt)
Constructor takes the mesh of TemplateFreeNavierStokesFluxControlElementBase which impose the pressur...
WomersleyProblem< ELEMENT, DIM > * womersley_problem_pt()
Access to underlying Womersley problem.
double * Current_volume_flux_pt
Pointer to double that specifies the currently imposed instantaneous volume flux into the impedance t...
virtual ~NavierStokesImpedanceTractionElementBase()
Empty vitual destructor.
void operator=(const NetFluxControlElementForWomersleyPressureControl &)
Broken assignment operator.
unsigned W_index
The velocity component (in terms of the nodal index) that represents the outflow component – the lat...
unsigned ndof_types() const
The number of "DOF types" that degrees of freedom in this element are sub-divided into - set to 1...
Information for documentation of results: Directory and file number to enable output in the form RESL...
const double & re_st() const
Product of Reynolds and Strouhal number (=Womersley number)
std::string directory() const
Output directory.
cstr elem_len * i
Definition: cfortran.h:607
Data * set_pressure_gradient_pt() const
Read-only access to pointer to pressure gradient.
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Fill in the element&#39;s contribution to the element&#39;s residual vector and Jacobian matrix.
unsigned ndof_types() const
The number of "DOF types" that degrees of freedom in this element are sub-divided into - set to 1...
void output_fct(std::ostream &outfile, const unsigned &nplot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
Output exact soln: r,z,u_exact at nplot^2 plot points.
double interpolated_u_womersley(const Vector< double > &s) const
Return FE representation of function value u(s) at local coordinate s.
double du_dt_womersley(const unsigned &n) const
du/dt at local node n. Uses suitably interpolated value for hanging nodes.
NavierStokesImpedanceTractionElement(FiniteElement *const &element_pt, const int &face_index)
WomersleyMesh(Mesh *n_st_outflow_mesh_pt, TimeStepper *time_stepper_pt, const unsigned &fixed_coordinate, const unsigned &w_index)
Constructor: Pass pointer to mesh of face elements in the outflow cross-section of a full Navier-Stok...
The Problem class.
Definition: problem.h:152
void get_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Compute element residual Vector and element Jacobian matrix Note: Jacobian is zero because the deriva...
ImposeFluxForWomersleyElement< DIM > * Flux_el_pt
Pointer to element that imposes the flux through the collection of Womersley elements.
double Dp_in_dq
Derivative of inflow pressure w.r.t. instantaenous volume flux (Note: Can be pre-computed) ...
A general Finite Element class.
Definition: elements.h:1274
Mesh * Womersley_mesh_pt
Pointer to mesh that contains the Womersley elements.
WomersleyProblem(double *re_st_pt, double *prescribed_volume_flux_pt, TimeStepper *time_stepper_pt, Mesh *womersley_mesh_pt)
Constructor: Pass pointer to Womersley number, pointer to the double that stores the currently impose...
void set_pressure_gradient_pt(Data *&pressure_gradient_data_pt)
Set pointer to pressure gradient (single-valued Data)
void(* UnsteadyExactSolutionFctPt)(const double &, const Vector< double > &, Vector< double > &)
Function pointer for function that computes Vector-valued time-dependent function as ...
Definition: elements.h:1729
char t
Definition: cfortran.h:572
void get_flux(const Vector< double > &s, Vector< double > &flux) const
Get flux: flux[i] = du/dx_i.
WomersleyEquations()
Constructor: Initialises the Pressure_gradient_data_pt to null.
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
Templated class for BDF-type time-steppers with fixed or variable timestep. 1st time derivative recov...
void actions_before_newton_solve()
Update the problem specs before solve (empty)
double total_volume_flux()
Get volume flux through all Womersley elements.
Data * Volume_flux_data_pt
Data object whose single value is the volume flux applied by the elements in the Flux_control_mesh_pt...
OomphInfo oomph_info
bool is_hanging() const
Test whether the node is geometrically hanging.
Definition: nodes.h:1207
long & eqn_number(const unsigned &i)
Return the equation number of the i-th stored variable.
Definition: nodes.h:365
WomersleyImpedanceTubeBase(const double &length, PrescribedVolumeFluxFctPt prescribed_volume_flux_fct_pt)
Constructor: Specify length of tube and pointer to function that specifies the prescribed volume flux...
PrescribedVolumeFluxFctPt Prescribed_volume_flux_fct_pt
Pointer to function that specifies the prescribed volume flux.
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:448
double shape_and_test_at_knot(const unsigned &ipt, Shape &psi, Shape &test) const
Function to compute the shape and test functions and to return the Jacobian of mapping.
WomersleyEquations(const WomersleyEquations &dummy)
Broken copy constructor.
std::map< unsigned, double > * Aux_integral_pt
Pointer to auxiliary integral, containing the derivative of the total volume flux through the outflow...
unsigned Fixed_coordinate
The coordinate (in the higher-dimensional Navier-Stokes mesh) that is constant in the outflow cross-s...
e
Definition: cfortran.h:575
~NetFluxControlElementForWomersleyPressureControl()
Empty Destructor - Data gets deleted automatically.
WomersleyImpedanceTubeBase< WOMERSLEY_ELEMENT, DIM > * Impedance_tube_pt
Pointer to ImpedanceTubeProblem that computes the flow resistance.
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Compute element residual Vector (wrapper)
void pin(const unsigned &i)
Pin the i-th stored variable.
Definition: nodes.h:383
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Compute element residual Vector and element Jacobian matrix (wrapper)
WomersleyOutflowImpedanceTube(const double &length, Mesh *navier_stokes_outflow_mesh_pt, const unsigned &fixed_coordinate, const unsigned &w_index)
Constructor: Pass length and mesh of face elements that are attached to the outflow cross-section of ...
void add_element_contribution_to_aux_integral(std::map< unsigned, double > *aux_integral_pt)
void doc_solution(DocInfo &doc_info, const double &z_out=0.0)
Doc the solution.
double get_volume_flux()
Compute total volume flux through element.
unsigned & number()
Number used (e.g.) for labeling output files.
void disable_info_in_newton_solve()
Disable the output of information when in the newton solver.
Definition: problem.h:2819
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Fill in the element&#39;s contribution to the element&#39;s residual vector.
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:587
void output(FILE *file_pt)
C_style output with default number of plot points.
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:992
QWomersleyElement(const QWomersleyElement< DIM, NNODE_1D > &dummy)
Broken copy constructor.
Element to impose volume flux through collection of Womersley elements, in exchange for treating the ...
void setup()
Setup terminate helper.
QWomersleyElement()
Constructor: Call constructors for QElement and Womersley equations.
TemplateFreeWomersleyImpedanceTubeBase * Womersley_tube_pt
Pointer to the Womersley impedance tube.
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
void(* SteadyExactSolutionFctPt)(const Vector< double > &, Vector< double > &)
Function pointer for function that computes vector-valued steady "exact solution" as ...
Definition: elements.h:1723
void precompute_aux_integrals()
Precompute auxiliary integrals required for the computation of the Jacobian in the NavierStokesImpeda...
double Zero
Static variable to hold the default value for the pseudo-solid&#39;s inertia parameter Lambda^2...
Mesh * Navier_stokes_outflow_mesh_pt
Pointer to the mesh of NavierStokesImpedanceTractionElements that are attached to the outflow cross-s...
void fill_in_contribution_to_residuals(Vector< double > &residuals)
This function returns the residuals.
unsigned self_test()
Self-test: Return 0 for OK.
GeneralisedElement *& element_pt(const unsigned long &e)
Return pointer to element e.
Definition: mesh.h:462
void set_aux_integral_pt(std::map< unsigned, double > *aux_integral_pt)
Set pointer to the precomputed auxiliary integral that contains the derivative of the total volume fl...
double * ReSt_pt
Pointer to global Reynolds number x Strouhal number (=Womersley)
void shift_time_values(const double &dt)
Shift history values to allow coputation of next timestep. Note: When used with a full Navier-Stokes ...
void set_pressure_gradient_and_add_as_external_data(Data *pressure_gradient_data_pt)
Set pointer to pressure gradient (single-valued Data) and treat it as external data – this can only ...
void get_response(double &p_in, double &dp_in_dq)
Compute inlet pressure, p_in, required to achieve the currently imposed, instantaneous volume flux q ...
Mesh *& navier_stokes_outflow_mesh_pt()
Access to mesh containing all NavierStokesImpedanceTractionElements that contribute to the volume flu...
Data * pressure_gradient_data_pt()
Access function to the single-valued Data object that contains the unknown pressure gradient (used if...
static char t char * s
Definition: cfortran.h:572
void output(std::ostream &outfile)
Output function: x,y,u or x,y,z,u.
double get_volume_flux()
Get integral of volume flux through element.
ImposeFluxForWomersleyElement(Mesh *womersley_mesh_pt, double *prescribed_flux_pt)
Constructor: Pass pointer to mesh that contains the Womersley elements whose volume flux is controlle...
double total_volume_flux_into_downstream_tube()
Compute total volume flux into the "downstream tube" that provides the impedance (computed by adding ...
virtual int u_local_eqn(const unsigned &n, const unsigned &i)
Access function that returns the local equation numbers for velocity components. u_local_eqn(n,i) = local equation number or < 0 if pinned. The default is to asssume that n is the local node number and the i-th velocity component is the i-th unknown stored at the node.
void output_fct(std::ostream &outfile, const unsigned &n_plot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
Output function for an exact solution: x,y,u_exact or x,y,z,u_exact at n_plot^DIM plot points...
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 setup(double *re_st_pt, const double &dt, const double &q_initial, TimeStepper *time_stepper_pt=0)
Set up the Womersley tubes so that a subsequent call to get_response(...) computes the inlet pressure...
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...
A class that represents a collection of data; each Data object may contain many different individual ...
Definition: nodes.h:89
void output_fct(std::ostream &outfile, const unsigned &nplot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
Output exact soln: x,y,u_exact or x,y,z,u_exact at nplot^DIM plot points.
Mesh * Navier_stokes_outflow_mesh_pt
Pointer to mesh containing the NavierStokesImpedanceTractionElements that contribute to the volume fl...
unsigned Volume_flux_data_id
Id of internal Data object whose single value is the volume flux.
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
void get_residuals(Vector< double > &residuals)
Compute residual vector: the volume flux constraint determines this element&#39;s one-and-only internal D...
void shape(const double &s, double *Psi)
Definition for 1D Lagrange shape functions. The value of all the shape functions at the local coordin...
Definition: shape.h:549
void output(std::ostream &outfile)
Output with default number of plot points.
void add_pressure_data(Data *pressure_data_pt)
Function to add to external data the Data object whose single value is the pressure applied at the bo...
Data * volume_flux_data_pt() const
Function to return a pointer to the Data object whose single value is the flux degree of freedom...
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Definition: timesteppers.h:562
virtual void set_impedance_tube_pt(TemplateFreeWomersleyImpedanceTubeBase *impedance_tube_pt)=0
Pass the pointer to the "impedance tube" that computes the flow resistance via the solution of Womers...
double & p_out()
Access fct to outlet pressure.
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
This function returns the residuals and the Jacobian, plus the Jacobian contribution for the NetFluxC...
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:477
virtual ~TemplateFreeWomersleyImpedanceTubeBase()
Empty virtual destructor.
void broken_assign(const std::string &class_name)
Issue error message and terminate execution.
void compute_error(std::ostream &outfile, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt, double &error, double &norm)
Get error against and norm of exact solution.
void actions_after_newton_solve()
Update the problem specs after solve (empty)
double * Prescribed_volume_flux_pt
Pointer to currently prescribed volume flux.
void output(std::ostream &outfile)
Output with default number of plot points.
virtual unsigned u_index_womersley() const
Return the index at which the unknown value is stored. The default value, 0, is appropriate for singl...
virtual void get_response(double &p_in, double &dp_in_dq)=0
Empty virtual dummy member function – every base class needs at least one virtual member function if...
~NavierStokesWomersleyPressureControlElement()
Destructor should not delete anything.
FaceGeometry()
Constructor: Call the constructor for the appropriate lower-dimensional QElement. ...
Data * Pressure_gradient_data_pt
Pointer to single-valued Data item that stores pressure gradient.
void fill_in_generic_residual_contribution_pressure_control(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
This function returns the residuals. flag=1(or 0): do (or don&#39;t) compute the Jacobian as well...
void output(std::ostream &outfile, const unsigned &nplot)
Output function: x,y,[z],u,v,[w],p in tecplot format.
double * Prescribed_flux_pt
Pointer to current value of prescribed flux.
virtual void set_aux_integral_pt(std::map< unsigned, double > *aux_integral_pt)=0
Pass the pointer to the pre-computed auxiliary integral to the element so it can be accessed when com...
virtual void build_face_element(const int &face_index, FaceElement *face_element_pt)
Function for building a lower dimensional FaceElement on the specified face of the FiniteElement...
Definition: elements.cc:5002
virtual void set_external_data_from_navier_stokes_outflow_mesh(Mesh *navier_stokes_outflow_mesh_pt_mesh_pt)=0
Pass the pointer to the mesh containing all NavierStokesImpedanceTractionElements that contribute to ...
Data * pressure_gradient_data_pt()
Read-only access to the single-valued Data item that stores the pressure gradient (to be determined v...
WomersleyProblem< ELEMENT, DIM > * Womersley_problem_pt
Pointer to Womersley problem that determines the pressure gradient along the tube.
bool is_steady() const
Flag to indicate if a timestepper has been made steady (possibly temporarily to switch off time-depen...
Definition: timesteppers.h:375
unsigned required_nvalue(const unsigned &n) const
Required # of `values&#39; (pinned or dofs) at node n.
A vector in the mathematical sense, initially developed for linear algebra type applications. If MPI then this vector can be distributed - its distribution is described by the LinearAlgebraDistribution object at Distribution_pt. Data is stored in a C-style pointer vector (double*)
Definition: double_vector.h:61
void set_external_data_from_navier_stokes_outflow_mesh(Mesh *navier_stokes_outflow_mesh_pt)
NavierStokesImpedanceTractionElements that contribute to the volume flux into the downstream "impedan...
void actions_before_implicit_timestep()
Update the problem specs before next timestep: Update time-varying pressure gradient (if prescribed) ...
unsigned Pressure_data_id
Id of external Data object whose single value is the pressure.
void output(FILE *file_pt)
C-style output function: x,y,u or x,y,z,u.
WomersleyImpedanceTubeBase(const double &length, Mesh *navier_stokes_outflow_mesh_pt)
Constructor: Specify length of tube and the pointer to the mesh of either NavierStokesImpedanceTracti...
void output(std::ostream &outfile, const unsigned &n_plot)
Output function: x,y,u or x,y,z,u at n_plot^DIM plot points.
double total_volume_flux_into_impedance_tube()
Compute total current volume flux into the "impedance tube" that provides the flow resistance (flux i...
PrescribedPressureGradientFctPt Prescribed_pressure_gradient_fct_pt
Fct pointer to fct that prescribes pressure gradient.
void operator=(const QWomersleyElement< DIM, NNODE_1D > &)
Broken assignment operator.
double zeta_nodal(const unsigned &n, const unsigned &k, const unsigned &i) const
The "global" intrinsic coordinate of the element when viewed as part of a geometric object should be ...
Data * Pressure_gradient_data_pt
Pointer to pressure gradient Data (single value Data item)
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
Definition: timesteppers.h:219
virtual double weight(const unsigned &i, const unsigned &j) const
Access function for j-th weight for the i-th derivative.
Definition: timesteppers.h:557
virtual void add_element_contribution_to_aux_integral(std::map< unsigned, double > *aux_integral_pt)=0
Add the element&#39;s contribution to the auxiliary integral used in the element&#39;s Jacobian. The aux_integral contains the derivative of the total volume flux through the outflow boundary of the (higher-dimensional) Navier-Stokes mesh w.r.t. to the discrete (global) (velocity) degrees of freedom.
~NavierStokesImpedanceTractionElement()
Destructor should not delete anything.
static bool Suppress_warning_about_unpinned_nst_dofs
Static bool to suppress warning.
A general mesh class.
Definition: mesh.h:74
std::map< unsigned, double > * Aux_integral_pt
Pointer to auxiliary integral, containing the derivative of the total volume flux through the outflow...
Mesh * build_mesh_and_apply_boundary_conditions(TimeStepper *time_stepper_pt)
Implement pure virtual fct (defined in the base class WomersleyImpedanceTubeBase) that builds the mes...
static DenseMatrix< double > Dummy_matrix
Empty dense matrix used as a dummy argument to combined residual and jacobian functions in the case w...
Definition: elements.h:228
NetFluxControlElementForWomersleyPressureControl(const NetFluxControlElementForWomersleyPressureControl &dummy)
Broken copy constructor.
void output(FILE *file_pt, const unsigned &n_plot)
C-style output function: x,y,u or x,y,z,u at n_plot^DIM plot points.
void operator=(const WomersleyEquations &)
Broken assignment operator.
double Length
Length of the tube.
Data * Pressure_gradient_data_pt
Data item whose one and only value contains the pressure gradient.
static double Default_ReSt_value
Static default value for the Womersley number.
void output(std::ostream &outfile)
Overload the output function.