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-2025 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
36using namespace std;
37
38using 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//======================================================================
64namespace 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//======================================================================
119template<class ELEMENT, class TIMESTEPPER>
120class RayleighProblem : public Problem
121{
122public:
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.
165
166private:
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//====================================================================
186template<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//========================================================================
252template<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//========================================================================
331template<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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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//=============================================================================
416template<class ELEMENT,class TIMESTEPPER>
418{
419
420 // Open trace file
421 char filename[100];
422 snprintf(filename, sizeof(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//======================================================================
496int 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
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.
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.