elastic_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-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// Driver for two-dimensional single fluid free surface problem, where the
27// mesh is deformed using a pseudo-solid 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// Constitutive law headers
39#include "constitutive.h"
40
41// Solid headers
42#include "solid.h"
43
44// The mesh
45#include "meshes/rectangular_quadmesh.h"
46
47using namespace std;
48
49using namespace oomph;
50
51
52//==start_of_namespace====================================================
53/// Namespace for physical parameters
54//========================================================================
56{
57
58 /// Reynolds number
59 double Re = 5.0;
60
61 /// Strouhal number
62 double St = 1.0;
63
64 /// Womersley number (Reynolds x Strouhal, computed automatically)
65 double ReSt;
66
67 /// Product of Reynolds number and inverse of Froude number
68 double ReInvFr = 5.0; // (Fr = 1)
69
70 /// Capillary number
71 double Ca = 0.01;
72
73 /// Direction of gravity
74 Vector<double> G(2);
75
76 /// Pseudo-solid Poisson ratio
77 double Nu = 0.1;
78
79} // End of namespace
80
81
82//////////////////////////////////////////////////////////////////////////
83//////////////////////////////////////////////////////////////////////////
84//////////////////////////////////////////////////////////////////////////
85
86
87//==start_of_problem_class================================================
88/// Single fluid free surface problem in a rectangular domain which is
89/// periodic in the x direction
90//========================================================================
91template <class ELEMENT, class TIMESTEPPER>
92class InterfaceProblem : public Problem
93{
94
95public:
96
97 /// Constructor: Pass the number of elements and the lengths of the
98 /// domain in the x and y directions (h is the height of the fluid layer
99 /// i.e. the length of the domain in the y direction)
100 InterfaceProblem(const unsigned &n_x, const unsigned &n_y,
101 const double &l_x, const double &h);
102
103 /// Destructor (empty)
105
106 /// Set initial conditions
108
109 /// Set boundary conditions
111
112 /// Document the solution
113 void doc_solution(DocInfo &doc_info);
114
115 /// Do unsteady run up to maximum time t_max with given timestep dt
116 void unsteady_run(const double &t_max, const double &dt);
117
118private:
119
120 /// No actions required before solve step
122
123 /// No actions required after solve step
125
126 /// Actions before the timestep: For maximum stability, reset
127 /// the current nodal positions to be the "stress-free" ones.
129 {
130 Bulk_mesh_pt->set_lagrangian_nodal_coordinates();
131 }
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 /// Pointer to the (specific) "bulk" mesh
137 ElasticRectangularQuadMesh<ELEMENT>* Bulk_mesh_pt;
138
139 /// Pointer to the "surface" mesh
141
142 // Pointer to the constitutive law used to determine the mesh deformation
143 ConstitutiveLaw* Constitutive_law_pt;
144
145 /// Width of domain
146 double Lx;
147
148 /// Trace file
149 ofstream Trace_file;
150
151}; // End of problem class
152
153
154
155//==start_of_constructor==================================================
156/// Constructor for single fluid free surface problem
157//========================================================================
158template <class ELEMENT, class TIMESTEPPER>
160InterfaceProblem(const unsigned &n_x, const unsigned &n_y,
161 const double &l_x, const double& h) : Lx(l_x)
162{
163
164 // Allocate the timestepper (this constructs the time object as well)
165 add_time_stepper_pt(new TIMESTEPPER);
166
167 // Build and assign the "bulk" mesh (the "true" boolean flag tells
168 // the mesh constructor that the domain is periodic in x)
169 Bulk_mesh_pt = new ElasticRectangularQuadMesh<ELEMENT>
170 (n_x,n_y,l_x,h,true,time_stepper_pt());
171
172 // Create the "surface mesh" that will contain only the interface
173 // elements. The constructor just creates the mesh without giving
174 // it any elements, nodes, etc.
175 Surface_mesh_pt = new Mesh;
176
177 // -----------------------------
178 // Create the interface elements
179 // -----------------------------
180
181 // Loop over those elements adjacent to the free surface
182 for(unsigned e=0;e<n_x;e++)
183 {
184 // Set a pointer to the bulk element we wish to our interface
185 // element to
186 FiniteElement* bulk_element_pt =
187 Bulk_mesh_pt->finite_element_pt(n_x*(n_y-1)+e);
188
189 // Create the interface element (on face 2 of the bulk element)
190 FiniteElement* interface_element_pt =
191 new ElasticLineFluidInterfaceElement<ELEMENT>(bulk_element_pt,2);
192
193 // Add the interface element to the surface mesh
194 this->Surface_mesh_pt->add_element_pt(interface_element_pt);
195 }
196
197 // Add the two sub-meshes to the problem
198 add_sub_mesh(Bulk_mesh_pt);
199 add_sub_mesh(Surface_mesh_pt);
200
201 // Combine all sub-meshes into a single mesh
202 build_global_mesh();
203
204 // --------------------------------------------
205 // Set the boundary conditions for this problem
206 // --------------------------------------------
207
208 // All nodes are free by default -- just pin the ones that have
209 // Dirichlet conditions here
210
211 // Determine number of mesh boundaries
212 const unsigned n_boundary = Bulk_mesh_pt->nboundary();
213
214 // Loop over mesh boundaries
215 for(unsigned b=0;b<n_boundary;b++)
216 {
217 // Determine number of nodes on boundary b
218 const unsigned n_node = Bulk_mesh_pt->nboundary_node(b);
219
220 // Loop over nodes on boundary b
221 for(unsigned n=0;n<n_node;n++)
222 {
223 // Fluid boundary conditions:
224 // --------------------------
225
226 // On lower boundary (solid wall), pin x and y components of
227 // the velocity (no slip/penetration)
228 if(b==0)
229 {
230 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(0);
231 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(1);
232 }
233
234 // On left and right boundaries, pin x-component of the velocity
235 // (no penetration of the periodic boundaries)
236 if(b==1 || b==3)
237 {
238 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(0);
239 }
240
241 // Solid boundary conditions:
242 // --------------------------
243
244 // On lower boundary (solid wall), pin vertical displacement
245 // (no penetration)
246 if(b==0)
247 {
248 Bulk_mesh_pt->boundary_node_pt(b,n)->pin_position(1);
249 }
250 } // End of loop over nodes on boundary b
251 } // End of loop over mesh boundaries
252
253 // Pin horizontal displacement of all nodes
254 const unsigned n_node = Bulk_mesh_pt->nnode();
255 for(unsigned n=0;n<n_node;n++) { Bulk_mesh_pt->node_pt(n)->pin_position(0); }
256
257 // Define a constitutive law for the solid equations: generalised Hookean
258 Constitutive_law_pt = new GeneralisedHookean(&Global_Physical_Variables::Nu);
259
260 // ----------------------------------------------------------------
261 // Complete the problem setup to make the elements fully functional
262 // ----------------------------------------------------------------
263
264 // Determine number of bulk elements in mesh
265 const unsigned n_element_bulk = Bulk_mesh_pt->nelement();
266
267 // Loop over the bulk elements
268 for(unsigned e=0;e<n_element_bulk;e++)
269 {
270 // Upcast from GeneralisedElement to the present element
271 ELEMENT *el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
272
273 // Set the Reynolds number
274 el_pt->re_pt() = &Global_Physical_Variables::Re;
275
276 // Set the Womersley number
277 el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
278
279 // Set the product of the Reynolds number and the inverse of the
280 // Froude number
281 el_pt->re_invfr_pt() = &Global_Physical_Variables::ReInvFr;
282
283 // Set the direction of gravity
284 el_pt->g_pt() = &Global_Physical_Variables::G;
285
286 // Set the constitutive law
287 el_pt->constitutive_law_pt() = Constitutive_law_pt;
288
289 } // End of loop over bulk elements
290
291 // Create a Data object whose single value stores the external pressure
292 Data* external_pressure_data_pt = new Data(1);
293
294 // Pin and set the external pressure to some arbitrary value
295 external_pressure_data_pt->pin(0);
296 external_pressure_data_pt->set_value(0,1.31);
297
298 // Determine number of 1D interface elements in mesh
299 const unsigned n_interface_element = Surface_mesh_pt->nelement();
300
301 // Loop over the interface elements
302 for(unsigned e=0;e<n_interface_element;e++)
303 {
304 // Upcast from GeneralisedElement to the present element
305 ElasticLineFluidInterfaceElement<ELEMENT>* el_pt =
306 dynamic_cast<ElasticLineFluidInterfaceElement<ELEMENT>*>
307 (Surface_mesh_pt->element_pt(e));
308
309 // Set the Strouhal number
310 el_pt->st_pt() = &Global_Physical_Variables::St;
311
312 // Set the Capillary number
313 el_pt->ca_pt() = &Global_Physical_Variables::Ca;
314
315 // Pass the Data item that contains the single external pressure value
316 el_pt->set_external_pressure_data(external_pressure_data_pt);
317
318 } // End of loop over interface elements
319
320 // Apply the boundary conditions
322
323 // Setup equation numbering scheme
324 cout << "Number of equations: " << assign_eqn_numbers() << std::endl;
325
326} // End of constructor
327
328
329
330//==start_of_set_initial_condition========================================
331/// Set initial conditions: Set all nodal velocities to zero and
332/// initialise the previous velocities and nodal positions to correspond
333/// to an impulsive start
334//========================================================================
335template <class ELEMENT, class TIMESTEPPER>
337{
338 // Determine number of nodes in mesh
339 const unsigned n_node = mesh_pt()->nnode();
340
341 // Loop over all nodes in mesh
342 for(unsigned n=0;n<n_node;n++)
343 {
344 // Loop over the two velocity components
345 for(unsigned i=0;i<2;i++)
346 {
347 // Set velocity component i of node n to zero
348 mesh_pt()->node_pt(n)->set_value(i,0.0);
349 }
350 }
351
352 // Initialise the previous velocity values and nodal positions
353 // for timestepping corresponding to an impulsive start
354 assign_initial_values_impulsive();
355
356} // End of set_initial_condition
357
358
359
360//==start_of_set_boundary_conditions======================================
361/// Set boundary conditions: Set both velocity components to zero
362/// on the bottom (solid) wall and the horizontal component only to zero
363/// on the side (periodic) boundaries
364//========================================================================
365template <class ELEMENT, class TIMESTEPPER>
367{
368 // Determine number of mesh boundaries
369 const unsigned n_boundary = Bulk_mesh_pt->nboundary();
370
371 // Loop over mesh boundaries
372 for(unsigned b=0;b<n_boundary;b++)
373 {
374 // Determine number of nodes on boundary b
375 const unsigned n_node = Bulk_mesh_pt->nboundary_node(b);
376
377 // Loop over nodes on boundary b
378 for(unsigned n=0;n<n_node;n++)
379 {
380 // Set x-component of the velocity to zero on all boundaries
381 // other than the free surface
382 if(b!=2)
383 {
384 Bulk_mesh_pt->boundary_node_pt(b,n)->set_value(0,0.0);
385 }
386
387 // Set y-component of the velocity to zero on the bottom wall
388 if(b==0)
389 {
390 Bulk_mesh_pt->boundary_node_pt(b,n)->set_value(1,0.0);
391 }
392 } // End of loop over nodes on boundary b
393 } // End of loop over mesh boundaries
394
395} // End of set_boundary_conditions
396
397
398
399//==start_of_deform_free_surface==========================================
400/// Deform the mesh/free surface to a prescribed function
401//========================================================================
402template <class ELEMENT, class TIMESTEPPER>
404deform_free_surface(const double &epsilon,const unsigned &n_periods)
405{
406 // Determine number of nodes in the "bulk" mesh
407 const unsigned n_node = Bulk_mesh_pt->nnode();
408
409 // Loop over all nodes in mesh
410 for(unsigned n=0;n<n_node;n++)
411 {
412 // Determine eulerian position of node
413 const double current_x_pos = Bulk_mesh_pt->node_pt(n)->x(0);
414 const double current_y_pos = Bulk_mesh_pt->node_pt(n)->x(1);
415
416 // Determine new vertical position of node
417 const double new_y_pos = current_y_pos
418 + (1.0-fabs(1.0-current_y_pos))*epsilon
419 *(cos(2.0*n_periods*MathematicalConstants::Pi*current_x_pos/Lx));
420
421 // Set new position
422 Bulk_mesh_pt->node_pt(n)->x(1) = new_y_pos;
423 }
424} // End of deform_free_surface
425
426
427
428//==start_of_doc_solution=================================================
429/// Document the solution
430//========================================================================
431template <class ELEMENT, class TIMESTEPPER>
433{
434
435 // Output the time
436 cout << "Time is now " << time_pt()->time() << std::endl;
437
438 // Upcast from GeneralisedElement to the present element
439 ElasticLineFluidInterfaceElement<ELEMENT>* el_pt =
440 dynamic_cast<ElasticLineFluidInterfaceElement<ELEMENT>*>
441 (Surface_mesh_pt->element_pt(0));
442
443 // Document time and vertical position of left hand side of interface
444 // in trace file
445 Trace_file << time_pt()->time() << " "
446 << el_pt->node_pt(0)->x(1) << std::endl;
447
448 ofstream some_file;
449 char filename[100];
450
451 // Set number of plot points (in each coordinate direction)
452 const unsigned npts = 5;
453
454 // Open solution output file
455 snprintf(filename, sizeof(filename), "%s/soln%i.dat",
456 doc_info.directory().c_str(),doc_info.number());
457 some_file.open(filename);
458
459 // Output solution to file
460 Bulk_mesh_pt->output(some_file,npts);
461
462 // Close solution output file
463 some_file.close();
464
465 // Open interface solution output file
466 snprintf(filename, sizeof(filename), "%s/interface_soln%i.dat",
467 doc_info.directory().c_str(),doc_info.number());
468 some_file.open(filename);
469
470 // Output solution to file
471 Surface_mesh_pt->output(some_file,npts);
472
473 // Close solution output file
474 some_file.close();
475
476} // End of doc_solution
477
478
479
480//==start_of_unsteady_run=================================================
481/// Perform run up to specified time t_max with given timestep dt
482//========================================================================
483template <class ELEMENT, class TIMESTEPPER>
485unsteady_run(const double &t_max, const double &dt)
486{
487
488 // Set value of epsilon
489 const double epsilon = 0.1;
490
491 // Set number of periods for cosine term
492 const unsigned n_periods = 1;
493
494 // Deform the mesh/free surface
495 deform_free_surface(epsilon,n_periods);
496
497 // Initialise DocInfo object
498 DocInfo doc_info;
499
500 // Set output directory
501 doc_info.set_directory("RESLT");
502
503 // Initialise counter for solutions
504 doc_info.number()=0;
505
506 // Open trace file
507 char filename[100];
508 snprintf(filename, sizeof(filename), "%s/trace.dat",doc_info.directory().c_str());
509 Trace_file.open(filename);
510
511 // Initialise trace file
512 Trace_file << "time, free surface height" << std::endl;
513
514 // Initialise timestep
515 initialise_dt(dt);
516
517 // Set initial conditions
518 set_initial_condition();
519
520 // Determine number of timesteps
521 const unsigned n_timestep = unsigned(t_max/dt);
522
523 // Doc initial solution
524 doc_solution(doc_info);
525
526 // Increment counter for solutions
527 doc_info.number()++;
528
529 // Timestepping loop
530 for(unsigned t=1;t<=n_timestep;t++)
531 {
532 // Output current timestep to screen
533 cout << "\nTimestep " << t << " of " << n_timestep << std::endl;
534
535 // Take one fixed timestep
536 unsteady_newton_solve(dt);
537
538 // Doc solution
539 doc_solution(doc_info);
540
541 // Increment counter for solutions
542 doc_info.number()++;
543
544 } // End of timestepping loop
545
546} // End of unsteady_run
547
548
549//////////////////////////////////////////////////////////////////////////
550//////////////////////////////////////////////////////////////////////////
551//////////////////////////////////////////////////////////////////////////
552
553
554//==start_of_main=========================================================
555/// Driver code for two-dimensional single fluid free surface problem
556//========================================================================
557int main(int argc, char* argv[])
558{
559 // Store command line arguments
560 CommandLineArgs::setup(argc,argv);
561
562 // Compute the Womersley number
565
566 /// Maximum time
567 double t_max = 0.6;
568
569 /// Duration of timestep
570 const double dt = 0.0025;
571
572 // If we are doing validation run, use smaller number of timesteps
573 if(CommandLineArgs::Argc>1) { t_max = 0.005; }
574
575 // Number of elements in x direction
576 const unsigned n_x = 12;
577
578 // Number of elements in y direction
579 const unsigned n_y = 12;
580
581 // Width of domain
582 const double l_x = 1.0;
583
584 // Height of fluid layer
585 const double h = 1.0;
586
587 // Set direction of gravity (vertically downwards)
590
591 // Set up the elastic test problem with QCrouzeixRaviartElements,
592 // using the BDF<2> timestepper
594 QPVDElement<2,3> > , BDF<2> >
595 problem(n_x,n_y,l_x,h);
596
597 // Run the unsteady simulation
598 problem.unsteady_run(t_max,dt);
599
600} // End of main
Single fluid free surface problem in a rectangular domain which is periodic in the x direction.
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.
ofstream Trace_file
Trace file.
void doc_solution(DocInfo &doc_info)
Document the solution.
ConstitutiveLaw * Constitutive_law_pt
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_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_before_implicit_timestep()
Actions before the timestep: For maximum stability, reset the current nodal positions to be the "stre...
void actions_after_newton_solve()
No actions required after solve step.
ElasticRectangularQuadMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the (specific) "bulk" mesh.
int main(int argc, char *argv[])
Driver code for two-dimensional single fluid free surface problem.
Namespace for physical parameters.
double ReSt
Womersley number (Reynolds x Strouhal, computed automatically)
Vector< double > G(2)
Direction of gravity.
double Nu
Pseudo-solid Poisson ratio.
double Ca
Capillary number.
double ReInvFr
Product of Reynolds number and inverse of Froude number.