shell_adaptive.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-2026 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
27// Driver code for benchmark of axisym non-linear solid equations
28// in cylindrical coordinates: Thick spherical shell with external pressure
29// Compare against analytical solution for an incompressible material
30// (Adaptive version)
31
32// Generic routines
33#include "generic.h"
34
35// Axisymmetric non-linear solid equations in cylindrical coords
36#include "axisym_solid.h"
37
38// Elements used to solve an auxillary problem for benchmarking
39#include "benchmark_elements.h"
40
41// Constitutive laws
42#include "constitutive.h"
43
44// The mesh: annular
45#include "meshes/annular_mesh.h"
46
47using namespace std;
48using namespace oomph;
49
50//=======start_namespace==========================================
51/// Global physical variables
52//================================================================
54{
55 // Poisson ratio - only used in the compressible case
56 // (nu = 0.5 for incompressible material)
57 double nu = 0.4;
58
59 // The external pressure
60 double ext_pressure = 0.05;
61
62 // Traction function for appyling a pressure on the outer surface
63 void external_pressure_fct(const double& time,
64 const Vector<double>& xi,
65 const Vector<double>& x,
66 const Vector<double>& n,
68 {
69 traction[0] = -1.0 * ext_pressure * n[0];
70 traction[1] = -1.0 * ext_pressure * n[1];
71 }
72} // namespace GlobalPhysicalVariables
73
74//=======start_namespace==========================================
75/// Global simulation settings
76//================================================================
77namespace GlobalSimSettings
78{
79 // Folder for output
80 string result_folder = "RESLT";
81
82 /// Flag for using a pressure formulation
83 bool use_pressure_formulation = false;
84
85 /// If using a pressure formulation, is the solid incompressible?
86 bool incompressible = false;
87
88 // Number of elements in radial direction
89 unsigned n_radial = 10;
90
91 // Number of elements in azimuthal direction
92 unsigned n_azimuthal = 10;
93
94 // Inner radius
95 double a = 1.0;
96
97 // Thickness
98 double h = 0.5;
99} // namespace GlobalSimSettings
100
101//======================start_mesh================================
102/// Upgrade the mesh to a solidmesh.
103/// Quarter-annulus in quadrant r>0, z>0
104/// ntheta - no. elements in theta direction
105/// nr - no. elements in the radial direction
106/// a - Inner radius of annulus
107/// h - thickness of annulus
108//================================================================
109template<class ELEMENT>
111 : public virtual RefineableTwoDAnnularMesh<ELEMENT>,
112 public virtual SolidMesh
113{
114public:
115 /// Constructor: Build mesh and copy Eulerian coords to Lagrangian
116 /// ones so that the initial configuration is the stress-free one.
118 const unsigned& ntheta,
119 const unsigned& nr,
120 const double& a,
121 const double& h,
124 ntheta, nr, 1.0, 1.0, false, time_stepper_pt),
126 0.25,
127 ntheta,
128 nr,
129 a,
130 h,
134 0.25,
135 ntheta,
136 nr,
137 a,
138 h,
141 {
142 // Make the current configuration the undeformed one by
143 // setting the nodal Lagrangian coordinates to their current
144 // Eulerian ones
146
147 // Set the undeformed domain
150
151 // Loop over all elements and set the undeformed macro element pointer
152 unsigned n_element = this->nelement();
153 for (unsigned e = 0; e < n_element; e++)
154 {
155 // Get pointer to full element type
156 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
157
158 // Set pointer to macro element so the curvlinear boundaries
159 // of the undeformed mesh/domain get picked up during adaptive
160 // mesh refinement
161 el_pt->set_undeformed_macro_elem_pt(
162 Undeformed_domain_pt->macro_element_pt(e));
163
164 // Use MacroElement representation for
165 // Lagrangian coordinates of newly created
166 // nodes
167 el_pt->enable_use_of_undeformed_macro_element_for_new_lagrangian_coords();
168 }
169 }
170
171 /// Destructor: Kill "undeformed" Domain
173 {
175 }
176
177private:
178 /// Pointer to "undeformed" Domain -- used to determine the
179 /// Lagrangian coordinates of any newly created SolidNodes during
180 /// Mesh refinement
182};
183
184//==============start_problem=========================================
185/// Unstructured solid problem
186//====================================================================
187template<class ELEMENT>
188class ShellProblem : public Problem
189{
190public:
191 /// Constructor:
193
194 /// Destructor
196
197 /// Doc the solution
199
200private:
201 /// Create the boundary elements
203
204 /// Complete the problem setup (attach function/physical parameter pointers)
206
207 /// Actions before adapt: Wipe the mesh of boundary elements
209 {
210 // Kill the elements and wipe surface mesh
212
213 // Rebuild the Problem's global mesh from its various sub-meshes
214 this->rebuild_global_mesh();
215 }
216
217 /// Any actions after refining the mesh
219 {
220 // Create the boundary elements
222
223 // Rebuild the Problem's global mesh from its various sub-meshes
224 this->rebuild_global_mesh();
225
226 // Setup the problem again
228
229 // Loop over elements and pin solid redundant solid pressures (if any)
230 unsigned n_el = Bulk_mesh_pt->nelement();
231 for (unsigned e = 0; e < n_el; e++)
232 {
233 // Cast to element with pressure dofs
236 Bulk_mesh_pt->element_pt(e));
237
238 // Cast will only succeed if we're using the pressure formulation
239 if (el_pt != 0)
240 {
241 el_pt->pin_elemental_redundant_nodal_solid_pressures();
242 }
243 }
244 }
245
246 /// Delete boundary elements and wipe the associated mesh
248 {
249 // How many surface elements are in the surface mesh
250 unsigned n_element = Surface_mesh_pt->nelement();
251 // Loop over the surface elements
252 for (unsigned e = 0; e < n_element; e++)
253 {
254 // Kill surface element
255 delete Surface_mesh_pt->element_pt(e);
256 }
257 // Wipe the mesh
258 Surface_mesh_pt->flush_element_and_node_storage();
259 } // end of delete_boundary_elements
260
261 /// Bulk mesh
263
264 /// Pointer to the "surface" mesh
266
267 // Pointer to error estimator
269
270 /// Pointer to constitutive law
272
273 // DocInfo object
275};
276
277//===============start_constructor========================================
278/// Axisymmetric compression of a finite thickness shell in cylindrical
279/// polar coordiantes
280//========================================================================
281template<class ELEMENT>
283{
284 // Set output directory
285 Doc_info.set_directory(GlobalSimSettings::result_folder);
286
287 // Initialise counter for solutions
288 Doc_info.number() = 0;
289
290 // Create the mesh: Quarter-annulus in the quadrant r>0, z>0.
296
297 // Create the "surface mesh" that will contain only the interface
298 // elements. The constructor just creates the mesh without giving
299 // it any elements, nodes, etc.
300 Surface_mesh_pt = new Mesh;
301
302 // Create the boundary elements
303 create_boundary_elements();
304
305 // Add the two submeshes
306 add_sub_mesh(Bulk_mesh_pt);
307 add_sub_mesh(Surface_mesh_pt);
308
309 // Combine all sub-meshes into a single mesh
311
312 // Set error estimator for bulk mesh
313 error_estimator_pt = new Z2ErrorEstimator;
314
315 // Set targets
316 Bulk_mesh_pt->spatial_error_estimator_pt() = error_estimator_pt;
317 Bulk_mesh_pt->max_permitted_error() = 1.0e-4;
318 Bulk_mesh_pt->min_permitted_error() = 1.0e-5;
319
320 // Define a constitutive law from a strain energy density function.
321 // Version depends on the incompressibility flag
324 {
326 }
327 else
328 {
330 }
331 Constitutive_law_pt =
333
334 // Complete the problem set up
335 complete_problem_setup();
336
337 // Assign eqn numbers
338 oomph_info << "Number of equations: " << assign_eqn_numbers() << std::endl;
339
340} // end constructor
341
342//========start_of_complete_problem_setup==================================
343/// Assign function/parameter pointers, set boundary conditions etc.
344//=========================================================================
345template<class ELEMENT>
347{
348 // Determine number of bulk elements in mesh
349 const unsigned n_element_bulk = Bulk_mesh_pt->nelement();
350
351 // Loop over the bulk elements
352 for (unsigned e = 0; e < n_element_bulk; e++)
353 {
354 // Upcast from GeneralisedElement to the present element
355 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(Bulk_mesh_pt->element_pt(e));
356
357 // Set the constitutive law
358 el_pt->constitutive_law_pt() = Constitutive_law_pt;
359
360 // Cast to element with pressure dofs
363 el_pt);
364
365 // Cast will only succeed if we're using the pressure formulation
366 // Can set compressible/incompressible and pin any redundant solid pressures
367 // here
368 if (el_with_pres_pt != 0)
369 {
371 {
372 el_with_pres_pt->set_incompressible();
373 }
374 else
375 {
376 el_with_pres_pt->set_compressible();
377 }
378 el_with_pres_pt->pin_elemental_redundant_nodal_solid_pressures();
379 }
380 } // End of loop over bulk elements
381
382 // Determine number of 1D interface elements in mesh
383 const unsigned n_interface_element = Surface_mesh_pt->nelement();
384
385 // Loop over the interface elements
386 for (unsigned e = 0; e < n_interface_element; e++)
387 {
388 // Upcast from GeneralisedElement to the present element
391 Surface_mesh_pt->element_pt(e));
392
393 // Set the traction function for applying pressure
395
396 } // End of loop over interface elements
397
398 // Set the Dirichlet boundary conditions for this problem
399 // Find number of nodes on boundary 3
400 unsigned n_boundary_node = Bulk_mesh_pt->nboundary_node(3);
401 // Loop over nodes on fixed boundary
402 for (unsigned n = 0; n < n_boundary_node; n++)
403 {
404 // Pin radial position on symmetry axis only
405 Bulk_mesh_pt->boundary_node_pt(3, n)->pin_position(0);
406 }
407 n_boundary_node = Bulk_mesh_pt->nboundary_node(1);
408 // Loop over nodes on fixed boundary
409 for (unsigned n = 0; n < n_boundary_node; n++)
410 {
411 // Pin radial position on symmetry axis only
412 Bulk_mesh_pt->boundary_node_pt(1, n)->pin_position(1);
413 }
414}
415
416//============start_of_create_boundary_elements===============
417/// Create boundary elements
418//=======================================================================
419template<class ELEMENT>
421{
422 // Loop over elements on boundary 2
423 unsigned n_element = Bulk_mesh_pt->nboundary_element(2);
424 for (unsigned e = 0; e < n_element; e++)
425 {
426 // Set a pointer to the bulk element we wish to our interface
427 // element to
429 dynamic_cast<ELEMENT*>(Bulk_mesh_pt->boundary_element_pt(2, e));
430
431 // Find the index of the face of element e along boundary b
432 int face_index = Bulk_mesh_pt->face_index_at_boundary(2, e);
433
434 // Create the interface element
437 face_index);
438
439 // Add the interface element to the surface mesh
440 this->Surface_mesh_pt->add_element_pt(interface_element_pt);
441 interface_element_pt->set_boundary_number_in_bulk_mesh(2);
442 }
443} // end of create_boundary_elements
444
445//========================================================================
446/// Doc the solution
447//========================================================================
448template<class ELEMENT>
450{
452 char filename[100];
453
454 // Number of plot points
455 unsigned npts = 3;
456
458 "%s/bulk_soln_nu_%g_pres_%g.dat",
459 Doc_info.directory().c_str(),
462 some_file.open(filename);
463 Bulk_mesh_pt->output(some_file, npts);
464 some_file.close();
465
467 "%s/bulk_soln_nu_%g_pres_%g.vtu",
468 Doc_info.directory().c_str(),
471 some_file.open(filename);
472 Bulk_mesh_pt->output_paraview(some_file, npts);
473 some_file.close();
474
475 // Increment the counter
476 Doc_info.number()++;
477}
478
479
480//==============start_auxillary_problem===============================
481/// Axisymmetric compression of a finite thickness shell in cylindrical
482/// polar coordiantes
483//====================================================================
484class AuxiliaryShellProblem : public Problem
485{
486public:
487 /// Constructor:
489
490 /// Destructor
492
493 /// Doc the solution
495
496 /// Solve the auxiliary problem
498
499private:
500 /// Bulk mesh
502
503 /// Pointer to the "surface" mesh
505
506 // DocInfo object
508};
509
510
511//===============start_constructor========================================
512/// Constructor for auxiliary 1D problem
513//========================================================================
515{
516 // Set output directory
518
519 // Initialise counter for solutions
520 Doc_info.number() = 0;
521
522 // Create the mesh: use n_radial elements to match the 2D problem
526
527 // Only complete the setup if the material is compressible. Otherwise,
528 // an analytical solution will be used.
530 {
531 // Set the Poisson ratio in each element
532 unsigned n_el = Bulk_mesh_pt->nelement();
533 for (unsigned e = 0; e < n_el; e++)
534 {
535 // Cast to element
537 dynamic_cast<ShellBenchmarkElement<3>*>(Bulk_mesh_pt->element_pt(e));
538
539 // Set the Poisson ratio
541 }
542
543 // Loop over nodes and set the initial guess in each node: r=R
544 unsigned n_node = Bulk_mesh_pt->nnode();
545 for (unsigned n = 0; n < n_node; n++)
546 {
547 double r = Bulk_mesh_pt->node_pt(n)->x(0);
548 Bulk_mesh_pt->node_pt(n)->set_value(0, r);
549 }
550
551 // Create the "surface mesh" for application of boundary pressure
552 Surface_mesh_pt = new Mesh;
553
554 // Cast to the right-most element in the mesh
556 dynamic_cast<ShellBenchmarkElement<3>*>(
558
559 // Find the index of the face of element e (0) along boundary b (1)
560 int face_index = 1; // Bulk_mesh_pt->face_index_at_boundary(1, 0);
561
562 // Create the interface element
565
566 // Add the interface element to the surface mesh
567 this->Surface_mesh_pt->add_element_pt(interface_element_pt);
568 interface_element_pt->set_boundary_number_in_bulk_mesh(1);
569
570 // Set the pressure in the element
571 interface_element_pt->pressure_pt() =
573
574 // Add the two submeshes
577
578 // Combine all sub-meshes into a single mesh
580
581 // Assign eqn numbers
582 oomph_info << "Number of equations for auxiliary problem: "
583 << assign_eqn_numbers() << std::endl;
584 }
585} // end constructor
586
587//========================================================================
588/// Solve the auxiliary problem
589//========================================================================
591{
592 // If the material is incompressible, the problem can be solved
593 // analytically, but yields an algebriac equation which must still be
594 // solved numerically. This is done using a manual newton solve.
596 {
597 // Storage for residual and Jacobian
598 double residual, jacobian;
599
600 // Containers for undeformed and deformed inner/outer radii.
601 Vector<double> xi(2), r(2);
604
605 // Set the initial guess
606 r[0] = xi[0];
607 r[1] = xi[1];
608
609 // Derivative of outer coord. w.r.t. inner coord.
610 double dr1_dr0;
611
612 // Poisson ratio in incompressible case
613 const double nu = 0.5;
614
615 // Residual from the initial guess
616 r[1] = pow(pow(r[0], 3.0) + pow(xi[1], 3.0) - pow(xi[0], 3.0), 1.0 / 3.0);
617 residual =
618 (1.0 / (1.0 + nu)) * (0.25 * pow(xi[0] / r[0], 4.0) + xi[0] / r[0] -
619 0.25 * pow(xi[1] / r[1], 4.0) - xi[1] / r[1]) -
621
622 // Newton iteration loop
623 while (std::fabs(residual) > 1.0e-8)
624 {
625 // Calculate the Jacobian
626 dr1_dr0 =
627 pow(pow(r[0], 3.0) + pow(xi[1], 3.0) - pow(xi[0], 3.0), -2.0 / 3.0) *
628 r[0] * r[0];
629 jacobian = (1.0 / (1.0 + nu)) *
630 (-1.0 * (pow(xi[0] / r[0], 4.0) + xi[0] / r[0]) / r[0] +
631 (pow(xi[1] / r[1], 4.0) + xi[1] / r[1]) * dr1_dr0 / r[1]);
632 // Take a Newton step
633 r[0] = r[0] - residual / jacobian;
634
635 // Calculate the new residual
636 r[1] = pow(pow(r[0], 3.0) + pow(xi[1], 3.0) - pow(xi[0], 3.0), 1.0 / 3.0);
637 residual =
638 (1 / (1.0 + nu)) * (0.25 * pow(xi[0] / r[0], 4.0) + xi[0] / r[0] -
639 0.25 * pow(xi[1] / r[1], 4.0) - xi[1] / r[1]) -
641 }
642
643 // With the solution obtained, set nodal values from the analytic solution
644 unsigned n_node = Bulk_mesh_pt->nnode();
645 for (unsigned n = 0; n < n_node; n++)
646 {
647 double xi_tmp = Bulk_mesh_pt->node_pt(n)->x(0);
648 Bulk_mesh_pt->node_pt(n)->set_value(
649 0, pow(pow(r[0], 3.0) + pow(xi_tmp, 3.0) - pow(xi[0], 3.0), 1.0 / 3.0));
650 }
651 }
652 // Otherwise, perform a standard Newton solve using the benchmark elements.
653 else
654 {
655 newton_solve();
656 }
657}
658
659//========================================================================
660/// Doc the solution
661//========================================================================
663{
665 char filename[100];
666
667 // Number of plot points
668 unsigned npts = 3;
669
671 "%s/aux_soln_nu_%g_pres_%g.dat",
672 Doc_info.directory().c_str(),
675 some_file.open(filename);
676 Bulk_mesh_pt->output(some_file, npts);
677 some_file.close();
678
679 // Increment the counter
680 Doc_info.number()++;
681}
682
683//===========start_main===================================================
684/// Demonstrate how to solve an unstructured solid problem
685//========================================================================
686int main(int argc, char* argv[])
687{
688 // Store command line arguments
690
693 "--use_solid_pressure", &str_temp_solid_pressure, "Use solid pressure");
694
697 "--incompressible", &str_temp_incompressible, "Incompressible");
698
700 "--poisson", &GlobalPhysicalVariables::nu, "Poisson");
701
704
705 istringstream(str_temp_solid_pressure) >> std::boolalpha >>
707 istringstream(str_temp_incompressible) >> std::boolalpha >>
709
710 // If the Poisson ratio has been set to 0.5, but the use_pressure_formulation
711 // and/or incompressible flags are false, set them to true
712 if (std::fabs(GlobalPhysicalVariables::nu - 0.5) < 1.0e-10 &&
715 {
716 std::string warning_message =
717 "Inconsistency: Poisson ratio has been set to 0.5 but the \n"
718 "use_pressure_formulation and/or incompressible flags are false. \n"
719 "Setting both of these to true to proceed.";
724 }
725
726 // If the incompressibility flag has been set, we must use the solid pressure
727 // formulation. The Poisson ratio will not be used in this case.
730 {
731 std::string warning_message =
732 "Inconsistency: The incompressibility flag is true but we are not using "
733 "\n"
734 "the solid pressure formulation. Setting use_solid_pressure to true to "
735 "proceed. \n";
739 }
740
741 // Set the Poisson ratio to 0.5 if incompressibility has been specified. This
742 // is solely for the purpose of output
744 {
746 }
747
748 // End consistency checks
749
750 // Create the problem
752 {
753 // Build the problem with an additional degree of freedom for pressure
755 problem;
756
757 problem.newton_solve(1);
758 problem.doc_solution();
759 }
760 else
761 {
762 // Standard build
764
765 problem.newton_solve(1);
766 problem.doc_solution();
767 }
768
769 // Build and solve the auxiliary problem
771 aux_problem.solve_auxiliary_problem();
772 aux_problem.doc_solution();
773} // end main
Axisymmetric compression of a finite thickness shell in cylindrical polar coordiantes.
Definition shell.cc:391
~AuxiliaryShellProblem()
Destructor.
Mesh * Surface_mesh_pt
Pointer to the "surface" mesh.
Definition shell.cc:410
AuxiliaryShellProblem()
Constructor:
OneDMesh< ShellBenchmarkElement< 3 > > * Bulk_mesh_pt
Bulk mesh.
Definition shell.cc:407
void doc_solution()
Doc the solution.
void solve_auxiliary_problem()
Solve the auxiliary problem.
Upgrade the mesh to a solidmesh. Quarter-annulus in quadrant r>0, z>0 ntheta - no....
Domain * Undeformed_domain_pt
Pointer to "undeformed" Domain – used to determine the Lagrangian coordinates of any newly created So...
Upgrade the mesh to a solidmesh. Quarter-annulus in quadrant r>0, z>0 ntheta - no....
Definition shell.cc:113
ElasticTwoDAnnularMesh(const unsigned &ntheta, const unsigned &nr, const double &a, const double &h, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Build mesh and copy Eulerian coords to Lagrangian ones so that the initial configuration...
Definition shell.cc:117
Axisymmetric compression of a finite thickness shell in cylindrical polar coordiantes.
Definition shell.cc:147
Z2ErrorEstimator * error_estimator_pt
void create_boundary_elements()
Create the boundary elements.
DocInfo Doc_info
Definition shell.cc:192
void complete_problem_setup()
Complete the problem setup (attach function/physical parameter pointers)
Mesh * Surface_mesh_pt
Pointer to the "surface" mesh.
Definition shell.cc:186
void actions_before_adapt()
Actions before adapt: Wipe the mesh of boundary elements.
void delete_boundary_elements()
Delete boundary elements and wipe the associated mesh.
Definition shell.cc:163
ElasticTwoDAnnularMesh< ELEMENT > * Bulk_mesh_pt
Bulk mesh.
Definition shell.cc:183
ElasticRefineableTwoDAnnularMesh< ELEMENT > * Bulk_mesh_pt
Bulk mesh.
ConstitutiveLaw * Constitutive_law_pt
Pointer to constitutive law.
Definition shell.cc:189
void doc_solution()
Doc the solution.
void actions_after_adapt()
Any actions after refining the mesh.
ShellProblem()
Constructor:
~ShellProblem()
Destructor.
Face elements for the application of an external pressure.
Global physical variables.
Definition shell.cc:55
void external_pressure_fct(const double &time, const Vector< double > &xi, const Vector< double > &x, const Vector< double > &n, Vector< double > &traction)
Definition shell.cc:64
Global simulation settings.
Definition shell.cc:79
unsigned n_azimuthal
Definition shell.cc:93
bool incompressible
If using a pressure formulation, is the solid incompressible?
Definition shell.cc:87
unsigned n_radial
Definition shell.cc:90
bool use_pressure_formulation
Flag for using a pressure formulation.
Definition shell.cc:84
string result_folder
Definition shell.cc:81
int main(int argc, char *argv[])
Demonstrate how to solve an unstructured solid problem.