fsi_channel_with_leaflet_precond.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 
31 // Generic oomph-lib includes
32 #include "generic.h"
33 #include "beam.h"
34 #include "navier_stokes.h"
35 #include "multi_physics.h"
36 #include "constitutive.h"
37 #include "solid.h"
38 
39 // The meshes
40 #include "meshes/channel_with_leaflet_mesh.h"
41 #include "meshes/one_d_lagrangian_mesh.h"
42 
43 using namespace std;
44 using namespace oomph;
45 
46 
47 /////////////////////////////////////////////////////////////////////
48 /////////////////////////////////////////////////////////////////////
49 /////////////////////////////////////////////////////////////////////
50 
51 namespace oomph {
52 
53 //============================================================================
54 /// Pseudo-Elastic Solid element class to overload the Block Preconditioner
55 /// methods ndof_types() and get_dof_numbers_for_unknowns() to differentiate
56 /// between DOFs subject to Lagrange multiplier and those that are not.
57 //============================================================================
58 template <class ELEMENT>
60  public virtual ELEMENT
61 {
62 
63 public:
64 
65  /// default constructor
66  PseudoElasticBulkElement() : ELEMENT() {}
67 
68  /// \short Return the number of DOF types associated with this element.
69  unsigned ndof_types() const
70  {
71  return 2*ELEMENT::dim();
72  }
73 
74  /// \short Create a list of pairs for all unknowns in this element,
75  /// so that the first entry in each pair contains the global equation
76  /// number of the unknown, while the second one contains the number
77  /// of the "DOF" that this unknown is associated with.
78  /// (Function can obviously only be called if the equation numbering
79  /// scheme has been set up.)\n
80  /// E.g. in a 3D problem there are 6 types of DOF:\n
81  /// 0 - x displacement (without lagr mult traction)\n
82  /// 1 - y displacement (without lagr mult traction)\n
83  /// 2 - z displacement (without lagr mult traction)\n
84  /// 4 - x displacement (with lagr mult traction)\n
85  /// 5 - y displacement (with lagr mult traction)\n
86  /// 6 - z displacement (with lagr mult traction)\n
88  std::list<std::pair<unsigned long,unsigned> >& dof_lookup_list) const
89  {
90  // temporary pair (used to store dof lookup prior to being added to list
91  std::pair<unsigned,unsigned> dof_lookup;
92 
93  // number of nodes
94  const unsigned n_node = this->nnode();
95 
96  //Get the number of position dofs and dimensions at the node
97  const unsigned n_position_type = ELEMENT::nnodal_position_type();
98  const unsigned nodal_dim = ELEMENT::nodal_dimension();
99 
100  //Integer storage for local unknown
101  int local_unknown=0;
102 
103  //Loop over the nodes
104  for(unsigned n=0;n<n_node;n++)
105  {
106  unsigned offset = 0;
107  if (this->node_pt(n)->nvalue() != this->required_nvalue(n))
108  {
109  offset = ELEMENT::dim();
110  }
111 
112  //Loop over position dofs
113  for(unsigned k=0;k<n_position_type;k++)
114  {
115  //Loop over dimension
116  for(unsigned i=0;i<nodal_dim;i++)
117  {
118  //If the variable is free
119  local_unknown = ELEMENT::position_local_eqn(n,k,i);
120 
121  // ignore pinned values
122  if (local_unknown >= 0)
123  {
124  // store dof lookup in temporary pair: First entry in pair
125  // is global equation number; second entry is dof type
126  dof_lookup.first = this->eqn_number(local_unknown);
127  dof_lookup.second = offset+i;
128 
129  // add to list
130  dof_lookup_list.push_front(dof_lookup);
131 
132  }
133  }
134  }
135  }
136  }
137 };
138 
139 
140 //===========start_face_geometry==============================================
141 /// FaceGeometry of wrapped element is the same as the underlying element
142 //============================================================================
143 template<class ELEMENT>
144 class FaceGeometry<PseudoElasticBulkElement<ELEMENT> > :
145  public virtual FaceGeometry<ELEMENT>
146 {
147 };
148 
149 
150 }
151 
152 
153 ///////////////////////////////////////////////////////////////////////
154 ///////////////////////////////////////////////////////////////////////
155 ///////////////////////////////////////////////////////////////////////
156 
157 
158 
159 #ifdef OOMPH_HAS_HYPRE
160 
161 //=========================================================================
162 /// Namespace for Navier Stokes LSC Preconditioner
163 //=========================================================================
165 {
166 
167  /// \short Create instance of Hypre preconditioner with settings that are
168  /// appropriate for serial solution of Navier-Stokes momentum block
169  Preconditioner* set_hypre_preconditioner()
170  {
171  HyprePreconditioner* hypre_preconditioner_pt
172  = new HyprePreconditioner;
173  hypre_preconditioner_pt->set_amg_iterations(2);
174  hypre_preconditioner_pt->amg_using_simple_smoothing();
175  hypre_preconditioner_pt->amg_simple_smoother() = 0;
176  hypre_preconditioner_pt->hypre_method() = HyprePreconditioner::BoomerAMG;
177  hypre_preconditioner_pt->amg_strength() = 0.25;
178  hypre_preconditioner_pt->amg_coarsening() = 3;
179  hypre_preconditioner_pt->amg_damping() = 2.0/3.0;
180  return hypre_preconditioner_pt;
181  }
182 }
183 
184 #endif
185 
186 /////////////////////////////////////////////////////////////////////
187 /////////////////////////////////////////////////////////////////////
188 /////////////////////////////////////////////////////////////////////
189 
190 
191 
192 
193 //==========================================================================
194 /// Global parameters
195 //==========================================================================
197 {
198  /// x-position of root of leaflet
199  double Leaflet_x0 = 1.0;
200 
201  /// height of leaflet
202  double Leaflet_height=0.5;
203 
204  /// length of fluid mesh to left of leaflet
205  double Fluid_length_left=1.0;
206 
207  /// length of fluid mesh to right of leaflet
208  double Fluid_length_right=3.0;
209 
210  /// height of fluid mesh
211  double Fluid_height=1.0;
212 
213  /// Num elements to left of leaflet in coarse mesh
214  unsigned Mesh_nleft=4;
215 
216  /// Num elements to right of leaflet in coarse mesh
217  unsigned Mesh_nright=12;
218 
219  /// Num elements in fluid mesh in y dirn adjacent to leaflet
220  unsigned Mesh_ny1=2;
221 
222  /// Num elements in fluid mesh in y dirn above leaflet
223  unsigned Mesh_ny2=2;
224 
225  /// Reynolds number
226  double Re=50.0;
227 
228  /// Womersley number: Product of Reynolds and Strouhal numbers
229  double ReSt=50.0;
230 
231  /// Non-dimensional wall thickness.
232  double H=0.05;
233 
234  /// \short Fluid structure interaction parameter: Ratio of stresses used
235  /// for non-dimensionalisation of fluid to solid stresses.
236  double Q=2.0e-7;
237 
238  /// Period for fluctuations in flux
239  double T=1.0;
240 
241  /// Min. flux
242  double U_base=1.0;
243 
244  /// Max. flux
245  double U_perturbation=0.5;
246 
247  /// \short Flux: Pulsatile flow
248  double flux(const double& t)
249  {
250  return U_base+U_perturbation*cos(2.0*MathematicalConstants::Pi*t/T);
251  }
252 
253  /// Pseudo-solid mass density
254  double Lambda_sq=0.0;
255 
256  /// Beam mass density
257  double Lambda_sq_beam=0.0;
258 
259  /// Pseudo-solid Poisson ratio
260  double Nu=0.1;
261 
262  /// Timestep for simulation: 40 steps per period
263  double Dt = T/40.0;
264 
265 } // end_of_namespace
266 
267 
268 ////////////////////////////////////////////////////////////////////////////
269 ////////////////////////////////////////////////////////////////////////////
270 ////////////////////////////////////////////////////////////////////////////
271 
272 
273 //==========================================================================
274 ///GeomObject: Undeformed straight, vertical leaflet
275 //==========================================================================
276 class UndeformedLeaflet : public GeomObject
277 {
278 
279 public:
280 
281  /// Constructor: argument is the x-coordinate of the leaflet
282  UndeformedLeaflet(const double& x0): GeomObject(1,2)
283  {
284  X0=x0;
285  }
286 
287  /// \short Position vector at Lagrangian coordinate zeta
288  void position(const Vector<double>& zeta, Vector<double>& r) const
289  {
290  // Position Vector
291  r[0] = X0;
292  r[1] = zeta[0];
293  }
294 
295 
296  /// \short Parametrised position on object: r(zeta). Evaluated at
297  /// previous timestep. t=0: current time; t>0: previous
298  /// timestep. Calls steady version.
299  void position(const unsigned& t, const Vector<double>& zeta,
300  Vector<double>& r) const
301  {
302  // Use the steady version
303  position(zeta,r);
304  } // end of position
305 
306 
307  /// \short Posn vector and its 1st & 2nd derivatives
308  /// w.r.t. to coordinates:
309  /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
310  /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ =
311  /// ddrdzeta(alpha,beta,i). Evaluated at current time.
312  void d2position(const Vector<double>& zeta,
313  Vector<double>& r,
314  DenseMatrix<double> &drdzeta,
315  RankThreeTensor<double> &ddrdzeta) const
316  {
317  // Position vector
318  r[0] = X0;
319  r[1] = zeta[0];
320 
321  // Tangent vector
322  drdzeta(0,0)=0.0;
323  drdzeta(0,1)=1.0;
324 
325  // Derivative of tangent vector
326  ddrdzeta(0,0,0)=0.0;
327  ddrdzeta(0,0,1)=0.0;
328  } // end of d2position
329 
330  /// Number of geometric Data in GeomObject: None.
331  unsigned ngeom_data() const {return 0;}
332 
333 private :
334 
335  /// x position of the undeformed leaflet's origin.
336  double X0;
337 
338 }; //end_of_undeformed_wall
339 
340 
341 ////////////////////////////////////////////////////////////////////////////
342 ////////////////////////////////////////////////////////////////////////////
343 ////////////////////////////////////////////////////////////////////////////
344 
345 
346 //==========================================================================
347 /// FSI leaflet in channel. Mesh update with pseudo-elasticity and
348 /// solved with pseudo-elastic fsi preconditioner.
349 //==========================================================================
350 template<class ELEMENT>
351 class FSIChannelWithLeafletProblem : public Problem
352 {
353 
354 public:
355 
356  /// \short Constructor: Pass multiplier for uniform mesh refinement
357  FSIChannelWithLeafletProblem(const unsigned& mesh_multiplier);
358 
359  /// Destructor empty
361  {
362  delete Bulk_mesh_pt;
363  delete Lagrange_multiplier_mesh_pt;
364  delete Wall_mesh_pt;
365  delete Bulk_time_stepper_pt;
366  delete Wall_time_stepper_pt;
367  delete Wall_geom_object_pt;
368  delete Undeformed_wall_pt;
369  delete Constitutive_law_pt;
370  }
371 
372  /// Actions after solve (empty)
374 
375  /// \short Actions before Newton solve:
376  /// Reset the pseudo-elastic undeformed configuration
378  {
379  // Reset undeformed configuration for pseudo-solid
380  Bulk_mesh_pt->set_lagrangian_nodal_coordinates();
381  }
382 
383  /// \short Update no slip before Newton convergence check
385  {
386  // Loop over the nodes to perform auxiliary node update (no slip)
387  unsigned nnod=Bulk_mesh_pt->nnode();
388  for (unsigned j=0;j<nnod;j++)
389  {
390  Bulk_mesh_pt->node_pt(j)->perform_auxiliary_node_update_fct();
391  }
392 
393  }
394 
395  /// Actions before implicit timestep: Update the inflow velocity
397  {
398  // Actual time
399  double t=time_pt()->time();
400 
401  // Amplitude of flow
402  double ampl=Global_Parameters::flux(t);
403 
404  // Update parabolic flow along boundary 3
405  unsigned ibound=3;
406  unsigned num_nod= Bulk_mesh_pt->nboundary_node(ibound);
407  for (unsigned inod=0;inod<num_nod;inod++)
408  {
409  double ycoord = Bulk_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
410  double uy = ampl*4.0*ycoord/Global_Parameters::Fluid_height*
411  (1.0-ycoord/Global_Parameters::Fluid_height);
412  Bulk_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,uy);
413  Bulk_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
414  }
415  } // end of actions_before_implicit_timestep
416 
417  /// Set iterative solver
418  void set_iterative_solver();
419 
420  /// Doc the solution
421  void doc_solution(DocInfo& doc_info);
422 
423  /// \short Create elements that enforce prescribed boundary motion
424  /// by Lagrange multipliers
425  void create_lagrange_multiplier_elements();
426 
427  /// Delete elements that enforce prescribed boundary motion
428  /// by Lagrange multipliers
429  void delete_lagrange_multiplier_elements();
430 
431  /// Doc parameters
433  {
434  oomph_info << "\n\n=================================================\n";
435  oomph_info << "Q : " << Global_Parameters::Q
436  << std::endl;
437  oomph_info << "Re : " << Global_Parameters::Re
438  << std::endl;
439  oomph_info << "Lambda_sq_beam : " << Global_Parameters::Lambda_sq_beam
440  << std::endl;
441  oomph_info << "T : " << Global_Parameters::T
442  <<std::endl;
443  oomph_info << "t : " << time_pt()->time()
444  << std::endl;
445  oomph_info << "tip x : "
446  << tip_node_pt()->x(0) << std::endl;
447  oomph_info << "tip y : "
448  << tip_node_pt()->x(1) << std::endl;
449  oomph_info << "=================================================\n\n";
450  }
451 
452 private:
453 
454  /// Helper fct; returns the node at the tip of the wall mesh
455  Node* tip_node_pt()
456  {
457  unsigned n_el_wall=Wall_mesh_pt->nelement();
458  return Wall_mesh_pt->finite_element_pt(n_el_wall-1)->node_pt(1);
459  }
460 
461 
462  /// Pointer to the fluid mesh
463  PseudoElasticChannelWithLeafletMesh<ELEMENT>* Bulk_mesh_pt;
464 
465  /// Pointer to the "wall" mesh
466  OneDLagrangianMesh<FSIHermiteBeamElement>* Wall_mesh_pt;
467 
468  /// Bulk timestepper
470 
471  /// Wall time stepper pt
472  Newmark<2>* Wall_time_stepper_pt;
473 
474  /// Pointers to mesh of Lagrange multiplier elements
476 
477  /// Constitutive law used to determine the mesh deformation
478  ConstitutiveLaw *Constitutive_law_pt;
479 
480  /// Geometric object for the leaflet (to apply lagrange mult)
481  MeshAsGeomObject* Wall_geom_object_pt;
482 
483  /// Geom object for the leaflet
485 
486 };
487 
488 
489 //==========================================================================
490 /// Constructor
491 //==========================================================================
492 template <class ELEMENT>
494 (const unsigned& mesh_multiplier)
495 {
496 
497  // Allocate the timesteppers
498  Bulk_time_stepper_pt=new BDF<2>;
499  add_time_stepper_pt(Bulk_time_stepper_pt);
500  Wall_time_stepper_pt=new Newmark<2>;
501  add_time_stepper_pt(Wall_time_stepper_pt);
502 
503  // Wall mesh
504  //----------
505 
506  // Geometric object that represents the undeformed leaflet
507  Undeformed_wall_pt=new UndeformedLeaflet(Global_Parameters::Leaflet_x0);
508 
509  //Create the "wall" mesh with FSI Hermite beam elements
510  unsigned n_wall_el=Global_Parameters::Mesh_ny1*mesh_multiplier;
511  Wall_mesh_pt = new OneDLagrangianMesh<FSIHermiteBeamElement>
513  Undeformed_wall_pt,Wall_time_stepper_pt);
514 
515 
516  // Fluid mesh
517  // ----------
518 
519  // Build a geometric object from the wall mesh
520  Wall_geom_object_pt=new MeshAsGeomObject(Wall_mesh_pt);
521 
522  //Build the fluid mesh
523  Bulk_mesh_pt =new PseudoElasticChannelWithLeafletMesh<ELEMENT>(
524  Wall_geom_object_pt,
529  Global_Parameters::Mesh_nleft*mesh_multiplier,
530  Global_Parameters::Mesh_nright*mesh_multiplier,
531  Global_Parameters::Mesh_ny1*mesh_multiplier,
532  Global_Parameters::Mesh_ny2*mesh_multiplier,
533  Bulk_time_stepper_pt);
534 
535 
536  // Construct the mesh of elements that enforce prescribed boundary motion
537  //-----------------------------------------------------------------------
538  // by Lagrange multipliers
539  //------------------------
540  Lagrange_multiplier_mesh_pt=new SolidMesh;
541  create_lagrange_multiplier_elements();
542 
543 
544  // Build global mesh
545  //------------------
546  add_sub_mesh(Bulk_mesh_pt);
547  add_sub_mesh(Lagrange_multiplier_mesh_pt);
548  add_sub_mesh(Wall_mesh_pt);
549  build_global_mesh();
550 
551 
552 
553  // Fluid boundary conditions
554  //--------------------------
555 
556  // Apply no-slip condition on all boundary nodes of the fluid mesh
557  unsigned num_bound = Bulk_mesh_pt->nboundary();
558  for(unsigned ibound=0;ibound<num_bound;ibound++)
559  {
560  unsigned num_nod= Bulk_mesh_pt->nboundary_node(ibound);
561  for (unsigned inod=0;inod<num_nod;inod++)
562  {
563  Bulk_mesh_pt->boundary_node_pt(ibound,inod)->pin(1);
564 
565  // Do not pin the x velocity of the outflow
566  if( ibound != 1)
567  {
568  Bulk_mesh_pt->boundary_node_pt(ibound,inod)->pin(0);
569  }
570  }
571  }
572 
573  // Pin the nodal position on all boundaries apart from
574  // the moving wall
575  for (unsigned ibound=0;ibound<7;ibound++)
576  {
577  if (ibound==0||ibound==1||ibound==2||ibound==3||ibound==6)
578  {
579  unsigned num_nod=Bulk_mesh_pt->nboundary_node(ibound);
580  for (unsigned inod=0;inod<num_nod;inod++)
581  {
582  for(unsigned i=0;i<2;i++)
583  {
584  if (!( (ibound==2)&&(i==0) ))
585  {
586  dynamic_cast<SolidNode*>(Bulk_mesh_pt->
587  boundary_node_pt(ibound, inod))
588  ->pin_position(i);
589  }
590  }
591  }
592  }
593  }
594 
595  // Setup parabolic flow along boundary 3 (everything else that's
596  // pinned has homogeneous boundary conditions so no action is required
597  // as that's the default assignment).
598  unsigned ibound=3;
599  unsigned num_nod= Bulk_mesh_pt->nboundary_node(ibound);
600  for (unsigned inod=0;inod<num_nod;inod++)
601  {
602  double ycoord = Bulk_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
603  double uy = 4.0*ycoord/Global_Parameters::Fluid_height*
604  (1.0-ycoord/Global_Parameters::Fluid_height);
605  Bulk_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,uy);
606  Bulk_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
607  }
608 
609  // Boundary conditions for wall mesh
610  // ---------------------------------
611 
612  // Set the boundary conditions: the lower end of the beam is fixed in space
613  unsigned b=0;
614 
615  // Pin displacements in both x and y directions
616  Wall_mesh_pt->boundary_node_pt(b,0)->pin_position(0);
617  Wall_mesh_pt->boundary_node_pt(b,0)->pin_position(1);
618 
619  // Infinite slope: Pin type 1 (slope) dof for displacement direction 0
620  Wall_mesh_pt->boundary_node_pt(b,0)->pin_position(1,0);
621 
622 
623  // Complete build of fluid elements
624  // --------------------------------
625 
626  //Set the constitutive law
627  Constitutive_law_pt = new GeneralisedHookean(&Global_Parameters::Nu);
628 
629  // Loop over the elements to set up element-specific
630  // things that cannot be handled by constructor
631  unsigned n_element = Bulk_mesh_pt->nelement();
632  for(unsigned e=0;e<n_element;e++)
633  {
634  // Upcast from GeneralisedElement to the present element
635  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
636 
637  //Set the Reynolds number
638  el_pt->re_pt() = &Global_Parameters::Re;
639 
640  //Set the Womersley number
641  el_pt->re_st_pt() = &Global_Parameters::ReSt;
642 
643  //Set the constitutive law
644  el_pt->constitutive_law_pt() = Constitutive_law_pt;
645 
646  // Density of pseudo-solid
647  el_pt->lambda_sq_pt()=&Global_Parameters::Lambda_sq;
648 
649  }// end loop over elements
650 
651 
652 
653  // Complete build of wall elements
654  // -------------------------------
655  n_element = Wall_mesh_pt->nelement();
656  for(unsigned e=0;e<n_element;e++)
657  {
658  // Upcast to the specific element type
659  FSIHermiteBeamElement *elem_pt =
660  dynamic_cast<FSIHermiteBeamElement*>(Wall_mesh_pt->element_pt(e));
661 
662  // Set physical parameters for each element:
663  elem_pt->h_pt() = &Global_Parameters::H;
664 
665  // Function that specifies the load ratios
666  elem_pt->q_pt() = &Global_Parameters::Q;
667 
668  // Set the undeformed shape for each element
669  elem_pt->undeformed_beam_pt() = Undeformed_wall_pt;
670 
671  // Density of beam
672  elem_pt->lambda_sq_pt()=&Global_Parameters::Lambda_sq_beam;
673 
674  // Leaflet is immersed and loaded by fluid on both sides
675  elem_pt->enable_fluid_loading_on_both_sides();
676 
677  // The normal to the leaflet, as computed by the
678  // FSIHermiteElements points out of the fluid rather than
679  // into the fluid (as assumed by default) when viewed from
680  // the "front" (face 0).
681  elem_pt->set_normal_pointing_out_of_fluid();
682 
683  }
684 
685  // Setup FSI
686  // ---------
687 
688  // The velocity of the fluid nodes on the wall (fluid mesh boundary 4,5)
689  // is set by the wall motion -- hence the no-slip condition must be
690  // re-applied whenever a node update is performed for these nodes.
691  // Such tasks may be performed automatically by the auxiliary node update
692  // function specified by a function pointer:
693  for(unsigned ibound=4;ibound<6;ibound++ )
694  {
695  unsigned num_nod= Bulk_mesh_pt->nboundary_node(ibound);
696  for (unsigned inod=0;inod<num_nod;inod++)
697  {
698  Bulk_mesh_pt->boundary_node_pt(ibound, inod)->
699  set_auxiliary_node_update_fct_pt(
700  FSI_functions::apply_no_slip_on_moving_wall);
701  }
702  }// aux node update fct has been set
703 
704 
705 
706  // Work out which fluid dofs affect the residuals of the wall elements:
707  // We pass the boundary between the fluid and solid meshes and
708  // pointers to the meshes. The interaction boundary is boundary 4 and 5
709  // of the 2D fluid mesh.
710 
711  // Front of leaflet (face=0, which is also the default so this argument
712  // could be omitted) meets boundary 4 of the fluid mesh.
713  unsigned face=0;
714  FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
715  (this,
716  4,
717  Bulk_mesh_pt,
718  Wall_mesh_pt,
719  face);
720 
721  // Back of leaflet: face 1, meets boundary 5 of fluid mesh
722  face=1;
723  FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
724  (this,5,Bulk_mesh_pt,Wall_mesh_pt,face);
725 
726  // Setup equation numbering scheme
727  //--------------------------------
728  oomph_info << "Number of equations: " << assign_eqn_numbers() << std::endl;
729 
730 }//end of constructor
731 
732 
733 //===========start_iterative_solver=========================================
734 /// Set iterative solver
735 //==========================================================================
736 template<class ELEMENT>
738 {
739  // Create the linear solver
740  IterativeLinearSolver* solver_pt=0;
741 
742  // If we have trilinos, use it
743 #ifdef OOMPH_HAS_TRILINOS
744 
745  // Create solver
746  solver_pt = new TrilinosAztecOOSolver;
747 
748  // Use GMRES
749  dynamic_cast<TrilinosAztecOOSolver*>(solver_pt)->solver_type()
750  = TrilinosAztecOOSolver::GMRES;
751 
752 #else
753 
754  // Use oomph-lib's own GMRES
755  solver_pt = new GMRES<CRDoubleMatrix>;
756 
757 #endif
758 
759  // Set solver
760  linear_solver_pt() = solver_pt;
761 
762  // Create preconditioner for 2D problem
763  unsigned dim=2;
764  PseudoElasticFSIPreconditioner* prec_pt=
765  new PseudoElasticFSIPreconditioner(dim, this);
766 
767  // Set preconditioner
768  solver_pt->preconditioner_pt() = prec_pt;
769 
770 
771  // Specify meshes that contain elements which classify the various
772  // degrees of freedom:
773  prec_pt->set_fluid_and_pseudo_elastic_mesh_pt(Bulk_mesh_pt);
774  prec_pt->set_solid_mesh_pt(Wall_mesh_pt);
775  prec_pt->set_lagrange_multiplier_mesh_pt(Lagrange_multiplier_mesh_pt);
776 
777 
778  // Use oomph-lib's Schur complement preconditioner as Navier-Stokes
779  // subsidiary preconditioner
780  if (!CommandLineArgs::command_line_flag_has_been_set("--suppress_lsc"))
781  {
782  oomph_info << "Enabling LSC preconditioner\n";
783  prec_pt->enable_navier_stokes_schur_complement_preconditioner();
784  }
785  else
786  {
787  prec_pt->disable_navier_stokes_schur_complement_preconditioner();
788  oomph_info << "Not using LSC preconditioner\n";
789  } // done disable lsc
790 
791 
792  // Use approximate block solves?
793  //------------------------------
794  if (CommandLineArgs::command_line_flag_has_been_set("--superlu_for_blocks"))
795  {
796  oomph_info << "Use SuperLU for block solves\n";
797  }
798  else
799  {
800  oomph_info << "Use optimal block solves\n";
801 
802  // Get pointer to Navier-Stokes Schur complement preconditioner
803  NavierStokesSchurComplementPreconditioner* ns_prec_pt =
804  prec_pt->navier_stokes_schur_complement_preconditioner_pt();
805 
806  // Navier Stokes momentum block
807  //-----------------------------
808 
809  // Block triangular for momentum block in LSC precond
810  BlockTriangularPreconditioner<CRDoubleMatrix>*
811  f_prec_pt = new BlockTriangularPreconditioner<CRDoubleMatrix>;
812 
813  // Set it
814  ns_prec_pt->set_f_preconditioner(f_prec_pt);
815 
816 #ifdef OOMPH_HAS_HYPRE
817 
818  // Use Hypre for diagonal blocks
819  f_prec_pt->set_subsidiary_preconditioner_function
821 
822 
823  // Navier Stokes Schur complement/pressure block
824  //----------------------------------------------
825 
826  // Build/set Hypre for Schur complement (pressure) block
827  HyprePreconditioner* p_prec_pt = new HyprePreconditioner;
828  p_prec_pt->disable_doc_time();
829  Hypre_default_settings::set_defaults_for_2D_poisson_problem(p_prec_pt);
830  ns_prec_pt->set_p_preconditioner(p_prec_pt);
831 
832 #endif
833 
834  // Pseudo elastic block
835  //---------------------
836 
837  // Use block upper triangular preconditioner for (pseudo-)elastic block
838  prec_pt->pseudo_elastic_preconditioner_pt()->elastic_preconditioner_type()
839  = PseudoElasticPreconditioner::Block_upper_triangular_preconditioner;
840 
841 #ifdef OOMPH_HAS_HYPRE
842 
843  // Use Hypre for diagonal blocks of (pseudo-)elastic preconditioner
844  prec_pt->pseudo_elastic_preconditioner_pt()->
845  set_elastic_subsidiary_preconditioner(
846  Pseudo_Elastic_Preconditioner_Subsidiary_Operator_Helper::
847  get_elastic_preconditioner_hypre);
848 
849 #endif
850 
851 #ifdef OOMPH_HAS_TRILINOS
852 
853  // Use Trilinos CG as subsidiary preconditioner (inexact solver) for
854  // linear (sub-)systems to be solved in the Lagrange multiplier block
855  prec_pt->pseudo_elastic_preconditioner_pt()->
856  set_lagrange_multiplier_subsidiary_preconditioner
857  (Pseudo_Elastic_Preconditioner_Subsidiary_Operator_Helper::
858  get_lagrange_multiplier_preconditioner);
859 
860 #endif
861  }
862 
863 } //end set_iterative_solver
864 
865 
866 //==========================================================================
867 /// Create elements that impose the prescribed boundary displacement
868 /// by Lagrange multipliers
869 //==========================================================================
870 template<class ELEMENT>
873 {
874  // Lagrange multiplier elements are located on boundary 4 and 5
875  for (unsigned b=4;b<=5;b++)
876  {
877  // How many bulk elements are adjacent to boundary b?
878  unsigned n_bulk_element = Bulk_mesh_pt->nboundary_element(b);
879 
880  // Loop over the bulk elements adjacent to boundary b?
881  for(unsigned e=0;e<n_bulk_element;e++)
882  {
883  // Get pointer to the bulk element that is adjacent to boundary b
884  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
885  Bulk_mesh_pt->boundary_element_pt(b,e));
886 
887  //Find the index of the face of element e along boundary b
888  int face_index = Bulk_mesh_pt->face_index_at_boundary(b,e);
889 
890  // Create new element
891  ImposeDisplacementByLagrangeMultiplierElement<ELEMENT> *el_pt =
892  new ImposeDisplacementByLagrangeMultiplierElement<ELEMENT>(
893  bulk_elem_pt,face_index);
894 
895  // Add to mesh
896  Lagrange_multiplier_mesh_pt->add_element_pt(el_pt);
897 
898  // Set the GeomObject that defines the boundary shape and set
899  // which bulk boundary we are attached to(needed to extract
900  // the boundary coordinate from the bulk nodes)
901  el_pt->set_boundary_shape_geom_object_pt(Wall_geom_object_pt,b);
902  }
903  }
904 
905  // Pin Lagrange multiplier unknowns for fluid nodes whose position
906  // is already pinned
907  unsigned n_element=Lagrange_multiplier_mesh_pt->nelement();
908  for(unsigned i=0;i<n_element;i++)
909  {
910  //Cast to a Lagrange multiplier element
911  ImposeDisplacementByLagrangeMultiplierElement<ELEMENT> *el_pt =
912  dynamic_cast<ImposeDisplacementByLagrangeMultiplierElement<ELEMENT>*>
913  (Lagrange_multiplier_mesh_pt->element_pt(i));
914 
915  // Loop over the nodes
916  unsigned nnod=el_pt->nnode();
917  for (unsigned j=0;j<nnod;j++)
918  {
919  Node* nod_pt = el_pt->node_pt(j);
920 
921  // Is the node also on boundary 0 or 6 (i.e. on bottom wall)>
922  if ((nod_pt->is_on_boundary(0))||(nod_pt->is_on_boundary(6)))
923  {
924  // How many nodal values were used by the "bulk" element
925  // that originally created this node?
926  unsigned n_bulk_value=el_pt->nbulk_value(j);
927 
928  // The remaining ones are Lagrange multipliers and we pin them.
929  unsigned nval=nod_pt->nvalue();
930  for (unsigned k=n_bulk_value;k<nval;k++)
931  {
932  nod_pt->pin(k);
933  }
934  }
935  }
936  }
937 }
938 
939 
940 
941 //====start_of_delete_lagrange_multiplier_elements=======================
942 /// Delete elements that impose the prescribed boundary displacement
943 /// and wipe the associated mesh
944 //=======================================================================
945 template<class ELEMENT>
948 {
949  // How many surface elements are in the surface mesh
950  unsigned n_element = Lagrange_multiplier_mesh_pt->nelement();
951 
952  // Loop over the surface elements
953  for(unsigned e=0;e<n_element;e++)
954  {
955  // Kill surface element
956  delete Lagrange_multiplier_mesh_pt->element_pt(e);
957  }
958 
959  // Wipe the mesh
960  Lagrange_multiplier_mesh_pt->flush_element_and_node_storage();
961 
962 } // end of delete_lagrange_multiplier_elements
963 
964 
965 
966 //==start_of_doc_solution=================================================
967 /// Doc the solution
968 //========================================================================
969 template<class ELEMENT>
971 {
972  ofstream some_file;
973  char filename[100];
974 
975  // Number of plot points
976  unsigned npts;
977  npts=5;
978 
979  // Output fluid solution
980  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
981  doc_info.number());
982  some_file.open(filename);
983  Bulk_mesh_pt->output(some_file,npts);
984  some_file.close();
985 
986  // Output wall solution
987  sprintf(filename,"%s/wall_soln%i.dat",doc_info.directory().c_str(),
988  doc_info.number());
989  some_file.open(filename);
990  Wall_mesh_pt->output(some_file,npts);
991  some_file.close();
992 
993  // Help me figure out what the "front" and "back" faces of the leaflet are
994  //------------------------------------------------------------------------
995 
996  // Output fluid elements on fluid mesh boundary 4 (associated with
997  // the "front")
998  unsigned bound=4;
999  sprintf(filename,"%s/bulk_boundary_elements_front_%i.dat",
1000  doc_info.directory().c_str(),
1001  doc_info.number());
1002  some_file.open(filename);
1003  unsigned nel= Bulk_mesh_pt->nboundary_element(bound);
1004  for (unsigned e=0;e<nel;e++)
1005  {
1006  dynamic_cast<ELEMENT*>(Bulk_mesh_pt->boundary_element_pt(bound,e))
1007  ->output(some_file,npts);
1008  }
1009  some_file.close();
1010 
1011 
1012  // Output fluid elements on fluid mesh boundary 5 (associated with
1013  // the "back")
1014  bound=5;
1015  sprintf(filename,"%s/bulk_boundary_elements_back_%i.dat",
1016  doc_info.directory().c_str(),
1017  doc_info.number());
1018  some_file.open(filename);
1019  nel= Bulk_mesh_pt->nboundary_element(bound);
1020  for (unsigned e=0;e<nel;e++)
1021  {
1022  dynamic_cast<ELEMENT*>(Bulk_mesh_pt->boundary_element_pt(bound,e))
1023  ->output(some_file,npts);
1024  }
1025  some_file.close();
1026 
1027 
1028  // Output normal vector on wall elements
1029  sprintf(filename,"%s/wall_normal_%i.dat",
1030  doc_info.directory().c_str(),
1031  doc_info.number());
1032  some_file.open(filename);
1033  nel=Wall_mesh_pt->nelement();
1034  Vector<double> s(1);
1035  Vector<double> x(2);
1036  Vector<double> xi(1);
1037  Vector<double> N(2);
1038  for (unsigned e=0;e<nel;e++)
1039  {
1040  // Get pointer to element
1041  FSIHermiteBeamElement* el_pt=
1042  dynamic_cast<FSIHermiteBeamElement*>(Wall_mesh_pt->element_pt(e));
1043 
1044  // Loop over plot points
1045  for (unsigned i=0;i<npts;i++)
1046  {
1047  s[0]=-1.0+2.0*double(i)/double(npts-1);
1048 
1049  // Get Eulerian position
1050  el_pt->interpolated_x(s,x);
1051 
1052  // Get unit normal
1053  el_pt->get_normal(s,N);
1054 
1055  // Get Lagrangian coordinate
1056  el_pt->interpolated_xi(s,xi);
1057 
1058  some_file << x[0] << " " << x[1] << " "
1059  << N[0] << " " << N[1] << " "
1060  << xi[0] << std::endl;
1061  }
1062  }
1063  some_file.close();
1064 
1065 } // end_of_doc_solution
1066 
1067 
1068 
1069 //=======start_of_main=====================================================
1070 /// Driver code
1071 //=========================================================================
1072 int main(int argc, char **argv)
1073 {
1074 #ifdef OOMPH_HAS_MPI
1075  MPI_Helpers::init(argc,argv);
1076 #endif
1077 
1078  // Switch off output modifier
1079  oomph_info.output_modifier_pt() = &default_output_modifier;
1080 
1081  // Store command line arguments
1082  CommandLineArgs::setup(argc,argv);
1083 
1084  // Multiplier for number of elements in coordinate directions.
1085  // Used for uniform mesh refinement studies.
1086  unsigned mesh_multiplier = 2;
1087  CommandLineArgs::specify_command_line_flag("--mesh_multiplier",
1088  &mesh_multiplier);
1089 
1090  // Suppress use of LSC preconditioner for Navier Stokes block
1091  CommandLineArgs::specify_command_line_flag("--suppress_lsc");
1092 
1093  // Use direct solver (SuperLU)
1094  CommandLineArgs::specify_command_line_flag("--use_direct_solver");
1095 
1096  // Use SuperLU for all block solves
1097  CommandLineArgs::specify_command_line_flag("--superlu_for_blocks");
1098 
1099  // Validation only?
1100  CommandLineArgs::specify_command_line_flag("--validate");
1101 
1102  // Parse command line
1103  CommandLineArgs::parse_and_assign();
1104 
1105  // Doc what has actually been specified on the command line
1106  CommandLineArgs::doc_specified_flags();
1107 
1108  //Set up the problem
1109  FSIChannelWithLeafletProblem<PseudoSolidNodeUpdateElement
1110  <QTaylorHoodElement<2>,
1112  problem_pt = new
1113  FSIChannelWithLeafletProblem<PseudoSolidNodeUpdateElement
1114  <QTaylorHoodElement<2>,
1116  (mesh_multiplier);
1117 
1118  // Initialise timestep
1119  problem_pt->initialise_dt(Global_Parameters::Dt);
1120 
1121  // Label for output
1122  DocInfo doc_info;
1123  doc_info.set_directory("RESLT");
1124 
1125 
1126  // Define processor-labeled output file for all on-screen stuff
1127  std::ofstream output_stream;
1128  char filename[1000];
1129 #ifdef OOMPH_HAS_MPI
1130  sprintf(filename,"%s/OUTPUT_STEADY.%i",doc_info.directory().c_str(),
1131  MPI_Helpers::communicator_pt()->my_rank());
1132 #else
1133  sprintf(filename,"%s/OUTPUT_STEADY.%i",doc_info.directory().c_str(),0);
1134 #endif
1135 
1136  output_stream.open(filename);
1137  oomph_info.stream_pt() = &output_stream;
1138  OomphLibWarning::set_stream_pt(&output_stream);
1139  OomphLibError::set_stream_pt(&output_stream);
1140 
1141 
1142  // Output initial configuration
1143  problem_pt->doc_solution(doc_info);
1144  doc_info.number()++;
1145 
1146  // Switch to iterative solver?
1147  if (!CommandLineArgs::command_line_flag_has_been_set("--use_direct_solver"))
1148  {
1149  problem_pt->set_iterative_solver();
1150  }
1151 
1152 
1153  // Steady solves
1154  //--------------
1155 
1156  // Increment Re and Womersley numbers in increments of 25.
1157  double target_re = Global_Parameters::Re;
1158  Global_Parameters::Re=25.0;
1160  while (Global_Parameters::Re<target_re)
1161  {
1162  problem_pt->steady_newton_solve();
1163  problem_pt->doc_parameters();
1164  Global_Parameters::Re+=25.0;
1166  problem_pt->doc_solution(doc_info);
1167  doc_info.number()++;
1168  }
1169 
1170  // Do final solve at desired Re
1171  Global_Parameters::Re=target_re;
1172  Global_Parameters::ReSt=target_re;
1173  problem_pt->steady_newton_solve();
1174  problem_pt->doc_parameters();
1175  problem_pt->doc_solution(doc_info);
1176  doc_info.number()++;
1177 
1178  // Unsteady solves
1179  //----------------
1180 
1181  // Define processor-labeled output file for all on-screen stuff
1182  output_stream.close();
1183 #ifdef OOMPH_HAS_MPI
1184  sprintf(filename,"%s/OUTPUT_UNSTEADY.%i",doc_info.directory().c_str(),
1185  MPI_Helpers::communicator_pt()->my_rank());
1186 #else
1187  sprintf(filename,"%s/OUTPUT_UNSTEADY.%i",doc_info.directory().c_str(),0);
1188 #endif
1189  output_stream.open(filename);
1190  oomph_info.stream_pt() = &output_stream;
1191  OomphLibWarning::set_stream_pt(&output_stream);
1192  OomphLibError::set_stream_pt(&output_stream);
1193 
1194  // Loop over timesteps for specified number of periods of fluctuating
1195  // inflow
1196  unsigned n_period=1;
1197 
1198  unsigned nstep=unsigned(double(n_period)
1200 
1201  if (CommandLineArgs::command_line_flag_has_been_set("--validate"))
1202  {
1203  nstep=3;
1204  }
1205  for (unsigned r = 0; r < nstep; r++)
1206  {
1207  problem_pt->unsteady_newton_solve(Global_Parameters::Dt);
1208  problem_pt->doc_parameters();
1209  problem_pt->doc_solution(doc_info);
1210  doc_info.number()++;
1211  }
1212 
1213  // clean up
1214  delete problem_pt;
1215 
1216  // Shut down
1217  oomph_info << "Done\n";
1218 
1219 #ifdef OOMPH_HAS_MPI
1220  MPI_Helpers::finalize();
1221 #endif
1222 
1223 } // end_of_main
1224 
1225 
1226 
1227 
void position(const Vector< double > &zeta, Vector< double > &r) const
Position vector at Lagrangian coordinate zeta.
Newmark< 2 > * Wall_time_stepper_pt
Wall time stepper pt.
void set_iterative_solver()
Set iterative solver.
double Q
Fluid structure interaction parameter: Ratio of stresses used for non-dimensionalisation of fluid to ...
FSIChannelWithLeafletProblem(const unsigned &mesh_multiplier)
Constructor: Pass multiplier for uniform mesh refinement.
unsigned ngeom_data() const
Number of geometric Data in GeomObject: None.
Preconditioner * set_hypre_preconditioner()
Create instance of Hypre preconditioner with settings that are appropriate for serial solution of Nav...
void actions_before_implicit_timestep()
Actions before implicit timestep: Update the inflow velocity.
int main(int argc, char **argv)
Driver code.
double Fluid_length_right
length of fluid mesh to right of leaflet
void position(const unsigned &t, const Vector< double > &zeta, Vector< double > &r) const
Parametrised position on object: r(zeta). Evaluated at previous timestep. t=0: current time; t>0: pre...
double Leaflet_height
height of leaflet
void doc_solution(DocInfo &doc_info)
Doc the solution.
void create_lagrange_multiplier_elements()
Create elements that enforce prescribed boundary motion by Lagrange multipliers.
double flux(const double &t)
Flux: Pulsatile flow.
MeshAsGeomObject * Wall_geom_object_pt
Geometric object for the leaflet (to apply lagrange mult)
void actions_before_newton_solve()
Actions before Newton solve: Reset the pseudo-elastic undeformed configuration.
SolidMesh * Lagrange_multiplier_mesh_pt
Pointers to mesh of Lagrange multiplier elements.
double Leaflet_x0
x-position of root of leaflet
double Lambda_sq_beam
Beam mass density.
double Fluid_length_left
length of fluid mesh to left of leaflet
UndeformedLeaflet(const double &x0)
Constructor: argument is the x-coordinate of the leaflet.
unsigned Mesh_ny2
Num elements in fluid mesh in y dirn above leaflet.
void actions_before_newton_convergence_check()
Update no slip before Newton convergence check.
unsigned Mesh_nleft
Num elements to left of leaflet in coarse mesh.
double X0
x position of the undeformed leaflet&#39;s origin.
GeomObject: Undeformed straight, vertical leaflet.
PseudoElasticChannelWithLeafletMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the fluid mesh.
double Lambda_sq
Pseudo-solid mass density.
Node * tip_node_pt()
Helper fct; returns the node at the tip of the wall mesh.
void actions_after_newton_solve()
Actions after solve (empty)
double Fluid_height
height of fluid mesh
BDF< 2 > * Bulk_time_stepper_pt
Bulk timestepper.
double Nu
Pseudo-solid Poisson ratio.
double ReSt
Womersley number: Product of Reynolds and Strouhal numbers.
void d2position(const Vector< double > &zeta, Vector< double > &r, DenseMatrix< double > &drdzeta, RankThreeTensor< double > &ddrdzeta) const
Posn vector and its 1st & 2nd derivatives w.r.t. to coordinates: = drdzeta(alpha,i). = ddrdzeta(alpha,beta,i). Evaluated at current time.
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...
Namespace for Navier Stokes LSC Preconditioner.
ConstitutiveLaw * Constitutive_law_pt
Constitutive law used to determine the mesh deformation.
unsigned ndof_types() const
Return the number of DOF types associated with this element.
double H
Non-dimensional wall thickness.
unsigned Mesh_ny1
Num elements in fluid mesh in y dirn adjacent to leaflet.
OneDLagrangianMesh< FSIHermiteBeamElement > * Wall_mesh_pt
Pointer to the "wall" mesh.
UndeformedLeaflet * Undeformed_wall_pt
Geom object for the leaflet.
double Dt
Timestep for simulation: 40 steps per period.
unsigned Mesh_nright
Num elements to right of leaflet in coarse mesh.
double T
Period for fluctuations in flux.