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-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//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
43using namespace std;
44using 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{
95public:
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
112namespace 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
119template<class ELEMENT>
120class AnnularSpineMesh : public SingleLayerCubicSpineMesh<ELEMENT>
121{
122
123public:
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//======================================================================
195template<class ELEMENT, class TIMESTEPPER>
196class InterfaceProblem : public Problem
197{
198
199public:
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
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//====================================================================
269template<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
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
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)
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
320
321 // Combine all sub-meshes into a single 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
378
379 // Set and pin the external pressure to some random value
380 external_pressure_data_pt->set_value(0,1.31);
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
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
423 el_pt->peclet_strouhal_s_pt() = &Global_Physical_Variables::Peclet_St_S;
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//========================================================================
442template<class ELEMENT, class TIMESTEPPER>
444{
445
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 /*snprintf(filename, sizeof(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 snprintf(filename, sizeof(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//=============================================================================
483template<class ELEMENT, class TIMESTEPPER>
485{
486
487 // Increase maximum residual
488 Problem::Max_residuals=500.0;
489
490 //Distort the mesh
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() =
500 }
501
502 //Make sure to update
503 Bulk_mesh_pt->node_update();
504
505 // Doc info object
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 snprintf(filename, sizeof(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
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
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//======================================================================
553int 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
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.
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 integrate_c()
Compute the concentration intergated over the surface area.
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.