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
41using namespace std;
42
43using 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//========================================================================
82template<class ELEMENT, class TIMESTEPPER>
83class InterfaceProblem : public Problem
84{
85
86public:
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
115private:
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//========================================================================
149template<class ELEMENT, class TIMESTEPPER>
151InterfaceProblem(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//========================================================================
296template <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//========================================================================
326template <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//========================================================================
363template <class ELEMENT, class TIMESTEPPER>
365deform_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//========================================================================
391template<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//========================================================================
428template<class ELEMENT, class TIMESTEPPER>
430unsteady_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//========================================================================
502int 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.
ofstream Trace_file
Trace file.
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)
double Lx
Width of domain.
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 Ca
Capillary number.
double ReInvFr
Product of Reynolds number and inverse of Froude number.
int main(int argc, char *argv[])
/////////////////////////////////////////////////////////////////////// /////////////////////////////...