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