fsi_pseudo_solid_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
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 RefineableCollapsibleChannelMesh<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,
69 &Mesh::Default_TimeStepper) :
70 CollapsibleChannelMesh<ELEMENT>(nup, ncollapsible, ndown, ny,
72 wall_pt,
74 RefineableCollapsibleChannelMesh<ELEMENT>(nup, ncollapsible, ndown, ny,
76 wall_pt,
78
79 {
80 /// Make the current configuration the undeformed one by
81 /// setting the nodal Lagrangian coordinates to their current
82 /// Eulerian ones
84 }
85
86};
87
88
89
90
91//==========start_of_BL_Squash =========================================
92/// Namespace to define the mapping [0,1] -> [0,1] that re-distributes
93/// nodal points across the channel width.
94//======================================================================
95namespace BL_Squash
96{
97
98 /// Boundary layer width
99 double Delta=0.1;
100
101 /// Fraction of points in boundary layer
102 double Fract_in_BL=0.5;
103
104 /// Mapping [0,1] -> [0,1] that re-distributes
105 /// nodal points across the channel width
106 double squash_fct(const double& s)
107 {
108 // Default return
109 double y=s;
110 if (s<0.5*Fract_in_BL)
111 {
112 y=Delta*2.0*s/Fract_in_BL;
113 }
114 else if (s>1.0-0.5*Fract_in_BL)
115 {
116 y=2.0*Delta/Fract_in_BL*s+1.0-2.0*Delta/Fract_in_BL;
117 }
118 else
119 {
120 y=(1.0-2.0*Delta)/(1.0-Fract_in_BL)*s+
121 (Delta-0.5*Fract_in_BL)/(1.0-Fract_in_BL);
122 }
123
124 return y;
125 }
126}// end of BL_Squash
127
128
129
130//////////////////////////////////////////////////////////////////////////
131//////////////////////////////////////////////////////////////////////////
132//////////////////////////////////////////////////////////////////////////
133
134
135//====start_of_underformed_wall============================================
136/// Undeformed wall is a steady, straight 1D line in 2D space
137/// \f[ x = X_0 + \zeta \f]
138/// \f[ y = H \f]
139//=========================================================================
140class UndeformedWall : public GeomObject
141{
142
143public:
144
145 /// Constructor: arguments are the starting point and the height
146 /// above y=0.
147 UndeformedWall(const double& x0, const double& h): GeomObject(1,2)
148 {
149 X0=x0;
150 H=h;
151 }
152
153
154 /// Position vector at Lagrangian coordinate zeta
156 {
157 // Position Vector
158 r[0] = zeta[0]+X0;
159 r[1] = H;
160 }
161
162
163 /// Parametrised position on object: r(zeta). Evaluated at
164 /// previous timestep. t=0: current time; t>0: previous
165 /// timestep. Calls steady version.
166 void position(const unsigned& t, const Vector<double>& zeta,
167 Vector<double>& r) const
168 {
169 // Use the steady version
170 position(zeta,r);
171
172 } // end of position
173
174
175 /// Posn vector and its 1st & 2nd derivatives
176 /// w.r.t. to coordinates:
177 /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
178 /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ =
179 /// ddrdzeta(alpha,beta,i). Evaluated at current time.
180 virtual void d2position(const Vector<double>& zeta,
184 {
185 // Position vector
186 r[0] = zeta[0]+X0;
187 r[1] = H;
188
189 // Tangent vector
190 drdzeta(0,0)=1.0;
191 drdzeta(0,1)=0.0;
192
193 // Derivative of tangent vector
194 ddrdzeta(0,0,0)=0.0;
195 ddrdzeta(0,0,1)=0.0;
196
197 } // end of d2position
198
199 private :
200
201 /// x position of the undeformed beam's left end.
202 double X0;
203
204 /// Height of the undeformed wall above y=0.
205 double H;
206
207}; //end_of_undeformed_wall
208
209
210
211
212
213
214
215//====start_of_physical_parameters=====================
216/// Namespace for phyical parameters
217//======================================================
219{
220 /// Reynolds number
221 double Re=50.0;
222
223 /// Womersley = Reynolds times Strouhal
224 double ReSt=50.0;
225
226 /// Default pressure on the left boundary
227 double P_up=0.0;
228
229 /// Pseudo-solid mass density
230 double Lambda_sq=0.0;
231
232 /// Pseudo-solid Poisson ratio
233 double Nu=0.1;
234
235 /// Traction applied on the fluid at the left (inflow) boundary
236 void prescribed_traction(const double& t,
237 const Vector<double>& x,
238 const Vector<double>& n,
240 {
241 traction.resize(2);
242 traction[0]=P_up;
243 traction[1]=0.0;
244
245 } //end traction
246
247 /// Non-dimensional wall thickness. As in Jensen & Heil (2003) paper.
248 double H=1.0e-2;
249
250 /// 2nd Piola Kirchhoff pre-stress. As in Jensen & Heil (2003) paper.
251 double Sigma0=1.0e3;
252
253 /// External pressure
254 double P_ext=0.0;
255
256 /// Load function: Apply a constant external pressure to the wall.
257 /// Note: This is the load without the fluid contribution!
258 /// Fluid load gets added on by FSIWallElement.
259 void load(const Vector<double>& xi, const Vector<double>& x,
261 {
262 for(unsigned i=0;i<2;i++)
263 {
264 load[i] = -P_ext*N[i];
265 }
266 } //end of load
267
268
269 /// Fluid structure interaction parameter: Ratio of stresses used for
270 /// non-dimensionalisation of fluid to solid stresses.
271 double Q=1.0e-5;
272
273
274} // end of namespace
275
276
277
278
279//====start_of_problem_class==========================================
280/// Problem class
281//====================================================================
282template <class ELEMENT>
283class FSICollapsibleChannelProblem : public Problem
284{
285
286 public :
287
288/// Constructor: The arguments are the number of elements and
289/// the lengths of the domain.
290 FSICollapsibleChannelProblem(const unsigned& nup,
291 const unsigned& ncollapsible,
292 const unsigned& ndown,
293 const unsigned& ny,
294 const double& lup,
295 const double& lcollapsible,
296 const double& ldown,
297 const double& ly);
298
299 /// Destructor (empty)
301
302 /// Access function for the specific bulk (fluid) mesh
304 {
305 // Upcast from pointer to the Mesh base class to the specific
306 // element type that we're using here.
307 return dynamic_cast<
309 (Bulk_mesh_pt);
310 }
311
312
313 /// Access function for the wall mesh
315 {
316 return Wall_mesh_pt;
317
318 } // end of access to wall mesh
319
320
321 /// Actions before adapt: Wipe the mesh of prescribed traction elements
323
324 /// Actions after adapt: Rebuild the mesh of prescribed traction elements
325 /// and reset FSI
327
328 /// Update the problem specs before solve (empty)
330
331 /// Update the problem after solve (empty)
333
334 /// Update no slip before Newton convergence check
336 {
337 // Moving wall: No slip; this implies that the velocity needs
338 // to be updated in response to wall motion
339 unsigned ibound=3;
340 unsigned num_nod=bulk_mesh_pt()->nboundary_node(ibound);
341 for (unsigned inod=0;inod<num_nod;inod++)
342 {
343 // Which node are we dealing with?
344 Node* node_pt=bulk_mesh_pt()->boundary_node_pt(ibound,inod);
345
346 // Apply no slip
347 FSI_functions::apply_no_slip_on_moving_wall(node_pt);
348 }
349 }
350
351
352 /// Doc the solution
354
355 /// Apply initial conditions
357
358private :
359
360 /// Create the prescribed traction elements on boundary b
361 void create_traction_elements(const unsigned &b,
362 Mesh* const &bulk_mesh_pt,
363 Mesh* const &traction_mesh_pt);
364
365 /// Delete prescribed traction elements from the surface mesh
367
368 /// Create elements that enforce prescribed boundary motion
369 /// by Lagrange multiplilers
371
372 /// Delete elements that enforce prescribed boundary motion
373 /// by Lagrange multiplilers
375
376 /// Number of elements in the x direction in the upstream part of the channel
377 unsigned Nup;
378
379 /// Number of elements in the x direction in the collapsible part of
380 /// the channel
381 unsigned Ncollapsible;
382
383 /// Number of elements in the x direction in the downstream part of the channel
384 unsigned Ndown;
385
386 /// Number of elements across the channel
387 unsigned Ny;
388
389 /// x-length in the upstream part of the channel
390 double Lup;
391
392 /// x-length in the collapsible part of the channel
393 double Lcollapsible;
394
395 /// x-length in the downstream part of the channel
396 double Ldown;
397
398 /// Transverse length
399 double Ly;
400
401 /// Pointer to the "bulk" mesh
403
404 /// Pointer to the "surface" mesh that applies the traction at the
405 /// inflow
407
408 /// Pointers to meshes of Lagrange multiplier elements
410
411 /// Pointer to the "wall" mesh
413
414 /// Geometric object incarnation of the wall mesh
416
417 /// Pointer to the left control node
419
420 /// Pointer to right control node
422
423 /// Pointer to control node on the wall
425
426 /// Constitutive law used to determine the mesh deformation
428
429};//end of problem class
430
431
432
433
434//=====start_of_constructor======================================
435/// Constructor for the collapsible channel problem
436//===============================================================
437template <class ELEMENT>
439 const unsigned& nup,
440 const unsigned& ncollapsible,
441 const unsigned& ndown,
442 const unsigned& ny,
443 const double& lup,
444 const double& lcollapsible,
445 const double& ldown,
446 const double& ly)
447{
448 // Store problem parameters
449 Nup=nup;
450 Ncollapsible=ncollapsible;
451 Ndown=ndown;
452 Ny=ny;
453 Lup=lup;
454 Lcollapsible=lcollapsible;
455 Ldown=ldown;
456 Ly=ly;
457
458
459 // Overwrite maximum allowed residual to accomodate bad initial guesses
460 Problem::Max_residuals=1000.0;
461
462 // Allocate the timestepper for the Navier-Stokes equations
464
465 // Add the fluid timestepper to the Problem's collection of timesteppers.
467
468 // Create a dummy Steady timestepper that stores two history values
470
471 // Add the wall timestepper to the Problem's collection of timesteppers.
473
474 // Geometric object that represents the undeformed wall:
475 // A straight line at height y=ly; starting at x=lup.
477
478 //Create the "wall" mesh with FSI Hermite beam elements, passing the
479 //dummy wall timestepper to the constructor
482
483 // Build a geometric object (one Lagrangian, two Eulerian coordinates)
484 // from the wall mesh
485 Wall_geom_object_pt=
486 new MeshAsGeomObject(Wall_mesh_pt);
487
488
489
490 //Build bulk (fluid) mesh
491 Bulk_mesh_pt =
493 (nup, ncollapsible, ndown, ny,
495 Wall_geom_object_pt,
497
498 // Set a non-trivial boundary-layer-squash function...
499 Bulk_mesh_pt->bl_squash_fct_pt() = &BL_Squash::squash_fct;
500
501 // ... and update the nodal positions accordingly
502 bool update_all_solid_nodes=true;
503 Bulk_mesh_pt->node_update(update_all_solid_nodes);
504 Bulk_mesh_pt->set_lagrangian_nodal_coordinates();
505
506 if (CommandLineArgs::Argc>1)
507 {
508 // One round of uniform refinement
509 Bulk_mesh_pt->refine_uniformly();
510 }
511
512 // Create "surface mesh" that will contain only the prescribed-traction
513 // elements. The constructor just creates the mesh without
514 // giving it any elements, nodes, etc.
515 Applied_fluid_traction_mesh_pt = new Mesh;
516
517 // Create prescribed-traction elements from all elements that are
518 // adjacent to boundary 5 (left boundary), but add them to a separate mesh.
519 create_traction_elements(5,Bulk_mesh_pt,Applied_fluid_traction_mesh_pt);
520
521 // Construct the mesh of elements that enforce prescribed boundary motion
522 // by Lagrange multipliers
523 Lagrange_multiplier_mesh_pt=new SolidMesh;
524 create_lagrange_multiplier_elements();
525
526 // Add the sub meshes to the problem
527 add_sub_mesh(Bulk_mesh_pt);
528 add_sub_mesh(Applied_fluid_traction_mesh_pt);
529 add_sub_mesh(Wall_mesh_pt);
530 add_sub_mesh(Lagrange_multiplier_mesh_pt);
531
532 // Combine all submeshes into a single Mesh
534
535 //Set errror estimator
537 bulk_mesh_pt()->spatial_error_estimator_pt()=error_estimator_pt;
538
539 //Set the constitutive law
540 Constitutive_law_pt = new GeneralisedHookean(&Global_Physical_Variables::Nu);
541
542
543 // Complete build of fluid mesh
544 //-----------------------------
545
546 // Loop over the elements to set up element-specific
547 // things that cannot be handled by constructor
548 unsigned n_element=Bulk_mesh_pt->nelement();
549 for(unsigned e=0;e<n_element;e++)
550 {
551
552 // Upcast from GeneralisedElement to the present element
553 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
554
555 //Set the Reynolds number
557
558 // Set the Womersley number
560
561 //Set the constitutive law
562 el_pt->constitutive_law_pt() = Constitutive_law_pt;
563
564 // Density of pseudo-solid
566
567 } // end loop over elements
568
569
570 // Pin redudant pressure dofs
572 pin_redundant_nodal_pressures(Bulk_mesh_pt->element_pt());
573
574
575 // Apply boundary conditions for fluid
576 //------------------------------------
577
578
579 //Pin the velocity on the boundaries
580 //x and y-velocities pinned along boundary 0 (bottom boundary) :
581 unsigned ibound=0;
582 unsigned num_nod= bulk_mesh_pt()->nboundary_node(ibound);
583 for (unsigned inod=0;inod<num_nod;inod++)
584 {
585 for(unsigned i=0;i<2;i++)
586 {
587 bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
588 }
589 }
590
591 //x and y-velocities pinned along boundaries 2, 3, 4 (top boundaries) :
592 for(ibound=2;ibound<5;ibound++)
593 {
594 num_nod= bulk_mesh_pt()->nboundary_node(ibound);
595 for (unsigned inod=0;inod<num_nod;inod++)
596 {
597 for(unsigned i=0;i<2;i++)
598 {
599 bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
600 }
601 }
602 }
603
604 //y-velocity pinned along boundary 1 (right boundary):
605 ibound=1;
606 num_nod= bulk_mesh_pt()->nboundary_node(ibound);
607 for (unsigned inod=0;inod<num_nod;inod++)
608 {
609 bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
610 }
611
612
613 //y-velocity pinned along boundary 5 (left boundary):
614 ibound=5;
615 num_nod= bulk_mesh_pt()->nboundary_node(ibound);
616 for (unsigned inod=0;inod<num_nod;inod++)
617 {
618
619 bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
620
621 }//end of pin_velocity
622
623
624 //Pin the nodal position on all boundaries apart from 3 (the moving wall)
625 for (unsigned ibound=0;ibound<6;ibound++)
626 {
627 if (ibound!=3)
628 {
629 unsigned num_nod= bulk_mesh_pt()->nboundary_node(ibound);
630 for (unsigned inod=0;inod<num_nod;inod++)
631 {
632 for(unsigned i=0;i<2;i++)
633 {
634 dynamic_cast<SolidNode*>(bulk_mesh_pt()->
636 ->pin_position(i);
637 }
638 }
639 }
640 }
641
642 // Now pin all nodal positions in the rigid bits
643 unsigned nnod=bulk_mesh_pt()->nnode();
644 for (unsigned j=0;j<nnod;j++)
645 {
646 SolidNode* nod_pt=dynamic_cast<SolidNode*>(bulk_mesh_pt()->node_pt(j));
647 if ((nod_pt->x(0)<=lup)||(nod_pt->x(0)>=(lup+lcollapsible)))
648 {
649 for(unsigned i=0;i<2;i++)
650 {
651 nod_pt->pin_position(i);
652 }
653 }
654 }
655
656
657 // Complete build of applied traction elements
658 //--------------------------------------------
659
660 // Loop over the traction elements to pass pointer to prescribed
661 // traction function
662 unsigned n_el=Applied_fluid_traction_mesh_pt->nelement();
663 for(unsigned e=0;e<n_el;e++)
664 {
665 // Upcast from GeneralisedElement to NavierStokes traction element
668 Applied_fluid_traction_mesh_pt->element_pt(e));
669
670 // Set the pointer to the prescribed traction function
672
673 }
674
675
676
677
678 // Complete build of wall elements
679 //--------------------------------
680
681 //Loop over the elements to set physical parameters etc.
682 n_element = wall_mesh_pt()->nelement();
683 for(unsigned e=0;e<n_element;e++)
684 {
685 // Upcast to the specific element type
687 dynamic_cast<FSIHermiteBeamElement*>(wall_mesh_pt()->element_pt(e));
688
689 // Set physical parameters for each element:
692
693 // Set the load vector for each element
694 elem_pt->load_vector_fct_pt() = &Global_Physical_Variables::load;
695
696 // Function that specifies the load ratios
698
699 // Set the undeformed shape for each element
700 elem_pt->undeformed_beam_pt() = undeformed_wall_pt;
701
702 // The normal on the wall elements as computed by the FSIHermiteElements
703 // points away from the fluid rather than into the fluid (as assumed
704 // by default)
705 elem_pt->set_normal_pointing_out_of_fluid();
706
707 } // end of loop over elements
708
709
710
711 // Boundary conditions for wall mesh
712 //----------------------------------
713
714 // Set the boundary conditions: Each end of the beam is fixed in space
715 // Loop over the boundaries (ends of the beam)
716 for(unsigned b=0;b<2;b++)
717 {
718 // Pin displacements in both x and y directions
719 wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(0);
720 wall_mesh_pt()->boundary_node_pt(b,0)->pin_position(1);
721 }
722
723
724
725 //Choose control nodes
726 //---------------------
727
728 // Left boundary
729 ibound=5;
730 num_nod= bulk_mesh_pt()->nboundary_node(ibound);
731 unsigned control_nod=num_nod/2;
732 Left_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
733
734 // Right boundary
735 ibound=1;
736 num_nod= bulk_mesh_pt()->nboundary_node(ibound);
738 Right_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
739
740
741 // Set the pointer to the control node on the wall
742 num_nod= wall_mesh_pt()->nnode();
743 Wall_node_pt=wall_mesh_pt()->node_pt(Ncollapsible/2);
744
745
746
747 // Setup FSI
748 //----------
749
750 // The velocity of the fluid nodes on the wall (fluid mesh boundary 3)
751 // is set by the wall motion -- hence the no-slip condition needs to be
752 // re-applied whenever a node update is performed for these nodes.
753 // Such tasks may be performed automatically by the auxiliary node update
754 // function specified by a function pointer:
755 ibound=3;
756 num_nod= bulk_mesh_pt()->nboundary_node(ibound);
757 for (unsigned inod=0;inod<num_nod;inod++)
758 {
759 bulk_mesh_pt()->boundary_node_pt(ibound, inod)->
761 FSI_functions::apply_no_slip_on_moving_wall);
762 }
763
764 // Work out which fluid dofs affect the residuals of the wall elements:
765 // We pass the boundary between the fluid and solid meshes and
766 // pointers to the meshes. The interaction boundary is boundary 3 of the
767 // 2D fluid mesh.
768 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
769 (this,3,Bulk_mesh_pt,Wall_mesh_pt);
770
771 // Setup equation numbering scheme
772 cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
773
774
775}//end of constructor
776
777
778
779
780//====start_of_doc_solution===================================================
781/// Doc the solution
782//============================================================================
783template <class ELEMENT>
786{
788 char filename[100];
789
790 // Number of plot points
791 unsigned npts;
792 npts=5;
793
794 // Output fluid solution
795 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
796 doc_info.number());
797 some_file.open(filename);
798 bulk_mesh_pt()->output(some_file,npts);
799 some_file.close();
800
801 // Document the wall shape
802 snprintf(filename, sizeof(filename), "%s/beam%i.dat",doc_info.directory().c_str(),
803 doc_info.number());
804 some_file.open(filename);
805 wall_mesh_pt()->output(some_file,npts);
806 some_file.close();
807
808 // Loop over all elements do dump out previous solutions
809 // (get the number of previous timesteps available from the wall
810 // timestepper)
811 unsigned nsteps=time_stepper_pt(1)->nprev_values();
812 for (unsigned t=0;t<=nsteps;t++)
813 {
814 snprintf(filename, sizeof(filename), "%s/wall%i-%i.dat",doc_info.directory().c_str(),
815 doc_info.number(),t);
816 some_file.open(filename);
817 unsigned n_elem=wall_mesh_pt()->nelement();
818 for (unsigned ielem=0;ielem<n_elem;ielem++)
819 {
820 dynamic_cast<FSIHermiteBeamElement*>(wall_mesh_pt()->element_pt(ielem))->
822 }
823 some_file.close();
824 } // end of output of previous solutions
825
826
827
828 // Write trace file
829 trace_file << time_pt()->time() << " "
830 << Wall_node_pt->x(1) << " "
831 << Left_node_pt->value(0) << " "
832 << Right_node_pt->value(0) << " "
834 << std::endl;
835
836} // end_of_doc_solution
837
838
839
840
841
842//=====start_of_create_traction_elements======================================
843/// Create the traction elements
844//============================================================================
845template <class ELEMENT>
847 const unsigned &b, Mesh* const &bulk_mesh_pt, Mesh* const &traction_mesh_pt)
848{
849
850 // How many bulk elements are adjacent to boundary b?
851 unsigned n_element = bulk_mesh_pt->nboundary_element(b);
852
853 // Loop over the bulk elements adjacent to boundary b?
854 for(unsigned e=0;e<n_element;e++)
855 {
856 // Get pointer to the bulk element that is adjacent to boundary b
857 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>
858 (bulk_mesh_pt->boundary_element_pt(b,e));
859
860 //What is the index of the face of element e along boundary
861 int face_index = bulk_mesh_pt->face_index_at_boundary(b,e);
862
863 // Build the corresponding prescribed-traction element
866
867 //Add the prescribed-traction element to the surface mesh
868 traction_mesh_pt->add_element_pt(flux_element_pt);
869
870 } //end of loop over bulk elements adjacent to boundary b
871
872} // end of create_traction_elements
873
874
875
876//============start_of_delete_traction_elements==============================
877/// Delete traction elements and wipe the surface mesh
878//=======================================================================
879template<class ELEMENT>
882{
883 // How many surface elements are in the surface mesh
884 unsigned n_element = surface_mesh_pt->nelement();
885
886 // Loop over the surface elements
887 for(unsigned e=0;e<n_element;e++)
888 {
889 // Kill surface element
890 delete surface_mesh_pt->element_pt(e);
891 }
892
893 // Wipe the mesh
894 surface_mesh_pt->flush_element_and_node_storage();
895
896} // end of delete_traction_elements
897
898
899//============start_of_create_lagrange_multiplier_elements===============
900/// Create elements that impose the prescribed boundary displacement
901//=======================================================================
902template<class ELEMENT>
905{
906 // Lagrange multiplier elements are located on boundary 3:
907 unsigned b=3;
908
909 // How many bulk elements are adjacent to boundary b?
910 unsigned n_element = bulk_mesh_pt()->nboundary_element(b);
911
912 // Loop over the bulk elements adjacent to boundary b?
913 for(unsigned e=0;e<n_element;e++)
914 {
915 // Get pointer to the bulk element that is adjacent to boundary b
916 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
917 bulk_mesh_pt()->boundary_element_pt(b,e));
918
919 //Find the index of the face of element e along boundary b
920 int face_index = bulk_mesh_pt()->face_index_at_boundary(b,e);
921
922 // Create new element and add to mesh
923 Lagrange_multiplier_mesh_pt->add_element_pt(
926 }
927
928
929 // Loop over the elements in the Lagrange multiplier element mesh
930 // for elements on the top boundary (boundary 3)
931 n_element=Lagrange_multiplier_mesh_pt->nelement();
932 for(unsigned i=0;i<n_element;i++)
933 {
934 //Cast to a Lagrange multiplier element
937 (Lagrange_multiplier_mesh_pt->element_pt(i));
938
939 // Set the GeomObject that defines the boundary shape and set
940 // which bulk boundary we are attached to(needed to extract
941 // the boundary coordinate from the bulk nodes)
942 el_pt->set_boundary_shape_geom_object_pt(Wall_geom_object_pt,b);
943
944 // Loop over the nodes
945 unsigned nnod=el_pt->nnode();
946 for (unsigned j=0;j<nnod;j++)
947 {
948 Node* nod_pt = el_pt->node_pt(j);
949
950 // Is the node also on boundary 2 or 4?
951 if ((nod_pt->is_on_boundary(2))||(nod_pt->is_on_boundary(4)))
952 {
953 // How many nodal values were used by the "bulk" element
954 // that originally created this node?
955 unsigned n_bulk_value=el_pt->nbulk_value(j);
956
957 // The remaining ones are Lagrange multipliers and we pin them.
958 unsigned nval=nod_pt->nvalue();
959 for (unsigned j=n_bulk_value;j<nval;j++)
960 {
961 nod_pt->pin(j);
962 }
963 }
964 }
965 }
966
967} // end of create_lagrange_multiplier_elements
968
969
970
971
972//====start_of_delete_lagrange_multiplier_elements=======================
973/// Delete elements that impose the prescribed boundary displacement
974/// and wipe the associated mesh
975//=======================================================================
976template<class ELEMENT>
979{
980 // How many surface elements are in the surface mesh
981 unsigned n_element = Lagrange_multiplier_mesh_pt->nelement();
982
983 // Loop over the surface elements
984 for(unsigned e=0;e<n_element;e++)
985 {
986 // Kill surface element
987 delete Lagrange_multiplier_mesh_pt->element_pt(e);
988 }
989
990 // Wipe the mesh
991 Lagrange_multiplier_mesh_pt->flush_element_and_node_storage();
992
993} // end of delete_lagrange_multiplier_elements
994
995
996
997//====start_of_apply_initial_condition========================================
998/// Apply initial conditions
999//============================================================================
1000template <class ELEMENT>
1002{
1003 // Check that timestepper is from the BDF family
1004 if (time_stepper_pt()->type()!="BDF")
1005 {
1006 std::ostringstream error_stream;
1007 error_stream << "Timestepper has to be from the BDF family!\n"
1008 << "You have specified a timestepper from the "
1009 << time_stepper_pt()->type() << " family" << std::endl;
1010
1011 throw OomphLibError(error_stream.str(),
1014 }
1015
1016 // Loop over the nodes to set initial guess everywhere
1017 unsigned num_nod = bulk_mesh_pt()->nnode();
1018 for (unsigned n=0;n<num_nod;n++)
1019 {
1020 // Get nodal coordinates
1021 Vector<double> x(2);
1022 x[0]=bulk_mesh_pt()->node_pt(n)->x(0);
1023 x[1]=bulk_mesh_pt()->node_pt(n)->x(1);
1024
1025 // Assign initial condition: Steady Poiseuille flow
1026 bulk_mesh_pt()->node_pt(n)->set_value(0,6.0*(x[1]/Ly)*(1.0-(x[1]/Ly)));
1027 bulk_mesh_pt()->node_pt(n)->set_value(1,0.0);
1028 }
1029
1030 // Assign initial values for an impulsive start
1031 bulk_mesh_pt()->assign_initial_values_impulsive();
1032 wall_mesh_pt()->assign_initial_values_impulsive();
1033
1034} // end of set_initial_condition
1035
1036
1037
1038
1039
1040//=========start_of_actions_before_adapt==================================
1041/// Actions before adapt: Wipe the mesh of prescribed traction elements
1042//========================================================================
1043template<class ELEMENT>
1045{
1046 // Kill the traction elements and wipe surface mesh
1047 delete_traction_elements(Applied_fluid_traction_mesh_pt);
1048
1049 // Kill the elements and wipe surface mesh
1050 delete_lagrange_multiplier_elements();
1051
1052 // Rebuild the global mesh.
1054
1055} // end of actions_before_adapt
1056
1057
1058
1059//==========start_of_actions_after_adapt==================================
1060/// Actions after adapt: Rebuild the mesh of prescribed traction elements
1061//========================================================================
1062template<class ELEMENT>
1064{
1065 // Create prescribed-flux elements from all elements that are
1066 // adjacent to boundary 5 and add them to surface mesh
1067 create_traction_elements(5,Bulk_mesh_pt,Applied_fluid_traction_mesh_pt);
1068
1069 // Create the elements that impose the displacement constraint
1070 // and attach them to the bulk elements that are
1071 // adjacent to the moving boundary
1072 create_lagrange_multiplier_elements();
1073
1074 // Rebuild the global mesh
1076
1077 // Unpin all pressure dofs
1079 unpin_all_pressure_dofs(Bulk_mesh_pt->element_pt());
1080
1081 // Pin redundant pressure dofs
1083 pin_redundant_nodal_pressures(Bulk_mesh_pt->element_pt());
1084
1085 // Loop over the traction elements to pass pointer to prescribed
1086 // traction function
1087 unsigned n_element=Applied_fluid_traction_mesh_pt->nelement();
1088 for(unsigned e=0;e<n_element;e++)
1089 {
1090 // Upcast from GeneralisedElement to NavierStokesTractionElement element
1093 Applied_fluid_traction_mesh_pt->element_pt(e));
1094
1095 // Set the pointer to the prescribed traction function
1097 }
1098
1099 // The functions used to update the no slip boundary conditions
1100 // must be set on any new nodes that have been created during the
1101 // mesh adaptation process.
1102 // There is no mechanism by which auxiliary update functions
1103 // are copied to newly created nodes.
1104 // (because, unlike boundary conditions, they don't occur exclusively
1105 // at boundaries)
1106
1107 // The velocity of the fluid nodes on the wall (fluid mesh boundary 3)
1108 // is set by the wall motion -- hence the no-slip condition needs to be
1109 // re-applied whenever a node update is performed for these nodes.
1110 // Such tasks may be performed automatically by the auxiliary node update
1111 // function specified by a function pointer:
1112 unsigned ibound=3;
1113 unsigned num_nod=bulk_mesh_pt()->nboundary_node(ibound);
1114 for (unsigned inod=0;inod<num_nod;inod++)
1115 {
1116 bulk_mesh_pt()->boundary_node_pt(ibound, inod)->
1118 FSI_functions::apply_no_slip_on_moving_wall);
1119 }
1120
1121 // (Re-)setup fsi: Work out which fluid dofs affect wall elements
1122 // the correspondance between wall dofs and fluid elements is handled
1123 // during the remeshing, but the "reverse" association must be done
1124 // separately. We need to set up the interaction every time because the fluid
1125 // element adjacent to a given solid element's integration point may have
1126 // changed.We pass the boundary between the fluid and solid meshes and
1127 // pointers to the meshes. The interaction boundary is boundary 3 of
1128 // the Fluid mesh.
1129 FSI_functions::setup_fluid_load_info_for_solid_elements<ELEMENT,2>
1130 (this,3,Bulk_mesh_pt,Wall_mesh_pt);
1131
1132} // end of actions_after_adapt
1133
1134
1135
1136//============start_of_main====================================================
1137/// Driver code for a collapsible channel problem with FSI.
1138/// Presence of command line arguments indicates validation run with
1139/// coarse resolution and small number of timesteps.
1140//=============================================================================
1141int main(int argc, char* argv[])
1142{
1143 // Store command line arguments
1144 CommandLineArgs::setup(argc,argv);
1145
1146 // Reduction in resolution for validation run?
1147 unsigned coarsening_factor=4;
1148 if (CommandLineArgs::Argc>1)
1149 {
1151 }
1152
1153 // Number of elements in the domain
1154 unsigned nup=20/coarsening_factor;
1155 unsigned ncollapsible=40/coarsening_factor;
1156 unsigned ndown=40/coarsening_factor;
1157 unsigned ny=16/coarsening_factor;
1158
1159 // Length of the domain
1160 double lup=5.0;
1161 double lcollapsible=10.0;
1162 double ldown=10.0;
1163 double ly=1.0;
1164
1165 // Set external pressure (on the wall stiffness scale).
1167
1168 // Pressure on the left boundary: This is consistent with steady
1169 // Poiseuille flow
1171
1172
1173#ifdef TAYLOR_HOOD
1174
1175 // Build the problem with QtaylorHoodElements
1180 problem(nup, ncollapsible, ndown, ny,
1182
1183#else
1184
1185 // Build the problem with QCrouzeixRaviartElements
1190 problem(nup, ncollapsible, ndown, ny,
1192
1193#endif
1194
1195 // Timestep. Note: Preliminary runs indicate that the period of
1196 // the oscillation is about 1 so this gives us 40 steps per period.
1197 double dt=1.0/40.0;
1198
1199 // Initial time for the simulation
1200 double t_min=0.0;
1201
1202 // Maximum time for simulation
1203 double t_max=3.5;
1204
1205 // Initialise timestep
1206 problem.time_pt()->time()=t_min;
1207 problem.initialise_dt(dt);
1208
1209 // Apply initial condition
1210 problem.set_initial_condition();
1211
1212 //Set output directory
1214 doc_info.set_directory("RESLT");
1215
1216 // Open a trace file
1218 char filename[100];
1219 snprintf(filename, sizeof(filename), "%s/trace.dat",doc_info.directory().c_str());
1220 trace_file.open(filename);
1221
1222 // Output the initial condition
1223 problem.doc_solution(doc_info, trace_file);
1224
1225 // Increment step number
1226 doc_info.number()++;
1227
1228 // Find number of timesteps (reduced for validation)
1229 unsigned nstep = unsigned((t_max-t_min)/dt);
1230 if (CommandLineArgs::Argc>1)
1231 {
1232 nstep=3;
1233 }
1234
1235
1236 // Set targets for spatial adaptivity
1237 problem.bulk_mesh_pt()->max_permitted_error()=1.0e-3;
1238 problem.bulk_mesh_pt()->min_permitted_error()=1.0e-5;
1239
1240 // Overwrite for validation run
1241 if (CommandLineArgs::Argc>1)
1242 {
1243 // Set targets for spatial adaptivity
1244 problem.bulk_mesh_pt()->max_permitted_error()=0.5e-2;
1245 problem.bulk_mesh_pt()->min_permitted_error()=0.5e-4;
1246 }
1247
1248 // When performing the first timestep, we can adapt the mesh as many times
1249 // as we want because the initial condition can be re-set
1250 unsigned max_adapt=3;
1251 bool first=true;
1252
1253 // Timestepping loop
1254 for (unsigned istep=0;istep<nstep;istep++)
1255 {
1256 // Solve the problem
1257 problem.unsteady_newton_solve(dt,max_adapt,first);
1258
1259 // Outpt the solution
1260 problem.doc_solution(doc_info, trace_file);
1261
1262 // Step number
1263 doc_info.number()++;
1264
1265 // We've done the first step
1266 first=false;
1267 max_adapt=1;
1268 }
1269
1270 // Close trace file.
1271 trace_file.close();
1272
1273}//end of main
1274
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 multiplilers.
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.
ElasticRefineableCollapsibleChannelMesh< ELEMENT > * bulk_mesh_pt()
Access function for the specific bulk (fluid) mesh.
double Lup
x-length in the upstream part of the channel
MeshAsGeomObject * Wall_geom_object_pt
Geometric object incarnation of the wall mesh.
void actions_before_adapt()
Actions before adapt: Wipe the mesh of prescribed traction elements.
MacroElementNodeUpdateCollapsibleChannelMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the "bulk" mesh.
void delete_lagrange_multiplier_elements()
Delete elements that enforce prescribed boundary motion by Lagrange multiplilers.
ElasticRefineableCollapsibleChannelMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the "bulk" mesh.
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.
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.
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 ...
Refineable collapsible channel mesh. The mesh is derived from the SimpleRectangularQuadMesh so it's n...
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.