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
41using namespace std;
42
43using namespace oomph;
44
45
46//==start_of_namespace====================================================
47/// Namespace for physical parameters
48//========================================================================
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//========================================================================
70template <class ELEMENT, class TIMESTEPPER>
71class RotatingCylinderProblem : public Problem
72{
73
74public:
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)
83
84 /// Set initial conditions
86
87 /// 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
104private:
105
106 /// Update the problem specs before solve.
107 /// Reset velocity boundary conditions just to be on the safe side...
109
110 /// No actions required after solve step
112
113 /// After adaptation: Pin pressure again (the previously pinned
114 /// value might have disappeared) and pin redudant pressure dofs
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//========================================================================
147template <class ELEMENT, class TIMESTEPPER>
149RotatingCylinderProblem(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//========================================================================
243template <class ELEMENT, class TIMESTEPPER>
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//========================================================================
273template <class ELEMENT, class TIMESTEPPER>
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//========================================================================
318template <class ELEMENT, class TIMESTEPPER>
320doc_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//========================================================================
350template <class ELEMENT, class TIMESTEPPER>
352unsteady_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//========================================================================
427int 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
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
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
Refineable rotating cylinder problem in a rectangular axisymmetric domain.
Definition spin_up.cc:72
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
void doc_solution(DocInfo &doc_info)
Document the solution.
Definition spin_up.cc:320
void actions_after_newton_solve()
No actions required after solve step.
Definition spin_up.cc:111
void set_boundary_conditions()
Set boundary conditions.
Definition spin_up.cc:274
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()
Destructor (empty)
Definition spin_up.cc:82
void set_initial_condition()
Set initial conditions.
Definition spin_up.cc:244
void actions_after_adapt()
After adaptation: Pin pressure again (the previously pinned value might have disappeared) and pin red...
Definition spin_up.cc:115
RefineableRectangularQuadMesh< ELEMENT > * mesh_pt()
Access function for the specific mesh.
Definition spin_up.cc:98
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
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
Namespace for physical parameters.
Definition spin_up.cc:50
double ReSt
Womersley number.
Definition spin_up.cc:56
double Re
Reynolds number.
Definition spin_up.cc:53
int main(int argc, char *argv[])
Driver code for axisymmetric spin-up problem.
Definition spin_up.cc:427