spine_channel2.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-2024 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 2-D Channel with changing width, using Taylor Hood
27// and Crouzeix Raviart elements.
28
29// Generic oomph-lib header
30#include "generic.h"
31
32// Navier Stokes headers
33#include "navier_stokes.h"
34
35// The mesh
36#include "meshes/channel_spine_mesh.h"
37
38using namespace std;
39
40using namespace oomph;
41
42//==start_of_namespace===================================================
43/// Namespace for physical parameters
44//=======================================================================
46{
47 /// Reynolds number
48 double Re=100;
49} // end_of_namespace
50
51
52/// ////////////////////////////////////////////////////////////////////
53/// ////////////////////////////////////////////////////////////////////
54// Deflected line as geometric object
55/// ////////////////////////////////////////////////////////////////////
56/// ////////////////////////////////////////////////////////////////////
57
58
59
60//=========================================================================
61/// Steady, spiked 1D line in 2D space
62/// \f[ x = \zeta \f]
63/// \f[ y = \left\{
64/// \begin{array}{cl}
65/// H + 2A\left(\frac{\zeta - \zeta_{\mbox{min}}}
66/// {\zeta_{\mbox{max}} - \zeta_{\mbox{min}}}\right) &
67/// \mbox{for } \zeta \leq \frac{1}{2}
68/// \left(\zeta_{\mbox{max}} + \zeta_{\mbox{min}}\right)\\H +
69/// 2A\left(\frac{\zeta - \zeta_{\mbox{max}}}
70/// {\zeta_{\mbox{min}} - \zeta_{\mbox{max}}}\right) &
71/// \mbox{for } \zeta > \frac{1}{2}
72/// \left(\zeta_{\mbox{max}}
73/// + \zeta_{\mbox{min}}\right)\\ \end{array}
74/// \right.\f]
75//=========================================================================
76class SpikedLine : public GeomObject
77{
78
79public:
80
81 /// Constructor: Four item of geometric data:
82 /// \code
83 /// Geom_data_pt[0]->value(0) = height
84 /// Geom_data_pt[0]->value(1) = amplitude
85 /// Geom_data_pt[0]->value(2) = zeta_min
86 /// Geom_data_pt[0]->value(3) = zeta_max
87 /// \endcode
88 SpikedLine(const Vector<Data*>& geom_data_pt) : GeomObject(1,2)
89 {
90#ifdef PARANOID
91 if (geom_data_pt.size()!=1)
92 {
93 std::ostringstream error_stream;
95 << "Wrong size for geom_data_pt " << geom_data_pt.size() << std::endl;
96 if (geom_data_pt[0]->nvalue()!=1)
97 {
98 error_stream << "Wrong geom_data_pt[0]->nvalue() "
99 << geom_data_pt[0]->nvalue() << std::endl;
100 }
101
102 throw OomphLibError(error_stream.str(),
105 }
106#endif
107 Geom_data_pt.resize(1);
109
110 // Data has been created externally: Must not clean up
111 Must_clean_up=false;
112 }
113
114
115 /// Constructor: Pass height, amplitude, zeta min and zeta max
116 /// (all are pinned by default)
117 SpikedLine(const double& height, const double& amplitude,
118 const double& zeta_min, const double& zeta_max)
119 : GeomObject(1,2)
120 {
121 // Create Data for deflected-line object
122 Geom_data_pt.resize(1);
123
124 // Create data: Four value, no timedependence, free by default
125 Geom_data_pt[0] = new Data(4);
126
127 // I've created the data, I need to clean up
128 Must_clean_up=true;
129
130 // Pin the data
131 for (unsigned i=0;i<4;i++) {Geom_data_pt[0]->pin(i);}
132
133 // Give it a value: Initial height
134 Geom_data_pt[0]->set_value(0,height);
135 Geom_data_pt[0]->set_value(1,amplitude);
136 Geom_data_pt[0]->set_value(2,zeta_min);
137 Geom_data_pt[0]->set_value(3,zeta_max);
138 }
139
140
141 /// Destructor: Clean up if necessary
143 {
144 // Do I need to clean up?
145 if (Must_clean_up)
146 {
147 delete Geom_data_pt[0];
148 Geom_data_pt[0]=0;
149 }
150 }
151
152
153 /// Position Vector at Lagrangian coordinate zeta
154 void position(const Vector<double>& zeta, Vector<double>& r) const
155 {
156#ifdef PARANOID
157 if (r.size()!=Ndim)
158 {
159 throw OomphLibError("The vector r has the wrong dimension\n",
162 }
163#endif
164 // Get parametres
165 double H = Geom_data_pt[0]->value(0);
166 double A = Geom_data_pt[0]->value(1);
167 double zeta_min = Geom_data_pt[0]->value(2);
168 double zeta_max = Geom_data_pt[0]->value(3);
169 double halfLz = (zeta_max-zeta_min)/2.0;
170
171 // Position Vector
172 r[0] = zeta[0];
173 if (zeta[0]-zeta_min<=halfLz)
174 {
175 r[1] = H + A*(zeta[0]-zeta_min)/halfLz;
176 }
177 else
178 {
179 r[1] = H - A*(zeta[0]-zeta_max)/halfLz;
180 }
181 }
182
183
184 /// Parametrised position on object: r(zeta). Evaluated at
185 /// previous timestep. t=0: current time; t>0: previous
186 /// timestep.
187 void position(const unsigned& t, const Vector<double>& zeta,
188 Vector<double>& r) const
189 {
190#ifdef PARANOID
192 {
193 std::ostringstream error_stream;
195 << "t > nprev_values() in SpikedLine: " << t << " "
196 << Geom_data_pt[0]->time_stepper_pt()->nprev_values() << std::endl;
197
198 throw OomphLibError(error_stream.str(),
201 }
202#endif
203
204 // Get parametres
205 double H = Geom_data_pt[0]->value(t,0);
206 double A = Geom_data_pt[0]->value(t,1);
207 double zeta_min = Geom_data_pt[0]->value(t,2);
208 double zeta_max = Geom_data_pt[0]->value(t,3);
209 double halfLz = (zeta_max-zeta_min)/2.0;
210
211 // Position Vector
212 r[0] = zeta[0];
213 if (zeta[0]-zeta_min<=halfLz)
214 {
215 r[1] = H + A*(zeta[0]-zeta_min)/halfLz;
216 }
217 else
218 {
219 r[1] = H - A*(zeta[0]-zeta_max)/halfLz;
220 }
221 }
222
223
224 /// Derivative of position Vector w.r.t. to coordinates:
225 /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
226 /// Evaluated at current time.
227 virtual void dposition(const Vector<double>& zeta,
229 {
230 // Get parametres
231 double A = Geom_data_pt[0]->value(1);
232 double zeta_min = Geom_data_pt[0]->value(2);
233 double zeta_max = Geom_data_pt[0]->value(3);
234 double halfLz = (zeta_max-zeta_min)/2.0;
235
236 // Tangent vector
237 drdzeta(0,0)=1.0;
238 if (zeta[0]-zeta_min<=halfLz)
239 {
240 drdzeta(0,1)=A/halfLz;
241 }
242 else
243 {
244 drdzeta(0,1)=-A/halfLz;
245 }
246 }
247
248
249 /// 2nd derivative of position Vector w.r.t. to coordinates:
250 /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ = ddrdzeta(alpha,beta,i).
251 /// Evaluated at current time.
252 virtual void d2position(const Vector<double>& zeta,
254 {
255 // Derivative of tangent vector
256 ddrdzeta(0,0,0)=0.0;
257 ddrdzeta(0,0,1)=0.0;
258 }
259
260
261 /// Posn Vector and its 1st & 2nd derivatives
262 /// w.r.t. to coordinates:
263 /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
264 /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ = ddrdzeta(alpha,beta,i).
265 /// Evaluated at current time.
266 virtual void d2position(const Vector<double>& zeta, Vector<double>& r,
269 {
270 // Get parametres
271 double H = Geom_data_pt[0]->value(0);
272 double A = Geom_data_pt[0]->value(1);
273 double zeta_min = Geom_data_pt[0]->value(2);
274 double zeta_max = Geom_data_pt[0]->value(3);
275 double halfLz = (zeta_max-zeta_min)/2.0;
276
277 // Position Vector
278 r[0] = zeta[0];
279 if (zeta[0]-zeta_min<=halfLz)
280 {
281 r[1] = H + A*(zeta[0]-zeta_min)/halfLz;
282 }
283 else
284 {
285 r[1] = H - A*(zeta[0]-zeta_max)/halfLz;
286 }
287
288 // Tangent vector
289 drdzeta(0,0)=1.0;
290 if (zeta[0]-zeta_min<=halfLz)
291 {
292 drdzeta(0,1)=A/halfLz;
293 }
294 else
295 {
296 drdzeta(0,1)=-A/halfLz;
297 }
298
299 // Derivative of tangent vector
300 ddrdzeta(0,0,0)=0.0;
301 ddrdzeta(0,0,1)=0.0;
302 }
303
304 /// How many items of Data does the shape of the object depend on?
305 unsigned ngeom_data() const {return Geom_data_pt.size();}
306
307 /// Return pointer to the j-th Data item that the object's
308 /// shape depends on
309 Data* geom_data_pt(const unsigned& j) {return Geom_data_pt[j];}
310
311
312private:
313
314 /// Vector of pointers to Data items that affects the object's shape
316
317 /// Do I need to clean up?
318 bool Must_clean_up;
319
320};
321
322/// ////////////////////////////////////////////////////////////////////
323/// ////////////////////////////////////////////////////////////////////
324/// ////////////////////////////////////////////////////////////////////
325
326
327
328//==start_of_problem_class============================================
329/// Channel flow, through a non-uniform channel, using Spines.
330//====================================================================
331template<class ELEMENT>
332class SpikedChannelSpineFlowProblem : public Problem
333{
334private:
335
336 /// Width of channel
337 double Ly;
338
339public:
340
341 /// Destructor: Empty
343
344 /// Update the after solve (empty)
346
347 /// Update the problem specs before solve.
348 /// set velocity boundary conditions just to be on the safe side...
350 {
351 // Update the mesh
352 mesh_pt()->node_update();
353
354 // Setup inflow flow along boundary 3:
355 unsigned ibound=3;
356 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
357 for (unsigned inod=0;inod<num_nod;inod++)
358 {
359 double y=mesh_pt()->boundary_node_pt(ibound,inod)->x(1);
360 // parabolic inflow
361 unsigned i=0;
362 mesh_pt()->boundary_node_pt(ibound,inod)->set_value(i,y*(Ly-y));
363 // 0 Tangential flow
364 i=1;
365 mesh_pt()->boundary_node_pt(ibound,inod)->set_value(i,0);
366 }
367
368 // Overwrite with no flow along top & bottom boundaries and
369 // parallel outflow
370 unsigned num_bound = mesh_pt()->nboundary();
371 for(unsigned ibound=0;ibound<num_bound-1;ibound++)
372 {
373 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
374 for (unsigned inod=0;inod<num_nod;inod++)
375 {
376 for (unsigned i=0;i<2;i++)
377 {
378 if (ibound!=1)
379 {
380 mesh_pt()->boundary_node_pt(ibound,inod)->set_value(i,0.0);
381 }
382 else
383 {
384 mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1,0.0);
385 }
386 }
387 }
388 }
389 // Leave boundary 1 free!
390 } // end_of_actions_before_newton_solve
391
392 /// Upcasted access function for the mesh
394 {
395 return dynamic_cast<ChannelSpineMesh<ELEMENT>*>(Problem::mesh_pt());
396 }
397
398 /// Constructor
400
401 /// Doc the solution
403
404}; // end_of_problem_class
405
406
407
408//==start_of_constructor==================================================
409/// Constructor for SpikedChannelSpineFlow problem
410///
411//========================================================================
412template<class ELEMENT>
414{
415
416 // Setup mesh
417
418 // # of elements in x-direction in left region
419 unsigned Nx0=3;
420 // # of elements in x-direction in centre region
421 unsigned Nx1=12;
422 // # of elements in x-direction in right region
423 unsigned Nx2=8;
424
425 // # of elements in y-direction
426 unsigned Ny=10;
427
428 // Domain length in x-direction in left region
429 double Lx0=0.5;
430 // Domain length in x-direction in centre region
431 double Lx1=0.7;
432 // Domain length in x-direction in right region
433 double Lx2=1.5;
434
435 // Domain length in y-direction
436 Ly=1.0;
437
438 double amplitude_upper = -0.4*Ly;
439 double zeta_min=Lx0;
440 double zeta_max=Lx0+Lx1;
441 GeomObject* UpperWall =
443
444 // Build and assign mesh
445 Problem::mesh_pt() = new ChannelSpineMesh<ELEMENT>(Nx0,Nx1,Nx2,Ny,
446 Lx0,Lx1,Lx2,Ly,
447 UpperWall);
448
449 // Set the boundary conditions for this problem: All nodes are
450 // free by default -- just pin the ones that have Dirichlet conditions
451 // here: All boundaries are Dirichlet boundaries, except on boundary 1
452 unsigned num_bound = mesh_pt()->nboundary();
453 for(unsigned ibound=0;ibound<num_bound;ibound++)
454 {
455 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
456 for (unsigned inod=0;inod<num_nod;inod++)
457 {
458 if (ibound!=1)
459 {
460 // Loop over values (u and v velocities)
461 for (unsigned i=0;i<2;i++)
462 {
463 mesh_pt()->boundary_node_pt(ibound,inod)->pin(i);
464 }
465 }
466 else
467 {
468 // Parallel outflow ==> no-slip
469 mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
470 }
471 }
472 } // end loop over boundaries
473
474 // Find number of elements in mesh
475 unsigned n_element = mesh_pt()->nelement();
476
477 // Loop over the elements to set up element-specific
478 // things that cannot be handled by constructor: Pass pointer to Reynolds
479 // number
480 for(unsigned e=0;e<n_element;e++)
481 {
482 // Upcast from GeneralisedElement to the present element
483 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
484 //Set the Reynolds number
486 } // end loop over elements
487
488 // Setup equation numbering scheme
489 cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
490
491} // end_of_constructor
492
493
494
495//==start_of_doc_solution=================================================
496/// Doc the solution
497//========================================================================
498template<class ELEMENT>
500{
501
503 char filename[100];
504
505 // Number of plot points
506 unsigned npts=5;
507
508 // Output solution
509 sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
510 doc_info.number());
511 some_file.open(filename);
512 mesh_pt()->output(some_file,npts);
513 some_file.close();
514
515} // end_of_doc_solution
516
517
518//==start_of_main======================================================
519/// Driver for SpikedChannelSpineFlow test problem
520//=====================================================================
521int main()
522{
523 // Set output directory
525 doc_info.set_directory("RESLT");
526 doc_info.number()=0;
527
528 // Solve problem with Taylor Hood elements
529 //---------------------------------------
530 {
531 //Build problem
533 problem;
534
535 // Solve the problem with automatic adaptation
536 problem.newton_solve();
537
538 //Output solution
539 problem.doc_solution(doc_info);
540 // Step number
541 doc_info.number()++;
542
543 } // end of Taylor Hood elements
544
545
546 // Solve problem with Crouzeix Raviart elements
547 //--------------------------------------------
548 {
549 // Build problem
551 problem;
552
553 // Solve the problem with automatic adaptation
554 problem.newton_solve();
555
556 //Output solution
557 problem.doc_solution(doc_info);
558 // Step number
559 doc_info.number()++;
560
561 } // end of Crouzeix Raviart elements
562
563
564} // end_of_main
565
566
567
568
569
570
571
572
573
574
575
////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
SpikedChannelSpineFlowProblem()
Constructor.
~SpikedChannelSpineFlowProblem()
Destructor: Empty.
void actions_after_newton_solve()
Update the after solve (empty)
void actions_before_newton_solve()
Update the problem specs before solve. set velocity boundary conditions just to be on the safe side....
void doc_solution(DocInfo &doc_info)
Doc the solution.
double Ly
Width of channel.
ChannelSpineMesh< ELEMENT > * mesh_pt()
Upcasted access function for the mesh.
Namespace for physical parameters.
double Re
Reynolds number.
int main()
Driver for SpikedChannelSpineFlow test problem.