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
36
37//Include the fluid mesh
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.
59 ElasticCollapsibleChannelMesh<ELEMENT>(const unsigned& nup,
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,
69 &Mesh::Default_TimeStepper) :
70 CollapsibleChannelMesh<ELEMENT>(nup, ncollapsible, ndown, ny,
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.
285 FSICollapsibleChannelProblem(const unsigned& nup,
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
335 FSI_functions::apply_no_slip_on_moving_wall(node_pt);
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
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 =
482 (nup, ncollapsible, ndown, ny,
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)->
734 FSI_functions::apply_no_slip_on_moving_wall);
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.
741 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
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
1021 CommandLineArgs::setup(argc,argv);
1022
1023 // Reduction in resolution for validation run?
1024 unsigned coarsening_factor=4;
1025 if (CommandLineArgs::Argc>1)
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
1057 problem(nup, ncollapsible, ndown, ny,
1059
1060#else
1061
1062 // Build the problem with QCrouzeixRaviartElements
1067 problem(nup, ncollapsible, ndown, ny,
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);
1107 if (CommandLineArgs::Argc>1)
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
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.
Basic collapsible channel mesh. The mesh is derived from the SimpleRectangularQuadMesh so it's node a...
GeomObject *& wall_pt()
Access function to GeomObject representing wall.
unsigned Ncollapsible
Number of element columns in collapsible part.
unsigned Nup
Number of element columns in upstream part.
unsigned Ndown
Number of element columns in downstream part.
unsigned Ny
Number of element rows across channel.
Collapsible channel mesh with MacroElement-based node update. The collapsible segment is represented ...
const unsigned & ny() const
Access function for number of elements in y directions.
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.