rayleigh_channel.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Copyright (C) 2006-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 Rayleigh-type problem: 2D channel whose upper
27 // wall oscillates periodically.
28 
29 // The oomphlib headers
30 #include "generic.h"
31 #include "navier_stokes.h"
32 
33 // The mesh
34 #include "meshes/rectangular_quadmesh.h"
35 
36 using namespace std;
37 
38 using namespace oomph;
39 
40 //===start_of_namespace=================================================
41 /// Namespace for global parameters
42 //======================================================================
44 {
45  /// Reynolds number
46  double Re;
47 
48  /// Womersley = Reynolds times Strouhal
49  double ReSt;
50 
51  /// Flag for long/short run: Default = perform long run
52  unsigned Long_run_flag=1;
53 
54  /// Flag for impulsive start: Default = start from exact
55  /// time-periodic solution.
57 
58 } // end of namespace
59 
60 
61 //==start_of_exact_solution=============================================
62 /// Namespace for exact solution
63 //======================================================================
64 namespace ExactSoln
65 {
66 
67  /// Exact solution of the problem as a vector
68  void get_exact_u(const double& t, const Vector<double>& x, Vector<double>& u)
69  {
70  double y=x[1];
71  // I=sqrt(-1)
72  complex<double> I(0.0,1.0);
73  // omega
74  double omega=2.0*MathematicalConstants::Pi;
75  // lambda
76  complex<double> lambda(0.0,omega*Global_Parameters::ReSt);
77  lambda = I*sqrt(lambda);
78 
79  // Solution
80  complex<double> sol(
81  exp(complex<double>(0.0,omega*t)) *
82  (exp(lambda*complex<double>(0.0,y))-exp(lambda*complex<double>(0.0,-y)))
83  /(exp(I*lambda)-exp(-I*lambda)) );
84 
85  // Assign real solution
86  u.resize(2);
87  u[0]=real(sol);
88  u[1]=0.0;
89  }
90 
91  /// Exact solution of the problem as a scalar
92  void get_exact_u(const double& t, const double& y,double& u)
93  {
94  // I=sqrt(-1)
95  complex<double> I(0.0,1.0);
96  // omega
97  double omega=2.0*MathematicalConstants::Pi;
98  // lambda
99  complex<double> lambda(0.0,omega*Global_Parameters::ReSt);
100  lambda = I*sqrt(lambda);
101  // Solution
102  complex<double> sol(
103  exp(complex<double>(0.0,omega*t)) *
104  (exp(lambda*complex<double>(0.0,y))-exp(lambda*complex<double>(0.0,-y)))
105  /(exp(I*lambda)-exp(-I*lambda)) );
106 
107  // Assign real solution
108  u=real(sol);
109 
110  }
111 
112 } // end of exact_solution
113 
114 
115 //===start_of_problem_class=============================================
116 /// Rayleigh-type problem: 2D channel whose upper
117 /// wall oscillates periodically.
118 //======================================================================
119 template<class ELEMENT, class TIMESTEPPER>
120 class RayleighProblem : public Problem
121 {
122 public:
123 
124  /// Constructor: Pass number of elements in x and y directions and
125  /// lengths
126  RayleighProblem(const unsigned &nx, const unsigned &ny,
127  const double &lx, const double &ly);
128 
129  //Update before solve is empty
131 
132  /// Update after solve is empty
134 
135  //Actions before timestep: Update no slip on upper oscillating wall
137  {
138  // No slip on upper boundary
139  unsigned ibound=2;
140  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
141  for (unsigned inod=0;inod<num_nod;inod++)
142  {
143  // Get exact solution
144  double y=mesh_pt()->boundary_node_pt(ibound,inod)->x(1);
145  double time=time_pt()->time();
146  double soln;
147  ExactSoln::get_exact_u(time,y,soln);
148 
149  // Assign exact solution to boundary
150  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(0,soln);
151  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1,0.0);
152  }
153 
154  } // end of actions_before_implicit_timestep
155 
156  /// Run an unsteady simulation
157  void unsteady_run(DocInfo& doc_info);
158 
159  /// Doc the solution
160  void doc_solution(DocInfo& doc_info);
161 
162  /// Set initial condition (incl previous timesteps) according
163  /// to specified function.
164  void set_initial_condition();
165 
166 private:
167 
168  /// Fix pressure in element e at pressure dof pdof and set to pvalue
169  void fix_pressure(const unsigned &e, const unsigned &pdof,
170  const double &pvalue)
171  {
172  //Cast to proper element and fix pressure
173  dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e))->
174  fix_pressure(pdof,pvalue);
175  }
176 
177  /// Trace file
178  ofstream Trace_file;
179 
180 }; // end of problem_class
181 
182 
183 //===start_of_constructor=============================================
184 /// Problem constructor
185 //====================================================================
186 template<class ELEMENT,class TIMESTEPPER>
188 (const unsigned &nx, const unsigned &ny,
189  const double &lx, const double& ly)
190 {
191  //Allocate the timestepper
192  add_time_stepper_pt(new TIMESTEPPER);
193 
194  //Now create the mesh with periodic boundary conditions in x direction
195  bool periodic_in_x=true;
196  Problem::mesh_pt() =
197  new RectangularQuadMesh<ELEMENT>(nx,ny,lx,ly,periodic_in_x,
198  time_stepper_pt());
199 
200  // Set the boundary conditions for this problem: All nodes are
201  // free by default -- just pin the ones that have Dirichlet conditions
202  // here
203  unsigned num_bound=mesh_pt()->nboundary();
204  for(unsigned ibound=0;ibound<num_bound;ibound++)
205  {
206  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
207  for (unsigned inod=0;inod<num_nod;inod++)
208  {
209  // No slip on top and bottom
210  if ((ibound==0)||(ibound==2))
211  {
212  mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
213  mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
214  }
215  // Horizontal outflow on the left (and right -- right bc not
216  // strictly necessary because of symmetry)
217  else if ((ibound==1)||(ibound==3))
218  {
219  mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
220  }
221  }
222  } // end loop over boundaries
223 
224  //Complete the problem setup to make the elements fully functional
225 
226  //Loop over the elements
227  unsigned n_el = mesh_pt()->nelement();
228  for(unsigned e=0;e<n_el;e++)
229  {
230  //Cast to a fluid element
231  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
232 
233  //Set the Reynolds number, etc
234  el_pt->re_pt() = &Global_Parameters::Re;
235  el_pt->re_st_pt() = &Global_Parameters::ReSt;
236  }
237 
238  // Now pin the pressure in first element at value 0 to 0.0
239  fix_pressure(0,0,0.0);
240 
241  //Assgn equation numbers
242  cout << assign_eqn_numbers() << std::endl;
243 } // end of constructor
244 
245 
246 
247 
248 //======================start_of_set_initial_condition====================
249 /// Set initial condition: Assign previous and current values
250 /// from exact solution.
251 //========================================================================
252 template<class ELEMENT,class TIMESTEPPER>
254 {
255 
256  // Check that timestepper is from the BDF family
257  if (time_stepper_pt()->type()!="BDF")
258  {
259  std::ostringstream error_stream;
260  error_stream << "Timestepper has to be from the BDF family!\n"
261  << "You have specified a timestepper from the "
262  << time_stepper_pt()->type() << " family" << std::endl;
263 
264  throw OomphLibError(error_stream.str(),
265  OOMPH_CURRENT_FUNCTION,
266  OOMPH_EXCEPTION_LOCATION);
267  }
268 
269  // Backup time in global Time object
270  double backed_up_time=time_pt()->time();
271 
272  // Past history needs to be established for t=time0-deltat, ...
273  // Then provide current values (at t=time0) which will also form
274  // the initial guess for the first solve at t=time0+deltat
275 
276  // Vector of exact solution value
277  Vector<double> soln(2);
278  Vector<double> x(2);
279 
280  //Find number of nodes in mesh
281  unsigned num_nod = mesh_pt()->nnode();
282 
283  // Set continuous times at previous timesteps:
284  // How many previous timesteps does the timestepper use?
285  int nprev_steps=time_stepper_pt()->nprev_values();
286  Vector<double> prev_time(nprev_steps+1);
287  for (int t=nprev_steps;t>=0;t--)
288  {
289  prev_time[t]=time_pt()->time(unsigned(t));
290  }
291 
292  // Loop over current & previous timesteps
293  for (int t=nprev_steps;t>=0;t--)
294  {
295  // Continuous time
296  double time=prev_time[t];
297  cout << "setting IC at time =" << time << std::endl;
298 
299  // Loop over the nodes to set initial guess everywhere
300  for (unsigned n=0;n<num_nod;n++)
301  {
302  // Get nodal coordinates
303  x[0]=mesh_pt()->node_pt(n)->x(0);
304  x[1]=mesh_pt()->node_pt(n)->x(1);
305 
306  // Get exact solution at previous time
307  ExactSoln::get_exact_u(time,x,soln);
308 
309  // Assign solution
310  mesh_pt()->node_pt(n)->set_value(t,0,soln[0]);
311  mesh_pt()->node_pt(n)->set_value(t,1,soln[1]);
312 
313  // Loop over coordinate directions: Mesh doesn't move, so
314  // previous position = present position
315  for (unsigned i=0;i<2;i++)
316  {
317  mesh_pt()->node_pt(n)->x(t,i)=x[i];
318  }
319  }
320  }
321 
322  // Reset backed up time for global timestepper
323  time_pt()->time()=backed_up_time;
324 
325 } // end of set_initial_condition
326 
327 
328 //==start_of_doc_solution=================================================
329 /// Doc the solution
330 //========================================================================
331 template<class ELEMENT,class TIMESTEPPER>
333 {
334  ofstream some_file;
335  char filename[100];
336 
337  // Number of plot points
338  unsigned npts=5;
339 
340  // Output solution
341  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
342  doc_info.number());
343  some_file.open(filename);
344  mesh_pt()->output(some_file,npts);
345 
346  // Write file as a tecplot text object
347  some_file << "TEXT X=2.5,Y=93.6,F=HELV,HU=POINT,C=BLUE,H=26,T=\"time = "
348  << time_pt()->time() << "\"";
349  // ...and draw a horizontal line whose length is proportional
350  // to the elapsed time
351  some_file << "GEOMETRY X=2.5,Y=98,T=LINE,C=BLUE,LT=0.4" << std::endl;
352  some_file << "1" << std::endl;
353  some_file << "2" << std::endl;
354  some_file << " 0 0" << std::endl;
355  some_file << time_pt()->time()*20.0 << " 0" << std::endl;
356 
357  some_file.close();
358 
359  // Output exact solution
360  //----------------------
361  sprintf(filename,"%s/exact_soln%i.dat",doc_info.directory().c_str(),
362  doc_info.number());
363  some_file.open(filename);
364  mesh_pt()->output_fct(some_file,npts,time_pt()->time(),
366  some_file.close();
367 
368  // Doc error
369  //----------
370  double error,norm;
371  sprintf(filename,"%s/error%i.dat",doc_info.directory().c_str(),
372  doc_info.number());
373  some_file.open(filename);
374  mesh_pt()->compute_error(some_file,
376  time_pt()->time(),
377  error,norm);
378  some_file.close();
379 
380  // Doc solution and error
381  //-----------------------
382  cout << "error: " << error << std::endl;
383  cout << "norm : " << norm << std::endl << std::endl;
384 
385  // Get time, position and exact soln at control node
386  unsigned n_control=37;
387  Vector<double> x(2), u(2);
388  double time=time_pt()->time();
389  Node* node_pt=
390  dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(n_control))->node_pt(1);
391  x[0] = node_pt->x(0);
392  x[1] = node_pt->x(1);
393  ExactSoln::get_exact_u(time,x,u);
394 
395  // Write trace file
396  Trace_file << time << " "
397  << x[0] << " "
398  << x[1] << " "
399  << node_pt->value(0) << " "
400  << node_pt->value(1) << " "
401  << u[0] << " "
402  << u[1] << " "
403  << abs(u[0]-node_pt->value(0)) << " "
404  << abs(u[1]-node_pt->value(1)) << " "
405  << error << " "
406  << norm << " "
407  << std::endl;
408 
409 
410 } // end_of_doc_solution
411 
412 
413 //===start_of_unsteady_run=====================================================
414 /// Unsteady run...
415 //=============================================================================
416 template<class ELEMENT,class TIMESTEPPER>
418 {
419 
420  // Open trace file
421  char filename[100];
422  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
423  Trace_file.open(filename);
424 
425  // Write tecplot header for trace file
426  Trace_file << "time" << ", "
427  << "x" << ", "
428  << "y" << ", "
429  << "u_1" << ", "
430  << "u_2" << ", "
431  << "u_exact_1" << ", "
432  << "u_exact_2" << ", "
433  << "error_1" << ", "
434  << "error_2" << ", "
435  << "L2 error" << ", "
436  << "L2 norm" << ", " << std::endl;
437 
438  //Set value of dt
439  double dt = 0.025;
440 
442  {
443  // Initialise all history values for an impulsive start
444  assign_initial_values_impulsive(dt);
445  cout << "IC = impulsive start" << std::endl;
446  }
447  else
448  {
449  // Initialise timestep
450  initialise_dt(dt);
451  // Set initial conditions.
452  set_initial_condition();
453  cout << "IC = exact solution" << std::endl;
454  }
455 
456  //Now do many timesteps
457  unsigned ntsteps=80;
458 
459  // If validation run only do 5 timesteps
461  {
462  ntsteps=5;
463  cout << "validation run" << std::endl;
464  }
465 
466  // Doc initial condition
467  doc_solution(doc_info);
468 
469  // increment counter
470  doc_info.number()++;
471 
472  //Loop over the timesteps
473  for(unsigned t=1;t<=ntsteps;t++)
474  {
475  cout << "TIMESTEP " << t << std::endl;
476 
477  //Take one fixed timestep
478  unsteady_newton_solve(dt);
479 
480  //Output the time
481  cout << "Time is now " << time_pt()->time() << std::endl;
482 
483  // Doc solution
484  doc_solution(doc_info);
485 
486  // increment counter
487  doc_info.number()++;
488  }
489 
490 } // end of unsteady run
491 
492 
493 //===start_of_main======================================================
494 /// Driver code for Rayleigh channel problem
495 //======================================================================
496 int main(int argc, char* argv[])
497 {
498 
499  /// Convert command line arguments (if any) into flags:
500  if (argc==1)
501  {
502  cout << "No command line arguments specified -- using defaults."
503  << std::endl;
504  }
505  else if (argc==3)
506  {
507  cout << "Two command line arguments specified:" << std::endl;
508  // Flag for long run
509  Global_Parameters::Long_run_flag=atoi(argv[1]);
510  /// Flag for impulsive start
512  }
513  else
514  {
515  std::string error_message =
516  "Wrong number of command line arguments. Specify none or two.\n";
517  error_message +=
518  "Arg1: Long_run_flag [0/1]\n";
519  error_message +=
520  "Arg2: Impulsive_start_flag [0/1]\n";
521 
522  throw OomphLibError(error_message,
523  OOMPH_CURRENT_FUNCTION,
524  OOMPH_EXCEPTION_LOCATION);
525  }
526  cout << "Long run flag: "
527  << Global_Parameters::Long_run_flag << std::endl;
528  cout << "Impulsive start flag: "
530 
531 
532  // Set physical parameters:
533 
534  // Womersley number = Reynolds number (St = 1)
537 
538  //Horizontal length of domain
539  double lx = 1.0;
540 
541  //Vertical length of domain
542  double ly = 1.0;
543 
544  // Number of elements in x-direction
545  unsigned nx=5;
546 
547  // Number of elements in y-direction
548  unsigned ny=10;
549 
550  // Solve with Crouzeix-Raviart elements
551  {
552  // Set up doc info
553  DocInfo doc_info;
554  doc_info.number()=0;
555  doc_info.set_directory("RESLT_CR");
556 
557  //Set up problem
558  RayleighProblem<QCrouzeixRaviartElement<2>,BDF<2> > problem(nx,ny,lx,ly);
559 
560  // Run the unsteady simulation
561  problem.unsteady_run(doc_info);
562  }
563 
564 
565 
566  // Solve with Taylor-Hood elements
567  {
568  // Set up doc info
569  DocInfo doc_info;
570  doc_info.number()=0;
571  doc_info.set_directory("RESLT_TH");
572 
573  //Set up problem
574  RayleighProblem<QTaylorHoodElement<2>,BDF<2> > problem(nx,ny,lx,ly);
575 
576  // Run the unsteady simulation
577  problem.unsteady_run(doc_info);
578  }
579 
580 } // end of main
Rayleigh-type problem: 2D channel whose upper wall oscillates periodically.
ofstream Trace_file
Trace file.
RayleighProblem(const unsigned &nx, const unsigned &ny, const double &lx, const double &ly)
Constructor: Pass number of elements in x and y directions and lengths.
void set_initial_condition()
Set initial condition (incl previous timesteps) according to specified function.
void unsteady_run(DocInfo &doc_info)
Run an unsteady simulation.
void actions_after_newton_solve()
Update after solve is empty.
void actions_before_implicit_timestep()
void actions_before_newton_solve()
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 doc_solution(DocInfo &doc_info)
Doc the solution.
Namespace for exact solution.
void get_exact_u(const double &t, const Vector< double > &x, Vector< double > &u)
Exact solution of the problem as a vector.
void get_exact_u(const double &t, const double &y, double &u)
Exact solution of the problem as a scalar.
Namespace for global parameters.
unsigned Long_run_flag
Flag for long/short run: Default = perform long run.
double ReSt
Womersley = Reynolds times Strouhal.
double Re
Reynolds number.
unsigned Impulsive_start_flag
Flag for impulsive start: Default = start from exact time-periodic solution.
int main(int argc, char *argv[])
Driver code for Rayleigh channel problem.