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-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// 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
37using namespace std;
38
39using 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//======================================================================
152template<class ELEMENT>
153class BrethertonElement : public ELEMENT
154{
155
156public:
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
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
328
329 }
330
331
332private:
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
391
392 /// Function pointer to the global function that specifies the
393 /// inflow velocity profile on the global mesh boundary Inflow_boundary
395
396};
397
398
399
400// Note: Specialisation must go into namespace!
401namespace oomph
402{
403
404//=======================================================================
405/// Face geometry of the Bretherton 2D Crouzeix_Raviart spine elements
406//=======================================================================
407template<>
408class 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//=======================================================================
419template<>
420class 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//=======================================================================
431template<>
432class 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//=======================================================================
445template<>
446class 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//======================================================================
463template<class ELEMENT>
464class BrethertonProblem : public Problem
465{
466
467
468public:
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
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
532private:
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//====================================================================
550template<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//========================================================================
725template<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//========================================================================
761template<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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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//=============================================================================
815template<class ELEMENT>
817{
818
819 // Increase maximum residual
820 Problem::Max_residuals=500.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 snprintf(filename, sizeof(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 = Problem::Max_residuals;
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//======================================================================
916int 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,...
"Bretherton element" is a fluid element (of type ELEMENT) for which the (inflow) velocity at those no...
unsigned Inflow_boundary
Number of the inflow boundary in the global mesh.
void(* InflowFctPt)(const Vector< double > &x, Vector< double > &veloc)
Typedef for pointer (global) function that specifies the the inflow.
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...
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...
InflowFctPt Inflow_fct_pt
Function pointer to the global function that specifies the inflow velocity profile on the global mesh...
void reassign_inflow()
For all nodes that are located on specified boundary re-assign the inflow velocity,...
Vector< Data * > Inflow_ext_data
Storage for the external Data that affects the inflow.
void get_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Overloaded Jacobian computation: Computes the Jacobian of the underlying element and then adds the FD...
BrethertonElement()
Constructor: Call the constructor of the underlying element.
DenseMatrix< int > Inflow_ext_data_eqn
Storage for the local equation numbers associated the Data values that affect the inflow.
Bretherton problem.
void activate_inflow_dependency()
Activate the dependency of the inflow velocity on the spine heights at the outflow.
BrethertonProblem()
Constructor:
void actions_after_newton_solve()
Update after solve can remain empty, because the update is performed automatically after every Newton...
void actions_before_newton_convergence_check()
Spine heights/lengths are unknowns in the problem so their values get corrected during each Newton st...
void actions_before_newton_solve()
Update before solve: empty.
void fix_pressure(const unsigned &e, const unsigned &l, const double &pvalue)
Fix pressure value l in element e to value p_value.
ELEMENT * Control_element_pt
Pointer to control element.
void doc_solution(DocInfo &doc_info)
Doc the solution.
void parameter_study(const unsigned &nsteps)
Run a parameter study; perform specified number of steps.
ofstream Trace_file
Trace file.
BrethertonSpineMesh< ELEMENT, SpineLineFluidInterfaceElement< ELEMENT > > * Bulk_mesh_pt
Pointer to bulk mesh.
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