hermite_element_quad_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// the mesh assembly functions for the hermite element quad mesh
27#ifndef OOMPH_HERMITE_ELEMENT_QUAD_MESH_TEMPLATE_HEADER
28#define OOMPH_HERMITE_ELEMENT_QUAD_MESH_TEMPLATE_HEADER
29
30#ifndef OOMPH_HERMITE_ELEMENT_QUAD_MESH_HEADER
31#error __FILE__ should only be included from hermite_element_quad_mesh.h.
32#endif // OOMPH_HERMITE_ELEMENT_QUAD_MESH_HEADER
33
34// Config header
35#ifdef HAVE_CONFIG_H
36#include <oomph-lib-config.h>
37#endif
38
39// oomph-lib includes
40
41namespace oomph
42{
43 //=============================================================================
44 /// sets the generalised position of the node (i.e. - x_i, dx_i/ds_0,
45 /// dx_i/ds_1 & d2x_i/ds_0ds_1 for i = 1,2). Takes the x,y coordinates of the
46 /// node from which its position can be determined.
47 //=============================================================================
48 template<class ELEMENT>
50 const unsigned& node_num_x, const unsigned& node_num_y, Node* node_pt)
51 {
52 // get the generalised macro element position of the node
54 generalised_macro_element_position_of_node(node_num_x, node_num_y, m_gen);
55
56 // copy the macro element coordinates
58 node_macro_position[0] = m_gen(0, 0);
59 node_macro_position[1] = m_gen(0, 1);
60
61 // get the global position of the nodes
63 Domain_pt->macro_element_pt(0)->macro_map(node_macro_position, x_node);
64
65 // get the jacobian of the mapping from macro to eulerian coordinates
67 Domain_pt->macro_element_pt(0)->assemble_macro_to_eulerian_jacobian(
69
70 // get the jacobian2 of the mapping from macro to eulerian coordinates
72 Domain_pt->macro_element_pt(0)->assemble_macro_to_eulerian_jacobian2(
74
75 // set x_0
76 node_pt->x_gen(0, 0) = x_node[0];
77 // set x_1
78 node_pt->x_gen(0, 1) = x_node[1];
79
80 // set dxi/dsj for i = 0,1 and j = 0,1
81 // computation : dxi/dsj = dm0/dsj*dxi/dm0 + dm1/dsj*dxi/dm1
82 for (unsigned i = 0; i < 2; i++)
83 {
84 for (unsigned j = 0; j < 2; j++)
85 {
86 node_pt->x_gen(j + 1, i) = m_gen(j + 1, 0) * jacobian_MX(0, i) +
87 m_gen(j + 1, 1) * jacobian_MX(1, i);
88 }
89 }
90
91 // set d2xi/ds0ds1 for i = 0,1
92 // d2xi/ds0ds1 = d2m0/ds0ds1*dxi/dm0
93 // + d2m1/ds0ds1*dxi/dm1
94 // + (dm0/ds1*d2xi/dm0^2 + dm1/ds1*d2xi/dm0dm1)*dm0/ds0
95 // + (dm0/ds1*d2xi/dm0dm1 + dm1/ds1*d2xi/dm1^2)*dm1/ds0
96 for (unsigned i = 0; i < 2; i++)
97 {
98 node_pt->x_gen(3, i) =
99 m_gen(3, 0) * jacobian_MX(0, i) + m_gen(3, 1) * jacobian_MX(1, i) +
100 m_gen(1, 0) * (m_gen(2, 0) * jacobian2_MX(0, i) +
101 m_gen(2, 1) * jacobian2_MX(2, i)) +
102 m_gen(1, 1) *
103 (m_gen(2, 0) * jacobian2_MX(2, i) + m_gen(2, 1) * jacobian2_MX(1, i));
104 }
105 }
106
107 //=============================================================================
108 /// sets the generalised position of the node (i.e. - x_i, dx_i/ds_0,
109 /// dx_i/ds_1 & d2x_i/ds_0ds_1 for i = 1,2). Takes the x,y coordinates of the
110 /// node from which its position can be determined. Also sets coordinates
111 /// on boundary vector for the node to be the generalised position of the node
112 /// in macro element coordinates
113 //=============================================================================
114 template<class ELEMENT>
116 const unsigned& node_num_x,
117 const unsigned& node_num_y,
118 BoundaryNode<Node>* node_pt)
119 {
120 // get the generalised macro element position of the node
121 DenseMatrix<double> m_gen(4, 2, 0.0);
122 generalised_macro_element_position_of_node(node_num_x, node_num_y, m_gen);
123
124 // copy the macro element coordinates
126 node_macro_position[0] = m_gen(0, 0);
127 node_macro_position[1] = m_gen(0, 1);
128
129 // get the global position of the nodes
131 Domain_pt->macro_element_pt(0)->macro_map(node_macro_position, x_node);
132
133 // get the jacobian of the mapping from macro to eulerian coordinates
135 Domain_pt->macro_element_pt(0)->assemble_macro_to_eulerian_jacobian(
137
138 // get the jacobian2 of the mapping from macro to eulerian coordinates
140 Domain_pt->macro_element_pt(0)->assemble_macro_to_eulerian_jacobian2(
142
143 // set x_0
144 node_pt->x_gen(0, 0) = x_node[0];
145 // set x_1
146 node_pt->x_gen(0, 1) = x_node[1];
147
148 // set dxi/dsj for i = 0,1 and j = 0,1
149 // computation : dxi/dsj = dm0/dsj*dxi/dm0 + dm1/dsj*dxi/dm1
150 for (unsigned i = 0; i < 2; i++)
151 {
152 for (unsigned j = 0; j < 2; j++)
153 {
154 node_pt->x_gen(j + 1, i) = m_gen(j + 1, 0) * jacobian_MX(0, i) +
155 m_gen(j + 1, 1) * jacobian_MX(1, i);
156 }
157 }
158
159 // set d2xi/ds0ds1 for i = 0,1
160 // d2xi/ds0ds1 = d2m0/ds0ds1*dxi/dm0
161 // + d2m1/ds0ds1*dxi/dm1
162 // + (dm0/ds1*d2xi/dm0^2 + dm1/ds1*d2xi/dm0dm1)*dm0/ds0
163 // + (dm0/ds1*d2xi/dm0dm1 + dm1/ds1*d2xi/dm1^2)*dm1/ds0
164 for (unsigned i = 0; i < 2; i++)
165 {
166 node_pt->x_gen(3, i) =
167 m_gen(3, 0) * jacobian_MX(0, i) + m_gen(3, 1) * jacobian_MX(1, i) +
168 m_gen(1, 0) * (m_gen(2, 0) * jacobian2_MX(0, i) +
169 m_gen(2, 1) * jacobian2_MX(2, i)) +
170 m_gen(1, 1) *
171 (m_gen(2, 0) * jacobian2_MX(2, i) + m_gen(2, 1) * jacobian2_MX(1, i));
172 }
173
174 // pass generalised macro element position to vector to pass to boundary
175 // node
176 for (unsigned b = 0; b < 4; b++)
177 {
178 if (node_pt->is_on_boundary(b))
179 {
181 boundary_macro_position[0] = m_gen(0, b % 2);
182 boundary_macro_position[1] = m_gen(1 + b % 2, b % 2);
184 }
185 }
186 }
187
188 //=============================================================================
189 /// computes the generalised position of the node at position
190 /// (node_num_x, node_num_y) in the macro element coordinate scheme.
191 /// index 0 of m_gen : 0 - m_i
192 /// 1 - dm_i/ds_0
193 /// 2 - dm_i/ds_1
194 /// 3 - d2m_i/ds_0ds_1 (where i is index 1 of m_gen)
195 //=============================================================================
196 template<class ELEMENT>
198 const unsigned& node_num_x,
199 const unsigned& node_num_y,
201 {
202 // obtain position of node
204 macro_coordinate_position(node_num_x, node_num_y, s_macro_N);
205
206 // set position of node in macro element coordinates
207 m_gen(0, 0) = s_macro_N[0];
208 m_gen(0, 1) = s_macro_N[1];
209
210 // if on left hand edge
211 if (node_num_x == 0)
212 {
213 // first dm0ds0 and dm1ds0
214
215 // get position of right node
217 macro_coordinate_position(node_num_x + 1, node_num_y, s_macro_R);
218
219 // compute dm0ds0
220 m_gen(1, 0) = 0.5 * (s_macro_R[0] - s_macro_N[0]);
221
222 // compute dm1ds0
223 m_gen(1, 1) = 0.5 * (s_macro_R[1] - s_macro_N[1]);
224
225 // now d2m0/ds0ds1 and d2m1/ds0ds1
226
227 // if lower left hand corner
228 if (node_num_y == 0)
229 {
230 // get position of upper right node
232 macro_coordinate_position(node_num_x + 1, node_num_y + 1, s_macro_UR);
233
234 // get position of upper node
236 macro_coordinate_position(node_num_x, node_num_y + 1, s_macro_U);
237
238 // compute d2m0/ds0ds1
239 m_gen(3, 0) =
240 0.5 * (0.5 * (s_macro_UR[0] - s_macro_U[0]) - m_gen(1, 0));
241
242 // compute d2m1/ds0ds1
243 m_gen(3, 1) =
244 0.5 * (0.5 * (s_macro_UR[1] - s_macro_U[1]) - m_gen(1, 1));
245 }
246
247 // else if upper left hand corner
248 else if (node_num_y == Nelement[1])
249 {
250 // get position of lower right node
252 macro_coordinate_position(node_num_x + 1, node_num_y - 1, s_macro_DR);
253
254 // get position of lower node
256 macro_coordinate_position(node_num_x, node_num_y - 1, s_macro_D);
257
258 // compute d2m0/ds0ds1
259 m_gen(3, 0) =
260 0.5 * (m_gen(1, 0) - 0.5 * (s_macro_DR[0] - s_macro_D[0]));
261
262 // compute d2m0/ds0ds1
263 m_gen(3, 1) =
264 0.5 * (m_gen(1, 1) - 0.5 * (s_macro_DR[1] - s_macro_D[1]));
265 }
266
267 // else left hand edge (not corner)
268 else
269 {
270 // get position of lower right node
272 macro_coordinate_position(node_num_x + 1, node_num_y - 1, s_macro_DR);
273
274 // get position of lower node
276 macro_coordinate_position(node_num_x, node_num_y - 1, s_macro_D);
277
278 // get position of upper right node
280 macro_coordinate_position(node_num_x + 1, node_num_y + 1, s_macro_UR);
281
282 // get position of upper node
284 macro_coordinate_position(node_num_x, node_num_y + 1, s_macro_U);
285
286 // compute d2m0/ds0ds1
287 m_gen(3, 0) =
288 0.5 * std::min(m_gen(1, 0) - 0.5 * (s_macro_DR[0] - s_macro_D[0]),
289 0.5 * (s_macro_UR[0] - s_macro_U[0]) - m_gen(1, 0));
290
291 // compute d2m1/ds0ds1
292 m_gen(3, 1) =
293 0.5 * std::min(m_gen(1, 1) - 0.5 * (s_macro_DR[1] - s_macro_D[1]),
294 0.5 * (s_macro_UR[1] - s_macro_U[1]) - m_gen(1, 1));
295 }
296 }
297
298 // if on right hand edge
299 else if (node_num_x == Nelement[0])
300 {
301 // first dm0ds0 and dm1ds0
302
303 // get position of left node
305 macro_coordinate_position(node_num_x - 1, node_num_y, s_macro_L);
306
307 // compute dm0ds0
308 m_gen(1, 0) = 0.5 * (s_macro_N[0] - s_macro_L[0]);
309
310 // compute dm1ds0
311 m_gen(1, 1) = 0.5 * (s_macro_N[1] - s_macro_L[1]);
312
313 // now d2m0/ds0ds1 and d2m1/ds0ds1
314
315 // if lower right hand corner
316 if (node_num_y == 0)
317 {
318 // get position of upper left node
320 macro_coordinate_position(node_num_x - 1, node_num_y + 1, s_macro_UL);
321
322 // get position of upper node
324 macro_coordinate_position(node_num_x, node_num_y + 1, s_macro_U);
325
326 // compute d2m0/ds0ds1
327 m_gen(3, 0) =
328 0.5 * (0.5 * (s_macro_U[0] - s_macro_UL[0]) - m_gen(1, 0));
329
330 // compute d2m1/ds0ds1
331 m_gen(3, 1) =
332 0.5 * (0.5 * (s_macro_U[1] - s_macro_UL[1]) - m_gen(1, 1));
333 }
334
335 // else if upper right hand corner
336 else if (node_num_y == Nelement[1])
337 {
338 // get position of lower left node
340 macro_coordinate_position(node_num_x - 1, node_num_y - 1, s_macro_DL);
341
342 // get position of lower node
344 macro_coordinate_position(node_num_x, node_num_y - 1, s_macro_D);
345
346 // compute d2m0/ds0ds1
347 m_gen(3, 0) =
348 0.5 * (m_gen(1, 0) - 0.5 * (s_macro_D[0] - s_macro_DL[0]));
349
350 // compute d2m0/ds0ds1
351 m_gen(3, 1) =
352 0.5 * (m_gen(1, 1) - 0.5 * (s_macro_D[1] - s_macro_DL[1]));
353 }
354
355 // else left hand edge (not corner)
356 else
357 {
358 // get position of lower left node
360 macro_coordinate_position(node_num_x - 1, node_num_y - 1, s_macro_DL);
361
362 // get position of lower node
364 macro_coordinate_position(node_num_x, node_num_y - 1, s_macro_D);
365
366 // get position of upper left node
368 macro_coordinate_position(node_num_x - 1, node_num_y + 1, s_macro_UL);
369
370 // get position of upper node
372 macro_coordinate_position(node_num_x, node_num_y + 1, s_macro_U);
373
374 // compute d2m0/ds0ds1
375 m_gen(3, 0) =
376 0.5 * std::min(m_gen(1, 0) - 0.5 * (s_macro_D[0] - s_macro_DL[0]),
377 0.5 * (s_macro_U[0] - s_macro_UL[0]) - m_gen(1, 0));
378
379 // compute d2m0/ds0ds1
380 m_gen(3, 1) =
381 0.5 * std::min(m_gen(1, 1) - 0.5 * (s_macro_D[1] - s_macro_DL[1]),
382 0.5 * (s_macro_U[1] - s_macro_UL[1]) - m_gen(1, 1));
383 }
384 }
385 //
386 else
387 {
388 // get position of left node
390 macro_coordinate_position(node_num_x - 1, node_num_y, s_macro_L);
391
392 // get position of right node
394 macro_coordinate_position(node_num_x + 1, node_num_y, s_macro_R);
395
396 // compute dm0/ds0
397 m_gen(1, 0) = 0.5 * std::min(s_macro_N[0] - s_macro_L[0],
398 s_macro_R[0] - s_macro_N[0]);
399
400 // compute dm1/ds0
402 node_space[0] = s_macro_N[1] - s_macro_L[1];
403 node_space[1] = s_macro_R[1] - s_macro_N[1];
404 // set nodal dof
405 if (node_space[0] > 0 && node_space[1] > 0)
406 m_gen(1, 1) = 0.5 * std::min(node_space[0], node_space[1]);
407 else if (node_space[0] < 0 && node_space[1] < 0)
408 m_gen(1, 1) = 0.5 * std::max(node_space[0], node_space[1]);
409 else
410 m_gen(1, 1) = 0;
411 }
412
413 // if node in lower row
414 if (node_num_y == 0)
415 {
416 // now dm0ds1 and dm1ds1
417
418 // get position of upper node
420 macro_coordinate_position(node_num_x, node_num_y + 1, s_macro_U);
421
422 // compute dm0ds1
423 m_gen(2, 0) = 0.5 * (s_macro_U[0] - s_macro_N[0]);
424
425 // compute dm1ds1
426 m_gen(2, 1) = 0.5 * (s_macro_U[1] - s_macro_N[1]);
427
428 // and if not corner node
429 if (node_num_x > 0 && node_num_x < Nelement[0])
430 {
431 // now dm0/ds0ds1 and dm1/ds0ds1
432
433 // get position of upper left node
435 macro_coordinate_position(node_num_x - 1, node_num_y + 1, s_macro_UL);
436
437 // get position of left node
439 macro_coordinate_position(node_num_x - 1, node_num_y, s_macro_L);
440
441 // get position of right node
443 macro_coordinate_position(node_num_x + 1, node_num_y, s_macro_R);
444
445 // get position of upper right node
447 macro_coordinate_position(node_num_x + 1, node_num_y + 1, s_macro_UR);
448
449 // set dm0/ds0ds1
450 m_gen(3, 0) =
451 0.5 * std::min(m_gen(2, 0) - 0.5 * (s_macro_UL[0] - s_macro_L[0]),
452 0.5 * (s_macro_UR[0] - s_macro_R[0]) - m_gen(2, 0));
453
454 // set dm1/ds0ds1
455 m_gen(3, 1) =
456 0.5 * std::min(m_gen(2, 1) - 0.5 * (s_macro_UL[1] - s_macro_L[1]),
457 0.5 * (s_macro_UR[1] - s_macro_R[1]) - m_gen(2, 1));
458 }
459 }
460
461 // else if node in upper row
462 else if (node_num_y == Nelement[1])
463 {
464 // now dm0ds1 and dm1ds1
465
466 // get position of lower node
468 macro_coordinate_position(node_num_x, node_num_y - 1, s_macro_D);
469
470 // compute dm0ds1
471 m_gen(2, 0) = 0.5 * (s_macro_N[0] - s_macro_D[0]);
472
473 // compute dm1ds1
474 m_gen(2, 1) = 0.5 * (s_macro_N[1] - s_macro_D[1]);
475
476 // and if not corner node
477 if (node_num_x > 0 && node_num_x < Nelement[0])
478 {
479 // now dm0/ds0ds1 and dm1/ds0ds1
480
481 // get position of lower left node
483 macro_coordinate_position(node_num_x - 1, node_num_y - 1, s_macro_DL);
484
485 // get position of left node
487 macro_coordinate_position(node_num_x - 1, node_num_y, s_macro_L);
488
489 // get position of right node
491 macro_coordinate_position(node_num_x + 1, node_num_y, s_macro_R);
492
493 // get position of lower right node
495 macro_coordinate_position(node_num_x + 1, node_num_y - 1, s_macro_DR);
496
497 // set dm0/ds0ds1
498 m_gen(3, 0) =
499 0.5 * std::min(m_gen(2, 0) - 0.5 * (s_macro_L[0] - s_macro_DL[0]),
500 0.5 * (s_macro_R[0] - s_macro_DR[0]) - m_gen(2, 0));
501
502 // set dm1/ds0ds1
503 m_gen(3, 1) =
504 0.5 * std::min(m_gen(2, 1) - 0.5 * (s_macro_L[1] - s_macro_DL[1]),
505 0.5 * (s_macro_R[1] - s_macro_DR[1]) - m_gen(2, 1));
506 }
507 }
508 else
509 {
510 // get position of upper node
512 macro_coordinate_position(node_num_x, node_num_y + 1, s_macro_U);
513
514 // get position of lower node
516 macro_coordinate_position(node_num_x, node_num_y - 1, s_macro_D);
517
518 // compute dm0/ds1
520 node_space[0] = s_macro_N[0] - s_macro_D[0];
521 node_space[1] = s_macro_U[0] - s_macro_N[0];
522 // set nodal coordinate
523 if (node_space[0] > 0 && node_space[1] > 0)
524 m_gen(2, 0) = 0.5 * std::min(node_space[0], node_space[1]);
525 else if (node_space[0] < 0 && node_space[1] < 0)
526 m_gen(2, 0) = 0.5 * std::max(node_space[0], node_space[1]);
527 else
528 m_gen(2, 0) = 0;
529
530 // compute dm1/ds1
531 m_gen(2, 1) = 0.5 * std::min(s_macro_N[1] - s_macro_D[1],
532 s_macro_U[1] - s_macro_N[1]);
533
534 // for interior nodes
535 if (node_num_x > 0 && node_num_x < Nelement[0])
536 {
537 // get position of left node
539 macro_coordinate_position(node_num_x - 1, node_num_y, s_macro_L);
540
541 // get position of upper left node
543 macro_coordinate_position(node_num_x - 1, node_num_y + 1, s_macro_UL);
544
545 // get position of upper right node
547 macro_coordinate_position(node_num_x + 1, node_num_y + 1, s_macro_UR);
548
549 // get position of right node
551 macro_coordinate_position(node_num_x + 1, node_num_y, s_macro_R);
552
553 // get position of lower right node
555 macro_coordinate_position(node_num_x + 1, node_num_y - 1, s_macro_DR);
556
557 // get position of lower left node
559 macro_coordinate_position(node_num_x - 1, node_num_y - 1, s_macro_DL);
560
562 // comute dm0/ds0ds1 wrt to node above and below
563 node_space[0] =
564 m_gen(1, 0) - 0.5 * std::min(s_macro_D[0] - s_macro_DL[0],
565 s_macro_DR[0] - s_macro_D[0]);
566 node_space[1] = 0.5 * std::min(s_macro_U[0] - s_macro_UL[0],
567 s_macro_UR[0] - s_macro_U[0]) -
568 m_gen(1, 0);
569 // set nodal dof
570 if (node_space[0] > 0 && node_space[1] > 0)
571 m_gen(3, 0) = 0.5 * std::min(node_space[0], node_space[1]);
572 else if (node_space[0] < 0 && node_space[1] < 0)
573 m_gen(3, 0) = 0.5 * std::max(node_space[0], node_space[1]);
574 else
575 m_gen(3, 0) = 0;
576
577 // comute dm1/ds0ds1 wrt node left and right
578 node_space[0] =
579 m_gen(2, 1) - 0.5 * std::min(s_macro_L[0] - s_macro_DL[0],
580 s_macro_UL[0] - s_macro_L[0]);
581 node_space[1] = 0.5 * std::min(s_macro_R[0] - s_macro_DR[0],
582 s_macro_UR[0] - s_macro_R[0]) -
583 m_gen(2, 1);
584 // set nodal dof
585 if (node_space[0] > 0 && node_space[1] > 0)
586 m_gen(3, 1) = 0.5 * std::min(node_space[0], node_space[1]);
587 else if (node_space[0] < 0 && node_space[1] < 0)
588 m_gen(3, 1) = 0.5 * std::max(node_space[0], node_space[1]);
589 else
590 m_gen(3, 1) = 0;
591 }
592 }
593 }
594
595 //=============================================================================
596 /// Generic mesh construction function to build the mesh
597 //=============================================================================
598 template<class ELEMENT>
600 {
601 // Mesh can only be built with 2D QHermiteElements.
602 MeshChecker::assert_geometric_element<QHermiteElementBase, ELEMENT>(2);
603
604 // Set the number of boundaries
605 set_nboundary(4);
606
607 // Allocate the store for the elements
608 Element_pt.resize(Nelement[0] * Nelement[1]);
609
610 // Allocate the memory for the first element
611 Element_pt[0] = new ELEMENT;
612 finite_element_pt(0)->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
613
614 // Can now allocate the store for the nodes
615 Node_pt.resize((1 + Nelement[0]) * (1 + Nelement[1]));
616
617 // Set up geometrical data
618 unsigned long node_count = 0;
619
620 // Now assign the topology
621 // Boundaries are numbered 0 1 2 3 from the bottom proceeding anticlockwise
622 // Pinned value are denoted by an integer value 1
623 // Thus if a node is on two boundaries, ORing the values of the
624 // boundary conditions will give the most restrictive case (pinning)
625
626 // we first create the lowest row of elements
627 // ##########################################
628 // ##########################################
629
630 // FIRST ELEMENT - lower left hand corner
631 // **************************************
632
633 // LOWER LEFT HAND NODE
634
635 // Set the corner node
636 // Allocate memory for the node
638 finite_element_pt(0)->construct_boundary_node(0, time_stepper_pt);
639
640 // Push the node back onto boundaries
641 add_boundary_node(0, Node_pt[node_count]);
642 add_boundary_node(3, Node_pt[node_count]);
643
644 // set the position of the boundary node
645 set_position_of_boundary_node(
646 0, 0, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
647
648 // Increment the node number
649 node_count++;
650
651 // LOWER RIGHT HAND NODE
652
653 // Allocate memory for the node
655 finite_element_pt(0)->construct_boundary_node(1, time_stepper_pt);
656
657 // Push the node back onto boundaries
658 add_boundary_node(0, Node_pt[node_count]);
659
660 // If we only have one column then the RHS node is on the right boundary
661 if (Nelement[0] == 1)
662 {
663 add_boundary_node(1, Node_pt[node_count]);
664 }
665
666 // set the position of the node
667 set_position_of_boundary_node(
668 1, 0, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
669
670 // Increment the node number
671 node_count++;
672
673 // UPPER LEFT HAND NODE
674
675 // Allocate memory for the node
677 finite_element_pt(0)->construct_boundary_node(2, time_stepper_pt);
678
679 // Push the node back onto boundaries
680 add_boundary_node(3, Node_pt[node_count]);
681
682 // If we only have one row, then the top node is on the top boundary
683 if (Nelement[1] == 1)
684 {
685 add_boundary_node(2, Node_pt[node_count]);
686 }
687
688 // set the position of the boundary node
689 set_position_of_boundary_node(
690 0, 1, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
691
692 // Increment the node number
693 node_count++;
694
695 // UPPER RIGHT NODE
696
697 // Allocate the memory for the node
699 finite_element_pt(0)->construct_node(3, time_stepper_pt);
700
701 // If we only have one column then the RHS node is on the right boundary
702 if (Nelement[0] == 1)
703 {
704 add_boundary_node(1, Node_pt[node_count]);
705 }
706 // If we only have one row, then the top node is on the top boundary
707 if (Nelement[1] == 1)
708 {
709 add_boundary_node(2, Node_pt[node_count]);
710 }
711
712 // if the node is a boundary node
713 if (Nelement[0] == 1 || Nelement[1] == 1)
714 {
715 // set the position of the boundary node
716 set_position_of_boundary_node(
717 1, 1, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
718 }
719 else
720 {
721 // set the position of the node
722 set_position_of_node(1, 1, Node_pt[node_count]);
723 }
724
725 // Increment the node number
726 node_count++;
727
728 // END OF FIRST ELEMENT
729
730 // CENTRE OF FIRST ROW OF ELEMENTS
731 // *******************************
732
733 // Now loop over the first row of elements, apart from final element
734 for (unsigned j = 1; j < (Nelement[0] - 1); j++)
735 {
736 // Allocate memory for new element
737 Element_pt[j] = new ELEMENT;
738 finite_element_pt(j)->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
739
740 // LOWER LEFT NODE
741
742 // lower left hand node column of nodes is same as lower right hand node
743 // in neighbouring element
744 finite_element_pt(j)->node_pt(0) = finite_element_pt(j - 1)->node_pt(1);
745
746 // LOWER RIGHT NODE
747
748 // Allocate memory for the nodes
750 finite_element_pt(j)->construct_boundary_node(1, time_stepper_pt);
751
752 // Push the node back onto boundaries
753 add_boundary_node(0, Node_pt[node_count]);
754
755 // set the position of the boundary node
756 set_position_of_boundary_node(
757 j + 1, 0, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
758
759 // Increment the node number
760 node_count++;
761
762 // UPPER LEFT NODE
763
764 // First column of nodes is same as neighbouring element
765 finite_element_pt(j)->node_pt(2) = finite_element_pt(j - 1)->node_pt(3);
766
767 // UPPER RIGHT NODE
768
769 // Allocate memory for the nodes
771 finite_element_pt(j)->construct_node(3, time_stepper_pt);
772
773 // If we only have one row, then the top node is on the top boundary
774 if (Nelement[0] == 1)
775 {
776 add_boundary_node(2, Node_pt[node_count]);
777 }
778
779 // set the position of the (boundary) node
780 if (Nelement[0] == 1)
781 set_position_of_boundary_node(
782 j + 1, 1, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
783 else
784 set_position_of_node(j + 1, 1, Node_pt[node_count]);
785
786 // Increment the node number
787 node_count++;
788 }
789
790 // FINAL ELEMENT IN FIRST ROW (lower right corner element)
791 // **************************
792
793 // Only allocate if there is more than one element in the row
794 if (Nelement[0] > 1)
795 {
796 // Allocate memory for element
797 Element_pt[Nelement[0] - 1] = new ELEMENT;
798 finite_element_pt(Nelement[0] - 1)
799 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
800
801 // LOWER LEFT NODE
802
803 // First column of nodes is same as neighbouring element
804 finite_element_pt(Nelement[0] - 1)->node_pt(0) =
805 finite_element_pt(Nelement[0] - 2)->node_pt(1);
806
807 // LOWER RIGHT NODE
808
809 // If we have an Xperiodic mesh then the final node is the first node in
810 // the row
811 if (Xperiodic == true)
812 {
813 // Note that this is periodic in the x-direction
814 finite_element_pt(Nelement[0] - 1)->node_pt(1) =
815 finite_element_pt(0)->node_pt(0);
816 }
817 // Otherwise it's a new final node
818 else
819 {
820 // Allocate memory for the node
821 Node_pt[node_count] = finite_element_pt(Nelement[0] - 1)
822 ->construct_boundary_node(1, time_stepper_pt);
823 }
824
825 // Push the node back onto boundaries
826 add_boundary_node(0, Node_pt[node_count]);
827 add_boundary_node(1, Node_pt[node_count]);
828
829 // set the position of the boundary node
830 set_position_of_boundary_node(
831 Nelement[0], 0, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
832
833 // if not periodic mesh
834 if (Xperiodic == false)
835 {
836 node_count++;
837 }
838
839 // UPPER LEFT NODE
840
841 // same as upper right node in element to left
842 finite_element_pt(Nelement[0] - 1)->node_pt(2) =
843 finite_element_pt(Nelement[0] - 2)->node_pt(3);
844
845 // If we only have one row, then the top node is on the top boundary
846 if (Nelement[1] == 1)
847 {
848 add_boundary_node(2, Node_pt[node_count]);
849 }
850
851 // set the position of the (boundary) node
852 if (Nelement[1] == 1)
853 set_position_of_boundary_node(
854 Nelement[0] - 1,
855 1,
856 dynamic_cast<BoundaryNode<Node>*>(
857 finite_element_pt(Nelement[0] - 2)->node_pt(3)));
858
859 // UPPER RIGHT NODE
860
861 // If we have an Xperiodic mesh then the nodes in the final column are
862 // those in the first column
863 if (Xperiodic == true)
864 {
865 // Note that these are periodic in the x-direction
866 finite_element_pt(Nelement[0] - 1)->node_pt(3) =
867 finite_element_pt(0)->node_pt(2);
868 }
869 // Otherwise we need new nodes for the final column
870 else
871 {
872 // Allocate memory for node
873 Node_pt[node_count] = finite_element_pt(Nelement[0] - 1)
874 ->construct_boundary_node(3, time_stepper_pt);
875 }
876
877 // If we only have one row, then the top node is on the top boundary
878 if (Nelement[1] == 1)
879 {
880 add_boundary_node(2, Node_pt[node_count]);
881 }
882
883 // boundary 1 node
884 add_boundary_node(1, Node_pt[node_count]);
885
886 // set the position of the boundary node
887 set_position_of_boundary_node(
888 Nelement[0], 1, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
889
890 // Increment the node number
891 if (Xperiodic == false)
892 {
893 // Increment the node number
894 node_count++;
895 }
896 }
897
898 // now create all remaining central rows
899 // #####################################
900 // #####################################
901
902 // Loop over remaining element rows in the mesh
903 for (unsigned i = 1; i < (Nelement[1] - 1); i++)
904 {
905 // set the first element in the row
906 // ********************************
907
908 // Allocate memory for element
909 Element_pt[Nelement[0] * i] = new ELEMENT;
910 finite_element_pt(Nelement[0] * i)
911 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
912
913 // The first row of nodes is copied from the element below
914 for (unsigned l = 0; l < 2; l++)
915 {
916 finite_element_pt(Nelement[0] * i)->node_pt(l) =
917 finite_element_pt(Nelement[0] * (i - 1))->node_pt(2 + l);
918 }
919
920 // UPPER LEFT HAND NODE
921
922 // Allocate memory for node
923 Node_pt[node_count] = finite_element_pt(Nelement[0] * i)
924 ->construct_boundary_node(2, time_stepper_pt);
925
926 // Push the node back onto boundaries
927 add_boundary_node(3, Node_pt[node_count]);
928
929 // set the position of the boundary node
930 set_position_of_boundary_node(
931 0, i + 1, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
932
933 // Increment the node number
934 node_count++;
935
936 // UPPER RIGHT HAND NODE
937
938 // If we only have one column, the node could be on the boundary
939 if (Nelement[0] == 1)
940 {
941 Node_pt[node_count] = finite_element_pt(Nelement[0] * i)
942 ->construct_boundary_node(3, time_stepper_pt);
943 }
944 else
945 {
946 Node_pt[node_count] = finite_element_pt(Nelement[0] * i)
947 ->construct_node(3, time_stepper_pt);
948 }
949
950 // If we only have one column then the RHS node is on the
951 // right boundary
952 if (Nelement[0] == 1)
953 {
954 add_boundary_node(1, Node_pt[node_count]);
955 }
956
957 // set the position of the (boundary) node
958 if (Nelement[0] == 1)
959 set_position_of_boundary_node(
960 1, i + 1, dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
961 else
962 set_position_of_node(1, i + 1, Node_pt[node_count]);
963
964 // Increment the node number
965 node_count++;
966
967 // loop over central elements in row
968 // *********************************
969
970 // Now loop over the rest of the elements in the row, apart from the last
971 for (unsigned j = 1; j < (Nelement[0] - 1); j++)
972 {
973 // Allocate memory for new element
974 Element_pt[Nelement[0] * i + j] = new ELEMENT;
975 finite_element_pt(Nelement[0] * i + j)
976 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
977
978 // LOWER LEFT AND RIGHT NODES
979
980 // The first row is copied from the lower element
981 for (unsigned l = 0; l < 2; l++)
982 {
983 finite_element_pt(Nelement[0] * i + j)->node_pt(l) =
984 finite_element_pt(Nelement[0] * (i - 1) + j)->node_pt(2 + l);
985 }
986
987 // UPPER LEFT NODE
988
989 // First column is same as neighbouring element
990 finite_element_pt(Nelement[0] * i + j)->node_pt(2) =
991 finite_element_pt(Nelement[0] * i + (j - 1))->node_pt(3);
992
993 // UPPER RIGHT NODE
994
995 // Allocate memory for the nodes
996 Node_pt[node_count] = finite_element_pt(Nelement[0] * i + j)
997 ->construct_node(3, time_stepper_pt);
998
999 // set position of node
1000 set_position_of_node(j + 1, i + 1, Node_pt[node_count]);
1001
1002 // Increment the node number
1003 node_count++;
1004 }
1005
1006 // final element in row
1007 // ********************
1008
1009 // Only if there is more than one column
1010 if (Nelement[0] > 1)
1011 {
1012 // Allocate memory for element
1013 Element_pt[Nelement[0] * i + Nelement[0] - 1] = new ELEMENT;
1014 finite_element_pt(Nelement[0] * i + Nelement[0] - 1)
1015 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
1016
1017 // LOWER LEFT AND RIGHT NODES
1018
1019 // The first row is copied from the lower element
1020 for (unsigned l = 0; l < 2; l++)
1021 {
1022 finite_element_pt(Nelement[0] * i + Nelement[0] - 1)->node_pt(l) =
1023 finite_element_pt(Nelement[0] * (i - 1) + Nelement[0] - 1)
1024 ->node_pt(2 + l);
1025 }
1026
1027 // UPPER LEFT NODE
1028
1029 // First node is same as neighbouring element
1030 finite_element_pt(Nelement[0] * i + Nelement[0] - 1)->node_pt(2) =
1031 finite_element_pt(Nelement[0] * i + Nelement[0] - 2)->node_pt(3);
1032
1033 // UPPER RIGHT NODE
1034
1035 // If we have an Xperiodic mesh then this node is the same
1036 // as the first node
1037 if (Xperiodic == true)
1038 {
1039 // Allocate memory for node, periodic in x-direction
1040 finite_element_pt(Nelement[0] * i + Nelement[0] - 1)->node_pt(3) =
1041 finite_element_pt(Nelement[0] * i)->node_pt(2);
1042 }
1043 // Otherwise allocate memory for a new node
1044 else
1045 {
1047 finite_element_pt(Nelement[0] * i + Nelement[0] - 1)
1048 ->construct_boundary_node(3, time_stepper_pt);
1049 }
1050
1051 // Push the node back onto boundaries
1052 add_boundary_node(1, Node_pt[node_count]);
1053
1054 // set position of boundary node
1055 set_position_of_boundary_node(
1056 Nelement[0],
1057 i + 1,
1058 dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
1059
1060 // Increment the node number
1061 node_count++;
1062 }
1063 }
1064
1065 // final element row
1066 // #################
1067 // #################
1068
1069 // only if there is more than one row
1070 if (Nelement[1] > 1)
1071 {
1072 // first element in upper row (upper left corner)
1073 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1074
1075 // Allocate memory for element
1076 Element_pt[Nelement[0] * (Nelement[1] - 1)] = new ELEMENT;
1077 finite_element_pt(Nelement[0] * (Nelement[1] - 1))
1078 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
1079
1080 // LOWER LEFT AND LOWER RIGHT NODES
1081
1082 // The first row of nodes is copied from the element below
1083 for (unsigned l = 0; l < 2; l++)
1084 {
1085 finite_element_pt(Nelement[0] * (Nelement[1] - 1))->node_pt(l) =
1086 finite_element_pt(Nelement[0] * (Nelement[1] - 2))->node_pt(2 + l);
1087 }
1088
1089 // UPPER LEFT NODE
1090
1091 // Allocate memory for node
1092 Node_pt[node_count] = finite_element_pt(Nelement[0] * (Nelement[1] - 1))
1093 ->construct_boundary_node(2, time_stepper_pt);
1094
1095 // Push the node back onto boundaries
1096 add_boundary_node(2, Node_pt[node_count]);
1097 add_boundary_node(3, Node_pt[node_count]);
1098
1099 // set position of boundary node
1100 set_position_of_boundary_node(
1101 0, Nelement[1], dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
1102
1103 // Increment the node number
1104 node_count++;
1105
1106 // UPPER RIGHT NODE
1107
1108 // Allocate memory for the node
1109 Node_pt[node_count] = finite_element_pt(Nelement[0] * (Nelement[1] - 1))
1110 ->construct_boundary_node(3, time_stepper_pt);
1111
1112 // Push the node back onto boundaries
1113 add_boundary_node(2, Node_pt[node_count]);
1114
1115 // set position of boundary node
1116 set_position_of_boundary_node(
1117 1, Nelement[1], dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
1118
1119 // Increment the node number
1120 node_count++;
1121
1122 // loop over central elements of upper element row
1123 // ***********************************************
1124
1125 // Now loop over the rest of the elements in the row, apart from the last
1126 for (unsigned j = 1; j < (Nelement[0] - 1); j++)
1127 {
1128 // Allocate memory for element
1129 Element_pt[Nelement[0] * (Nelement[1] - 1) + j] = new ELEMENT;
1130 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + j)
1131 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
1132
1133 // LOWER LEFT AND LOWER RIGHT NODES
1134
1135 // The first row is copied from the lower element
1136 for (unsigned l = 0; l < 2; l++)
1137 {
1138 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + j)->node_pt(l) =
1139 finite_element_pt(Nelement[0] * (Nelement[1] - 2) + j)
1140 ->node_pt(2 + l);
1141 }
1142
1143 // UPPER LEFT NODE
1144
1145 // First column is same as neighbouring element
1146 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + j)->node_pt(2) =
1147 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + (j - 1))
1148 ->node_pt(3);
1149
1150 // UPPER RIGHT NODE
1151
1152 // Allocate memory for node
1154 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + j)
1155 ->construct_boundary_node(3, time_stepper_pt);
1156
1157 // Push the node back onto boundaries
1158 add_boundary_node(2, Node_pt[node_count]);
1159
1160 // set position of boundary node
1161 set_position_of_boundary_node(
1162 j + 1,
1163 Nelement[1],
1164 dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
1165
1166 // Increment the node number
1167 node_count++;
1168
1169 } // End of loop over central elements in row
1170
1171 // final element in row (upper right corner)
1172 // *****************************************
1173
1174 // Only if there is more than one column
1175 if (Nelement[0] > 1)
1176 {
1177 // Allocate memory for element
1178 Element_pt[Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 1] =
1179 new ELEMENT;
1180 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 1)
1181 ->set_macro_elem_pt(Domain_pt->macro_element_pt(0));
1182
1183 // LOWER LEFT AND LOWER RIGHT NODES
1184
1185 // The first row is copied from the lower element
1186 for (unsigned l = 0; l < 2; l++)
1187 {
1188 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 1)
1189 ->node_pt(l) =
1190 finite_element_pt(Nelement[0] * (Nelement[1] - 2) + Nelement[0] - 1)
1191 ->node_pt(2 + l);
1192 }
1193
1194 // UPPER LEFT NODE
1195
1196 // First column is same as neighbouring element
1197 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 1)
1198 ->node_pt(2) =
1199 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 2)
1200 ->node_pt(3);
1201
1202 // UPPER RIGHT NODE
1203
1204 // If we have an Xperiodic mesh, the node must be copied from
1205 // the first column
1206 if (Xperiodic == true)
1207 {
1208 // Allocate memory for node, periodic in x-direction
1209 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 1)
1210 ->node_pt(3) =
1211 finite_element_pt(Nelement[0] * (Nelement[1] - 1))->node_pt(2);
1212 }
1213 // Otherwise, allocate new memory for node
1214 else
1215 {
1217 finite_element_pt(Nelement[0] * (Nelement[1] - 1) + Nelement[0] - 1)
1218 ->construct_boundary_node(3, time_stepper_pt);
1219 }
1220
1221 // Push the node back onto boundaries
1222 add_boundary_node(1, Node_pt[node_count]);
1223 add_boundary_node(2, Node_pt[node_count]);
1224
1225 // set position of boundary node
1226 set_position_of_boundary_node(
1227 Nelement[0],
1228 Nelement[1],
1229 dynamic_cast<BoundaryNode<Node>*>(Node_pt[node_count]));
1230
1231 // Increment the node number
1232 node_count++;
1233 }
1234 }
1235
1236 // Setup boundary element lookup schemes
1237 setup_boundary_element_info();
1238 }
1239
1240 //=============================================================================
1241 /// Setup lookup schemes which establish which elements are located
1242 /// next to which boundaries (Doc to outfile if it's open). Specific version
1243 /// for HermiteQuadMesh to ensure that the order of the elements in
1244 /// Boundary_element_pt matches the actual order along the boundary. This is
1245 /// required when hijacking the BiharmonicElement to apply the
1246 /// BiharmonicFluidBoundaryElement in
1247 /// BiharmonicFluidProblem::impose_traction_free_edge(...)
1248 //================================================================
1249 template<class ELEMENT>
1251 std::ostream& outfile)
1252 {
1253 bool doc = false;
1254 if (outfile) doc = true;
1255
1256 // Number of boundaries
1257 unsigned nbound = nboundary();
1258
1259 // Wipe/allocate storage for arrays
1260 Boundary_element_pt.clear();
1261 Face_index_at_boundary.clear();
1262 Boundary_element_pt.resize(nbound);
1263 Face_index_at_boundary.resize(nbound);
1264
1265 // Temporary vector of sets of pointers to elements on the boundaries:
1268
1269 // Matrix map for working out the fixed local coord for elements on boundary
1271
1272 // Loop over elements
1273 //-------------------
1274 unsigned nel = nelement();
1275 for (unsigned e = 0; e < nel; e++)
1276 {
1277 // Get pointer to element
1278 FiniteElement* fe_pt = finite_element_pt(e);
1279
1280 if (doc) outfile << "Element: " << e << " " << fe_pt << std::endl;
1281
1282 // Only include 2D elements! Some meshes contain interface elements too.
1283 if (fe_pt->dim() == 2)
1284 {
1285 // Loop over the element's nodes and find out which boundaries they're
1286 // on
1287 // ----------------------------------------------------------------------
1288 unsigned nnode_1d = fe_pt->nnode_1d();
1289
1290 // Loop over nodes in order
1291 for (unsigned i0 = 0; i0 < nnode_1d; i0++)
1292 {
1293 for (unsigned i1 = 0; i1 < nnode_1d; i1++)
1294 {
1295 // Local node number
1296 unsigned j = i0 + i1 * nnode_1d;
1297
1298 // Get pointer to vector of boundaries that this
1299 // node lives on
1300 std::set<unsigned>* boundaries_pt = 0;
1302
1303 // If the node lives on some boundaries....
1304 if (boundaries_pt != 0)
1305 {
1306 // Loop over boundaries
1307 // unsigned nbound=(*boundaries_pt).size();
1308 for (std::set<unsigned>::iterator it = boundaries_pt->begin();
1309 it != boundaries_pt->end();
1310 ++it)
1311 {
1312 // Add pointer to finite element to set for the appropriate
1313 // boundary -- storage in set makes sure we don't count elements
1314 // multiple times
1316 bool temp_flag = true;
1317 for (unsigned temp_i = 0; temp_i < temp_size; temp_i++)
1318 {
1320 temp_flag = false;
1321 }
1322 if (temp_flag)
1324
1325 // For the current element/boundary combination, create
1326 // a vector that stores an indicator which element boundaries
1327 // the node is located (boundary_identifier=-/+1 for nodes
1328 // on the left/right boundary; boundary_identifier=-/+2 for
1329 // nodes on the lower/upper boundary. We determine these indices
1330 // for all corner nodes of the element and add them to a vector
1331 // to a vector. This allows us to decide which face of the
1332 // element coincides with the boundary since the (quad!) element
1333 // must have exactly two corner nodes on the boundary.
1334 if (boundary_identifier(*it, fe_pt) == 0)
1335 {
1337 }
1338
1339 // Are we at a corner node?
1340 if (((i0 == 0) || (i0 == nnode_1d - 1)) &&
1341 ((i1 == 0) || (i1 == nnode_1d - 1)))
1342 {
1343 // Create index to represent position relative to s_0
1345 .push_back(1 * (2 * i0 / (nnode_1d - 1) - 1));
1346
1347 // Create index to represent position relative to s_1
1349 .push_back(2 * (2 * i1 / (nnode_1d - 1) - 1));
1350 }
1351 }
1352 }
1353 // else
1354 // {
1355 // oomph_info << "...does not live on any boundaries " <<
1356 // std::endl;
1357 // }
1358 }
1359 }
1360 }
1361 // else
1362 //{
1363 // oomph_info << "Element " << e << " does not qualify" << std::endl;
1364 //}
1365 }
1366
1367 // Now copy everything across into permanent arrays
1368 //-------------------------------------------------
1369
1370 // Note: vector_of_boundary_element_pt contains all elements
1371 // that have (at least) one corner node on a boundary -- can't copy
1372 // them across into Boundary_element_pt
1373 // yet because some of them might have only one node on the
1374 // the boundary in which case they don't qualify as
1375 // boundary elements!
1376
1377 // Loop over boundaries
1378 //---------------------
1379 for (unsigned i = 0; i < nbound; i++)
1380 {
1381 // Number of elements on this boundary (currently stored in a set)
1383
1384 // Allocate storage for the coordinate identifiers
1385 Face_index_at_boundary[i].resize(nel);
1386
1387 // Loop over elements that have at least one corner node on this boundary
1388 //-----------------------------------------------------------------------
1389 unsigned e_count = 0;
1393 it++)
1394 {
1395 // Recover pointer to element
1397
1398 // Initialise count for boundary identiers (-2,-1,1,2)
1399 std::map<int, unsigned> count;
1400
1401 // Loop over coordinates
1402 for (unsigned ii = 0; ii < 2; ii++)
1403 {
1404 // Loop over upper/lower end of coordinates
1405 for (int sign = -1; sign < 3; sign += 2)
1406 {
1407 count[(ii + 1) * sign] = 0;
1408 }
1409 }
1410
1411 // Loop over boundary indicators for this element/boundary
1412 unsigned n_indicators = (*boundary_identifier(i, fe_pt)).size();
1413 for (unsigned j = 0; j < n_indicators; j++)
1414 {
1416 }
1417 delete boundary_identifier(i, fe_pt);
1418
1419 // Determine the correct boundary indicator by checking that it
1420 // occurs twice (since two corner nodes of the element's boundary
1421 // need to be located on the domain boundary
1422 int indicator = -10;
1423
1424 // Check that we're finding exactly one boundary indicator
1425 bool found = false;
1426
1427 // Loop over coordinates
1428 for (unsigned ii = 0; ii < 2; ii++)
1429 {
1430 // Loop over upper/lower end of coordinates
1431 for (int sign = -1; sign < 3; sign += 2)
1432 {
1433 if (count[(ii + 1) * sign] == 2)
1434 {
1435 // Check that we haven't found multiple boundaries
1436 if (found)
1437 {
1438 std::string error_message =
1439 "Trouble: Multiple boundary identifiers!\n";
1440 error_message += "Elements should only have at most 2 corner ";
1441 error_message += "nodes on any one boundary.\n";
1442
1443 throw OomphLibError(error_message,
1446 }
1447 found = true;
1448 indicator = (ii + 1) * sign;
1449 }
1450 }
1451 }
1452
1453 // Element has exactly two corner nodes on boundary
1454 if (found)
1455 {
1456 // Add to permanent storage
1457 Boundary_element_pt[i].push_back(fe_pt);
1458
1459 // Now convert boundary indicator into information required
1460 // for FaceElements
1461 switch (indicator)
1462 {
1463 case -2:
1464
1465 // s_1 is fixed at -1.0:
1466 Face_index_at_boundary[i][e_count] = -2;
1467 break;
1468
1469 case -1:
1470
1471 // s_0 is fixed at -1.0:
1472 Face_index_at_boundary[i][e_count] = -1;
1473 break;
1474
1475 case 1:
1476
1477 // s_0 is fixed at 1.0:
1478 Face_index_at_boundary[i][e_count] = 1;
1479 break;
1480
1481 case 2:
1482
1483 // s_1 is fixed at 1.0:
1484 Face_index_at_boundary[i][e_count] = 2;
1485 break;
1486
1487 default:
1488
1489 throw OomphLibError("Never get here",
1492 }
1493
1494 // Increment counter
1495 e_count++;
1496 }
1497 }
1498 }
1499
1500 // Doc?
1501 //-----
1502 if (doc)
1503 {
1504 // Loop over boundaries
1505 for (unsigned i = 0; i < nbound; i++)
1506 {
1507 unsigned nel = Boundary_element_pt[i].size();
1508 outfile << "Boundary: " << i << " is adjacent to " << nel << " elements"
1509 << std::endl;
1510
1511 // Loop over elements on given boundary
1512 for (unsigned e = 0; e < nel; e++)
1513 {
1514 FiniteElement* fe_pt = Boundary_element_pt[i][e];
1515 outfile << "Boundary element:" << fe_pt
1516 << " Face index of element along boundary is "
1517 << Face_index_at_boundary[i][e] << std::endl;
1518 }
1519 }
1520 }
1521
1522 // Lookup scheme has now been setup yet
1523 Lookup_for_elements_next_boundary_is_setup = true;
1524 }
1525} // namespace oomph
1526
1527#endif
e
Definition cfortran.h:571
cstr elem_len * i
Definition cfortran.h:603
A general Finite Element class.
Definition elements.h:1317
virtual void set_macro_elem_pt(MacroElement *macro_elem_pt)
Set pointer to macro element – can be overloaded in derived elements to perform additional tasks.
Definition elements.h:1876
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition elements.h:2615
Node ** Node_pt
Storage for pointers to the nodes in the element.
Definition elements.h:1323
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
virtual unsigned nnode_1d() const
Return the number of nodes along one edge of the element Default is to return zero — must be overload...
Definition elements.h:2222
TimeStepper *& time_stepper_pt()
Access function for pointer to time stepper: Null if object is not time-dependent.
void generalised_macro_element_position_of_node(const unsigned &node_num_x, const unsigned &node_num_y, DenseMatrix< double > &m_gen)
computes the generalised position of the node at position (node_num_x, node_num_y) in the macro eleme...
virtual void setup_boundary_element_info()
Setup lookup schemes which establish whic elements are located next to mesh's boundaries (wrapper to ...
void set_position_of_boundary_node(const unsigned &node_num_x, const unsigned &node_num_y, BoundaryNode< Node > *node_pt)
sets the generalised position of the node (i.e. - x_i, dx_i/ds_0, dx_i/ds_1 & d2x_i/ds_0ds_1 for i = ...
void set_position_of_node(const unsigned &node_num_x, const unsigned &node_num_y, Node *node_pt)
sets the generalised position of the node (i.e. - x_i, dx_i/ds_0, dx_i/ds_1 & d2x_i/ds_0ds_1 for i = ...
virtual void build_mesh(TimeStepper *time_stepper_pt)
Generic mesh construction function to build the mesh.
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
virtual bool is_on_boundary() const
Test whether the Node lies on a boundary. The "bulk" Node cannot lie on a boundary,...
Definition nodes.h:1373
virtual void get_boundaries_pt(std::set< unsigned > *&boundaries_pt)
Return a pointer to set of mesh boundaries that this node occupies; this will be overloaded by Bounda...
Definition nodes.h:1365
virtual void set_coordinates_on_boundary(const unsigned &b, const unsigned &k, const Vector< double > &boundary_zeta)
Set the vector of the k-th generalised boundary coordinates on mesh boundary b. Broken virtual interf...
Definition nodes.cc:2394
void resize(const unsigned &n_value)
Resize the number of equations.
Definition nodes.cc:2167
double & x_gen(const unsigned &k, const unsigned &i)
Reference to the generalised position x(k,i). ‘Type’: k; Coordinate direction: i.
Definition nodes.h:1126
An OomphLibError object which should be thrown when an run-time error is encountered....
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition Vector.h:58
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).