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