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