3d_rayleigh_instability_surfactant.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 //Three-dimensional free-surface test case including insoluble
27 //surfactant transport. This is a 3D implementation of the
28 //axisymmetric Rayleigh--Plateau problem solved in
29 //rayleigh_instability_insoluble_surfactant.cc, which
30 //should be a hard test
31 //because it's implemented in Cartesian coordinates.
32 //The main aim is to test the implementation of the surface transport
33 //equations in 3D (2D surface).
34 
35 // The oomphlib headers
36 #include "generic.h"
37 #include "navier_stokes.h"
38 #include "fluid_interface.h"
39 
40 // The basic mesh
41 #include "meshes/single_layer_cubic_spine_mesh.h"
42 
43 using namespace std;
44 using namespace oomph;
45 
46 
47 //==start_of_namespace==================================================
48 /// Namepspace for global parameters, chosen from Campana et al. as
49 /// in the axisymmetric problem.
50 //======================================================================
52 {
53  //Film thickness parameter
54  double Film_Thickness = 0.2;
55 
56  /// Reynolds number
57  double Re = 40.0;
58 
59  /// Womersley = Reynolds times Strouhal
60  double ReSt = Re;
61 
62  /// Product of Reynolds and Froude number
63  double ReInvFr = 0.0;
64 
65  /// Capillary number
66  double Ca = pow(Film_Thickness,3.0);
67 
68  /// Direction of gravity
69  Vector<double> G(3);
70 
71 // Wavelength of the domain
72  double Alpha = 1.047;
73 
74  /// Free surface cosine deformation parameter
75  double Epsilon = 1.0e-3;
76 
77  /// Surface Elasticity number (weak case)
78  double Beta = 3.6e-3;
79 
80  /// Surface Peclet number
81  double Peclet_S = 4032.0;
82 
83  /// Sufrace Peclet number multiplied by Strouhal number
84  double Peclet_St_S = 1.0;
85 
86 } // End of namespace
87 
88 
89 
90 //=======================================================================
91 /// Function-type-object to perform comparison of elements in y-direction
92 //=======================================================================
94 {
95 public:
96 
97  /// Comparison. Are the values identical or not?
98  bool operator()(GeneralisedElement* const &x, GeneralisedElement* const &y)
99  const
100  {
101  FiniteElement* cast_x = dynamic_cast<FiniteElement*>(x);
102  FiniteElement* cast_y = dynamic_cast<FiniteElement*>(y);
103 
104  if((cast_x ==0) || (cast_y==0)) {return 0;}
105  else
106  {return (cast_x->node_pt(0)->x(1)< cast_y->node_pt(0)->x(1));}
107 
108  }
109 };
110 
111 
112 namespace oomph
113 {
114 //===================================================================
115 /// Deform the existing cubic spine mesh into a annular section
116 /// with spines directed radially inwards from the wall
117 //===================================================================
118 
119 template<class ELEMENT>
120 class AnnularSpineMesh : public SingleLayerCubicSpineMesh<ELEMENT>
121 {
122 
123 public:
124  AnnularSpineMesh(const unsigned &n_r, const unsigned &n_y,
125  const unsigned &n_theta,
126  const double &r_min, const double &r_max,
127  const double &l_y,
128  const double &theta_min, const double &theta_max,
129  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper):
130  //This will make a cubic mesh with n_r in the x-direction
131  //n_y in the y-direction and n_theta in the z-direction
132  //The coordinates will run from 0 to r_max, 0 to l_y and 0 to theta_max
133  SingleLayerCubicSpineMesh<ELEMENT>(n_theta,n_y,n_r,r_max,l_y,theta_max,
134  time_stepper_pt)
135  {
136 
137  //Find out how many nodes there are
138  unsigned n_node = this->nnode();
139 
140  //Loop over all nodes
141  for (unsigned n=0;n<n_node;n++)
142  {
143  //pointer to node
144  Node* nod_pt=this->node_pt(n);
145  SpineNode* spine_node_pt=dynamic_cast<SpineNode*>(nod_pt);
146  //Get x/y/z coordinates
147  double x_old=nod_pt->x(0);
148  double y_old=nod_pt->x(1);
149  double z_old=nod_pt->x(2);
150 
151  //Mapping
152 
153  double r = r_min + (r_max-r_min)*z_old;
154  double theta = (theta_min + (theta_max-theta_min)*x_old);
155  double y = y_old;
156 
157 
158  if(spine_node_pt->spine_pt()->ngeom_parameter()==0)
159  {
160  spine_node_pt->h() =
162  spine_node_pt->spine_pt()->add_geom_parameter(theta);
163  }
164 
165  //cout << spine_node_pt->spine_pt()->ngeom_parameter() << std::endl;
166  //Set new nodal coordinates
167  nod_pt->x(0)=r*cos(theta);
168  nod_pt->x(2)=r*sin(theta);
169  nod_pt->x(1)=y;
170  //cout << "one" << theta << std::endl;
171  }
172 
173  }
174 
175 
176  virtual void spine_node_update(SpineNode* spine_node_pt)
177  {
178  //Get fraction along the spine
179  double W = spine_node_pt->fraction();
180  //Get theta along the spine
181  double theta = spine_node_pt->spine_pt()->geom_parameter(0);
182  //Get spine height
183  double H = spine_node_pt->h();
184  //Set the value of y
185  spine_node_pt->x(0) = (1.0-W*H)*cos(theta);
186  spine_node_pt->x(2) = (1.0-W*H)*sin(theta);
187  }
188 };
189 }
190 
191 //======================================================================
192 /// Single fluid interface problem including transport of an
193 /// insoluble surfactant.
194 //======================================================================
195 template<class ELEMENT, class TIMESTEPPER>
196 class InterfaceProblem : public Problem
197 {
198 
199 public:
200 
201  /// Constructor: Pass number of elements in x and y directions. Also lengths
202  /// of the domain in x- and y-directions and the height of the layer
203 
204  InterfaceProblem(const unsigned &n_r, const unsigned &n_y,
205  const unsigned &n_theta, const double &r_min,
206  const double &r_max, const double &l_y,
207  const double &theta_max);
208 
209  /// Spine heights/lengths are unknowns in the problem so their
210  /// values get corrected during each Newton step. However,
211  /// changing their value does not automatically change the
212  /// nodal positions, so we need to update all of them
213  void actions_before_newton_convergence_check(){Bulk_mesh_pt->node_update();}
214 
215  /// Run an unsteady simulation with specified number of steps
216  void unsteady_run(const unsigned& nstep);
217 
218  /// Doc the solution
219  void doc_solution(DocInfo& doc_info);
220 
221  /// Compute the total mass
223  {
224  double mass = 0.0;
225 
226  // Determine number of 1D interface elements in mesh
227  const unsigned n_interface_element = Surface_mesh_pt->nelement();
228 
229  // Loop over the interface elements
230  for(unsigned e=0;e<n_interface_element;e++)
231  {
232  // Upcast from GeneralisedElement to the present element
235  (Surface_mesh_pt->element_pt(e));
236 
237  mass += el_pt->integrate_c();
238  }
239  return mass;
240  }
241 
242 
243 
244  private:
245 
246  /// Trace file
247  ofstream Trace_file;
248 
249  /// Axial lengths of domain
250  double R_max;
251 
252  double L_y;
253 
254  /// Pointer to bulk mesh
256 
257  /// Pointer to the surface mes
259 
260  /// Pointer to a node for documentation purposes
262 
263 };
264 
265 
266 //====================================================================
267 /// Problem constructor
268 //====================================================================
269 template<class ELEMENT, class TIMESTEPPER>
271 (const unsigned &n_r, const unsigned &n_y,const unsigned &n_theta,
272  const double &r_min,
273  const double &r_max, const double &l_y, const double &theta_max)
274  : R_max(r_max), L_y(l_y)
275 {
276  //this->linear_solver_pt() = new HSL_MA42;
277 
278  //static_cast<HSL_MA42*>(this->linear_solver_pt())->lenbuf_factor0() = 3.0;
279  //static_cast<HSL_MA42*>(this->linear_solver_pt())->lenbuf_factor1() = 3.0;
280  //static_cast<HSL_MA42*>(this->linear_solver_pt())->lenbuf_factor2() = 3.0;
281 
282  //Allocate the timestepper
283  add_time_stepper_pt(new TIMESTEPPER);
284 
285  //Now create the bulk mesh -- this should be your new annular mesh
287  (n_r,n_y,n_theta,r_min,r_max,l_y,0.0,theta_max,time_stepper_pt());
288 
289  //Set the documented node
290  Document_node_pt = Bulk_mesh_pt->boundary_node_pt(5,0);
291 
292 
293  //Create the surface mesh that will contain the interface elements
294  //First create storage, but with no elements or nodes
295  Surface_mesh_pt = new Mesh;
296 
297  // Loop over those elements adjacent to the free surface,
298  // which we shall choose to be the upper surface
299  for(unsigned e1=0;e1<n_y;e1++)
300  {
301  for(unsigned e2=0;e2<n_theta;e2++)
302  {
303  // Set a pointer to the bulk element we wish to our interface
304  // element to
305  FiniteElement* bulk_element_pt =
306  Bulk_mesh_pt->finite_element_pt(n_theta*n_y*(n_r-1) + e2 + e1*n_theta);
307 
308  // Create the interface element (on face 3 of the bulk element)
309  FiniteElement* interface_element_pt =
311 
312  // Add the interface element to the surface mesh
313  this->Surface_mesh_pt->add_element_pt(interface_element_pt);
314  }
315  }
316 
317  // Add the two sub-meshes to the problem
318  add_sub_mesh(Bulk_mesh_pt);
319  add_sub_mesh(Surface_mesh_pt);
320 
321  // Combine all sub-meshes into a single mesh
322  build_global_mesh();
323 
324  //Pin all nodes on the bottom
325  unsigned long n_boundary_node = Bulk_mesh_pt->nboundary_node(0);
326  for(unsigned long n=0;n<n_boundary_node;n++)
327  {
328  for(unsigned i=0;i<3;i++)
329  {
330  Bulk_mesh_pt->boundary_node_pt(0,n)->pin(i);
331  }
332  }
333 
334  //On the front and back (y=const) pin in y-direction
335  for(unsigned b=1;b<4;b+=2)
336  {
337  n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
338  for(unsigned long n=0;n<n_boundary_node;n++)
339  {
340  Bulk_mesh_pt->boundary_node_pt(b,n)->pin(1);
341  }
342  }
343 
344  //On sides pin in z-direction
345  for(unsigned b=4;b<5;b+=2)
346  {
347  n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
348  for(unsigned long n=0;n<n_boundary_node;n++)
349  {
350  Bulk_mesh_pt->boundary_node_pt(b,n)->pin(2);
351  }
352  }
353 
354  // pinning the top surface
355  for(unsigned b=2;b<3;b++)
356  {
357  n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
358  for(unsigned long n=0;n<n_boundary_node;n++)
359  {
360  Bulk_mesh_pt->boundary_node_pt(b,n)->pin(0);
361  }
362  }
363 
364  //Loop over the upper surface and set the surface concentration
365  {
366  unsigned b=5;
367  n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
368  for(unsigned long n=0;n<n_boundary_node;n++)
369  {
370  Bulk_mesh_pt->boundary_node_pt(b,n)->set_value(3,1.0);;
371  }
372  }
373 
374 
375  //Create a Data object whose single value stores the
376  //external pressure
377  Data* external_pressure_data_pt = new Data(1);
378 
379  // Set and pin the external pressure to some random value
380  external_pressure_data_pt->set_value(0,1.31);
381  external_pressure_data_pt->pin(0);
382 
383  //Complete the problem setup to make the elements fully functional
384 
385  //Loop over the elements in the layer
386  unsigned n_bulk=Bulk_mesh_pt->nelement();
387  for(unsigned e=0;e<n_bulk;e++)
388  {
389  //Cast to a fluid element
390  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
391 
392  //Set the Reynolds number, etc
393  el_pt->re_pt() = &Global_Physical_Variables::Re;
394  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
395  el_pt->re_invfr_pt() = &Global_Physical_Variables::ReInvFr;
396  el_pt->g_pt() = &Global_Physical_Variables::G;
397  }
398 
399  //Loop over 2D interface elements and set capillary number and
400  //the external pressure
401  unsigned long interface_element_pt_range =
402  Surface_mesh_pt->nelement();
403  for(unsigned e=0;e<interface_element_pt_range;e++)
404  {
405  //Cast to a interface element
408  (Surface_mesh_pt->element_pt(e));
409 
410  //Set the Capillary number
412 
413  //Pass the Data item that contains the single external pressure value
414  el_pt->set_external_pressure_data(external_pressure_data_pt);
415 
416  // Set the surface elasticity number
418 
419  // Set the surface peclect number
421 
422  // Set the surface peclect number multiplied by strouhal number
424 
425  }
426 
427  //Do equation numbering
428  cout << assign_eqn_numbers() << std::endl;
429 
430  //Now sort the elements to minimise frontwidth
431  std::sort(mesh_pt()->element_pt().begin(),
432  mesh_pt()->element_pt().end(),
433  ElementCmp());
434 
435 }
436 
437 
438 
439 //========================================================================
440 /// Doc the solution
441 //========================================================================
442 template<class ELEMENT, class TIMESTEPPER>
444 {
445 
446  ofstream some_file;
447  char filename[100];
448 
449  // Number of plot points
450  unsigned npts=5;
451 
452  //Output the time
453  cout << "Time is now " << time_pt()->time() << std::endl;
454 
455  // Doc
456  Trace_file << time_pt()->time();
457  Trace_file << " " << Document_node_pt->x(0) << " "
458  << this->compute_total_mass()
459  << std::endl;
460 
461 
462  // Output solution, bulk elements followed by surface elements
463  /*sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
464  doc_info.number());
465  some_file.open(filename);
466  Bulk_mesh_pt->output(some_file,npts);
467  some_file.close();*/
468  //Change the file name and output surface in separate file
469  sprintf(filename,"%s/surface%i.dat",doc_info.directory().c_str(),
470  doc_info.number());
471  some_file.open(filename);
472  Surface_mesh_pt->output(some_file,npts);
473  some_file.close();
474 
475 }
476 
477 
478 
479 
480  //=============================================================================
481 /// Unsteady run with specified number of steps
482 //=============================================================================
483 template<class ELEMENT, class TIMESTEPPER>
485 {
486 
487  // Increase maximum residual
488  Problem::Max_residuals=500.0;
489 
490  //Distort the mesh
491  const double epsilon=Global_Physical_Variables::Epsilon;
492  unsigned Nspine = Bulk_mesh_pt->nspine();
493  for(unsigned i=0;i<Nspine;i++)
494  {
495  double y_value = Bulk_mesh_pt->boundary_node_pt(0,i)->x(1);
496 
497  Bulk_mesh_pt->spine_pt(i)->height() =
499  + epsilon*cos(Global_Physical_Variables::Alpha*y_value));
500  }
501 
502  //Make sure to update
503  Bulk_mesh_pt->node_update();
504 
505  // Doc info object
506  DocInfo doc_info;
507 
508  // Set output directory
509  doc_info.set_directory("RESLT");
510  doc_info.number()=0;
511 
512  // Open trace file
513  char filename[100];
514  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
515  Trace_file.open(filename);
516 
517  Trace_file << "VARIABLES=\"time\",";
518  Trace_file << "\"h<sub>left</sub>\",\"h<sub>right</sub>\"";
519  Trace_file << "\n";
520 
521  //Set value of dt
522  double dt = 0.1;
523 
524  //Initialise all time values
525  assign_initial_values_impulsive(dt);
526 
527  //Doc initial solution
528  doc_solution(doc_info);
529 
530 //Loop over the timesteps
531  for(unsigned t=1;t<=nstep;t++)
532  {
533  cout << "TIMESTEP " << t << std::endl;
534 
535  //Take one fixed timestep
536  unsteady_newton_solve(dt);
537 
538  // Doc solution
539  doc_info.number()++;
540  doc_solution(doc_info);
541  }
542 
543 }
544 
545 
546 //==start_of_main========================================================
547 /// Driver code for unsteady two-layer fluid problem. If there are
548 /// any command line arguments, we regard this as a validation run
549 /// and perform only two steps.
550 
551 // In my version we will change nsteps in the programs
552 //======================================================================
553 int main(int argc, char *argv[])
554 {
555 
556  // Set physical parameters:
557 
558  //Set direction of gravity: Vertically downwards
562 
563  //Set the film thickness
565 
566  //Axial lngth of domain
567  double r_max = 1.0;
568  double r_min = r_max - h;
569 
570  const double pi = MathematicalConstants::Pi;
571 
572  double alpha = Global_Physical_Variables::Alpha;
573 
574  double l_y = pi/alpha;
575 
576  double theta_max = 0.5*pi;
577 
578  // Number of elements in r and theta direction
579  unsigned n_r = 4;
580 
581  unsigned n_theta = 4;
582 
583  // Number of elements in axial direction
584  unsigned n_y = 20;
585 
586 
587  {
588  //Set up the spine test problem
589  //The minimum values are all assumed to be zero.
591  problem(n_r,n_y,n_theta,r_min,r_max,l_y,theta_max);
592 
593  // Number of steps:
594  unsigned nstep;
595  if(argc > 1)
596  {
597  // Validation run: Just five steps
598  nstep=5;
599  }
600  else
601  {
602  // Full run otherwise
603  nstep=1000;
604  }
605 
606  // Run the unsteady simulation
607  problem.unsteady_run(nstep);
608  }
609 } // End of main
610 
611 
int main(int argc, char *argv[])
Driver code for unsteady two-layer fluid problem. If there are any command line arguments,...
Function-type-object to perform comparison of elements in y-direction.
bool operator()(GeneralisedElement *const &x, GeneralisedElement *const &y) const
Comparison. Are the values identical or not?
Single fluid interface problem including transport of an insoluble surfactant.
Mesh * Surface_mesh_pt
Pointer to the surface mes.
void unsteady_run(const unsigned &nstep)
Run an unsteady simulation with specified number of steps.
InterfaceProblem(const unsigned &n_r, const unsigned &n_y, const unsigned &n_theta, const double &r_min, const double &r_max, const double &l_y, const double &theta_max)
Constructor: Pass number of elements in x and y directions. Also lengths of the domain in x- and y-di...
AnnularSpineMesh< ELEMENT > * Bulk_mesh_pt
Pointer to bulk mesh.
void doc_solution(DocInfo &doc_info)
Doc the solution.
Node * Document_node_pt
Pointer to a node for documentation purposes.
double R_max
Axial lengths of domain.
double compute_total_mass()
Compute the total mass.
void actions_before_newton_convergence_check()
Spine heights/lengths are unknowns in the problem so their values get corrected during each Newton st...
Deform the existing cubic spine mesh into a annular section with spines directed radially inwards fro...
virtual void spine_node_update(SpineNode *spine_node_pt)
AnnularSpineMesh(const unsigned &n_r, const unsigned &n_y, const unsigned &n_theta, const double &r_min, const double &r_max, const double &l_y, const double &theta_min, const double &theta_max, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
double *& ca_pt()
Pointer to the Capillary number.
void set_external_pressure_data(Data *external_pressure_data_pt)
Set the Data that contains the single pressure value that specifies the "external pressure" for the i...
double *& peclet_s_pt()
Access function for pointer to the surface Peclet number.
double *& beta_pt()
Access function for pointer to the Elasticity number.
double integrate_c()
Compute the concentration intergated over the surface area.
double *& peclet_strouhal_s_pt()
Access function for pointer to the surface Peclet x Strouhal number.
Namepspace for global parameters, chosen from Campana et al. as in the axisymmetric problem.
double ReSt
Womersley = Reynolds times Strouhal.
double Epsilon
Free surface cosine deformation parameter.
double Beta
Surface Elasticity number (weak case)
double Alpha
Wavelength of the domain.
double ReInvFr
Product of Reynolds and Froude number.
double Peclet_St_S
Sufrace Peclet number multiplied by Strouhal number.
Vector< double > G(3)
Direction of gravity.