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
generic
Qelements.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
// Non-inline member functions for Qelements
27
28
// Include the appropriate headers
29
#include "
Qelements.h
"
30
31
namespace
oomph
32
{
33
////////////////////////////////////////////////////////////////
34
// 1D Qelements
35
////////////////////////////////////////////////////////////////
36
37
//=======================================================================
38
/// Assign the static Default_integration_scheme
39
//=======================================================================
40
template
<
unsigned
NNODE_1D>
41
Gauss<1, NNODE_1D> QElement<1, NNODE_1D>::Default_integration_scheme;
42
43
44
//==================================================================
45
/// Return the node at the specified local coordinate
46
//==================================================================
47
template
<
unsigned
NNODE_1D>
48
Node
*
QElement<1, NNODE_1D>::get_node_at_local_coordinate
(
49
const
Vector<double>
&
s
)
const
50
{
51
// Load the tolerance into a local variable
52
double
tol
=
FiniteElement::Node_location_tolerance
;
53
// There is one possible index.
54
Vector<int>
index(1);
55
56
// Determine the index
57
// -------------------
58
59
// If we are at the lower limit, the index is zero
60
if
(std::fabs(
s
[0] + 1.0) <
tol
)
61
{
62
index[0] = 0;
63
}
64
// If we are at the upper limit, the index is the number of nodes minus 1
65
else
if
(std::fabs(
s
[0] - 1.0) <
tol
)
66
{
67
index[0] =
NNODE_1D
- 1;
68
}
69
// Otherwise, we have to calculate the index in general
70
else
71
{
72
// For uniformly spaced nodes the node number would be
73
double
float_index
= 0.5 * (1.0 +
s
[0]) * (
NNODE_1D
- 1);
74
// Convert to an integer by taking the floor (rounding down)
75
index[0] =
static_cast<
int
>
(std::floor(
float_index
));
76
// What is the excess. This should be safe because the
77
// we have rounded down
78
double
excess
=
float_index
- index[0];
79
// If the excess is bigger than our tolerance there is no node,
80
// return null
81
// Note that we test at both lower and upper ends.
82
if
((
excess
>
tol
) && ((1.0 -
excess
) >
tol
))
83
{
84
return
0;
85
}
86
// If we are at the upper end (i.e. the system has actually rounded up)
87
// we need to add one to the index
88
if
((1.0 -
excess
) <=
tol
)
89
{
90
index[0] += 1;
91
}
92
}
93
// If we've got here we have a node, so let's return a pointer to it
94
return
node_pt
(index[0]);
95
}
96
97
98
//=======================================================================
99
/// Shape function for specific QElement<1,..>
100
//=======================================================================
101
template
<
unsigned
NNODE_1D>
102
void
QElement<1, NNODE_1D>::shape
(
const
Vector<double>
&
s
,
Shape
&
psi
)
const
103
{
104
// Local storage for the shape functions
105
double
Psi[
NNODE_1D
];
106
// Call the OneDimensional Shape functions
107
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi);
108
109
// Now let's loop over the nodal points in the element
110
// and copy the values back in
111
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
112
{
113
psi
[
i
] = Psi[
i
];
114
}
115
}
116
117
//=======================================================================
118
/// Derivatives of shape functions for specific QElement<1,..>
119
//=======================================================================
120
template
<
unsigned
NNODE_1D>
121
void
QElement<1, NNODE_1D>::dshape_local
(
const
Vector<double>
&
s
,
122
Shape
&
psi
,
123
DShape
&
dpsids
)
const
124
{
125
// Local storage
126
double
Psi[
NNODE_1D
];
127
double
DPsi[
NNODE_1D
];
128
// Call the shape functions and derivatives
129
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi);
130
OneDimLagrange::dshape<NNODE_1D>(
s
[0], DPsi);
131
132
// Loop over shape functions in element and assign to psi
133
for
(
unsigned
l
= 0;
l
<
NNODE_1D
;
l
++)
134
{
135
psi
[
l
] = Psi[
l
];
136
dpsids
(
l
, 0) = DPsi[
l
];
137
}
138
}
139
140
//=======================================================================
141
/// Second derivatives of shape functions for specific QElement<1,..>:
142
/// d2psids(i,0) = \f$ d^2 \psi_j / d s^2 \f$
143
//=======================================================================
144
template
<
unsigned
NNODE_1D>
145
void
QElement<1, NNODE_1D>::d2shape_local
(
const
Vector<double>
&
s
,
146
Shape
&
psi
,
147
DShape
&
dpsids
,
148
DShape
&
d2psids
)
const
149
{
150
// Local storage for the shape functions
151
double
Psi[
NNODE_1D
];
152
double
DPsi[
NNODE_1D
];
153
double
D2Psi
[
NNODE_1D
];
154
// Call the shape functions and derivatives
155
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi);
156
OneDimLagrange::dshape<NNODE_1D>(
s
[0], DPsi);
157
OneDimLagrange::d2shape<NNODE_1D>(
s
[0],
D2Psi
);
158
159
// Loop over shape functions in element and assign to psi
160
for
(
unsigned
l
= 0;
l
<
NNODE_1D
;
l
++)
161
{
162
psi
[
l
] = Psi[
l
];
163
dpsids
(
l
, 0) = DPsi[
l
];
164
d2psids
(
l
, 0) =
D2Psi
[
l
];
165
}
166
}
167
168
//=======================================================================
169
/// The output function for general 1D QElements
170
//=======================================================================
171
template
<
unsigned
NNODE_1D>
172
void
QElement<1, NNODE_1D>::output
(std::ostream&
outfile
)
173
{
174
output
(
outfile
,
NNODE_1D
);
175
}
176
177
//=======================================================================
178
/// The output function for n_plot points in each coordinate direction
179
//=======================================================================
180
template
<
unsigned
NNODE_1D>
181
void
QElement<1, NNODE_1D>::output
(std::ostream&
outfile
,
182
const
unsigned
&
n_plot
)
183
{
184
// Local variables
185
Vector<double>
s
(1);
186
187
// Tecplot header info
188
outfile
<<
"ZONE I="
<<
n_plot
<< std::endl;
189
190
// Find the dimension of the nodes
191
unsigned
n_dim
= this->
nodal_dimension
();
192
193
// Loop over plot points
194
for
(
unsigned
l
= 0;
l
<
n_plot
;
l
++)
195
{
196
s
[0] = -1.0 +
l
* 2.0 / (
n_plot
- 1);
197
// Output the coordinates
198
for
(
unsigned
i
= 0;
i
<
n_dim
;
i
++)
199
{
200
outfile
<<
interpolated_x
(
s
,
i
) <<
" "
;
201
}
202
outfile
<< std::endl;
203
}
204
outfile
<< std::endl;
205
}
206
207
208
//=======================================================================
209
/// C style output function for general 1D QElements
210
//=======================================================================
211
template
<
unsigned
NNODE_1D>
212
void
QElement<1, NNODE_1D>::output
(
FILE
*
file_pt
)
213
{
214
output
(
file_pt
,
NNODE_1D
);
215
}
216
217
//=======================================================================
218
/// C style output function for n_plot points in each coordinate direction
219
//=======================================================================
220
template
<
unsigned
NNODE_1D>
221
void
QElement<1, NNODE_1D>::output
(
FILE
*
file_pt
,
const
unsigned
&
n_plot
)
222
{
223
// Local variables
224
Vector<double>
s
(1);
225
226
// Tecplot header info
227
// outfile << "ZONE I=" << n_plot << std::endl;
228
fprintf
(
file_pt
,
"ZONE I=%i\n"
,
n_plot
);
229
230
// Find the dimension of the first node
231
unsigned
n_dim
= this->
nodal_dimension
();
232
233
// Loop over plot points
234
for
(
unsigned
l
= 0;
l
<
n_plot
;
l
++)
235
{
236
s
[0] = -1.0 +
l
* 2.0 / (
n_plot
- 1);
237
238
// Output the coordinates
239
for
(
unsigned
i
= 0;
i
<
n_dim
;
i
++)
240
{
241
// outfile << interpolated_x(s,i) << " " ;
242
fprintf
(
file_pt
,
"%g "
,
interpolated_x
(
s
,
i
));
243
}
244
// outfile << std::endl;
245
fprintf
(
file_pt
,
"\n"
);
246
}
247
// outfile << std::endl;
248
fprintf
(
file_pt
,
"\n"
);
249
}
250
251
252
////////////////////////////////////////////////////////////////
253
// 2D Qelements
254
////////////////////////////////////////////////////////////////
255
256
/// Assign the spatial integration scheme
257
template
<
unsigned
NNODE_1D>
258
Gauss<2, NNODE_1D>
QElement<2, NNODE_1D>::Default_integration_scheme
;
259
260
261
//==================================================================
262
/// Return the node at the specified local coordinate
263
//==================================================================
264
template
<
unsigned
NNODE_1D>
265
Node
*
QElement<2, NNODE_1D>::get_node_at_local_coordinate
(
266
const
Vector<double>
&
s
)
const
267
{
268
// Load the tolerance into a local variable
269
double
tol
=
FiniteElement::Node_location_tolerance
;
270
// There are two possible indices.
271
Vector<int>
index(2);
272
// Loop over the coordinates and determine the indices
273
for
(
unsigned
i
= 0;
i
< 2;
i
++)
274
{
275
// If we are at the lower limit, the index is zero
276
if
(std::fabs(
s
[
i
] + 1.0) <
tol
)
277
{
278
index[
i
] = 0;
279
}
280
// If we are at the upper limit, the index is the number of nodes minus 1
281
else
if
(std::fabs(
s
[
i
] - 1.0) <
tol
)
282
{
283
index[
i
] =
NNODE_1D
- 1;
284
}
285
// Otherwise, we have to calculate the index in general
286
else
287
{
288
// For uniformly spaced nodes the node number would be
289
double
float_index
= 0.5 * (1.0 +
s
[
i
]) * (
NNODE_1D
- 1);
290
// Convert to an integer by taking the floor (rounding down)
291
index[
i
] =
static_cast<
int
>
(std::floor(
float_index
));
292
// What is the excess. This should be safe because the
293
// we have rounded down
294
double
excess
=
float_index
- index[
i
];
295
// If the excess is bigger than our tolerance there is no node,
296
// return null
297
// Note that we test at both lower and upper ends.
298
if
((
excess
>
tol
) && ((1.0 -
excess
) >
tol
))
299
{
300
return
0;
301
}
302
// If we are at the upper end (i.e. the system has actually rounded up)
303
// we need to add one to the index
304
if
((1.0 -
excess
) <=
tol
)
305
{
306
index[
i
] += 1;
307
}
308
}
309
}
310
// If we've got here we have a node, so let's return a pointer to it
311
return
node_pt
(index[0] +
NNODE_1D
* index[1]);
312
}
313
314
315
//=======================================================================
316
/// Shape function for specific QElement<2,..>
317
///
318
//=======================================================================
319
template
<
unsigned
NNODE_1D>
320
void
QElement<2, NNODE_1D>::shape
(
const
Vector<double>
&
s
,
Shape
&
psi
)
const
321
{
322
// Local storage
323
double
Psi[2][
NNODE_1D
];
324
// Call the OneDimensional Shape functions
325
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi[0]);
326
OneDimLagrange::shape<NNODE_1D>(
s
[1], Psi[1]);
327
// Index for the shape functions
328
unsigned
index = 0;
329
330
// Now let's loop over the nodal points in the element
331
// s1 is the "x" coordinate, s2 the "y"
332
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
333
{
334
for
(
unsigned
j
= 0;
j
<
NNODE_1D
;
j
++)
335
{
336
// Multiply the two 1D functions together to get the 2D function
337
psi
[index] = Psi[1][
i
] * Psi[0][
j
];
338
// Incremenet the index
339
++index;
340
}
341
}
342
}
343
344
//=======================================================================
345
/// Derivatives of shape functions for specific QElement<2,..>
346
//=======================================================================
347
template
<
unsigned
NNODE_1D>
348
void
QElement<2, NNODE_1D>::dshape_local
(
const
Vector<double>
&
s
,
349
Shape
&
psi
,
350
DShape
&
dpsids
)
const
351
{
352
// Local storage
353
double
Psi[2][
NNODE_1D
];
354
double
DPsi[2][
NNODE_1D
];
355
unsigned
index = 0;
356
357
// Call the shape functions and derivatives
358
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi[0]);
359
OneDimLagrange::shape<NNODE_1D>(
s
[1], Psi[1]);
360
OneDimLagrange::dshape<NNODE_1D>(
s
[0], DPsi[0]);
361
OneDimLagrange::dshape<NNODE_1D>(
s
[1], DPsi[1]);
362
363
// Loop over shape functions in element
364
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
365
{
366
for
(
unsigned
j
= 0;
j
<
NNODE_1D
;
j
++)
367
{
368
// Assign the values
369
dpsids
(index, 0) = Psi[1][
i
] * DPsi[0][
j
];
370
dpsids
(index, 1) = DPsi[1][
i
] * Psi[0][
j
];
371
psi
[index] = Psi[1][
i
] * Psi[0][
j
];
372
// Increment the index
373
++index;
374
}
375
}
376
}
377
378
379
//=======================================================================
380
/// Second derivatives of shape functions for specific QElement<2,..>:
381
/// d2psids(i,0) = \f$ \partial^2 \psi_j / \partial s_0^2 \f$
382
/// d2psids(i,1) = \f$ \partial^2 \psi_j / \partial s_1^2 \f$
383
/// d2psids(i,2) = \f$ \partial^2 \psi_j / \partial s_0 \partial s_1 \f$
384
//=======================================================================
385
template
<
unsigned
NNODE_1D>
386
void
QElement<2, NNODE_1D>::d2shape_local
(
const
Vector<double>
&
s
,
387
Shape
&
psi
,
388
DShape
&
dpsids
,
389
DShape
&
d2psids
)
const
390
{
391
// Local storage
392
double
Psi[2][
NNODE_1D
];
393
double
DPsi[2][
NNODE_1D
];
394
double
D2Psi
[2][
NNODE_1D
];
395
// Index for the assembly process
396
unsigned
index = 0;
397
398
// Call the shape functions and derivatives
399
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi[0]);
400
OneDimLagrange::shape<NNODE_1D>(
s
[1], Psi[1]);
401
OneDimLagrange::dshape<NNODE_1D>(
s
[0], DPsi[0]);
402
OneDimLagrange::dshape<NNODE_1D>(
s
[1], DPsi[1]);
403
OneDimLagrange::d2shape<NNODE_1D>(
s
[0],
D2Psi
[0]);
404
OneDimLagrange::d2shape<NNODE_1D>(
s
[1],
D2Psi
[1]);
405
406
// Loop over shape functions in element
407
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
408
{
409
for
(
unsigned
j
= 0;
j
<
NNODE_1D
;
j
++)
410
{
411
// Assign the values
412
psi
[index] = Psi[1][
i
] * Psi[0][
j
];
413
// First derivatives
414
dpsids
(index, 0) = Psi[1][
i
] * DPsi[0][
j
];
415
dpsids
(index, 1) = DPsi[1][
i
] * Psi[0][
j
];
416
// Second derivatives
417
// N.B. index 2 is the mixed derivative
418
d2psids
(index, 0) = Psi[1][
i
] *
D2Psi
[0][
j
];
419
d2psids
(index, 1) =
D2Psi
[1][
i
] * Psi[0][
j
];
420
d2psids
(index, 2) = DPsi[1][
i
] * DPsi[0][
j
];
421
// Increment the index
422
++index;
423
}
424
}
425
}
426
427
428
//===========================================================
429
/// The output function for QElement<2,NNODE_1D>
430
//===========================================================
431
template
<
unsigned
NNODE_1D>
432
void
QElement<2, NNODE_1D>::output
(std::ostream&
outfile
)
433
{
434
output
(
outfile
,
NNODE_1D
);
435
}
436
437
//=======================================================================
438
/// The output function for n_plot points in each coordinate direction
439
//=======================================================================
440
template
<
unsigned
NNODE_1D>
441
void
QElement<2, NNODE_1D>::output
(std::ostream&
outfile
,
442
const
unsigned
&
n_plot
)
443
{
444
// Local variables
445
Vector<double>
s
(2);
446
447
// Tecplot header info
448
outfile
<<
"ZONE I="
<<
n_plot
<<
", J="
<<
n_plot
<< std::endl;
449
450
// Find the dimension of the nodes
451
unsigned
n_dim
= this->
nodal_dimension
();
452
453
// Loop over plot points
454
for
(
unsigned
l2
= 0;
l2
<
n_plot
;
l2
++)
455
{
456
s
[1] = -1.0 +
l2
* 2.0 / (
n_plot
- 1);
457
for
(
unsigned
l1
= 0;
l1
<
n_plot
;
l1
++)
458
{
459
s
[0] = -1.0 +
l1
* 2.0 / (
n_plot
- 1);
460
461
// Output the coordinates
462
for
(
unsigned
i
= 0;
i
<
n_dim
;
i
++)
463
{
464
outfile
<<
interpolated_x
(
s
,
i
) <<
" "
;
465
}
466
outfile
<< std::endl;
467
}
468
}
469
outfile
<< std::endl;
470
}
471
472
473
//===========================================================
474
/// C-style output function for QElement<2,NNODE_1D>
475
//===========================================================
476
template
<
unsigned
NNODE_1D>
477
void
QElement<2, NNODE_1D>::output
(
FILE
*
file_pt
)
478
{
479
output
(
file_pt
,
NNODE_1D
);
480
}
481
482
//=======================================================================
483
/// C-style output function for n_plot points in each coordinate direction
484
//=======================================================================
485
template
<
unsigned
NNODE_1D>
486
void
QElement<2, NNODE_1D>::output
(
FILE
*
file_pt
,
const
unsigned
&
n_plot
)
487
{
488
// Local variables
489
Vector<double>
s
(2);
490
491
// Find the dimension of the nodes
492
unsigned
n_dim
= this->
nodal_dimension
();
493
494
// Tecplot header info
495
// outfile << "ZONE I=" << n_plot << ", J=" << n_plot << std::endl;
496
fprintf
(file_pt,
"ZONE I=%i, J=%i\n"
,
n_plot
,
n_plot
);
497
498
// Loop over element nodes
499
for
(
unsigned
l2
= 0;
l2
<
n_plot
;
l2
++)
500
{
501
s
[1] = -1.0 +
l2
* 2.0 / (
n_plot
- 1);
502
for
(
unsigned
l1
= 0;
l1
<
n_plot
;
l1
++)
503
{
504
s
[0] = -1.0 +
l1
* 2.0 / (
n_plot
- 1);
505
506
// Output the coordinates
507
for
(
unsigned
i
= 0;
i
<
n_dim
;
i
++)
508
{
509
// outfile << interpolated_x(s,i) << " " ;
510
fprintf
(
file_pt
,
"%g "
,
interpolated_x
(
s
,
i
));
511
}
512
// outfile << std::endl;
513
fprintf
(
file_pt
,
"\n"
);
514
}
515
}
516
// outfile << std::endl;
517
fprintf
(
file_pt
,
"\n"
);
518
}
519
520
521
////////////////////////////////////////////////////////////////
522
// 3D Qelements
523
////////////////////////////////////////////////////////////////
524
525
/// Assign the spatial integration scheme
526
template
<
unsigned
NNODE_1D>
527
Gauss<3, NNODE_1D>
QElement<3, NNODE_1D>::Default_integration_scheme
;
528
529
//==================================================================
530
/// Return the node at the specified local coordinate
531
//==================================================================
532
template
<
unsigned
NNODE_1D>
533
Node
*
QElement<3, NNODE_1D>::get_node_at_local_coordinate
(
534
const
Vector<double>
&
s
)
const
535
{
536
// Load the tolerance into a local variable
537
double
tol
=
FiniteElement::Node_location_tolerance
;
538
// There are now three possible indices
539
Vector<int>
index(3);
540
// Loop over the coordinates
541
for
(
unsigned
i
= 0;
i
< 3;
i
++)
542
{
543
// If we are at the lower limit, the index is zero
544
if
(std::fabs(
s
[
i
] + 1.0) <
tol
)
545
{
546
index[
i
] = 0;
547
}
548
// If we are at the upper limit, the index is the number of nodes minus 1
549
else
if
(std::fabs(
s
[
i
] - 1.0) <
tol
)
550
{
551
index[
i
] =
NNODE_1D
- 1;
552
}
553
// Otherwise, we have to calculate the index in general
554
else
555
{
556
// For uniformly spaced nodes the node number would be
557
double
float_index
= 0.5 * (1.0 +
s
[
i
]) * (
NNODE_1D
- 1);
558
// Conver to an integer by taking the floor (rounding down)
559
index[
i
] =
static_cast<
int
>
(std::floor(
float_index
));
560
// What is the excess. This should be safe because
561
// we have rounded down
562
double
excess
=
float_index
- index[
i
];
563
// If the excess is bigger than our tolerance there is no node,
564
// return null. Note that we test at both ends
565
if
((
excess
>
tol
) && ((1.0 -
excess
) >
tol
))
566
{
567
return
0;
568
}
569
// If we are at the upper end (i.e. the system has actually rounded up)
570
// we need to add one to the index
571
if
((1.0 -
excess
) <=
tol
)
572
{
573
index[
i
] += 1;
574
}
575
}
576
}
577
// If we've got here we have a node, so let's return a pointer to it
578
return
node_pt
(index[0] +
NNODE_1D
* index[1] +
579
NNODE_1D
*
NNODE_1D
* index[2]);
580
}
581
582
583
//=======================================================================
584
/// Shape function for specific QElement<3,..>
585
//=======================================================================
586
template
<
unsigned
NNODE_1D>
587
void
QElement<3, NNODE_1D>::shape
(
const
Vector<double>
&
s
,
Shape
&
psi
)
const
588
{
589
// Local storage
590
double
Psi[3][
NNODE_1D
];
591
592
// Call the OneDimensional Shape functions
593
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi[0]);
594
OneDimLagrange::shape<NNODE_1D>(
s
[1], Psi[1]);
595
OneDimLagrange::shape<NNODE_1D>(
s
[2], Psi[2]);
596
597
// Index for the shape functions
598
unsigned
index = 0;
599
600
// Now let's loop over the nodal points in the element
601
// s1 is the "x" coordinate, s2 the "y"
602
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
603
{
604
for
(
unsigned
j
= 0;
j
<
NNODE_1D
;
j
++)
605
{
606
for
(
unsigned
k
= 0;
k
<
NNODE_1D
;
k
++)
607
{
608
/*Multiply the three 1D functions together to get the 3D function*/
609
psi
[index] = Psi[2][
i
] * Psi[1][
j
] * Psi[0][
k
];
610
// Increment the index
611
++index;
612
}
613
}
614
}
615
}
616
617
//=======================================================================
618
/// Derivatives of shape functions for specific QElement<3,..>
619
//=======================================================================
620
template
<
unsigned
NNODE_1D>
621
void
QElement<3, NNODE_1D>::dshape_local
(
const
Vector<double>
&
s
,
622
Shape
&
psi
,
623
DShape
&
dpsids
)
const
624
{
625
// Local storage
626
double
Psi[3][
NNODE_1D
];
627
double
DPsi[3][
NNODE_1D
];
628
// Index of the total shape function
629
unsigned
index = 0;
630
631
// Call the OneDimensional Shape functions and derivatives
632
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi[0]);
633
OneDimLagrange::shape<NNODE_1D>(
s
[1], Psi[1]);
634
OneDimLagrange::shape<NNODE_1D>(
s
[2], Psi[2]);
635
OneDimLagrange::dshape<NNODE_1D>(
s
[0], DPsi[0]);
636
OneDimLagrange::dshape<NNODE_1D>(
s
[1], DPsi[1]);
637
OneDimLagrange::dshape<NNODE_1D>(
s
[2], DPsi[2]);
638
639
640
// Loop over shape functions in element
641
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
642
{
643
for
(
unsigned
j
= 0;
j
<
NNODE_1D
;
j
++)
644
{
645
for
(
unsigned
k
= 0;
k
<
NNODE_1D
;
k
++)
646
{
647
// Assign the values
648
dpsids
(index, 0) = Psi[2][
i
] * Psi[1][
j
] * DPsi[0][
k
];
649
dpsids
(index, 1) = Psi[2][
i
] * DPsi[1][
j
] * Psi[0][
k
];
650
dpsids
(index, 2) = DPsi[2][
i
] * Psi[1][
j
] * Psi[0][
k
];
651
652
psi
[index] = Psi[2][
i
] * Psi[1][
j
] * Psi[0][
k
];
653
// Increment the index
654
++index;
655
}
656
}
657
}
658
}
659
660
//=======================================================================
661
/// Second derivatives of shape functions for specific QElement<3,..>:
662
/// d2psids(i,0) = \f$ \partial^2 \psi_j / \partial s_0^2 \f$
663
/// d2psids(i,1) = \f$ \partial^2 \psi_j / \partial s_1^2 \f$
664
/// d2psids(i,2) = \f$ \partial^2 \psi_j / \partial s_2^2 \f$
665
/// d2psids(i,3) = \f$ \partial^2 \psi_j / \partial s_0 \partial s_1 \f$
666
/// d2psids(i,4) = \f$ \partial^2 \psi_j / \partial s_0 \partial s_2 \f$
667
/// d2psids(i,5) = \f$ \partial^2 \psi_j / \partial s_1 \partial s_2 \f$
668
//=======================================================================
669
template
<
unsigned
NNODE_1D>
670
void
QElement<3, NNODE_1D>::d2shape_local
(
const
Vector<double>
&
s
,
671
Shape
&
psi
,
672
DShape
&
dpsids
,
673
DShape
&
d2psids
)
const
674
{
675
// Local storage
676
double
Psi[3][
NNODE_1D
];
677
double
DPsi[3][
NNODE_1D
];
678
double
D2Psi
[3][
NNODE_1D
];
679
// Index of the shape function
680
unsigned
index = 0;
681
682
// Call the OneDimensional Shape functions and derivatives
683
OneDimLagrange::shape<NNODE_1D>(
s
[0], Psi[0]);
684
OneDimLagrange::shape<NNODE_1D>(
s
[1], Psi[1]);
685
OneDimLagrange::shape<NNODE_1D>(
s
[2], Psi[2]);
686
OneDimLagrange::dshape<NNODE_1D>(
s
[0], DPsi[0]);
687
OneDimLagrange::dshape<NNODE_1D>(
s
[1], DPsi[1]);
688
OneDimLagrange::dshape<NNODE_1D>(
s
[2], DPsi[2]);
689
OneDimLagrange::d2shape<NNODE_1D>(
s
[0],
D2Psi
[0]);
690
OneDimLagrange::d2shape<NNODE_1D>(
s
[1],
D2Psi
[1]);
691
OneDimLagrange::d2shape<NNODE_1D>(
s
[2],
D2Psi
[2]);
692
693
// Loop over shape functions in element
694
for
(
unsigned
i
= 0;
i
<
NNODE_1D
;
i
++)
695
{
696
for
(
unsigned
j
= 0;
j
<
NNODE_1D
;
j
++)
697
{
698
for
(
unsigned
k
= 0;
k
<
NNODE_1D
;
k
++)
699
{
700
// Assign the values
701
psi
[index] = Psi[2][
i
] * Psi[1][
j
] * Psi[0][
k
];
702
703
dpsids
(index, 0) = Psi[2][
i
] * Psi[1][
j
] * DPsi[0][
k
];
704
dpsids
(index, 1) = Psi[2][
i
] * DPsi[1][
j
] * Psi[0][
k
];
705
dpsids
(index, 2) = DPsi[2][
i
] * Psi[1][
j
] * Psi[0][
k
];
706
707
// Second derivative values
708
d2psids
(index, 0) = Psi[2][
i
] * Psi[1][
j
] *
D2Psi
[0][
k
];
709
d2psids
(index, 1) = Psi[2][
i
] *
D2Psi
[1][
j
] * Psi[0][
k
];
710
d2psids
(index, 2) =
D2Psi
[2][
i
] * Psi[1][
j
] * Psi[0][
k
];
711
// Convention for higher indices
712
// 3: mixed 12, 4: mixed 13, 5: mixed 23
713
d2psids
(index, 3) = Psi[2][
i
] * DPsi[1][
j
] * DPsi[0][
k
];
714
d2psids
(index, 4) = DPsi[2][
i
] * Psi[1][
j
] * DPsi[0][
k
];
715
d2psids
(index, 5) = DPsi[2][
i
] * DPsi[1][
j
] * Psi[0][
k
];
716
// Increment the index
717
++index;
718
}
719
}
720
}
721
}
722
723
//===========================================================
724
/// The output function for QElement<3,NNODE_1D>
725
//===========================================================
726
template
<
unsigned
NNODE_1D>
727
void
QElement<3, NNODE_1D>::output
(std::ostream&
outfile
)
728
{
729
output
(
outfile
,
NNODE_1D
);
730
}
731
732
//=======================================================================
733
/// The output function for n_plot points in each coordinate direction
734
//=======================================================================
735
template
<
unsigned
NNODE_1D>
736
void
QElement<3, NNODE_1D>::output
(std::ostream&
outfile
,
737
const
unsigned
&
n_plot
)
738
{
739
// Local variables
740
Vector<double>
s
(3);
741
742
// Tecplot header info
743
outfile
<<
"ZONE I="
<<
n_plot
<<
", J="
<<
n_plot
<<
", K="
<<
n_plot
744
<< std::endl;
745
746
// Find the dimension of the nodes
747
unsigned
n_dim
= this->
nodal_dimension
();
748
749
// Loop over element nodes
750
for
(
unsigned
l3
= 0;
l3
<
n_plot
;
l3
++)
751
{
752
s
[2] = -1.0 +
l3
* 2.0 / (
n_plot
- 1);
753
for
(
unsigned
l2
= 0;
l2
<
n_plot
;
l2
++)
754
{
755
s
[1] = -1.0 +
l2
* 2.0 / (
n_plot
- 1);
756
for
(
unsigned
l1
= 0;
l1
<
n_plot
;
l1
++)
757
{
758
s
[0] = -1.0 +
l1
* 2.0 / (
n_plot
- 1);
759
760
// Output the coordinates
761
for
(
unsigned
i
= 0;
i
<
n_dim
;
i
++)
762
{
763
outfile
<<
interpolated_x
(
s
,
i
) <<
" "
;
764
}
765
outfile
<< std::endl;
766
}
767
}
768
}
769
outfile
<< std::endl;
770
}
771
772
773
//===========================================================
774
/// C-style output function for QElement<3,NNODE_1D>
775
//===========================================================
776
template
<
unsigned
NNODE_1D>
777
void
QElement<3, NNODE_1D>::output
(
FILE
*
file_pt
)
778
{
779
output
(
file_pt
,
NNODE_1D
);
780
}
781
782
//=======================================================================
783
/// C-style output function for n_plot points in each coordinate direction
784
//=======================================================================
785
template
<
unsigned
NNODE_1D>
786
void
QElement<3, NNODE_1D>::output
(
FILE
*
file_pt
,
const
unsigned
&
n_plot
)
787
{
788
// Local variables
789
Vector<double>
s
(3);
790
791
// Tecplot header info
792
fprintf
(
file_pt
,
"ZONE I=%i, J=%i, K=%i\n"
,
n_plot
,
n_plot
,
n_plot
);
793
794
// Find the dimension of the nodes
795
unsigned
n_dim
= this->
nodal_dimension
();
796
797
// Loop over element nodes
798
for
(
unsigned
l3
= 0;
l3
<
n_plot
;
l3
++)
799
{
800
s
[2] = -1.0 +
l3
* 2.0 / (
n_plot
- 1);
801
for
(
unsigned
l2
= 0;
l2
<
n_plot
;
l2
++)
802
{
803
s
[1] = -1.0 +
l2
* 2.0 / (
n_plot
- 1);
804
for
(
unsigned
l1
= 0;
l1
<
n_plot
;
l1
++)
805
{
806
s
[0] = -1.0 +
l1
* 2.0 / (
n_plot
- 1);
807
808
// Output the coordinates
809
for
(
unsigned
i
= 0;
i
<
n_dim
;
i
++)
810
{
811
fprintf
(
file_pt
,
"%g "
,
interpolated_x
(
s
,
i
));
812
}
813
fprintf
(
file_pt
,
"\n"
);
814
}
815
}
816
}
817
fprintf
(
file_pt
,
"\n"
);
818
}
819
820
821
//===================================================================
822
// Build required templates
823
//===================================================================
824
template
class
QElement<1, 2>
;
825
template
class
QElement<1, 3>
;
826
template
class
QElement<1, 4>
;
827
828
template
class
QElement<2, 2>
;
829
template
class
QElement<2, 3>
;
830
template
class
QElement<2, 4>
;
831
832
template
class
QElement<3, 2>
;
833
template
class
QElement<3, 3>
;
834
template
class
QElement<3, 4>
;
835
836
#ifdef OOMPH_3_5_BRICK_FOR_MESHING_ONLY_NO_INTEGRATION
837
template
class
QElement<3, 5>
;
838
#endif
839
}
// namespace oomph
Qelements.h
s
static char t char * s
Definition
cfortran.h:568
i
cstr elem_len * i
Definition
cfortran.h:603
oomph::DShape
A Class for the derivatives of shape functions The class design is essentially the same as Shape,...
Definition
shape.h:278
oomph::FiniteElement::Node_location_tolerance
static const double Node_location_tolerance
Default value for the tolerance to be used when locating nodes via local coordinates.
Definition
elements.h:1378
oomph::FiniteElement::interpolated_x
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
Definition
elements.cc:3992
oomph::FiniteElement::node_pt
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition
elements.h:2179
oomph::FiniteElement::nodal_dimension
unsigned nodal_dimension() const
Return the required Eulerian dimension of the nodes in this element.
Definition
elements.h:2488
oomph::Node
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition
nodes.h:906
oomph::QElement
General QElement class.
Definition
Qelements.h:459
oomph::Shape
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
Definition
shape.h:76
oomph::TAdvectionDiffusionReactionElement
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
Definition
Tadvection_diffusion_reaction_elements.h:66
oomph::TAdvectionDiffusionReactionElement::output
void output(std::ostream &outfile)
Output function: x,y,u or x,y,z,u.
Definition
Tadvection_diffusion_reaction_elements.h:96
oomph::TAdvectionDiffusionReactionElement::TAdvectionDiffusionReactionElement
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
Definition
Tadvection_diffusion_reaction_elements.h:70
oomph
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
Definition
advection_diffusion_elements.cc:30