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-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 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
35using namespace std;
36
37using 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//======================================================================
63namespace 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//======================================================================
124template<class ELEMENT, class TIMESTEPPER>
125class RayleighTractionProblem : public Problem
126{
127public:
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.
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
160private:
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//====================================================================
178template<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//========================================================================
271template<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//========================================================================
338template<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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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//=======================================================================
423template<class ELEMENT,class TIMESTEPPER>
425create_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//=============================================================================
457template<class ELEMENT,class TIMESTEPPER>
459{
460
461 // Open trace file
462 char filename[100];
463 snprintf(filename, sizeof(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//======================================================================
537int 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
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.