osc_quarter_ellipse.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 //Driver for 2D Navier-Stokes problem in moving domain
27 
28 //Generic routines
29 #include "generic.h"
30 
31 // The Navier Stokes equations
32 #include "navier_stokes.h"
33 
34 // Mesh
35 #include "meshes/quarter_circle_sector_mesh.h"
36 
37 using namespace std;
38 
39 using namespace oomph;
40 
41 //============start_of_MyEllipse===========================================
42 /// Oscillating ellipse
43 /// \f[ x = (A + \widehat{A} \cos(2\pi t/T)) \cos(\xi) \f]
44 /// \f[ y = \frac{\sin(\xi)}{A + \widehat{A} \cos(2\pi t/T)} \f]
45 /// Note that cross-sectional area is conserved.
46 //=========================================================================
47 class MyEllipse : public GeomObject
48 {
49 
50 public:
51 
52  /// Constructor: Pass initial x-half axis, amplitude of x-variation,
53  /// period of oscillation and pointer to time object.
54  MyEllipse(const double& a, const double& a_hat,
55  const double& period, Time* time_pt) :
56  GeomObject(1,2), A(a), A_hat(a_hat), T(period), Time_pt(time_pt) {}
57 
58  /// Destructor: Empty
59  virtual ~MyEllipse() {}
60 
61  /// Current position vector to material point at
62  /// Lagrangian coordinate xi
63  void position(const Vector<double>& xi, Vector<double>& r) const
64  {
65  // Get current time:
66  double time=Time_pt->time();
67 
68  // Position vector
69  double axis=A+A_hat*cos(2.0*MathematicalConstants::Pi*time/T);
70  r[0] = axis*cos(xi[0]);
71  r[1] = (1.0/axis)*sin(xi[0]);
72  }
73 
74  /// Parametrised position on object: r(xi). Evaluated at
75  /// previous time level. t=0: current time; t>0: previous
76  /// time level.
77  void position(const unsigned& t, const Vector<double>& xi,
78  Vector<double>& r) const
79  {
80  // Get current time:
81  double time=Time_pt->time(t);
82 
83  // Position vector
84  double axis=A+A_hat*cos(2.0*MathematicalConstants::Pi*time/T);
85  r[0] = axis*cos(xi[0]);
86  r[1] = (1.0/axis)*sin(xi[0]);
87  }
88 
89 private:
90 
91  /// x-half axis
92  double A;
93 
94  /// Amplitude of variation in x-half axis
95  double A_hat;
96 
97  /// Period of oscillation
98  double T;
99 
100  /// Pointer to time object
101  Time* Time_pt;
102 
103 }; // end of MyEllipse
104 
105 
106 
107 /// ////////////////////////////////////////////////////////////////////
108 /// ////////////////////////////////////////////////////////////////////
109 /// /////////////////////////////////////////////////////////////////////
110 
111 
112 
113 //===start_of_namespace=================================================
114 /// Namepspace for global parameters
115 //======================================================================
117 {
118 
119  /// Reynolds number
120  double Re=100.0;
121 
122  /// Womersley = Reynolds times Strouhal
123  double ReSt=100.0;
124 
125  /// x-Half axis length
126  double A=1.0;
127 
128  /// x-Half axis amplitude
129  double A_hat=0.1;
130 
131  /// Period of oscillations
132  double T=1.0;
133 
134  /// Exact solution of the problem as a vector containing u,v,p
135  void get_exact_u(const double& t, const Vector<double>& x, Vector<double>& u)
136  {
137  using namespace MathematicalConstants;
138 
139  // Strouhal number
140  double St = ReSt/Re;
141 
142  // Half axis
143  double a=A+A_hat*cos(2.0*Pi*t/T);
144  double adot=-2.0*A_hat*Pi*sin(2.0*Pi*t/T)/T;
145  u.resize(3);
146 
147  // Velocity solution
148  u[0]=adot*x[0]/a;
149  u[1]=-adot*x[1]/a;
150 
151  // Pressure solution
152  u[2]=(2.0*A_hat*Pi*Pi*Re*(x[0]*x[0]*St*cos(2.0*Pi*t/T)*A +
153  x[0]*x[0]*St*A_hat - x[0]*x[0]*A_hat +
154  x[0]*x[0]*A_hat*cos(2.0*Pi*t/T)*cos(2.0*Pi*t/T) -
155  x[1]*x[1]*St*cos(2.0*Pi*t/T)*A -
156  x[1]*x[1]*St*A_hat - x[1]*x[1]*A_hat +
157  x[1]*x[1]*A_hat*cos(2.0*Pi*t/T)*cos(2.0*Pi*t/T) ))
158  /(T*T*(A*A + 2.0*A*A_hat*cos(2.0*Pi*t/T) +
159  A_hat*A_hat*cos(2.0*Pi*t/T)*cos(2.0*Pi*t/T) ));
160  }
161 
162 
163 } // end of namespace
164 
165 
166 
167 
168 /// ////////////////////////////////////////////////////////////////////
169 /// ////////////////////////////////////////////////////////////////////
170 /// /////////////////////////////////////////////////////////////////////
171 
172 
173 
174 //=====start_of_problem_class=========================================
175 /// Navier-Stokes problem in an oscillating ellipse domain.
176 //====================================================================
177 template<class ELEMENT, class TIMESTEPPER>
178 class OscEllipseProblem : public Problem
179 {
180 
181 public:
182 
183  /// Constructor
185 
186  /// Destructor (empty)
188 
189  /// Update the problem specs after solve (empty)
191 
192  /// Update problem specs before solve (empty)
194 
195  /// Actions before adapt (empty)
197 
198  /// Actions after adaptation, pin relevant pressures
200  {
201  // Unpin all pressure dofs
202  RefineableNavierStokesEquations<2>::
203  unpin_all_pressure_dofs(mesh_pt()->element_pt());
204 
205  // Pin redundant pressure dofs
206  RefineableNavierStokesEquations<2>::
207  pin_redundant_nodal_pressures(mesh_pt()->element_pt());
208 
209  // Now set the first pressure dof in the first element to 0.0
210  fix_pressure(0,0,0.0);
211 
212  } // end of actions_after_adapt
213 
214 
215  /// Update the problem specs before next timestep
217  {
218  // Update the domain shape
219  mesh_pt()->node_update();
220 
221  // Ring boundary: No slip; this implies that the velocity needs
222  // to be updated in response to wall motion
223  unsigned ibound=1;
224  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
225  for (unsigned inod=0;inod<num_nod;inod++)
226  {
227  // Which node are we dealing with?
228  Node* node_pt=mesh_pt()->boundary_node_pt(ibound,inod);
229 
230  // Apply no slip
231  FSI_functions::apply_no_slip_on_moving_wall(node_pt);
232  }
233  }
234 
235  /// Update the problem specs after timestep (empty)
237 
238  /// Doc the solution
239  void doc_solution(DocInfo& doc_info);
240 
241  /// Timestepping loop
242  void unsteady_run(DocInfo& doc_info);
243 
244  /// Set initial condition
245  void set_initial_condition();
246 
247 private:
248 
249  /// Fix pressure in element e at pressure dof pdof and set to pvalue
250  void fix_pressure(const unsigned &e, const unsigned &pdof,
251  const double &pvalue)
252  {
253  //Cast to proper element and fix pressure
254  dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e))->
255  fix_pressure(pdof,pvalue);
256  } // end_of_fix_pressure
257 
258  /// Pointer to GeomObject that specifies the domain bondary
259  GeomObject* Wall_pt;
260 
261 }; // end of problem_class
262 
263 
264 
265 //========start_of_constructor============================================
266 /// Constructor for Navier-Stokes problem on an oscillating ellipse domain.
267 //========================================================================
268 template<class ELEMENT, class TIMESTEPPER>
270 {
271 
272  //Create the timestepper and add it to the problem
273  add_time_stepper_pt(new TIMESTEPPER);
274 
275  // Setup mesh
276  //-----------
277 
278  // Build geometric object that forms the curvilinear domain boundary:
279  // an oscillating ellipse
280 
281  // Half axes
283 
284  // Variations of half axes
286 
287  // Period of the oscillation
288  double period=Global_Physical_Variables::T;
289 
290  // Create GeomObject that specifies the domain bondary
291  Wall_pt=new MyEllipse(a,a_hat,period,Problem::time_pt());
292 
293 
294  // Start and end coordinates of curvilinear domain boundary on ellipse
295  double xi_lo=0.0;
296  double xi_hi=MathematicalConstants::Pi/2.0;
297 
298  // Now create the mesh. Separating line between the two
299  // elements next to the curvilinear boundary is located half-way
300  // along the boundary.
301  double fract_mid=0.5;
302  Problem::mesh_pt() = new RefineableQuarterCircleSectorMesh<ELEMENT>(
303  Wall_pt,xi_lo,fract_mid,xi_hi,time_stepper_pt());
304 
305  // Set error estimator NOT NEEDED IN CURRENT PROBLEM SINCE
306  // WE'RE ONLY REFINING THE MESH UNIFORMLY
307  //Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
308  //mesh_pt()->spatial_error_estimator_pt()=error_estimator_pt;
309 
310 
311  // Fluid boundary conditions
312  //--------------------------
313  // Ring boundary: No slip; this also implies that the velocity needs
314  // to be updated in response to wall motion
315  unsigned ibound=1;
316  {
317  unsigned num_nod= mesh_pt()->nboundary_node(ibound);
318  for (unsigned inod=0;inod<num_nod;inod++)
319  {
320 
321  // Pin both velocities
322  for (unsigned i=0;i<2;i++)
323  {
324  mesh_pt()->boundary_node_pt(ibound,inod)->pin(i);
325  }
326  }
327  } // end boundary 1
328 
329  // Bottom boundary:
330  ibound=0;
331  {
332  unsigned num_nod= mesh_pt()->nboundary_node(ibound);
333  for (unsigned inod=0;inod<num_nod;inod++)
334  {
335  // Pin vertical velocity
336  {
337  mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
338  }
339  }
340  } // end boundary 0
341 
342  // Left boundary:
343  ibound=2;
344  {
345  unsigned num_nod= mesh_pt()->nboundary_node(ibound);
346  for (unsigned inod=0;inod<num_nod;inod++)
347  {
348  // Pin horizontal velocity
349  {
350  mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
351  }
352  }
353  } // end boundary 2
354 
355 
356  // Complete the build of all elements so they are fully functional
357  //----------------------------------------------------------------
358 
359  // Find number of elements in mesh
360  unsigned n_element = mesh_pt()->nelement();
361 
362  // Loop over the elements to set up element-specific
363  // things that cannot be handled by constructor
364  for(unsigned i=0;i<n_element;i++)
365  {
366  // Upcast from FiniteElement to the present element
367  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
368 
369  //Set the Reynolds number, etc
370  el_pt->re_pt() = &Global_Physical_Variables::Re;
371  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
372  }
373 
374  // Pin redundant pressure dofs
375  RefineableNavierStokesEquations<2>::
376  pin_redundant_nodal_pressures(mesh_pt()->element_pt());
377 
378  // Now set the first pressure dof in the first element to 0.0
379  fix_pressure(0,0,0.0);
380 
381  // Do equation numbering
382  cout << "Number of equations: " << assign_eqn_numbers() << std::endl;
383 
384 } // end of constructor
385 
386 
387 //======================start_of_set_initial_condition====================
388 /// Set initial condition: Assign previous and current values
389 /// from exact solution.
390 //========================================================================
391 template<class ELEMENT,class TIMESTEPPER>
393 {
394  // Backup time in global timestepper
395  double backed_up_time=time_pt()->time();
396 
397  // Past history for velocities must be established for t=time0-deltat, ...
398  // Then provide current values (at t=time0) which will also form
399  // the initial guess for first solve at t=time0+deltat
400 
401  // Vector of exact solution value
402  Vector<double> soln(3);
403  Vector<double> x(2);
404 
405  //Find number of nodes in mesh
406  unsigned num_nod = mesh_pt()->nnode();
407 
408  // Get continuous times at previous timesteps
409  int nprev_steps=time_stepper_pt()->nprev_values();
410  Vector<double> prev_time(nprev_steps+1);
411  for (int itime=nprev_steps;itime>=0;itime--)
412  {
413  prev_time[itime]=time_pt()->time(unsigned(itime));
414  }
415 
416  // Loop over current & previous timesteps (in outer loop because
417  // the mesh also moves!)
418  for (int itime=nprev_steps;itime>=0;itime--)
419  {
420  double time=prev_time[itime];
421 
422  // Set global time (because this is how the geometric object refers
423  // to continous time )
424  time_pt()->time()=time;
425 
426  cout << "setting IC at time =" << time << std::endl;
427 
428  // Update the mesh for this value of the continuous time
429  // (The wall object reads the continous time from the
430  // global time object)
431  mesh_pt()->node_update();
432 
433  // Loop over the nodes to set initial guess everywhere
434  for (unsigned jnod=0;jnod<num_nod;jnod++)
435  {
436  // Get nodal coordinates
437  x[0]=mesh_pt()->node_pt(jnod)->x(0);
438  x[1]=mesh_pt()->node_pt(jnod)->x(1);
439 
440  // Get exact solution (unsteady stagnation point flow)
442 
443  // Assign solution
444  mesh_pt()->node_pt(jnod)->set_value(itime,0,soln[0]);
445  mesh_pt()->node_pt(jnod)->set_value(itime,1,soln[1]);
446 
447  // Loop over coordinate directions
448  for (unsigned i=0;i<2;i++)
449  {
450  mesh_pt()->node_pt(jnod)->x(itime,i)=x[i];
451  }
452  }
453  } // end of loop over previous timesteps
454 
455  // Reset backed up time for global timestepper
456  time_pt()->time()=backed_up_time;
457 
458 } // end of set_initial_condition
459 
460 
461 
462 //=======start_of_doc_solution============================================
463 /// Doc the solution
464 //========================================================================
465 template<class ELEMENT, class TIMESTEPPER>
467 {
468  ofstream some_file;
469  char filename[100];
470 
471  // Number of plot points
472  unsigned npts;
473  npts=5;
474 
475  // Output solution
476  //-----------------
477  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
478  doc_info.number());
479  some_file.open(filename);
480  mesh_pt()->output(some_file,npts);
481  some_file << "TEXT X=2.5,Y=93.6,F=HELV,HU=POINT,C=BLUE,H=26,T=\"time = "
482  << time_pt()->time() << "\"";
483  some_file << "GEOMETRY X=2.5,Y=98,T=LINE,C=BLUE,LT=0.4" << std::endl;
484  some_file << "1" << std::endl;
485  some_file << "2" << std::endl;
486  some_file << " 0 0" << std::endl;
487  some_file << time_pt()->time()*20.0 << " 0" << std::endl;
488 
489  // Write dummy zones that force tecplot to keep the axis limits constant
490  // while the domain is moving.
491  some_file << "ZONE I=2,J=2" << std::endl;
492  some_file << "0.0 0.0 -0.65 -0.65 -200.0" << std::endl;
493  some_file << "1.15 0.0 -0.65 -0.65 -200.0" << std::endl;
494  some_file << "0.0 1.15 -0.65 -0.65 -200.0" << std::endl;
495  some_file << "1.15 1.15 -0.65 -0.65 -200.0" << std::endl;
496  some_file << "ZONE I=2,J=2" << std::endl;
497  some_file << "0.0 0.0 0.65 0.65 300.0" << std::endl;
498  some_file << "1.15 0.0 0.65 0.65 300.0" << std::endl;
499  some_file << "0.0 1.15 0.65 0.65 300.0" << std::endl;
500  some_file << "1.15 1.15 0.65 0.65 300.0" << std::endl;
501 
502  some_file.close();
503 
504  // Output exact solution
505  //----------------------
506  sprintf(filename,"%s/exact_soln%i.dat",doc_info.directory().c_str(),
507  doc_info.number());
508  some_file.open(filename);
509  mesh_pt()->output_fct(some_file,npts,time_pt()->time(),
511  some_file.close();
512 
513  // Doc error
514  //----------
515  double error,norm;
516  sprintf(filename,"%s/error%i.dat",doc_info.directory().c_str(),
517  doc_info.number());
518  some_file.open(filename);
519  mesh_pt()->compute_error(some_file,
521  time_pt()->time(),
522  error,norm);
523  some_file.close();
524 
525 
526  // Doc solution and error
527  //-----------------------
528  cout << "error: " << error << std::endl;
529  cout << "norm : " << norm << std::endl << std::endl;
530 
531 
532  // Plot wall posn
533  //---------------
534  sprintf(filename,"%s/Wall%i.dat",doc_info.directory().c_str(),
535  doc_info.number());
536  some_file.open(filename);
537 
538  unsigned nplot=100;
539  for (unsigned iplot=0;iplot<nplot;iplot++)
540  {
541  Vector<double> xi_wall(1), r_wall(2);
542  xi_wall[0]=0.5*MathematicalConstants::Pi*double(iplot)/double(nplot-1);
543  Wall_pt->position(xi_wall,r_wall);
544  some_file << r_wall[0] << " " << r_wall[1] << std::endl;
545  }
546  some_file.close();
547 
548  // Increment number of doc
549  doc_info.number()++;
550 
551 } // end of doc_solution
552 
553 
554 //=======start_of_unsteady_run============================================
555 /// Unsteady run
556 //========================================================================
557 template<class ELEMENT, class TIMESTEPPER>
559 {
560 
561  // Specify duration of the simulation
562  double t_max=3.0;
563 
564  // Initial timestep
565  double dt=0.025;
566 
567  // Initialise timestep
568  initialise_dt(dt);
569 
570  // Set initial conditions.
571  set_initial_condition();
572 
573  // Alternative initial conditions: impulsive start; see exercise.
574  //assign_initial_values_impulsive();
575 
576  // find number of steps
577  unsigned nstep = unsigned(t_max/dt);
578 
579  // If validation: Reduce number of timesteps performed and
580  // use coarse-ish mesh
581  if (CommandLineArgs::Argc>1)
582  {
583  nstep=2;
584  refine_uniformly();
585  cout << "validation run" << std::endl;
586  }
587  else
588  {
589  // Refine the mesh three times, to resolve the pressure distribution
590  // (the velocities could be represented accurately on a much coarser mesh).
591  refine_uniformly();
592  refine_uniformly();
593  refine_uniformly();
594  }
595 
596  // Output solution initial
597  doc_solution(doc_info);
598 
599  // Timestepping loop
600  for (unsigned istep=0;istep<nstep;istep++)
601  {
602  cout << "TIMESTEP " << istep << std::endl;
603  cout << "Time is now " << time_pt()->time() << std::endl;
604 
605  // Take timestep
606  unsteady_newton_solve(dt);
607 
608  //Output solution
609  doc_solution(doc_info);
610  }
611 
612 } // end of unsteady_run
613 
614 
615 /// /////////////////////////////////////////////////////////////////////
616 /// /////////////////////////////////////////////////////////////////////
617 
618 
619 //======start_of_main=================================================
620 /// Driver code for unsteady Navier-Stokes flow, driven by
621 /// oscillating ellipse. If the code is executed with command line
622 /// arguments, a validation run is performed.
623 //====================================================================
624 int main(int argc, char* argv[])
625 {
626 
627  // Store command line arguments
628  CommandLineArgs::setup(argc,argv);
629 
630 
631  // Solve with Crouzeix-Raviart elements
632  {
633  // Create DocInfo object with suitable directory name for output
634  DocInfo doc_info;
635  doc_info.set_directory("RESLT_CR");
636 
637  //Set up problem
639 
640  // Run the unsteady simulation
641  problem.unsteady_run(doc_info);
642  }
643 
644  // Solve with Taylor-Hood elements
645  {
646  // Create DocInfo object with suitable directory name for output
647  DocInfo doc_info;
648  doc_info.set_directory("RESLT_TH");
649 
650  //Set up problem
652 
653  // Run the unsteady simulation
654  problem.unsteady_run(doc_info);
655  }
656 
657 
658 }; // end of main
659 
660 
661 
Oscillating ellipse.
double A_hat
Amplitude of variation in x-half axis.
MyEllipse(const double &a, const double &a_hat, const double &period, Time *time_pt)
Constructor: Pass initial x-half axis, amplitude of x-variation, period of oscillation and pointer to...
void position(const Vector< double > &xi, Vector< double > &r) const
Current position vector to material point at Lagrangian coordinate xi.
void position(const unsigned &t, const Vector< double > &xi, Vector< double > &r) const
Parametrised position on object: r(xi). Evaluated at previous time level. t=0: current time; t>0: pre...
double A
x-half axis
double T
Period of oscillation.
Time * Time_pt
Pointer to time object.
virtual ~MyEllipse()
Destructor: Empty.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
void actions_before_newton_solve()
Update problem specs before solve (empty)
void actions_after_newton_solve()
Update the problem specs after solve (empty)
~OscEllipseProblem()
Destructor (empty)
void actions_after_adapt()
Actions after adaptation, pin relevant pressures.
void actions_before_implicit_timestep()
Update the problem specs before next timestep.
GeomObject * Wall_pt
Pointer to GeomObject that specifies the domain bondary.
void actions_after_implicit_timestep()
Update the problem specs after timestep (empty)
void fix_pressure(const unsigned &e, const unsigned &pdof, const double &pvalue)
Fix pressure in element e at pressure dof pdof and set to pvalue.
void actions_before_adapt()
Actions before adapt (empty)
OscEllipseProblem()
Constructor.
void unsteady_run(DocInfo &doc_info)
Timestepping loop.
void set_initial_condition()
Set initial condition.
void doc_solution(DocInfo &doc_info)
Doc the solution.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
double ReSt
Womersley = Reynolds times Strouhal.
double A_hat
x-Half axis amplitude
double T
Period of oscillations.
double A
x-Half axis length
double Re
Reynolds number.
void get_exact_u(const double &t, const Vector< double > &x, Vector< double > &u)
Exact solution of the problem as a vector containing u,v,p.
int main(int argc, char *argv[])
/////////////////////////////////////////////////////////////////////