turek_flag.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 stuff
27#include "generic.h"
28
29// Solid mechanics
30#include "solid.h"
31
32// Navier Stokes
33#include "navier_stokes.h"
34
35// Multiphysics for FSI preconditioner
36#include "multi_physics.h"
37
38// The meshes
39#include "meshes/cylinder_with_flag_mesh.h"
40#include "meshes/rectangular_quadmesh.h"
41
42
43using namespace std;
44
45using namespace oomph;
46
47
48//=====start_of_global_parameters=================================
49/// Global variables
50//================================================================
52{
53
54 /// Default case ID
55 string Case_ID="FSI1";
56
57 /// Reynolds number (default assignment for FSI1 test case)
58 double Re=20.0;
59
60 /// Strouhal number (default assignment for FSI1 test case)
61 double St=0.5;
62
63 /// Product of Reynolds and Strouhal numbers (default
64 /// assignment for FSI1 test case)
65 double ReSt=10.0;
66
67 /// FSI parameter (default assignment for FSI1 test case)
68 double Q=1.429e-6;
69
70 /// Density ratio (solid to fluid; default assignment for FSI1
71 /// test case)
72 double Density_ratio=1.0;
73
74 /// Height of flag
75 double H=0.2;
76
77 /// x position of centre of cylinder
78 double Centre_x=2.0;
79
80 /// y position of centre of cylinder
81 double Centre_y=2.0;
82
83 /// Radius of cylinder
84 double Radius=0.5;
85
86 /// Pointer to constitutive law
87 ConstitutiveLaw* Constitutive_law_pt=0;
88
89 /// Timescale ratio for solid (dependent parameter
90 /// assigned in set_parameters())
91 double Lambda_sq=0.0;
92
93 /// Timestep
94 double Dt=0.1;
95
96 /// Ignore fluid (default assignment for FSI1 test case)
98
99 /// Elastic modulus
100 double E=1.0;
101
102 /// Poisson's ratio
103 double Nu=0.4;
104
105 /// Non-dim gravity (default assignment for FSI1 test case)
106 double Gravity=0.0;
107
108 /// Non-dimensional gravity as body force
109 void gravity(const double& time,
110 const Vector<double> &xi,
111 Vector<double> &b)
112 {
113 b[0]=0.0;
114 b[1]=-Gravity;
115 }
116
117 /// Period for ramping up in flux
118 double Ramp_period=2.0;
119
120 /// Min. flux
121 double Min_flux=0.0;
122
123 /// Max. flux
124 double Max_flux=1.0;
125
126 /// Flux increases between Min_flux and Max_flux over
127 /// period Ramp_period
128 double flux(const double& t)
129 {
130 if (t<Ramp_period)
131 {
132 return Min_flux+(Max_flux-Min_flux)*
133 0.5*(1.0-cos(MathematicalConstants::Pi*t/Ramp_period));
134 }
135 else
136 {
137 return Max_flux;
138 }
139 } // end of specification of ramped influx
140
141
142 /// Set parameters for the various test cases
143 void set_parameters(const string& case_id)
144 {
145
146 // Remember which case we're dealing with
147 Case_ID=case_id;
148
149 // Setup independent parameters depending on test case
150 if (case_id=="FSI1")
151 {
152 // Reynolds number based on diameter of cylinder
153 Re=20.0;
154
155 // Strouhal number based on timescale of one second
156 St=0.5;
157
158 // Womersley number
159 ReSt=Re*St;
160
161 // FSI parameter
162 Q=1.429e-6;
163
164 // Timestep -- aiming for about 40 steps per period
165 Dt=0.1;
166
167 // Density ratio
168 Density_ratio=1.0;
169
170 // Gravity
171 Gravity=0.0;
172
173 // Max. flux
174 Max_flux=1.0;
175
176 // Ignore fluid
178
179 // Compute dependent parameters
180
181 // Timescale ratio for solid
183 }
184 else if (case_id=="FSI2")
185 {
186 // Reynolds number based on diameter of cylinder
187 Re=100.0;
188
189 // Strouhal number based on timescale of one second
190 St=0.1;
191
192 // Womersley number
193 ReSt=Re*St;
194
195 // FSI parameter
196 Q=7.143e-6;
197
198 // Timestep -- aiming for about 40 steps per period
199 Dt=0.01;
200
201 // Density ratio
202 Density_ratio=10.0;
203
204 // Gravity
205 Gravity=0.0;
206
207 // Max. flux
208 Max_flux=1.0;
209
210 // Ignore fluid
212
213 // Compute dependent parameters
214
215 // Timescale ratio for solid
217 }
218 else if (case_id=="FSI3")
219 {
220 // Reynolds number based on diameter of cylinder
221 Re=200.0;
222
223 // Strouhal number based on timescale of one second
224 St=0.05;
225
226 // Womersley number
227 ReSt=Re*St;
228
229 // FSI parameter
230 Q=3.571e-6;
231
232 // Timestep -- aiming for about 40 steps per period
233 Dt=0.005;
234
235 // Density ratio
236 Density_ratio=1.0;
237
238 // Gravity
239 Gravity=0.0;
240
241 // Max. flux
242 Max_flux=1.0;
243
244 // Ignore fluid
246
247 // Compute dependent parameters
248
249 // Timescale ratio for solid
251 }
252 else if (case_id=="CSM1")
253 {
254 // Reynolds number based on diameter of cylinder
255 Re=0.0;
256
257 // Strouhal number based on timescale of one second
258 // (irrelevant here)
259 St=0.0;
260
261 // Womersley number
262 ReSt=Re*St;
263
264 // FSI parameter
265 Q=0.0;
266
267 // Timestep -- irrelevant here
268 Dt=0.005;
269
270 // Density ratio (switch off wall inertia)
271 Density_ratio=0.0;
272
273 // Gravity
274 Gravity=1.429e-4;
275
276 // Max. flux
277 Max_flux=0.0;
278
279 // Ignore fluid
281
282 // Compute dependent parameters
283
284 // Timescale ratio for solid
285 Lambda_sq=0.0;
286 }
287 else if (case_id=="CSM3")
288 {
289 // Reynolds number based on diameter of cylinder
290 Re=0.0;
291
292 // Strouhal number based on timescale of one second
293 // (irrelevant here)
294 St=0.0;
295
296 // Womersley number
297 ReSt=Re*St;
298
299 // Timestep -- aiming for about 40 steps per period
300 Dt=0.025;
301
302 // FSI parameter
303 Q=0.0;
304
305 // Density ratio (not used here)
306 Density_ratio=0.0;
307
308 // Gravity
309 Gravity=1.429e-4;
310
311 // Max. flux
312 Max_flux=0.0;
313
314 // Ignore fluid
316
317 // Compute dependent parameters
318
319 // Set timescale ratio for solid based on the
320 // observed period of oscillation of about 1 sec)
321 Lambda_sq=7.143e-6;
322 }
323 else
324 {
325 std::cout << "Wrong case_id: " << case_id << std::endl;
326 exit(0);
327 }
328
329
330 // Ramp period (20 timesteps)
331 Ramp_period=Dt*20.0;
332
333 // "Big G" Linear constitutive equations:
334 Constitutive_law_pt = new GeneralisedHookean(&Nu,&E);
335
336 // Doc
337 oomph_info << std::endl;
338 oomph_info << "-------------------------------------------"
339 << std::endl;
340 oomph_info << "Case: " << case_id << std::endl;
341 oomph_info << "Re = " << Re << std::endl;
342 oomph_info << "St = " << St << std::endl;
343 oomph_info << "ReSt = " << ReSt << std::endl;
344 oomph_info << "Q = " << Q << std::endl;
345 oomph_info << "Dt = " << Dt << std::endl;
346 oomph_info << "Ramp_period = " << Ramp_period << std::endl;
347 oomph_info << "Max_flux = " << Max_flux << std::endl;
348 oomph_info << "Density_ratio = " << Density_ratio << std::endl;
349 oomph_info << "Lambda_sq = " << Lambda_sq << std::endl;
350 oomph_info << "Gravity = " << Gravity << std::endl;
351 oomph_info << "Ignore fluid = " << Ignore_fluid_loading<< std::endl;
352 oomph_info << "-------------------------------------------"
353 << std::endl << std::endl;
354 }
355
356}// end_of_namespace
357
358
359
360///////////////////////////////////////////////////////////////////////
361///////////////////////////////////////////////////////////////////////
362///////////////////////////////////////////////////////////////////////
363
364
365
366//====start_of_problem_class===========================================
367/// Problem class
368//======================================================================
369template< class FLUID_ELEMENT,class SOLID_ELEMENT >
370class TurekProblem : public Problem
371{
372
373public:
374
375 /// Constructor: Pass length and height of domain
376 TurekProblem(const double &length, const double &height);
377
378 /// Access function for the fluid mesh
379 RefineableAlgebraicCylinderWithFlagMesh<FLUID_ELEMENT>* fluid_mesh_pt()
380 { return Fluid_mesh_pt;}
381
382 /// Access function for the solid mesh
383 ElasticRefineableRectangularQuadMesh<SOLID_ELEMENT>*& solid_mesh_pt()
384 {return Solid_mesh_pt;}
385
386 /// Access function for the i-th mesh of FSI traction elements
387 SolidMesh*& traction_mesh_pt(const unsigned& i)
388 {return Traction_mesh_pt[i];}
389
390 /// Actions after adapt: Re-setup the fsi lookup scheme
391 void actions_after_adapt();
392
393 /// Doc the solution
394 void doc_solution(DocInfo& doc_info, ofstream& trace_file);
395
396 /// Update function (empty)
398
399 /// Update function (empty)
401
402 /// Update the (dependent) fluid node positions following the
403 /// update of the solid variables before performing Newton convergence
404 /// check
406
407 /// Update the time-dependent influx
409
410private:
411
412 /// Create FSI traction elements
414
415 /// Pointer to solid mesh
416 ElasticRefineableRectangularQuadMesh<SOLID_ELEMENT>* Solid_mesh_pt;
417
418 /// Pointer to fluid mesh
419 RefineableAlgebraicCylinderWithFlagMesh<FLUID_ELEMENT>* Fluid_mesh_pt;
420
421 /// Vector of pointers to mesh of FSI traction elements
422 Vector<SolidMesh*> Traction_mesh_pt;
423
424 /// Combined mesh of traction elements -- only used for documentation
426
427 /// Overall height of domain
429
430 /// Overall length of domain
432
433 /// Pointer to solid control node
435
436 /// Pointer to fluid control node
438
439};// end_of_problem_class
440
441
442//=====start_of_constructor=============================================
443/// Constructor: Pass length and height of domain
444//======================================================================
445template< class FLUID_ELEMENT,class SOLID_ELEMENT >
447TurekProblem(const double &length,
448 const double &height) : Domain_height(height),
449 Domain_length(length)
450
451{
452 // Increase max. number of iterations in Newton solver to
453 // accomodate possible poor initial guesses
454 Max_newton_iterations=20;
455 Max_residuals=1.0e4;
456
457 // Build solid mesh
458 //------------------
459
460 // # of elements in x-direction
461 unsigned n_x=20;
462
463 // # of elements in y-direction
464 unsigned n_y=2;
465
466 // Domain length in y-direction
467 double l_y=Global_Parameters::H;
468
469 // Create the flag timestepper (consistent with BDF<2> for fluid)
470 Newmark<2>* flag_time_stepper_pt=new Newmark<2>;
471 add_time_stepper_pt(flag_time_stepper_pt);
472
473 /// Left point on centreline of flag so that the top and bottom
474 /// vertices merge with the cylinder.
475 Vector<double> origin(2);
480 origin[1]=Global_Parameters::Centre_y-0.5*l_y;
481
482 // Set length of flag so that endpoint actually stretches all the
483 // way to x=6:
484 double l_x=6.0-origin[0];
485
486 //Now create the mesh
487 solid_mesh_pt() = new ElasticRefineableRectangularQuadMesh<SOLID_ELEMENT>(
488 n_x,n_y,l_x,l_y,origin,flag_time_stepper_pt);
489
490 // Set error estimator for the solid mesh
491 Z2ErrorEstimator* solid_error_estimator_pt=new Z2ErrorEstimator;
492 solid_mesh_pt()->spatial_error_estimator_pt()=solid_error_estimator_pt;
493
494
495 // Element that contains the control point
496 FiniteElement* el_pt=solid_mesh_pt()->finite_element_pt(n_x*n_y/2-1);
497
498 // How many nodes does it have?
499 unsigned nnod=el_pt->nnode();
500
501 // Get the control node
502 Solid_control_node_pt=el_pt->node_pt(nnod-1);
503
504 std::cout << "Coordinates of solid control point "
505 << Solid_control_node_pt->x(0) << " "
506 << Solid_control_node_pt->x(1) << " " << std::endl;
507
508 // Refine the mesh uniformly
509 solid_mesh_pt()->refine_uniformly();
510
511 //Do not allow the solid mesh to be refined again
512 solid_mesh_pt()->disable_adaptation();
513
514
515 // Build mesh of solid traction elements that apply the fluid
516 //------------------------------------------------------------
517 // traction to the solid elements
518 //-------------------------------
519
520 // Create storage for Meshes of FSI traction elements at the bottom
521 // top and left boundaries of the flag
522 Traction_mesh_pt.resize(3);
523
524 // Now construct the traction element meshes
525 Traction_mesh_pt[0]=new SolidMesh;
526 Traction_mesh_pt[1]=new SolidMesh;
527 Traction_mesh_pt[2]=new SolidMesh;
528
529 // Build the FSI traction elements
531
532 // Loop over traction elements, pass the FSI parameter and tell them
533 // the boundary number in the bulk solid mesh -- this is required so
534 // they can get access to the boundary coordinates!
535 for (unsigned bound=0;bound<3;bound++)
536 {
537 unsigned n_face_element = Traction_mesh_pt[bound]->nelement();
538 for(unsigned e=0;e<n_face_element;e++)
539 {
540 //Cast the element pointer and specify boundary number
541 FSISolidTractionElement<SOLID_ELEMENT,2>* elem_pt=
542 dynamic_cast<FSISolidTractionElement<SOLID_ELEMENT,2>*>
543 (Traction_mesh_pt[bound]->element_pt(e));
544
545 // Specify boundary number
546 elem_pt->set_boundary_number_in_bulk_mesh(bound);
547
548 // Function that specifies the load ratios
549 elem_pt->q_pt() = &Global_Parameters::Q;
550
551 }
552 } // build of FSISolidTractionElements is complete
553
554
555 // Turn the three meshes of FSI traction elements into compound
556 // geometric objects (one Lagrangian, two Eulerian coordinates)
557 // that determine the boundary of the fluid mesh
558 MeshAsGeomObject*
559 bottom_flag_pt=
560 new MeshAsGeomObject
561 (Traction_mesh_pt[0]);
562
563 MeshAsGeomObject* tip_flag_pt=
564 new MeshAsGeomObject
565 (Traction_mesh_pt[1]);
566
567 MeshAsGeomObject* top_flag_pt=
568 new MeshAsGeomObject
569 (Traction_mesh_pt[2]);
570
571
572 // Build fluid mesh
573 //-----------------
574
575 //Create a new Circle object as the central cylinder
576 Circle* cylinder_pt = new Circle(Global_Parameters::Centre_x,
579
580 // Allocate the fluid timestepper
581 BDF<2>* fluid_time_stepper_pt=new BDF<2>;
582 add_time_stepper_pt(fluid_time_stepper_pt);
583
584 // Build fluid mesh
586 new RefineableAlgebraicCylinderWithFlagMesh<FLUID_ELEMENT>
587 (cylinder_pt,
588 top_flag_pt,
589 bottom_flag_pt,
590 tip_flag_pt,
591 length, height,
596 fluid_time_stepper_pt);
597
598
599 // I happen to have found out by inspection that
600 // node 5 in the hand-coded fluid mesh is at the
601 // upstream tip of the cylinder
603
604 // Set error estimator for the fluid mesh
605 Z2ErrorEstimator* fluid_error_estimator_pt=new Z2ErrorEstimator;
606 fluid_mesh_pt()->spatial_error_estimator_pt()=fluid_error_estimator_pt;
607
608 // Refine uniformly
609 Fluid_mesh_pt->refine_uniformly();
610
611
612 // Build combined global mesh
613 //---------------------------
614
615 // Add Solid mesh the problem's collection of submeshes
616 add_sub_mesh(solid_mesh_pt());
617
618 // Add traction sub-meshes
619 for (unsigned i=0;i<3;i++)
620 {
621 add_sub_mesh(traction_mesh_pt(i));
622 }
623
624 // Add fluid mesh
625 add_sub_mesh(fluid_mesh_pt());
626
627 // Build combined "global" mesh
628 build_global_mesh();
629
630
631
632 // Apply solid boundary conditons
633 //-------------------------------
634
635 //Solid mesh: Pin the left boundary (boundary 3) in both directions
636 unsigned n_side = mesh_pt()->nboundary_node(3);
637
638 //Loop over the nodes
639 for(unsigned i=0;i<n_side;i++)
640 {
641 solid_mesh_pt()->boundary_node_pt(3,i)->pin_position(0);
642 solid_mesh_pt()->boundary_node_pt(3,i)->pin_position(1);
643 }
644
645 // Pin the redundant solid pressures (if any)
646 PVDEquationsBase<2>::pin_redundant_nodal_solid_pressures(
647 solid_mesh_pt()->element_pt());
648
649 // Apply fluid boundary conditions
650 //--------------------------------
651
652 //Fluid mesh: Horizontal, traction-free outflow; pinned elsewhere
653 unsigned num_bound = fluid_mesh_pt()->nboundary();
654 for(unsigned ibound=0;ibound<num_bound;ibound++)
655 {
656 unsigned num_nod= fluid_mesh_pt()->nboundary_node(ibound);
657 for (unsigned inod=0;inod<num_nod;inod++)
658 {
659 // Parallel, axially traction free outflow at downstream end
660 if (ibound != 1)
661 {
662 fluid_mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
663 fluid_mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
664 }
665 else
666 {
667 fluid_mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
668 }
669 }
670 }//end_of_pin
671
672 // Pin redundant pressure dofs in fluid mesh
673 RefineableNavierStokesEquations<2>::
674 pin_redundant_nodal_pressures(fluid_mesh_pt()->element_pt());
675
676
677 // Apply boundary conditions for fluid
678 //-------------------------------------
679
680 // Impose parabolic flow along boundary 3
681 // Current flow rate
682 double t=0.0;
683 double ampl=Global_Parameters::flux(t);
684 unsigned ibound=3;
685 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
686 for (unsigned inod=0;inod<num_nod;inod++)
687 {
688 double ycoord = Fluid_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
689 double uy = ampl*6.0*ycoord/Domain_height*(1.0-ycoord/Domain_height);
690 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,uy);
691 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
692 }
693
694
695 // Complete build of solid elements
696 //---------------------------------
697
698 //Pass problem parameters to solid elements
699 unsigned n_element =solid_mesh_pt()->nelement();
700 for(unsigned i=0;i<n_element;i++)
701 {
702 //Cast to a solid element
703 SOLID_ELEMENT *el_pt = dynamic_cast<SOLID_ELEMENT*>(
704 solid_mesh_pt()->element_pt(i));
705
706 // Set the constitutive law
707 el_pt->constitutive_law_pt() =
709
710 //Set the body force
711 el_pt->body_force_fct_pt() = Global_Parameters::gravity;
712
713 // Timescale ratio for solid
714 el_pt->lambda_sq_pt() = &Global_Parameters::Lambda_sq;
715 }
716
717
718
719 // Complete build of fluid elements
720 //---------------------------------
721
722 // Set physical parameters in the fluid mesh
723 unsigned nelem=fluid_mesh_pt()->nelement();
724 for (unsigned e=0;e<nelem;e++)
725 {
726 // Upcast from GeneralisedElement to the present element
727 FLUID_ELEMENT* el_pt = dynamic_cast<FLUID_ELEMENT*>
728 (fluid_mesh_pt()->element_pt(e));
729
730 //Set the Reynolds number
731 el_pt->re_pt() = &Global_Parameters::Re;
732
733 //Set the Womersley number
734 el_pt->re_st_pt() = &Global_Parameters::ReSt;
735
736 }//end_of_loop
737
738
739
740 // Setup FSI
741 //----------
742
743 // Pass Strouhal number to the helper function that automatically applies
744 // the no-slip condition
745 FSI_functions::Strouhal_for_no_slip=Global_Parameters::St;
746
747 // The velocity of the fluid nodes on the wall (fluid mesh boundary 5,6,7)
748 // is set by the wall motion -- hence the no-slip condition must be
749 // re-applied whenever a node update is performed for these nodes.
750 // Such tasks may be performed automatically by the auxiliary node update
751 // function specified by a function pointer:
753 {
754 for(unsigned ibound=5;ibound<8;ibound++ )
755 {
756 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
757 for (unsigned inod=0;inod<num_nod;inod++)
758 {
759 Fluid_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 } // done automatic application of no-slip
764
765 // Work out which fluid dofs affect the residuals of the wall elements:
766 // We pass the boundary between the fluid and solid meshes and
767 // pointers to the meshes. The interaction boundary are boundaries 5,6,7
768 // of the 2D fluid mesh.
769 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
770 (this,5,Fluid_mesh_pt,Traction_mesh_pt[0]);
771
772 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
773 (this,6,Fluid_mesh_pt,Traction_mesh_pt[2]);
774
775 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
776 (this,7,Fluid_mesh_pt,Traction_mesh_pt[1]);
777 }
778
779
780 // Build solver/preconditioner
781 //----------------------------
782
783 // Build iterative linear solver
784 GMRES<CRDoubleMatrix>* iterative_linear_solver_pt =
785 new GMRES<CRDoubleMatrix>;
786
787 // Set maximum number of iterations
788 iterative_linear_solver_pt->max_iter() = 100;
789
790 // Set tolerance
791 iterative_linear_solver_pt->tolerance() = 1.0e-10;
792
793 // Assign solver
794 linear_solver_pt()=iterative_linear_solver_pt;
795
796 // Build preconditioner
797 FSIPreconditioner* prec_pt=new FSIPreconditioner(this);
798
799 // Set Navier Stokes mesh:
800 prec_pt->set_navier_stokes_mesh(Fluid_mesh_pt);
801
802 // Set solid mesh:
803 prec_pt->set_wall_mesh(solid_mesh_pt());
804
805 // Retain fluid onto solid terms
806 prec_pt->use_block_triangular_version_with_fluid_on_solid();
807
808 // Set preconditioner
809 iterative_linear_solver_pt->preconditioner_pt()= prec_pt;
810
811 // By default, the LSC Preconditioner uses SuperLU as
812 // an exact preconditioner (i.e. a solver) for the
813 // momentum and Schur complement blocks.
814 // Can overwrite this by passing pointers to
815 // other preconditioners that perform the (approximate)
816 // solves of these blocks.
817
818#ifdef OOMPH_HAS_HYPRE
819//Only use HYPRE if we don't have MPI enabled
820#ifndef OOMPH_HAS_MPI
821
822 // Create internal preconditioners used on Schur block
823 Preconditioner* P_matrix_preconditioner_pt = new HyprePreconditioner;
824
825 HyprePreconditioner* P_hypre_solver_pt =
826 static_cast<HyprePreconditioner*>(P_matrix_preconditioner_pt);
827
828 // Set defaults parameters for use as preconditioner on Poisson-type
829 // problem
830 Hypre_default_settings::
831 set_defaults_for_2D_poisson_problem(P_hypre_solver_pt);
832
833 // Use Hypre for the Schur complement block
834 prec_pt->navier_stokes_preconditioner_pt()->
835 set_p_preconditioner(P_matrix_preconditioner_pt);
836
837 // Shut up
838 P_hypre_solver_pt->disable_doc_time();
839
840#endif
841#endif
842
843 // Assign equation numbers
844 cout << assign_eqn_numbers() << std::endl;
845
846
847}//end_of_constructor
848
849
850
851//====start_of_actions_before_newton_convergence_check===================
852/// Update the (dependent) fluid node positions following the
853/// update of the solid variables
854//=======================================================================
855template <class FLUID_ELEMENT,class SOLID_ELEMENT>
858{
859 fluid_mesh_pt()->node_update();
860}
861
862
863
864//===== start_of_actions_before_implicit_timestep=========================
865/// Actions before implicit timestep: Update inflow profile
866//========================================================================
867template <class FLUID_ELEMENT,class SOLID_ELEMENT>
870{
871 // Current time
872 double t=time_pt()->time();
873
874 // Amplitude of flow
875 double ampl=Global_Parameters::flux(t);
876
877 // Update parabolic flow along boundary 3
878 unsigned ibound=3;
879 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
880 for (unsigned inod=0;inod<num_nod;inod++)
881 {
882 double ycoord = Fluid_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
883 double uy = ampl*6.0*ycoord/Domain_height*(1.0-ycoord/Domain_height);
884 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,uy);
885 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
886 }
887
888} //end_of_actions_before_implicit_timestep
889
890
891//=====================start_of_actions_after_adapt=======================
892/// Actions after adapt: Re-setup FSI
893//========================================================================
894template<class FLUID_ELEMENT,class SOLID_ELEMENT >
896{
897 // Unpin all pressure dofs
898 RefineableNavierStokesEquations<2>::
899 unpin_all_pressure_dofs(fluid_mesh_pt()->element_pt());
900
901 // Pin redundant pressure dofs
902 RefineableNavierStokesEquations<2>::
903 pin_redundant_nodal_pressures(fluid_mesh_pt()->element_pt());
904
905 // Unpin all solid pressure dofs
906 PVDEquationsBase<2>::
907 unpin_all_solid_pressure_dofs(solid_mesh_pt()->element_pt());
908
909 // Pin the redundant solid pressures (if any)
910 PVDEquationsBase<2>::pin_redundant_nodal_solid_pressures(
911 solid_mesh_pt()->element_pt());
912
913
914 // The velocity of the fluid nodes on the wall (fluid mesh boundary 5,6,7)
915 // is set by the wall motion -- hence the no-slip condition must be
916 // re-applied whenever a node update is performed for these nodes.
917 // Such tasks may be performed automatically by the auxiliary node update
918 // function specified by a function pointer:
920 {
921 for(unsigned ibound=5;ibound<8;ibound++ )
922 {
923 unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
924 for (unsigned inod=0;inod<num_nod;inod++)
925 {
926 Fluid_mesh_pt->boundary_node_pt(ibound, inod)->
927 set_auxiliary_node_update_fct_pt(
928 FSI_functions::apply_no_slip_on_moving_wall);
929 }
930 }
931
932
933 // Re-setup the fluid load information for fsi solid traction elements
934 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
935 (this,5,Fluid_mesh_pt,Traction_mesh_pt[0]);
936
937 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
938 (this,6,Fluid_mesh_pt,Traction_mesh_pt[2]);
939
940 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
941 (this,7,Fluid_mesh_pt,Traction_mesh_pt[1]);
942 }
943
944
945}// end of actions_after_adapt
946
947
948
949//============start_of_create_traction_elements==============================
950/// Create FSI traction elements
951//=======================================================================
952template<class FLUID_ELEMENT,class SOLID_ELEMENT >
954{
955
956 // Container to collect all nodes in the traction meshes
957 std::set<SolidNode*> all_nodes;
958
959 // Traction elements are located on boundaries 0-2:
960 for (unsigned b=0;b<3;b++)
961 {
962 // How many bulk elements are adjacent to boundary b?
963 unsigned n_element = solid_mesh_pt()->nboundary_element(b);
964
965 // Loop over the bulk elements adjacent to boundary b?
966 for(unsigned e=0;e<n_element;e++)
967 {
968 // Get pointer to the bulk element that is adjacent to boundary b
969 SOLID_ELEMENT* bulk_elem_pt = dynamic_cast<SOLID_ELEMENT*>(
970 solid_mesh_pt()->boundary_element_pt(b,e));
971
972 //What is the index of the face of the element e along boundary b
973 int face_index = solid_mesh_pt()->face_index_at_boundary(b,e);
974
975 // Create new element and add to mesh
976 Traction_mesh_pt[b]->add_element_pt(
977 new FSISolidTractionElement<SOLID_ELEMENT,2>(bulk_elem_pt,face_index));
978
979 } //end of loop over bulk elements adjacent to boundary b
980
981 // Identify unique nodes
982 unsigned nnod=solid_mesh_pt()->nboundary_node(b);
983 for (unsigned j=0;j<nnod;j++)
984 {
985 all_nodes.insert(solid_mesh_pt()->boundary_node_pt(b,j));
986 }
987 }
988
989 // Build combined mesh of fsi traction elements
990 Combined_traction_mesh_pt=new SolidMesh(Traction_mesh_pt);
991
992 // Stick nodes into combined traction mesh
993 for (std::set<SolidNode*>::iterator it=all_nodes.begin();
994 it!=all_nodes.end();it++)
995 {
996 Combined_traction_mesh_pt->add_node_pt(*it);
997 }
998
999} // end of create_traction_elements
1000
1001
1002
1003
1004//=====start_of_doc_solution========================================
1005/// Doc the solution
1006//==================================================================
1007template<class FLUID_ELEMENT,class SOLID_ELEMENT >
1009 DocInfo& doc_info, ofstream& trace_file)
1010{
1011
1012// FSI_functions::doc_fsi<AlgebraicNode>(Fluid_mesh_pt,
1013// Combined_traction_mesh_pt,
1014// doc_info);
1015
1016// pause("done");
1017
1018 ofstream some_file;
1019 char filename[100];
1020
1021 // Number of plot points
1022 unsigned n_plot = 5;
1023
1024 // Output solid solution
1025 snprintf(filename, sizeof(filename), "%s/solid_soln%i.dat",doc_info.directory().c_str(),
1026 doc_info.number());
1027 some_file.open(filename);
1028 solid_mesh_pt()->output(some_file,n_plot);
1029 some_file.close();
1030
1031 // Output fluid solution
1032 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
1033 doc_info.number());
1034 some_file.open(filename);
1035 fluid_mesh_pt()->output(some_file,n_plot);
1036 some_file.close();
1037
1038
1039//Output the traction
1040 snprintf(filename, sizeof(filename), "%s/traction%i.dat",doc_info.directory().c_str(),
1041 doc_info.number());
1042 some_file.open(filename);
1043// Loop over the traction meshes
1044 for(unsigned i=0;i<3;i++)
1045 {
1046 // Loop over the element in traction_mesh_pt
1047 unsigned n_element = Traction_mesh_pt[i]->nelement();
1048 for(unsigned e=0;e<n_element;e++)
1049 {
1050 FSISolidTractionElement<SOLID_ELEMENT,2>* el_pt =
1051 dynamic_cast<FSISolidTractionElement<SOLID_ELEMENT,2>* > (
1052 Traction_mesh_pt[i]->element_pt(e) );
1053
1054 el_pt->output(some_file,5);
1055 }
1056 }
1057 some_file.close();
1058
1059
1060 // Write trace (we're only using Taylor Hood elements so we know that
1061 // the pressure is the third value at the fluid control node...
1062 trace_file << time_pt()->time() << " "
1063 << Solid_control_node_pt->x(0) << " "
1064 << Solid_control_node_pt->x(1) << " "
1065 << Fluid_control_node_pt->value(2) << " "
1066 << Global_Parameters::flux(time_pt()->time()) << " "
1067 << std::endl;
1068
1069 cout << "Doced solution for step "
1070 << doc_info.number()
1071 << std::endl << std::endl << std::endl;
1072
1073}//end_of_doc_solution
1074
1075
1076
1077//=======start_of_main==================================================
1078/// Driver
1079//======================================================================
1080int main(int argc, char* argv[])
1081{
1082 // Store command line arguments
1083 CommandLineArgs::setup(argc,argv);
1084
1085 // Get case id as string
1086 string case_id="FSI1";
1087 if (CommandLineArgs::Argc==1)
1088 {
1089 oomph_info << "No command line arguments; running self-test FSI1"
1090 << std::endl;
1091 }
1092 else if (CommandLineArgs::Argc==2)
1093 {
1094 case_id=CommandLineArgs::Argv[1];
1095 }
1096 else
1097 {
1098 oomph_info << "Wrong number of command line arguments" << std::endl;
1099 oomph_info << "Enter none (for default) or one (namely the case id"
1100 << std::endl;
1101 oomph_info << "which should be one of: FSI1, FSI2, FSI3, CSM1"
1102 << std::endl;
1103 }
1104 std::cout << "Running case " << case_id << std::endl;
1105
1106 // Setup parameters for case identified by command line
1107 // argument
1109
1110 // Prepare output
1111 DocInfo doc_info;
1112 ofstream trace_file;
1113 doc_info.set_directory("RESLT");
1114 trace_file.open("RESLT/trace.dat");
1115
1116 // Length and height of domain
1117 double length=25.0;
1118 double height=4.1;
1119
1120 //Set up the problem
1122 RefineableQPVDElement<2,3> > problem(length, height);
1123
1124 // Default number of timesteps
1125 unsigned nstep=4000;
1126 if (Global_Parameters::Case_ID=="FSI1")
1127 {
1128 std::cout << "Reducing number of steps for FSI1 " << std::endl;
1129 nstep=400;
1130 }
1131
1132 if (CommandLineArgs::Argc==1)
1133 {
1134 std::cout << "Reducing number of steps for validation " << std::endl;
1135 nstep=2;
1136 }
1137
1138 //Timestep:
1139 double dt=Global_Parameters::Dt;
1140
1141 // Initialise timestep
1142 problem.initialise_dt(dt);
1143
1144 // Impulsive start
1145 problem.assign_initial_values_impulsive(dt);
1146
1147 // Doc the initial condition
1148 problem.doc_solution(doc_info,trace_file);
1149 doc_info.number()++;
1150
1151 // Don't re-set the initial conditions when adapting during first
1152 // timestep
1153 bool first = false;
1154
1155 // Max number of adaptation for time-stepping
1156 unsigned max_adapt=1;
1157
1158 for(unsigned i=0;i<nstep;i++)
1159 {
1160 // Solve the problem
1161 problem.unsteady_newton_solve(dt,max_adapt,first);
1162
1163 // Output the solution
1164 problem.doc_solution(doc_info,trace_file);
1165
1166 // Step number
1167 doc_info.number()++;
1168 }
1169
1170 trace_file.close();
1171
1172}//end of main
1173
1174
1175
1176
1177
1178
1179
1180
Problem class.
Vector< SolidMesh * > Traction_mesh_pt
Vector of pointers to mesh of FSI traction elements.
RefineableAlgebraicCylinderWithFlagMesh< FLUID_ELEMENT > * Fluid_mesh_pt
Pointer to fluid mesh.
ElasticRefineableRectangularQuadMesh< SOLID_ELEMENT > * Solid_mesh_pt
Pointer to solid mesh.
double Domain_height
Overall height of domain.
void doc_solution(DocInfo &doc_info, ofstream &trace_file)
Doc the solution.
void actions_after_adapt()
Actions after adapt: Re-setup the fsi lookup scheme.
RefineableAlgebraicCylinderWithFlagMesh< FLUID_ELEMENT > * fluid_mesh_pt()
Access function for the fluid mesh.
Node * Fluid_control_node_pt
Pointer to fluid control node.
void actions_before_implicit_timestep()
Update the time-dependent influx.
TurekProblem(const double &length, const double &height)
Constructor: Pass length and height of domain.
ElasticRefineableRectangularQuadMesh< SOLID_ELEMENT > *& solid_mesh_pt()
Access function for the solid mesh.
SolidMesh *& traction_mesh_pt(const unsigned &i)
Access function for the i-th mesh of FSI traction elements.
void actions_before_newton_solve()
Update function (empty)
void actions_before_newton_convergence_check()
Update the (dependent) fluid node positions following the update of the solid variables before perfor...
SolidMesh * Combined_traction_mesh_pt
Combined mesh of traction elements – only used for documentation.
void create_fsi_traction_elements()
Create FSI traction elements.
Node * Solid_control_node_pt
Pointer to solid control node.
void actions_after_newton_solve()
Update function (empty)
double Domain_length
Overall length of domain.
Global variables.
Definition turek_flag.cc:52
double Centre_x
x position of centre of cylinder
Definition turek_flag.cc:78
double Radius
Radius of cylinder.
Definition turek_flag.cc:84
double Max_flux
Max. flux.
void gravity(const double &time, const Vector< double > &xi, Vector< double > &b)
Non-dimensional gravity as body force.
double Nu
Poisson's ratio.
double Gravity
Non-dim gravity (default assignment for FSI1 test case)
double Lambda_sq
Timescale ratio for solid (dependent parameter assigned in set_parameters())
Definition turek_flag.cc:91
double Density_ratio
Density ratio (solid to fluid; default assignment for FSI1 test case)
Definition turek_flag.cc:72
double flux(const double &t)
Flux increases between Min_flux and Max_flux over period Ramp_period.
double Min_flux
Min. flux.
double Q
FSI parameter (default assignment for FSI1 test case)
Definition turek_flag.cc:68
double ReSt
Product of Reynolds and Strouhal numbers (default assignment for FSI1 test case)
Definition turek_flag.cc:65
string Case_ID
Default case ID.
Definition turek_flag.cc:55
void set_parameters(const string &case_id)
Set parameters for the various test cases.
double Re
Reynolds number (default assignment for FSI1 test case)
Definition turek_flag.cc:58
double E
Elastic modulus.
bool Ignore_fluid_loading
Ignore fluid (default assignment for FSI1 test case)
Definition turek_flag.cc:97
double Dt
Timestep.
Definition turek_flag.cc:94
double H
Height of flag.
Definition turek_flag.cc:75
ConstitutiveLaw * Constitutive_law_pt
Pointer to constitutive law.
Definition turek_flag.cc:87
double St
Strouhal number (default assignment for FSI1 test case)
Definition turek_flag.cc:61
double Centre_y
y position of centre of cylinder
Definition turek_flag.cc:81
double Ramp_period
Period for ramping up in flux.
int main(int argc, char *argv[])
Driver.