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
src
meshes
fsi_driven_cavity_mesh.template.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
#ifndef OOMPH_FSI_DRIVEN_CAVITY_MESH_TEMPLATE_HEADER
27
#define OOMPH_FSI_DRIVEN_CAVITY_MESH_TEMPLATE_HEADER
28
29
#ifndef OOMPH_FSI_DRIVEN_CAVITY_MESH_HEADER
30
#error __FILE__ should only be included from fsi_driven_cavity_mesh.h.
31
#endif
// OOMPH_FSI_DRIVEN_CAVITY_MESH_HEADER
32
33
// Include the headers file for collapsible channel
34
35
namespace
oomph
36
{
37
//========================================================================
38
/// Constructor: Pass number of elements, lengths, pointer to GeomObject
39
/// that defines the collapsible segment and pointer to TimeStepper
40
/// (defaults to the default timestepper, Steady).
41
//========================================================================
42
template
<
class
ELEMENT>
43
FSIDrivenCavityMesh<ELEMENT>::FSIDrivenCavityMesh
(
44
const
unsigned
& nx,
45
const
unsigned
& ny,
46
const
double
&
lx
,
47
const
double
&
ly
,
48
const
double
&
gap_fraction
,
49
GeomObject
* wall_pt,
50
TimeStepper
*
time_stepper_pt
)
51
:
SimpleRectangularQuadMesh
<
ELEMENT
>(nx, ny,
lx
,
ly
,
time_stepper_pt
),
52
Nx(nx),
53
Ny(ny),
54
Gap_fraction(
gap_fraction
),
55
Wall_pt(wall_pt)
56
{
57
// Mesh can only be built with 2D Qelements.
58
MeshChecker::assert_geometric_element<QElementGeometricBase, ELEMENT>(2);
59
60
// Update the boundary numbering scheme and set boundary coordinate
61
//-----------------------------------------------------------------
62
63
// (Note: The original SimpleRectangularQuadMesh had four boundaries.
64
// We need to overwrite the boundary lookup scheme for the current
65
// mesh so that the collapsible segment becomes identifiable).
66
// While we're doing this, we're also setting up a boundary
67
// coordinate for the nodes located on the collapsible segment.
68
// The boundary coordinate can be used to setup FSI.
69
70
// How many boundaries does the mesh have now?
71
unsigned
nbound
= this->
nboundary
();
72
for
(
unsigned
b
= 0;
b
<
nbound
;
b
++)
73
{
74
// Remove all nodes on this boundary from the mesh's lookup scheme
75
// and also delete the reverse lookup scheme held by the nodes
76
this->
remove_boundary_nodes
(
b
);
77
}
78
79
#ifdef PARANOID
80
// Sanity check
81
unsigned
nnod
= this->
nnode
();
82
for
(
unsigned
j
= 0;
j
<
nnod
;
j
++)
83
{
84
if
(this->
node_pt
(
j
)->
is_on_boundary
())
85
{
86
std::ostringstream
error_message
;
87
error_message
<<
"Node "
<<
j
<<
"is still on boundary "
<< std::endl;
88
89
throw
OomphLibError
(
error_message
.str(),
90
OOMPH_CURRENT_FUNCTION
,
91
OOMPH_EXCEPTION_LOCATION
);
92
}
93
}
94
#endif
95
96
// Change the numbers of boundaries
97
this->
set_nboundary
(6);
98
99
// Get the number of nodes along the element edge from first element
100
unsigned
nnode_1d
= this->
finite_element_pt
(0)->nnode_1d();
101
102
// Vector of Lagrangian coordinates used as boundary coordinate
103
Vector<double>
zeta
(1);
104
105
// Zeta increment over elements (used for assignment of
106
// boundary coordinate)
107
double
dzeta
=
lx
/
double
(
nx
);
108
109
// Manually loop over the elements near the boundaries and
110
// assign nodes to boundaries. Also set up boundary coordinate
111
unsigned
nelem
= this->
nelement
();
112
for
(
unsigned
e
= 0;
e
<
nelem
;
e
++)
113
{
114
// Bottom row of elements
115
if
(
e
<
nx
)
116
{
117
for
(
unsigned
i
= 0;
i
<
nnode_1d
;
i
++)
118
{
119
this->
add_boundary_node
(0, this->
finite_element_pt
(
e
)->
node_pt
(
i
));
120
}
121
}
122
// Collapsible bit
123
if
(
e
> ((
ny
- 1) *
nx
) - 1)
124
{
125
for
(
unsigned
i
= 0;
i
<
nnode_1d
;
i
++)
126
{
127
this->
add_boundary_node
(
128
3, this->
finite_element_pt
(
e
)->
node_pt
(2 *
nnode_1d
+
i
));
129
130
// What column of elements are we in?
131
unsigned
ix
=
e
- (
ny
- 1) *
nx
;
132
133
// Zeta coordinate
134
zeta
[0] =
135
double
(
ix
) *
dzeta
+
double
(
i
) *
dzeta
/
double
(
nnode_1d
- 1);
136
137
// Set boundary coordinate
138
this->
finite_element_pt
(
e
)
139
->node_pt(2 *
nnode_1d
+
i
)
140
->set_coordinates_on_boundary(3,
zeta
);
141
}
142
}
143
// Left end
144
if
(
e
% (
nx
) == 0)
145
{
146
for
(
unsigned
i
= 0;
i
<
nnode_1d
;
i
++)
147
{
148
Node
*
nod_pt
= this->
finite_element_pt
(
e
)->node_pt(
i
*
nnode_1d
);
149
150
// Rigid bit?
151
if
(
nod_pt
->x(1) >=
ly
*
Gap_fraction
)
152
{
153
this->
add_boundary_node
(4,
nod_pt
);
154
}
155
// Free bit
156
else
157
{
158
this->
add_boundary_node
(5,
nod_pt
);
159
}
160
}
161
}
162
// Right end
163
if
(
e
%
nx
==
nx
- 1)
164
{
165
for
(
unsigned
i
= 0;
i
<
nnode_1d
;
i
++)
166
{
167
Node
*
nod_pt
=
168
this->
finite_element_pt
(
e
)->node_pt((
i
+ 1) *
nnode_1d
- 1);
169
170
// Rigid bit?
171
if
(
nod_pt
->x(1) >=
ly
*
Gap_fraction
)
172
{
173
this->
add_boundary_node
(2,
nod_pt
);
174
}
175
// Free bit
176
else
177
{
178
this->
add_boundary_node
(1,
nod_pt
);
179
}
180
}
181
}
182
}
183
184
// Re-setup lookup scheme that establishes which elements are located
185
// on the mesh boundaries (doesn't need to be wiped)
186
this->setup_boundary_element_info();
187
188
// We have only bothered to parametrise boundary 3
189
this->
set_boundary_coordinate_exists
(3);
190
}
191
192
193
///////////////////////////////////////////////////////////////////////////
194
///////////////////////////////////////////////////////////////////////////
195
///////////////////////////////////////////////////////////////////////////
196
197
//=================================================================
198
/// Perform algebraic mesh update at time level t (t=0: present;
199
/// t>0: previous)
200
//=================================================================
201
template
<
class
ELEMENT>
202
void
AlgebraicFSIDrivenCavityMesh<ELEMENT>::algebraic_node_update
(
203
const
unsigned
&
t
,
AlgebraicNode
*&
node_pt
)
204
{
205
#ifdef PARANOID
206
// We're updating the nodal positions (!) at time level t
207
// and determine them by evaluating the wall GeomObject's
208
// position at that gime level. I believe this only makes sense
209
// if the t-th history value in the positional timestepper
210
// actually represents previous values (rather than some
211
// generalised quantity). Hence if this function is called with
212
// t>nprev_values(), we issue a warning and terminate the execution.
213
// It *might* be possible that the code still works correctly
214
// even if this condition is violated (e.g. if the GeomObject's
215
// position() function returns the appropriate "generalised"
216
// position value that is required by the timestepping scheme but it's
217
// probably worth flagging this up and forcing the user to manually switch
218
// off this warning if he/she is 100% sure that this is kosher.
219
if
(
t
>
node_pt
->position_time_stepper_pt()->nprev_values())
220
{
221
std::string
error_message
=
222
"Trying to update the nodal position at a time level"
;
223
error_message
+=
"beyond the number of previous values in the nodes'"
;
224
error_message
+=
"position timestepper. This seems highly suspect!"
;
225
error_message
+=
"If you're sure the code behaves correctly"
;
226
error_message
+=
"in your application, remove this warning "
;
227
error_message
+=
"or recompile with PARNOID switched off."
;
228
229
std::string
function_name
=
"AlgebraicFSIDrivenCavityMesh::"
;
230
function_name
+=
"algebraic_node_update()"
;
231
232
throw
OomphLibError
(
233
error_message
,
OOMPH_CURRENT_FUNCTION
,
OOMPH_EXCEPTION_LOCATION
);
234
}
235
#endif
236
237
// Extract references for update by copy construction
238
Vector<double>
ref_value
(
node_pt
->vector_ref_value());
239
240
// First reference value: Original x-position. Used as the start point
241
// for the lines connecting the nodes in the vertical direction
242
double
x_bottom
=
ref_value
[0];
243
244
// Second reference value: Fractional position along
245
// straight line from the bottom (at the original x position)
246
// to the reference point on the upper wall
247
double
fract
=
ref_value
[1];
248
249
// Third reference value: Reference local coordinate
250
// in GeomObject that represents the upper wall (local coordinate
251
// in finite element if the wall GeomObject is a finite element mesh)
252
Vector<double>
s
(1);
253
s
[0] =
ref_value
[2];
254
255
// Fourth reference value: zeta coordinate on the upper wall
256
// If the wall is a simple GeomObject, zeta[0]=s[0]
257
// but if it's a compound GeomObject (e.g. a finite element mesh)
258
// zeta scales during mesh refinement, whereas s[0] and the
259
// pointer to the geom object have to be re-computed.
260
// double zeta=ref_value[3]; // not needed here
261
262
// Extract geometric objects for update by copy construction
263
Vector<GeomObject*>
geom_object_pt
(
node_pt
->vector_geom_object_pt());
264
265
// Pointer to actual wall geom object (either the same as the wall object
266
// or the pointer to the actual finite element)
267
GeomObject
*
geom_obj_pt
=
geom_object_pt
[0];
268
269
// Get position vector to wall at previous timestep t!
270
Vector<double>
r_wall
(2);
271
geom_obj_pt
->position(
t
,
s
,
r_wall
);
272
273
// Assign new nodal coordinate
274
node_pt
->x(
t
, 0) =
x_bottom
+
fract
* (
r_wall
[0] -
x_bottom
);
275
node_pt
->x(
t
, 1) =
fract
*
r_wall
[1];
276
}
277
278
//=====start_setup=================================================
279
/// Setup algebraic mesh update -- assumes that mesh has
280
/// initially been set up with a flush upper wall.
281
//=================================================================
282
template
<
class
ELEMENT>
283
void
AlgebraicFSIDrivenCavityMesh<ELEMENT>::setup_algebraic_node_update
()
284
{
285
// Loop over all nodes in mesh
286
unsigned
nnod
= this->
nnode
();
287
for
(
unsigned
j
= 0;
j
<
nnod
;
j
++)
288
{
289
// Get pointer to node -- recall that that Mesh::node_pt(...) has been
290
// overloaded in the AlgebraicMesh class to return a pointer to
291
// an AlgebraicNode.
292
AlgebraicNode
*
nod_pt
=
node_pt
(
j
);
293
294
// Get coordinates
295
double
x =
nod_pt
->x(0);
296
double
y =
nod_pt
->x(1);
297
298
// Get zeta coordinate on the undeformed wall
299
Vector<double>
zeta
(1);
300
zeta
[0] = x;
301
302
// Get pointer to geometric (sub-)object and Lagrangian coordinate
303
// on that sub-object. For a wall that is represented by
304
// a single geom object, this simply returns the input.
305
// If the geom object consists of sub-objects (e.g.
306
// if it is a finite element mesh representing a wall,
307
// then we'll obtain the pointer to the finite element
308
// (in its incarnation as a GeomObject) and the
309
// local coordinate in that element.
310
GeomObject
*
geom_obj_pt
;
311
Vector<double>
s
(1);
312
this->Wall_pt->locate_zeta(
zeta
,
geom_obj_pt
,
s
);
313
314
// Get position vector to wall:
315
Vector<double>
r_wall
(2);
316
geom_obj_pt
->position(
s
,
r_wall
);
317
318
// Sanity check: Confirm that the wall is in its undeformed position
319
#ifdef PARANOID
320
if
((std::fabs(
r_wall
[0] - x) > 1.0e-15) &&
321
(std::fabs(
r_wall
[1] - y) > 1.0e-15))
322
{
323
std::ostringstream
error_stream
;
324
error_stream
<<
"Wall must be in its undeformed position when\n"
325
<<
"algebraic node update information is set up!\n "
326
<<
"x-discrepancy: "
<< std::fabs(
r_wall
[0] - x)
327
<< std::endl
328
<<
"y-discrepancy: "
<< std::fabs(
r_wall
[1] - y)
329
<< std::endl;
330
331
throw
OomphLibError
(
332
error_stream
.str(),
OOMPH_CURRENT_FUNCTION
,
OOMPH_EXCEPTION_LOCATION
);
333
}
334
#endif
335
336
// One geometric object is involved in update operation
337
Vector<GeomObject*>
geom_object_pt
(1);
338
339
// The actual geometric object (If the wall is simple GeomObject
340
// this is the same as Wall_pt; if it's a compound GeomObject
341
// this points to the sub-object)
342
geom_object_pt
[0] =
geom_obj_pt
;
343
344
// The update function requires four parameters:
345
Vector<double>
ref_value
(4);
346
347
// First reference value: Original x-position
348
ref_value
[0] =
r_wall
[0];
349
350
// Second reference value: fractional position along
351
// straight line from the bottom (at the original x position)
352
// to the point on the wall)
353
ref_value
[1] = y /
r_wall
[1];
354
355
// Third reference value: Reference local coordinate
356
// in wall element (local coordinate in FE if we're dealing
357
// with a wall mesh)
358
ref_value
[2] =
s
[0];
359
360
// Fourth reference value: zeta coordinate on wall
361
// If the wall is a simple GeomObject, zeta[0]=s[0]
362
// but if it's a compound GeomObject (e.g. a finite element mesh)
363
// zeta scales during mesh refinement, whereas s[0] and the
364
// pointer to the geom object have to be re-computed.
365
ref_value
[3] =
zeta
[0];
366
367
// Setup algebraic update for node: Pass update information
368
nod_pt
->add_node_update_info(
this
,
// mesh
369
geom_object_pt
,
// vector of geom objects
370
ref_value
);
// vector of ref. values
371
}
372
373
}
// end of setup_algebraic_node_update
374
375
376
////////////////////////////////////////////////////////////////////
377
////////////////////////////////////////////////////////////////////
378
////////////////////////////////////////////////////////////////////
379
380
//========start_update_node_update=================================
381
/// Update the geometric references that are used
382
/// to update node after mesh adaptation.
383
//=================================================================
384
template
<
class
ELEMENT>
385
void
RefineableAlgebraicFSIDrivenCavityMesh<ELEMENT>::update_node_update
(
386
AlgebraicNode
*&
node_pt
)
387
{
388
// Extract reference values for node update by copy construction
389
Vector<double>
ref_value
(
node_pt
->vector_ref_value());
390
391
// First reference value: Original x-position
392
// double x_bottom=ref_value[0]; // not needed here
393
394
// Second reference value: fractional position along
395
// straight line from the bottom (at the original x position)
396
// to the point on the wall)
397
// double fract=ref_value[1]; // not needed here
398
399
// Third reference value: Reference local coordinate
400
// in GeomObject (local coordinate in finite element if the wall
401
// GeomObject is a finite element mesh)
402
// Vector<double> s(1);
403
// s[0]=ref_value[2]; // This needs to be re-computed!
404
405
// Fourth reference value: intrinsic coordinate on the (possibly
406
// compound) wall.
407
double
zeta
=
ref_value
[3];
408
409
// Extract geometric objects for update by copy construction
410
Vector<GeomObject*>
geom_object_pt
(
node_pt
->vector_geom_object_pt());
411
412
// Pointer to actual wall geom object (either the same as wall object
413
// or the pointer to the actual finite element)
414
// GeomObject* geom_obj_pt=geom_object_pt[0]; // This needs to be
415
// re-computed!
416
417
// Get zeta coordinate on wall (as vector)
418
Vector<double>
zeta_wall
(1);
419
zeta_wall
[0] =
zeta
;
420
421
// Get pointer to geometric (sub-)object and Lagrangian coordinate
422
// on that sub-object. For a wall that is represented by
423
// a single geom object, this simply returns the input.
424
// If the geom object consists of sub-objects (e.g.
425
// if it is a finite element mesh representing a wall,
426
// then we'll obtain the pointer to the finite element
427
// (in its incarnation as a GeomObject) and the
428
// local coordinate in that element.
429
Vector<double>
s
(1);
430
GeomObject
*
geom_obj_pt
;
431
this->Wall_pt->locate_zeta(
zeta_wall
,
geom_obj_pt
,
s
);
432
433
// Update the pointer to the (sub-)GeomObject within which the
434
// reference point is located. (If the wall is simple GeomObject
435
// this is the same as Wall_pt; if it's a compound GeomObject
436
// this points to the sub-object)
437
geom_object_pt
[0] =
geom_obj_pt
;
438
439
// First reference value: Original x-position
440
// ref_value[0]=r_wall[0]; // unchanged
441
442
// Second reference value: fractional position along
443
// straight line from the bottom (at the original x position)
444
// to the point on the wall)
445
// ref_value[1]=y/r_wall[1]; // unchanged
446
447
// Update third reference value: Reference local coordinate
448
// in wall element (local coordinate in FE if we're dealing
449
// with a wall mesh)
450
ref_value
[2] =
s
[0];
451
452
// Fourth reference value: zeta coordinate on wall
453
// If the wall is a simple GeomObject, zeta[0]=s[0]
454
// but if it's a compound GeomObject (e.g. a finite element mesh)
455
// zeta scales during mesh refinement, whereas s[0] and the
456
// pointer to the geom object have to be re-computed.
457
// ref_value[3]=zeta[0]; //unchanged
458
459
// Kill the existing node update info
460
node_pt
->kill_node_update_info();
461
462
// Setup algebraic update for node: Pass update information
463
node_pt
->add_node_update_info(
this
,
// mesh
464
geom_object_pt
,
// vector of geom objects
465
ref_value
);
// vector of ref. values
466
}
467
468
}
// namespace oomph
469
#endif
oomph::AlgebraicFSIDrivenCavityMesh::algebraic_node_update
void algebraic_node_update(const unsigned &t, AlgebraicNode *&node_pt)
Update nodal position at time level t (t=0: present; t>0: previous)
Definition
fsi_driven_cavity_mesh.template.cc:202
oomph::AlgebraicFSIDrivenCavityMesh::setup_algebraic_node_update
void setup_algebraic_node_update()
Function to setup the algebraic node update.
Definition
fsi_driven_cavity_mesh.template.cc:283
oomph::FSIDrivenCavityMesh::Gap_fraction
double Gap_fraction
Fraction of the gap next to moving lid, relative to the height of the domain.
Definition
fsi_driven_cavity_mesh.h:88
oomph::FSIDrivenCavityMesh::FSIDrivenCavityMesh
FSIDrivenCavityMesh(const unsigned &nx, const unsigned &ny, const double &lx, const double &ly, const double &gap_fraction, GeomObject *wall_pt, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass number of elements, number of elements, fractional height of the gap above the movi...
Definition
fsi_driven_cavity_mesh.template.cc:43
oomph::GeompackQuadMesh
Quadrilateral mesh generator; Uses input from Geompack++. See: http://members.shaw....
Definition
geompack_mesh.h:42
oomph::RefineableAlgebraicFSIDrivenCavityMesh::update_node_update
void update_node_update(AlgebraicNode *&node_pt)
Update the node update data for specified node following any mesh adapation.
Definition
fsi_driven_cavity_mesh.template.cc:385
oomph::SimpleRectangularQuadMesh
Simple rectangular 2D Quad mesh class. Nx : number of elements in the x direction.
Definition
simple_rectangular_quadmesh.h:58
oomph::SimpleRectangularQuadMesh::ny
const unsigned & ny() const
Access function for number of elements in y directions.
Definition
simple_rectangular_quadmesh.h:77
oomph::SimpleRectangularQuadMesh::nx
const unsigned & nx() const
Access function for number of elements in x directions.
Definition
simple_rectangular_quadmesh.h:71
oomph
Definition
annular_domain.h:35