fsi_chan_problem.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// 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 
28 
29 //====start_of_physical_parameters=====================
30 /// Namespace for phyical parameters
31 //======================================================
33 {
34 
35  /// Reynolds number
36  double Re=250.0;
37 
38  /// Womersley = Reynolds times Strouhal
39  double ReSt=250.0;
40 
41  /// Non-dimensional wall thickness. As in Heil (2004) paper.
42  double H=1.0e-2;
43 
44  /// 2nd Piola Kirchhoff pre-stress. As in Heil (2004) paper.
45  double Sigma0=1.0e3;
46 
47  /// Pointer to Data object that stores external pressure
48  Data* P_ext_data_pt=0;
49 
50  /// Min. pressure. Only used in steady runs during parameter
51  /// incrementation. Use 1.5 for values of Re to
52  /// span the range in Heil (2004) paper.
53  double Pmin=1.5;
54 
55  /// Max. pressure. Only used in steady runs during parameter
56  /// incrementation. Use 2.0 for Re=250; 3.7 for Re=0 to
57  /// span the range in Heil (2004) paper.
58  double Pmax=2.0;
59 
60  /// Jump in pressure after a restart -- used to give a steady
61  /// solution a kick before starting a time-dependent run
62  double P_step=0.0;
63 
64  /// Current prescribed vertical position of control point
65  /// (only used for displacement control)
66  double Yprescr = 1.0;
67 
68  /// Min. of prescribed vertical position of conrol point
69  /// (only used during parameter study with displacement control).
70  /// 0.6 corresponds to the value in Heil (2004) paper for static runs.
71  double Yprescr_min=0.6;
72 
73  /// Load function: Apply a constant external pressure to the wall.
74  /// Note: This is the load without the fluid contribution!
75  /// Fluid load gets added on by FSIWallElement.
76  void load(const Vector<double>& xi, const Vector<double>& x,
77  const Vector<double>& N, Vector<double>& load)
78  {
79  for(unsigned i=0;i<2;i++)
80  {
81  load[i] = -P_ext_data_pt->value(0)*N[i];
82  }
83  } //end of load
84 
85 
86  /// Fluid structure interaction parameter: Ratio of stresses used for
87  /// non-dimensionalisation of fluid to solid stresses. As in Heil (2004)
88  /// paper
89  double Q=1.0e-2;
90 
91 
92 } // end of namespace
93 
94 
95 
96 /// /////////////////////////////////////////////////////////////////////
97 /// /////////////////////////////////////////////////////////////////////
98 /// /////////////////////////////////////////////////////////////////////
99 
100 
101 
102 
103 //==========start_of_BL_Squash =========================================
104 /// Namespace to define the mapping [0,1] -> [0,1] that re-distributes
105 /// nodal points across the channel width.
106 //======================================================================
107 namespace BL_Squash
108 {
109 
110  /// Boundary layer width
111  double Delta=0.1;
112 
113  /// Fraction of points in boundary layer
114  double Fract_in_BL=0.5;
115 
116  /// Mapping [0,1] -> [0,1] that re-distributes
117  /// nodal points across the channel width
118  double squash_fct(const double& s)
119  {
120  // Default return
121  double y=s;
122  if (s<0.5*Fract_in_BL)
123  {
124  y=Delta*2.0*s/Fract_in_BL;
125  }
126  else if (s>1.0-0.5*Fract_in_BL)
127  {
128  y=2.0*Delta/Fract_in_BL*s+1.0-2.0*Delta/Fract_in_BL;
129  }
130  else
131  {
132  y=(1.0-2.0*Delta)/(1.0-Fract_in_BL)*s+
133  (Delta-0.5*Fract_in_BL)/(1.0-Fract_in_BL);
134  }
135 
136  return y;
137  }
138 }// end of BL_Squash
139 
140 
141 
142 
143 
144 
145 /// ///////////////////////////////////////////////////////////////////////
146 /// ///////////////////////////////////////////////////////////////////////
147 /// ///////////////////////////////////////////////////////////////////////
148 
149 
150 //====start_of_underformed_wall============================================
151 /// Undeformed wall is a steady, straight 1D line in 2D space
152 /// \f[ x = X_0 + \zeta \f]
153 /// \f[ y = H \f]
154 //=========================================================================
155 class UndeformedWall : public GeomObject
156 {
157 
158 public:
159 
160  /// Constructor: arguments are the starting point and the height
161  /// above y=0.
162  UndeformedWall(const double& x0, const double& h): GeomObject(1,2)
163  {
164  X0=x0;
165  H=h;
166  }
167 
168 
169  /// Position vector at Lagrangian coordinate zeta
170  void position(const Vector<double>& zeta, Vector<double>& r) const
171  {
172  // Position Vector
173  r[0] = zeta[0]+X0;
174  r[1] = H;
175  }
176 
177 
178  /// Parametrised position on object: r(zeta). Evaluated at
179  /// previous timestep. t=0: current time; t>0: previous
180  /// timestep. Calls steady version.
181  void position(const unsigned& t, const Vector<double>& zeta,
182  Vector<double>& r) const
183  {
184  // Use the steady version
185  position(zeta,r);
186 
187  } // end of position
188 
189 
190  /// Posn vector and its 1st & 2nd derivatives
191  /// w.r.t. to coordinates:
192  /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
193  /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ =
194  /// ddrdzeta(alpha,beta,i). Evaluated at current time.
195  virtual void d2position(const Vector<double>& zeta,
196  Vector<double>& r,
197  DenseMatrix<double> &drdzeta,
198  RankThreeTensor<double> &ddrdzeta) const
199  {
200  // Position vector
201  r[0] = zeta[0]+X0;
202  r[1] = H;
203 
204  // Tangent vector
205  drdzeta(0,0)=1.0;
206  drdzeta(0,1)=0.0;
207 
208  // Derivative of tangent vector
209  ddrdzeta(0,0,0)=0.0;
210  ddrdzeta(0,0,1)=0.0;
211 
212  } // end of d2position
213 
214 private :
215 
216  /// x position of the undeformed beam's left end.
217  double X0;
218 
219  /// Height of the undeformed wall above y=0.
220  double H;
221 
222 }; //end_of_undeformed_wall
223 
224 
225 
226 
227 
228 
229 //====Namespace_for_flags================================
230 /// Namespace for flags
231 //======================================================
232 namespace Flags
233 {
234 
235  /// Resolution factor (multiplier for number of elements across channel)
236  unsigned Resolution_factor=1;
237 
238  /// Use displacement control (1) or not (0)
239  unsigned Use_displ_control=1;
240 
241  /// Steady run (1) or unsteady run (0)
242  unsigned Steady_flag=1;
243 
244  /// Number of steps in parameter study
245  unsigned Nsteps=5;
246 
247  /// String to identify the run type in trace file
249 
250  /// Name of restart file
251  string Restart_file_name="";
252 
253  /// Doc flags
254  void doc_flags()
255  {
256 
257  std::cout << "\nFlags:\n"
258  << "======\n";
259 
260  std::cout << "-- Resolution factor: " << Resolution_factor << std::endl;
261 
262  if (Steady_flag)
263  {
264  std::cout << "-- Steady run " << std::endl;
265  if (Use_displ_control)
266  {
267  std::cout << "-- Using displacement control " << std::endl;
268  }
269  else
270  {
271  std::cout << "-- Not using displacement control " << std::endl;
272  }
273  }
274  else
275  {
276  std::cout << "-- Unsteady run " << std::endl;
277  if (Use_displ_control)
278  {
279  std::cout << "-- Not using displacement control (command line flag\n"
280  << " overwritten because it's an unsteady run!) "
281  << std::endl;
282  }
283  }
284 
285  std::cout << "-- Reynolds number: "
286  << Global_Physical_Variables::Re << std::endl;
287 
288  std::cout << "-- FSI parameter Q: "
289  << Global_Physical_Variables::Q << std::endl;
290 
291 
292  if (Restart_file_name!="")
293  {
294  std::cout << "-- Performing restart from: " << Restart_file_name
295  << std::endl;
296  std::cout << "-- Jump in pressure: " << Global_Physical_Variables::P_step
297  << std::endl;
298  }
299  else
300  {
301  std::cout << "-- No restart " << std::endl;
302  }
303  std::cout << std::endl;
304  }
305 
306 }
307 
308 
309 
310 //====start_of_problem_class==========================================
311 /// Problem class
312 //====================================================================
313 template <class ELEMENT>
314 class FSICollapsibleChannelProblem : public virtual Problem
315 {
316 
317 public :
318 
319 /// Constructor: The arguments are the number of elements and
320 /// the lengths of the domain.
321  FSICollapsibleChannelProblem(const unsigned& nup,
322  const unsigned& ncollapsible,
323  const unsigned& ndown,
324  const unsigned& ny,
325  const double& lup,
326  const double& lcollapsible,
327  const double& ldown,
328  const double& ly,
329  const bool& displ_control,
330  const bool& steady_flag);
331 
332  /// Destructor
334  {
335  // Mesh gets killed in general problem destructor
336  }
337 
338  /// Steady run; virtual so it can be overloaded in derived problem
339  /// classes
340  virtual void steady_run();
341 
342  /// Unsteady run; virtual so it can be overloaded in derived problem
343  /// classes. Specify timestep or use default of 0.1
344  virtual void unsteady_run(const double& dt=0.1);
345 
346  /// Access function for the specific bulk (fluid) mesh
347  AlgebraicCollapsibleChannelMesh<ELEMENT>* bulk_mesh_pt()
348  {
349  // Upcast from pointer to the Mesh base class to the specific
350  // element type that we're using here.
351  return dynamic_cast<
352  AlgebraicCollapsibleChannelMesh<ELEMENT>*>
353  (Bulk_mesh_pt);
354  }
355 
356  /// Access function for the wall mesh
357  OneDLagrangianMesh<FSIHermiteBeamElement>* wall_mesh_pt()
358  {
359  return Wall_mesh_pt;
360 
361  } // end of access to wall mesh
362 
363 
364  /// Actions before solve. Reset counter for number of Newton iterations
366  {
367  Newton_iter=0;
368  }
369 
370  /// Update the problem after solve (empty)
372 
373 
374  /// Update before checking Newton convergence: Update the
375  /// nodal positions in the fluid mesh in response to possible
376  /// changes in the wall shape.
378  {
379  // Update mesh
380  Bulk_mesh_pt->node_update();
381 
382  // Increment counter
383  Newton_iter++;
384  }
385 
386 
387  /// Doc the steady solution
388  virtual void doc_solution_steady(DocInfo& doc_info, ofstream& trace_file,
389  const double& cpu, const unsigned &niter);
390 
391  /// Doc the unsteady solution
392  virtual void doc_solution_unsteady(DocInfo& doc_info, ofstream& trace_file,
393  const double& cpu, const unsigned &niter);
394 
395  /// Apply initial conditions
396  void set_initial_condition();
397 
398 
399  protected:
400 
401  /// Dump problem to disk to allow for restart.
402  void dump_it(ofstream& dump_file);
403 
404  /// Read problem for restart from specified restart file.
405  void restart(ifstream& restart_file);
406 
407  /// Number of elements in the x direction in the upstream part of the channel
408  unsigned Nup;
409 
410  /// Number of elements in the x direction in the collapsible part of
411  /// the channel
412  unsigned Ncollapsible;
413 
414  /// Number of elements in the x direction in the downstream part of the channel
415  unsigned Ndown;
416 
417  /// Number of elements across the channel
418  unsigned Ny;
419 
420  /// x-length in the upstream part of the channel
421  double Lup;
422 
423  /// x-length in the collapsible part of the channel
424  double Lcollapsible;
425 
426  /// x-length in the downstream part of the channel
427  double Ldown;
428 
429  /// Transverse length
430  double Ly;
431 
432  /// Pointer to the "bulk" mesh
433  AlgebraicCollapsibleChannelMesh<ELEMENT>* Bulk_mesh_pt;
434 
435  /// Pointer to the mesh that contains the displacement control element
437 
438  /// Use displacement control?
440 
441  /// Pointer to the "wall" mesh
442  OneDLagrangianMesh<FSIHermiteBeamElement>* Wall_mesh_pt;
443 
444  /// Pointer to the left control node
446 
447  /// Pointer to right control node
449 
450  /// Pointer to control node on the wall
452 
453  /// Flag for steady run
455 
456  /// Pointer to GeomObject at which displacement control is applied
457  /// (or at which wall displacement is monitored in unsteady runs)
458  GeomObject* Ctrl_geom_obj_pt;
459 
460  /// Vector of local coordinates of displacement control point
461  /// in Ctrl_geom_obj_pt
462  Vector<double> S_displ_ctrl;
463 
464  /// Pointer to geometric object (one Lagrangian, two Eulerian
465  /// coordinates) that will be built from the wall mesh
466  MeshAsGeomObject* Wall_geom_object_pt;
467 
468  /// Counter for Newton iterations
469  unsigned Newton_iter;
470 
471  /// DocInfo object
472  DocInfo Doc_info;
473 
474 };//end of problem class
475 
476 
477 
478 
479 //=====start_of_constructor======================================
480 /// Constructor for the collapsible channel problem
481 //===============================================================
482 template <class ELEMENT>
484  const unsigned& nup,
485  const unsigned& ncollapsible,
486  const unsigned& ndown,
487  const unsigned& ny,
488  const double& lup,
489  const double& lcollapsible,
490  const double& ldown,
491  const double& ly,
492  const bool& displ_control,
493  const bool& steady_flag)
494 {
495 
496 
497  // Initialise number of Newton iterations
498  Newton_iter=0;
499 
500  // Store problem parameters
501  Nup=nup;
502  Ncollapsible=ncollapsible;
503  Ndown=ndown;
504  Ny=ny;
505  Lup=lup;
506  Lcollapsible=lcollapsible;
507  Ldown=ldown;
508  Ly=ly;
509  Steady_flag=steady_flag;
510 
511  // Displacement control only makes sense for steady problems
512  if (Steady_flag)
513  {
514  Displ_control=displ_control;
515  }
516  else
517  {
518  Displ_control=false;
519  if (displ_control)
520  {
521  std::cout << "Switched off displacement control for unsteady run!"
522  << std::endl;
523  }
524  }
525 
526 
527  // Overwrite maximum allowed residual to accomodate bad initial guesses
528  Problem::Max_residuals=1000.0;
529 
530  // Allocate the timestepper -- this constructs the Problem's
531  // time object with a sufficient amount of storage to store the
532  // previous timsteps. Note: This is appropriate even for
533  // the steady problem as we'll explicitly call the *steady*
534  // Newton solver which disables the timesteppers
535  // during the solve.
536  add_time_stepper_pt(new BDF<2>);
537 
538  // Create a dummy Steady timestepper that stores two history values
539  Steady<2>* wall_time_stepper_pt = new Steady<2>;
540 
541  // Add the wall timestepper to the Problem's collection of timesteppers.
542  add_time_stepper_pt(wall_time_stepper_pt);
543 
544  // Geometric object that represents the undeformed wall:
545  // A straight line at height y=ly; starting at x=lup.
546  UndeformedWall* undeformed_wall_pt=new UndeformedWall(lup,ly);
547 
548  //Create the "wall" mesh with FSI Hermite elements
549  Wall_mesh_pt = new OneDLagrangianMesh<FSIHermiteBeamElement>
550  (Ncollapsible,Lcollapsible,undeformed_wall_pt,wall_time_stepper_pt);
551 
552  // Build a geometric object (one Lagrangian, two Eulerian coordinates)
553  // from the wall mesh
554  Wall_geom_object_pt=
555  new MeshAsGeomObject(Wall_mesh_pt);
556 
557  // Get pointer to/local coordinate in wall geom object that contains
558  // control node -- adjusted for different values of Q, so that
559  // the point is located near the point of strongest collapse.
560  Vector<double> zeta_displ_ctrl(1);
561  zeta_displ_ctrl[0]=3.5;
562  if (std::abs(Global_Physical_Variables::Q-1.0e-3)<1.0e-10)
563  {
564  zeta_displ_ctrl[0]=3.0;
565  }
566  //if (std::abs(Global_Physical_Variables::Q-1.0e-4)<1.0e-10)
567  if (Global_Physical_Variables::Q<=1.0e-4)
568  {
569  zeta_displ_ctrl[0]=2.5;
570  }
571  std::cout << "Wall control point located at zeta=" <<zeta_displ_ctrl[0]
572  << std::endl;
573  S_displ_ctrl.resize(1);
574 
575  // Locate control point (pointer to GeomObject and local coordinate in it)
576  Wall_geom_object_pt->locate_zeta(zeta_displ_ctrl,
577  Ctrl_geom_obj_pt,
578  S_displ_ctrl);
579 
580 
581  // Normal load incrementation or unsteady run
582  //===========================================
583  Displ_control_mesh_pt=new Mesh;
584 
585  // Choose element in which displacement control is applied:
586  SolidFiniteElement* controlled_element_pt=
587  dynamic_cast<SolidFiniteElement*>(Ctrl_geom_obj_pt);
588 
589  // Fix the displacement in the vertical (1) direction...
590  unsigned controlled_direction=1;
591 
592  // Pointer to displacement control element
593  DisplacementControlElement* displ_control_el_pt;
594 
595  // Build displacement control element
596  displ_control_el_pt=
597  new DisplacementControlElement(controlled_element_pt,
598  S_displ_ctrl,
599  controlled_direction,
601 
602  // The constructor of the DisplacementControlElement has created
603  // a new Data object whose one-and-only value contains the
604  // adjustable load: Use this Data object in the load function:
605  Global_Physical_Variables::P_ext_data_pt=displ_control_el_pt->
606  displacement_control_load_pt();
607 
608  // Add the displacement-control element to its own mesh
609  Displ_control_mesh_pt->add_element_pt(displ_control_el_pt);
610 
611 
612  if (!Displ_control)
613  {
614  // Create Data object whose one-and-only value contains the
615  // (in principle) adjustable load
617 
618  //Pin the external pressure because it isn't actually adjustable.
620  }
621 
622  //Build bulk (fluid) mesh
623  Bulk_mesh_pt =
624  new AlgebraicCollapsibleChannelMesh<ELEMENT>
625  (nup, ncollapsible, ndown, ny,
626  lup, lcollapsible, ldown, ly,
627  Wall_geom_object_pt,
628  time_stepper_pt());
629 
630 
631  // Add the sub meshes to the problem
632  add_sub_mesh(Bulk_mesh_pt);
633  add_sub_mesh(Wall_mesh_pt);
634  add_sub_mesh(Displ_control_mesh_pt);
635 
636  // Combine all submeshes into a single Mesh
637  build_global_mesh();
638 
639 
640  // Complete build of fluid mesh
641  //-----------------------------
642 
643  // Loop over the elements to set up element-specific
644  // things that cannot be handled by constructor
645  unsigned n_element=Bulk_mesh_pt->nelement();
646  for(unsigned e=0;e<n_element;e++)
647  {
648  // Upcast from GeneralisedElement to the present element
649  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
650 
651  //Set the Reynolds number
652  el_pt->re_pt() = &Global_Physical_Variables::Re;
653 
654  // Set the Womersley number
655  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
656 
657  // Switch off mesh velocity in steady runs
658  if (Flags::Steady_flag)
659  {
660  el_pt->disable_ALE();
661  }
662  else
663  {
664  // Is element in rigid part?
665  bool is_in_rigid_part=true;
666 
667  // Number of nodes
668  unsigned nnod=el_pt->nnode();
669  for (unsigned j=0;j<nnod;j++)
670  {
671  double x=el_pt->node_pt(j)->x(0);
672  if ((x>=Lup)&&(x<=(Lup+Lcollapsible)))
673  {
674  is_in_rigid_part=false;
675  break;
676  }
677  }
678  if (is_in_rigid_part)
679  {
680  el_pt->disable_ALE();
681  }
682  }
683 
684  } // end loop over elements
685 
686 
687 
688  // Apply boundary conditions for fluid
689  //------------------------------------
690 
691  //Pin the velocity on the boundaries
692  //x and y-velocities pinned along boundary 0 (bottom boundary) :
693  unsigned ibound=0;
694  unsigned num_nod= bulk_mesh_pt()->nboundary_node(ibound);
695  for (unsigned inod=0;inod<num_nod;inod++)
696  {
697  for(unsigned i=0;i<2;i++)
698  {
699  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
700  }
701  }
702 
703  //x and y-velocities pinned along boundaries 2, 3, 4 (top boundaries) :
704  for(ibound=2;ibound<5;ibound++)
705  {
706  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
707  for (unsigned inod=0;inod<num_nod;inod++)
708  {
709  for(unsigned i=0;i<2;i++)
710  {
711  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
712  }
713  }
714  }
715 
716  //y-velocity pinned along boundary 1 (right boundary):
717  ibound=1;
718  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
719  for (unsigned inod=0;inod<num_nod;inod++)
720  {
721  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
722  }
723 
724 
725  //Both velocities pinned along boundary 5 (left boundary):
726  ibound=5;
727  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
728  for (unsigned inod=0;inod<num_nod;inod++)
729  {
730  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(0);
731  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
732  }
733 //end of pin_velocity
734 
735 
736  // Complete build of wall elements
737  //--------------------------------
738 
739  //Loop over the elements to set physical parameters etc.
740  n_element = wall_mesh_pt()->nelement();
741  for(unsigned e=0;e<n_element;e++)
742  {
743  // Upcast to the specific element type
744  FSIHermiteBeamElement *elem_pt =
745  dynamic_cast<FSIHermiteBeamElement*>(wall_mesh_pt()->element_pt(e));
746 
747  // Set physical parameters for each element:
748  elem_pt->sigma0_pt() = &Global_Physical_Variables::Sigma0;
749  elem_pt->h_pt() = &Global_Physical_Variables::H;
750 
751  // Set the load vector for each element
752  elem_pt->load_vector_fct_pt() = &Global_Physical_Variables::load;
753 
754  // Function that specifies the load ratios
755  elem_pt->q_pt() = &Global_Physical_Variables::Q;
756 
757  // Set the undeformed shape for each element
758  elem_pt->undeformed_beam_pt() = undeformed_wall_pt;
759 
760  // The normal on the wall elements as computed by the FSIHermiteElements
761  // points away from the fluid rather than into the fluid (as assumed
762  // by default)
763  elem_pt->set_normal_pointing_out_of_fluid();
764 
765  // Displacement control? If so, the load on *all* elements
766  // is affected by an unknown -- the external pressure, stored
767  // as the one-and-only value in a Data object: Add it to the
768  // elements' external Data.
769  if (Displ_control)
770  {
771  //The external pressure is external data for all elements
772  elem_pt->add_external_data(Global_Physical_Variables::P_ext_data_pt);
773  }
774 
775 
776  } // end of loop over elements
777 
778 
779 
780  // Boundary conditions for wall mesh
781  //----------------------------------
782 
783  // Set the boundary conditions: Each end of the beam is fixed in space
784  // Loop over the boundaries (ends of the beam)
785  for(unsigned b=0;b<2;b++)
786  {
787  // Pin displacements in both x and y directions
788  wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(0);
789  wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(1);
790  }
791 
792 
793 
794  //Choose control nodes
795  //---------------------
796 
797  // Left boundary
798  ibound=5;
799  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
800  unsigned control_nod=num_nod/2;
801  Left_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
802 
803  // Right boundary
804  ibound=1;
805  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
806  control_nod=num_nod/2;
807  Right_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
808 
809 
810  // Set the pointer to the control node on the wall
811  num_nod= wall_mesh_pt()->nnode();
812  Wall_node_pt=wall_mesh_pt()->node_pt(Ncollapsible/2);
813 
814 
815 
816  // Setup FSI
817  //----------
818 
819  // The velocity of the fluid nodes on the wall (fluid mesh boundary 3)
820  // is set by the wall motion -- hence the no-slip condition must be
821  // re-applied whenever a node update is performed for these nodes.
822  // Such tasks may be performed automatically by the auxiliary node update
823  // function specified by a function pointer:
824  ibound=3;
825  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
826  for (unsigned inod=0;inod<num_nod;inod++)
827  {
828  static_cast<AlgebraicNode*>(
829  bulk_mesh_pt()->boundary_node_pt(ibound, inod))->
830  set_auxiliary_node_update_fct_pt(
831  FSI_functions::apply_no_slip_on_moving_wall);
832  }
833 
834  // Work out which fluid dofs affect the residuals of the wall elements:
835  // We pass the boundary between the fluid and solid meshes and
836  // pointers to the meshes. The interaction boundary is boundary 3 of the
837  // 2D fluid mesh.
838  FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
839  (this,3,Bulk_mesh_pt,Wall_mesh_pt);
840 
841  // Setup equation numbering scheme
842  cout <<"Total number of equations: " << assign_eqn_numbers() << std::endl;
843 
844 }//end of constructor
845 
846 
847 
848 //====start_of_doc_solution_steady============================================
849 /// Doc the solution for a steady run
850 //============================================================================
851 template <class ELEMENT>
854  DocInfo &doc_info,
855  ofstream& trace_file,
856  const double& cpu, const unsigned &niter)
857 {
858 
859  ofstream some_file;
860  char filename[100];
861 
862  // Number of plot points
863  unsigned npts;
864  npts=5;
865 
866  // Output fluid solution
867  sprintf(filename,"%s/soln%i.dat",Doc_info.directory().c_str(),
868  Doc_info.number());
869  some_file.open(filename);
870  bulk_mesh_pt()->output(some_file,npts);
871  some_file.close();
872 
873  // Document the wall shape
874  sprintf(filename,"%s/beam%i.dat",Doc_info.directory().c_str(),
875  Doc_info.number());
876  some_file.open(filename);
877  wall_mesh_pt()->output(some_file,npts);
878  some_file.close();
879 
880 // Write restart file
881  sprintf(filename,"%s/restart%i.dat",Doc_info.directory().c_str(),
882  Doc_info.number());
883  some_file.open(filename);
884  some_file.precision(16);
885  dump_it(some_file);
886  some_file.close();
887 
888  // Write trace file
889  trace_file << Global_Physical_Variables::P_ext_data_pt->value(0) << " ";
890  trace_file << Global_Physical_Variables::Yprescr << " ";
891 
892  // Write trace file
893  trace_file << Left_node_pt->value(0) << " "
894  << Right_node_pt->value(0) << " "
895  << cpu << " "
896  << Newton_iter << " "
897  << std::endl;
898 
899 
900 } // end_of_doc_solution_steady
901 
902 
903 
904 
905 
906 
907 
908 
909 //====start_of_doc_solution_unsteady==========================================
910 /// Doc the solution for an unstady run
911 //============================================================================
912 template <class ELEMENT>
915  DocInfo& doc_info,
916  ofstream& trace_file,
917  const double& cpu,
918  const unsigned &niter)
919 {
920 
921  ofstream some_file;
922  char filename[100];
923 
924  // Number of plot points
925  unsigned npts;
926  npts=5;
927 
928  // Output fluid solution
929  sprintf(filename,"%s/soln%i.dat",Doc_info.directory().c_str(),
930  Doc_info.number());
931  some_file.open(filename);
932  bulk_mesh_pt()->output(some_file,npts);
933  some_file.close();
934 
935  // Document the wall shape
936  sprintf(filename,"%s/beam%i.dat",Doc_info.directory().c_str(),
937  Doc_info.number());
938  some_file.open(filename);
939  wall_mesh_pt()->output(some_file,npts);
940  some_file.close();
941 
942 // Write restart file
943  sprintf(filename,"%s/restart%i.dat",Doc_info.directory().c_str(),
944  Doc_info.number());
945  some_file.open(filename);
946  dump_it(some_file);
947  some_file.close();
948 
949  // Write trace file
950  trace_file << time_pt()->time() << " ";
951 
952  // Get/doc y-coordinate of control point
953  Vector<double> r(2);
954  Ctrl_geom_obj_pt->position(S_displ_ctrl,r);
955  trace_file << r[1] << " ";
956 
957  // Write trace file
958  trace_file << Left_node_pt->value(0) << " "
959  << Right_node_pt->value(0) << " "
960  << cpu << " "
961  << Newton_iter << " "
962  << std::endl;
963 
964 
965 } // end_of_doc_solution_steady
966 
967 
968 
969 
970 //=====start_of_dump_it===================================================
971 /// Dump the solution to disk to allow for restart
972 //========================================================================
973 template <class ELEMENT>
975 {
976 
977  // Number of submeshes must agree when dumping/restarting so
978  // temporarily add displacement control mesh back in before dumping...
979  if (!Displ_control)
980  {
981  flush_sub_meshes();
982  add_sub_mesh(Bulk_mesh_pt);
983  add_sub_mesh(Wall_mesh_pt);
984  add_sub_mesh(Displ_control_mesh_pt);
985  rebuild_global_mesh();
986  assign_eqn_numbers();
987  }
988 
989  // Write current external pressure
990  dump_file << Global_Physical_Variables::P_ext_data_pt->value(0)
991  << " # external pressure" << std::endl;
992 
993  // Call generic dump()
994  Problem::dump(dump_file);
995 
996  // ...strip displacement control mesh back out after dumping if
997  // we don't actually need it
998  if (!Displ_control)
999  {
1000  flush_sub_meshes();
1001  add_sub_mesh(Bulk_mesh_pt);
1002  add_sub_mesh(Wall_mesh_pt);
1003  rebuild_global_mesh();
1004  assign_eqn_numbers();
1005  }
1006 
1007 
1008 } // end of dump_it
1009 
1010 
1011 
1012 //=================start_of_restart=======================================
1013 /// Read solution from disk for restart
1014 //========================================================================
1015 template <class ELEMENT>
1017 {
1018 
1019 
1020 
1021 // Read external pressure
1022 
1023 // Read line up to termination sign
1024  string input_string;
1025  getline(restart_file,input_string,'#');
1026  restart_file.ignore(80,'\n');
1027 
1029  {
1030  std::cout
1031  << "Increasing external pressure from "
1032  << double(atof(input_string.c_str())) << " to "
1033  << double(atof(input_string.c_str()))+Global_Physical_Variables::P_step
1034  << std::endl;
1035  }
1036  else
1037  {
1038  std::cout << "Running with unchanged external pressure of "
1039  << double(atof(input_string.c_str())) << std::endl;
1040  }
1041 
1042  // Set external pressure
1044  set_value(0,double(atof(input_string.c_str()))+
1046 
1047  // Read the generic problem data from restart file
1048  Problem::read(restart_file);
1049 
1050  //Now update the position of the nodes to be consistent with
1051  //the possible precision loss caused by reading in the data from disk
1052  this->Bulk_mesh_pt->node_update();
1053 
1054  // Strip out displacement control mesh if we don't need it
1055  if (!Displ_control)
1056  {
1057  flush_sub_meshes();
1058  add_sub_mesh(Bulk_mesh_pt);
1059  add_sub_mesh(Wall_mesh_pt);
1060  rebuild_global_mesh();
1061  assign_eqn_numbers();
1062  }
1063 
1064 
1065 } // end of restart
1066 
1067 
1068 
1069 //====start_of_apply_initial_condition========================================
1070 /// Apply initial conditions
1071 //============================================================================
1072 template <class ELEMENT>
1074 {
1075 
1076  // Check that timestepper is from the BDF family
1077  if (!Steady_flag)
1078  {
1079  if (time_stepper_pt()->type()!="BDF")
1080  {
1081  std::ostringstream error_stream;
1082  error_stream << "Timestepper has to be from the BDF family!\n"
1083  << "You have specified a timestepper from the "
1084  << time_stepper_pt()->type() << " family" << std::endl;
1085 
1086  throw OomphLibError(error_stream.str(),
1087  OOMPH_CURRENT_FUNCTION,
1088  OOMPH_EXCEPTION_LOCATION);
1089  }
1090  }
1091 
1092 
1093  // Pointer to restart file
1094  ifstream* restart_file_pt=0;
1095 
1096  // Restart?
1097  //---------
1098 
1099  if (Flags::Restart_file_name!="")
1100  {
1101  // Open restart file
1102  restart_file_pt= new ifstream(Flags::Restart_file_name.c_str(),
1103  ios_base::in);
1104  if (restart_file_pt!=0)
1105  {
1106  cout << "Have opened " << Flags::Restart_file_name <<
1107  " for restart. " << std::endl;
1108  restart(*restart_file_pt);
1109  return;
1110  }
1111  else
1112  {
1113  std::ostringstream error_stream;
1114  error_stream
1115  << "ERROR while trying to open " << Flags::Restart_file_name <<
1116  " for restart." << std::endl;
1117 
1118  throw OomphLibError(
1119  error_stream.str(),
1120  OOMPH_CURRENT_FUNCTION,
1121  OOMPH_EXCEPTION_LOCATION);
1122  }
1123  }
1124 
1125 
1126  // No restart
1127  else
1128  {
1129  // Update the mesh
1130  bulk_mesh_pt()->node_update();
1131 
1132  // Loop over the nodes to set initial guess everywhere
1133  unsigned num_nod = bulk_mesh_pt()->nnode();
1134  for (unsigned n=0;n<num_nod;n++)
1135  {
1136  // Get nodal coordinates
1137  Vector<double> x(2);
1138  x[0]=bulk_mesh_pt()->node_pt(n)->x(0);
1139  x[1]=bulk_mesh_pt()->node_pt(n)->x(1);
1140 
1141  // Assign initial condition: Steady Poiseuille flow
1142  bulk_mesh_pt()->node_pt(n)->set_value(0,6.0*(x[1]/Ly)*(1.0-(x[1]/Ly)));
1143  bulk_mesh_pt()->node_pt(n)->set_value(1,0.0);
1144  }
1145 
1146  // Assign initial values for an impulsive start
1147  bulk_mesh_pt()->assign_initial_values_impulsive();
1148  }
1149 
1150 
1151 } // end of set_initial_condition
1152 
1153 
1154 
1155 
1156 //====steady_run==============================================================
1157 /// Steady run
1158 //============================================================================
1159 template <class ELEMENT>
1161 {
1162 
1163  // Set initial value for external pressure (on the wall stiffness scale).
1164  // This can be overwritten in set_initial_condition.
1166  set_value(0,Global_Physical_Variables::Pmin);
1167 
1168  // Apply initial condition
1169  set_initial_condition();
1170 
1171  //Set output directory
1172  Doc_info.set_directory("RESLT");
1173 
1174  // Open a trace file
1175  ofstream trace_file;
1176  char filename[100];
1177  sprintf(filename,"%s/trace.dat",Doc_info.directory().c_str());
1178  trace_file.open(filename);
1179 
1180  // Write trace file header
1181  trace_file << "VARIABLES=\"p<sub>ext</sub>\","
1182  << "\"y<sub>ctrl</sub>\",";
1183  trace_file << "\"u_1\","
1184  << "\"u_2\","
1185  << "\"CPU time for solve\","
1186  << "\"Number of Newton iterations\","
1187  << std::endl;
1188 
1189  trace_file << "ZONE T=\"";
1190  trace_file << "Re=" << Global_Physical_Variables::Re << ", ";
1191  trace_file << "Q=" << Global_Physical_Variables::Q << ", ";
1192  trace_file << "resolution factor: " << Flags::Resolution_factor << ". ";
1193  trace_file << Flags::Run_identifier_string << "\" ";
1194  trace_file << std::endl;
1195 
1196  // Output the initial solution (dummy for CPU time)
1197  doc_solution_steady(Doc_info,trace_file,0.0,0);
1198 
1199  // Increment step number
1200  Doc_info.number()++;
1201 
1202 
1203  // Increment for external pressure (on the wall stiffness scale)
1204  double delta_p=(Global_Physical_Variables::Pmax-
1206 
1207  // Initial and final values for control position
1209 
1210  // Steady run
1211  double delta_y=
1213  double(Flags::Nsteps-1);
1214 
1215 
1216  // Parameter study
1217  //----------------
1218  for (unsigned istep=0;istep<Flags::Nsteps;istep++)
1219  {
1220 
1221  // Displacement control?
1222  if (Displ_control)
1223  {
1224  std::cout << "Solving for control displ = "
1226  << std::endl;
1227  }
1228  else
1229  {
1230  std::cout << "Solving for p_ext = "
1232  << std::endl;
1233  }
1234 
1235  // Solve the problem
1236  //------------------
1237  clock_t t_start = clock();
1238 
1239  // Explit call to the steady Newton solve.
1240  steady_newton_solve();
1241 
1242  clock_t t_end= clock();
1243  double cpu=double(t_end-t_start)/CLOCKS_PER_SEC;
1244 
1245 
1246  // Outpt the solution
1247  //-------------------
1248  doc_solution_steady(Doc_info,trace_file,cpu,0);
1249 
1250  // Step number
1251  Doc_info.number()++;
1252 
1253  // Adjust control parameter
1254  if (Displ_control)
1255  {
1256  // Increment control position
1258  }
1259  else
1260  {
1261  // Increment external pressure
1262  double old_p=Global_Physical_Variables::P_ext_data_pt->value(0);
1263  Global_Physical_Variables::P_ext_data_pt->set_value(0,old_p+delta_p);
1264  }
1265 
1266  }
1267 
1268  // Close trace file.
1269  trace_file.close();
1270 
1271 }
1272 
1273 
1274 
1275 
1276 
1277 
1278 //====unsteady_run============================================================
1279 /// Unsteady run. Specify timestep or use default of 0.1.
1280 //============================================================================
1281 template <class ELEMENT>
1283 {
1284 
1285  // Set initial value for external pressure (on the wall stiffness scale).
1286  // Will be overwritten by restart data if a restart file (and pressure
1287  // jump) are specified
1289  set_value(0,Global_Physical_Variables::Pmax);
1290 
1291  // Initialise timestep -- also sets the weights for all timesteppers
1292  // in the problem.
1293  initialise_dt(dt);
1294 
1295  std::cout << "Pressure before set initial: "
1297  << std::endl;
1298 
1299  // Apply initial condition
1300  set_initial_condition();
1301 
1302  std::cout << "Pressure after set initial: "
1304  << std::endl;
1305 
1306  //Set output directory
1307  Doc_info.set_directory("RESLT");
1308 
1309  // Open a trace file
1310  ofstream trace_file;
1311  char filename[100];
1312  sprintf(filename,"%s/trace.dat",Doc_info.directory().c_str());
1313  trace_file.open(filename);
1314 
1315 
1316  // Write trace file header
1317  trace_file << "VARIABLES=\"time\","
1318  << "\"y<sub>ctrl</sub>\",";
1319  trace_file << "\"u_1\","
1320  << "\"u_2\","
1321  << "\"CPU time for solve\","
1322  << "\"Number of Newton iterations\""
1323  << std::endl;
1324 
1325  trace_file << "ZONE T=\"";
1326  trace_file << "Re=" << Global_Physical_Variables::Re << ", ";
1327  trace_file << "Q=" << Global_Physical_Variables::Q << ", ";
1328  trace_file << "resolution factor: " << Flags::Resolution_factor << ". ";
1329  trace_file << Flags::Run_identifier_string << "\" ";
1330  trace_file << std::endl;
1331 
1332  // Output the initial solution (dummy for CPU time)
1333  doc_solution_unsteady(Doc_info,trace_file,0.0,0);
1334 
1335  // Increment step number
1336  Doc_info.number()++;
1337 
1338  // Timestepping loop
1339  //------------------
1340  for (unsigned istep=0;istep<Flags::Nsteps;istep++)
1341  {
1342 
1343  // Solve the problem
1344  //------------------
1345  clock_t t_start = clock();
1346 
1347  // Explit call to the unsteady Newton solve.
1348  unsteady_newton_solve(dt);
1349 
1350  clock_t t_end= clock();
1351  double cpu=double(t_end-t_start)/CLOCKS_PER_SEC;
1352 
1353 
1354  // Output the solution
1355  //--------------------
1356  doc_solution_unsteady(Doc_info,trace_file,cpu,0);
1357 
1358  // Step number
1359  Doc_info.number()++;
1360 
1361  }
1362 
1363  // Close trace file.
1364  trace_file.close();
1365 
1366 }
1367 
1368 
OneDLagrangianMesh< FSIHermiteBeamElement > * Wall_mesh_pt
Pointer to the "wall" mesh.
double Ldown
x-length in the downstream part of the channel
GeomObject * Ctrl_geom_obj_pt
Pointer to GeomObject at which displacement control is applied (or at which wall displacement is moni...
MeshAsGeomObject * Wall_geom_object_pt
Pointer to geometric object (one Lagrangian, two Eulerian coordinates) that will be built from the wa...
Vector< double > S_displ_ctrl
Vector of local coordinates of displacement control point in Ctrl_geom_obj_pt.
unsigned Nup
Number of elements in the x direction in the upstream part of the channel.
virtual void steady_run()
Steady run; virtual so it can be overloaded in derived problem classes.
void dump_it(ofstream &dump_file)
Dump problem to disk to allow for restart.
void restart(ifstream &restart_file)
Read problem for restart from specified restart file.
DocInfo Doc_info
DocInfo object.
Node * Wall_node_pt
Pointer to control node on the wall.
unsigned Ncollapsible
Number of elements in the x direction in the collapsible part of the channel.
void actions_after_newton_solve()
Update the problem after solve (empty)
Node * Left_node_pt
Pointer to the left control node.
unsigned Ny
Number of elements across the channel.
Node * Right_node_pt
Pointer to right control node.
unsigned Ndown
Number of elements in the x direction in the downstream part of the channel.
virtual void doc_solution_steady(DocInfo &doc_info, ofstream &trace_file, const double &cpu, const unsigned &niter)
Doc the steady solution.
unsigned Newton_iter
Counter for Newton iterations.
AlgebraicCollapsibleChannelMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the "bulk" mesh.
FSICollapsibleChannelProblem(const unsigned &nup, const unsigned &ncollapsible, const unsigned &ndown, const unsigned &ny, const double &lup, const double &lcollapsible, const double &ldown, const double &ly, const bool &displ_control, const bool &steady_flag)
Constructor: The arguments are the number of elements and the lengths of the domain.
double Lup
x-length in the upstream part of the channel
bool Steady_flag
Flag for steady run.
AlgebraicCollapsibleChannelMesh< ELEMENT > * bulk_mesh_pt()
Access function for the specific bulk (fluid) mesh.
virtual void doc_solution_unsteady(DocInfo &doc_info, ofstream &trace_file, const double &cpu, const unsigned &niter)
Doc the unsteady solution.
~FSICollapsibleChannelProblem()
Destructor.
void actions_before_newton_convergence_check()
Update before checking Newton convergence: Update the nodal positions in the fluid mesh in response t...
Mesh * Displ_control_mesh_pt
Pointer to the mesh that contains the displacement control element.
void actions_before_newton_solve()
Actions before solve. Reset counter for number of Newton iterations.
double Ly
Transverse length.
virtual void unsteady_run(const double &dt=0.1)
Unsteady run; virtual so it can be overloaded in derived problem classes. Specify timestep or use def...
double Lcollapsible
x-length in the collapsible part of the channel
OneDLagrangianMesh< FSIHermiteBeamElement > * wall_mesh_pt()
Access function for the wall mesh.
bool Displ_control
Use displacement control?
void set_initial_condition()
Apply initial conditions.
/////////////////////////////////////////////////////////////////////// /////////////////////////////...
double H
Height of the undeformed wall above y=0.
virtual 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,...
double X0
x position of the undeformed beam's left end.
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.
UndeformedWall(const double &x0, const double &h)
Constructor: arguments are the starting point and the height above y=0.
///////////////////////////////////////////////////////////////////// ///////////////////////////////...
double squash_fct(const double &s)
Mapping [0,1] -> [0,1] that re-distributes nodal points across the channel width.
double Delta
Boundary layer width.
double Fract_in_BL
Fraction of points in boundary layer.
Extend namespace for control flags.
string Restart_file_name
Name of restart file.
string Run_identifier_string
String to identify the run type in trace file.
unsigned Resolution_factor
Resolution factor (multiplier for number of elements across channel)
unsigned Nsteps
Number of steps in parameter study.
unsigned Steady_flag
Steady run (1) or unsteady run (0)
void doc_flags()
Doc flags.
unsigned Use_displ_control
Use displacement control (1) or not (0)
Namespace for phyical parameters.
double ReSt
Womersley = Reynolds times Strouhal.
void load(const Vector< double > &xi, const Vector< double > &x, const Vector< double > &N, Vector< double > &load)
Load function: Apply a constant external pressure to the wall. Note: This is the load without the flu...
double Pmax
Max. pressure. Only used in steady runs during parameter incrementation. Use 2.0 for Re=250; 3....
double Sigma0
2nd Piola Kirchhoff pre-stress. As in Heil (2004) paper.
double Q
Fluid structure interaction parameter: Ratio of stresses used for non-dimensionalisation of fluid to ...
double P_step
Jump in pressure after a restart – used to give a steady solution a kick before starting a time-depen...
double Re
Reynolds number.
Data * P_ext_data_pt
Pointer to Data object that stores external pressure.
double Yprescr_min
Min. of prescribed vertical position of conrol point (only used during parameter study with displacem...
double Pmin
Min. pressure. Only used in steady runs during parameter incrementation. Use 1.5 for values of Re to ...
double H
Non-dimensional wall thickness. As in Heil (2004) paper.
double Yprescr
Current prescribed vertical position of control point (only used for displacement control)