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