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