spine_two_layer_interface_axisym.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 an axisymmetric two fluid interface problem, where
27 // the mesh is deformed using a spine-based node-update strategy
28 
29 // Generic oomph-lib header
30 #include "generic.h"
31 
32 // Axisymmetric Navier-Stokes headers
33 #include "axisym_navier_stokes.h"
34 #include "navier_stokes.h"
35 
36 // Interface headers
37 #include "fluid_interface.h"
38 
39 // Bessel function headers
40 #include "oomph_crbond_bessel.h"
41 
42 // The mesh
43 #include "meshes/two_layer_spine_mesh.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  /// Strouhal number
60  double St = 1.0;
61 
62  /// Womersley number (Reynolds x Strouhal, computed automatically)
63  double ReSt;
64 
65  /// Product of Reynolds number and inverse of Froude number
66  double ReInvFr = 5.0; // (Fr = 1)
67 
68  /// Ratio of viscosity in upper fluid to viscosity in lower
69  /// fluid. Reynolds number etc. is based on viscosity in lower fluid.
70  double Viscosity_Ratio = 0.1;
71 
72  /// Ratio of density in upper fluid to density in lower
73  /// fluid. Reynolds number etc. is based on density in lower fluid.
74  double Density_Ratio = 0.5;
75 
76  /// Capillary number
77  double Ca = 0.01;
78 
79  /// Direction of gravity
80  Vector<double> G(3);
81 
82 } // End of namespace
83 
84 
85 /// ///////////////////////////////////////////////////////////////////////
86 /// ///////////////////////////////////////////////////////////////////////
87 /// ///////////////////////////////////////////////////////////////////////
88 
89 
90 //==start_of_problem_class================================================
91 /// Axisymmetric two fluid interface problem in a rectangular domain
92 //========================================================================
93 template <class ELEMENT, class TIMESTEPPER>
94 class InterfaceProblem : public Problem
95 {
96 
97 public:
98 
99  /// Constructor: Pass the number of elements and the width of the
100  /// domain in the r direction. Also pass the number of elements in
101  /// the z direction of the bottom (fluid 1) and top (fluid 2) layers,
102  /// along with the heights of both layers.
103  InterfaceProblem(const unsigned &n_r, const unsigned &n_z1,
104  const unsigned &n_z2, const double &l_r,
105  const double &h1, const double &h2);
106 
107  /// Destructor (empty)
109 
110  /// Set initial conditions
112 
113  /// Set boundary conditions
115 
116  /// Doc the solution
117  void doc_solution(DocInfo &doc_info);
118 
119  /// Do unsteady run up to maximum time t_max with given timestep dt
120  void unsteady_run(const double &t_max, const double &dt);
121 
122 private:
123 
124  /// Spine heights/lengths are unknowns in the problem so their
125  /// values get corrected during each Newton step. However, changing
126  /// their value does not automatically change the nodal positions, so
127  /// we need to update all of them here.
129  {
130  Bulk_mesh_pt->node_update();
131  }
132 
133  /// No actions required before solve step
135 
136  /// Update after solve can remain empty, because the update
137  /// is performed automatically after every Newton step.
139 
140  /// Deform the mesh/free surface to a prescribed function
141  void deform_free_surface(const double &epsilon, const double &k);
142 
143  /// Fix pressure in element e at pressure dof pdof and set to pvalue
144  void fix_pressure(const unsigned &e,
145  const unsigned &pdof,
146  const double &pvalue)
147  {
148  // Fix the pressure at that element
149  dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e))->
150  fix_pressure(pdof,pvalue);
151  }
152 
153  /// Width of domain
154  double Lr;
155 
156  /// Trace file
157  ofstream Trace_file;
158 
159 
160  /// Pointer to the specific bulk mesh
161  TwoLayerSpineMesh<ELEMENT>* Bulk_mesh_pt;
162 
163  /// Pointer to the surface mesh
164  Mesh* Surface_mesh_pt;
165 
166 
167 }; // End of problem class
168 
169 
170 
171 //==start_of_constructor==================================================
172 /// Constructor for axisymmetric two fluid interface problem
173 //========================================================================
174 template <class ELEMENT, class TIMESTEPPER>
176 InterfaceProblem(const unsigned &n_r, const unsigned &n_z1,
177  const unsigned &n_z2, const double &l_r,
178  const double& h1, const double &h2) : Lr(l_r)
179 {
180 
181  // Allocate the timestepper (this constructs the time object as well)
182  add_time_stepper_pt(new TIMESTEPPER);
183 
184  // Build and assign mesh
185  Bulk_mesh_pt = new TwoLayerSpineMesh<ELEMENT>
186  (n_r,n_z1,n_z2,l_r,h1,h2,time_stepper_pt());
187 
188  Surface_mesh_pt = new Mesh;
189 
190  //Loop over the horizontal elements
191  for(unsigned i=0;i<n_r;i++)
192  {
193  //Construct a new 1D line element on the face on which the local
194  //coordinate 1 is fixed at its max. value (1) -- Face 2
195  FiniteElement *interface_element_pt = new
196  SpineAxisymmetricFluidInterfaceElement<ELEMENT>(
197  Bulk_mesh_pt->finite_element_pt(n_r*(n_z1-1)+i),2);
198 
199  this->Surface_mesh_pt->add_element_pt(interface_element_pt);
200  }
201 
202  // --------------------------------------------
203  // Set the boundary conditions for this problem
204  // --------------------------------------------
205 
206  // All nodes are free by default -- just pin the ones that have
207  // Dirichlet conditions here
208 
209  // Loop over mesh boundaries
210  for(unsigned b=0;b<6;b++)
211  {
212  // Determine number of nodes on boundary b
213  const unsigned n_node = Bulk_mesh_pt->nboundary_node(b);
214 
215  // Loop over nodes on boundary b
216  for (unsigned n=0;n<n_node;n++)
217  {
218  // Pin radial and azimuthal velocities on all boundaries
219  // (no slip/penetration)
220  Bulk_mesh_pt->boundary_node_pt(b,n)->pin(0);
221  Bulk_mesh_pt->boundary_node_pt(b,n)->pin(2);
222 
223  // Pin axial velocity on top (b=3) and bottom (b=0) boundaries
224  // (no penetration). Because we have a slippery outer wall we do
225  // NOT pin the axial velocity on this boundary (b=1,2); similarly,
226  // we do not pin the axial velocity on the symmetry boundary (b=4,5).
227  if(b==0 || b==3) { Bulk_mesh_pt->boundary_node_pt(b,n)->pin(1); }
228  }
229  }
230 
231  // Pin all azimuthal velocities throughout the bulk of the domain
232  const unsigned n_node = Bulk_mesh_pt->nnode();
233  for(unsigned n=0;n<n_node;n++) { Bulk_mesh_pt->node_pt(n)->pin(2); }
234 
235  // ----------------------------------------------------------------
236  // Complete the problem setup to make the elements fully functional
237  // ----------------------------------------------------------------
238 
239  // Determine number of bulk elements in lower/upper fluids
240  const unsigned n_lower = Bulk_mesh_pt->nlower();
241  const unsigned n_upper = Bulk_mesh_pt->nupper();
242 
243  // Loop over bulk elements in lower fluid
244  for(unsigned e=0;e<n_lower;e++)
245  {
246  // Upcast from GeneralisedElement to the present element
247  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->
248  lower_layer_element_pt(e));
249 
250  // Set the Reynolds number
251  el_pt->re_pt() = &Global_Physical_Variables::Re;
252 
253  // Set the Womersley number
254  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
255 
256  // Set the product of the Reynolds number and the inverse of the
257  // Froude number
258  el_pt->re_invfr_pt() = &Global_Physical_Variables::ReInvFr;
259 
260  // Set the direction of gravity
261  el_pt->g_pt() = &Global_Physical_Variables::G;
262 
263  } // End of loop over bulk elements in lower fluid
264 
265  // Loop over bulk elements in upper fluid
266  for(unsigned e=0;e<n_upper;e++)
267  {
268  // Upcast from GeneralisedElement to the present element
269  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->
270  upper_layer_element_pt(e));
271 
272  // Set the Reynolds number
273  el_pt->re_pt() = &Global_Physical_Variables::Re;
274 
275  // Set the Womersley number
276  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
277 
278  // Set the product of the Reynolds number and the inverse of the
279  // Froude number
280  el_pt->re_invfr_pt() = &Global_Physical_Variables::ReInvFr;
281 
282  // Set the viscosity ratio
283  el_pt->viscosity_ratio_pt() = &Global_Physical_Variables::Viscosity_Ratio;
284 
285  // Set the density ratio
286  el_pt->density_ratio_pt() = &Global_Physical_Variables::Density_Ratio;
287 
288  // Set the direction of gravity
289  el_pt->g_pt() = &Global_Physical_Variables::G;
290 
291  } // End of loop over bulk elements in upper fluid
292 
293  // Set the pressure in the first element at 'node' 0 to 0.0
294  fix_pressure(0,0,0.0);
295 
296  // Determine number of 1D interface elements in mesh
297  unsigned n_interface_element = Surface_mesh_pt->nelement();
298 
299  // Loop over interface elements
300  for(unsigned e=0;e<n_interface_element;e++)
301  {
302  // Upcast from GeneralisedElement to the present element
303  SpineAxisymmetricFluidInterfaceElement<ELEMENT>* el_pt =
304  dynamic_cast<SpineAxisymmetricFluidInterfaceElement<ELEMENT>*>
305  (Surface_mesh_pt->element_pt(e));
306 
307  // Set the Strouhal number
308  el_pt->ca_pt() = &Global_Physical_Variables::St;
309 
310  // Set the Capillary number
311  el_pt->ca_pt() = &Global_Physical_Variables::Ca;
312 
313  } // End of loop over interface elements
314 
315  // Apply the boundary conditions
317 
318 
319  this->add_sub_mesh(Bulk_mesh_pt);
320  this->add_sub_mesh(Surface_mesh_pt);
321 
322  this->build_global_mesh();
323 
324 
325  // Setup equation numbering scheme
326  cout << "Number of equations: " << assign_eqn_numbers() << std::endl;
327 
328 } // End of constructor
329 
330 
331 
332 //==start_of_set_initial_condition========================================
333 /// Set initial conditions: Set all nodal velocities to zero and
334 /// initialise the previous velocities and nodal positions to correspond
335 /// to an impulsive start
336 //========================================================================
337 template <class ELEMENT, class TIMESTEPPER>
339 {
340  // Determine number of nodes in mesh
341  const unsigned n_node = Bulk_mesh_pt->nnode();
342 
343  // Loop over all nodes in mesh
344  for(unsigned n=0;n<n_node;n++)
345  {
346  // Loop over the three velocity components
347  for(unsigned i=0;i<3;i++)
348  {
349  // Set velocity component i of node n to zero
350  Bulk_mesh_pt->node_pt(n)->set_value(i,0.0);
351  }
352  }
353 
354  // Initialise the previous velocity values for timestepping
355  // corresponding to an impulsive start
356  assign_initial_values_impulsive();
357 
358 } // End of set_initial_condition
359 
360 
361 
362 //==start_of_set_boundary_conditions======================================
363 /// Set boundary conditions: Set all velocity components to zero
364 /// on the top and bottom (solid) walls and the radial and azimuthal
365 /// components only to zero on the side boundaries
366 //========================================================================
367 template <class ELEMENT, class TIMESTEPPER>
369 {
370  // Loop over mesh boundaries
371  for(unsigned b=0;b<6;b++)
372  {
373  // Determine number of nodes on boundary b
374  const unsigned n_node = Bulk_mesh_pt->nboundary_node(b);
375 
376  // Loop over nodes on boundary b
377  for(unsigned n=0;n<n_node;n++)
378  {
379  // Set radial component of the velocity to zero on all boundaries
380  Bulk_mesh_pt->boundary_node_pt(b,n)->set_value(0,0.0);
381 
382  // Set azimuthal component of the velocity to zero on all boundaries
383  Bulk_mesh_pt->boundary_node_pt(b,n)->set_value(2,0.0);
384 
385  // Set axial component of the velocity to zero on solid boundaries
386  if(b==0 || b==3)
387  {
388  Bulk_mesh_pt->boundary_node_pt(b,n)->set_value(1,0.0);
389  }
390  }
391  }
392 } // End of set_boundary_conditions
393 
394 
395 
396 //==start_of_deform_free_surface==========================================
397 /// Deform the mesh/free surface to a prescribed function
398 //========================================================================
399 template <class ELEMENT, class TIMESTEPPER>
401 deform_free_surface(const double &epsilon,const double &k)
402 {
403  // Initialise Bessel functions (only need the first!)
404  double j0, j1, y0, y1, j0p, j1p, y0p, y1p;
405 
406  // Determine number of spines in mesh
407  const unsigned n_spine = Bulk_mesh_pt->nspine();
408 
409  // Loop over spines in mesh
410  for(unsigned i=0;i<n_spine;i++)
411  {
412  // Determine radial coordinate of spine
413  const double r_value = Bulk_mesh_pt->boundary_node_pt(0,i)->x(0);
414 
415  // Compute Bessel functions
416  CRBond_Bessel::bessjy01a(k*r_value,j0,j1,y0,y1,j0p,j1p,y0p,y1p);
417 
418  // Set spine height
419  Bulk_mesh_pt->spine_pt(i)->height() = 1.0 + epsilon*j0;
420  }
421 
422  // Update nodes in bulk mesh
423  Bulk_mesh_pt->node_update();
424 
425 } // End of deform_free_surface
426 
427 
428 
429 //==start_of_doc_solution=================================================
430 /// Doc the solution
431 //========================================================================
432 template <class ELEMENT, class TIMESTEPPER>
434 {
435 
436  // Output the time
437  cout << "Time is now " << time_pt()->time() << std::endl;
438 
439  // Document time and vertical position of left hand side of interface
440  // in trace file
441  Trace_file << time_pt()->time() << " "
442  << Bulk_mesh_pt->spine_pt(0)->height() << " " << std::endl;
443 
444  ofstream some_file;
445  char filename[100];
446 
447  // Set number of plot points (in each coordinate direction)
448  const unsigned npts = 5;
449 
450  // Open solution output file
451  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
452  doc_info.number());
453  some_file.open(filename);
454 
455  // Output solution to file
456  Bulk_mesh_pt->output(some_file,npts);
457  Surface_mesh_pt->output(some_file,npts);
458 
459  // Close solution output file
460  some_file.close();
461 
462 } // End of doc_solution
463 
464 
465 
466 //==start_of_unsteady_run=================================================
467 /// Perform run up to specified time t_max with given timestep dt
468 //========================================================================
469 template <class ELEMENT, class TIMESTEPPER>
471 unsteady_run(const double &t_max, const double &dt)
472 {
473 
474  // Set value of epsilon
475  const double epsilon = 0.1;
476 
477  // Set value of k in Bessel function J_0(kr)
478  const double k_bessel = 3.8317;
479 
480  // Deform the mesh/free surface
481  deform_free_surface(epsilon,k_bessel);
482 
483  // Initialise DocInfo object
484  DocInfo doc_info;
485 
486  // Set output directory
487  doc_info.set_directory("RESLT");
488 
489  // Initialise counter for solutions
490  doc_info.number()=0;
491 
492  // Open trace file
493  char filename[100];
494  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
495  Trace_file.open(filename);
496 
497  // Initialise trace file
498  Trace_file << "time, interface height" << std::endl;
499 
500  // Initialise timestep
501  initialise_dt(dt);
502 
503  // Set initial conditions
504  set_initial_condition();
505 
506  // Determine number of timesteps
507  const unsigned n_timestep = unsigned(t_max/dt);
508 
509  // Doc initial solution
510  doc_solution(doc_info);
511 
512  // Increment counter for solutions
513  doc_info.number()++;
514 
515  // Timestepping loop
516  for(unsigned t=1;t<=n_timestep;t++)
517  {
518  // Output current timestep to screen
519  cout << "\nTimestep " << t << " of " << n_timestep << std::endl;
520 
521  // Take one fixed timestep
522  unsteady_newton_solve(dt);
523 
524  // Doc solution
525  doc_solution(doc_info);
526 
527  // Increment counter for solutions
528  doc_info.number()++;
529 
530  } // End of timestepping loop
531 
532 } // End of unsteady_run
533 
534 
535 /// ///////////////////////////////////////////////////////////////////////
536 /// ///////////////////////////////////////////////////////////////////////
537 /// ///////////////////////////////////////////////////////////////////////
538 
539 
540 //==start_of_main=========================================================
541 /// Driver code for axisymmetric two fluid interface problem
542 //========================================================================
543 int main(int argc, char* argv[])
544 {
545  // Store command line arguments
546  CommandLineArgs::setup(argc,argv);
547 
548  // Compute the Womersley number
551 
552  /// Maximum time
553  double t_max = 1.2;
554 
555  /// Duration of timestep
556  const double dt = 0.005;
557 
558  // If we are doing validation run, use smaller number of timesteps
559  if(CommandLineArgs::Argc>1) { t_max = 0.01; }
560 
561  // Number of elements in r direction
562  const unsigned n_r = 12;
563 
564  // Number of elements in z direction in lower fluid (fluid 1)
565  const unsigned n_z1 = 12;
566 
567  // Number of elements in z direction in upper fluid (fluid 2)
568  const unsigned n_z2 = 12;
569 
570  // Width of domain
571  const double l_r = 1.0;
572 
573  // Height of lower fluid layer
574  const double h1 = 1.0;
575 
576  // Height of upper fluid layer
577  const double h2 = 1.0;
578 
579  // Set direction of gravity (vertically downwards)
583 
584  // Set up the spine test problem with AxisymmetricQCrouzeixRaviartElements,
585  // using the BDF<2> timestepper
587  BDF<2> >
588  problem(n_r,n_z1,n_z2,l_r,h1,h2);
589 
590  // Run the unsteady simulation
591  problem.unsteady_run(t_max,dt);
592 
593 } // End of main
/////////////////////////////////////////////////////////////////////// /////////////////////////////...
Mesh * Surface_mesh_pt
Pointer to the "surface" mesh.
void set_initial_condition()
Set initial conditions.
TwoLayerSpineMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the specific bulk mesh.
void doc_solution(DocInfo &doc_info)
Doc the solution.
void set_boundary_conditions()
Set boundary conditions.
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 deform_free_surface(const double &epsilon, const double &k)
Deform the mesh/free surface to a prescribed function.
void actions_before_newton_convergence_check()
Spine heights/lengths are unknowns in the problem so their values get corrected during each Newton st...
ElasticRefineableTwoLayerMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the (specific) "bulk" mesh.
void actions_before_newton_solve()
No actions required before solve step.
void unsteady_run(const double &t_max, const double &dt)
Do unsteady run up to maximum time t_max with given timestep dt.
void actions_after_newton_solve()
Update after solve can remain empty, because the update is performed automatically after every Newton...
Namespace for physical parameters.
double ReSt
Womersley number (Reynolds x Strouhal)
double Density_Ratio
Ratio of density in upper fluid to density in lower fluid. Reynolds number etc. is based on density i...
double ReInvFr
Product of Reynolds number and inverse of Froude number.
double Viscosity_Ratio
Ratio of viscosity in upper fluid to viscosity in lower fluid. Reynolds number etc....
Vector< double > G(3)
Direction of gravity.
int main(int argc, char *argv[])
/////////////////////////////////////////////////////////////////////// /////////////////////////////...