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
33namespace oomph
34{
35 //=================================================================
36 /// Constructor: Pass pointer to timestepper.
37 /// (defaults to (Steady) default timestepper defined in Mesh)
38 //=================================================================
39 template<class ELEMENT>
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>
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>
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);
138
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
233 zeta[0] =
234 xi_lo + (xi_hi - xi_lo) * double(i0) / double(n_node_1d - 1);
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;
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));
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;
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));
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
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;
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));
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;
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));
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(
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 {
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));
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(),
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>
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>
662 {
663#ifdef PARANOID
664 /// Pointer to algebraic element in lower body
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(
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;
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;
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;
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;
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>
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
902
903 // Get position of point on fish back near tail
904 zeta[0] = zeta_near_tail;
907
908 // Get position on symmetry line
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>
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
952 zeta[0] = zeta_wall;
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
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
char t
Definition cfortran.h:568
Base class for algebraic elements.
void setup_algebraic_node_update()
Setup algebraic update operation for all nodes (separate function because this task needs to be perfo...
void node_update_in_body(const unsigned &t, AlgebraicNode *&node_pt)
Algebraic update function for nodes in upper/lower body.
void node_update_in_fin(const unsigned &t, AlgebraicNode *&node_pt)
Algebraic update function for nodes in upper/lower fin.
Algebraic nodes are nodes with an algebraic positional update function.
Circle in 2D space.
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
void position(const Vector< double > &zeta, Vector< double > &r) const
Return the parametrised position of the FiniteElement in its incarnation as a GeomObject,...
Definition elements.h:2680
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
Fish shaped domain, represented by four MacroElements. Shape is parametrised by GeomObject that repre...
Definition fish_domain.h:43
Fish shaped mesh. The geometry is defined by the Domain object FishDomain.
Definition fish_mesh.h:54
FishMesh(TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to timestepper (defaults to the (Steady) default timestepper defined in Mes...
void build_mesh(TimeStepper *time_stepper_pt)
Build the mesh, using the geometric object identified by Back_pt.
bool Must_kill_fish_back
Do I need to kill the fish back geom object?
Definition fish_mesh.h:109
A geometric object is an object that provides a parametrised description of its shape via the functio...
unsigned ndim() const
Access function to # of Eulerian coordinates.
TimeStepper *& time_stepper_pt()
Access function for pointer to time stepper: Null if object is not time-dependent.
unsigned nlagrangian() const
Access function to # of Lagrangian coordinates.
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition mesh.h:477
const Vector< GeneralisedElement * > & element_pt() const
Return reference to the Vector of elements.
Definition mesh.h:464
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
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
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
void resize(const unsigned &n_value)
Resize the number of equations.
Definition nodes.cc:2167
An OomphLibError object which should be thrown when an run-time error is encountered....
void setup_adaptivity()
Setup all the information that's required for spatial adaptivity: Set pointers to macro elements and ...
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
void output(std::ostream &outfile)
Output function: x,y,u or x,y,z,u.
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...