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-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 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 snprintf(filename, sizeof(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
Simple spine mesh class derived from standard 2D mesh. Vertical lines of nodes are located on spines.
Channel flow, through a non-uniform channel, using Spines.
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.