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-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 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
45using namespace std;
46
47using 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
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//========================================================================
93template <class ELEMENT, class TIMESTEPPER>
94class InterfaceProblem : public Problem
95{
96
97public:
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
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
122private:
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))->
151 }
152
153 /// Width of domain
154 double Lr;
155
156 /// Trace file
158
159
160 /// Pointer to the specific bulk mesh
162
163 /// Pointer to the surface mesh
165
166
167}; // End of problem class
168
169
170
171//==start_of_constructor==================================================
172/// Constructor for axisymmetric two fluid interface problem
173//========================================================================
174template <class ELEMENT, class TIMESTEPPER>
176InterfaceProblem(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)
183
184 // Build and assign mesh
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
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->
249
250 // Set the Reynolds number
252
253 // Set the Womersley number
255
256 // Set the product of the Reynolds number and the inverse of the
257 // Froude number
259
260 // Set the direction of gravity
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->
271
272 // Set the Reynolds number
274
275 // Set the Womersley number
277
278 // Set the product of the Reynolds number and the inverse of the
279 // Froude number
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
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
305 (Surface_mesh_pt->element_pt(e));
306
307 // Set the Strouhal number
309
310 // Set the Capillary number
312
313 } // End of loop over interface elements
314
315 // Apply the boundary conditions
317
318
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//========================================================================
337template <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
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//========================================================================
367template <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//========================================================================
399template <class ELEMENT, class TIMESTEPPER>
401deform_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
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//========================================================================
432template <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
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 snprintf(filename, sizeof(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//========================================================================
469template <class ELEMENT, class TIMESTEPPER>
471unsteady_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
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 snprintf(filename, sizeof(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
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
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//========================================================================
543int main(int argc, char* argv[])
544{
545 // Store command line arguments
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> >
589
590 // Run the unsteady simulation
591 problem.unsteady_run(t_max,dt);
592
593} // End of main
Two layer mesh which employs a pseudo-solid node-update strategy. This class is essentially a wrapper...
ElasticRefineableTwoLayerMesh(const unsigned &nx, const unsigned &ny1, const unsigned &ny2, const double &lx, const double &h1, const double &h2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass number of elements in x-direction, number of elements in y-direction in bottom and ...
Axisymmetric two fluid interface problem in a rectangular domain.
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[])
Driver code for axisymmetric two fluid interface problem.