fsi_channel_with_leaflet.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-2025 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// Generic oomph-lib includes
27#include "generic.h"
28#include "beam.h"
29#include "navier_stokes.h"
30#include "multi_physics.h"
31
32//Include the mesh
33#include "meshes/channel_with_leaflet_mesh.h"
34
35// The wall mesh
36#include "meshes/one_d_lagrangian_mesh.h"
37
38
39using namespace std;
40using namespace oomph;
41
42
43//==== start_of_global_parameters================================
44/// Global parameters
45//===============================================================
47{
48 /// Reynolds number
49 double Re=50.0;
50
51 /// Womersley number: Product of Reynolds and Strouhal numbers
52 double ReSt=50.0;
53
54 /// Non-dimensional wall thickness.
55 double H=0.05;
56
57 /// Fluid structure interaction parameter: Ratio of stresses used for
58 /// non-dimensionalisation of fluid to solid stresses.
59 double Q=1.0e-6;
60
61 /// Period for fluctuations in flux
62 double Period=2.0;
63
64 /// Min. flux
65 double Min_flux=1.0;
66
67 /// Max. flux
68 double Max_flux=2.0;
69
70 /// Flux: Pulsatile flow fluctuating between Min_flux and Max_flux
71 /// with period Period
72 double flux(const double& t)
73 {
74 return Min_flux+
75 (Max_flux-Min_flux)*0.5*(1.0-cos(2.0*MathematicalConstants::Pi*t/Period));
76 }
77
78} // end_of_namespace
79
80
81
82///////////////////////////////////////////////////////////////////////
83//////////////////////////////////////////////////////////////////////
84///////////////////////////////////////////////////////////////////////
85
86
87//=====start_of_undeformed_leaflet=================================
88/// GeomObject: Undeformed straight, vertical leaflet
89//=================================================================
90class UndeformedLeaflet : public GeomObject
91{
92
93public:
94
95 /// Constructor: argument is the x-coordinate of the leaflet
96 UndeformedLeaflet(const double& x0): GeomObject(1,2)
97 {
98 X0=x0;
99 }
100
101 /// Position vector at Lagrangian coordinate zeta
102 void position(const Vector<double>& zeta, Vector<double>& r) const
103 {
104 // Position Vector
105 r[0] = X0;
106 r[1] = zeta[0];
107 }
108
109
110 /// Parametrised position on object: r(zeta). Evaluated at
111 /// previous timestep. t=0: current time; t>0: previous
112 /// timestep. Calls steady version.
113 void position(const unsigned& t, const Vector<double>& zeta,
114 Vector<double>& r) const
115 {
116 // Use the steady version
117 position(zeta,r);
118 } // end of position
119
120
121 /// Posn vector and its 1st & 2nd derivatives
122 /// w.r.t. to coordinates:
123 /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
124 /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ =
125 /// ddrdzeta(alpha,beta,i). Evaluated at current time.
126 void d2position(const Vector<double>& zeta,
127 Vector<double>& r,
128 DenseMatrix<double> &drdzeta,
129 RankThreeTensor<double> &ddrdzeta) const
130 {
131 // Position vector
132 r[0] = X0;
133 r[1] = zeta[0];
134
135 // Tangent vector
136 drdzeta(0,0)=0.0;
137 drdzeta(0,1)=1.0;
138
139 // Derivative of tangent vector
140 ddrdzeta(0,0,0)=0.0;
141 ddrdzeta(0,0,1)=0.0;
142 } // end of d2position
143
144 /// Number of geometric Data in GeomObject: None.
145 unsigned ngeom_data() const {return 0;}
146
147 private :
148
149 /// x position of the undeformed leaflet's origin.
150 double X0;
151
152}; //end_of_undeformed_wall
153
154
155///////////////////////////////////////////////////////////////////////
156//////////////////////////////////////////////////////////////////////
157///////////////////////////////////////////////////////////////////////
158
159
160//=====start_of_problem_class========================================
161/// FSI leaflet in channel
162//===================================================================
163template<class ELEMENT>
164class FSIChannelWithLeafletProblem : public Problem
165{
166
167public:
168
169 /// Constructor: Pass the lenght of the domain at the left
170 /// of the leaflet lleft,the lenght of the domain at the right of the
171 /// leaflet lright,the height of the leaflet hleaflet, the total height
172 /// of the domain htot, the number of macro-elements at the left of the
173 /// leaflet nleft, the number of macro-elements at the right of the
174 /// leaflet nright, the number of macro-elements under hleaflet ny1,
175 /// the number of macro-elements above hleaflet ny2, the abscissa
176 /// of the origin of the leaflet x_0.
177 FSIChannelWithLeafletProblem(const double& lleft,
178 const double& lright, const double& hleaflet,
179 const double& htot,
180 const unsigned& nleft, const unsigned& nright,
181 const unsigned& ny1, const unsigned& ny2,
182 const double& x_0);
183
184 /// Destructor empty
186
187 /// Actions after solve (empty)
189
190 /// Actions before solve (empty)
192
193 /// Actions after adaptation
194 void actions_after_adapt();
195
196 /// Access function to the wall mesh
197 OneDLagrangianMesh<FSIHermiteBeamElement>* wall_mesh_pt()
198 {
199 return Wall_mesh_pt;
200 }
201
202 /// Access function to fluid mesh
203 RefineableAlgebraicChannelWithLeafletMesh<ELEMENT>* fluid_mesh_pt()
204 {
205 return Fluid_mesh_pt;
206 }
207
208 /// Doc the solution
209 void doc_solution(DocInfo& doc_info, ofstream& trace);
210
211
212/// Update the inflow velocity
214 {
215 // Actual time
216 double t=time_pt()->time();
217
218 // Amplitude of flow
219 double ampl=Global_Physical_Variables::flux(t);
220
221 // Update parabolic flow along boundary 3
222 unsigned ibound=3;
223 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
224 for (unsigned inod=0;inod<num_nod;inod++)
225 {
226 double ycoord = Fluid_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
227 double uy = ampl*6.0*ycoord/Htot*(1.0-ycoord/Htot);
228 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,uy);
229 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
230 }
231 } // end of actions_before_implicit_timestep
232
233 /// Update before checking Newton convergence: Update the
234 /// nodal positions in the fluid mesh in response to possible
235 /// changes in the wall shape
237 {
238 Fluid_mesh_pt->node_update();
239 }
240
241private:
242
243 /// Pointer to the fluid mesh
244 RefineableAlgebraicChannelWithLeafletMesh<ELEMENT>* Fluid_mesh_pt;
245
246 /// Pointer to the "wall" mesh
247 OneDLagrangianMesh<FSIHermiteBeamElement>* Wall_mesh_pt;
248
249 /// Pointer to the GeomObject that represents the wall
250 GeomObject* Leaflet_pt;
251
252 /// Total height of the domain
253 double Htot;
254
255};
256
257
258
259
260
261//=====start_of_constructor==============================================
262/// Constructor
263//=======================================================================
264template <class ELEMENT>
266 const double& lleft,
267 const double& lright,
268 const double& hleaflet,
269 const double& htot,
270 const unsigned& nleft,
271 const unsigned& nright,
272 const unsigned& ny1,
273 const unsigned& ny2,
274 const double& x_0) : Htot(htot)
275{
276 // Timesteppers:
277 //--------------
278
279 // Allocate the timestepper
280 BDF<2>* fluid_time_stepper_pt=new BDF<2>;
281 add_time_stepper_pt(fluid_time_stepper_pt);
282
283 // Allocate the wall timestepper
284 Steady<2>* wall_time_stepper_pt=new Steady<2>;
285 add_time_stepper_pt(wall_time_stepper_pt);
286
287
288 // Discretise leaflet
289 //-------------------
290
291 // Geometric object that represents the undeformed leaflet
292 UndeformedLeaflet* undeformed_wall_pt=new UndeformedLeaflet(x_0);
293
294 //Create the "wall" mesh with FSI Hermite beam elements
295 unsigned n_wall_el=5;
296 Wall_mesh_pt = new OneDLagrangianMesh<FSIHermiteBeamElement>
297 (n_wall_el,hleaflet,undeformed_wall_pt,wall_time_stepper_pt);
298
299
300 // Provide GeomObject representation of leaflet mesh and build fluid mesh
301 //-----------------------------------------------------------------------
302
303 // Build a geometric object (one Lagrangian, two Eulerian coordinates)
304 // from the wall mesh
305 MeshAsGeomObject* wall_geom_object_pt=
306 new MeshAsGeomObject(Wall_mesh_pt);
307
308//Build the mesh
309 Fluid_mesh_pt =new RefineableAlgebraicChannelWithLeafletMesh<ELEMENT>(
310 wall_geom_object_pt,
311 lleft, lright,
312 hleaflet,
313 htot,nleft,
314 nright,ny1,ny2,
315 fluid_time_stepper_pt);
316
317 // Set error estimator
318 Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
319 Fluid_mesh_pt->spatial_error_estimator_pt()=error_estimator_pt;
320
321
322
323 // Build global mesh
324 //------------------
325
326 // Add the sub meshes to the problem
327 add_sub_mesh(Fluid_mesh_pt);
328 add_sub_mesh(Wall_mesh_pt);
329
330 // Combine all submeshes into a single Mesh
331 build_global_mesh();
332
333
334
335 // Fluid boundary conditions
336 //--------------------------
337
338 //Pin the boundary nodes of the fluid mesh
339 unsigned num_bound = Fluid_mesh_pt->nboundary();
340 for(unsigned ibound=0;ibound<num_bound;ibound++)
341 {
342 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
343 for (unsigned inod=0;inod<num_nod;inod++)
344 {
345 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->pin(1);
346
347 // Do not pin the x velocity of the outflow
348 if( ibound != 1)
349 {
350 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->pin(0);
351 }
352 }
353 }
354 // end loop over boundaries
355
356
357 // Setup parabolic flow along boundary 3 (everything else that's
358 // pinned has homogenous boundary conditions so no action is required
359 // as that's the default assignment). Inflow profile is parabolic
360 // and this is interpolated correctly during mesh refinement so
361 // no re-assignment necessary after adaptation.
362 unsigned ibound=3;
363 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
364 for (unsigned inod=0;inod<num_nod;inod++)
365 {
366 double ycoord = Fluid_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
367 double uy = 6.0*ycoord/htot*(1.0-ycoord/htot);
368 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,uy);
369 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
370 }// end of setup boundary condition
371
372
373
374 // Boundary conditions for wall mesh
375 //----------------------------------
376
377 // Set the boundary conditions: the lower end of the beam is fixed in space
378 unsigned b=0;
379
380 // Pin displacements in both x and y directions
381 wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(0);
382 wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(1);
383
384 // Infinite slope: Pin type 1 (slope) dof for displacement direction 0
385 wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(1,0);
386
387
388
389 // Complete build of fluid elements
390 //---------------------------------
391 unsigned n_element = Fluid_mesh_pt->nelement();
392
393 // Loop over the elements to set up element-specific
394 // things that cannot be handled by constructor
395 for(unsigned e=0;e<n_element;e++)
396 {
397 // Upcast from GeneralisedElement to the present element
398 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Fluid_mesh_pt->element_pt(e));
399
400 //Set the Reynolds number
401 el_pt->re_pt() = &Global_Physical_Variables::Re;
402
403 //Set the Womersley number
404 el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
405
406 }// end loop over elements
407
408
409 // Pin redudant pressure dofs
410 RefineableNavierStokesEquations<2>::
411 pin_redundant_nodal_pressures(Fluid_mesh_pt->element_pt());
412
413
414 // Complete build of wall elements
415 //--------------------------------
416 n_element = wall_mesh_pt()->nelement();
417 for(unsigned e=0;e<n_element;e++)
418 {
419 // Upcast to the specific element type
420 FSIHermiteBeamElement *elem_pt =
421 dynamic_cast<FSIHermiteBeamElement*>(wall_mesh_pt()->element_pt(e));
422
423 // Set physical parameters for each element:
424 elem_pt->h_pt() = &Global_Physical_Variables::H;
425
426 // Function that specifies the load ratios
427 elem_pt->q_pt() = &Global_Physical_Variables::Q;
428
429 // Set the undeformed shape for each element
430 elem_pt->undeformed_beam_pt() = undeformed_wall_pt;
431
432 // Leaflet is immersed and loaded by fluid on both sides
433 elem_pt->enable_fluid_loading_on_both_sides();
434
435 // The normal to the leaflet, as computed by the
436 // FSIHermiteElements points away from the fluid rather than
437 // into the fluid (as assumed by default) when viewed from
438 // the "front" (face 0).
439 elem_pt->set_normal_pointing_out_of_fluid();
440
441 } // end of loop over elements
442
443
444 // Setup FSI
445 //----------
446
447 // The velocity of the fluid nodes on the wall (fluid mesh boundary 4,5)
448 // is set by the wall motion -- hence the no-slip condition must be
449 // re-applied whenever a node update is performed for these nodes.
450 // Such tasks may be performed automatically by the auxiliary node update
451 // function specified by a function pointer:
452 for(unsigned ibound=4;ibound<6;ibound++ )
453 {
454 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
455 for (unsigned inod=0;inod<num_nod;inod++)
456 {
457 Fluid_mesh_pt->boundary_node_pt(ibound, inod)->
458 set_auxiliary_node_update_fct_pt(
459 FSI_functions::apply_no_slip_on_moving_wall);
460 }
461 }// aux node update fct has been set
462
463 // Work out which fluid dofs affect the residuals of the wall elements:
464 // We pass the boundary between the fluid and solid meshes and
465 // pointers to the meshes. The interaction boundary is boundary 4 and 5
466 // of the 2D fluid mesh.
467
468 // Front of leaflet: Set face=0 (which is also the default so this argument
469 // could be omitted)
470 unsigned face=0;
471 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
472 (this,4,Fluid_mesh_pt,Wall_mesh_pt,face);
473
474 // Back of leaflet: face 1, needs to be specified explicitly
475 face=1;
476 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
477 (this,5,Fluid_mesh_pt,Wall_mesh_pt,face);
478
479 // Setup equation numbering scheme
480 cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
481
482
483
484 // Choose iterative solvers with FSI preconditioner (only if not test)
485 //====================================================================
486 if (CommandLineArgs::Argc==1)
487 {
488
489 // Build iterative linear solver
490 //------------------------------
491 GMRES<CRDoubleMatrix>* iterative_linear_solver_pt =
492 new GMRES<CRDoubleMatrix>;
493
494 // Set maximum number of iterations
495 iterative_linear_solver_pt->max_iter() = 200;
496
497 // Pass solver to problem:
498 linear_solver_pt()=iterative_linear_solver_pt;
499
500
501 // Build preconditioner
502 //---------------------
503 FSIPreconditioner* prec_pt=new FSIPreconditioner(this);
504
505 // Set Navier Stokes mesh:
506 prec_pt->set_navier_stokes_mesh(Fluid_mesh_pt);
507
508 // Set solid mesh:
509 prec_pt->set_wall_mesh(Wall_mesh_pt);
510
511 // By default, the Schur complement preconditioner uses SuperLU as
512 // an exact preconditioner (i.e. a solver) for the
513 // momentum and Schur complement blocks.
514 // Can overwrite this by passing pointers to
515 // other preconditioners that perform the (approximate)
516 // solves of these blocks
517
518
519#ifdef OOMPH_HAS_HYPRE
520
521 // Create internal preconditioner used on Schur block
522 //---------------------------------------------------
523 HyprePreconditioner* p_preconditioner_pt = new HyprePreconditioner;
524
525 // Shut up!
526 p_preconditioner_pt->disable_doc_time();
527
528 // Set defaults parameters for use as preconditioner on Poisson-type problem
529 Hypre_default_settings::
530 set_defaults_for_2D_poisson_problem(p_preconditioner_pt);
531
532 // Pass to preconditioner
533 //prec_pt->set_p_preconditioner(p_preconditioner_pt);
534
535
536 // Create internal preconditioner used on momentum block
537 //------------------------------------------------------
538 HyprePreconditioner* f_preconditioner_pt = new HyprePreconditioner;
539
540 // Shut up!
541 f_preconditioner_pt->disable_doc_time();
542
543 // Set default parameters for use as preconditioner in for momentum
544 // block in Navier-Stokes problem
545 Hypre_default_settings::
546 set_defaults_for_navier_stokes_momentum_block(f_preconditioner_pt);
547
548 // Use Hypre for momentum block
549 //prec_pt->set_f_preconditioner(f_preconditioner_pt);
550
551#endif
552
553 // Retain fluid onto solid terms in FSI preconditioner
554 prec_pt->use_block_triangular_version_with_fluid_on_solid();
555
556 // Pass preconditioner to iterative linear solver
557 iterative_linear_solver_pt->preconditioner_pt()= prec_pt;
558
559 }
560
561
562}//end of constructor
563
564
565
566
567
568//==== start_of_actions_after_adapt=================================
569/// Actions_after_adapt()
570//==================================================================
571template<class ELEMENT>
573{
574 // Unpin all pressure dofs
575 RefineableNavierStokesEquations<2>::
576 unpin_all_pressure_dofs(Fluid_mesh_pt->element_pt());
577
578 // Pin redundant pressure dofs
579 RefineableNavierStokesEquations<2>::
580 pin_redundant_nodal_pressures(Fluid_mesh_pt->element_pt());
581
582
583 // (Re-)apply the no slip condition on the moving wall
584 //-----------------------------------------------------
585
586 // The velocity of the fluid nodes on the wall (fluid mesh boundary 4,5)
587 // is set by the wall motion -- hence the no-slip condition must be
588 // re-applied whenever a node update is performed for these nodes.
589 // Such tasks may be performed automatically by the auxiliary node update
590 // function specified by a function pointer:
591 for(unsigned ibound=4;ibound<6;ibound++ )
592 {
593 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
594 for (unsigned inod=0;inod<num_nod;inod++)
595 {
596 Fluid_mesh_pt->boundary_node_pt(ibound, inod)->
597 set_auxiliary_node_update_fct_pt(
598 FSI_functions::apply_no_slip_on_moving_wall);
599 }
600 } // aux node update fct has been (re-)set
601
602
603
604 // Re-setup FSI
605 //-------------
606
607 // Work out which fluid dofs affect the residuals of the wall elements:
608 // We pass the boundary between the fluid and solid meshes and
609 // pointers to the meshes. The interaction boundary is boundary 4 and 5
610 // of the 2D fluid mesh.
611
612 // Front of leaflet: Set face=0 (which is also the default so this argument
613 // could be omitted)
614 unsigned face=0;
615 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
616 (this,4,Fluid_mesh_pt,Wall_mesh_pt,face);
617
618 // Back of leaflet: face 1, needs to be specified explicitly
619 face=1;
620 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
621 (this,5,Fluid_mesh_pt,Wall_mesh_pt,face);
622
623} // end_of_actions_after_adapt
624
625
626
627
628
629//==start_of_doc_solution=================================================
630/// Doc the solution
631//========================================================================
632template<class ELEMENT>
634 ofstream& trace)
635{
636 ofstream some_file;
637 char filename[100];
638
639 // Number of plot points
640 unsigned npts;
641 npts=5;
642
643 // Output fluid solution
644 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
645 doc_info.number());
646 some_file.open(filename);
647 Fluid_mesh_pt->output(some_file,npts);
648 some_file.close();
649
650 // Output wall solution
651 snprintf(filename, sizeof(filename), "%s/wall_soln%i.dat",doc_info.directory().c_str(),
652 doc_info.number());
653 some_file.open(filename);
654 Wall_mesh_pt->output(some_file,npts);
655 some_file.close();
656
657 // Get node at tip of leaflet
658 unsigned n_el_wall=Wall_mesh_pt->nelement();
659 Node* tip_node_pt=Wall_mesh_pt->finite_element_pt(n_el_wall-1)->node_pt(1);
660
661 // Get time:
662 double time=time_pt()->time();
663
664 // Write trace file
665 trace << time << " "
666 << Global_Physical_Variables::flux(time) << " "
667 << tip_node_pt->x(0) << " "
668 << tip_node_pt->x(1) << " "
669 << tip_node_pt->dposition_dt(0) << " "
670 << tip_node_pt->dposition_dt(1) << " "
671 << doc_info.number() << " "
672 << std::endl;
673
674
675 // Help me figure out what the "front" and "back" faces of the leaflet are
676 //------------------------------------------------------------------------
677
678 // Output fluid elements on fluid mesh boundary 4 (associated with
679 // the "front")
680 unsigned bound=4;
681 snprintf(filename, sizeof(filename), "%s/fluid_boundary_elements_front_%i.dat",
682 doc_info.directory().c_str(),
683 doc_info.number());
684 some_file.open(filename);
685 unsigned nel= Fluid_mesh_pt->nboundary_element(bound);
686 for (unsigned e=0;e<nel;e++)
687 {
688 dynamic_cast<ELEMENT*>(Fluid_mesh_pt->boundary_element_pt(bound,e))
689 ->output(some_file,npts);
690 }
691 some_file.close();
692
693
694 // Output fluid elements on fluid mesh boundary 5 (associated with
695 // the "back")
696 bound=5;
697 snprintf(filename, sizeof(filename), "%s/fluid_boundary_elements_back_%i.dat",
698 doc_info.directory().c_str(),
699 doc_info.number());
700 some_file.open(filename);
701 nel= Fluid_mesh_pt->nboundary_element(bound);
702 for (unsigned e=0;e<nel;e++)
703 {
704 dynamic_cast<ELEMENT*>(Fluid_mesh_pt->boundary_element_pt(bound,e))
705 ->output(some_file,npts);
706 }
707 some_file.close();
708
709
710 // Output normal vector on wall elements
711 snprintf(filename, sizeof(filename), "%s/wall_normal_%i.dat",
712 doc_info.directory().c_str(),
713 doc_info.number());
714 some_file.open(filename);
715 nel=Wall_mesh_pt->nelement();
716 Vector<double> s(1);
717 Vector<double> x(2);
718 Vector<double> xi(1);
719 Vector<double> N(2);
720 for (unsigned e=0;e<nel;e++)
721 {
722 // Get pointer to element
723 FSIHermiteBeamElement* el_pt=
724 dynamic_cast<FSIHermiteBeamElement*>(Wall_mesh_pt->element_pt(e));
725
726 // Loop over plot points
727 for (unsigned i=0;i<npts;i++)
728 {
729 s[0]=-1.0+2.0*double(i)/double(npts-1);
730
731 // Get Eulerian position
732 el_pt->interpolated_x(s,x);
733
734 // Get unit normal
735 el_pt->get_normal(s,N);
736
737 // Get Lagrangian coordinate
738 el_pt->interpolated_xi(s,xi);
739
740 some_file << x[0] << " " << x[1] << " "
741 << N[0] << " " << N[1] << " "
742 << xi[0] << std::endl;
743 }
744 }
745 some_file.close();
746
747} // end_of_doc_solution
748
749
750
751
752//======= start_of_main================================================
753/// Driver code -- pass a command line argument if you want to run
754/// the code in validation mode where it only performs a few steps
755//=====================================================================
756int main(int argc, char* argv[])
757{
758 // Store command line arguments
759 CommandLineArgs::setup(argc,argv);
760
761 //Parameters for the leaflet: x-position of root and height
762 double x_0 = 1.0;
763 double hleaflet=0.5;
764
765 // Number of elements in various regions of mesh
766 unsigned nleft=6;
767 unsigned nright=18;
768 unsigned ny1=3;
769 unsigned ny2=3;
770
771 // Dimensions of fluid mesh: length to the left and right of leaflet
772 // and total height
773 double lleft =1.0;
774 double lright=3.0;
775 double htot=1.0;
776
777 //Build the problem
779 AlgebraicElement<RefineableQTaylorHoodElement<2> > >
780 problem(lleft,lright,hleaflet,
781 htot,nleft,nright,ny1,ny2,x_0);
782
783 // Set up doc info
784 DocInfo doc_info;
785 doc_info.set_directory("RESLT");
786
787 // Trace file
788 ofstream trace;
789 char filename[100];
790 snprintf(filename, sizeof(filename), "%s/trace.dat",doc_info.directory().c_str());
791 trace.open(filename);
792
793
794 // Number of timesteps (reduced for validation)
795 unsigned nstep=200;
796 if (CommandLineArgs::Argc>1)
797 {
798 nstep=2;
799 }
800
801 //Timestep:
802 double dt=0.05;
803
804 // Initialise timestep
805 problem.initialise_dt(dt);
806
807 // Doc initial guess for steady solve
808 problem.doc_solution(doc_info,trace);
809 doc_info.number()++;
810
811
812 // Initial loop to increment the Reynolds number in sequence of steady solves
813 //---------------------------------------------------------------------------
814 unsigned n_increment=4;
815 // Just to one step for validation run
816 if (CommandLineArgs::Argc>1)
817 {
818 n_increment=1;
819 }
820
821 // Set max. number of adaptations
822 unsigned max_adapt=3;
823
825 for (unsigned i=0;i<n_increment;i++)
826 {
827 // Increase Re and ReSt (for St=1)
830
831 // Solve the steady problem
832 std::cout << "Computing a steady solution for Re="
833 << Global_Physical_Variables::Re << std::endl;
834 problem.steady_newton_solve(max_adapt);
835 problem.doc_solution(doc_info,trace);
836 doc_info.number()++;
837 } // reached final Reynolds number
838
839
840
841 // Proper time-dependent run
842 //--------------------------
843
844 // Limit the number of adaptations during unsteady run to one per timestep
845 max_adapt=1;
846
847 // Don't re-set the initial conditions when adapting the mesh
848 bool first = false;
849
850 // Timestepping loop
851 for (unsigned istep=0;istep<nstep;istep++)
852 {
853 // Solve the problem
854 problem.unsteady_newton_solve(dt,max_adapt,first);
855
856 // Output the solution
857 problem.doc_solution(doc_info,trace);
858
859 // Step number
860 doc_info.number()++;
861
862 }
863
864}//end of main
865
866
void doc_solution(DocInfo &doc_info, ofstream &trace)
Doc the solution.
RefineableAlgebraicChannelWithLeafletMesh< ELEMENT > * Fluid_mesh_pt
Pointer to the fluid mesh.
OneDLagrangianMesh< FSIHermiteBeamElement > * wall_mesh_pt()
Access function to the wall mesh.
double Htot
Total height of the domain.
void actions_before_newton_solve()
Actions before solve (empty)
OneDLagrangianMesh< FSIHermiteBeamElement > * Wall_mesh_pt
Pointer to the "wall" mesh.
FSIChannelWithLeafletProblem(const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, const double &x_0)
Constructor: Pass the lenght of the domain at the left of the leaflet lleft,the lenght of the domain ...
void actions_after_newton_solve()
Actions after solve (empty)
void actions_before_implicit_timestep()
Update the inflow velocity.
void actions_after_adapt()
Actions after adaptation.
RefineableAlgebraicChannelWithLeafletMesh< ELEMENT > * fluid_mesh_pt()
Access function to fluid mesh.
GeomObject * Leaflet_pt
Pointer to the GeomObject that represents the wall.
void actions_before_newton_convergence_check()
Update before checking Newton convergence: Update the nodal positions in the fluid mesh in response t...
GeomObject: Undeformed straight, vertical leaflet.
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.
int main(int argc, char *argv[])
Driver code – pass a command line argument if you want to run the code in validation mode where it on...
double ReSt
Womersley number: Product of Reynolds and Strouhal numbers.
double Period
Period for fluctuations in flux.
double Q
Fluid structure interaction parameter: Ratio of stresses used for non-dimensionalisation of fluid to ...
double flux(const double &t)
Flux: Pulsatile flow fluctuating between Min_flux and Max_flux with period Period.
double H
Non-dimensional wall thickness.