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