helical_pipe.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 for a 3D navier stokes steady entry flow problem in a helical
27/// tube
28
29//Generic routines
30#include "generic.h"
31#include "navier_stokes.h"
32
33// The mesh
34#include "meshes/tube_mesh.h"
35
36using namespace std;
37
38using namespace oomph;
39
40
41//=start_of_MyHelicalCylinder==============================================
42/// The arguemts are the radius of the tube, its curvature in the x,y plane
43/// and the pitch of the helix
44//=========================================================================
45class MyHelicalCylinder : public GeomObject
46{
47public:
48
49/// Constructor
50 MyHelicalCylinder(const double &radius, const double& delta,
51 const double& pitch) :
52 GeomObject(3,3),Radius(radius),Delta(delta),P(pitch)
53 {
54 //Set the value of pi
55 Pi = MathematicalConstants::Pi;
56 }
57
58/// Destructor
60
61/// Lagrangian coordinate xi
62void position (const Vector<double>& xi, Vector<double>& r) const
63{
64 r[0] = (1.0/Delta)*cos(xi[0]) + xi[2]*Radius*cos(xi[0])*cos(xi[1]);
65 r[1] = (1.0/Delta)*sin(xi[0]) + xi[2]*Radius*sin(xi[0])*cos(xi[1]);
66 r[2] = P*xi[0]/(2.0*Pi) - xi[2]*Radius*sin(xi[1]);
67}
68
69
70/// Return the position of the tube as a function of time
71/// (doesn't move as a function of time)
72void position(const unsigned& t,
73 const Vector<double>& xi, Vector<double>& r) const
74 {
75 position(xi,r);
76 }
77
78private:
79 double Pi;
80 double Radius;
81 double Delta;
82 double P;
83};
84
85//=start_of_namespace================================================
86/// Namespace for physical parameters
87//===================================================================
89{
90 /// Reynolds number
91 double Re=50;
92
93 double Delta=0.5;
94 double Pitch = 1.0;
95} // end_of_namespace
96
97
98//=start_of_problem_class=============================================
99/// Entry flow problem in tapered tube domain
100//====================================================================
101template<class ELEMENT>
102class SteadyHelicalProblem : public Problem
103{
104
105public:
106
107 /// Constructor: Pass DocInfo object and target errors
108 SteadyHelicalProblem(DocInfo& doc_info, const double& min_error_target,
109 const double& max_error_target);
110
111 /// Destructor (empty)
113
114 /// Update the problem specs before solve
116
117 /// After adaptation: Pin redudant pressure dofs.
119 {
120 // Pin redudant pressure dofs
121 RefineableNavierStokesEquations<3>::
122 pin_redundant_nodal_pressures(mesh_pt()->element_pt());
123 }
124
125 /// Doc the solution
126 void doc_solution();
127
128 /// Overload generic access function by one that returns
129 /// a pointer to the specific mesh
130 RefineableTubeMesh<ELEMENT>* mesh_pt()
131 {
132 return dynamic_cast<RefineableTubeMesh<ELEMENT>*>(Problem::mesh_pt());
133 }
134
135private:
136
137 /// Exponent for bluntness of velocity profile
138 int Alpha;
139
140 /// Doc info object
141 DocInfo Doc_info;
142
143 /// Pointer to GeomObject that specifies the domain boundary
144 GeomObject*Wall_pt;
145
146}; // end_of_problem_class
147
148
149
150
151//=start_of_constructor===================================================
152/// Constructor: Pass DocInfo object and error targets
153//========================================================================
154template<class ELEMENT>
156 const double& min_error_target,
157 const double& max_error_target)
158 : Doc_info(doc_info)
159{
160 //Increase the value of the maximum residuals so that the first
161 //newton step converges.
162 Max_residuals = 100.0;
163
164 // Setup mesh:
165 //------------
166
167 //Build geometric object that forms the domain boundary: a tapered curved tube
170
171// Create GeomObject that specifies the domain boundary
172Wall_pt=new MyHelicalCylinder(1.0,d,p);
173
174//Define pi
175 const double pi = MathematicalConstants::Pi;
176
177
178//Set the centerline coordinates for the start and end of the helix
179 Vector<double> centreline_limits(2);
180 centreline_limits[0] = 0.0;
181 centreline_limits[1] = pi;
182
183 //Set the positions of the angles that divide the outer ring of elements
184 Vector<double> theta_positions(4);
185 theta_positions[0] = -0.75*pi;
186 theta_positions[1] = -0.25*pi;
187 theta_positions[2] = 0.25*pi;
188 theta_positions[3] = 0.75*pi;
189
190 //Define the radial fraction for the central box in the domain
191 Vector<double> radial_frac(4,0.5);
192
193 // Number of layers in the mesh
194 unsigned nlayer=6;
195
196 // Build and assign mesh
197 Problem::mesh_pt()=
198 new RefineableTubeMesh<ELEMENT>(Wall_pt,
199 centreline_limits,
200 theta_positions,
201 radial_frac,
202 nlayer);
203
204 // Set error estimator
205 Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
206 mesh_pt()->spatial_error_estimator_pt()=error_estimator_pt;
207
208 // Error targets for adaptive refinement
209 mesh_pt()->max_permitted_error()=max_error_target;
210 mesh_pt()->min_permitted_error()=min_error_target;
211
212 // Set the boundary conditions for this problem: All nodal values are
213 // free by default -- just pin the ones that have Dirichlet conditions
214 // here.
215 //Choose the conventional form by setting gamma to zero
216 //The boundary conditions will be pseudo-traction free (d/dn = 0)
217 ELEMENT::Gamma[0] = 0.0;
218 ELEMENT::Gamma[1] = 0.0;
219 ELEMENT::Gamma[2] = 0.0;
220
221 unsigned num_bound = mesh_pt()->nboundary();
222 for(unsigned ibound=0;ibound<num_bound;ibound++)
223 {
224 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
225 for (unsigned inod=0;inod<num_nod;inod++)
226 {
227 // Boundary 0 is the inlet symmetry boundary:
228 // Boundary 1 is the tube wall
229 // Pin all values
230 if((ibound==0) || (ibound==1))
231 {
232 mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
233 mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
234 mesh_pt()->boundary_node_pt(ibound,inod)->pin(2);
235 }
236
237 //Boundary 2 is the outflow boundary, pin z,
238 //leave traction free
239 if(ibound==2)
240 {
241 //mesh_pt()->boundary_node_pt(ibound,inod)->pin(2);
242 }
243 }
244 } // end loop over boundaries
245
246 // Loop over the elements to set up element-specific
247 // things that cannot be handled by constructor
248 unsigned n_element = mesh_pt()->nelement();
249 for(unsigned i=0;i<n_element;i++)
250 {
251 // Upcast from GeneralisedElement to the present element
252 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
253
254 //Set the Reynolds number, etc
255 el_pt->re_pt() = &Global_Physical_Variables::Re;
256 }
257
258 // Pin redudant pressure dofs
259 RefineableNavierStokesEquations<3>::
260 pin_redundant_nodal_pressures(mesh_pt()->element_pt());
261
262 //Attach the boundary conditions to the mesh
263 cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
264
265} // end_of_constructor
266
267
268//=start_of_actions_before_newton_solve==========================================
269/// Set the inflow boundary conditions
270//========================================================================
271template<class ELEMENT>
273{
274
275 // (Re-)assign velocity profile at inflow values
276 //--------------------------------------------
277 unsigned ibound=0;
278 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
279 for (unsigned inod=0;inod<num_nod;inod++)
280 {
281 // Recover coordinates
282 double x=mesh_pt()->boundary_node_pt(ibound,inod)->x(0) -
284 double z=mesh_pt()->boundary_node_pt(ibound,inod)->x(2);
285 double r=sqrt(x*x+z*z);
286
287 // Bluntish profile for axial velocity (component 1)
288 mesh_pt()->boundary_node_pt(ibound,inod)->
289 set_value(1,(1.0-pow(r,2.0)));
290 }
291
292} // end_of_actions_before_newton_solve
293
294
295//=start_of_doc_solution==================================================
296/// Doc the solution
297//========================================================================
298template<class ELEMENT>
300{
301
302 ofstream some_file;
303 char filename[100];
304
305 // Number of plot points
306 unsigned npts;
307 npts=5;
308
309 //Need high precision for large radii of curvature
310 //some_file.precision(10);
311 // Output solution
312 snprintf(filename, sizeof(filename), "%s/soln_Re%g.dat",Doc_info.directory().c_str(),
314 some_file.open(filename);
315 mesh_pt()->output(some_file,npts);
316 some_file.close();
317
318} // end_of_doc_solution
319
320
321
322
323////////////////////////////////////////////////////////////////////////
324////////////////////////////////////////////////////////////////////////
325////////////////////////////////////////////////////////////////////////
326
327
328//=start_of_main=======================================================
329/// Driver for 3D entry flow into a tapered tube. If there are
330/// any command line arguments, we regard this as a validation run
331/// and perform only a single adaptation
332//=====================================================================
333int main(int argc, char* argv[])
334{
335
336 // Store command line arguments
337 CommandLineArgs::setup(argc,argv);
338
339 // Allow (up to) two rounds of fully automatic adapation in response to
340 //-----------------------------------------------------------------------
341 // error estimate
342 //---------------
343 unsigned max_adapt;
344 double max_error_target,min_error_target;
345
346 // Set max number of adaptations in black-box Newton solver and
347 // error targets for adaptation
348 if (CommandLineArgs::Argc==1)
349 {
350 // Up to two adaptations
351 max_adapt=2;
352
353 // Error targets for adaptive refinement
354 max_error_target=0.005;
355 min_error_target=0.0005;
356 }
357 // Validation run: Only one adaptation. Relax error targets
358 // to ensure that not all elements are refined so we're getting
359 // some hanging nodes.
360 else
361 {
362 // Validation run: Just one round of adaptation
363 max_adapt=1;
364
365 // Error targets for adaptive refinement
366 max_error_target=0.02;
367 min_error_target=0.002;
368 }
369 // end max_adapt setup
370
371
372 // Set up doc info
373 DocInfo doc_info;
374
375 // Do Taylor-Hood elements
376 //------------------------
377 {
378 // Set output directory
379 doc_info.set_directory("RESLT_TH");
380
381 // Step number
382 doc_info.number()=0;
383
384 // Build problem
386 problem(doc_info,min_error_target,max_error_target);
387
388 cout << " Doing Taylor-Hood elements " << std::endl;
389
390 // Solve the problem
391 problem.newton_solve(max_adapt);
392 // Doc solution after solving
393 problem.doc_solution();
394 }
395
396 // Do Crouzeix-Raviart elements
397 //------------------------
398 {
399 // Set output directory
400 doc_info.set_directory("RESLT_CR");
401
402 // Step number
403 doc_info.number()=0;
404
405 // Build problem
407 problem(doc_info,min_error_target,max_error_target);
408
409 cout << " Doing Crouzeix-Raviart elements " << std::endl;
410
411 // Solve the problem
412 problem.newton_solve(max_adapt);
413 // Doc solution after solving
414 problem.doc_solution();
415 }
416
417} // end_of_main
418
419
The arguemts are the radius of the tube, its curvature in the x,y plane and the pitch of the helix.
MyHelicalCylinder(const double &radius, const double &delta, const double &pitch)
Constructor.
virtual ~MyHelicalCylinder()
Destructor.
void position(const Vector< double > &xi, Vector< double > &r) const
Lagrangian coordinate xi.
void position(const unsigned &t, const Vector< double > &xi, Vector< double > &r) const
Return the position of the tube as a function of time (doesn't move as a function of time)
Entry flow problem in tapered tube domain.
DocInfo Doc_info
Doc info object.
GeomObject * Wall_pt
Pointer to GeomObject that specifies the domain boundary.
void actions_before_newton_solve()
Update the problem specs before solve.
RefineableTubeMesh< ELEMENT > * mesh_pt()
Overload generic access function by one that returns a pointer to the specific mesh.
void actions_after_adapt()
After adaptation: Pin redudant pressure dofs.
SteadyHelicalProblem(DocInfo &doc_info, const double &min_error_target, const double &max_error_target)
Constructor: Pass DocInfo object and target errors.
~SteadyHelicalProblem()
Destructor (empty)
int Alpha
Exponent for bluntness of velocity profile.
void doc_solution()
Doc the solution.
int main(int argc, char *argv[])
Driver for 3D entry flow into a tapered tube. If there are any command line arguments,...
Namespace for physical parameters.
double Re
Reynolds number.
double Delta
The desired curvature of the pipe.