clamped_shell_with_arclength_cont.cc
Go to the documentation of this file.
1//LIC// ====================================================================
2//LIC// This file forms part of oomph-lib, the object-oriented,
3//LIC// multi-physics finite-element library, available
4//LIC// at http://www.oomph-lib.org.
5//LIC//
6//LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7//LIC//
8//LIC// This library is free software; you can redistribute it and/or
9//LIC// modify it under the terms of the GNU Lesser General Public
10//LIC// License as published by the Free Software Foundation; either
11//LIC// version 2.1 of the License, or (at your option) any later version.
12//LIC//
13//LIC// This library is distributed in the hope that it will be useful,
14//LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15//LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16//LIC// Lesser General Public License for more details.
17//LIC//
18//LIC// You should have received a copy of the GNU Lesser General Public
19//LIC// License along with this library; if not, write to the Free Software
20//LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21//LIC// 02110-1301 USA.
22//LIC//
23//LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24//LIC//
25//LIC//====================================================================
26//Driver function for a simple test shell problem:
27//Calculate the deformation of an elastic tube approximated
28//using Kirchoff--Love shell theory
29
30//Standard system includes
31#include <iostream>
32#include <fstream>
33#include <cmath>
34#include <typeinfo>
35#include <algorithm>
36#include <cstdio>
37
38//Include files from the finite-element library
39#include "generic.h"
40#include "shell.h"
41#include "meshes/rectangular_quadmesh.h"
42
43using namespace std;
44
45using namespace oomph;
46
47//========================================================================
48/// Global variables that represent physical properties
49//========================================================================
51{
52
53 /// Prescribed position of control point
54 double Prescribed_y = 1.0;
55
56 /// Pointer to pressure load (stored in Data so it can
57 /// become an unknown in the problem when displacement control is used
59
60 /// Return a reference to the external pressure
61 /// load on the elastic tube.
62 double external_pressure()
63 {return (*Pext_data_pt->value_pt(0))*pow(0.05,3)/12.0;}
64
65
66 /// Load function, normal pressure loading
67 void press_load(const Vector<double> &xi,
68 const Vector<double> &x,
69 const Vector<double> &N,
71 {
72 for(unsigned i=0;i<3;i++)
73 {
75 }
76 }
77
78}
79
80//========================================================================
81/// A 2D Mesh class. The tube wall is represented by two Lagrangian
82/// coordinates that correspond to z and theta in cylindrical polars.
83/// The required mesh is therefore a 2D mesh and is therefore inherited
84/// from the generic RectangularQuadMesh
85//=======================================================================
86template <class ELEMENT>
87class ShellMesh : public virtual RectangularQuadMesh<ELEMENT>,
88 public virtual SolidMesh
89{
90public:
91
92 /// Constructor for the mesh
93 ShellMesh(const unsigned &nx, const unsigned &ny,
94 const double &lx, const double &ly);
95
96 /// In all elastic problems, the nodes must be assigned an undeformed,
97 /// or reference, position, corresponding to the stress-free state
98 /// of the elastic body. This function assigns the undeformed position
99 /// for the nodes on the elastic tube
101
102};
103
104
105
106
107
108//=======================================================================
109/// Mesh constructor
110/// Argument list:
111/// nx : number of elements in the axial direction
112/// ny : number of elements in the azimuthal direction
113/// lx : length in the axial direction
114/// ly : length in theta direction
115//=======================================================================
116template <class ELEMENT>
117ShellMesh<ELEMENT>::ShellMesh(const unsigned &nx,
118 const unsigned &ny,
119 const double &lx,
120 const double &ly) :
121 RectangularQuadMesh<ELEMENT>(nx,ny,lx,ly)
122{
123 //Find out how many nodes there are
124 unsigned n_node = nnode();
125
126 //Now in this case it is the Lagrangian coordinates that we want to set,
127 //so we have to loop over all nodes and set them to the Eulerian
128 //coordinates that are set by the generic mesh generator
129 for(unsigned i=0;i<n_node;i++)
130 {
131 node_pt(i)->xi(0) = node_pt(i)->x(0);
132 node_pt(i)->xi(1) = node_pt(i)->x(1);
133 }
134
135
136 //Assign gradients, etc for the Lagrangian coordinates of
137 //hermite-type elements
138
139 //Read out number of position dofs
140 unsigned n_position_type = finite_element_pt(0)->nnodal_position_type();
141
142 //If this is greater than 1 set the slopes, which are the distances between
143 //nodes. If the spacing were non-uniform, this part would be more difficult
144 if(n_position_type > 1)
145 {
146 double xstep = (this->Xmax - this->Xmin)/((this->Np-1)*this->Nx);
147 double ystep = (this->Ymax - this->Ymin)/((this->Np-1)*this->Ny);
148 for(unsigned n=0;n<n_node;n++)
149 {
150 //The factor 0.5 is because our reference element has length 2.0
151 node_pt(n)->xi_gen(1,0) = 0.5*xstep;
152 node_pt(n)->xi_gen(2,1) = 0.5*ystep;
153 }
154 }
155}
156
157
158//=======================================================================
159/// Set the undeformed coordinates of the nodes
160//=======================================================================
161template <class ELEMENT>
164{
165 //Find out how many nodes there are
166 unsigned n_node = nnode();
167
168 //Loop over all the nodes
169 for(unsigned n=0;n<n_node;n++)
170 {
171 //Get the Lagrangian coordinates
173 xi[0] = node_pt(n)->xi(0);
174 xi[1] = node_pt(n)->xi(1);
175
176 //Assign memory for values of derivatives, etc
177 Vector<double> R(3);
180
181 //Get the geometrical information from the geometric object
182 undeformed_midplane_pt->d2position(xi,R,a,dadxi);
183
184 //Loop over coordinate directions
185 for(unsigned i=0;i<3;i++)
186 {
187 //Set the position
188 node_pt(n)->x_gen(0,i) = R[i];
189
190 //Set the derivative wrt Lagrangian coordinates
191 //Note that we need to scale by the length of each element here!!
192 node_pt(n)->x_gen(1,i) = 0.5*a(0,i)*((this->Xmax - this->Xmin)/this->Nx);
193 node_pt(n)->x_gen(2,i) = 0.5*a(1,i)*((this->Ymax - this->Ymin)/this->Ny);
194
195 //Set the mixed derivative
196 //(symmetric so doesn't matter which one we use)
197 node_pt(n)->x_gen(3,i) = 0.25*dadxi(0,1,i);
198 }
199 }
200}
201
202
203
204// //========================================================================
205// /// A 2D mesh class for the problem.
206// /// The tube is represented by two Lagrangian coordinates that correspond
207// /// to z and theta in cylindrical polars. The required mesh is therefore a
208// /// 2D mesh and is therefore inherited from the generic RectangularQuadMesh
209// //=======================================================================
210// template <class ELEMENT>
211// class ShellMesh : public virtual RectangularQuadMesh<ELEMENT>,
212// public virtual SolidMesh
213// {
214// public:
215// //Constructor for the mesh
216// ShellMesh(const unsigned &nx, const unsigned &ny,
217// const double &lx, const double &ly);
218
219// //In all elastic problems, the nodes must be assigned an undeformed,
220// //or reference, position, corresponding to the stress-free state
221// //of the elastic body. This function assigns the undeformed position
222// //for the nodes on the elastic tube
223// void assign_undeformed_positions(GeomObject* const &undeformed_midplane_pt);
224// };
225
226// //Define the mesh constructor
227// //Argument list:
228// // nx : number of elements in the axial direction
229// // ny : number of elements in the azimuthal direction
230// // lx : length in the axial direction
231// // ly : length in theta direction
232// template <class ELEMENT>
233// ShellMesh<ELEMENT>::ShellMesh(const unsigned &nx,
234// const unsigned &ny,
235// const double &lx, const double &ly) :
236// RectangularQuadMesh<ELEMENT>(nx,ny,lx,ly)
237// {
238// //Find out how many nodes there are
239// unsigned Nnode = nnode();
240
241// //Now in this case it is the Lagrangian coordinates that we want to set,
242// //so we have to loop over all nodes and set them to the Eulerian
243// //coordinates that are set by the generic mesh generator
244// for(unsigned i=0;i<Nnode;i++)
245// {
246// node_pt(i)->xi(0) = node_pt(i)->x(0);
247// node_pt(i)->xi(1) = node_pt(i)->x(1);
248// }
249
250// //Assign gradients, etc for the Lagrangian coordinates of
251// //hermite-type elements
252
253// //Read out number of position dofs
254// unsigned Nposition_type = finite_element_pt(0)->nnodal_position_type();
255// std::cout << Nposition_type << std::endl;
256// //If this is greater than 1 set the slopes, which are the distances between
257// //nodes. If the spacing were non-uniform, this part would be more difficult
258// if(Nposition_type > 1)
259// {
260// double xstep = (this->Xmax - this->Xmin)/((this->Np-1)*this->Nx);
261// double ystep = (this->Ymax - this->Ymin)/((this->Np-1)*this->Ny);
262// for(unsigned n=0;n<Nnode;n++)
263// {
264// //The factor 0.5 is because our reference element has length 2.0
265// node_pt(n)->xi_gen(1,0) = 0.5*xstep;
266// node_pt(n)->xi_gen(2,1) = 0.5*ystep;
267// }
268// }
269// }
270
271// //Set the undeformed values of the nodes
272// template <class ELEMENT>
273// void ShellMesh<ELEMENT>::
274// assign_undeformed_positions(GeomObject* const &undeformed_midplane_pt)
275// {
276// //Find out how many nodes there are
277// unsigned n_node = nnode();
278// //Loop over all the nodes
279// for(unsigned n=0;n<n_node;n++)
280// {
281// //Get the lagrangian coordinates
282// Vector<double> xi(2);
283// xi[0] = node_pt(n)->xi(0); xi[1] = node_pt(n)->xi(1);
284
285// //Assign memory for values of derivatives, etc
286// Vector<double> R(3);
287// DenseMatrix<double> a(2,3);
288// RankThreeTensor<double> dadxi(2,2,3);
289
290// //Get the geometrical information from the geometric object
291// undeformed_midplane_pt->d2position(xi,R,a,dadxi);
292
293// //Loop over coordinate directions
294// for(unsigned i=0;i<3;i++)
295// {
296// //Set the position
297// node_pt(n)->x_gen(0,i) = R[i];
298// //Set the derivative wrt Lagrangian coordinates
299// //Note that we need to scale by the length of each element here!!
300// node_pt(n)->x_gen(1,i) = 0.5*a(0,i)*((this->Xmax - this->Xmin)/this->Nx);
301// node_pt(n)->x_gen(2,i) = 0.5*a(1,i)*((this->Ymax - this->Ymin)/this->Ny);
302// //Set the mixed derivative
303// //(symmetric so doesn't matter which one we use)
304// node_pt(n)->x_gen(3,i) = 0.25*dadxi(0,1,i);
305// }
306// }
307// }
308
309
310
311//======================================================================
312//Problem class to solve the deformation of an elastic tube
313//=====================================================================
314template<class ELEMENT>
315class ShellProblem : public Problem
316{
317
318public:
319
320 /// Constructor
321 ShellProblem(const unsigned &nx, const unsigned &ny,
322 const double &lx, const double &ly);
323
324 /// Destructor: delete mesh, geometric object
326 {
327 delete Problem::mesh_pt();
329 }
330
331
332 /// Overload Access function for the mesh
334 {return dynamic_cast<ShellMesh<ELEMENT>*>(Problem::mesh_pt());}
335
336 /// Actions after solve empty
338
339 /// Actions before solve empty
341
342 //A self_test function
343 void solve();
344
345private:
346
347 /// Pointer to GeomObject that specifies the undeformed midplane
349
350 /// First trace node
352
353 /// Second trace node
355
356};
357
358
359
360//======================================================================
361/// Constructor
362//======================================================================
363template<class ELEMENT>
364ShellProblem<ELEMENT>::ShellProblem(const unsigned &nx, const unsigned &ny,
365 const double &lx, const double &ly)
366{
367 //Suppress the warning about empty mesh_level_timestepper_stuff
369
370 //Use the continuation timestepper
372
373 //Create the undeformed midplane object
374 Undeformed_midplane_pt = new EllipticalTube(1.0,1.0);
375
376 //Now create the mesh
377 Problem::mesh_pt() = new ShellMesh<ELEMENT>(nx,ny,lx,ly);
378
379 //Set the undeformed positions in the mesh
380 mesh_pt()->assign_undeformed_positions(Undeformed_midplane_pt);
381
382 //Reorder the elements, since I know what's best for them....
383 mesh_pt()->element_reorder();
384
385 //Apply boundary conditions to the ends of the tube
386 unsigned n_ends = mesh_pt()->nboundary_node(1);
387 //Loop over the node
388 for(unsigned i=0;i<n_ends;i++)
389 {
390 //Pin in the axial direction (prevents rigid body motions)
391 mesh_pt()->boundary_node_pt(1,i)->pin_position(2);
392 mesh_pt()->boundary_node_pt(3,i)->pin_position(2);
393 //Derived conditions
394 mesh_pt()->boundary_node_pt(1,i)->pin_position(2,2);
395 mesh_pt()->boundary_node_pt(3,i)->pin_position(2,2);
396
397 //------------------CLAMPING CONDITIONS----------------------
398 //------Pin positions in the transverse directions-----------
399 // Comment these out to get the ring case
400 mesh_pt()->boundary_node_pt(1,i)->pin_position(0);
401 mesh_pt()->boundary_node_pt(3,i)->pin_position(0);
402 //Derived conditions
403 mesh_pt()->boundary_node_pt(1,i)->pin_position(2,0);
404 mesh_pt()->boundary_node_pt(3,i)->pin_position(2,0);
405
406 mesh_pt()->boundary_node_pt(1,i)->pin_position(1);
407 mesh_pt()->boundary_node_pt(3,i)->pin_position(1);
408 //Derived conditions
409 mesh_pt()->boundary_node_pt(1,i)->pin_position(2,1);
410 mesh_pt()->boundary_node_pt(3,i)->pin_position(2,1);
411 //----------------------------------------------------------
412
413 // Set the axial gradients of the transverse coordinates to be
414 // zero --- need to be enforced for ring or tube buckling
415 //Pin dx/dz and dy/dz
416 mesh_pt()->boundary_node_pt(1,i)->pin_position(1,0);
417 mesh_pt()->boundary_node_pt(1,i)->pin_position(1,1);
418 mesh_pt()->boundary_node_pt(3,i)->pin_position(1,0);
419 mesh_pt()->boundary_node_pt(3,i)->pin_position(1,1);
420 //Derived conditions
421 mesh_pt()->boundary_node_pt(1,i)->pin_position(3,0);
422 mesh_pt()->boundary_node_pt(1,i)->pin_position(3,1);
423 mesh_pt()->boundary_node_pt(3,i)->pin_position(3,0);
424 mesh_pt()->boundary_node_pt(3,i)->pin_position(3,1);
425 }
426
427 //Now loop over the sides and apply symmetry conditions
428 unsigned n_side = mesh_pt()->nboundary_node(0);
429 for(unsigned i=0;i<n_side;i++)
430 {
431 //At the side where theta is 0, pin in the y direction
432 mesh_pt()->boundary_node_pt(0,i)->pin_position(1);
433 //Derived condition
434 mesh_pt()->boundary_node_pt(0,i)->pin_position(1,1);
435 //Pin dx/dtheta and dz/dtheta
436 mesh_pt()->boundary_node_pt(0,i)->pin_position(2,0);
437 mesh_pt()->boundary_node_pt(0,i)->pin_position(2,2);
438 //Pin the mixed derivative
439 mesh_pt()->boundary_node_pt(0,i)->pin_position(3,0);
440 mesh_pt()->boundary_node_pt(0,i)->pin_position(3,2);
441
442 //At the side when theta is 0.5pi pin in the x direction
443 mesh_pt()->boundary_node_pt(2,i)->pin_position(0);
444 //Derived condition
445 mesh_pt()->boundary_node_pt(2,i)->pin_position(1,0);
446 //Pin dy/dtheta and dz/dtheta
447 mesh_pt()->boundary_node_pt(2,i)->pin_position(2,1);
448 mesh_pt()->boundary_node_pt(2,i)->pin_position(2,2);
449 //Pin the mixed derivative
450 mesh_pt()->boundary_node_pt(2,i)->pin_position(3,1);
451 mesh_pt()->boundary_node_pt(2,i)->pin_position(3,2);
452
453 //Set an initial kick to make sure that we hop onto the
454 //non-axisymmetric branch
455 if((i>1) && (i<n_side-1))
456 {
457 mesh_pt()->boundary_node_pt(0,i)->x(0) += 0.05;
458 mesh_pt()->boundary_node_pt(2,i)->x(1) -= 0.1;
459 }
460 }
461
462
463 // Setup displacement control
464 //---------------------------
465
466
467
468// //Setup displacement control
469// //Fix the displacement at the mid-point of the tube in the "vertical"
470// //(y) direction.
471// //Set the displacement control element (located halfway along the tube)
472// Disp_ctl_element_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(3*Ny-1));
473// //The midpoint of the tube is located exactly half-way along the element
474// Vector<double> s(2); s[0] = 1.0; s[1] = 0.0; //s[1] = 0.5
475// //Fix the displacement at this point in the y (1) direction
476// Disp_ctl_element_pt->fix_displacement_for_displacement_control(s,1);
477// //Set the pointer to the prescribed position
478// Disp_ctl_element_pt->prescribed_position_pt() = &Prescribed_y;
479
480
481
482 // Choose element in which displacement control is applied: This
483 // one is located halfway along the tube)
485 dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(3*ny-1));
486
487 // Fix the displacement in the y (1) direction...
488 unsigned controlled_direction=1;
489
490 // Local coordinate of the control point within the controlled element
492 s_displ_control[0]=1.0;
493 s_displ_control[1]=0.0;
494
495 // Pointer to displacement control element
497
498 // Build displacement control element
504
505 // The constructor of the DisplacementControlElement has created
506 // a new Data object whose one-and-only value contains the
507 // adjustable load: Use this Data object in the load function:
510
511 // Add the displacement-control element to the mesh
512 mesh_pt()->add_element_pt(displ_control_el_pt);
513
514
515
516 // Complete build of shell elements
517 //---------------------------------
518
519 //Find number of shell elements in mesh
520 unsigned n_element = nx*ny;
521
522 //Explicit pointer to first element in the mesh
523 ELEMENT* first_el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(0));
524
525 //Loop over the elements
526 for(unsigned e=0;e<n_element;e++)
527 {
528 //Cast to a shell element
529 ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
530
531 //Set the load function
532 el_pt->load_vector_fct_pt() = & Global_Physical_Variables::press_load;
533
534 //Set the undeformed surface
535 el_pt->undeformed_midplane_pt() = Undeformed_midplane_pt;
536
537 //The external pressure is external data for all elements
539
540 //Pre-compute the second derivatives wrt Lagrangian coordinates
541 //for the first element only
542 if(e==0)
543 {
544 el_pt->pre_compute_d2shape_lagrangian_at_knots();
545 }
546
547 //Otherwise set the values to be the same as those in the first element
548 //this is OK because the Lagrangian mesh is uniform.
549 else
550 {
551 el_pt->set_dshape_lagrangian_stored_from_element(first_el_pt);
552 }
553 }
554
555 //Set pointers to two trace nodes, used for output
556 Trace_node_pt = mesh_pt()->finite_element_pt(2*ny-1)->node_pt(3);
557 Trace_node2_pt = mesh_pt()->finite_element_pt(ny)->node_pt(1);
558
559 // Do equation numbering
560 cout << std::endl;
561 cout << "------------------DISPLACEMENT CONTROL--------------------"
562 << std::endl;
563 cout << "# of dofs " << assign_eqn_numbers() << std::endl;
564 cout << "----------------------------------------------------------"
565 << std::endl;
566 cout << std::endl;
567
568}
569
570
571//================================================================
572// /Define the solve function, disp ctl and then continuation
573//================================================================
574template<class ELEMENT>
576{
577
578 //Increase the maximum number of Newton iterations.
579 //Finding the first buckled solution requires a large(ish) number
580 //of Newton steps -- shells are just a bit twitchy
582
583 //Open an output trace file
584 ofstream trace("trace.dat");
585
586 //Change in displacemenet
587 double disp_incr = 0.05;
588
589 //Gradually compress the tube by decreasing the value of the prescribed
590 //position from 1 to zero in steps of 0.05 initially and then 0.1
591 for(unsigned i=1;i<13;i++)
592 {
593 //By the time we reach the second time round increase the incremenet
594 if(i==3) {disp_incr = 0.1;}
595 //Reduce prescribed y by our chosen increment
597
598 cout << std::endl << "Increasing displacement: Prescribed_y is "
600
601 // Solve
602 newton_solve();
603
604 //Output the pressure
606 << " "
607 //Position of first trace node
608 << Trace_node_pt->x(0) << " " << Trace_node_pt->x(1) << " "
609 //Position of second trace node
610 << Trace_node2_pt->x(0) << " " << Trace_node2_pt->x(1) << std::endl;
611 }
612
613 //Close the trace file
614 trace.close();
615
616 //Output the tube shape in the most strongly collapsed configuration
617 ofstream file("final_shape.dat");
618 mesh_pt()->output(file,5);
619 file.close();
620
621
622 //Switch from displacement control to arc-length continuation and
623 //trace back up the solution branch
624
625 //Now pin the external pressure
627
628 //Re-assign the equation numbers
629 cout << std::endl;
630 cout << "-----------------ARC-LENGTH CONTINUATION --------------"
631 << std::endl;
632 cout << "# of dofs " << assign_eqn_numbers() << std::endl;
633 cout << "-------------------------------------------------------"
634 << std::endl;
635 cout << std::endl;
636
637 //Set the maximum number of Newton iterations to something more reasonable
639
640 //Set the desired number of Newton iterations per arc-length step
642
643 //Set the proportion of the arc length
645
646 //Check for any bifurcations using the sign of the determinant of the Jacobian
648
649 //Set an initial value for the step size
650 double ds = -0.5;
651
652 //Open a different trace file
653 trace.open("trace_disp.dat");
654 //Take fifteen continuation steps
655 for(unsigned i=0;i<15;i++)
656 {
659
660 //Output the pressure
662 << " "
663 //Position of first trace node
664 << Trace_node_pt->x(0) << " " << Trace_node_pt->x(1) << " "
665 //Position of second trace node
666 << Trace_node2_pt->x(0) << " " << Trace_node2_pt->x(1) << std::endl;
667 }
668
669 //Close the trace file
670 trace.close();
671
672}
673
674
675//====================================================================
676/// Driver
677//====================================================================
678int main()
679{
680
681 //Length of domain
682 double L = 10.0;
684
685 //Set up the problem
687 problem(5,5,L,L_phi);
688
689 //Solve the problem
690 problem.solve();
691}
692
693
694
695
696
697
A 2D Mesh class. The tube wall is represented by two Lagrangian coordinates that correspond to z and ...
void assign_undeformed_positions(GeomObject *const &undeformed_midplane_pt)
In all elastic problems, the nodes must be assigned an undeformed, or reference, position,...
ShellMesh(const unsigned &nx, const unsigned &ny, const double &lx, const double &ly)
Constructor for the mesh.
Node * Trace_node2_pt
Second trace node.
GeomObject * Undeformed_midplane_pt
Pointer to GeomObject that specifies the undeformed midplane.
ShellProblem(const unsigned &nx, const unsigned &ny, const double &lx, const double &ly)
Constructor.
void actions_before_newton_solve()
Actions before solve empty.
Node * Trace_node_pt
First trace node.
void actions_after_newton_solve()
Actions after solve empty.
~ShellProblem()
Destructor: delete mesh, geometric object.
ShellMesh< ELEMENT > * mesh_pt()
Overload Access function for the mesh.
Global variables that represent physical properties.
double external_pressure()
Return a reference to the external pressure load on the elastic tube.
void press_load(const Vector< double > &xi, const Vector< double > &x, const Vector< double > &N, Vector< double > &load)
Load function, normal pressure loading.
double Prescribed_y
Prescribed position of control point.
Data * Pext_data_pt
Pointer to pressure load (stored in Data so it can become an unknown in the problem when displacement...