bretherton.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 // The 2D Bretherton problem
27 #include<algorithm>
28 
29 // The oomphlib headers
30 #include "generic.h"
31 #include "navier_stokes.h"
32 #include "fluid_interface.h"
33 
34 // The mesh
35 #include "meshes/bretherton_spine_mesh.h"
36 
37 using namespace std;
38 
39 using namespace oomph;
40 
41 //======================================================================
42 /// Namepspace for global parameters
43 //======================================================================
45 {
46 
47  /// Reynolds number
48  double Re;
49 
50  /// Womersley = Reynolds times Strouhal
51  double ReSt;
52 
53  /// Product of Reynolds and Froude number
54  double ReInvFr;
55 
56  /// Capillary number
57  double Ca;
58 
59  /// Direction of gravity
60  Vector<double> G(2);
61 
62  /// Pointer to film thickness at outflow on the lower wall
63  double* H_lo_pt;
64 
65  /// Pointer to film thickness at outflow on the upper wall
66  double* H_up_pt;
67 
68  /// Pointer to y-position at inflow on the lower wall
69  double* Y_lo_pt;
70 
71  /// Pointer to y-position at inflow on the upper wall
72  double* Y_up_pt;
73 
74  /// Set inflow velocity, based on spine heights at outflow
75  /// and channel width at inflow
76  void inflow(const Vector<double>& x, Vector<double>& veloc)
77  {
78 #ifdef PARANOID
79  std::ostringstream error_stream;
80  bool throw_error=false;
81  if (H_lo_pt==0)
82  {
83  error_stream << "You must set H_lo_pt\n";
84  throw_error = true;
85  }
86  if (H_up_pt==0)
87  {
88  error_stream << "You must set H_up_pt\n";
89  throw_error = true;
90  }
91  if (Y_lo_pt==0)
92  {
93  error_stream << "You must set Y_lo_pt\n";
94  throw_error = true;
95  }
96  if (Y_up_pt==0)
97  {
98  error_stream << "You must set Y_up_pt\n";
99  throw_error = true;
100  }
101 
102  //If one of the errors has occured
103  if(throw_error)
104  {
105  throw OomphLibError(error_stream.str(),
106  OOMPH_CURRENT_FUNCTION,
107  OOMPH_EXCEPTION_LOCATION);
108  }
109 #endif
110 
111 
112  // Get average film thickness far ahead
113  double h_av=0.5*(*H_lo_pt+*H_up_pt);
114 
115  // Get position of upper and lower wall at inflow
116  double y_up=*Y_up_pt;
117  double y_lo=*Y_lo_pt;
118 
119  // Constant in velocity profile
120  double C =6.0*(2.0*h_av+y_lo-y_up)/
121  (y_up*y_up*y_up-y_lo*y_lo*y_lo-h_av*y_up*
122  y_up*y_up+h_av*y_lo*y_lo*y_lo-3.0*y_lo*y_up*y_up+
123  3.0*y_lo*y_lo*y_up+3.0*y_lo*y_up*y_up*h_av-3.0*y_lo*y_lo*y_up*h_av);
124 
125  // y coordinate
126  double y=x[1];
127 
128  // Parallel, parabolic inflow
129  veloc[0]=-1.0+C*(1.0-h_av)*((y_lo-y)*(y_up-y));
130  veloc[1]=0.0;
131  }
132 
133 }
134 
135 
136 
137 
138 //======================================================================
139 /// "Bretherton element" is a fluid element (of type ELEMENT) for which
140 /// the (inflow) velocity at those nodes that are located on a
141 /// specified Mesh boundary is prescribed by Dirichlet boundary
142 /// conditions. The key is that prescribed velocity profile can be
143 /// a function of some external Data -- this dependency must
144 /// be taken into account when computing the element's Jacobian
145 /// matrix.
146 ///
147 /// This element type is useful, for instance, in the Bretherton
148 /// problem, where the parabolic "inflow" profile is a function
149 /// of the film thickness (represented by Spine heights) at the
150 /// "outflow".
151 //======================================================================
152 template<class ELEMENT>
153 class BrethertonElement : public ELEMENT
154 {
155 
156 public:
157 
158  /// Typedef for pointer (global) function that specifies the
159  /// the inflow
160  typedef void (*InflowFctPt)(const Vector<double>& x, Vector<double>& veloc);
161 
162  /// Constructor: Call the constructor of the underlying element
163  BrethertonElement() : ELEMENT() {}
164 
165 
166  /// Activate the dependency of the "inflow" on the external
167  /// data. Pass the vector of pointers to the external Data that affects
168  /// the inflow, the id of the boundary on which the inflow
169  /// condition is to be applied and the function pointer to
170  /// to the global function that defines the inflow
172  const Vector<Data*>& inflow_ext_data,
173  const unsigned& inflow_boundary,
174  InflowFctPt inflow_fct_pt)
175  {
176  // Copy data that affects the inflow
177  unsigned n_ext=inflow_ext_data.size();
178  Inflow_ext_data.resize(n_ext);
179  for (unsigned i=0;i<n_ext;i++)
180  {
181  Inflow_ext_data[i]=inflow_ext_data[i];
182  }
183  // Set inflow boundary
184  Inflow_boundary=inflow_boundary;
185  // Set fct pointer to inflow condition
186  Inflow_fct_pt=inflow_fct_pt;
187  }
188 
189 
190  /// Overload assign local equation numbers: Add the dependency
191  /// on the external Data that affects the inflow profile
192  void assign_local_eqn_numbers(const bool &store_local_dof_pt)
193  {
194  // Call the element's local equation numbering procedure first
195  ELEMENT::assign_local_eqn_numbers(store_local_dof_pt);
196 
197  // Now add the equation numbers for the Data that affects the inflow
198  // profile
199 
200  // Number of local equations so far
201  unsigned local_eqn_count = this->ndof();
202 
203  // Find out max. number of values stored at all Data values
204  // that affect the inflow
205  unsigned max_nvalue=0;
206  unsigned n_ext=Inflow_ext_data.size();
207  for (unsigned i=0;i<n_ext;i++)
208  {
209  // The external Data:
210  Data* data_pt=Inflow_ext_data[i];
211  // Number of values
212  unsigned n_val=data_pt->nvalue();
213  if (n_val>max_nvalue) max_nvalue=n_val;
214  }
215 
216  // Allocate sufficient storage
217  Inflow_ext_data_eqn.resize(n_ext,max_nvalue);
218 
219  //A local queue to store the global equation numbers
220  std::deque<unsigned long> global_eqn_number_queue;
221 
222  // Loop over external Data that affect the "inflow"
223  for (unsigned i=0;i<n_ext;i++)
224  {
225  // The external Data:
226  Data* data_pt=Inflow_ext_data[i];
227 
228  // Loop over number of values:
229  unsigned n_val=data_pt->nvalue();
230  for (unsigned ival=0;ival<n_val;ival++)
231  {
232 
233  // Is it free or pinned?
234  long eqn_number=data_pt->eqn_number(ival);
235  if (eqn_number>=0)
236  {
237  // Add global equation number to local storage
238  global_eqn_number_queue.push_back(eqn_number);
239  // Store local equation number associated with this external dof
240  Inflow_ext_data_eqn(i,ival)=local_eqn_count;
241  // Increment counter for local dofs
242  local_eqn_count++;
243  }
244  else
245  {
246  Inflow_ext_data_eqn(i,ival)=-1;
247  }
248  }
249  }
250 
251  //Now add our global equations numbers to the internal element storage
252  this->add_global_eqn_numbers(global_eqn_number_queue,
253  GeneralisedElement::Dof_pt_deque);
254  }
255 
256 
257  /// Overloaded Jacobian computation: Computes the Jacobian
258  /// of the underlying element and then adds the FD
259  /// operations to evaluate the derivatives w.r.t. the Data values
260  /// that affect the inflow.
261  void get_jacobian(Vector<double>& residuals,
262  DenseMatrix<double>& jacobian)
263  {
264  // Loop over Data values that affect the inflow
265  unsigned n_ext=Inflow_ext_data.size();
266 
267  // Call "normal" jacobian first.
268  ELEMENT::get_jacobian(residuals,jacobian);
269 
270  if (n_ext==0) return;
271 
272  // Get ready for FD operations
273  Vector<double> residuals_plus(residuals.size());
274  double fd_step=1.0e-8;
275 
276  // Loop over Data values that affect the inflow
277  for (unsigned i=0;i<n_ext;i++)
278  {
279  // Get Data item
280  Data* data_pt=Inflow_ext_data[i];
281 
282  // Loop over values
283  unsigned n_val=data_pt->nvalue();
284  for (unsigned ival=0;ival<n_val;ival++)
285  {
286  // Local equation number
287  int local_eqn=Inflow_ext_data_eqn(i,ival);
288 
289  // Dof or pinned?
290  if (local_eqn>=0)
291  {
292  //get pointer to the data value
293  double *value_pt = data_pt->value_pt(ival);
294 
295  //Backup Data value
296  double backup = *value_pt;
297 
298  // Do FD step
299  *value_pt += fd_step;
300 
301  // Re-assign the inflow velocities for nodes in this element
302  reassign_inflow();
303 
304 
305  // Fill in the relevant column in the Jacobian matrix
306  unsigned n_dof = this->ndof();
307  //Zero the residuals
308  for(unsigned idof=0;idof<n_dof;idof++) {residuals_plus[idof] = 0.0;}
309  // Re-compute the element residual
310  this->get_residuals(residuals_plus);
311 
312  for(unsigned idof=0;idof<n_dof;idof++)
313  {
314  jacobian(idof,local_eqn)=
315  (residuals_plus[idof]-residuals[idof])/fd_step;
316  }
317 
318  //Reset spine height
319  *value_pt = backup;
320 
321  }
322  // Note: Re-assignment of inflow is done on the fly during next loop
323  }
324  }
325 
326  // Final re-assign for the inflow velocities for nodes in this element
327  reassign_inflow();
328 
329  }
330 
331 
332 private:
333 
334 
335 
336  /// For all nodes that are located on specified boundary
337  /// re-assign the inflow velocity, using the function pointed to
338  /// by the function pointer
340  {
341  // Loop over all nodes in element -- if they are
342  // on inflow boundary, re-assign their velocities
343  Vector<double> x(2);
344  Vector<double> veloc(2);
345  unsigned n_nod = this->nnode();
346  for (unsigned j=0;j<n_nod;j++)
347  {
348  Node* nod_pt = this->node_pt(j);
349 
350  if(nod_pt->is_on_boundary(Inflow_boundary))
351  {
352 #ifdef PARANOID
353  for (unsigned i=0;i<2;i++)
354  {
355  if (nod_pt->eqn_number(0)>=0)
356  {
357  std::ostringstream error_stream;
358  error_stream
359  << "We're assigning a Dirichlet condition for the "
360  << i << "-th "
361  << "velocity, even though it is not pinned!\n"
362  << "This can't be right! I'm bailing out..."
363  << std::endl;
364 
365  throw OomphLibError(error_stream.str(),
366  OOMPH_CURRENT_FUNCTION,
367  OOMPH_EXCEPTION_LOCATION);
368  }
369  }
370 #endif
371  // Get inflow profile
372  x[0]=nod_pt->x(0);
373  x[1]=nod_pt->x(1);
374  Inflow_fct_pt(x,veloc);
375  nod_pt->set_value(0,veloc[0]);
376  nod_pt->set_value(1,veloc[1]);
377  }
378  }
379  }
380 
381 
382  /// Storage for the external Data that affects the inflow
383  Vector<Data*> Inflow_ext_data;
384 
385  /// Storage for the local equation numbers associated the Data
386  /// values that affect the inflow
387  DenseMatrix<int> Inflow_ext_data_eqn;
388 
389  /// Number of the inflow boundary in the global mesh
390  unsigned Inflow_boundary;
391 
392  /// Function pointer to the global function that specifies the
393  /// inflow velocity profile on the global mesh boundary Inflow_boundary
394  InflowFctPt Inflow_fct_pt;
395 
396 };
397 
398 
399 
400 // Note: Specialisation must go into namespace!
401 namespace oomph
402 {
403 
404 //=======================================================================
405 /// Face geometry of the Bretherton 2D Crouzeix_Raviart spine elements
406 //=======================================================================
407 template<>
408 class FaceGeometry<BrethertonElement<SpineElement<QCrouzeixRaviartElement<2> > > >: public virtual QElement<1,3>
409 {
410  public:
411  FaceGeometry() : QElement<1,3>() {}
412 };
413 
414 
415 
416 //=======================================================================
417 /// Face geometry of the Bretherton 2D Taylor Hood spine elements
418 //=======================================================================
419 template<>
420 class FaceGeometry<BrethertonElement<SpineElement<QTaylorHoodElement<2> > > >: public virtual QElement<1,3>
421 {
422  public:
423  FaceGeometry() : QElement<1,3>() {}
424 };
425 
426 
427 //=======================================================================
428 /// Face geometry of the Face geometry of the
429 /// the Bretherton 2D Crouzeix_Raviart spine elements
430 //=======================================================================
431 template<>
432 class FaceGeometry<FaceGeometry<BrethertonElement<
433  SpineElement<QCrouzeixRaviartElement<2> > > > >: public virtual PointElement
434 {
435  public:
436  FaceGeometry() : PointElement() {}
437 };
438 
439 
440 
441 //=======================================================================
442 /// Face geometry of face geometry of
443 /// the Bretherton 2D Taylor Hood spine elements
444 //=======================================================================
445 template<>
446 class FaceGeometry<FaceGeometry<BrethertonElement<SpineElement<QTaylorHoodElement<2> > > > >: public virtual PointElement
447 {
448  public:
449  FaceGeometry() : PointElement() {}
450 };
451 
452 
453 }
454 
455 /// //////////////////////////////////////////////////////////////////
456 /// //////////////////////////////////////////////////////////////////
457 /// //////////////////////////////////////////////////////////////////
458 
459 
460 //======================================================================
461 /// Bretherton problem
462 //======================================================================
463 template<class ELEMENT>
464 class BrethertonProblem : public Problem
465 {
466 
467 
468 public:
469 
470  /// Constructor:
472 
473  /// Spine heights/lengths are unknowns in the problem so their
474  /// values get corrected during each Newton step. However,
475  /// changing their value does not automatically change the
476  /// nodal positions, so we need to update all of them
478  {
479  // Update
480  Bulk_mesh_pt->node_update();
481 
482  // Apply inflow on boundary 1
483  unsigned ibound=1;
484  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
485 
486  // Coordinate and velocity
487  Vector<double> x(2);
488  Vector<double> veloc(2);
489 
490  // Loop over all nodes
491  for (unsigned inod=0;inod<num_nod;inod++)
492  {
493  // Get nodal position
494  x[0]=mesh_pt()->boundary_node_pt(ibound,inod)->x(0);
495  x[1]=mesh_pt()->boundary_node_pt(ibound,inod)->x(1);
496 
497  // Get inflow profile
499  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(0,veloc[0]);
500  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1,veloc[1]);
501  }
502  }
503 
504  /// Update before solve: empty
506 
507  /// Update after solve can remain empty, because the update
508  /// is performed automatically after every Newton step.
510 
511  /// Fix pressure value l in element e to value p_value
512  void fix_pressure(const unsigned &e, const unsigned &l,
513  const double &pvalue)
514  {
515  //Fix the pressure at that element
516  dynamic_cast<ELEMENT *>(Bulk_mesh_pt->element_pt(e))->
517  fix_pressure(l,pvalue);
518  }
519 
520 
521  /// Activate the dependency of the inflow velocity on the
522  /// spine heights at the outflow
523  void activate_inflow_dependency();
524 
525  /// Run a parameter study; perform specified number of steps
526  void parameter_study(const unsigned& nsteps);
527 
528  /// Doc the solution
529  void doc_solution(DocInfo& doc_info);
530 
531 
532 private:
533 
534  /// Pointer to control element
536 
537  /// Trace file
538  ofstream Trace_file;
539 
540  /// Pointer to bulk mesh
541  BrethertonSpineMesh<ELEMENT,SpineLineFluidInterfaceElement<ELEMENT> >*
543 
544 };
545 
546 
547 //====================================================================
548 /// Problem constructor
549 //====================================================================
550 template<class ELEMENT>
552 {
553 
554  // Number of elements in the deposited film region
555  unsigned nx1=24;
556 
557  // Number of elements in the bottom part of the transition region
558  unsigned nx2=6;
559 
560  // Number of elements in channel region
561  unsigned nx3=12;
562 
563  // Number of elements in the vertical part of the transition region
564  // (=half the number of elements in the liquid filled region ahead
565  // of the finger tip)
566  unsigned nhalf=4;
567 
568  // Number of elements through thickness of deposited film
569  unsigned nh=3;
570 
571  // Thickness of deposited film
572  double h=0.1;
573 
574  // Create wall geom objects
575  GeomObject* lower_wall_pt=new StraightLine(-1.0);
576  GeomObject* upper_wall_pt=new StraightLine( 1.0);
577 
578  // Start coordinate on wall
579  double xi0=-4.0;
580 
581  // End of transition region on wall
582  double xi1=1.5;
583 
584  // End of liquid filled region (inflow) on wall
585  double xi2=5.0;
586 
587  //Now create the mesh
588  Bulk_mesh_pt = new BrethertonSpineMesh<ELEMENT,
589  SpineLineFluidInterfaceElement<ELEMENT> >
590  (nx1,nx2,nx3,nh,nhalf,h,lower_wall_pt,upper_wall_pt,xi0,0.0,xi1,xi2);
591 
592  // Make bulk mesh the global mesh
593  mesh_pt()=Bulk_mesh_pt;
594 
595  // Store the control element
596  Control_element_pt=Bulk_mesh_pt->control_element_pt();
597 
598 
599  // Set pointers to quantities that determine the inflow profile
600 
601  // Film thickness at outflow on lower wall:
603  Bulk_mesh_pt->spine_pt(0)->spine_height_pt()->value_pt(0);
604 
605  // Film thickness at outflow on upper wall:
606  unsigned last_spine=Bulk_mesh_pt->nfree_surface_spines()-1;
608  Bulk_mesh_pt->spine_pt(last_spine)->spine_height_pt()->value_pt(0);
609 
610  // Current y-position on lower wall at inflow
611  unsigned ibound=1;
613  Bulk_mesh_pt->boundary_node_pt(ibound,0)->x_pt(0,1);
614 
615  // Current y-position on upper wall at inflow
616  unsigned nnod=Bulk_mesh_pt->nboundary_node(ibound);
618  Bulk_mesh_pt->boundary_node_pt(ibound,nnod-1)->x_pt(0,1);
619 
620  // Activate dependency of inflow on spine heights at outflow
621  activate_inflow_dependency();
622 
623  // Set the boundary conditions for this problem: All nodes are
624  // free by default -- just pin the ones that have Dirichlet conditions
625  // here
626 
627  // No slip on boundaries 0 1 and 2
628  for(unsigned ibound=0;ibound<=2;ibound++)
629  {
630  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
631  for (unsigned inod=0;inod<num_nod;inod++)
632  {
633  mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
634  mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
635  }
636  }
637 
638  // Uniform, parallel outflow on boundaries 3 and 5
639  for(unsigned ibound=3;ibound<=5;ibound+=2)
640  {
641  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
642  for (unsigned inod=0;inod<num_nod;inod++)
643  {
644  mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
645  mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
646  }
647  }
648 
649  // Pin central spine
650  unsigned central_spine=(Bulk_mesh_pt->nfree_surface_spines()-1)/2;
651  Bulk_mesh_pt->spine_pt(central_spine)->spine_height_pt()->pin(0);
652 
653 
654  // No slip in moving frame on boundaries 0 and 2
655  for (unsigned ibound=0;ibound<=2;ibound+=2)
656  {
657  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
658  for (unsigned inod=0;inod<num_nod;inod++)
659  {
660  // Parallel flow
661  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(0,-1.0);
662  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1, 0.0);
663  }
664  }
665 
666 
667  // Parallel, uniform outflow on boundaries 3 and 5
668  for (unsigned ibound=3;ibound<=5;ibound+=2)
669  {
670  unsigned num_nod=mesh_pt()->nboundary_node(ibound);
671  for (unsigned inod=0;inod<num_nod;inod++)
672  {
673  // Parallel inflow
674  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(0,-1.0);
675  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1, 0.0);
676  }
677  }
678 
679 
680 
681  //Complete the problem setup to make the elements fully functional
682 
683  //Loop over the elements in the layer
684  unsigned n_bulk=Bulk_mesh_pt->nbulk();
685  for(unsigned i=0;i<n_bulk;i++)
686  {
687  //Cast to a fluid element
688  ELEMENT *el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->
689  bulk_element_pt(i));
690  //Set the Reynolds number, etc
691  el_pt->re_pt() = &Global_Physical_Variables::Re;
692  el_pt->re_st_pt() = &Global_Physical_Variables::ReSt;
693  el_pt->re_invfr_pt() = &Global_Physical_Variables::ReInvFr;
694  el_pt->g_pt() = &Global_Physical_Variables::G;
695  }
696 
697  //Loop over 1D interface elements and set capillary number
698  unsigned interface_element_pt_range = Bulk_mesh_pt->ninterface_element();
699  for(unsigned i=0;i<interface_element_pt_range;i++)
700  {
701  //Cast to a interface element
702  SpineLineFluidInterfaceElement<ELEMENT>* el_pt =
703  dynamic_cast<SpineLineFluidInterfaceElement<ELEMENT>*>
704  (Bulk_mesh_pt->interface_element_pt(i));
705 
706  //Set the Capillary number
707  el_pt->ca_pt() = &Global_Physical_Variables::Ca;
708  }
709 
710  //Update the nodal positions, so that the domain is correct
711  //(and therefore the repeated node test passes)
712  Bulk_mesh_pt->node_update();
713 
714  //Do equation numbering
715  cout << "Number of unknowns: " << assign_eqn_numbers() << std::endl;
716 
717 }
718 
719 
720 
721 //========================================================================
722 /// Activate the dependency of the inflow velocity on the
723 /// spine heights at the outflow
724 //========================================================================
725 template<class ELEMENT>
727 {
728 
729  // Spine heights that affect the inflow
730  Vector<Data*> outflow_spines(2);
731 
732  // First spine
733  outflow_spines[0]=Bulk_mesh_pt->spine_pt(0)->spine_height_pt();
734 
735  // Last proper spine
736  unsigned last_spine=Bulk_mesh_pt->nfree_surface_spines()-1;
737  outflow_spines[1]=Bulk_mesh_pt->spine_pt(last_spine)->spine_height_pt();;
738 
739 
740 
741  /// Loop over elements on inflow boundary (1)
742  unsigned ibound=1;
743  unsigned nel=Bulk_mesh_pt->nboundary_element(ibound);
744  for (unsigned e=0;e<nel;e++)
745  {
746  // Get pointer to element
747  ELEMENT* el_pt=dynamic_cast<ELEMENT*>(Bulk_mesh_pt->
748  boundary_element_pt(ibound,e));
749  // Activate depency on inflow
750  el_pt->activate_inflow_dependency_on_external_data(
751  outflow_spines,ibound,&Global_Physical_Variables::inflow);
752  }
753 
754 }
755 
756 
757 
758 //========================================================================
759 /// Doc the solution
760 //========================================================================
761 template<class ELEMENT>
763 {
764 
765  ofstream some_file;
766  char filename[100];
767 
768  // Number of plot points
769  unsigned npts=5;
770 
771  // Control coordinate: At bubble tip
772  Vector<double> s(2);
773  s[0]=1.0;
774  s[1]=1.0;
775 
776  // Last proper spine
777  unsigned last_spine=Bulk_mesh_pt->nfree_surface_spines()-1;
778 
779  // Doc
780  Trace_file << " " << Global_Physical_Variables::Ca;
781  Trace_file << " " << Bulk_mesh_pt->spine_pt(0)->height();
782  Trace_file << " " << Bulk_mesh_pt->spine_pt(last_spine)->height();
783  Trace_file << " " << 1.3375*pow(Global_Physical_Variables::Ca,2.0/3.0);
784  Trace_file << " " << -Control_element_pt->interpolated_p_nst(s)*
786  Trace_file << " " << 1.0+3.8*pow(Global_Physical_Variables::Ca,2.0/3.0);
787  Trace_file << " " << Control_element_pt->interpolated_u_nst(s,0);
788  Trace_file << " " << Control_element_pt->interpolated_u_nst(s,1);
789  Trace_file << std::endl;
790 
791 
792  // Output solution
793  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
794  doc_info.number());
795  some_file.open(filename);
796  Bulk_mesh_pt->output(some_file,npts);
797  some_file.close();
798 
799 
800  // Output boundaries
801  sprintf(filename,"%s/boundaries%i.dat",doc_info.directory().c_str(),
802  doc_info.number());
803  some_file.open(filename);
804  Bulk_mesh_pt->output_boundaries(some_file);
805  some_file.close();
806 
807 }
808 
809 
810 
811 
812 //=============================================================================
813 /// Parameter study
814 //=============================================================================
815 template<class ELEMENT>
817 {
818 
819  // Increase maximum residual
820  Problem::Max_residuals=100.0;
821 
822  // Set output directory
823  DocInfo doc_info;
824  doc_info.set_directory("RESLT");
825  doc_info.number()=0;
826 
827 
828  // Open trace file
829  char filename[100];
830  sprintf(filename,"%s/trace.dat",doc_info.directory().c_str());
831  Trace_file.open(filename);
832 
833  Trace_file << "VARIABLES=\"Ca\",";
834  Trace_file << "\"h<sub>bottom</sub>\",\"h<sub>too</sub>\",";
835  Trace_file << "\"h<sub>Bretherton</sub>\",\"p<sub>tip</sub>\",";
836  Trace_file << "\"p<sub>tip (Bretherton)</sub>\",\"u<sub>stag</sub>\",";
837  Trace_file << "\"v<sub>stag</sub>\"";
838  Trace_file << "\"<greek>a</greek><sub>bottom</sub>\",";
839  Trace_file << "\"<greek>a</greek><sub>top</sub>\"";
840  Trace_file << std::endl;
841 
842  // Initial scaling factor for Ca reduction
843  double factor=2.0;
844 
845  //Doc initial solution
846  doc_solution(doc_info);
847 
848 //Loop over the steps
849  for (unsigned step=1;step<=nsteps;step++)
850  {
851  cout << std::endl << "STEP " << step << std::endl;
852 
853  // Newton method tends to converge is initial stays bounded below
854  // a certain tolerance: This is cheaper to check than restarting
855  // the Newton method after divergence (we'd also need to back up
856  // the previous solution)
857  double maxres=100.0;
858  while (true)
859  {
860  cout << "Checking max. res for Ca = "
861  << Global_Physical_Variables::Ca << ". Max. residual: ";
862 
863  // Check the maximum residual
864  DoubleVector residuals;
865  actions_before_newton_solve();
866  actions_before_newton_convergence_check();
867  get_residuals(residuals);
868  double max_res=residuals.max();
869  cout << max_res;
870 
871  // Check what to do
872  if (max_res>maxres)
873  {
874  cout << ". Too big!" << std::endl;
875  // Reset the capillary number
877  // Reduce the factor
878  factor=1.0+(factor-1.0)/1.5;
879  cout << "New reduction factor: " << factor << std::endl;
880  // Reduce the capillary number
882  // Try again
883  continue;
884  }
885  // Looks promising: Let's proceed to solve
886  else
887  {
888  cout << ". OK" << std::endl << std::endl;
889  // Break out of the Ca adjustment loop
890  break;
891  }
892  }
893 
894  //Solve
895  cout << "Solving for capillary number: "
896  << Global_Physical_Variables::Ca << std::endl;
897  newton_solve();
898 
899  // Doc solution
900  doc_info.number()++;
901  doc_solution(doc_info);
902 
903  // Reduce the capillary number
905  }
906 
907 }
908 
909 
910 
911 //======================================================================
912 /// Driver code for unsteady two-layer fluid problem. If there are
913 /// any command line arguments, we regard this as a validation run
914 /// and perform only a single step.
915 //======================================================================
916 int main(int argc, char* argv[])
917 {
918 
919 
920  // Store command line arguments
921  CommandLineArgs::setup(argc,argv);
922 
923  // Set physical parameters:
924 
925  //Set direction of gravity: Vertically downwards
928 
929  // Womersley number = Reynolds number (St = 1)
932 
933  // The Capillary number
935 
936  // Re/Fr -- a measure of gravity...
938 
939  //Set up the problem
941 
942  // Self test:
943  problem.self_test();
944 
945  // Number of steps:
946  unsigned nstep;
947  if (CommandLineArgs::Argc>1)
948  {
949  // Validation run: Just one step
950  nstep=2;
951  }
952  else
953  {
954  // Full run otherwise
955  nstep=30;
956  }
957 
958  // Run the parameter study: Perform nstep steps
959  problem.parameter_study(nstep);
960 
961 }
962 
int main(int argc, char *argv[])
Driver code for unsteady two-layer fluid problem. If there are any command line arguments,...
Definition: bretherton.cc:916
"Bretherton element" is a fluid element (of type ELEMENT) for which the (inflow) velocity at those no...
Definition: bretherton.cc:154
unsigned Inflow_boundary
Number of the inflow boundary in the global mesh.
Definition: bretherton.cc:390
void assign_local_eqn_numbers(const bool &store_local_dof_pt)
Overload assign local equation numbers: Add the dependency on the external Data that affects the infl...
Definition: bretherton.cc:192
void activate_inflow_dependency_on_external_data(const Vector< Data * > &inflow_ext_data, const unsigned &inflow_boundary, InflowFctPt inflow_fct_pt)
Activate the dependency of the "inflow" on the external data. Pass the vector of pointers to the exte...
Definition: bretherton.cc:171
InflowFctPt Inflow_fct_pt
Function pointer to the global function that specifies the inflow velocity profile on the global mesh...
Definition: bretherton.cc:394
void reassign_inflow()
For all nodes that are located on specified boundary re-assign the inflow velocity,...
Definition: bretherton.cc:339
Vector< Data * > Inflow_ext_data
Storage for the external Data that affects the inflow.
Definition: bretherton.cc:383
void get_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Overloaded Jacobian computation: Computes the Jacobian of the underlying element and then adds the FD...
Definition: bretherton.cc:261
BrethertonElement()
Constructor: Call the constructor of the underlying element.
Definition: bretherton.cc:163
DenseMatrix< int > Inflow_ext_data_eqn
Storage for the local equation numbers associated the Data values that affect the inflow.
Definition: bretherton.cc:387
////////////////////////////////////////////////////////////////// //////////////////////////////////...
Definition: bretherton.cc:465
void activate_inflow_dependency()
Activate the dependency of the inflow velocity on the spine heights at the outflow.
Definition: bretherton.cc:726
BrethertonProblem()
Constructor:
Definition: bretherton.cc:551
void actions_after_newton_solve()
Update after solve can remain empty, because the update is performed automatically after every Newton...
Definition: bretherton.cc:509
void actions_before_newton_convergence_check()
Spine heights/lengths are unknowns in the problem so their values get corrected during each Newton st...
Definition: bretherton.cc:477
void actions_before_newton_solve()
Update before solve: empty.
Definition: bretherton.cc:505
void fix_pressure(const unsigned &e, const unsigned &l, const double &pvalue)
Fix pressure value l in element e to value p_value.
Definition: bretherton.cc:512
ELEMENT * Control_element_pt
Pointer to control element.
Definition: bretherton.cc:535
void doc_solution(DocInfo &doc_info)
Doc the solution.
Definition: bretherton.cc:762
void parameter_study(const unsigned &nsteps)
Run a parameter study; perform specified number of steps.
Definition: bretherton.cc:816
ofstream Trace_file
Trace file.
Definition: bretherton.cc:538
BrethertonSpineMesh< ELEMENT, SpineLineFluidInterfaceElement< ELEMENT > > * Bulk_mesh_pt
Pointer to bulk mesh.
Definition: bretherton.cc:542
Namepspace for global parameters.
Definition: bretherton.cc:45
double ReSt
Womersley = Reynolds times Strouhal.
Definition: bretherton.cc:51
void inflow(const Vector< double > &x, Vector< double > &veloc)
Set inflow velocity, based on spine heights at outflow and channel width at inflow.
Definition: bretherton.cc:76
double * H_lo_pt
Pointer to film thickness at outflow on the lower wall.
Definition: bretherton.cc:63
Vector< double > G(2)
Direction of gravity.
double * Y_up_pt
Pointer to y-position at inflow on the upper wall.
Definition: bretherton.cc:72
double * H_up_pt
Pointer to film thickness at outflow on the upper wall.
Definition: bretherton.cc:66
double * Y_lo_pt
Pointer to y-position at inflow on the lower wall.
Definition: bretherton.cc:69
double Ca
Capillary number.
Definition: bretherton.cc:57
double ReInvFr
Product of Reynolds and Froude number.
Definition: bretherton.cc:54
double Re
Reynolds number.
Definition: bretherton.cc:48