fsi_pseudo_solid_collapsible_channel_adapt.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 "navier_stokes.h"
30 #include "beam.h"
31 #include "solid.h"
32 #include "constitutive.h"
33 
34 // The wall mesh
35 #include "meshes/one_d_lagrangian_mesh.h"
36 
37 //Include the fluid mesh
38 #include "meshes/collapsible_channel_mesh.h"
39 
40 using namespace std;
41 
42 using namespace oomph;
43 
44 
45 //======================================================================
46 /// Upgrade mesh to solid mesh
47 //======================================================================
48 template <class ELEMENT>
50  public virtual RefineableCollapsibleChannelMesh<ELEMENT>,
51  public virtual SolidMesh
52 {
53 
54 
55 public:
56 
57  /// Constructor: Build mesh and copy Eulerian coords to Lagrangian
58  /// ones so that the initial configuration is the stress-free one.
60  const unsigned& ncollapsible,
61  const unsigned& ndown,
62  const unsigned& ny,
63  const double& lup,
64  const double& lcollapsible,
65  const double& ldown,
66  const double& ly,
67  GeomObject* wall_pt,
68  TimeStepper* time_stepper_pt=
69  &Mesh::Default_TimeStepper) :
70  CollapsibleChannelMesh<ELEMENT>(nup, ncollapsible, ndown, ny,
71  lup, lcollapsible, ldown, ly,
72  wall_pt,
73  time_stepper_pt),
74  RefineableCollapsibleChannelMesh<ELEMENT>(nup, ncollapsible, ndown, ny,
75  lup, lcollapsible, ldown, ly,
76  wall_pt,
77  time_stepper_pt)
78 
79  {
80  /// Make the current configuration the undeformed one by
81  /// setting the nodal Lagrangian coordinates to their current
82  /// Eulerian ones
83  set_lagrangian_nodal_coordinates();
84  }
85 
86 };
87 
88 
89 
90 
91 //==========start_of_BL_Squash =========================================
92 /// Namespace to define the mapping [0,1] -> [0,1] that re-distributes
93 /// nodal points across the channel width.
94 //======================================================================
95 namespace BL_Squash
96 {
97 
98  /// Boundary layer width
99  double Delta=0.1;
100 
101  /// Fraction of points in boundary layer
102  double Fract_in_BL=0.5;
103 
104  /// Mapping [0,1] -> [0,1] that re-distributes
105  /// nodal points across the channel width
106  double squash_fct(const double& s)
107  {
108  // Default return
109  double y=s;
110  if (s<0.5*Fract_in_BL)
111  {
112  y=Delta*2.0*s/Fract_in_BL;
113  }
114  else if (s>1.0-0.5*Fract_in_BL)
115  {
116  y=2.0*Delta/Fract_in_BL*s+1.0-2.0*Delta/Fract_in_BL;
117  }
118  else
119  {
120  y=(1.0-2.0*Delta)/(1.0-Fract_in_BL)*s+
121  (Delta-0.5*Fract_in_BL)/(1.0-Fract_in_BL);
122  }
123 
124  return y;
125  }
126 }// end of BL_Squash
127 
128 
129 
130 /// ///////////////////////////////////////////////////////////////////////
131 /// ///////////////////////////////////////////////////////////////////////
132 /// ///////////////////////////////////////////////////////////////////////
133 
134 
135 //====start_of_underformed_wall============================================
136 /// Undeformed wall is a steady, straight 1D line in 2D space
137 /// \f[ x = X_0 + \zeta \f]
138 /// \f[ y = H \f]
139 //=========================================================================
140 class UndeformedWall : public GeomObject
141 {
142 
143 public:
144 
145  /// Constructor: arguments are the starting point and the height
146  /// above y=0.
147  UndeformedWall(const double& x0, const double& h): GeomObject(1,2)
148  {
149  X0=x0;
150  H=h;
151  }
152 
153 
154  /// Position vector at Lagrangian coordinate zeta
155  void position(const Vector<double>& zeta, Vector<double>& r) const
156  {
157  // Position Vector
158  r[0] = zeta[0]+X0;
159  r[1] = H;
160  }
161 
162 
163  /// Parametrised position on object: r(zeta). Evaluated at
164  /// previous timestep. t=0: current time; t>0: previous
165  /// timestep. Calls steady version.
166  void position(const unsigned& t, const Vector<double>& zeta,
167  Vector<double>& r) const
168  {
169  // Use the steady version
170  position(zeta,r);
171 
172  } // end of position
173 
174 
175  /// Posn vector and its 1st & 2nd derivatives
176  /// w.r.t. to coordinates:
177  /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
178  /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ =
179  /// ddrdzeta(alpha,beta,i). Evaluated at current time.
180  virtual void d2position(const Vector<double>& zeta,
181  Vector<double>& r,
182  DenseMatrix<double> &drdzeta,
183  RankThreeTensor<double> &ddrdzeta) const
184  {
185  // Position vector
186  r[0] = zeta[0]+X0;
187  r[1] = H;
188 
189  // Tangent vector
190  drdzeta(0,0)=1.0;
191  drdzeta(0,1)=0.0;
192 
193  // Derivative of tangent vector
194  ddrdzeta(0,0,0)=0.0;
195  ddrdzeta(0,0,1)=0.0;
196 
197  } // end of d2position
198 
199  private :
200 
201  /// x position of the undeformed beam's left end.
202  double X0;
203 
204  /// Height of the undeformed wall above y=0.
205  double H;
206 
207 }; //end_of_undeformed_wall
208 
209 
210 
211 
212 
213 
214 
215 //====start_of_physical_parameters=====================
216 /// Namespace for phyical parameters
217 //======================================================
219 {
220  /// Reynolds number
221  double Re=50.0;
222 
223  /// Womersley = Reynolds times Strouhal
224  double ReSt=50.0;
225 
226  /// Default pressure on the left boundary
227  double P_up=0.0;
228 
229  /// Pseudo-solid mass density
230  double Lambda_sq=0.0;
231 
232  /// Pseudo-solid Poisson ratio
233  double Nu=0.1;
234 
235  /// Traction applied on the fluid at the left (inflow) boundary
236  void prescribed_traction(const double& t,
237  const Vector<double>& x,
238  const Vector<double>& n,
239  Vector<double>& traction)
240  {
241  traction.resize(2);
242  traction[0]=P_up;
243  traction[1]=0.0;
244 
245  } //end traction
246 
247  /// Non-dimensional wall thickness. As in Jensen & Heil (2003) paper.
248  double H=1.0e-2;
249 
250  /// 2nd Piola Kirchhoff pre-stress. As in Jensen & Heil (2003) paper.
251  double Sigma0=1.0e3;
252 
253  /// External pressure
254  double P_ext=0.0;
255 
256  /// Load function: Apply a constant external pressure to the wall.
257  /// Note: This is the load without the fluid contribution!
258  /// Fluid load gets added on by FSIWallElement.
259  void load(const Vector<double>& xi, const Vector<double>& x,
260  const Vector<double>& N, Vector<double>& load)
261  {
262  for(unsigned i=0;i<2;i++)
263  {
264  load[i] = -P_ext*N[i];
265  }
266  } //end of load
267 
268 
269  /// Fluid structure interaction parameter: Ratio of stresses used for
270  /// non-dimensionalisation of fluid to solid stresses.
271  double Q=1.0e-5;
272 
273 
274 } // end of namespace
275 
276 
277 
278 
279 //====start_of_problem_class==========================================
280 /// Problem class
281 //====================================================================
282 template <class ELEMENT>
283 class FSICollapsibleChannelProblem : public Problem
284 {
285 
286  public :
287 
288 /// Constructor: The arguments are the number of elements and
289 /// the lengths of the domain.
290  FSICollapsibleChannelProblem(const unsigned& nup,
291  const unsigned& ncollapsible,
292  const unsigned& ndown,
293  const unsigned& ny,
294  const double& lup,
295  const double& lcollapsible,
296  const double& ldown,
297  const double& ly);
298 
299  /// Destructor (empty)
301 
302  /// Access function for the specific bulk (fluid) mesh
304  {
305  // Upcast from pointer to the Mesh base class to the specific
306  // element type that we're using here.
307  return dynamic_cast<
309  (Bulk_mesh_pt);
310  }
311 
312 
313  /// Access function for the wall mesh
314  OneDLagrangianMesh<FSIHermiteBeamElement>* wall_mesh_pt()
315  {
316  return Wall_mesh_pt;
317 
318  } // end of access to wall mesh
319 
320 
321  /// Actions before adapt: Wipe the mesh of prescribed traction elements
323 
324  /// Actions after adapt: Rebuild the mesh of prescribed traction elements
325  /// and reset FSI
327 
328  /// Update the problem specs before solve (empty)
330 
331  /// Update the problem after solve (empty)
333 
334  /// Update no slip before Newton convergence check
336  {
337  // Moving wall: No slip; this implies that the velocity needs
338  // to be updated in response to wall motion
339  unsigned ibound=3;
340  unsigned num_nod=bulk_mesh_pt()->nboundary_node(ibound);
341  for (unsigned inod=0;inod<num_nod;inod++)
342  {
343  // Which node are we dealing with?
344  Node* node_pt=bulk_mesh_pt()->boundary_node_pt(ibound,inod);
345 
346  // Apply no slip
347  FSI_functions::apply_no_slip_on_moving_wall(node_pt);
348  }
349  }
350 
351 
352  /// Doc the solution
353  void doc_solution(DocInfo& doc_info,ofstream& trace_file);
354 
355  /// Apply initial conditions
357 
358 private :
359 
360  /// Create the prescribed traction elements on boundary b
361  void create_traction_elements(const unsigned &b,
362  Mesh* const &bulk_mesh_pt,
363  Mesh* const &traction_mesh_pt);
364 
365  /// Delete prescribed traction elements from the surface mesh
366  void delete_traction_elements(Mesh* const &traction_mesh_pt);
367 
368  /// Create elements that enforce prescribed boundary motion
369  /// by Lagrange multiplilers
371 
372  /// Delete elements that enforce prescribed boundary motion
373  /// by Lagrange multiplilers
375 
376  /// Number of elements in the x direction in the upstream part of the channel
377  unsigned Nup;
378 
379  /// Number of elements in the x direction in the collapsible part of
380  /// the channel
381  unsigned Ncollapsible;
382 
383  /// Number of elements in the x direction in the downstream part of the channel
384  unsigned Ndown;
385 
386  /// Number of elements across the channel
387  unsigned Ny;
388 
389  /// x-length in the upstream part of the channel
390  double Lup;
391 
392  /// x-length in the collapsible part of the channel
393  double Lcollapsible;
394 
395  /// x-length in the downstream part of the channel
396  double Ldown;
397 
398  /// Transverse length
399  double Ly;
400 
401  /// Pointer to the "bulk" mesh
403 
404  /// Pointer to the "surface" mesh that applies the traction at the
405  /// inflow
406  Mesh* Applied_fluid_traction_mesh_pt;
407 
408  /// Pointers to meshes of Lagrange multiplier elements
409  SolidMesh* Lagrange_multiplier_mesh_pt;
410 
411  /// Pointer to the "wall" mesh
412  OneDLagrangianMesh<FSIHermiteBeamElement>* Wall_mesh_pt;
413 
414  /// Geometric object incarnation of the wall mesh
415  MeshAsGeomObject* Wall_geom_object_pt;
416 
417  /// Pointer to the left control node
418  Node* Left_node_pt;
419 
420  /// Pointer to right control node
421  Node* Right_node_pt;
422 
423  /// Pointer to control node on the wall
424  Node* Wall_node_pt;
425 
426  /// Constitutive law used to determine the mesh deformation
427  ConstitutiveLaw *Constitutive_law_pt;
428 
429 };//end of problem class
430 
431 
432 
433 
434 //=====start_of_constructor======================================
435 /// Constructor for the collapsible channel problem
436 //===============================================================
437 template <class ELEMENT>
439  const unsigned& nup,
440  const unsigned& ncollapsible,
441  const unsigned& ndown,
442  const unsigned& ny,
443  const double& lup,
444  const double& lcollapsible,
445  const double& ldown,
446  const double& ly)
447 {
448  // Store problem parameters
449  Nup=nup;
450  Ncollapsible=ncollapsible;
451  Ndown=ndown;
452  Ny=ny;
453  Lup=lup;
454  Lcollapsible=lcollapsible;
455  Ldown=ldown;
456  Ly=ly;
457 
458 
459  // Overwrite maximum allowed residual to accomodate bad initial guesses
460  Problem::Max_residuals=1000.0;
461 
462  // Allocate the timestepper for the Navier-Stokes equations
463  BDF<2>* fluid_time_stepper_pt=new BDF<2>;
464 
465  // Add the fluid timestepper to the Problem's collection of timesteppers.
466  add_time_stepper_pt(fluid_time_stepper_pt);
467 
468  // Create a dummy Steady timestepper that stores two history values
469  Steady<2>* wall_time_stepper_pt = new Steady<2>;
470 
471  // Add the wall timestepper to the Problem's collection of timesteppers.
472  add_time_stepper_pt(wall_time_stepper_pt);
473 
474  // Geometric object that represents the undeformed wall:
475  // A straight line at height y=ly; starting at x=lup.
476  UndeformedWall* undeformed_wall_pt=new UndeformedWall(lup,ly);
477 
478  //Create the "wall" mesh with FSI Hermite beam elements, passing the
479  //dummy wall timestepper to the constructor
480  Wall_mesh_pt = new OneDLagrangianMesh<FSIHermiteBeamElement>
481  (Ncollapsible,Lcollapsible,undeformed_wall_pt,wall_time_stepper_pt);
482 
483  // Build a geometric object (one Lagrangian, two Eulerian coordinates)
484  // from the wall mesh
485  Wall_geom_object_pt=
486  new MeshAsGeomObject(Wall_mesh_pt);
487 
488 
489 
490  //Build bulk (fluid) mesh
491  Bulk_mesh_pt =
493  (nup, ncollapsible, ndown, ny,
494  lup, lcollapsible, ldown, ly,
495  Wall_geom_object_pt,
496  fluid_time_stepper_pt);
497 
498  // Set a non-trivial boundary-layer-squash function...
499  Bulk_mesh_pt->bl_squash_fct_pt() = &BL_Squash::squash_fct;
500 
501  // ... and update the nodal positions accordingly
502  bool update_all_solid_nodes=true;
503  Bulk_mesh_pt->node_update(update_all_solid_nodes);
504  Bulk_mesh_pt->set_lagrangian_nodal_coordinates();
505 
506  if (CommandLineArgs::Argc>1)
507  {
508  // One round of uniform refinement
509  Bulk_mesh_pt->refine_uniformly();
510  }
511 
512  // Create "surface mesh" that will contain only the prescribed-traction
513  // elements. The constructor just creates the mesh without
514  // giving it any elements, nodes, etc.
515  Applied_fluid_traction_mesh_pt = new Mesh;
516 
517  // Create prescribed-traction elements from all elements that are
518  // adjacent to boundary 5 (left boundary), but add them to a separate mesh.
519  create_traction_elements(5,Bulk_mesh_pt,Applied_fluid_traction_mesh_pt);
520 
521  // Construct the mesh of elements that enforce prescribed boundary motion
522  // by Lagrange multipliers
523  Lagrange_multiplier_mesh_pt=new SolidMesh;
524  create_lagrange_multiplier_elements();
525 
526  // Add the sub meshes to the problem
527  add_sub_mesh(Bulk_mesh_pt);
528  add_sub_mesh(Applied_fluid_traction_mesh_pt);
529  add_sub_mesh(Wall_mesh_pt);
530  add_sub_mesh(Lagrange_multiplier_mesh_pt);
531 
532  // Combine all submeshes into a single Mesh
533  build_global_mesh();
534 
535  //Set errror estimator
536  Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
537  bulk_mesh_pt()->spatial_error_estimator_pt()=error_estimator_pt;
538 
539  //Set the constitutive law
540  Constitutive_law_pt = new GeneralisedHookean(&Global_Physical_Variables::Nu);
541 
542 
543  // Complete build of fluid mesh
544  //-----------------------------
545 
546  // Loop over the elements to set up element-specific
547  // things that cannot be handled by constructor
548  unsigned n_element=Bulk_mesh_pt->nelement();
549  for(unsigned e=0;e<n_element;e++)
550  {
551 
552  // Upcast from GeneralisedElement to the present element
553  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
554 
555  //Set the Reynolds number
556  el_pt->re_pt() = &Global_Physical_Variables::Re;
557 
558  // Set the Womersley number
559  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
560 
561  //Set the constitutive law
562  el_pt->constitutive_law_pt() = Constitutive_law_pt;
563 
564  // Density of pseudo-solid
565  el_pt->lambda_sq_pt()=&Global_Physical_Variables::Lambda_sq;
566 
567  } // end loop over elements
568 
569 
570  // Pin redudant pressure dofs
571  RefineableNavierStokesEquations<2>::
572  pin_redundant_nodal_pressures(Bulk_mesh_pt->element_pt());
573 
574 
575  // Apply boundary conditions for fluid
576  //------------------------------------
577 
578 
579  //Pin the velocity on the boundaries
580  //x and y-velocities pinned along boundary 0 (bottom boundary) :
581  unsigned ibound=0;
582  unsigned num_nod= bulk_mesh_pt()->nboundary_node(ibound);
583  for (unsigned inod=0;inod<num_nod;inod++)
584  {
585  for(unsigned i=0;i<2;i++)
586  {
587  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
588  }
589  }
590 
591  //x and y-velocities pinned along boundaries 2, 3, 4 (top boundaries) :
592  for(ibound=2;ibound<5;ibound++)
593  {
594  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
595  for (unsigned inod=0;inod<num_nod;inod++)
596  {
597  for(unsigned i=0;i<2;i++)
598  {
599  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
600  }
601  }
602  }
603 
604  //y-velocity pinned along boundary 1 (right boundary):
605  ibound=1;
606  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
607  for (unsigned inod=0;inod<num_nod;inod++)
608  {
609  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
610  }
611 
612 
613  //y-velocity pinned along boundary 5 (left boundary):
614  ibound=5;
615  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
616  for (unsigned inod=0;inod<num_nod;inod++)
617  {
618 
619  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
620 
621  }//end of pin_velocity
622 
623 
624  //Pin the nodal position on all boundaries apart from 3 (the moving wall)
625  for (unsigned ibound=0;ibound<6;ibound++)
626  {
627  if (ibound!=3)
628  {
629  unsigned num_nod= bulk_mesh_pt()->nboundary_node(ibound);
630  for (unsigned inod=0;inod<num_nod;inod++)
631  {
632  for(unsigned i=0;i<2;i++)
633  {
634  dynamic_cast<SolidNode*>(bulk_mesh_pt()->
635  boundary_node_pt(ibound, inod))
636  ->pin_position(i);
637  }
638  }
639  }
640  }
641 
642  // Now pin all nodal positions in the rigid bits
643  unsigned nnod=bulk_mesh_pt()->nnode();
644  for (unsigned j=0;j<nnod;j++)
645  {
646  SolidNode* nod_pt=dynamic_cast<SolidNode*>(bulk_mesh_pt()->node_pt(j));
647  if ((nod_pt->x(0)<=lup)||(nod_pt->x(0)>=(lup+lcollapsible)))
648  {
649  for(unsigned i=0;i<2;i++)
650  {
651  nod_pt->pin_position(i);
652  }
653  }
654  }
655 
656 
657  // Complete build of applied traction elements
658  //--------------------------------------------
659 
660  // Loop over the traction elements to pass pointer to prescribed
661  // traction function
662  unsigned n_el=Applied_fluid_traction_mesh_pt->nelement();
663  for(unsigned e=0;e<n_el;e++)
664  {
665  // Upcast from GeneralisedElement to NavierStokes traction element
666  NavierStokesTractionElement<ELEMENT> *el_pt =
667  dynamic_cast< NavierStokesTractionElement<ELEMENT>*>(
668  Applied_fluid_traction_mesh_pt->element_pt(e));
669 
670  // Set the pointer to the prescribed traction function
671  el_pt->traction_fct_pt() = &Global_Physical_Variables::prescribed_traction;
672 
673  }
674 
675 
676 
677 
678  // Complete build of wall elements
679  //--------------------------------
680 
681  //Loop over the elements to set physical parameters etc.
682  n_element = wall_mesh_pt()->nelement();
683  for(unsigned e=0;e<n_element;e++)
684  {
685  // Upcast to the specific element type
686  FSIHermiteBeamElement *elem_pt =
687  dynamic_cast<FSIHermiteBeamElement*>(wall_mesh_pt()->element_pt(e));
688 
689  // Set physical parameters for each element:
690  elem_pt->sigma0_pt() = &Global_Physical_Variables::Sigma0;
691  elem_pt->h_pt() = &Global_Physical_Variables::H;
692 
693  // Set the load vector for each element
694  elem_pt->load_vector_fct_pt() = &Global_Physical_Variables::load;
695 
696  // Function that specifies the load ratios
697  elem_pt->q_pt() = &Global_Physical_Variables::Q;
698 
699  // Set the undeformed shape for each element
700  elem_pt->undeformed_beam_pt() = undeformed_wall_pt;
701 
702  // The normal on the wall elements as computed by the FSIHermiteElements
703  // points away from the fluid rather than into the fluid (as assumed
704  // by default)
705  elem_pt->set_normal_pointing_out_of_fluid();
706 
707  } // end of loop over elements
708 
709 
710 
711  // Boundary conditions for wall mesh
712  //----------------------------------
713 
714  // Set the boundary conditions: Each end of the beam is fixed in space
715  // Loop over the boundaries (ends of the beam)
716  for(unsigned b=0;b<2;b++)
717  {
718  // Pin displacements in both x and y directions
719  wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(0);
720  wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(1);
721  }
722 
723 
724 
725  //Choose control nodes
726  //---------------------
727 
728  // Left boundary
729  ibound=5;
730  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
731  unsigned control_nod=num_nod/2;
732  Left_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
733 
734  // Right boundary
735  ibound=1;
736  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
737  control_nod=num_nod/2;
738  Right_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
739 
740 
741  // Set the pointer to the control node on the wall
742  num_nod= wall_mesh_pt()->nnode();
743  Wall_node_pt=wall_mesh_pt()->node_pt(Ncollapsible/2);
744 
745 
746 
747  // Setup FSI
748  //----------
749 
750  // The velocity of the fluid nodes on the wall (fluid mesh boundary 3)
751  // is set by the wall motion -- hence the no-slip condition needs to be
752  // re-applied whenever a node update is performed for these nodes.
753  // Such tasks may be performed automatically by the auxiliary node update
754  // function specified by a function pointer:
755  ibound=3;
756  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
757  for (unsigned inod=0;inod<num_nod;inod++)
758  {
759  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->
760  set_auxiliary_node_update_fct_pt(
761  FSI_functions::apply_no_slip_on_moving_wall);
762  }
763 
764  // Work out which fluid dofs affect the residuals of the wall elements:
765  // We pass the boundary between the fluid and solid meshes and
766  // pointers to the meshes. The interaction boundary is boundary 3 of the
767  // 2D fluid mesh.
768  FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
769  (this,3,Bulk_mesh_pt,Wall_mesh_pt);
770 
771  // Setup equation numbering scheme
772  cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
773 
774 
775 }//end of constructor
776 
777 
778 
779 
780 //====start_of_doc_solution===================================================
781 /// Doc the solution
782 //============================================================================
783 template <class ELEMENT>
785  ofstream& trace_file)
786 {
787  ofstream some_file;
788  char filename[100];
789 
790  // Number of plot points
791  unsigned npts;
792  npts=5;
793 
794  // Output fluid solution
795  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
796  doc_info.number());
797  some_file.open(filename);
798  bulk_mesh_pt()->output(some_file,npts);
799  some_file.close();
800 
801  // Document the wall shape
802  sprintf(filename,"%s/beam%i.dat",doc_info.directory().c_str(),
803  doc_info.number());
804  some_file.open(filename);
805  wall_mesh_pt()->output(some_file,npts);
806  some_file.close();
807 
808  // Loop over all elements do dump out previous solutions
809  // (get the number of previous timesteps available from the wall
810  // timestepper)
811  unsigned nsteps=time_stepper_pt(1)->nprev_values();
812  for (unsigned t=0;t<=nsteps;t++)
813  {
814  sprintf(filename,"%s/wall%i-%i.dat",doc_info.directory().c_str(),
815  doc_info.number(),t);
816  some_file.open(filename);
817  unsigned n_elem=wall_mesh_pt()->nelement();
818  for (unsigned ielem=0;ielem<n_elem;ielem++)
819  {
820  dynamic_cast<FSIHermiteBeamElement*>(wall_mesh_pt()->element_pt(ielem))->
821  output(t,some_file,npts);
822  }
823  some_file.close();
824  } // end of output of previous solutions
825 
826 
827 
828  // Write trace file
829  trace_file << time_pt()->time() << " "
830  << Wall_node_pt->x(1) << " "
831  << Left_node_pt->value(0) << " "
832  << Right_node_pt->value(0) << " "
834  << std::endl;
835 
836 } // end_of_doc_solution
837 
838 
839 
840 
841 
842 //=====start_of_create_traction_elements======================================
843 /// Create the traction elements
844 //============================================================================
845 template <class ELEMENT>
847  const unsigned &b, Mesh* const &bulk_mesh_pt, Mesh* const &traction_mesh_pt)
848 {
849 
850  // How many bulk elements are adjacent to boundary b?
851  unsigned n_element = bulk_mesh_pt->nboundary_element(b);
852 
853  // Loop over the bulk elements adjacent to boundary b?
854  for(unsigned e=0;e<n_element;e++)
855  {
856  // Get pointer to the bulk element that is adjacent to boundary b
857  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>
858  (bulk_mesh_pt->boundary_element_pt(b,e));
859 
860  //What is the index of the face of element e along boundary
861  int face_index = bulk_mesh_pt->face_index_at_boundary(b,e);
862 
863  // Build the corresponding prescribed-traction element
864  NavierStokesTractionElement<ELEMENT>* flux_element_pt =
865  new NavierStokesTractionElement<ELEMENT>(bulk_elem_pt,face_index);
866 
867  //Add the prescribed-traction element to the surface mesh
868  traction_mesh_pt->add_element_pt(flux_element_pt);
869 
870  } //end of loop over bulk elements adjacent to boundary b
871 
872 } // end of create_traction_elements
873 
874 
875 
876 //============start_of_delete_traction_elements==============================
877 /// Delete traction elements and wipe the surface mesh
878 //=======================================================================
879 template<class ELEMENT>
881 delete_traction_elements(Mesh* const &surface_mesh_pt)
882 {
883  // How many surface elements are in the surface mesh
884  unsigned n_element = surface_mesh_pt->nelement();
885 
886  // Loop over the surface elements
887  for(unsigned e=0;e<n_element;e++)
888  {
889  // Kill surface element
890  delete surface_mesh_pt->element_pt(e);
891  }
892 
893  // Wipe the mesh
894  surface_mesh_pt->flush_element_and_node_storage();
895 
896 } // end of delete_traction_elements
897 
898 
899 //============start_of_create_lagrange_multiplier_elements===============
900 /// Create elements that impose the prescribed boundary displacement
901 //=======================================================================
902 template<class ELEMENT>
905 {
906  // Lagrange multiplier elements are located on boundary 3:
907  unsigned b=3;
908 
909  // How many bulk elements are adjacent to boundary b?
910  unsigned n_element = bulk_mesh_pt()->nboundary_element(b);
911 
912  // Loop over the bulk elements adjacent to boundary b?
913  for(unsigned e=0;e<n_element;e++)
914  {
915  // Get pointer to the bulk element that is adjacent to boundary b
916  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
917  bulk_mesh_pt()->boundary_element_pt(b,e));
918 
919  //Find the index of the face of element e along boundary b
920  int face_index = bulk_mesh_pt()->face_index_at_boundary(b,e);
921 
922  // Create new element and add to mesh
923  Lagrange_multiplier_mesh_pt->add_element_pt(
924  new ImposeDisplacementByLagrangeMultiplierElement<ELEMENT>(
925  bulk_elem_pt,face_index));
926  }
927 
928 
929  // Loop over the elements in the Lagrange multiplier element mesh
930  // for elements on the top boundary (boundary 3)
931  n_element=Lagrange_multiplier_mesh_pt->nelement();
932  for(unsigned i=0;i<n_element;i++)
933  {
934  //Cast to a Lagrange multiplier element
935  ImposeDisplacementByLagrangeMultiplierElement<ELEMENT> *el_pt =
936  dynamic_cast<ImposeDisplacementByLagrangeMultiplierElement<ELEMENT>*>
937  (Lagrange_multiplier_mesh_pt->element_pt(i));
938 
939  // Set the GeomObject that defines the boundary shape and set
940  // which bulk boundary we are attached to(needed to extract
941  // the boundary coordinate from the bulk nodes)
942  el_pt->set_boundary_shape_geom_object_pt(Wall_geom_object_pt,b);
943 
944  // Loop over the nodes
945  unsigned nnod=el_pt->nnode();
946  for (unsigned j=0;j<nnod;j++)
947  {
948  Node* nod_pt = el_pt->node_pt(j);
949 
950  // Is the node also on boundary 2 or 4?
951  if ((nod_pt->is_on_boundary(2))||(nod_pt->is_on_boundary(4)))
952  {
953  // How many nodal values were used by the "bulk" element
954  // that originally created this node?
955  unsigned n_bulk_value=el_pt->nbulk_value(j);
956 
957  // The remaining ones are Lagrange multipliers and we pin them.
958  unsigned nval=nod_pt->nvalue();
959  for (unsigned j=n_bulk_value;j<nval;j++)
960  {
961  nod_pt->pin(j);
962  }
963  }
964  }
965  }
966 
967 } // end of create_lagrange_multiplier_elements
968 
969 
970 
971 
972 //====start_of_delete_lagrange_multiplier_elements=======================
973 /// Delete elements that impose the prescribed boundary displacement
974 /// and wipe the associated mesh
975 //=======================================================================
976 template<class ELEMENT>
979 {
980  // How many surface elements are in the surface mesh
981  unsigned n_element = Lagrange_multiplier_mesh_pt->nelement();
982 
983  // Loop over the surface elements
984  for(unsigned e=0;e<n_element;e++)
985  {
986  // Kill surface element
987  delete Lagrange_multiplier_mesh_pt->element_pt(e);
988  }
989 
990  // Wipe the mesh
991  Lagrange_multiplier_mesh_pt->flush_element_and_node_storage();
992 
993 } // end of delete_lagrange_multiplier_elements
994 
995 
996 
997 //====start_of_apply_initial_condition========================================
998 /// Apply initial conditions
999 //============================================================================
1000 template <class ELEMENT>
1002 {
1003  // Check that timestepper is from the BDF family
1004  if (time_stepper_pt()->type()!="BDF")
1005  {
1006  std::ostringstream error_stream;
1007  error_stream << "Timestepper has to be from the BDF family!\n"
1008  << "You have specified a timestepper from the "
1009  << time_stepper_pt()->type() << " family" << std::endl;
1010 
1011  throw OomphLibError(error_stream.str(),
1012  OOMPH_CURRENT_FUNCTION,
1013  OOMPH_EXCEPTION_LOCATION);
1014  }
1015 
1016  // Loop over the nodes to set initial guess everywhere
1017  unsigned num_nod = bulk_mesh_pt()->nnode();
1018  for (unsigned n=0;n<num_nod;n++)
1019  {
1020  // Get nodal coordinates
1021  Vector<double> x(2);
1022  x[0]=bulk_mesh_pt()->node_pt(n)->x(0);
1023  x[1]=bulk_mesh_pt()->node_pt(n)->x(1);
1024 
1025  // Assign initial condition: Steady Poiseuille flow
1026  bulk_mesh_pt()->node_pt(n)->set_value(0,6.0*(x[1]/Ly)*(1.0-(x[1]/Ly)));
1027  bulk_mesh_pt()->node_pt(n)->set_value(1,0.0);
1028  }
1029 
1030  // Assign initial values for an impulsive start
1031  bulk_mesh_pt()->assign_initial_values_impulsive();
1032  wall_mesh_pt()->assign_initial_values_impulsive();
1033 
1034 } // end of set_initial_condition
1035 
1036 
1037 
1038 
1039 
1040 //=========start_of_actions_before_adapt==================================
1041 /// Actions before adapt: Wipe the mesh of prescribed traction elements
1042 //========================================================================
1043 template<class ELEMENT>
1045 {
1046  // Kill the traction elements and wipe surface mesh
1047  delete_traction_elements(Applied_fluid_traction_mesh_pt);
1048 
1049  // Kill the elements and wipe surface mesh
1050  delete_lagrange_multiplier_elements();
1051 
1052  // Rebuild the global mesh.
1053  rebuild_global_mesh();
1054 
1055 } // end of actions_before_adapt
1056 
1057 
1058 
1059 //==========start_of_actions_after_adapt==================================
1060 /// Actions after adapt: Rebuild the mesh of prescribed traction elements
1061 //========================================================================
1062 template<class ELEMENT>
1064 {
1065  // Create prescribed-flux elements from all elements that are
1066  // adjacent to boundary 5 and add them to surface mesh
1067  create_traction_elements(5,Bulk_mesh_pt,Applied_fluid_traction_mesh_pt);
1068 
1069  // Create the elements that impose the displacement constraint
1070  // and attach them to the bulk elements that are
1071  // adjacent to the moving boundary
1072  create_lagrange_multiplier_elements();
1073 
1074  // Rebuild the global mesh
1075  rebuild_global_mesh();
1076 
1077  // Unpin all pressure dofs
1078  RefineableNavierStokesEquations<2>::
1079  unpin_all_pressure_dofs(Bulk_mesh_pt->element_pt());
1080 
1081  // Pin redundant pressure dofs
1082  RefineableNavierStokesEquations<2>::
1083  pin_redundant_nodal_pressures(Bulk_mesh_pt->element_pt());
1084 
1085  // Loop over the traction elements to pass pointer to prescribed
1086  // traction function
1087  unsigned n_element=Applied_fluid_traction_mesh_pt->nelement();
1088  for(unsigned e=0;e<n_element;e++)
1089  {
1090  // Upcast from GeneralisedElement to NavierStokesTractionElement element
1091  NavierStokesTractionElement<ELEMENT> *el_pt =
1092  dynamic_cast<NavierStokesTractionElement<ELEMENT>*>(
1093  Applied_fluid_traction_mesh_pt->element_pt(e));
1094 
1095  // Set the pointer to the prescribed traction function
1096  el_pt->traction_fct_pt() = &Global_Physical_Variables::prescribed_traction;
1097  }
1098 
1099  // The functions used to update the no slip boundary conditions
1100  // must be set on any new nodes that have been created during the
1101  // mesh adaptation process.
1102  // There is no mechanism by which auxiliary update functions
1103  // are copied to newly created nodes.
1104  // (because, unlike boundary conditions, they don't occur exclusively
1105  // at boundaries)
1106 
1107  // The velocity of the fluid nodes on the wall (fluid mesh boundary 3)
1108  // is set by the wall motion -- hence the no-slip condition needs to be
1109  // re-applied whenever a node update is performed for these nodes.
1110  // Such tasks may be performed automatically by the auxiliary node update
1111  // function specified by a function pointer:
1112  unsigned ibound=3;
1113  unsigned num_nod=bulk_mesh_pt()->nboundary_node(ibound);
1114  for (unsigned inod=0;inod<num_nod;inod++)
1115  {
1116  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->
1117  set_auxiliary_node_update_fct_pt(
1118  FSI_functions::apply_no_slip_on_moving_wall);
1119  }
1120 
1121  // (Re-)setup fsi: Work out which fluid dofs affect wall elements
1122  // the correspondance between wall dofs and fluid elements is handled
1123  // during the remeshing, but the "reverse" association must be done
1124  // separately. We need to set up the interaction every time because the fluid
1125  // element adjacent to a given solid element's integration point may have
1126  // changed.We pass the boundary between the fluid and solid meshes and
1127  // pointers to the meshes. The interaction boundary is boundary 3 of
1128  // the Fluid mesh.
1129  FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
1130  (this,3,Bulk_mesh_pt,Wall_mesh_pt);
1131 
1132 } // end of actions_after_adapt
1133 
1134 
1135 
1136 //============start_of_main====================================================
1137 /// Driver code for a collapsible channel problem with FSI.
1138 /// Presence of command line arguments indicates validation run with
1139 /// coarse resolution and small number of timesteps.
1140 //=============================================================================
1141 int main(int argc, char* argv[])
1142 {
1143  // Store command line arguments
1144  CommandLineArgs::setup(argc,argv);
1145 
1146  // Reduction in resolution for validation run?
1147  unsigned coarsening_factor=4;
1148  if (CommandLineArgs::Argc>1)
1149  {
1150  coarsening_factor=4;
1151  }
1152 
1153  // Number of elements in the domain
1154  unsigned nup=20/coarsening_factor;
1155  unsigned ncollapsible=40/coarsening_factor;
1156  unsigned ndown=40/coarsening_factor;
1157  unsigned ny=16/coarsening_factor;
1158 
1159  // Length of the domain
1160  double lup=5.0;
1161  double lcollapsible=10.0;
1162  double ldown=10.0;
1163  double ly=1.0;
1164 
1165  // Set external pressure (on the wall stiffness scale).
1167 
1168  // Pressure on the left boundary: This is consistent with steady
1169  // Poiseuille flow
1170  Global_Physical_Variables::P_up=12.0*(lup+lcollapsible+ldown);
1171 
1172 
1173 #ifdef TAYLOR_HOOD
1174 
1175  // Build the problem with QtaylorHoodElements
1177  <RefineablePseudoSolidNodeUpdateElement<
1178  RefineableQTaylorHoodElement<2>,
1179  RefineableQPVDElement<2,3> > >
1180  problem(nup, ncollapsible, ndown, ny,
1181  lup, lcollapsible, ldown, ly);
1182 
1183 #else
1184 
1185  // Build the problem with QCrouzeixRaviartElements
1187  <RefineablePseudoSolidNodeUpdateElement<
1188  RefineableQCrouzeixRaviartElement<2>,
1189  RefineableQPVDElement<2,3> > >
1190  problem(nup, ncollapsible, ndown, ny,
1191  lup, lcollapsible, ldown, ly);
1192 
1193 #endif
1194 
1195  // Timestep. Note: Preliminary runs indicate that the period of
1196  // the oscillation is about 1 so this gives us 40 steps per period.
1197  double dt=1.0/40.0;
1198 
1199  // Initial time for the simulation
1200  double t_min=0.0;
1201 
1202  // Maximum time for simulation
1203  double t_max=3.5;
1204 
1205  // Initialise timestep
1206  problem.time_pt()->time()=t_min;
1207  problem.initialise_dt(dt);
1208 
1209  // Apply initial condition
1210  problem.set_initial_condition();
1211 
1212  //Set output directory
1213  DocInfo doc_info;
1214  doc_info.set_directory("RESLT");
1215 
1216  // Open a trace file
1217  ofstream trace_file;
1218  char filename[100];
1219  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
1220  trace_file.open(filename);
1221 
1222  // Output the initial condition
1223  problem.doc_solution(doc_info, trace_file);
1224 
1225  // Increment step number
1226  doc_info.number()++;
1227 
1228  // Find number of timesteps (reduced for validation)
1229  unsigned nstep = unsigned((t_max-t_min)/dt);
1230  if (CommandLineArgs::Argc>1)
1231  {
1232  nstep=3;
1233  }
1234 
1235 
1236  // Set targets for spatial adaptivity
1237  problem.bulk_mesh_pt()->max_permitted_error()=1.0e-3;
1238  problem.bulk_mesh_pt()->min_permitted_error()=1.0e-5;
1239 
1240  // Overwrite for validation run
1241  if (CommandLineArgs::Argc>1)
1242  {
1243  // Set targets for spatial adaptivity
1244  problem.bulk_mesh_pt()->max_permitted_error()=0.5e-2;
1245  problem.bulk_mesh_pt()->min_permitted_error()=0.5e-4;
1246  }
1247 
1248  // When performing the first timestep, we can adapt the mesh as many times
1249  // as we want because the initial condition can be re-set
1250  unsigned max_adapt=3;
1251  bool first=true;
1252 
1253  // Timestepping loop
1254  for (unsigned istep=0;istep<nstep;istep++)
1255  {
1256  // Solve the problem
1257  problem.unsteady_newton_solve(dt,max_adapt,first);
1258 
1259  // Outpt the solution
1260  problem.doc_solution(doc_info, trace_file);
1261 
1262  // Step number
1263  doc_info.number()++;
1264 
1265  // We've done the first step
1266  first=false;
1267  max_adapt=1;
1268  }
1269 
1270  // Close trace file.
1271  trace_file.close();
1272 
1273 }//end of main
1274 
void create_lagrange_multiplier_elements()
Create elements that enforce prescribed boundary motion by Lagrange multiplilers.
void actions_after_newton_solve()
Update the problem after solve (empty)
void actions_before_adapt()
Actions before adapt: Wipe the mesh of prescribed traction elements.
void delete_lagrange_multiplier_elements()
Delete elements that enforce prescribed boundary motion by Lagrange multiplilers.
ElasticRefineableCollapsibleChannelMesh< ELEMENT > * bulk_mesh_pt()
Access function for the specific bulk (fluid) mesh.
ElasticRefineableCollapsibleChannelMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the "bulk" mesh.
void actions_before_newton_convergence_check()
Update no slip before Newton convergence check.
void actions_before_newton_solve()
Update the problem specs before solve (empty)
void actions_after_adapt()
Actions after adapt: Rebuild the mesh of prescribed traction elements and reset FSI.
OneDLagrangianMesh< FSIHermiteBeamElement > * wall_mesh_pt()
Access function for the wall mesh.
void delete_traction_elements(Mesh *const &traction_mesh_pt)
Delete prescribed traction elements from the surface mesh.
void create_traction_elements(const unsigned &b, Mesh *const &bulk_mesh_pt, Mesh *const &traction_mesh_pt)
Create the prescribed traction elements on boundary b.
void set_initial_condition()
Apply initial conditions.
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)
Constructor: The arguments are the number of elements and the lengths of the domain.
void doc_solution(DocInfo &doc_info, ofstream &trace_file)
Doc the solution.
/////////////////////////////////////////////////////////////////////// /////////////////////////////...
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,...
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.
int main(int argc, char *argv[])
Driver code for a collapsible channel problem with FSI. Presence of command line arguments indicates ...
Namespace to define the mapping [0,1] -> [0,1] that re-distributes nodal points across the channel wi...
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.
Namespace for phyical parameters.
double P_ext
External pressure.
double ReSt
Womersley = Reynolds times Strouhal.
void prescribed_traction(const double &t, const Vector< double > &x, const Vector< double > &n, Vector< double > &traction)
Traction applied on the fluid at the left (inflow) boundary.
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 Nu
Pseudo-solid Poisson ratio.
double Sigma0
2nd Piola Kirchhoff pre-stress. As in Jensen & Heil (2003) paper.
double Q
Fluid structure interaction parameter: Ratio of stresses used for non-dimensionalisation of fluid to ...
double Lambda_sq
Pseudo-solid mass density.
double P_up
Default pressure on the left boundary.
double H
Non-dimensional wall thickness. As in Jensen & Heil (2003) paper.