refineable_mesh.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
27
28#ifdef OOMPH_HAS_MPI
29#include "mpi.h"
30#endif
31
32#include <cstdlib>
33#include <stdlib.h>
34#include <limits>
35
36#include "refineable_mesh.h"
37
38namespace oomph
39{
40 //========================================================================
41 /// Get refinement pattern of mesh: Consider the hypothetical mesh
42 /// obtained by truncating the refinement of the current mesh to a given level
43 /// (where \c level=0 is the un-refined base mesh). To advance
44 /// to the next refinement level, we need to refine (split) the
45 /// \c to_be_refined[level].size() elements identified by the
46 /// element numbers contained in \c vector to_be_refined[level][...]
47 //========================================================================
49 Vector<Vector<unsigned>>& to_be_refined)
50 {
51 // Extract *all* elements from current (fully refined) mesh.
54
55 // Find out maximum refinement level
56 unsigned max_level = 0;
57 unsigned nnodes = all_tree_nodes_pt.size();
58 for (unsigned e = 0; e < nnodes; e++)
59 {
60 unsigned level = all_tree_nodes_pt[e]->level();
61 if (level > max_level) max_level = level;
62 }
63
64 // Assign storage for refinement pattern
65 to_be_refined.clear();
66 to_be_refined.resize(max_level);
68
69 // Initialise count of elements that exist in mesh when refinement
70 // has proceeded to this level
71 for (unsigned l = 0; l < max_level; l++)
72 {
73 el_count[l] = 0;
74 }
75
76 // Loop over all levels and extract all elements that exist
77 // in reference mesh when refinement has proceeded to this level
78 for (unsigned l = 0; l < max_level; l++)
79 {
80 // Loop over all elements (tree nodes)
81 for (unsigned e = 0; e < nnodes; e++)
82 {
83 // What level does this element exist on?
84 unsigned level = all_tree_nodes_pt[e]->level();
85
86 // Element is part of the mesh at this refinement level
87 // if it exists at this level OR if it exists at a lower level
88 // and is a leaf
89 if ((level == l) || ((level < l) && (all_tree_nodes_pt[e]->is_leaf())))
90 {
91 // If element exsts at this level and is not a leaf it will
92 // be refined when we move to the next level:
93 if ((level == l) && (!all_tree_nodes_pt[e]->is_leaf()))
94 {
95 // Add element number (in mesh at current refinement level)
96 // to the list of elements that need to be refined
97 to_be_refined[l].push_back(el_count[l]);
98 }
99 // Element exists in this mesh: Add to counter
100 el_count[l]++;
101 }
102 }
103 }
104 }
105
106 //========================================================================
107 /// Extract the elements at a particular refinement level in
108 /// the refinement pattern (used in Mesh::redistribute or whatever it's
109 /// going to be called (RefineableMeshBase::reduce_halo_layers or something)
110 //========================================================================
112 unsigned& refinement_level, Vector<RefineableElement*>& level_elements)
113 {
114 // Extract *all* elements from current (fully refined) mesh.
117
118 // Add the element to the vector if its level matches refinement_level
119 unsigned nnodes = all_tree_nodes_pt.size();
120 for (unsigned e = 0; e < nnodes; e++)
121 {
122 unsigned level = all_tree_nodes_pt[e]->level();
123 if (level == refinement_level)
124 {
125 level_elements.push_back(
126 dynamic_cast<RefineableElement*>(all_tree_nodes_pt[e]->object_pt()));
127 }
128 }
129 }
130
131 //========================================================================
132 /// Refine original, unrefined mesh according to specified refinement
133 /// pattern (relative to original, unrefined mesh).
134 //========================================================================
136 Vector<Vector<unsigned>>& to_be_refined)
137 {
138 // Get mesh back to unrefined state
139 unsigned my_max, my_min;
141
142 // Max refinement level:
143 unsigned my_max_level = to_be_refined.size();
144
145 unsigned global_max = 0;
146 unsigned global_max_level = 0;
147 Vector<unsigned> data(2, 0);
148 data[0] = my_max;
149 data[1] = my_max_level;
151#ifdef OOMPH_HAS_MPI
152 if (this->is_mesh_distributed())
153 {
154 MPI_Allreduce(&data[0],
155 &global_data[0],
156 2,
158 MPI_MAX,
159 Comm_pt->mpi_comm());
162 }
163 else
164#endif
165 {
168 }
169
170
171 for (unsigned i = 0; i < global_max; i++)
172 {
174 }
175
176 // Do refinement steps in current mesh
177 for (unsigned l = 0; l < global_max_level; l++)
178 {
179 // Loop over elements that need to be refined at this level
180 unsigned n_to_be_refined = 0;
181 if (l < my_max_level) n_to_be_refined = to_be_refined[l].size();
182
183 // Select relevant elements to be refined
184 for (unsigned i = 0; i < n_to_be_refined; i++)
185 {
186 dynamic_cast<RefineableElement*>(this->element_pt(to_be_refined[l][i]))
187 ->select_for_refinement();
188 }
189
190 // Now do the actual mesh refinement
191 adapt_mesh();
192 }
193 }
194
195
196 //========================================================================
197 /// Refine base mesh according to refinement pattern in restart file
198 //========================================================================
200 {
201 // Assign storage for refinement pattern
202 Vector<Vector<unsigned>> to_be_refined;
203
204 // Read refinement pattern
205 read_refinement(restart_file, to_be_refined);
206
207 // Refine
208 refine_base_mesh(to_be_refined);
209 }
210
211
212 //========================================================================
213 /// Dump refinement pattern to allow for rebuild
214 ///
215 //========================================================================
217 {
218 // Assign storage for refinement pattern
219 Vector<Vector<unsigned>> to_be_refined;
220
221 // Get refinement pattern of reference mesh:
222 get_refinement_pattern(to_be_refined);
223
224 // Dump max refinement level:
225 unsigned max_level = to_be_refined.size();
226 outfile << max_level << " # max. refinement level " << std::endl;
227
228 // Doc the numbers of the elements that need to be refined at this level
229 for (unsigned l = 0; l < max_level; l++)
230 {
231 // Loop over elements that need to be refined at this level
232 unsigned n_to_be_refined = to_be_refined[l].size();
233 outfile << n_to_be_refined << " # number of elements to be refined. "
234 << "What follows are the numbers of the elements. " << std::endl;
235
236 // Select relevant elements to be refined
237 for (unsigned i = 0; i < n_to_be_refined; i++)
238 {
239 outfile << to_be_refined[l][i] << std::endl;
240 }
241 }
242 }
243
244
245 //========================================================================
246 /// Read refinement pattern to allow for rebuild
247 ///
248 //========================================================================
250 std::ifstream& restart_file, Vector<Vector<unsigned>>& to_be_refined)
251 {
252 std::string input_string;
253
254 // Read max refinement level:
255
256 // Read line up to termination sign
257 getline(restart_file, input_string, '#');
258
259 // Ignore rest of line
260 restart_file.ignore(80, '\n');
261
262 // Convert
263 unsigned max_level = std::atoi(input_string.c_str());
264
265 // Assign storage for refinement pattern
266 to_be_refined.resize(max_level);
267
268 // Read the number of the elements that need to be refined at different
269 // levels
270 for (unsigned l = 0; l < max_level; l++)
271 {
272 // Read line up to termination sign
273 getline(restart_file, input_string, '#');
274
275 // Ignore rest of line
276 restart_file.ignore(80, '\n');
277
278 // Convert
279 unsigned n_to_be_refined = atoi(input_string.c_str());
280 ;
281
282 // Assign storage
283 to_be_refined[l].resize(n_to_be_refined);
284
285 // Read numbers of the elements that need to be refined
286 for (unsigned i = 0; i < n_to_be_refined; i++)
287 {
288 restart_file >> to_be_refined[l][i];
289 }
290 }
291 }
292
293
294 //========================================================================
295 /// Do adaptive refinement for mesh.
296 /// - Pass Vector of error estimates for all elements.
297 /// - Refine those whose errors exceeds the threshold
298 /// - (Try to) unrefine those whose errors is less than
299 /// threshold (only possible if the three brothers also want to be
300 /// unrefined, of course.)
301 /// - Update the nodal positions in the whole lot
302 /// - Store # of refined/unrefined elements.
303 /// - Doc refinement process (if required)
304 //========================================================================
306 {
307 // Set the refinement tolerance to be the max permissible error
309
310 // Set the unrefinement tolerance to be the min permissible error
312
313 // Setup doc info
315 if (doc_info_pt() == 0)
316 {
317 local_doc_info.disable_doc();
318 }
319 else
320 {
322 }
323
324
325 // Check that the errors make sense
327 {
328 std::ostringstream error_stream;
329 error_stream << "Refinement tolerance <= Unrefinement tolerance"
330 << refine_tol << " " << unrefine_tol << std::endl
331 << "doesn't make sense and will almost certainly crash"
332 << std::endl
333 << "this beautiful code!" << std::endl;
334
335 throw OomphLibError(
337 }
338
339
340 // Select elements for refinement and unrefinement
341 //================================================
342 // Reset counter for number of elements that would like to be
343 // refined further but can't
345
346 // Note: Yes, this needs to be a map because we'll have to check
347 // the refinement wishes of brothers (who we only access via pointers)
348 std::map<RefineableElement*, bool> wants_to_be_unrefined;
349
350 // Initialise a variable to store the number of elements for refinment
351 unsigned n_refine = 0;
352
353 // Loop over all elements and mark them according to the error criterion
354 unsigned long Nelement = this->nelement();
355 for (unsigned long e = 0; e < Nelement; e++)
356 {
357 // (Cast) pointer to the element
359 dynamic_cast<RefineableElement*>(this->element_pt(e));
360
361 // Initially element is not to be refined
363
364 // If the element error exceeds the threshold ...
366 {
367 // ... and its refinement level is less than the maximum desired level
368 // mark is to be refined
369 if ((el_pt->refinement_is_enabled()) &&
370 (el_pt->refinement_level() < max_refinement_level()))
371 {
372 el_pt->select_for_refinement();
373 n_refine++;
374 }
375 // ... otherwise mark it as having been over-ruled
376 else
377 {
379 }
380 }
381
382 // Now worry about unrefinement (first pass):
383
384 // Is my error too small AND do I have a father?
385 if ((elemental_error[e] < unrefine_tol) &&
386 (el_pt->tree_pt()->father_pt() != 0))
387 {
388 // Flag to indicate whether to unrefine
389 bool unrefine = true;
390 unsigned n_sons = el_pt->tree_pt()->father_pt()->nsons();
391
392 // Are all brothers leaf nodes?
393 for (unsigned ison = 0; ison < n_sons; ison++)
394 {
395 // (At least) one brother is not a leaf: end of story; we're not doing
396 // it (= the unrefinement)
397 if (!(el_pt->tree_pt()->father_pt()->son_pt(ison)->is_leaf()))
398 {
399 unrefine = false;
400 }
401 }
402
403 // Don't allow unrefinement of elements that would become larger
404 // than the minimum legal refinement level
405 if (el_pt->refinement_level() - 1 < min_refinement_level())
406 {
407 unrefine = false;
408 }
409
410 // So, all things considered, is the element eligbible for refinement?
411 if (unrefine)
412 {
414 }
415 else
416 {
418 }
419 }
420 }
421
422 oomph_info << " \n Number of elements to be refined: " << n_refine
423 << std::endl;
424 oomph_info << " \n Number of elements whose refinement was overruled: "
425 << nrefinement_overruled() << std::endl;
426
427 // Second pass for unrefinement --- an element cannot be unrefined unless
428 // all brothers want to be unrefined.
429 // Loop over all elements again and let the first set of sons check if their
430 // brothers also want to be unrefined
431 unsigned n_unrefine = 0;
432 for (unsigned long e = 0; e < Nelement; e++)
433 {
434 //(Cast) pointer to the element
436 dynamic_cast<RefineableElement*>(this->element_pt(e));
437
438 // hierher: This is a bit naughty... We want to put the
439 // first son in charge -- the statement below assumes (correctly) that the
440 // enumeration of all (!) trees starts with son types.
441 // This is correct for oc and quadtrees but will bite us if we
442 // ever introduce other trees if/when we accidentally break this
443 // tacit assumption. Not sure what to do about it for
444 // now other than leaving it hierher-ed...
445 if (el_pt->tree_pt()->son_type() == OcTreeNames::LDB)
446 {
447 // Do all sons want to be unrefined?
448 bool unrefine = true;
449 unsigned n_sons = el_pt->tree_pt()->father_pt()->nsons();
450 for (unsigned ison = 0; ison < n_sons; ison++)
451 {
452 if (!(wants_to_be_unrefined[dynamic_cast<RefineableElement*>(
453 el_pt->tree_pt()->father_pt()->son_pt(ison)->object_pt())]))
454 {
455 // One guy isn't cooperating and spoils the party.
456 unrefine = false;
457 }
458 }
459
460 // Tell father that his sons need to be merged
461 if (unrefine)
462 {
463 el_pt->tree_pt()
464 ->father_pt()
465 ->object_pt()
466 ->select_sons_for_unrefinement();
468 }
469 // Otherwise mark the sons as not to be touched
470 else
471 {
472 el_pt->tree_pt()
473 ->father_pt()
474 ->object_pt()
475 ->deselect_sons_for_unrefinement();
476 }
477 }
478 }
479 oomph_info << " \n Number of elements to be merged : " << n_unrefine
480 << std::endl
481 << std::endl;
482
483
484 // Now do the actual mesh adaptation
485 //---------------------------------
486
487 // Check whether its worth our while
488 // Either some elements want to be refined,
489 // or the number that want to be unrefined are greater than the
490 // specified tolerance
491
492 // In a parallel job, it is possible that one process may not have
493 // any elements to refine, BUT a neighbouring process may refine an
494 // element which changes the hanging status of a node that is on
495 // both processes (i.e. a halo(ed) node). To get around this issue,
496 // ALL processes need to call adapt_mesh if ANY refinement is to
497 // take place anywhere.
498
499 unsigned total_n_refine = 0;
500#ifdef OOMPH_HAS_MPI
501 // Sum n_refine across all processors
502 if (this->is_mesh_distributed())
503 {
506 1,
508 MPI_SUM,
509 Comm_pt->mpi_comm());
510 }
511 else
512 {
514 }
515#else
517#endif
518
519 // There may be some issues with unrefinement too, but I have not
520 // been able to come up with an example (either in my head or in a
521 // particular problem) where anything has arisen. I can see that
522 // there may be an issue if n_unrefine differs across processes so
523 // that (total_n_unrefine > max_keep_unrefined()) on some but not
524 // all processes. I haven't seen any examples of this yet so the
525 // following code may or may not work! (Andy, 06/03/08)
526
527 unsigned total_n_unrefine = 0;
528#ifdef OOMPH_HAS_MPI
529 // Sum n_unrefine across all processors
530 if (this->is_mesh_distributed())
531 {
534 1,
536 MPI_SUM,
537 Comm_pt->mpi_comm());
538 }
539 else
540 {
542 }
543#else
545#endif
546
547 oomph_info << "---> " << total_n_refine << " elements to be refined, and "
548 << total_n_unrefine << " to be unrefined, in total.\n"
549 << std::endl;
550
552 {
553#ifdef PARANOID
554#ifdef OOMPH_HAS_MPI
555
556
557 // Sanity check: Each processor checks if the enforced unrefinement of
558 // its haloed element is matched by enforced unrefinement of the
559 // corresponding halo elements on the other processors.
560 if (this->is_mesh_distributed())
561 {
562 // Store number of processors and current process
564 int n_proc = Comm_pt->nproc();
565 int my_rank = Comm_pt->my_rank();
566
567 // Loop over processes: Each processor sends unrefinement pattern
568 // for halo elements with processor d to processor d where it's
569 // compared against the unrefinement pattern for the corresponding
570 // haloed elements
571 for (int d = 0; d < n_proc; d++)
572 {
573 // No halo with self: Send unrefinement info to proc d
574 if (d != my_rank)
575 {
576 // Get the vector of halo elements whose non-halo counterpart
577 // are on processor d
579
580 // Create vector containing (0)1 to indicate that
581 // halo element is (not) to be unrefined
582 unsigned nhalo = halo_elem_pt.size();
584 for (unsigned e = 0; e < nhalo; e++)
585 {
586 if (dynamic_cast<RefineableElement*>(halo_elem_pt[e])
587 ->sons_to_be_unrefined())
588 {
590 }
591 }
592
593 // Trap the case when there are no halo elements
594 // so that we don't get a segfault in the MPI send
595 if (nhalo > 0)
596 {
597 // Send it across to proc d
599 nhalo,
600 MPI_INT,
601 d,
602 0,
603 Comm_pt->mpi_comm());
604 }
605 }
606 // else (d=my_rank): Receive unrefinement pattern from all
607 // other processors (dd)
608 else
609 {
610 // Loop over other processors
611 for (int dd = 0; dd < n_proc; dd++)
612 {
613 // No halo with yourself
614 if (dd != d)
615 {
616 // Get the vector of haloed elements on current processor
617 // with processor dd
619 this->haloed_element_pt(dd));
620
621 // Ask processor dd to send vector containing (0)1 for
622 // halo element with current processor to be (not)unrefined
623 unsigned nhaloed = haloed_elem_pt.size();
625 // Trap to catch the case that there are no haloed elements
626 if (nhaloed > 0)
627 {
628 // Receive unrefinement pattern of haloes from proc dd
630 nhaloed,
631 MPI_INT,
632 dd,
633 0,
634 Comm_pt->mpi_comm(),
635 &status);
636 }
637
638 // Check it
639 for (unsigned e = 0; e < nhaloed; e++)
640 {
641 if (((halo_to_be_unrefined[e] == 0) &&
642 (dynamic_cast<RefineableElement*>(haloed_elem_pt[e])
643 ->sons_to_be_unrefined())) ||
644 ((halo_to_be_unrefined[e] == 1) &&
645 (!dynamic_cast<RefineableElement*>(haloed_elem_pt[e])
646 ->sons_to_be_unrefined())))
647 {
648 std::ostringstream error_message;
649 error_message
650 << "Error in unrefinement: \n"
651 << "Haloed element: " << e << " on proc " << my_rank
652 << " \n"
653 << "wants to be unrefined whereas its halo counterpart "
654 "on\n"
655 << "proc " << dd << " doesn't (or vice versa)...\n"
656 << "This is most likely because the error estimator\n"
657 << "has not assigned the same errors to halo and haloed\n"
658 << "elements -- it ought to!\n";
659 throw OomphLibError(error_message.str(),
662 }
663 }
664 }
665 }
666 }
667 }
668
669
670 // Loop over processes: Each processor sends refinement pattern
671 // for halo elements with processor d to processor d where it's
672 // compared against the refinement pattern for the corresponding
673 // haloed elements
674 for (int d = 0; d < n_proc; d++)
675 {
676 // No halo with self: Send refinement info to proc d
677 if (d != my_rank)
678 {
679 // Get the vector of halo elements whose non-halo counterpart
680 // are on processor d
682
683 // Create vector containing (0)1 to indicate that
684 // halo element is (not) to be refined
685 unsigned nhalo = halo_elem_pt.size();
687 for (unsigned e = 0; e < nhalo; e++)
688 {
689 if (dynamic_cast<RefineableElement*>(halo_elem_pt[e])
690 ->to_be_refined())
691 {
693 }
694 }
695
696 // Trap the case when there are no halo elements
697 // so that we don't get a segfault in the MPI send
698 if (nhalo > 0)
699 {
700 // Send it across to proc d
702 nhalo,
703 MPI_INT,
704 d,
705 0,
706 Comm_pt->mpi_comm());
707 }
708 }
709 // else (d=my_rank): Receive refinement pattern from all
710 // other processors (dd)
711 else
712 {
713 // Loop over other processors
714 for (int dd = 0; dd < n_proc; dd++)
715 {
716 // No halo with yourself
717 if (dd != d)
718 {
719 // Get the vector of haloed elements on current processor
720 // with processor dd
722 this->haloed_element_pt(dd));
723
724 // Ask processor dd to send vector containing (0)1 for
725 // halo element with current processor to be (not)refined
726 unsigned nhaloed = haloed_elem_pt.size();
728 // Trap to catch the case that there are no haloed elements
729 if (nhaloed > 0)
730 {
731 // Receive unrefinement pattern of haloes from proc dd
733 nhaloed,
734 MPI_INT,
735 dd,
736 0,
737 Comm_pt->mpi_comm(),
738 &status);
739 }
740
741 // Check it
742 for (unsigned e = 0; e < nhaloed; e++)
743 {
744 if (((halo_to_be_refined[e] == 0) &&
745 (dynamic_cast<RefineableElement*>(haloed_elem_pt[e])
746 ->to_be_refined())) ||
747 ((halo_to_be_refined[e] == 1) &&
748 (!dynamic_cast<RefineableElement*>(haloed_elem_pt[e])
749 ->to_be_refined())))
750 {
751 std::ostringstream error_message;
752 error_message
753 << "Error in refinement: \n"
754 << "Haloed element: " << e << " on proc " << my_rank
755 << " \n"
756 << "wants to be refined whereas its halo counterpart on\n"
757 << "proc " << dd << " doesn't (or vice versa)...\n"
758 << "This is most likely because the error estimator\n"
759 << "has not assigned the same errors to halo and haloed\n"
760 << "elements -- it ought to!\n";
761 throw OomphLibError(error_message.str(),
764 }
765 }
766 }
767 }
768 }
769 }
770 }
771
772#endif
773#endif
774
775 // Perform the actual adaptation
777
778 // The number of refineable elements is still local to each process
781 }
782 // If not worthwhile, say so but still reorder nodes and kill
783 // external storage for consistency in parallel computations
784 else
785 {
786#ifdef OOMPH_HAS_MPI
787 // Delete any external element storage - any interaction will still
788 // be set up on the fly again, so we need to get rid of old information.
789 // This particularly causes problems in multi-domain examples where
790 // we decide not to refine one of the meshes
792#endif
793
794 // Reorder the nodes within the mesh's node vector
795 // to establish a standard ordering regardless of the sequence
796 // of mesh refinements -- this is required to allow dump/restart
797 // on refined meshes
798 this->reorder_nodes();
799
800#ifdef OOMPH_HAS_MPI
801
802 // Now (re-)classify halo and haloed nodes and synchronise hanging
803 // nodes
804 // This is required in cases where delete_all_external_storage()
805 // made dependent nodes to external halo nodes nonhanging.
806 if (this->is_mesh_distributed())
807 {
811 }
812
813#endif
814
815 if (n_refine == 0)
816 {
817 oomph_info << " Not enough benefit in adapting mesh." << std::endl
818 << std::endl;
819 }
820 Nunrefined = 0;
821 Nrefined = 0;
822 }
823 }
824
825 //========================================================================
826 /// Get max/min refinement level
827 //========================================================================
829 unsigned& min_refinement_level, unsigned& max_refinement_level)
830 {
831 // Initialise
834
835 // Loop over all elements
836 unsigned long n_element = this->nelement();
837 if (n_element == 0)
838 {
841 }
842 else
843 {
844 for (unsigned long e = 0; e < n_element; e++)
845 {
846 // Get the refinement level of the element
847 unsigned level = dynamic_cast<RefineableElement*>(this->element_pt(e))
848 ->refinement_level();
849
850 if (level > max_refinement_level) max_refinement_level = level;
851 if (level < min_refinement_level) min_refinement_level = level;
852 }
853 }
854 }
855
856
857 //================================================================
858 /// Adapt mesh, which exists in two representations,
859 /// namely as:
860 /// - a FE mesh
861 /// - a forest of Oc or QuadTrees
862 ///
863 /// Refinement/derefinement process is documented (in tecplot-able form)
864 /// if requested.
865 ///
866 /// Procedure:
867 /// - Loop over all elements and do the refinement for those who want to
868 /// be refined. Note: Refinement/splitting only allocates elements but
869 /// doesn't build them.
870 /// - Build the new elements (i.e. give them nodes (create new ones where
871 /// necessary), assign boundary conditions, and add nodes to mesh
872 /// and mesh boundaries.
873 /// - For all nodes that were hanging on the previous mesh (and are still
874 /// marked as such), fill in their nodal values (consistent
875 /// with the current hanging node scheme) to make sure they are fully
876 /// functional, should they have become non-hanging during the
877 /// mesh-adaptation. Then mark the nodes as non-hanging.
878 /// - Unrefine selected elements (which may cause nodes to be re-built).
879 /// - Add the new elements to the mesh (by completely overwriting
880 /// the old Vector of elements).
881 /// - Delete any nodes that have become obsolete.
882 /// - Mark up hanging nodes and setup hanging node scheme (incl.
883 /// recursive cleanup for hanging nodes that depend on other
884 /// hanging nodes).
885 /// - Adjust position of hanging nodes to make sure their position
886 /// is consistent with the FE-based represenetation of their larger
887 /// neighbours.
888 /// - run a quick self-test on the neighbour finding scheme and
889 /// check the integrity of the elements (if PARANOID)
890 /// - doc hanging node status, boundary conditions, neighbour
891 /// scheme if requested.
892 ///
893 ///
894 /// After adaptation, all nodes (whether new or old) have up-to-date
895 /// current and previous values.
896 ///
897 /// If refinement process is being documented, the following information
898 /// is documented:
899 /// - The files
900 /// - "neighbours.dat"
901 /// - "all_nodes.dat"
902 /// - "new_nodes.dat"
903 /// - "hang_nodes_*.dat"
904 /// where the * denotes a direction (n,s,e,w) in 2D
905 /// or (r,l,u,d,f,b) in 3D
906 /// .
907 /// can be viewed with
908 /// - QHangingNodes.mcr
909 /// .
910 /// - The file
911 /// - "hangnodes_withmasters.dat"
912 /// .
913 /// can be viewed with
914 /// - QHangingNodesWithMasters.mcr
915 /// .
916 /// to check the hanging node status.
917 /// - The neighbour status of the elements is documented in
918 /// - "neighbours.dat"
919 /// .
920 /// and can be viewed with
921 /// - QuadTreeNeighbours.mcr
922 /// .
923 //=================================================================
925 {
926#ifdef OOMPH_HAS_MPI
927 // Delete any external element storage before performing the adaptation
928 // (in particular, external halo nodes that are on mesh boundaries)
930#endif
931
932 // Only perform the adapt step if the mesh has any elements. This is
933 // relevant in a distributed problem with multiple meshes, where a
934 // particular process may not have any elements on a particular submesh.
935 if (this->nelement() > 0)
936 {
937 // Pointer to mesh needs to be passed to some functions
938 Mesh* mesh_pt = this;
939
940 double t_start = 0.0;
942 {
944 }
945
946 // Do refinement(=splitting) of elements that have been selected
947 // This function encapsulates the template parameter
949
950
952 {
953 double t_end = TimingHelpers::timer();
954 oomph_info << "Time for split_elements_if_required: " << t_end - t_start
955 << std::endl;
957 }
958
959 // Now elements have been created -- build all the leaves
960 //-------------------------------------------------------
961 // Firstly put all the elements into a vector
964
965
967 {
968 double t_end = TimingHelpers::timer();
969 oomph_info << "Time for stick_leaves_into_vector: " << t_end - t_start
970 << std::endl;
972 }
973
974 // If we are documenting the output, create the filename
975 std::ostringstream fullname;
976 std::ofstream new_nodes_file;
978 {
979 fullname << doc_info.directory() << "/new_nodes" << doc_info.number()
980 << ".dat";
981 new_nodes_file.open(fullname.str().c_str());
982 }
983
984
985 // Build all elements and store vector of pointers to new nodes
986 // (Note: build() checks if the element has been built
987 // already, i.e. if it's not a new element).
990 unsigned long num_tree_nodes = leaf_nodes_pt.size();
991 for (unsigned long e = 0; e < num_tree_nodes; e++)
992 {
993 // Pre-build must be performed before any elements are built
994 leaf_nodes_pt[e]->object_pt()->pre_build(mesh_pt, new_node_pt);
995 }
996 for (unsigned long e = 0; e < num_tree_nodes; e++)
997 {
998 // Now do the actual build of the new elements
999 leaf_nodes_pt[e]->object_pt()->build(
1001 }
1002
1003
1004 double t_end = 0.0;
1006 {
1008 oomph_info << "Time for building " << num_tree_nodes
1009 << " new elements: " << t_end - t_start << std::endl;
1011 }
1012
1013 // Close the new nodes files, if it was opened
1015 {
1016 new_nodes_file.close();
1017 }
1018
1019 // Loop over all nodes in mesh and free the dofs of those that were
1020 //-----------------------------------------------------------------
1021 // pinned only because they were hanging nodes. Also update their
1022 //-----------------------------------------------------------------
1023 // nodal values so that they contain data that is consistent
1024 //----------------------------------------------------------
1025 // with the hanging node representation
1026 //-------------------------------------
1027 // (Even if the nodal data isn't actually accessed because the node
1028 // is still hanging -- we don't know this yet, and this step makes
1029 // sure that all nodes are fully functional and up-to-date, should
1030 // they become non-hanging below).
1031 //
1032 //
1033 // However, if we have a fixed mesh and hanging nodes on the boundary
1034 // become non-hanging they will not necessarily respect the curvilinear
1035 // boundaries. This can only happen in 3D of course because it is not
1036 // possible to have hanging nodes on boundaries in 2D.
1037 // The solution is to store those nodes on the boundaries that are
1038 // currently hanging and then check to see whether they have changed
1039 // status at the end of the refinement procedure.
1040 // If it has changed, then we need to adjust their positions.
1041 const unsigned n_boundary = this->nboundary();
1042 const unsigned mesh_dim = this->finite_element_pt(0)->dim();
1044
1045 unsigned long n_node = this->nnode();
1046 for (unsigned long n = 0; n < n_node; n++)
1047 {
1048 // Get the pointer to the node
1049 Node* nod_pt = this->node_pt(n);
1050
1051 // Get the number of values in the node
1052 unsigned n_value = nod_pt->nvalue();
1053
1054 // We need to find if any of the values are hanging
1055 bool is_hanging = nod_pt->is_hanging();
1056 // Loop over the values and find out whether any are hanging
1057 for (unsigned n = 0; n < n_value; n++)
1058 {
1059 is_hanging |= nod_pt->is_hanging(n);
1060 }
1061
1062 // If the node is hanging then ...
1063 if (is_hanging)
1064 {
1065 // Unless they are turned into hanging nodes again below
1066 // (this might or might not happen), fill in all the necessary
1067 // data to make them 'proper' nodes again.
1068
1069 // Reconstruct the nodal values/position from the node's
1070 // hanging node representation
1071 unsigned nt = nod_pt->ntstorage();
1072 Vector<double> values(n_value);
1073 unsigned n_dim = nod_pt->ndim();
1074 Vector<double> position(n_dim);
1075 // Loop over all history values
1076 for (unsigned t = 0; t < nt; t++)
1077 {
1078 nod_pt->value(t, values);
1079 for (unsigned i = 0; i < n_value; i++)
1080 {
1081 nod_pt->set_value(t, i, values[i]);
1082 }
1083 nod_pt->position(t, position);
1084 for (unsigned i = 0; i < n_dim; i++)
1085 {
1086 nod_pt->x(t, i) = position[i];
1087 }
1088 }
1089
1090 // If it's an algebraic node: Update its previous nodal positions too
1091 AlgebraicNode* alg_node_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
1092 if (alg_node_pt != 0)
1093 {
1094 bool update_all_time_levels = true;
1096 }
1097
1098
1099 // If it's a Solid node, update Lagrangian coordinates
1100 // from its hanging node representation
1101 SolidNode* solid_node_pt = dynamic_cast<SolidNode*>(nod_pt);
1102 if (solid_node_pt != 0)
1103 {
1105 for (unsigned i = 0; i < n_lagrangian; i++)
1106 {
1107 solid_node_pt->xi(i) = solid_node_pt->lagrangian_position(i);
1108 }
1109 }
1110
1111 // Now store geometrically hanging nodes on boundaries that
1112 // may need updating after refinement.
1113 // There will only be a problem if we have 3 spatial dimensions
1114 if ((mesh_dim > 2) && (nod_pt->is_hanging()))
1115 {
1116 // If the node is on a boundary then add a pointer to the node
1117 // to our lookup scheme
1118 if (nod_pt->is_on_boundary())
1119 {
1120 // Storage for the boundaries on which the Node is located
1121 std::set<unsigned>* boundaries_pt;
1122 nod_pt->get_boundaries_pt(boundaries_pt);
1123 if (boundaries_pt != 0)
1124 {
1125 // Loop over the boundaries and add a pointer to the node
1126 // to the appropriate storage scheme
1127 for (std::set<unsigned>::iterator it = boundaries_pt->begin();
1128 it != boundaries_pt->end();
1129 ++it)
1130 {
1132 }
1133 }
1134 }
1135 }
1136
1137 } // End of is_hanging
1138
1139 // Initially mark all nodes as 'non-hanging' and `obsolete'
1140 nod_pt->set_nonhanging();
1141 nod_pt->set_obsolete();
1142 }
1143
1145 {
1147 oomph_info << "Time for sorting out initial hanging status: "
1148 << t_end - t_start << std::endl;
1150 }
1151
1152 // Unrefine all the selected elements: This needs to be
1153 //-----------------------------------------------------
1154 // all elements, because the father elements are not actually leaves.
1155 //-------------------------------------------------------------------
1156
1157 // Unrefine
1158 for (unsigned long e = 0; e < Forest_pt->ntree(); e++)
1159 {
1161 mesh_pt);
1162 }
1163
1165 {
1167 oomph_info << "Time for unrefinement: " << t_end - t_start << std::endl;
1169 }
1170
1171 // Add the newly created elements to mesh
1172 //---------------------------------------
1173
1174 // Stick all elements into a new vector
1175 //(note the leaves may have changed, so this is not duplicated work)
1178
1179 // Copy the elements into the mesh Vector
1181 Element_pt.resize(num_tree_nodes);
1182 for (unsigned long e = 0; e < num_tree_nodes; e++)
1183 {
1184 Element_pt[e] = tree_nodes_pt[e]->object_pt();
1185
1186 // Now loop over all nodes in element and mark them as non-obsolete
1187 // Logic: Initially all nodes in the unrefined mesh were labeled
1188 // as deleteable. Then we create new elements (whose newly created
1189 // nodes are obviously non-obsolete), and killed some other elements (by
1190 // by deleting them and marking the nodes that were not shared by
1191 // their father as obsolete. Now we loop over all the remaining
1192 // elements and (re-)label all their nodes as non-obsolete. This
1193 // saves some nodes that were regarded as obsolete by deleted
1194 // elements but are still required in some surviving ones
1195 // from a tragic early death...
1197 unsigned n_node = this_el_pt->nnode(); // caching pre-loop
1198 for (unsigned n = 0; n < n_node; n++)
1199 {
1201 }
1202 }
1203
1204
1206 {
1208 oomph_info << "Time for adding elements to mesh: " << t_end - t_start
1209 << std::endl;
1211 }
1212
1213 // Cannot delete nodes that are still marked as obsolete
1214 // because they may still be required to assemble the hanging schemes
1215 //-------------------------------------------------------------------
1216
1217 // Mark up hanging nodes
1218 //----------------------
1219
1220 // Output streams for the hanging nodes
1222 // Setup the output files for hanging nodes, this must be called
1223 // precisely once for the forest. Note that the files will only
1224 // actually be opened if doc_info.is_doc_enabled() is true
1226
1227 for (unsigned long e = 0; e < num_tree_nodes; e++)
1228 {
1229 // Generic setup
1230 tree_nodes_pt[e]->object_pt()->setup_hanging_nodes(
1232 // Element specific setup
1233 tree_nodes_pt[e]->object_pt()->further_setup_hanging_nodes();
1234 }
1235
1236 // Close the hanging node files and delete the memory allocated
1237 // for the streams
1239
1240
1242 {
1244 oomph_info << "Time for setup_hanging_nodes() and "
1245 "further_setup_hanging_nodes() for "
1246 << num_tree_nodes << " elements: " << t_end - t_start
1247 << std::endl;
1249 }
1250
1251 // Read out the number of continously interpolated values
1252 // from one of the elements (assuming it's the same in all elements)
1253 unsigned ncont_interpolated_values =
1254 tree_nodes_pt[0]->object_pt()->ncont_interpolated_values();
1255
1256 // Complete the hanging nodes schemes by dealing with the
1257 // recursively hanging nodes
1258 complete_hanging_nodes(ncont_interpolated_values);
1259
1261 {
1263 oomph_info << "Time for complete_hanging_nodes: " << t_end - t_start
1264 << std::endl;
1266 }
1267
1268 /// Update the boundary element info -- this can be a costly procedure
1269 /// and for this reason the mesh writer might have decided not to
1270 /// set up this scheme. If so, we won't change this and suppress
1271 /// its creation...
1273 {
1275 }
1276
1278 {
1279 t_end = TimingHelpers::timer();
1280 oomph_info << "Time for boundary element info: " << t_end - t_start
1281 << std::endl;
1283 }
1284
1285#ifdef PARANOID
1286
1287 // Doc/check the neighbours
1288 //-------------------------
1291
1292 // Check the neighbours
1294
1295 // Check the integrity of the elements
1296 // -----------------------------------
1297
1298 // Loop over elements and get the elemental integrity
1299 double max_error = 0.0;
1300 for (unsigned long e = 0; e < num_tree_nodes; e++)
1301 {
1302 double max_el_error;
1303 tree_nodes_pt[e]->object_pt()->check_integrity(max_el_error);
1304 // If the elemental error is greater than our maximum error
1305 // reset the maximum
1306 if (max_el_error > max_error)
1307 {
1309 }
1310 }
1311
1313 {
1314 std::ostringstream error_stream;
1315 error_stream << "Mesh refined: Max. error in integrity check: "
1316 << max_error << " is too big\n";
1318 << "i.e. bigger than RefineableElement::max_integrity_tolerance()="
1320 << std::endl;
1321
1322 std::ofstream some_file;
1323 some_file.open("ProblemMesh.dat");
1324 for (unsigned long n = 0; n < n_node; n++)
1325 {
1326 // Get the pointer to the node
1327 Node* nod_pt = this->node_pt(n);
1328 // Get the dimension
1329 unsigned n_dim = nod_pt->ndim();
1330 // Output the coordinates
1331 for (unsigned i = 0; i < n_dim; i++)
1332 {
1333 some_file << this->node_pt(n)->x(i) << " ";
1334 }
1335 some_file << std::endl;
1336 }
1337 some_file.close();
1338
1339 error_stream << "Doced problem mesh in ProblemMesh.dat" << std::endl;
1340
1341 throw OomphLibError(
1343 }
1344 else
1345 {
1346 oomph_info << "Mesh refined: Max. error in integrity check: "
1347 << max_error << " is OK" << std::endl;
1349 << "i.e. less than RefineableElement::max_integrity_tolerance()="
1351 << std::endl;
1352 }
1353
1354
1356 {
1358 oomph_info << "Time for (paranoid only) checking of integrity: "
1359 << t_end - t_start << std::endl;
1361 }
1362
1363#endif
1364
1365 // Loop over all elements other than the final level and deactivate the
1366 // objects, essentially set the pointer that point to nodes that are
1367 // about to be deleted to NULL. This must take place here because nodes
1368 // addressed by elements that are dead but still living in the tree might
1369 // have been made obsolete in the last round of refinement
1370 for (unsigned long e = 0; e < Forest_pt->ntree(); e++)
1371 {
1374 }
1375
1376 // Now we can prune the dead nodes from the mesh.
1378
1380 {
1381 t_end = TimingHelpers::timer();
1382 oomph_info << "Time for deactivating objects and pruning nodes: "
1383 << t_end - t_start << std::endl;
1385 }
1386
1387 // Finally: Reorder the nodes within the mesh's node vector
1388 // to establish a standard ordering regardless of the sequence
1389 // of mesh refinements -- this is required to allow dump/restart
1390 // on refined meshes
1391 this->reorder_nodes();
1392
1394 {
1395 t_end = TimingHelpers::timer();
1396 oomph_info << "Time for reordering " << nnode()
1397 << " nodes: " << t_end - t_start << std::endl;
1399 }
1400
1401 // Now we can correct the nodes on boundaries that were hanging that
1402 // are no longer hanging
1403 // Only bother if we have more than two dimensions
1404 if (mesh_dim > 2)
1405 {
1406 // Loop over the boundaries
1407 for (unsigned b = 0; b < n_boundary; b++)
1408 {
1409 // Remove deleted nodes from the set
1410 unsigned n_del = deleted_node_pt.size();
1411 for (unsigned j = 0; j < n_del; j++)
1412 {
1414 }
1415
1416 // If the nodes that were hanging are still hanging then remove them
1417 // from the set (note increment is not in for command for efficiencty)
1418 for (std::set<Node*>::iterator it =
1420 it != hanging_nodes_on_boundary_pt[b].end();)
1421 {
1422 if ((*it)->is_hanging())
1423 {
1424 hanging_nodes_on_boundary_pt[b].erase(it++);
1425 }
1426 else
1427 {
1428 ++it;
1429 }
1430 }
1431
1432 // Are there any nodes that have changed geometric hanging status
1433 // on the boundary
1434 // The slightly painful part is that we must adjust the position
1435 // via the macro-elements which are only available through the
1436 // elements and not the nodes.
1437 if (hanging_nodes_on_boundary_pt[b].size() > 0)
1438 {
1439 // If so we loop over all elements adjacent to the boundary
1440 unsigned n_boundary_element = this->nboundary_element(b);
1441 for (unsigned e = 0; e < n_boundary_element; ++e)
1442 {
1443 // Get a pointer to the element
1445
1446 // Do we have a solid element
1448 dynamic_cast<SolidFiniteElement*>(el_pt);
1449
1450 // Determine whether there is a macro element
1451 bool macro_present = (el_pt->macro_elem_pt() != 0);
1452 // Or a solid macro element
1453 if (solid_el_pt != 0)
1454 {
1455 macro_present |= (solid_el_pt->undeformed_macro_elem_pt() != 0);
1456 }
1457
1458 // Only bother to do anything if there is a macro element
1459 // or undeformed macro element in a SolidElement
1460 if (macro_present)
1461 {
1462 // Loop over the nodes
1463 // ALH: (could optimise to only loop over
1464 // node associated with the boundary with more effort)
1465 unsigned n_el_node = el_pt->nnode();
1466 for (unsigned n = 0; n < n_el_node; n++)
1467 {
1468 // Cache pointer to the node
1469 Node* nod_pt = el_pt->node_pt(n);
1470 if (nod_pt->is_on_boundary(b))
1471 {
1472 // Is the Node in our set
1473 std::set<Node*>::iterator it =
1475
1476 // If we have found the Node then update the position
1477 // to be consistent with the macro-element representation
1479 {
1480 // Specialise local and global coordinates to 3D
1481 // because there is only a problem in 3D.
1482 Vector<double> s(3), x(3);
1483
1484 // Find the local coordinate of the ndoe
1486
1487 // Find the number of time history values
1488 const unsigned ntstorage = nod_pt->ntstorage();
1489
1490 // Do we have a solid node
1492 dynamic_cast<SolidNode*>(nod_pt);
1493 if (solid_node_pt)
1494 {
1495 // Assign Lagrangian coordinates from undeformed
1496 // macro element (if it has one -- get_x_and_xi()
1497 // does "the right thing" anyway. Leave actual
1498 // nodal positions alone -- we're doing a solid
1499 // mechanics problem and once we're going
1500 // the nodal positions are always computed, never
1501 // (automatically) reset to macro-element based
1502 // positions; not even on pinned boundaries
1503 // because the user may have other ideas about where
1504 // these should go -- pinning means "don't touch the
1505 // value", not "leave where the macro-element thinks
1506 // it should be"
1507 Vector<double> x_fe(3), xi(3), xi_fe(3);
1508 solid_el_pt->get_x_and_xi(s, x_fe, x, xi_fe, xi);
1509 for (unsigned i = 0; i < 3; i++)
1510 {
1511 solid_node_pt->xi(i) = xi[i];
1512 }
1513 }
1514 else
1515 {
1516 // Set position and history values from the
1517 // macro-element representation
1518 for (unsigned t = 0; t < ntstorage; t++)
1519 {
1520 // Get the history value from the macro element
1521 el_pt->get_x(t, s, x);
1522
1523 // Set the coordinate to that of the macroelement
1524 // representation
1525 for (unsigned i = 0; i < 3; i++)
1526 {
1527 nod_pt->x(t, i) = x[i];
1528 }
1529 }
1530 } // End of non-solid node case
1531
1532 // Now remove the node from the list
1534
1535 // If there are no Nodes left then exit the loops
1536 if (hanging_nodes_on_boundary_pt[b].size() == 0)
1537 {
1539 break;
1540 }
1541 }
1542 }
1543 }
1544 } // End of macro element case
1545 }
1546 }
1547 }
1548 } // End of case when we have fixed nodal positions
1549
1550
1551 // Final doc
1552 //-----------
1554 {
1555 // Doc the boundary conditions ('0' for non-existent, '1' for free,
1556 //----------------------------------------------------------------
1557 // '2' for pinned -- ideal for tecplot scatter sizing.
1558 //----------------------------------------------------
1559 // num_tree_nodes=tree_nodes_pt.size();
1560
1561 // Determine maximum number of values at any node in this type of
1562 // element
1563 RefineableElement* el_pt = tree_nodes_pt[0]->object_pt();
1564 // Initalise max_nval
1565 unsigned max_nval = 0;
1566 for (unsigned n = 0; n < el_pt->nnode(); n++)
1567 {
1568 if (el_pt->node_pt(n)->nvalue() > max_nval)
1569 {
1570 max_nval = el_pt->node_pt(n)->nvalue();
1571 }
1572 }
1573
1574 // Open the output file
1575 std::ofstream bcs_file;
1576 fullname.str("");
1577 fullname << doc_info.directory() << "/bcs" << doc_info.number()
1578 << ".dat";
1579 bcs_file.open(fullname.str().c_str());
1580
1581 // Loop over elements
1582 for (unsigned long e = 0; e < num_tree_nodes; e++)
1583 {
1584 el_pt = tree_nodes_pt[e]->object_pt();
1585 // Loop over nodes in element
1586 unsigned n_nod = el_pt->nnode();
1587 for (unsigned n = 0; n < n_nod; n++)
1588 {
1589 // Get pointer to the node
1590 Node* nod_pt = el_pt->node_pt(n);
1591 // Find the dimension of the node
1592 unsigned n_dim = nod_pt->ndim();
1593 // Write the nodal coordinates to the file
1594 for (unsigned i = 0; i < n_dim; i++)
1595 {
1596 bcs_file << nod_pt->x(i) << " ";
1597 }
1598
1599 // Loop over all values in this element
1600 for (unsigned i = 0; i < max_nval; i++)
1601 {
1602 // Value exists at this node:
1603 if (i < nod_pt->nvalue())
1604 {
1605 bcs_file << " " << 1 + nod_pt->is_pinned(i);
1606 }
1607 // ...if not just dump out a zero
1608 else
1609 {
1610 bcs_file << " 0 ";
1611 }
1612 }
1613 bcs_file << std::endl;
1614 }
1615 }
1616 bcs_file.close();
1617
1618 // Doc all nodes
1619 //---------------
1620 std::ofstream all_nodes_file;
1621 fullname.str("");
1622 fullname << doc_info.directory() << "/all_nodes" << doc_info.number()
1623 << ".dat";
1624 all_nodes_file.open(fullname.str().c_str());
1625
1626 all_nodes_file << "ZONE \n";
1627
1628 // Need to recompute the number of nodes since it may have
1629 // changed during mesh refinement/unrefinement
1630 n_node = this->nnode();
1631 for (unsigned long n = 0; n < n_node; n++)
1632 {
1633 Node* nod_pt = this->node_pt(n);
1634 unsigned n_dim = nod_pt->ndim();
1635 for (unsigned i = 0; i < n_dim; i++)
1636 {
1637 all_nodes_file << this->node_pt(n)->x(i) << " ";
1638 }
1639 all_nodes_file << std::endl;
1640 }
1641
1642 all_nodes_file.close();
1643
1644
1645 // Doc all hanging nodes:
1646 //-----------------------
1647 std::ofstream some_file;
1648 fullname.str("");
1649 fullname << doc_info.directory() << "/all_hangnodes"
1650 << doc_info.number() << ".dat";
1651 some_file.open(fullname.str().c_str());
1652 for (unsigned long n = 0; n < n_node; n++)
1653 {
1654 Node* nod_pt = this->node_pt(n);
1655
1656 if (nod_pt->is_hanging())
1657 {
1658 unsigned n_dim = nod_pt->ndim();
1659 for (unsigned i = 0; i < n_dim; i++)
1660 {
1661 some_file << nod_pt->x(i) << " ";
1662 }
1663
1664 // ALH: Added this to stop Solid problems seg-faulting
1665 if (this->node_pt(n)->nvalue() > 0)
1666 {
1667 some_file << " " << nod_pt->raw_value(0);
1668 }
1669 some_file << std::endl;
1670 }
1671 }
1672 some_file.close();
1673
1674 // Doc all hanging nodes and their masters
1675 // View with QHangingNodesWithMasters.mcr
1676 fullname.str("");
1677 fullname << doc_info.directory() << "/geometric_hangnodes_withmasters"
1678 << doc_info.number() << ".dat";
1679 some_file.open(fullname.str().c_str());
1680 for (unsigned long n = 0; n < n_node; n++)
1681 {
1682 Node* nod_pt = this->node_pt(n);
1683 if (nod_pt->is_hanging())
1684 {
1685 unsigned n_dim = nod_pt->ndim();
1686 unsigned nmaster = nod_pt->hanging_pt()->nmaster();
1687 some_file << "ZONE I=" << nmaster + 1 << std::endl;
1688 for (unsigned i = 0; i < n_dim; i++)
1689 {
1690 some_file << nod_pt->x(i) << " ";
1691 }
1692 some_file << " 2 " << std::endl;
1693
1694 for (unsigned imaster = 0; imaster < nmaster; imaster++)
1695 {
1697 nod_pt->hanging_pt()->master_node_pt(imaster);
1698 unsigned n_dim = master_nod_pt->ndim();
1699 for (unsigned i = 0; i < n_dim; i++)
1700 {
1701 some_file << master_nod_pt->x(i) << " ";
1702 }
1703 some_file << " 1 " << std::endl;
1704 }
1705 }
1706 }
1707 some_file.close();
1708
1709 // Doc all hanging nodes and their masters
1710 // View with QHangingNodesWithMasters.mcr
1711 for (unsigned i = 0; i < ncont_interpolated_values; i++)
1712 {
1713 fullname.str("");
1715 << "/nonstandard_hangnodes_withmasters" << i << "_"
1716 << doc_info.number() << ".dat";
1717 some_file.open(fullname.str().c_str());
1718 unsigned n_nod = this->nnode();
1719 for (unsigned long n = 0; n < n_nod; n++)
1720 {
1721 Node* nod_pt = this->node_pt(n);
1722 if (nod_pt->is_hanging(i))
1723 {
1724 if (nod_pt->hanging_pt(i) != nod_pt->hanging_pt())
1725 {
1726 unsigned nmaster = nod_pt->hanging_pt(i)->nmaster();
1727 some_file << "ZONE I=" << nmaster + 1 << std::endl;
1728 unsigned n_dim = nod_pt->ndim();
1729 for (unsigned j = 0; j < n_dim; j++)
1730 {
1731 some_file << nod_pt->x(j) << " ";
1732 }
1733 some_file << " 2 " << std::endl;
1734 for (unsigned imaster = 0; imaster < nmaster; imaster++)
1735 {
1737 nod_pt->hanging_pt(i)->master_node_pt(imaster);
1738 unsigned n_dim = master_nod_pt->ndim();
1739 for (unsigned j = 0; j < n_dim; j++)
1740 {
1741 // some_file << master_nod_pt->x(i) << " ";
1742 }
1743 some_file << " 1 " << std::endl;
1744 }
1745 }
1746 }
1747 }
1748 some_file.close();
1749 }
1750 } // End of documentation
1751 } // End if (this->nelement()>0)
1752
1753
1754#ifdef OOMPH_HAS_MPI
1755
1756 // Now (re-)classify halo and haloed nodes and synchronise hanging
1757 // nodes
1758 if (this->is_mesh_distributed())
1759 {
1761 }
1762
1763#endif
1764 }
1765
1766
1767 //========================================================================
1768 /// Refine mesh uniformly
1769 //========================================================================
1771 {
1772 // Select all elements for refinement
1773 unsigned long Nelement = this->nelement();
1774 for (unsigned long e = 0; e < Nelement; e++)
1775 {
1776 dynamic_cast<RefineableElement*>(this->element_pt(e))
1777 ->select_for_refinement();
1778 }
1779
1780 // Do the actual mesh adaptation
1782 }
1783
1784
1785 //========================================================================
1786 /// p-refine mesh uniformly
1787 //========================================================================
1789 {
1790 // Select all elements for refinement
1791 unsigned long Nelement = this->nelement();
1792 for (unsigned long e = 0; e < Nelement; e++)
1793 {
1794 // Get pointer to p-refineable element
1796 dynamic_cast<PRefineableElement*>(this->element_pt(e));
1797 // Mark for p-refinement if possible. If not then p_adapt_mesh() will
1798 // report the error.
1799 if (el_pt != 0)
1800 {
1801 el_pt->select_for_p_refinement();
1802 }
1803 }
1804
1805 // Do the actual mesh adaptation
1807 }
1808
1809
1810 //========================================================================
1811 /// Refine mesh by splitting the elements identified
1812 /// by their numbers.
1813 //========================================================================
1816 {
1817#ifdef OOMPH_HAS_MPI
1818 if (this->is_mesh_distributed())
1819 {
1820 std::ostringstream warn_stream;
1821 warn_stream << "You are attempting to refine selected elements of a "
1822 << std::endl
1823 << "distributed mesh. This may have undesired effects."
1824 << std::endl;
1825
1827 "TreeBasedRefineableMeshBase::refine_selected_elements()",
1829 }
1830#endif
1831
1832 // Select elements for refinement
1833 unsigned long nref = elements_to_be_refined.size();
1834 for (unsigned long e = 0; e < nref; e++)
1835 {
1836 dynamic_cast<RefineableElement*>(
1837 this->element_pt(elements_to_be_refined[e]))
1838 ->select_for_refinement();
1839 }
1840
1841 // Do the actual mesh adaptation
1842 adapt_mesh();
1843 }
1844
1845
1846 //========================================================================
1847 /// Refine mesh by splitting the elements identified
1848 /// by their pointers
1849 //========================================================================
1852 {
1853#ifdef OOMPH_HAS_MPI
1854 if (this->is_mesh_distributed())
1855 {
1856 std::ostringstream warn_stream;
1857 warn_stream << "You are attempting to refine selected elements of a "
1858 << std::endl
1859 << "distributed mesh. This may have undesired effects."
1860 << std::endl;
1861
1863 "TreeBasedRefineableMeshBase::refine_selected_elements()",
1865 }
1866#endif
1867
1868 // Select elements for refinement
1869 unsigned long nref = elements_to_be_refined_pt.size();
1870 for (unsigned long e = 0; e < nref; e++)
1871 {
1872 elements_to_be_refined_pt[e]->select_for_refinement();
1873 }
1874
1875 // Do the actual mesh adaptation
1876 adapt_mesh();
1877 }
1878
1879
1880 //========================================================================
1881 /// Refine to same degree as the reference mesh.
1882 //========================================================================
1885 {
1886 // Assign storage for refinement pattern
1887 Vector<Vector<unsigned>> to_be_refined;
1888
1889 // Get refinement pattern of reference mesh:
1890 ref_mesh_pt->get_refinement_pattern(to_be_refined);
1891
1892 // Refine mesh according to given refinement pattern
1893 refine_base_mesh(to_be_refined);
1894 }
1895
1896
1897 //========================================================================
1898 /// Refine to same degree as the reference mesh minus one. Useful
1899 /// function for multigrid solvers; allows the easy copy of a mesh
1900 /// to the level of refinement just below the current one. Returns
1901 /// a boolean variable which indicates if the reference mesh has not
1902 /// been refined at all
1903 //========================================================================
1907 {
1908 // Assign storage for refinement pattern
1909 Vector<Vector<unsigned>> to_be_refined;
1910
1911 // Get refinement pattern of reference mesh:
1912 ref_mesh_pt->get_refinement_pattern(to_be_refined);
1913
1914 // Find the length of the vector
1915 unsigned nrefinement_levels = to_be_refined.size();
1916
1917 // If the reference mesh has not been refined a single time then
1918 // we cannot create an unrefined copy so stop here
1919 if (nrefinement_levels == 0)
1920 {
1921 return false;
1922 }
1923 // If the reference mesh has been refined at least once
1924 else
1925 {
1926 // Remove the last entry of the vector to make sure we refine to
1927 // the same level minus one
1928 to_be_refined.resize(nrefinement_levels - 1);
1929
1930 // Refine mesh according to given refinement pattern
1931 refine_base_mesh(to_be_refined);
1932
1933 // Indicate that it was possible to create an unrefined copy
1934 return true;
1935 }
1936 }
1937
1938
1939 //========================================================================
1940 /// Refine mesh once so that its topology etc becomes that of the
1941 /// (finer!) reference mesh -- if possible! Useful for meshes in multigrid
1942 /// hierarchies. If the meshes are too different and the conversion
1943 /// cannot be performed, the code dies (provided PARANOID is enabled).
1944 //========================================================================
1947 {
1948 oomph_info << "WARNING : This has not been checked comprehensively yet"
1949 << std::endl
1950 << "Check it and remove this break " << std::endl;
1951 pause("Yes really pause");
1952
1953#ifdef PARANOID
1954 // The max. refinement levels of the two meshes need to differ
1955 // by one, otherwise what we're doing here doesn't make sense.
1956 unsigned my_min, my_max;
1958
1959 unsigned ref_min, ref_max;
1960 ref_mesh_pt->get_refinement_levels(ref_min, ref_max);
1961
1962 if (ref_max != my_max + 1)
1963 {
1964 std::ostringstream error_stream;
1966 << "Meshes definitely don't differ by one refinement level \n"
1967 << "max. refinement levels: " << ref_max << " " << my_max << std::endl;
1968
1969 throw OomphLibError(
1971 }
1972#endif
1973
1974 // Vector storing the elements of the uniformly unrefined mesh
1976
1977 // Map storing which father elements have already been added to coarse mesh
1978 // (Default return is 0).
1979 std::map<Tree*, unsigned> father_element_included;
1980
1981 // Extract active elements (=leaf nodes in the quadtree) from reference
1982 // mesh.
1984 ref_mesh_pt->forest_pt()->stick_leaves_into_vector(leaf_nodes_pt);
1985
1986 // Loop over all elements (in their quadtree impersonation) and
1987 // check if their fathers's sons are all leaves too:
1988 unsigned nelem = leaf_nodes_pt.size();
1989 for (unsigned e = 0; e < nelem; e++)
1990 {
1991 // Pointer to leaf node
1993
1994 // Get pointer to father:
1995 Tree* father_pt = leaf_pt->father_pt();
1996
1997 // If we don't have a father we're at the root level in which
1998 // case this element can't be unrefined.
1999 if (0 == father_pt)
2000 {
2001 coarse_elements_pt.push_back(leaf_pt);
2002 }
2003 else
2004 {
2005 // Loop over the father's sons to check if they're
2006 // all non-leafs, i.e. if they can be unrefined
2007 bool can_unrefine = true;
2008 unsigned n_sons = father_pt->nsons();
2009 for (unsigned i = 0; i < n_sons; i++)
2010 {
2011 // If (at least) one of the sons is not a leaf, we can't unrefine
2012 if (!father_pt->son_pt(i)->is_leaf()) can_unrefine = false;
2013 }
2014
2015 // If we can unrefine, the father element will be
2016 // an element in the coarse mesh, the sons won't
2017 if (can_unrefine)
2018 {
2019 if (father_element_included[father_pt] == 0)
2020 {
2021 coarse_elements_pt.push_back(father_pt);
2022 father_element_included[father_pt] = 1;
2023 }
2024 }
2025 // Son will still be there on the coarse mesh
2026 else
2027 {
2028 coarse_elements_pt.push_back(leaf_pt);
2029 }
2030 }
2031 }
2032
2033 // Number of elements in ref mesh if it was unrefined uniformly:
2034 unsigned nel_coarse = coarse_elements_pt.size();
2035
2036
2037#ifdef PARANOID
2038 bool stop_it = false;
2039 // The numbers had better match otherwise we might as well stop now...
2040 if (nel_coarse != this->nelement())
2041 {
2042 oomph_info << "Number of elements in uniformly unrefined reference mesh: "
2043 << nel_coarse << std::endl;
2044 oomph_info << "Number of elements in 'this' mesh: " << nel_coarse
2045 << std::endl;
2046 oomph_info << "don't match" << std::endl;
2047 stop_it = true;
2048 }
2049#endif
2050
2051 // Now loop over all elements in uniformly coarsened reference mesh
2052 // and check if add the number of any element that was created
2053 // by having had its sons merged to the vector of elements that
2054 // need to get refined if we go the other way
2056 for (unsigned i = 0; i < nel_coarse; i++)
2057 {
2059 {
2060 elements_to_be_refined.push_back(i);
2061 }
2062 }
2063
2064
2065#ifdef PARANOID
2066 // Doc troublesome meshes:
2067 if (stop_it)
2068 {
2069 std::ofstream some_file;
2070 some_file.open("orig_mesh.dat");
2071 this->output(some_file);
2072 some_file.close();
2073 oomph_info << "Documented original ('this')mesh in orig_mesh.dat"
2074 << std::endl;
2075 }
2076#endif
2077
2078
2079 // Now refine precisely these elements in "this" mesh.
2081
2082
2083#ifdef PARANOID
2084
2085 // Check if the nodal positions of all element's nodes agree
2086 // in the two fine meshes:
2087 double tol = 1.0e-5;
2088 for (unsigned e = 0; e < nelem; e++)
2089 {
2090 // Get elements
2091 FiniteElement* ref_el_pt = ref_mesh_pt->finite_element_pt(e);
2093
2094 // Loop over nodes
2095 unsigned nnod = ref_el_pt->nnode();
2096 for (unsigned j = 0; j < nnod; j++)
2097 {
2098 // Get nodes
2100 Node* node_pt = el_pt->node_pt(j);
2101
2102 // Check error in position
2103 double error = 0.0;
2104 unsigned ndim = node_pt->ndim();
2105 for (unsigned i = 0; i < ndim; i++)
2106 {
2107 error += pow(node_pt->x(i) - ref_node_pt->x(i), 2);
2108 }
2109 error = sqrt(error);
2110
2111 if (error > tol)
2112 {
2113 oomph_info << "Error in nodal position of node " << j << ": " << error
2114 << " [tol=" << tol << "]" << std::endl;
2115 stop_it = true;
2116 }
2117 }
2118 }
2119
2120 // Do we have a death wish?
2121 if (stop_it)
2122 {
2123 // Doc troublesome meshes:
2124 std::ofstream some_file;
2125 some_file.open("refined_mesh.dat");
2126 this->output(some_file);
2127 some_file.close();
2128
2129 some_file.open("finer_mesh.dat");
2131 some_file.close();
2132
2133 throw OomphLibError(
2134 "Bailing out. Doced refined_mesh.dat finer_mesh.dat\n",
2137 }
2138
2139#endif
2140 }
2141
2142
2143 //========================================================================
2144 /// Unrefine mesh uniformly. Return 0 for success,
2145 /// 1 for failure (if unrefinement has reached the coarsest permitted
2146 /// level)
2147 //========================================================================
2149 {
2150 // We can't just select all elements for unrefinement
2151 // because they need to merge with their brothers.
2152 // --> Rather than repeating the convoluted logic of
2153 // RefineableQuadMesh<ELEMENT>::adapt(Vector<double>& elemental_error)
2154 // here (code duplication!) hack it by filling the error
2155 // vector with values that ensure unrefinement for all
2156 // elements where this is possible
2157
2158 // Create dummy vector for elemental errors
2159 unsigned long Nelement = this->nelement();
2161
2162 // In order to force unrefinement, set the min permitted error to
2163 // be the default and then set the actual error to be below this.
2164 // This avoids problems when the actual min error is zero (or small)
2165 // For sanity's sake, also set the max permitted error back to the default
2166 // so that we have a max error bigger than a min error
2167 const double current_min_error = this->min_permitted_error();
2168 const double current_max_error = this->max_permitted_error();
2169
2170 this->min_permitted_error() = 1.0e-5;
2171 this->max_permitted_error() = 1.0e-3;
2172
2173 double error = min_permitted_error() / 100.0;
2174 for (unsigned long e = 0; e < Nelement; e++)
2175 {
2177 }
2178
2179 // Temporarily lift any restrictions on the minimum number of
2180 // elements that need to be unrefined to make it worthwhile
2181 unsigned backup = max_keep_unrefined();
2182 max_keep_unrefined() = 0;
2183
2184 // Do the actual mesh adaptation with fake error vector
2186
2187 // Reset the minimum number of elements that need to be unrefined
2188 // to make it worthwhile
2189 max_keep_unrefined() = backup;
2190
2191 // Now restore the error tolerances
2192 this->min_permitted_error() = current_min_error;
2193 this->max_permitted_error() = current_max_error;
2194
2195 // Has the unrefinement actually changed anything?
2196 if (Nelement == this->nelement())
2197 {
2198 return 1;
2199 }
2200 else
2201 {
2202 return 0;
2203 }
2204 }
2205
2206 //==================================================================
2207 /// Given a node, return a vector of pointers to master nodes and a
2208 /// vector of the associated weights.
2209 /// This is done recursively, so if a node is not hanging,
2210 /// the node is regarded as its own master node which has weight 1.0.
2211 //==================================================================
2213 Node*& nod_pt,
2216 const int& i)
2217 {
2218 // Is the node hanging in the variable i
2219 if (nod_pt->is_hanging(i))
2220 {
2221 // Loop over all master nodes
2222 HangInfo* const hang_pt = nod_pt->hanging_pt(i);
2223 unsigned nmaster = hang_pt->nmaster();
2224
2225 for (unsigned m = 0; m < nmaster; m++)
2226 {
2227 // Get the master node
2228 Node* master_nod_pt = hang_pt->master_node_pt(m);
2229
2230 // Keep in memory the size of the list before adding the nodes this
2231 // master node depends on. This is required so that the recursion is
2232 // only performed on these particular master nodes. A master node
2233 // could contain contributions from two separate pseudo-masters.
2234 // These contributions must be summed, not multiplied.
2236
2237 // Now check which master nodes this master node depends on
2240
2241 // Multiply old weight by new weight for all the nodes this master
2242 // node depends on
2243 unsigned n_new_master_node = master_nodes.size();
2244
2245 double mtr_weight = hang_pt->master_weight(m);
2246
2247 for (unsigned k = first_new_node; k < n_new_master_node; k++)
2248 {
2250 }
2251 }
2252 }
2253 else
2254 // Node isn't hanging so it enters itself with the full weight
2255 {
2256 master_nodes.push_back(nod_pt);
2257 hang_weights.push_back(1.0);
2258 }
2259 }
2260
2261
2262 //==================================================================
2263 /// Complete the hanging node scheme recursively.
2264 /// After the initial markup scheme, hanging nodes
2265 /// can depend on other hanging nodes ---> AAAAAAAAARGH!
2266 /// Need to translate this into a scheme where all
2267 /// hanging nodes only depend on non-hanging nodes...
2268 //==================================================================
2270 const int& ncont_interpolated_values)
2271 {
2272 // Number of nodes in mesh
2273 unsigned long n_node = this->nnode();
2274 double min_weight = 1.0e-8; // RefineableBrickElement::min_weight_value();
2275
2276 // Loop over the nodes in the mesh
2277 for (unsigned long n = 0; n < n_node; n++)
2278 {
2279 // Assign a local pointer to the node
2280 Node* nod_pt = this->node_pt(n);
2281
2282 // Loop over the values,
2283 // N.B. geometric hanging data is stored at the index -1
2284 for (int i = -1; i < ncont_interpolated_values; i++)
2285 {
2286 // Is the node hanging?
2287 if (nod_pt->is_hanging(i))
2288 {
2289 // If it is geometric OR has hanging node data that differs
2290 // from the geometric data, we must do some work
2291 if ((i == -1) || (nod_pt->hanging_pt(i) != nod_pt->hanging_pt()))
2292 {
2293 // Find out the ultimate map of dependencies: Master nodes
2294 // and associated weights
2299
2300 // put them into a map to merge all the occurences of the same node
2301 // (add the weights)
2302 std::map<Node*, double> hang_weights;
2303 unsigned n_master = master_nodes.size();
2304 for (unsigned k = 0; k < n_master; k++)
2305 {
2306 if (std::fabs(hanging_weights[k]) > min_weight)
2308 }
2309
2310 // Create new hanging data (we know how many data there are)
2312
2313 unsigned hang_weights_index = 0;
2314 // Copy the map into the HangInfo object
2315 typedef std::map<Node*, double>::iterator IT;
2316 for (IT it = hang_weights.begin(); it != hang_weights.end(); ++it)
2317 {
2318 hang_pt->set_master_node_pt(
2319 hang_weights_index, it->first, it->second);
2321 }
2322
2323 // Assign the new hanging pointer to the appropriate value
2324 nod_pt->set_hanging_pt(hang_pt, i);
2325 }
2326 }
2327 }
2328 }
2329
2330#ifdef PARANOID
2331
2332 // Check hanging node scheme: The weights need to add up to one
2333 //-------------------------------------------------------------
2334 // Loop over all values indices
2335 for (int i = -1; i < ncont_interpolated_values; i++)
2336 {
2337 // Loop over all nodes in mesh
2338 for (unsigned long n = 0; n < n_node; n++)
2339 {
2340 // Set a local pointer to the node
2341 Node* nod_pt = this->node_pt(n);
2342
2343 // Is it hanging?
2344 if (nod_pt->is_hanging(i))
2345 {
2346 unsigned nmaster = nod_pt->hanging_pt(i)->nmaster();
2347 double sum = 0.0;
2348 for (unsigned imaster = 0; imaster < nmaster; imaster++)
2349 {
2350 sum += nod_pt->hanging_pt(i)->master_weight(imaster);
2351 }
2352 if (std::fabs(sum - 1.0) > 1.0e-7)
2353 {
2354 oomph_info << "WARNING: Sum of master node weights fabs(sum-1.0) "
2355 << std::fabs(sum - 1.0) << " for node number " << n
2356 << " at value " << i << std::endl;
2357 }
2358 }
2359 }
2360 }
2361#endif
2362 }
2363
2364 // Sorting out the cases of nodes that are hanging on at least one but not
2365 // all of the processors for which they are part of the local mesh.
2366
2367#ifdef OOMPH_HAS_MPI
2368
2369 //========================================================================
2370 /// Deal with nodes that are hanging on one process but not another
2371 /// (i.e. the hanging status of the haloed and halo layers disagrees)
2372 //========================================================================
2374 const unsigned& ncont_interpolated_values)
2375 {
2376 // Store number of processors and current process
2378 int n_proc = Comm_pt->nproc();
2379 int my_rank = Comm_pt->my_rank();
2380
2381 double t_start = 0.0;
2382 double t_end = 0.0;
2383
2384 // Storage for the hanging status of halo/haloed nodes on elements
2387
2388 // Counter for the number of nodes which require additional synchronisation
2390
2392 {
2394 }
2395
2396 // Store number of continuosly interpolated values as int
2397 int ncont_inter_values = ncont_interpolated_values;
2398
2399 // Loop over processes: Each processor checks that is haloed nodes
2400 // with proc d have consistent hanging stats with halo counterparts.
2401 for (int d = 0; d < n_proc; d++)
2402 {
2403 // No halo with self: Setup hang info for my haloed nodes with proc d
2404 // then get ready to receive halo info from processor d.
2405 if (d != my_rank)
2406 {
2407 // Loop over haloed nodes
2408 unsigned nh = nhaloed_node(d);
2409 for (unsigned j = 0; j < nh; j++)
2410 {
2411 // Get node
2412 Node* nod_pt = haloed_node_pt(d, j);
2413
2414 // Loop over the hanging status for each interpolated variable
2415 // (and the geometry)
2416 for (int icont = -1; icont < ncont_inter_values; icont++)
2417 {
2418 // Store the hanging status of this haloed node
2419 if (nod_pt->is_hanging(icont))
2420 {
2421 unsigned n_master = nod_pt->hanging_pt(icont)->nmaster();
2422 haloed_hanging[d].push_back(n_master);
2423 }
2424 else
2425 {
2426 haloed_hanging[d].push_back(0);
2427 }
2428 }
2429 }
2430
2431 // Receive the hanging status information from the corresponding process
2432 unsigned count_haloed = haloed_hanging[d].size();
2433
2434#ifdef PARANOID
2435 // Check that number of halo and haloed data match
2436 unsigned tmp = 0;
2437 MPI_Recv(&tmp, 1, MPI_UNSIGNED, d, 0, Comm_pt->mpi_comm(), &status);
2438 if (tmp != count_haloed)
2439 {
2440 std::ostringstream error_stream;
2441 error_stream << "Number of halo data, " << tmp
2442 << ", does not match number of haloed data, "
2443 << count_haloed << std::endl;
2444 throw OomphLibError(error_stream.str(),
2447 }
2448#endif
2449
2450 // Get the data (if any)
2451 if (count_haloed != 0)
2452 {
2453 halo_hanging[d].resize(count_haloed);
2454 MPI_Recv(&halo_hanging[d][0],
2456 MPI_INT,
2457 d,
2458 0,
2459 Comm_pt->mpi_comm(),
2460 &status);
2461 }
2462 }
2463 else // d==my_rank, i.e. current process: Send halo hanging status
2464 // to process dd where it's received (see above) and compared
2465 // and compared against the hang status of the haloed nodes
2466 {
2467 for (int dd = 0; dd < n_proc; dd++)
2468 {
2469 // No halo with yourself
2470 if (dd != d)
2471 {
2472 // Storage for halo hanging status and counter
2474
2475 // Loop over halo nodes
2476 unsigned nh = nhalo_node(dd);
2477 for (unsigned j = 0; j < nh; j++)
2478 {
2479 // Get node
2481
2482 // Loop over the hanging status for each interpolated variable
2483 // (and the geometry)
2484 for (int icont = -1; icont < ncont_inter_values; icont++)
2485 {
2486 // Store hanging status of halo node
2487 if (nod_pt->is_hanging(icont))
2488 {
2489 unsigned n_master = nod_pt->hanging_pt(icont)->nmaster();
2490 local_halo_hanging.push_back(n_master);
2491 }
2492 else
2493 {
2494 local_halo_hanging.push_back(0);
2495 }
2496 }
2497 }
2498
2499
2500 // Send the information to the relevant process
2501 unsigned count_halo = local_halo_hanging.size();
2502
2503#ifdef PARANOID
2504 // Check that number of halo and haloed data match
2505 MPI_Send(&count_halo, 1, MPI_UNSIGNED, dd, 0, Comm_pt->mpi_comm());
2506#endif
2507
2508 // Send data (if any)
2509 if (count_halo != 0)
2510 {
2512 count_halo,
2513 MPI_INT,
2514 dd,
2515 0,
2516 Comm_pt->mpi_comm());
2517 }
2518 }
2519 }
2520 }
2521 }
2522
2524 {
2526 oomph_info << "Time for first all-to-all in synchronise_hanging_nodes(): "
2527 << t_end - t_start << std::endl;
2529 }
2530
2531
2532 // Now compare equivalent halo and haloed vectors to find discrepancies.
2533 // It is possible that a master node may not be on either process involved
2534 // in the halo-haloed scheme; to work round this, we use the shared_node
2535 // storage scheme, which stores all nodes that are on each pair of
2536 // processors in the same order on each of the two processors
2537
2538 // Vector to store info that will help us locate master nodes on "other"
2539 // processor
2541
2542 // Copy vector-based representation of shared nodes into
2543 // map for faster search
2545 for (int d = 0; d < n_proc; d++)
2546 {
2547 unsigned n = Shared_node_pt[d].size();
2548 for (unsigned jj = 0; jj < n; jj++)
2549 {
2551 }
2552 }
2553
2554
2555 // Loop over domains: Each processor checks consistency of hang status
2556 // of its haloed nodes with proc d against the halo counterpart. Haloed
2557 // wins if there are any discrepancies.
2558 for (int d = 0; d < n_proc; d++)
2559 {
2560 // No halo with yourself
2561 if (d != my_rank)
2562 {
2563 unsigned discrepancy_count = 0;
2564 unsigned discrepancy_count_buff = 0;
2565
2566 // Storage for hanging information that needs to be sent to the
2567 // relevant process if there is a discrepancy in the hanging status
2570 // Buffer storage for data to be sent
2571 //(We need this because we cannot tell until the end of the loop over
2572 // a node's master nodes whether we can reconcile its hanging status)
2575
2576 // Counter for traversing haloed data
2577 unsigned count = 0;
2578
2579 // Indicate presence of discrepancy. Default: there's none
2580 unsigned discrepancy = 0;
2581 unsigned discrepancy_buff = 0;
2582
2583 // Loop over haloed nodes
2584 unsigned nh = nhaloed_node(d);
2585 for (unsigned j = 0; j < nh; j++)
2586 {
2587 // Get node
2588 Node* nod_pt = haloed_node_pt(d, j);
2589
2590 // Loop over the hanging status for each interpolated variable
2591 // (and the geometry)
2592 for (int icont = -1; icont < ncont_inter_values; icont++)
2593 {
2594 // Compare hanging status of halo/haloed counterpart structure
2595
2596 // Haloed is is hanging and haloed has different number
2597 // of master nodes (which includes none in which case it isn't
2598 // hanging)
2599 if ((haloed_hanging[d][count] > 0) &&
2601 {
2602 discrepancy_buff = 1;
2604
2605 // Flag to check if all masters of this node have been found
2606 bool found_all_masters = true;
2607
2608 // Find master nodes of haloed node
2609 HangInfo* hang_pt = nod_pt->hanging_pt(icont);
2610 unsigned nhd_master = hang_pt->nmaster();
2611
2612 // Add the number of master nodes to the hanging_nodes vector
2613 send_data_buff.push_back(nhd_master);
2614
2615 // Loop over master nodes required for HangInfo
2616 for (unsigned m = 0; m < nhd_master; m++)
2617 {
2618 // Get mth master node
2619 Node* master_nod_pt = hang_pt->master_node_pt(m);
2620
2621
2622 // //------------------------------------------
2623 // // Old direct search is much slower!
2624 // // Keep this code alive to allow comparisons
2625 //
2626 // double t_start_search=TimingHelpers::timer();
2627 //
2628 // // This node will be shared: find it!
2629 // bool found=false;
2630 //
2631 // // Look in shared node storage with domain d
2632 // first unsigned nnod_shared=nshared_node(d); for
2633 // (unsigned k=0; k<nnod_shared; k++)
2634 // {
2635 // if (master_nod_pt==shared_node_pt(d,k))
2636 // {
2637 // // Found a master: Put its number in the
2638 // shared
2639 // // scheme into send data
2640 // send_data.push_back(k);
2641 //
2642 // // Add the weight
2643 // send_double_data.push_back(hang_pt->master_weight(m));
2644 //
2645 // // Done
2646 // found=true;
2647 // break;
2648 // }
2649 // }
2650 //
2651 // double t_end_search=TimingHelpers::timer();
2652 // t_start_search_total+=(t_end_search-t_start_search);
2653 //
2654 // // end direct search demo
2655 // //-----------------------
2656
2657
2658 // This node will be shared: find it!
2659 bool found = false;
2660
2661 // Which processor holds the non-halo counterpart of this
2662 // node?
2664
2665 // Try to find node in map with proc d and get iterator to entry
2666 std::map<Node*, unsigned>::iterator it =
2668
2669 // If it's not in there iterator points to end of
2670 // set
2671 if (it != shared_node_map[d].end())
2672 {
2673 // Found a master: When looking up the node in the shared
2674 // node scheme on processor d, which processor do I work
2675 // with? The current one
2676 send_data_buff.push_back(my_rank);
2677
2678 // Found a master: Send its index in the shared
2679 // node scheme
2680 send_data_buff.push_back((*it).second);
2681
2682 // Add the weight
2683 send_double_data_buff.push_back(hang_pt->master_weight(m));
2684
2685 // Done
2686 found = true;
2687 }
2688
2689 // If we haven't found it in the shared node scheme with proc d
2690 // find it in the shared node scheme with the processor that
2691 // holds the non-halo version
2692 if (!found)
2693 {
2694 // This is odd -- can't currently handle the case where
2695 // node is owned by current processor (indicated by
2696 // non_halo_proc_id being negative
2697 if (non_halo_proc_id < 0)
2698 {
2699 // This case is now handled by the function
2700 // additional_synchronise_hanging_nodes()
2701 // called (if necessary) at the end
2702 // OomphLibWarning(
2703 // "Odd: missing master node is owned by current proc. Will
2704 // crash below.",
2705 // "TreeBasedRefineableMeshBase::synchronise_hanging_nodes(...)",
2706 // OOMPH_EXCEPTION_LOCATION);
2707 }
2708 else // i.e. (non_halo_proc_id>=0)
2709 {
2710 if (shared_node_map[non_halo_proc_id].size() > 0)
2711 {
2712 std::map<Node*, unsigned>::iterator it =
2714
2715 // If it's not in there iterator points to end of
2716 // set
2718 {
2719 // Found a master: Send ID of processor that holds
2720 // non-halo (the fact that this is different from
2721 // my_rank (the processor that sends this) will alert
2722 // the other processor to the fact that it needs to
2724
2725 // Found a master: Send its index in the shared
2726 // node scheme
2727 send_data_buff.push_back((*it).second);
2728
2729 // Add the weight
2730 send_double_data_buff.push_back(
2731 hang_pt->master_weight(m));
2732
2733 // Done
2734 found = true;
2735
2736 // It is possible that the master node found in the
2737 // shared storage with processor <non_halo_proc_id> does
2738 // not actually exist on processor d. If it does turn
2739 // out to exist, we are ok, but if not then we must
2740 // remember to create it later (in the
2741 // additional_synchronise_hanging_nodes() function)
2742 }
2743 }
2744 }
2745 }
2746
2747 /*
2748 // Don't throw error, we will construct missing master nodes in
2749 // additional_synchronise_hanging_nodes() below
2750
2751 // Paranoid check: if we haven't found the master node
2752 // then throw an error
2753 if (!found)
2754 {
2755 char filename[100];
2756 std::ofstream some_file;
2757 snprintf(filename, sizeof(filename),
2758 "sync_hanging_node_crash_mesh_proc%i.dat", my_rank);
2759 some_file.open(filename);
2760 this->output(some_file);
2761 some_file.close();
2762
2763 snprintf(filename, sizeof(filename),
2764 "sync_hanging_node_crash_mesh_with_haloes_proc%i.dat",
2765 my_rank);
2766 some_file.open(filename);
2767 this->enable_output_of_halo_elements();
2768 this->output(some_file);
2769 this->disable_output_of_halo_elements();
2770 some_file.close();
2771
2772
2773 std::set<unsigned> other_proc_id;
2774 other_proc_id.insert(d);
2775 other_proc_id.insert(non_halo_proc_id);
2776 for (std::set<unsigned>::iterator it=other_proc_id.begin();
2777 it!=other_proc_id.end();it++)
2778 {
2779 unsigned d_doc=(*it);
2780
2781 sprintf(
2782 filename,
2783 "sync_hanging_node_crash_halo_elements_with_proc%i_proc%i.dat",
2784 d_doc,my_rank);
2785 some_file.open(filename);
2786 Vector<GeneralisedElement*>
2787 halo_elem_pt(this->halo_element_pt(d_doc));
2788 unsigned nelem=halo_elem_pt.size();
2789 for (unsigned e=0;e<nelem;e++)
2790 {
2791 FiniteElement* el_pt=
2792 dynamic_cast<FiniteElement*>(halo_elem_pt[e]);
2793 el_pt->output(some_file);
2794 }
2795 some_file.close();
2796
2797 sprintf(
2798 filename,
2799 "sync_hanging_node_crash_haloed_elements_with_proc%i_proc%i.dat",
2800 d_doc,my_rank);
2801 some_file.open(filename);
2802 Vector<GeneralisedElement*>
2803 haloed_elem_pt(this->haloed_element_pt(d_doc));
2804 nelem=haloed_elem_pt.size();
2805 for (unsigned e=0;e<nelem;e++)
2806 {
2807 FiniteElement* el_pt=
2808 dynamic_cast<FiniteElement*>(haloed_elem_pt[e]);
2809 el_pt->output(some_file);
2810 }
2811 some_file.close();
2812
2813
2814 sprintf(
2815 filename,
2816 "sync_hanging_node_crash_shared_nodes_with_proc%i_proc%i.dat",
2817 d_doc,my_rank);
2818 some_file.open(filename);
2819 unsigned n=nshared_node(d_doc);
2820 for (unsigned j=0;j<n;j++)
2821 {
2822 Node* nod_pt=shared_node_pt(d_doc,j);
2823 unsigned nd=nod_pt->ndim();
2824 for (unsigned i=0;i<nd;i++)
2825 {
2826 some_file << nod_pt->x(i) << " ";
2827 }
2828 some_file << "\n";
2829 }
2830 some_file.close();
2831
2832
2833 sprintf(
2834 filename,
2835 "sync_hanging_node_crash_halo_nodes_with_proc%i_proc%i.dat",
2836 d_doc,my_rank);
2837 some_file.open(filename);
2838 n=nhalo_node(d_doc);
2839 for (unsigned j=0;j<n;j++)
2840 {
2841 Node* nod_pt=halo_node_pt(d_doc,j);
2842 unsigned nd=nod_pt->ndim();
2843 for (unsigned i=0;i<nd;i++)
2844 {
2845 some_file << nod_pt->x(i) << " ";
2846 }
2847 some_file << "\n";
2848 }
2849 some_file.close();
2850
2851
2852 sprintf(
2853 filename,
2854 "sync_hanging_node_crash_haloed_nodes_with_proc%i_proc%i.dat",
2855 d_doc,my_rank);
2856 some_file.open(filename);
2857 n=nhaloed_node(d_doc);
2858 for (unsigned j=0;j<n;j++)
2859 {
2860 Node* nod_pt=haloed_node_pt(d_doc,j);
2861 unsigned nd=nod_pt->ndim();
2862 for (unsigned i=0;i<nd;i++)
2863 {
2864 some_file << nod_pt->x(i) << " ";
2865 }
2866 some_file << "\n";
2867 }
2868 some_file.close();
2869
2870 } // end of loop over all inter-processor lookup schemes
2871
2872 std::ostringstream error_stream;
2873 unsigned n=master_nod_pt->ndim();
2874 error_stream << "Error: Master node at:\n\n";
2875 for (unsigned i=0;i<n;i++)
2876 {
2877 error_stream << master_nod_pt->x(i) << " ";
2878 }
2879 error_stream << "\n\nnot found for icont="
2880 << icont << "in "
2881 << "shared node storage with proc " << d <<
2882 "\n"
2883 << "or in shared node storage with proc "
2884 << non_halo_proc_id
2885 << " which is where its non-halo counterpart
2886 lives.\n"
2887 << "Relevant files:
2888 sync_hanging_node_crash*.dat\n\n"
2889 << "Hanging node itself: \n\n";
2890 n=nod_pt->ndim();
2891 for (unsigned i=0;i<n;i++)
2892 {
2893 error_stream << nod_pt->x(i) << " ";
2894 }
2895 error_stream << nod_pt->non_halo_proc_ID();
2896 error_stream << "\n\nMaster nodes:\n\n";
2897 for (unsigned m=0; m<nhd_master; m++)
2898 {
2899 Node* master_nod_pt=hang_pt->master_node_pt(m);
2900 n=master_nod_pt->ndim();
2901 for (unsigned i=0;i<n;i++)
2902 {
2903 error_stream << master_nod_pt->x(i) << " ";
2904 }
2905 error_stream << master_nod_pt->non_halo_proc_ID();
2906 error_stream << "\n";
2907 }
2908
2909 // try to find it somewhere else -- sub-optimal search but
2910 // we're about to die on our arses (yes, plural -- this is
2911 // a parallel run!) anyway...
2912 for (int dddd=0;dddd<n_proc;dddd++)
2913 {
2914 bool loc_found=false;
2915 unsigned nnnod_shared=nshared_node(dddd);
2916 for (unsigned k=0; k<nnnod_shared; k++)
2917 {
2918 if (master_nod_pt==shared_node_pt(dddd,k))
2919 {
2920 loc_found=true;
2921 error_stream
2922 << "Found that master node as " << k
2923 << "-th entry in shared node storage with proc "
2924 << dddd << "\n";
2925 }
2926 }
2927 if (!loc_found)
2928 {
2929 error_stream
2930 << "Did not find that master node in shared node storage
2931 with proc "
2932 << dddd << "\n";
2933 }
2934 }
2935 error_stream << "\n\n";
2936
2937 throw OomphLibError(
2938 error_stream.str(),
2939 OOMPH_CURRENT_FUNCTION,
2940 OOMPH_EXCEPTION_LOCATION);
2941 }
2942 */
2943
2944 // Check if the master has been found
2945 if (!found)
2946 {
2947 // If this master hasn't been found then set the flag
2948 found_all_masters = false;
2949 // No need to continue searching for masters
2950 break;
2951 }
2952
2953
2954 } // loop over master nodes
2955
2956
2957 // Check if we need to send the data
2959 {
2960 // All masters were found, so populate send data from buffer
2963 for (unsigned i = 0; i < send_data_buff.size(); i++)
2964 {
2965 send_data.push_back(send_data_buff[i]);
2966 }
2967 for (unsigned i = 0; i < send_double_data_buff.size(); i++)
2968 {
2970 }
2971
2972 // Clear buffers and reset
2973 discrepancy_buff = 0;
2975 send_data_buff.clear();
2976 send_double_data_buff.clear();
2977 }
2978 else
2979 {
2980 // At least one master node was not found, so we can't
2981 // reconcile the hanging status of this node yet. We tell
2982 // the other processor to do nothing for now.
2983 send_data.push_back(0);
2984
2985 // Clear buffers and reset
2986 discrepancy_buff = 0;
2988 send_data_buff.clear();
2989 send_double_data_buff.clear();
2990
2991 // Set flag to trigger another round of synchronisation
2993 }
2994 }
2995 // Haloed node isn't hanging but halo is: the latter
2996 // shouldn't so send a -1 to indicate that it's to be made
2997 // non-hanging
2998 else if ((haloed_hanging[d][count] == 0) &&
2999 (halo_hanging[d][count] > 0))
3000 {
3001 discrepancy = 1;
3003 send_data.push_back(-1);
3004 }
3005 // Both halo and haloed node have the same number of masters
3006 // we're happy!
3007 else if (haloed_hanging[d][count] == halo_hanging[d][count])
3008 {
3009 send_data.push_back(0);
3010 }
3011 else
3012 {
3013 std::ostringstream error_stream;
3014 error_stream << "Never get here!\n "
3015 << "haloed_hanging[d][count]="
3016 << haloed_hanging[d][count]
3017 << "; halo_hanging[d][count]="
3018 << halo_hanging[d][count] << std::endl;
3019 throw OomphLibError(error_stream.str(),
3022 }
3023 // Increment counter for number of haloed data
3024 count++;
3025 } // end of loop over icont
3026 } // end of loop over haloed nodes
3027
3028 // Now send all the required info to the equivalent halo layer -
3029 // If there are no discrepancies, no need to send anything
3031 if (discrepancy == 0)
3032 {
3033 MPI_Send(&n_all_send[0], 2, MPI_UNSIGNED, d, 0, Comm_pt->mpi_comm());
3034 }
3035 else
3036 {
3037 // How much data is there to be sent?
3038 n_all_send[0] = send_data.size();
3040 MPI_Send(&n_all_send[0], 2, MPI_UNSIGNED, d, 0, Comm_pt->mpi_comm());
3041
3042 // Send flat-packed ints
3043 if (n_all_send[0] != 0)
3044 {
3045 MPI_Send(
3046 &send_data[0], n_all_send[0], MPI_INT, d, 1, Comm_pt->mpi_comm());
3047 }
3048
3049 // Send flat-packed double data
3050 if (n_all_send[1] != 0)
3051 {
3053 n_all_send[1],
3054 MPI_DOUBLE,
3055 d,
3056 1,
3057 Comm_pt->mpi_comm());
3058 }
3059 }
3060 }
3061 else // (d==my_rank), current process
3062 {
3063 // Receive the master nodes and weights in order to modify the
3064 // hanging status of nodes in the halo layer
3065 for (int dd = 0; dd < n_proc; dd++)
3066 {
3067 if (dd != d) // don't talk to yourself
3068 {
3069 // Are we going to receive anything? This is zero
3070 // either if there are no discrepancies or there's zero
3071 // data to be sent.
3073 MPI_Recv(&n_all_recv[0],
3074 2,
3076 dd,
3077 0,
3078 Comm_pt->mpi_comm(),
3079 &status);
3080
3081 // Storage for received information
3084
3085 // Receive unsigneds (if any)
3086 if (n_all_recv[0] != 0)
3087 {
3088 // Receive the data
3089 receive_data.resize(n_all_recv[0]);
3091 n_all_recv[0],
3092 MPI_INT,
3093 dd,
3094 1,
3095 Comm_pt->mpi_comm(),
3096 &status);
3097 }
3098
3099 // Receive doubles (if any)
3100 if (n_all_recv[1] != 0)
3101 {
3102 // Receive the data
3105 n_all_recv[1],
3106 MPI_DOUBLE,
3107 dd,
3108 1,
3109 Comm_pt->mpi_comm(),
3110 &status);
3111 }
3112
3113 // If no information, no need to do anything else
3114 if (n_all_recv[0] != 0)
3115 {
3116 // Counters for traversing received data
3117 unsigned count = 0;
3118 unsigned count_double = 0;
3119
3120 // Loop over halo nodes
3121 unsigned nh = nhalo_node(dd);
3122 for (unsigned j = 0; j < nh; j++)
3123 {
3124 // Get node
3126
3127 // Loop over the hanging status for each interpolated variable
3128 // (and the geometry)
3129 for (int icont = -1; icont < ncont_inter_values; icont++)
3130 {
3131 // Read next entry
3132 int next_entry = receive_data[count++];
3133
3134 // If it's positive, then the number tells us how
3135 // many master nodes we have
3136 if (next_entry > 0)
3137 {
3138 unsigned nhd_master = unsigned(next_entry);
3139
3140 // Set up a new HangInfo for this node
3142
3143 // Now set up the master nodes and weights
3144 for (unsigned m = 0; m < nhd_master; m++)
3145 {
3146 // Get the sent master node (a shared node) and
3147 // the weight
3148
3149 // ID of proc in whose shared node lookup scheme
3150 // the sending processor found the node
3151 unsigned shared_node_proc =
3153
3154 // Index of node in the shared node lookup scheme
3156
3157 // Get weight
3159
3160 // If the shared node processor is the same as the
3161 // the sending processor we can processor everything here
3162 if (shared_node_proc == unsigned(dd))
3163 {
3164 // Get node
3167
3168 // Set as a master node (with corresponding weight)
3169 hang_pt->set_master_node_pt(
3171 }
3172 // ...otherwise we have do another communication with
3173 // intermediate processor that holds the non-halo
3174 // version of the master node -- only that processor can
3175 // translate the index of the node the share node
3176 // lookup scheme with the sending processor to the
3177 // index in the shared node lookup scheme with this
3178 // processor
3179 else
3180 {
3181 // Store
3183 tmp.Sending_processor = dd;
3184 tmp.Shared_node_id_on_sending_processor =
3186 tmp.Shared_node_proc = shared_node_proc;
3187 tmp.Weight = mtr_weight;
3188 tmp.Hang_pt = hang_pt;
3189 tmp.Master_node_index = m;
3190 tmp.Node_pt = nod_pt;
3191 tmp.icont = icont;
3192 hang_info.push_back(tmp);
3193 }
3194 }
3195
3196 // Set the hanging pointer for the current halo node
3197 // (does delete any previous hang data)
3198 nod_pt->set_hanging_pt(hang_pt, icont);
3199 }
3200 // Negative entry: the hanging node already exists,
3201 // but it shouldn't, so set it to nonhanging
3202 else if (next_entry < 0)
3203 {
3204 nod_pt->set_hanging_pt(0, icont);
3205 }
3206
3207 } // end of loop over icont
3208 } // end of loop over nodes
3209 } // end of anything to receive
3210 }
3211 }
3212 }
3213 } // end loop over all processors
3214
3215
3217 {
3219 oomph_info << "Time for second all-to-all in synchronise_hanging_nodes() "
3220 << t_end - t_start << std::endl;
3222 }
3223
3224
3225 // Now identify master nodes by translating index in shared
3226 // node lookup scheme from the lookup scheme with the sending
3227 // processor to that with the current processor
3228 unsigned n = hang_info.size();
3229
3230 // Is there anything to do be done?
3231 unsigned global_n = 0;
3232 MPI_Allreduce(&n, &global_n, 1, MPI_UNSIGNED, MPI_MAX, Comm_pt->mpi_comm());
3233 if (global_n == 0)
3234 {
3236 << "No need for reconciliation of wrongly synchronised hang nodes\n";
3237 }
3238 // Reconcilation required
3239 else
3240 {
3241 oomph_info << "Need to reconcile of wrongly syncronised hang nodes\n";
3242
3243 // Storage for the translated entries (in order) for/from
3244 // other processors
3245 // Must be ints so that an entry of -1 tells the other processor
3246 // that the node could not be found. See comment above for why
3247 // this may be necessary.
3249
3250 // Storage for how-many-th entry in this processor's
3251 // hang_info vector will be completed by processor rank.
3253
3254 // Send information to intermediate processor that holds
3255 // non-halo version of missing master node
3256 {
3257 // Storage for number of data to be sent to each processor
3259
3260 // Storage for all values to be sent to all processors
3262
3263 // Start location within send_data for data to be sent to each processor
3265
3266 // Loop over all processors
3267 for (int rank = 0; rank < n_proc; rank++)
3268 {
3269 // Set the offset for the current processor
3271
3272 // Don't bother to do anything if the processor in the loop is the
3273 // current processor
3274 if (rank != my_rank)
3275 {
3276 // Search through the (typically few) entries
3277 // in hang info vector to find the ones that
3278 // must be sent to proc rank (the proc that holds
3279 // the non-halo version of the "missing" master node
3280 for (unsigned i = 0; i < n; i++)
3281 {
3283 if (tmp.Shared_node_proc == unsigned(rank))
3284 {
3285 // Add the sending processor
3286 send_data.push_back(tmp.Sending_processor);
3287
3288 // Add the index of the missing master node
3289 // in the shared node lookup scheme between
3290 // sending processor and processor rank
3291 send_data.push_back(tmp.Shared_node_id_on_sending_processor);
3292
3293 // Record the how-many-th entry in this processor's
3294 // hang_info vector will be completed by processor rank.
3295 hang_info_index_for_proc[rank].push_back(i);
3296 }
3297 }
3298 }
3299
3300 // Find the number of data added to the vector
3302 }
3303
3304
3305 // Storage for the number of data to be received from each processor
3307
3308 // Now send numbers of data to be sent between all processors
3309 MPI_Alltoall(&send_n[0],
3310 1,
3311 MPI_INT,
3312 &receive_n[0],
3313 1,
3314 MPI_INT,
3315 Comm_pt->mpi_comm());
3316
3317 // We now prepare the data to be received
3318 // by working out the displacements from the received data
3320 int receive_data_count = 0;
3321 for (int rank = 0; rank < n_proc; ++rank)
3322 {
3323 // Displacement is number of data received so far
3326 }
3327
3328 // Now resize the receive buffer for all data from all processors
3329 // Make sure that it has a size of at least one
3330 if (receive_data_count == 0)
3331 {
3333 }
3335
3336 // Make sure that the send buffer has size at least one
3337 // so that we don't get a segmentation fault
3338 if (send_data.size() == 0)
3339 {
3340 send_data.resize(1);
3341 }
3342
3343 // Now send the data between all the processors
3345 &send_n[0],
3347 MPI_INT,
3348 &receive_data[0],
3349 &receive_n[0],
3351 MPI_INT,
3352 Comm_pt->mpi_comm());
3353
3354 // Now use the received data to update the halo nodes
3355 for (int send_rank = 0; send_rank < n_proc; send_rank++)
3356 {
3357 // Don't bother to do anything for the processor corresponding to the
3358 // current processor or if no data were received from this processor
3359 if ((send_rank != my_rank) && (receive_n[send_rank] != 0))
3360 {
3361 // Counter for the data within the large array
3363
3364 // We're reading two numbers per missing halo node
3365 unsigned n_rec = unsigned(receive_n[send_rank]);
3366 for (unsigned i = 0; i < n_rec / 2; i++)
3367 {
3368 // Receive orig sending proc
3370 count++;
3371
3372 // Receive the index of the missing master node
3373 // in the shared node lookup scheme between
3374 // orig sending processor and current processor
3377 count++;
3378
3379 // Extract node from shared node lookup scheme
3382
3383 // Now find it in shared halo scheme with the processor
3384 // that's sent the request
3385
3386 std::map<Node*, unsigned>::iterator it =
3388
3389 // If it's not in there iterator points to end of
3390 // set
3391 if (it != shared_node_map[send_rank].end())
3392 {
3393 // Store it so we can send it back
3394 translated_entry[send_rank].push_back((*it).second);
3395 }
3396 else
3397 {
3398 // This node has not been found in the shared scheme, so
3399 // the translation query has failed. We send a -1 to tell
3400 // the other processor the bad news.
3401 translated_entry[send_rank].push_back(-1);
3402
3403 /*
3404 // We don't need to crash anymore because the function
3405 // additional_synchronise_hanging_nodes() will magically
3406 // sort out all the problems!
3407 std::ostringstream error_stream;
3408 error_stream
3409 << "Received translation query for shared node"
3410 << " entry " << shared_node_id_on_orig_sending_proc
3411 << " with processor " << orig_sending_proc
3412 << " from proc " << send_rank << std::endl
3413 << "but did not find node in shared node scheme with proc "
3414 << send_rank << std::endl;
3415 throw OomphLibError(
3416 error_stream.str(),
3417 OOMPH_CURRENT_FUNCTION,
3418 OOMPH_EXCEPTION_LOCATION);
3419 */
3420 }
3421 }
3422 }
3423 } // End of data is received
3424
3425 } // end of sending stuff to intermediate processor that holds
3426 // non halo version of missing master node
3427
3428
3430 {
3433 << "Time for third all-to-all in synchronise_hanging_nodes() "
3434 << t_end - t_start << std::endl;
3436 }
3437
3438
3439 // Send information back to processor that needs to identify
3440 // missing master node via shared node lookup scheme with
3441 // this processor
3442 {
3443 // Storage for number of data to be sent to each processor
3445
3446 // Storage for all values to be sent to all processors
3448
3449 // Start location within send_data for data to be sent to each processor
3451
3452 // Loop over all processors
3453 for (int rank = 0; rank < n_proc; rank++)
3454 {
3455 // Set the offset for the current processor
3457
3458 // Don't bother to do anything if the processor in the loop is the
3459 // current processor
3460 if (rank != my_rank)
3461 {
3462 // Put the translated entries for processor rank into
3463 // send data
3464 unsigned n = translated_entry[rank].size();
3465 for (unsigned j = 0; j < n; j++)
3466 {
3467 send_data.push_back(translated_entry[rank][j]);
3468 }
3469 }
3470
3471 // Find the number of data added to the vector
3473 }
3474
3475
3476 // Storage for the number of data to be received from each processor
3478
3479 // Now send numbers of data to be sent between all processors
3480 MPI_Alltoall(&send_n[0],
3481 1,
3482 MPI_INT,
3483 &receive_n[0],
3484 1,
3485 MPI_INT,
3486 Comm_pt->mpi_comm());
3487
3488 // We now prepare the data to be received
3489 // by working out the displacements from the received data
3491 int receive_data_count = 0;
3492 for (int rank = 0; rank < n_proc; ++rank)
3493 {
3494 // Displacement is number of data received so far
3497 }
3498
3499 // Now resize the receive buffer for all data from all processors
3500 // Make sure that it has a size of at least one
3501 if (receive_data_count == 0)
3502 {
3504 }
3506
3507 // Make sure that the send buffer has size at least one
3508 // so that we don't get a segmentation fault
3509 if (send_data.size() == 0)
3510 {
3511 send_data.resize(1);
3512 }
3513
3514 // Now send the data between all the processors
3516 &send_n[0],
3518 MPI_INT,
3519 &receive_data[0],
3520 &receive_n[0],
3522 MPI_INT,
3523 Comm_pt->mpi_comm());
3524
3525 // Now use the received data to update the halo nodes
3526 for (int send_rank = 0; send_rank < n_proc; send_rank++)
3527 {
3528 // Don't bother to do anything for the processor corresponding to the
3529 // current processor or if no data were received from this processor
3530 if ((send_rank != my_rank) && (receive_n[send_rank] != 0))
3531 {
3532 // Counter for the data within the large array
3534
3535 // We're reading one number per missing halo node
3536 unsigned n_rec = unsigned(receive_n[send_rank]);
3537 for (unsigned i = 0; i < n_rec; i++)
3538 {
3539 // Index of missing master node in shared node lookup scheme
3540 // with processor send_rank:
3541 // Must be an int because failure returns -1
3542 int index = receive_data[count];
3543 count++;
3544
3545 // Translation query has been successful if index >= 0
3546 if (index >= 0)
3547 {
3548 // Recall information associated with that missing master
3549 unsigned hang_info_index =
3552
3553 // Extract node from shared node lookup scheme
3555
3556 // Set as a master node (with corresponding weight)
3557 tmp.Hang_pt->set_master_node_pt(
3558 tmp.Master_node_index, master_nod_pt, tmp.Weight);
3559 }
3560 else
3561 {
3562 // Translation query has failed. This is the processor
3563 // on which the node was a halo, so we must delete the
3564 // partial hang info.
3565
3566 // Recall information associated with that missing master
3567 unsigned hang_info_index =
3570
3571 // Delete partial hanging information
3572 tmp.Node_pt->set_hanging_pt(0, tmp.icont);
3573
3574 // Set flag to trigger another round of synchronisation
3575 // This works even though we don't own the node that
3576 // still requires synchrionisation because this variable
3577 // is reduced over all processors at the end
3579 }
3580 }
3581 }
3582 } // End of data is received
3583
3584 } // end of completing hang info for missing master nodes
3585
3586
3588 {
3591 << "Time for fourth all-to-all in synchronise_hanging_nodes() "
3592 << t_end - t_start << std::endl;
3593 }
3594 } // end of reconciliation required
3595
3596
3597 // Get global number of nodes still requiring synchronisation due to
3598 // missing master nodes
3599 // This will only be necessary for meshes involving elements
3600 // with nonuniformly spaced nodes. All other cases will continue to
3601 // work as before because all nodes will have been successfully
3602 // synchronised by now
3606 1,
3608 MPI_MAX,
3609 Comm_pt->mpi_comm());
3611 {
3612 double tt_start = 0.0;
3614 {
3616 }
3617
3618 oomph_info << "Need to do additional synchronisation of hanging nodes"
3619 << std::endl;
3620
3621 // Do additional synchronisation
3622 additional_synchronise_hanging_nodes(ncont_interpolated_values);
3623
3624 double tt_end = 0.0;
3626 {
3629 << "Time for RefineableMesh::additional_synchronise_hanging_nodes() "
3630 << "in TreeBasedRefineableMeshBase::synchronise_hanging_nodes(): "
3631 << tt_end - tt_start << std::endl;
3633 }
3634 }
3635 else
3636 {
3637 oomph_info << "No need to do additional synchronisation of hanging nodes"
3638 << std::endl;
3639 }
3640 }
3641
3642 //========================================================================
3643 /// Synchronise the positions of non-hanging nodes that depend on
3644 /// non-existent neighbours (e.g. h-refinement of neighbouring elements
3645 /// with different p-orders where the shared edge is on the outer edge of
3646 /// the halo layer)
3647 //========================================================================
3649 {
3650 // Store number of processors and current process
3652 int n_proc = Comm_pt->nproc();
3653 int my_rank = Comm_pt->my_rank();
3654
3655 double t_start = 0.0;
3656 double t_end = 0.0;
3657
3658 // Storage for the hanging status of halo/haloed nodes on elements
3661
3663 {
3665 }
3666
3667 // Loop over processes: Each processor checks if its nonhaning nodes in
3668 // haloed elements with proc d require additional information to determine
3669 // their positions.
3670 for (int d = 0; d < n_proc; d++)
3671 {
3672 // No halo with self: Setup hang info for my haloed nodes with proc d
3673 // then get ready to receive halo info from processor d.
3674 if (d != my_rank)
3675 {
3676 // Receive the position information from the corresponding process
3677 unsigned recv_unsigneds_count = 0;
3679 1,
3681 d,
3682 0,
3683 Comm_pt->mpi_comm(),
3684 &status);
3685 unsigned recv_doubles_count = 0;
3687 1,
3689 d,
3690 1,
3691 Comm_pt->mpi_comm(),
3692 &status);
3693
3694 // Get the data (if any)
3695 if (recv_unsigneds_count != 0)
3696 {
3698 MPI_Recv(&recv_unsigneds[d][0],
3701 d,
3702 0,
3703 Comm_pt->mpi_comm(),
3704 &status);
3705 }
3706 if (recv_doubles_count != 0)
3707 {
3709 MPI_Recv(&recv_doubles[d][0],
3711 MPI_DOUBLE,
3712 d,
3713 1,
3714 Comm_pt->mpi_comm(),
3715 &status);
3716 }
3717
3718 // Counters for received data
3719 unsigned recv_unsigneds_index = 0;
3720 double recv_doubles_index = 0;
3721
3722 // Get halo elements with processor d
3724
3725 // Loop over recieved indices
3727 {
3728 // Get (finite) element
3729 FiniteElement* el_pt = dynamic_cast<FiniteElement*>(
3731
3732 // If we have a finite element...
3733 if (el_pt != 0)
3734 {
3735 // Get dimension
3736 unsigned n_dim = el_pt->dim();
3737
3738 // Get node
3739 Node* nod_pt =
3741
3742 // Get current position
3744 for (unsigned dir = 0; dir < n_dim; dir++)
3745 {
3746 x_cur[dir] = nod_pt->x(dir);
3747 }
3748
3749 // Get recieved position
3751 for (unsigned dir = 0; dir < n_dim; dir++)
3752 {
3754 }
3755
3756 // Compare actual and expected positions
3757 bool node_pos_differs = false;
3758 for (unsigned dir = 0; dir < n_dim; dir++)
3759 {
3761 (std::fabs(x_cur[dir] - x_rec[dir]) > 1.0e-14);
3762 }
3763
3764 // Set the actual position
3766 for (unsigned dir = 0; dir < n_dim; dir++)
3767 {
3769 }
3770 }
3771 }
3772
3774 {
3775 std::ostringstream error_stream;
3776 error_stream << "recv_unsigneds_count != recv_unsigneds_index ( "
3778 << ")" << std::endl;
3779 throw OomphLibError(
3780 error_stream.str(),
3781 "TreeBasedRefineableMeshBase::synchronise_nonhanging_nodes()",
3783 }
3784 }
3785 else // d==my_rank, i.e. current process: Send halo hanging status
3786 // to process dd where it's received (see above) and compared
3787 // and compared against the hang status of the haloed nodes
3788 {
3789 for (int dd = 0; dd < n_proc; dd++)
3790 {
3791 // No halo with yourself
3792 if (dd != d)
3793 {
3794 // Storage for halo hanging status and counter
3797
3798 // Set to store nodes whose position requires adjustment
3799 std::set<Node*> nodes_requiring_adjustment;
3800
3801 // Get haloed elements with processor dd
3803 this->haloed_element_pt(dd));
3804
3805 // Loop over haloed elements with processor dd
3806 unsigned nh = haloed_element_pt.size();
3807 for (unsigned e = 0; e < nh; e++)
3808 {
3809 // Get (finite) element
3811 dynamic_cast<FiniteElement*>(haloed_element_pt[e]);
3812
3813 // If we have a finite element...
3814 if (el_pt != 0)
3815 {
3816 // Get dimension
3817 unsigned n_dim = el_pt->dim();
3818
3819 // Loop over element nodes
3820 unsigned n_node = el_pt->nnode();
3821 for (unsigned j = 0; j < n_node; j++)
3822 {
3823 // Get node
3824 Node* nod_pt = el_pt->node_pt(j);
3825
3826 // Only do non-hanging nodes
3827 if (!nod_pt->is_hanging())
3828 {
3829 // Check if node's position is the same as that interpolated
3830 // using its local coordinate in the haloed element
3831
3832 // Loop over all history values
3833 unsigned nt = nod_pt->ntstorage();
3834 for (unsigned t = 0; t < nt; t++)
3835 {
3836 // Get expected position
3839 el_pt->get_x(t, s, x_exp);
3840
3841 // Get actual position
3843 for (unsigned dir = 0; dir < n_dim; dir++)
3844 {
3845 x_act[dir] = nod_pt->x(dir);
3846 }
3847
3848 // Compare actual and expected positions
3849 bool node_pos_differs = false;
3850 for (unsigned dir = 0; dir < n_dim; dir++)
3851 {
3854 (std::fabs(x_act[dir] - x_exp[dir]) > 1.0e-14);
3855 }
3856
3857 // If the node's actual position differs from its
3858 // expected position we need to communicate this
3859 // information to processors on which this is a halo node
3860 if (node_pos_differs)
3861 {
3862 // Check that node has not been done already
3863 if (nodes_requiring_adjustment.insert(nod_pt).second)
3864 {
3865 // Send index of haloed element
3866 send_unsigneds.push_back(e);
3867 // Send index of node in the element
3868 send_unsigneds.push_back(j);
3869 // Send actual position of node
3870 for (unsigned dir = 0; dir < n_dim; dir++)
3871 {
3872 send_doubles.push_back(x_act[dir]);
3873 }
3874 }
3875 }
3876 }
3877 }
3878 }
3879 }
3880 }
3881
3882 // Send the information to the relevant process
3885
3886 if (send_unsigneds_count > 0)
3887 {
3888 // exit(1);
3889 }
3890
3891 // Tell processor dd how much data to receive
3893 1,
3895 dd,
3896 0,
3897 Comm_pt->mpi_comm());
3898 MPI_Send(
3899 &send_doubles_count, 1, MPI_UNSIGNED, dd, 1, Comm_pt->mpi_comm());
3900
3901 // Send data (if any)
3902 if (send_unsigneds_count != 0)
3903 {
3907 dd,
3908 0,
3909 Comm_pt->mpi_comm());
3910 }
3911 if (send_doubles_count != 0)
3912 {
3915 MPI_DOUBLE,
3916 dd,
3917 1,
3918 Comm_pt->mpi_comm());
3919 }
3920 }
3921 }
3922 }
3923 }
3924
3926 {
3928 oomph_info << "Time for synchronise_nonhanging_nodes(): "
3929 << t_end - t_start << std::endl;
3931 }
3932 }
3933
3934#endif
3935
3936
3937 ////////////////////////////////////////////////////////////////////
3938 ////////////////////////////////////////////////////////////////////
3939 ////////////////////////////////////////////////////////////////////
3940
3941
3942 //========================================================================
3943 /// Do adaptive p-refinement for mesh.
3944 /// - Pass Vector of error estimates for all elements.
3945 /// - p-refine those whose errors exceeds the threshold
3946 /// - p-unrefine those whose errors is less than
3947 /// threshold.
3948 //========================================================================
3951 {
3952 // Set the refinement tolerance to be the max permissible error
3953 double refine_tol = this->max_permitted_error();
3954
3955 // Set the unrefinement tolerance to be the min permissible error
3956 double unrefine_tol = this->min_permitted_error();
3957
3958 // Setup doc info
3960 if (doc_info_pt() == 0)
3961 {
3962 local_doc_info.disable_doc();
3963 }
3964 else
3965 {
3966 local_doc_info = this->doc_info();
3967 }
3968
3969
3970 // Check that the errors make sense
3971 if (refine_tol <= unrefine_tol)
3972 {
3973 std::ostringstream error_stream;
3974 error_stream << "Refinement tolerance <= Unrefinement tolerance"
3975 << refine_tol << " " << unrefine_tol << std::endl
3976 << "doesn't make sense and will almost certainly crash"
3977 << std::endl
3978 << "this beautiful code!" << std::endl;
3979
3980 throw OomphLibError(
3982 }
3983
3984
3985 // Select elements for refinement and unrefinement
3986 //==============================================
3987 // Reset counter for number of elements that would like to be
3988 // refined further but can't
3989 this->nrefinement_overruled() = 0;
3990
3991 unsigned n_refine = 0;
3992 unsigned n_unrefine = 0;
3993 // Loop over all elements and mark them according to the error criterion
3994 unsigned long Nelement = this->nelement();
3995 for (unsigned long e = 0; e < Nelement; e++)
3996 {
3997 //(Cast) pointer to the element
3999 dynamic_cast<PRefineableElement*>(this->element_pt(e));
4000
4001 // Check that we can p-refine the element
4002 if (el_pt != 0)
4003 {
4004 // Initially element is not to be refined
4005 el_pt->deselect_for_p_refinement();
4006 el_pt->deselect_for_p_unrefinement();
4007
4008 // If the element error exceeds the threshold ...
4010 {
4011 // ... and its refinement level is less than the maximum desired level
4012 // mark is to be refined
4013 if ((el_pt->p_refinement_is_enabled()) &&
4014 (el_pt->p_order() < this->max_p_refinement_level()))
4015 {
4016 el_pt->select_for_p_refinement();
4017 n_refine++;
4018 }
4019 // ... otherwise mark it as having been over-ruled
4020 else
4021 {
4022 this->nrefinement_overruled() += 1;
4023 }
4024 }
4025 if (elemental_error[e] < unrefine_tol)
4026 {
4027 // ... and its refinement level is more than the minimum desired level
4028 // mark is to be refined
4029 //(Also check that we don't unrefine past the initial refinement
4030 // level)
4031 if ((el_pt->p_refinement_is_enabled()) &&
4032 (el_pt->p_order() > this->min_p_refinement_level()) &&
4033 (el_pt->p_order() > el_pt->initial_p_order()))
4034 {
4035 el_pt->select_for_p_unrefinement();
4036 n_unrefine++;
4037 }
4038 // Don't mark as overruled - it's misleading
4039 /// / ... otherwise mark it as having been over-ruled
4040 // else
4041 // {
4042 // this->nrefinement_overruled()+=1;
4043 // }
4044 }
4045 } // End of check for p-refineability of element
4046 else
4047 {
4048 oomph_info << "p-refinement is not possible for these elements"
4049 << std::endl;
4050 // Don't try to p-refine any more elements
4051 break;
4052 }
4053 }
4054
4055 oomph_info << " \n Number of elements to be refined: " << n_refine
4056 << std::endl;
4057 oomph_info << " \n Number of elements whose refinement was overruled: "
4058 << this->nrefinement_overruled() << std::endl;
4059
4060 oomph_info << " \n Number of elements to be unrefined : " << n_unrefine
4061 << std::endl
4062 << std::endl;
4063
4064
4065 // Now do the actual mesh adaptation
4066 //---------------------------------
4067
4068 // Check whether its worth our while
4069 // Either some elements want to be refined,
4070 // or the number that want to be unrefined are greater than the
4071 // specified tolerance
4072
4073 // In a parallel job, it is possible that one process may not have
4074 // any elements to refine, BUT a neighbouring process may refine an
4075 // element which changes the hanging status of a node that is on
4076 // both processes (i.e. a halo(ed) node). To get around this issue,
4077 // ALL processes need to call adapt_mesh if ANY refinement is to
4078 // take place anywhere.
4079
4080 unsigned total_n_refine = 0;
4081#ifdef OOMPH_HAS_MPI
4082 // Sum n_refine across all processors
4083 if (this->is_mesh_distributed())
4084 {
4087 1,
4089 MPI_SUM,
4090 Comm_pt->mpi_comm());
4091 }
4092 else
4093 {
4095 }
4096#else
4098#endif
4099
4100 // There may be some issues with unrefinement too, but I have not
4101 // been able to come up with an example (either in my head or in a
4102 // particular problem) where anything has arisen. I can see that
4103 // there may be an issue if n_unrefine differs across processes so
4104 // that (total_n_unrefine > max_keep_unrefined()) on some but not
4105 // all processes. I haven't seen any examples of this yet so the
4106 // following code may or may not work! (Andy, 06/03/08)
4107
4108 unsigned total_n_unrefine = 0;
4109#ifdef OOMPH_HAS_MPI
4110 // Sum n_unrefine across all processors
4111 if (this->is_mesh_distributed())
4112 {
4115 1,
4117 MPI_SUM,
4118 Comm_pt->mpi_comm());
4119 }
4120 else
4121 {
4123 }
4124#else
4126#endif
4127
4128 oomph_info << "---> " << total_n_refine << " elements to be refined, and "
4129 << total_n_unrefine << " to be unrefined, in total."
4130 << std::endl;
4131
4132 if ((total_n_refine > 0) || (total_n_unrefine > this->max_keep_unrefined()))
4133 {
4134#ifdef PARANOID
4135#ifdef OOMPH_HAS_MPI
4136
4137 // Sanity check: Each processor checks if the enforced unrefinement of
4138 // its haloed element is matched by enforced unrefinement of the
4139 // corresponding halo elements on the other processors.
4140 if (this->is_mesh_distributed())
4141 {
4142 // Store number of processors and current process
4144 int n_proc = Comm_pt->nproc();
4145 int my_rank = Comm_pt->my_rank();
4146
4147 // Loop over all other domains/processors
4148 for (int d = 0; d < n_proc; d++)
4149 {
4150 // Don't talk to yourself
4151 if (d != my_rank)
4152 {
4153 {
4154 // Get the vector of halo elements whose non-halo counterpart
4155 // are on processor d
4157 this->halo_element_pt(d));
4158
4159 // Create vector containing (0)1 to indicate that
4160 // halo element is (not) to be unrefined
4161 unsigned nhalo = halo_elem_pt.size();
4163 for (unsigned e = 0; e < nhalo; e++)
4164 {
4165 if (dynamic_cast<PRefineableElement*>(halo_elem_pt[e])
4166 ->to_be_p_unrefined())
4167 {
4169 }
4170 }
4171
4172 // Trap the case when there are no halo elements
4173 // so that we don't get a segfault in the MPI send
4174 if (nhalo > 0)
4175 {
4176 // Send it across
4178 nhalo,
4179 MPI_INT,
4180 d,
4181 0,
4182 Comm_pt->mpi_comm());
4183 }
4184 }
4185
4186 {
4187 // Get the vector of haloed elements on current processor
4189 this->haloed_element_pt(d));
4190
4191 // Ask processor d to send vector containing (0)1 for
4192 // halo element with current processor to be (not)unrefined
4193 unsigned nhaloed = haloed_elem_pt.size();
4195 // Trap to catch the case that there are no haloed elements
4196 if (nhaloed > 0)
4197 {
4199 nhaloed,
4200 MPI_INT,
4201 d,
4202 0,
4203 Comm_pt->mpi_comm(),
4204 &status);
4205 }
4206
4207 // Check it
4208 for (unsigned e = 0; e < nhaloed; e++)
4209 {
4210 if (((halo_to_be_unrefined[e] == 0) &&
4211 (dynamic_cast<PRefineableElement*>(haloed_elem_pt[e])
4212 ->to_be_p_unrefined())) ||
4213 ((halo_to_be_unrefined[e] == 1) &&
4214 (!dynamic_cast<PRefineableElement*>(haloed_elem_pt[e])
4215 ->to_be_p_unrefined())))
4216 {
4217 std::ostringstream error_message;
4218 error_message
4219 << "Error in refinement: \n"
4220 << "Haloed element: " << e << " on proc " << my_rank
4221 << " \n"
4222 << "wants to be unrefined whereas its halo counterpart on\n"
4223 << "proc " << d << " doesn't (or vice versa)...\n"
4224 << "This is most likely because the error estimator\n"
4225 << "has not assigned the same errors to halo and haloed\n"
4226 << "elements -- it ought to!\n";
4227 throw OomphLibError(error_message.str(),
4230 }
4231 }
4232 }
4233 }
4234 }
4235
4236
4237 // Loop over all other domains/processors
4238 for (int d = 0; d < n_proc; d++)
4239 {
4240 // Don't talk to yourself
4241 if (d != my_rank)
4242 {
4243 {
4244 // Get the vector of halo elements whose non-halo counterpart
4245 // are on processor d
4247 this->halo_element_pt(d));
4248
4249 // Create vector containing (0)1 to indicate that
4250 // halo element is (not) to be refined
4251 unsigned nhalo = halo_elem_pt.size();
4253 for (unsigned e = 0; e < nhalo; e++)
4254 {
4255 if (dynamic_cast<PRefineableElement*>(halo_elem_pt[e])
4256 ->to_be_p_refined())
4257 {
4258 halo_to_be_refined[e] = 1;
4259 }
4260 }
4261
4262 // Send it across
4263 if (nhalo > 0)
4264 {
4266 nhalo,
4267 MPI_INT,
4268 d,
4269 0,
4270 Comm_pt->mpi_comm());
4271 }
4272 }
4273
4274 {
4275 // Get the vector of haloed elements on current processor
4277 this->haloed_element_pt(d));
4278
4279 // Ask processor d to send vector containing (0)1 for
4280 // halo element with current processor to be (not)refined
4281 unsigned nhaloed = haloed_elem_pt.size();
4283 if (nhaloed > 0)
4284 {
4286 nhaloed,
4287 MPI_INT,
4288 d,
4289 0,
4290 Comm_pt->mpi_comm(),
4291 &status);
4292 }
4293
4294 // Check it
4295 for (unsigned e = 0; e < nhaloed; e++)
4296 {
4297 if (((halo_to_be_refined[e] == 0) &&
4298 (dynamic_cast<PRefineableElement*>(haloed_elem_pt[e])
4299 ->to_be_p_refined())) ||
4300 ((halo_to_be_refined[e] == 1) &&
4301 (!dynamic_cast<PRefineableElement*>(haloed_elem_pt[e])
4302 ->to_be_p_refined())))
4303 {
4304 std::ostringstream error_message;
4305 error_message
4306 << "Error in refinement: \n"
4307 << "Haloed element: " << e << " on proc " << my_rank
4308 << " \n"
4309 << "wants to be refined whereas its halo counterpart on\n"
4310 << "proc " << d << " doesn't (or vice versa)...\n"
4311 << "This is most likely because the error estimator\n"
4312 << "has not assigned the same errors to halo and haloed\n"
4313 << "elements -- it ought to!\n";
4314 throw OomphLibError(error_message.str(),
4317 }
4318 }
4319 }
4320 }
4321 }
4322 }
4323#endif
4324#endif
4325
4326 // Perform the actual adaptation
4328
4329 // The number of refineable elements is still local to each process
4330 this->Nunrefined = n_unrefine;
4331 this->Nrefined = n_refine;
4332 }
4333 // If not worthwhile, say so but still reorder nodes and kill external
4334 // storage for consistency in parallel computations
4335 else
4336 {
4337#ifdef OOMPH_HAS_MPI
4338 // Delete any external element storage - any interaction will still
4339 // be set up on the fly again, so we need to get rid of old information.
4340 // This particularly causes problems in multi-domain examples where
4341 // we decide not to refine one of the meshes
4343#endif
4344
4345 // Reorder the nodes within the mesh's node vector
4346 // to establish a standard ordering regardless of the sequence
4347 // of mesh refinements -- this is required to allow dump/restart
4348 // on refined meshes
4349 this->reorder_nodes();
4350
4351#ifdef OOMPH_HAS_MPI
4352
4353 // Now (re-)classify halo and haloed nodes and synchronise hanging
4354 // nodes
4355 // This is required in cases where delete_all_external_storage()
4356 // made dependent nodes to external halo nodes nonhanging.
4357 if (this->is_mesh_distributed())
4358 {
4362 }
4363
4364#endif
4365
4366 if (n_refine == 0)
4367 {
4368 oomph_info << "\n Not enough benefit in adapting mesh. " << std::endl
4369 << std::endl;
4370 }
4371 this->Nunrefined = 0;
4372 this->Nrefined = 0;
4373 }
4374 }
4375
4376
4377 //================================================================
4378 /// p-adapt mesh, which exists in two representations,
4379 /// namely as:
4380 /// - a FE mesh
4381 /// - a forest of Oc or QuadTrees
4382 ///
4383 /// p-refinement/derefinement process is documented (in tecplot-able form)
4384 /// if requested.
4385 ///
4386 /// Procedure:
4387 /// - Loop over all elements and do the p-refinement/unrefinement for
4388 /// those who want to be refined. Note: p-refinement builds fully-
4389 /// functional elements.
4390 /// - For all nodes that were hanging on the previous mesh (and are still
4391 /// marked as such), fill in their nodal values (consistent
4392 /// with the current hanging node scheme) to make sure they are fully
4393 /// functional, should they have become non-hanging during the
4394 /// mesh-adaptation. Then mark the nodes as non-hanging.
4395 /// - Delete any nodes that have become obsolete.
4396 /// - Mark up hanging nodes and setup hanging node scheme (incl.
4397 /// recursive cleanup for hanging nodes that depend on other
4398 /// hanging nodes).
4399 /// - run a quick self-test on the neighbour finding scheme and
4400 /// check the integrity of the elements (if PARANOID)
4401 /// - doc hanging node status, boundary conditions, neighbour
4402 /// scheme if requested.
4403 ///
4404 ///
4405 /// After adaptation, all nodes (whether new or old) have up-to-date
4406 /// current and previous values.
4407 ///
4408 /// If refinement process is being documented, the following information
4409 /// is documented:
4410 /// - The files
4411 /// - "neighbours.dat"
4412 /// - "all_nodes.dat"
4413 /// - "new_nodes.dat"
4414 /// - "hang_nodes_*.dat"
4415 /// where the * denotes a direction (n,s,e,w) in 2D
4416 /// or (r,l,u,d,f,b) in 3D
4417 /// .
4418 /// can be viewed with
4419 /// - QHangingNodes.mcr
4420 /// .
4421 /// - The file
4422 /// - "hangnodes_withmasters.dat"
4423 /// .
4424 /// can be viewed with
4425 /// - QHangingNodesWithMasters.mcr
4426 /// .
4427 /// to check the hanging node status.
4428 /// - The neighbour status of the elements is documented in
4429 /// - "neighbours.dat"
4430 /// .
4431 /// and can be viewed with
4432 /// - QuadTreeNeighbours.mcr
4433 /// .
4434 //=================================================================
4436 {
4437#ifdef OOMPH_HAS_MPI
4438 // Delete any external element storage before performing the adaptation
4439 // (in particular, external halo nodes that are on mesh boundaries)
4441#endif
4442
4443 // Only perform the adapt step if the mesh has any elements. This is
4444 // relevant in a distributed problem with multiple meshes, where a
4445 // particular process may not have any elements on a particular submesh.
4446 if (this->nelement() > 0)
4447 {
4448 double t_start = 0.0;
4450 {
4452 }
4453
4454 // Do refinement/unrefinement if required
4456
4458 {
4459 double t_end = TimingHelpers::timer();
4460 oomph_info << "Time for p-refinement/unrefinement: " << t_end - t_start
4461 << std::endl;
4463 }
4464
4465 // Loop over all nodes in mesh and free the dofs of those that were
4466 //-----------------------------------------------------------------
4467 // pinned only because they were hanging nodes. Also update their
4468 //-----------------------------------------------------------------
4469 // nodal values so that they contain data that is consistent
4470 //----------------------------------------------------------
4471 // with the hanging node representation
4472 //-------------------------------------
4473 // (Even if the nodal data isn't actually accessed because the node
4474 // is still hanging -- we don't know this yet, and this step makes
4475 // sure that all nodes are fully functional and up-to-date, should
4476 // they become non-hanging below).
4477 //
4478 //
4479 // However, if we have a fixed mesh and hanging nodes on the boundary
4480 // become non-hanging they will not necessarily respect the curvilinear
4481 // boundaries. This can only happen in 3D of course because it is not
4482 // possible to have hanging nodes on boundaries in 2D.
4483 // The solution is to store those nodes on the boundaries that are
4484 // currently hanging and then check to see whether they have changed
4485 // status at the end of the refinement procedure.
4486 // If it has changed, then we need to adjust their positions.
4487 const unsigned n_boundary = this->nboundary();
4488 const unsigned mesh_dim = this->finite_element_pt(0)->dim();
4490
4491 unsigned long n_node = this->nnode();
4492 for (unsigned long n = 0; n < n_node; n++)
4493 {
4494 // Get the pointer to the node
4495 Node* nod_pt = this->node_pt(n);
4496
4497 // Get the number of values in the node
4498 unsigned n_value = nod_pt->nvalue();
4499
4500 // We need to find if any of the values are hanging
4501 bool is_hanging = nod_pt->is_hanging();
4502 // Loop over the values and find out whether any are hanging
4503 for (unsigned n = 0; n < n_value; n++)
4504 {
4505 is_hanging |= nod_pt->is_hanging(n);
4506 }
4507
4508 // If the node is hanging then ...
4509 if (is_hanging)
4510 {
4511 // Unless they are turned into hanging nodes again below
4512 // (this might or might not happen), fill in all the necessary
4513 // data to make them 'proper' nodes again.
4514
4515 // Reconstruct the nodal values/position from the node's
4516 // hanging node representation
4517 unsigned nt = nod_pt->ntstorage();
4518 Vector<double> values(n_value);
4519 unsigned n_dim = nod_pt->ndim();
4520 Vector<double> position(n_dim);
4521 // Loop over all history values
4522 for (unsigned t = 0; t < nt; t++)
4523 {
4524 nod_pt->value(t, values);
4525 for (unsigned i = 0; i < n_value; i++)
4526 {
4527 nod_pt->set_value(t, i, values[i]);
4528 }
4529 nod_pt->position(t, position);
4530 for (unsigned i = 0; i < n_dim; i++)
4531 {
4532 nod_pt->x(t, i) = position[i];
4533 }
4534 }
4535
4536 // If it's an algebraic node: Update its previous nodal positions too
4537 AlgebraicNode* alg_node_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
4538 if (alg_node_pt != 0)
4539 {
4540 bool update_all_time_levels = true;
4542 }
4543
4544
4545 // If it's a Solid node, update Lagrangian coordinates
4546 // from its hanging node representation
4547 SolidNode* solid_node_pt = dynamic_cast<SolidNode*>(nod_pt);
4548 if (solid_node_pt != 0)
4549 {
4551 for (unsigned i = 0; i < n_lagrangian; i++)
4552 {
4553 solid_node_pt->xi(i) = solid_node_pt->lagrangian_position(i);
4554 }
4555 }
4556
4557 // Now store geometrically hanging nodes on boundaries that
4558 // may need updating after refinement.
4559 // There will only be a problem if we have 3 spatial dimensions
4560 if ((mesh_dim > 2) && (nod_pt->is_hanging()))
4561 {
4562 // If the node is on a boundary then add a pointer to the node
4563 // to our lookup scheme
4564 if (nod_pt->is_on_boundary())
4565 {
4566 // Storage for the boundaries on which the Node is located
4567 std::set<unsigned>* boundaries_pt;
4568 nod_pt->get_boundaries_pt(boundaries_pt);
4569 if (boundaries_pt != 0)
4570 {
4571 // Loop over the boundaries and add a pointer to the node
4572 // to the appropriate storage scheme
4573 for (std::set<unsigned>::iterator it = boundaries_pt->begin();
4574 it != boundaries_pt->end();
4575 ++it)
4576 {
4578 }
4579 }
4580 }
4581 }
4582
4583 } // End of is_hanging
4584
4585 // Initially mark all nodes as 'non-hanging' and `obsolete'
4586 nod_pt->set_nonhanging();
4587 nod_pt->set_obsolete();
4588 }
4589
4590 double t_end = 0.0;
4592 {
4594 oomph_info << "Time for sorting out initial hanging status: "
4595 << t_end - t_start << std::endl;
4597 }
4598
4599 // Stick all elements into a vector
4601 this->forest_pt()->stick_leaves_into_vector(tree_nodes_pt);
4602
4603 // Copy the elements into the mesh Vector
4604 unsigned long num_tree_nodes = tree_nodes_pt.size();
4605 this->element_pt().resize(num_tree_nodes);
4606 for (unsigned long e = 0; e < num_tree_nodes; e++)
4607 {
4608 this->element_pt(e) = tree_nodes_pt[e]->object_pt();
4609
4610 // Now loop over all nodes in element and mark them as non-obsolete
4612 unsigned n_node = this_el_pt->nnode(); // caching pre-loop
4613 for (unsigned n = 0; n < n_node; n++)
4614 {
4616 }
4617
4618 // Mark up so that repeated refinements do not occur
4619 // (Required because refined element is the same element as the
4620 // original)
4622 dynamic_cast<PRefineableElement*>(this->element_pt(e));
4623 cast_el_pt->deselect_for_p_refinement();
4624 cast_el_pt->deselect_for_p_unrefinement();
4625 }
4626
4627 // Cannot delete nodes that are still marked as obsolete
4628 // because they may still be required to assemble the hanging schemes
4629 //-------------------------------------------------------------------
4630
4631 // Mark up hanging nodes
4632 //----------------------
4633
4634 // Output streams for the hanging nodes
4636 // Setup the output files for hanging nodes, this must be called
4637 // precisely once for the forest. Note that the files will only
4638 // actually be opened if doc_info.Doc_flag is true
4639 this->forest_pt()->open_hanging_node_files(doc_info,
4641
4642 for (unsigned long e = 0; e < num_tree_nodes; e++)
4643 {
4644 // Generic setup
4645 tree_nodes_pt[e]->object_pt()->setup_hanging_nodes(
4647 // Element specific setup
4648 tree_nodes_pt[e]->object_pt()->further_setup_hanging_nodes();
4649 }
4650
4651 // Close the hanging node files and delete the memory allocated
4652 // for the streams
4653 this->forest_pt()->close_hanging_node_files(doc_info,
4655
4656
4658 {
4660 oomph_info << "Time for setup_hanging_nodes() and "
4661 "further_setup_hanging_nodes() for "
4662 << num_tree_nodes << " elements: " << t_end - t_start
4663 << std::endl;
4665 }
4666
4667 // Read out the number of continously interpolated values
4668 // from one of the elements (assuming it's the same in all elements)
4669 unsigned ncont_interpolated_values =
4670 tree_nodes_pt[0]->object_pt()->ncont_interpolated_values();
4671
4672 // Complete the hanging nodes schemes by dealing with the
4673 // recursively hanging nodes
4674 this->complete_hanging_nodes(ncont_interpolated_values);
4675
4676
4678 {
4680 oomph_info << "Time for complete_hanging_nodes: " << t_end - t_start
4681 << std::endl;
4683 }
4684
4685 /// Update the boundary element info -- this can be a costly procedure
4686 /// and for this reason the mesh writer might have decided not to set up
4687 /// this scheme. If so, we won't change this and suppress its creation...
4689 {
4691 }
4692
4694 {
4695 t_end = TimingHelpers::timer();
4696 oomph_info << "Time for boundary element info: " << t_end - t_start
4697 << std::endl;
4699 }
4700
4701 // BENFLAG: Reset all the node update elements.
4702 // This is necessary to prevent the following case: A node N is
4703 // shared between two elements, A and B. The update element for
4704 // the node is set to A, say. Element A is p-refined and now
4705 // nolonger has N as a node. However the node update element for N
4706 // is still A but the node doesn't exist in A.
4708 dynamic_cast<MacroElementNodeUpdateElementBase*>(this->element_pt(0));
4709 if (first_macro_el_pt != 0)
4710 {
4711 // Now set the node update info elementwise
4712 for (unsigned e = 0; e < this->nelement(); e++)
4713 {
4714 // Cast to macro element
4716 dynamic_cast<MacroElementNodeUpdateElementBase*>(
4717 this->element_pt(e));
4718 if (macro_el_pt != 0)
4719 {
4720 // Get vector of geometric objects from element (construct vector
4721 // via copy operation)
4722 Vector<GeomObject*> geom_object_pt(macro_el_pt->geom_object_pt());
4723
4724 // (Re)set node update info for all the nodes in the element
4725 macro_el_pt->set_node_update_info(geom_object_pt);
4726 }
4727 }
4728 }
4729
4730#ifdef PARANOID
4731
4732 // Doc/check the neighbours
4733 //-------------------------
4735 this->forest_pt()->stick_all_tree_nodes_into_vector(all_tree_nodes_pt);
4736
4737 // Check the neighbours
4738 this->forest_pt()->check_all_neighbours(doc_info);
4739
4740 // Check the integrity of the elements
4741 // -----------------------------------
4742
4743 // Loop over elements and get the elemental integrity
4744 double max_error = 0.0;
4745 for (unsigned long e = 0; e < num_tree_nodes; e++)
4746 {
4747 double max_el_error;
4748 tree_nodes_pt[e]->object_pt()->check_integrity(max_el_error);
4749 // If the elemental error is greater than our maximum error
4750 // reset the maximum
4751 if (max_el_error > max_error)
4752 {
4754 }
4755 }
4756
4758 {
4759 std::ostringstream error_stream;
4760 error_stream << "Mesh refined: Max. error in integrity check: "
4761 << max_error << " is too big"
4762 << "\ni.e. bigger than RefineableElement::"
4763 << "max_integrity_tolerance()="
4765 << std::endl;
4766
4767 std::ofstream some_file;
4768 some_file.open("ProblemMesh.dat");
4769 for (unsigned long n = 0; n < n_node; n++)
4770 {
4771 // Get the pointer to the node
4772 Node* nod_pt = this->node_pt(n);
4773 // Get the dimension
4774 unsigned n_dim = nod_pt->ndim();
4775 // Output the coordinates
4776 for (unsigned i = 0; i < n_dim; i++)
4777 {
4778 some_file << this->node_pt(n)->x(i) << " ";
4779 }
4780 some_file << std::endl;
4781 }
4782 some_file.close();
4783
4784 error_stream << "Documented problem mesh in ProblemMesh.dat"
4785 << std::endl;
4786
4787 throw OomphLibError(
4789 }
4790 else
4791 {
4792 oomph_info << "Mesh refined: Max. error in integrity check: "
4793 << max_error << " is OK" << std::endl;
4795 << "i.e. less than RefineableElement::max_integrity_tolerance()="
4797 << std::endl;
4798 }
4799
4800
4802 {
4804 oomph_info << "Time for (paranoid only) checking of integrity: "
4805 << t_end - t_start << std::endl;
4807 }
4808
4809#endif
4810
4811 // Loop over all elements other than the final level and deactivate the
4812 // objects, essentially set the pointer that point to nodes that are
4813 // about to be deleted to NULL. This must take place here because nodes
4814 // addressed by elements that are dead but still living in the tree might
4815 // have been made obsolete in the last round of refinement
4816 //(Not strictly required, as tree structure has not changed, but does no
4817 // harm)
4818 for (unsigned long e = 0; e < this->forest_pt()->ntree(); e++)
4819 {
4821 }
4822
4823 // Now we can prune the dead nodes from the mesh.
4825
4827 {
4828 t_end = TimingHelpers::timer();
4829 oomph_info << "Time for deactivating objects and pruning nodes: "
4830 << t_end - t_start << std::endl;
4832 }
4833
4834 // Finally: Reorder the nodes within the mesh's node vector
4835 // to establish a standard ordering regardless of the sequence
4836 // of mesh refinements -- this is required to allow dump/restart
4837 // on refined meshes
4838 this->reorder_nodes();
4839
4841 {
4842 t_end = TimingHelpers::timer();
4843 oomph_info << "Time for reordering " << nnode()
4844 << " nodes: " << t_end - t_start << std::endl;
4846 }
4847
4848 // Now we can correct the nodes on boundaries that were hanging that
4849 // are no longer hanging
4850 // Only bother if we have more than two dimensions
4851 if (mesh_dim > 2)
4852 {
4853 // Loop over the boundaries
4854 for (unsigned b = 0; b < n_boundary; b++)
4855 {
4856 // Remove deleted nodes from the set
4857 unsigned n_del = deleted_node_pt.size();
4858 for (unsigned j = 0; j < n_del; j++)
4859 {
4861 }
4862
4863 // If the nodes that were hanging are still hanging then remove them
4864 // from the set (note increment is not in for command for efficiencty)
4865 for (std::set<Node*>::iterator it =
4867 it != hanging_nodes_on_boundary_pt[b].end();)
4868 {
4869 if ((*it)->is_hanging())
4870 {
4871 hanging_nodes_on_boundary_pt[b].erase(it++);
4872 }
4873 else
4874 {
4875 ++it;
4876 }
4877 }
4878
4879 // Are there any nodes that have changed geometric hanging status
4880 // on the boundary
4881 // The slightly painful part is that we must adjust the position
4882 // via the macro-elements which are only available through the
4883 // elements and not the nodes.
4884 if (hanging_nodes_on_boundary_pt[b].size() > 0)
4885 {
4886 // If so we loop over all elements adjacent to the boundary
4887 unsigned n_boundary_element = this->nboundary_element(b);
4888 for (unsigned e = 0; e < n_boundary_element; ++e)
4889 {
4890 // Get a pointer to the element
4892
4893 // Do we have a solid element
4895 dynamic_cast<SolidFiniteElement*>(el_pt);
4896
4897 // Determine whether there is a macro element
4898 bool macro_present = (el_pt->macro_elem_pt() != 0);
4899 // Or a solid macro element
4900 if (solid_el_pt != 0)
4901 {
4902 macro_present |= (solid_el_pt->undeformed_macro_elem_pt() != 0);
4903 }
4904
4905 // Only bother to do anything if there is a macro element
4906 // or undeformed macro element in a SolidElement
4907 if (macro_present)
4908 {
4909 // Loop over the nodes
4910 // ALH: (could optimise to only loop over
4911 // node associated with the boundary with more effort)
4912 unsigned n_el_node = el_pt->nnode();
4913 for (unsigned n = 0; n < n_el_node; n++)
4914 {
4915 // Cache pointer to the node
4916 Node* nod_pt = el_pt->node_pt(n);
4917 if (nod_pt->is_on_boundary(b))
4918 {
4919 // Is the Node in our set
4920 std::set<Node*>::iterator it =
4922 // If we have found the Node then update the position
4923 // to be consistent with the macro-element representation
4925 {
4926 // Specialise local and global coordinates to 3D
4927 // because there is only a problem in 3D.
4928 Vector<double> s(3), x(3);
4929 // Find the local coordinate of the ndoe
4931 // Find the number of time history values
4932 const unsigned ntstorage = nod_pt->ntstorage();
4933
4934 // Do we have a solid node
4936 dynamic_cast<SolidNode*>(nod_pt);
4937 if (solid_node_pt)
4938 {
4939 // Assign Lagrangian coordinates from undeformed
4940 // macro element (if it has one -- get_x_and_xi()
4941 // does "the right thing" anyway. Leave actual
4942 // nodal positions alone -- we're doing a solid
4943 // mechanics problem and once we're going
4944 // the nodal positions are always computed, never
4945 // (automatically) reset to macro-element based
4946 // positions; not even on pinned boundaries
4947 // because the user may have other ideas about where
4948 // these should go -- pinning means "don't touch the
4949 // value", not "leave where the macro-element thinks
4950 // it should be"
4951 Vector<double> x_fe(3), xi(3), xi_fe(3);
4952 solid_el_pt->get_x_and_xi(s, x_fe, x, xi_fe, xi);
4953 for (unsigned i = 0; i < 3; i++)
4954 {
4955 solid_node_pt->xi(i) = xi[i];
4956 }
4957 }
4958 else
4959 {
4960 // Set position and history values from the
4961 // macro-element representation
4962 for (unsigned t = 0; t < ntstorage; t++)
4963 {
4964 // Get the history value from the macro element
4965 el_pt->get_x(t, s, x);
4966
4967 // Set the coordinate to that of the macroelement
4968 // representation
4969 for (unsigned i = 0; i < 3; i++)
4970 {
4971 nod_pt->x(t, i) = x[i];
4972 }
4973 }
4974 } // End of non-solid node case
4975
4976 // Now remove the node from the list
4978 // If there are no Nodes left then exit the loops
4979 if (hanging_nodes_on_boundary_pt[b].size() == 0)
4980 {
4982 break;
4983 }
4984 }
4985 }
4986 }
4987 } // End of macro element case
4988 }
4989 }
4990 }
4991 } // End of case when we have fixed nodal positions
4992
4993 // Final doc
4994 //-----------
4996 {
4997 // Doc the boundary conditions ('0' for non-existent, '1' for free,
4998 //----------------------------------------------------------------
4999 // '2' for pinned -- ideal for tecplot scatter sizing.
5000 //----------------------------------------------------
5001 // num_tree_nodes=tree_nodes_pt.size();
5002
5003 // Determine maximum number of values at any node in this type of
5004 // element
5005 RefineableElement* el_pt = tree_nodes_pt[0]->object_pt();
5006 // Initalise max_nval
5007 unsigned max_nval = 0;
5008 for (unsigned n = 0; n < el_pt->nnode(); n++)
5009 {
5010 if (el_pt->node_pt(n)->nvalue() > max_nval)
5011 {
5012 max_nval = el_pt->node_pt(n)->nvalue();
5013 }
5014 }
5015
5016 // Open the output file
5017 std::ostringstream fullname;
5018 std::ofstream bcs_file;
5019 fullname.str("");
5020 fullname << doc_info.directory() << "/bcs" << doc_info.number()
5021 << ".dat";
5022 bcs_file.open(fullname.str().c_str());
5023
5024 // Loop over elements
5025 for (unsigned long e = 0; e < num_tree_nodes; e++)
5026 {
5027 el_pt = tree_nodes_pt[e]->object_pt();
5028 // Loop over nodes in element
5029 unsigned n_nod = el_pt->nnode();
5030 for (unsigned n = 0; n < n_nod; n++)
5031 {
5032 // Get pointer to the node
5033 Node* nod_pt = el_pt->node_pt(n);
5034 // Find the dimension of the node
5035 unsigned n_dim = nod_pt->ndim();
5036 // Write the nodal coordinates to the file
5037 for (unsigned i = 0; i < n_dim; i++)
5038 {
5039 bcs_file << nod_pt->x(i) << " ";
5040 }
5041
5042 // Loop over all values in this element
5043 for (unsigned i = 0; i < max_nval; i++)
5044 {
5045 // Value exists at this node:
5046 if (i < nod_pt->nvalue())
5047 {
5048 bcs_file << " " << 1 + nod_pt->is_pinned(i);
5049 }
5050 // ...if not just dump out a zero
5051 else
5052 {
5053 bcs_file << " 0 ";
5054 }
5055 }
5056 bcs_file << std::endl;
5057 }
5058 }
5059 bcs_file.close();
5060
5061 // Doc all nodes
5062 //---------------
5063 std::ofstream all_nodes_file;
5064 fullname.str("");
5065 fullname << doc_info.directory() << "/all_nodes" << doc_info.number()
5066 << ".dat";
5067 all_nodes_file.open(fullname.str().c_str());
5068
5069 all_nodes_file << "ZONE \n";
5070
5071 // Need to recompute the number of nodes since it may have
5072 // changed during mesh refinement/unrefinement
5073 n_node = this->nnode();
5074 for (unsigned long n = 0; n < n_node; n++)
5075 {
5076 Node* nod_pt = this->node_pt(n);
5077 unsigned n_dim = nod_pt->ndim();
5078 for (unsigned i = 0; i < n_dim; i++)
5079 {
5080 all_nodes_file << this->node_pt(n)->x(i) << " ";
5081 }
5082 all_nodes_file << std::endl;
5083 }
5084
5085 all_nodes_file.close();
5086
5087
5088 // Doc all hanging nodes:
5089 //-----------------------
5090 std::ofstream some_file;
5091 fullname.str("");
5092 fullname << doc_info.directory() << "/all_hangnodes"
5093 << doc_info.number() << ".dat";
5094 some_file.open(fullname.str().c_str());
5095 for (unsigned long n = 0; n < n_node; n++)
5096 {
5097 Node* nod_pt = this->node_pt(n);
5098
5099 if (nod_pt->is_hanging())
5100 {
5101 unsigned n_dim = nod_pt->ndim();
5102 for (unsigned i = 0; i < n_dim; i++)
5103 {
5104 some_file << nod_pt->x(i) << " ";
5105 }
5106
5107 // ALH: Added this to stop Solid problems seg-faulting
5108 if (this->node_pt(n)->nvalue() > 0)
5109 {
5110 some_file << " " << nod_pt->raw_value(0);
5111 }
5112 some_file << std::endl;
5113 }
5114 }
5115 some_file.close();
5116
5117 // Doc all hanging nodes and their masters
5118 // View with QHangingNodesWithMasters.mcr
5119 fullname.str("");
5120 fullname << doc_info.directory() << "/geometric_hangnodes_withmasters"
5121 << doc_info.number() << ".dat";
5122 some_file.open(fullname.str().c_str());
5123 for (unsigned long n = 0; n < n_node; n++)
5124 {
5125 Node* nod_pt = this->node_pt(n);
5126 if (nod_pt->is_hanging())
5127 {
5128 unsigned n_dim = nod_pt->ndim();
5129 unsigned nmaster = nod_pt->hanging_pt()->nmaster();
5130 some_file << "ZONE I=" << nmaster + 1 << std::endl;
5131 for (unsigned i = 0; i < n_dim; i++)
5132 {
5133 some_file << nod_pt->x(i) << " ";
5134 }
5135 some_file << " 2 " << std::endl;
5136
5137 for (unsigned imaster = 0; imaster < nmaster; imaster++)
5138 {
5140 nod_pt->hanging_pt()->master_node_pt(imaster);
5141 unsigned n_dim = master_nod_pt->ndim();
5142 for (unsigned i = 0; i < n_dim; i++)
5143 {
5144 some_file << master_nod_pt->x(i) << " ";
5145 }
5146 some_file << " 1 " << std::endl;
5147 }
5148 }
5149 }
5150 some_file.close();
5151
5152 // Doc all hanging nodes and their masters
5153 // View with QHangingNodesWithMasters.mcr
5154 for (unsigned i = 0; i < ncont_interpolated_values; i++)
5155 {
5156 fullname.str("");
5158 << "/nonstandard_hangnodes_withmasters" << i << "_"
5159 << doc_info.number() << ".dat";
5160 some_file.open(fullname.str().c_str());
5161 unsigned n_nod = this->nnode();
5162 for (unsigned long n = 0; n < n_nod; n++)
5163 {
5164 Node* nod_pt = this->node_pt(n);
5165 if (nod_pt->is_hanging(i))
5166 {
5167 if (nod_pt->hanging_pt(i) != nod_pt->hanging_pt())
5168 {
5169 unsigned nmaster = nod_pt->hanging_pt(i)->nmaster();
5170 some_file << "ZONE I=" << nmaster + 1 << std::endl;
5171 unsigned n_dim = nod_pt->ndim();
5172 for (unsigned j = 0; j < n_dim; j++)
5173 {
5174 some_file << nod_pt->x(j) << " ";
5175 }
5176 some_file << " 2 " << std::endl;
5177 for (unsigned imaster = 0; imaster < nmaster; imaster++)
5178 {
5180 nod_pt->hanging_pt(i)->master_node_pt(imaster);
5181 unsigned n_dim = master_nod_pt->ndim();
5182 for (unsigned j = 0; j < n_dim; j++)
5183 {
5184 // some_file << master_nod_pt->x(i) << " ";
5185 }
5186 some_file << " 1 " << std::endl;
5187 }
5188 }
5189 }
5190 }
5191 some_file.close();
5192 }
5193
5194 } // End of documentation
5195 } // End if (this->nelement()>0)
5196
5197 /// /BENFLAG: Check that all the nodes belong to their update elements
5198 // std::cout << "p_adapt_mesh(): Checking stuff works!" << std::endl;
5199 // for(unsigned j=0; j<this->nnode(); j++)
5200 // {
5201 // MacroElementNodeUpdateNode* macro_nod_pt =
5202 // dynamic_cast<MacroElementNodeUpdateNode*>(this->node_pt(j));
5203 // if(macro_nod_pt!=0)
5204 // {
5205 // bool big_problem = true;
5206 // std::cout << "Node " << macro_nod_pt << " at [ " << macro_nod_pt->x(0)
5207 // << ", " << macro_nod_pt->x(1) << " ]" << std::endl; FiniteElement*
5208 // up_el_pt =
5209 // dynamic_cast<FiniteElement*>(macro_nod_pt->node_update_element_pt());
5210 // for(unsigned l=0; l<up_el_pt->nnode(); l++)
5211 // {
5212 // if(up_el_pt->node_pt(l)==macro_nod_pt)
5213 // {
5214 // big_problem = false;
5215 // break;
5216 // }
5217 // }
5218 // if(big_problem)
5219 // {
5220 // std::cout << " This node doesn't exist in it's update element!" <<
5221 // std::endl;
5222 // }
5223 // }
5224 // }
5225
5226#ifdef OOMPH_HAS_MPI
5227
5228 // Now (re-)classify halo and haloed nodes and synchronise hanging
5229 // nodes
5230 if (this->is_mesh_distributed())
5231 {
5233 }
5234
5235#endif
5236 }
5237
5238 //========================================================================
5239 /// p-unrefine mesh uniformly
5240 /// Unlike in h-refinement, we can simply p-unrefine each element in the mesh
5241 //========================================================================
5243 {
5244 // Select all elements for unrefinement
5245 unsigned long Nelement = this->nelement();
5246 for (unsigned long e = 0; e < Nelement; e++)
5247 {
5248 // Get pointer to p-refineable element
5250 dynamic_cast<PRefineableElement*>(this->element_pt(e));
5251 // Mark for p-refinement if possible. If not then p_adapt_mesh() will
5252 // report the error.
5253 if (el_pt != 0)
5254 {
5255 el_pt->select_for_p_unrefinement();
5256 }
5257 }
5258
5259 // Do the actual mesh adaptation
5261 }
5262
5263 //========================================================================
5264 /// p-refine mesh by refining the elements identified by their numbers.
5265 //========================================================================
5268 {
5269#ifdef OOMPH_HAS_MPI
5270 if (this->is_mesh_distributed())
5271 {
5272 std::ostringstream warn_stream;
5273 warn_stream << "You are attempting to refine selected elements of a "
5274 << std::endl
5275 << "distributed mesh. This may have undesired effects."
5276 << std::endl;
5277
5279 "TreeBasedRefineableMeshBase::refine_selected_elements()",
5281 }
5282#endif
5283
5284 // Select elements for refinement
5285 unsigned long nref = elements_to_be_refined.size();
5286 for (unsigned long e = 0; e < nref; e++)
5287 {
5288 // Get pointer to p-refineable element
5290 this->element_pt(elements_to_be_refined[e]));
5291 // Mark for p-refinement if possible. If not then p_adapt_mesh() will
5292 // report the error.
5293 if (el_pt != 0)
5294 {
5295 el_pt->select_for_p_refinement();
5296 }
5297 }
5298
5299 // Do the actual mesh adaptation
5300 p_adapt_mesh();
5301 }
5302
5303 //========================================================================
5304 /// p-refine mesh by refining the elements identified by their pointers.
5305 //========================================================================
5308 {
5309#ifdef OOMPH_HAS_MPI
5310 if (this->is_mesh_distributed())
5311 {
5312 std::ostringstream warn_stream;
5313 warn_stream << "You are attempting to refine selected elements of a "
5314 << std::endl
5315 << "distributed mesh. This may have undesired effects."
5316 << std::endl;
5317
5319 "TreeBasedRefineableMeshBase::refine_selected_elements()",
5321 }
5322#endif
5323
5324 // Select elements for refinement
5325 unsigned long nref = elements_to_be_refined_pt.size();
5326 for (unsigned long e = 0; e < nref; e++)
5327 {
5328 elements_to_be_refined_pt[e]->select_for_p_refinement();
5329 }
5330
5331 // Do the actual mesh adaptation
5332 p_adapt_mesh();
5333 }
5334
5335} // 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
Algebraic nodes are nodes with an algebraic positional update function.
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition nodes.h:483
Information for documentation of results: Directory and file number to enable output in the form RESL...
bool is_doc_enabled() const
Are we documenting?
void disable_doc()
Disable documentation.
std::string directory() const
Output directory.
unsigned & number()
Number used (e.g.) for labeling output files.
A general Finite Element class.
Definition elements.h:1317
void position(const Vector< double > &zeta, Vector< double > &r) const
Return the parametrised position of the FiniteElement in its incarnation as a GeomObject,...
Definition elements.h:2680
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
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition elements.h:2615
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
Node ** Node_pt
Storage for pointers to the nodes in the element.
Definition elements.h:1323
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
virtual void node_update()
Update the positions of all nodes in the element using each node update function. The default impleme...
Definition elements.cc:5102
int non_halo_proc_ID()
ID of processor ID that holds non-halo counterpart of halo element; negative if not a halo.
Definition elements.h:1157
unsigned ndim() const
Access function to # of Eulerian coordinates.
unsigned nlagrangian() const
Access function to # of Lagrangian coordinates.
Class that contains data for hanging nodes.
Definition nodes.h:742
Base class for elements that allow MacroElement-based node update.
A general mesh class.
Definition mesh.h:67
std::map< unsigned, Vector< Node * > > Shared_node_pt
Map of vectors holding the pointers to the shared nodes. These are all the nodes that are on two "nei...
Definition mesh.h:116
bool is_mesh_distributed() const
Boolean to indicate if Mesh has been distributed.
Definition mesh.h:1596
bool Lookup_for_elements_next_boundary_is_setup
Flag to indicate that the lookup schemes for elements that are adjacent to the boundaries has been se...
Definition mesh.h:87
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition mesh.h:477
unsigned nboundary_element(const unsigned &b) const
Return number of finite elements that are adjacent to boundary b.
Definition mesh.h:886
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition mesh.h:440
virtual void setup_boundary_element_info()
Interface for function that is used to setup the boundary information (Empty virtual function – imple...
Definition mesh.h:279
FiniteElement * boundary_element_pt(const unsigned &b, const unsigned &e) const
Return pointer to e-th finite element on boundary b.
Definition mesh.h:848
OomphCommunicator * Comm_pt
Pointer to communicator – set to NULL if mesh is not distributed.
Definition mesh.h:119
virtual void reorder_nodes(const bool &use_old_ordering=true)
Re-order nodes in the order in which they appear in elements – can be overloaded for more efficient r...
Definition mesh.cc:508
unsigned nboundary() const
Return number of boundaries.
Definition mesh.h:835
Vector< GeneralisedElement * > halo_element_pt(const unsigned &p)
Return vector of halo elements in this Mesh whose non-halo counterpart is held on processor p.
Definition mesh.h:1748
unsigned long nnode() const
Return number of nodes in the mesh.
Definition mesh.h:604
void delete_all_external_storage()
Wipe the storage for all externally-based elements.
Definition mesh.cc:9190
unsigned nhalo_node()
Total number of halo nodes in this Mesh.
Definition mesh.h:1886
const Vector< GeneralisedElement * > & element_pt() const
Return reference to the Vector of elements.
Definition mesh.h:464
void output(std::ostream &outfile)
Output for all elements.
Definition mesh.cc:2027
Node * haloed_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th haloed node in this Mesh whose halo counterpart is held on processor p.
Definition mesh.h:2022
Vector< Node * > prune_dead_nodes()
Prune nodes. Nodes that have been marked as obsolete are removed from the mesh (and its boundary-node...
Definition mesh.cc:966
Vector< GeneralisedElement * > Element_pt
Vector of pointers to generalised elements.
Definition mesh.h:186
unsigned nhaloed_node()
Total number of haloed nodes in this Mesh.
Definition mesh.h:1993
Node * halo_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th halo node in this Mesh whose non-halo counterpart is held on processor p.
Definition mesh.h:1922
Vector< GeneralisedElement * > haloed_element_pt(const unsigned &p)
Return vector of haloed elements in this Mesh whose haloing counterpart is held on processor p.
Definition mesh.h:1787
Node * shared_node_pt(const unsigned &p, const unsigned &j)
Access fct to the j-th shared node in this Mesh who has a counterpart on processor p.
Definition mesh.h:2118
unsigned long nelement() const
Return number of elements in the mesh.
Definition mesh.h:598
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
void set_non_obsolete()
Mark node as non-obsolete.
Definition nodes.h:1442
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition nodes.h:1054
void set_hanging_pt(HangInfo *const &hang_pt, const int &i)
Set the hanging data for the i-th value. (hang_pt=0 to make non-hanging)
Definition nodes.cc:2068
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
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
void select_for_p_refinement()
Select the element for p-refinement.
void deselect_for_p_refinement()
Deselect the element for p-refinement.
RefineableElements are FiniteElements that may be subdivided into children to provide a better local ...
Tree * tree_pt()
Access function: Pointer to quadtree representation of this element.
static double & max_integrity_tolerance()
Max. allowed discrepancy in element integrity check.
void deselect_for_refinement()
Deselect the element for refinement.
unsigned & max_keep_unrefined()
Max. number of elements that we allow to remain unrefined if no other mesh adaptation is required (to...
unsigned Nrefined
Stats: Number of elements that were refined.
double & min_permitted_error()
Access fct for min. error (i.e. (try to) merge elements if their error is smaller)
DocInfo *& doc_info_pt()
Access fct for pointer to DocInfo.
DocInfo doc_info()
Access fct for DocInfo.
unsigned & nrefinement_overruled()
Number of elements that would have liked to be refined further but can't because they've reached the ...
unsigned Nunrefined
Stats: Number of elements that were unrefined.
double & max_error()
Access fct for max. actual error in present solution (i.e. before re-solve on adapted mesh)
double & max_permitted_error()
Access fct for max. error (i.e. split elements if their error is larger)
SolidFiniteElement class.
Definition elements.h:3565
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...
void output(std::ostream &outfile)
Output function: x,y,u or x,y,z,u.
Base class for tree-based refineable meshes.
TreeForest * forest_pt()
Return pointer to the Forest represenation of the mesh.
void synchronise_hanging_nodes(const unsigned &ncont_interpolated_values)
Synchronise the hanging nodes if the mesh is distributed.
virtual void read_refinement(std::ifstream &restart_file, Vector< Vector< unsigned > > &to_be_refined)
Read refinement pattern to allow for rebuild.
virtual void get_elements_at_refinement_level(unsigned &refinement_level, Vector< RefineableElement * > &level_elements)
Extract the elements at a particular refinement level in the refinement pattern - used in Mesh::redis...
virtual void refine_selected_elements(const Vector< unsigned > &elements_to_be_refined)
Refine mesh by splitting the elements identified by their numbers.
unsigned & max_p_refinement_level()
Access fct for max. permissible p-refinement level (relative to base mesh)
virtual void dump_refinement(std::ostream &outfile)
Dump refinement pattern to allow for rebuild.
unsigned unrefine_uniformly()
Unrefine mesh uniformly: Return 0 for success, 1 for failure (if unrefinement has reached the coarses...
virtual void refine(std::ifstream &restart_file)
Refine mesh according to refinement pattern in restart file.
virtual void additional_synchronise_hanging_nodes(const unsigned &ncont_interpolated_values)=0
Additional synchronisation of hanging nodes Required for reconcilliation of hanging nodes on the oute...
unsigned & min_refinement_level()
Access fct for min. permissible refinement level (relative to base mesh)
virtual void adapt_mesh()
Perform the actual tree-based mesh adaptation. A simple wrapper to call the function without document...
virtual void refine_base_mesh_as_in_reference_mesh(TreeBasedRefineableMeshBase *const &ref_mesh_pt)
Refine base mesh to same degree as reference mesh (relative to original unrefined mesh).
virtual void get_refinement_pattern(Vector< Vector< unsigned > > &to_be_refined)
Extract refinement pattern: Consider the hypothetical mesh obtained by truncating the refinement of t...
void complete_hanging_nodes_recursively(Node *&nod_pt, Vector< Node * > &master_nodes, Vector< double > &hang_weights, const int &ival)
Auxiliary routine for recursive hanging node completion.
unsigned & max_refinement_level()
Access fct for max. permissible refinement level (relative to base mesh)
virtual void refine_as_in_reference_mesh(TreeBasedRefineableMeshBase *const &ref_mesh_pt)
Refine mesh once so that its topology etc becomes that of the (finer!) reference mesh – if possible!...
void refine_base_mesh(Vector< Vector< unsigned > > &to_be_refined)
Refine base mesh according to specified refinement pattern.
void p_refine_selected_elements(const Vector< unsigned > &elements_to_be_refined)
p-refine mesh by refining the elements identified by their numbers.
virtual void split_elements_if_required()=0
Split all the elements in the mesh if required. This template free interface will be overloaded in Re...
void refine_uniformly()
Refine mesh uniformly.
void p_unrefine_uniformly(DocInfo &doc_info)
p-unrefine mesh uniformly
virtual void get_refinement_levels(unsigned &min_refinement_level, unsigned &max_refinement_level)
Get max/min refinement levels in mesh.
virtual void p_refine_elements_if_required()=0
p-refine all the elements in the mesh if required. This template free interface will be overloaded in...
void complete_hanging_nodes(const int &ncont_interpolated_values)
Complete the hanging node scheme recursively.
TreeForest * Forest_pt
Forest representation of the mesh.
void synchronise_nonhanging_nodes()
Synchronise the positions of non-hanging nodes that depend on non-existent neighbours (e....
void adapt(const Vector< double > &elemental_error)
Adapt mesh: Refine elements whose error is lager than err_max and (try to) unrefine those whose error...
void p_adapt(const Vector< double > &elemental_error)
p-adapt mesh: Refine elements whose error is lager than err_max and (try to) unrefine those whose err...
void p_adapt_mesh()
Perform the actual tree-based mesh p-adaptation. A simple wrapper to call the function without docume...
virtual bool refine_base_mesh_as_in_reference_mesh_minus_one(TreeBasedRefineableMeshBase *const &ref_mesh_pt)
Refine base mesh to same degree as reference mesh minus one level of refinement (relative to original...
void p_refine_uniformly()
p-refine mesh uniformly
void classify_halo_and_haloed_nodes(DocInfo &doc_info, const bool &report_stats)
Classify all halo and haloed information in the mesh (overloaded version from Mesh base class....
virtual void check_all_neighbours(DocInfo &doc_info)=0
Document/check the neighbours of all the nodes in the forest. This must be overloaded for different t...
void stick_all_tree_nodes_into_vector(Vector< Tree * > &all_forest_nodes)
Traverse forest and stick pointers to all "nodes" into Vector.
Definition tree.cc:405
virtual void open_hanging_node_files(DocInfo &doc_info, Vector< std::ofstream * > &output_stream)=0
Open output files that will store any hanging nodes in the forest. Return a vector of the output stre...
unsigned ntree()
Number of trees in forest.
Definition tree.h:460
void stick_leaves_into_vector(Vector< Tree * > &forest_nodes)
Traverse forst and stick pointers to leaf "nodes" into Vector.
Definition tree.cc:379
TreeRoot * tree_pt(const unsigned &i) const
Return pointer to i-th tree in forest.
Definition tree.h:466
void close_hanging_node_files(DocInfo &doc_info, Vector< std::ofstream * > &output_stream)
Close output files that will store any hanging nodes in the forest and delete any associated storage....
Definition tree.cc:432
A generalised tree base class that abstracts the common functionality between the quad- and octrees u...
Definition tree.h:74
unsigned nsons() const
Return number of sons (zero if it's a leaf node)
Definition tree.h:129
void traverse_all(Tree::VoidMemberFctPt member_function)
Traverse the tree and execute void Tree member function member_function() at all its "nodes".
Definition tree.cc:145
bool is_leaf()
Return true if the tree is a leaf node.
Definition tree.h:220
int son_type() const
Return son type.
Definition tree.h:214
Tree * son_pt(const int &son_index) const
Return pointer to the son for a given index. Note that to aid code readability specific enums have be...
Definition tree.h:103
void deactivate_object()
Call the RefineableElement's deactivate_element() function.
Definition tree.cc:341
void traverse_all_but_leaves(Tree::VoidMemberFctPt member_function)
Traverse the tree and execute void Tree member function member_function() at all its "nodes" aparat f...
Definition tree.cc:184
void merge_sons_if_required(Mesh *&mesh_pt)
If required, merge the four sons for unrefinement – criterion: bool object_pt()-> sons_to_be_unrefine...
Definition tree.cc:302
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition Vector.h:58
bool Doc_comprehensive_timings
Global boolean to switch on comprehensive timing – can probably be declared const false when developm...
double timer()
returns the time in seconds after some point in past
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
void pause(std::string message)
Pause and display message.
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...
Helper struct to collate data required during TreeBasedRefineableMeshBase::synchronise_hanging_nodes.