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