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-2023 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 
38 using namespace std;
39 
40 using 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 //=========================================================================
76 class SpikedLine : public GeomObject
77 {
78 
79 public:
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;
94  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(),
103  OOMPH_CURRENT_FUNCTION,
104  OOMPH_EXCEPTION_LOCATION);
105  }
106 #endif
107  Geom_data_pt.resize(1);
108  Geom_data_pt[0]=geom_data_pt[0];
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
142  ~SpikedLine()
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",
160  OOMPH_CURRENT_FUNCTION,
161  OOMPH_EXCEPTION_LOCATION);
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
191  if (t>Geom_data_pt[0]->time_stepper_pt()->nprev_values())
192  {
193  std::ostringstream error_stream;
194  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(),
199  OOMPH_CURRENT_FUNCTION,
200  OOMPH_EXCEPTION_LOCATION);
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,
228  DenseMatrix<double> &drdzeta) const
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,
253  RankThreeTensor<double> &ddrdzeta) const
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,
267  DenseMatrix<double> &drdzeta,
268  RankThreeTensor<double> &ddrdzeta) const
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 
312 private:
313 
314  /// Vector of pointers to Data items that affects the object's shape
315  Vector<Data*> Geom_data_pt;
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 //====================================================================
331 template<class ELEMENT>
332 class SpikedChannelSpineFlowProblem : public Problem
333 {
334 private:
335 
336  /// Width of channel
337  double Ly;
338 
339 public:
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
393  ChannelSpineMesh<ELEMENT>* mesh_pt()
394  {
395  return dynamic_cast<ChannelSpineMesh<ELEMENT>*>(Problem::mesh_pt());
396  }
397 
398  /// Constructor
400 
401  /// Doc the solution
402  void doc_solution(DocInfo& doc_info);
403 
404 }; // end_of_problem_class
405 
406 
407 
408 //==start_of_constructor==================================================
409 /// Constructor for SpikedChannelSpineFlow problem
410 ///
411 //========================================================================
412 template<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 =
442  new SpikedLine(Ly,amplitude_upper,zeta_min,zeta_max);
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
485  el_pt->re_pt() = &Global_Physical_Variables2::Re;
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 //========================================================================
498 template<class ELEMENT>
500 {
501 
502  ofstream some_file;
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 //=====================================================================
521 int main()
522 {
523  // Set output directory
524  DocInfo doc_info;
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 
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
ChannelSpineMesh< ELEMENT > * mesh_pt()
Upcasted access function for the mesh.
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.
Namespace for physical parameters.
double Re
Reynolds number.
double A
Amplitude of indentation.
double height(const double &x)
Height of domain.
double H
Undeformed height of domain.
int main()
Driver for SpikedChannelSpineFlow test problem.