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