hp_refineable_elements.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 hp-refineable elements
27
28// oomph-lib includes
29#include "algebraic_elements.h"
32// #include "shape.h"
33
34namespace oomph
35{
36 ////////////////////////////////////////////////////////////////
37 // 1D PRefineableQElements
38 ////////////////////////////////////////////////////////////////
39
40 /// Get local coordinates of node j in the element; vector sets its own size
41 template<unsigned INITIAL_NNODE_1D>
79
80 /// Get the local fractino of node j in the element
81 template<unsigned INITIAL_NNODE_1D>
88
89 template<unsigned INITIAL_NNODE_1D>
91 const unsigned& n1d, const unsigned& i)
92 {
93 switch (this->nnode_1d())
94 {
95 case 2:
97 return 0.5 *
99 case 3:
101 return 0.5 *
103 case 4:
105 return 0.5 *
107 case 5:
109 return 0.5 *
111 case 6:
113 return 0.5 *
115 case 7:
117 return 0.5 *
119 default:
120 std::ostringstream error_message;
121 error_message << "\nERROR: Exceeded maximum polynomial order for";
122 error_message << "\n shape functions.\n";
123 throw OomphLibError(error_message.str(),
126 return 0.0;
127 }
128 }
129
130
131 //==================================================================
132 /// Return the node at the specified local coordinate
133 //==================================================================
134 template<unsigned INITIAL_NNODE_1D>
136 const Vector<double>& s) const
137 {
138 // Load the tolerance into a local variable
140 // There is one possible index.
141 Vector<int> index(1);
142
143 // Determine the index
144 // -------------------
145
146 // If we are at the lower limit, the index is zero
147 if (std::fabs(s[0] + 1.0) < tol)
148 {
149 index[0] = 0;
150 }
151 // If we are at the upper limit, the index is the number of nodes minus 1
152 else if (std::fabs(s[0] - 1.0) < tol)
153 {
154 index[0] = this->nnode_1d() - 1;
155 }
156 // Otherwise, we have to calculate the index in general
157 else
158 {
159 // Compute Gauss-Lobatto-Legendre node positions
161 Orthpoly::gll_nodes(this->nnode_1d(), z);
162 // Loop over possible internal nodal positions
163 for (unsigned n = 1; n < this->nnode_1d() - 1; n++)
164 {
165 if (std::fabs(z[n] - s[0]) < tol)
166 {
167 index[0] = n;
168 break;
169 }
170 }
171 // No matching nodes
172 return 0;
173 }
174 // If we've got here we have a node, so let's return a pointer to it
175 return this->node_pt(index[0]);
176 }
177
178 //===================================================================
179 /// If a neighbouring element's son has already created a node at
180 /// a position corresponding to the local fractional position within the
181 /// present element, s_fraction, return
182 /// a pointer to that node. If not, return NULL (0). If the node is
183 /// periodic the flag is_periodic will be true
184 //===================================================================
185 template<unsigned INITIAL_NNODE_1D>
188 bool& is_periodic)
189 {
190 // Not possible in 1D case, so return null pointer
191 return 0;
192 }
193
194 //==================================================================
195 /// Set the correct p-order of the element based on that of its
196 /// father. Then construct an integration scheme of the correct order.
197 /// If an adopted father is specified, information from this is
198 /// used instead of using the father found from the tree.
199 //==================================================================
200 template<unsigned INITIAL_NNODE_1D>
202 Tree* const& adopted_father_pt, const unsigned& initial_p_order)
203 {
204 // Storage for pointer to my father (in binarytree impersonation)
205 BinaryTree* father_pt = 0;
206
207 // Check if an adopted father has been specified
208 if (adopted_father_pt != 0)
209 {
210 // Get pointer to my father (in binarytree impersonation)
211 father_pt = dynamic_cast<BinaryTree*>(adopted_father_pt);
212 }
213 // Check if element is in a tree
214 else if (Tree_pt != 0)
215 {
216 // Get pointer to my father (in binarytree impersonation)
217 father_pt = dynamic_cast<BinaryTree*>(binary_tree_pt()->father_pt());
218 }
219 // else
220 // {
221 // throw OomphLibError(
222 // "Element not in a tree, and no adopted father has been
223 // specified!", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
224 // }
225
226 // Check if element has father
227 if (father_pt != 0 || initial_p_order != 0)
228 {
229 if (father_pt != 0)
230 {
233 father_pt->object_pt());
234 if (father_el_pt != 0)
235 {
236 unsigned father_p_order = father_el_pt->p_order();
237 // Set p-order to that of father
238 P_order = father_p_order;
239 }
240 }
241 else
242 {
243 P_order = initial_p_order;
244 }
245
246 // Now sort out the element...
247 // (has p nodes)
248 unsigned new_n_node = P_order;
249
250 // Allocate new space for Nodes (at the element level)
251 this->set_n_node(new_n_node);
252
253 // Set integration scheme
254 delete this->integral_pt();
255 switch (P_order)
256 {
257 case 2:
259 break;
260 case 3:
262 break;
263 case 4:
265 break;
266 case 5:
268 break;
269 case 6:
271 break;
272 case 7:
274 break;
275 default:
276 std::ostringstream error_message;
277 error_message << "\nERROR: Exceeded maximum polynomial order for";
278 error_message << "\n integration scheme.\n";
279 throw OomphLibError(error_message.str(),
282 }
283 }
284 }
285
286 //==================================================================
287 /// Check the father element for any required nodes which
288 /// already exist
289 //==================================================================
290 template<unsigned INITIAL_NNODE_1D>
292 Mesh*& mesh_pt, Vector<Node*>& new_node_pt)
293 {
294 /*
295 //Pointer to my father (in binarytree impersonation)
296 BinaryTree* father_pt =
297 dynamic_cast<BinaryTree*>(binary_tree_pt()->father_pt());
298
299 // Check if element has father
300 if (father_pt!=0)
301 {
302 PRefineableQElement<1>* father_el_pt =
303 dynamic_cast<PRefineableQElement<1>*>
304 (this->tree_pt()->father_pt()->object_pt());
305 if (father_el_pt!=0)
306 {
307 // Pre-build actions
308 //??
309 }
310 else
311 {
312 std::ostringstream error_message;
313 error_message <<"\nERROR: Dynamic cast failed!\n";
314 throw OomphLibError(error_message.str(),
315 OOMPH_CURRENT_FUNCTION,
316 OOMPH_EXCEPTION_LOCATION);
317 }
318 }
319 */
320 }
321
322 //==================================================================
323 /// p-refine the element inc times. (If inc<0 then p-unrefinement
324 /// is performed.)
325 //==================================================================
326 template<unsigned INITIAL_NNODE_1D>
328 const int& inc, Mesh* const& mesh_pt, GeneralisedElement* const& clone_pt)
329 {
330 // Cast clone to correct type
333
334 // Check if we can use it
335 if (clone_el_pt == 0)
336 {
337 throw OomphLibError(
338 "Cloned copy must be a PRefineableQElement<1,INITIAL_NNODE_1D>!",
341 }
342#ifdef PARANOID
343 // Clone exists, so check that it is infact a copy of me
344 else
345 {
346 // Flag to keep track of check
347 bool clone_is_ok = true;
348
349 // Does the clone have the correct p-order?
350 clone_is_ok = clone_is_ok && (clone_el_pt->p_order() == this->p_order());
351
352 if (!clone_is_ok)
353 {
354 std::ostringstream err_stream;
355 err_stream << "Clone element has a different p-order from me,"
356 << std::endl
357 << "but it is supposed to be a copy!" << std::endl;
358
359 throw OomphLibError(
361 }
362
363 // Does the clone have the same number of nodes as me?
364 clone_is_ok = clone_is_ok && (clone_el_pt->nnode() == this->nnode());
365
366 if (!clone_is_ok)
367 {
368 std::ostringstream err_stream;
369 err_stream << "Clone element has a different number of nodes from me,"
370 << std::endl
371 << "but it is supposed to be a copy!" << std::endl;
372
373 throw OomphLibError(
375 }
376
377 // Does the clone have the same nodes as me?
378 for (unsigned n = 0; n < this->nnode(); n++)
379 {
381 clone_is_ok && (clone_el_pt->node_pt(n) == this->node_pt(n));
382 }
383
384 if (!clone_is_ok)
385 {
386 std::ostringstream err_stream;
387 err_stream << "The nodes belonging to the clone element are different"
388 << std::endl
389 << "from mine, but it is supposed to be a copy!"
390 << std::endl;
391
392 throw OomphLibError(
394 }
395
396 // If we get to here then the clone has all the information we require
397 }
398#endif
399
400 // Currently we can't handle the case of generalised coordinates
401 // since we haven't established how they should be interpolated.
402 // Buffer this case:
403 if (clone_el_pt->node_pt(0)->nposition_type() != 1)
404 {
405 throw OomphLibError("Can't handle generalised nodal positions (yet).",
408 }
409
410 // Timestepper should be the same for all nodes -- use it
411 // to create timesteppers for new nodes
413
414 // Get number of history values (incl. present)
415 unsigned ntstorage = time_stepper_pt->ntstorage();
416
417 // Increment p-order of the element
418 P_order += inc;
419
420 // Change integration scheme
421 delete this->integral_pt();
422 switch (P_order)
423 {
424 case 2:
426 break;
427 case 3:
429 break;
430 case 4:
432 break;
433 case 5:
435 break;
436 case 6:
438 break;
439 case 7:
441 break;
442 default:
443 std::ostringstream error_message;
444 error_message << "\nERROR: Exceeded maximum polynomial order for";
445 error_message << "\n integration scheme.\n";
446 throw OomphLibError(error_message.str(),
449 }
450
451 // Allocate new space for Nodes (at the element level)
452 this->set_n_node(P_order);
453
454 // Copy vertex nodes and create new internal nodes
455 //------------------------------------------------
456
457 // Setup vertex coordinates in element:
458 //-------------------------------------
461 s_left[0] = -1.0;
462 s_right[0] = 1.0;
463
464 // Local coordinate in element
465 Vector<double> s(1);
466
468 // Loop over all nodes in the element
469 for (unsigned n = 0; n < P_order; n++)
470 {
471 // Get the fractional position (in the current element) of the node
472 // in the direction of s[0]
474
475 // Evaluate the local coordinate of the node in the father element
476 s[0] = s_left[0] + (s_right[0] - s_left[0]) * s_fraction[0];
477
478 // Initialise flag: So far, this node hasn't been built or copied yet
479 bool node_done = false;
480
481 // Get the pointer to the node in this element (or rather, its clone),
482 // returns NULL if there is not node
484
485 // Does this node already exist in this element?
486 //----------------------------------------------
487 if (created_node_pt != 0)
488 {
489 // Copy node across
490 this->node_pt(n) = created_node_pt;
491
492 // Make sure that we update the values at the node so that they
493 // are consistent with the present representation. This is only
494 // needed for mixed interpolation where the value at the father
495 // could now become active.
496
497 // Loop over all history values
498 for (unsigned t = 0; t < ntstorage; t++)
499 {
500 // Get values from father element
501 // Note: get_interpolated_values() sets Vector size itself
503 clone_el_pt->get_interpolated_values(t, s, prev_values);
504 // Find the minimum number of values
505 //(either those stored at the node, or those returned by
506 // the function)
507 unsigned n_val_at_node = created_node_pt->nvalue();
509 // Use the ternary conditional operator here
513 // Assign the values that we can
514 for (unsigned k = 0; k < n_var; k++)
515 {
516 created_node_pt->set_value(t, k, prev_values[k]);
517 }
518 }
519
520 // Indicate that node has been created by copy
521 node_done = true;
522 }
523
524 // Node does not exist in this element
525 //------------------------------------
526
527 // If the node has not been built anywhere ---> build it here
528 if (!node_done)
529 {
530 // In a 1D mesh any node which lies on the boundary must exist in
531 // the initial (coarse) mesh, so any newly-built nodes cannot be
532 // boundary nodes. Therefore we always create a normal "bulk" node.
533
534 // Create node and set the pointer to it from the element
536
537 // Now we set the position and values at the newly created node
538
539 // Now we set the position and values at the newly created node.
540 // In the first instance use macro element or FE representation
541 // to create past and present nodal positions.
542 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC ELEMENTS AS NOT
543 // ALL OF THEM NECESSARILY IMPLEMENT NONTRIVIAL NODE UPDATE
544 // FUNCTIONS. CALLING THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL
545 // LEAVE THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
546 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL NOT ASSIGN SENSIBLE
547 // INITIAL POSITIONS!)
548
549 // Loop over history values
550 for (unsigned t = 0; t < ntstorage; t++)
551 {
552 // Allocate storage for the previous position of the node
554
555 // Get position from father element -- this uses the macro
556 // element representation if appropriate.
558
559 // Set the previous position of the new node
560 created_node_pt->x(t, 0) = x_prev[0];
561
562 // Allocate storage for the previous values at the node
563 // NOTE: the size of this vector is equal to the number of values
564 // (e.g. 3 velocity components and 1 pressure, say)
565 // associated with each node and NOT the number of history values
566 // which the node stores!
568
569 // Get values from father element
570 // Note: get_interpolated_values() sets Vector size itself.
571 clone_el_pt->get_interpolated_values(t, s, prev_values);
572
573 // Determine the number of values at the new node
574 const unsigned n_value = created_node_pt->nvalue();
575
576 // Loop over all values and set the previous values
577 for (unsigned v = 0; v < n_value; v++)
578 {
579 created_node_pt->set_value(t, v, prev_values[v]);
580 }
581 } // End of loop over history values
582
583 // Add new node to mesh (if requested)
584 if (mesh_pt != 0)
585 {
587 }
588
590 dynamic_cast<AlgebraicElementBase*>(this);
591
592 // If we do have an algebraic element
593 if (alg_el_pt != 0)
594 {
595 std::string error_message = "Have not implemented p-refinement for";
596 error_message += "Algebraic p-refineable elements yet\n";
597
598 throw OomphLibError(
599 error_message,
600 "PRefineableQElement<1,INITIAL_NNODE_1D>::p_refine()",
602 }
603
604 } // End of case where we build the node ourselves
605
606 // Check if the element is an algebraic element
608 dynamic_cast<AlgebraicElementBase*>(this);
609
610 // If the element is an algebraic element, setup
611 // node position (past and present) from algebraic node update
612 // function. This over-writes previous assingments that
613 // were made based on the macro-element/FE representation.
614 // NOTE: YES, THIS NEEDS TO BE CALLED REPEATEDLY IF THE
615 // NODE IS MEMBER OF MULTIPLE ELEMENTS: THEY ALL ASSIGN
616 // THE SAME NODAL POSITIONS BUT WE NEED TO ADD THE REMESH
617 // INFO FOR *ALL* ROOT ELEMENTS!
618 if (alg_el_pt != 0)
619 {
620 // Build algebraic node update info for new node
621 // This sets up the node update data for all node update
622 // functions that are shared by all nodes in the father
623 // element
624 alg_el_pt->setup_algebraic_node_update(
625 this->node_pt(n), s, clone_el_pt);
626 }
627
628 } // End of loop over all nodes in element
629
630
631 // If the element is a MacroElementNodeUpdateElement, set
632 // the update parameters for the current element's nodes --
633 // all this needs is the vector of (pointers to the)
634 // geometric objects that affect the MacroElement-based
635 // node update -- this needs to be done to set the node
636 // update info for newly created nodes
639 if (clone_m_el_pt != 0)
640 {
641 // Get vector of geometric objects from father (construct vector
642 // via copy operation)
643 Vector<GeomObject*> geom_object_pt(clone_m_el_pt->geom_object_pt());
644
645 // Cast current element to MacroElementNodeUpdateElement:
647 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
648
649#ifdef PARANOID
650 if (m_el_pt == 0)
651 {
652 std::string error_message =
653 "Failed to cast to MacroElementNodeUpdateElementBase*\n";
654 error_message +=
655 "Strange -- if my clone is a MacroElementNodeUpdateElement\n";
656 error_message += "then I should be too....\n";
657
658 throw OomphLibError(
660 }
661#endif
662 // Build update info by passing vector of geometric objects:
663 // This sets the current element to be the update element
664 // for all of the element's nodes -- this is reversed
665 // if the element is ever un-refined in the father element's
666 // rebuild_from_sons() function which overwrites this
667 // assignment to avoid nasty segmentation faults that occur
668 // when a node tries to update itself via an element that no
669 // longer exists...
670 m_el_pt->set_node_update_info(geom_object_pt);
671 }
672
673
674 // Loop over all nodes in element again, to re-set the positions
675 // This must be done using the new element's macro-element
676 // representation, rather than the old version which may be
677 // of a different p-order!
678 for (unsigned n = 0; n < P_order; n++)
679 {
680 // Get the fractional position of the node in the direction of s[0]
682 // Local coordinate
683 s[0] = s_left[0] + (s_right[0] - s_left[0]) * s_fraction[0];
684
685 // Loop over # of history values
686 for (unsigned t = 0; t < ntstorage; t++)
687 {
688 // Get position from father element -- this uses the macro
689 // element representation if appropriate. If the node
690 // turns out to be a hanging node later on, then
691 // its position gets adjusted in line with its
692 // hanging node interpolation.
694 this->get_x(t, s, x_prev);
695
696 // Set previous positions of the new node
697 this->node_pt(n)->x(t, 0) = x_prev[0];
698 }
699 }
700
701 // Not necessary to delete the old nodes since all original nodes are in the
702 // current mesh and so will be pruned as part of the mesh adaption process.
703
704 // Do any further-build required
705 this->further_build();
706 }
707
708 //=======================================================================
709 /// Shape functions for PRefineableQElement<DIM>
710 //=======================================================================
711 template<unsigned INITIAL_NNODE_1D>
713 Shape& psi) const
714 {
715 switch (p_order())
716 {
717 case 2:
718 {
719 // Calculate nodal positions
721 // Create one-dim shape functions
723 // Now let's loop over the nodal points in the element
724 // and copy the values back in
725 for (unsigned i = 0; i < p_order(); i++)
726 {
727 psi(i) = psi1[i];
728 }
729 break;
730 }
731 case 3:
732 {
733 // Calculate nodal positions
735 // Create one-dim shape functions
737 // Now let's loop over the nodal points in the element
738 // and copy the values back in
739 for (unsigned i = 0; i < p_order(); i++)
740 {
741 psi(i) = psi1[i];
742 }
743 break;
744 }
745 case 4:
746 {
747 // Calculate nodal positions
749 // Create one-dim shape functions
751 // Now let's loop over the nodal points in the element
752 // and copy the values back in
753 for (unsigned i = 0; i < p_order(); i++)
754 {
755 psi(i) = psi1[i];
756 }
757 break;
758 }
759 case 5:
760 {
761 // Calculate nodal positions
763 // Create one-dim shape functions
765 // Now let's loop over the nodal points in the element
766 // and copy the values back in
767 for (unsigned i = 0; i < p_order(); i++)
768 {
769 psi(i) = psi1[i];
770 }
771 break;
772 }
773 case 6:
774 {
775 // Calculate nodal positions
777 // Create one-dim shape functions
779 // Now let's loop over the nodal points in the element
780 // and copy the values back in
781 for (unsigned i = 0; i < p_order(); i++)
782 {
783 psi(i) = psi1[i];
784 }
785 break;
786 }
787 case 7:
788 {
789 // Calculate nodal positions
791 // Create one-dim shape functions
793 // Now let's loop over the nodal points in the element
794 // and copy the values back in
795 for (unsigned i = 0; i < p_order(); i++)
796 {
797 psi(i) = psi1[i];
798 }
799 break;
800 }
801 default:
802 oomph_info << "\n ERROR: PRefineableQElement::shape() exceeded maximum";
803 oomph_info << "\n polynomial order for shape functions."
804 << std::endl;
805 }
806 }
807
808 //=======================================================================
809 /// Derivatives of shape functions for PRefineableQElement<DIM>
810 //=======================================================================
811 template<unsigned INITIAL_NNODE_1D>
813 const Vector<double>& s, Shape& psi, DShape& dpsi) const
814 {
815 switch (p_order())
816 {
817 case 2:
818 {
819 // Calculate nodal positions
821 // Call the shape functions and derivatives
824 // Loop over shapes and copy across
825 for (unsigned i = 0; i < p_order(); i++)
826 {
827 psi(i) = psi1[i];
828 dpsi(i, 0) = dpsi1ds[i];
829 }
830
831 break;
832 }
833 case 3:
834 {
835 // Calculate nodal positions
837 // Call the shape functions and derivatives
840 // Loop over shapes and copy across
841 for (unsigned i = 0; i < p_order(); i++)
842 {
843 psi(i) = psi1[i];
844 dpsi(i, 0) = dpsi1ds[i];
845 }
846 break;
847 }
848 case 4:
849 {
850 // Calculate nodal positions
852 // Call the shape functions and derivatives
855 // Loop over shapes and copy across
856 for (unsigned i = 0; i < p_order(); i++)
857 {
858 psi(i) = psi1[i];
859 dpsi(i, 0) = dpsi1ds[i];
860 }
861 break;
862 }
863 case 5:
864 {
865 // Calculate nodal positions
867 // Call the shape functions and derivatives
870 // Loop over shapes and copy across
871 for (unsigned i = 0; i < p_order(); i++)
872 {
873 psi(i) = psi1[i];
874 dpsi(i, 0) = dpsi1ds[i];
875 }
876 break;
877 }
878 case 6:
879 {
880 // Calculate nodal positions
882 // Call the shape functions and derivatives
885 // Loop over shapes and copy across
886 for (unsigned i = 0; i < p_order(); i++)
887 {
888 psi(i) = psi1[i];
889 dpsi(i, 0) = dpsi1ds[i];
890 }
891 break;
892 }
893 case 7:
894 {
895 // Calculate nodal positions
897 // Call the shape functions and derivatives
900 // Loop over shapes and copy across
901 for (unsigned i = 0; i < p_order(); i++)
902 {
903 psi(i) = psi1[i];
904 dpsi(i, 0) = dpsi1ds[i];
905 }
906 break;
907 }
908 default:
910 << "\n ERROR: PRefineableQElement::dshape_local() exceeded maximum";
911 oomph_info << "\n polynomial order for shape functions."
912 << std::endl;
913 }
914 }
915
916 //=======================================================================
917 /// Second derivatives of shape functions for PRefineableQElement<DIM>
918 /// d2psids(i,0) = \f$ d^2 \psi_j / d s^2 \f$
919 //=======================================================================
920 template<unsigned INITIAL_NNODE_1D>
922 const Vector<double>& s, Shape& psi, DShape& dpsids, DShape& d2psids) const
923 {
924 std::ostringstream error_message;
925 error_message
926 << "\nd2shape_local currently not implemented for this element\n";
927 throw OomphLibError(
929 }
930
931 //=================================================================
932 /// Internal function to set up the hanging nodes on a particular
933 /// edge of the element. (Not required in 1D.)
934 //=================================================================
935 template<unsigned INITIAL_NNODE_1D>
937 const int& value_id, const int& my_edge, std::ofstream& output_hangfile)
938 {
939 }
940
941 //=======================================================================
942 /// Rebuild the element from nodes found in its sons
943 /// Adjusts its p-order to be the maximum of its sons' p-orders
944 //=======================================================================
945 template<unsigned INITIAL_NNODE_1D>
947 Mesh*& mesh_pt)
948 {
949 // Get p-orders of sons
950 unsigned n_sons = this->tree_pt()->nsons();
952 unsigned max_son_p_order = 0;
953 for (unsigned ison = 0; ison < n_sons; ison++)
954 {
956 this->tree_pt()->son_pt(ison)->object_pt());
957 son_p_order[ison] = el_pt->p_order();
960 }
961
962 unsigned old_Nnode = this->nnode();
963 unsigned old_P_order = this->p_order();
964 // Set p-order of the element
965 this->p_order() = max_son_p_order;
966
967 // Change integration scheme
968 delete this->integral_pt();
969 switch (this->p_order())
970 {
971 case 2:
973 break;
974 case 3:
976 break;
977 case 4:
979 break;
980 case 5:
982 break;
983 case 6:
985 break;
986 case 7:
988 break;
989 default:
990 std::ostringstream error_message;
991 error_message << "\nERROR: Exceeded maximum polynomial order for";
992 error_message << "\n integration scheme.\n";
993 throw OomphLibError(error_message.str(),
996 }
997
998 // Back up pointers to old nodes before they are lost
1000 for (unsigned n = 0; n < old_Nnode; n++)
1001 {
1002 old_node_pt[n] = this->node_pt(n);
1003 }
1004
1005 // Allocate new space for Nodes (at the element level)
1006 this->set_n_node(this->p_order());
1007
1008 // Copy vertex nodes and create new edge and internal nodes
1009 //---------------------------------------------------------
1010
1011 // Copy vertex nodes
1012 this->node_pt(0) = old_node_pt[0];
1013 this->node_pt(this->p_order() - 1) = old_node_pt[old_P_order - 1];
1014
1015
1016 //=============================================================
1017 // Below this line is copied from RefineableQSpectralElement<2>
1018
1019 // The timestepper should be the same for all nodes and node 0 should
1020 // never be deleted.
1021 if (this->node_pt(0) == 0)
1022 {
1023 throw OomphLibError("The vertex node (0) does not exist",
1026 }
1027
1029
1030 // Determine number of history values stored
1031 const unsigned ntstorage = time_stepper_pt->ntstorage();
1032
1033 // Allocate storage for local coordinates
1035
1036 // Determine the number of nodes in the element
1037 const unsigned n_node = this->nnode_1d();
1038
1039 // Loop over the nodes in the element
1040 for (unsigned n = 0; n < n_node; n++)
1041 {
1042 // Get the fractional position of the node in the direction of s[0]
1044
1045 // Determine the local coordinate in the father element
1046 s[0] = -1.0 + 2.0 * s_fraction[0];
1047
1048 // If the node has not been built
1049 if (this->node_pt(n) == 0)
1050 {
1051 // Has the node been created by one of its neighbours?
1052 bool is_periodic = false;
1054 this->node_created_by_neighbour(s_fraction, is_periodic);
1055
1056 // If it has, set the pointer
1057 if (created_node_pt != 0)
1058 {
1059 // If the node is periodic
1060 if (is_periodic)
1061 {
1062 throw OomphLibError("Cannot handle periodic nodes yet",
1065 }
1066 // Non-periodic case, just set the pointer
1067 else
1068 {
1069 this->node_pt(n) = created_node_pt;
1070 }
1071 }
1072 // Otherwise, we need to build it
1073 else
1074 {
1075 // First, find the son element in which the node should live
1076
1077 // Find coordinate in the son
1079 using namespace BinaryTreeNames;
1080 int son = -10;
1081 // If s_fraction is between 0 and 0.5, we are in the left son
1082 if (s_fraction[0] < 0.5)
1083 {
1084 son = L;
1085 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
1086 }
1087 // Otherwise we are in the right son
1088 else
1089 {
1090 son = R;
1091 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
1092 }
1093
1094 // Get the pointer to the son element in which the new node
1095 // would live
1098 this->tree_pt()->son_pt(son)->object_pt());
1099
1100 // In 1D we should never be rebuilding an element's vertex nodes
1101 // (since they will never be deleted), so throw an error if we
1102 // appear to be doing so
1103#ifdef PARANOID
1104 if (n == 0 || n == n_node - 1)
1105 {
1106 std::string error_message =
1107 "I am trying to rebuild one of the (two) vertex nodes in\n";
1108 error_message +=
1109 "this 1D element. It should not have been possible to delete\n";
1110 error_message += "either of these!\n";
1111
1112 throw OomphLibError(
1114 }
1115#endif
1116
1117 // With this in mind we will always be creating normal "bulk" nodes
1118 this->node_pt(n) = this->construct_node(n, time_stepper_pt);
1119
1120 // Now we set the position and values at the newly created node
1121
1122 // In the first instance use macro element or FE representation
1123 // to create past and present nodal positions.
1124 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC ELEMENTS AS NOT
1125 // ALL OF THEM NECESSARILY IMPLEMENT NONTRIVIAL NODE UPDATE
1126 // FUNCTIONS. CALLING THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL
1127 // LEAVE THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
1128 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL NOT ASSIGN SENSIBLE
1129 // INITIAL POSITIONS!)
1130
1131 // Loop over history values
1132 for (unsigned t = 0; t < ntstorage; t++)
1133 {
1134 // Allocate storage for the previous position of the node
1136
1137 // Get position from son element -- this uses the macro element
1138 // representation if appropriate
1140
1141 // Set the previous position of the new node
1142 this->node_pt(n)->x(t, 0) = x_prev[0];
1143
1144 // Allocate storage for the previous values at the node
1145 // NOTE: the size of this vector is equal to the number of values
1146 // (e.g. 3 velocity components and 1 pressure, say)
1147 // associated with each node and NOT the number of history values
1148 // which the node stores!
1150
1151 // Get values from son element
1152 // Note: get_interpolated_values() sets Vector size itself.
1153 son_el_pt->get_interpolated_values(t, s_in_son, prev_values);
1154
1155 // Determine the number of values at the new node
1156 const unsigned n_value = this->node_pt(n)->nvalue();
1157
1158 // Loop over all values and set the previous values
1159 for (unsigned v = 0; v < n_value; v++)
1160 {
1161 this->node_pt(n)->set_value(t, v, prev_values[v]);
1162 }
1163 } // End of loop over history values
1164
1165 // Add new node to mesh
1166 mesh_pt->add_node_pt(this->node_pt(n));
1167
1168 } // End of case where we build the node ourselves
1169
1170 } // End of if this node has not been built
1171 } // End of loop over nodes in element
1172
1173 // Check if the element is an algebraic element
1174 // This is done on all nodes in the element after reconstruction
1175 // rather than as the nodes are built
1176 AlgebraicElementBase* alg_el_pt = dynamic_cast<AlgebraicElementBase*>(this);
1177
1178 // If so, throw error
1179 if (alg_el_pt != 0)
1180 {
1181 std::string error_message =
1182 "Have not implemented rebuilding from sons for";
1183 error_message += "Algebraic p-refineable elements yet\n";
1184
1185 throw OomphLibError(
1186 error_message,
1187 "PRefineableQElement<1,INITIAL_NNODE_1D>::rebuild_from_sons()",
1189 }
1190 }
1191
1192 //=================================================================
1193 /// Check inter-element continuity of
1194 /// - nodal positions
1195 /// - (nodally) interpolated function values
1196 //====================================================================
1197 template<unsigned INITIAL_NNODE_1D>
1203
1204 ////////////////////////////////////////////////////////////////
1205 // 2D PRefineableQElements
1206 ////////////////////////////////////////////////////////////////
1207
1208 /// Get local coordinates of node j in the element; vector sets its own size
1209 template<unsigned INITIAL_NNODE_1D>
1211 const unsigned& n, Vector<double>& s) const
1212 {
1213 s.resize(2);
1214 unsigned Nnode_1d = this->nnode_1d();
1215 unsigned n0 = n % Nnode_1d;
1216 unsigned n1 = n / Nnode_1d;
1217
1218 switch (Nnode_1d)
1219 {
1220 case 2:
1224 break;
1225 case 3:
1229 break;
1230 case 4:
1234 break;
1235 case 5:
1239 break;
1240 case 6:
1244 break;
1245 case 7:
1249 break;
1250 default:
1251 std::ostringstream error_message;
1252 error_message << "\nERROR: Exceeded maximum polynomial order for";
1253 error_message << "\n shape functions.\n";
1254 throw OomphLibError(error_message.str(),
1257 break;
1258 }
1259 }
1260
1261 /// Get the local fractino of node j in the element
1262 template<unsigned INITIAL_NNODE_1D>
1264 const unsigned& n, Vector<double>& s_fraction)
1265 {
1267 s_fraction[0] = 0.5 * (s_fraction[0] + 1.0);
1268 s_fraction[1] = 0.5 * (s_fraction[1] + 1.0);
1269 }
1270
1271 template<unsigned INITIAL_NNODE_1D>
1273 const unsigned& n1d, const unsigned& i)
1274 {
1275 switch (this->nnode_1d())
1276 {
1277 case 2:
1279 return 0.5 *
1281 case 3:
1283 return 0.5 *
1285 case 4:
1287 return 0.5 *
1289 case 5:
1291 return 0.5 *
1293 case 6:
1295 return 0.5 *
1297 case 7:
1299 return 0.5 *
1301 default:
1302 std::ostringstream error_message;
1303 error_message << "\nERROR: Exceeded maximum polynomial order for";
1304 error_message << "\n shape functions.\n";
1305 throw OomphLibError(error_message.str(),
1308 return 0.0;
1309 }
1310 }
1311
1312
1313 //==================================================================
1314 /// Return the node at the specified local coordinate
1315 //==================================================================
1316 template<unsigned INITIAL_NNODE_1D>
1318 const Vector<double>& s) const
1319 {
1320 unsigned Nnode_1d = this->nnode_1d();
1321 // Load the tolerance into a local variable
1323 // There are two possible indices.
1324 Vector<int> index(2);
1325
1326 // Loop over indices
1327 for (unsigned i = 0; i < 2; i++)
1328 {
1329 // Determine the index
1330 // -------------------
1331
1332 bool is_found = false;
1333
1334 // If we are at the lower limit, the index is zero
1335 if (std::fabs(s[i] + 1.0) < tol)
1336 {
1337 index[i] = 0;
1338 is_found = true;
1339 }
1340 // If we are at the upper limit, the index is the number of nodes minus 1
1341 else if (std::fabs(s[i] - 1.0) < tol)
1342 {
1343 index[i] = Nnode_1d - 1;
1344 is_found = true;
1345 }
1346 // Otherwise, we have to calculate the index in general
1347 else
1348 {
1349 // Compute Gauss-Lobatto-Legendre node positions
1352 // Loop over possible internal nodal positions
1353 for (unsigned n = 1; n < Nnode_1d - 1; n++)
1354 {
1355 if (std::fabs(z[n] - s[i]) < tol)
1356 {
1357 index[i] = n;
1358 is_found = true;
1359 break;
1360 }
1361 }
1362 }
1363
1364 if (!is_found)
1365 {
1366 // No matching nodes
1367 return 0;
1368 }
1369 }
1370 // If we've got here we have a node, so let's return a pointer to it
1371 return this->node_pt(index[0] + Nnode_1d * index[1]);
1372 }
1373
1374 //===================================================================
1375 /// If a neighbouring element has already created a node at
1376 /// a position corresponding to the local fractional position within the
1377 /// present element, s_fraction, return
1378 /// a pointer to that node. If not, return NULL (0). If the node is
1379 /// periodic the flag is_periodic will be true
1380 //===================================================================
1381 template<unsigned INITIAL_NNODE_1D>
1384 {
1385 using namespace QuadTreeNames;
1386
1387 // Calculate the edges on which the node lies
1389 if (s_fraction[0] == 0.0)
1390 {
1391 edges.push_back(W);
1392 }
1393 if (s_fraction[0] == 1.0)
1394 {
1395 edges.push_back(E);
1396 }
1397 if (s_fraction[1] == 0.0)
1398 {
1399 edges.push_back(S);
1400 }
1401 if (s_fraction[1] == 1.0)
1402 {
1403 edges.push_back(N);
1404 }
1405
1406 // Find the number of edges
1407 unsigned n_size = edges.size();
1408 // If there are no edges, then there is no neighbour, return 0
1409 if (n_size == 0)
1410 {
1411 return 0;
1412 }
1413
1417 Vector<double> s(2);
1418
1421
1422 // Loop over the edges
1423 for (unsigned j = 0; j < n_size; j++)
1424 {
1425 // Find pointer to neighbouring element along edge
1427 neigh_pt = quadtree_pt()->gteq_edge_neighbour(edges[j],
1429 s_lo_neigh,
1430 s_hi_neigh,
1431 neigh_edge,
1432 diff_level,
1434
1435 // Neighbour exists
1436 if (neigh_pt != 0)
1437 {
1438 // Have any of its vertex nodes been created yet?
1439 // (Must look in incomplete neighbours because after the
1440 // pre-build they may contain pointers to the required nodes. e.g.
1441 // h-refinement of neighbouring linear and quadratic elements)
1442 bool a_vertex_node_is_built = false;
1444 dynamic_cast<QElement<2, INITIAL_NNODE_1D>*>(neigh_pt->object_pt());
1445 if (neigh_obj_pt == 0)
1446 {
1447 throw OomphLibError("Not a quad element!",
1448 "PRefineableQElement<2,INITIAL_NNODE_1D>::node_"
1449 "created_by_neighbour()",
1451 }
1452 for (unsigned vnode = 0; vnode < neigh_obj_pt->nvertex_node(); vnode++)
1453 {
1456 break;
1457 }
1459 {
1460 // We now need to translate the nodal location
1461 // as defined in terms of the fractional coordinates of the present
1462 // element into those of its neighbour
1463
1464 // Calculate the local coordinate in the neighbour
1465 // Note that we need to use the translation scheme in case
1466 // the local coordinates are swapped in the neighbour.
1467 for (unsigned i = 0; i < 2; i++)
1468 {
1469 s[i] = s_lo_neigh[i] +
1471 }
1472
1473 // Find the node in the neighbour
1476
1477 // If there is a node, return it
1478 if (neighbour_node_pt != 0)
1479 {
1480 // Now work out whether it's a periodic boundary
1481 // only possible if we have moved into a neighbouring tree
1483 {
1484 // Return whether the neighbour is periodic
1485 is_periodic =
1486 quadtree_pt()->root_pt()->is_neighbour_periodic(edges[j]);
1487 }
1488 // Return the pointer to the neighbouring node
1489 return neighbour_node_pt;
1490 }
1491 }
1492 }
1493 }
1494 // Node not found, return null
1495 return 0;
1496 }
1497
1498 //===================================================================
1499 /// If a neighbouring element's son has already created a node at
1500 /// a position corresponding to the local fractional position within the
1501 /// present element, s_fraction, return
1502 /// a pointer to that node. If not, return NULL (0). If the node is
1503 /// periodic the flag is_periodic will be true
1504 //===================================================================
1505 template<unsigned INITIAL_NNODE_1D>
1508 bool& is_periodic)
1509 {
1510 using namespace QuadTreeNames;
1511
1512 // Calculate the edges on which the node lies
1514 if (s_fraction[0] == 0.0)
1515 {
1516 edges.push_back(W);
1517 }
1518 if (s_fraction[0] == 1.0)
1519 {
1520 edges.push_back(E);
1521 }
1522 if (s_fraction[1] == 0.0)
1523 {
1524 edges.push_back(S);
1525 }
1526 if (s_fraction[1] == 1.0)
1527 {
1528 edges.push_back(N);
1529 }
1530
1531 // Find the number of edges
1532 unsigned n_size = edges.size();
1533 // If there are no edges, then there is no neighbour, return 0
1534 if (n_size == 0)
1535 {
1536 return 0;
1537 }
1538
1542 Vector<double> s(2);
1543
1546
1547 // Loop over the edges
1548 for (unsigned j = 0; j < n_size; j++)
1549 {
1550 // Find pointer to neighbouring element along edge
1552 neigh_pt = quadtree_pt()->gteq_edge_neighbour(edges[j],
1554 s_lo_neigh,
1555 s_hi_neigh,
1556 neigh_edge,
1557 diff_level,
1559
1560 // Neighbour exists
1561 if (neigh_pt != 0)
1562 {
1563 // Have its nodes been created yet?
1564 // (Must look in sons of unfinished neighbours too!!!)
1565 if (1)
1566 {
1567 // We now need to translate the nodal location
1568 // as defined in terms of the fractional coordinates of the present
1569 // element into those of its neighbour
1570
1571 // Calculate the local coordinate in the neighbour
1572 // Note that we need to use the translation scheme in case
1573 // the local coordinates are swapped in the neighbour.
1574 for (unsigned i = 0; i < 2; i++)
1575 {
1576 s[i] = s_lo_neigh[i] +
1578 }
1579
1580 // Check if the element has sons
1581 if (neigh_pt->nsons() != 0)
1582 {
1583 // First, find the son element in which the node should live
1584
1585 // Find coordinates in the sons
1587 int son = -10;
1588 // If negative on the west side
1589 if (s[0] < 0.0)
1590 {
1591 // On the south side
1592 if (s[1] < 0.0)
1593 {
1594 // It's the southwest son
1595 son = SW;
1596 s_in_son[0] = 1.0 + 2.0 * s[0];
1597 s_in_son[1] = 1.0 + 2.0 * s[1];
1598 }
1599 // On the north side
1600 else
1601 {
1602 // It's the northwest son
1603 son = NW;
1604 s_in_son[0] = 1.0 + 2.0 * s[0];
1605 s_in_son[1] = -1.0 + 2.0 * s[1];
1606 }
1607 }
1608 else
1609 {
1610 // On the south side
1611 if (s[1] < 0.0)
1612 {
1613 // It's the southeast son
1614 son = SE;
1615 s_in_son[0] = -1.0 + 2.0 * s[0];
1616 s_in_son[1] = 1.0 + 2.0 * s[1];
1617 }
1618 // On the north side
1619 else
1620 {
1621 // It's the northeast son
1622 son = NE;
1623 s_in_son[0] = -1.0 + 2.0 * s[0];
1624 s_in_son[1] = -1.0 + 2.0 * s[1];
1625 }
1626 }
1627
1628 // Find the node in the neighbour's son
1630 neigh_pt->son_pt(son)->object_pt()->get_node_at_local_coordinate(
1631 s_in_son);
1632
1633 // If there is a node, return it
1634 if (neighbour_son_node_pt != 0)
1635 {
1636 // Now work out whether it's a periodic boundary
1637 // only possible if we have moved into a neighbouring tree
1639 {
1640 // Return whether the neighbour is periodic
1641 is_periodic =
1642 quadtree_pt()->root_pt()->is_neighbour_periodic(edges[j]);
1643 }
1644 // Return the pointer to the neighbouring node
1645 return neighbour_son_node_pt;
1646 }
1647 }
1648 else
1649 {
1650 // No sons to search in, so no node can be found
1651 return 0;
1652 }
1653 }
1654 }
1655 }
1656 // Node not found, return null
1657 return 0;
1658 }
1659
1660 //==================================================================
1661 /// Set the correct p-order of the element based on that of its
1662 /// father. Then construct an integration scheme of the correct order.
1663 /// If an adopted father is specified, information from this is
1664 /// used instead of using the father found from the tree.
1665 //==================================================================
1666 template<unsigned INITIAL_NNODE_1D>
1668 Tree* const& adopted_father_pt, const unsigned& initial_p_order)
1669 {
1670 // Storage for pointer to my father (in quadtree impersonation)
1671 QuadTree* father_pt = 0;
1672
1673 // Check if an adopted father has been specified
1674 if (adopted_father_pt != 0)
1675 {
1676 // Get pointer to my father (in quadtree impersonation)
1677 father_pt = dynamic_cast<QuadTree*>(adopted_father_pt);
1678 }
1679 // Check if element is in a tree
1680 else if (Tree_pt != 0)
1681 {
1682 // Get pointer to my father (in quadtree impersonation)
1683 father_pt = dynamic_cast<QuadTree*>(quadtree_pt()->father_pt());
1684 }
1685 // else
1686 // {
1687 // throw OomphLibError(
1688 // "Element not in a tree, and no adopted father has been
1689 // specified!", OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1690 // }
1691
1692 // Check if we found a father
1693 if (father_pt != 0 || initial_p_order != 0)
1694 {
1695 if (father_pt != 0)
1696 {
1699 father_pt->object_pt());
1700 if (father_el_pt != 0)
1701 {
1702 unsigned father_p_order = father_el_pt->p_order();
1703 // Set p-order to that of father
1704 P_order = father_p_order;
1705 }
1706 }
1707 else
1708 {
1709 P_order = initial_p_order;
1710 }
1711
1712 // Now sort out the element...
1713 // (has p^2 nodes)
1714 unsigned new_n_node = P_order * P_order;
1715
1716 // Allocate new space for Nodes (at the element level)
1717 this->set_n_node(new_n_node);
1718
1719 // Set integration scheme
1720 delete this->integral_pt();
1721 switch (P_order)
1722 {
1723 case 2:
1725 break;
1726 case 3:
1728 break;
1729 case 4:
1731 break;
1732 case 5:
1734 break;
1735 case 6:
1737 break;
1738 case 7:
1740 break;
1741 default:
1742 std::ostringstream error_message;
1743 error_message << "\nERROR: Exceeded maximum polynomial order for";
1744 error_message << "\n integration scheme.\n";
1745 throw OomphLibError(error_message.str(),
1748 }
1749 }
1750 }
1751
1752 //==================================================================
1753 /// Check the father element for any required nodes which
1754 /// already exist
1755 //==================================================================
1756 template<unsigned INITIAL_NNODE_1D>
1758 Mesh*& mesh_pt, Vector<Node*>& new_node_pt)
1759 {
1760 using namespace QuadTreeNames;
1761
1762 // Get the number of 1d nodes
1763 unsigned n_p = nnode_1d();
1764
1765 // Check whether static father_bound needs to be created
1766 if (Father_bound[n_p].nrow() == 0)
1767 {
1768 setup_father_bounds();
1769 }
1770
1771 // Pointer to my father (in quadtree impersonation)
1772 QuadTree* father_pt = dynamic_cast<QuadTree*>(quadtree_pt()->father_pt());
1773
1774 // What type of son am I? Ask my quadtree representation...
1775 int son_type = Tree_pt->son_type();
1776
1777 // Has somebody build me already? (If any nodes have not been built)
1778 if (!nodes_built())
1779 {
1780#ifdef PARANOID
1781 if (father_pt == 0)
1782 {
1783 std::string error_message =
1784 "Something fishy here: I have no father and yet \n";
1785 error_message += "I have no nodes. Who has created me then?!\n";
1786
1787 throw OomphLibError(
1789 }
1790#endif
1791
1792 // Return pointer to father element
1794 dynamic_cast<RefineableQElement<2>*>(father_pt->object_pt());
1795
1796 // Timestepper should be the same for all nodes in father
1797 // element -- use it create timesteppers for new nodes
1800
1801 // Number of history values (incl. present)
1802 unsigned ntstorage = time_stepper_pt->ntstorage();
1803
1806 Vector<double> s(2);
1807 Vector<double> x(2);
1808
1809 // Setup vertex coordinates in father element:
1810 //--------------------------------------------
1811 switch (son_type)
1812 {
1813 case SW:
1814 s_lo[0] = -1.0;
1815 s_hi[0] = 0.0;
1816 s_lo[1] = -1.0;
1817 s_hi[1] = 0.0;
1818 break;
1819
1820 case SE:
1821 s_lo[0] = 0.0;
1822 s_hi[0] = 1.0;
1823 s_lo[1] = -1.0;
1824 s_hi[1] = 0.0;
1825 break;
1826
1827 case NE:
1828 s_lo[0] = 0.0;
1829 s_hi[0] = 1.0;
1830 s_lo[1] = 0.0;
1831 s_hi[1] = 1.0;
1832 break;
1833
1834 case NW:
1835 s_lo[0] = -1.0;
1836 s_hi[0] = 0.0;
1837 s_lo[1] = 0.0;
1838 s_hi[1] = 1.0;
1839 break;
1840 }
1841
1842 // Pass macro element pointer on to sons and
1843 // set coordinates in macro element
1844 // hierher why can I see this?
1845 if (father_el_pt->macro_elem_pt() != 0)
1846 {
1848 for (unsigned i = 0; i < 2; i++)
1849 {
1850 s_macro_ll(i) =
1851 father_el_pt->s_macro_ll(i) +
1852 0.5 * (s_lo[i] + 1.0) *
1853 (father_el_pt->s_macro_ur(i) - father_el_pt->s_macro_ll(i));
1854 s_macro_ur(i) =
1855 father_el_pt->s_macro_ll(i) +
1856 0.5 * (s_hi[i] + 1.0) *
1857 (father_el_pt->s_macro_ur(i) - father_el_pt->s_macro_ll(i));
1858 }
1859 }
1860
1861
1862 // If the father element hasn't been generated yet, we're stuck...
1863 if (father_el_pt->node_pt(0) == 0)
1864 {
1865 throw OomphLibError(
1866 "Trouble: father_el_pt->node_pt(0)==0\n Can't build son element!\n",
1869 }
1870 else
1871 {
1872 unsigned jnod = 0;
1875
1877 // Loop over nodes in element
1878 for (unsigned i0 = 0; i0 < n_p; i0++)
1879 {
1880 // Get the fractional position of the node in the direction of s[0]
1882 // Local coordinate in father element
1883 s[0] = s_lo[0] + (s_hi[0] - s_lo[0]) * s_fraction[0];
1884
1885 for (unsigned i1 = 0; i1 < n_p; i1++)
1886 {
1887 // Get the fractional position of the node in the direction of s[1]
1889 // Local coordinate in father element
1890 s[1] = s_lo[1] + (s_hi[1] - s_lo[1]) * s_fraction[1];
1891
1892 // Local node number
1893 jnod = i0 + n_p * i1;
1894
1895 // Check whether the father's node is periodic if so, complain
1896 /* {
1897 Node* father_node_pt = father_el_pt->node_pt(jnod);
1898 if((father_node_pt->is_a_copy()) ||
1899 (father_node_pt->position_is_a_copy()))
1900 {
1901 throw OomphLibError(
1902 "Can't handle periodic nodes (yet).",
1903 OOMPH_CURRENT_FUNCTION,
1904 OOMPH_EXCEPTION_LOCATION);
1905 }
1906 }*/
1907
1908 // Initialise flag: So far, this node hasn't been built
1909 // or copied yet
1910 // bool node_done=false;
1911
1912 // Get the pointer to the node in the father, returns NULL
1913 // if there is not node
1916
1917 // Does this node already exist in father element?
1918 //------------------------------------------------
1919 if (created_node_pt != 0)
1920 {
1921 // Copy node across
1922 this->node_pt(jnod) = created_node_pt;
1923
1924 // Make sure that we update the values at the node so that
1925 // they are consistent with the present representation.
1926 // This is only need for mixed interpolation where the value
1927 // at the father could now become active.
1928
1929 // Loop over all history values
1930 for (unsigned t = 0; t < ntstorage; t++)
1931 {
1932 // Get values from father element
1933 // Note: get_interpolated_values() sets Vector size itself.
1935 father_el_pt->get_interpolated_values(t, s, prev_values);
1936 // Find the minimum number of values
1937 //(either those stored at the node, or those returned by
1938 // the function)
1939 unsigned n_val_at_node = created_node_pt->nvalue();
1941 // Use the ternary conditional operator here
1945 // Assign the values that we can
1946 for (unsigned k = 0; k < n_var; k++)
1947 {
1948 created_node_pt->set_value(t, k, prev_values[k]);
1949 }
1950 }
1951
1952 // Node has been created by copy
1953 // node_done=true;
1954 }
1955 } // End of vertical loop over nodes in element
1956 } // End of horizontal loop over nodes in element
1957 } // Sanity check: Father element has been generated
1958
1959 } // End of nodes not built
1960 else
1961 {
1962 // If this is element updates by macro element node update then we need
1963 // to set the correct info in the nodes here since this won't get done
1964 // later in build() because we already have all our nodes created.
1966 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
1967 if (m_el_pt != 0)
1968 {
1969 // Get vector of geometric objects
1970 Vector<GeomObject*> geom_object_pt = m_el_pt->geom_object_pt();
1971
1972 /// / Build update info by passing vector of geometric objects:
1973 /// / This sets the current element to be the update element
1974 /// / for all of the element's nodes -- this is reversed
1975 /// / if the element is ever un-refined in the father element's
1976 /// / rebuild_from_sons() function which overwrites this
1977 /// / assignment to avoid nasty segmentation faults that occur
1978 /// / when a node tries to update itself via an element that no
1979 /// / longer exists...
1980 m_el_pt->set_node_update_info(geom_object_pt);
1981 }
1982 }
1983 }
1984
1985 //==================================================================
1986 /// p-refine the element inc times. (If inc<0 then p-unrefinement
1987 /// is performed.)
1988 //==================================================================
1989 template<unsigned INITIAL_NNODE_1D>
1991 const int& inc, Mesh* const& mesh_pt, GeneralisedElement* const& clone_pt)
1992 {
1993 // Cast clone to correct type
1996
1997 // If it is a MacroElement then we need to copy the geometric data too.
1999 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
2000 if (m_el_pt != 0)
2001 {
2004
2005 // Store local copy of geom object vector, so it can be passed on
2006 // to son elements (and their nodes) during refinement
2007 unsigned ngeom_object = m_el_pt->ngeom_object();
2008 clone_m_el_pt->geom_object_pt().resize(ngeom_object);
2009 for (unsigned i = 0; i < ngeom_object; i++)
2010 {
2011 clone_m_el_pt->geom_object_pt()[i] = m_el_pt->geom_object_pt(i);
2012 }
2013
2014 // clone_m_el_pt->set_node_update_info(m_el_pt->geom_object_pt());
2015 }
2016
2017 // Check if we can use it
2018 if (clone_el_pt == 0)
2019 {
2020 throw OomphLibError(
2021 "Cloned copy must be a PRefineableQElement<2,INITIAL_NNODE_1D>!",
2024 }
2025#ifdef PARANOID
2026 // Clone exists, so check that it is infact a copy of me
2027 else
2028 {
2029 // Flag to keep track of check
2030 bool clone_is_ok = true;
2031
2032 // Does the clone have the correct p-order?
2033 clone_is_ok = clone_is_ok && (clone_el_pt->p_order() == this->p_order());
2034
2035 if (!clone_is_ok)
2036 {
2037 std::ostringstream err_stream;
2038 err_stream << "Clone element has a different p-order from me,"
2039 << std::endl
2040 << "but it is supposed to be a copy!" << std::endl;
2041
2042 throw OomphLibError(
2044 }
2045
2046 // Does the clone have the same number of nodes as me?
2047 clone_is_ok = clone_is_ok && (clone_el_pt->nnode() == this->nnode());
2048
2049 if (!clone_is_ok)
2050 {
2051 std::ostringstream err_stream;
2052 err_stream << "Clone element has a different number of nodes from me,"
2053 << std::endl
2054 << "but it is supposed to be a copy!" << std::endl;
2055
2056 throw OomphLibError(
2058 }
2059
2060 // Does the clone have the same nodes as me?
2061 for (unsigned n = 0; n < this->nnode(); n++)
2062 {
2063 clone_is_ok =
2064 clone_is_ok && (clone_el_pt->node_pt(n) == this->node_pt(n));
2065 }
2066
2067 if (!clone_is_ok)
2068 {
2069 std::ostringstream err_stream;
2070 err_stream << "The nodes belonging to the clone element are different"
2071 << std::endl
2072 << "from mine, but it is supposed to be a copy!"
2073 << std::endl;
2074
2075 throw OomphLibError(
2077 }
2078
2079 // Is this a macro element?
2081 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
2082 if (m_el_pt != 0)
2083 {
2084 // Get macro element version of clone
2087
2088 // Does the clone have the same geometric objects?
2090 clone_m_el_pt->geom_object_pt());
2091 Vector<GeomObject*> geom_object_pt(m_el_pt->geom_object_pt());
2092
2093 clone_is_ok =
2094 clone_is_ok && (geom_object_pt.size() == clone_geom_object_pt.size());
2095 for (unsigned n = 0;
2096 n < std::min(geom_object_pt.size(), clone_geom_object_pt.size());
2097 n++)
2098 {
2099 clone_is_ok =
2100 clone_is_ok && (clone_geom_object_pt[n] == geom_object_pt[n]);
2101 }
2102
2103 if (!clone_is_ok)
2104 {
2105 std::ostringstream err_stream;
2106 err_stream << "The clone element has different geometric objects"
2107 << std::endl
2108 << "from mine, but it is supposed to be a copy!"
2109 << std::endl;
2110
2111 throw OomphLibError(
2112 err_stream.str(),
2113 "PRefineableQElement<2,INITIAL_NNODE_1D>::p_refine()",
2115 }
2116 }
2117
2118 // If we get to here then the clone has all the information we require
2119 }
2120#endif
2121
2122 // Currently we can't handle the case of generalised coordinates
2123 // since we haven't established how they should be interpolated.
2124 // Buffer this case:
2125 if (clone_el_pt->node_pt(0)->nposition_type() != 1)
2126 {
2127 throw OomphLibError("Can't handle generalised nodal positions (yet).",
2130 }
2131
2132 // Timestepper should be the same for all nodes -- use it
2133 // to create timesteppers for new nodes
2135
2136 // Get number of history values (incl. present)
2137 unsigned ntstorage = time_stepper_pt->ntstorage();
2138
2139 // Increment p-order of the element
2140 P_order += inc;
2141
2142 // Change integration scheme
2143 delete this->integral_pt();
2144 switch (P_order)
2145 {
2146 case 2:
2148 break;
2149 case 3:
2151 break;
2152 case 4:
2154 break;
2155 case 5:
2157 break;
2158 case 6:
2160 break;
2161 case 7:
2163 break;
2164 default:
2165 std::ostringstream error_message;
2166 error_message << "\nERROR: Exceeded maximum polynomial order for";
2167 error_message << "\n integration scheme.\n";
2168 throw OomphLibError(error_message.str(),
2171 }
2172
2173 // Allocate new space for Nodes (at the element level)
2174 this->set_n_node(P_order * P_order);
2175
2176 // Copy vertex nodes and create new edge and internal nodes
2177 //---------------------------------------------------------
2178
2179 // Setup vertex coordinates in element:
2180 //-------------------------------------
2183 s_lo[0] = -1.0;
2184 s_hi[0] = 1.0;
2185 s_lo[1] = -1.0;
2186 s_hi[1] = 1.0;
2187
2188 // Local coordinate in element
2189 Vector<double> s(2);
2190
2191 unsigned jnod = 0;
2192
2194 // Loop over nodes in element
2195 for (unsigned i0 = 0; i0 < P_order; i0++)
2196 {
2197 // Get the fractional position of the node in the direction of s[0]
2199 // Local coordinate
2200 s[0] = s_lo[0] + (s_hi[0] - s_lo[0]) * s_fraction[0];
2201
2202 for (unsigned i1 = 0; i1 < P_order; i1++)
2203 {
2204 // Get the fractional position of the node in the direction of s[1]
2206 // Local coordinate
2207 s[1] = s_lo[1] + (s_hi[1] - s_lo[1]) * s_fraction[1];
2208
2209 // Local node number
2210 jnod = i0 + P_order * i1;
2211
2212 // Initialise flag: So far, this node hasn't been built
2213 // or copied yet
2214 bool node_done = false;
2215
2216 // Get the pointer to the node in this element (or rather, its clone),
2217 // returns NULL if there is not node
2219
2220 // Does this node already exist in this element?
2221 //----------------------------------------------
2222 if (created_node_pt != 0)
2223 {
2224 // Copy node across
2225 this->node_pt(jnod) = created_node_pt;
2226
2227 // Make sure that we update the values at the node so that
2228 // they are consistent with the present representation.
2229 // This is only need for mixed interpolation where the value
2230 // at the father could now become active.
2231
2232 // Loop over all history values
2233 for (unsigned t = 0; t < ntstorage; t++)
2234 {
2235 // Get values from father element
2236 // Note: get_interpolated_values() sets Vector size itself.
2238 clone_el_pt->get_interpolated_values(t, s, prev_values);
2239 // Find the minimum number of values
2240 //(either those stored at the node, or those returned by
2241 // the function)
2242 unsigned n_val_at_node = created_node_pt->nvalue();
2244 // Use the ternary conditional operator here
2248 // Assign the values that we can
2249 for (unsigned k = 0; k < n_var; k++)
2250 {
2251 created_node_pt->set_value(t, k, prev_values[k]);
2252 }
2253 }
2254
2255 // Node has been created by copy
2256 node_done = true;
2257 }
2258 // Node does not exist in this element but might already
2259 //------------------------------------------------------
2260 // have been created by neighbouring elements
2261 //-------------------------------------------
2262 else
2263 {
2264 // Was the node created by one of its neighbours
2265 // Whether or not the node lies on an edge can be calculated
2266 // by from the fractional position
2267 bool is_periodic = false;
2268 created_node_pt = node_created_by_neighbour(s_fraction, is_periodic);
2269
2270 // If the node was so created, assign the pointers
2271 if (created_node_pt != 0)
2272 {
2273 // If the node is periodic
2274 if (is_periodic)
2275 {
2276 // Now the node must be on a boundary, but we don't know which
2277 // one
2278 // The returned created_node_pt is actually the neighbouring
2279 // periodic node
2281
2282 // Determine the edge on which the new node will live
2283 //(cannot be a vertex node)
2284 int my_bound = Tree::OMEGA;
2285 if (s_fraction[0] == 0.0) my_bound = QuadTreeNames::W;
2286 else if (s_fraction[0] == 1.0)
2288 else if (s_fraction[1] == 0.0)
2290 else if (s_fraction[1] == 1.0)
2292
2293 // Storage for the set of Mesh boundaries on which the
2294 // appropriate edge lives.
2295 // [New nodes should always be mid-edge nodes and therefore
2296 // only live on one boundary but just to play it safe...]
2297 std::set<unsigned> boundaries;
2298 // Only get the boundaries if we are at the edge of
2299 // an element. Nodes in the centre of an element cannot be
2300 // on Mesh boundaries
2301 if (my_bound != Tree::OMEGA)
2302 {
2303 clone_el_pt->get_boundaries(my_bound, boundaries);
2304 }
2305
2306#ifdef PARANOID
2307 // Case where a new node lives on more than one boundary
2308 // seems fishy enough to flag
2309 if (boundaries.size() > 1)
2310 {
2311 throw OomphLibError(
2312 "boundaries.size()!=1 seems a bit strange..\n",
2315 }
2316
2317 // Case when there are no boundaries, we are in big trouble
2318 if (boundaries.size() == 0)
2319 {
2320 std::ostringstream error_stream;
2321 error_stream << "Periodic node is not on a boundary...\n"
2322 << "Coordinates: " << created_node_pt->x(0) << " "
2323 << created_node_pt->x(1) << "\n";
2324 throw OomphLibError(error_stream.str(),
2327 }
2328#endif
2329
2330 // Create node and set the pointer to it from the element
2333 // Make the node periodic from the neighbour
2334 created_node_pt->make_periodic(neighbour_node_pt);
2335
2336 // Loop over # of history values
2337 for (unsigned t = 0; t < ntstorage; t++)
2338 {
2339 // Get position from father element -- this uses the macro
2340 // element representation if appropriate. If the node
2341 // turns out to be a hanging node later on, then
2342 // its position gets adjusted in line with its
2343 // hanging node interpolation.
2346 // Set previous positions of the new node
2347 for (unsigned i = 0; i < 2; i++)
2348 {
2349 created_node_pt->x(t, i) = x_prev[i];
2350 }
2351 }
2352
2353 // Check if we need to add nodes to the mesh
2354 if (mesh_pt != 0)
2355 {
2356 // Next, we Update the boundary lookup schemes
2357 // Loop over the boundaries stored in the set
2358 for (std::set<unsigned>::iterator it = boundaries.begin();
2359 it != boundaries.end();
2360 ++it)
2361 {
2362 // Add the node to the boundary
2364
2365 // If we have set an intrinsic coordinate on this
2366 // mesh boundary then it must also be interpolated on
2367 // the new node
2368 // Now interpolate the intrinsic boundary coordinate
2369 if (mesh_pt->boundary_coordinate_exists(*it) == true)
2370 {
2372 clone_el_pt->interpolated_zeta_on_edge(
2373 *it, my_bound, s, zeta);
2374
2375 created_node_pt->set_coordinates_on_boundary(*it, zeta);
2376 }
2377 }
2378
2379 // Make sure that we add the node to the mesh
2380 mesh_pt->add_node_pt(created_node_pt);
2381 }
2382 } // End of periodic case
2383 // Otherwise the node is not periodic, so just set the
2384 // pointer to the neighbours node
2385 else
2386 {
2387 this->node_pt(jnod) = created_node_pt;
2388 }
2389 // Node has been created
2390 node_done = true;
2391 }
2392 // Node does not exist in neighbour element but might already
2393 //-----------------------------------------------------------
2394 // have been created by a son of a neighbouring element
2395 //-----------------------------------------------------
2396 else
2397 {
2398 // Was the node created by one of its neighbours' sons
2399 // Whether or not the node lies on an edge can be calculated
2400 // by from the fractional position
2401 bool is_periodic = false;
2403 node_created_by_son_of_neighbour(s_fraction, is_periodic);
2404
2405 // If the node was so created, assign the pointers
2406 if (created_node_pt != 0)
2407 {
2408 // If the node is periodic
2409 if (is_periodic)
2410 {
2411 // Now the node must be on a boundary, but we don't know which
2412 // one
2413 // The returned created_node_pt is actually the neighbouring
2414 // periodic node
2416
2417 // Determine the edge on which the new node will live
2418 //(cannot be a vertex node)
2419 int my_bound = Tree::OMEGA;
2420 if (s_fraction[0] == 0.0) my_bound = QuadTreeNames::W;
2421 else if (s_fraction[0] == 1.0)
2423 else if (s_fraction[1] == 0.0)
2425 else if (s_fraction[1] == 1.0)
2427
2428 // Storage for the set of Mesh boundaries on which the
2429 // appropriate edge lives.
2430 // [New nodes should always be mid-edge nodes and therefore
2431 // only live on one boundary but just to play it safe...]
2432 std::set<unsigned> boundaries;
2433 // Only get the boundaries if we are at the edge of
2434 // an element. Nodes in the centre of an element cannot be
2435 // on Mesh boundaries
2436 if (my_bound != Tree::OMEGA)
2437 {
2438 clone_el_pt->get_boundaries(my_bound, boundaries);
2439 }
2440
2441#ifdef PARANOID
2442 // Case where a new node lives on more than one boundary
2443 // seems fishy enough to flag
2444 if (boundaries.size() > 1)
2445 {
2446 throw OomphLibError(
2447 "boundaries.size()!=1 seems a bit strange..\n",
2450 }
2451
2452 // Case when there are no boundaries, we are in big trouble
2453 if (boundaries.size() == 0)
2454 {
2455 std::ostringstream error_stream;
2456 error_stream << "Periodic node is not on a boundary...\n"
2457 << "Coordinates: " << created_node_pt->x(0)
2458 << " " << created_node_pt->x(1) << "\n";
2459 throw OomphLibError(error_stream.str(),
2462 }
2463#endif
2464
2465 // Create node and set the pointer to it from the element
2468 // Make the node periodic from the neighbour
2469 created_node_pt->make_periodic(neighbour_node_pt);
2470
2471 // Loop over # of history values
2472 for (unsigned t = 0; t < ntstorage; t++)
2473 {
2474 // Get position from father element -- this uses the macro
2475 // element representation if appropriate. If the node
2476 // turns out to be a hanging node later on, then
2477 // its position gets adjusted in line with its
2478 // hanging node interpolation.
2481 // Set previous positions of the new node
2482 for (unsigned i = 0; i < 2; i++)
2483 {
2484 created_node_pt->x(t, i) = x_prev[i];
2485 }
2486 }
2487
2488 // Check if we need to add nodes to the mesh
2489 if (mesh_pt != 0)
2490 {
2491 // Next, we Update the boundary lookup schemes
2492 // Loop over the boundaries stored in the set
2493 for (std::set<unsigned>::iterator it = boundaries.begin();
2494 it != boundaries.end();
2495 ++it)
2496 {
2497 // Add the node to the boundary
2499
2500 // If we have set an intrinsic coordinate on this
2501 // mesh boundary then it must also be interpolated on
2502 // the new node
2503 // Now interpolate the intrinsic boundary coordinate
2504 if (mesh_pt->boundary_coordinate_exists(*it) == true)
2505 {
2507 clone_el_pt->interpolated_zeta_on_edge(
2508 *it, my_bound, s, zeta);
2509
2510 created_node_pt->set_coordinates_on_boundary(*it, zeta);
2511 }
2512 }
2513
2514 // Make sure that we add the node to the mesh
2515 mesh_pt->add_node_pt(created_node_pt);
2516 }
2517 } // End of periodic case
2518 // Otherwise the node is not periodic, so just set the
2519 // pointer to the neighbours node
2520 else
2521 {
2522 this->node_pt(jnod) = created_node_pt;
2523 }
2524 // Node has been created
2525 node_done = true;
2526 } // Node does not exist in son of neighbouring element
2527 } // Node does not exist in neighbouring element
2528 } // Node does not exist in this element
2529
2530 // Node has not been built anywhere ---> build it here
2531 if (!node_done)
2532 {
2533 // Firstly, we need to determine whether or not a node lies
2534 // on the boundary before building it, because
2535 // we actually assign a different type of node on boundaries.
2536
2537 // Determine the edge on which the new node will live
2538 //(cannot be a vertex node)
2539 int my_bound = Tree::OMEGA;
2540 if (s_fraction[0] == 0.0) my_bound = QuadTreeNames::W;
2541 else if (s_fraction[0] == 1.0)
2543 else if (s_fraction[1] == 0.0)
2545 else if (s_fraction[1] == 1.0)
2547
2548 // Storage for the set of Mesh boundaries on which the
2549 // appropriate edge lives.
2550 // [New nodes should always be mid-edge nodes and therefore
2551 // only live on one boundary but just to play it safe...]
2552 std::set<unsigned> boundaries;
2553 // Only get the boundaries if we are at the edge of
2554 // an element. Nodes in the centre of an element cannot be
2555 // on Mesh boundaries
2556 if (my_bound != Tree::OMEGA)
2557 {
2558 clone_el_pt->get_boundaries(my_bound, boundaries);
2559 }
2560
2561#ifdef PARANOID
2562 // Case where a new node lives on more than one boundary
2563 // seems fishy enough to flag
2564 if (boundaries.size() > 1)
2565 {
2566 throw OomphLibError("boundaries.size()!=1 seems a bit strange..\n",
2569 }
2570#endif
2571
2572 // If the node lives on a mesh boundary,
2573 // then we need to create a boundary node
2574 if (boundaries.size() > 0)
2575 {
2576 // Create node and set the pointer to it from the element
2579
2580 // Now we need to work out whether to pin the values at
2581 // the new node based on the boundary conditions applied at
2582 // its Mesh boundary
2583
2584 // Get the boundary conditions from the father
2585 Vector<int> bound_cons(this->ncont_interpolated_values());
2586 clone_el_pt->get_bcs(my_bound, bound_cons);
2587
2588 // Loop over the values and pin, if necessary
2589 unsigned n_value = created_node_pt->nvalue();
2590 for (unsigned k = 0; k < n_value; k++)
2591 {
2592 if (bound_cons[k])
2593 {
2594 created_node_pt->pin(k);
2595 }
2596 }
2597
2598 // Solid node? If so, deal with the positional boundary
2599 // conditions:
2601 dynamic_cast<SolidNode*>(created_node_pt);
2602 if (solid_node_pt != 0)
2603 {
2604 // Get the positional boundary conditions from the father:
2605 unsigned n_dim = created_node_pt->ndim();
2609#ifdef PARANOID
2610 if (clone_solid_el_pt == 0)
2611 {
2612 std::string error_message =
2613 "We have a SolidNode outside a refineable SolidElement\n";
2614 error_message +=
2615 "during mesh refinement -- this doesn't make sense";
2616
2617 throw OomphLibError(error_message,
2620 }
2621#endif
2623
2624 // Loop over the positions and pin, if necessary
2625 for (unsigned k = 0; k < n_dim; k++)
2626 {
2627 if (solid_bound_cons[k])
2628 {
2629 solid_node_pt->pin_position(k);
2630 }
2631 }
2632 } // End of if solid_node_pt
2633
2634
2635 // Check if we need to add nodes to the mesh
2636 if (mesh_pt != 0)
2637 {
2638 // Next, we Update the boundary lookup schemes
2639 // Loop over the boundaries stored in the set
2640 for (std::set<unsigned>::iterator it = boundaries.begin();
2641 it != boundaries.end();
2642 ++it)
2643 {
2644 // Add the node to the boundary
2646
2647 // If we have set an intrinsic coordinate on this
2648 // mesh boundary then it must also be interpolated on
2649 // the new node
2650 // Now interpolate the intrinsic boundary coordinate
2651 if (mesh_pt->boundary_coordinate_exists(*it) == true)
2652 {
2654 clone_el_pt->interpolated_zeta_on_edge(
2655 *it, my_bound, s, zeta);
2656
2657 created_node_pt->set_coordinates_on_boundary(*it, zeta);
2658 }
2659 }
2660 }
2661 }
2662 // Otherwise the node is not on a Mesh boundary and
2663 // we create a normal "bulk" node
2664 else
2665 {
2666 // Create node and set the pointer to it from the element
2668 }
2669
2670 // Now we set the position and values at the newly created node
2671
2672 // In the first instance use macro element or FE representation
2673 // to create past and present nodal positions.
2674 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC
2675 // ELEMENTS AS NOT ALL OF THEM NECESSARILY IMPLEMENT
2676 // NONTRIVIAL NODE UPDATE FUNCTIONS. CALLING
2677 // THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL LEAVE
2678 // THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
2679 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL
2680 // NOT ASSIGN SENSIBLE INITIAL POSITONS!
2681
2682 // Loop over # of history values
2683 for (unsigned t = 0; t < ntstorage; t++)
2684 {
2685 // Get position from father element -- this uses the macro
2686 // element representation if appropriate. If the node
2687 // turns out to be a hanging node later on, then
2688 // its position gets adjusted in line with its
2689 // hanging node interpolation.
2692
2693 // Set previous positions of the new node
2694 for (unsigned i = 0; i < 2; i++)
2695 {
2696 created_node_pt->x(t, i) = x_prev[i];
2697 }
2698 }
2699
2700 // Loop over all history values
2701 for (unsigned t = 0; t < ntstorage; t++)
2702 {
2703 // Get values from father element
2704 // Note: get_interpolated_values() sets Vector size itself.
2706 clone_el_pt->get_interpolated_values(t, s, prev_values);
2707 // Initialise the values at the new node
2708 unsigned n_value = created_node_pt->nvalue();
2709 for (unsigned k = 0; k < n_value; k++)
2710 {
2711 created_node_pt->set_value(t, k, prev_values[k]);
2712 }
2713 }
2714
2715 // Add new node to mesh (if requested)
2716 if (mesh_pt != 0)
2717 {
2718 mesh_pt->add_node_pt(created_node_pt);
2719 }
2720
2722 dynamic_cast<AlgebraicElementBase*>(this);
2723
2724 // If we do have an algebraic element
2725 if (alg_el_pt != 0)
2726 {
2727 std::string error_message = "Have not implemented p-refinement for";
2728 error_message += "Algebraic p-refineable elements yet\n";
2729
2730 throw OomphLibError(
2731 error_message,
2732 "PRefineableQElement<2,INITIAL_NNODE_1D>::p_refine()",
2734 }
2735
2736 } // End of case when we build the node ourselves
2737
2738 // Check if the element is an algebraic element
2740 dynamic_cast<AlgebraicElementBase*>(this);
2741
2742 // If the element is an algebraic element, setup
2743 // node position (past and present) from algebraic node update
2744 // function. This over-writes previous assingments that
2745 // were made based on the macro-element/FE representation.
2746 // NOTE: YES, THIS NEEDS TO BE CALLED REPEATEDLY IF THE
2747 // NODE IS MEMBER OF MULTIPLE ELEMENTS: THEY ALL ASSIGN
2748 // THE SAME NODAL POSITIONS BUT WE NEED TO ADD THE REMESH
2749 // INFO FOR *ALL* ROOT ELEMENTS!
2750 if (alg_el_pt != 0)
2751 {
2752 // Build algebraic node update info for new node
2753 // This sets up the node update data for all node update
2754 // functions that are shared by all nodes in the father
2755 // element
2756 alg_el_pt->setup_algebraic_node_update(
2757 this->node_pt(jnod), s, clone_el_pt);
2758 }
2759
2760 } // End of vertical loop over nodes in element
2761
2762 } // End of horizontal loop over nodes in element
2763
2764
2765 // If the element is a MacroElementNodeUpdateElement, set
2766 // the update parameters for the current element's nodes --
2767 // all this needs is the vector of (pointers to the)
2768 // geometric objects that affect the MacroElement-based
2769 // node update -- this needs to be done to set the node
2770 // update info for newly created nodes
2773 if (clone_m_el_pt != 0)
2774 {
2775 // Get vector of geometric objects from father (construct vector
2776 // via copy operation)
2777 Vector<GeomObject*> geom_object_pt(clone_m_el_pt->geom_object_pt());
2778
2779 // Cast current element to MacroElementNodeUpdateElement:
2781 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
2782
2783#ifdef PARANOID
2784 if (m_el_pt == 0)
2785 {
2786 std::string error_message =
2787 "Failed to cast to MacroElementNodeUpdateElementBase*\n";
2788 error_message +=
2789 "Strange -- if my clone is a MacroElementNodeUpdateElement\n";
2790 error_message += "then I should be too....\n";
2791
2792 throw OomphLibError(
2794 }
2795#endif
2796 // Build update info by passing vector of geometric objects:
2797 // This sets the current element to be the update element
2798 // for all of the element's nodes -- this is reversed
2799 // if the element is ever un-refined in the father element's
2800 // rebuild_from_sons() function which overwrites this
2801 // assignment to avoid nasty segmentation faults that occur
2802 // when a node tries to update itself via an element that no
2803 // longer exists...
2804 m_el_pt->set_node_update_info(geom_object_pt);
2805
2806 /// / Now loop over nodes in element
2807 // unsigned n_node = this->nnode();
2808 // for (unsigned j=0;j<n_node;j++)
2809 // {
2810 // // Get local coordinate in element (Vector sets its own size)
2811 // Vector<double> s_in_node_update_element;
2812 // this->local_coordinate_of_node(j,s_in_node_update_element);
2813 //
2814 // // Pass the lot to the node
2815 // static_cast<MacroElementNodeUpdateNode*>(this->node_pt(j))->
2816 // set_node_update_info(this,s_in_node_update_element,m_el_pt->geom_object_pt());
2817 // }
2818
2819 /// /BENFLAG:
2820 // std::cout << "Checking that all the nodes have this as their update
2821 // element..." << std::endl;
2822 // std::cout << "this = " << this << std::endl;
2823 // for(unsigned j=0; j<this->nnode(); j++)
2824 // {
2825 // //std::cout << this->node_pt(j) << ": [" << this->node_pt(j)->x(0)
2826 // << "," << this->node_pt(j)->x(1) << "] update element: " <<
2827 // dynamic_cast<MacroElementNodeUpdateNode*>(this->node_pt(j))->node_update_element_pt()
2828 // << std::endl; MacroElementNodeUpdateNode* mac_nod_pt =
2829 // dynamic_cast<MacroElementNodeUpdateNode*>(this->node_pt(j));
2830 // if(mac_nod_pt->node_update_element_pt()!=this)
2831 // {
2832 // std::cout << "Something's not right! Update element is wrong..." <<
2833 // std::endl;
2834 // }
2835 // FiniteElement* up_el_pt =
2836 // dynamic_cast<FiniteElement*>(mac_nod_pt->node_update_element_pt());
2837 // bool not_good = true;
2838 // for(unsigned l=0; l<up_el_pt->nnode(); l++)
2839 // {
2840 // if(up_el_pt->node_pt(l)==mac_nod_pt)
2841 // {
2842 // not_good = false;
2843 // break;
2844 // }
2845 // }
2846 // if(not_good==true)
2847 // {
2848 // std::cout << "Macro node doesn't belong to its update element!" <<
2849 // std::endl;
2850 // }
2851 // }
2852
2853
2854 // Loop over all nodes in element again, to re-set the positions
2855 // This must be done using the new element's macro-element
2856 // representation, rather than the old version which may be
2857 // of a different p-order!
2858 for (unsigned i0 = 0; i0 < P_order; i0++)
2859 {
2860 // Get the fractional position of the node in the direction of s[0]
2862 // Local coordinate
2863 s[0] = s_lo[0] + (s_hi[0] - s_lo[0]) * s_fraction[0];
2864
2865 for (unsigned i1 = 0; i1 < P_order; i1++)
2866 {
2867 // Get the fractional position of the node in the direction of s[1]
2869 // Local coordinate
2870 s[1] = s_lo[1] + (s_hi[1] - s_lo[1]) * s_fraction[1];
2871
2872 // Local node number
2873 jnod = i0 + P_order * i1;
2874
2875 // Loop over # of history values
2876 for (unsigned t = 0; t < ntstorage; t++)
2877 {
2878 // Get position from father element -- this uses the macro
2879 // element representation if appropriate. If the node
2880 // turns out to be a hanging node later on, then
2881 // its position gets adjusted in line with its
2882 // hanging node interpolation.
2884 this->get_x(t, s, x_prev);
2885
2886 // Set previous positions of the new node
2887 for (unsigned i = 0; i < 2; i++)
2888 {
2889 this->node_pt(jnod)->x(t, i) = x_prev[i];
2890 }
2891 }
2892 }
2893 }
2894 }
2895
2896 // Not necessary to delete the old nodes since all original nodes are in the
2897 // current mesh and so will be pruned as part of the mesh adaption process.
2898
2899 // Do any further-build required
2900 this->further_build();
2901 }
2902
2903 //=======================================================================
2904 /// Shape functions for PRefineableQElement<DIM>
2905 //=======================================================================
2906 template<unsigned INITIAL_NNODE_1D>
2908 Shape& psi) const
2909 {
2910 switch (p_order())
2911 {
2912 case 2:
2913 {
2914 // Call the OneDimensional Shape functions
2917
2918 // Now let's loop over the nodal points in the element
2919 // and copy the values back in
2920 for (unsigned i = 0; i < 2; i++)
2921 {
2922 for (unsigned j = 0; j < 2; j++)
2923 {
2924 psi(2 * i + j) = psi2[i] * psi1[j];
2925 }
2926 }
2927 break;
2928 }
2929 case 3:
2930 {
2931 // Call the OneDimensional Shape functions
2934
2935 // Now let's loop over the nodal points in the element
2936 // and copy the values back in
2937 for (unsigned i = 0; i < 3; i++)
2938 {
2939 for (unsigned j = 0; j < 3; j++)
2940 {
2941 psi(3 * i + j) = psi2[i] * psi1[j];
2942 }
2943 }
2944 break;
2945 }
2946 case 4:
2947 {
2948 // Call the OneDimensional Shape functions
2951
2952 // Now let's loop over the nodal points in the element
2953 // and copy the values back in
2954 for (unsigned i = 0; i < 4; i++)
2955 {
2956 for (unsigned j = 0; j < 4; j++)
2957 {
2958 psi(4 * i + j) = psi2[i] * psi1[j];
2959 }
2960 }
2961 break;
2962 }
2963 case 5:
2964 {
2965 // Call the OneDimensional Shape functions
2968
2969 // Now let's loop over the nodal points in the element
2970 // and copy the values back in
2971 for (unsigned i = 0; i < 5; i++)
2972 {
2973 for (unsigned j = 0; j < 5; j++)
2974 {
2975 psi(5 * i + j) = psi2[i] * psi1[j];
2976 }
2977 }
2978 break;
2979 }
2980 case 6:
2981 {
2982 // Call the OneDimensional Shape functions
2985
2986 // Now let's loop over the nodal points in the element
2987 // and copy the values back in
2988 for (unsigned i = 0; i < 6; i++)
2989 {
2990 for (unsigned j = 0; j < 6; j++)
2991 {
2992 psi(6 * i + j) = psi2[i] * psi1[j];
2993 }
2994 }
2995 break;
2996 }
2997 case 7:
2998 {
2999 // Call the OneDimensional Shape functions
3002
3003 // Now let's loop over the nodal points in the element
3004 // and copy the values back in
3005 for (unsigned i = 0; i < 7; i++)
3006 {
3007 for (unsigned j = 0; j < 7; j++)
3008 {
3009 psi(7 * i + j) = psi2[i] * psi1[j];
3010 }
3011 }
3012 break;
3013 }
3014 default:
3015 std::ostringstream error_message;
3016 error_message << "\nERROR: Exceeded maximum polynomial order for";
3017 error_message << "\n polynomial order for shape functions.\n";
3018 throw OomphLibError(error_message.str(),
3021 }
3022 }
3023
3024 //=======================================================================
3025 /// Derivatives of shape functions for PRefineableQElement<DIM>
3026 //=======================================================================
3027 template<unsigned INITIAL_NNODE_1D>
3029 const Vector<double>& s, Shape& psi, DShape& dpsids) const
3030 {
3031 switch (p_order())
3032 {
3033 case 2:
3034 {
3035 // Call the shape functions and derivatives
3039
3040 // Index for the shape functions
3041 unsigned index = 0;
3042 // Loop over shape functions in element
3043 for (unsigned i = 0; i < 2; i++)
3044 {
3045 for (unsigned j = 0; j < 2; j++)
3046 {
3047 // Assign the values
3048 dpsids(index, 0) = psi2[i] * dpsi1ds[j];
3049 dpsids(index, 1) = dpsi2ds[i] * psi1[j];
3050 psi[index] = psi2[i] * psi1[j];
3051 // Increment the index
3052 ++index;
3053 }
3054 }
3055 break;
3056 }
3057 case 3:
3058 {
3059 // Call the shape functions and derivatives
3063
3064 // Index for the shape functions
3065 unsigned index = 0;
3066 // Loop over shape functions in element
3067 for (unsigned i = 0; i < 3; i++)
3068 {
3069 for (unsigned j = 0; j < 3; j++)
3070 {
3071 // Assign the values
3072 dpsids(index, 0) = psi2[i] * dpsi1ds[j];
3073 dpsids(index, 1) = dpsi2ds[i] * psi1[j];
3074 psi[index] = psi2[i] * psi1[j];
3075 // Increment the index
3076 ++index;
3077 }
3078 }
3079 break;
3080 }
3081 case 4:
3082 {
3083 // Call the shape functions and derivatives
3087
3088 // Index for the shape functions
3089 unsigned index = 0;
3090 // Loop over shape functions in element
3091 for (unsigned i = 0; i < 4; i++)
3092 {
3093 for (unsigned j = 0; j < 4; j++)
3094 {
3095 // Assign the values
3096 dpsids(index, 0) = psi2[i] * dpsi1ds[j];
3097 dpsids(index, 1) = dpsi2ds[i] * psi1[j];
3098 psi[index] = psi2[i] * psi1[j];
3099 // Increment the index
3100 ++index;
3101 }
3102 }
3103 break;
3104 }
3105 case 5:
3106 {
3107 // Call the shape functions and derivatives
3111
3112 // Index for the shape functions
3113 unsigned index = 0;
3114 // Loop over shape functions in element
3115 for (unsigned i = 0; i < 5; i++)
3116 {
3117 for (unsigned j = 0; j < 5; j++)
3118 {
3119 // Assign the values
3120 dpsids(index, 0) = psi2[i] * dpsi1ds[j];
3121 dpsids(index, 1) = dpsi2ds[i] * psi1[j];
3122 psi[index] = psi2[i] * psi1[j];
3123 // Increment the index
3124 ++index;
3125 }
3126 }
3127 break;
3128 }
3129 case 6:
3130 {
3131 // Call the shape functions and derivatives
3135
3136 // Index for the shape functions
3137 unsigned index = 0;
3138 // Loop over shape functions in element
3139 for (unsigned i = 0; i < 6; i++)
3140 {
3141 for (unsigned j = 0; j < 6; j++)
3142 {
3143 // Assign the values
3144 dpsids(index, 0) = psi2[i] * dpsi1ds[j];
3145 dpsids(index, 1) = dpsi2ds[i] * psi1[j];
3146 psi[index] = psi2[i] * psi1[j];
3147 // Increment the index
3148 ++index;
3149 }
3150 }
3151 break;
3152 }
3153 case 7:
3154 {
3155 // Call the shape functions and derivatives
3159
3160 // Index for the shape functions
3161 unsigned index = 0;
3162 // Loop over shape functions in element
3163 for (unsigned i = 0; i < 7; i++)
3164 {
3165 for (unsigned j = 0; j < 7; j++)
3166 {
3167 // Assign the values
3168 dpsids(index, 0) = psi2[i] * dpsi1ds[j];
3169 dpsids(index, 1) = dpsi2ds[i] * psi1[j];
3170 psi[index] = psi2[i] * psi1[j];
3171 // Increment the index
3172 ++index;
3173 }
3174 }
3175 break;
3176 }
3177 default:
3178 std::ostringstream error_message;
3179 error_message << "\nERROR: Exceeded maximum polynomial order for";
3180 error_message << "\n polynomial order for shape functions.\n";
3181 throw OomphLibError(error_message.str(),
3184 }
3185 }
3186
3187 //=======================================================================
3188 /// Second derivatives of shape functions for PRefineableQElement<DIM>
3189 /// d2psids(i,0) = \f$ d^2 \psi_j / d s^2 \f$
3190 //=======================================================================
3191 template<unsigned INITIAL_NNODE_1D>
3193 const Vector<double>& s, Shape& psi, DShape& dpsids, DShape& d2psids) const
3194 {
3195 std::ostringstream error_message;
3196 error_message
3197 << "\nd2shape_local currently not implemented for this element\n";
3198 throw OomphLibError(
3199 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
3200 }
3201
3202 //=======================================================================
3203 /// Rebuild the element from nodes found in its sons
3204 /// Adjusts its p-order to be the maximum of its sons' p-orders
3205 //=======================================================================
3206 template<unsigned INITIAL_NNODE_1D>
3208 Mesh*& mesh_pt)
3209 {
3210 using namespace QuadTreeNames;
3211
3212 // Get p-orders of sons
3213 unsigned n_sons = this->tree_pt()->nsons();
3215 unsigned max_son_p_order = 0;
3216 for (unsigned ison = 0; ison < n_sons; ison++)
3217 {
3219 this->tree_pt()->son_pt(ison)->object_pt());
3220 son_p_order[ison] = el_pt->p_order();
3223 }
3224
3225 unsigned old_Nnode = this->nnode();
3226 unsigned old_P_order = this->p_order();
3227 // Set p-order of the element
3228 this->p_order() = max_son_p_order;
3229
3230 // Change integration scheme
3231 delete this->integral_pt();
3232 switch (this->p_order())
3233 {
3234 case 2:
3236 break;
3237 case 3:
3239 break;
3240 case 4:
3242 break;
3243 case 5:
3245 break;
3246 case 6:
3248 break;
3249 case 7:
3251 break;
3252 default:
3253 std::ostringstream error_message;
3254 error_message << "\nERROR: Exceeded maximum polynomial order for";
3255 error_message << "\n integration scheme.\n";
3256 throw OomphLibError(error_message.str(),
3259 }
3260
3261 // Back up pointers to old nodes before they are lost
3263 for (unsigned n = 0; n < old_Nnode; n++)
3264 {
3265 old_node_pt[n] = this->node_pt(n);
3266 }
3267
3268 // Allocate new space for Nodes (at the element level)
3269 this->set_n_node(this->p_order() * this->p_order());
3270
3271 // Copy vertex nodes which were populated in the pre-build
3272 this->node_pt(0) = old_node_pt[0];
3273 this->node_pt(this->p_order() - 1) = old_node_pt[old_P_order - 1];
3274 this->node_pt(this->p_order() * (this->p_order() - 1)) =
3276 this->node_pt(this->p_order() * this->p_order() - 1) =
3278
3279 // Copy midpoint nodes from sons if new p-order is odd
3280 if (this->p_order() % 2 == 1)
3281 {
3282 // Work out which is midpoint node
3283 unsigned midpoint = (this->p_order() - 1) / 2;
3284
3285 // Bottom edge
3286 this->node_pt(midpoint) = dynamic_cast<RefineableQElement<2>*>(
3287 quadtree_pt()->son_pt(SW)->object_pt())
3288 ->vertex_node_pt(1);
3289 // Left edge
3290 this->node_pt(midpoint * this->p_order()) =
3291 dynamic_cast<RefineableQElement<2>*>(
3292 quadtree_pt()->son_pt(SW)->object_pt())
3293 ->vertex_node_pt(2);
3294 // Top edge
3295 this->node_pt((this->p_order() - 1) * this->p_order() + midpoint) =
3296 dynamic_cast<RefineableQElement<2>*>(
3297 quadtree_pt()->son_pt(NE)->object_pt())
3298 ->vertex_node_pt(2);
3299 // Right edge
3300 this->node_pt((midpoint + 1) * this->p_order() - 1) =
3301 dynamic_cast<RefineableQElement<2>*>(
3302 quadtree_pt()->son_pt(NE)->object_pt())
3303 ->vertex_node_pt(1);
3304 }
3305
3306
3307 // The timestepper should be the same for all nodes and node 0 should
3308 // never be deleted.
3309 if (this->node_pt(0) == 0)
3310 {
3311 throw OomphLibError("The Corner node (0) does not exist",
3314 }
3315
3317 unsigned ntstorage = time_stepper_pt->ntstorage();
3318
3319 unsigned jnod = 0;
3321 // Loop over the nodes in the element
3322 unsigned n_p = this->nnode_1d();
3323 for (unsigned i0 = 0; i0 < n_p; i0++)
3324 {
3325 // Get the fractional position of the node
3327 // Local coordinate
3328 s[0] = -1.0 + 2.0 * s_fraction[0];
3329
3330 for (unsigned i1 = 0; i1 < n_p; i1++)
3331 {
3332 // Get the fractional position of the node in the direction of s[1]
3334 // Local coordinate in father element
3335 s[1] = -1.0 + 2.0 * s_fraction[1];
3336
3337 // Set the local node number
3338 jnod = i0 + n_p * i1;
3339
3340 // Initialise flag: So far, this node hasn't been built
3341 // or copied yet
3342 bool node_done = false;
3343
3344 // Get the pointer to the node in this element, returns NULL
3345 // if there is not node
3347
3348 // Does this node already exist in this element?
3349 //----------------------------------------------
3350 if (created_node_pt != 0)
3351 {
3352 // Copy node across
3353 this->node_pt(jnod) = created_node_pt;
3354
3355 // Node has been created by copy
3356 node_done = true;
3357 }
3358 // Node does not exist in this element but might already
3359 //------------------------------------------------------
3360 // have been created by neighbouring elements
3361 //-------------------------------------------
3362 else
3363 {
3364 // Was the node created by one of its neighbours
3365 // Whether or not the node lies on an edge can be calculated
3366 // by from the fractional position
3367 bool is_periodic = false;
3368 created_node_pt = node_created_by_neighbour(s_fraction, is_periodic);
3369
3370 // If the node was so created, assign the pointers
3371 if (created_node_pt != 0)
3372 {
3373 // If the node is periodic
3374 if (is_periodic)
3375 {
3376 throw OomphLibError("Cannot handle periodic nodes yet",
3379 }
3380 // Non-periodic case, just set the pointer
3381 else
3382 {
3383 this->node_pt(jnod) = created_node_pt;
3384 }
3385 // Node has been created
3386 node_done = true;
3387 }
3388 } // Node does not exist in this element
3389
3390 // Node has not been built anywhere ---> build it here
3391 if (!node_done)
3392 {
3393 // First, find the son element in which the node should live
3394
3395 // Find coordinates in the sons
3397 using namespace QuadTreeNames;
3398 int son = -10;
3399 // If negative on the west side
3400 if (s_fraction[0] < 0.5)
3401 {
3402 // On the south side
3403 if (s_fraction[1] < 0.5)
3404 {
3405 // It's the southwest son
3406 son = SW;
3407 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
3408 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
3409 }
3410 // On the north side
3411 else
3412 {
3413 // It's the northwest son
3414 son = NW;
3415 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
3416 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
3417 }
3418 }
3419 else
3420 {
3421 // On the south side
3422 if (s_fraction[1] < 0.5)
3423 {
3424 // It's the southeast son
3425 son = SE;
3426 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
3427 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
3428 }
3429 // On the north side
3430 else
3431 {
3432 // It's the northeast son
3433 son = NE;
3434 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
3435 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
3436 }
3437 }
3438
3439 // Get the pointer to the son element in which the new node
3440 // would live
3443 this->tree_pt()->son_pt(son)->object_pt());
3444
3445 // If we are rebuilding, then worry about the boundary conditions
3446 // Find the boundary of the node
3447 // Initially none
3448 int boundary = Tree::OMEGA;
3449 // If we are on the western boundary
3450 if (i0 == 0)
3451 {
3452 boundary = W;
3453 }
3454 // If we are on the eastern boundary
3455 else if (i0 == n_p - 1)
3456 {
3457 boundary = E;
3458 }
3459
3460 // If we are on the southern boundary
3461 if (i1 == 0)
3462 {
3463 // If we already have already set the boundary, we're on a corner
3464 switch (boundary)
3465 {
3466 case W:
3467 boundary = SW;
3468 break;
3469 case E:
3470 boundary = SE;
3471 break;
3472 // Boundary not set
3473 default:
3474 boundary = S;
3475 break;
3476 }
3477 }
3478 // If we are the northern bounadry
3479 else if (i1 == n_p - 1)
3480 {
3481 // If we already have a boundary
3482 switch (boundary)
3483 {
3484 case W:
3485 boundary = NW;
3486 break;
3487 case E:
3488 boundary = NE;
3489 break;
3490 default:
3491 boundary = N;
3492 break;
3493 }
3494 }
3495
3496 // set of boundaries that this edge in the son lives on
3497 std::set<unsigned> boundaries;
3498
3499 // Now get the boundary conditions from the son
3500 // The boundaries will be common to the son because there can be
3501 // no rotations here
3502 if (boundary != Tree::OMEGA)
3503 {
3504 son_el_pt->get_boundaries(boundary, boundaries);
3505 }
3506
3507 // If the node lives on a boundary:
3508 // Construct a boundary node,
3509 // Get boundary conditions and
3510 // update all lookup schemes
3511 if (boundaries.size() > 0)
3512 {
3513 // Construct the new node
3516
3517 // Get the boundary conditions from the son
3518 Vector<int> bound_cons(this->ncont_interpolated_values());
3519 son_el_pt->get_bcs(boundary, bound_cons);
3520
3521 // Loop over the values and pin if necessary
3522 unsigned nval = created_node_pt->nvalue();
3523 for (unsigned k = 0; k < nval; k++)
3524 {
3525 if (bound_cons[k])
3526 {
3527 created_node_pt->pin(k);
3528 }
3529 }
3530
3531 // Solid node? If so, deal with the positional boundary
3532 // conditions:
3534 dynamic_cast<SolidNode*>(created_node_pt);
3535 if (solid_node_pt != 0)
3536 {
3537 // Get the positional boundary conditions from the father:
3538 unsigned n_dim = created_node_pt->ndim();
3541 dynamic_cast<RefineableSolidQElement<2>*>(son_el_pt);
3542#ifdef PARANOID
3543 if (son_solid_el_pt == 0)
3544 {
3545 std::string error_message =
3546 "We have a SolidNode outside a refineable SolidElement\n";
3547 error_message +=
3548 "during mesh refinement -- this doesn't make sense\n";
3549
3550 throw OomphLibError(error_message,
3553 }
3554#endif
3555 son_solid_el_pt->get_solid_bcs(boundary, solid_bound_cons);
3556
3557 // Loop over the positions and pin, if necessary
3558 for (unsigned k = 0; k < n_dim; k++)
3559 {
3560 if (solid_bound_cons[k])
3561 {
3562 solid_node_pt->pin_position(k);
3563 }
3564 }
3565 } // End of if solid_node_pt
3566
3567
3568 // Next we update the boundary look-up schemes
3569 // Loop over the boundaries stored in the set
3570 for (std::set<unsigned>::iterator it = boundaries.begin();
3571 it != boundaries.end();
3572 ++it)
3573 {
3574 // Add the node to the boundary
3576
3577 // If we have set an intrinsic coordinate on this
3578 // mesh boundary then it must also be interpolated on
3579 // the new node
3580 // Now interpolate the intrinsic boundary coordinate
3581 if (mesh_pt->boundary_coordinate_exists(*it) == true)
3582 {
3584 son_el_pt->interpolated_zeta_on_edge(
3585 *it, boundary, s_in_son, zeta);
3586
3587 created_node_pt->set_coordinates_on_boundary(*it, zeta);
3588 }
3589 }
3590 }
3591 // Otherwise the node is not on a Mesh boundary
3592 // and we create a normal "bulk" node
3593 else
3594 {
3595 // Construct the new node
3597 }
3598
3599 // Now we set the position and values at the newly created node
3600
3601 // In the first instance use macro element or FE representation
3602 // to create past and present nodal positions.
3603 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC
3604 // ELEMENTS AS NOT ALL OF THEM NECESSARILY IMPLEMENT
3605 // NONTRIVIAL NODE UPDATE FUNCTIONS. CALLING
3606 // THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL LEAVE
3607 // THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
3608 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL
3609 // NOT ASSIGN SENSIBLE INITIAL POSITONS!
3610
3611 // Loop over # of history values
3612 // Loop over # of history values
3613 for (unsigned t = 0; t < ntstorage; t++)
3614 {
3615 using namespace QuadTreeNames;
3616 // Get the position from the son
3618
3619 // Now let's fill in the value
3621 for (unsigned i = 0; i < 2; i++)
3622 {
3623 created_node_pt->x(t, i) = x_prev[i];
3624 }
3625 }
3626
3627 // Now set up the values
3628 // Loop over all history values
3629 for (unsigned t = 0; t < ntstorage; t++)
3630 {
3631 // Get values from father element
3632 // Note: get_interpolated_values() sets Vector size itself.
3634 son_el_pt->get_interpolated_values(t, s_in_son, prev_values);
3635
3636 // Initialise the values at the new node
3637 for (unsigned k = 0; k < created_node_pt->nvalue(); k++)
3638 {
3639 created_node_pt->set_value(t, k, prev_values[k]);
3640 }
3641 }
3642
3643 // Add the node to the mesh
3644 mesh_pt->add_node_pt(created_node_pt);
3645
3646 // Check if the element is an algebraic element
3648 dynamic_cast<AlgebraicElementBase*>(this);
3649
3650 // If we do have an algebraic element
3651 if (alg_el_pt != 0)
3652 {
3653 std::string error_message =
3654 "Have not implemented rebuilding from sons for";
3655 error_message += "Algebraic p-refineable elements yet\n";
3656
3657 throw OomphLibError(
3658 error_message,
3659 "PRefineableQElement<2,INITIAL_NNODE_1D>::rebuild_from_sons()",
3661 }
3662
3663 } // End of the case when we build the node ourselves
3664 }
3665 } // End of loop over all nodes in element
3666
3667
3668 // If the element is a MacroElementNodeUpdateElement, set the update
3669 // parameters for the current element's nodes. These need to be reset
3670 // (as in MacroElementNodeUpdateElement<ELEMENT>::rebuild_from_sons())
3671 // because the nodes in this element have changed
3673 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
3674 if (m_el_pt != 0)
3675 {
3676 // Loop over the nodes
3677 for (unsigned j = 0; j < this->nnode(); j++)
3678 {
3679 // Get local coordinate in element (Vector sets its own size)
3680 Vector<double> s_in_node_update_element;
3681 this->local_coordinate_of_node(j, s_in_node_update_element);
3682
3683 // Get vector of geometric objects
3684 Vector<GeomObject*> geom_object_pt(m_el_pt->geom_object_pt());
3685
3686 // Pass the lot to the node
3687 static_cast<MacroElementNodeUpdateNode*>(this->node_pt(j))
3688 ->set_node_update_info(
3689 this, s_in_node_update_element, geom_object_pt);
3690 }
3691 }
3692
3693 // MacroElementNodeUpdateElementBase* m_el_pt=dynamic_cast<
3694 // MacroElementNodeUpdateElementBase*>(this);
3695 // if(m_el_pt!=0)
3696 // {
3697 // Loop over all nodes in element again, to re-set the positions
3698 // This must be done using the new element's macro-element
3699 // representation, rather than the old version which may be
3700 // of a different p-order!
3701 for (unsigned i0 = 0; i0 < n_p; i0++)
3702 {
3703 // Get the fractional position of the node
3705 // Local coordinate
3706 s[0] = -1.0 + 2.0 * s_fraction[0];
3707
3708 for (unsigned i1 = 0; i1 < n_p; i1++)
3709 {
3710 // Get the fractional position of the node in the direction of s[1]
3712 // Local coordinate in father element
3713 s[1] = -1.0 + 2.0 * s_fraction[1];
3714
3715 // Set the local node number
3716 jnod = i0 + n_p * i1;
3717
3718 // Loop over # of history values
3719 for (unsigned t = 0; t < ntstorage; t++)
3720 {
3721 // Get position from father element -- this uses the macro
3722 // element representation if appropriate. If the node
3723 // turns out to be a hanging node later on, then
3724 // its position gets adjusted in line with its
3725 // hanging node interpolation.
3727 this->get_x(t, s, x_prev);
3728
3729 // Set previous positions of the new node
3730 for (unsigned i = 0; i < 2; i++)
3731 {
3732 this->node_pt(jnod)->x(t, i) = x_prev[i];
3733 }
3734 }
3735 }
3736 }
3737 // }
3738 }
3739
3740 //=================================================================
3741 /// Check inter-element continuity of
3742 /// - nodal positions
3743 /// - (nodally) interpolated function values
3744 /// Overloaded to not check differences in the value. Mortaring
3745 /// doesn't enforce strong continuity between elements.
3746 //====================================================================
3747 template<unsigned INITIAL_NNODE_1D>
3749 double& max_error)
3750 {
3751 // Overloaded to *not* check for continuity in value of interpolated
3752 // variables. This is necessary because mortaring does not ensure continuity
3753 // across element boundaries. It therefore makes no sense to test for this.
3754
3755 // Dummy set max_error to 0
3756 max_error = 0.0;
3757
3758 // With macro-elements, (strong) continuity in position is nolonger
3759 // guaranteed either, so we don't check for this either. In fact, we do
3760 // nothing at all.
3761 if (this->macro_elem_pt() != 0)
3762 {
3763 // We have a macro element, so do nothing!
3764 return;
3765 }
3766
3767 using namespace QuadTreeNames;
3768
3769 // Number of nodes along edge
3770 unsigned n_p = nnode_1d();
3771
3772 // Number of timesteps (incl. present) for which continuity is
3773 // to be checked.
3774 unsigned n_time = 1;
3775
3776 // Initialise errors
3777 max_error = 0.0;
3779 double max_error_val = 0.0;
3780
3781 Vector<int> edges(4);
3782 edges[0] = S;
3783 edges[1] = N;
3784 edges[2] = W;
3785 edges[3] = E;
3786
3787 // Loop over the edges
3788 for (unsigned edge_counter = 0; edge_counter < 4; edge_counter++)
3789 {
3794
3795 // Find pointer to neighbour in this direction
3797 neigh_pt = quadtree_pt()->gteq_edge_neighbour(edges[edge_counter],
3799 s_lo_neigh,
3800 s_hi_neigh,
3801 neigh_edge,
3802 diff_level,
3804
3805 // Neighbour exists and has existing nodes
3806 if ((neigh_pt != 0) && (neigh_pt->object_pt()->nodes_built()))
3807 {
3808 // Need to exclude periodic nodes from this check
3809 // There are only periodic nodes if we are in a neighbouring tree
3810 bool is_periodic = false;
3812 {
3813 // Is it periodic
3814 is_periodic = this->tree_pt()->root_pt()->is_neighbour_periodic(
3816 }
3817
3818 // We also need to exclude edges which may have hanging nodes
3819 // because mortaring does not guarantee (strong) continuity
3820 // in position or in value at nonconforming element boundaries
3821 bool exclude_this_edge = false;
3822 if (diff_level != 0)
3823 {
3824 // h-type nonconformity (dependent)
3825 exclude_this_edge = true;
3826 }
3827 else if (neigh_pt->nsons() != 0)
3828 {
3829 // h-type nonconformity (master)
3830 exclude_this_edge = true;
3831 }
3832 else
3833 {
3834 unsigned my_p_order = this->p_order();
3835 unsigned neigh_p_order =
3836 dynamic_cast<PRefineableQElement*>(neigh_pt->object_pt())
3837 ->p_order();
3839 {
3840 // p-type nonconformity
3841 exclude_this_edge = true;
3842 }
3843 }
3844
3845 // With macro-elements, (strong) continuity in position is nolonger
3846 // guaranteed either, so we don't check for this either. In fact, we do
3847 // nothing at all.
3848 if (dynamic_cast<FiniteElement*>(neigh_pt->object_pt())
3849 ->macro_elem_pt() != 0)
3850 {
3851 // We have a macro element, so do nothing!
3852 break;
3853 }
3854
3855 // Check conforming edges
3856 if (!exclude_this_edge)
3857 {
3858 // Loop over nodes along the edge
3859 for (unsigned i0 = 0; i0 < n_p; i0++)
3860 {
3861 // Storage for pointer to the local node
3862 Node* local_node_pt = 0;
3863
3864 switch (edge_counter)
3865 {
3866 case 0:
3867 // Local fraction of node
3869 s_fraction[1] = 0.0;
3870 // Get pointer to local node
3871 local_node_pt = this->node_pt(i0);
3872 break;
3873
3874 case 1:
3875 // Local fraction of node
3877 s_fraction[1] = 1.0;
3878 // Get pointer to local node
3879 local_node_pt = this->node_pt(i0 + n_p * (n_p - 1));
3880 break;
3881
3882 case 2:
3883 // Local fraction of node
3884 s_fraction[0] = 0.0;
3886 // Get pointer to local node
3887 local_node_pt = this->node_pt(n_p * i0);
3888 break;
3889
3890 case 3:
3891 // Local fraction of node
3892 s_fraction[0] = 1.0;
3893 s_fraction[1] = this->local_one_d_fraction_of_node(i0, 1);
3894 // Get pointer to local node
3895 local_node_pt = this->node_pt(n_p - 1 + n_p * i0);
3896 break;
3897 }
3898
3899 // Calculate the local coordinate and the local coordinate as viewed
3900 // from the neighbour
3902 for (unsigned i = 0; i < 2; i++)
3903 {
3904 // Local coordinate in this element
3905 s[i] = -1.0 + 2.0 * s_fraction[i];
3906 // Local coordinate in the neighbour
3907 s_in_neighb[i] =
3908 s_lo_neigh[i] +
3910 }
3911
3912 // Loop over timesteps
3913 for (unsigned t = 0; t < n_time; t++)
3914 {
3915 // Get the nodal position from neighbour element
3917 neigh_pt->object_pt()->interpolated_x(
3919
3920 // Check error only if the node is NOT periodic
3921 if (is_periodic == false)
3922 {
3923 for (int i = 0; i < 2; i++)
3924 {
3925 // Find the spatial error
3926 double err =
3927 std::fabs(local_node_pt->x(t, i) - x_in_neighb[i]);
3928
3929 // If it's bigger than our tolerance, say so
3930 if (err > 1e-9)
3931 {
3932 oomph_info << "errx " << err << " " << t << " "
3933 << local_node_pt->x(t, i) << " "
3934 << x_in_neighb[i] << std::endl;
3935
3936 oomph_info << "at " << local_node_pt->x(0) << " "
3937 << local_node_pt->x(1) << std::endl;
3938 }
3939
3940 // If it's bigger than the previous max error, it is the
3941 // new max error!
3942 if (err > max_error_x[i])
3943 {
3944 max_error_x[i] = err;
3945 }
3946 }
3947 }
3948
3949 // Get the values from neighbour element. Note: # of values
3950 // gets set by routine (because in general we don't know
3951 // how many interpolated values a certain element has
3953 neigh_pt->object_pt()->get_interpolated_values(
3955
3956 // Get the values in current element.
3957 Vector<double> values;
3958 this->get_interpolated_values(t, s, values);
3959
3960 // Now figure out how many continuously interpolated values there
3961 // are
3962 unsigned num_val =
3963 neigh_pt->object_pt()->ncont_interpolated_values();
3964
3965 // Check error
3966 for (unsigned ival = 0; ival < num_val; ival++)
3967 {
3968 double err = std::fabs(values[ival] - values_in_neighb[ival]);
3969
3970 if (err > 1.0e-10)
3971 {
3972 oomph_info << local_node_pt->x(0) << " "
3973 << local_node_pt->x(1) << " \n# "
3974 << "erru (S)" << err << " " << ival << " "
3975 << this->get_node_number(local_node_pt) << " "
3976 << values[ival] << " " << values_in_neighb[ival]
3977 << std::endl;
3978 }
3979
3980 if (err > max_error_val)
3981 {
3983 }
3984 }
3985 }
3986 }
3987 }
3988 }
3989 }
3990
3991 max_error = max_error_x[0];
3992 if (max_error_x[1] > max_error) max_error = max_error_x[1];
3993 if (max_error_val > max_error) max_error = max_error_val;
3994
3995 if (max_error > 1e-9)
3996 {
3997 oomph_info << "\n#------------------------------------ \n#Max error ";
3998 oomph_info << max_error_x[0] << " " << max_error_x[1] << " "
3999 << max_error_val << std::endl;
4000 oomph_info << "#------------------------------------ \n " << std::endl;
4001 }
4002 }
4003
4004 //=================================================================
4005 /// Internal function to set up the hanging nodes on a particular
4006 /// edge of the element.
4007 /// Implements the mortarting method to enforce continuity weakly
4008 /// across non-conforming element boundaries \f$\Gamma\f$ using an
4009 /// integral matching condition
4010 /// \f[ \int_\Gamma (u_{\mbox{S}} - u_{\mbox{M}}) \psi \mbox{d} s = 0 \f]
4011 /// for all polynomials \f$\psi\f$ on \f$\Gamma\f$ of degree at most
4012 /// p-2 (where p is the spectral-order of the dependent element) and a
4013 /// vertex matching condition
4014 /// \f[ (u_{\mbox{S}} - u_{\mbox{M}})\big\vert_{\partial\Gamma} = 0.\f]
4015 ///
4016 /// The algorithm works as follows:
4017 /// - First the element determines if its edge my_edge is on the
4018 /// master or dependent side of the non-conformity.
4019 /// At h-type non-conformities
4020 /// we choose long edges to be masters, and at p-type nonconformities the
4021 /// edge with lower p-order is the master.
4022 /// - Mortaring is performed by the dependent side.
4023 /// - If a vertex node of the mortar is shared between master and dependent
4024 /// element then the vertex matching condition is enforced automatically.
4025 /// Otherwise it must be imposed by constraining its value to that at on
4026 /// the master side.
4027 /// - The integral matching condition is discretised and the mortar test
4028 /// functions \f$ \psi \f$ are chosen to be derivatives of Legendre
4029 /// polynomials of degree p-1.
4030 /// - The mortar mass matrix M is constructed. Its entries are the
4031 /// mortar test functions evaluated at the dependent nodal positions,
4032 /// so it is diagonal.
4033 /// - The local projection matrix is constructed for the master element by
4034 /// applying the discretised integral matching condition along the mortar
4035 /// using the appropriate quadrature order.
4036 /// - The global projection matrix is then assembled by subtracting
4037 /// contributions from the mortar vertex nodes.
4038 /// - The mortar system \f$ M\xi^s = P\hat{\xi^m} \f$ is constructed,
4039 /// where \f$ \xi^m \f$ and \f$ \xi^s \f$ are the nodal values at
4040 /// the master and dependent nodes respectively.
4041 /// - The conformity matrix \f$ C = M^{-1}P \f$ is computed. This is
4042 /// straightforward since the mass matrix is diagonal.
4043 /// - Finally, the master nodes and weights for each dependent node
4044 /// are read from
4045 /// the conformity matrix and stored in the dependents' hanging schemes.
4046 ///
4047 /// The positions of the dependent nodes are set to be consistent with their
4048 /// hanging schemes.
4049 //=================================================================
4050 template<unsigned INITIAL_NNODE_1D>
4052 const int& value_id, const int& my_edge, std::ofstream& output_hangfile)
4053 {
4054 using namespace QuadTreeNames;
4055
4061
4062 // Find pointer to neighbour in this direction
4064 neigh_pt = this->quadtree_pt()->gteq_edge_neighbour(my_edge,
4066 s_lo_neigh,
4067 s_hi_neigh,
4068 neigh_edge,
4069 diff_level,
4071
4072 // Work out master/dependent edges
4073 //----------------------------
4074
4075 // Set up booleans
4076 // bool h_type_master = false;
4077 bool h_type_dependent = false;
4078 // bool p_type_master = false;
4079 bool p_type_dependent = false;
4080
4081 // Neighbour exists and all nodes have been created
4082 if (neigh_pt != 0)
4083 {
4084 // Check if neighbour is bigger than me
4085 if (diff_level != 0)
4086 {
4087 // Dependent at h-type non-conformity
4088 h_type_dependent = true;
4089 }
4090 // Check if neighbour is the same size as me
4091 else if (neigh_pt->nsons() == 0)
4092 {
4093 // Neighbour is same size as me
4094 // Find p-orders of each element
4095 unsigned my_p_order =
4096 dynamic_cast<PRefineableQElement<2, INITIAL_NNODE_1D>*>(this)
4097 ->p_order();
4098 unsigned neigh_p_order =
4100 neigh_pt->object_pt())
4101 ->p_order();
4102
4103 // Check for p-type non-conformity
4105 {
4106 // At a conforming interface
4107 }
4108 else if (neigh_p_order < my_p_order)
4109 {
4110 // Dependent at p-type non-conformity
4111 p_type_dependent = true;
4112 }
4113 else
4114 {
4115 // Master at p-type non-conformity
4116 // p_type_master = true;
4117 }
4118 }
4119 // Neighbour must be smaller than me
4120 else
4121 {
4122 // Master at h-type non-conformity
4123 // h_type_master = true;
4124 }
4125 }
4126 else
4127 {
4128 // Edge is on a boundary
4129 }
4130
4131 // Now do the mortaring
4132 //---------------------
4134 {
4135 // Compute the active coordinate index along the this side of mortar
4136 unsigned active_coord_index;
4137 if (my_edge == N || my_edge == S) active_coord_index = 0;
4138 else if (my_edge == E || my_edge == W)
4140 else
4141 {
4142 throw OomphLibError("Cannot transform coordinates",
4145 }
4146
4147 // Get pointer to neighbouring master element (in p-refineable form)
4150 neigh_pt->object_pt());
4151
4152 // Create vector of master and dependent nodes
4153 //----------------------------------------
4154 Vector<Node*> master_node_pt, dependent_node_pt;
4157
4158 // Number of nodes in one dimension
4159 const unsigned my_n_p = this->ninterpolating_node_1d(value_id);
4160 const unsigned neigh_n_p = neigh_obj_pt->ninterpolating_node_1d(value_id);
4161
4162 // Test for the periodic node case
4163 // Are we crossing a periodic boundary
4164 bool is_periodic = false;
4166 {
4167 is_periodic =
4168 this->tree_pt()->root_pt()->is_neighbour_periodic(my_edge);
4169 }
4170
4171 // If it is periodic we actually need to get the node in
4172 // the neighbour of the neighbour (which will be a parent of
4173 // the present element) so that the "fixed" coordinate is
4174 // correctly calculated.
4175 // The idea is to replace the neigh_pt and associated data
4176 // with those of the neighbour of the neighbour
4177 if (is_periodic)
4178 {
4179 throw OomphLibError(
4180 "Cannot do mortaring with periodic hanging nodes yet! (Fix me!)",
4181 "PRefineableQElement<2,INITIAL_NNODE_1D>::quad_hang_helper()",
4183 } // End of special treatment for periodic hanging nodes
4184
4185 // Storage for pointers to the nodes and their numbers along the master
4186 // edge
4187 unsigned neighbour_node_number = 0;
4189
4190 // Loop over nodes along the edge
4191 for (unsigned i0 = 0; i0 < neigh_n_p; i0++)
4192 {
4193 // Find the neighbour's node
4194 switch (neigh_edge)
4195 {
4196 case N:
4198 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
4200 break;
4201
4202 case S:
4204 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
4206 break;
4207
4208 case E:
4210 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
4212 break;
4213
4214 case W:
4216 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
4218 break;
4219
4220 default:
4221 throw OomphLibError("my_edge not N, S, W, E\n",
4224 }
4225
4226 // Set node as master node
4228 master_node_pt.push_back(neighbour_node_pt);
4229 }
4230
4231 // Storage for pointers to the local nodes and their numbers along my edge
4232 unsigned local_node_number = 0;
4233 Node* local_node_pt = 0;
4234
4235 // Loop over the nodes along my edge
4236 for (unsigned i0 = 0; i0 < my_n_p; i0++)
4237 {
4238 // Storage for the fractional position of the node in the element
4240
4241 // Find the local node and the fractional position of the node
4242 // which depends on the edge, of course
4243 switch (my_edge)
4244 {
4245 case N:
4246 s_fraction[0] =
4247 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
4248 s_fraction[1] = 1.0;
4249 local_node_number = i0 + my_n_p * (my_n_p - 1);
4251 this->interpolating_node_pt(local_node_number, value_id);
4252 break;
4253
4254 case S:
4255 s_fraction[0] =
4256 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
4257 s_fraction[1] = 0.0;
4260 this->interpolating_node_pt(local_node_number, value_id);
4261 break;
4262
4263 case E:
4264 s_fraction[0] = 1.0;
4265 s_fraction[1] =
4266 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
4269 this->interpolating_node_pt(local_node_number, value_id);
4270 break;
4271
4272 case W:
4273 s_fraction[0] = 0.0;
4274 s_fraction[1] =
4275 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
4278 this->interpolating_node_pt(local_node_number, value_id);
4279 break;
4280
4281 default:
4282 throw OomphLibError("my_edge not N, S, W, E\n",
4285 }
4286
4287 // Add node to vector of dependent element nodes
4290
4291 // Store node's local fraction
4293 }
4294
4295 // Store the number of dependent and master nodes for use later
4296 const unsigned n_dependent_nodes = dependent_node_pt.size();
4297 const unsigned n_master_nodes = master_node_pt.size();
4298 const unsigned dependent_element_nnode_1d = my_n_p;
4299 const unsigned master_element_nnode_1d = neigh_n_p;
4300
4301 // Storage for master shapes
4302 Shape master_shapes(neigh_obj_pt->ninterpolating_node(value_id));
4303
4304 // Get master and dependent nodal positions
4305 //-------------------------------------
4314
4315 // Apply the vertex matching condition
4316 //------------------------------------
4317 // Vertiex matching is ensured automatically in cases where there is a
4318 // node at each end of the mortar that is shared between the master and
4319 // dependent elements. Where this is not the case, the vertex matching
4320 // condition must be enforced by constraining the value of the unknown at
4321 // the node on the dependent side to be the same as the value at that
4322 // location in the master.
4323
4324 // Store positions of the mortar vertex/non-vertex nodes in the dependent
4325 // element
4326 const unsigned n_mortar_vertices = 2;
4328 vertex_pos[0] = 0;
4329 vertex_pos[1] = this->ninterpolating_node_1d(value_id) - 1;
4331 for (unsigned i = 0; i < my_n_p - n_mortar_vertices; i++)
4332 {
4333 non_vertex_pos[i] = i + 1;
4334 }
4335
4336 // Check if the mortar vertices are shared
4337 for (unsigned v = 0; v < n_mortar_vertices; v++)
4338 {
4339 // Search master node storage for the node
4340 bool node_is_shared = false;
4341 for (unsigned i = 0; i < master_node_pt.size(); i++)
4342 {
4343 if (dependent_node_pt[vertex_pos[v]] == master_node_pt[i])
4344 {
4345 node_is_shared = true;
4346 break;
4347 }
4348 }
4349
4350 // If the node is not shared then we must constrain its value by setting
4351 // up a hanging scheme
4352 if (!node_is_shared)
4353 {
4354 // Calculate weights. These are just the master shapes evaluated at
4355 // this dependent node's position
4356
4357 // Work out this node's location in the master
4359 for (unsigned i = 0; i < 2; i++)
4360 {
4361 s_in_neigh[i] =
4362 s_lo_neigh[i] +
4364 (s_hi_neigh[i] - s_lo_neigh[i]);
4365 }
4366
4367 // Get master shapes at dependent nodal positions
4368 neigh_obj_pt->interpolating_basis(
4370
4371 // Remove any existing hanging node info
4372 // (This may be a bit wasteful, but guarantees correctness)
4373 dependent_node_pt[vertex_pos[v]]->set_nonhanging();
4374
4375 // Set up hanging scheme for this node
4377 for (unsigned m = 0; m < n_master_nodes; m++)
4378 {
4379 explicit_hang_pt->set_master_node_pt(
4380 m, master_node_pt[m], master_shapes[master_node_number[m]]);
4381 }
4382
4383 // Make node hang
4385 -1);
4386 }
4387 }
4388
4389 // Check that there are actually dependent nodes for which we still need
4390 // to construct a hanging scheme. If not then there is nothing more to do.
4392 {
4393 // Assemble mass matrix for mortar
4394 //--------------------------------
4398 for (unsigned i = 0; i < shared_node_M.size(); i++)
4399 {
4401 }
4402
4403 // Fill in part corresponding to dependent nodal positions (unknown)
4404 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4405 {
4406 // Use L'Hosptal's rule:
4407 psi[i] =
4408 pow(-1.0, int((dependent_element_nnode_1d - 1) - i - 1)) *
4411 // Put in contribution
4413 }
4414
4415 // Fill in part corresponding to dependent element vertices (known)
4416 for (unsigned v = 0; v < shared_node_M.size(); v++)
4417 {
4418 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4419 {
4420 // Check if denominator is zero
4421 if (std::fabs(dependent_nodal_position[non_vertex_pos[i]] -
4423 {
4424 // We're ok
4425 psi[i] =
4426 pow(-1.0, int((dependent_element_nnode_1d - 1) - i - 1)) *
4431 }
4432 // Check if numerator is zero
4433 else if (std::fabs(Orthpoly::dlegendre(
4436 {
4437 // We can use l'hopital's rule
4438 psi[i] =
4439 pow(-1.0, int((dependent_element_nnode_1d - 1) - i - 1)) *
4443 }
4444 else
4445 {
4446 // We can't use l'hopital's rule
4447 throw OomphLibError(
4448 "Cannot use l'Hopital's rule. Dividing by zero is not allowed!",
4449 "PRefineableQElement<2,INITIAL_NNODE_1D>::quad_hang_helper()",
4451 }
4452 // Put in contribution
4454 }
4455 }
4456
4457 // Assemble local projection matrix for mortar
4458 //--------------------------------------------
4459
4460 // Have only one local projection matrix because there is just one
4461 // master
4463 for (unsigned i = 0; i < P.size(); i++)
4464 {
4465 P[i].resize(n_master_nodes, 0.0);
4466 }
4467
4468 // Storage for local coordinate
4469 Vector<double> s(2);
4470
4471 // Sum contributions from master element shapes (quadrature).
4472 // The order of quadrature must be high enough to evaluate a polynomial
4473 // of order N_s + N_m - 3 exactly, where N_s = n_dependent_nodes, N_m =
4474 // n_master_nodes.
4475 // (Use pointers for the quadrature knots and weights so that
4476 // data is not unnecessarily copied)
4477 // unsigned quadrature_order =
4478 // std::max(dependent_element_nnode_1d,master_element_nnode_1d);
4481 {
4482 // Use the same quadrature order as the dependent element (me)
4485 }
4486 else
4487 {
4488 // Use the same quadrature order as the master element (neighbour)
4490 quadrature_weight = &master_weight;
4491 }
4492
4493 // Quadrature loop
4494 for (unsigned q = 0; q < (*quadrature_weight).size(); q++)
4495 {
4496 // Evaluate mortar test functions at the quadrature knots in the
4497 // dependent
4498 // s[active_coord_index] = (*quadrature_knot)[q];
4500 s_on_mortar[0] = (*quadrature_knot)[q];
4501
4502 // Get psi
4503 for (unsigned k = 0; k < n_dependent_nodes - n_mortar_vertices; k++)
4504 {
4505 // Check if denominator is zero
4506 if (std::fabs(dependent_nodal_position[non_vertex_pos[k]] -
4507 s_on_mortar[0]) >= 1.0e-08)
4508 {
4509 // We're ok
4510 psi[k] =
4511 pow(-1.0, int((dependent_element_nnode_1d - 1) - k - 1)) *
4513 s_on_mortar[0]) /
4515 }
4516 // Check if numerator is zero
4517 else if (std::fabs(Orthpoly::dlegendre(
4519 1.0e-8)
4520 {
4521 // We can use l'Hopital's rule
4522 psi[k] =
4523 pow(-1.0, int((dependent_element_nnode_1d - 1) - k - 1)) *
4525 s_on_mortar[0]);
4526 }
4527 else
4528 {
4529 // We can't use l'hopital's rule
4530 throw OomphLibError(
4531 "Cannot use l'Hopital's rule. Dividing by zero is not allowed!",
4532 "PRefineableQElement<2,INITIAL_NNODE_1D>::quad_hang_helper()",
4534 }
4535 }
4536
4537 // Convert coordinate on mortar to local fraction in dependent element
4539 for (unsigned i = 0; i < 2; i++)
4540 {
4542 0.5 * (s_on_mortar[0] + 1.0) :
4544 }
4545
4546 // Project active coordinate into master element
4548 for (unsigned i = 0; i < 2; i++)
4549 {
4551 (s_hi_neigh[i] - s_lo_neigh[i]);
4552 }
4553
4554 // Evaluate master shapes at projections of local quadrature knots
4555 neigh_obj_pt->interpolating_basis(
4557
4558 // Populate local projection matrix
4559 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4560 {
4561 for (unsigned j = 0; j < n_master_nodes; j++)
4562 {
4563 P[i][j] += master_shapes[master_node_number[j]] * psi[i] *
4564 (*quadrature_weight)[q];
4565 }
4566 }
4567 }
4568
4569 // Assemble global projection matrices for mortar
4570 //-----------------------------------------------
4571 // Need to subtract contributions from the "known unknowns"
4572 // corresponding to the nodes at the vertices of the mortar
4573
4574 // Assemble contributions from mortar vertex nodes
4575 for (unsigned v = 0; v < n_mortar_vertices; v++)
4576 {
4577 // Convert coordinate on mortar to local fraction in dependent element
4579 for (unsigned i = 0; i < 2; i++)
4580 {
4581 s_fraction[i] =
4582 (i == active_coord_index) ?
4583 0.5 * (dependent_nodal_position[vertex_pos[v]] + 1.0) :
4585 }
4586
4587 // Project active coordinate into master element
4589 for (unsigned i = 0; i < 2; i++)
4590 {
4592 (s_hi_neigh[i] - s_lo_neigh[i]);
4593 }
4594
4595 // Get master shapes at dependent nodal positions
4596 neigh_obj_pt->interpolating_basis(
4598
4599 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4600 {
4601 for (unsigned k = 0; k < n_master_nodes; k++)
4602 {
4603 P[i][k] -=
4605 }
4606 }
4607 }
4608
4609 // Solve mortar system
4610 //--------------------
4611 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4612 {
4613 for (unsigned j = 0; j < n_master_nodes; j++)
4614 {
4615 P[i][j] /= diag_M[i];
4616 }
4617 }
4618
4619 // Create structures to hold the hanging info
4620 //-------------------------------------------
4622 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4623 {
4625 }
4626
4627 // Copy information to hanging nodes
4628 //----------------------------------
4629 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4630 {
4631 for (unsigned j = 0; j < n_master_nodes; j++)
4632 {
4633 hang_info_pt[i]->set_master_node_pt(j, master_node_pt[j], P[i][j]);
4634 }
4635 }
4636
4637 // Set pointers to hanging info
4638 //-----------------------------
4639 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
4640 {
4641 // Check that the node shoule actually hang.
4642 // That is, if the polynomial orders of the elements at a p-type
4643 // non-conormity are both odd then the middle node on the edge is
4644 // shared but a hanging scheme has been constructed for it.
4645 bool node_is_really_shared = false;
4646 for (unsigned m = 0; m < hang_info_pt[i]->nmaster(); m++)
4647 {
4648 // We can simply check if the hanging scheme lists itself as a
4649 // master
4650 if (hang_info_pt[i]->master_node_pt(m) ==
4652 {
4653 node_is_really_shared = true;
4654
4655#ifdef PARANOID
4656 // Also check the corresponding weight: it should be 1
4657 if (std::fabs(hang_info_pt[i]->master_weight(m) - 1.0) > 1.0e-06)
4658 {
4659 throw OomphLibError(
4660 "Something fishy here -- with shared node at a mortar vertex",
4661 "PRefineableQElemen<2,INITIAL_NNODE_1D>t::quad_hang_helper()",
4663 }
4664#endif
4665 }
4666 }
4667
4668 // Now we can make the node hang, if it isn't a shared node
4670 {
4671 dependent_node_pt[non_vertex_pos[i]]->set_hanging_pt(
4672 hang_info_pt[i], -1);
4673 }
4674 }
4675
4676 } // End of case where there are still dependent nodes
4677
4678#ifdef PARANOID
4679 // Check all dependent nodes, hanging or otherwise
4680 for (unsigned i = 0; i < n_dependent_nodes; i++)
4681 {
4682 // Check that weights sum to 1 for those that hang
4683 if (dependent_node_pt[i]->is_hanging())
4684 {
4685 // Check that weights sum to 1
4686 double weight_sum = 0.0;
4687 for (unsigned m = 0;
4688 m < dependent_node_pt[i]->hanging_pt()->nmaster();
4689 m++)
4690 {
4691 weight_sum += dependent_node_pt[i]->hanging_pt()->master_weight(m);
4692 }
4693
4694 // Warn if not
4695 if (std::fabs(weight_sum - 1.0) > 1.0e-08)
4696 {
4697 oomph_info << "Sum of master weights: " << weight_sum << std::endl;
4699 "Weights in hanging scheme do not sum to 1",
4700 "PRefineableQElement<2,INITIAL_NNODE_1D>::quad_hang_helper()",
4702 }
4703 }
4704 else
4705 {
4706 // Check that this node is shared with the master element if it
4707 // isn't hanging
4708 bool is_master = false;
4709 for (unsigned n = 0; n < n_master_nodes; n++)
4710 {
4711 if (dependent_node_pt[i] == master_node_pt[n])
4712 {
4713 // Node is a master
4714 is_master = true;
4715 break;
4716 }
4717 }
4718
4719 if (!is_master)
4720 {
4721 // Throw error
4722 std::ostringstream error_string;
4723 error_string << "This node in the dependent element is neither"
4724 << std::endl
4725 << "hanging or shared with a master element."
4726 << std::endl;
4727
4728 throw OomphLibError(
4729 error_string.str(),
4730 "PRefineableQElement<2,INITIAL_NNODE_1D>::quad_hang_helper()",
4732 }
4733 }
4734 }
4735#endif
4736
4737 // Finally, Loop over all dependent nodes and fine-tune their positions
4738 //-----------------------------------------------------------------
4739 // Here we simply set the node's positions to be consistent
4740 // with the hanging scheme. This is not strictly necessary
4741 // because it is done in the mesh adaptation before the node
4742 // becomes non-hanging later on. We make no attempt to ensure
4743 // (strong) continuity in the position across the mortar.
4744 for (unsigned i = 0; i < n_dependent_nodes; i++)
4745 {
4746 // Only fine-tune hanging nodes
4747 if (dependent_node_pt[i]->is_hanging())
4748 {
4749 // If we are doing the position, then
4750 if (value_id == -1)
4751 {
4752 // Get the local coordinate of this dependent node
4754 this->local_coordinate_of_node(dependent_node_number[i], s_local);
4755
4756 // Get the position from interpolation in this element via
4757 // the hanging scheme
4759 this->interpolated_x(s_local, x_in_neighb);
4760
4761 // Fine adjust the coordinates (macro map will pick up boundary
4762 // accurately but will lead to different element edges)
4763 dependent_node_pt[i]->x(0) = x_in_neighb[0];
4764 dependent_node_pt[i]->x(1) = x_in_neighb[1];
4765 }
4766 }
4767 }
4768 } // End of case where this interface is to be mortared
4769 }
4770
4771 ////////////////////////////////////////////////////////////////
4772 // 3D PRefineableQElements
4773 ////////////////////////////////////////////////////////////////
4774
4775 /// Get local coordinates of node j in the element; vector sets its own size
4776 template<unsigned INITIAL_NNODE_1D>
4778 const unsigned& n, Vector<double>& s) const
4779 {
4780 s.resize(3);
4781 unsigned Nnode_1d = this->nnode_1d();
4782 unsigned n0 = n % Nnode_1d;
4783 unsigned n1 = unsigned(double(n) / double(Nnode_1d)) % Nnode_1d;
4784 unsigned n2 = unsigned(double(n) / double(Nnode_1d * Nnode_1d));
4785
4786 switch (Nnode_1d)
4787 {
4788 case 2:
4793 break;
4794 case 3:
4799 break;
4800 case 4:
4805 break;
4806 case 5:
4811 break;
4812 case 6:
4817 break;
4818 case 7:
4823 break;
4824 default:
4825 std::ostringstream error_message;
4826 error_message << "\nERROR: Exceeded maximum polynomial order for";
4827 error_message << "\n shape functions.\n";
4828 throw OomphLibError(error_message.str(),
4831 break;
4832 }
4833 }
4834
4835 /// Get the local fractino of node j in the element
4836 template<unsigned INITIAL_NNODE_1D>
4838 const unsigned& n, Vector<double>& s_fraction)
4839 {
4841 s_fraction[0] = 0.5 * (s_fraction[0] + 1.0);
4842 s_fraction[1] = 0.5 * (s_fraction[1] + 1.0);
4843 s_fraction[2] = 0.5 * (s_fraction[2] + 1.0);
4844 }
4845
4846 template<unsigned INITIAL_NNODE_1D>
4848 const unsigned& n1d, const unsigned& i)
4849 {
4850 switch (this->nnode_1d())
4851 {
4852 case 2:
4854 return 0.5 *
4856 case 3:
4858 return 0.5 *
4860 case 4:
4862 return 0.5 *
4864 case 5:
4866 return 0.5 *
4868 case 6:
4870 return 0.5 *
4872 case 7:
4874 return 0.5 *
4876 default:
4877 std::ostringstream error_message;
4878 error_message << "\nERROR: Exceeded maximum polynomial order for";
4879 error_message << "\n shape functions.\n";
4880 throw OomphLibError(error_message.str(),
4883 return 0.0;
4884 }
4885 }
4886
4887 //==================================================================
4888 /// Return the node at the specified local coordinate
4889 //==================================================================
4890 template<unsigned INITIAL_NNODE_1D>
4892 const Vector<double>& s) const
4893 {
4894 unsigned Nnode_1d = this->nnode_1d();
4895 // Load the tolerance into a local variable
4897 // There are two possible indices.
4898 Vector<int> index(3);
4899
4900 // Loop over indices
4901 for (unsigned i = 0; i < 3; i++)
4902 {
4903 // Determine the index
4904 // -------------------
4905
4906 bool is_found = false;
4907
4908 // If we are at the lower limit, the index is zero
4909 if (std::fabs(s[i] + 1.0) < tol)
4910 {
4911 index[i] = 0;
4912 is_found = true;
4913 }
4914 // If we are at the upper limit, the index is the number of nodes minus 1
4915 else if (std::fabs(s[i] - 1.0) < tol)
4916 {
4917 index[i] = Nnode_1d - 1;
4918 is_found = true;
4919 }
4920 // Otherwise, we have to calculate the index in general
4921 else
4922 {
4923 // Compute Gauss-Lobatto-Legendre node positions
4926 // Loop over possible internal nodal positions
4927 for (unsigned n = 1; n < Nnode_1d - 1; n++)
4928 {
4929 if (std::fabs(z[n] - s[i]) < tol)
4930 {
4931 index[i] = n;
4932 is_found = true;
4933 break;
4934 }
4935 }
4936 }
4937
4938 if (!is_found)
4939 {
4940 // No matching nodes
4941 return 0;
4942 }
4943 }
4944 // If we've got here we have a node, so let's return a pointer to it
4945 return this->node_pt(index[0] + Nnode_1d * index[1] +
4946 Nnode_1d * Nnode_1d * index[2]);
4947 }
4948
4949 //===================================================================
4950 /// If a neighbouring element has already created a node at
4951 /// a position corresponding to the local fractional position within the
4952 /// present element, s_fraction, return
4953 /// a pointer to that node. If not, return NULL (0).
4954 //===================================================================
4955 template<unsigned INITIAL_NNODE_1D>
4958 {
4959 using namespace OcTreeNames;
4960
4961 // Calculate the faces/edges on which the node lies
4964
4965 if (s_fraction[0] == 0.0)
4966 {
4967 faces.push_back(L);
4968 if (s_fraction[1] == 0.0)
4969 {
4970 edges.push_back(LD);
4971 }
4972 if (s_fraction[2] == 0.0)
4973 {
4974 edges.push_back(LB);
4975 }
4976 if (s_fraction[1] == 1.0)
4977 {
4978 edges.push_back(LU);
4979 }
4980 if (s_fraction[2] == 1.0)
4981 {
4982 edges.push_back(LF);
4983 }
4984 }
4985
4986 if (s_fraction[0] == 1.0)
4987 {
4988 faces.push_back(R);
4989 if (s_fraction[1] == 0.0)
4990 {
4991 edges.push_back(RD);
4992 }
4993 if (s_fraction[2] == 0.0)
4994 {
4995 edges.push_back(RB);
4996 }
4997 if (s_fraction[1] == 1.0)
4998 {
4999 edges.push_back(RU);
5000 }
5001 if (s_fraction[2] == 1.0)
5002 {
5003 edges.push_back(RF);
5004 }
5005 }
5006
5007 if (s_fraction[1] == 0.0)
5008 {
5009 faces.push_back(D);
5010 if (s_fraction[2] == 0.0)
5011 {
5012 edges.push_back(DB);
5013 }
5014 if (s_fraction[2] == 1.0)
5015 {
5016 edges.push_back(DF);
5017 }
5018 }
5019
5020 if (s_fraction[1] == 1.0)
5021 {
5022 faces.push_back(U);
5023 if (s_fraction[2] == 0.0)
5024 {
5025 edges.push_back(UB);
5026 }
5027 if (s_fraction[2] == 1.0)
5028 {
5029 edges.push_back(UF);
5030 }
5031 }
5032
5033 if (s_fraction[2] == 0.0)
5034 {
5035 faces.push_back(B);
5036 }
5037
5038 if (s_fraction[2] == 1.0)
5039 {
5040 faces.push_back(F);
5041 }
5042
5043 // Find the number of faces
5044 unsigned n_face = faces.size();
5045
5046 // Find the number of edges
5047 unsigned n_edge = edges.size();
5048
5052 Vector<double> s(3);
5053
5056
5057 // Loop over the faces on which the node lies
5058 //------------------------------------------
5059 for (unsigned j = 0; j < n_face; j++)
5060 {
5061 // Find pointer to neighbouring element along face
5063 neigh_pt = octree_pt()->gteq_face_neighbour(faces[j],
5065 s_lo_neigh,
5066 s_hi_neigh,
5067 neigh_face,
5068 diff_level,
5070
5071 // Neighbour exists
5072 if (neigh_pt != 0)
5073 {
5074 // Have any of its vertex nodes been created yet?
5075 // (Must look in incomplete neighbours because after the
5076 // pre-build they may contain pointers to the required nodes. e.g.
5077 // h-refinement of neighbouring linear and quadratic elements)
5078 bool a_vertex_node_is_built = false;
5080 dynamic_cast<QElement<3, INITIAL_NNODE_1D>*>(neigh_pt->object_pt());
5081 if (neigh_obj_pt == 0)
5082 {
5083 throw OomphLibError("Not a quad element!",
5086 }
5087 for (unsigned vnode = 0; vnode < neigh_obj_pt->nvertex_node(); vnode++)
5088 {
5091 break;
5092 }
5094 {
5095 // We now need to translate the nodal location, defined in terms
5096 // of the fractional coordinates of the present element into
5097 // those of its neighbour. For this we use the information returned
5098 // to use from the octree function.
5099
5100 // Calculate the local coordinate in the neighbour
5101 // Note that we need to use the translation scheme in case
5102 // the local coordinates are swapped in the neighbour.
5103 for (unsigned i = 0; i < 3; i++)
5104 {
5105 s[i] = s_lo_neigh[i] +
5107 }
5108
5109 // Find the node in the neighbour
5112
5113 // If there is a node, return it
5114 if (neighbour_node_pt != 0)
5115 {
5116 // Now work out whether it's a periodic boundary
5117 // only possible if we have moved into a neighbouring tree
5119 {
5120 // Return whether the neighbour is periodic
5121 is_periodic =
5122 octree_pt()->root_pt()->is_neighbour_periodic(faces[j]);
5123 }
5124
5125 return neighbour_node_pt;
5126 }
5127 }
5128 }
5129 } // End of loop over faces
5130
5131
5132 // Loop over the edges on which the node lies
5133 //------------------------------------------
5134 for (unsigned j = 0; j < n_edge; j++)
5135 {
5136 // Even if we restrict ourselves to true edge neighbours (i.e.
5137 // elements that are not also face neighbours) there may be multiple
5138 // edge neighbours across the edges between multiple root octrees.
5139 // When making the first call to OcTree::gteq_true_edge_neighbour(...)
5140 // we simply return the first one of these multiple edge neighbours
5141 // (if there are any at all, of course) and also return the total number
5142 // of true edge neighbours. If the node in question already exists
5143 // on the first edge neighbour we're done. If it doesn't it may exist
5144 // on other edge neighbours so we repeat the process over all
5145 // other edge neighbours (bailing out if a node is found, of course).
5146
5147 // Initially return the zero-th true edge neighbour
5148 unsigned i_root_edge_neighbour = 0;
5149
5150 // Initialise the total number of true edge neighbours
5151 unsigned nroot_edge_neighbour = 0;
5152
5153 // Keep searching until we've found the node or until we've checked
5154 // all available edge neighbours
5155 bool keep_searching = true;
5156 while (keep_searching)
5157 {
5158 // Find pointer to neighbouring element along edge
5160 neigh_pt = octree_pt()->gteq_true_edge_neighbour(edges[j],
5164 s_lo_neigh,
5165 s_hi_neigh,
5166 neigh_face,
5167 diff_level);
5168
5169 // Neighbour exists
5170 if (neigh_pt != 0)
5171 {
5172 // Have any of its vertex nodes been created yet?
5173 // (Must look in incomplete neighbours because after the
5174 // pre-build they may contain pointers to the required nodes. e.g.
5175 // h-refinement of neighbouring linear and quadratic elements)
5176 bool a_vertex_node_is_built = false;
5178 dynamic_cast<QElement<3, INITIAL_NNODE_1D>*>(neigh_pt->object_pt());
5179 if (neigh_obj_pt == 0)
5180 {
5181 throw OomphLibError("Not a quad element!",
5184 }
5185 for (unsigned vnode = 0; vnode < neigh_obj_pt->nvertex_node();
5186 vnode++)
5187 {
5190 break;
5191 }
5193 {
5194 // We now need to translate the nodal location, defined in terms
5195 // of the fractional coordinates of the present element into
5196 // those of its neighbour. For this we use the information returned
5197 // to use from the octree function.
5198
5199 // Calculate the local coordinate in the neighbour
5200 // Note that we need to use the translation scheme in case
5201 // the local coordinates are swapped in the neighbour.
5202 for (unsigned i = 0; i < 3; i++)
5203 {
5205 (s_hi_neigh[i] - s_lo_neigh[i]);
5206 }
5207
5208 // Find the node in the neighbour
5211
5212 // If there is a node, return it
5213 if (neighbour_node_pt != 0)
5214 {
5215 // Get the faces on which the edge lies
5218
5219 // Get the number of entries in the vector
5221
5222 // Loop over the faces
5223 for (unsigned i_face = 0; i_face < n_faces_attached_to_edge;
5224 i_face++)
5225 {
5226 // Is the node periodic in the face direction?
5227 is_periodic = octree_pt()->root_pt()->is_neighbour_periodic(
5229
5230 // Check if the edge is periodic in the i_face-th face direction
5231 if (is_periodic)
5232 {
5233 // We're done!
5234 break;
5235 }
5236 } // for (unsigned
5237 // i_face=0;i_face<n_faces_attached_to_edge;i_face++)
5238
5239 return neighbour_node_pt;
5240 }
5241 }
5242 }
5243
5244 // Keep searching, but only if there are further edge neighbours
5245 // Try next root edge neighbour
5247
5248 // Have we exhausted the search
5250 {
5251 keep_searching = false;
5252 }
5253
5254 } // End of while keep searching over all true edge neighbours
5255
5256 } // End of loop over edges
5257
5258 // Node not found, return null
5259 return 0;
5260 }
5261
5262 //===================================================================
5263 /// If a neighbouring element's son has already created a node at
5264 /// a position corresponding to the local fractional position within the
5265 /// present element, s_fraction, return
5266 /// a pointer to that node. If not, return NULL (0). If the node is
5267 /// periodic the flag is_periodic will be true
5268 //===================================================================
5269 template<unsigned INITIAL_NNODE_1D>
5272 bool& is_periodic)
5273 {
5274 using namespace OcTreeNames;
5275
5276 // Calculate the faces/edges on which the node lies
5279
5280 if (s_fraction[0] == 0.0)
5281 {
5282 faces.push_back(L);
5283 if (s_fraction[1] == 0.0)
5284 {
5285 edges.push_back(LD);
5286 }
5287 if (s_fraction[2] == 0.0)
5288 {
5289 edges.push_back(LB);
5290 }
5291 if (s_fraction[1] == 1.0)
5292 {
5293 edges.push_back(LU);
5294 }
5295 if (s_fraction[2] == 1.0)
5296 {
5297 edges.push_back(LF);
5298 }
5299 }
5300
5301 if (s_fraction[0] == 1.0)
5302 {
5303 faces.push_back(R);
5304 if (s_fraction[1] == 0.0)
5305 {
5306 edges.push_back(RD);
5307 }
5308 if (s_fraction[2] == 0.0)
5309 {
5310 edges.push_back(RB);
5311 }
5312 if (s_fraction[1] == 1.0)
5313 {
5314 edges.push_back(RU);
5315 }
5316 if (s_fraction[2] == 1.0)
5317 {
5318 edges.push_back(RF);
5319 }
5320 }
5321
5322 if (s_fraction[1] == 0.0)
5323 {
5324 faces.push_back(D);
5325 if (s_fraction[2] == 0.0)
5326 {
5327 edges.push_back(DB);
5328 }
5329 if (s_fraction[2] == 1.0)
5330 {
5331 edges.push_back(DF);
5332 }
5333 }
5334
5335 if (s_fraction[1] == 1.0)
5336 {
5337 faces.push_back(U);
5338 if (s_fraction[2] == 0.0)
5339 {
5340 edges.push_back(UB);
5341 }
5342 if (s_fraction[2] == 1.0)
5343 {
5344 edges.push_back(UF);
5345 }
5346 }
5347
5348 if (s_fraction[2] == 0.0)
5349 {
5350 faces.push_back(B);
5351 }
5352
5353 if (s_fraction[2] == 1.0)
5354 {
5355 faces.push_back(F);
5356 }
5357
5358 // Find the number of faces
5359 unsigned n_face = faces.size();
5360
5361 // Find the number of edges
5362 unsigned n_edge = edges.size();
5363
5367 Vector<double> s(3);
5368
5371
5372 // Loop over the faces on which the node lies
5373 //------------------------------------------
5374 for (unsigned j = 0; j < n_face; j++)
5375 {
5376 // Find pointer to neighbouring element along face
5378 neigh_pt = octree_pt()->gteq_face_neighbour(faces[j],
5380 s_lo_neigh,
5381 s_hi_neigh,
5382 neigh_face,
5383 diff_level,
5385
5386 // Neighbour exists
5387 if (neigh_pt != 0)
5388 {
5389 // Have its nodes been created yet?
5390 // (Must look in sons of unfinished neighbours too!!!)
5391 if (true)
5392 {
5393 // We now need to translate the nodal location, defined in terms
5394 // of the fractional coordinates of the present element into
5395 // those of its neighbour. For this we use the information returned
5396 // to use from the octree function.
5397
5398 // Calculate the local coordinate in the neighbour
5399 // Note that we need to use the translation scheme in case
5400 // the local coordinates are swapped in the neighbour.
5401 for (unsigned i = 0; i < 3; i++)
5402 {
5403 s[i] = s_lo_neigh[i] +
5405 }
5406
5407 // Check if the element has sons
5408 if (neigh_pt->nsons() != 0)
5409 {
5410 // First, find the son element in which the node should live
5411
5412 // Find coordinates in the sons
5414 int son = -10;
5415 // On the left
5416 if (s[0] < 0.0)
5417 {
5418 // On the bottom
5419 if (s[1] < 0.0)
5420 {
5421 // On the back
5422 if (s[2] < 0.0)
5423 {
5424 // It's the LDB son
5426 s_in_son[0] = 1.0 + 2.0 * s[0];
5427 s_in_son[1] = 1.0 + 2.0 * s[1];
5428 s_in_son[2] = 1.0 + 2.0 * s[2];
5429 }
5430 // On the front
5431 else
5432 {
5433 // It's the LDF son
5435 s_in_son[0] = 1.0 + 2.0 * s[0];
5436 s_in_son[1] = 1.0 + 2.0 * s[1];
5437 s_in_son[2] = -1.0 + 2.0 * s[2];
5438 }
5439 }
5440 // On the top
5441 else
5442 {
5443 // On the back
5444 if (s[2] < 0.0)
5445 {
5446 // It's the LUB son
5448 s_in_son[0] = 1.0 + 2.0 * s[0];
5449 s_in_son[1] = -1.0 + 2.0 * s[1];
5450 s_in_son[2] = 1.0 + 2.0 * s[2];
5451 }
5452 // On the front
5453 else
5454 {
5455 // It's the LUF son
5457 s_in_son[0] = 1.0 + 2.0 * s[0];
5458 s_in_son[1] = -1.0 + 2.0 * s[1];
5459 s_in_son[2] = -1.0 + 2.0 * s[2];
5460 }
5461 }
5462 }
5463 // On the right
5464 else
5465 {
5466 // On the bottom
5467 if (s[1] < 0.0)
5468 {
5469 // On the back
5470 if (s[2] < 0.0)
5471 {
5472 // It's the RDB son
5474 s_in_son[0] = -1.0 + 2.0 * s[0];
5475 s_in_son[1] = 1.0 + 2.0 * s[1];
5476 s_in_son[2] = 1.0 + 2.0 * s[2];
5477 }
5478 // On the front
5479 else
5480 {
5481 // It's the RDF son
5483 s_in_son[0] = -1.0 + 2.0 * s[0];
5484 s_in_son[1] = 1.0 + 2.0 * s[1];
5485 s_in_son[2] = -1.0 + 2.0 * s[2];
5486 }
5487 }
5488 // On the top
5489 else
5490 {
5491 // On the back
5492 if (s[2] < 0.0)
5493 {
5494 // It's the RUB son
5496 s_in_son[0] = -1.0 + 2.0 * s[0];
5497 s_in_son[1] = -1.0 + 2.0 * s[1];
5498 s_in_son[2] = 1.0 + 2.0 * s[2];
5499 }
5500 // On the front
5501 else
5502 {
5503 // It's the RUF son
5505 s_in_son[0] = -1.0 + 2.0 * s[0];
5506 s_in_son[1] = -1.0 + 2.0 * s[1];
5507 s_in_son[2] = -1.0 + 2.0 * s[2];
5508 }
5509 }
5510 }
5511
5512 // Find the node in the neighbour's son
5514 neigh_pt->son_pt(son)->object_pt()->get_node_at_local_coordinate(
5515 s_in_son);
5516
5517 // If there is a node, return it
5518 if (neighbour_son_node_pt != 0)
5519 {
5520 // Now work out whether it's a periodic boundary
5521 // only possible if we have moved into a neighbouring tree
5523 {
5524 // Return whether the neighbour is periodic
5525 is_periodic =
5526 octree_pt()->root_pt()->is_neighbour_periodic(faces[j]);
5527 }
5528
5529 // Return the pointer to the neighbouring node
5530 return neighbour_son_node_pt;
5531 }
5532 }
5533 }
5534 }
5535 } // End of loop over faces
5536
5537
5538 // Loop over the edges on which the node lies
5539 //------------------------------------------
5540 for (unsigned j = 0; j < n_edge; j++)
5541 {
5542 // Even if we restrict ourselves to true edge neighbours (i.e.
5543 // elements that are not also face neighbours) there may be multiple
5544 // edge neighbours across the edges between multiple root octrees.
5545 // When making the first call to OcTree::gteq_true_edge_neighbour(...)
5546 // we simply return the first one of these multiple edge neighbours
5547 // (if there are any at all, of course) and also return the total number
5548 // of true edge neighbours. If the node in question already exists
5549 // on the first edge neighbour we're done. If it doesn't it may exist
5550 // on other edge neighbours so we repeat the process over all
5551 // other edge neighbours (bailing out if a node is found, of course).
5552
5553 // Initially return the zero-th true edge neighbour
5554 unsigned i_root_edge_neighbour = 0;
5555
5556 // Initialise the total number of true edge neighbours
5557 unsigned nroot_edge_neighbour = 0;
5558
5559 // Keep searching until we've found the node or until we've checked
5560 // all available edge neighbours
5561 bool keep_searching = true;
5562 while (keep_searching)
5563 {
5564 // Find pointer to neighbouring element along edge
5566 neigh_pt = octree_pt()->gteq_true_edge_neighbour(edges[j],
5570 s_lo_neigh,
5571 s_hi_neigh,
5572 neigh_face,
5573 diff_level);
5574
5575 // Neighbour exists
5576 if (neigh_pt != 0)
5577 {
5578 // Have its nodes been created yet?
5579 // (Must look in sons of unfinished neighbours too!!!)
5580 if (true)
5581 {
5582 // We now need to translate the nodal location, defined in terms
5583 // of the fractional coordinates of the present element into
5584 // those of its neighbour. For this we use the information returned
5585 // to use from the octree function.
5586
5587 // Calculate the local coordinate in the neighbour
5588 // Note that we need to use the translation scheme in case
5589 // the local coordinates are swapped in the neighbour.
5590 for (unsigned i = 0; i < 3; i++)
5591 {
5593 (s_hi_neigh[i] - s_lo_neigh[i]);
5594 }
5595
5596 // Check if the element has sons
5597 if (neigh_pt->nsons() != 0)
5598 {
5599 // First, find the son element in which the node should live
5600
5601 // Find coordinates in the sons
5603 int son = -10;
5604 // On the left
5605 if (s[0] < 0.0)
5606 {
5607 // On the bottom
5608 if (s[1] < 0.0)
5609 {
5610 // On the back
5611 if (s[2] < 0.0)
5612 {
5613 // It's the LDB son
5615 s_in_son[0] = 1.0 + 2.0 * s[0];
5616 s_in_son[1] = 1.0 + 2.0 * s[1];
5617 s_in_son[2] = 1.0 + 2.0 * s[2];
5618 }
5619 // On the front
5620 else
5621 {
5622 // It's the LDF son
5624 s_in_son[0] = 1.0 + 2.0 * s[0];
5625 s_in_son[1] = 1.0 + 2.0 * s[1];
5626 s_in_son[2] = -1.0 + 2.0 * s[2];
5627 }
5628 }
5629 // On the top
5630 else
5631 {
5632 // On the back
5633 if (s[2] < 0.0)
5634 {
5635 // It's the LUB son
5637 s_in_son[0] = 1.0 + 2.0 * s[0];
5638 s_in_son[1] = -1.0 + 2.0 * s[1];
5639 s_in_son[2] = 1.0 + 2.0 * s[2];
5640 }
5641 // On the front
5642 else
5643 {
5644 // It's the LUF son
5646 s_in_son[0] = 1.0 + 2.0 * s[0];
5647 s_in_son[1] = -1.0 + 2.0 * s[1];
5648 s_in_son[2] = -1.0 + 2.0 * s[2];
5649 }
5650 }
5651 }
5652 // On the right
5653 else
5654 {
5655 // On the bottom
5656 if (s[1] < 0.0)
5657 {
5658 // On the back
5659 if (s[2] < 0.0)
5660 {
5661 // It's the RDB son
5663 s_in_son[0] = -1.0 + 2.0 * s[0];
5664 s_in_son[1] = 1.0 + 2.0 * s[1];
5665 s_in_son[2] = 1.0 + 2.0 * s[2];
5666 }
5667 // On the front
5668 else
5669 {
5670 // It's the RDF son
5672 s_in_son[0] = -1.0 + 2.0 * s[0];
5673 s_in_son[1] = 1.0 + 2.0 * s[1];
5674 s_in_son[2] = -1.0 + 2.0 * s[2];
5675 }
5676 }
5677 // On the top
5678 else
5679 {
5680 // On the back
5681 if (s[2] < 0.0)
5682 {
5683 // It's the RUB son
5685 s_in_son[0] = -1.0 + 2.0 * s[0];
5686 s_in_son[1] = -1.0 + 2.0 * s[1];
5687 s_in_son[2] = 1.0 + 2.0 * s[2];
5688 }
5689 // On the front
5690 else
5691 {
5692 // It's the RUF son
5694 s_in_son[0] = -1.0 + 2.0 * s[0];
5695 s_in_son[1] = -1.0 + 2.0 * s[1];
5696 s_in_son[2] = -1.0 + 2.0 * s[2];
5697 }
5698 }
5699 }
5700
5701 // Find the node in the neighbour's son
5703 neigh_pt->son_pt(son)
5704 ->object_pt()
5706
5707 // If there is a node, return it
5708 if (neighbour_son_node_pt != 0)
5709 {
5710 // Get the faces on which the edge lies
5713
5714 // Get the number of entries in the vector
5715 unsigned n_faces_attached_to_edge =
5717
5718 // Loop over the faces
5719 for (unsigned i_face = 0; i_face < n_faces_attached_to_edge;
5720 i_face++)
5721 {
5722 // Is the node periodic in the face direction?
5723 is_periodic = octree_pt()->root_pt()->is_neighbour_periodic(
5725
5726 // Check if the edge is periodic in the i_face-th face
5727 // direction
5728 if (is_periodic)
5729 {
5730 // We're done!
5731 break;
5732 }
5733 } // for (unsigned
5734 // i_face=0;i_face<n_faces_attached_to_edge;i_face++)
5735
5736 // Return the pointer to the neighbouring node
5737 return neighbour_son_node_pt;
5738 }
5739 }
5740 }
5741 }
5742
5743 // Keep searching, but only if there are further edge neighbours
5744 // Try next root edge neighbour
5746
5747 // Have we exhausted the search
5749 {
5750 keep_searching = false;
5751 }
5752
5753 } // End of while keep searching over all true edge neighbours
5754
5755 } // End of loop over edges
5756
5757 // Node not found, return null
5758 return 0;
5759 }
5760
5761 //==================================================================
5762 /// Set the correct p-order of the element based on that of its
5763 /// father. Then construct an integration scheme of the correct order.
5764 /// If an adopted father is specified, information from this is
5765 /// used instead of using the father found from the tree.
5766 //==================================================================
5767 template<unsigned INITIAL_NNODE_1D>
5769 Tree* const& adopted_father_pt, const unsigned& initial_p_order)
5770 {
5771 // Storage for pointer to my father (in octree impersonation)
5772 OcTree* father_pt = 0;
5773
5774 // Check if an adopted father has been specified
5775 if (adopted_father_pt != 0)
5776 {
5777 // Get pointer to my father (in octree impersonation)
5778 father_pt = dynamic_cast<OcTree*>(adopted_father_pt);
5779 }
5780 // Check if element is in a tree
5781 else if (Tree_pt != 0)
5782 {
5783 // Get pointer to my father (in octree impersonation)
5784 father_pt = dynamic_cast<OcTree*>(octree_pt()->father_pt());
5785 }
5786 else
5787 {
5788 // throw OomphLibError(
5789 // "Element not in a tree, and no adopted father has been
5790 // specified!",
5791 // "PRefineableQElement<2,INITIAL_NNODE_1D>::initial_setup()",
5792 // OOMPH_EXCEPTION_LOCATION);
5793 }
5794
5795 // Check if element has father
5796 if (father_pt != 0 || initial_p_order != 0)
5797 {
5798 if (father_pt != 0)
5799 {
5802 father_pt->object_pt());
5803 if (father_el_pt != 0)
5804 {
5805 unsigned father_p_order = father_el_pt->p_order();
5806 // Set p-order to that of father
5807 P_order = father_p_order;
5808 }
5809 }
5810 else
5811 {
5812 P_order = initial_p_order;
5813 }
5814
5815 // Now sort out the element...
5816 // (has p^3 nodes)
5817 unsigned new_n_node = P_order * P_order * P_order;
5818
5819 // Allocate new space for Nodes (at the element level)
5820 this->set_n_node(new_n_node);
5821
5822 // Set integration scheme
5823 delete this->integral_pt();
5824 switch (P_order)
5825 {
5826 case 2:
5828 break;
5829 case 3:
5831 break;
5832 case 4:
5834 break;
5835 case 5:
5837 break;
5838 case 6:
5840 break;
5841 case 7:
5843 break;
5844 default:
5845 std::ostringstream error_message;
5846 error_message << "\nERROR: Exceeded maximum polynomial order for";
5847 error_message << "\n integration scheme.\n";
5848 throw OomphLibError(error_message.str(),
5851 }
5852 }
5853 }
5854
5855 //==================================================================
5856 /// Check the father element for any required nodes which
5857 /// already exist
5858 //==================================================================
5859 template<unsigned INITIAL_NNODE_1D>
5861 Mesh*& mesh_pt, Vector<Node*>& new_node_pt)
5862 {
5863 using namespace OcTreeNames;
5864
5865 // Get the number of 1d nodes
5866 unsigned n_p = nnode_1d();
5867
5868 // Check whether static father_bound needs to be created
5869 if (Father_bound[n_p].nrow() == 0)
5870 {
5871 setup_father_bounds();
5872 }
5873
5874 // Pointer to my father (in quadtree impersonation)
5875 OcTree* father_pt = dynamic_cast<OcTree*>(octree_pt()->father_pt());
5876
5877 // What type of son am I? Ask my quadtree representation...
5878 int son_type = Tree_pt->son_type();
5879
5880 // Has somebody build me already? (If any nodes have not been built)
5881 if (!nodes_built())
5882 {
5883#ifdef PARANOID
5884 if (father_pt == 0)
5885 {
5886 std::string error_message =
5887 "Something fishy here: I have no father and yet \n";
5888 error_message += "I have no nodes. Who has created me then?!\n";
5889
5890 throw OomphLibError(
5891 error_message,
5892 "PRefineableQElement<3,INITIAL_NNODE_1D>::pre_build()",
5894 }
5895#endif
5896
5897 // Return pointer to father element
5899 dynamic_cast<RefineableQElement<3>*>(father_pt->object_pt());
5900
5901 // Timestepper should be the same for all nodes in father
5902 // element -- use it create timesteppers for new nodes
5905
5906 // Number of history values (incl. present)
5907 unsigned ntstorage = time_stepper_pt->ntstorage();
5908
5909 /// / Pass pointer to time object:
5910 // this->time_pt()=father_el_pt->time_pt();
5911
5914 Vector<double> s(3);
5915 Vector<double> x(3);
5916
5917 // Setup vertex coordinates in father element:
5918 //--------------------------------------------
5919 switch (son_type)
5920 {
5921 case LDB:
5922 s_lo[0] = -1.0;
5923 s_hi[0] = 0.0;
5924 s_lo[1] = -1.0;
5925 s_hi[1] = 0.0;
5926 s_lo[2] = -1.0;
5927 s_hi[2] = 0.0;
5928 break;
5929
5930 case LDF:
5931 s_lo[0] = -1.0;
5932 s_hi[0] = 0.0;
5933 s_lo[1] = -1.0;
5934 s_hi[1] = 0.0;
5935 s_lo[2] = 0.0;
5936 s_hi[2] = 1.0;
5937 break;
5938
5939 case LUB:
5940 s_lo[0] = -1.0;
5941 s_hi[0] = 0.0;
5942 s_lo[1] = 0.0;
5943 s_hi[1] = 1.0;
5944 s_lo[2] = -1.0;
5945 s_hi[2] = 0.0;
5946 break;
5947
5948 case LUF:
5949 s_lo[0] = -1.0;
5950 s_hi[0] = 0.0;
5951 s_lo[1] = 0.0;
5952 s_hi[1] = 1.0;
5953 s_lo[2] = 0.0;
5954 s_hi[2] = 1.0;
5955 break;
5956
5957 case RDB:
5958 s_lo[0] = 0.0;
5959 s_hi[0] = 1.0;
5960 s_lo[1] = -1.0;
5961 s_hi[1] = 0.0;
5962 s_lo[2] = -1.0;
5963 s_hi[2] = 0.0;
5964 break;
5965
5966 case RDF:
5967 s_lo[0] = 0.0;
5968 s_hi[0] = 1.0;
5969 s_lo[1] = -1.0;
5970 s_hi[1] = 0.0;
5971 s_lo[2] = 0.0;
5972 s_hi[2] = 1.0;
5973 break;
5974
5975 case RUB:
5976 s_lo[0] = 0.0;
5977 s_hi[0] = 1.0;
5978 s_lo[1] = 0.0;
5979 s_hi[1] = 1.0;
5980 s_lo[2] = -1.0;
5981 s_hi[2] = 0.0;
5982 break;
5983
5984 case RUF:
5985 s_lo[0] = 0.0;
5986 s_hi[0] = 1.0;
5987 s_lo[1] = 0.0;
5988 s_hi[1] = 1.0;
5989 s_lo[2] = 0.0;
5990 s_hi[2] = 1.0;
5991 break;
5992 }
5993
5994 /// / Pass macro element pointer on to sons and
5995 /// / set coordinates in macro element
5996 /// / hierher why can I see this?
5997 // if(father_el_pt->macro_elem_pt()!=0)
5998 // {
5999 // set_macro_elem_pt(father_el_pt->macro_elem_pt());
6000 // for(unsigned i=0;i<2;i++)
6001 // {
6002 // s_macro_ll(i)= father_el_pt->s_macro_ll(i)+
6003 // 0.5*(s_lo[i]+1.0)*(father_el_pt->s_macro_ur(i)-
6004 // father_el_pt->s_macro_ll(i));
6005 // s_macro_ur(i)= father_el_pt->s_macro_ll(i)+
6006 // 0.5*(s_hi[i]+1.0)*(father_el_pt->s_macro_ur(i)-
6007 // father_el_pt->s_macro_ll(i));
6008 // }
6009 // }
6010
6011
6012 // If the father element hasn't been generated yet, we're stuck...
6013 if (father_el_pt->node_pt(0) == 0)
6014 {
6015 throw OomphLibError(
6016 "Trouble: father_el_pt->node_pt(0)==0\n Can't build son element!\n",
6017 "PRefineableQElement<3,INITIAL_NNODE_1D>::pre_build()",
6019 }
6020 else
6021 {
6022 unsigned jnod = 0;
6023
6025 // Loop over nodes in element
6026 for (unsigned i0 = 0; i0 < n_p; i0++)
6027 {
6028 // Get the fractional position of the node in the direction of s[0]
6030 // Local coordinate in father element
6031 s[0] = s_lo[0] + (s_hi[0] - s_lo[0]) * s_fraction[0];
6032
6033 for (unsigned i1 = 0; i1 < n_p; i1++)
6034 {
6035 // Get the fractional position of the node in the direction of s[1]
6037 // Local coordinate in father element
6038 s[1] = s_lo[1] + (s_hi[1] - s_lo[1]) * s_fraction[1];
6039
6040 for (unsigned i2 = 0; i2 < n_p; i2++)
6041 {
6042 // Get the fractional position of the node in the direction of
6043 // s[2]
6045 // Local coordinate in father element
6046 s[2] = s_lo[2] + (s_hi[2] - s_lo[2]) * s_fraction[2];
6047
6048 // Local node number
6049 jnod = i0 + n_p * i1 + n_p * n_p * i2;
6050
6051 // Get the pointer to the node in the father, returns NULL
6052 // if there is not node
6055
6056 // Does this node already exist in father element?
6057 //------------------------------------------------
6058 if (created_node_pt != 0)
6059 {
6060 // Copy node across
6061 this->node_pt(jnod) = created_node_pt;
6062
6063 // Make sure that we update the values at the node so that
6064 // they are consistent with the present representation.
6065 // This is only need for mixed interpolation where the value
6066 // at the father could now become active.
6067
6068 // Loop over all history values
6069 for (unsigned t = 0; t < ntstorage; t++)
6070 {
6071 // Get values from father element
6072 // Note: get_interpolated_values() sets Vector size itself.
6074 father_el_pt->get_interpolated_values(t, s, prev_values);
6075 // Find the minimum number of values
6076 //(either those stored at the node, or those returned by
6077 // the function)
6078 unsigned n_val_at_node = created_node_pt->nvalue();
6080 // Use the ternary conditional operator here
6084 // Assign the values that we can
6085 for (unsigned k = 0; k < n_var; k++)
6086 {
6087 created_node_pt->set_value(t, k, prev_values[k]);
6088 }
6089 }
6090 }
6091 } // End of loop i2
6092 } // End of loop i1
6093 } // End of loop i0
6094 } // Sanity check: Father element has been generated
6095
6096 } // End of nodes not built
6097 }
6098
6099 //==================================================================
6100 /// p-refine the element inc times. (If inc<0 then p-unrefinement
6101 /// is performed.)
6102 //==================================================================
6103 template<unsigned INITIAL_NNODE_1D>
6105 const int& inc, Mesh* const& mesh_pt, GeneralisedElement* const& clone_pt)
6106 {
6107 // Number of dimensions
6108 unsigned n_dim = 3;
6109
6110 // Cast clone to correct type
6113
6114 // Check if we can use it
6115 if (clone_el_pt == 0)
6116 {
6117 throw OomphLibError(
6118 "Cloned copy must be a PRefineableQElement<3,INITIAL_NNODE_1D>!",
6121 }
6122#ifdef PARANOID
6123 // Clone exists, so check that it is infact a copy of me
6124 else
6125 {
6126 // Flag to keep track of check
6127 bool clone_is_ok = true;
6128
6129 // Does the clone have the correct p-order?
6130 clone_is_ok = clone_is_ok && (clone_el_pt->p_order() == this->p_order());
6131
6132 if (!clone_is_ok)
6133 {
6134 std::ostringstream err_stream;
6135 err_stream << "Clone element has a different p-order from me,"
6136 << std::endl
6137 << "but it is supposed to be a copy!" << std::endl;
6138
6139 throw OomphLibError(
6141 }
6142
6143 // Does the clone have the same number of nodes as me?
6144 clone_is_ok = clone_is_ok && (clone_el_pt->nnode() == this->nnode());
6145
6146 if (!clone_is_ok)
6147 {
6148 std::ostringstream err_stream;
6149 err_stream << "Clone element has a different number of nodes from me,"
6150 << std::endl
6151 << "but it is supposed to be a copy!" << std::endl;
6152
6153 throw OomphLibError(
6155 }
6156
6157 // Does the clone have the same nodes as me?
6158 for (unsigned n = 0; n < this->nnode(); n++)
6159 {
6160 clone_is_ok =
6161 clone_is_ok && (clone_el_pt->node_pt(n) == this->node_pt(n));
6162 }
6163
6164 if (!clone_is_ok)
6165 {
6166 std::ostringstream err_stream;
6167 err_stream << "The nodes belonging to the clone element are different"
6168 << std::endl
6169 << "from mine, but it is supposed to be a copy!"
6170 << std::endl;
6171
6172 throw OomphLibError(
6174 }
6175
6176 // If we get to here then the clone has all the information we require
6177 }
6178#endif
6179
6180 // Currently we can't handle the case of generalised coordinates
6181 // since we haven't established how they should be interpolated.
6182 // Buffer this case:
6183 if (clone_el_pt->node_pt(0)->nposition_type() != 1)
6184 {
6185 throw OomphLibError("Can't handle generalised nodal positions (yet).",
6188 }
6189
6190 // Timestepper should be the same for all nodes -- use it
6191 // to create timesteppers for new nodes
6193
6194 // Get number of history values (incl. present)
6195 unsigned ntstorage = time_stepper_pt->ntstorage();
6196
6197 // Increment p-order of the element
6198 this->P_order += inc;
6199
6200 // Change integration scheme
6201 delete this->integral_pt();
6202 switch (this->P_order)
6203 {
6204 case 2:
6206 break;
6207 case 3:
6209 break;
6210 case 4:
6212 break;
6213 case 5:
6215 break;
6216 case 6:
6218 break;
6219 case 7:
6221 break;
6222 default:
6223 std::ostringstream error_message;
6224 error_message << "\nERROR: Exceeded maximum polynomial order for";
6225 error_message << "\n integration scheme.\n";
6226 throw OomphLibError(error_message.str(),
6229 }
6230
6231 // Allocate new space for Nodes (at the element level)
6232 this->set_n_node(this->P_order * this->P_order * this->P_order);
6233
6234 // Copy vertex nodes and create new edge and internal nodes
6235 //---------------------------------------------------------
6236
6237 // Setup vertex coordinates in element:
6238 //-------------------------------------
6241 s_lo[0] = -1.0;
6242 s_hi[0] = 1.0;
6243 s_lo[1] = -1.0;
6244 s_hi[1] = 1.0;
6245 s_lo[2] = -1.0;
6246 s_hi[2] = 1.0;
6247
6248 // Local coordinate in element
6249 Vector<double> s(n_dim, 0.0);
6250
6251 unsigned jnod = 0;
6252
6254 // Loop over nodes in element
6255 for (unsigned i0 = 0; i0 < this->P_order; i0++)
6256 {
6257 // Get the fractional position of the node in the direction of s[0]
6259 // Local coordinate
6260 s[0] = s_lo[0] + (s_hi[0] - s_lo[0]) * s_fraction[0];
6261
6262 for (unsigned i1 = 0; i1 < this->P_order; i1++)
6263 {
6264 // Get the fractional position of the node in the direction of s[1]
6266 // Local coordinate
6267 s[1] = s_lo[1] + (s_hi[1] - s_lo[1]) * s_fraction[1];
6268
6269 for (unsigned i2 = 0; i2 < this->P_order; i2++)
6270 {
6271 // Get the fractional position of the node in the direction of s[2]
6273 // Local coordinate
6274 s[2] = s_lo[2] + (s_hi[2] - s_lo[2]) * s_fraction[2];
6275
6276 // Local node number
6277 jnod = i0 + this->P_order * i1 + this->P_order * this->P_order * i2;
6278
6279 // Initialise flag: So far, this node hasn't been built
6280 // or copied yet
6281 bool node_done = false;
6282
6283 // Get the pointer to the node in this element (or rather, its clone),
6284 // returns NULL if there is not node
6286 // created_node_pt = 0;
6287
6288 // Does this node already exist in this element?
6289 //----------------------------------------------
6290 if (created_node_pt != 0)
6291 {
6292 // Copy node across
6293 this->node_pt(jnod) = created_node_pt;
6294
6295 // Make sure that we update the values at the node so that
6296 // they are consistent with the present representation.
6297 // This is only need for mixed interpolation where the value
6298 // at the father could now become active.
6299
6300 // Loop over all history values
6301 for (unsigned t = 0; t < ntstorage; t++)
6302 {
6303 // Get values from father element
6304 // Note: get_interpolated_values() sets Vector size itself.
6306 clone_el_pt->get_interpolated_values(t, s, prev_values);
6307 // Find the minimum number of values
6308 //(either those stored at the node, or those returned by
6309 // the function)
6310 unsigned n_val_at_node = created_node_pt->nvalue();
6312 // Use the ternary conditional operator here
6316 // Assign the values that we can
6317 for (unsigned k = 0; k < n_var; k++)
6318 {
6319 created_node_pt->set_value(t, k, prev_values[k]);
6320 }
6321 }
6322
6323 // Node has been created by copy
6324 node_done = true;
6325 }
6326 // Node does not exist in this element but might already
6327 //------------------------------------------------------
6328 // have been created by neighbouring elements
6329 //-------------------------------------------
6330 else
6331 {
6332 // Was the node created by one of its neighbours
6333 // Whether or not the node lies on an edge can be calculated
6334 // by from the fractional position
6335 bool is_periodic = false;
6337 this->node_created_by_neighbour(s_fraction, is_periodic);
6338
6339 // If the node was so created, assign the pointers
6340 if (created_node_pt != 0)
6341 {
6342 // If the node is periodic
6343 if (is_periodic)
6344 {
6345 // Now the node must be on a boundary, but we don't know which
6346 // one
6347 // The returned created_node_pt is actually the neighbouring
6348 // periodic node
6350
6351 // Determine the edge on which the new node will live
6352 //(cannot be a vertex node)
6353 int my_bound = Tree::OMEGA;
6354 // If we are on the left face
6355 if (i0 == 0)
6356 {
6358 }
6359 // If we are on the right face
6360 else if (i0 == this->P_order - 1)
6361 {
6363 }
6364
6365 // If we are on the bottom face
6366 if (i1 == 0)
6367 {
6368 // If we already have already set the boundary, we're on an
6369 // edge
6370 switch (my_bound)
6371 {
6372 case OcTreeNames::L:
6374 break;
6375 case OcTreeNames::R:
6377 break;
6378 // Boundary not set
6379 default:
6381 break;
6382 }
6383 }
6384 // If we are the top face
6385 else if (i1 == this->P_order - 1)
6386 {
6387 // If we already have a boundary
6388 switch (my_bound)
6389 {
6390 case OcTreeNames::L:
6392 break;
6393 case OcTreeNames::R:
6395 break;
6396 default:
6398 break;
6399 }
6400 }
6401
6402 // If we are on the back face
6403 if (i2 == 0)
6404 {
6405 // If we already have already set the boundary, we're on an
6406 // edge
6407 switch (my_bound)
6408 {
6409 case OcTreeNames::L:
6411 break;
6412 case OcTreeNames::LD:
6414 break;
6415 case OcTreeNames::LU:
6417 break;
6418 case OcTreeNames::R:
6420 break;
6421 case OcTreeNames::RD:
6423 break;
6424 case OcTreeNames::RU:
6426 break;
6427 case OcTreeNames::D:
6429 break;
6430 case OcTreeNames::U:
6432 break;
6433 // Boundary not set
6434 default:
6436 break;
6437 }
6438 }
6439 // If we are the front face
6440 else if (i2 == this->P_order - 1)
6441 {
6442 // If we already have a boundary
6443 switch (my_bound)
6444 {
6445 case OcTreeNames::L:
6447 break;
6448 case OcTreeNames::LD:
6450 break;
6451 case OcTreeNames::LU:
6453 break;
6454 case OcTreeNames::R:
6456 break;
6457 case OcTreeNames::RD:
6459 break;
6460 case OcTreeNames::RU:
6462 break;
6463 case OcTreeNames::D:
6465 break;
6466 case OcTreeNames::U:
6468 break;
6469 default:
6471 break;
6472 }
6473 }
6474
6475 // Storage for the set of Mesh boundaries on which the
6476 // appropriate edge lives.
6477 // [New nodes should always be mid-edge nodes and therefore
6478 // only live on one boundary but just to play it safe...]
6479 std::set<unsigned> boundaries;
6480 // Only get the boundaries if we are at the edge of
6481 // an element. Nodes in the centre of an element cannot be
6482 // on Mesh boundaries
6483 if (my_bound != Tree::OMEGA)
6484 {
6485 clone_el_pt->get_boundaries(my_bound, boundaries);
6486 }
6487
6488#ifdef PARANOID
6489 // Case where a new node lives on more than one boundary
6490 // seems fishy enough to flag
6491 if (boundaries.size() > 2)
6492 {
6493 throw OomphLibError(
6494 "boundaries.size()>2 seems a bit strange..\n",
6497 }
6498
6499 // Case when there are no boundaries, we are in big trouble
6500 if (boundaries.size() == 0)
6501 {
6502 std::ostringstream error_stream;
6503 error_stream << "Periodic node is not on a boundary...\n"
6504 << "Coordinates: " << created_node_pt->x(0)
6505 << " " << created_node_pt->x(1) << "\n";
6506 throw OomphLibError(error_stream.str(),
6509 }
6510#endif
6511
6512 // Create node and set the pointer to it from the element
6515 // Make the node periodic from the neighbour
6516 created_node_pt->make_periodic(neighbour_node_pt);
6517
6518 // Loop over # of history values
6519 for (unsigned t = 0; t < ntstorage; t++)
6520 {
6521 // Get position from father element -- this uses the macro
6522 // element representation if appropriate. If the node
6523 // turns out to be a hanging node later on, then
6524 // its position gets adjusted in line with its
6525 // hanging node interpolation.
6528 // Set previous positions of the new node
6529 for (unsigned i = 0; i < n_dim; i++)
6530 {
6531 created_node_pt->x(t, i) = x_prev[i];
6532 }
6533 }
6534
6535 // Check if we need to add nodes to the mesh
6536 if (mesh_pt != 0)
6537 {
6538 // Next, we Update the boundary lookup schemes
6539 // Loop over the boundaries stored in the set
6540 for (std::set<unsigned>::iterator it = boundaries.begin();
6541 it != boundaries.end();
6542 ++it)
6543 {
6544 // Add the node to the boundary
6546
6547 // If we have set an intrinsic coordinate on this
6548 // mesh boundary then it must also be interpolated on
6549 // the new node
6550 // Now interpolate the intrinsic boundary coordinate
6551 if (mesh_pt->boundary_coordinate_exists(*it) == true)
6552 {
6553 Vector<double> zeta(2, 0.0);
6554 clone_el_pt->interpolated_zeta_on_face(
6555 *it, my_bound, s, zeta);
6556
6557 created_node_pt->set_coordinates_on_boundary(*it, zeta);
6558 }
6559 }
6560
6561 // Make sure that we add the node to the mesh
6562 mesh_pt->add_node_pt(created_node_pt);
6563 }
6564 } // End of periodic case
6565 // Otherwise the node is not periodic, so just set the
6566 // pointer to the neighbours node
6567 else
6568 {
6569 this->node_pt(jnod) = created_node_pt;
6570 }
6571 // Node has been created
6572 node_done = true;
6573 }
6574 // Node does not exist in neighbour element but might already
6575 //-----------------------------------------------------------
6576 // have been created by a son of a neighbouring element
6577 //-----------------------------------------------------
6578 else
6579 {
6580 // Was the node created by one of its neighbours' sons
6581 // Whether or not the node lies on an edge can be calculated
6582 // by from the fractional position
6583 bool is_periodic = false;
6585 this->node_created_by_son_of_neighbour(s_fraction, is_periodic);
6586
6587 // If the node was so created, assign the pointers
6588 if (created_node_pt != 0)
6589 {
6590 // If the node is periodic
6591 if (is_periodic)
6592 {
6593 // Now the node must be on a boundary, but we don't know which
6594 // one
6595 // The returned created_node_pt is actually the neighbouring
6596 // periodic node
6598
6599 // Determine the edge on which the new node will live
6600 //(cannot be a vertex node)
6601 int my_bound = Tree::OMEGA;
6602 // If we are on the left face
6603 if (i0 == 0)
6604 {
6606 }
6607 // If we are on the right face
6608 else if (i0 == this->P_order - 1)
6609 {
6611 }
6612
6613 // If we are on the bottom face
6614 if (i1 == 0)
6615 {
6616 // If we already have already set the boundary, we're on an
6617 // edge
6618 switch (my_bound)
6619 {
6620 case OcTreeNames::L:
6622 break;
6623 case OcTreeNames::R:
6625 break;
6626 // Boundary not set
6627 default:
6629 break;
6630 }
6631 }
6632 // If we are the top face
6633 else if (i1 == this->P_order - 1)
6634 {
6635 // If we already have a boundary
6636 switch (my_bound)
6637 {
6638 case OcTreeNames::L:
6640 break;
6641 case OcTreeNames::R:
6643 break;
6644 default:
6646 break;
6647 }
6648 }
6649
6650 // If we are on the back face
6651 if (i2 == 0)
6652 {
6653 // If we already have already set the boundary, we're on an
6654 // edge
6655 switch (my_bound)
6656 {
6657 case OcTreeNames::L:
6659 break;
6660 case OcTreeNames::LD:
6662 break;
6663 case OcTreeNames::LU:
6665 break;
6666 case OcTreeNames::R:
6668 break;
6669 case OcTreeNames::RD:
6671 break;
6672 case OcTreeNames::RU:
6674 break;
6675 case OcTreeNames::D:
6677 break;
6678 case OcTreeNames::U:
6680 break;
6681 // Boundary not set
6682 default:
6684 break;
6685 }
6686 }
6687 // If we are the front face
6688 else if (i2 == this->P_order - 1)
6689 {
6690 // If we already have a boundary
6691 switch (my_bound)
6692 {
6693 case OcTreeNames::L:
6695 break;
6696 case OcTreeNames::LD:
6698 break;
6699 case OcTreeNames::LU:
6701 break;
6702 case OcTreeNames::R:
6704 break;
6705 case OcTreeNames::RD:
6707 break;
6708 case OcTreeNames::RU:
6710 break;
6711 case OcTreeNames::D:
6713 break;
6714 case OcTreeNames::U:
6716 break;
6717 default:
6719 break;
6720 }
6721 }
6722
6723 // Storage for the set of Mesh boundaries on which the
6724 // appropriate edge lives.
6725 // [New nodes should always be mid-edge nodes and therefore
6726 // only live on one boundary but just to play it safe...]
6727 std::set<unsigned> boundaries;
6728 // Only get the boundaries if we are at the edge of
6729 // an element. Nodes in the centre of an element cannot be
6730 // on Mesh boundaries
6731 if (my_bound != Tree::OMEGA)
6732 {
6733 clone_el_pt->get_boundaries(my_bound, boundaries);
6734 }
6735
6736#ifdef PARANOID
6737 // Case where a new node lives on more than one boundary
6738 // seems fishy enough to flag
6739 if (boundaries.size() > 2)
6740 {
6741 throw OomphLibError(
6742 "boundaries.size()>2 seems a bit strange..\n",
6745 }
6746
6747 // Case when there are no boundaries, we are in big trouble
6748 if (boundaries.size() == 0)
6749 {
6750 std::ostringstream error_stream;
6751 error_stream << "Periodic node is not on a boundary...\n"
6752 << "Coordinates: " << created_node_pt->x(0)
6753 << " " << created_node_pt->x(1) << "\n";
6754 throw OomphLibError(error_stream.str(),
6757 }
6758#endif
6759
6760 // Create node and set the pointer to it from the element
6763 // Make the node periodic from the neighbour
6764 created_node_pt->make_periodic(neighbour_node_pt);
6765
6766 // Loop over # of history values
6767 for (unsigned t = 0; t < ntstorage; t++)
6768 {
6769 // Get position from father element -- this uses the macro
6770 // element representation if appropriate. If the node
6771 // turns out to be a hanging node later on, then
6772 // its position gets adjusted in line with its
6773 // hanging node interpolation.
6776 // Set previous positions of the new node
6777 for (unsigned i = 0; i < n_dim; i++)
6778 {
6779 created_node_pt->x(t, i) = x_prev[i];
6780 }
6781 }
6782
6783 // Check if we need to add nodes to the mesh
6784 if (mesh_pt != 0)
6785 {
6786 // Next, we Update the boundary lookup schemes
6787 // Loop over the boundaries stored in the set
6788 for (std::set<unsigned>::iterator it = boundaries.begin();
6789 it != boundaries.end();
6790 ++it)
6791 {
6792 // Add the node to the boundary
6794
6795 // If we have set an intrinsic coordinate on this
6796 // mesh boundary then it must also be interpolated on
6797 // the new node
6798 // Now interpolate the intrinsic boundary coordinate
6799 if (mesh_pt->boundary_coordinate_exists(*it) == true)
6800 {
6801 Vector<double> zeta(2, 0.0);
6802 clone_el_pt->interpolated_zeta_on_face(
6803 *it, my_bound, s, zeta);
6804
6805 created_node_pt->set_coordinates_on_boundary(*it, zeta);
6806 }
6807 }
6808
6809 // Make sure that we add the node to the mesh
6810 mesh_pt->add_node_pt(created_node_pt);
6811 }
6812 } // End of periodic case
6813 // Otherwise the node is not periodic, so just set the
6814 // pointer to the neighbours node
6815 else
6816 {
6817 this->node_pt(jnod) = created_node_pt;
6818 }
6819 // Node has been created
6820 node_done = true;
6821 } // Node does not exist in son of neighbouring element
6822 } // Node does not exist in neighbouring element
6823 } // Node does not exist in this element
6824
6825 // Node has not been built anywhere ---> build it here
6826 if (!node_done)
6827 {
6828 // Firstly, we need to determine whether or not a node lies
6829 // on the boundary before building it, because
6830 // we actually assign a different type of node on boundaries.
6831
6832 // Initially none
6833 int my_bound = Tree::OMEGA;
6834 // If we are on the left face
6835 if (i0 == 0)
6836 {
6838 }
6839 // If we are on the right face
6840 else if (i0 == this->P_order - 1)
6841 {
6843 }
6844
6845 // If we are on the bottom face
6846 if (i1 == 0)
6847 {
6848 // If we already have already set the boundary, we're on an edge
6849 switch (my_bound)
6850 {
6851 case OcTreeNames::L:
6853 break;
6854 case OcTreeNames::R:
6856 break;
6857 // Boundary not set
6858 default:
6860 break;
6861 }
6862 }
6863 // If we are the top face
6864 else if (i1 == this->P_order - 1)
6865 {
6866 // If we already have a boundary
6867 switch (my_bound)
6868 {
6869 case OcTreeNames::L:
6871 break;
6872 case OcTreeNames::R:
6874 break;
6875 default:
6877 break;
6878 }
6879 }
6880
6881 // If we are on the back face
6882 if (i2 == 0)
6883 {
6884 // If we already have already set the boundary, we're on an edge
6885 switch (my_bound)
6886 {
6887 case OcTreeNames::L:
6889 break;
6890 case OcTreeNames::LD:
6892 break;
6893 case OcTreeNames::LU:
6895 break;
6896 case OcTreeNames::R:
6898 break;
6899 case OcTreeNames::RD:
6901 break;
6902 case OcTreeNames::RU:
6904 break;
6905 case OcTreeNames::D:
6907 break;
6908 case OcTreeNames::U:
6910 break;
6911 // Boundary not set
6912 default:
6914 break;
6915 }
6916 }
6917 // If we are the front face
6918 else if (i2 == this->P_order - 1)
6919 {
6920 // If we already have a boundary
6921 switch (my_bound)
6922 {
6923 case OcTreeNames::L:
6925 break;
6926 case OcTreeNames::LD:
6928 break;
6929 case OcTreeNames::LU:
6931 break;
6932 case OcTreeNames::R:
6934 break;
6935 case OcTreeNames::RD:
6937 break;
6938 case OcTreeNames::RU:
6940 break;
6941 case OcTreeNames::D:
6943 break;
6944 case OcTreeNames::U:
6946 break;
6947 default:
6949 break;
6950 }
6951 }
6952
6953 // Storage for the set of Mesh boundaries on which the
6954 // appropriate edge lives.
6955 // [New nodes should always be mid-edge nodes and therefore
6956 // only live on one boundary but just to play it safe...]
6957 std::set<unsigned> boundaries;
6958 // Only get the boundaries if we are at the edge of
6959 // an element. Nodes in the centre of an element cannot be
6960 // on Mesh boundaries
6961 if (my_bound != Tree::OMEGA)
6962 clone_el_pt->get_boundaries(my_bound, boundaries);
6963
6964#ifdef PARANOID
6965 // Case where a new node lives on more than two boundaries
6966 // seems fishy enough to flag
6967 if (boundaries.size() > 2)
6968 {
6969 throw OomphLibError(
6970 "boundaries.size()>2 seems a bit strange..\n",
6971 "PRefineableQElement<3,INITIAL_NNODE_1D>::p_refine()",
6973 }
6974#endif
6975
6976 // If the node lives on a mesh boundary,
6977 // then we need to create a boundary node
6978 if (boundaries.size() > 0)
6979 {
6980 // Create node and set the pointer to it from the element
6983
6984 // Now we need to work out whether to pin the values at
6985 // the new node based on the boundary conditions applied at
6986 // its Mesh boundary
6987
6988 // Get the boundary conditions from the father
6989 Vector<int> bound_cons(this->ncont_interpolated_values());
6990 clone_el_pt->get_bcs(my_bound, bound_cons);
6991
6992 // Loop over the values and pin, if necessary
6993 unsigned n_value = created_node_pt->nvalue();
6994 for (unsigned k = 0; k < n_value; k++)
6995 {
6996 if (bound_cons[k])
6997 {
6998 created_node_pt->pin(k);
6999 }
7000 }
7001
7002 // Solid node? If so, deal with the positional boundary
7003 // conditions:
7005 dynamic_cast<SolidNode*>(created_node_pt);
7006 if (solid_node_pt != 0)
7007 {
7008 // Get the positional boundary conditions from the father:
7009 unsigned n_dim = created_node_pt->ndim();
7013#ifdef PARANOID
7014 if (clone_solid_el_pt == 0)
7015 {
7016 std::string error_message =
7017 "We have a SolidNode outside a refineable SolidElement\n";
7018 error_message +=
7019 "during mesh refinement -- this doesn't make sense";
7020
7021 throw OomphLibError(
7022 error_message,
7023 "PRefineableQElement<3,INITIAL_NNODE_1D>::p_refine()",
7025 }
7026#endif
7028
7029 // Loop over the positions and pin, if necessary
7030 for (unsigned k = 0; k < n_dim; k++)
7031 {
7032 if (solid_bound_cons[k])
7033 {
7034 solid_node_pt->pin_position(k);
7035 }
7036 }
7037 } // End of if solid_node_pt
7038
7039 // Next, we Update the boundary lookup schemes
7040
7041 // Check if we need to add nodes to the mesh
7042 if (mesh_pt != 0)
7043 {
7044 // Loop over the boundaries stored in the set
7045 for (std::set<unsigned>::iterator it = boundaries.begin();
7046 it != boundaries.end();
7047 ++it)
7048 {
7049 // Add the node to the boundary
7051
7052 // If we have set an intrinsic coordinate on this
7053 // mesh boundary then it must also be interpolated on
7054 // the new node
7055 // Now interpolate the intrinsic boundary coordinate
7056 if (mesh_pt->boundary_coordinate_exists(*it) == true)
7057 {
7059 clone_el_pt->interpolated_zeta_on_face(
7060 *it, my_bound, s, zeta);
7061
7062 created_node_pt->set_coordinates_on_boundary(*it, zeta);
7063 }
7064 }
7065 }
7066 }
7067 // Otherwise the node is not on a Mesh boundary and
7068 // we create a normal "bulk" node
7069 else
7070 {
7071 // Create node and set the pointer to it from the element
7073 }
7074
7075 // Now we set the position and values at the newly created node
7076
7077 // In the first instance use macro element or FE representation
7078 // to create past and present nodal positions.
7079 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC
7080 // ELEMENTS AS NOT ALL OF THEM NECESSARILY IMPLEMENT
7081 // NONTRIVIAL NODE UPDATE FUNCTIONS. CALLING
7082 // THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL LEAVE
7083 // THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
7084 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL
7085 // NOT ASSIGN SENSIBLE INITIAL POSITONS!
7086
7087 // Loop over # of history values
7088 for (unsigned t = 0; t < ntstorage; t++)
7089 {
7090 // Get position from father element -- this uses the macro
7091 // element representation if appropriate. If the node
7092 // turns out to be a hanging node later on, then
7093 // its position gets adjusted in line with its
7094 // hanging node interpolation.
7097
7098 // Set previous positions of the new node
7099 for (unsigned i = 0; i < 3; i++)
7100 {
7101 created_node_pt->x(t, i) = x_prev[i];
7102 }
7103 }
7104
7105 // Loop over all history values
7106 for (unsigned t = 0; t < ntstorage; t++)
7107 {
7108 // Get values from father element
7109 // Note: get_interpolated_values() sets Vector size itself.
7111 clone_el_pt->get_interpolated_values(t, s, prev_values);
7112
7113 // Initialise the values at the new node
7114 unsigned n_value = created_node_pt->nvalue();
7115 for (unsigned k = 0; k < n_value; k++)
7116 {
7117 created_node_pt->set_value(t, k, prev_values[k]);
7118 }
7119 }
7120
7121 // Add new node to mesh (if requested)
7122 if (mesh_pt != 0)
7123 {
7124 mesh_pt->add_node_pt(created_node_pt);
7125 }
7126
7128 dynamic_cast<AlgebraicElementBase*>(this);
7129
7130 // If we do have an algebraic element
7131 if (alg_el_pt != 0)
7132 {
7133 std::string error_message =
7134 "Have not implemented p-refinement for";
7135 error_message += "Algebraic p-refineable elements yet\n";
7136
7137 throw OomphLibError(
7138 error_message,
7139 "PRefineableQElement<3,INITIAL_NNODE_1D>::p_refine()",
7141 }
7142
7143 } // End of case when we build the node ourselves
7144
7145 // Check if the element is an algebraic element
7147 dynamic_cast<AlgebraicElementBase*>(this);
7148
7149 // If the element is an algebraic element, setup
7150 // node position (past and present) from algebraic node update
7151 // function. This over-writes previous assingments that
7152 // were made based on the macro-element/FE representation.
7153 // NOTE: YES, THIS NEEDS TO BE CALLED REPEATEDLY IF THE
7154 // NODE IS MEMBER OF MULTIPLE ELEMENTS: THEY ALL ASSIGN
7155 // THE SAME NODAL POSITIONS BUT WE NEED TO ADD THE REMESH
7156 // INFO FOR *ALL* ROOT ELEMENTS!
7157 if (alg_el_pt != 0)
7158 {
7159 // Build algebraic node update info for new node
7160 // This sets up the node update data for all node update
7161 // functions that are shared by all nodes in the father
7162 // element
7163 alg_el_pt->setup_algebraic_node_update(
7164 this->node_pt(jnod), s, clone_el_pt);
7165 }
7166
7167 } // End of vertical loop over nodes in element (i2)
7168
7169 } // End of horizontal loop over nodes in element (i1)
7170
7171 } // End of horizontal loop over nodes in element (i0)
7172
7173
7174 // Loop over all nodes in element again, to re-set the positions
7175 // This must be done using the new element's macro-element
7176 // representation, rather than the old version which may be
7177 // of a different p-order!
7178 for (unsigned i0 = 0; i0 < this->P_order; i0++)
7179 {
7180 // Get the fractional position of the node in the direction of s[0]
7182 // Local coordinate
7183 s[0] = s_lo[0] + (s_hi[0] - s_lo[0]) * s_fraction[0];
7184
7185 for (unsigned i1 = 0; i1 < this->P_order; i1++)
7186 {
7187 // Get the fractional position of the node in the direction of s[1]
7189 // Local coordinate
7190 s[1] = s_lo[1] + (s_hi[1] - s_lo[1]) * s_fraction[1];
7191
7192 for (unsigned i2 = 0; i2 < this->P_order; i2++)
7193 {
7194 // Get the fractional position of the node in the direction of s[2]
7196 // Local coordinate
7197 s[2] = s_lo[2] + (s_hi[2] - s_lo[2]) * s_fraction[2];
7198
7199 // Local node number
7200 jnod = i0 + this->P_order * i1 + this->P_order * this->P_order * i2;
7201
7202 // Loop over # of history values
7203 for (unsigned t = 0; t < ntstorage; t++)
7204 {
7205 // Get position from father element -- this uses the macro
7206 // element representation if appropriate. If the node
7207 // turns out to be a hanging node later on, then
7208 // its position gets adjusted in line with its
7209 // hanging node interpolation.
7211 this->get_x(t, s, x_prev);
7212
7213 // Set previous positions of the new node
7214 for (unsigned i = 0; i < 3; i++)
7215 {
7216 this->node_pt(jnod)->x(t, i) = x_prev[i];
7217 }
7218 }
7219 }
7220 }
7221 }
7222
7223
7224 // If the element is a MacroElementNodeUpdateElement, set
7225 // the update parameters for the current element's nodes --
7226 // all this needs is the vector of (pointers to the)
7227 // geometric objects that affect the MacroElement-based
7228 // node update -- this needs to be done to set the node
7229 // update info for newly created nodes
7232 if (clone_m_el_pt != 0)
7233 {
7234 // Get vector of geometric objects from father (construct vector
7235 // via copy operation)
7236 Vector<GeomObject*> geom_object_pt(clone_m_el_pt->geom_object_pt());
7237
7238 // Cast current element to MacroElementNodeUpdateElement:
7240 dynamic_cast<MacroElementNodeUpdateElementBase*>(this);
7241
7242#ifdef PARANOID
7243 if (m_el_pt == 0)
7244 {
7245 std::string error_message =
7246 "Failed to cast to MacroElementNodeUpdateElementBase*\n";
7247 error_message +=
7248 "Strange -- if my clone is a MacroElementNodeUpdateElement\n";
7249 error_message += "then I should be too....\n";
7250
7251 throw OomphLibError(
7252 error_message,
7253 "PRefineableQElement<3,INITIAL_NNODE_1D>::p_refine()",
7255 }
7256#endif
7257 // Build update info by passing vector of geometric objects:
7258 // This sets the current element to be the update element
7259 // for all of the element's nodes -- this is reversed
7260 // if the element is ever un-refined in the father element's
7261 // rebuild_from_sons() function which overwrites this
7262 // assignment to avoid nasty segmentation faults that occur
7263 // when a node tries to update itself via an element that no
7264 // longer exists...
7265 m_el_pt->set_node_update_info(geom_object_pt);
7266 }
7267
7268 // Not necessary to delete the old nodes since all original nodes are in the
7269 // current mesh and so will be pruned as part of the mesh adaption process.
7270
7271 // Do any further-build required
7272 this->further_build();
7273 }
7274
7275 //=======================================================================
7276 /// Shape functions for PRefineableQElement<DIM>
7277 //=======================================================================
7278 template<unsigned INITIAL_NNODE_1D>
7280 Shape& psi) const
7281 {
7282 switch (P_order)
7283 {
7284 case 2:
7285 {
7286 // Call the OneDimensional Shape functions
7289
7290 // Now let's loop over the nodal points in the element
7291 // and copy the values back in
7292 for (unsigned i = 0; i < P_order; i++)
7293 {
7294 for (unsigned j = 0; j < P_order; j++)
7295 {
7296 for (unsigned k = 0; k < P_order; k++)
7297 {
7298 psi(P_order * P_order * i + P_order * j + k) =
7299 psi3[i] * psi2[j] * psi1[k];
7300 }
7301 }
7302 }
7303 break;
7304 }
7305 case 3:
7306 {
7307 // Call the OneDimensional Shape functions
7310
7311 // Now let's loop over the nodal points in the element
7312 // and copy the values back in
7313 for (unsigned i = 0; i < P_order; i++)
7314 {
7315 for (unsigned j = 0; j < P_order; j++)
7316 {
7317 for (unsigned k = 0; k < P_order; k++)
7318 {
7319 psi(P_order * P_order * i + P_order * j + k) =
7320 psi3[i] * psi2[j] * psi1[k];
7321 }
7322 }
7323 }
7324 break;
7325 }
7326 case 4:
7327 {
7328 // Call the OneDimensional Shape functions
7331
7332 // Now let's loop over the nodal points in the element
7333 // and copy the values back in
7334 for (unsigned i = 0; i < P_order; i++)
7335 {
7336 for (unsigned j = 0; j < P_order; j++)
7337 {
7338 for (unsigned k = 0; k < P_order; k++)
7339 {
7340 psi(P_order * P_order * i + P_order * j + k) =
7341 psi3[i] * psi2[j] * psi1[k];
7342 }
7343 }
7344 }
7345 break;
7346 }
7347 case 5:
7348 {
7349 // Call the OneDimensional Shape functions
7352
7353 // Now let's loop over the nodal points in the element
7354 // and copy the values back in
7355 for (unsigned i = 0; i < P_order; i++)
7356 {
7357 for (unsigned j = 0; j < P_order; j++)
7358 {
7359 for (unsigned k = 0; k < P_order; k++)
7360 {
7361 psi(P_order * P_order * i + P_order * j + k) =
7362 psi3[i] * psi2[j] * psi1[k];
7363 }
7364 }
7365 }
7366 break;
7367 }
7368 case 6:
7369 {
7370 // Call the OneDimensional Shape functions
7373
7374 // Now let's loop over the nodal points in the element
7375 // and copy the values back in
7376 for (unsigned i = 0; i < P_order; i++)
7377 {
7378 for (unsigned j = 0; j < P_order; j++)
7379 {
7380 for (unsigned k = 0; k < P_order; k++)
7381 {
7382 psi(P_order * P_order * i + P_order * j + k) =
7383 psi3[i] * psi2[j] * psi1[k];
7384 }
7385 }
7386 }
7387 break;
7388 }
7389 case 7:
7390 {
7391 // Call the OneDimensional Shape functions
7394
7395 // Now let's loop over the nodal points in the element
7396 // and copy the values back in
7397 for (unsigned i = 0; i < P_order; i++)
7398 {
7399 for (unsigned j = 0; j < P_order; j++)
7400 {
7401 for (unsigned k = 0; k < P_order; k++)
7402 {
7403 psi(P_order * P_order * i + P_order * j + k) =
7404 psi3[i] * psi2[j] * psi1[k];
7405 }
7406 }
7407 }
7408 break;
7409 }
7410 default:
7411 std::ostringstream error_message;
7412 error_message << "\nERROR: Exceeded maximum polynomial order for";
7413 error_message << "\n polynomial order for shape functions.\n";
7414 throw OomphLibError(error_message.str(),
7417 }
7418 }
7419
7420 //=======================================================================
7421 /// Derivatives of shape functions for PRefineableQElement<DIM>
7422 //=======================================================================
7423 template<unsigned INITIAL_NNODE_1D>
7425 const Vector<double>& s, Shape& psi, DShape& dpsids) const
7426 {
7427 switch (P_order)
7428 {
7429 case 2:
7430 {
7431 // Call the shape functions and derivatives
7435 dpsi3ds(s[2]);
7436
7437 // Index for the shape functions
7438 unsigned index = 0;
7439 // Loop over shape functions in element
7440 for (unsigned i = 0; i < P_order; i++)
7441 {
7442 for (unsigned j = 0; j < P_order; j++)
7443 {
7444 for (unsigned k = 0; k < P_order; k++)
7445 {
7446 // Assign the values
7447 dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
7448 dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
7449 dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
7450 psi[index] = psi3[i] * psi2[j] * psi1[k];
7451 // Increment the index
7452 ++index;
7453 }
7454 }
7455 }
7456 break;
7457 }
7458 case 3:
7459 {
7460 // Call the shape functions and derivatives
7464 dpsi3ds(s[2]);
7465
7466 // Index for the shape functions
7467 unsigned index = 0;
7468 // Loop over shape functions in element
7469 for (unsigned i = 0; i < P_order; i++)
7470 {
7471 for (unsigned j = 0; j < P_order; j++)
7472 {
7473 for (unsigned k = 0; k < P_order; k++)
7474 {
7475 // Assign the values
7476 dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
7477 dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
7478 dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
7479 psi[index] = psi3[i] * psi2[j] * psi1[k];
7480 // Increment the index
7481 ++index;
7482 }
7483 }
7484 }
7485 break;
7486 }
7487 case 4:
7488 {
7489 // Call the shape functions and derivatives
7493 dpsi3ds(s[2]);
7494
7495 // Index for the shape functions
7496 unsigned index = 0;
7497 // Loop over shape functions in element
7498 for (unsigned i = 0; i < P_order; i++)
7499 {
7500 for (unsigned j = 0; j < P_order; j++)
7501 {
7502 for (unsigned k = 0; k < P_order; k++)
7503 {
7504 // Assign the values
7505 dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
7506 dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
7507 dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
7508 psi[index] = psi3[i] * psi2[j] * psi1[k];
7509 // Increment the index
7510 ++index;
7511 }
7512 }
7513 }
7514 break;
7515 }
7516 case 5:
7517 {
7518 // Call the shape functions and derivatives
7522 dpsi3ds(s[2]);
7523
7524 // Index for the shape functions
7525 unsigned index = 0;
7526 // Loop over shape functions in element
7527 for (unsigned i = 0; i < P_order; i++)
7528 {
7529 for (unsigned j = 0; j < P_order; j++)
7530 {
7531 for (unsigned k = 0; k < P_order; k++)
7532 {
7533 // Assign the values
7534 dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
7535 dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
7536 dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
7537 psi[index] = psi3[i] * psi2[j] * psi1[k];
7538 // Increment the index
7539 ++index;
7540 }
7541 }
7542 }
7543 break;
7544 }
7545 case 6:
7546 {
7547 // Call the shape functions and derivatives
7551 dpsi3ds(s[2]);
7552
7553 // Index for the shape functions
7554 unsigned index = 0;
7555 // Loop over shape functions in element
7556 for (unsigned i = 0; i < P_order; i++)
7557 {
7558 for (unsigned j = 0; j < P_order; j++)
7559 {
7560 for (unsigned k = 0; k < P_order; k++)
7561 {
7562 // Assign the values
7563 dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
7564 dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
7565 dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
7566 psi[index] = psi3[i] * psi2[j] * psi1[k];
7567 // Increment the index
7568 ++index;
7569 }
7570 }
7571 }
7572 break;
7573 }
7574 case 7:
7575 {
7576 // Call the shape functions and derivatives
7580 dpsi3ds(s[2]);
7581
7582 // Index for the shape functions
7583 unsigned index = 0;
7584 // Loop over shape functions in element
7585 for (unsigned i = 0; i < P_order; i++)
7586 {
7587 for (unsigned j = 0; j < P_order; j++)
7588 {
7589 for (unsigned k = 0; k < P_order; k++)
7590 {
7591 // Assign the values
7592 dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
7593 dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
7594 dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
7595 psi[index] = psi3[i] * psi2[j] * psi1[k];
7596 // Increment the index
7597 ++index;
7598 }
7599 }
7600 }
7601 break;
7602 }
7603 default:
7604 std::ostringstream error_message;
7605 error_message << "\nERROR: Exceeded maximum polynomial order for";
7606 error_message << "\n polynomial order for shape functions.\n";
7607 throw OomphLibError(error_message.str(),
7610 }
7611 }
7612
7613 //=======================================================================
7614 /// Second derivatives of shape functions for PRefineableQElement<DIM>
7615 /// d2psids(i,0) = \f$ d^2 \psi_j / d s^2 \f$
7616 //=======================================================================
7617 template<unsigned INITIAL_NNODE_1D>
7619 const Vector<double>& s, Shape& psi, DShape& dpsids, DShape& d2psids) const
7620 {
7621 std::ostringstream error_message;
7622 error_message
7623 << "\nd2shape_local currently not implemented for this element\n";
7624 throw OomphLibError(
7625 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
7626 }
7627
7628 //=======================================================================
7629 /// Rebuild the element from nodes found in its sons
7630 /// Adjusts its p-order to be the maximum of its sons' p-orders
7631 //=======================================================================
7632 template<unsigned INITIAL_NNODE_1D>
7634 Mesh*& mesh_pt)
7635 {
7636 // Get p-orders of sons
7637 unsigned n_sons = this->tree_pt()->nsons();
7639 unsigned max_son_p_order = 0;
7640 for (unsigned ison = 0; ison < n_sons; ison++)
7641 {
7643 this->tree_pt()->son_pt(ison)->object_pt());
7644 son_p_order[ison] = el_pt->p_order();
7647 }
7648
7649 unsigned old_Nnode = this->nnode();
7650 unsigned old_P_order = this->p_order();
7651 // Set p-order of the element
7652 this->p_order() = max_son_p_order;
7653
7654 // Change integration scheme
7655 delete this->integral_pt();
7656 switch (this->p_order())
7657 {
7658 case 2:
7660 break;
7661 case 3:
7663 break;
7664 case 4:
7666 break;
7667 case 5:
7669 break;
7670 case 6:
7672 break;
7673 case 7:
7675 break;
7676 default:
7677 std::ostringstream error_message;
7678 error_message << "\nERROR: Exceeded maximum polynomial order for";
7679 error_message << "\n integration scheme.\n";
7680 throw OomphLibError(
7681 error_message.str(),
7682 "PRefineableQElement<3,INITIAL_NNODE_1D>::rebuild_from_sons()",
7684 }
7685
7686 // Back up pointers to old nodes before they are lost
7688 for (unsigned n = 0; n < old_Nnode; n++)
7689 {
7690 old_node_pt[n] = this->node_pt(n);
7691 }
7692
7693 // Allocate new space for Nodes (at the element level)
7694 this->set_n_node(this->p_order() * this->p_order() * this->p_order());
7695
7696 // Copy vertex nodes which were populated in the pre-build
7697 this->node_pt(0) = old_node_pt[0];
7698 this->node_pt(this->p_order() - 1) = old_node_pt[old_P_order - 1];
7699 this->node_pt(this->p_order() * (this->p_order() - 1)) =
7701 this->node_pt(this->p_order() * this->p_order() - 1) =
7703 this->node_pt(this->p_order() * this->p_order() * (this->p_order() - 1)) =
7705 this->node_pt((this->p_order() * this->p_order() + 1) *
7706 (this->p_order() - 1)) =
7708 this->node_pt(this->p_order() * (this->p_order() + 1) *
7709 (this->p_order() - 1)) =
7711 this->node_pt(this->p_order() * this->p_order() * this->p_order() - 1) =
7713
7714 // Copy midpoint nodes from sons if new p-order is odd
7715 if (this->p_order() % 2 == 1)
7716 {
7717 // Work out which is midpoint node
7718 unsigned n_p = this->p_order();
7719 unsigned m = (n_p - 1) / 2;
7720
7721 unsigned s0space = m;
7722 unsigned s1space = m * n_p;
7723 unsigned s2space = m * n_p * n_p;
7724
7725 // Back face
7726 // DB edge
7727 this->node_pt(1 * s0space + 0 * s1space + 0 * s2space) =
7728 dynamic_cast<RefineableQElement<3>*>(
7729 octree_pt()->son_pt(OcTreeNames::RDB)->object_pt())
7730 ->vertex_node_pt(0);
7731 // LB edge
7732 this->node_pt(0 * s0space + 1 * s1space + 0 * s2space) =
7733 dynamic_cast<RefineableQElement<3>*>(
7734 octree_pt()->son_pt(OcTreeNames::LUB)->object_pt())
7735 ->vertex_node_pt(0);
7736 // B centre
7737 this->node_pt(1 * s0space + 1 * s1space + 0 * s2space) =
7738 dynamic_cast<RefineableQElement<3>*>(
7739 octree_pt()->son_pt(OcTreeNames::RUB)->object_pt())
7740 ->vertex_node_pt(0);
7741 // RB edge
7742 this->node_pt(2 * s0space + 1 * s1space + 0 * s2space) =
7743 dynamic_cast<RefineableQElement<3>*>(
7744 octree_pt()->son_pt(OcTreeNames::RUB)->object_pt())
7745 ->vertex_node_pt(1);
7746 // UB edge
7747 this->node_pt(1 * s0space + 2 * s1space + 0 * s2space) =
7748 dynamic_cast<RefineableQElement<3>*>(
7749 octree_pt()->son_pt(OcTreeNames::RUB)->object_pt())
7750 ->vertex_node_pt(2);
7751
7752 // Mid-way between between back and front faces
7753 // LD edge
7754 this->node_pt(0 * s0space + 0 * s1space + 1 * s2space) =
7755 dynamic_cast<RefineableQElement<3>*>(
7756 octree_pt()->son_pt(OcTreeNames::LDF)->object_pt())
7757 ->vertex_node_pt(0);
7758 // D centre
7759 this->node_pt(1 * s0space + 0 * s1space + 1 * s2space) =
7760 dynamic_cast<RefineableQElement<3>*>(
7761 octree_pt()->son_pt(OcTreeNames::LDF)->object_pt())
7762 ->vertex_node_pt(1);
7763 // RD edge
7764 this->node_pt(2 * s0space + 0 * s1space + 1 * s2space) =
7765 dynamic_cast<RefineableQElement<3>*>(
7766 octree_pt()->son_pt(OcTreeNames::RDF)->object_pt())
7767 ->vertex_node_pt(1);
7768 // L centre
7769 this->node_pt(0 * s0space + 1 * s1space + 1 * s2space) =
7770 dynamic_cast<RefineableQElement<3>*>(
7771 octree_pt()->son_pt(OcTreeNames::LUF)->object_pt())
7772 ->vertex_node_pt(0);
7773 // Centre
7774 this->node_pt(1 * s0space + 1 * s1space + 1 * s2space) =
7775 dynamic_cast<RefineableQElement<3>*>(
7776 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7777 ->vertex_node_pt(0);
7778 // R centre
7779 this->node_pt(2 * s0space + 1 * s1space + 1 * s2space) =
7780 dynamic_cast<RefineableQElement<3>*>(
7781 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7782 ->vertex_node_pt(1);
7783 // LU edge
7784 this->node_pt(0 * s0space + 2 * s1space + 1 * s2space) =
7785 dynamic_cast<RefineableQElement<3>*>(
7786 octree_pt()->son_pt(OcTreeNames::LUF)->object_pt())
7787 ->vertex_node_pt(2);
7788 // U center
7789 this->node_pt(1 * s0space + 2 * s1space + 1 * s2space) =
7790 dynamic_cast<RefineableQElement<3>*>(
7791 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7792 ->vertex_node_pt(2);
7793 // RU edge
7794 this->node_pt(2 * s0space + 2 * s1space + 1 * s2space) =
7795 dynamic_cast<RefineableQElement<3>*>(
7796 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7797 ->vertex_node_pt(3);
7798
7799 // Front face
7800 // DF edge
7801 this->node_pt(1 * s0space + 0 * s1space + 2 * s2space) =
7802 dynamic_cast<RefineableQElement<3>*>(
7803 octree_pt()->son_pt(OcTreeNames::LDF)->object_pt())
7804 ->vertex_node_pt(5);
7805 // LF edge
7806 this->node_pt(0 * s0space + 1 * s1space + 2 * s2space) =
7807 dynamic_cast<RefineableQElement<3>*>(
7808 octree_pt()->son_pt(OcTreeNames::LUF)->object_pt())
7809 ->vertex_node_pt(4);
7810 // F centre
7811 this->node_pt(1 * s0space + 1 * s1space + 2 * s2space) =
7812 dynamic_cast<RefineableQElement<3>*>(
7813 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7814 ->vertex_node_pt(4);
7815 // RF edge
7816 this->node_pt(2 * s0space + 1 * s1space + 2 * s2space) =
7817 dynamic_cast<RefineableQElement<3>*>(
7818 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7819 ->vertex_node_pt(5);
7820 // UF edge
7821 this->node_pt(1 * s0space + 2 * s1space + 2 * s2space) =
7822 dynamic_cast<RefineableQElement<3>*>(
7823 octree_pt()->son_pt(OcTreeNames::RUF)->object_pt())
7824 ->vertex_node_pt(6);
7825 }
7826
7827 // The timestepper should be the same for all nodes and node 0 should
7828 // never be deleted.
7829 if (this->node_pt(0) == 0)
7830 {
7831 throw OomphLibError(
7832 "The Corner node (0) does not exist",
7833 "PRefineableQElement<3,INITIAL_NNODE_1D>::rebuild_from_sons()",
7835 }
7836
7838 unsigned ntstorage = time_stepper_pt->ntstorage();
7839
7840 unsigned jnod = 0;
7842 // Loop over the nodes in the element
7843 unsigned n_p = this->nnode_1d();
7844 for (unsigned i0 = 0; i0 < n_p; i0++)
7845 {
7846 // Get the fractional position of the node
7848 // Local coordinate
7849 s[0] = -1.0 + 2.0 * s_fraction[0];
7850
7851 for (unsigned i1 = 0; i1 < n_p; i1++)
7852 {
7853 // Get the fractional position of the node in the direction of s[1]
7855 // Local coordinate in father element
7856 s[1] = -1.0 + 2.0 * s_fraction[1];
7857
7858 for (unsigned i2 = 0; i2 < n_p; i2++)
7859 {
7860 // Get the fractional position of the node in the direction of s[2]
7862 // Local coordinate in father element
7863 s[2] = -1.0 + 2.0 * s_fraction[2];
7864
7865 // Set the local node number
7866 jnod = i0 + n_p * i1 + n_p * n_p * i2;
7867
7868 // Initialise flag: So far, this node hasn't been built
7869 // or copied yet
7870 bool node_done = false;
7871
7872 // Get the pointer to the node in this element, returns NULL
7873 // if there is not node
7875
7876 // Does this node already exist in this element?
7877 //----------------------------------------------
7878 if (created_node_pt != 0)
7879 {
7880 // Copy node across
7881 this->node_pt(jnod) = created_node_pt;
7882
7883 // Node has been created by copy
7884 node_done = true;
7885 }
7886 // Node does not exist in this element but might already
7887 //------------------------------------------------------
7888 // have been created by neighbouring elements
7889 //-------------------------------------------
7890 else
7891 {
7892 // Was the node created by one of its neighbours
7893 // Whether or not the node lies on an edge can be calculated
7894 // by from the fractional position
7895 bool is_periodic = false;
7897 node_created_by_neighbour(s_fraction, is_periodic);
7898
7899 // If the node was so created, assign the pointers
7900 if (created_node_pt != 0)
7901 {
7902 // If the node is periodic
7903 if (is_periodic)
7904 {
7905 throw OomphLibError("Cannot handle periodic nodes yet",
7908 }
7909 // Non-periodic case, just set the pointer
7910 else
7911 {
7912 this->node_pt(jnod) = created_node_pt;
7913 }
7914 // Node has been created
7915 node_done = true;
7916 }
7917 } // Node does not exist in this element
7918
7919 // Node has not been built anywhere ---> build it here
7920 if (!node_done)
7921 {
7922 // First, find the son element in which the node should live
7923
7924 // Find coordinates in the sons
7926 // using namespace OcTreeNames;
7927 int son = -10;
7928 // On the left
7929 if (s_fraction[0] < 0.5)
7930 {
7931 // On the bottom
7932 if (s_fraction[1] < 0.5)
7933 {
7934 // On the back
7935 if (s_fraction[2] < 0.5)
7936 {
7937 // It's the LDB son
7939 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
7940 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
7941 s_in_son[2] = -1.0 + 4.0 * s_fraction[2];
7942 }
7943 // On the front
7944 else
7945 {
7946 // It's the LDF son
7948 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
7949 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
7950 s_in_son[2] = -1.0 + 4.0 * (s_fraction[2] - 0.5);
7951 }
7952 }
7953 // On the top
7954 else
7955 {
7956 // On the back
7957 if (s[2] < 0.0)
7958 {
7959 // It's the LUB son
7961 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
7962 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
7963 s_in_son[2] = -1.0 + 4.0 * s_fraction[2];
7964 }
7965 // On the front
7966 else
7967 {
7968 // It's the LUF son
7970 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
7971 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
7972 s_in_son[2] = -1.0 + 4.0 * (s_fraction[2] - 0.5);
7973 }
7974 }
7975 }
7976 // On the right
7977 else
7978 {
7979 // On the bottom
7980 if (s[1] < 0.0)
7981 {
7982 // On the back
7983 if (s[2] < 0.0)
7984 {
7985 // It's the RDB son
7987 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
7988 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
7989 s_in_son[2] = -1.0 + 4.0 * s_fraction[2];
7990 }
7991 // On the front
7992 else
7993 {
7994 // It's the RDF son
7996 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
7997 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
7998 s_in_son[2] = -1.0 + 4.0 * (s_fraction[2] - 0.5);
7999 }
8000 }
8001 // On the top
8002 else
8003 {
8004 // On the back
8005 if (s[2] < 0.0)
8006 {
8007 // It's the RUB son
8009 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
8010 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
8011 s_in_son[2] = -1.0 + 4.0 * s_fraction[2];
8012 }
8013 // On the front
8014 else
8015 {
8016 // It's the RUF son
8018 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
8019 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
8020 s_in_son[2] = -1.0 + 4.0 * (s_fraction[2] - 0.5);
8021 }
8022 }
8023 }
8024
8025 // Get the pointer to the son element in which the new node
8026 // would live
8029 this->tree_pt()->son_pt(son)->object_pt());
8030
8031 // If we are rebuilding, then worry about the boundary conditions
8032 // Find the boundary of the node
8033 // Initially none
8034 int boundary = Tree::OMEGA;
8035 // If we are on the left face
8036 if (i0 == 0)
8037 {
8038 boundary = OcTreeNames::L;
8039 }
8040 // If we are on the right face
8041 else if (i0 == n_p - 1)
8042 {
8043 boundary = OcTreeNames::R;
8044 }
8045
8046 // If we are on the bottom face
8047 if (i1 == 0)
8048 {
8049 // If we already have already set the boundary, we're on an edge
8050 switch (boundary)
8051 {
8052 case OcTreeNames::L:
8053 boundary = OcTreeNames::LD;
8054 break;
8055 case OcTreeNames::R:
8056 boundary = OcTreeNames::RD;
8057 break;
8058 // Boundary not set
8059 default:
8060 boundary = OcTreeNames::D;
8061 break;
8062 }
8063 }
8064 // If we are the top face
8065 else if (i1 == n_p - 1)
8066 {
8067 // If we already have a boundary
8068 switch (boundary)
8069 {
8070 case OcTreeNames::L:
8071 boundary = OcTreeNames::LU;
8072 break;
8073 case OcTreeNames::R:
8074 boundary = OcTreeNames::RU;
8075 break;
8076 default:
8077 boundary = OcTreeNames::U;
8078 break;
8079 }
8080 }
8081
8082 // If we are on the back face
8083 if (i2 == 0)
8084 {
8085 // If we already have already set the boundary, we're on an edge
8086 switch (boundary)
8087 {
8088 case OcTreeNames::L:
8089 boundary = OcTreeNames::LB;
8090 break;
8091 case OcTreeNames::LD:
8092 boundary = OcTreeNames::LDB;
8093 break;
8094 case OcTreeNames::LU:
8095 boundary = OcTreeNames::LUB;
8096 break;
8097 case OcTreeNames::R:
8098 boundary = OcTreeNames::RB;
8099 break;
8100 case OcTreeNames::RD:
8101 boundary = OcTreeNames::RDB;
8102 break;
8103 case OcTreeNames::RU:
8104 boundary = OcTreeNames::RUB;
8105 break;
8106 case OcTreeNames::D:
8107 boundary = OcTreeNames::DB;
8108 break;
8109 case OcTreeNames::U:
8110 boundary = OcTreeNames::UB;
8111 break;
8112 // Boundary not set
8113 default:
8114 boundary = OcTreeNames::B;
8115 break;
8116 }
8117 }
8118 // If we are the front face
8119 else if (i2 == n_p - 1)
8120 {
8121 // If we already have a boundary
8122 switch (boundary)
8123 {
8124 case OcTreeNames::L:
8125 boundary = OcTreeNames::LF;
8126 break;
8127 case OcTreeNames::LD:
8128 boundary = OcTreeNames::LDF;
8129 break;
8130 case OcTreeNames::LU:
8131 boundary = OcTreeNames::LUF;
8132 break;
8133 case OcTreeNames::R:
8134 boundary = OcTreeNames::RF;
8135 break;
8136 case OcTreeNames::RD:
8137 boundary = OcTreeNames::RDF;
8138 break;
8139 case OcTreeNames::RU:
8140 boundary = OcTreeNames::RUF;
8141 break;
8142 case OcTreeNames::D:
8143 boundary = OcTreeNames::DF;
8144 break;
8145 case OcTreeNames::U:
8146 boundary = OcTreeNames::UF;
8147 break;
8148 default:
8149 boundary = OcTreeNames::F;
8150 break;
8151 }
8152 }
8153
8154 // set of boundaries that this edge in the son lives on
8155 std::set<unsigned> boundaries;
8156
8157 // Now get the boundary conditions from the son
8158 // The boundaries will be common to the son because there can be
8159 // no rotations here
8160 if (boundary != Tree::OMEGA)
8161 {
8162 son_el_pt->get_boundaries(boundary, boundaries);
8163 }
8164
8165 // If the node lives on a boundary:
8166 // Construct a boundary node,
8167 // Get boundary conditions and
8168 // update all lookup schemes
8169 if (boundaries.size() > 0)
8170 {
8171 // Construct the new node
8174
8175 // Get the boundary conditions from the son
8176 Vector<int> bound_cons(this->ncont_interpolated_values());
8177 son_el_pt->get_bcs(boundary, bound_cons);
8178
8179 // Loop over the values and pin if necessary
8180 unsigned nval = created_node_pt->nvalue();
8181 for (unsigned k = 0; k < nval; k++)
8182 {
8183 if (bound_cons[k])
8184 {
8185 created_node_pt->pin(k);
8186 }
8187 }
8188
8189 // Solid node? If so, deal with the positional boundary
8190 // conditions:
8192 dynamic_cast<SolidNode*>(created_node_pt);
8193 if (solid_node_pt != 0)
8194 {
8195 // Get the positional boundary conditions from the father:
8196 unsigned n_dim = created_node_pt->ndim();
8199 dynamic_cast<RefineableSolidQElement<3>*>(son_el_pt);
8200#ifdef PARANOID
8201 if (son_solid_el_pt == 0)
8202 {
8203 std::string error_message =
8204 "We have a SolidNode outside a refineable SolidElement\n";
8205 error_message +=
8206 "during mesh refinement -- this doesn't make sense\n";
8207
8208 throw OomphLibError(error_message,
8209 "PRefineableQElement<3,INITIAL_NNODE_1D>:"
8210 ":rebuild_from_sons()",
8212 }
8213#endif
8214 son_solid_el_pt->get_solid_bcs(boundary, solid_bound_cons);
8215
8216 // Loop over the positions and pin, if necessary
8217 for (unsigned k = 0; k < n_dim; k++)
8218 {
8219 if (solid_bound_cons[k])
8220 {
8221 solid_node_pt->pin_position(k);
8222 }
8223 }
8224 } // End of if solid_node_pt
8225
8226
8227 // Next we update the boundary look-up schemes
8228 // Loop over the boundaries stored in the set
8229 for (std::set<unsigned>::iterator it = boundaries.begin();
8230 it != boundaries.end();
8231 ++it)
8232 {
8233 // Add the node to the boundary
8235
8236 // If we have set an intrinsic coordinate on this
8237 // mesh boundary then it must also be interpolated on
8238 // the new node
8239 // Now interpolate the intrinsic boundary coordinate
8240 if (mesh_pt->boundary_coordinate_exists(*it) == true)
8241 {
8243 son_el_pt->interpolated_zeta_on_face(
8244 *it, boundary, s_in_son, zeta);
8245
8246 created_node_pt->set_coordinates_on_boundary(*it, zeta);
8247 }
8248 }
8249 }
8250 // Otherwise the node is not on a Mesh boundary
8251 // and we create a normal "bulk" node
8252 else
8253 {
8254 // Construct the new node
8256 }
8257
8258 // Now we set the position and values at the newly created node
8259
8260 // In the first instance use macro element or FE representation
8261 // to create past and present nodal positions.
8262 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC
8263 // ELEMENTS AS NOT ALL OF THEM NECESSARILY IMPLEMENT
8264 // NONTRIVIAL NODE UPDATE FUNCTIONS. CALLING
8265 // THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL LEAVE
8266 // THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
8267 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL
8268 // NOT ASSIGN SENSIBLE INITIAL POSITONS!
8269
8270 // Loop over # of history values
8271 // Loop over # of history values
8272 for (unsigned t = 0; t < ntstorage; t++)
8273 {
8274 using namespace QuadTreeNames;
8275 // Get the position from the son
8277
8278 // Now let's fill in the value
8280 for (unsigned i = 0; i < 3; i++)
8281 {
8282 created_node_pt->x(t, i) = x_prev[i];
8283 }
8284 }
8285
8286 // Now set up the values
8287 // Loop over all history values
8288 for (unsigned t = 0; t < ntstorage; t++)
8289 {
8290 // Get values from father element
8291 // Note: get_interpolated_values() sets Vector size itself.
8293 son_el_pt->get_interpolated_values(t, s_in_son, prev_values);
8294
8295 // Initialise the values at the new node
8296 for (unsigned k = 0; k < created_node_pt->nvalue(); k++)
8297 {
8298 created_node_pt->set_value(t, k, prev_values[k]);
8299 }
8300 }
8301
8302 // Add the node to the mesh
8303 mesh_pt->add_node_pt(created_node_pt);
8304
8305 // Check if the element is an algebraic element
8307 dynamic_cast<AlgebraicElementBase*>(this);
8308
8309 // If we do have an algebraic element
8310 if (alg_el_pt != 0)
8311 {
8312 std::string error_message =
8313 "Have not implemented rebuilding from sons for";
8314 error_message += "Algebraic p-refineable elements yet\n";
8315
8316 throw OomphLibError(
8317 error_message,
8318 "PRefineableQElement<3,INITIAL_NNODE_1D>::rebuild_from_sons()",
8320 }
8321
8322 } // End of the case when we build the node ourselves
8323 }
8324 }
8325 }
8326 }
8327
8328 //=================================================================
8329 /// Check inter-element continuity of
8330 /// - nodal positions
8331 /// - (nodally) interpolated function values
8332 /// Overloaded to not check differences in the value. Mortaring
8333 /// doesn't enforce strong continuity between elements.
8334 //====================================================================
8335 template<unsigned INITIAL_NNODE_1D>
8337 double& max_error)
8338 {
8339 // Not yet implemented -- doing nothing for now.
8340
8341 // Dummy set max_error to 0
8342 max_error = 0.0;
8343 return;
8344 }
8345
8346 //=================================================================
8347 /// Internal function to set up the hanging nodes on a particular
8348 /// edge of the element. Implements the mortar method.
8349 //=================================================================
8350 template<unsigned INITIAL_NNODE_1D>
8352 const int& value_id, const int& my_face, std::ofstream& output_hangfile)
8353 {
8354 using namespace OcTreeNames;
8355
8361
8362 // Find pointer to neighbour in this direction
8364 neigh_pt = this->octree_pt()->gteq_face_neighbour(my_face,
8366 s_lo_neigh,
8367 s_hi_neigh,
8368 neigh_face,
8369 diff_level,
8371
8372 // Work out master/dependent faces
8373 //----------------------------
8374
8375 // Set up booleans
8376 // bool h_type_master = false;
8377 bool h_type_dependent = false;
8378 // bool p_type_master = false;
8379 bool p_type_dependent = false;
8380
8381 // Neighbour exists
8382 if (neigh_pt != 0)
8383 {
8384 // Check if neighbour is bigger than me
8385 if (diff_level != 0)
8386 {
8387 // Dependent at h-type non-conformity
8388 h_type_dependent = true;
8389 }
8390 // Check if neighbour is the same size as me
8391 else if (neigh_pt->nsons() == 0)
8392 {
8393 // Neighbour is same size as me
8394 // Find p-orders of each element
8395 unsigned my_p_order =
8396 dynamic_cast<PRefineableQElement<3, INITIAL_NNODE_1D>*>(this)
8397 ->p_order();
8398 unsigned neigh_p_order =
8400 neigh_pt->object_pt())
8401 ->p_order();
8402
8403 // Check for p-type non-conformity
8405 {
8406 // At a conforming interface
8407 }
8408 else if (neigh_p_order < my_p_order)
8409 {
8410 // Dependent at p-type non-conformity
8411 p_type_dependent = true;
8412 }
8413 else
8414 {
8415 // Master at p-type non-conformity
8416 // p_type_master = true;
8417 }
8418 }
8419 // Neighbour must be smaller than me
8420 else
8421 {
8422 // Master at h-type non-conformity
8423 // h_type_master = true;
8424 }
8425 }
8426 else
8427 {
8428 // Face is on a boundary
8429 }
8430
8431 // Work out master/dependent edges
8432 //----------------------------
8433 if (h_type_dependent || p_type_dependent || true)
8434 {
8435 // Get edges of face and the face that shares that edge
8437 switch (my_face)
8438 {
8439 case L:
8440 face_edge[0] = LB;
8441 face_edge_other_face[0] = B;
8442 face_edge[1] = LF;
8443 face_edge_other_face[1] = F;
8444 face_edge[2] = LD;
8445 face_edge_other_face[2] = D;
8446 face_edge[3] = LU;
8447 face_edge_other_face[3] = U;
8448 break;
8449 case R:
8450 face_edge[0] = RB;
8451 face_edge_other_face[0] = B;
8452 face_edge[1] = RF;
8453 face_edge_other_face[1] = F;
8454 face_edge[2] = RD;
8455 face_edge_other_face[2] = D;
8456 face_edge[3] = RU;
8457 face_edge_other_face[3] = U;
8458 break;
8459 case U:
8460 face_edge[0] = UB;
8461 face_edge_other_face[0] = B;
8462 face_edge[1] = UF;
8463 face_edge_other_face[1] = F;
8464 face_edge[2] = LU;
8465 face_edge_other_face[2] = L;
8466 face_edge[3] = RU;
8467 face_edge_other_face[3] = R;
8468 break;
8469 case D:
8470 face_edge[0] = DB;
8471 face_edge_other_face[0] = B;
8472 face_edge[1] = DF;
8473 face_edge_other_face[1] = F;
8474 face_edge[2] = LD;
8475 face_edge_other_face[2] = L;
8476 face_edge[3] = RD;
8477 face_edge_other_face[3] = R;
8478 break;
8479 case B:
8480 face_edge[0] = DB;
8481 face_edge_other_face[0] = D;
8482 face_edge[1] = UB;
8483 face_edge_other_face[1] = U;
8484 face_edge[2] = LB;
8485 face_edge_other_face[2] = L;
8486 face_edge[3] = RB;
8487 face_edge_other_face[3] = R;
8488 break;
8489 case F:
8490 face_edge[0] = DF;
8491 face_edge_other_face[0] = D;
8492 face_edge[1] = UF;
8493 face_edge_other_face[1] = U;
8494 face_edge[2] = LF;
8495 face_edge_other_face[2] = L;
8496 face_edge[3] = RF;
8497 face_edge_other_face[3] = R;
8498 break;
8499 default:
8500 throw OomphLibError(
8501 "my_face not L, R, D, U, B, F\n",
8502 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
8504 }
8505
8506 // Loop over edges of face
8507 for (unsigned i = 0; i < 4; i++)
8508 {
8509 // Get edge
8510 unsigned my_edge = face_edge[i];
8511
8512 // Separate storage for edge mortaring
8513 OcTree* edge_neigh_pt = 0;
8517 int neigh_edge = 0, edge_diff_level = 0;
8518 unsigned edge_p_order =
8519 dynamic_cast<PRefineableQElement<3, INITIAL_NNODE_1D>*>(this)
8520 ->p_order();
8521
8522 // Temporary storage to keep track of master edge
8527
8528 // Initially return the zero-th true edge neighbour
8529 unsigned i_root_edge_neighbour = 0;
8530
8531 // Initialise the total number of true edge neighbours
8532 unsigned nroot_edge_neighbour = 0;
8533
8534 // Flag to keep track of master status of my_edge
8535 bool my_edge_is_master = true;
8536 // unsigned master_edge_index=0;
8537
8538 // Keep searching until we've found the node or until we've checked
8539 // all available edge neighbours
8540 bool keep_searching = true;
8541 bool search_faces = false;
8542 bool first_face_searched = false;
8543 // unsigned index=0;
8544 while (keep_searching)
8545 {
8546 // Pointer to edge neighbouring OcTree
8548
8549 // Looking in edge neighbours that are also face neighbours
8550 // if(index==0 || index==1)
8551 if (search_faces)
8552 {
8554 {
8555 // Find pointer to neighbour in this direction
8557 this->octree_pt()->gteq_face_neighbour(my_face,
8564
8565 // Mark first face as searched
8566 first_face_searched = true;
8567 }
8568 else
8569 {
8570 // Find pointer to neighbour in this direction
8572 this->octree_pt()->gteq_face_neighbour(face_edge_other_face[i],
8579
8580 // Search is finally exhausted
8581 keep_searching = false;
8582 }
8583 }
8584 // Looking in a true edge neighbour
8585 else
8586 {
8587 // Find pointer to neighbour in this direction
8589 this->octree_pt()->gteq_true_edge_neighbour(my_edge,
8597 }
8598
8599 // Set up booleans
8600 // bool h_type_edge_master = false;
8601 // bool h_type_edge_dependent = false;
8602 // bool p_type_edge_master = false;
8603 // bool p_type_edge_dependent = false;
8604
8605 // Flag to check if we have a new edge master
8606 bool new_edge_master = false;
8607
8608 // Edge neighbour exists
8609 if (tmp_edge_neigh_pt != 0)
8610 {
8611 // Check if neighbour is bigger than my biggest neighbour
8613 {
8614 // Dependent at h-type non-conformity
8615 // h_type_edge_dependent = true;
8616 new_edge_master = true;
8617 // Update edge_diff_level and p-order
8619 edge_p_order =
8621 tmp_edge_neigh_pt->object_pt())
8622 ->p_order();
8623 }
8624 // Check if neighbour is the same size as my biggest neighbour
8626 tmp_edge_neigh_pt->nsons() == 0)
8627 {
8628 // Neighbour is same size as me
8629 // Find p-orders of each element
8630 // unsigned my_p_order =
8631 // dynamic_cast<PRefineableQElement<3,INITIAL_NNODE_1D>*>
8632 // (this)->p_order();
8633 unsigned tmp_edge_neigh_p_order =
8635 tmp_edge_neigh_pt->object_pt())
8636 ->p_order();
8637
8638 // Check for p-type non-conformity
8640 {
8641 // At a conforming interface
8642 }
8644 {
8645 // Dependent at p-type non-conformity
8646 // p_type_edge_dependent = true;
8647 new_edge_master = true;
8648 // Update edge_diff_level and p-order
8650 edge_p_order =
8652 tmp_edge_neigh_pt->object_pt())
8653 ->p_order();
8654 }
8655 else
8656 {
8657 // Master at p-type non-conformity
8658 // p_type_edge_master = true;
8659 }
8660 }
8661 // Neighbour must be smaller than me
8662 else
8663 {
8664 // Master at h-type non-conformity
8665 // h_type_edge_master = true;
8666 }
8667 }
8668 else
8669 {
8670 // Edge is on a boundary
8671 }
8672
8673 // Update master neighbour information
8674 if (new_edge_master)
8675 {
8676 // Store new master edge information
8683
8684 // Set this edge as dependent
8685 my_edge_is_master = false;
8686 }
8687
8688 // Keep searching, but only if there are further edge neighbours
8689 // Try next root edge neighbour
8691
8692 // Increment counter
8693 // index++;
8694
8695 // Have we exhausted the search over true edge neighbours
8696 // if (index>2 && i_root_edge_neighbour>=nroot_edge_neighbour)
8698 {
8699 // keep_searching = false;
8700 // Extend search to face neighours (these are edge neighbours too)
8701 search_faces = true;
8702 }
8703
8704 } // End of while keep searching over all face and true edge neighbours
8705
8706 // Now do edge mortaring to enforce the mortar vertex matching condition
8707 if (!my_edge_is_master)
8708 {
8709 // Compute the active coordinate index along the this side of mortar
8710 unsigned active_coord_index;
8711 switch (my_edge)
8712 {
8713 case DB:
8714 case DF:
8715 case UB:
8716 case UF:
8718 break;
8719 case LB:
8720 case RB:
8721 case LF:
8722 case RF:
8724 break;
8725 case LD:
8726 case RD:
8727 case LU:
8728 case RU:
8730 break;
8731 default:
8732 throw OomphLibError(
8733 "Cannot transform coordinates",
8734 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
8736 }
8737
8738 // Get pointer to neighbouring master element (in p-refineable form)
8742 edge_neigh_pt->object_pt());
8743
8744 // Create vector of master and dependent nodes
8745 //----------------------------------------
8746 Vector<Node*> master_node_pt, dependent_node_pt;
8749
8750 // Number of nodes in one dimension
8751 const unsigned my_n_p = this->ninterpolating_node_1d(value_id);
8752 const unsigned neigh_n_p =
8753 edge_neigh_obj_pt->ninterpolating_node_1d(value_id);
8754
8755 // Storage for pointers to the nodes and their numbers along the
8756 // master edge
8757 unsigned neighbour_node_number = 0;
8759
8760 // Loop over nodes along the edge
8761 bool master_is_not_edge = false;
8762 for (unsigned i0 = 0; i0 < neigh_n_p; i0++)
8763 {
8764 const unsigned s0space = 1;
8765 const unsigned s1space = neigh_n_p;
8766 const unsigned s2space = neigh_n_p * neigh_n_p;
8767
8768 // Find the neighbour's node
8769 switch (neigh_edge)
8770 {
8771 case DB:
8773 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8775 break;
8776 case UB:
8778 (neigh_n_p - 1) * s1space + i0 * s0space;
8779 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8781 break;
8782 case DF:
8784 (neigh_n_p - 1) * s2space + i0 * s0space;
8785 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8787 break;
8788 case UF:
8790 (neigh_n_p - 1) * s2space +
8791 i0 * s0space;
8792 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8794 break;
8795
8796 case LB:
8798 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8800 break;
8801 case RB:
8803 (neigh_n_p - 1) * s0space + i0 * s1space;
8804 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8806 break;
8807 case LF:
8809 (neigh_n_p - 1) * s2space + i0 * s1space;
8810 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8812 break;
8813 case RF:
8815 (neigh_n_p - 1) * s2space +
8816 i0 * s1space;
8817 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8819 break;
8820
8821 case LD:
8823 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8825 break;
8826 case RD:
8828 (neigh_n_p - 1) * s0space + i0 * s2space;
8829 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8831 break;
8832 case LU:
8834 (neigh_n_p - 1) * s1space + i0 * s2space;
8835 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8837 break;
8838 case RU:
8840 (neigh_n_p - 1) * s1space +
8841 i0 * s2space;
8842 neighbour_node_pt = edge_neigh_obj_pt->interpolating_node_pt(
8844 break;
8845
8846 default:
8847 // Master 'edge' may be a face instead, so no need to throw an
8848 // error
8849 master_is_not_edge = true;
8850 }
8851
8852 if (master_is_not_edge) break;
8853
8854 // Set node as master node
8856 master_node_pt.push_back(neighbour_node_pt);
8857 }
8858
8859 // Now if edge is really a face (from an edge neighbour that isn't
8860 // a true edge neighbour) each node on the face is (potentially) a
8861 // master
8863 {
8864 // Loop over nodes along the face
8865 for (unsigned i0 = 0; i0 < neigh_n_p; i0++)
8866 {
8867 // Loop over nodes along the face
8868 for (unsigned i1 = 0; i1 < neigh_n_p; i1++)
8869 {
8870 const unsigned s0space = 1;
8871 const unsigned s1space = neigh_n_p;
8872 const unsigned s2space = neigh_n_p * neigh_n_p;
8873
8874 // Find the neighbour's node
8875 switch (neigh_edge)
8876 {
8877 case B:
8880 edge_neigh_obj_pt->interpolating_node_pt(
8882 break;
8883 case F:
8885 (neigh_n_p - 1) * s2space + i0 * s0space + i1 * s1space;
8887 edge_neigh_obj_pt->interpolating_node_pt(
8889 break;
8890 case D:
8893 edge_neigh_obj_pt->interpolating_node_pt(
8895 break;
8896 case U:
8898 (neigh_n_p - 1) * s1space + i0 * s0space + i1 * s2space;
8900 edge_neigh_obj_pt->interpolating_node_pt(
8902 break;
8903 case L:
8906 edge_neigh_obj_pt->interpolating_node_pt(
8908 break;
8909 case R:
8911 (neigh_n_p - 1) * s0space + i0 * s1space + i1 * s2space;
8913 edge_neigh_obj_pt->interpolating_node_pt(
8915 break;
8916
8917 default:
8918 throw OomphLibError("neigh_edge not recognised\n",
8919 "PRefineableQElement<3,INITIAL_NNODE_"
8920 "1D>::oc_hang_helper()",
8922 }
8923
8924 // Set node as master node
8926 master_node_pt.push_back(neighbour_node_pt);
8927 }
8928 }
8929 }
8930
8931 // Storage for pointers to the local nodes and their numbers along my
8932 // edge
8933 unsigned local_node_number = 0;
8934 Node* local_node_pt = 0;
8935
8936 // Loop over the nodes along my edge
8937 for (unsigned i0 = 0; i0 < my_n_p; i0++)
8938 {
8939 // Storage for the fractional position of the node in the element
8941
8942 const unsigned s0space = 1;
8943 const unsigned s1space = my_n_p;
8944 const unsigned s2space = my_n_p * my_n_p;
8945
8946 // Find the local node and the fractional position of the node
8947 // which depends on the edge, of course
8948 switch (my_edge)
8949 {
8950 case DB:
8951 s_fraction[0] =
8952 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
8953 s_fraction[1] = 0.0;
8954 s_fraction[2] = 0.0;
8957 this->interpolating_node_pt(local_node_number, value_id);
8958 break;
8959 case UB:
8960 s_fraction[0] =
8961 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
8962 s_fraction[1] = 1.0;
8963 s_fraction[2] = 0.0;
8966 this->interpolating_node_pt(local_node_number, value_id);
8967 break;
8968 case DF:
8969 s_fraction[0] =
8970 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
8971 s_fraction[1] = 0.0;
8972 s_fraction[2] = 1.0;
8975 this->interpolating_node_pt(local_node_number, value_id);
8976 break;
8977 case UF:
8978 s_fraction[0] =
8979 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
8980 s_fraction[1] = 1.0;
8981 s_fraction[2] = 1.0;
8982 local_node_number = (my_n_p - 1) * s1space +
8983 (my_n_p - 1) * s2space + i0 * s0space;
8985 this->interpolating_node_pt(local_node_number, value_id);
8986 break;
8987
8988 case LB:
8989 s_fraction[0] = 0.0;
8990 s_fraction[1] =
8991 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
8992 s_fraction[2] = 0.0;
8995 this->interpolating_node_pt(local_node_number, value_id);
8996 break;
8997 case RB:
8998 s_fraction[0] = 1.0;
8999 s_fraction[1] =
9000 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
9001 s_fraction[2] = 0.0;
9004 this->interpolating_node_pt(local_node_number, value_id);
9005 break;
9006 case LF:
9007 s_fraction[0] = 0.0;
9008 s_fraction[1] =
9009 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
9010 s_fraction[2] = 1.0;
9013 this->interpolating_node_pt(local_node_number, value_id);
9014 break;
9015 case RF:
9016 s_fraction[0] = 1.0;
9017 s_fraction[1] =
9018 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
9019 s_fraction[2] = 1.0;
9020 local_node_number = (my_n_p - 1) * s0space +
9021 (my_n_p - 1) * s2space + i0 * s1space;
9023 this->interpolating_node_pt(local_node_number, value_id);
9024 break;
9025
9026 case LD:
9027 s_fraction[0] = 0.0;
9028 s_fraction[1] = 0.0;
9029 s_fraction[2] =
9030 local_one_d_fraction_of_interpolating_node(i0, 2, value_id);
9033 this->interpolating_node_pt(local_node_number, value_id);
9034 break;
9035 case RD:
9036 s_fraction[0] = 1.0;
9037 s_fraction[1] = 0.0;
9038 s_fraction[2] =
9039 local_one_d_fraction_of_interpolating_node(i0, 2, value_id);
9042 this->interpolating_node_pt(local_node_number, value_id);
9043 break;
9044 case LU:
9045 s_fraction[0] = 0.0;
9046 s_fraction[1] = 1.0;
9047 s_fraction[2] =
9048 local_one_d_fraction_of_interpolating_node(i0, 2, value_id);
9051 this->interpolating_node_pt(local_node_number, value_id);
9052 break;
9053 case RU:
9054 s_fraction[0] = 1.0;
9055 s_fraction[1] = 1.0;
9056 s_fraction[2] =
9057 local_one_d_fraction_of_interpolating_node(i0, 2, value_id);
9058 local_node_number = (my_n_p - 1) * s0space +
9059 (my_n_p - 1) * s1space + i0 * s2space;
9061 this->interpolating_node_pt(local_node_number, value_id);
9062 break;
9063
9064 default:
9065 throw OomphLibError(
9066 "my_edge not recognised\n",
9067 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9069 }
9070
9071 // Add node to vector of dependent element nodes
9074
9075 // Store node's local fraction
9077 }
9078
9079 // Store the number of dependent and master nodes for use later
9080 const unsigned n_dependent_nodes = dependent_node_pt.size();
9081 const unsigned n_master_nodes = master_node_pt.size();
9082 const unsigned dependent_element_nnode_1d = my_n_p;
9083 const unsigned master_element_nnode_1d = neigh_n_p;
9084
9085 // Storage for master shapes
9086 Shape master_shapes(edge_neigh_obj_pt->ninterpolating_node(value_id));
9087
9088 // Get master and dependent nodal positions
9089 //-------------------------------------
9099
9100 // Apply the (1D) vertex matching condition
9101 //-----------------------------------------
9102 // Vertiex matching is ensured automatically in cases where there is a
9103 // node at each end of the mortar that is shared between the master
9104 // and dependent elements. Where this is not the case, the vertex
9105 // matching condition must be enforced by constraining the value of
9106 // the unknown at the node on the dependent side to be the same as the
9107 // value at that location in the master.
9108
9109 // Store positions of the mortar vertex/non-vertex nodes in the
9110 // dependent element
9111 const unsigned n_mortar_vertices = 2;
9113 vertex_pos[0] = 0;
9114 vertex_pos[1] = this->ninterpolating_node_1d(value_id) - 1;
9116 for (unsigned i = 0; i < my_n_p - n_mortar_vertices; i++)
9117 {
9118 non_vertex_pos[i] = i + 1;
9119 }
9120
9121 // If the node is on a master edge, we may be setting the
9122 // hanging info incorrectly. Hanging schemes (if they are
9123 // required) for such nodes are instead computed by the
9124 // dependent edge. We dont't want to overwrite them here!
9126 for (unsigned v = 0; v < n_mortar_vertices; v++)
9127 {
9128 // Check if each node is on the master edge
9130 unsigned non_extreme_coordinate = 0;
9132 for (unsigned i = 0; i < 3; i++)
9133 {
9134 // Work out this node's location in the master
9135 s_in_neigh[i] =
9139
9140 // Check if local coordinate in master element takes non-extreme
9141 // value
9142 if (std::fabs(std::fabs(s_in_neigh[i]) - 1.0) > 1.0e-14)
9143 {
9145 if (non_extreme_coordinate > 1)
9146 {
9148 break;
9149 }
9150 }
9151 }
9152 }
9153
9154 // Now work out if my edge coincides with the master edge
9156 for (unsigned v = 0; v < n_mortar_vertices; v++)
9157 {
9160 }
9161
9162 // Check if we need to apply the (1D) vertex matching condition at the
9163 // mortar vertices. This is trivially satisfied if the node is shared
9164 // with the master, but if not then we need to constrain it.
9165 for (unsigned v = 0; v < n_mortar_vertices; v++)
9166 {
9167 // Don't make hanging node if my edge doesn't coincide with
9168 // the master edge *and* this node is on the master edge!
9169 // (We are not in a position to determine its hanging status.)
9172 continue;
9173
9174 // Search master node storage for the node
9175 bool node_is_shared = false;
9176 for (unsigned i = 0; i < master_node_pt.size(); i++)
9177 {
9178 if (dependent_node_pt[vertex_pos[v]] == master_node_pt[i])
9179 {
9180 node_is_shared = true;
9181 break;
9182 }
9183 }
9184
9185 // If the node is not shared then we must constrain its value by
9186 // setting up a hanging scheme
9187 if (!node_is_shared)
9188 {
9189 // Calculate weights. These are just the master shapes evaluated
9190 // at this dependent node's position
9191
9192 // Work out this node's location in the master
9194 for (unsigned i = 0; i < 3; i++)
9195 {
9198 [edge_translate_s[i]] *
9200 }
9201
9202 // Get master shapes at dependent nodal positions
9203 edge_neigh_obj_pt->interpolating_basis(
9205
9206 // Remove any existing hanging node info
9207 // (This may be a bit wasteful, but guarantees correctness)
9208 dependent_node_pt[vertex_pos[v]]->set_nonhanging();
9209
9210 // Don't include master nodes with zero weights
9212 for (unsigned m = 0; m < n_master_nodes; m++)
9213 {
9214 // Compare weights to some (small) tolerance
9215 if (std::fabs(master_shapes[master_node_number[m]]) < 1.0e-14)
9216 {
9217 // Store
9218 master_node_zero_weight.push_back(m);
9219 }
9220 }
9221
9222 // Set up hanging scheme for this node
9225 unsigned mindex = 0;
9226 for (unsigned m = 0; m < n_master_nodes; m++)
9227 {
9228 // Check that master doesn't have zero weight
9229 bool skip = false;
9230 for (unsigned i = 0; i < master_node_zero_weight.size(); i++)
9231 {
9232 if (m == master_node_zero_weight[i]) skip = true;
9233 }
9234
9235 // Add pointer and weight to hang info
9236 if (!skip)
9237 explicit_hang_pt->set_master_node_pt(
9238 mindex++,
9239 master_node_pt[m],
9241 }
9242
9243 /// / Set up hanging scheme for this node
9244 // HangInfo* explicit_hang_pt = new HangInfo(n_master_nodes);
9245 // for(unsigned m=0; m<n_master_nodes; m++)
9246 // {
9247 // explicit_hang_pt->set_master_node_pt(m,master_node_pt[m],master_shapes[master_node_number[m]]);
9248 // }
9249
9250 // Make node hang
9252 -1);
9253
9254 /// / Print out hanging scheme
9255 // std::cout << "Hanging node: "
9256 // << dependent_node_pt[vertex_pos[v]]->x(0) << " "
9257 // << dependent_node_pt[vertex_pos[v]]->x(1) << " "
9258 // << dependent_node_pt[vertex_pos[v]]->x(2) << " "
9259 // << std::endl;
9260 // for(unsigned m=0; m<explicit_hang_pt->nmaster(); m++)
9261 // {
9262 // std::cout << " m = " << m << ": "
9263 // << explicit_hang_pt->master_node_pt(m)->x(0) << " "
9264 // << explicit_hang_pt->master_node_pt(m)->x(1) << " "
9265 // << explicit_hang_pt->master_node_pt(m)->x(2) << " "
9266 // << "w = " << explicit_hang_pt->master_weight(m) << "
9267 // "
9268 // << std::endl;
9269 // }
9270
9271 // Check there are no zero weights at this stage
9272 for (unsigned m = 0; m < explicit_hang_pt->nmaster(); m++)
9273 {
9274 if (std::fabs(explicit_hang_pt->master_weight(m)) < 1.0e-14)
9275 {
9276 throw OomphLibError(
9277 "Master has zero weight!",
9278 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9280 }
9281 }
9282 }
9283 }
9284
9285 // Check that there are actually dependent nodes for which we still
9286 // need to construct a hanging scheme. If not then there is nothing
9287 // more to do.
9289 {
9290 // Assemble mass matrix for mortar
9291 //--------------------------------
9295 for (unsigned i = 0; i < shared_node_M.size(); i++)
9296 {
9298 }
9299
9300 // Fill in part corresponding to dependent nodal positions (unknown)
9301 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
9302 {
9303 // Use L'Hosptal's rule:
9304 psi[i] =
9305 pow(-1.0, int((dependent_element_nnode_1d - 1) - i - 1)) *
9309 // Put in contribution
9311 }
9312
9313 // Fill in part corresponding to dependent element vertices (known)
9314 for (unsigned v = 0; v < shared_node_M.size(); v++)
9315 {
9316 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices;
9317 i++)
9318 {
9319 // Check if denominator is zero
9320 if (std::fabs(dependent_nodal_position[non_vertex_pos[i]] -
9322 1.0e-8)
9323 {
9324 // We're ok
9325 psi[i] =
9326 pow(-1.0, int((dependent_element_nnode_1d - 1) - i - 1)) *
9332 }
9333 // Check if numerator is zero
9334 else if (std::fabs(Orthpoly::dlegendre(
9337 {
9338 // We can use l'hopital's rule
9339 psi[i] =
9340 pow(-1.0, int((dependent_element_nnode_1d - 1) - i - 1)) *
9344 }
9345 else
9346 {
9347 // We can't use l'hopital's rule
9348 throw OomphLibError(
9349 "Cannot use l'Hopital's rule. Dividing by zero is not "
9350 "allowed!",
9351 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9353 }
9354 // Put in contribution
9356 }
9357 }
9358
9359 // Assemble local projection matrix for mortar
9360 //--------------------------------------------
9361
9362 // Have only one local projection matrix because there is just one
9363 // master
9365 for (unsigned i = 0; i < P.size(); i++)
9366 {
9367 P[i].resize(n_master_nodes, 0.0);
9368 }
9369
9370 // Storage for local coordinate
9371 Vector<double> s(3);
9372
9373 // Sum contributions from master element shapes (quadrature).
9374 // The order of quadrature must be high enough to evaluate a
9375 // polynomial of order N_s + N_m - 3 exactly, where N_s =
9376 // n_dependent_nodes, N_m = n_master_nodes. (Use pointers for the
9377 // quadrature knots and weights so that data is not unnecessarily
9378 // copied)
9379 // unsigned quadrature_order =
9380 // std::max(dependent_element_nnode_1d,master_element_nnode_1d);
9383 {
9384 // Use the same quadrature order as the dependent element (me)
9387 }
9388 else
9389 {
9390 // Use the same quadrature order as the master element (neighbour)
9392 quadrature_weight = &master_weight;
9393 }
9394
9395 // Quadrature loop
9396 for (unsigned q = 0; q < (*quadrature_weight).size(); q++)
9397 {
9398 // Evaluate mortar test functions at the quadrature knots in the
9399 // dependent
9400 // s[active_coord_index] = (*quadrature_knot)[q];
9402 s_on_mortar[0] = (*quadrature_knot)[q];
9403
9404 // Get psi
9405 for (unsigned k = 0; k < n_dependent_nodes - n_mortar_vertices;
9406 k++)
9407 {
9408 // Check if denominator is zero
9409 if (std::fabs(dependent_nodal_position[non_vertex_pos[k]] -
9410 s_on_mortar[0]) >= 1.0e-08)
9411 {
9412 // We're ok
9413 psi[k] =
9414 pow(-1.0, int((dependent_element_nnode_1d - 1) - k - 1)) *
9416 s_on_mortar[0]) /
9418 s_on_mortar[0]);
9419 }
9420 // Check if numerator is zero
9421 else if (std::fabs(Orthpoly::dlegendre(
9423 1.0e-8)
9424 {
9425 // We can use l'Hopital's rule
9426 psi[k] =
9427 pow(-1.0, int((dependent_element_nnode_1d - 1) - k - 1)) *
9429 s_on_mortar[0]);
9430 }
9431 else
9432 {
9433 // We can't use l'hopital's rule
9434 throw OomphLibError(
9435 "Cannot use l'Hopital's rule. Dividing by zero is not "
9436 "allowed!",
9437 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9439 }
9440 }
9441
9442 // Convert coordinate on mortar to local fraction in dependent
9443 // element
9445 for (unsigned i = 0; i < 3; i++)
9446 {
9448 0.5 * (s_on_mortar[0] + 1.0) :
9450 }
9451
9452 // Project active coordinate into master element
9454 for (unsigned i = 0; i < 3; i++)
9455 {
9459 }
9460
9461 // Evaluate master shapes at projections of local quadrature knots
9462 edge_neigh_obj_pt->interpolating_basis(
9464
9465 // Populate local projection matrix
9466 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices;
9467 i++)
9468 {
9469 for (unsigned j = 0; j < n_master_nodes; j++)
9470 {
9471 P[i][j] += master_shapes[master_node_number[j]] * psi[i] *
9472 (*quadrature_weight)[q];
9473 }
9474 }
9475 }
9476
9477 /// / Print out local projection matrix
9478 // std::cout << "P_e:" << std::endl;
9479 // for(unsigned i=0; i<P.size(); i++)
9480 // {
9481 // for(unsigned j=0; j<P[i].size(); j++)
9482 // {
9483 // std::cout << " " << P[i][j];
9484 // }
9485 // }
9486 // std::cout << std::endl;
9487
9488 // Assemble global projection matrices for mortar
9489 //-----------------------------------------------
9490 // Need to subtract contributions from the "known unknowns"
9491 // corresponding to the nodes at the vertices of the mortar
9492
9493 // Assemble contributions from mortar vertex nodes
9494 for (unsigned v = 0; v < n_mortar_vertices; v++)
9495 {
9496 // Convert coordinate on mortar to local fraction in dependent
9497 // element
9499 for (unsigned i = 0; i < 3; i++)
9500 {
9501 s_fraction[i] =
9502 (i == active_coord_index) ?
9503 0.5 * (dependent_nodal_position[vertex_pos[v]] + 1.0) :
9505 }
9506
9507 // Project active coordinate into master element
9509 for (unsigned i = 0; i < 3; i++)
9510 {
9514 }
9515
9516 // Get master shapes at dependent nodal positions
9517 edge_neigh_obj_pt->interpolating_basis(
9519
9520 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices;
9521 i++)
9522 {
9523 for (unsigned k = 0; k < n_master_nodes; k++)
9524 {
9525 P[i][k] -=
9527 }
9528 }
9529 }
9530
9531 /// / Print out global projection matrix
9532 // std::cout << "P:" << std::endl;
9533 // for(unsigned i=0; i<P.size(); i++)
9534 // {
9535 // for(unsigned j=0; j<P[i].size(); j++)
9536 // {
9537 // std::cout << " " << P[i][j];
9538 // }
9539 // std::cout << std::endl;
9540 // }
9541 // std::cout << std::endl;
9542
9543 // Solve mortar system
9544 //--------------------
9545 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
9546 {
9547 for (unsigned j = 0; j < n_master_nodes; j++)
9548 {
9549 P[i][j] /= diag_M[i];
9550 }
9551 }
9552
9553 /// / Print out solved global projection matrix
9554 // std::cout << "solved P:" << std::endl;
9555 // for(unsigned i=0; i<P.size(); i++)
9556 // {
9557 // for(unsigned j=0; j<P[i].size(); j++)
9558 // {
9559 // std::cout << " " << P[i][j];
9560 // }
9561 // std::cout << std::endl;
9562 // }
9563 // std::cout << std::endl;
9564
9565 // Create and populate structures to hold the hanging info
9566 //--------------------------------------------------------
9568 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
9569 {
9570 // Don't include master nodes with zero weights
9572 for (unsigned m = 0; m < n_master_nodes; m++)
9573 {
9574 // Compare weights to some (small) tolerance
9575 if (std::fabs(P[i][m]) < 1.0e-14)
9576 {
9577 // Store
9578 master_node_zero_weight.push_back(m);
9579 }
9580 }
9581
9582 // Set up hanging scheme for this node
9583 hang_info_pt[i] =
9585 unsigned mindex = 0;
9586 for (unsigned m = 0; m < n_master_nodes; m++)
9587 {
9588 // Check that master doesn't have zero weight
9589 bool skip = false;
9590 for (unsigned k = 0; k < master_node_zero_weight.size(); k++)
9591 {
9592 if (m == master_node_zero_weight[k]) skip = true;
9593 }
9594
9595 // Add pointer and weight to hang info
9596 if (!skip)
9597 hang_info_pt[i]->set_master_node_pt(
9598 mindex++, master_node_pt[m], P[i][m]);
9599 }
9600 }
9601
9602 /// / Create structures to hold the hanging info
9603 /// /-------------------------------------------
9604 // Vector<HangInfo*> hang_info_pt(n_dependent_nodes);
9605 // for (unsigned i=0; i<n_dependent_nodes-n_mortar_vertices; i++)
9606 // {
9607 // hang_info_pt[i] = new HangInfo(n_master_nodes);
9608 // }
9609 //
9610 /// / Copy information to hanging nodes
9611 /// /----------------------------------
9612 // for(unsigned i=0; i<n_dependent_nodes-n_mortar_vertices; i++)
9613 // {
9614 // for(unsigned j=0; j<n_master_nodes; j++)
9615 // {
9616 // hang_info_pt[i]->set_master_node_pt(j,master_node_pt[j],P[i][j]);
9617 // }
9618 // }
9619
9620 // Set pointers to hanging info
9621 //-----------------------------
9622 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
9623 {
9624 // Check that the node shoule actually hang.
9625 // That is, if the polynomial orders of the elements at a p-type
9626 // non-conormity are both odd then the middle node on the edge is
9627 // shared but a hanging scheme has been constructed for it.
9628 bool node_is_really_shared = false;
9629 for (unsigned m = 0; m < hang_info_pt[i]->nmaster(); m++)
9630 {
9631 // We can simply check if the hanging scheme lists itself as a
9632 // master
9633 if (hang_info_pt[i]->master_node_pt(m) ==
9635 {
9636 node_is_really_shared = true;
9637
9638#ifdef PARANOID
9639 // Also check the corresponding weight: it should be 1
9640 if (std::fabs(hang_info_pt[i]->master_weight(m) - 1.0) >
9641 1.0e-06)
9642 {
9643 throw OomphLibError("Something fishy here -- with shared "
9644 "node at a mortar vertex",
9645 "PRefineableQElemen<2,INITIAL_NNODE_1D>"
9646 "t::quad_hang_helper()",
9648 }
9649#endif
9650 }
9651 }
9652
9653 // Now we can make the node hang, if it isn't a shared node
9655 {
9656 dependent_node_pt[non_vertex_pos[i]]->set_hanging_pt(
9657 hang_info_pt[i], -1);
9658
9659 /// / Print out hanging scheme
9660 // std::cout << "Hanging node: "
9661 // << dependent_node_pt[non_vertex_pos[i]]->x(0) << " "
9662 // << dependent_node_pt[non_vertex_pos[i]]->x(1) << " "
9663 // << dependent_node_pt[non_vertex_pos[i]]->x(2) << " "
9664 // << std::endl;
9665 // for(unsigned m=0; m<hang_info_pt[i]->nmaster(); m++)
9666 // {
9667 // std::cout << " m = " << m << ": "
9668 // << hang_info_pt[i]->master_node_pt(m)->x(0) << " "
9669 // << hang_info_pt[i]->master_node_pt(m)->x(1) << " "
9670 // << hang_info_pt[i]->master_node_pt(m)->x(2) << " "
9671 // << "w = " << hang_info_pt[i]->master_weight(m) <<
9672 // " "
9673 // << std::endl;
9674 // }
9675 }
9676 }
9677
9678 } // End of case where there are still dependent nodes
9679
9680 } // End of edge is dependent
9681
9682 } // End of loop over face edges
9683
9684 } // End of if face is dependent
9685
9686 // Now do the mortaring
9687 //---------------------
9689 {
9690 // Compute the active coordinate indices along the this side of mortar
9692 if (my_face == B || my_face == F)
9693 {
9694 active_coord_index[0] = 0;
9695 active_coord_index[1] = 1;
9696 }
9697 else if (my_face == D || my_face == U)
9698 {
9699 active_coord_index[0] = 0;
9700 active_coord_index[1] = 2;
9701 }
9702 else if (my_face == L || my_face == R)
9703 {
9704 active_coord_index[0] = 1;
9705 active_coord_index[1] = 2;
9706 }
9707 else
9708 {
9709 throw OomphLibError(
9710 "Cannot transform coordinates",
9711 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9713 }
9714
9715 // Get pointer to neighbouring master element (in p-refineable form)
9718 neigh_pt->object_pt());
9719
9720 // Create vector of master and dependent nodes
9721 //----------------------------------------
9722 Vector<Node*> master_node_pt, dependent_node_pt;
9725
9726 // Number of nodes in one dimension
9727 const unsigned my_n_p = this->ninterpolating_node_1d(value_id);
9728 const unsigned neigh_n_p = neigh_obj_pt->ninterpolating_node_1d(value_id);
9729
9730 // Storage for pointers to the nodes and their numbers along the master
9731 // edge
9732 unsigned neighbour_node_number = 0;
9734
9735 // Loop over nodes on the face
9736 for (unsigned i0 = 0; i0 < neigh_n_p; i0++)
9737 {
9738 for (unsigned i1 = 0; i1 < neigh_n_p; i1++)
9739 {
9740 const unsigned s0space = 1;
9741 const unsigned s1space = neigh_n_p;
9742 const unsigned s2space = neigh_n_p * neigh_n_p;
9743
9744 // Find the neighbour's node
9745 switch (neigh_face)
9746 {
9747 case B:
9749 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
9751 break;
9752
9753 case F:
9755 (neigh_n_p - 1) * s2space + i0 * s0space + i1 * s1space;
9756 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
9758 break;
9759
9760 case D:
9762 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
9764 break;
9765
9766 case U:
9768 (neigh_n_p - 1) * s1space + i0 * s0space + i1 * s2space;
9769 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
9771 break;
9772
9773 case L:
9775 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
9777 break;
9778
9779 case R:
9781 (neigh_n_p - 1) * s0space + i0 * s1space + i1 * s2space;
9782 neighbour_node_pt = neigh_obj_pt->interpolating_node_pt(
9784 break;
9785
9786 default:
9787 throw OomphLibError(
9788 "my_face not L, R, D, U, B, F\n",
9789 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9791 }
9792
9793 // Set node as master node
9795 master_node_pt.push_back(neighbour_node_pt);
9796 }
9797 }
9798
9799 // Storage for pointers to the local nodes and their numbers along my edge
9800 unsigned local_node_number = 0;
9801 Node* local_node_pt = 0;
9802
9803 // Loop over the nodes along my edge
9804 for (unsigned i0 = 0; i0 < my_n_p; i0++)
9805 {
9806 for (unsigned i1 = 0; i1 < my_n_p; i1++)
9807 {
9808 // Storage for the fractional position of the node in the element
9810
9811 const unsigned s0space = 1;
9812 const unsigned s1space = my_n_p;
9813 const unsigned s2space = my_n_p * my_n_p;
9814
9815 // Find the local node and the fractional position of the node
9816 // which depends on the edge, of course
9817 switch (my_face)
9818 {
9819 case B:
9820 s_fraction[0] =
9821 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
9822 s_fraction[1] =
9823 local_one_d_fraction_of_interpolating_node(i1, 1, value_id);
9824 s_fraction[2] = 0.0;
9827 this->interpolating_node_pt(local_node_number, value_id);
9828 break;
9829
9830 case F:
9831 s_fraction[0] =
9832 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
9833 s_fraction[1] =
9834 local_one_d_fraction_of_interpolating_node(i1, 1, value_id);
9835 s_fraction[2] = 1.0;
9837 (my_n_p - 1) * s2space + i0 * s0space + i1 * s1space;
9839 this->interpolating_node_pt(local_node_number, value_id);
9840 break;
9841
9842 case D:
9843 s_fraction[0] =
9844 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
9845 s_fraction[1] = 0.0;
9846 s_fraction[2] =
9847 local_one_d_fraction_of_interpolating_node(i1, 2, value_id);
9850 this->interpolating_node_pt(local_node_number, value_id);
9851 break;
9852
9853 case U:
9854 s_fraction[0] =
9855 local_one_d_fraction_of_interpolating_node(i0, 0, value_id);
9856 s_fraction[1] = 1.0;
9857 s_fraction[2] =
9858 local_one_d_fraction_of_interpolating_node(i1, 2, value_id);
9860 (my_n_p - 1) * s1space + i0 * s0space + i1 * s2space;
9862 this->interpolating_node_pt(local_node_number, value_id);
9863 break;
9864
9865 case L:
9866 s_fraction[0] = 0.0;
9867 s_fraction[1] =
9868 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
9869 s_fraction[2] =
9870 local_one_d_fraction_of_interpolating_node(i1, 2, value_id);
9873 this->interpolating_node_pt(local_node_number, value_id);
9874 break;
9875
9876 case R:
9877 s_fraction[0] = 1.0;
9878 s_fraction[1] =
9879 local_one_d_fraction_of_interpolating_node(i0, 1, value_id);
9880 s_fraction[2] =
9881 local_one_d_fraction_of_interpolating_node(i1, 2, value_id);
9883 (my_n_p - 1) * s0space + i0 * s1space + i1 * s2space;
9885 this->interpolating_node_pt(local_node_number, value_id);
9886 break;
9887
9888 default:
9889 throw OomphLibError(
9890 "my_face not L, R, D, U, B, F\n",
9891 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
9893 }
9894
9895 // Add node to vector of dependent element nodes
9898
9899 // Store node's local fraction
9901 }
9902 }
9903
9904 // Store the number of dependent and master nodes for use later
9905 const unsigned n_dependent_nodes = dependent_node_pt.size();
9906 const unsigned n_master_nodes = master_node_pt.size();
9907 const unsigned dependent_element_nnode_1d = my_n_p;
9908 const unsigned master_element_nnode_1d = neigh_n_p;
9909
9910 /// / Print out dependent and master node coords
9911 // std::cout << "Dependent nodes on face: " <<
9912 // OcTree::Direct_string[my_face] << std::endl; for(unsigned i=0;
9913 // i<dependent_node_pt.size(); i++)
9914 // {
9915 // std::cout << i << ": "
9916 // << dependent_node_pt[i]->x(0) << " "
9917 // << dependent_node_pt[i]->x(1) << " "
9918 // << dependent_node_pt[i]->x(2) << " "
9919 // << std::endl;
9920 // }
9921 // std::cout << "Master nodes on face: " << OcTree::Direct_string[my_face]
9922 // << std::endl; for(unsigned i=0; i<master_node_pt.size(); i++)
9923 // {
9924 // std::cout << i << ": "
9925 // << master_node_pt[i]->x(0) << " "
9926 // << master_node_pt[i]->x(1) << " "
9927 // << master_node_pt[i]->x(2) << " "
9928 // << std::endl;
9929 // }
9930
9931 // Storage for master shapes
9932 Shape master_shapes(neigh_obj_pt->ninterpolating_node(value_id));
9933
9934 // Get master and dependent nodal positions
9935 //-------------------------------------
9936 // Get in 1D
9946
9947 // Storage for 2D
9950 for (unsigned i = 0; i < dependent_nodal_position.size(); i++)
9951 {
9952 dependent_nodal_position[i].resize(2);
9953 }
9958 for (unsigned i = 0; i < master_nodal_position.size(); i++)
9959 {
9960 master_nodal_position[i].resize(2);
9961 }
9964
9965 // Fill in coordinates and weights in 2D
9966 unsigned dependent_index = 0;
9967 for (unsigned i = 0; i < dependent_element_nnode_1d; i++)
9968 {
9969 for (unsigned j = 0; j < dependent_element_nnode_1d; j++)
9970 {
9978 }
9979 }
9980 unsigned master_index = 0;
9981 for (unsigned i = 0; i < master_element_nnode_1d; i++)
9982 {
9983 for (unsigned j = 0; j < master_element_nnode_1d; j++)
9984 {
9987 master_weight[master_index] =
9989 master_index++;
9990 }
9991 }
9992
9993 // Apply the vertex matching condition
9994 //------------------------------------
9995 // Vertiex matching is ensured automatically in cases where there is a
9996 // node at each end of the mortar that is shared between the master and
9997 // dependent elements. Where this is not the case, the vertex matching
9998 // condition must be enforced by constraining the value of the unknown at
9999 // the node on the dependent side to be the same as the value at that
10000 // location in the master.
10001
10002 // Store positions of the mortar vertex/non-vertex nodes in the dependent
10003 // element
10004 // const unsigned n_mortar_vertices = 4;
10005 // Vector<unsigned> vertex_pos(n_mortar_vertices);
10006 // vertex_pos[0] = 0;
10007 // vertex_pos[1] = my_n_p-1;
10008 // vertex_pos[2] = my_n_p*(my_n_p-1);
10009 // vertex_pos[3] = my_n_p*my_n_p-1;
10012 unsigned nvindex = 0;
10013 for (unsigned i = 1; i < dependent_element_nnode_1d - 1; i++)
10014 {
10015 for (unsigned j = 1; j < dependent_element_nnode_1d - 1; j++)
10016 {
10018 }
10019 }
10021 for (unsigned i = 0; i < n_dependent_nodes; i++)
10022 {
10023 // Check if node number is in the non-vertex storage
10024 bool node_is_vertex = true;
10025 for (unsigned j = 0; j < non_vertex_pos.size(); j++)
10026 {
10027 if (i == non_vertex_pos[j])
10028 {
10029 // Node is not a vertex
10030 node_is_vertex = false;
10031 break;
10032 }
10033 }
10034 // If we get here and the node is a vertex then add it's index
10035 if (node_is_vertex)
10036 {
10037 vertex_pos.push_back(i);
10038 }
10039 }
10040 // Store number of mortar vertices
10041 const unsigned n_mortar_vertices = vertex_pos.size();
10042
10043 // Check that there are actually dependent nodes for which we still need
10044 // to construct a hanging scheme. If not then there is nothing more to do.
10046 {
10047 // Assemble mass matrix for mortar
10048 //--------------------------------
10052 for (unsigned i = 0; i < shared_node_M.size(); i++)
10053 {
10055 }
10056
10057 // Fill in part corresponding to dependent nodal positions (unknown)
10058 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10059 {
10060 // Mortar test functions in 2D are just the cross product of the 1D
10061 // test functions Initialise to 1
10062 psi[i] = 1.0;
10063 // Take product in each direction
10064 for (unsigned dir = 0; dir < 2; dir++)
10065 {
10066 unsigned index1d =
10067 (dir == 0) ? i : i % (dependent_element_nnode_1d - 2);
10068 // Use L'Hosptal's rule:
10069 psi[i] *=
10070 pow(-1.0, int((dependent_element_nnode_1d - 1) - index1d - 1)) *
10074 }
10075 // Put in contribution
10077 }
10078
10079 /// / Print out diag(M)
10080 // std::cout << "diag(M):" << std::endl;
10081 // for(unsigned i=0; i<diag_M.size(); i++)
10082 // {
10083 // std::cout << " " << diag_M[i];
10084 // }
10085 // std::cout << std::endl;
10086
10087 // Fill in part corresponding to dependent element vertices (known)
10088 for (unsigned v = 0; v < shared_node_M.size(); v++)
10089 {
10090 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10091 {
10092 // Mortar test functions in 2D are just the cross product of the 1D
10093 // test functions Initialise to 1
10094 psi[i] = 1.0;
10095 // Take product in each direction
10096 for (unsigned dir = 0; dir < 2; dir++)
10097 {
10098 unsigned index1d =
10099 (dir == 0) ? i : i % (dependent_element_nnode_1d - 2);
10100 // Check if denominator is zero
10101 if (std::fabs(dependent_nodal_position[non_vertex_pos[i]][dir] -
10103 1.0e-8)
10104 {
10105 // We're ok
10106 psi[i] *=
10107 pow(-1.0,
10108 int((dependent_element_nnode_1d - 1) - index1d - 1)) *
10114 }
10115 // Check if numerator is zero
10116 else if (std::fabs(Orthpoly::dlegendre(
10119 1.0e-8)
10120 {
10121 // We can use l'hopital's rule
10122 psi[i] *=
10123 pow(-1.0,
10124 int((dependent_element_nnode_1d - 1) - index1d - 1)) *
10128 }
10129 else
10130 {
10131 // We can't use l'hopital's rule
10132 throw OomphLibError(
10133 "Cannot use l'Hopital's rule. Dividing by zero is not "
10134 "allowed!",
10135 "PRefineableQElement<3,INITIAL_NNODE_1D>::quad_hang_helper()",
10137 }
10138 }
10139 // Put in contribution
10141 }
10142 }
10143
10144 /// / Print out diag(M)
10145 // std::cout << "shared node M:" << std::endl;
10146 // for(unsigned i=0; i<shared_node_M.size(); i++)
10147 // {
10148 // for(unsigned j=0; j<shared_node_M[i].size(); j++)
10149 // {
10150 // std::cout << " " << shared_node_M[i][j];
10151 // }
10152 // }
10153 // std::cout << std::endl;
10154
10155 // Assemble local projection matrix for mortar
10156 //--------------------------------------------
10157
10158 // Have only one local projection matrix because there is just one
10159 // master
10161 for (unsigned i = 0; i < P.size(); i++)
10162 {
10163 P[i].resize(n_master_nodes, 0.0);
10164 }
10165
10166 // Storage for local coordinate
10167 Vector<double> s(3);
10168
10169 // Sum contributions from master element shapes (quadrature).
10170 // The order of quadrature must be high enough to evaluate a polynomial
10171 // of order N_s + N_m - 3 exactly, where N_s = n_dependent_nodes, N_m =
10172 // n_master_nodes.
10173 // (Use pointers for the quadrature knots and weights so that
10174 // data is not unnecessarily copied)
10175 // unsigned quadrature_order =
10176 // std::max(dependent_element_nnode_1d,master_element_nnode_1d);
10180 {
10181 // Use the same quadrature order as the dependent element (me)
10184 }
10185 else
10186 {
10187 // Use the same quadrature order as the master element (neighbour)
10189 quadrature_weight = &master_weight;
10190 }
10191
10192 // Quadrature loop
10193 for (unsigned q = 0; q < (*quadrature_weight).size(); q++)
10194 {
10195 // Evaluate mortar test functions at the quadrature knots in the
10196 // dependent
10198 for (unsigned i = 0; i < 2; i++)
10199 {
10200 s_on_mortar[i] = (*quadrature_knot)[q][i];
10201 }
10202
10203 // Get psi
10204 for (unsigned k = 0; k < n_dependent_nodes - n_mortar_vertices; k++)
10205 {
10206 // Mortar test functions in 2D are just the cross product of the 1D
10207 // test functions Initialise to 1
10208 psi[k] = 1.0;
10209 // Take product in each direction
10210 for (unsigned dir = 0; dir < 2; dir++)
10211 {
10212 unsigned index1d =
10213 (dir == 0) ? k : k % (dependent_element_nnode_1d - 2);
10214 // Check if denominator is zero
10215 if (std::fabs(dependent_nodal_position[non_vertex_pos[k]][dir] -
10216 s_on_mortar[dir]) >= 1.0e-08)
10217 {
10218 // We're ok
10219 psi[k] *=
10220 pow(-1.0,
10221 int((dependent_element_nnode_1d - 1) - index1d - 1)) *
10223 s_on_mortar[dir]) /
10225 s_on_mortar[dir]);
10226 }
10227 // Check if numerator is zero
10228 else if (std::fabs(Orthpoly::dlegendre(
10230 1.0e-8)
10231 {
10232 // We can use l'Hopital's rule
10233 psi[k] *=
10234 pow(-1.0,
10235 int((dependent_element_nnode_1d - 1) - index1d - 1)) *
10237 s_on_mortar[dir]);
10238 }
10239 else
10240 {
10241 // We can't use l'hopital's rule
10242 throw OomphLibError(
10243 "Cannot use l'Hopital's rule. Dividing by zero is not "
10244 "allowed!",
10245 "PRefineableQElement<3,INITIAL_NNODE_1D>::quad_hang_helper()",
10247 }
10248 }
10249 }
10250
10251 // Convert coordinate on mortar to local fraction in dependent element
10253 for (unsigned i = 0; i < 3; i++)
10254 {
10255 if (i == active_coord_index[0])
10256 {
10257 s_fraction[i] = 0.5 * (s_on_mortar[0] + 1.0);
10258 }
10259 else if (i == active_coord_index[1])
10260 {
10261 s_fraction[i] = 0.5 * (s_on_mortar[1] + 1.0);
10262 }
10263 else
10264 {
10266 }
10267 }
10268
10269 // Project active coordinate into master element
10271 for (unsigned i = 0; i < 3; i++)
10272 {
10274 (s_hi_neigh[i] - s_lo_neigh[i]);
10275 }
10276
10277 // Evaluate master shapes at projections of local quadrature knots
10278 neigh_obj_pt->interpolating_basis(
10280
10281 // Populate local projection matrix
10282 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10283 {
10284 for (unsigned j = 0; j < n_master_nodes; j++)
10285 {
10286 P[i][j] += master_shapes[master_node_number[j]] * psi[i] *
10287 (*quadrature_weight)[q];
10288 }
10289 }
10290 }
10291
10292 /// / Print out local projection matrix
10293 // std::cout << "P_e:" << std::endl;
10294 // for(unsigned i=0; i<P.size(); i++)
10295 // {
10296 // for(unsigned j=0; j<P[i].size(); j++)
10297 // {
10298 // std::cout << " " << P[i][j];
10299 // }
10300 // }
10301 // std::cout << std::endl;
10302
10303 // Assemble global projection matrices for mortar
10304 //-----------------------------------------------
10305 // Need to subtract contributions from the "known unknowns"
10306 // corresponding to the nodes at the vertices of the mortar
10307
10308 // Assemble contributions from mortar vertex nodes
10309 for (unsigned v = 0; v < n_mortar_vertices; v++)
10310 {
10311 // Convert coordinate on mortar to local fraction in dependent element
10313 for (unsigned i = 0; i < 3; i++)
10314 {
10315 if (i == active_coord_index[0])
10316 {
10317 s_fraction[i] =
10318 0.5 * (dependent_nodal_position[vertex_pos[v]][0] + 1.0);
10319 }
10320 else if (i == active_coord_index[1])
10321 {
10322 s_fraction[i] =
10323 0.5 * (dependent_nodal_position[vertex_pos[v]][1] + 1.0);
10324 }
10325 else
10326 {
10328 }
10329 }
10330
10331 // Project active coordinate into master element
10333 for (unsigned i = 0; i < 3; i++)
10334 {
10336 (s_hi_neigh[i] - s_lo_neigh[i]);
10337 }
10338
10339 // Get master shapes at dependent nodal positions
10340 neigh_obj_pt->interpolating_basis(
10342
10343 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10344 {
10345 for (unsigned k = 0; k < n_master_nodes; k++)
10346 {
10347 P[i][k] -=
10349 }
10350 }
10351 }
10352
10353 /// / Print out global projection matrix
10354 // std::cout << "P:" << std::endl;
10355 // for(unsigned i=0; i<P.size(); i++)
10356 // {
10357 // for(unsigned j=0; j<P[i].size(); j++)
10358 // {
10359 // std::cout << " " << P[i][j];
10360 // }
10361 // }
10362 // std::cout << std::endl;
10363
10364 // Solve mortar system
10365 //--------------------
10366 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10367 {
10368 for (unsigned j = 0; j < n_master_nodes; j++)
10369 {
10370 P[i][j] /= diag_M[i];
10371 }
10372 }
10373
10374 /// / Print out solved matrix
10375 // std::cout << "solved P:" << std::endl;
10376 // for(unsigned i=0; i<P.size(); i++)
10377 // {
10378 // for(unsigned j=0; j<P[i].size(); j++)
10379 // {
10380 // std::cout << " " << P[i][j];
10381 // }
10382 // }
10383 // std::cout << std::endl;
10384
10385 // Create structures to hold the hanging info
10386 //-------------------------------------------
10388 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10389 {
10391 }
10392
10393 // Copy information to hanging nodes
10394 //----------------------------------
10395 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10396 {
10397 for (unsigned j = 0; j < n_master_nodes; j++)
10398 {
10399 hang_info_pt[i]->set_master_node_pt(j, master_node_pt[j], P[i][j]);
10400 }
10401 }
10402
10403 // Set pointers to hanging info
10404 //-----------------------------
10405 for (unsigned i = 0; i < n_dependent_nodes - n_mortar_vertices; i++)
10406 {
10407 // Check that the node shoule actually hang.
10408 // That is, if the polynomial orders of the elements at a p-type
10409 // non-conormity are both odd then the middle node on the edge is
10410 // shared but a hanging scheme has been constructed for it.
10411 bool node_is_really_shared = false;
10412 for (unsigned m = 0; m < hang_info_pt[i]->nmaster(); m++)
10413 {
10414 // We can simply check if the hanging scheme lists itself as a
10415 // master
10416 if (hang_info_pt[i]->master_node_pt(m) ==
10418 {
10419 node_is_really_shared = true;
10420
10421#ifdef PARANOID
10422 // Also check the corresponding weight: it should be 1
10423 if (std::fabs(hang_info_pt[i]->master_weight(m) - 1.0) > 1.0e-06)
10424 {
10425 throw OomphLibError(
10426 "Something fishy here -- with shared node at a mortar vertex",
10427 "PRefineableQElement<3,INITIAL_NNODE_1D>::quad_hang_helper()",
10429 }
10430#endif
10431 }
10432 }
10433
10434 // Now we can make the node hang, if it isn't a shared node
10436 {
10437 dependent_node_pt[non_vertex_pos[i]]->set_hanging_pt(
10438 hang_info_pt[i], -1);
10439 }
10440 }
10441
10442 } // End of case where there are still dependent nodes
10443
10444#ifdef PARANOID
10445 // Check all dependent nodes, hanging or otherwise
10446 for (unsigned i = 0; i < n_dependent_nodes; i++)
10447 {
10448 // Check that weights sum to 1 for those that hang
10449 if (dependent_node_pt[i]->is_hanging())
10450 {
10451 // Check that weights sum to 1 and no zero weights
10452 double weight_sum = 0.0;
10453 bool zero_weight = false;
10454 for (unsigned m = 0;
10455 m < dependent_node_pt[i]->hanging_pt()->nmaster();
10456 m++)
10457 {
10458 weight_sum += dependent_node_pt[i]->hanging_pt()->master_weight(m);
10459 if (std::fabs(dependent_node_pt[i]->hanging_pt()->master_weight(
10460 m)) < 1.0e-14)
10461 {
10462 zero_weight = true;
10463 oomph_info << "In the hanging scheme for dependent node " << i
10464 << ", master node " << m << " has weight "
10465 << dependent_node_pt[i]->hanging_pt()->master_weight(m)
10466 << " < 1.0e-14" << std::endl;
10467 }
10468 }
10469
10470 // Warn if not
10471 if (std::fabs(weight_sum - 1.0) > 1.0e-08)
10472 {
10473 oomph_info << "Sum of master weights: " << weight_sum << std::endl;
10475 "Weights in hanging scheme do not sum to 1",
10476 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
10478 }
10479 if (zero_weight)
10480 {
10482 "Zero weights present in hanging schemes",
10483 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
10485 }
10486
10487 // Also check that dependent nodes do not have themselves as masters
10488 // bool dependent_should_not_be_hanging = false;
10489 for (unsigned m = 0;
10490 m < dependent_node_pt[i]->hanging_pt()->nmaster();
10491 m++)
10492 {
10493 if (dependent_node_pt[i] ==
10494 dependent_node_pt[i]->hanging_pt()->master_node_pt(m))
10495 {
10496 // This shouldn't happen!
10497 throw OomphLibError(
10498 "Dependent node has itself as a master!",
10499 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
10501 }
10502 if (dependent_node_pt[i]
10503 ->hanging_pt()
10504 ->master_node_pt(m)
10505 ->is_hanging())
10506 {
10507 // Check if node is master of master
10508 Node* new_nod_pt =
10509 dependent_node_pt[i]->hanging_pt()->master_node_pt(m);
10510 for (unsigned mm = 0; mm < new_nod_pt->hanging_pt()->nmaster();
10511 mm++)
10512 {
10513 if (dependent_node_pt[i] ==
10514 new_nod_pt->hanging_pt()->master_node_pt(mm))
10515 {
10516 std::cout << "++++++++++++++++++++++++++++++++++++++++"
10517 << std::endl;
10518 std::cout
10519 << " Dependent node: " << dependent_node_pt[i]
10520 << " = " << dependent_node_pt[i]->x(0) << " "
10521 << dependent_node_pt[i]->x(1) << " "
10522 << dependent_node_pt[i]->x(2) << " " << std::endl;
10523 std::cout
10524 << " Master node: "
10525 << dependent_node_pt[i]->hanging_pt()->master_node_pt(m)
10526 << " = "
10527 << dependent_node_pt[i]->hanging_pt()->master_node_pt(m)->x(
10528 0)
10529 << " "
10530 << dependent_node_pt[i]->hanging_pt()->master_node_pt(m)->x(
10531 1)
10532 << " "
10533 << dependent_node_pt[i]->hanging_pt()->master_node_pt(m)->x(
10534 2)
10535 << " " << std::endl;
10536 std::cout << "Master's master node: "
10538 ->hanging_pt()
10539 ->master_node_pt(m)
10540 ->hanging_pt()
10541 ->master_node_pt(mm)
10542 << " = "
10544 ->hanging_pt()
10545 ->master_node_pt(m)
10546 ->hanging_pt()
10547 ->master_node_pt(mm)
10548 ->x(0)
10549 << " "
10551 ->hanging_pt()
10552 ->master_node_pt(m)
10553 ->hanging_pt()
10554 ->master_node_pt(mm)
10555 ->x(1)
10556 << " "
10558 ->hanging_pt()
10559 ->master_node_pt(m)
10560 ->hanging_pt()
10561 ->master_node_pt(mm)
10562 ->x(2)
10563 << " " << std::endl;
10564
10565
10566 // Print out hanging scheme
10567 std::cout << "Hanging node: " << dependent_node_pt[i]->x(0)
10568 << " " << dependent_node_pt[i]->x(1) << " "
10569 << dependent_node_pt[i]->x(2) << " " << std::endl;
10570 for (unsigned m_tmp = 0;
10571 m_tmp < dependent_node_pt[i]->hanging_pt()->nmaster();
10572 m_tmp++)
10573 {
10574 std::cout
10575 << " m = " << m_tmp << ": "
10577 ->hanging_pt()
10578 ->master_node_pt(m_tmp)
10579 ->x(0)
10580 << " "
10582 ->hanging_pt()
10583 ->master_node_pt(m_tmp)
10584 ->x(1)
10585 << " "
10587 ->hanging_pt()
10588 ->master_node_pt(m_tmp)
10589 ->x(2)
10590 << " "
10591 << "w = "
10592 << dependent_node_pt[i]->hanging_pt()->master_weight(
10593 m_tmp)
10594 << " " << std::endl;
10595 }
10596
10597 // Print out hanging scheme
10598 std::cout << "Master node " << m
10599 << " of Hanging node: " << new_nod_pt->x(0) << " "
10600 << new_nod_pt->x(1) << " " << new_nod_pt->x(2)
10601 << " " << std::endl;
10602 for (unsigned mm_tmp = 0;
10603 mm_tmp < new_nod_pt->hanging_pt()->nmaster();
10604 mm_tmp++)
10605 {
10606 std::cout
10607 << " mm = " << mm_tmp << ": "
10608 << new_nod_pt->hanging_pt()->master_node_pt(mm_tmp)->x(0)
10609 << " "
10610 << new_nod_pt->hanging_pt()->master_node_pt(mm_tmp)->x(1)
10611 << " "
10612 << new_nod_pt->hanging_pt()->master_node_pt(mm_tmp)->x(2)
10613 << " "
10614 << "w = "
10615 << new_nod_pt->hanging_pt()->master_weight(mm_tmp) << " "
10616 << std::endl;
10617 }
10618
10619 // This shouldn't happen!
10620 throw OomphLibError(
10621 "Dependent node has itself as a master of a master!",
10622 "PRefineableQElement<3,INITIAL_NNODE_1D>::oc_hang_helper()",
10624 }
10625 }
10626 }
10627 }
10628 }
10629 else
10630 {
10631 // Check that this node is shared with the master element if it
10632 // isn't hanging
10633 bool is_master = false;
10634 for (unsigned n = 0; n < n_master_nodes; n++)
10635 {
10636 if (dependent_node_pt[i] == master_node_pt[n])
10637 {
10638 // Node is a master
10639 is_master = true;
10640 break;
10641 }
10642 }
10643
10644 if (!is_master)
10645 {
10646 /// / Throw error
10647 // std::ostringstream error_string;
10648 // error_string
10649 // << "This node in the dependent element is neither" << std::endl
10650 // << "hanging or shared with a master element." << std::endl;
10651 //
10652 // throw OomphLibError(
10653 // error_string.str(),
10654 // "PRefineableQElement<3,INITIAL_NNODE_1D>::quad_hang_helper()",
10655 // OOMPH_EXCEPTION_LOCATION);
10656 }
10657 }
10658 }
10659#endif
10660
10661 // Finally, Loop over all dependent nodes and fine-tune their positions
10662 //-----------------------------------------------------------------
10663 // Here we simply set the node's positions to be consistent
10664 // with the hanging scheme. This is not strictly necessary
10665 // because it is done in the mesh adaptation before the node
10666 // becomes non-hanging later on. We make no attempt to ensure
10667 // (strong) continuity in the position across the mortar.
10668 for (unsigned i = 0; i < n_dependent_nodes; i++)
10669 {
10670 // Only fine-tune hanging nodes
10671 if (dependent_node_pt[i]->is_hanging())
10672 {
10673 // If we are doing the position, then
10674 if (value_id == -1)
10675 {
10676 // Get the local coordinate of this dependent node
10678 this->local_coordinate_of_node(dependent_node_number[i], s_local);
10679
10680 // Get the position from interpolation in this element via
10681 // the hanging scheme
10683 this->interpolated_x(s_local, x_in_neighb);
10684
10685 // Fine adjust the coordinates (macro map will pick up boundary
10686 // accurately but will lead to different element edges)
10687 dependent_node_pt[i]->x(0) = x_in_neighb[0];
10688 dependent_node_pt[i]->x(1) = x_in_neighb[1];
10689 dependent_node_pt[i]->x(2) = x_in_neighb[2];
10690 }
10691 }
10692 }
10693 } // End of case where this interface is to be mortared
10694 }
10695
10696 //===================================================================
10697 // Build required templates
10698 //===================================================================
10699 template class PRefineableQElement<1, 2>;
10700 template class PRefineableQElement<1, 3>;
10701 template class PRefineableQElement<1, 4>;
10702
10703 template class PRefineableQElement<2, 2>;
10704 template class PRefineableQElement<2, 3>;
10705 template class PRefineableQElement<2, 4>;
10706
10707 template class PRefineableQElement<3, 2>;
10708 template class PRefineableQElement<3, 3>;
10709 template class PRefineableQElement<3, 4>;
10710
10711} // namespace oomph
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.
BinaryTree class: Recursively defined, generalised binary tree.
Definition binary_tree.h:92
A Class for the derivatives of shape functions The class design is essentially the same as Shape,...
Definition shape.h:278
TimeStepper *& time_stepper_pt()
Return the pointer to the timestepper.
Definition nodes.h:238
void set_value(const unsigned &i, const double &value_)
Set the i-th stored data value to specified value. The only reason that we require an explicit set fu...
Definition nodes.h:271
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition nodes.h:483
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
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition elements.h:1967
virtual void local_coordinate_of_node(const unsigned &j, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size (broken virtual)
Definition elements.h:1846
virtual Node * get_node_at_local_coordinate(const Vector< double > &s) const
If there is a node at this local coordinate, return the pointer to the node.
Definition elements.cc:3912
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object.
Definition elements.h:2513
static const double Node_location_tolerance
Default value for the tolerance to be used when locating nodes via local coordinates.
Definition elements.h:1378
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
unsigned nnode() const
Return the number of nodes.
Definition elements.h:2214
void get_x(const Vector< double > &s, Vector< double > &x) const
Global coordinates as function of local coordinates. Either via FE representation or via macro-elemen...
Definition elements.h:1889
MacroElement * macro_elem_pt()
Access function to pointer to macro element.
Definition elements.h:1882
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition elements.h:2542
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
void set_n_node(const unsigned &n)
Set the number of nodes in the element to n, by resizing the storage for pointers to the Node objects...
Definition elements.h:1408
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
virtual void set_integration_scheme(Integral *const &integral_pt)
Set the spatial integration scheme.
Definition elements.cc:3240
virtual double local_one_d_fraction_of_node(const unsigned &n1d, const unsigned &i)
Get the local fraction of any node in the n-th position in a one dimensional expansion along the i-th...
Definition elements.h:1862
int get_node_number(Node *const &node_pt) const
Return the number of the node *node_pt if this node is in the element, else return -1;.
Definition elements.cc:3844
A Generalised Element class.
Definition elements.h:73
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.
Class that contains data for hanging nodes.
Definition nodes.h:742
Base class for elements that allow MacroElement-based node update.
MacroElementNodeUpdate nodes are nodes with a positional update function, based on their element's Ma...
A general mesh class.
Definition mesh.h:67
void add_boundary_node(const unsigned &b, Node *const &node_pt)
Add a (pointer to) a node to the b-th boundary.
Definition mesh.cc:243
void add_node_pt(Node *const &node_pt)
Add a (pointer to a) node to the mesh.
Definition mesh.h:619
bool boundary_coordinate_exists(const unsigned &i) const
Indicate whether the i-th boundary has an intrinsic coordinate.
Definition mesh.h:571
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
unsigned nposition_type() const
Number of coordinate types needed in the mapping between local and global coordinates.
Definition nodes.h:1016
OcTree class: Recursively defined, generalised octree.
Definition octree.h:114
static Vector< int > faces_of_common_edge(const int &edge)
Function that, given an edge, returns the two faces on which it.
Definition octree.cc:268
static double nodal_position(const unsigned &n)
Definition shape.h:1250
static void calculate_nodal_positions()
Static function used to populate the stored positions.
Definition shape.h:1241
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
p-refineable version of RefineableElement
A class that is used to template the p-refineable Q elements by dimension. It's really nothing more t...
Definition Qelements.h:2274
QuadTree class: Recursively defined, generalised quadtree.
Definition quadtree.h:104
A class that is used to template the refineable Q elements by dimension. It's really nothing more tha...
Definition Qelements.h:2259
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
Definition shape.h:76
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition nodes.h:1686
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element.
unsigned nvertex_node() const
Number of vertex nodes in the element.
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
A generalised tree base class that abstracts the common functionality between the quad- and octrees u...
Definition tree.h:74
int son_type() const
Return son type.
Definition tree.h:214
RefineableElement * object_pt() const
Return the pointer to the object (RefineableElement) represented by the tree.
Definition tree.h:88
static const int OMEGA
Default value for an unassigned neighbour.
Definition tree.h:262
double dlegendre(const unsigned &p, const double &x)
Calculates first derivative of Legendre polynomial of degree p at x using three term recursive formul...
Definition orthpoly.h:121
double ddlegendre(const unsigned &p, const double &x)
Calculates second derivative of Legendre polynomial of degree p at x using three term recursive formu...
Definition orthpoly.h:144
void gll_nodes(const unsigned &Nnode, Vector< double > &x)
Calculates the Gauss Lobatto Legendre abscissas for degree p = NNode-1.
Definition orthpoly.cc:33
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...