refineable_mesh.template.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26// Templated refineable mesh functions
27
28// Include guards to prevent multiple inclusion of the header
29#ifndef OOMPH_REFINEABLE_MESH_TEMPLATE_HEADER
30#define OOMPH_REFINEABLE_MESH_TEMPLATE_HEADER
31
32// Config header generated by autoconfig
33#ifdef HAVE_CONFIG_H
34#include <oomph-lib-config.h>
35#endif
36
37// oomph-lib headers
38#include "refineable_mesh.h"
39#include "missing_masters.h"
40
41namespace oomph
42{
43#ifdef OOMPH_HAS_MPI
44
45 //========================================================================
46 /// Additional actions required to synchronise halo nodes where master
47 /// nodes could not be found during synchronise_hanging_nodes().
48 /// Overloaded from Mesh class to take care of master nodes on
49 /// the outer edge of the halo layer which do not exist on that
50 /// processor. This fixes problems with the synchronisation of
51 /// hanging nodes for elements with non-uniformly spaced nodes.
52 //========================================================================
53 template<class ELEMENT>
55 const unsigned& ncont_interpolated_values)
56 {
57 // Check if additional synchronisation of hanging nodes is disabled
58 if (is_additional_synchronisation_of_hanging_nodes_disabled() == true)
59 {
60 return;
61 }
62
63 // This provides all the node-adding helper functions required to
64 // reconstruct the missing halo master nodes on this processor
65 using namespace Missing_masters_functions;
66
67
68 double t_start = 0.0;
69 double t_end = 0.0;
71 {
73 }
74
75 // Store number of processors and current process
77 int n_proc = Comm_pt->nproc();
78 int my_rank = Comm_pt->my_rank();
79
80
81#ifdef PARANOID
82 // Paranoid check to make sure nothing else is using the
83 // external storage. This will need to be changed at some
84 // point if we are to use non-uniformly spaced nodes in
85 // multi-domain problems.
86 bool err = false;
87 // Print out external storage
88 for (int d = 0; d < n_proc; d++)
89 {
90 if (d != my_rank)
91 {
92 // Check to see if external storage is being used by anybody else
93 if (nexternal_haloed_node(d) != 0)
94 {
95 err = true;
96 oomph_info << "Processor " << my_rank
97 << "'s external haloed nodes with processor " << d
98 << " are:" << std::endl;
99 for (unsigned i = 0; i < nexternal_haloed_node(d); i++)
100 {
101 oomph_info << "external_haloed_node_pt(" << d << "," << i
102 << ") = " << external_haloed_node_pt(d, i) << std::endl;
103 oomph_info << "x = ( " << external_haloed_node_pt(d, i)->x(0)
104 << " , " << external_haloed_node_pt(d, i)->x(1) << " )"
105 << std::endl;
106 }
107 }
108 }
109 }
110 for (int d = 0; d < n_proc; d++)
111 {
112 if (d != my_rank)
113 {
114 // Check to see if external storage is being used by anybody else
115 if (nexternal_halo_node(d) != 0)
116 {
117 err = true;
118 oomph_info << "Processor " << my_rank
119 << "'s external halo nodes with processor " << d
120 << " are:" << std::endl;
121 for (unsigned i = 0; i < nexternal_halo_node(d); i++)
122 {
123 oomph_info << "external_halo_node_pt(" << d << "," << i
124 << ") = " << external_halo_node_pt(d, i) << std::endl;
125 oomph_info << "x = ( " << external_halo_node_pt(d, i)->x(0) << " , "
126 << external_halo_node_pt(d, i)->x(1) << " )"
127 << std::endl;
128 }
129 }
130 }
131 }
132 if (err)
133 {
134 std::ostringstream err_stream;
135 err_stream << "There are already some nodes in the external storage"
136 << std::endl
137 << "for this mesh. This bit assumes that nothing else"
138 << std::endl
139 << "uses this storage (for now).";
140 throw OomphLibError(
142 }
143#endif
144
145
146 // Compare the halo and haloed nodes for discrepancies in hanging status
147
148 // Storage for the hanging status of halo/haloed nodes on elements
151
152 // Storage for the haloed nodes with discrepancies in their hanging status
153 // with each processor
155 n_proc);
156
158 {
160 }
161
162 // Store number of continuosly interpolated values as int
163 int ncont_inter_values = ncont_interpolated_values;
164
165 // Loop over processes: Each processor checks that is haloed nodes
166 // with proc d have consistent hanging stats with halo counterparts.
167 for (int d = 0; d < n_proc; d++)
168 {
169 // No halo with self: Setup hang info for my haloed nodes with proc d
170 // then get ready to receive halo info from processor d.
171 if (d != my_rank)
172 {
173 // Loop over haloed nodes
174 unsigned nh = nhaloed_node(d);
175 for (unsigned j = 0; j < nh; j++)
176 {
177 // Get node
178 Node* nod_pt = haloed_node_pt(d, j);
179
180 // Loop over the hanging status for each interpolated variable
181 // (and the geometry)
182 for (int icont = -1; icont < ncont_inter_values; icont++)
183 {
184 // Store the hanging status of this haloed node
185 if (nod_pt->is_hanging(icont))
186 {
187 unsigned n_master = nod_pt->hanging_pt(icont)->nmaster();
188 haloed_hanging[d].push_back(n_master);
189 }
190 else
191 {
192 haloed_hanging[d].push_back(0);
193 }
194 }
195 }
196
197 // Receive the hanging status information from the corresponding process
198 unsigned count_haloed = haloed_hanging[d].size();
199
200#ifdef PARANOID
201 // Check that number of halo and haloed data match
202 unsigned tmp = 0;
203 MPI_Recv(&tmp, 1, MPI_UNSIGNED, d, 0, Comm_pt->mpi_comm(), &status);
204 if (tmp != count_haloed)
205 {
206 std::ostringstream error_stream;
207 error_stream << "Number of halo data, " << tmp
208 << ", does not match number of haloed data, "
209 << count_haloed << std::endl;
210 throw OomphLibError(error_stream.str(),
213 }
214#endif
215
216 // Get the data (if any)
217 if (count_haloed != 0)
218 {
219 halo_hanging[d].resize(count_haloed);
220 MPI_Recv(&halo_hanging[d][0],
222 MPI_INT,
223 d,
224 0,
225 Comm_pt->mpi_comm(),
226 &status);
227 }
228 }
229 else // d==my_rank, i.e. current process: Send halo hanging status
230 // to process dd where it's received (see above) and compared
231 // and compared against the hang status of the haloed nodes
232 {
233 for (int dd = 0; dd < n_proc; dd++)
234 {
235 // No halo with yourself
236 if (dd != d)
237 {
238 // Storage for halo hanging status and counter
240
241 // Loop over halo nodes
242 unsigned nh = nhalo_node(dd);
243 for (unsigned j = 0; j < nh; j++)
244 {
245 // Get node
246 Node* nod_pt = halo_node_pt(dd, j);
247
248 // Loop over the hanging status for each interpolated variable
249 // (and the geometry)
250 for (int icont = -1; icont < ncont_inter_values; icont++)
251 {
252 // Store hanging status of halo node
253 if (nod_pt->is_hanging(icont))
254 {
255 unsigned n_master = nod_pt->hanging_pt(icont)->nmaster();
256 local_halo_hanging.push_back(n_master);
257 }
258 else
259 {
260 local_halo_hanging.push_back(0);
261 }
262 }
263 }
264
265
266 // Send the information to the relevant process
268
269#ifdef PARANOID
270 // Check that number of halo and haloed data match
271 MPI_Send(&count_halo, 1, MPI_UNSIGNED, dd, 0, Comm_pt->mpi_comm());
272#endif
273
274 // Send data (if any)
275 if (count_halo != 0)
276 {
279 MPI_INT,
280 dd,
281 0,
282 Comm_pt->mpi_comm());
283 }
284 }
285 }
286 }
287 }
288
290 {
292 oomph_info << "Time for first all-to-all in "
293 "additional_synchronise_hanging_nodes(): "
294 << t_end - t_start << std::endl;
296 }
297
298
299 // Now compare equivalent halo and haloed vectors to find discrepancies.
300 // It is possible that a master node may not be on either process involved
301 // in the halo-haloed scheme; to work round this, we use the shared_node
302 // storage scheme, which stores all nodes that are on each pair of
303 // processors in the same order on each of the two processors
304
305
306 // Loop over domains: Each processor checks consistency of hang status
307 // of its haloed nodes with proc d against the halo counterpart. Haloed
308 // wins if there are any discrepancies.
309 for (int d = 0; d < n_proc; d++)
310 {
311 // No halo with yourself
312 if (d != my_rank)
313 {
314 // Counter for traversing haloed data
315 unsigned count = 0;
316
317 // Loop over haloed nodes
318 unsigned nh = nhaloed_node(d);
319 for (unsigned j = 0; j < nh; j++)
320 {
321 // Get node
322 Node* nod_pt = haloed_node_pt(d, j);
323
324 // Loop over the hanging status for each interpolated variable
325 // (and the geometry)
326 for (int icont = -1; icont < ncont_inter_values; icont++)
327 {
328 // Compare hanging status of halo/haloed counterpart structure
329
330 // Haloed is is hanging and haloed has different number
331 // of master nodes (which includes none in which case it isn't
332 // hanging)
333 if ((haloed_hanging[d][count] > 0) &&
335 {
336 // Store this node so it can be synchronised later
338 std::pair<Node*, unsigned>(nod_pt, d));
339 }
340 // Increment counter for number of haloed data
341 count++;
342 } // end of loop over icont
343 } // end of loop over haloed nodes
344 }
345 } // end loop over all processors
346
347
348 // Populate external halo(ed) node storage with master nodes of halo(ed)
349 // nodes
350
351 // Loop over domains: Each processor checks consistency of hang status
352 // of its haloed nodes with proc d against the halo counterpart. Haloed
353 // wins if there are any discrepancies.
354 for (int d = 0; d < n_proc; d++)
355 {
356 // No halo with yourself
357 if (d != my_rank)
358 {
359 // Now add haloed master nodes to external storage
360 //===============================================
361
362 // Storage for data to be sent
365
366 // Count number of haloed nonmaster nodes for halo process
369
370 // Loop over hanging halo nodes with discrepancies
371 std::map<Node*, unsigned>::iterator j;
374 j++)
375 {
376 Node* nod_pt = (*j).first;
377 // Find index of this haloed node in the halo storage of processor d
378 //(But find in shared node storage in case it is actually haloed on
379 // another processor which we don't know about)
380 std::vector<Node*>::iterator it = std::find(
381 Shared_node_pt[d].begin(), Shared_node_pt[d].end(), nod_pt);
382 if (it != Shared_node_pt[d].end())
383 {
384 // Tell other processor to create this node
385 // send_unsigneds.push_back(1);
387
388 // Tell the other processor where to find this node in its halo node
389 // storage
390 unsigned index = it - Shared_node_pt[d].begin();
391 haloed_nonmaster_node_index.push_back(index);
392
393 // Tell this processor that this node is really a haloed node
394 // This also packages up the data which needs to be sent to the
395 // processor on which the halo equivalent node lives
396 recursively_add_masters_of_external_haloed_node(d,
397 nod_pt,
398 this,
402 }
403 else
404 {
405 throw OomphLibError("Haloed node not found in haloed node storage",
408 }
409 }
410
411 // How much data needs to be sent?
414
415 // Send ammount of data
416 MPI_Send(
417 &send_unsigneds_count, 1, MPI_UNSIGNED, d, 0, Comm_pt->mpi_comm());
418 MPI_Send(
419 &send_doubles_count, 1, MPI_UNSIGNED, d, 1, Comm_pt->mpi_comm());
420
421 // Send to halo process the number of haloed nodes we processed
423 1,
425 d,
426 2,
427 Comm_pt->mpi_comm());
429 {
433 d,
434 3,
435 Comm_pt->mpi_comm());
436 }
437
438 // Send data about external halo nodes
439 if (send_unsigneds_count > 0)
440 {
441 // Only send if there is anything to send
445 d,
446 4,
447 Comm_pt->mpi_comm());
448 }
449 if (send_doubles_count > 0)
450 {
451 // Only send if there is anything to send
455 d,
456 5,
457 Comm_pt->mpi_comm());
458 }
459 }
460 else // (d==my_rank), current process
461 {
462 // Now construct and add halo versions of master nodes to external
463 // storage
464 //=======================================================================
465
466 // Loop over processors to get data
467 for (int dd = 0; dd < n_proc; dd++)
468 {
469 // Don't talk to yourself
470 if (dd != d)
471 {
472 // How much data to be received
473 unsigned nrecv_unsigneds = 0;
474 unsigned nrecv_doubles = 0;
476 1,
478 dd,
479 0,
480 Comm_pt->mpi_comm(),
481 &status);
483 1,
485 dd,
486 1,
487 Comm_pt->mpi_comm(),
488 &status);
489
490 // Get from haloed process the number of halo nodes we need to
491 // process
494 1,
496 dd,
497 2,
498 Comm_pt->mpi_comm(),
499 &status);
503 {
507 dd,
508 3,
509 Comm_pt->mpi_comm(),
510 &status);
511 }
512
513 // Storage for data to be received
516
517 // Receive data about external haloed equivalent nodes
518 if (nrecv_unsigneds > 0)
519 {
520 // Only send if there is anything to send
524 dd,
525 4,
526 Comm_pt->mpi_comm(),
527 &status);
528 }
529 if (nrecv_doubles > 0)
530 {
531 // Only send if there is anything to send
535 dd,
536 5,
537 Comm_pt->mpi_comm(),
538 &status);
539 }
540
541 // Counters for flat packed data counters
542 unsigned recv_unsigneds_count = 0;
543 unsigned recv_doubles_count = 0;
544
545 // Loop over halo nodes with discrepancies in their hanging status
546 for (unsigned j = 0; j < nhalo_nonmaster_nodes_to_process; j++)
547 {
548 // Get pointer to halo nonmaster node which needs processing
549 //(But given index is its index in the shared storage)
550 Node* nod_pt = shared_node_pt(dd, halo_nonmaster_node_index[j]);
551
552#ifdef PARANOID
553 // Check if we have a MacroElementNodeUpdateNode
554 if (dynamic_cast<MacroElementNodeUpdateNode*>(nod_pt))
555 {
556 // BENFLAG: The construction of missing master nodes for
557 // MacroElementNodeUpdateNodes does not work as
558 // expected. They require MacroElementNodeUpdateElements
559 // to be created for the missing halo nodes which will
560 // be added. It behaves as expected until duplicate
561 // nodes are pruned at the problem level.
562 std::ostringstream err_stream;
564 << "This currently doesn't work for" << std::endl
565 << "MacroElementNodeUpdateNodes because these require"
566 << std::endl
567 << "MacroElementNodeUpdateElements to be created for"
568 << std::endl
569 << "the missing halo nodes which will be added" << std::endl;
570 throw OomphLibError(err_stream.str(),
573 // OomphLibWarning(err_stream.str(),
574 // OOMPH_CURRENT_FUNCTION,
575 // OOMPH_EXCEPTION_LOCATION);
576 }
577#endif
578
579 // Construct copy of node and add to external halo node storage.
580 unsigned loc_p = (unsigned)dd;
581 unsigned node_index;
583 nod_pt,
584 this,
585 loc_p,
592 }
593
594 } // end of dd!=d
595 } // end of second loop over all processors
596 }
597 } // end loop over all processors
598
599
601 {
603 oomph_info << "Time for second all-to-all in "
604 "additional_synchronise_hanging_nodes() "
605 << t_end - t_start << std::endl;
607 }
608
609 // Populate external halo(ed) node storage with master nodes of halo(ed)
610 // nodes [end]
611
612 // Count how many external halo/haloed nodes are added
613 unsigned external_halo_count = 0;
614 unsigned external_haloed_count = 0;
615
616 // Flag to test whether we attampt to add any duplicate haloed nodes to the
617 // shared storage -- if this is the case then we have duplicate halo nodes
618 // on another processor but with different pointers and the shared scheme
619 // will not be set up correctly
620 bool duplicate_haloed_node_exists = false;
621
622 // Loop over all the processors and add the shared nodes
623 for (int d = 0; d < n_proc; d++)
624 {
625 // map of bools for whether the (external) node has been shared,
626 // initialised to 0 (false) for each domain d
627 std::map<Node*, bool> node_shared;
628
629 // For all domains lower than the current domain: Do halos first
630 // then haloed, to ensure correct order in lookup scheme from
631 // the other side
632 if (d < my_rank)
633 {
634 // Do external halo nodes
635 unsigned nexternal_halo_nod = nexternal_halo_node(d);
636 for (unsigned j = 0; j < nexternal_halo_nod; j++)
637 {
638 Node* nod_pt = external_halo_node_pt(d, j);
639
640 // Add it as a shared node from current domain
641 if (!node_shared[nod_pt])
642 {
643 this->add_shared_node_pt(d, nod_pt);
644 node_shared[nod_pt] = true;
646 }
647
648 } // end loop over nodes
649
650 // Do external haloed nodes
651 unsigned nexternal_haloed_nod = nexternal_haloed_node(d);
652 for (unsigned j = 0; j < nexternal_haloed_nod; j++)
653 {
654 Node* nod_pt = external_haloed_node_pt(d, j);
655
656 // Add it as a shared node from current domain
657 if (!node_shared[nod_pt])
658 {
659 this->add_shared_node_pt(d, nod_pt);
660 node_shared[nod_pt] = true;
662 }
663 else
664 {
666 }
667
668 } // end loop over nodes
669 }
670
671 // If the domain is bigger than the current rank: Do haloed first
672 // then halo, to ensure correct order in lookup scheme from
673 // the other side
674 if (d > my_rank)
675 {
676 // Do external haloed nodes
677 unsigned nexternal_haloed_nod = nexternal_haloed_node(d);
678 for (unsigned j = 0; j < nexternal_haloed_nod; j++)
679 {
680 Node* nod_pt = external_haloed_node_pt(d, j);
681
682 // Add it as a shared node from current domain
683 if (!node_shared[nod_pt])
684 {
685 this->add_shared_node_pt(d, nod_pt);
686 node_shared[nod_pt] = true;
688 }
689 else
690 {
692 }
693
694 } // end loop over nodes
695
696 // Do external halo nodes
697 unsigned nexternal_halo_nod = nexternal_halo_node(d);
698 for (unsigned j = 0; j < nexternal_halo_nod; j++)
699 {
700 Node* nod_pt = external_halo_node_pt(d, j);
701
702 // Add it as a shared node from current domain
703 if (!node_shared[nod_pt])
704 {
705 this->add_shared_node_pt(d, nod_pt);
706 node_shared[nod_pt] = true;
708 }
709
710 } // end loop over nodes
711
712 } // end if (d ...)
713
714 } // end loop over processes
715
716
717 // Say how many external halo/haloed nodes were added
718 oomph_info << "INFO: " << external_halo_count << " external halo nodes and"
719 << std::endl;
720 oomph_info << "INFO: " << external_haloed_count
721 << " external haloed nodes were added to the shared node scheme"
722 << std::endl;
723
724 // If we added duplicate haloed nodes, throw an error
726 {
727 // This problem should now be avoided because we are using existing
728 // communication methods to locate nodes in this case. The error used
729 // to arise as follows:
730 /// / Let my_rank==A. If this has happened then it means that
731 /// / duplicate haloed nodes exist on another processor (B). This
732 /// / problem arises if a master of a haloed node with a discrepancy
733 /// / is haloed with a different processor (C). A copy is constructed
734 /// / in the external halo storage on processor (B) because that node
735 /// / is not found in the (internal) haloed storage on (A) with (B)
736 /// / but that node already exists on processor (B) in the (internal)
737 /// / halo storage with processor (C). Thus two copies of this master
738 /// / node now exist on processor (B).
739
740 std::ostringstream err_stream;
741 err_stream << "Duplicate halo nodes exist on another processor!"
742 << std::endl
743 << "(See source code for more detailed explanation)"
744 << std::endl;
745
746 throw OomphLibError(
748 }
749
750
752 {
754 oomph_info << "Time for identification of shared nodes in "
755 "additional_synchronise_hanging_nodes(): "
756 << t_end - t_start << std::endl;
757 }
758 }
759
760#endif
761
762} // namespace oomph
763
764#endif
cstr elem_len * i
Definition cfortran.h:603
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
MacroElementNodeUpdate nodes are nodes with a positional update function, based on their element's Ma...
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
An OomphLibError object which should be thrown when an run-time error is encountered....
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
void additional_synchronise_hanging_nodes(const unsigned &ncont_interpolated_values)
Additional setup of shared node scheme This is Required for reconcilliation of hanging nodes acrross ...
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).
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...