adaptive_interface.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//A driver program to solve the problem of a cylinder rotating near a free
27//surface
28
29#include "generic.h"
30#include "navier_stokes.h"
31#include "solid.h"
32#include "fluid_interface.h"
33
34using namespace oomph;
35
36
37//=start_of_namespace================================================
38/// Namespace for physical parameters
39//===================================================================
41{
42
43 /// Pseudo-solid Poisson ratio
44 double Nu=0.1;
45
46 /// Direction of the wall normal vector
47 Vector<double> Wall_normal;
48
49 /// Function that specifies the wall unit normal
50 void wall_unit_normal_fct(const Vector<double> &x,
51 Vector<double> &normal)
52 {
53 normal=Wall_normal;
54 }
55
56} // end_of_namespace
57
58
59//My own Ellipse class
60class GeneralEllipse : public GeomObject
61{
62private:
63 //Internal data to store the centre and semi-axes
65
66public:
67
68 //Constructor
69 GeneralEllipse(const double &centre_x, const double &centre_y,
70 const double &a, const double &b)
71 : GeomObject(1,2), centre_x_pt(0), centre_y_pt(0), a_pt(0), b_pt(0)
72 {
73 centre_x_pt = new double(centre_x);
74 centre_y_pt = new double(centre_y);
75 a_pt = new double(a);
76 b_pt = new double(b);
77 }
78
79 //Destructor
81 {
82 delete centre_x_pt;
83 delete centre_y_pt;
84 delete a_pt;
85 delete b_pt;
86 }
87
88 //Return the position
89 void position(const Vector<double> &xi, Vector<double> &r) const
90 {
91 r[0] = *centre_x_pt + *a_pt*cos(xi[0]);
92 r[1] = *centre_y_pt + *b_pt*sin(xi[0]);
93 }
94};
95
96
97//A Domain
98class CylinderAndInterfaceDomain : public Domain
99{
100public:
102
103private:
104
109
110
111 /// Geometric object that represents the rotating cylinder
112 GeomObject* Cylinder_pt;
113
114public:
115
116 //Constructor, pass the length and height of the domain
117 CylinderAndInterfaceDomain(const double &Length, const double &Height)
118 {
119
120 centre_x = Length/2.0;
121 centre_y = Height/2.0; //3.0*Height/4.0;
122 //Create a new ellipse object to represent the rotating cylinder
123 Cylinder_pt = new GeneralEllipse(centre_x,centre_y,0.2*Height,0.2*Height);
124
125 //Set some basic coordinates
126
127 Lower_left[0] = 0.0;
128 Lower_left[1] = 0.0;
129
130 Upper_left[0] = 0.0;
131 Upper_left[1] = Height;
132
133 Lower_right[0] = Length;
134 Lower_right[1] = 0.0;
135
136 Upper_right[0] = Length;
137 Upper_right[1] = Height;
138
139
140 //Let's just do some mid coordinates
141 Lower_mid_left[0] = Length/10.0;
142 Lower_mid_left[1] = 0.0;
143
144 Upper_mid_left[0] = Length/10.0;
145 Upper_mid_left[1] = Height;
146
147 Vector<double> xi(1), f(2);
148 xi[0] = -3.0*atan(1.0);
149 Cylinder_pt->position(xi,f);
150
151 Lower_centre_left[0] = f[0];
152 Lower_centre_left[1] = f[1];
153
154 xi[0] = 3.0*atan(1.0);
155 Cylinder_pt->position(xi,f);
156
157 Upper_centre_left[0] = f[0];
158 Upper_centre_left[1] = f[1];
159
160
161 Lower_mid_right[0] = 9.0*Length/10.0;
162 Lower_mid_right[1] = 0.0;
163
164 Upper_mid_right[0] = 9.0*Length/10.0;
165 Upper_mid_right[1] = Height;
166
167
168 xi[0] = -1.0*atan(1.0);
169 Cylinder_pt->position(xi,f);
170
171 Lower_centre_right[0] = f[0];
172 Lower_centre_right[1] = f[1];
173
174 xi[0] = 1.0*atan(1.0);
175 Cylinder_pt->position(xi,f);
176
177 Upper_centre_right[0] = f[0];
178 Upper_centre_right[1] = f[1];
179
180 //There are six macro elements
181 Macro_element_pt.resize(6);
182
183 // Build the macro elements
184 for (unsigned i=0;i<6;i++)
185 {Macro_element_pt[i]= new QMacroElement<2>(this,i);}
186 }
187
188 // Destructor: Empty; cleanup done in base class
190
191 //Private little interpolation problem
192 void linear_interpolate(double Left[2], double Right[2],
193 const double &s, Vector<double> &f)
194 {
195 for(unsigned i=0;i<2;i++)
196 {
197 f[i] = Left[i] + (Right[i] - Left[i])*0.5*(s+1.0);
198 }
199 }
200
201
202
203 // Sort out the vector representation of the i-th macro element
204 void macro_element_boundary(const unsigned &time,
205 const unsigned &m,
206 const unsigned &direction,
207 const Vector<double> &s,
208 Vector<double>& f)
209 {
210
211 using namespace QuadTreeNames;
212
213#ifdef WARN_ABOUT_SUBTLY_CHANGED_OOMPH_INTERFACES
214 // Warn about time argument being moved to the front
215 OomphLibWarning(
216 "Order of function arguments has changed between versions 0.8 and 0.85",
217 "CylinderAndInterfaceDomain::macro_element_boundary(...)",
218 OOMPH_EXCEPTION_LOCATION);
219#endif
220
221 Vector<double> xi(1);
222
223 //Switch on the macro element
224 switch(m)
225 {
226 //Macro element 0, is the left-hand film
227 case 0:
228
229 switch(direction)
230 {
231 case N:
233 break;
234
235 case S:
237 break;
238
239 case W:
241 break;
242
243 case E:
245 break;
246
247 default:
248 std::ostringstream error_stream;
249 error_stream << "Direction is incorrect: " << direction << std::endl;
250
251 throw OomphLibError(error_stream.str(),
252 OOMPH_CURRENT_FUNCTION,
253 OOMPH_EXCEPTION_LOCATION);
254 }
255
256 break;
257
258 //Macro element 1, is immediately left of the cylinder
259 case 1:
260
261 switch(direction)
262 {
263 case N:
265 break;
266
267 case S:
269 break;
270
271 case W:
273 break;
274
275 case E:
276 xi[0] = 5.0*atan(1.0) - 2.0*atan(1.0)*0.5*(1.0+s[0]);
277 Cylinder_pt->position(xi,f);
278 break;
279
280 default:
281 std::ostringstream error_stream;
282 error_stream << "Direction is incorrect: " << direction << std::endl;
283
284 throw OomphLibError(error_stream.str(),
285 OOMPH_CURRENT_FUNCTION,
286 OOMPH_EXCEPTION_LOCATION);
287 }
288
289 break;
290
291 //Macro element 2, is immediately above the cylinder
292 case 2:
293
294 switch(direction)
295 {
296 case N:
298 break;
299
300 case S:
301 xi[0] = 3.0*atan(1.0) - 2.0*atan(1.0)*0.5*(1.0+s[0]);
302 Cylinder_pt->position(xi,f);
303 break;
304
305 case W:
307 break;
308
309 case E:
311 break;
312
313 default:
314 std::ostringstream error_stream;
315 error_stream << "Direction is incorrect: " << direction << std::endl;
316
317 throw OomphLibError(error_stream.str(),
318 OOMPH_CURRENT_FUNCTION,
319 OOMPH_EXCEPTION_LOCATION);
320 }
321
322 break;
323
324 //Macro element 3, is immediately right of the cylinder
325 case 3:
326
327 switch(direction)
328 {
329 case N:
331 break;
332
333 case S:
335 break;
336
337 case W:
338 xi[0] = -atan(1.0) + 2.0*atan(1.0)*0.5*(1.0+s[0]);
339 Cylinder_pt->position(xi,f);
340 break;
341
342 case E:
344 break;
345
346 default:
347 std::ostringstream error_stream;
348 error_stream << "Direction is incorrect: " << direction << std::endl;
349
350 throw OomphLibError(error_stream.str(),
351 OOMPH_CURRENT_FUNCTION,
352 OOMPH_EXCEPTION_LOCATION);
353 }
354
355 break;
356
357 //Macro element 4, is immediately below cylinder
358 case 4:
359
360 switch(direction)
361 {
362 case N:
363 //linear_interpolate(Lower_centre_left,Lower_centre_right,s[0],f);
364 xi[0] = -3.0*atan(1.0) + 2.0*atan(1.0)*0.5*(1.0+s[0]);
365 Cylinder_pt->position(xi,f);
366 break;
367
368 case S:
370 break;
371
372 case W:
374 break;
375
376 case E:
378 break;
379
380 default:
381 std::ostringstream error_stream;
382 error_stream << "Direction is incorrect: " << direction << std::endl;
383
384 throw OomphLibError(error_stream.str(),
385 OOMPH_CURRENT_FUNCTION,
386 OOMPH_EXCEPTION_LOCATION);
387 }
388
389 break;
390
391 //Macro element 5, is right film
392 case 5:
393
394 switch(direction)
395 {
396 case N:
398 break;
399
400 case S:
402 break;
403
404 case W:
406 break;
407
408 case E:
410 break;
411
412 default:
413 std::ostringstream error_stream;
414 error_stream << "Direction is incorrect: " << direction << std::endl;
415
416 throw OomphLibError(error_stream.str(),
417 OOMPH_CURRENT_FUNCTION,
418 OOMPH_EXCEPTION_LOCATION);
419 }
420
421 break;
422
423 default:
424 std::ostringstream error_stream;
425 error_stream << "Wrong domain number: " << m<< std::endl;
426
427 throw OomphLibError(error_stream.str(),
428 OOMPH_CURRENT_FUNCTION,
429 OOMPH_EXCEPTION_LOCATION);
430 }
431
432 }
433
434};
435
436//Now I need to actually create a Mesh
437template<class ELEMENT>
438class CylinderAndInterfaceMesh : public virtual SolidMesh
439{
440 double Height;
441
442protected:
443 //Pointer to the domain
445
446public:
447
448 //Access function to the domain
450
451 //Constructor,
452 CylinderAndInterfaceMesh(const double &length, const double &height,
453 TimeStepper* time_stepper_pt) : Height(height)
454 {
455 //Create the domain
456 Domain_pt = new CylinderAndInterfaceDomain(length,height);
457
458 //Initialise the node counter
459 unsigned node_count=0;
460 //Vectors Used to get data from domains
461 Vector<double> s(2), r(2);
462
463 //Setup temporary storage for the Node
464 Vector<Node *> Tmp_node_pt;
465
466 //Now blindly loop over the macro elements and associate and finite
467 //element with each
468 unsigned Nmacro_element = Domain_pt->nmacro_element();
469 for(unsigned e=0;e<Nmacro_element;e++)
470 {
471 //Create the FiniteElement and add to the Element_pt Vector
472 Element_pt.push_back(new ELEMENT);
473
474 //Read out the number of linear points in the element
475 unsigned Np =
476 dynamic_cast<ELEMENT*>(finite_element_pt(e))->nnode_1d();
477
478 //Loop over nodes in the column
479 for(unsigned l1=0;l1<Np;l1++)
480 {
481 //Loop over the nodes in the row
482 for(unsigned l2=0;l2<Np;l2++)
483 {
484 //Allocate the memory for the node
485 Tmp_node_pt.push_back(finite_element_pt(e)->
486 construct_node(l1*Np+l2,time_stepper_pt));
487
488 //Read out the position of the node from the macro element
489 s[0] = -1.0 + 2.0*(double)l2/(double)(Np-1);
490 s[1] = -1.0 + 2.0*(double)l1/(double)(Np-1);
491 Domain_pt->macro_element_pt(e)->macro_map(s,r);
492
493 //Set the position of the node
494 Tmp_node_pt[node_count]->x(0) = r[0];
495 Tmp_node_pt[node_count]->x(1) = r[1];
496
497 //Increment the node number
498 node_count++;
499 }
500 }
501 } //End of loop over macro elements
502
503 //Now the elements have been created, but there will be nodes in
504 //common, need to loop over the common edges and sort it, by reassigning
505 //pointers and the deleting excess nodes
506
507 //Read out the number of linear points in the element
508 unsigned Np =
509 dynamic_cast<ELEMENT*>(finite_element_pt(0))->nnode_1d();
510
511 //DelaunayEdge between Elements 0 and 1
512 for(unsigned n=0;n<Np;n++)
513 {
514 //Set the nodes in element 1 to be the same as in element 0
515 finite_element_pt(1)->node_pt(Np*n)
516 = finite_element_pt(0)->node_pt(n*Np+Np-1);
517
518 //Remove the nodes in element 1 from the temporaray node list
519 delete Tmp_node_pt[Np*Np + Np*n];
520 Tmp_node_pt[Np*Np + Np*n] = 0;
521 }
522
523 //DelaunayEdge between Elements 1 and 2
524 for(unsigned n=0;n<Np;n++)
525 {
526 //Set the nodes in element 2 to be the same as in element 1
527 finite_element_pt(2)->node_pt(n*Np)
528 = finite_element_pt(1)->node_pt((Np-1)*Np+Np-1-n);
529
530 //Remove the nodes in element 2 from the temporaray node list
531 delete Tmp_node_pt[2*Np*Np + n*Np];
532 Tmp_node_pt[2*Np*Np + n*Np] = 0;
533 }
534
535 //DelaunayEdge between Elements 1 and 4
536 for(unsigned n=0;n<Np;n++)
537 {
538 //Set the nodes in element 4 to be the same as in element 1
539 finite_element_pt(4)->node_pt(n*Np)
540 = finite_element_pt(1)->node_pt(n);
541
542 //Remove the nodes in element 2 from the temporaray node list
543 delete Tmp_node_pt[4*Np*Np + n*Np];
544 Tmp_node_pt[4*Np*Np + n*Np] = 0;
545 }
546
547 //DelaunayEdge between Element 2 and 3
548 for(unsigned n=0;n<Np;n++)
549 {
550 //Set the nodes in element 3 to be the same as in element 2
551 finite_element_pt(3)->node_pt(Np*(Np-1)+n)
552 = finite_element_pt(2)->node_pt(Np*n+Np-1);
553
554 //Remove the nodes in element 3 from the temporaray node list
555 delete Tmp_node_pt[3*Np*Np + Np*(Np-1)+n];
556 Tmp_node_pt[3*Np*Np + Np*(Np-1)+n] = 0;
557 }
558
559
560 //DelaunayEdge between Element 4 and 3
561 for(unsigned n=0;n<Np;n++)
562 {
563 //Set the nodes in element 3 to be the same as in element 4
564 finite_element_pt(3)->node_pt(n)
565 = finite_element_pt(4)->node_pt(Np*(Np-n-1)+Np-1);
566
567 //Remove the nodes in element 3 from the temporaray node list
568 delete Tmp_node_pt[3*Np*Np + n];
569 Tmp_node_pt[3*Np*Np + n] = 0;
570 }
571
572
573 //DelaunayEdge between Element 3 and 5
574 for(unsigned n=0;n<Np;n++)
575 {
576 //Set the nodes in element 5 to be the same as in element 3
577 finite_element_pt(5)->node_pt(n*Np)
578 = finite_element_pt(3)->node_pt(Np*n+Np-1);
579
580 //Remove the nodes in element 5 from the temporaray node list
581 delete Tmp_node_pt[5*Np*Np + n*Np];
582 Tmp_node_pt[5*Np*Np + n*Np] = 0;
583 }
584
585 //Now set the actual true nodes
586 for(unsigned n=0;n<node_count;n++)
587 {
588 if(Tmp_node_pt[n]!=0) {Node_pt.push_back(Tmp_node_pt[n]);}
589 }
590
591
592
593 //Finally set the nodes on the boundaries
594 set_nboundary(5);
595
596 for(unsigned n=0;n<Np;n++)
597 {
598 //Left hand side
599 Node* temp_node_pt = finite_element_pt(0)->node_pt(n*Np);
600 this->convert_to_boundary_node(temp_node_pt);
601 add_boundary_node(3,temp_node_pt);
602
603
604 //Right hand side
605 temp_node_pt = finite_element_pt(5)->node_pt(n*Np+Np-1);
606 this->convert_to_boundary_node(temp_node_pt);
607 add_boundary_node(1,temp_node_pt);
608
609 //LH part of lower boundary
610 temp_node_pt = finite_element_pt(0)->node_pt(n);
611 this->convert_to_boundary_node(temp_node_pt);
612 add_boundary_node(0,temp_node_pt);
613
614 //First part of upper boundary
615 temp_node_pt = finite_element_pt(0)->node_pt(Np*(Np-1)+n);
616 this->convert_to_boundary_node(temp_node_pt);
617 add_boundary_node(2,temp_node_pt);
618
619 //First part of hole boundary
620 temp_node_pt = finite_element_pt(4)->node_pt(Np*(Np-1)+n);
621 this->convert_to_boundary_node(temp_node_pt);
622 add_boundary_node(4,temp_node_pt);
623 }
624
625 for(unsigned n=1;n<Np;n++)
626 {
627 //Middle of lower boundary
628 Node* temp_node_pt = finite_element_pt(4)->node_pt(n);
629 this->convert_to_boundary_node(temp_node_pt);
630 add_boundary_node(0,temp_node_pt);
631
632 //Middle of upper boundary
633 temp_node_pt = finite_element_pt(2)->node_pt(Np*(Np-1)+n);
634 this->convert_to_boundary_node(temp_node_pt);
635 add_boundary_node(2,temp_node_pt);
636
637 //Next part of hole
638 temp_node_pt = finite_element_pt(3)->node_pt(n*Np);
639 this->convert_to_boundary_node(temp_node_pt);
640 add_boundary_node(4,temp_node_pt);
641 }
642
643 for(unsigned n=1;n<Np;n++)
644 {
645 //Final part of lower boundary
646 Node* temp_node_pt = finite_element_pt(5)->node_pt(n);
647 this->convert_to_boundary_node(temp_node_pt);
648 add_boundary_node(0,temp_node_pt);
649
650 //Middle of upper boundary
651 temp_node_pt = finite_element_pt(5)->node_pt(Np*(Np-1)+n);
652 this->convert_to_boundary_node(temp_node_pt);
653 add_boundary_node(2,temp_node_pt);
654
655 //Next part of hole
656 temp_node_pt = finite_element_pt(2)->node_pt(Np-n-1);
657 this->convert_to_boundary_node(temp_node_pt);
658 add_boundary_node(4,temp_node_pt);
659 }
660
661 for(unsigned n=1;n<Np-1;n++)
662 {
663 //Final part of hole
664 Node* temp_node_pt = finite_element_pt(1)->node_pt(Np*(Np-n-1)+Np-1);
665 this->convert_to_boundary_node(temp_node_pt);
666 add_boundary_node(4,temp_node_pt);
667 }
668
669 //Now loop over all the nodes and set their Lagrangian coordinates
670 unsigned Nnode = nnode();
671 for(unsigned n=0;n<Nnode;n++)
672 {
673 //Cast node to an elastic node
674 SolidNode* temp_pt = static_cast<SolidNode*>(Node_pt[n]);
675 for(unsigned i=0;i<2;i++)
676 {temp_pt->xi(i) = temp_pt->x(i);}
677 }
678 }
679
680
681};
682
683//Now let's do the adaptive mesh
684template<class ELEMENT>
686 public CylinderAndInterfaceMesh<ELEMENT>, public RefineableQuadMesh<ELEMENT>
687{
688public:
689
690 // Constructor
691 RefineableCylinderAndInterfaceMesh(const double &length, const double &height,
692 TimeStepper* time_stepper_pt) :
693 CylinderAndInterfaceMesh<ELEMENT>(length,height,time_stepper_pt)
694 {
695
696 // Nodal positions etc. were created in constructor for
697 // Cylinder...<...>. Need to setup adaptive information.
698
699 // Loop over all elements and set macro element pointer
700 for (unsigned e=0;e<6;e++)
701 {
702 dynamic_cast<ELEMENT*>(this->element_pt(e))->
703 set_macro_elem_pt(this->Domain_pt->macro_element_pt(e));
704 }
705
706 // Setup quadtree forest for mesh refinement
707 this->setup_quadtree_forest();
708
709 // Setup the boundary element info
710 this->setup_boundary_element_info();
711
712 }
713
714
715 /// Destructor: Empty
717
718
719};
720
721template<class ELEMENT>
723{
724private:
725 double Length, Height;
726
727 //Constitutive law used to determine the mesh deformation
728 ConstitutiveLaw *Constitutive_law_pt;
729
731
732public:
733
734 double Re, Ca, ReInvFr, Bo;
735
736 double Omega;
737
738 double Volume;
739
740 double Angle;
741
742 Vector<double> G;
743
744 /// Constructor: Pass flag to indicate if you want
745 /// a constant source function or the tanh profile.
746 RefineableRotatingCylinderProblem(const double &length, const double &height);
747
748 /// Update the problem specs after solve (empty)
750
751 /// Update the problem specs before solve:
753
754 /// Strip off the interface before adaptation
760
761 void actions_after_adapt() {finish_problem_setup(); this->rebuild_global_mesh();}
762
763 /// Complete problem setup: Setup element-specific things
764 /// (source fct pointers etc.)
766
767 //Access function for the mesh
769
770 //Access function for surface mesh
772
773 //Access function for point mesh
775
776 /// The volume constraint mesh
778
780
781 void solve();
782
783 /// Create the volume constraint elements
785 {
786 //The single volume constraint element
787 VolumeConstraintElement* vol_constraint_element =
788 new VolumeConstraintElement(&Volume,Traded_pressure_data_pt,0);
789 Volume_constraint_mesh_pt->add_element_pt(vol_constraint_element);
790
791 //Loop over all boundaries (or just 1 and 2 why?)
792 for(unsigned b=0;b<4;b++)
793 {
794 // How many bulk fluid elements are adjacent to boundary b?
795 unsigned n_element = Bulk_mesh_pt->nboundary_element(b);
796
797 // Loop over the bulk fluid elements adjacent to boundary b?
798 for(unsigned e=0;e<n_element;e++)
799 {
800 // Get pointer to the bulk fluid element that is
801 // adjacent to boundary b
802 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
803 Bulk_mesh_pt->boundary_element_pt(b,e));
804
805 //Find the index of the face of element e along boundary b
806 int face_index = Bulk_mesh_pt->face_index_at_boundary(b,e);
807
808 // Create new element
809 ElasticLineVolumeConstraintBoundingElement<ELEMENT>* el_pt =
810 new ElasticLineVolumeConstraintBoundingElement<ELEMENT>(
811 bulk_elem_pt,face_index);
812
813 //Set the "master" volume control element
814 el_pt->set_volume_constraint_element(vol_constraint_element);
815
816 // Add it to the mesh
817 Volume_constraint_mesh_pt->add_element_pt(el_pt);
818 }
819 }
820 }
821
823 {
824 unsigned n_element = Volume_constraint_mesh_pt->nelement();
825 for(unsigned e=0;e<n_element;e++)
826 {
827 delete Volume_constraint_mesh_pt->element_pt(e);
828 }
829 Volume_constraint_mesh_pt->flush_element_and_node_storage();
830 }
831
833 {
834 //Find number of elements adjacent to upper boundary
835 unsigned n_boundary_element = Bulk_mesh_pt->nboundary_element(2);
836 //The boundary elements do no necessarily come in order, so we will
837 //need to detect the element adjacent to boundary 1.
838 //The index of that element in our array will be stored in this variable
839 //(initialised to a negative and therefore invalid number)
840 int final_element_index=-1;
841 //Loop over the elements adjacent to the boundary
842 for(unsigned e=0;e<n_boundary_element;e++)
843 {
844 //Create the free surface element (on face 2)
845 FiniteElement *free_surface_element_pt
846 = new ElasticLineFluidInterfaceElement<ELEMENT>
847 (Bulk_mesh_pt->boundary_element_pt(2,e),
848 Bulk_mesh_pt->face_index_at_boundary(2,e));
849 //Push it back onto the stack
850 Surface_mesh_pt->add_element_pt(free_surface_element_pt);
851
852 //Check whether the element is on the boundary 1
853 unsigned n_node = free_surface_element_pt->nnode();
854 //Only need to check the end nodes
855 if((free_surface_element_pt->node_pt(0)->is_on_boundary(1)) ||
856 (free_surface_element_pt->node_pt(n_node-1)->is_on_boundary(1)))
857 {
858 final_element_index=e;
859 }
860 }
861
862 unsigned Nfree = Surface_mesh_pt->nelement();
863 oomph_info << Nfree << " free surface elements assigned" << std::endl;
864
865 if(final_element_index == -1)
866 {
867 throw OomphLibError("No Surface Element adjacent to boundary 1\n",
868 OOMPH_CURRENT_FUNCTION,
869 OOMPH_EXCEPTION_LOCATION);
870 }
871
872 //Make the edge point
873 FiniteElement* point_element_pt=
874 dynamic_cast<ElasticLineFluidInterfaceElement<ELEMENT>*>
875 (Surface_mesh_pt->element_pt(final_element_index))
876 ->make_bounding_element(1);
877
878 //Add it to the stack
879 Point_mesh_pt->add_element_pt(point_element_pt);
880 }
881
882 //Function to delete the free surface elements
884 {
885 //Find the number of traction elements
886 unsigned Nfree_surface = Surface_mesh_pt->nelement();
887
888 //The traction elements are ALWAYS? stored at the end
889 //So delete and remove them, add one to get rid of the constraint
890 for(unsigned e=0;e<Nfree_surface;e++)
891 {
892 delete Surface_mesh_pt->element_pt(e);
893 }
894 Surface_mesh_pt->flush_element_and_node_storage();
895
896 delete Point_mesh_pt->element_pt(0);
897 Point_mesh_pt->flush_element_and_node_storage();
898 }
899
900
901};
902
903
904//========================================================================
905/// Constructor for adaptive Poisson problem in deformable fish-shaped
906/// domain. Pass bool to indicate if we want a constant source
907/// function or the one that produces a tanh step.
908//========================================================================
909template<class ELEMENT>
911 const double &length, const double &height) : Length(length), Height(height),
912 Re(0.0), Ca(0.001),
913 ReInvFr(0.0),
914 Bo(0.0), Omega(1.0),
915 Volume(12.0),
916 Angle(1.57)
917{
921
922 G.resize(2);
923 G[0] = 0.0; G[1] = -1.0;
924
925 /// Set the initial value of the ReInvFr = Bo/Ca
926 ReInvFr = Bo/Ca;
927
928 /// Build a linear solver: Use HSL's MA42 frontal solver
929 //linear_solver_pt() = new HSL_MA42;
930
931 //Set the constituive law
932 Constitutive_law_pt = new GeneralisedHookean(&Global_Physical_Variables::Nu);
933
934
935 /// Switch off full doc for frontal solver
936 //static_cast<HSL_MA42*>(linear_solver_pt())->disable_doc_stats();
937
938 //Allocate the timestepper (no timedependence)
939 add_time_stepper_pt(new Steady<0>);
940
941 // Build mesh
944 Problem::time_stepper_pt());
945
946 // Set error estimator
948 Bulk_mesh_pt->spatial_error_estimator_pt()=error_estimator_pt;
949
950
951 //Refine the problem a couple of times
952 bool update_all_solid_nodes=true;
953 Bulk_mesh_pt->refine_uniformly();
955 Bulk_mesh_pt->refine_uniformly();
957 //Bulk_mesh_pt->refine_uniformly();
958 //refine_uniformly();
959 //refine_uniformly();
960
961 // Loop over all elements and unset macro element pointer
962 unsigned Nelement = Bulk_mesh_pt->nelement();
963 for(unsigned e=0;e<Nelement;e++)
964 {
965 dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e))->
967 }
968
969
970 //The external pressure is a piece of global data
973
974 // Complete the build of all elements so they are fully functional
975
976 Surface_mesh_pt = new Mesh;
977 Point_mesh_pt = new Mesh;
979
981
986
987 this->build_global_mesh();
988
989 //Attach the boundary conditions to the mesh
990 oomph_info <<"Number of equations: " << assign_eqn_numbers() << std::endl;
991
992}
993
994
995//========================================================================
996/// Complete build of Poisson problem:
997/// Loop over elements and setup pointers to source function
998///
999//========================================================================
1000template<class ELEMENT>
1002{
1003 //Now sort out the free surface
1004 this->create_free_surface_elements();
1005 //Create the volume constraint elements
1006 this->create_volume_constraint_elements();
1007
1008 // Set the boundary conditions for this problem: All nodes are
1009 // free by default -- just pin the ones that have Dirichlet conditions
1010 // here.
1011
1012 //Pin bottom and cylinder
1013 unsigned num_bound = Bulk_mesh_pt->nboundary();
1014 for(unsigned ibound=0;ibound<num_bound;ibound+=4)
1015 {
1016 unsigned num_nod= Bulk_mesh_pt->nboundary_node(ibound);
1017 for (unsigned inod=0;inod<num_nod;inod++)
1018 {
1019 Bulk_mesh_pt->boundary_node_pt(ibound,inod)->pin(0);
1020 Bulk_mesh_pt->boundary_node_pt(ibound,inod)->pin(1);
1021 }
1022 }
1023
1024 //Pin u and v on LHS
1025 {
1026 unsigned num_nod= Bulk_mesh_pt->nboundary_node(3);
1027 for (unsigned inod=0;inod<num_nod;inod++)
1028 {
1029 Bulk_mesh_pt->boundary_node_pt(3,inod)->pin(0);
1030 //Bulk_mesh_pt->boundary_node_pt(3,inod)->pin(1);
1031 }
1032 }
1033
1034 //Pin u and v on RHS
1035 {
1036 unsigned num_nod= Bulk_mesh_pt->nboundary_node(1);
1037 for (unsigned inod=0;inod<num_nod;inod++)
1038 {
1039 Bulk_mesh_pt->boundary_node_pt(1,inod)->pin(0);
1040 Bulk_mesh_pt->boundary_node_pt(1,inod)->pin(1);
1041 }
1042 }
1043
1044
1045 dynamic_cast<FluidInterfaceBoundingElement*>
1046 (Point_mesh_pt->element_pt(0))->set_contact_angle(&Angle);
1047
1048 dynamic_cast<FluidInterfaceBoundingElement*>
1049 (Point_mesh_pt->element_pt(0))->ca_pt() = &Ca;
1050
1051
1052 dynamic_cast<FluidInterfaceBoundingElement*>
1053 (Point_mesh_pt->element_pt(0))->
1055
1056 //Pin one pressure
1057 dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(0))->fix_pressure(0,0.0);
1058
1059 //Loop over the lower boundary and pin nodal positions in both directions
1060 unsigned num_nod= Bulk_mesh_pt->nboundary_node(0);
1061 for (unsigned inod=0;inod<num_nod;inod++)
1062 {
1063 Bulk_mesh_pt->boundary_node_pt(0,inod)->pin_position(0);
1064 Bulk_mesh_pt->boundary_node_pt(0,inod)->pin_position(1);
1065 }
1066
1067 //Loop over the RHS side and pin in x and y
1068 num_nod= Bulk_mesh_pt->nboundary_node(1);
1069 for (unsigned inod=0;inod<num_nod;inod++)
1070 {
1071 Bulk_mesh_pt->boundary_node_pt(1,inod)->pin_position(0);
1072 //Bulk_mesh_pt->boundary_node_pt(1,inod)->pin_position(1);
1073 }
1074
1075
1076 //Loop over the LHS side and pin in x
1077 num_nod= Bulk_mesh_pt->nboundary_node(3);
1078 for (unsigned inod=0;inod<num_nod;inod++)
1079 {
1080 Bulk_mesh_pt->boundary_node_pt(3,inod)->pin_position(0);
1081 //Bulk_mesh_pt->boundary_node_pt(3,inod)->pin_position(1);
1082 }
1083
1084 //Loop over the cylinder and pin nodal positions in both directions
1085 num_nod= Bulk_mesh_pt->nboundary_node(4);
1086 for (unsigned inod=0;inod<num_nod;inod++)
1087 {
1088 Bulk_mesh_pt->boundary_node_pt(4,inod)->pin_position(0);
1089 Bulk_mesh_pt->boundary_node_pt(4,inod)->pin_position(1);
1090 }
1091
1092
1093 //Find number of elements in mesh
1094 unsigned Nfluid = Bulk_mesh_pt->nelement();
1095 //Find the number of free surface elements
1096 unsigned Nfree = Surface_mesh_pt->nelement();
1097
1098 // Loop over the elements to set up element-specific
1099 // things that cannot be handled by constructor
1100 for(unsigned i=0;i<Nfluid;i++)
1101 {
1102 // Upcast from FiniteElement to the present element
1103 ELEMENT *temp_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(i));
1104
1105 //Set the source function pointer
1106 temp_pt->re_pt() = &Re;
1107 temp_pt->re_invfr_pt() = &ReInvFr;
1108 temp_pt->g_pt() = &G;
1109
1110 //Assign the mesh deformation constitutive law
1111 temp_pt->constitutive_law_pt() = Constitutive_law_pt;
1112
1113 }
1114
1115
1116 // Pin the redundant solid pressures (if any)
1118 Bulk_mesh_pt->element_pt());
1119
1120 //Loop over the free surface elements
1121 for(unsigned i=0;i<Nfree;i++)
1122 {
1123 // Upcast from FiniteElement to the present element
1126 (Surface_mesh_pt->element_pt(i));
1127 //Set the Capillary number
1128 temp_pt->ca_pt() = &Ca;
1129
1130 //Pass the Data item that contains the external pressure
1131 temp_pt->set_external_pressure_data(this->global_data_pt(0));
1132 }
1133
1134}
1135
1136template<class ELEMENT>
1138{
1139 //Only bother to set non-zero velocity on the cylinder
1140 unsigned Nnode = Bulk_mesh_pt->nboundary_node(4);
1141 for(unsigned n=0;n<Nnode;n++)
1142 {
1143 //Get x and y
1144 double x = Bulk_mesh_pt->boundary_node_pt(4,n)->x(0);
1145 double y = Bulk_mesh_pt->boundary_node_pt(4,n)->x(1);
1146
1147 //Now find the vector distance to the centre
1148 double len_x = x - Bulk_mesh_pt->domain_pt()->centre_x;
1149 double len_y = y - Bulk_mesh_pt->domain_pt()->centre_y;
1150
1151 //Calculate the angle and radius
1152 double r = sqrt(len_x*len_x + len_y*len_y);
1153 double theta = atan2(len_y,len_x);
1154
1155 //Now set the velocities
1156 Bulk_mesh_pt->boundary_node_pt(4,n)->set_value(0,-Omega*r*sin(theta));
1157 Bulk_mesh_pt->boundary_node_pt(4,n)->set_value(1, Omega*r*cos(theta));
1158 }
1159}
1160
1161template<class ELEMENT>
1163{
1164 Newton_solver_tolerance = 1.0e-8;
1165 //Document the solution
1166 std::ofstream filenamee("input.dat");
1167 Bulk_mesh_pt->output(filenamee,5);
1168 Surface_mesh_pt->output(filenamee,5);
1169 //Point_mesh_pt->output(filenamee,5);
1170 filenamee.close();
1171
1172 //Solve the initial value problem
1173 newton_solve();
1174
1175 std::ofstream filename("first.dat");
1176 Bulk_mesh_pt->output(filename,5);
1177 Surface_mesh_pt->output(filename,5);
1178 //Point_mesh_pt->output(filename,5);
1179 filename.close();
1180
1181 //Initialise the value of the arc-length
1182 double ds=0.001;
1183
1184 std::ofstream trace("trace.dat");
1185
1186 trace << Ca << " " << ReInvFr << " "
1187 << Bulk_mesh_pt->boundary_node_pt(2,0)->x(1) << std::endl;
1188
1189// bool flag=true, fflag=true;
1190
1191 for(unsigned i=0;i<2;i++)
1192 {
1193 if(i<5)
1194 {
1195 //Decrease the contact angle
1196 Angle -= 0.1;
1197 newton_solve(2);
1198 //newton_solve();
1199 }
1200 else
1201 {
1202 //do an arc-length continuation step in Ca
1204 }
1205
1206 // if(flag)
1207// {
1208// //Do an arc-length continuation step in ReInvFr
1209// ds = arc_length_step_solve(&ReInvFr,ds);
1210// }
1211// else
1212// {
1213// //Reset arc-length parameters
1214// if(fflag) {reset_arc_length_parameters(); fflag=false;}
1215// ds = 0.001;
1216// //Now do it in Ca
1217// ds = arc_length_step_solve(&Ca,ds);
1218// }
1219
1220// if(Bulk_mesh_pt->boundary_node_pt(2,0)->x(1) < 4.0)
1221// {flag=false;}
1222
1223 trace << Ca << " " << ReInvFr << " " << Angle << " "
1224 << Bulk_mesh_pt->boundary_node_pt(2,0)->x(1) << std::endl;
1225
1226 char file[100];
1227 snprintf(file, sizeof(file), "step%i.dat",i);
1228 filename.open(file);
1229 Bulk_mesh_pt->output(filename,5);
1230 Surface_mesh_pt->output(filename,5);
1231 //Point_mesh_pt->output(filename,5);
1232 filename.close();
1233
1234 //Now reset the values of the lagrange multipliers and the xi's
1235 //An updated lagrangian approach
1236
1237 //Now loop over all the nodes and set their Lagrangian coordinates
1238 unsigned Nnode = Bulk_mesh_pt->nnode();
1239 for(unsigned n=0;n<Nnode;n++)
1240 {
1241 //Cast node to an elastic node
1242 SolidNode* temp_pt = static_cast<SolidNode*>(Bulk_mesh_pt->node_pt(n));
1243 for(unsigned j=0;j<2;j++) {temp_pt->xi(j) = temp_pt->x(j);}
1244 }
1245
1246 //Find the number of free surface elements
1247 unsigned Nfree = Surface_mesh_pt->nelement();
1248 //Loop over the free surface elements
1249 for(unsigned n=0;n<Nfree;n++)
1250 {
1251 // Upcast from FiniteElement to the present element
1254 (Surface_mesh_pt->element_pt(n));
1255 unsigned Nnode = temp_pt->nnode();
1256 //Reset the lagrange multipliers
1257 for(unsigned j=0;j<Nnode;j++) {temp_pt->lagrange(j) = 0.0;}
1258 }
1259 }
1260
1261 //Document the solution
1262 //filename.open("output.dat");
1263 //Bulk_mesh_pt->output(filename,5);
1264 //filename.close();
1265 trace.close();
1266}
1267
1268
1269///////////////////////////////////////////////////////////////////////
1270///////////////////////////////////////////////////////////////////////
1271///////////////////////////////////////////////////////////////////////
1272
1273
1274 int main()
1275 {
1276
1280
1281 //ofstream filename("mesh.dat");
1282 //problem.Bulk_mesh_pt->output(filename,5);
1283
1284 problem.solve();
1285 }
int main()
CylinderAndInterfaceDomain(const double &Length, const double &Height)
void macro_element_boundary(const unsigned &time, const unsigned &m, const unsigned &direction, const Vector< double > &s, Vector< double > &f)
GeomObject * Cylinder_pt
Geometric object that represents the rotating cylinder.
void linear_interpolate(double Left[2], double Right[2], const double &s, Vector< double > &f)
CylinderAndInterfaceDomain * Domain_pt
CylinderAndInterfaceMesh(const double &length, const double &height, TimeStepper *time_stepper_pt)
CylinderAndInterfaceDomain * domain_pt()
GeneralEllipse(const double &centre_x, const double &centre_y, const double &a, const double &b)
void position(const Vector< double > &xi, Vector< double > &r) const
RefineableCylinderAndInterfaceMesh(const double &length, const double &height, TimeStepper *time_stepper_pt)
virtual ~RefineableCylinderAndInterfaceMesh()
Destructor: Empty.
void create_volume_constraint_elements()
Create the volume constraint elements.
void actions_before_newton_solve()
Update the problem specs before solve:
Mesh * Volume_constraint_mesh_pt
The volume constraint mesh.
void finish_problem_setup()
Complete problem setup: Setup element-specific things (source fct pointers etc.)
void actions_before_adapt()
Strip off the interface before adaptation.
RefineableRotatingCylinderProblem(const double &length, const double &height)
Constructor: Pass flag to indicate if you want a constant source function or the tanh profile.
RefineableCylinderAndInterfaceMesh< ELEMENT > * Bulk_mesh_pt
void actions_after_newton_solve()
Update the problem specs after solve (empty)
Namespace for physical 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.