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