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-2023 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 //========================================================================
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)
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
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 //========================================================================
147 template <class ELEMENT, class 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>
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>
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>
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  sprintf(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>
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
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 
/////////////////////////////////////////////////////////////////////// /////////////////////////////...
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
RefineableRectangularQuadMesh< ELEMENT > * mesh_pt()
Access function for the specific mesh.
Definition: spin_up.cc:98
~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
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[])
/////////////////////////////////////////////////////////////////////// /////////////////////////////...
Definition: spin_up.cc:427