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
fish_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_FISH_MESH_TEMPLATE_HEADER
27
#define OOMPH_FISH_MESH_TEMPLATE_HEADER
28
29
#ifndef OOMPH_FISH_MESH_HEADER
30
#error __FILE__ should only be included from fish_mesh.h.
31
#endif
// OOMPH_FISH_MESH_HEADER
32
33
namespace
oomph
34
{
35
//=================================================================
36
/// Constructor: Pass pointer to timestepper.
37
/// (defaults to (Steady) default timestepper defined in Mesh)
38
//=================================================================
39
template
<
class
ELEMENT>
40
FishMesh<ELEMENT>::FishMesh
(
TimeStepper
*
time_stepper_pt
)
41
{
42
// Mesh can only be built with 2D Qelements.
43
MeshChecker::assert_geometric_element<QElementGeometricBase, ELEMENT>(2);
44
45
// Fish back is a circle of radius 1, centred at (0.5,0.0)
46
double
x_c
= 0.5;
47
double
y_c
= 0.0;
48
double
r_back
= 1.0;
49
Back_pt =
new
Circle
(
x_c
,
y_c
,
r_back
);
50
51
// I've created the fishback -- I need to kill it.
52
Must_kill_fish_back =
true
;
53
54
// Now build the mesh
55
build_mesh(
time_stepper_pt
);
56
}
57
58
//=================================================================
59
/// Constructor: Pass pointer GeomObject that defines
60
/// the fish's back and pointer to timestepper.
61
/// (defaults to (Steady) default timestepper defined in Mesh)
62
//=================================================================
63
template
<
class
ELEMENT>
64
FishMesh<ELEMENT>::FishMesh
(GeomObject*
back_pt
,
TimeStepper
*
time_stepper_pt
)
65
: Back_pt(
back_pt
)
66
{
67
// Mesh can only be built with 2D Qelements.
68
MeshChecker::assert_geometric_element<QElementGeometricBase, ELEMENT>(2);
69
70
// Back_pt has already been set....
71
Must_kill_fish_back
=
false
;
72
73
// Now build the mesh
74
build_mesh
(
time_stepper_pt
);
75
}
76
77
//============================start_build_mesh=====================
78
/// Build the mesh, using the geometric object that
79
/// defines the fish's back.
80
//=================================================================
81
template
<
class
ELEMENT>
82
void
FishMesh<ELEMENT>::build_mesh
(
TimeStepper
*
time_stepper_pt
)
83
{
84
// Mesh can only be built with 2D Qelements.
85
MeshChecker::assert_geometric_element<QElementGeometricBase, ELEMENT>(2);
86
87
// Build domain: Pass the pointer to the geometric object that
88
// represents the fish's back (pointed to by the FishMesh's
89
// private data member, Back_pt, and the values of the
90
// Lagrangian coordinate along this object at the "tail"
91
// and the "nose":
92
double
xi_lo
= 2.6;
93
double
xi_hi
= 0.4;
94
Domain_pt
=
new
FishDomain
(Back_pt,
xi_lo
,
xi_hi
);
95
96
// Plot the domain? Keep this here in case we need to doc it
97
bool
plot_it
=
false
;
98
if
(
plot_it
)
99
{
100
// Output the domain
101
std::ofstream
some_file
;
102
103
// Number of plot points in each coordinate direction.
104
unsigned
npts
= 10;
105
106
// Output domain
107
some_file
.open(
"fish_domain.dat"
);
108
Domain_pt
->output(
some_file
,
npts
);
109
Domain_pt
->output_macro_element_boundaries(
some_file
,
npts
);
110
some_file
.close();
111
}
112
113
// Set the number of boundaries
114
set_nboundary
(7);
115
116
// We will store boundary coordinates on the curvilinear boundaries
117
//(boundaries 0 and 4) along the fish's belly and its back.
118
set_boundary_coordinate_exists
(0);
119
set_boundary_coordinate_exists
(4);
120
121
// Allocate the storage for the elements
122
unsigned
nelem
= 4;
123
Element_pt
.resize(
nelem
);
124
125
// Create dummy element so we can determine the number of nodes
126
ELEMENT
*
dummy_el_pt
=
new
ELEMENT
;
127
128
// Read out the number of linear points in the element
129
unsigned
n_node_1d
=
dummy_el_pt
->nnode_1d();
130
131
// Kill the element
132
delete
dummy_el_pt
;
133
134
// Can now allocate the store for the nodes
135
unsigned
nnodes_total
=
136
(2 * (
n_node_1d
- 1) + 1) * (2 * (
n_node_1d
- 1) + 1);
137
Node_pt
.resize(
nnodes_total
);
138
139
Vector<double>
s
(2),
s_fraction
(2);
140
Vector<double>
r
(2);
141
142
// Create elements and all nodes in element
143
//-----------------------------------------
144
// (ignore repetitions for now -- we'll clean them up later)
145
//----------------------------------------------------------
146
for
(
unsigned
e
= 0;
e
<
nelem
;
e
++)
147
{
148
// Create element
149
Element_pt
[
e
] =
new
ELEMENT
;
150
151
// Loop over rows in y/s_1-direction
152
for
(
unsigned
i1
= 0;
i1
<
n_node_1d
;
i1
++)
153
{
154
// Loop over rows in x/s_0-direction
155
for
(
unsigned
i0
= 0;
i0
<
n_node_1d
;
i0
++)
156
{
157
// Local node number
158
unsigned
j_local
=
i0
+
i1
*
n_node_1d
;
159
160
// Create the node and store pointer to it
161
Node
*
node_pt
=
162
finite_element_pt
(
e
)->construct_node(
j_local
,
time_stepper_pt
);
163
164
// Work out the node's coordinates in the finite element's local
165
// coordinate system:
166
finite_element_pt
(
e
)->local_fraction_of_node(
j_local
,
s_fraction
);
167
168
s
[0] = -1.0 + 2.0 *
s_fraction
[0];
169
s
[1] = -1.0 + 2.0 *
s_fraction
[1];
170
171
// Get the global position of the node from macro element mapping
172
Domain_pt
->macro_element_pt(
e
)->macro_map(
s
,
r
);
173
174
// Set the nodal position
175
node_pt
->x(0) =
r
[0];
176
node_pt
->x(1) =
r
[1];
177
}
178
}
179
}
// end of loop over elements
180
181
// Kill repeated nodes and replace by pointers to nodes in appropriate
182
//---------------------------------------------------------------------
183
// neighbour. Also add node pointers to boundary arrays.
184
//------------------------------------------------------
185
186
// Initialise number of global nodes
187
unsigned
node_count
= 0;
188
189
// Check for error in node killing
190
bool
stopit
=
false
;
191
192
// Max tolerance for error in node killing
193
double
Max_tol_in_node_killing
= 1.0e-12;
194
195
// First element: Lower body: all nodes survive
196
//---------------------------------------------
197
unsigned
e
= 0;
198
199
// Loop over rows in y/s_1-direction
200
for
(
unsigned
i1
= 0;
i1
<
n_node_1d
;
i1
++)
201
{
202
// Loop over rows in x/s_0-direction
203
for
(
unsigned
i0
= 0;
i0
<
n_node_1d
;
i0
++)
204
{
205
// Local node number
206
unsigned
j_local
=
i0
+
i1
*
n_node_1d
;
207
208
// No duplicate node: Copy new node across to mesh
209
Node_pt
[
node_count
] =
finite_element_pt
(
e
)->node_pt(
j_local
);
210
211
// Set boundaries:
212
213
// If we're on the boundary need to convert the node
214
// into a boundary node
215
if
((
i0
== 0) || (
i1
== 0))
216
{
217
this->
convert_to_boundary_node
(
Node_pt
[
node_count
]);
218
}
219
220
// Lower jaw: boundary 6
221
if
(
i0
== 0)
222
{
223
add_boundary_node
(6,
Node_pt
[
node_count
]);
224
}
225
226
// Belly: boundary 0
227
if
(
i1
== 0)
228
{
229
add_boundary_node
(0,
Node_pt
[
node_count
]);
230
231
// Set the boundary coordinate
232
Vector<double>
zeta
(1);
233
zeta
[0] =
234
xi_lo
+ (
xi_hi
-
xi_lo
) *
double
(
i0
) /
double
(
n_node_1d
- 1);
235
Node_pt
[
node_count
]->set_coordinates_on_boundary(0,
zeta
);
236
}
237
238
// Increment node counter
239
node_count
++;
240
}
241
}
242
243
// Lower fin: Western row of nodes is duplicate from element 0
244
//------------------------------------------------------------
245
e
= 1;
246
247
// Loop over rows in y/s_1-direction
248
for
(
unsigned
i1
= 0;
i1
<
n_node_1d
;
i1
++)
249
{
250
// Loop over rows in x/s_0-direction
251
for
(
unsigned
i0
= 0;
i0
<
n_node_1d
;
i0
++)
252
{
253
// Local node number
254
unsigned
j_local
=
i0
+
i1
*
n_node_1d
;
255
256
// Has the node been killed?
257
bool
killed
=
false
;
258
259
// First vertical row of nodes in s_1 direction get killed
260
// and re-directed to nodes in element 0
261
if
(
i0
== 0)
262
{
263
// Neighbour element
264
unsigned
e_neigh
= 0;
265
266
// Node in neighbour element
267
unsigned
i0_neigh
=
n_node_1d
- 1;
268
unsigned
i1_neigh
=
i1
;
269
unsigned
j_local_neigh
=
i0_neigh
+
i1_neigh
*
n_node_1d
;
270
271
// Check:
272
for
(
unsigned
i
= 0;
i
< 2;
i
++)
273
{
274
double
error
= std::fabs(
275
finite_element_pt
(
e
)->
node_pt
(
j_local
)->x(
i
) -
276
finite_element_pt
(
e_neigh
)->
node_pt
(
j_local_neigh
)->x(
i
));
277
if
(
error
>
Max_tol_in_node_killing
)
278
{
279
oomph_info
<<
"Error in node killing for i "
<<
i
<<
" "
<<
error
280
<< std::endl;
281
stopit
=
true
;
282
}
283
}
284
285
// Kill node
286
delete
finite_element_pt
(
e
)->node_pt(
j_local
);
287
killed
=
true
;
288
289
// Set pointer to neighbour:
290
finite_element_pt
(
e
)->node_pt(
j_local
) =
291
finite_element_pt
(
e_neigh
)->node_pt(
j_local_neigh
);
292
}
293
294
// No duplicate node: Copy across to mesh
295
if
(!
killed
)
296
{
297
// Copy the node across
298
Node_pt
[
node_count
] =
finite_element_pt
(
e
)->node_pt(
j_local
);
299
300
// If we're on a boundary turn the node into
301
// a boundary node
302
if
((
i1
== 0) || (
i0
==
n_node_1d
- 1))
303
{
304
this->
convert_to_boundary_node
(
Node_pt
[
node_count
]);
305
}
306
307
// Increment node counter
308
node_count
++;
309
}
310
311
// Set boundaries:
312
313
// Bottom of tail: boundary 1
314
if
(
i1
== 0)
315
{
316
add_boundary_node
(1,
finite_element_pt
(
e
)->
node_pt
(
j_local
));
317
}
318
319
// Vertical bit of tail: boundary 2
320
if
(
i0
==
n_node_1d
- 1)
321
{
322
add_boundary_node
(2,
finite_element_pt
(
e
)->
node_pt
(
j_local
));
323
}
324
}
325
}
326
327
// Upper body: Southern row of nodes is duplicate from element 0
328
//--------------------------------------------------------------
329
e
= 2;
330
331
// Loop over rows in y/s_1-direction
332
for
(
unsigned
i1
= 0;
i1
<
n_node_1d
;
i1
++)
333
{
334
// Loop over rows in x/s_0-direction
335
for
(
unsigned
i0
= 0;
i0
<
n_node_1d
;
i0
++)
336
{
337
// Local node number
338
unsigned
j_local
=
i0
+
i1
*
n_node_1d
;
339
340
// Has the node been killed?
341
bool
killed
=
false
;
342
343
// First horizontal row of nodes in s_0 direction get killed
344
// and re-directed to nodes in element 0
345
if
(
i1
== 0)
346
{
347
// Neighbour element
348
unsigned
e_neigh
= 0;
349
350
// Node in neighbour element
351
unsigned
i0_neigh
=
i0
;
352
unsigned
i1_neigh
=
n_node_1d
- 1;
353
unsigned
j_local_neigh
=
i0_neigh
+
i1_neigh
*
n_node_1d
;
354
355
// Check:
356
for
(
unsigned
i
= 0;
i
< 2;
i
++)
357
{
358
double
error
= std::fabs(
359
finite_element_pt
(
e
)->
node_pt
(
j_local
)->x(
i
) -
360
finite_element_pt
(
e_neigh
)->
node_pt
(
j_local_neigh
)->x(
i
));
361
if
(
error
>
Max_tol_in_node_killing
)
362
{
363
oomph_info
<<
"Error in node killing for i "
<<
i
<<
" "
<<
error
364
<< std::endl;
365
stopit
=
true
;
366
}
367
}
368
369
// Kill node
370
delete
finite_element_pt
(
e
)->node_pt(
j_local
);
371
killed
=
true
;
372
373
// Set pointer to neighbour:
374
finite_element_pt
(
e
)->node_pt(
j_local
) =
375
finite_element_pt
(
e_neigh
)->node_pt(
j_local_neigh
);
376
}
377
378
// No duplicate node: Copy across to mesh
379
if
(!
killed
)
380
{
381
// Copy the old node across to the mesh
382
Node_pt
[
node_count
] =
finite_element_pt
(
e
)->node_pt(
j_local
);
383
384
// If we're on a boundary, convert the node into a boundary
385
// node. This will automatically update the entry in the mesh
386
if
((
i1
==
n_node_1d
- 1) || (
i0
== 0))
387
{
388
this->
convert_to_boundary_node
(
Node_pt
[
node_count
]);
389
}
390
391
// Increment node counter
392
node_count
++;
393
}
394
395
// Set boundaries:
396
397
// Back: boundary 4
398
if
(
i1
==
n_node_1d
- 1)
399
{
400
add_boundary_node
(4,
finite_element_pt
(
e
)->
node_pt
(
j_local
));
401
402
// Set the boundary coordinate
403
Vector<double>
zeta
(1);
404
zeta
[0] =
405
xi_lo
+ (
xi_hi
-
xi_lo
) *
double
(
i0
) /
double
(
n_node_1d
- 1);
406
finite_element_pt
(
e
)->node_pt(
j_local
)->set_coordinates_on_boundary(
407
4,
zeta
);
408
}
409
410
// Upper jaw: boundary 5
411
if
(
i0
== 0)
412
{
413
add_boundary_node
(5,
finite_element_pt
(
e
)->
node_pt
(
j_local
));
414
}
415
}
416
}
417
418
// Upper fin: Western/southern row of nodes is duplicate from element 2/1
419
//-----------------------------------------------------------------------
420
e
= 3;
421
422
// Loop over rows in y/s_1-direction
423
for
(
unsigned
i1
= 0;
i1
<
n_node_1d
;
i1
++)
424
{
425
// Loop over rows in x/s_0-direction
426
for
(
unsigned
i0
= 0;
i0
<
n_node_1d
;
i0
++)
427
{
428
// Local node number
429
unsigned
j_local
=
i0
+
i1
*
n_node_1d
;
430
431
// Has the node been killed?
432
bool
killed
=
false
;
433
434
// First vertical row of nodes in s_1 direction get killed
435
// and re-directed to nodes in element 2
436
if
(
i0
== 0)
437
{
438
// Neighbour element
439
unsigned
e_neigh
= 2;
440
441
// Node in neighbour element
442
unsigned
i0_neigh
=
n_node_1d
- 1;
443
unsigned
i1_neigh
=
i1
;
444
unsigned
j_local_neigh
=
i0_neigh
+
i1_neigh
*
n_node_1d
;
445
446
// Check:
447
for
(
unsigned
i
= 0;
i
< 2;
i
++)
448
{
449
double
error
= std::fabs(
450
finite_element_pt
(
e
)->
node_pt
(
j_local
)->x(
i
) -
451
finite_element_pt
(
e_neigh
)->
node_pt
(
j_local_neigh
)->x(
i
));
452
if
(
error
>
Max_tol_in_node_killing
)
453
{
454
oomph_info
<<
"Error in node killing for i "
<<
i
<<
" "
<<
error
455
<< std::endl;
456
stopit
=
true
;
457
}
458
}
459
460
// Kill node
461
delete
finite_element_pt
(
e
)->node_pt(
j_local
);
462
killed
=
true
;
463
464
// Set pointer to neighbour:
465
finite_element_pt
(
e
)->node_pt(
j_local
) =
466
finite_element_pt
(
e_neigh
)->node_pt(
j_local_neigh
);
467
}
468
469
// First horizontal row of nodes in s_0 direction (apart from
470
// first node get killed and re-directed to nodes in element 1
471
if
((
i0
!= 0) && (
i1
== 0))
472
{
473
// Neighbour element
474
unsigned
e_neigh
= 1;
475
476
// Node in neighbour element
477
unsigned
i0_neigh
=
i0
;
478
unsigned
i1_neigh
=
n_node_1d
- 1;
479
unsigned
j_local_neigh
=
i0_neigh
+
i1_neigh
*
n_node_1d
;
480
481
// Check:
482
for
(
unsigned
i
= 0;
i
< 2;
i
++)
483
{
484
double
error
= std::fabs(
485
finite_element_pt
(
e
)->
node_pt
(
j_local
)->x(
i
) -
486
finite_element_pt
(
e_neigh
)->
node_pt
(
j_local_neigh
)->x(
i
));
487
if
(
error
>
Max_tol_in_node_killing
)
488
{
489
oomph_info
<<
"Error in node killing for i "
<<
i
<<
" "
<<
error
490
<< std::endl;
491
stopit
=
true
;
492
}
493
}
494
495
// Kill node
496
delete
finite_element_pt
(
e
)->node_pt(
j_local
);
497
killed
=
true
;
498
499
// Set pointer to neighbour:
500
finite_element_pt
(
e
)->node_pt(
j_local
) =
501
finite_element_pt
(
e_neigh
)->node_pt(
j_local_neigh
);
502
}
503
504
// No duplicate node: Copy across to mesh
505
if
(!
killed
)
506
{
507
// Now copy the node across
508
Node_pt
[
node_count
] =
finite_element_pt
(
e
)->node_pt(
j_local
);
509
510
// If we're on the boundary, convert the node into
511
// a boundary node
512
if
((
i1
==
n_node_1d
- 1) || (
i0
==
n_node_1d
- 1))
513
{
514
this->
convert_to_boundary_node
(
Node_pt
[
node_count
]);
515
}
516
517
// Increment node counter
518
node_count
++;
519
}
520
521
// Set boundaries:
522
523
// To of tail: boundary 3
524
if
(
i1
==
n_node_1d
- 1)
525
{
526
add_boundary_node
(3,
finite_element_pt
(
e
)->
node_pt
(
j_local
));
527
}
528
529
// Vertical bit of tail: boundary 2
530
if
(
i0
==
n_node_1d
- 1)
531
{
532
add_boundary_node
(2,
finite_element_pt
(
e
)->
node_pt
(
j_local
));
533
}
534
}
535
}
536
537
// Terminate if there's been an error
538
if
(
stopit
)
539
{
540
std::ostringstream
error_message
;
541
error_message
<<
"Error occured in node killing!\n"
;
542
error_message
543
<<
"Max. permitted difference in position of the two nodes\n "
544
<<
"that get 'merged' : "
<<
Max_tol_in_node_killing
<< std::endl;
545
546
throw
OomphLibError
(
547
error_message
.str(),
OOMPH_CURRENT_FUNCTION
,
OOMPH_EXCEPTION_LOCATION
);
548
}
549
550
// Loop over all elements and set macro element pointer
551
unsigned
n_element
= this->
nelement
();
552
for
(
unsigned
e
= 0;
e
<
n_element
;
e
++)
553
{
554
// Get pointer to element
555
FiniteElement
*
el_pt
= this->
finite_element_pt
(
e
);
556
557
// Set pointer to macro element to enable MacroElement-based
558
// remesh. Also enables the curvlinear boundaries
559
// of the mesh/domain get picked up during adaptive
560
// mesh refinement in derived classes.
561
el_pt
->set_macro_elem_pt(this->
Domain_pt
->macro_element_pt(e));
562
}
563
564
// Setup boundary element lookup schemes
565
setup_boundary_element_info();
566
567
// Check the boundary coordinates
568
#ifdef PARANOID
569
{
570
Vector<double>
zeta
(1);
571
Vector<double>
r
(2);
572
bool
stopit
=
false
;
573
unsigned
num_bound
=
nboundary
();
574
for
(
unsigned
ibound
= 0;
ibound
<
num_bound
;
ibound
++)
575
{
576
if
(
boundary_coordinate_exists
(
ibound
))
577
{
578
unsigned
num_nod
=
nboundary_node
(
ibound
);
579
for
(
unsigned
inod
= 0;
inod
<
num_nod
;
inod
++)
580
{
581
// Get the boundary coordinate
582
boundary_node_pt
(
ibound
,
inod
)
583
->get_coordinates_on_boundary(
ibound
,
zeta
);
584
585
// Get position from wall object
586
Back_pt->position(
zeta
,
r
);
587
588
// Flip it
589
if
(
ibound
== 0)
r
[1] = -
r
[1];
590
591
// Check:
592
for
(
unsigned
i
= 0;
i
< 2;
i
++)
593
{
594
double
error
=
595
std::fabs(
r
[
i
] -
boundary_node_pt
(
ibound
,
inod
)->x(
i
));
596
if
(
error
>
Max_tol_in_node_killing
)
597
{
598
oomph_info
<<
"Error in boundary coordinate for direction "
<<
i
599
<<
" on boundary "
<<
ibound
<<
":"
<<
error
600
<< std::endl;
601
602
oomph_info
<<
"x: "
<<
r
[0] <<
" "
603
<<
boundary_node_pt
(
ibound
,
inod
)->x(0) << std::endl;
604
605
oomph_info
<<
"y: "
<<
r
[1] <<
" "
606
<<
boundary_node_pt
(
ibound
,
inod
)->x(1) << std::endl
607
<< std::endl;
608
stopit
=
true
;
609
}
610
}
611
}
612
}
613
}
614
615
// Terminate if there's been an error
616
if
(
stopit
)
617
{
618
std::ostringstream
error_message
;
619
error_message
<<
"Error occured in boundary coordinate setup!\n"
;
620
error_message
<<
"Max. tolerance: "
<<
Max_tol_in_node_killing
621
<< std::endl;
622
623
throw
OomphLibError
(
error_message
.str(),
624
OOMPH_CURRENT_FUNCTION
,
625
OOMPH_EXCEPTION_LOCATION
);
626
}
627
}
628
#endif
629
}
630
631
632
////////////////////////////////////////////////////////////////////////
633
////////////////////////////////////////////////////////////////////////
634
////////////////////////////////////////////////////////////////////////
635
636
//=========start_setup_adaptivity=========================================
637
/// Setup all the information that's required for spatial adaptivity:
638
/// Build quadtree forest.
639
//========================================================================
640
template
<
class
ELEMENT>
641
void
RefineableFishMesh<ELEMENT>::setup_adaptivity
()
642
{
643
// Setup quadtree forest
644
this->
setup_quadtree_forest
();
645
646
}
// end of setup_adaptivity
647
648
649
///////////////////////////////////////////////////////////////////////
650
///////////////////////////////////////////////////////////////////////
651
// AlgebraicElement fish-shaped mesh
652
///////////////////////////////////////////////////////////////////////
653
///////////////////////////////////////////////////////////////////////
654
655
//======================================================================
656
/// Setup algebraic update operation. Nodes are "suspended"
657
/// from the fish's back and the upper edge of the fin. Nodes
658
/// in the lower half are placed symmetrically.
659
//======================================================================
660
template
<
class
ELEMENT>
661
void
AlgebraicFishMesh<ELEMENT>::setup_algebraic_node_update
()
662
{
663
#ifdef PARANOID
664
/// Pointer to algebraic element in lower body
665
AlgebraicElementBase
*
lower_body_pt
=
666
dynamic_cast<
AlgebraicElementBase
*
>
(Mesh::element_pt(0));
667
668
if
(
lower_body_pt
== 0)
669
{
670
std::ostringstream
error_message
;
671
error_message
<<
"Element in AlgebraicFishMesh must be\n"
672
<<
"derived from AlgebraicElementBase\n"
673
<<
"but it is of type: "
674
<<
typeid
(Mesh::element_pt(0)).
name
() << std::endl;
675
676
throw
OomphLibError
(
677
error_message
.str(),
OOMPH_CURRENT_FUNCTION
,
OOMPH_EXCEPTION_LOCATION
);
678
}
679
#endif
680
681
// Read out the number of linear points in the element
682
unsigned
n_p
=
683
dynamic_cast<
ELEMENT
*
>
(
FishMesh<ELEMENT>::Mesh::finite_element_pt
(0))
684
->
nnode_1d
();
685
686
// Element 0: Lower body
687
//----------------------
688
{
689
unsigned
ielem
= 0;
690
FiniteElement
*
el_pt
= Mesh::finite_element_pt(
ielem
);
691
692
// Loop over rows in y/s_1-direction
693
for
(
unsigned
i1
= 0;
i1
<
n_p
;
i1
++)
694
{
695
// Loop over rows in x/s_0-direction
696
for
(
unsigned
i0
= 0;
i0
<
n_p
;
i0
++)
697
{
698
// Local node number
699
unsigned
jnod
=
i0
+
i1
*
n_p
;
700
701
// One geometric object is involved in update operation
702
Vector<GeomObject*>
geom_object_pt
(1);
703
geom_object_pt
[0] = this->Back_pt;
704
705
// The update function requires three parameters:
706
Vector<double>
ref_value
(3);
707
708
// First reference value: fractional x-position
709
ref_value
[0] =
double
(
i0
) /
double
(
n_p
- 1);
710
711
// Second reference value: fractional position along
712
// straight line from position on horizontal symmetry line to
713
// point on fish back
714
ref_value
[1] = 1.0 -
double
(
i1
) /
double
(
n_p
- 1);
715
716
// Third reference value: Sign (are we above or below the
717
// symmetry line?)
718
ref_value
[2] = -1.0;
719
720
// Setup algebraic update for node: Pass update information
721
dynamic_cast<
AlgebraicNode
*
>
(
el_pt
->node_pt(
jnod
))
722
->
add_node_update_info
(this->Lower_body,
// enumerated ID
723
this
,
// mesh
724
geom_object_pt
,
// vector of geom objects
725
ref_value
);
// vector of ref. values
726
}
727
}
728
}
729
730
// Element 1: Lower fin
731
//---------------------
732
{
733
unsigned
ielem
= 1;
734
FiniteElement
*
el_pt
= Mesh::finite_element_pt(
ielem
);
735
736
// Loop over rows in y/s_1-direction
737
for
(
unsigned
i1
= 0;
i1
<
n_p
;
i1
++)
738
{
739
// Loop over rows in x/s_0-direction
740
for
(
unsigned
i0
= 0;
i0
<
n_p
;
i0
++)
741
{
742
// Local node number
743
unsigned
jnod
=
i0
+
i1
*
n_p
;
744
745
// One geometric object is involved in update operation
746
Vector<GeomObject*>
geom_object_pt
(1);
747
geom_object_pt
[0] = this->Back_pt;
748
749
// The update function requires three parameters:
750
Vector<double>
ref_value
(3);
751
752
// First reference value: fractional x-position
753
ref_value
[0] =
double
(
i0
) /
double
(
n_p
- 1);
754
755
// Second reference value: fractional position along
756
// straight line from position on horizontal symmetry line to
757
// point on fish back
758
ref_value
[1] = 1.0 -
double
(
i1
) /
double
(
n_p
- 1);
759
760
// Third reference value: Sign (are we above or below the
761
// symmetry line?)
762
ref_value
[2] = -1.0;
763
764
// Setup algebraic update for node: Pass update information
765
dynamic_cast<
AlgebraicNode
*
>
(
el_pt
->node_pt(
jnod
))
766
->
add_node_update_info
(this->Lower_fin,
// enumerated ID
767
this
,
// mesh
768
geom_object_pt
,
// vector of geom objects
769
ref_value
);
// vector of ref. values
770
}
771
}
772
}
773
774
// Element 2: Upper body
775
//----------------------
776
{
777
unsigned
ielem
= 2;
778
FiniteElement
*
el_pt
= Mesh::finite_element_pt(
ielem
);
779
780
// Loop over rows in y/s_1-direction
781
for
(
unsigned
i1
= 0;
i1
<
n_p
;
i1
++)
782
{
783
// Loop over rows in x/s_0-direction
784
for
(
unsigned
i0
= 0;
i0
<
n_p
;
i0
++)
785
{
786
// Local node number
787
unsigned
jnod
=
i0
+
i1
*
n_p
;
788
789
// One geometric object is involved in update operation
790
Vector<GeomObject*>
geom_object_pt
(1);
791
geom_object_pt
[0] = this->Back_pt;
792
793
// The update function requires three parameters:
794
Vector<double>
ref_value
(3);
795
796
// First reference value: fractional x-position
797
ref_value
[0] =
double
(
i0
) /
double
(
n_p
- 1);
798
799
// Second reference value: fractional position along
800
// straight line from position on horizontal symmetry line to
801
// point on fish back
802
ref_value
[1] =
double
(
i1
) /
double
(
n_p
- 1);
803
804
// Third reference value: Sign (are we above or below the
805
// symmetry line?)
806
ref_value
[2] = 1.0;
807
808
// Setup algebraic update for node: Pass update information
809
dynamic_cast<
AlgebraicNode
*
>
(
el_pt
->node_pt(
jnod
))
810
->
add_node_update_info
(this->Upper_body,
// enumerated ID
811
this
,
// mesh
812
geom_object_pt
,
// vector of geom objects
813
ref_value
);
// vector of ref. values
814
}
815
}
816
}
817
818
// Element 3: Upper fin
819
//---------------------
820
{
821
unsigned
ielem
= 3;
822
FiniteElement
*
el_pt
= Mesh::finite_element_pt(
ielem
);
823
824
// Loop over rows in y/s_1-direction
825
for
(
unsigned
i1
= 0;
i1
<
n_p
;
i1
++)
826
{
827
// Loop over rows in x/s_0-direction
828
for
(
unsigned
i0
= 0;
i0
<
n_p
;
i0
++)
829
{
830
// Local node number
831
unsigned
jnod
=
i0
+
i1
*
n_p
;
832
833
// One geometric object is involved in update operation
834
Vector<GeomObject*>
geom_object_pt
(1);
835
geom_object_pt
[0] = this->Back_pt;
836
837
// The update function requires three parameters:
838
Vector<double>
ref_value
(3);
839
840
// First reference value: fractional x-position
841
ref_value
[0] =
double
(
i0
) /
double
(
n_p
- 1);
842
843
// Second reference value: fractional position along
844
// straight line from position on horizontal symmetry line to
845
// point on fish back
846
ref_value
[1] =
double
(
i1
) /
double
(
n_p
- 1);
847
848
// Third reference value: Sign (are we above or below the
849
// symmetry line?)
850
ref_value
[2] = 1.0;
851
852
// Setup algebraic update for node: Pass update information
853
dynamic_cast<
AlgebraicNode
*
>
(
el_pt
->node_pt(
jnod
))
854
->
add_node_update_info
(this->Upper_fin,
// enumerated ID
855
this
,
// mesh
856
geom_object_pt
,
// vector of geom objects
857
ref_value
);
// vector of ref. values
858
}
859
}
860
}
861
}
862
863
//======================================================================
864
/// Algebraic update function: Update in (upper or lower) body
865
/// according to wall shape at time level t (t=0: present; t>0: previous)
866
//======================================================================
867
template
<
class
ELEMENT>
868
void
AlgebraicFishMesh<ELEMENT>::node_update_in_body
(
const
unsigned
&
t
,
869
AlgebraicNode
*&
node_pt
)
870
{
871
// Pointer to geometric object that represents the fish back:
872
GeomObject*
back_pt
=
node_pt
->geom_object_pt(
unsigned
(0));
873
874
// Fixed reference value: x-position of mouth
875
double
x_mouth = this->
Domain_pt
->x_mouth();
876
877
// Fixed reference value: Lagrangian coordinate of point
878
// over mouth
879
double
zeta_mouth
= this->
Domain_pt
->xi_nose();
880
881
// Fixed reference value: Lagrangian coordinate of point
882
// near tail
883
double
zeta_near_tail
= this->
Domain_pt
->xi_tail();
884
885
// First reference value: fractional x-position
886
double
fract_x
=
node_pt
->ref_value(
unsigned
(0));
887
888
// Second reference value: fractional position along
889
// straight line from position on horizontal symmetry line to
890
// point on fish back
891
double
fract_y
=
node_pt
->ref_value(1);
892
893
// Third reference value: Sign (are we above or below the
894
// symmetry line?)
895
double
sign
=
node_pt
->ref_value(2);
896
897
// Get position on fish back
898
Vector<double>
zeta
(
back_pt
->nlagrangian());
899
zeta
[0] =
zeta_mouth
+
fract_x
* (
zeta_near_tail
-
zeta_mouth
);
900
Vector<double>
r_back
(
back_pt
->ndim());
901
back_pt
->position(
t
,
zeta
,
r_back
);
902
903
// Get position of point on fish back near tail
904
zeta
[0] =
zeta_near_tail
;
905
Vector<double>
r_near_tail
(
back_pt
->ndim());
906
back_pt
->position(
t
,
zeta
,
r_near_tail
);
907
908
// Get position on symmetry line
909
Vector<double>
r_sym
(2);
910
r_sym
[0] = x_mouth +
fract_x
* (
r_near_tail
[0] - x_mouth);
911
r_sym
[1] = 0.0;
912
913
// Assign new nodal coordinate
914
node_pt
->x(
t
, 0) =
r_sym
[0] +
fract_y
* (
r_back
[0] -
r_sym
[0]);
915
node_pt
->x(
t
, 1) =
sign
* (
r_sym
[1] +
fract_y
* (
r_back
[1] -
r_sym
[1]));
916
}
917
918
//======================================================================
919
/// Algebraic update function: Update in (upper or lower) fin
920
/// according to wall shape at time level t (t=0: present; t>0: previous)
921
//======================================================================
922
template
<
class
ELEMENT>
923
void
AlgebraicFishMesh<ELEMENT>::node_update_in_fin
(
const
unsigned
&
t
,
924
AlgebraicNode
*&
node_pt
)
925
{
926
// Pointer to geometric object that represents the fish back:
927
GeomObject*
back_pt
=
node_pt
->geom_object_pt(
unsigned
(0));
928
929
// Fixed reference value: End coordinate on fish back
930
double
zeta_wall
= this->
Domain_pt
->xi_tail();
931
932
// Fixed reference value: x-position of end of tail
933
double
x_tail
= this->
Domain_pt
->x_fin();
934
935
// Fixed reference value: y-position of end of tail
936
double
y_tail
= this->
Domain_pt
->y_fin();
937
938
// First reference value: fractional position in x-direction
939
double
fract_x
=
node_pt
->ref_value(
unsigned
(0));
940
941
// Second reference value: fractional position along
942
// vertical line from position on horizontal symmetry line to
943
// point on upper end of tail
944
double
fract_y
=
node_pt
->ref_value(1);
945
946
// Third reference value: Sign (are we above or below the
947
// symmetry line?)
948
double
sign
=
node_pt
->ref_value(2);
949
950
// Get position on fish back
951
Vector<double>
zeta
(
back_pt
->nlagrangian());
952
zeta
[0] =
zeta_wall
;
953
Vector<double>
r_back
(
back_pt
->ndim());
954
back_pt
->position(
t
,
zeta
,
r_back
);
955
956
// y-position on top edge of fin:
957
double
y_fin_edge
=
r_back
[1] +
fract_x
* (
y_tail
-
r_back
[1]);
958
959
// Assign new nodal coordinate
960
node_pt
->x(
t
, 0) =
r_back
[0] +
fract_x
* (
x_tail
-
r_back
[0]);
961
node_pt
->x(
t
, 1) =
sign
*
fract_y
*
y_fin_edge
;
962
}
963
964
}
// namespace oomph
965
#endif
oomph::AlgebraicFishMesh::setup_algebraic_node_update
void setup_algebraic_node_update()
Setup algebraic update operation for all nodes (separate function because this task needs to be perfo...
Definition
fish_mesh.template.cc:661
oomph::AlgebraicFishMesh::node_update_in_body
void node_update_in_body(const unsigned &t, AlgebraicNode *&node_pt)
Algebraic update function for nodes in upper/lower body.
Definition
fish_mesh.template.cc:868
oomph::AlgebraicFishMesh::node_update_in_fin
void node_update_in_fin(const unsigned &t, AlgebraicNode *&node_pt)
Algebraic update function for nodes in upper/lower fin.
Definition
fish_mesh.template.cc:923
oomph::CollapsibleChannelMesh::Domain_pt
CollapsibleChannelDomain * Domain_pt
Pointer to domain.
Definition
collapsible_channel_mesh.h:145
oomph::FishDomain
Fish shaped domain, represented by four MacroElements. Shape is parametrised by GeomObject that repre...
Definition
fish_domain.h:43
oomph::FishMesh
Fish shaped mesh. The geometry is defined by the Domain object FishDomain.
Definition
fish_mesh.h:54
oomph::FishMesh::FishMesh
FishMesh(TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to timestepper (defaults to the (Steady) default timestepper defined in Mes...
Definition
fish_mesh.template.cc:40
oomph::FishMesh::build_mesh
void build_mesh(TimeStepper *time_stepper_pt)
Build the mesh, using the geometric object identified by Back_pt.
Definition
fish_mesh.template.cc:82
oomph::FishMesh::Must_kill_fish_back
bool Must_kill_fish_back
Do I need to kill the fish back geom object?
Definition
fish_mesh.h:109
oomph::MacroElementNodeUpdateCollapsibleChannelMesh
Collapsible channel mesh with MacroElement-based node update. The collapsible segment is represented ...
Definition
collapsible_channel_mesh.h:236
oomph::RefineableFishMesh::setup_adaptivity
void setup_adaptivity()
Setup all the information that's required for spatial adaptivity: Set pointers to macro elements and ...
Definition
fish_mesh.template.cc:641
oomph
Definition
annular_domain.h:35