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-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 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
47 using namespace std;
48 
49 using 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 //============================================================================
73 template<class ELEMENT>
74 class CapProblem : public Problem
75 {
76 
77 public:
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
102  void actions_before_newton_convergence_check() {Bulk_mesh_pt->node_update();}
103 
104  /// Create the volume constraint elements
106 
107  /// Doc the solution
108  void doc_solution(DocInfo& doc_info);
109 
110 private:
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
122  Mesh* Volume_constraint_mesh_pt;
123 
124  /// Trace file
125  ofstream Trace_file;
126 
127  /// Data that is traded for the volume constraint
128  Data* Traded_pressure_data_pt;
129 
130 };
131 
132 
133 
134 //======================================================================
135 /// Constructor
136 //======================================================================
137 template<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
168 Point_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 //========================================================================
255 template<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 //======================================================================
334 template<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  sprintf(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 //========================================================================
387 template<class ELEMENT>
388 void 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  sprintf(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 //========================================================================
451 template <class ELEMENT>
453  public ElasticRectangularQuadMesh<ELEMENT>
454 {
455 
456 public:
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->Boundary_coordinate_exists[4]=true;
538  this->Boundary_coordinate_exists[5]=true;
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->Boundary_coordinate_exists[3]=true;
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->Boundary_coordinate_exists[2]=true;
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->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
706  {return Interface_upper_boundary_element_pt.size();}
707 
708  /// Number of elements in top layer
709  unsigned long ninterface_lower() const
710  {return Interface_lower_boundary_element_pt.size();}
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 
722 private:
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 //============================================================================
748 template<class ELEMENT>
749 class PseudoSolidCapProblem : public Problem
750 {
751 public:
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 
769 private:
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
807  Mesh* Free_surface_mesh_pt;
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 //======================================================================
827 template<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 //==========================================================================
949 template<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 //========================================================================
985 template<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 //========================================================================
1015 template<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 //========================================================================
1097 template<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 //======================================================================
1134 template<class ELEMENT>
1135 void 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  sprintf(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 //========================================================================
1182 template<class ELEMENT>
1183 void PseudoSolidCapProblem<ELEMENT>::doc_solution(DocInfo& doc_info)
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  sprintf(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 //======================================================================
1229 int 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.
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...
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 *& lower_layer_element_pt(const unsigned long &i)
Access functions for pointers to elements in bottom layer.
Vector< FiniteElement * > Lower_layer_element_pt
Vector of pointers to element in the upper layer.
FiniteElement *& interface_upper_boundary_element_pt(const unsigned long &i)
Access functions for pointers to elements in upper 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 ...
FiniteElement *& interface_lower_boundary_element_pt(const unsigned long &i)
Access functions for pointers to elements in bottom layer.
FiniteElement *& upper_layer_element_pt(const unsigned long &i)
Access functions for pointers to elements in upper layer.
Vector< FiniteElement * > Interface_lower_boundary_element_pt
Vector of pointers to the elements adjacent to the interface on the lower 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.