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
About
People
Contact/Get involved
Publications
Acknowledgements
Copyright
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-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
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
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
//=====================================================================
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
Simple spine mesh class derived from standard 2D mesh. Vertical lines of nodes are located on spines.
Definition
simple_spine_channel.cc:91
SpikedChannelSpineFlowProblem
Channel flow, through a non-uniform channel, using Spines.
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