Toggle navigation
Documentation
Big picture
The finite element method
The data structure
Not-so-quick guide
Optimisation
Order of action functions
Example codes and tutorials
List of example codes and tutorials
Meshing
Solvers
MPI parallel processing
Post-processing/visualisation
Other
Change log
Creating documentation
Coding conventions
Index
FAQ
Installation
Installation guide
Copyright
About
People
Contact/Get involved
Publications
Acknowledgements
Picture show
Go
demo_drivers
navier_stokes
spine_channel
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
38
using namespace
std;
39
40
using namespace
oomph;
41
42
//==start_of_namespace===================================================
43
/// Namespace for physical parameters
44
//=======================================================================
45
namespace
Global_Physical_Variables2
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
342
~SpikedChannelSpineFlowProblem
(){}
343
344
/// Update the after solve (empty)
345
void
actions_after_newton_solve
() {}
346
347
/// Update the problem specs before solve.
348
/// set velocity boundary conditions just to be on the safe side...
349
void
actions_before_newton_solve
()
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
399
SpikedChannelSpineFlowProblem
();
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>
413
SpikedChannelSpineFlowProblem<ELEMENT>::SpikedChannelSpineFlowProblem
()
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>
499
void
SpikedChannelSpineFlowProblem<ELEMENT>::doc_solution
(
DocInfo
&
doc_info
)
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
532
SpikedChannelSpineFlowProblem<SpineElement<QTaylorHoodElement<2>
> >
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
550
SpikedChannelSpineFlowProblem<SpineElement<QCrouzeixRaviartElement<2>
> >
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
SimpleSpineMesh
////////////////////////////////////////////////////////////////////
Definition
simple_spine_channel.cc:91
SpikedChannelSpineFlowProblem
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
Definition
spine_channel2.cc:333
SpikedChannelSpineFlowProblem::SpikedChannelSpineFlowProblem
SpikedChannelSpineFlowProblem()
Constructor.
Definition
spine_channel2.cc:413
SpikedChannelSpineFlowProblem::~SpikedChannelSpineFlowProblem
~SpikedChannelSpineFlowProblem()
Destructor: Empty.
Definition
spine_channel2.cc:342
SpikedChannelSpineFlowProblem::actions_after_newton_solve
void actions_after_newton_solve()
Update the after solve (empty)
Definition
spine_channel2.cc:345
SpikedChannelSpineFlowProblem::actions_before_newton_solve
void actions_before_newton_solve()
Update the problem specs before solve. set velocity boundary conditions just to be on the safe side....
Definition
spine_channel2.cc:349
SpikedChannelSpineFlowProblem::doc_solution
void doc_solution(DocInfo &doc_info)
Doc the solution.
Definition
spine_channel2.cc:499
SpikedChannelSpineFlowProblem::Ly
double Ly
Width of channel.
Definition
spine_channel2.cc:337
SpikedChannelSpineFlowProblem::mesh_pt
ChannelSpineMesh< ELEMENT > * mesh_pt()
Upcasted access function for the mesh.
Definition
spine_channel2.cc:393
Global_Physical_Variables2
Namespace for physical parameters.
Definition
spine_channel2.cc:46
Global_Physical_Variables2::Re
double Re
Reynolds number.
Definition
spine_channel2.cc:48
main
int main()
Driver for SpikedChannelSpineFlow test problem.
Definition
spine_channel2.cc:521