collapsible_channel_algebraic.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-2023 Matthias Heil and Andrew Hazel
7 //LIC//
8 //LIC// This library is free software; you can redistribute it and/or
9 //LIC// modify it under the terms of the GNU Lesser General Public
10 //LIC// License as published by the Free Software Foundation; either
11 //LIC// version 2.1 of the License, or (at your option) any later version.
12 //LIC//
13 //LIC// This library is distributed in the hope that it will be useful,
14 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 //LIC// Lesser General Public License for more details.
17 //LIC//
18 //LIC// You should have received a copy of the GNU Lesser General Public
19 //LIC// License along with this library; if not, write to the Free Software
20 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 //LIC// 02110-1301 USA.
22 //LIC//
23 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24 //LIC//
25 //LIC//====================================================================
26 #include <iostream>
27 
28 // Generic oomph-lib includes
29 #include "generic.h"
30 
31 // Generic oomph-lib includes
32 #include "navier_stokes.h"
33 
34 //Include the mesh
35 #include "my_alg_channel_mesh.h"
36 
37 using namespace std;
38 
39 using namespace oomph;
40 
41 //==========start_of_BL_Squash =========================================
42 /// Namespace to define the mapping [0,1] -> [0,1] that re-distributes
43 /// nodal points across the channel width.
44 //======================================================================
45 namespace BL_Squash
46 {
47 
48  /// Boundary layer width
49  double Delta=0.1;
50 
51  /// Fraction of points in boundary layer
52  double Fract_in_BL=0.5;
53 
54  /// Mapping [0,1] -> [0,1] that re-distributes
55  /// nodal points across the channel width
56  double squash_fct(const double& s)
57  {
58  // Default return
59  double y=s;
60  if (s<0.5*Fract_in_BL)
61  {
62  y=Delta*2.0*s/Fract_in_BL;
63  }
64  else if (s>1.0-0.5*Fract_in_BL)
65  {
66  y=2.0*Delta/Fract_in_BL*s+1.0-2.0*Delta/Fract_in_BL;
67  }
68  else
69  {
70  y=(1.0-2.0*Delta)/(1.0-Fract_in_BL)*s+
71  (Delta-0.5*Fract_in_BL)/(1.0-Fract_in_BL);
72  }
73 
74  return y;
75  }
76 }// end of BL_Squash
77 
78 
79 
80 
81 
82 //===============start_of_oscillating_wall=================================
83 /// Straight, horizontal channel wall at \f$ y=H \f$ deforms into an
84 /// oscillating parabola. The amplitude of the oscillation
85 /// \f$ A \f$ and its period is \f$ T \f$.
86 /// The position vector to a point on the wall, parametrised by
87 /// the Lagrangian coordinate \f$ \zeta \in [0,L]\f$, is therefore given by
88 /// \f[ {\bf R}(\zeta,t) =
89 /// \left(
90 /// \begin{array}{c}
91 /// L_{up} + \zeta \newline
92 /// 1
93 /// \end{array}
94 /// \right)
95 /// + A
96 /// \left(
97 /// \begin{array}{l}
98 /// - B \sin\left(\frac{2\pi}{L_{collapsible}}\zeta\right) \newline
99 /// \left(\frac{2}{L_{collapsible}}\right)^2 \zeta \ (L-\zeta)
100 /// \end{array}
101 /// \right)
102 /// \ \sin\left(\frac{2\pi t}{T}\right)
103 /// \ {\cal R}(t)
104 /// \f]
105 /// The parameter \f$ B \f$ is zero by default. If it is set to a nonzero
106 /// value, the material particles on the wall also perform some
107 /// horizontal motion. The "ramp" function
108 /// \f[
109 /// {\cal R}(t) = \left\{
110 /// \begin{array}{ll}
111 /// \frac{1}{2}(1-\cos(\pi t/T)) & \mbox{for $t<T$} \\ 1 & \mbox{for $t \ge T$}
112 /// \end{array}
113 /// \right.
114 /// \f]
115 /// provides a "smooth" startup of the oscillation.
116 //=========================================================================
117 class OscillatingWall : public GeomObject
118 {
119 
120 public:
121 
122  /// Constructor : It's a 2D object, parametrised by
123  /// one Lagrangian coordinate. Arguments: height at ends, x-coordinate of
124  /// left end, length, amplitude of deflection, period of oscillation, and
125  /// pointer to time object
126  OscillatingWall(const double& h, const double& x_left, const double& l,
127  const double& a, const double& period, Time* time_pt) :
128  GeomObject(1,2), H(h), Length(l), X_left(x_left), A(a), B(0.0), T(period),
129  Time_pt(time_pt)
130  {}
131 
132  /// Destructor: Empty
133  ~OscillatingWall(){}
134 
135 /// Access function to the amplitude
136  double& amplitude(){return A;}
137 
138 /// Access function to the period
139  double& period(){return T;}
140 
141  /// Position vector at Lagrangian coordinate zeta
142  /// at time level t.
143  void position(const unsigned& t, const Vector<double>&zeta,
144  Vector<double>& r) const
145  {
146  using namespace MathematicalConstants;
147 
148  // Smoothly ramp up the oscillation during the first period
149  double ramp=1.0;
150  if (Time_pt->time(t)<T)
151  {
152  ramp=0.5*(1.0-cos(Pi*Time_pt->time(t)/T));
153  }
154 
155  // Position vector
156  r[0] = zeta[0]+X_left
157  -B*A*sin(2.0*3.14159*zeta[0]/Length)*
158  sin(2.0*Pi*(Time_pt->time(t))/T)*ramp;
159 
160  r[1] = H+A*((Length-zeta[0])*zeta[0])/pow(0.5*Length,2)*
161  sin(2.0*Pi*(Time_pt->time(t))/T)*ramp;
162 
163  } // end of "unsteady" version
164 
165 
166  /// "Current" position vector at Lagrangian coordinate zeta
167  void position(const Vector<double>&zeta, Vector<double>& r) const
168  {
169  position (0, zeta, r);
170  }
171 
172  /// Number of geometric Data in GeomObject: None.
173  unsigned ngeom_data() const {return 0;}
174 
175 private:
176 
177  /// Height at ends
178  double H;
179 
180  /// Length
181  double Length;
182 
183  /// x-coordinate of left end
184  double X_left;
185 
186  /// Amplitude of oscillation
187  double A;
188 
189  /// Relative amplitude of horizontal wall motion
190  double B;
191 
192  /// Period of the oscillations
193  double T;
194 
195  /// Pointer to the global time object
196  Time* Time_pt;
197 
198 }; // end of oscillating wall
199 
200 
201 
202 
203 //====start_of_Global_Physical_Variables================
204 /// Namespace for phyical parameters
205 //======================================================
206 namespace Global_Physical_Variables
207 {
208  /// Reynolds number
209  double Re=50.0;
210 
211  /// Womersley = Reynolds times Strouhal
212  double ReSt=50.0;
213 
214  /// Default pressure on the left boundary
215  double P_up=0.0;
216 
217  /// Traction required at the left boundary
218  void prescribed_traction(const double& t,
219  const Vector<double>& x,
220  const Vector<double>& n,
221  Vector<double>& traction)
222  {
223  traction.resize(2);
224  traction[0]=P_up;
225  traction[1]=0.0;
226  }
227 
228 } // end of Global_Physical_Variables
229 
230 
231 
232 //=======start_of_problem_class=======================================
233 /// Problem class
234 //====================================================================
235 template <class ELEMENT>
236 class CollapsibleChannelProblem : public Problem
237 {
238 
239  public :
240 
241  /// Constructor : the arguments are the number of elements,
242  /// the length of the domain and the amplitude and period of
243  /// the oscillations
244  CollapsibleChannelProblem(const unsigned& nup,
245  const unsigned& ncollapsible,
246  const unsigned& ndown,
247  const unsigned& ny,
248  const double& lup,
249  const double& lcollapsible,
250  const double& ldown,
251  const double& ly,
252  const double& amplitude,
253  const double& period);
254 
255  /// Empty destructor
256  ~CollapsibleChannelProblem() {}
257 
258 
259 #ifdef ADAPTIVE
260 
261  /// Access function for the specific mesh
263  {
264 
265  // Upcast from pointer to the Mesh base class to the specific
266  // element type that we're using here.
268  (Bulk_mesh_pt);
269 
270  } // end of access to bulk mesh
271 
272 #else
273 
274  /// Access function for the specific mesh
276  {
277 
278  // Upcast from pointer to the Mesh base class to the specific
279  // element type that we're using here.
280  return dynamic_cast<MyAlgebraicCollapsibleChannelMesh<ELEMENT>*>
281  (Bulk_mesh_pt);
282 
283  } // end of access to bulk mesh
284 
285 #endif
286 
287 
288  /// Update the problem specs before solve (empty)
289  void actions_before_newton_solve(){}
290 
291  /// Update the problem after solve (empty)
292  void actions_after_newton_solve(){}
293 
294  /// Update the velocity boundary condition on the moving wall
295  void actions_before_implicit_timestep();
296 
297  /// Actions before adapt: Wipe the mesh of prescribed traction elements
298  void actions_before_adapt();
299 
300  /// Actions after adapt: Rebuild the mesh of prescribed traction elements
301  void actions_after_adapt();
302 
303  /// Apply initial conditions
304  void set_initial_condition();
305 
306  /// Doc the solution
307  void doc_solution(DocInfo& doc_info, ofstream& trace_file);
308 
309 private :
310 
311  /// Create the prescribed traction elements on boundary b
312  /// of the bulk mesh and stick them into the surface mesh.
313  void create_traction_elements(const unsigned &b,
314  Mesh* const &bulk_mesh_pt,
315  Mesh* const &surface_mesh_pt);
316 
317  /// Delete prescribed traction elements from the surface mesh
318  void delete_traction_elements(Mesh* const &surface_mesh_pt);
319 
320  /// Number of elements in the x direction in the upstream part of the channel
321  unsigned Nup;
322 
323  /// Number of elements in the x direction in the "collapsible"
324  /// part of the channel
325  unsigned Ncollapsible;
326 
327  /// Number of elements in the x direction in the downstream part of the channel
328  unsigned Ndown;
329 
330  /// Number of elements across the channel
331  unsigned Ny;
332 
333  /// x-length in the upstream part of the channel
334  double Lup;
335 
336  /// x-length in the "collapsible" part of the channel
337  double Lcollapsible;
338 
339  /// x-length in the downstream part of the channel
340  double Ldown;
341 
342  /// Transverse length
343  double Ly;
344 
345  /// Pointer to the geometric object that parametrises the "collapsible" wall
346  OscillatingWall* Wall_pt;
347 
348  /// Pointer to the "bulk" mesh
350 
351  /// Pointer to the "surface" mesh that contains the applied traction
352  /// elements
353  Mesh* Surface_mesh_pt;
354 
355  /// Pointer to the left control node
356  Node* Left_node_pt;
357 
358  /// Pointer to right control node
359  Node* Right_node_pt;
360 
361 }; // end of problem class
362 
363 
364 
365 
366 //===start_of_constructor=======================================
367 /// Constructor for the collapsible channel problem
368 //===============================================================
369 template <class ELEMENT>
370 CollapsibleChannelProblem<ELEMENT>::CollapsibleChannelProblem(
371  const unsigned& nup,
372  const unsigned& ncollapsible,
373  const unsigned& ndown,
374  const unsigned& ny,
375  const double& lup,
376  const double& lcollapsible,
377  const double& ldown,
378  const double& ly,
379  const double& amplitude,
380  const double& period)
381 {
382  // Number of elements
383  Nup=nup;
384  Ncollapsible=ncollapsible;
385  Ndown=ndown;
386  Ny=ny;
387 
388  // Lengths of domain
389  Lup=lup;
390  Lcollapsible=lcollapsible;
391  Ldown=ldown;
392  Ly=ly;
393 
394  // Overwrite maximum allowed residual to accomodate possibly
395  // poor initial guess for solution
396  Problem::Max_residuals=10000;
397 
398  // Allocate the timestepper -- this constructs the Problem's
399  // time object with a sufficient amount of storage to store the
400  // previous timsteps.
401  add_time_stepper_pt(new BDF<2>);
402 
403  // Parameters for wall object
404  double height=ly;
405  double length=lcollapsible;
406  double x_left=lup;
407 
408  //Create the geometric object that represents the wall
409  Wall_pt=new OscillatingWall(height, x_left, length, amplitude, period,
410  time_pt());
411 
412 #ifdef ADAPTIVE
413 
414  // Build mesh
416  nup, ncollapsible, ndown, ny,
417  lup, lcollapsible, ldown, ly,
418  Wall_pt,
419 #ifdef USE_BL_SQUASH_FCT
421 #endif
422  time_stepper_pt());
423 
424 #else
425 
426  // Build mesh
428  nup, ncollapsible, ndown, ny,
429  lup, lcollapsible, ldown, ly,
430  Wall_pt,
431 #ifdef USE_BL_SQUASH_FCT
433 #endif
434  time_stepper_pt());
435 
436 #endif
437 
438 
439  // Create "surface mesh" that will contain only the prescribed-traction
440  // elements at the inflow. The default constructor just creates the mesh
441  // without giving it any elements, nodes, etc.
442  Surface_mesh_pt = new Mesh;
443 
444  // Create prescribed-traction elements from all elements that are
445  // adjacent to boundary 5 (inflow boundary), and add them to the surface mesh.
446  create_traction_elements(5,Bulk_mesh_pt,Surface_mesh_pt);
447 
448  // Add the two sub meshes to the problem
449  add_sub_mesh(Bulk_mesh_pt);
450  add_sub_mesh(Surface_mesh_pt);
451 
452  // Combine all submeshes added so far into a single Mesh
453  build_global_mesh();
454 
455 
456 #ifdef ADAPTIVE
457 
458  //Set errror estimator for bulk mesh
459  Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
461  (Bulk_mesh_pt)->spatial_error_estimator_pt()=error_estimator_pt;
462 
463 #endif
464 
465  // Loop over the elements to set up element-specific
466  // things that cannot be handled by constructor
467  unsigned n_element=Bulk_mesh_pt->nelement();
468  for(unsigned e=0;e<n_element;e++)
469  {
470  // Upcast from GeneralisedElement to the present element
471  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
472 
473  //Set the Reynolds number
474  el_pt->re_pt() = &Global_Physical_Variables::Re;
475 
476  // Set the Womersley number
477  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
478 
479  } // end loop over bulk elements
480 
481 
482 
483  // Loop over the traction elements to pass pointer to prescribed
484  // traction function
485  unsigned n_el=Surface_mesh_pt->nelement();
486  for(unsigned e=0;e<n_el;e++)
487  {
488  // Upcast from GeneralisedElement to NavierStokes traction element
489  NavierStokesTractionElement<ELEMENT> *el_pt =
490  dynamic_cast< NavierStokesTractionElement<ELEMENT>*>(
491  Surface_mesh_pt->element_pt(e));
492 
493  // Set the pointer to the prescribed traction function
494  el_pt->traction_fct_pt() = &Global_Physical_Variables::prescribed_traction;
495 
496  } // end loop over applied traction elements
497 
498 
499 
500 
501  //Pin the velocity on the boundaries
502  //x and y-velocities pinned along boundary 0 (bottom boundary) :
503  unsigned ibound=0;
504  unsigned num_nod= bulk_mesh_pt()->nboundary_node(ibound);
505  for (unsigned inod=0;inod<num_nod;inod++)
506  {
507  for(unsigned i=0;i<2;i++)
508  {
509  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(i);
510  }
511  }
512 
513 
514  //x and y-velocities pinned along boundary 2, 3, 4 (top boundaries) :
515  for(unsigned ib=2;ib<5;ib++)
516  {
517  num_nod= bulk_mesh_pt()->nboundary_node(ib);
518  for (unsigned inod=0;inod<num_nod;inod++)
519  {
520  for(unsigned i=0;i<2;i++)
521  {
522  bulk_mesh_pt()->boundary_node_pt(ib, inod)->pin(i);
523  }
524  }
525  }
526 
527  //y-velocity pinned along boundary 1 (right boundary):
528  ibound=1;
529  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
530  for (unsigned inod=0;inod<num_nod;inod++)
531  {
532  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
533  }
534 
535 
536  //y-velocity pinned along boundary 5 (left boundary):
537  ibound=5;
538  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
539  for (unsigned inod=0;inod<num_nod;inod++)
540  {
541  bulk_mesh_pt()->boundary_node_pt(ibound, inod)->pin(1);
542  }// end of pin_velocity
543 
544 
545 
546  //Select control nodes "half way" up the inflow/outflow cross-sections
547  //--------------------------------------------------------------------
548 
549  // Left boundary
550  ibound=5;
551  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
552  unsigned control_nod=num_nod/2;
553  Left_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
554 
555  // Right boundary
556  ibound=1;
557  num_nod= bulk_mesh_pt()->nboundary_node(ibound);
558  control_nod=num_nod/2;
559  Right_node_pt= bulk_mesh_pt()->boundary_node_pt(ibound, control_nod);
560 
561  // Setup equation numbering scheme
562  cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
563 
564 } //end of constructor
565 
566 
567 
568 
569 //====start_of_doc_solution===================================================
570 /// Doc the solution
571 //============================================================================
572 template <class ELEMENT>
573 void CollapsibleChannelProblem<ELEMENT>:: doc_solution(DocInfo& doc_info,
574  ofstream& trace_file)
575 {
576  ofstream some_file;
577  char filename[100];
578 
579  // Number of plot points
580  unsigned npts;
581  npts=5;
582 
583  // Output solution
584  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
585  doc_info.number());
586  some_file.open(filename);
587  bulk_mesh_pt()->output(some_file,npts);
588  some_file.close();
589 
590 
591  // Get the position of the midpoint on the geometric object
592  Vector<double> zeta(1);
593  zeta[0]=0.5*Lcollapsible;
594  Vector<double> wall_point(2);
595  Wall_pt->position(zeta,wall_point);
596 
597  // Write trace file
598  trace_file << time_pt()->time() << " "
599  << wall_point[1] << " "
600  << Left_node_pt->value(0) << " "
601  << Right_node_pt->value(0) << " "
602  << std::endl;
603 
604  // Output wall shape
605  sprintf(filename,"%s/wall%i.dat",doc_info.directory().c_str(),
606  doc_info.number());
607  some_file.open(filename);
608  unsigned nplot=100;
609  for (unsigned i=0;i<nplot;i++)
610  {
611  zeta[0]=double(i)/double(nplot-1)*Lcollapsible;
612  Wall_pt->position(zeta,wall_point);
613  some_file << wall_point[0] << " "
614  << wall_point[1] << std::endl;
615  }
616  some_file.close();
617 
618 } // end_of_doc_solution
619 
620 
621 
622 
623 //==========start_of_create_traction_elements==================================
624 /// Create the traction elements
625 //============================================================================
626 template <class ELEMENT>
627 void CollapsibleChannelProblem<ELEMENT>::create_traction_elements(
628  const unsigned &b, Mesh* const &bulk_mesh_pt, Mesh* const &surface_mesh_pt)
629 {
630  // How many bulk elements are adjacent to boundary b?
631  unsigned n_element = bulk_mesh_pt->nboundary_element(b);
632 
633  // Loop over the bulk elements adjacent to boundary b
634  for(unsigned e=0;e<n_element;e++)
635  {
636  // Get pointer to the bulk element that is adjacent to boundary b
637  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>
638  (bulk_mesh_pt->boundary_element_pt(b,e));
639 
640  //What is the index of the face of element e that lies along boundary b
641  int face_index = bulk_mesh_pt->face_index_at_boundary(b,e);
642 
643  // Build the corresponding prescribed-traction element
644  NavierStokesTractionElement<ELEMENT>* traction_element_pt =
645  new NavierStokesTractionElement<ELEMENT>(bulk_elem_pt,face_index);
646 
647  //Add the prescribed-flux element to the surface mesh
648  surface_mesh_pt->add_element_pt(traction_element_pt);
649 
650  } //end of loop over bulk elements adjacent to boundary b
651 
652 } // end of create_traction_elements
653 
654 
655 //============start_of_delete_traction_elements==============================
656 /// Delete traction elements and wipe the surface mesh
657 //=======================================================================
658 template<class ELEMENT>
659 void CollapsibleChannelProblem<ELEMENT>::
660 delete_traction_elements(Mesh* const &surface_mesh_pt)
661 {
662  // How many surface elements are in the surface mesh
663  unsigned n_element = surface_mesh_pt->nelement();
664 
665  // Loop over the surface elements
666  for(unsigned e=0;e<n_element;e++)
667  {
668  // Kill surface element
669  delete surface_mesh_pt->element_pt(e);
670  }
671 
672  // Wipe the mesh
673  surface_mesh_pt->flush_element_and_node_storage();
674 
675 } // end of delete_traction_elements
676 
677 
678 
679 //=======start_of_apply_initial_conditions===================================
680 /// Apply initial conditions: Impulsive start from steady Poiseuille
681 
682 //============================================================================
683 template <class ELEMENT>
684 void CollapsibleChannelProblem<ELEMENT>::set_initial_condition()
685 {
686 
687  // Check that timestepper is from the BDF family
688  if (time_stepper_pt()->type()!="BDF")
689  {
690  std::ostringstream error_stream;
691  error_stream
692  << "Timestepper has to be from the BDF family!\n"
693  << "You have specified a timestepper from the "
694  << time_stepper_pt()->type() << " family" << std::endl;
695 
696  throw OomphLibError(error_stream.str(),
697  OOMPH_CURRENT_FUNCTION,
698  OOMPH_EXCEPTION_LOCATION);
699  }
700 
701  // Update the mesh
702  bulk_mesh_pt()->node_update();
703 
704  // Loop over the nodes to set initial guess everywhere
705  unsigned num_nod = bulk_mesh_pt()->nnode();
706  for (unsigned n=0;n<num_nod;n++)
707  {
708  // Get nodal coordinates
709  Vector<double> x(2);
710  x[0]=bulk_mesh_pt()->node_pt(n)->x(0);
711  x[1]=bulk_mesh_pt()->node_pt(n)->x(1);
712 
713  // Assign initial condition: Steady Poiseuille flow
714  bulk_mesh_pt()->node_pt(n)->set_value(0,6.0*(x[1]/Ly)*(1.0-(x[1]/Ly)));
715  bulk_mesh_pt()->node_pt(n)->set_value(1,0.0);
716  }
717 
718  // Assign initial values for an impulsive start
719  bulk_mesh_pt()->assign_initial_values_impulsive();
720 
721 
722 } // end of set_initial_condition
723 
724 
725 
726 //=====start_of_actions_before_implicit_timestep==============================
727 /// Execute the actions before timestep: Update the velocity
728 /// boundary condition on the moving wall
729 //============================================================================
730 template <class ELEMENT>
731 void CollapsibleChannelProblem<ELEMENT>::actions_before_implicit_timestep()
732 {
733  // Update the domain shape
734  bulk_mesh_pt()->node_update();
735 
736  // Moving wall: No slip; this implies that the velocity needs
737  // to be updated in response to wall motion
738  unsigned ibound=3;
739  unsigned num_nod=bulk_mesh_pt()->nboundary_node(ibound);
740  for (unsigned inod=0;inod<num_nod;inod++)
741  {
742  // Which node are we dealing with?
743  Node* node_pt=bulk_mesh_pt()->boundary_node_pt(ibound,inod);
744 
745  // Apply no slip
746  FSI_functions::apply_no_slip_on_moving_wall(node_pt);
747  }
748 } //end of actions_before_implicit_timestep
749 
750 
751 
752 
753 //=========start_of_actions_before_adapt==================================
754 /// Actions before adapt: Wipe the mesh of prescribed traction elements
755 //========================================================================
756 template<class ELEMENT>
757 void CollapsibleChannelProblem<ELEMENT>::actions_before_adapt()
758 {
759  // Kill the traction elements and wipe surface mesh
760  delete_traction_elements(Surface_mesh_pt);
761 
762  // Rebuild the global mesh.
763  rebuild_global_mesh();
764 
765 } // end of actions_before_adapt
766 
767 
768 //==========start_of_actions_after_adapt==================================
769 /// Actions after adapt: Rebuild the mesh of prescribed traction elements
770 //========================================================================
771 template<class ELEMENT>
772 void CollapsibleChannelProblem<ELEMENT>::actions_after_adapt()
773 {
774  // Create prescribed-flux elements from all elements that are
775  // adjacent to boundary 5 and add them to surface mesh
776  create_traction_elements(5,Bulk_mesh_pt,Surface_mesh_pt);
777 
778  // Rebuild the global mesh
779  rebuild_global_mesh();
780 
781  // Loop over the traction elements to pass pointer to prescribed
782  // traction function
783  unsigned n_element=Surface_mesh_pt->nelement();
784  for(unsigned e=0;e<n_element;e++)
785  {
786  // Upcast from GeneralisedElement to NavierStokesTractionElement element
787  NavierStokesTractionElement<ELEMENT> *el_pt =
788  dynamic_cast<NavierStokesTractionElement<ELEMENT>*>(
789  Surface_mesh_pt->element_pt(e));
790 
791  // Set the pointer to the prescribed traction function
792  el_pt->traction_fct_pt() = &Global_Physical_Variables::prescribed_traction;
793  }
794 } // end of actions_after_adapt
795 
796 
797 
798 //=======start_of_driver_code==================================================
799 /// Driver code for an unsteady adaptive collapsible channel problem
800 /// with prescribed wall motion. Presence of command line arguments
801 /// indicates validation run with coarse resolution and small number of
802 /// timesteps.
803 //=============================================================================
804 int main(int argc, char* argv[])
805 {
806 
807  // Store command line arguments
808  CommandLineArgs::setup(argc,argv);
809 
810  // Reduction in resolution for validation run?
811  unsigned coarsening_factor=1;
812  if (CommandLineArgs::Argc>1)
813  {
814  coarsening_factor=4;
815  }
816 
817  // Number of elements in the domain
818  unsigned nup=20/coarsening_factor;
819  unsigned ncollapsible=40/coarsening_factor;
820  unsigned ndown=40/coarsening_factor;
821  unsigned ny=16/coarsening_factor;
822 
823  // Length of the domain
824  double lup=5.0;
825  double lcollapsible=10.0;
826  double ldown=10.0;
827  double ly=1.0;
828 
829  // Initial amplitude of the wall deformation
830  double amplitude=1.0e-2; // ADJUST
831 
832  // Period of oscillation
833  double period=0.45;
834 
835  // Pressure/applied traction on the left boundary: This is consistent with
836  // steady Poiseuille flow
837  Global_Physical_Variables::P_up=12.0*(lup+lcollapsible+ldown);
838 
839 
840  //Set output directory
841  DocInfo doc_info;
842  doc_info.set_directory("RESLT");
843 
844  // Open a trace file
845  ofstream trace_file;
846  char filename[100];
847  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
848  trace_file.open(filename);
849 
850 
851 #ifdef ADAPTIVE
852 
853  // Build the problem with Crouzeix Raviart Element
854  CollapsibleChannelProblem<AlgebraicElement<RefineableQCrouzeixRaviartElement<2> > >
855  problem(nup, ncollapsible, ndown, ny,
856  lup, lcollapsible, ldown, ly,
857  amplitude,period);
858 
859 #else
860 
861  // Build the problem with Crouzeix Raviart Element
862  CollapsibleChannelProblem<AlgebraicElement<QCrouzeixRaviartElement<2> > >
863  problem(nup, ncollapsible, ndown, ny,
864  lup, lcollapsible, ldown, ly,
865  amplitude,period);
866 
867 
868 #endif
869 
870 
871 
872  // Number of timesteps per period
873  unsigned nsteps_per_period=40;
874 
875  // Number of periods
876  unsigned nperiod=3;
877 
878  // Number of timesteps (reduced for validation)
879  unsigned nstep=nsteps_per_period*nperiod;
880  if (CommandLineArgs::Argc>1)
881  {
882  nstep=3;
883  }
884 
885  //Timestep:
886  double dt=period/double(nsteps_per_period);
887 
888  // Start time
889  double t_min=0.0;
890 
891  // Initialise timestep and set initial conditions
892  problem.time_pt()->time()=t_min;
893  problem.initialise_dt(dt);
894  problem.set_initial_condition();
895 
896  // Output the initial solution
897  problem.doc_solution(doc_info, trace_file);
898 
899  // Step number
900  doc_info.number()++;
901 
902 #ifdef ADAPTIVE
903 
904  // Set targets for spatial adaptivity
905  problem.bulk_mesh_pt()->max_permitted_error()=1.0e-3;
906  problem.bulk_mesh_pt()->min_permitted_error()=1.0e-5;
907 
908  // Overwrite with reduced targets for validation run to force
909  // some refinement during the first few timesteps
910  if (CommandLineArgs::Argc>1)
911  {
912  problem.bulk_mesh_pt()->max_permitted_error()=1.0e-4;
913  problem.bulk_mesh_pt()->min_permitted_error()=1.0e-6;
914  }
915 
916  // First timestep: We may re-assign the initial condition
917  // following any mesh adaptation.
918  bool first=true;
919 
920  // Max. number of adaptations during first timestep
921  unsigned max_adapt=10;
922 
923 #endif
924 
925  // Timestepping loop
926  for (unsigned istep=0;istep<nstep;istep++)
927  {
928 
929 #ifdef ADAPTIVE
930 
931  // Solve the problem
932  problem.unsteady_newton_solve(dt, max_adapt, first);
933 
934 #else
935 
936 
937  // Solve the problem
938  problem.unsteady_newton_solve(dt);
939 
940 #endif
941 
942  // Outpt the solution
943  problem.doc_solution(doc_info, trace_file);
944 
945  // Step number
946  doc_info.number()++;
947 
948 #ifdef ADAPTIVE
949 
950  // We've done one step: Don't re-assign the initial conditions
951  // and limit the number of adaptive mesh refinements to one
952  // per timestep.
953  first=false;
954  max_adapt=1;
955 
956 #endif
957 
958  }
959 
960  trace_file.close();
961 
962 } //end of driver code
963 
964 
965 
966 // {
967 // unsigned n=100;
968 // for (unsigned i=0;i<n;i++)
969 // {
970 // double s=double(i)/double(n-1);
971 // cout << s << " " << BL_Squash::squash_fct(s) << std::endl;
972 // }
973 // pause("done");
974 // }
Collapsible channel mesh with algebraic node update.
//////////////////////////////////////////////////////////////////////// ////////////////////////////...
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.
///////////////////////////////////////////////////////////////// ///////////////////////////////////...