static_two_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 code for a 2D interface hydrostatics problem.
27// The system consists of equal volumes of two fluids with
28// the same physical properties, but a non-zero surface tension at
29// their interface, in a domain of height 2 and half-width 0.5.
30// The program solves for the interface position as the contact angle
31// at the wall, alpha, decreases from pi/2. The resulting shapes should all be
32// circular arcs and the pressure jump across the interface should be
33// cos(alpha)/0.5 = 2 cos(alpha)/Ca.
34
35//OOMPH-LIB include files
36#include "generic.h"
37#include "navier_stokes.h"
38#include "fluid_interface.h"
39#include "constitutive.h"
40#include "solid.h"
41
42// The mesh
43#include "meshes/two_layer_spine_mesh.h"
44#include "meshes/rectangular_quadmesh.h"
45
46//Use the std namespace
47using namespace std;
48
49using namespace oomph;
50
51//The global physical variables
53{
54 /// Pseudo-solid Poisson ratio
55 double Nu=0.1;
56
57 /// Direction of the wall normal vector
58 Vector<double> Wall_normal;
59
60 /// Function that specifies the wall unit normal
61 void wall_unit_normal_fct(const Vector<double> &x,
62 Vector<double> &normal)
63 {
64 normal=Wall_normal;
65 }
66
67}
68
69//============================================================================
70/// A Problem class that solves the Navier--Stokes equations + free surface
71/// in a 2D geometry using a spine-based node update
72//============================================================================
73template<class ELEMENT>
74class CapProblem : public Problem
75{
76
77public:
78
79 //Constructor:
80 //Nx: Number of elements in the x (horizontal) direction
81 //Nh1: Number of elements in the y (vertical) direction in the lower fluid
82 //Nh2: Number of elements in the y (vertical) direction in the upper fluid
83 CapProblem(const unsigned &Nx, const unsigned &Nh1,
84 const unsigned &Nh2);
85
86 /// Peform a parameter study: Solve problem for a range of contact angles
87 void parameter_study();
88
89 /// Finish full specification of the elements
91
92 /// Pointer to the mesh
93 TwoLayerSpineMesh<SpineElement<ELEMENT> >* Bulk_mesh_pt;
94
95 /// Pointer to the surface mesh
96 Mesh* Surface_mesh_pt;
97
98 /// Pointer to the point mesh
99 Mesh* Point_mesh_pt;
100
101 /// Update the spine mesh after every Newton step
103
104 /// Create the volume constraint elements
106
107 /// Doc the solution
108 void doc_solution(DocInfo& doc_info);
109
110private:
111
112 /// The Capillary number
113 double Ca;
114
115 /// The volume of the fluid
116 double Volume;
117
118 /// The contact angle
119 double Angle;
120
121 /// The volume constraint mesh
123
124 /// Trace file
125 ofstream Trace_file;
126
127 /// Data that is traded for the volume constraint
129
130};
131
132
133
134//======================================================================
135/// Constructor
136//======================================================================
137template<class ELEMENT>
139(const unsigned &Nx, const unsigned &Nh1, const unsigned &Nh2) :
140 Ca(2.1), //Initialise value of Ca to some random value
141 Volume(0.5), //Initialise the value of the volume
142 Angle(0.5*MathematicalConstants::Pi) //Initialise the contact angle
143{
147
148 //Construct our mesh
149 Bulk_mesh_pt = new TwoLayerSpineMesh<SpineElement<ELEMENT> >
150 (Nx,Nh1,Nh2,0.5,1.0,1.0);
151
152 Surface_mesh_pt = new Mesh;
153
154 //Loop over the horizontal elements
155 unsigned n_interface_lower = Bulk_mesh_pt->ninterface_lower();
156 for(unsigned i=0;i<n_interface_lower;i++)
157 {
158 //Construct a new 1D line element adjacent to the interface
159 FiniteElement *interface_element_pt = new
160 SpineLineFluidInterfaceElement<SpineElement<ELEMENT> >(
161 Bulk_mesh_pt->interface_lower_boundary_element_pt(i),
162 Bulk_mesh_pt->interface_lower_face_index_at_boundary(i));
163
164 this->Surface_mesh_pt->add_element_pt(interface_element_pt);
165 }
166
167//Create the point mesh
168Point_mesh_pt = new Mesh;
169 {
170 //Make the point (contact) element from the last surface element
171 FiniteElement* point_element_pt =
172 dynamic_cast<SpineLineFluidInterfaceElement<
173 SpineElement<ELEMENT> >*>(Surface_mesh_pt->element_pt(n_interface_lower-1))
174 ->make_bounding_element(1);
175
176 //Add it to the mesh
177 this->Point_mesh_pt->add_element_pt(point_element_pt);
178}
179
180 //Hijack one of the pressure values in the upper fluid. Its value
181 //will affect the residual of that element but it will not
182 //be determined by it!
183 Traded_pressure_data_pt = dynamic_cast<ELEMENT*>(
184 Bulk_mesh_pt->upper_layer_element_pt(0))->hijack_internal_value(0,0);
185
186 // Loop over the elements on the interface to pass pointer to Ca and
187 // the pointer to the Data item that contains the single
188 // (pressure) value that is "traded" for the volume constraint
189 unsigned n_interface = Surface_mesh_pt->nelement();
190 for(unsigned e=0;e<n_interface;e++)
191 {
192 //Cast to a 1D element
193 SpineLineFluidInterfaceElement<SpineElement<ELEMENT> >*el_pt=
194 dynamic_cast<SpineLineFluidInterfaceElement<
195 SpineElement<ELEMENT> >*>
196 (Surface_mesh_pt->element_pt(e));
197 //Set the Capillary number
198 el_pt->ca_pt() = &Ca;
199 }
200
201
202 //Set the boundary conditions
203
204 //Pin the velocities on all external boundaries apart from the symmetry
205 //line (boundaries 4 and 5) where only the horizontal velocity is pinned
206 for (unsigned b=0;b<6;b++)
207 {
208 //Find the number of nodes on the boundary
209 unsigned n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
210 //Loop over the nodes on the boundary
211 for(unsigned n=0;n<n_boundary_node;n++)
212 {
213 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(0);
214 if ((b!=4) && (b!=5))
215 {
216 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(1);
217 }
218 }
219 }
220 // Set the contact angle boundary condition for the rightmost element
221 dynamic_cast<FluidInterfaceBoundingElement*>(
222 Point_mesh_pt->element_pt(0))->set_contact_angle(&Angle);
223
224 dynamic_cast<FluidInterfaceBoundingElement*>(
225 Point_mesh_pt->element_pt(0))->ca_pt() = &Ca;
226
227 dynamic_cast<FluidInterfaceBoundingElement*>(
228 Point_mesh_pt->element_pt(0))->wall_unit_normal_fct_pt() =
230
231 // Pin a single pressure value: Set the pressure dof 0 in element 0
232 // of the lower layer to zero.
233 dynamic_cast<ELEMENT*>(Bulk_mesh_pt->lower_layer_element_pt(0))
234 ->fix_pressure(0,0.0);
235
237
238 this->add_sub_mesh(Bulk_mesh_pt);
239 this->add_sub_mesh(Surface_mesh_pt);
240 this->add_sub_mesh(Point_mesh_pt);
241 this->add_sub_mesh(Volume_constraint_mesh_pt);
242
243 this->build_global_mesh();
244
245 //Setup all the equation numbering and look-up schemes
246 cout << "Number of unknowns: " << assign_eqn_numbers() << std::endl;
247
248}
249
250
251
252//======================================================================
253/// Create the volume constraint elements
254//========================================================================
255template<class ELEMENT>
257{
258 //The single volume constraint element
259 Volume_constraint_mesh_pt = new Mesh;
260 VolumeConstraintElement* vol_constraint_element =
261 new VolumeConstraintElement(&Volume,Traded_pressure_data_pt,0);
262 Volume_constraint_mesh_pt->add_element_pt(vol_constraint_element);
263
264 //Loop over lower boundaries (or just 1, why?)
265 unsigned lower_boundaries[3]={0,1,5};
266 for(unsigned i=0;i<3;i++)
267 {
268 //Read out the actual boundaries
269 unsigned b = lower_boundaries[i];
270
271 // How many bulk fluid elements are adjacent to boundary b?
272 unsigned n_element = Bulk_mesh_pt->nboundary_element(b);
273
274 // Loop over the bulk fluid elements adjacent to boundary b?
275 for(unsigned e=0;e<n_element;e++)
276 {
277 // Get pointer to the bulk fluid element that is
278 // adjacent to boundary b
279 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
280 Bulk_mesh_pt->boundary_element_pt(b,e));
281
282 //Find the index of the face of element e along boundary b
283 int face_index = Bulk_mesh_pt->face_index_at_boundary(b,e);
284
285 // Create new element
286 SpineLineVolumeConstraintBoundingElement<ELEMENT>* el_pt =
287 new SpineLineVolumeConstraintBoundingElement<ELEMENT>(
288 bulk_elem_pt,face_index);
289
290 //Set the "master" volume control element
291 el_pt->set_volume_constraint_element(vol_constraint_element);
292
293 // Add it to the mesh
294 Volume_constraint_mesh_pt->add_element_pt(el_pt);
295 }
296 }
297
298 //Loop over one side of the interface
299 {
300 // How many bulk fluid elements are adjacent to boundary b?
301 unsigned n_element = Bulk_mesh_pt->ninterface_lower();
302
303 // Loop over the bulk fluid elements adjacent to boundary b?
304 for(unsigned e=0;e<n_element;e++)
305 {
306 // Get pointer to the bulk fluid element that is
307 // adjacent to boundary b
308 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
309 Bulk_mesh_pt->interface_lower_boundary_element_pt(e));
310
311 //Find the index of the face of element e along boundary b
312 int face_index = Bulk_mesh_pt->interface_lower_face_index_at_boundary(e);
313
314 // Create new element
315 SpineLineVolumeConstraintBoundingElement<ELEMENT>* el_pt =
316 new SpineLineVolumeConstraintBoundingElement<ELEMENT>(
317 bulk_elem_pt,face_index);
318
319 //Set the "master" volume control element
320 el_pt->set_volume_constraint_element(vol_constraint_element);
321
322 // Add it to the mesh
323 Volume_constraint_mesh_pt->add_element_pt(el_pt);
324 }
325 }
326
327}
328
329
330
331//======================================================================
332/// Perform a parameter study
333//======================================================================
334template<class ELEMENT>
336{
337
338 // Create DocInfo object (allows checking if output directory exists)
339 DocInfo doc_info;
340 doc_info.set_directory("RESLT");
341 doc_info.number()=0;
342
343
344 // Open trace file
345 char filename[100];
346 snprintf(filename, sizeof(filename), "%s/trace.dat",doc_info.directory().c_str());
347 Trace_file.open(filename);
348 Trace_file << "VARIABLES=\"<greek>a</greek><sub>prescribed</sub>\",";
349 Trace_file << "\"h<sub>left</sub>\",\"h<sub>right</sub>\",";
350 Trace_file << "\"<greek>a</greek><sub>left</sub>\",";
351 Trace_file << "\"<greek>a</greek><sub>right</sub>\",";
352 Trace_file << "\"p<sub>upper</sub>\",";
353 Trace_file << "\"p<sub>lower</sub>\",";
354 Trace_file << "\"p<sub>exact</sub>\"";
355 Trace_file << std::endl;
356
357 // Doc initial state
358 doc_solution(doc_info);
359
360 // Bump up counter
361 doc_info.number()++;
362
363 //Gradually increase the contact angle
364 for(unsigned i=0;i<6;i++)
365 {
366 //Solve the problem
367 steady_newton_solve();
368
369 //Output result
370 doc_solution(doc_info);
371
372 // Bump up counter
373 doc_info.number()++;
374
375 //Decrease the contact angle
376 Angle -= 5.0*MathematicalConstants::Pi/180.0;
377 }
378
379}
380
381
382
383
384//========================================================================
385/// Doc the solution
386//========================================================================
387template<class ELEMENT>
388void CapProblem<ELEMENT>::doc_solution(DocInfo& doc_info)
389{
390
391 ofstream some_file;
392 char filename[100];
393
394 // Number of plot points
395 unsigned npts=5;
396
397
398 //Output domain
399 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
400 doc_info.number());
401 some_file.open(filename);
402 Bulk_mesh_pt->output(some_file,npts);
403 Surface_mesh_pt->output(some_file,npts);
404 some_file.close();
405
406
407 // Number of interface elements
408// unsigned ninterface=Bulk_mesh_pt->ninterface_element();
409
410 // Number of spines
411 unsigned nspine=Bulk_mesh_pt->nspine();
412
413 // Doc
414 Trace_file << Angle*180.0/MathematicalConstants::Pi;
415 Trace_file << " " << Bulk_mesh_pt->spine_pt(0)->height();
416 Trace_file << " " << Bulk_mesh_pt->spine_pt(nspine-1)->height();
417 Trace_file << " "
418 << dynamic_cast<ELEMENT*>(
419 Bulk_mesh_pt->upper_layer_element_pt(0))->p_nst(0);
420 Trace_file << " "
421 << dynamic_cast<ELEMENT*>(
422 Bulk_mesh_pt->lower_layer_element_pt(0))->p_nst(0);
423 Trace_file << " " << 2.0*cos(Angle)/Ca;
424 Trace_file << std::endl;
425
426}
427
428
429
430//==start_of_specific_mesh_class==========================================
431/// Two layer mesh which employs a pseudo-solid node-update strategy.
432/// This class is essentially a wrapper to an
433/// ElasticRectangularQuadMesh, with an additional boundary
434/// to represent the interface between the two fluid layers. In addition,
435/// the mesh paritions the elements into those above and below
436/// the interface and relabels boundaries so that we can impose
437/// a volume constraint on the lower or upper fluid.
438///
439/// 3
440/// ---------------------------------------
441/// | |
442/// 4 | | 2
443/// | 6 |
444/// ---------------------------------------
445/// | |
446/// 5 | | 1
447/// | |
448/// ---------------------------------------
449/// 0
450//========================================================================
451template <class ELEMENT>
453 public ElasticRectangularQuadMesh<ELEMENT>
454{
455
456public:
457
458 /// Constructor: Pass number of elements in x-direction, number of
459 /// elements in y-direction in bottom and top layer, respectively,
460 /// axial length and height of top and bottom layers, a boolean
461 /// flag to make the mesh periodic in the x-direction, and pointer
462 /// to timestepper (defaults to Steady timestepper)
463 ElasticTwoLayerMesh(const unsigned &nx,
464 const unsigned &ny1,
465 const unsigned &ny2,
466 const double &lx,
467 const double &h1,
468 const double &h2,
469 const bool& periodic_in_x=false,
470 TimeStepper* time_stepper_pt=
471 &Mesh::Default_TimeStepper)
472 :
473 RectangularQuadMesh<ELEMENT>(nx,ny1+ny2,lx,h1+h2,
474 periodic_in_x,time_stepper_pt),
475 ElasticRectangularQuadMesh<ELEMENT>(nx,ny1+ny2,lx,h1+h2,
476 periodic_in_x,time_stepper_pt)
477 {
478 //Set up the pointers to elements in the upper and lower fluid
479 //Calculate numbers of nodes in upper and lower regions
480 unsigned long n_lower = nx*ny1;
481 unsigned long n_upper = nx*ny2;
482 //Loop over lower elements and push back onto the array
483 Lower_layer_element_pt.resize(n_lower);
484 for(unsigned e=0;e<n_lower;e++)
485 {
486 Lower_layer_element_pt[e] = this->finite_element_pt(e);
487 }
488 //Loop over upper elements and push back onto the array
489 Upper_layer_element_pt.resize(n_upper);
490 for(unsigned e=0;e<n_upper;e++)
491 {
492 Upper_layer_element_pt[e] = this->finite_element_pt(n_lower + e);
493 }
494 //end of upper and lower fluid element assignment
495
496 //Set the elements adjacent to the interface on both sides
499 {
500 unsigned count_lower=nx*(ny1-1);
501 unsigned count_upper= count_lower + nx;
502 for(unsigned e=0;e<nx;e++)
503 {
505 this->finite_element_pt(count_lower); ++count_lower;
507 this->finite_element_pt(count_upper); ++count_upper;
508 }
509 } //end of bulk elements next to interface setup
510
511
512 // Reset the number of boundaries
513 this->set_nboundary(7);
514
515 //Relabel the boundary nodes
516 //Storage for the boundary coordinates that will be transferred directly
517 Vector<double> b_coord;
518 {
519 //Store the interface level
520 const double y_interface = h1 - this->Ymin;
521
522 //Nodes on boundary 3 are now on boundaries 4 and 5
523 unsigned n_boundary_node = this->nboundary_node(3);
524 //Loop over the nodes remove them from boundary 3
525 //and add them to boundary 4 or 5 depending on their y coordinate
526 for(unsigned n=0;n<n_boundary_node;n++)
527 {
528 //Cache pointer to the node
529 Node* const nod_pt = this->boundary_node_pt(3,n);
530 //Get the boundary coordinates if set
531 if(this->boundary_coordinate_exists(3))
532 {
533 b_coord.resize(nod_pt->ncoordinates_on_boundary(3));
534 nod_pt->get_coordinates_on_boundary(3,b_coord);
535 //Indicate that the boundary coordinates are to be set on the
536 //new boundaries
537 this->set_boundary_coordinate_exists(4);
538 this->set_boundary_coordinate_exists(5);
539 }
540
541 //Now remove the node from the old boundary
542 nod_pt->remove_from_boundary(3);
543
544 //Find the height of the node
545 double y = nod_pt->x(1);
546 //If it's above or equal to the interface, it's on boundary 4
547 if(y >= y_interface)
548 {
549 this->add_boundary_node(4,nod_pt);
550 //Add the boundary coordinate if it has been set up
551 if(this->boundary_coordinate_exists(4))
552 {
553 nod_pt->set_coordinates_on_boundary(4,b_coord);
554 }
555 }
556 //otherwise it's on boundary 5
557 if(y <= y_interface)
558 {
559 this->add_boundary_node(5,nod_pt);
560 //Add the boundary coordinate if it has been set up
561 if(this->boundary_coordinate_exists(5))
562 {
563 nod_pt->set_coordinates_on_boundary(5,b_coord);
564 }
565 }
566 }
567
568 //Now clear the boundary node information from boundary 3
569 this->Boundary_node_pt[3].clear();
570
571 //Relabel the nodes on boundary 2 to be on boundary 3
572 n_boundary_node = this->nboundary_node(2);
573 //Loop over the nodes remove them from boundary 2
574 //and add them to boundary 3
575 for(unsigned n=0;n<n_boundary_node;n++)
576 {
577 //Cache pointer to the node
578 Node* const nod_pt = this->boundary_node_pt(2,n);
579 //Get the boundary coordinates if set
580 if(this->boundary_coordinate_exists(2))
581 {
582 b_coord.resize(nod_pt->ncoordinates_on_boundary(2));
583 nod_pt->get_coordinates_on_boundary(2,b_coord);
584 this->set_boundary_coordinate_exists(3);
585 }
586
587 //Now remove the node from the boundary 2
588 nod_pt->remove_from_boundary(2);
589 //and add to boundary 3
590 this->add_boundary_node(3,nod_pt);
591 if(this->boundary_coordinate_exists(3))
592 {
593 nod_pt->set_coordinates_on_boundary(3,b_coord);
594 }
595 }
596
597 //Clear the information from boundary 2
598 this->Boundary_node_pt[2].clear();
599
600 //Storage for nodes that should be removed from boundary 1
601 std::list<Node*> nodes_to_be_removed;
602
603 //Nodes on boundary 1 are now on boundaries 1 and 2
604 n_boundary_node = this->nboundary_node(1);
605 //Loop over the nodes remove some of them from boundary 1
606 for(unsigned n=0;n<n_boundary_node;n++)
607 {
608 //Cache pointer to the node
609 Node* const nod_pt = this->boundary_node_pt(1,n);
610
611 //Find the height of the node
612 double y = nod_pt->x(1);
613 //If it's above or equal to the interface it's on boundary 2
614 if(y >= y_interface)
615 {
616 //Get the boundary coordinates if set
617 if(this->boundary_coordinate_exists(1))
618 {
619 b_coord.resize(nod_pt->ncoordinates_on_boundary(1));
620 nod_pt->get_coordinates_on_boundary(1,b_coord);
621 this->set_boundary_coordinate_exists(2);
622 }
623
624 //Now remove the node from the boundary 1 if above interace
625 if(y > y_interface)
626 {
627 nodes_to_be_removed.push_back(nod_pt);
628 }
629 //Always add to boundary 2
630 this->add_boundary_node(2,nod_pt);
631 //Add the boundary coordinate if it has been set up
632 if(this->boundary_coordinate_exists(2))
633 {
634 nod_pt->set_coordinates_on_boundary(2,b_coord);
635 }
636 }
637 }
638
639 //Loop over the nodes that are to be removed from boundary 1 and remove
640 //them
641 for(std::list<Node*>::iterator it=nodes_to_be_removed.begin();
642 it!=nodes_to_be_removed.end();++it)
643 {
644 this->remove_boundary_node(1,*it);
645 }
646 nodes_to_be_removed.clear();
647 } //end of boundary relabelling
648
649 //Add the nodes to the interface
650
651 //Read out number of linear points in the element
652 unsigned n_p = dynamic_cast<ELEMENT*>
653 (this->finite_element_pt(0))->nnode_1d();
654
655 //Add the nodes on the interface to the boundary 6
656 //Storage for boundary coordinates (x-coordinate)
657 b_coord.resize(1);
658 this->set_boundary_coordinate_exists(6);
659 //Starting index of the nodes
660 unsigned n_start=0;
661 for(unsigned e=0;e<nx;e++)
662 {
663 //If we are past the
664 if(e>0) {n_start=1;}
665 //Get the layer of elements just above the interface
666 FiniteElement* el_pt = this->finite_element_pt(nx*ny1+e);
667 //The first n_p nodes lie on the boundary
668 for(unsigned n=n_start;n<n_p;n++)
669 {
670 Node* nod_pt = el_pt->node_pt(n);
671 this->convert_to_boundary_node(nod_pt);
672 this->add_boundary_node(6,nod_pt);
673 b_coord[0] = nod_pt->x(0);
674 nod_pt->set_coordinates_on_boundary(6,b_coord);
675 }
676 }
677
678 // Set up the boundary element information
679 this->setup_boundary_element_info();
680 }
681
682 /// Access functions for pointers to elements in upper layer
683 FiniteElement* &upper_layer_element_pt(const unsigned long &i)
684 {return Upper_layer_element_pt[i];}
685
686 /// Access functions for pointers to elements in bottom layer
687 FiniteElement* &lower_layer_element_pt(const unsigned long &i)
688 {return Lower_layer_element_pt[i];}
689
690 /// Number of elements in upper layer
691 unsigned long nupper() const {return Upper_layer_element_pt.size();}
692
693 /// Number of elements in top layer
694 unsigned long nlower() const {return Lower_layer_element_pt.size();}
695
696 /// Access functions for pointers to elements in upper layer
697 FiniteElement* &interface_upper_boundary_element_pt(const unsigned long &i)
699
700 /// Access functions for pointers to elements in bottom layer
701 FiniteElement* &interface_lower_boundary_element_pt(const unsigned long &i)
703
704 /// Number of elements in upper layer
705 unsigned long ninterface_upper() const
707
708 /// Number of elements in top layer
709 unsigned long ninterface_lower() const
711
712 /// Index of the face of the elements next to the interface
713 /// in the upper region (always -2)
715 {return -2;}
716
717 /// Index of the face of the elements next to the interface in
718 /// the lower region (always 2)
720 {return 2;}
721
722private:
723
724 /// Vector of pointers to element in the upper layer
725 Vector <FiniteElement *> Lower_layer_element_pt;
726
727 /// Vector of pointers to element in the lower layer
728 Vector <FiniteElement *> Upper_layer_element_pt;
729
730 /// Vector of pointers to the elements adjacent to the interface
731 /// on the lower layer
732 Vector <FiniteElement*> Interface_lower_boundary_element_pt;
733
734 /// Vector of pointers to the element adjacent to the interface
735 /// on the upper layer
736 Vector<FiniteElement *> Interface_upper_boundary_element_pt;
737
738
739}; // End of specific mesh class
740
741
742
743//===========start_of_pseudo_elastic_class====================================
744/// A class that solves the Navier--Stokes equations
745/// to compute the shape of a static interface between two fluids in a
746/// rectangular container with an imposed contact angle at the boundary.
747//============================================================================
748template<class ELEMENT>
749class PseudoSolidCapProblem : public Problem
750{
751public:
752
753 //Constructor: Arguements are the number of elements in the x
754 //direction and in each of the layers
756 const unsigned &Nx, const unsigned &Nh1, const unsigned &Nh2);
757
758 /// Destructor: clean up memory allocated by the object
760
761 /// Peform a parameter study: Solve problem for a range of contact angles
762 /// Pass name of output directory as a string
763 void parameter_study(const string& dir_name);
764
765 /// Doc the solution
766 void doc_solution(DocInfo& doc_info);
767
768
769private:
770
771 /// Create the free surface elements
773
774 /// Create the volume constraint elements
776
777 /// Create the contact angle element
779
780 /// The Capillary number
781 double Ca;
782
783 /// The prescribed volume of the fluid
784 double Volume;
785
786 /// The external pressure
787 double Pext;
788
789 /// The contact angle
790 double Angle;
791
792 /// Constitutive law used to determine the mesh deformation
793 ConstitutiveLaw *Constitutive_law_pt;
794
795 // Pointer to the (single valued) Data item that
796 // will contain the pressure value that we're
797 // trading for the volume constraint
799
800 /// Trace file
801 ofstream Trace_file;
802
803 /// Storage for the bulk mesh
805
806 /// Storage for the free surface mesh
808
809 /// Storage for the element bounding the free surface
811
812 /// Storage for the elements that compute the enclosed volume
814
815 /// Storage for the volume constraint
817
818}; //end_of_pseudo_solid_problem_class
819
820
821
822//============start_of_constructor=====================================
823/// Constructor: Pass boolean flag to indicate if the volume
824/// constraint is applied by hijacking an internal pressure
825/// or the external pressure
826//======================================================================
827template<class ELEMENT>
829 const unsigned &Nx, const unsigned &Nh1, const unsigned &Nh2) :
830 Ca(2.1), //Initialise value of Ca to some random value
831 Volume(0.5), //Initialise the value of the volume
832 Pext(1.23), //Initialise the external pressure to some random value
833 Angle(0.5*MathematicalConstants::Pi) //Initialise the contact angle
834{
835 //Set the wall normal
839
840 //Construct mesh
841 Bulk_mesh_pt = new ElasticTwoLayerMesh<ELEMENT>(Nx,Nh1,Nh2,0.5,1.0,1.0);
842
843 //Hijack one of the pressure values in the fluid and use it
844 //as the pressure whose value is determined by the volume constraint.
845 //(Its value will affect the residual of that element but it will not
846 //be determined by it, i.e. it's hijacked).
847 Traded_pressure_data_pt = dynamic_cast<ELEMENT*>(
848 Bulk_mesh_pt->upper_layer_element_pt(0))->hijack_internal_value(0,0);
849
850 //Set the constituive law
852 new GeneralisedHookean(&Global_Physical_Variables::Nu);
853
854 //Loop over the elements to set the consitutive law
855 unsigned n_bulk = Bulk_mesh_pt->nelement();
856 for(unsigned e=0;e<n_bulk;e++)
857 {
858 ELEMENT* el_pt =
859 dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
860
861 el_pt->constitutive_law_pt() = Constitutive_law_pt;
862 }
863
864 //Set the boundary conditions
865
866 //Fluid velocity conditions
867 //Pin the velocities on all external boundaries apart from the symmetry
868 //line boundaries 4 and 5) where only the horizontal velocity is pinned
869 for (unsigned b=0;b<6;b++)
870 {
871 //Find the number of nodes on the boundary
872 unsigned n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
873 //Loop over the nodes on the boundary
874 for(unsigned n=0;n<n_boundary_node;n++)
875 {
876 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(0);
877 if((b!=4) && (b!=5))
878 {
879 Bulk_mesh_pt->boundary_node_pt(b,n)->pin(1);
880 }
881 }
882 } //end_of_fluid_boundary_conditions
883
884 //PesudoSolid boundary conditions,
885 for(unsigned b=0;b<6;b++)
886 {
887 //Find the number of nodes on the boundary
888 unsigned n_boundary_node = Bulk_mesh_pt->nboundary_node(b);
889 //Loop over the nodes on the boundary
890 for(unsigned n=0;n<n_boundary_node;n++)
891 {
892 //Pin vertical displacement on the bottom and top
893 if((b==0) || (b==3))
894 {
895 static_cast<SolidNode*>(Bulk_mesh_pt->boundary_node_pt(b,n))
896 ->pin_position(1);
897 }
898 //Pin horizontal displacement on the sides
899 if((b==1) || (b==2) || (b==4) || (b==5))
900 {
901 static_cast<SolidNode*>(Bulk_mesh_pt->boundary_node_pt(b,n))
902 ->pin_position(0);
903 }
904 }
905 } //end_of_solid_boundary_conditions
906
907 //Constrain all nodes only to move vertically (not horizontally)
908 {
909 unsigned n_node = Bulk_mesh_pt->nnode();
910 for(unsigned n=0;n<n_node;n++)
911 {
912 static_cast<SolidNode*>(Bulk_mesh_pt->node_pt(n))->pin_position(0);
913 }
914 } //end_of_constraint
915
916 // Pin a single pressure value in the lower fluid:
917 // Set the pressure dof 0 in element 0 to zero.
918 dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(0))->fix_pressure(0,0.0);
919
920 //Create the free surface elements
922
923 //Create the volume constraint elements
925
926 //Need to make the bounding element
928
929 //Now need to add all the meshes
930 this->add_sub_mesh(Bulk_mesh_pt);
931 this->add_sub_mesh(Free_surface_mesh_pt);
932 this->add_sub_mesh(Volume_computation_mesh_pt);
933 this->add_sub_mesh(Volume_constraint_mesh_pt);
934 this->add_sub_mesh(Free_surface_bounding_mesh_pt);
935
936 //and build the global mesh
937 this->build_global_mesh();
938
939 //Setup all the equation numbering and look-up schemes
940 cout << "Number of unknowns: " << assign_eqn_numbers() << std::endl;
941
942} //end_of_constructor
943
944
945//==========================================================================
946/// Destructor. Make sure to clean up all allocated memory, so that multiple
947/// instances of the problem don't lead to excessive memory usage.
948//==========================================================================
949template<class ELEMENT>
951{
952 //Delete the contact angle element
953 delete Free_surface_bounding_mesh_pt->element_pt(0);
954 Free_surface_bounding_mesh_pt->flush_element_and_node_storage();
955 delete Free_surface_bounding_mesh_pt;
956 //Delete the volume constraint mesh
957 delete Volume_constraint_mesh_pt;
958 //Delete the surface volume computation elements
959 unsigned n_element = Volume_computation_mesh_pt->nelement();
960 for(unsigned e=0;e<n_element;e++)
961 {delete Volume_computation_mesh_pt->element_pt(e);}
962 //Now flush the storage
963 Volume_computation_mesh_pt->flush_element_and_node_storage();
964 //Now delete the mesh
965 delete Volume_computation_mesh_pt;
966 //Delete the free surface elements
967 n_element = Free_surface_mesh_pt->nelement();
968 for(unsigned e=0;e<n_element;e++)
969 {delete Free_surface_mesh_pt->element_pt(e);}
970 //Now flush the storage
971 Free_surface_mesh_pt->flush_element_and_node_storage();
972 //Now delete the mesh
973 delete Free_surface_mesh_pt;
974
975 //Delete the constitutive law
976 delete Constitutive_law_pt;
977
978 //Then delete the bulk mesh
979 delete Bulk_mesh_pt;
980}
981
982//============create_free_surface_element================================
983/// Create the free surface elements
984//========================================================================
985template<class ELEMENT>
987{
988 //Allocate storage for the free surface mesh
989 Free_surface_mesh_pt = new Mesh;
990
991 // How many bulk fluid elements are adjacent to the interface
992 unsigned n_element = Bulk_mesh_pt->ninterface_lower();
993 //Loop over them
994 for(unsigned e=0;e<n_element;e++)
995 {
996 // Create new element
997 ElasticLineFluidInterfaceElement<ELEMENT>* el_pt =
998 new ElasticLineFluidInterfaceElement<ELEMENT>(
999 Bulk_mesh_pt->interface_lower_boundary_element_pt(e),
1000 Bulk_mesh_pt->interface_lower_face_index_at_boundary(e));
1001
1002 // Add it to the mesh
1003 Free_surface_mesh_pt->add_element_pt(el_pt);
1004
1005 //Add the capillary number
1006 el_pt->ca_pt() = &Ca;
1007 }
1008
1009}
1010
1011
1012//============start_of_create_volume_constraint_elements==================
1013/// Create the volume constraint elements
1014//========================================================================
1015template<class ELEMENT>
1017{
1018 //Build the single volume constraint element
1019 Volume_constraint_mesh_pt = new Mesh;
1020 VolumeConstraintElement* vol_constraint_element =
1021 new VolumeConstraintElement(&Volume,Traded_pressure_data_pt,0);
1022 Volume_constraint_mesh_pt->add_element_pt(vol_constraint_element);
1023
1024 //Now create the volume computation elements
1025 Volume_computation_mesh_pt = new Mesh;
1026
1027 //Loop over lower boundaries (or just 1, why?)
1028 unsigned lower_boundaries[3]={0,1,5};
1029 //Loop over all boundaries
1030 for(unsigned i=0;i<3;i++)
1031 {
1032 //Read out the actual boundary
1033 unsigned b= lower_boundaries[i];
1034 // How many bulk fluid elements are adjacent to boundary b?
1035 unsigned n_element = Bulk_mesh_pt->nboundary_element(b);
1036
1037 // Loop over the bulk fluid elements adjacent to boundary b?
1038 for(unsigned e=0;e<n_element;e++)
1039 {
1040 // Get pointer to the bulk fluid element that is
1041 // adjacent to boundary b
1042 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
1043 Bulk_mesh_pt->boundary_element_pt(b,e));
1044
1045 //Find the index of the face of element e along boundary b
1046 int face_index = Bulk_mesh_pt->face_index_at_boundary(b,e);
1047
1048 // Create new element
1049 ElasticLineVolumeConstraintBoundingElement<ELEMENT>* el_pt =
1050 new ElasticLineVolumeConstraintBoundingElement<ELEMENT>(
1051 bulk_elem_pt,face_index);
1052
1053 //Set the "master" volume control element
1054 el_pt->set_volume_constraint_element(vol_constraint_element);
1055
1056 // Add it to the mesh
1057 Volume_computation_mesh_pt->add_element_pt(el_pt);
1058 }
1059 }
1060
1061
1062 //Loop over one side of the interface
1063 {
1064 // How many bulk fluid elements are adjacent to boundary b?
1065 unsigned n_element = Bulk_mesh_pt->ninterface_lower();
1066
1067 // Loop over the bulk fluid elements adjacent to boundary b?
1068 for(unsigned e=0;e<n_element;e++)
1069 {
1070 // Get pointer to the bulk fluid element that is
1071 // adjacent to boundary b
1072 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
1073 Bulk_mesh_pt->interface_lower_boundary_element_pt(e));
1074
1075 //Find the index of the face of element e along boundary b
1076 int face_index = Bulk_mesh_pt->interface_lower_face_index_at_boundary(e);
1077
1078 // Create new element
1079 ElasticLineVolumeConstraintBoundingElement<ELEMENT>* el_pt =
1080 new ElasticLineVolumeConstraintBoundingElement<ELEMENT>(
1081 bulk_elem_pt,face_index);
1082
1083 //Set the "master" volume control element
1084 el_pt->set_volume_constraint_element(vol_constraint_element);
1085
1086 // Add it to the mesh
1087 Volume_constraint_mesh_pt->add_element_pt(el_pt);
1088 }
1089 }
1090
1091
1092} //end_of_create_volume_constraint_elements
1093
1094//==========start_of_create_contact_angle_elements========================
1095/// Create the contact angle element
1096//========================================================================
1097template<class ELEMENT>
1099{
1100 Free_surface_bounding_mesh_pt = new Mesh;
1101
1102 //Find the element at the end of the free surface
1103 //The elements are assigned in order of increasing x coordinate
1104 unsigned n_free_surface = Free_surface_mesh_pt->nelement();
1105
1106 //Make the bounding element for the contact angle constraint
1107 //which works because the order of elements in the mesh is known
1108 FluidInterfaceBoundingElement* el_pt =
1109 dynamic_cast<ElasticLineFluidInterfaceElement<ELEMENT>*>
1110 (Free_surface_mesh_pt->element_pt(n_free_surface-1))->
1111 make_bounding_element(1);
1112
1113 //Set the contact angle (strong imposition)
1114 el_pt->set_contact_angle(&Angle);
1115
1116 //Set the capillary number
1117 el_pt->ca_pt() = &Ca;
1118
1119 //Set the wall normal of the external boundary
1120 el_pt->wall_unit_normal_fct_pt()
1122
1123 //Add the element to the mesh
1124 Free_surface_bounding_mesh_pt->add_element_pt(el_pt);
1125
1126} //end_of_create_contact_angle_element
1127
1128
1129
1130//================start_of_parameter_study===========================
1131/// Perform a parameter study. Pass name of output directory as
1132/// a string
1133//======================================================================
1134template<class ELEMENT>
1135void PseudoSolidCapProblem<ELEMENT>::parameter_study(const string& dir_name)
1136{
1137 // Create DocInfo object (allows checking if output directory exists)
1138 DocInfo doc_info;
1139 doc_info.set_directory(dir_name);
1140 doc_info.number()=0;
1141
1142 // Open trace file
1143 char filename[100];
1144 snprintf(filename, sizeof(filename), "%s/trace.dat",doc_info.directory().c_str());
1145 Trace_file.open(filename);
1146 Trace_file << "VARIABLES=\"<greek>a</greek><sub>prescribed</sub>\",";
1147 Trace_file << "\"h<sub>left</sub>\",\"h<sub>right</sub>\",";
1148 Trace_file << "\"p<sub>fluid</sub>-p<sub>ext</sub>\",";
1149 Trace_file << "\"<greek>D</greek>p<sub>exact</sub>\"";
1150 Trace_file << std::endl;
1151
1152 // Doc initial state
1153 doc_solution(doc_info);
1154
1155 // Bump up counter
1156 doc_info.number()++;
1157
1158 //Solve the problem for six different contact angles
1159 for(unsigned i=0;i<6;i++)
1160 {
1161 //Solve the problem
1162 steady_newton_solve();
1163
1164 //Output result
1165 doc_solution(doc_info);
1166
1167 // Bump up counter
1168 doc_info.number()++;
1169
1170 //Decrease the contact angle
1171 Angle -= 5.0*MathematicalConstants::Pi/180.0;
1172 }
1173
1174}
1175
1176
1177
1178
1179//==============start_of_doc_solution=====================================
1180/// Doc the solution
1181//========================================================================
1182template<class ELEMENT>
1184{
1185 //Output stream
1186 ofstream some_file;
1187 char filename[100];
1188
1189 // Number of plot points
1190 unsigned npts=5;
1191
1192 //Output domain
1193 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
1194 doc_info.number());
1195 some_file.open(filename);
1196 Bulk_mesh_pt->output(some_file,npts);
1197 some_file.close();
1198
1199
1200 // Number of interface elements
1201 unsigned ninterface=Free_surface_mesh_pt->nelement();
1202 //Find number of nodes in the last interface element
1203 unsigned np = Free_surface_mesh_pt->finite_element_pt(ninterface-1)->nnode();
1204 // Document the contact angle (in degrees), the height of the interface at
1205 // the centre of the container, the height at the wall, the computed
1206 // pressure drop across the interface and
1207 // the analytic prediction of the pressure drop.
1208 Trace_file << Angle*180.0/MathematicalConstants::Pi;
1209 Trace_file << " " << Free_surface_mesh_pt->finite_element_pt(0)
1210 ->node_pt(0)->x(1)
1211 << " "
1212 << Free_surface_mesh_pt->finite_element_pt(ninterface-1)
1213 ->node_pt(np-1)->x(1);
1214 Trace_file << " "
1215 << dynamic_cast<ELEMENT*>(
1216 Bulk_mesh_pt->upper_layer_element_pt(0))->p_nst(0);
1217 Trace_file << " "
1218 << dynamic_cast<ELEMENT*>(
1219 Bulk_mesh_pt->lower_layer_element_pt(0))->p_nst(0);
1220 Trace_file << " " << 2.0*cos(Angle)/Ca;
1221 Trace_file << std::endl;
1222
1223} //end_of_doc_solution
1224
1225
1226//======================================================================
1227/// Main driver: Build problem and initiate parameter study
1228//======================================================================
1229int main()
1230{
1231 {
1232 //Construct the problem with 4 x (4+4) elements
1234
1235 //Solve the problem :)
1236 problem.parameter_study();
1237 }
1238
1239 {
1240 //Construct the problem with 4 x (4+4) elements
1241 PseudoSolidCapProblem<Hijacked<
1242 PseudoSolidNodeUpdateElement<QCrouzeixRaviartElement<2>,
1243 QPVDElementWithPressure<2> > > >
1244 problem(4,4,4);
1245
1246 //Solve the problem :)
1247 problem.parameter_study("RESLT_elastic");
1248 }
1249
1250}
A Problem class that solves the Navier–Stokes equations + free surface in a 2D geometry using a spine...
Mesh * Volume_constraint_mesh_pt
The volume constraint mesh.
Data * Traded_pressure_data_pt
Data that is traded for the volume constraint.
Mesh * Surface_mesh_pt
The mesh for the interface elements.
ofstream Trace_file
Trace file.
void finish_problem_setup()
Finish full specification of the elements.
void parameter_study()
Peform a parameter study: Solve problem for a range of contact angles.
SingleLayerSpineMesh< SpineElement< ELEMENT > > * Bulk_mesh_pt
The bulk mesh of fluid elements.
double Angle
The contact angle.
void doc_solution(DocInfo &doc_info)
Doc the solution.
TwoLayerSpineMesh< SpineElement< ELEMENT > > * Bulk_mesh_pt
Pointer to the mesh.
void create_volume_constraint_elements()
Create the volume constraint elements.
CapProblem(const bool &hijack_internal)
Constructor: Pass boolean flag to indicate if the volume constraint is applied by hijacking an intern...
double Volume
The volume of the fluid.
void actions_before_newton_convergence_check()
Update the spine mesh after every Newton step.
Mesh * Point_mesh_pt
The mesh for the element at the contact point.
double Ca
The Capillary number.
void parameter_study(const string &dir_name)
Perform a parameter study: Solve problem for a range of contact angles Pass name of output directory ...
Two layer mesh which employs a pseudo-solid node-update strategy. This class is essentially a wrapper...
int interface_upper_face_index_at_boundary(const unsigned &e)
Index of the face of the elements next to the interface in the upper region (always -2)
int interface_lower_face_index_at_boundary(const unsigned &e)
Index of the face of the elements next to the interface in the lower region (always 2)
unsigned long nlower() const
Number of elements in top layer.
FiniteElement *& upper_layer_element_pt(const unsigned long &i)
Access functions for pointers to elements in upper layer.
Vector< FiniteElement * > Lower_layer_element_pt
Vector of pointers to element in the upper layer.
FiniteElement *& lower_layer_element_pt(const unsigned long &i)
Access functions for pointers to elements in bottom layer.
unsigned long ninterface_upper() const
Number of elements in upper layer.
ElasticTwoLayerMesh(const unsigned &nx, const unsigned &ny1, const unsigned &ny2, const double &lx, const double &h1, const double &h2, const bool &periodic_in_x=false, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass number of elements in x-direction, number of elements in y-direction in bottom and ...
Vector< FiniteElement * > Interface_lower_boundary_element_pt
Vector of pointers to the elements adjacent to the interface on the lower layer.
FiniteElement *& interface_lower_boundary_element_pt(const unsigned long &i)
Access functions for pointers to elements in bottom layer.
FiniteElement *& interface_upper_boundary_element_pt(const unsigned long &i)
Access functions for pointers to elements in upper layer.
Vector< FiniteElement * > Upper_layer_element_pt
Vector of pointers to element in the lower layer.
unsigned long nupper() const
Number of elements in upper layer.
unsigned long ninterface_lower() const
Number of elements in top layer.
Vector< FiniteElement * > Interface_upper_boundary_element_pt
Vector of pointers to the element adjacent to the interface on the upper layer.
A class that solves the Navier–Stokes equations to compute the shape of a static interface in a recta...
void create_volume_constraint_elements()
Create the volume constraint elements.
Mesh * Volume_computation_mesh_pt
Storage for the elements that compute the enclosed volume.
ElasticTwoLayerMesh< ELEMENT > * Bulk_mesh_pt
Storage for the bulk mesh.
void doc_solution(DocInfo &doc_info)
Doc the solution.
double Volume
The prescribed volume of the fluid.
PseudoSolidCapProblem(const bool &hijack_internal)
Constructor: Boolean flag indicates if volume constraint is applied by hijacking internal or external...
double Angle
The contact angle.
Mesh * Volume_constraint_mesh_pt
Storage for the volume constraint.
Mesh * Bulk_mesh_pt
Storage for the bulk mesh.
ofstream Trace_file
Trace file.
void create_contact_angle_element()
Create the contact angle element.
ConstitutiveLaw * Constitutive_law_pt
Constitutive law used to determine the mesh deformation.
Mesh * Free_surface_bounding_mesh_pt
Storage for the element bounding the free surface.
void create_free_surface_elements()
Create the free surface elements.
Mesh * Free_surface_mesh_pt
Storage for the free surface mesh.
double Ca
The Capillary number.
void parameter_study(const string &dir_name)
Peform a parameter study: Solve problem for a range of contact angles Pass name of output directory a...
~PseudoSolidCapProblem()
Destructor: clean up memory allocated by the object.
double Pext
The external pressure.
Namespace for phyical parameters.
void wall_unit_normal_fct(const Vector< double > &x, Vector< double > &normal)
Function that specifies the wall unit normal.
double Nu
Pseudo-solid Poisson ratio.
Vector< double > Wall_normal
Direction of the wall normal vector.
int main()
Main driver: Build problem and initiate parameter study.