Toggle navigation
Documentation
Big picture
The finite element method
The data structure
Not-so-quick guide
Optimisation
Order of action functions
Example codes and tutorials
List of example codes and tutorials
Meshing
Solvers
MPI parallel processing
Post-processing/visualisation
Other
Change log
Creating documentation
Coding conventions
Index
FAQ
About
People
Contact/Get involved
Publications
Acknowledgements
Copyright
Picture show
Go
demo_drivers
axisym_navier_stokes
spin_up
spin_up.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 axisymmetic spin-up problem in a cylinder, using both
27
// axisymmetric Taylor--Hood and Crouzeix--Raviart elements
28
29
// Generic oomph-lib header
30
#include "generic.h"
31
32
// Navier--Stokes headers
33
#include "navier_stokes.h"
34
35
// Axisymmetric Navier--Stokes headers
36
#include "axisym_navier_stokes.h"
37
38
// The mesh
39
#include "meshes/rectangular_quadmesh.h"
40
41
using namespace
std;
42
43
using namespace
oomph;
44
45
46
//==start_of_namespace====================================================
47
/// Namespace for physical parameters
48
//========================================================================
49
namespace
Global_Physical_Variables
50
{
51
52
/// Reynolds number
53
double
Re
= 5.0;
54
55
/// Womersley number
56
double
ReSt
= 5.0;
57
58
}
// End of namespace
59
60
61
//////////////////////////////////////////////////////////////////////////
62
//////////////////////////////////////////////////////////////////////////
63
//////////////////////////////////////////////////////////////////////////
64
65
66
//==start_of_problem_class================================================
67
/// Refineable rotating cylinder problem in a rectangular
68
/// axisymmetric domain
69
//========================================================================
70
template
<
class
ELEMENT,
class
TIMESTEPPER>
71
class
RotatingCylinderProblem
:
public
Problem
72
{
73
74
public
:
75
76
/// Constructor: Pass the number of elements and the lengths of the
77
/// domain in the radial (r) and axial (z) directions
78
RotatingCylinderProblem
(
const
unsigned
& n_r,
const
unsigned
& n_z,
79
const
double
& l_r,
const
double
& l_z);
80
81
/// Destructor (empty)
82
~RotatingCylinderProblem
() {}
83
84
/// Set initial conditions
85
void
set_initial_condition
();
86
87
/// Set boundary conditions
88
void
set_boundary_conditions
();
89
90
/// Document the solution
91
void
doc_solution
(DocInfo &doc_info);
92
93
/// Do unsteady run up to maximum time t_max with given timestep dt
94
void
unsteady_run
(
const
double
& t_max,
const
double
& dt,
95
const
string
dir_name);
96
97
/// Access function for the specific mesh
98
RefineableRectangularQuadMesh<ELEMENT>*
mesh_pt
()
99
{
100
return
dynamic_cast<
RefineableRectangularQuadMesh<ELEMENT>*
>
101
(Problem::mesh_pt());
102
}
103
104
private
:
105
106
/// Update the problem specs before solve.
107
/// Reset velocity boundary conditions just to be on the safe side...
108
void
actions_before_newton_solve
() {
set_boundary_conditions
(); }
109
110
/// No actions required after solve step
111
void
actions_after_newton_solve
() {}
112
113
/// After adaptation: Pin pressure again (the previously pinned
114
/// value might have disappeared) and pin redudant pressure dofs
115
void
actions_after_adapt
()
116
{
117
// Unpin all pressure dofs
118
RefineableAxisymmetricNavierStokesEquations::
119
unpin_all_pressure_dofs(
mesh_pt
()->element_pt());
120
121
// Pin redudant pressure dofs
122
RefineableAxisymmetricNavierStokesEquations::
123
pin_redundant_nodal_pressures(
mesh_pt
()->element_pt());
124
125
// Now set the pressure in first element at 'node' 0 to 0.0
126
fix_pressure
(0,0,0.0);
127
128
}
// End of actions_after_adapt
129
130
/// Fix pressure in element e at pressure dof pdof and set to pvalue
131
void
fix_pressure
(
const
unsigned
& e,
132
const
unsigned
& pdof,
133
const
double
& pvalue)
134
{
135
// Cast to actual element and fix pressure
136
dynamic_cast<
ELEMENT*
>
(
mesh_pt
()->element_pt(e))->
137
fix_pressure
(pdof,pvalue);
138
}
139
140
};
// End of problem class
141
142
143
144
//==start_of_constructor==================================================
145
/// Constructor for refineable rotating cylinder problem
146
//========================================================================
147
template
<
class
ELEMENT,
class
TIMESTEPPER>
148
RotatingCylinderProblem<ELEMENT,TIMESTEPPER>::
149
RotatingCylinderProblem
(
const
unsigned
& n_r,
const
unsigned
& n_z,
150
const
double
& l_r,
const
double
& l_z)
151
{
152
153
// Allocate the timestepper (this constructs the time object as well)
154
add_time_stepper_pt(
new
TIMESTEPPER);
155
156
// Build and assign mesh
157
Problem::mesh_pt() =
new
RefineableRectangularQuadMesh<ELEMENT>
158
(n_r,n_z,l_r,l_z,time_stepper_pt());
159
160
// Create and set the error estimator for spatial adaptivity
161
mesh_pt()->spatial_error_estimator_pt() =
new
Z2ErrorEstimator;
162
163
// Set the maximum refinement level for the mesh to 4
164
mesh_pt()->max_refinement_level() = 4;
165
166
// Override the maximum and minimum permitted errors
167
mesh_pt()->max_permitted_error() = 1.0e-2;
168
mesh_pt()->min_permitted_error() = 1.0e-3;
169
170
// --------------------------------------------
171
// Set the boundary conditions for this problem
172
// --------------------------------------------
173
174
// All nodes are free by default -- just pin the ones that have
175
// Dirichlet conditions here
176
177
// Determine number of mesh boundaries
178
const
unsigned
n_boundary = mesh_pt()->nboundary();
179
180
// Loop over mesh boundaries
181
for
(
unsigned
b=0;b<n_boundary;b++)
182
{
183
// Determine number of nodes on boundary b
184
const
unsigned
n_node = mesh_pt()->nboundary_node(b);
185
186
// Loop over nodes on boundary b
187
for
(
unsigned
n=0;n<n_node;n++)
188
{
189
// Pin values for radial velocity on all boundaries
190
mesh_pt()->boundary_node_pt(b,n)->pin(0);
191
192
// Pin values for axial velocity on all SOLID boundaries (b = 0,1,2)
193
if
(b!=3) { mesh_pt()->boundary_node_pt(b,n)->pin(1); }
194
195
// Pin values for azimuthal velocity on all boundaries
196
mesh_pt()->boundary_node_pt(b,n)->pin(2);
197
198
}
// End of loop over nodes on boundary b
199
}
// End of loop over mesh boundaries
200
201
// ----------------------------------------------------------------
202
// Complete the problem setup to make the elements fully functional
203
// ----------------------------------------------------------------
204
205
// Determine number of elements in mesh
206
const
unsigned
n_element = mesh_pt()->nelement();
207
208
// Loop over the elements
209
for
(
unsigned
e=0;e<n_element;e++)
210
{
211
// Upcast from GeneralisedElement to the present element
212
ELEMENT* el_pt =
dynamic_cast<
ELEMENT*
>
(mesh_pt()->element_pt(e));
213
214
// Set the Reynolds number
215
el_pt->re_pt() = &
Global_Physical_Variables::Re
;
216
217
// Set the Womersley number
218
el_pt->re_st_pt() = &
Global_Physical_Variables::ReSt
;
219
220
// The mesh remains fixed
221
el_pt->disable_ALE();
222
223
}
// End of loop over elements
224
225
// Pin redundant pressure dofs
226
RefineableAxisymmetricNavierStokesEquations::
227
pin_redundant_nodal_pressures(mesh_pt()->element_pt());
228
229
// Now set the pressure in first element at 'node' 0 to 0.0
230
fix_pressure(0,0,0.0);
231
232
// Set up equation numbering scheme
233
cout <<
"Number of equations: "
<< assign_eqn_numbers() << std::endl;
234
235
}
// End of constructor
236
237
238
239
//==start_of_set_initial_condition========================================
240
/// Set initial conditions: Set all nodal velocities to zero and
241
/// initialise the previous velocities to correspond to an impulsive start
242
//========================================================================
243
template
<
class
ELEMENT,
class
TIMESTEPPER>
244
void
RotatingCylinderProblem<ELEMENT,TIMESTEPPER>::set_initial_condition
()
245
{
246
// Determine number of nodes in mesh
247
const
unsigned
n_node = mesh_pt()->nnode();
248
249
// Loop over all nodes in mesh
250
for
(
unsigned
n=0;n<n_node;n++)
251
{
252
// Loop over the three velocity components
253
for
(
unsigned
i=0;i<3;i++)
254
{
255
// Set velocity component i of node n to zero
256
mesh_pt()->node_pt(n)->set_value(i,0.0);
257
}
258
}
259
260
// Initialise the previous velocity values for timestepping
261
// corresponding to an impulsive start
262
assign_initial_values_impulsive();
263
264
}
// End of set_initial_condition
265
266
267
268
//==start_of_set_boundary_conditions======================================
269
/// Set boundary conditions: Set both velocity components to zero
270
/// on the bottom (solid) wall and the horizontal component only to zero
271
/// on the side (periodic) boundaries
272
//========================================================================
273
template
<
class
ELEMENT,
class
TIMESTEPPER>
274
void
RotatingCylinderProblem<ELEMENT,TIMESTEPPER>::set_boundary_conditions
()
275
{
276
// Determine number of mesh boundaries
277
const
unsigned
n_boundary = mesh_pt()->nboundary();
278
279
// Loop over mesh boundaries
280
for
(
unsigned
b=0;b<n_boundary;b++)
281
{
282
// Determine number of nodes on boundary b
283
const
unsigned
n_node = mesh_pt()->nboundary_node(b);
284
285
// Loop over nodes on boundary b
286
for
(
unsigned
n=0;n<n_node;n++)
287
{
288
// For the solid boundaries (boundaries 0,1,2)
289
if
(b<3)
290
{
291
// Get the radial component of position
292
const
double
r_pos = mesh_pt()->boundary_node_pt(b,n)->x(0);
293
294
// Set all velocity components to no flow along boundary
295
mesh_pt()->boundary_node_pt(b,n)->set_value(0,0,0.0);
// Radial
296
mesh_pt()->boundary_node_pt(b,n)->set_value(0,1,0.0);
// Axial
297
mesh_pt()->boundary_node_pt(b,n)->set_value(0,2,r_pos);
// Azimuthal
298
}
299
300
// For the symmetry boundary (boundary 3)
301
if
(b==3)
302
{
303
// Set only the radial (i=0) and azimuthal (i=2) velocity components
304
// to no flow along boundary (axial component is unconstrained)
305
mesh_pt()->boundary_node_pt(b,n)->set_value(0,0,0.0);
306
mesh_pt()->boundary_node_pt(b,n)->set_value(0,2,0.0);
307
}
308
}
// End of loop over nodes on boundary b
309
}
// End of loop over mesh boundaries
310
311
}
// End of set_boundary_conditions
312
313
314
315
//==start_of_doc_solution=================================================
316
/// Document the solution
317
//========================================================================
318
template
<
class
ELEMENT,
class
TIMESTEPPER>
319
void
RotatingCylinderProblem<ELEMENT,TIMESTEPPER>::
320
doc_solution
(DocInfo& doc_info)
321
{
322
323
// Output the time
324
cout <<
"Time is now "
<< time_pt()->time() << std::endl;
325
326
ofstream some_file;
327
char
filename[100];
328
329
// Set number of plot points (in each coordinate direction)
330
const
unsigned
npts = 5;
331
332
// Open solution output file
333
snprintf(filename,
sizeof
(filename),
"%s/soln%i.dat"
,
334
doc_info.directory().c_str(),doc_info.number());
335
some_file.open(filename);
336
337
// Output solution to file
338
mesh_pt()->output(some_file,npts);
339
340
// Close solution output file
341
some_file.close();
342
343
}
// End of doc_solution
344
345
346
347
//==start_of_unsteady_run=================================================
348
/// Perform run up to specified time t_max with given timestep dt
349
//========================================================================
350
template
<
class
ELEMENT,
class
TIMESTEPPER>
351
void
RotatingCylinderProblem<ELEMENT,TIMESTEPPER>::
352
unsteady_run
(
const
double
& t_max,
const
double
& dt,
const
string
dir_name)
353
{
354
355
// Initialise DocInfo object
356
DocInfo doc_info;
357
358
// Set output directory
359
doc_info.set_directory(dir_name);
360
361
// Initialise counter for solutions
362
doc_info.number()=0;
363
364
// Initialise timestep
365
initialise_dt(dt);
366
367
// Set initial condition
368
set_initial_condition();
369
370
// Maximum number of spatial adaptations per timestep
371
unsigned
max_adapt = 4;
372
373
// Call refine_uniformly twice
374
for
(
unsigned
i=0;i<2;i++) { refine_uniformly(); }
375
376
// Determine number of timesteps
377
const
unsigned
n_timestep = unsigned(t_max/dt);
378
379
// Doc initial solution
380
doc_solution(doc_info);
381
382
// Increment counter for solutions
383
doc_info.number()++;
384
385
// Are we on the first timestep? At this point, yes!
386
bool
first_timestep =
true
;
387
388
// Specify normalising factor explicitly
389
Z2ErrorEstimator* error_pt =
dynamic_cast<
Z2ErrorEstimator*
>
390
(mesh_pt()->spatial_error_estimator_pt());
391
error_pt->reference_flux_norm() = 0.01;
392
393
// Timestepping loop
394
for
(
unsigned
t=1;t<=n_timestep;t++)
395
{
396
// Output current timestep to screen
397
cout <<
"\nTimestep "
<< t <<
" of "
<< n_timestep << std::endl;
398
399
// Take fixed timestep with spatial adaptivity
400
unsteady_newton_solve(dt,max_adapt,first_timestep);
401
402
// No longer on first timestep, so set first_timestep flag to false
403
first_timestep =
false
;
404
405
// Reset maximum number of adaptations for all future timesteps
406
max_adapt = 1;
407
408
// Doc solution
409
doc_solution(doc_info);
410
411
// Increment counter for solutions
412
doc_info.number()++;
413
414
}
// End of timestepping loop
415
416
}
// End of unsteady_run
417
418
419
//////////////////////////////////////////////////////////////////////////
420
//////////////////////////////////////////////////////////////////////////
421
//////////////////////////////////////////////////////////////////////////
422
423
424
//==start_of_main=========================================================
425
/// Driver code for axisymmetric spin-up problem
426
//========================================================================
427
int
main
(
int
argc,
char
* argv[])
428
{
429
// Store command line arguments
430
CommandLineArgs::setup(argc,argv);
431
432
/// Maximum time
433
double
t_max = 1.0;
434
435
/// Duration of timestep
436
const
double
dt = 0.01;
437
438
// If we are doing validation run, use smaller number of timesteps
439
if
(CommandLineArgs::Argc>1) { t_max = 0.02; }
440
441
// Number of elements in radial (r) direction
442
const
unsigned
n_r = 2;
443
444
// Number of elements in axial (z) direction
445
const
unsigned
n_z = 2;
446
447
// Length in radial (r) direction
448
const
double
l_r = 1.0;
449
450
// Length in axial (z) direction
451
const
double
l_z = 1.4;
452
453
// -----------------------------------------
454
// RefineableAxisymmetricQTaylorHoodElements
455
// -----------------------------------------
456
{
457
cout <<
"Doing RefineableAxisymmetricQTaylorHoodElement"
<< std::endl;
458
459
// Build the problem with RefineableAxisymmetricQTaylorHoodElements
460
RotatingCylinderProblem
461
<RefineableAxisymmetricQTaylorHoodElement, BDF<2> >
462
problem(n_r,n_z,l_r,l_z);
463
464
// Solve the problem and output the solution
465
problem.unsteady_run(t_max,dt,
"RESLT_TH"
);
466
}
467
468
// ----------------------------------------------
469
// RefineableAxisymmetricQCrouzeixRaviartElements
470
// ----------------------------------------------
471
{
472
cout <<
"Doing RefineableAxisymmetricQCrouzeixRaviartElement"
<< std::endl;
473
474
// Build the problem with RefineableAxisymmetricQCrouzeixRaviartElements
475
RotatingCylinderProblem
476
<RefineableAxisymmetricQCrouzeixRaviartElement, BDF<2> >
477
problem(n_r,n_z,l_r,l_z);
478
479
// Solve the problem and output the solution
480
problem.unsteady_run(t_max,dt,
"RESLT_CR"
);
481
}
482
483
}
// End of main
484
485
486
487
488
489
RotatingCylinderProblem
Refineable rotating cylinder problem in a rectangular axisymmetric domain.
Definition
spin_up.cc:72
RotatingCylinderProblem::fix_pressure
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.
Definition
spin_up.cc:131
RotatingCylinderProblem::doc_solution
void doc_solution(DocInfo &doc_info)
Document the solution.
Definition
spin_up.cc:320
RotatingCylinderProblem::actions_after_newton_solve
void actions_after_newton_solve()
No actions required after solve step.
Definition
spin_up.cc:111
RotatingCylinderProblem::set_boundary_conditions
void set_boundary_conditions()
Set boundary conditions.
Definition
spin_up.cc:274
RotatingCylinderProblem::RotatingCylinderProblem
RotatingCylinderProblem(const unsigned &n_r, const unsigned &n_z, const double &l_r, const double &l_z)
Constructor: Pass the number of elements and the lengths of the domain in the radial (r) and axial (z...
Definition
spin_up.cc:149
RotatingCylinderProblem::~RotatingCylinderProblem
~RotatingCylinderProblem()
Destructor (empty)
Definition
spin_up.cc:82
RotatingCylinderProblem::set_initial_condition
void set_initial_condition()
Set initial conditions.
Definition
spin_up.cc:244
RotatingCylinderProblem::actions_after_adapt
void actions_after_adapt()
After adaptation: Pin pressure again (the previously pinned value might have disappeared) and pin red...
Definition
spin_up.cc:115
RotatingCylinderProblem::mesh_pt
RefineableRectangularQuadMesh< ELEMENT > * mesh_pt()
Access function for the specific mesh.
Definition
spin_up.cc:98
RotatingCylinderProblem::unsteady_run
void unsteady_run(const double &t_max, const double &dt, const string dir_name)
Do unsteady run up to maximum time t_max with given timestep dt.
Definition
spin_up.cc:352
RotatingCylinderProblem::actions_before_newton_solve
void actions_before_newton_solve()
Update the problem specs before solve. Reset velocity boundary conditions just to be on the safe side...
Definition
spin_up.cc:108
Global_Physical_Variables
Namespace for physical parameters.
Definition
spin_up.cc:50
Global_Physical_Variables::ReSt
double ReSt
Womersley number.
Definition
spin_up.cc:56
Global_Physical_Variables::Re
double Re
Reynolds number.
Definition
spin_up.cc:53
main
int main(int argc, char *argv[])
Driver code for axisymmetric spin-up problem.
Definition
spin_up.cc:427