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