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