osc_quarter_ellipse.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 2D Navier-Stokes problem in moving domain
27
28//Generic routines
29#include "generic.h"
30
31// The Navier Stokes equations
32#include "navier_stokes.h"
33
34// Mesh
35#include "meshes/quarter_circle_sector_mesh.h"
36
37using namespace std;
38
39using namespace oomph;
40
41//============start_of_MyEllipse===========================================
42/// Oscillating ellipse
43/// \f[ x = (A + \widehat{A} \cos(2\pi t/T)) \cos(\xi) \f]
44/// \f[ y = \frac{\sin(\xi)}{A + \widehat{A} \cos(2\pi t/T)} \f]
45/// Note that cross-sectional area is conserved.
46//=========================================================================
47class MyEllipse : public GeomObject
48{
49
50public:
51
52 /// Constructor: Pass initial x-half axis, amplitude of x-variation,
53 /// period of oscillation and pointer to time object.
54 MyEllipse(const double& a, const double& a_hat,
55 const double& period, Time* time_pt) :
56 GeomObject(1,2), A(a), A_hat(a_hat), T(period), Time_pt(time_pt) {}
57
58 /// Destructor: Empty
59 virtual ~MyEllipse() {}
60
61 /// Current position vector to material point at
62 /// Lagrangian coordinate xi
63 void position(const Vector<double>& xi, Vector<double>& r) const
64 {
65 // Get current time:
66 double time=Time_pt->time();
67
68 // Position vector
69 double axis=A+A_hat*cos(2.0*MathematicalConstants::Pi*time/T);
70 r[0] = axis*cos(xi[0]);
71 r[1] = (1.0/axis)*sin(xi[0]);
72 }
73
74 /// Parametrised position on object: r(xi). Evaluated at
75 /// previous time level. t=0: current time; t>0: previous
76 /// time level.
77 void position(const unsigned& t, const Vector<double>& xi,
78 Vector<double>& r) const
79 {
80 // Get current time:
81 double time=Time_pt->time(t);
82
83 // Position vector
84 double axis=A+A_hat*cos(2.0*MathematicalConstants::Pi*time/T);
85 r[0] = axis*cos(xi[0]);
86 r[1] = (1.0/axis)*sin(xi[0]);
87 }
88
89private:
90
91 /// x-half axis
92 double A;
93
94 /// Amplitude of variation in x-half axis
95 double A_hat;
96
97 /// Period of oscillation
98 double T;
99
100 /// Pointer to time object
101 Time* Time_pt;
102
103}; // end of MyEllipse
104
105
106
107///////////////////////////////////////////////////////////////////////
108///////////////////////////////////////////////////////////////////////
109////////////////////////////////////////////////////////////////////////
110
111
112
113//===start_of_namespace=================================================
114/// Namepspace for global parameters
115//======================================================================
117{
118
119 /// Reynolds number
120 double Re=100.0;
121
122 /// Womersley = Reynolds times Strouhal
123 double ReSt=100.0;
124
125 /// x-Half axis length
126 double A=1.0;
127
128 /// x-Half axis amplitude
129 double A_hat=0.1;
130
131 /// Period of oscillations
132 double T=1.0;
133
134 /// Exact solution of the problem as a vector containing u,v,p
135 void get_exact_u(const double& t, const Vector<double>& x, Vector<double>& u)
136 {
137 using namespace MathematicalConstants;
138
139 // Strouhal number
140 double St = ReSt/Re;
141
142 // Half axis
143 double a=A+A_hat*cos(2.0*Pi*t/T);
144 double adot=-2.0*A_hat*Pi*sin(2.0*Pi*t/T)/T;
145 u.resize(3);
146
147 // Velocity solution
148 u[0]=adot*x[0]/a;
149 u[1]=-adot*x[1]/a;
150
151 // Pressure solution
152 u[2]=(2.0*A_hat*Pi*Pi*Re*(x[0]*x[0]*St*cos(2.0*Pi*t/T)*A +
153 x[0]*x[0]*St*A_hat - x[0]*x[0]*A_hat +
154 x[0]*x[0]*A_hat*cos(2.0*Pi*t/T)*cos(2.0*Pi*t/T) -
155 x[1]*x[1]*St*cos(2.0*Pi*t/T)*A -
156 x[1]*x[1]*St*A_hat - x[1]*x[1]*A_hat +
157 x[1]*x[1]*A_hat*cos(2.0*Pi*t/T)*cos(2.0*Pi*t/T) ))
158 /(T*T*(A*A + 2.0*A*A_hat*cos(2.0*Pi*t/T) +
159 A_hat*A_hat*cos(2.0*Pi*t/T)*cos(2.0*Pi*t/T) ));
160 }
161
162
163} // end of namespace
164
165
166
167
168///////////////////////////////////////////////////////////////////////
169///////////////////////////////////////////////////////////////////////
170////////////////////////////////////////////////////////////////////////
171
172
173
174//=====start_of_problem_class=========================================
175/// Navier-Stokes problem in an oscillating ellipse domain.
176//====================================================================
177template<class ELEMENT, class TIMESTEPPER>
178class OscEllipseProblem : public Problem
179{
180
181public:
182
183 /// Constructor
185
186 /// Destructor (empty)
188
189 /// Update the problem specs after solve (empty)
191
192 /// Update problem specs before solve (empty)
194
195 /// Actions before adapt (empty)
197
198 /// Actions after adaptation, pin relevant pressures
200 {
201 // Unpin all pressure dofs
202 RefineableNavierStokesEquations<2>::
203 unpin_all_pressure_dofs(mesh_pt()->element_pt());
204
205 // Pin redundant pressure dofs
206 RefineableNavierStokesEquations<2>::
207 pin_redundant_nodal_pressures(mesh_pt()->element_pt());
208
209 // Now set the first pressure dof in the first element to 0.0
210 fix_pressure(0,0,0.0);
211
212 } // end of actions_after_adapt
213
214
215 /// Update the problem specs before next timestep
217 {
218 // Update the domain shape
219 mesh_pt()->node_update();
220
221 // Ring boundary: No slip; this implies that the velocity needs
222 // to be updated in response to wall motion
223 unsigned ibound=1;
224 unsigned num_nod=mesh_pt()->nboundary_node(ibound);
225 for (unsigned inod=0;inod<num_nod;inod++)
226 {
227 // Which node are we dealing with?
228 Node* node_pt=mesh_pt()->boundary_node_pt(ibound,inod);
229
230 // Apply no slip
231 FSI_functions::apply_no_slip_on_moving_wall(node_pt);
232 }
233 }
234
235 /// Update the problem specs after timestep (empty)
237
238 /// Doc the solution
239 void doc_solution(DocInfo& doc_info);
240
241 /// Timestepping loop
242 void unsteady_run(DocInfo& doc_info);
243
244 /// Set initial condition
246
247private:
248
249 /// Fix pressure in element e at pressure dof pdof and set to pvalue
250 void fix_pressure(const unsigned &e, const unsigned &pdof,
251 const double &pvalue)
252 {
253 //Cast to proper element and fix pressure
254 dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e))->
255 fix_pressure(pdof,pvalue);
256 } // end_of_fix_pressure
257
258 /// Pointer to GeomObject that specifies the domain bondary
259 GeomObject* Wall_pt;
260
261}; // end of problem_class
262
263
264
265//========start_of_constructor============================================
266/// Constructor for Navier-Stokes problem on an oscillating ellipse domain.
267//========================================================================
268template<class ELEMENT, class TIMESTEPPER>
270{
271
272 //Create the timestepper and add it to the problem
273 add_time_stepper_pt(new TIMESTEPPER);
274
275 // Setup mesh
276 //-----------
277
278 // Build geometric object that forms the curvilinear domain boundary:
279 // an oscillating ellipse
280
281 // Half axes
283
284 // Variations of half axes
286
287 // Period of the oscillation
288 double period=Global_Physical_Variables::T;
289
290 // Create GeomObject that specifies the domain bondary
291 Wall_pt=new MyEllipse(a,a_hat,period,Problem::time_pt());
292
293
294 // Start and end coordinates of curvilinear domain boundary on ellipse
295 double xi_lo=0.0;
296 double xi_hi=MathematicalConstants::Pi/2.0;
297
298 // Now create the mesh. Separating line between the two
299 // elements next to the curvilinear boundary is located half-way
300 // along the boundary.
301 double fract_mid=0.5;
302 Problem::mesh_pt() = new RefineableQuarterCircleSectorMesh<ELEMENT>(
303 Wall_pt,xi_lo,fract_mid,xi_hi,time_stepper_pt());
304
305 // Set error estimator NOT NEEDED IN CURRENT PROBLEM SINCE
306 // WE'RE ONLY REFINING THE MESH UNIFORMLY
307 //Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
308 //mesh_pt()->spatial_error_estimator_pt()=error_estimator_pt;
309
310
311 // Fluid boundary conditions
312 //--------------------------
313 // Ring boundary: No slip; this also implies that the velocity needs
314 // to be updated in response to wall motion
315 unsigned ibound=1;
316 {
317 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
318 for (unsigned inod=0;inod<num_nod;inod++)
319 {
320
321 // Pin both velocities
322 for (unsigned i=0;i<2;i++)
323 {
324 mesh_pt()->boundary_node_pt(ibound,inod)->pin(i);
325 }
326 }
327 } // end boundary 1
328
329 // Bottom boundary:
330 ibound=0;
331 {
332 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
333 for (unsigned inod=0;inod<num_nod;inod++)
334 {
335 // Pin vertical velocity
336 {
337 mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
338 }
339 }
340 } // end boundary 0
341
342 // Left boundary:
343 ibound=2;
344 {
345 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
346 for (unsigned inod=0;inod<num_nod;inod++)
347 {
348 // Pin horizontal velocity
349 {
350 mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
351 }
352 }
353 } // end boundary 2
354
355
356 // Complete the build of all elements so they are fully functional
357 //----------------------------------------------------------------
358
359 // Find number of elements in mesh
360 unsigned n_element = mesh_pt()->nelement();
361
362 // Loop over the elements to set up element-specific
363 // things that cannot be handled by constructor
364 for(unsigned i=0;i<n_element;i++)
365 {
366 // Upcast from FiniteElement to the present element
367 ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
368
369 //Set the Reynolds number, etc
370 el_pt->re_pt() = &Global_Physical_Variables::Re;
371 el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
372 }
373
374 // Pin redundant pressure dofs
375 RefineableNavierStokesEquations<2>::
376 pin_redundant_nodal_pressures(mesh_pt()->element_pt());
377
378 // Now set the first pressure dof in the first element to 0.0
379 fix_pressure(0,0,0.0);
380
381 // Do equation numbering
382 cout << "Number of equations: " << assign_eqn_numbers() << std::endl;
383
384} // end of constructor
385
386
387//======================start_of_set_initial_condition====================
388/// Set initial condition: Assign previous and current values
389/// from exact solution.
390//========================================================================
391template<class ELEMENT,class TIMESTEPPER>
393{
394 // Backup time in global timestepper
395 double backed_up_time=time_pt()->time();
396
397 // Past history for velocities must be established for t=time0-deltat, ...
398 // Then provide current values (at t=time0) which will also form
399 // the initial guess for first solve at t=time0+deltat
400
401 // Vector of exact solution value
402 Vector<double> soln(3);
403 Vector<double> x(2);
404
405 //Find number of nodes in mesh
406 unsigned num_nod = mesh_pt()->nnode();
407
408 // Get continuous times at previous timesteps
409 int nprev_steps=time_stepper_pt()->nprev_values();
410 Vector<double> prev_time(nprev_steps+1);
411 for (int itime=nprev_steps;itime>=0;itime--)
412 {
413 prev_time[itime]=time_pt()->time(unsigned(itime));
414 }
415
416 // Loop over current & previous timesteps (in outer loop because
417 // the mesh also moves!)
418 for (int itime=nprev_steps;itime>=0;itime--)
419 {
420 double time=prev_time[itime];
421
422 // Set global time (because this is how the geometric object refers
423 // to continous time )
424 time_pt()->time()=time;
425
426 cout << "setting IC at time =" << time << std::endl;
427
428 // Update the mesh for this value of the continuous time
429 // (The wall object reads the continous time from the
430 // global time object)
431 mesh_pt()->node_update();
432
433 // Loop over the nodes to set initial guess everywhere
434 for (unsigned jnod=0;jnod<num_nod;jnod++)
435 {
436 // Get nodal coordinates
437 x[0]=mesh_pt()->node_pt(jnod)->x(0);
438 x[1]=mesh_pt()->node_pt(jnod)->x(1);
439
440 // Get exact solution (unsteady stagnation point flow)
442
443 // Assign solution
444 mesh_pt()->node_pt(jnod)->set_value(itime,0,soln[0]);
445 mesh_pt()->node_pt(jnod)->set_value(itime,1,soln[1]);
446
447 // Loop over coordinate directions
448 for (unsigned i=0;i<2;i++)
449 {
450 mesh_pt()->node_pt(jnod)->x(itime,i)=x[i];
451 }
452 }
453 } // end of loop over previous timesteps
454
455 // Reset backed up time for global timestepper
456 time_pt()->time()=backed_up_time;
457
458} // end of set_initial_condition
459
460
461
462//=======start_of_doc_solution============================================
463/// Doc the solution
464//========================================================================
465template<class ELEMENT, class TIMESTEPPER>
467{
468 ofstream some_file;
469 char filename[100];
470
471 // Number of plot points
472 unsigned npts;
473 npts=5;
474
475 // Output solution
476 //-----------------
477 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
478 doc_info.number());
479 some_file.open(filename);
480 mesh_pt()->output(some_file,npts);
481 some_file << "TEXT X=2.5,Y=93.6,F=HELV,HU=POINT,C=BLUE,H=26,T=\"time = "
482 << time_pt()->time() << "\"";
483 some_file << "GEOMETRY X=2.5,Y=98,T=LINE,C=BLUE,LT=0.4" << std::endl;
484 some_file << "1" << std::endl;
485 some_file << "2" << std::endl;
486 some_file << " 0 0" << std::endl;
487 some_file << time_pt()->time()*20.0 << " 0" << std::endl;
488
489 // Write dummy zones that force tecplot to keep the axis limits constant
490 // while the domain is moving.
491 some_file << "ZONE I=2,J=2" << std::endl;
492 some_file << "0.0 0.0 -0.65 -0.65 -200.0" << std::endl;
493 some_file << "1.15 0.0 -0.65 -0.65 -200.0" << std::endl;
494 some_file << "0.0 1.15 -0.65 -0.65 -200.0" << std::endl;
495 some_file << "1.15 1.15 -0.65 -0.65 -200.0" << std::endl;
496 some_file << "ZONE I=2,J=2" << std::endl;
497 some_file << "0.0 0.0 0.65 0.65 300.0" << std::endl;
498 some_file << "1.15 0.0 0.65 0.65 300.0" << std::endl;
499 some_file << "0.0 1.15 0.65 0.65 300.0" << std::endl;
500 some_file << "1.15 1.15 0.65 0.65 300.0" << std::endl;
501
502 some_file.close();
503
504 // Output exact solution
505 //----------------------
506 snprintf(filename, sizeof(filename), "%s/exact_soln%i.dat",doc_info.directory().c_str(),
507 doc_info.number());
508 some_file.open(filename);
509 mesh_pt()->output_fct(some_file,npts,time_pt()->time(),
511 some_file.close();
512
513 // Doc error
514 //----------
515 double error,norm;
516 snprintf(filename, sizeof(filename), "%s/error%i.dat",doc_info.directory().c_str(),
517 doc_info.number());
518 some_file.open(filename);
519 mesh_pt()->compute_error(some_file,
521 time_pt()->time(),
522 error,norm);
523 some_file.close();
524
525
526 // Doc solution and error
527 //-----------------------
528 cout << "error: " << error << std::endl;
529 cout << "norm : " << norm << std::endl << std::endl;
530
531
532 // Plot wall posn
533 //---------------
534 snprintf(filename, sizeof(filename), "%s/Wall%i.dat",doc_info.directory().c_str(),
535 doc_info.number());
536 some_file.open(filename);
537
538 unsigned nplot=100;
539 for (unsigned iplot=0;iplot<nplot;iplot++)
540 {
541 Vector<double> xi_wall(1), r_wall(2);
542 xi_wall[0]=0.5*MathematicalConstants::Pi*double(iplot)/double(nplot-1);
543 Wall_pt->position(xi_wall,r_wall);
544 some_file << r_wall[0] << " " << r_wall[1] << std::endl;
545 }
546 some_file.close();
547
548 // Increment number of doc
549 doc_info.number()++;
550
551} // end of doc_solution
552
553
554//=======start_of_unsteady_run============================================
555/// Unsteady run
556//========================================================================
557template<class ELEMENT, class TIMESTEPPER>
559{
560
561 // Specify duration of the simulation
562 double t_max=3.0;
563
564 // Initial timestep
565 double dt=0.025;
566
567 // Initialise timestep
568 initialise_dt(dt);
569
570 // Set initial conditions.
571 set_initial_condition();
572
573 // Alternative initial conditions: impulsive start; see exercise.
574 //assign_initial_values_impulsive();
575
576 // find number of steps
577 unsigned nstep = unsigned(t_max/dt);
578
579 // If validation: Reduce number of timesteps performed and
580 // use coarse-ish mesh
581 if (CommandLineArgs::Argc>1)
582 {
583 nstep=2;
584 refine_uniformly();
585 cout << "validation run" << std::endl;
586 }
587 else
588 {
589 // Refine the mesh three times, to resolve the pressure distribution
590 // (the velocities could be represented accurately on a much coarser mesh).
591 refine_uniformly();
592 refine_uniformly();
593 refine_uniformly();
594 }
595
596 // Output solution initial
597 doc_solution(doc_info);
598
599 // Timestepping loop
600 for (unsigned istep=0;istep<nstep;istep++)
601 {
602 cout << "TIMESTEP " << istep << std::endl;
603 cout << "Time is now " << time_pt()->time() << std::endl;
604
605 // Take timestep
606 unsteady_newton_solve(dt);
607
608 //Output solution
609 doc_solution(doc_info);
610 }
611
612} // end of unsteady_run
613
614
615////////////////////////////////////////////////////////////////////////
616////////////////////////////////////////////////////////////////////////
617
618
619//======start_of_main=================================================
620/// Driver code for unsteady Navier-Stokes flow, driven by
621/// oscillating ellipse. If the code is executed with command line
622/// arguments, a validation run is performed.
623//====================================================================
624int main(int argc, char* argv[])
625{
626
627 // Store command line arguments
628 CommandLineArgs::setup(argc,argv);
629
630
631 // Solve with Crouzeix-Raviart elements
632 {
633 // Create DocInfo object with suitable directory name for output
634 DocInfo doc_info;
635 doc_info.set_directory("RESLT_CR");
636
637 //Set up problem
639
640 // Run the unsteady simulation
641 problem.unsteady_run(doc_info);
642 }
643
644 // Solve with Taylor-Hood elements
645 {
646 // Create DocInfo object with suitable directory name for output
647 DocInfo doc_info;
648 doc_info.set_directory("RESLT_TH");
649
650 //Set up problem
652
653 // Run the unsteady simulation
654 problem.unsteady_run(doc_info);
655 }
656
657
658}; // end of main
659
660
661
Oscillating ellipse.
double A_hat
Amplitude of variation in x-half axis.
MyEllipse(const double &a, const double &a_hat, const double &period, Time *time_pt)
Constructor: Pass initial x-half axis, amplitude of x-variation, period of oscillation and pointer to...
void position(const Vector< double > &xi, Vector< double > &r) const
Current position vector to material point at Lagrangian coordinate xi.
void position(const unsigned &t, const Vector< double > &xi, Vector< double > &r) const
Parametrised position on object: r(xi). Evaluated at previous time level. t=0: current time; t>0: pre...
double A
x-half axis
double T
Period of oscillation.
Time * Time_pt
Pointer to time object.
virtual ~MyEllipse()
Destructor: Empty.
Navier-Stokes problem in an oscillating ellipse domain.
void actions_before_newton_solve()
Update problem specs before solve (empty)
void actions_after_newton_solve()
Update the problem specs after solve (empty)
~OscEllipseProblem()
Destructor (empty)
void actions_after_adapt()
Actions after adaptation, pin relevant pressures.
void actions_before_implicit_timestep()
Update the problem specs before next timestep.
GeomObject * Wall_pt
Pointer to GeomObject that specifies the domain bondary.
void actions_after_implicit_timestep()
Update the problem specs after timestep (empty)
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 actions_before_adapt()
Actions before adapt (empty)
OscEllipseProblem()
Constructor.
void unsteady_run(DocInfo &doc_info)
Timestepping loop.
void set_initial_condition()
Set initial condition.
void doc_solution(DocInfo &doc_info)
Doc the solution.
Namepspace for global parameters.
double ReSt
Womersley = Reynolds times Strouhal.
double A_hat
x-Half axis amplitude
double T
Period of oscillations.
double A
x-Half axis length
void get_exact_u(const double &t, const Vector< double > &x, Vector< double > &u)
Exact solution of the problem as a vector containing u,v,p.
int main(int argc, char *argv[])
Driver code for unsteady Navier-Stokes flow, driven by oscillating ellipse. If the code is executed w...