spine_channel.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// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 // Driver for 2-D Channel with changing width, using Taylor Hood
31 // and Crouzeix Raviart elements.
32 
33 // Generic oomph-lib header
34 #include "generic.h"
35 
36 // Navier Stokes headers
37 #include "navier_stokes.h"
38 
39 // The mesh
40 #include "meshes/channel_spine_mesh.h"
41 
42 using namespace std;
43 
44 using namespace oomph;
45 
46 //==start_of_namespace===================================================
47 /// Namespace for physical parameters
48 //=======================================================================
50 {
51 
52  /// Reynolds number
53  double Re=100;
54 
55 } // end_of_namespace
56 
57 
58 
59 
60 
61 ///////////////////////////////////////////////////////////////////////
62 ///////////////////////////////////////////////////////////////////////
63 // Sinusoidal wall as geometric object
64 ///////////////////////////////////////////////////////////////////////
65 ///////////////////////////////////////////////////////////////////////
66 
67 
68 
69 //=========================================================================
70 /// Geometric object representing a sinusoidal wall, parametrised by
71 /// \f[ x = \zeta \f]
72 /// \f[ y = H + A \sin\left(\frac{2\pi \left(\zeta-\zeta_{\mbox{min}}\right)}
73 /// {\zeta_{\mbox{max}}-\zeta_{\mbox{min}}}
74 /// \right)\f]
75 //=========================================================================
76 class SinusoidalWall : public GeomObject
77 {
78 
79 public:
80 
81  /// \short Constructor: Pass height, amplitude, zeta min and zeta max
82  /// (all are pinned by default)
83  SinusoidalWall(const double& height, const double& amplitude,
84  const double& zeta_min, const double& zeta_max)
85  : GeomObject(1,2)
86  {
87  // Make space for the geometric data
88  Geom_data_pt.resize(1);
89 
90  // Create data: Four value, no time dependence, free by default
91  Geom_data_pt[0] = new Data(4);
92 
93  // I've created the data, I have to clean up
94  Must_clean_up=true;
95 
96  // Pin the data
97  for (unsigned i=0;i<4;i++) {Geom_data_pt[0]->pin(i);}
98 
99  // Give it a value: Initial height
100  Geom_data_pt[0]->set_value(0,height);
101  Geom_data_pt[0]->set_value(1,amplitude);
102  Geom_data_pt[0]->set_value(2,zeta_min);
103  Geom_data_pt[0]->set_value(3,zeta_max);
104  }
105 
106 
107  /// \short Constructor: One item of geometric data, containing
108  /// four values:
109  /// \code
110  /// Geom_data_pt[0]->value(0) = height
111  /// Geom_data_pt[0]->value(1) = amplitude
112  /// Geom_data_pt[0]->value(2) = zeta_min
113  /// Geom_data_pt[0]->value(3) = zeta_max
114  /// \endcode
115  SinusoidalWall(const Vector<Data*>& geom_data_pt) : GeomObject(1,2)
116  {
117 #ifdef PARANOID
118  if(geom_data_pt.size()!=1)
119  {
120  std::ostringstream error_stream;
121  error_stream
122  << "Wrong size for geom_data_pt " << geom_data_pt.size() << std::endl;
123  if (geom_data_pt[0]->nvalue()!=1)
124  {
125  error_stream << "Wrong geom_data_pt[0]->nvalue() "
126  << geom_data_pt[0]->nvalue() << std::endl;
127  }
128 
129  throw OomphLibError(error_stream.str(),
130  OOMPH_CURRENT_FUNCTION,
131  OOMPH_EXCEPTION_LOCATION);
132  }
133 #endif
134  Geom_data_pt.resize(1);
135  Geom_data_pt[0]=geom_data_pt[0];
136 
137  // Data has been created externally: Must not clean up
138  Must_clean_up=false;
139  }
140 
141 
142 
143  /// Destructor: Clean up if necessary
145  {
146  // Do I need to clean up?
147  if (Must_clean_up)
148  {
149  delete Geom_data_pt[0];
150  Geom_data_pt[0]=0;
151  }
152  }
153 
154  /// Access function for horizontal half axis
155  double& height(){return *Geom_data_pt[0]->value_pt(0);}
156 
157  /// Access function for vertical half axis
158  double& amplitude(){return *Geom_data_pt[0]->value_pt(1);}
159 
160 
161  /// \short Position vector at Lagrangian coordinate zeta
162  void position(const Vector<double>& zeta, Vector<double>& r) const
163  {
164 #ifdef PARANOID
165  if (r.size()!=Ndim)
166  {
167  throw OomphLibError("The vector r has the wrong dimension\n",
168  OOMPH_CURRENT_FUNCTION,
169  OOMPH_EXCEPTION_LOCATION);
170  }
171 #endif
172  // Get parameters
173  double H = Geom_data_pt[0]->value(0);
174  double A = Geom_data_pt[0]->value(1);
175  double zeta_min = Geom_data_pt[0]->value(2);
176  double zeta_max = Geom_data_pt[0]->value(3);
177  double Lz = zeta_max-zeta_min;
178 
179  // Position Vector
180  r[0] = zeta[0];
181  r[1] = H + A*sin(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz);
182  }
183 
184 
185  /// \short Parametrised position on object: r(zeta). Evaluated at
186  /// previous timestep. t=0: current time; t>0: previous
187  /// timestep.
188  void position(const unsigned& t, const Vector<double>& zeta,
189  Vector<double>& r) const
190  {
191 #ifdef PARANOID
192  if (t>Geom_data_pt[0]->time_stepper_pt()->nprev_values())
193  {
194  std::ostringstream error_stream;
195  error_stream
196  << "t > nprev_values() in SpikedLine: " << t << " "
197  << Geom_data_pt[0]->time_stepper_pt()->nprev_values() << std::endl;
198 
199  throw OomphLibError(error_stream.str(),
200  OOMPH_CURRENT_FUNCTION,
201  OOMPH_EXCEPTION_LOCATION);
202  }
203 #endif
204 
205  // Get parameters
206  double H = Geom_data_pt[0]->value(t,0);
207  double A = Geom_data_pt[0]->value(t,1);
208  double zeta_min = Geom_data_pt[0]->value(t,2);
209  double zeta_max = Geom_data_pt[0]->value(t,3);
210  double Lz = zeta_max-zeta_min;
211 
212  // Position Vector at time level t
213  r[0] = zeta[0];
214  r[1] = H + A*sin(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz);
215  }
216 
217 
218 
219  /// \short Derivative of position Vector w.r.t. to coordinates:
220  /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
221  /// Evaluated at current time.
222  virtual void dposition(const Vector<double>& zeta,
223  DenseMatrix<double> &drdzeta) const
224  {
225  // Get parametres
226  double A = Geom_data_pt[0]->value(1);
227  double zeta_min = Geom_data_pt[0]->value(2);
228  double zeta_max = Geom_data_pt[0]->value(3);
229  double Lz = zeta_max-zeta_min;
230 
231  // Tangent vector
232  drdzeta(0,0)=1.0;
233  drdzeta(0,1)=MathematicalConstants::Pi*A*
234  cos(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz)/Lz;
235  }
236 
237 
238  /// \short 2nd derivative of position Vector w.r.t. to coordinates:
239  /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ = ddrdzeta(alpha,beta,i).
240  /// Evaluated at current time.
241  virtual void d2position(const Vector<double>& zeta,
242  RankThreeTensor<double> &ddrdzeta) const
243  {
244  // Get parametres
245  double A = Geom_data_pt[0]->value(1);
246  double zeta_min = Geom_data_pt[0]->value(2);
247  double zeta_max = Geom_data_pt[0]->value(3);
248  double Lz = zeta_max-zeta_min;
249 
250  // Derivative of tangent vector
251  ddrdzeta(0,0,0)=0.0;
252  ddrdzeta(0,0,1)=-MathematicalConstants::Pi*MathematicalConstants::Pi*
253  A*sin(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz)/(Lz*Lz);
254  }
255 
256 
257  /// \short Posn Vector and its 1st & 2nd derivatives
258  /// w.r.t. to coordinates:
259  /// \f$ \frac{dR_i}{d \zeta_\alpha}\f$ = drdzeta(alpha,i).
260  /// \f$ \frac{d^2R_i}{d \zeta_\alpha d \zeta_\beta}\f$ = ddrdzeta(alpha,beta,i).
261  /// Evaluated at current time.
262  virtual void d2position(const Vector<double>& zeta, Vector<double>& r,
263  DenseMatrix<double> &drdzeta,
264  RankThreeTensor<double> &ddrdzeta) const
265  {
266  // Get parametres
267  double H = Geom_data_pt[0]->value(0);
268  double A = Geom_data_pt[0]->value(1);
269  double zeta_min = Geom_data_pt[0]->value(2);
270  double zeta_max = Geom_data_pt[0]->value(3);
271  double Lz = zeta_max-zeta_min;
272 
273  // Position Vector
274  r[0] = zeta[0];
275  r[1] = H + A*sin(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz);
276 
277  // Tangent vector
278  drdzeta(0,0)=1.0;
279  drdzeta(0,1)=MathematicalConstants::Pi*A*
280  cos(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz)/Lz;
281 
282  // Derivative of tangent vector
283  ddrdzeta(0,0,0)=0.0;
284  ddrdzeta(0,0,1)=-MathematicalConstants::Pi*MathematicalConstants::Pi*
285  A*sin(MathematicalConstants::Pi*(zeta[0]-zeta_min)/Lz)/(Lz*Lz);
286  }
287 
288  /// How many items of Data does the shape of the object depend on?
289  unsigned ngeom_data() const {return Geom_data_pt.size();}
290 
291  /// \short Return pointer to the j-th Data item that the object's
292  /// shape depends on
293  Data* geom_data_pt(const unsigned& j) {return Geom_data_pt[j];}
294 
295 
296 private:
297 
298  /// \short Vector of pointers to Data items that affects the object's shape
299  Vector<Data*> Geom_data_pt;
300 
301  /// Do I need to clean up?
303 
304 };
305 
306 ///////////////////////////////////////////////////////////////////////
307 ///////////////////////////////////////////////////////////////////////
308 ///////////////////////////////////////////////////////////////////////
309 
310 
311 
312 //==start_of_problem_class============================================
313 /// Channel flow through a non-uniform channel whose geometry is defined
314 /// by a spine mesh.
315 //====================================================================
316 template<class ELEMENT>
317 class ChannelSpineFlowProblem : public Problem
318 {
319 
320 public:
321 
322  /// Constructor
324 
325  /// Destructor: (empty)
327 
328  /// \short Update the problem specs before solve.
329  /// Set velocity boundary conditions just to be on the safe side...
331  {
332  // Update the mesh
333  mesh_pt()->node_update();
334 
335  } // end_of_actions_before_newton_solve
336 
337 
338  /// Update the after solve (empty)
340 
341  /// Doc the solution
342  void doc_solution(DocInfo& doc_info);
343 
344 private:
345 
346  /// Width of channel
347  double Ly;
348 
349 
350 }; // end_of_problem_class
351 
352 
353 
354 //==start_of_constructor==================================================
355 /// Constructor for ChannelSpineFlow problem
356 //========================================================================
357 template<class ELEMENT>
359 {
360 
361  // Setup mesh
362 
363  // # of elements in x-direction in left region
364  unsigned Nx0=3;
365  // # of elements in x-direction in centre region
366  unsigned Nx1=12;
367  // # of elements in x-direction in right region
368  unsigned Nx2=8;
369 
370  // # of elements in y-direction
371  unsigned Ny=10;
372 
373  // Domain length in x-direction in left region
374  double Lx0=0.5;
375  // Domain length in x-direction in centre region
376  double Lx1=0.7;
377  // Domain length in x-direction in right region
378  double Lx2=1.5;
379 
380  // Domain length in y-direction
381  Ly=1.0;
382 
383  // Build geometric object that represents the sinusoidal bump on
384  // the upper wall:
385 
386  // 40% indendentation
387  double amplitude_upper = -0.4*Ly;
388  // Minimum and maximum coordinates of bump
389  double zeta_min=Lx0;
390  double zeta_max=Lx0+Lx1;
391  GeomObject* UpperWall =
392  new SinusoidalWall(Ly,amplitude_upper,zeta_min,zeta_max);
393 
394  // Build and assign mesh -- pass pointer to geometric object
395  // that represents the sinusoidal bump on the upper wall
396  Problem::mesh_pt() = new ChannelSpineMesh<ELEMENT>(Nx0,Nx1,Nx2,Ny,
397  Lx0,Lx1,Lx2,Ly,
398  UpperWall);
399 
400 
401  // Set the boundary conditions for this problem: All nodes are
402  // free by default -- just pin the ones that have Dirichlet conditions
403  // here: All boundaries are Dirichlet boundaries, except on boundary 1
404  unsigned num_bound = mesh_pt()->nboundary();
405  for(unsigned ibound=0;ibound<num_bound;ibound++)
406  {
407  unsigned num_nod= mesh_pt()->nboundary_node(ibound);
408  for (unsigned inod=0;inod<num_nod;inod++)
409  {
410  if (ibound!=1)
411  {
412  // Loop over values (u and v velocities)
413  for (unsigned i=0;i<2;i++)
414  {
415  mesh_pt()->boundary_node_pt(ibound,inod)->pin(i);
416  }
417  }
418  else
419  {
420  // Parallel outflow ==> no-slip
421  mesh_pt()->boundary_node_pt(ibound,inod)->pin(1);
422  }
423  }
424  } // end loop over boundaries
425 
426 
427  // No slip on stationary upper and lower walls (boundaries 0 and 2)
428  // and parallel outflow (boundary 1)
429  for (unsigned ibound=0;ibound<num_bound-1;ibound++)
430  {
431  unsigned num_nod= mesh_pt()->nboundary_node(ibound);
432  for (unsigned inod=0;inod<num_nod;inod++)
433  {
434  if (ibound!=1)
435  {
436  for (unsigned i=0;i<2;i++)
437  {
438  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(i,0.0);
439  }
440  }
441  else
442  {
443  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1,0.0);
444  }
445  }
446  }
447 
448 
449  // Setup parabolic inflow along boundary 3:
450  unsigned ibound=3;
451  unsigned num_nod= mesh_pt()->nboundary_node(ibound);
452  for (unsigned inod=0;inod<num_nod;inod++)
453  {
454  double y=mesh_pt()->boundary_node_pt(ibound,inod)->x(1);
455  // Parallel, parabolic inflow
456  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(0,y*(Ly-y));
457  mesh_pt()->boundary_node_pt(ibound,inod)->set_value(1,0.0);
458  }
459 
460  // Find number of elements in mesh
461  unsigned n_element = mesh_pt()->nelement();
462 
463  // Loop over the elements to set up element-specific
464  // things that cannot be handled by constructor: Pass
465  // pointer to Reynolds number
466  for(unsigned e=0;e<n_element;e++)
467  {
468  // Upcast from GeneralisedElement to the present element
469  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
470  //Set the Reynolds number
471  el_pt->re_pt() = &Global_Physical_Variables::Re;
472  }
473 
474  // Setup equation numbering scheme
475  cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
476 
477 } // end_of_constructor
478 
479 
480 
481 
482 //==start_of_doc_solution=================================================
483 /// Doc the solution
484 //========================================================================
485 template<class ELEMENT>
487 {
488 
489  ofstream some_file;
490  char filename[100];
491 
492  // Number of plot points
493  unsigned npts=5;
494 
495  // Output solution
496  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
497  doc_info.number());
498  some_file.open(filename);
499  mesh_pt()->output(some_file,npts);
500  some_file.close();
501 
502 } // end_of_doc_solution
503 
504 
505 
506 //==start_of_main======================================================
507 /// Driver for channel flow problem with spine mesh.
508 //=====================================================================
509 int main()
510 {
511 
512  // Set output directory
513  DocInfo doc_info;
514  doc_info.set_directory("RESLT");
515  doc_info.number()=0;
516 
517  // Solve problem with Taylor Hood elements
518  //---------------------------------------
519  {
520  //Build problem
522  problem;
523 
524  // Solve the problem with automatic adaptation
525  problem.newton_solve();
526 
527  //Output solution
528  problem.doc_solution(doc_info);
529  // Step number
530  doc_info.number()++;
531 
532  } // end of Taylor Hood elements
533 
534 
535  // Solve problem with Crouzeix Raviart elements
536  //--------------------------------------------
537  {
538  // Build problem
540  problem;
541 
542  // Solve the problem with automatic adaptation
543  problem.newton_solve();
544 
545  //Output solution
546  problem.doc_solution(doc_info);
547  // Step number
548  doc_info.number()++;
549 
550  } // end of Crouzeix Raviart elements
551 
552 
553 
554 } // end_of_main
555 
556 
557 
558 
559 //====================================================================
560 /// Create the files to illustrate the sparse node update operation
561 //====================================================================
563 {
564 
565  // Set output directory
566  DocInfo doc_info;
567  doc_info.set_directory("RESLT");
568 
569  // Setup mesh
570 
571  // # of elements in x-direction in left region
572  unsigned Nx0=1;
573  // # of elements in x-direction in centre region
574  unsigned Nx1=5;
575  // # of elements in x-direction in right region
576  unsigned Nx2=1;
577 
578  // # of elements in y-direction
579  unsigned Ny=5;
580 
581  // Domain length in x-direction in left region
582  double Lx0=0.5;
583  // Domain length in x-direction in centre region
584  double Lx1=1.0;
585  // Domain length in x-direction in right region
586  double Lx2=0.5;
587 
588  // Domain length in y-direction
589  double Ly=1.0;
590 
591  double amplitude_upper = -0.4*Ly;
592  double zeta_min=Lx0;
593  double zeta_max=Lx0+Lx1;
594  GeomObject* UpperWall =
595  new SinusoidalWall(Ly,amplitude_upper,zeta_min,zeta_max);
596 
597  // Build and assign mesh
598  ChannelSpineMesh<SpineElement<QTaylorHoodElement<2> > >* mesh_pt =
599  new ChannelSpineMesh<SpineElement<QTaylorHoodElement<2> > >(Nx0,Nx1,Nx2,Ny,
600  Lx0,Lx1,Lx2,Ly,
601  UpperWall);
602 
603  // Update *all* nodal positions
604  mesh_pt->node_update();
605 
606  // Change the amplitude
607  dynamic_cast<SinusoidalWall*>(mesh_pt->wall_pt())->amplitude()=0.5;
608 
609  unsigned count=0;
610  ofstream some_file;
611  char filename[100];
612 
613  // Number of plot points
614  unsigned npts=5;
615 
616  // Output solution
617  sprintf(filename,"%s/mesh_update%i.dat",doc_info.directory().c_str(),
618  count);
619  count++;
620 
621  // Loop over spines in centre
622  unsigned n_node = mesh_pt->nnode();
623  for (unsigned inode=0;inode<n_node;inode++)
624  {
625  SpineNode* node_pt=dynamic_cast<SpineNode*>(
626  mesh_pt->node_pt(inode));
627  if (node_pt->node_update_fct_id()==1)
628  {
629  node_pt->node_update();
630  // Output solution
631  some_file.open(filename);
632  sprintf(filename,"%s/mesh_update%i.dat",doc_info.directory().c_str(),
633  count);
634  count++;
635  mesh_pt->output(some_file,npts);
636  some_file.close();
637  }
638  }
639 
640 }
641 
642 
643 
644 
645 
646 
647 
648 
649 
650 
651 
652 
653 
654 
virtual void d2position(const Vector< double > &zeta, Vector< double > &r, DenseMatrix< double > &drdzeta, RankThreeTensor< double > &ddrdzeta) const
Posn Vector and its 1st & 2nd derivatives w.r.t. to coordinates: = drdzeta(alpha,i). = ddrdzeta(alpha,beta,i). Evaluated at current time.
~SinusoidalWall()
Destructor: Clean up if necessary.
void doc_solution(DocInfo &doc_info)
Doc the solution.
double height(const double &x)
Height of domain.
SinusoidalWall(const double &height, const double &amplitude, const double &zeta_min, const double &zeta_max)
Constructor: Pass height, amplitude, zeta min and zeta max (all are pinned by default) ...
bool Must_clean_up
Do I need to clean up?
void actions_after_newton_solve()
Update the after solve (empty)
double & height()
Access function for horizontal half axis.
Data * geom_data_pt(const unsigned &j)
Return pointer to the j-th Data item that the object&#39;s shape depends on.
SinusoidalWall(const Vector< Data *> &geom_data_pt)
Constructor: One item of geometric data, containing four values:
ChannelSpineFlowProblem()
Constructor.
virtual void d2position(const Vector< double > &zeta, RankThreeTensor< double > &ddrdzeta) const
2nd derivative of position Vector w.r.t. to coordinates: = ddrdzeta(alpha,beta,i). Evaluated at current time.
~ChannelSpineFlowProblem()
Destructor: (empty)
Vector< Data * > Geom_data_pt
Vector of pointers to Data items that affects the object&#39;s shape.
double Re
Reynolds number.
unsigned ngeom_data() const
How many items of Data does the shape of the object depend on?
void position(const unsigned &t, const Vector< double > &zeta, Vector< double > &r) const
Parametrised position on object: r(zeta). Evaluated at previous timestep. t=0: current time; t>0: pre...
void doc_sparse_node_update()
Create the files to illustrate the sparse node update operation.
void position(const Vector< double > &zeta, Vector< double > &r) const
Position vector at Lagrangian coordinate zeta.
Namespace for physical parameters.
double H
Undeformed height of domain.
int main()
Driver for channel flow problem with spine mesh.
void actions_before_newton_solve()
Update the problem specs before solve. Set velocity boundary conditions just to be on the safe side...
double & amplitude()
Access function for vertical half axis.
double A
Amplitude of indentation.
virtual void dposition(const Vector< double > &zeta, DenseMatrix< double > &drdzeta) const
Derivative of position Vector w.r.t. to coordinates: = drdzeta(alpha,i). Evaluated at current time...