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